Merge branch 'logspace-ai:dev' into Add-Ollama-LLM

This commit is contained in:
yamonkjd 2023-12-23 03:58:56 +09:00 committed by GitHub
commit 6c0b4fb416
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 833 additions and 650 deletions

View file

@ -8,6 +8,7 @@ on:
- dev
paths:
- "pyproject.toml"
workflow_dispatch:
env:
POETRY_VERSION: "1.5.1"
@ -40,7 +41,7 @@ jobs:
generateReleaseNotes: true
prerelease: true
tag: v${{ steps.check-version.outputs.version }}
commit: main
commit: dev
- name: Publish to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}

1005
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "langflow"
version = "0.6.3a5"
version = "0.6.3"
description = "A Python package with a built-in web application"
authors = ["Logspace <contact@logspace.ai>"]
maintainers = [

View file

@ -1,19 +1,15 @@
import time
from fastapi import (APIRouter, Depends, HTTPException, Query, WebSocket,
WebSocketException, status)
from fastapi import APIRouter, Depends, HTTPException, Query, WebSocket, WebSocketException, status
from fastapi.responses import StreamingResponse
from langflow.api.utils import build_input_keys_response, format_elapsed_time
from langflow.api.v1.schemas import (BuildStatus, BuiltResponse, InitResponse,
StreamData)
from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData
from langflow.graph.graph.base import Graph
from langflow.services.auth.utils import (get_current_active_user,
get_current_user_by_jwt)
from langflow.services.auth.utils import get_current_active_user, get_current_user_by_jwt
from langflow.services.cache.service import BaseCacheService
from langflow.services.cache.utils import update_build_status
from langflow.services.chat.service import ChatService
from langflow.services.deps import (get_cache_service, get_chat_service,
get_session)
from langflow.services.deps import get_cache_service, get_chat_service, get_session
from loguru import logger
from sqlmodel import Session

View file

@ -1,5 +1,5 @@
from http import HTTPStatus
from typing import Annotated, List, Optional, Union
from typing import Annotated, Any, List, Optional, Union
import sqlalchemy as sa
from fastapi import APIRouter, Body, Depends, HTTPException, UploadFile, status
@ -16,7 +16,7 @@ from langflow.api.v1.schemas import (
)
from langflow.interface.custom.custom_component import CustomComponent
from langflow.interface.custom.directory_reader import DirectoryReader
from langflow.interface.custom.utils import build_custom_component_template, create_and_validate_component
from langflow.interface.custom.utils import build_custom_component_template
from langflow.processing.process import process_graph_cached, process_tweaks
from langflow.services.auth.utils import api_key_security, get_current_active_user
from langflow.services.cache.utils import save_uploaded_file
@ -49,7 +49,7 @@ async def process_graph_data(
task_service: "TaskService" = Depends(get_task_service),
sync: bool = True,
):
task_result = None
task_result: Any = None
task_status = None
if tweaks:
try:
@ -270,7 +270,7 @@ async def custom_component(
raw_code: CustomComponentCode,
user: User = Depends(get_current_active_user),
):
component = create_and_validate_component(raw_code.code)
component = CustomComponent(code=raw_code.code)
built_frontend_node = build_custom_component_template(component, user_id=user.id)
@ -289,7 +289,6 @@ async def reload_custom_component(path: str, user: User = Depends(get_current_ac
raise ValueError(content)
extractor = CustomComponent(code=content)
extractor.validate()
return build_custom_component_template(extractor, user_id=user.id)
except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc))
@ -300,7 +299,7 @@ async def custom_component_update(
raw_code: CustomComponentCode,
user: User = Depends(get_current_active_user),
):
component = create_and_validate_component(raw_code.code)
component = CustomComponent(code=raw_code.code)
component_node = build_custom_component_template(component, user_id=user.id, update_field=raw_code.field)
# Update the field

View file

@ -1,7 +1,6 @@
from typing import Callable, List, Union
from typing import Callable, List, Optional, Union
from langchain.agents import AgentExecutor, AgentType, initialize_agent, types
from langflow import CustomComponent
from langflow.field_typing import BaseChatMemory, BaseLanguageModel, Tool
@ -20,18 +19,34 @@ class AgentInitializerComponent(CustomComponent):
"memory": {"display_name": "Memory"},
"tools": {"display_name": "Tools"},
"llm": {"display_name": "Language Model"},
"code": {"advanced": True},
}
def build(
self, agent: str, llm: BaseLanguageModel, memory: BaseChatMemory, tools: List[Tool], max_iterations: int
self,
agent: str,
llm: BaseLanguageModel,
tools: List[Tool],
max_iterations: int,
memory: Optional[BaseChatMemory] = None,
) -> Union[AgentExecutor, Callable]:
agent = AgentType(agent)
return initialize_agent(
tools=tools,
llm=llm,
agent=agent,
memory=memory,
return_intermediate_steps=True,
handle_parsing_errors=True,
max_iterations=max_iterations,
)
if memory:
return initialize_agent(
tools=tools,
llm=llm,
agent=agent,
memory=memory,
return_intermediate_steps=True,
handle_parsing_errors=True,
max_iterations=max_iterations,
)
else:
return initialize_agent(
tools=tools,
llm=llm,
agent=agent,
return_intermediate_steps=True,
handle_parsing_errors=True,
max_iterations=max_iterations,
)

View file

@ -3,6 +3,7 @@ from langflow import CustomComponent
from langchain.llms.base import BaseLanguageModel
from langchain.chat_models.azure_openai import AzureChatOpenAI
class AzureChatOpenAIComponent(CustomComponent):
display_name: str = "AzureChatOpenAI"
description: str = "LLM model from Azure OpenAI."
@ -16,7 +17,7 @@ class AzureChatOpenAIComponent(CustomComponent):
"gpt-4-32k",
"gpt-4-vision",
]
def build_config(self):
return {
"model": {
@ -28,7 +29,7 @@ class AzureChatOpenAIComponent(CustomComponent):
"azure_endpoint": {
"display_name": "Azure Endpoint",
"required": True,
"info": "Your Azure endpoint, including the resource.. Example: `https://example-resource.azure.openai.com/`"
"info": "Your Azure endpoint, including the resource.. Example: `https://example-resource.azure.openai.com/`",
},
"azure_deployment": {
"display_name": "Deployment Name",
@ -40,18 +41,14 @@ class AzureChatOpenAIComponent(CustomComponent):
"required": True,
"advanced": True,
},
"api_key": {
"display_name": "API Key",
"required": True,
"password": True
},
"api_key": {"display_name": "API Key", "required": True, "password": True},
"temperature": {
"display_name": "Temperature",
"value": 0.7,
"field_type": "float",
"required": False,
},
"max_tokens": {
"max_tokens": {
"display_name": "Max Tokens",
"value": 1000,
"required": False,
@ -71,8 +68,6 @@ class AzureChatOpenAIComponent(CustomComponent):
temperature: float = 0.7,
max_tokens: Optional[int] = 1000,
) -> BaseLanguageModel:
return AzureChatOpenAI(
model=model,
azure_endpoint=azure_endpoint,
@ -80,5 +75,5 @@ class AzureChatOpenAIComponent(CustomComponent):
api_version=api_version,
api_key=api_key,
temperature=temperature,
max_tokens=max_tokens
max_tokens=max_tokens,
)

View file

@ -1,4 +1,4 @@
import weaviate # type: ignore
import weaviate # type: ignore
from typing import Optional, Union
from langflow import CustomComponent
@ -12,18 +12,29 @@ from langchain.embeddings.base import Embeddings
class WeaviateVectorStore(CustomComponent):
display_name: str = "Weaviate"
description: str = "Implementation of Vector Store using Weaviate"
documentation = (
"https://python.langchain.com/docs/integrations/vectorstores/weaviate"
)
documentation = "https://python.langchain.com/docs/integrations/vectorstores/weaviate"
beta = True
field_config = {
"url": {"display_name": "Weaviate URL", "value": "http://localhost:8080"},
"api_key": { "display_name": "API Key", "password": True,"required": False, },
"index_name": {"display_name": "Index name","required": False,},
"text_key": {"display_name": "Text Key","required": False, "advanced": True, "value": "text"},
"api_key": {
"display_name": "API Key",
"password": True,
"required": False,
},
"index_name": {
"display_name": "Index name",
"required": False,
},
"text_key": {"display_name": "Text Key", "required": False, "advanced": True, "value": "text"},
"documents": {"display_name": "Documents", "is_list": True},
"embedding": {"display_name": "Embedding"},
"attributes": {"display_name": "Attributes", "required": False, "is_list": True, "field_type": "str", "advanced": True},
"attributes": {
"display_name": "Attributes",
"required": False,
"is_list": True,
"field_type": "str",
"advanced": True,
},
"search_by_text": {"display_name": "Search By Text", "field_type": "bool", "advanced": True},
"code": {"show": False},
}

View file

@ -1,6 +1,6 @@
from typing import Any, List, Optional
from typing import Any, Optional
from langchain.agents import AgentExecutor, AgentType, Tool, ZeroShotAgent, initialize_agent
from langchain.agents import AgentExecutor, ZeroShotAgent
from langchain.agents.agent_toolkits import (
SQLDatabaseToolkit,
VectorStoreInfo,
@ -15,7 +15,6 @@ from langchain.agents.agent_toolkits.vectorstore.prompt import ROUTER_PREFIX as
from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
from langchain.base_language import BaseLanguageModel
from langchain.chains.llm import LLMChain
from langchain.memory.chat_memory import BaseChatMemory
from langchain.sql_database import SQLDatabase
from langchain.tools.sql_database.prompt import QUERY_CHECKER
from langchain_experimental.agents.agent_toolkits.pandas.prompt import PREFIX as PANDAS_PREFIX

View file

@ -6,8 +6,7 @@ from typing import Any, Dict, List, Type, Union
from cachetools import TTLCache, cachedmethod, keys
from fastapi import HTTPException
from langflow.interface.custom.schema import (CallableCodeDetails,
ClassCodeDetails)
from langflow.interface.custom.schema import CallableCodeDetails, ClassCodeDetails
class CodeSyntaxError(HTTPException):

View file

@ -36,4 +36,4 @@ def extract_union_types_from_generic_alias(return_type: GenericAlias) -> list:
"""
Extracts the inner type from a type hint that is a Union.
"""
return list(return_type.__args__)
return list(return_type.__args__)

View file

@ -5,10 +5,11 @@ from uuid import UUID
import yaml
from cachetools import TTLCache, cachedmethod
from fastapi import HTTPException
from langflow.interface.custom.code_parser.utils import (
extract_inner_type_from_generic_alias,
extract_union_types_from_generic_alias)
from langflow.interface.custom.directory_reader import DirectoryReader
extract_union_types_from_generic_alias,
)
from langflow.services.database.models.flow import Flow
from langflow.services.database.utils import session_getter
from langflow.services.deps import get_credential_service, get_db_service
@ -46,34 +47,6 @@ class CustomComponent(Component):
def build_config(self):
return self.field_config
def _class_template_validation(self, code: str):
TYPE_HINT_LIST = ["Optional", "Prompt", "PromptTemplate", "LLMChain"]
if not code:
raise HTTPException(
status_code=400,
detail={
"error": self.ERROR_CODE_NULL,
"traceback": "",
},
)
reader = DirectoryReader("", False)
for type_hint in TYPE_HINT_LIST:
if reader._is_type_hint_used_in_args(type_hint, code) and not reader._is_type_hint_imported(
type_hint, code
):
error_detail = {
"error": "Type hint Error",
"traceback": f"Type hint '{type_hint}' is used but not imported in the code.",
}
raise HTTPException(status_code=400, detail=error_detail)
return True
def validate(self) -> bool:
return self._class_template_validation(self.code) if self.code else False
@property
def tree(self):
return self.get_code_tree(self.code or "")
@ -206,8 +179,7 @@ class CustomComponent(Component):
return validate.create_function(self.code, self.function_entrypoint_name)
async def load_flow(self, flow_id: str, tweaks: Optional[dict] = None) -> Any:
from langflow.processing.process import (build_sorted_vertices,
process_tweaks)
from langflow.processing.process import build_sorted_vertices, process_tweaks
db_service = get_db_service()
with session_getter(db_service) as session:

View file

@ -65,12 +65,13 @@ class DirectoryReader:
def filter_loaded_components(self, data: dict, with_errors: bool) -> dict:
from langflow.interface.custom.utils import build_component
items = [
{
"name": menu["name"],
"path": menu["path"],
"components": [
(*build_component(component),component)
(*build_component(component), component)
for component in menu["components"]
if (component["error"] if with_errors else not component["error"])
],

View file

@ -1,6 +1,5 @@
from langflow.interface.custom.directory_reader import DirectoryReader
from langflow.template.frontend_node.custom_components import \
CustomComponentFrontendNode
from langflow.template.frontend_node.custom_components import CustomComponentFrontendNode
from loguru import logger
@ -75,10 +74,9 @@ def create_invalid_component_template(component, component_name):
"""Create a template for an invalid component."""
component_code = component["code"]
component_frontend_node = CustomComponentFrontendNode(
description="ERROR - Check your Python Code",
display_name=f"ERROR - {component_name}",
)
description="ERROR - Check your Python Code",
display_name=f"ERROR - {component_name}",
)
component_frontend_node.error = component.get("error", None)
field = component_frontend_node.template.get_field("code")
@ -144,4 +142,4 @@ def build_menu_items(menu_item):
except Exception as exc:
logger.error(f"Error loading Component: {component['output_types']}")
logger.exception(f"Error while building custom component {component['output_types']}: {exc}")
return menu_items
return menu_items

View file

@ -7,18 +7,20 @@ from typing import Any, Dict, List, Optional, Union
from uuid import UUID
from fastapi import HTTPException
from loguru import logger
from langflow.field_typing.range_spec import RangeSpec
from langflow.interface.custom.code_parser.utils import extract_inner_type
from langflow.interface.custom.custom_component import CustomComponent
from langflow.interface.custom.directory_reader.utils import (
build_custom_component_list_from_path, determine_component_name,
merge_nested_dicts_with_renaming)
build_custom_component_list_from_path,
determine_component_name,
merge_nested_dicts_with_renaming,
)
from langflow.interface.importing.utils import eval_custom_component_code
from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.custom_components import \
CustomComponentFrontendNode
from langflow.template.frontend_node.custom_components import CustomComponentFrontendNode
from langflow.utils.util import get_base_classes
from loguru import logger
def add_output_types(frontend_node: CustomComponentFrontendNode, return_types: List[str]):
@ -174,9 +176,7 @@ def get_field_dict(field: Union[TemplateField, dict]):
return field
def run_build_config(
custom_component: CustomComponent, user_id: Optional[Union[str, UUID]] = None, update_field=None
):
def run_build_config(custom_component: CustomComponent, user_id: Optional[Union[str, UUID]] = None, update_field=None):
"""Build the field configuration for a custom component"""
try:
@ -311,7 +311,6 @@ def create_component_template(component):
component_output_types = component["output_types"]
component_extractor = CustomComponent(code=component_code)
component_extractor.validate()
component_template = build_custom_component_template(component_extractor)
component_template["output_types"] = component_output_types
@ -343,12 +342,6 @@ def build_custom_components(settings_service):
return custom_components_from_file
def create_and_validate_component(code: str) -> CustomComponent:
component = CustomComponent(code=code)
component.validate()
return component
def update_field_dict(field_dict):
"""Update the field dictionary by calling options() or value() if they are callable"""
if "options" in field_dict and callable(field_dict["options"]):
@ -378,6 +371,3 @@ def build_component(component):
component_name = determine_component_name(component)
component_template = create_component_template(component)
return component_name, component_template

View file

@ -1,9 +1,7 @@
from cachetools import LRUCache, cached
from langflow.interface.agents.base import agent_creator
from langflow.interface.chains.base import chain_creator
from langflow.interface.custom.directory_reader.utils import \
merge_nested_dicts_with_renaming
from langflow.interface.custom.directory_reader.utils import merge_nested_dicts_with_renaming
from langflow.interface.custom.utils import build_custom_components
from langflow.interface.document_loaders.base import documentloader_creator
from langflow.interface.embeddings.base import embedding_creator
@ -70,5 +68,3 @@ def get_all_types_dict(settings_service):
native_components = build_langchain_types_dict()
custom_components_from_file = build_custom_components(settings_service)
return merge_nested_dicts_with_renaming(native_components, custom_components_from_file)

View file

@ -5,6 +5,7 @@ from langchain.agents import AgentExecutor
from langchain.chains.base import Chain
from langchain.schema import AgentAction, Document
from langchain.vectorstores.base import VectorStore
from langchain_core.messages import AIMessage
from langchain_core.runnables.base import Runnable
from langflow.interface.custom.custom_component import CustomComponent
from langflow.interface.run import build_sorted_vertices, get_memory_key, update_memory_keys
@ -105,10 +106,23 @@ def get_build_result(data_graph, session_id):
return build_sorted_vertices(data_graph)
def process_inputs(inputs: Optional[dict], artifacts: Dict[str, Any]) -> dict:
def process_inputs(
inputs: Optional[Union[dict, List[dict]]] = None, artifacts: Optional[Dict[str, Any]] = None
) -> Union[dict, List[dict]]:
if inputs is None:
inputs = {}
if artifacts is None:
artifacts = {}
if isinstance(inputs, dict):
inputs = update_inputs_dict(inputs, artifacts)
elif isinstance(inputs, List):
inputs = [update_inputs_dict(inp, artifacts) for inp in inputs]
return inputs
def update_inputs_dict(inputs: dict, artifacts: Dict[str, Any]) -> dict:
for key, value in artifacts.items():
if key == "repr":
continue
@ -125,6 +139,11 @@ async def process_runnable(runnable: Runnable, inputs: Union[dict, List[dict]]):
result = await runnable.ainvoke(inputs)
else:
raise ValueError(f"Runnable {runnable} does not support inputs of type {type(inputs)}")
# Check if the result is a list of AIMessages
if isinstance(result, list) and all(isinstance(r, AIMessage) for r in result):
result = [r.content for r in result]
elif isinstance(result, AIMessage):
result = result.content
return result

View file

@ -2,7 +2,7 @@ import ast
import contextlib
import importlib
from types import FunctionType
from typing import Dict
from typing import Dict, List, Optional, Union
from langflow.field_typing.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES
@ -260,14 +260,13 @@ def get_default_imports(code_string):
"""
Returns a dictionary of default imports for the dynamic class constructor.
"""
typing_module = importlib.import_module("typing")
default_imports = {
"Optional": typing_module.Optional,
"List": typing_module.List,
"Dict": typing_module.Dict,
"Union": typing_module.Union,
}
default_imports = {
"Optional": Optional,
"List": List,
"Dict": Dict,
"Union": Union,
}
langflow_imports = list(CUSTOM_COMPONENT_SUPPORTED_TYPES.keys())
necessary_imports = find_names_in_code(code_string, langflow_imports)
langflow_module = importlib.import_module("langflow.field_typing")

View file

@ -1,14 +1,13 @@
from typing import TYPE_CHECKING, Any, Dict, Optional
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
from asgiref.sync import async_to_sync
from celery.exceptions import SoftTimeLimitExceeded # type: ignore
from loguru import logger
from rich import print
from langflow.core.celery_app import celery_app
from langflow.processing.process import Result, generate_result, process_inputs
from langflow.services.deps import get_session_service
from langflow.services.manager import initialize_session_service
from loguru import logger
from rich import print
if TYPE_CHECKING:
from langflow.graph.vertex.base import Vertex
@ -35,7 +34,7 @@ def build_vertex(self, vertex: "Vertex") -> "Vertex":
@celery_app.task(acks_late=True)
def process_graph_cached_task(
data_graph: Dict[str, Any],
inputs: Optional[dict] = None,
inputs: Optional[Union[dict, List[dict]]] = None,
clear_cache=False,
session_id=None,
) -> Dict[str, Any]:

View file

@ -231,7 +231,7 @@ export default function ExtraSidebar(): JSX.Element {
return (
<div className="side-bar-arrangement">
<div className="side-bar-buttons-arrangement">
{hasStore && (
{hasStore && validApiKey && (
<ShadTooltip
content={
!hasApiKey || !validApiKey
@ -263,7 +263,7 @@ export default function ExtraSidebar(): JSX.Element {
</button>
</ShadTooltip>
</div>
{!hasStore && ExportMemo}
{(!hasApiKey || !validApiKey) && ExportMemo}
<ShadTooltip content={"Code"} side="top">
<div className="side-bar-button">
{flow && flow.data && (

View file

@ -129,12 +129,12 @@ import { NotionIcon } from "../icons/Notion";
import { OpenAiIcon } from "../icons/OpenAi";
import { PineconeIcon } from "../icons/Pinecone";
import { QDrantIcon } from "../icons/QDrant";
import { WeaviateIcon } from "../icons/Weaviate";
import { SearxIcon } from "../icons/Searx";
import { ShareIcon } from "../icons/Share";
import { Share2Icon } from "../icons/Share2";
import SvgSlackIcon from "../icons/Slack/SlackIcon";
import { VertexAIIcon } from "../icons/VertexAI";
import { WeaviateIcon } from "../icons/Weaviate";
import SvgWikipedia from "../icons/Wikipedia/Wikipedia";
import SvgWolfram from "../icons/Wolfram/Wolfram";
import { HackerNewsIcon } from "../icons/hackerNews";

File diff suppressed because one or more lines are too long

View file

@ -3,14 +3,10 @@ import types
from uuid import uuid4
import pytest
from fastapi import HTTPException
from langflow.interface.custom.base import CustomComponent
from langflow.interface.custom.code_parser.code_parser import (CodeParser,
CodeSyntaxError)
from langflow.interface.custom.custom_component.component import (
Component, ComponentCodeNullError)
from langflow.interface.custom.utils import (build_custom_component_template,
create_and_validate_component)
from langflow.interface.custom.code_parser.code_parser import CodeParser, CodeSyntaxError
from langflow.interface.custom.custom_component.component import Component, ComponentCodeNullError
from langflow.interface.custom.utils import build_custom_component_template
from langflow.services.database.models.flow import Flow, FlowCreate
code_default = """
@ -368,16 +364,6 @@ def test_component_get_code_tree_syntax_error():
component.get_code_tree(component.code)
def test_custom_component_class_template_validation_no_code():
"""
Test the _class_template_validation method of the CustomComponent class
raises the HTTPException when the code is None.
"""
custom_component = CustomComponent(code=None, function_entrypoint_name="build")
with pytest.raises(HTTPException):
custom_component._class_template_validation(custom_component.code)
def test_custom_component_get_code_tree_syntax_error():
"""
Test the get_code_tree method of the CustomComponent class
@ -535,12 +521,12 @@ def test_build_config_field_value_keys(component):
def test_create_and_validate_component_valid_code(test_component_code):
component = create_and_validate_component(test_component_code)
component = CustomComponent(code=test_component_code)
assert isinstance(component, CustomComponent)
def test_build_langchain_template_custom_component_valid_code(test_component_code):
component = create_and_validate_component(test_component_code)
component = CustomComponent(code=test_component_code)
frontend_node = build_custom_component_template(component)
assert isinstance(frontend_node, dict)
template = frontend_node["template"]
@ -554,7 +540,7 @@ def test_build_langchain_template_custom_component_valid_code(test_component_cod
def test_build_langchain_template_custom_component_templatefield(test_component_with_templatefield_code):
component = create_and_validate_component(test_component_with_templatefield_code)
component = CustomComponent(code=test_component_with_templatefield_code)
frontend_node = build_custom_component_template(component)
assert isinstance(frontend_node, dict)
template = frontend_node["template"]