Makes build method async to support async in CC
This commit is contained in:
parent
686b97e695
commit
842ba2835a
14 changed files with 189 additions and 184 deletions
52
poetry.lock
generated
52
poetry.lock
generated
|
|
@ -411,17 +411,17 @@ files = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "boto3"
|
name = "boto3"
|
||||||
version = "1.29.5"
|
version = "1.29.6"
|
||||||
description = "The AWS SDK for Python"
|
description = "The AWS SDK for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">= 3.7"
|
python-versions = ">= 3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "boto3-1.29.5-py3-none-any.whl", hash = "sha256:030b0f0faf8d44f97e67a5411644243482f33ebf1c45338bb40662239a16dda4"},
|
{file = "boto3-1.29.6-py3-none-any.whl", hash = "sha256:f4d19e01d176c3a5a05e4af733185ff1891b08a3c38d4a439800fa132aa6e9be"},
|
||||||
{file = "boto3-1.29.5.tar.gz", hash = "sha256:76fc6a17781c27558c526e899579ccf530df10eb279261fe7800540f0043917e"},
|
{file = "boto3-1.29.6.tar.gz", hash = "sha256:d1d0d979a70bf9b0b13ae3b017f8523708ad953f62d16f39a602d67ee9b25554"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
botocore = ">=1.32.5,<1.33.0"
|
botocore = ">=1.32.6,<1.33.0"
|
||||||
jmespath = ">=0.7.1,<2.0.0"
|
jmespath = ">=0.7.1,<2.0.0"
|
||||||
s3transfer = ">=0.7.0,<0.8.0"
|
s3transfer = ">=0.7.0,<0.8.0"
|
||||||
|
|
||||||
|
|
@ -430,13 +430,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "botocore"
|
name = "botocore"
|
||||||
version = "1.32.5"
|
version = "1.32.6"
|
||||||
description = "Low-level, data-driven core of boto 3."
|
description = "Low-level, data-driven core of boto 3."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">= 3.7"
|
python-versions = ">= 3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "botocore-1.32.5-py3-none-any.whl", hash = "sha256:b8960c955ba275915bf022c54c896c2dac1038289d8a5ace92d1431257c0a439"},
|
{file = "botocore-1.32.6-py3-none-any.whl", hash = "sha256:4454f967a4d1a01e3e6205c070455bc4e8fd53b5b0753221581ae679c55a9dfd"},
|
||||||
{file = "botocore-1.32.5.tar.gz", hash = "sha256:75a68f942cd87baff83b3a20dfda11b3aeda48aad32e4dcd6fe8992c0cb0e7db"},
|
{file = "botocore-1.32.6.tar.gz", hash = "sha256:ecec876103783b5efe6099762dda60c2af67e45f7c0ab4568e8265d11c6c449b"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -3488,13 +3488,13 @@ zookeeper = ["kazoo (>=2.8.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain"
|
name = "langchain"
|
||||||
version = "0.0.339"
|
version = "0.0.340"
|
||||||
description = "Building applications with LLMs through composability"
|
description = "Building applications with LLMs through composability"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8.1,<4.0"
|
python-versions = ">=3.8.1,<4.0"
|
||||||
files = [
|
files = [
|
||||||
{file = "langchain-0.0.339-py3-none-any.whl", hash = "sha256:fec250074a6fbb3711a51423d830006d69f34aedb67604df39c642be80852cbb"},
|
{file = "langchain-0.0.340-py3-none-any.whl", hash = "sha256:f80f40b52ef82424e38e894db8b8048b6505da100679e72613316f8d8b0243fb"},
|
||||||
{file = "langchain-0.0.339.tar.gz", hash = "sha256:34eb4d7987d979663e361da435479c6f0648a170dae3eb1e9f0f7417f033a2c1"},
|
{file = "langchain-0.0.340.tar.gz", hash = "sha256:1a6bd2511bbb81e42d2a3d7291ee03de180accab851181ee9fdbb7fbaef6c57c"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -3559,13 +3559,13 @@ six = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langfuse"
|
name = "langfuse"
|
||||||
version = "1.7.4"
|
version = "1.7.5"
|
||||||
description = "A client library for accessing langfuse"
|
description = "A client library for accessing langfuse"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8.1,<4.0"
|
python-versions = ">=3.8.1,<4.0"
|
||||||
files = [
|
files = [
|
||||||
{file = "langfuse-1.7.4-py3-none-any.whl", hash = "sha256:f5f1e19eac2d01e9854f567f0946f47dac3be59ee40f335e616355b3545018f3"},
|
{file = "langfuse-1.7.5-py3-none-any.whl", hash = "sha256:ebbcc52f454a9c7cfc9f382e66fddafddb0219f9233598317bbcb66c215b39b6"},
|
||||||
{file = "langfuse-1.7.4.tar.gz", hash = "sha256:5813d2f43e7ba106ae58f048d81c7091fd681be73b35d87d53ac321f999738ae"},
|
{file = "langfuse-1.7.5.tar.gz", hash = "sha256:99fc5a30b157a16cc3dcb82e84af13fabc2fd0d192be32ef2ad6d9a7fe27d130"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -3936,13 +3936,13 @@ typing-extensions = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "metaphor-python"
|
name = "metaphor-python"
|
||||||
version = "0.1.20"
|
version = "0.1.21"
|
||||||
description = "A Python package for the Metaphor API."
|
description = "A Python package for the Metaphor API."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
files = [
|
files = [
|
||||||
{file = "metaphor-python-0.1.20.tar.gz", hash = "sha256:a1ee7a3b21ff8644553a73bc08a4d475abed182d9a1a0a72c729910837081d50"},
|
{file = "metaphor-python-0.1.21.tar.gz", hash = "sha256:72604c45c7bf447613f9cdf713c6c57612f1790ead2e78b13c65588e5d7aa279"},
|
||||||
{file = "metaphor_python-0.1.20-py3-none-any.whl", hash = "sha256:ac06b5bf86f6fb1b2371b8be6589766da8eccd6876d56e94b738654dea9adc9a"},
|
{file = "metaphor_python-0.1.21-py3-none-any.whl", hash = "sha256:b17099f6c37e26a77fbb77a242163fa6b64aad487687264f4ec6c9d16665c5a8"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -6143,6 +6143,24 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
|
||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest-asyncio"
|
||||||
|
version = "0.21.1"
|
||||||
|
description = "Pytest support for asyncio"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pytest-asyncio-0.21.1.tar.gz", hash = "sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d"},
|
||||||
|
{file = "pytest_asyncio-0.21.1-py3-none-any.whl", hash = "sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
pytest = ">=7.0.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
|
||||||
|
testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest-cov"
|
name = "pytest-cov"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
|
|
@ -9078,4 +9096,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.9,<3.11"
|
python-versions = ">=3.9,<3.11"
|
||||||
content-hash = "8335fd767d4ff476b3caf2a9e05fcce1f353d4b3a2e181585080a618099669fa"
|
content-hash = "62e47482eefda134f0801744360624549d7024861380f51f280f60450d768615"
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ qianfan = "0.0.5"
|
||||||
pgvector = "^0.2.3"
|
pgvector = "^0.2.3"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
pytest-asyncio = "^0.21.1"
|
||||||
types-redis = "^4.6.0.5"
|
types-redis = "^4.6.0.5"
|
||||||
ipykernel = "^6.21.2"
|
ipykernel = "^6.21.2"
|
||||||
mypy = "^1.1.1"
|
mypy = "^1.1.1"
|
||||||
|
|
|
||||||
|
|
@ -8,21 +8,20 @@ from fastapi import (
|
||||||
status,
|
status,
|
||||||
)
|
)
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
|
from loguru import logger
|
||||||
|
from sqlmodel import Session
|
||||||
|
|
||||||
from langflow.api.utils import build_input_keys_response
|
from langflow.api.utils import build_input_keys_response
|
||||||
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.graph.graph.base import Graph
|
||||||
from langflow.services.auth.utils import (
|
from langflow.services.auth.utils import (
|
||||||
get_current_active_user,
|
get_current_active_user,
|
||||||
get_current_user_by_jwt,
|
get_current_user_by_jwt,
|
||||||
)
|
)
|
||||||
from langflow.services.cache.utils import update_build_status
|
|
||||||
from loguru import logger
|
|
||||||
from langflow.services.deps import get_chat_service, get_session, get_cache_service
|
|
||||||
from sqlmodel import Session
|
|
||||||
from langflow.services.chat.service import ChatService
|
|
||||||
from langflow.services.cache.service import BaseCacheService
|
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
|
||||||
|
|
||||||
router = APIRouter(tags=["Chat"])
|
router = APIRouter(tags=["Chat"])
|
||||||
|
|
||||||
|
|
@ -164,9 +163,9 @@ async def stream_build(
|
||||||
}
|
}
|
||||||
yield str(StreamData(event="log", data=log_dict))
|
yield str(StreamData(event="log", data=log_dict))
|
||||||
if vertex.is_task:
|
if vertex.is_task:
|
||||||
vertex = try_running_celery_task(vertex, user_id)
|
vertex = await try_running_celery_task(vertex, user_id)
|
||||||
else:
|
else:
|
||||||
vertex.build(user_id=user_id)
|
await vertex.build(user_id=user_id)
|
||||||
params = vertex._built_object_repr()
|
params = vertex._built_object_repr()
|
||||||
valid = True
|
valid = True
|
||||||
logger.debug(f"Building node {str(vertex.vertex_type)}")
|
logger.debug(f"Building node {str(vertex.vertex_type)}")
|
||||||
|
|
@ -193,7 +192,7 @@ async def stream_build(
|
||||||
|
|
||||||
yield str(StreamData(event="message", data=response))
|
yield str(StreamData(event="message", data=response))
|
||||||
|
|
||||||
langchain_object = graph.build()
|
langchain_object = await graph.build()
|
||||||
# Now we need to check the input_keys to send them to the client
|
# Now we need to check the input_keys to send them to the client
|
||||||
if hasattr(langchain_object, "input_keys"):
|
if hasattr(langchain_object, "input_keys"):
|
||||||
input_keys_response = build_input_keys_response(langchain_object, artifacts)
|
input_keys_response = build_input_keys_response(langchain_object, artifacts)
|
||||||
|
|
@ -224,7 +223,7 @@ async def stream_build(
|
||||||
raise HTTPException(status_code=500, detail=str(exc))
|
raise HTTPException(status_code=500, detail=str(exc))
|
||||||
|
|
||||||
|
|
||||||
def try_running_celery_task(vertex, user_id):
|
async def try_running_celery_task(vertex, user_id):
|
||||||
# Try running the task in celery
|
# Try running the task in celery
|
||||||
# and set the task_id to the local vertex
|
# and set the task_id to the local vertex
|
||||||
# if it fails, run the task locally
|
# if it fails, run the task locally
|
||||||
|
|
@ -236,5 +235,5 @@ def try_running_celery_task(vertex, user_id):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.debug(f"Error running task in celery: {exc}")
|
logger.debug(f"Error running task in celery: {exc}")
|
||||||
vertex.task_id = None
|
vertex.task_id = None
|
||||||
vertex.build(user_id=user_id)
|
await vertex.build(user_id=user_id)
|
||||||
return vertex
|
return vertex
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
from typing import Dict, Generator, List, Type, Union
|
from typing import Dict, Generator, List, Type, Union
|
||||||
|
|
||||||
from langchain.chains.base import Chain
|
from langchain.chains.base import Chain
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from langflow.graph.edge.base import Edge
|
from langflow.graph.edge.base import Edge
|
||||||
from langflow.graph.graph.constants import lazy_load_vertex_dict
|
from langflow.graph.graph.constants import lazy_load_vertex_dict
|
||||||
from langflow.graph.graph.utils import process_flow
|
from langflow.graph.graph.utils import process_flow
|
||||||
|
|
@ -8,7 +10,6 @@ from langflow.graph.vertex.base import Vertex
|
||||||
from langflow.graph.vertex.types import FileToolVertex, LLMVertex, ToolkitVertex
|
from langflow.graph.vertex.types import FileToolVertex, LLMVertex, ToolkitVertex
|
||||||
from langflow.interface.tools.constants import FILE_TOOLS
|
from langflow.interface.tools.constants import FILE_TOOLS
|
||||||
from langflow.utils import payload
|
from langflow.utils import payload
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
|
|
||||||
class Graph:
|
class Graph:
|
||||||
|
|
@ -116,13 +117,13 @@ class Graph:
|
||||||
connected_nodes: List[Vertex] = [edge.source for edge in self.edges if edge.target == node]
|
connected_nodes: List[Vertex] = [edge.source for edge in self.edges if edge.target == node]
|
||||||
return connected_nodes
|
return connected_nodes
|
||||||
|
|
||||||
def build(self) -> Chain:
|
async def build(self) -> Chain:
|
||||||
"""Builds the graph."""
|
"""Builds the graph."""
|
||||||
# Get root node
|
# Get root node
|
||||||
root_node = payload.get_root_node(self)
|
root_node = payload.get_root_node(self)
|
||||||
if root_node is None:
|
if root_node is None:
|
||||||
raise ValueError("No root node found")
|
raise ValueError("No root node found")
|
||||||
return root_node.build()
|
return await root_node.build()
|
||||||
|
|
||||||
def topological_sort(self) -> List[Vertex]:
|
def topological_sort(self) -> List[Vertex]:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,18 @@
|
||||||
import ast
|
import ast
|
||||||
|
import inspect
|
||||||
import pickle
|
import pickle
|
||||||
|
import types
|
||||||
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from langflow.graph.utils import UnbuiltObject
|
from langflow.graph.utils import UnbuiltObject
|
||||||
from langflow.graph.vertex.utils import is_basic_type
|
from langflow.graph.vertex.utils import is_basic_type
|
||||||
from langflow.interface.initialize import loading
|
from langflow.interface.initialize import loading
|
||||||
from langflow.interface.listing import lazy_load_dict
|
from langflow.interface.listing import lazy_load_dict
|
||||||
from langflow.utils.constants import DIRECT_TYPES
|
from langflow.utils.constants import DIRECT_TYPES
|
||||||
from loguru import logger
|
|
||||||
from langflow.utils.util import sync_to_async
|
from langflow.utils.util import sync_to_async
|
||||||
|
|
||||||
|
|
||||||
import inspect
|
|
||||||
import types
|
|
||||||
from typing import Any, Dict, List, Optional
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from langflow.graph.edge.base import Edge
|
from langflow.graph.edge.base import Edge
|
||||||
|
|
||||||
|
|
@ -216,18 +214,18 @@ class Vertex:
|
||||||
self._raw_params = params
|
self._raw_params = params
|
||||||
self.params = params
|
self.params = params
|
||||||
|
|
||||||
def _build(self, user_id=None):
|
async def _build(self, user_id=None):
|
||||||
"""
|
"""
|
||||||
Initiate the build process.
|
Initiate the build process.
|
||||||
"""
|
"""
|
||||||
logger.debug(f"Building {self.vertex_type}")
|
logger.debug(f"Building {self.vertex_type}")
|
||||||
self._build_each_node_in_params_dict(user_id)
|
await self._build_each_node_in_params_dict(user_id)
|
||||||
self._get_and_instantiate_class(user_id)
|
await self._get_and_instantiate_class(user_id)
|
||||||
self._validate_built_object()
|
self._validate_built_object()
|
||||||
|
|
||||||
self._built = True
|
self._built = True
|
||||||
|
|
||||||
def _build_each_node_in_params_dict(self, user_id=None):
|
async def _build_each_node_in_params_dict(self, user_id=None):
|
||||||
"""
|
"""
|
||||||
Iterates over each node in the params dictionary and builds it.
|
Iterates over each node in the params dictionary and builds it.
|
||||||
"""
|
"""
|
||||||
|
|
@ -236,9 +234,9 @@ class Vertex:
|
||||||
if value == self:
|
if value == self:
|
||||||
del self.params[key]
|
del self.params[key]
|
||||||
continue
|
continue
|
||||||
self._build_node_and_update_params(key, value, user_id)
|
await self._build_node_and_update_params(key, value, user_id)
|
||||||
elif isinstance(value, list) and self._is_list_of_nodes(value):
|
elif isinstance(value, list) and self._is_list_of_nodes(value):
|
||||||
self._build_list_of_nodes_and_update_params(key, value, user_id)
|
await self._build_list_of_nodes_and_update_params(key, value, user_id)
|
||||||
|
|
||||||
def _is_node(self, value):
|
def _is_node(self, value):
|
||||||
"""
|
"""
|
||||||
|
|
@ -252,7 +250,7 @@ class Vertex:
|
||||||
"""
|
"""
|
||||||
return all(self._is_node(node) for node in value)
|
return all(self._is_node(node) for node in value)
|
||||||
|
|
||||||
def get_result(self, user_id=None, timeout=None) -> Any:
|
async def get_result(self, user_id=None, timeout=None) -> Any:
|
||||||
# Check if the Vertex was built already
|
# Check if the Vertex was built already
|
||||||
if self._built:
|
if self._built:
|
||||||
return self._built_object
|
return self._built_object
|
||||||
|
|
@ -268,27 +266,27 @@ class Vertex:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# If there's no task_id, build the vertex locally
|
# If there's no task_id, build the vertex locally
|
||||||
self.build(user_id)
|
await self.build(user_id)
|
||||||
return self._built_object
|
return self._built_object
|
||||||
|
|
||||||
def _build_node_and_update_params(self, key, node, user_id=None):
|
async def _build_node_and_update_params(self, key, node, user_id=None):
|
||||||
"""
|
"""
|
||||||
Builds a given node and updates the params dictionary accordingly.
|
Builds a given node and updates the params dictionary accordingly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = node.get_result(user_id)
|
result = await node.get_result(user_id)
|
||||||
self._handle_func(key, result)
|
self._handle_func(key, result)
|
||||||
if isinstance(result, list):
|
if isinstance(result, list):
|
||||||
self._extend_params_list_with_result(key, result)
|
self._extend_params_list_with_result(key, result)
|
||||||
self.params[key] = result
|
self.params[key] = result
|
||||||
|
|
||||||
def _build_list_of_nodes_and_update_params(self, key, nodes: List["Vertex"], user_id=None):
|
async def _build_list_of_nodes_and_update_params(self, key, nodes: List["Vertex"], user_id=None):
|
||||||
"""
|
"""
|
||||||
Iterates over a list of nodes, builds each and updates the params dictionary.
|
Iterates over a list of nodes, builds each and updates the params dictionary.
|
||||||
"""
|
"""
|
||||||
self.params[key] = []
|
self.params[key] = []
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
built = node.get_result(user_id)
|
built = await node.get_result(user_id)
|
||||||
if isinstance(built, list):
|
if isinstance(built, list):
|
||||||
if key not in self.params:
|
if key not in self.params:
|
||||||
self.params[key] = []
|
self.params[key] = []
|
||||||
|
|
@ -318,14 +316,14 @@ class Vertex:
|
||||||
if isinstance(self.params[key], list):
|
if isinstance(self.params[key], list):
|
||||||
self.params[key].extend(result)
|
self.params[key].extend(result)
|
||||||
|
|
||||||
def _get_and_instantiate_class(self, user_id=None):
|
async def _get_and_instantiate_class(self, user_id=None):
|
||||||
"""
|
"""
|
||||||
Gets the class from a dictionary and instantiates it with the params.
|
Gets the class from a dictionary and instantiates it with the params.
|
||||||
"""
|
"""
|
||||||
if self.base_type is None:
|
if self.base_type is None:
|
||||||
raise ValueError(f"Base type for node {self.vertex_type} not found")
|
raise ValueError(f"Base type for node {self.vertex_type} not found")
|
||||||
try:
|
try:
|
||||||
result = loading.instantiate_class(
|
result = await loading.instantiate_class(
|
||||||
node_type=self.vertex_type,
|
node_type=self.vertex_type,
|
||||||
base_type=self.base_type,
|
base_type=self.base_type,
|
||||||
params=self.params,
|
params=self.params,
|
||||||
|
|
@ -358,9 +356,9 @@ class Vertex:
|
||||||
|
|
||||||
raise ValueError(message)
|
raise ValueError(message)
|
||||||
|
|
||||||
def build(self, force: bool = False, user_id=None, *args, **kwargs) -> Any:
|
async def build(self, force: bool = False, user_id=None, *args, **kwargs) -> Any:
|
||||||
if not self._built or force:
|
if not self._built or force:
|
||||||
self._build(user_id, *args, **kwargs)
|
await self._build(user_id, *args, **kwargs)
|
||||||
|
|
||||||
return self._built_object
|
return self._built_object
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import ast
|
import ast
|
||||||
from typing import Any, Dict, List, Optional, Union
|
from typing import Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
from langflow.graph.vertex.base import Vertex
|
|
||||||
from langflow.graph.utils import flatten_list
|
from langflow.graph.utils import flatten_list
|
||||||
|
from langflow.graph.vertex.base import Vertex
|
||||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -34,18 +34,18 @@ class AgentVertex(Vertex):
|
||||||
elif isinstance(source_node, ChainVertex):
|
elif isinstance(source_node, ChainVertex):
|
||||||
self.chains.append(source_node)
|
self.chains.append(source_node)
|
||||||
|
|
||||||
def build(self, force: bool = False, user_id=None, *args, **kwargs) -> Any:
|
async def build(self, force: bool = False, user_id=None, *args, **kwargs) -> Any:
|
||||||
if not self._built or force:
|
if not self._built or force:
|
||||||
self._set_tools_and_chains()
|
self._set_tools_and_chains()
|
||||||
# First, build the tools
|
# First, build the tools
|
||||||
for tool_node in self.tools:
|
for tool_node in self.tools:
|
||||||
tool_node.build(user_id=user_id)
|
await tool_node.build(user_id=user_id)
|
||||||
|
|
||||||
# Next, build the chains and the rest
|
# Next, build the chains and the rest
|
||||||
for chain_node in self.chains:
|
for chain_node in self.chains:
|
||||||
chain_node.build(tools=self.tools, user_id=user_id)
|
await chain_node.build(tools=self.tools, user_id=user_id)
|
||||||
|
|
||||||
self._build(user_id=user_id)
|
await self._build(user_id=user_id)
|
||||||
|
|
||||||
return self._built_object
|
return self._built_object
|
||||||
|
|
||||||
|
|
@ -62,13 +62,13 @@ class LLMVertex(Vertex):
|
||||||
def __init__(self, data: Dict, params: Optional[Dict] = None):
|
def __init__(self, data: Dict, params: Optional[Dict] = None):
|
||||||
super().__init__(data, base_type="llms", params=params)
|
super().__init__(data, base_type="llms", params=params)
|
||||||
|
|
||||||
def build(self, force: bool = False, user_id=None, *args, **kwargs) -> Any:
|
async def build(self, force: bool = False, user_id=None, *args, **kwargs) -> Any:
|
||||||
# LLM is different because some models might take up too much memory
|
# LLM is different because some models might take up too much memory
|
||||||
# or time to load. So we only load them when we need them.ß
|
# or time to load. So we only load them when we need them.ß
|
||||||
if self.vertex_type == self.built_node_type:
|
if self.vertex_type == self.built_node_type:
|
||||||
return self.class_built_object
|
return self.class_built_object
|
||||||
if not self._built or force:
|
if not self._built or force:
|
||||||
self._build(user_id=user_id)
|
await self._build(user_id=user_id)
|
||||||
self.built_node_type = self.vertex_type
|
self.built_node_type = self.vertex_type
|
||||||
self.class_built_object = self._built_object
|
self.class_built_object = self._built_object
|
||||||
# Avoid deepcopying the LLM
|
# Avoid deepcopying the LLM
|
||||||
|
|
@ -90,11 +90,11 @@ class WrapperVertex(Vertex):
|
||||||
def __init__(self, data: Dict):
|
def __init__(self, data: Dict):
|
||||||
super().__init__(data, base_type="wrappers")
|
super().__init__(data, base_type="wrappers")
|
||||||
|
|
||||||
def build(self, force: bool = False, user_id=None, *args, **kwargs) -> Any:
|
async def build(self, force: bool = False, user_id=None, *args, **kwargs) -> Any:
|
||||||
if not self._built or force:
|
if not self._built or force:
|
||||||
if "headers" in self.params:
|
if "headers" in self.params:
|
||||||
self.params["headers"] = ast.literal_eval(self.params["headers"])
|
self.params["headers"] = ast.literal_eval(self.params["headers"])
|
||||||
self._build(user_id=user_id)
|
await self._build(user_id=user_id)
|
||||||
return self._built_object
|
return self._built_object
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -193,7 +193,7 @@ class ChainVertex(Vertex):
|
||||||
def __init__(self, data: Dict):
|
def __init__(self, data: Dict):
|
||||||
super().__init__(data, base_type="chains")
|
super().__init__(data, base_type="chains")
|
||||||
|
|
||||||
def build(
|
async def build(
|
||||||
self,
|
self,
|
||||||
force: bool = False,
|
force: bool = False,
|
||||||
user_id=None,
|
user_id=None,
|
||||||
|
|
@ -212,9 +212,9 @@ class ChainVertex(Vertex):
|
||||||
if isinstance(value, PromptVertex):
|
if isinstance(value, PromptVertex):
|
||||||
# Build the PromptVertex, passing the tools if available
|
# Build the PromptVertex, passing the tools if available
|
||||||
tools = kwargs.get("tools", None)
|
tools = kwargs.get("tools", None)
|
||||||
self.params[key] = value.build(tools=tools, force=force)
|
self.params[key] = await value.build(tools=tools, force=force)
|
||||||
|
|
||||||
self._build(user_id=user_id)
|
await self._build(user_id=user_id)
|
||||||
|
|
||||||
return self._built_object
|
return self._built_object
|
||||||
|
|
||||||
|
|
@ -223,7 +223,7 @@ class PromptVertex(Vertex):
|
||||||
def __init__(self, data: Dict):
|
def __init__(self, data: Dict):
|
||||||
super().__init__(data, base_type="prompts")
|
super().__init__(data, base_type="prompts")
|
||||||
|
|
||||||
def build(
|
async def build(
|
||||||
self,
|
self,
|
||||||
force: bool = False,
|
force: bool = False,
|
||||||
user_id=None,
|
user_id=None,
|
||||||
|
|
@ -236,7 +236,7 @@ class PromptVertex(Vertex):
|
||||||
self.params["input_variables"] = []
|
self.params["input_variables"] = []
|
||||||
# Check if it is a ZeroShotPrompt and needs a tool
|
# Check if it is a ZeroShotPrompt and needs a tool
|
||||||
if "ShotPrompt" in self.vertex_type:
|
if "ShotPrompt" in self.vertex_type:
|
||||||
tools = [tool_node.build(user_id=user_id) for tool_node in tools] if tools is not None else []
|
tools = [await tool_node.build(user_id=user_id) for tool_node in tools] if tools is not None else []
|
||||||
# flatten the list of tools if it is a list of lists
|
# flatten the list of tools if it is a list of lists
|
||||||
# first check if it is a list
|
# first check if it is a list
|
||||||
if tools and isinstance(tools, list) and isinstance(tools[0], list):
|
if tools and isinstance(tools, list) and isinstance(tools[0], list):
|
||||||
|
|
@ -257,7 +257,7 @@ class PromptVertex(Vertex):
|
||||||
elif isinstance(self.params, dict):
|
elif isinstance(self.params, dict):
|
||||||
self.params.pop("input_variables", None)
|
self.params.pop("input_variables", None)
|
||||||
|
|
||||||
self._build(user_id=user_id)
|
await self._build(user_id=user_id)
|
||||||
return self._built_object
|
return self._built_object
|
||||||
|
|
||||||
def _built_object_repr(self):
|
def _built_object_repr(self):
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ from uuid import UUID
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
|
||||||
from langflow.field_typing.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES
|
from langflow.field_typing.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES
|
||||||
from langflow.interface.custom.component import Component
|
from langflow.interface.custom.component import Component
|
||||||
from langflow.interface.custom.directory_reader import DirectoryReader
|
from langflow.interface.custom.directory_reader import DirectoryReader
|
||||||
|
|
@ -189,7 +188,7 @@ class CustomComponent(Component):
|
||||||
def get_function(self):
|
def get_function(self):
|
||||||
return validate.create_function(self.code, self.function_entrypoint_name)
|
return validate.create_function(self.code, self.function_entrypoint_name)
|
||||||
|
|
||||||
def load_flow(self, flow_id: str, tweaks: Optional[dict] = None) -> Any:
|
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()
|
db_service = get_db_service()
|
||||||
|
|
@ -199,7 +198,7 @@ class CustomComponent(Component):
|
||||||
raise ValueError(f"Flow {flow_id} not found")
|
raise ValueError(f"Flow {flow_id} not found")
|
||||||
if tweaks:
|
if tweaks:
|
||||||
graph_data = process_tweaks(graph_data=graph_data, tweaks=tweaks)
|
graph_data = process_tweaks(graph_data=graph_data, tweaks=tweaks)
|
||||||
return build_sorted_vertices(graph_data, self.user_id)
|
return await build_sorted_vertices(graph_data, self.user_id)
|
||||||
|
|
||||||
def list_flows(self, *, get_session: Optional[Callable] = None) -> List[Flow]:
|
def list_flows(self, *, get_session: Optional[Callable] = None) -> List[Flow]:
|
||||||
if not self.user_id:
|
if not self.user_id:
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import inspect
|
||||||
import json
|
import json
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Sequence, Type
|
from typing import TYPE_CHECKING, Any, Callable, Dict, Sequence, Type
|
||||||
|
|
||||||
|
|
@ -10,9 +11,6 @@ from langchain.chains.base import Chain
|
||||||
from langchain.document_loaders.base import BaseLoader
|
from langchain.document_loaders.base import BaseLoader
|
||||||
from langchain.schema import Document
|
from langchain.schema import Document
|
||||||
from langchain.vectorstores.base import VectorStore
|
from langchain.vectorstores.base import VectorStore
|
||||||
from loguru import logger
|
|
||||||
from pydantic import ValidationError
|
|
||||||
|
|
||||||
from langflow.interface.custom_lists import CUSTOM_NODES
|
from langflow.interface.custom_lists import CUSTOM_NODES
|
||||||
from langflow.interface.importing.utils import eval_custom_component_code, get_function, import_by_type
|
from langflow.interface.importing.utils import eval_custom_component_code, get_function, import_by_type
|
||||||
from langflow.interface.initialize.llm import initialize_vertexai
|
from langflow.interface.initialize.llm import initialize_vertexai
|
||||||
|
|
@ -24,6 +22,8 @@ from langflow.interface.toolkits.base import toolkits_creator
|
||||||
from langflow.interface.utils import load_file_into_dict
|
from langflow.interface.utils import load_file_into_dict
|
||||||
from langflow.interface.wrappers.base import wrapper_creator
|
from langflow.interface.wrappers.base import wrapper_creator
|
||||||
from langflow.utils import validate
|
from langflow.utils import validate
|
||||||
|
from loguru import logger
|
||||||
|
from pydantic import ValidationError
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
|
|
@ -36,7 +36,7 @@ def build_vertex_in_params(params: Dict) -> Dict:
|
||||||
return {key: value.build() if isinstance(value, Vertex) else value for key, value in params.items()}
|
return {key: value.build() if isinstance(value, Vertex) else value for key, value in params.items()}
|
||||||
|
|
||||||
|
|
||||||
def instantiate_class(node_type: str, base_type: str, params: Dict, user_id=None) -> Any:
|
async def instantiate_class(node_type: str, base_type: str, params: Dict, user_id=None) -> Any:
|
||||||
"""Instantiate class from module type and key, and params"""
|
"""Instantiate class from module type and key, and params"""
|
||||||
params = convert_params_to_sets(params)
|
params = convert_params_to_sets(params)
|
||||||
params = convert_kwargs(params)
|
params = convert_kwargs(params)
|
||||||
|
|
@ -48,7 +48,7 @@ def instantiate_class(node_type: str, base_type: str, params: Dict, user_id=None
|
||||||
return custom_node(**params)
|
return custom_node(**params)
|
||||||
logger.debug(f"Instantiating {node_type} of type {base_type}")
|
logger.debug(f"Instantiating {node_type} of type {base_type}")
|
||||||
class_object = import_by_type(_type=base_type, name=node_type)
|
class_object = import_by_type(_type=base_type, name=node_type)
|
||||||
return instantiate_based_on_type(class_object, base_type, node_type, params, user_id=user_id)
|
return await instantiate_based_on_type(class_object, base_type, node_type, params, user_id=user_id)
|
||||||
|
|
||||||
|
|
||||||
def convert_params_to_sets(params):
|
def convert_params_to_sets(params):
|
||||||
|
|
@ -75,7 +75,7 @@ def convert_kwargs(params):
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
def instantiate_based_on_type(class_object, base_type, node_type, params, user_id):
|
async def instantiate_based_on_type(class_object, base_type, node_type, params, user_id):
|
||||||
if base_type == "agents":
|
if base_type == "agents":
|
||||||
return instantiate_agent(node_type, class_object, params)
|
return instantiate_agent(node_type, class_object, params)
|
||||||
elif base_type == "prompts":
|
elif base_type == "prompts":
|
||||||
|
|
@ -109,20 +109,28 @@ def instantiate_based_on_type(class_object, base_type, node_type, params, user_i
|
||||||
elif base_type == "memory":
|
elif base_type == "memory":
|
||||||
return instantiate_memory(node_type, class_object, params)
|
return instantiate_memory(node_type, class_object, params)
|
||||||
elif base_type == "custom_components":
|
elif base_type == "custom_components":
|
||||||
return instantiate_custom_component(node_type, class_object, params, user_id)
|
return await instantiate_custom_component(node_type, class_object, params, user_id)
|
||||||
elif base_type == "wrappers":
|
elif base_type == "wrappers":
|
||||||
return instantiate_wrapper(node_type, class_object, params)
|
return instantiate_wrapper(node_type, class_object, params)
|
||||||
else:
|
else:
|
||||||
return class_object(**params)
|
return class_object(**params)
|
||||||
|
|
||||||
|
|
||||||
def instantiate_custom_component(node_type, class_object, params, user_id):
|
async def instantiate_custom_component(node_type, class_object, params, user_id):
|
||||||
# we need to make a copy of the params because we will be
|
|
||||||
# modifying it
|
|
||||||
params_copy = params.copy()
|
params_copy = params.copy()
|
||||||
class_object: "CustomComponent" = eval_custom_component_code(params_copy.pop("code"))
|
class_object: "CustomComponent" = eval_custom_component_code(params_copy.pop("code"))
|
||||||
custom_component = class_object(user_id=user_id)
|
custom_component = class_object(user_id=user_id)
|
||||||
|
|
||||||
|
# Determine if the build method is asynchronous
|
||||||
|
is_async = inspect.iscoroutinefunction(custom_component.build)
|
||||||
|
|
||||||
|
if is_async:
|
||||||
|
# Await the build method directly if it's async
|
||||||
|
built_object = await custom_component.build(**params_copy)
|
||||||
|
else:
|
||||||
|
# Call the build method directly if it's sync
|
||||||
built_object = custom_component.build(**params_copy)
|
built_object = custom_component.build(**params_copy)
|
||||||
|
|
||||||
return built_object, {"repr": custom_component.custom_repr()}
|
return built_object, {"repr": custom_component.custom_repr()}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
from typing import Dict, Tuple, Optional, Union
|
from typing import Dict, Optional, Tuple, Union
|
||||||
from langflow.graph import Graph
|
|
||||||
from loguru import logger
|
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
def build_sorted_vertices(data_graph, user_id: Optional[Union[str, UUID]] = None) -> Tuple[Graph, Dict]:
|
from langflow.graph import Graph
|
||||||
|
|
||||||
|
|
||||||
|
async def build_sorted_vertices(data_graph, user_id: Optional[Union[str, UUID]] = None) -> Tuple[Graph, Dict]:
|
||||||
"""
|
"""
|
||||||
Build langchain object from data_graph.
|
Build langchain object from data_graph.
|
||||||
"""
|
"""
|
||||||
|
|
@ -14,28 +16,12 @@ def build_sorted_vertices(data_graph, user_id: Optional[Union[str, UUID]] = None
|
||||||
sorted_vertices = graph.topological_sort()
|
sorted_vertices = graph.topological_sort()
|
||||||
artifacts = {}
|
artifacts = {}
|
||||||
for vertex in sorted_vertices:
|
for vertex in sorted_vertices:
|
||||||
vertex.build(user_id=user_id)
|
await vertex.build(user_id=user_id)
|
||||||
if vertex.artifacts:
|
if vertex.artifacts:
|
||||||
artifacts.update(vertex.artifacts)
|
artifacts.update(vertex.artifacts)
|
||||||
return graph, artifacts
|
return graph, artifacts
|
||||||
|
|
||||||
|
|
||||||
def build_langchain_object(data_graph):
|
|
||||||
"""
|
|
||||||
Build langchain object from data_graph.
|
|
||||||
"""
|
|
||||||
|
|
||||||
logger.debug("Building langchain object")
|
|
||||||
nodes = data_graph["nodes"]
|
|
||||||
# Add input variables
|
|
||||||
# nodes = payload.extract_input_variables(nodes)
|
|
||||||
# Nodes, edges and root node
|
|
||||||
edges = data_graph["edges"]
|
|
||||||
graph = Graph(nodes, edges)
|
|
||||||
|
|
||||||
return graph.build()
|
|
||||||
|
|
||||||
|
|
||||||
def get_memory_key(langchain_object):
|
def get_memory_key(langchain_object):
|
||||||
"""
|
"""
|
||||||
Given a LangChain object, this function retrieves the current memory key from the object's memory attribute.
|
Given a LangChain object, this function retrieves the current memory key from the object's memory attribute.
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,15 @@
|
||||||
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from langchain.schema import AgentAction
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||||
from langflow.interface.run import (
|
|
||||||
build_sorted_vertices,
|
from langchain.chains.base import Chain
|
||||||
get_memory_key,
|
from langchain.schema import AgentAction, Document
|
||||||
update_memory_keys,
|
from langchain.vectorstores.base import VectorStore
|
||||||
)
|
from langflow.graph import Graph
|
||||||
|
from langflow.interface.run import build_sorted_vertices, get_memory_key, update_memory_keys
|
||||||
from langflow.services.deps import get_session_service
|
from langflow.services.deps import get_session_service
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from langflow.graph import Graph
|
|
||||||
from langchain.chains.base import Chain
|
|
||||||
from langchain.vectorstores.base import VectorStore
|
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
||||||
from langchain.schema import Document
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -164,8 +160,8 @@ async def process_graph_cached(
|
||||||
if session_id is None:
|
if session_id is None:
|
||||||
session_id = session_service.generate_key(session_id=session_id, data_graph=data_graph)
|
session_id = session_service.generate_key(session_id=session_id, data_graph=data_graph)
|
||||||
# Load the graph using SessionService
|
# Load the graph using SessionService
|
||||||
graph, artifacts = session_service.load_session(session_id, data_graph)
|
graph, artifacts = await session_service.load_session(session_id, data_graph)
|
||||||
built_object = graph.build()
|
built_object = await graph.build()
|
||||||
processed_inputs = process_inputs(inputs, artifacts)
|
processed_inputs = process_inputs(inputs, artifacts)
|
||||||
result = generate_result(built_object, processed_inputs)
|
result = generate_result(built_object, processed_inputs)
|
||||||
# langchain_object is now updated with the new memory
|
# langchain_object is now updated with the new memory
|
||||||
|
|
@ -202,7 +198,7 @@ def load_flow_from_json(flow: Union[Path, str, dict], tweaks: Optional[dict] = N
|
||||||
graph = Graph(nodes, edges)
|
graph = Graph(nodes, edges)
|
||||||
|
|
||||||
if build:
|
if build:
|
||||||
langchain_object = graph.build()
|
langchain_object = asyncio.run(graph.build())
|
||||||
|
|
||||||
if hasattr(langchain_object, "verbose"):
|
if hasattr(langchain_object, "verbose"):
|
||||||
langchain_object.verbose = True
|
langchain_object.verbose = True
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from langflow.interface.run import build_sorted_vertices
|
from langflow.interface.run import build_sorted_vertices
|
||||||
from langflow.services.base import Service
|
from langflow.services.base import Service
|
||||||
from langflow.services.cache.utils import compute_dict_hash
|
from langflow.services.cache.utils import compute_dict_hash
|
||||||
|
|
@ -14,7 +15,7 @@ class SessionService(Service):
|
||||||
def __init__(self, cache_service):
|
def __init__(self, cache_service):
|
||||||
self.cache_service: "BaseCacheService" = cache_service
|
self.cache_service: "BaseCacheService" = cache_service
|
||||||
|
|
||||||
def load_session(self, key, data_graph):
|
async def load_session(self, key, data_graph):
|
||||||
# Check if the data is cached
|
# Check if the data is cached
|
||||||
if key in self.cache_service:
|
if key in self.cache_service:
|
||||||
return self.cache_service.get(key)
|
return self.cache_service.get(key)
|
||||||
|
|
@ -23,7 +24,7 @@ class SessionService(Service):
|
||||||
key = self.generate_key(session_id=None, data_graph=data_graph)
|
key = self.generate_key(session_id=None, data_graph=data_graph)
|
||||||
|
|
||||||
# If not cached, build the graph and cache it
|
# If not cached, build the graph and cache it
|
||||||
graph, artifacts = build_sorted_vertices(data_graph)
|
graph, artifacts = await build_sorted_vertices(data_graph)
|
||||||
|
|
||||||
self.cache_service.set(key, (graph, artifacts))
|
self.cache_service.set(key, (graph, artifacts))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,9 @@
|
||||||
from typing import TYPE_CHECKING, Any, Dict, Optional
|
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||||
|
|
||||||
|
from asgiref.sync import async_to_sync
|
||||||
from celery.exceptions import SoftTimeLimitExceeded # type: ignore
|
from celery.exceptions import SoftTimeLimitExceeded # type: ignore
|
||||||
|
|
||||||
from langflow.core.celery_app import celery_app
|
from langflow.core.celery_app import celery_app
|
||||||
from langflow.processing.process import (
|
from langflow.processing.process import Result, generate_result, process_inputs
|
||||||
Result,
|
|
||||||
generate_result,
|
|
||||||
process_inputs,
|
|
||||||
)
|
|
||||||
from langflow.services.deps import get_session_service
|
from langflow.services.deps import get_session_service
|
||||||
from langflow.services.manager import initialize_session_service
|
from langflow.services.manager import initialize_session_service
|
||||||
|
|
||||||
|
|
@ -27,7 +23,7 @@ def build_vertex(self, vertex: "Vertex") -> "Vertex":
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
vertex.task_id = self.request.id
|
vertex.task_id = self.request.id
|
||||||
vertex.build()
|
async_to_sync(vertex.build)()
|
||||||
return vertex
|
return vertex
|
||||||
except SoftTimeLimitExceeded as e:
|
except SoftTimeLimitExceeded as e:
|
||||||
raise self.retry(exc=SoftTimeLimitExceeded("Task took too long"), countdown=2) from e
|
raise self.retry(exc=SoftTimeLimitExceeded("Task took too long"), countdown=2) from e
|
||||||
|
|
@ -47,7 +43,7 @@ def process_graph_cached_task(
|
||||||
if session_id is None:
|
if session_id is None:
|
||||||
session_id = session_service.generate_key(session_id=session_id, data_graph=data_graph)
|
session_id = session_service.generate_key(session_id=session_id, data_graph=data_graph)
|
||||||
# Load the graph using SessionService
|
# Load the graph using SessionService
|
||||||
graph, artifacts = session_service.load_session(session_id, data_graph)
|
graph, artifacts = async_to_sync(session_service.load_session)(session_id, data_graph)
|
||||||
built_object = graph.build()
|
built_object = graph.build()
|
||||||
processed_inputs = process_inputs(inputs, artifacts)
|
processed_inputs = process_inputs(inputs, artifacts)
|
||||||
result = generate_result(built_object, processed_inputs)
|
result = generate_result(built_object, processed_inputs)
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,30 @@
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
import pickle
|
import pickle
|
||||||
|
from pathlib import Path
|
||||||
from typing import Type, Union
|
from typing import Type, Union
|
||||||
from langflow.graph.edge.base import Edge
|
|
||||||
from langflow.graph.vertex.base import Vertex
|
|
||||||
from langchain.agents import AgentExecutor
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from langchain.agents import AgentExecutor
|
||||||
from langchain.chains.base import Chain
|
from langchain.chains.base import Chain
|
||||||
from langchain.llms.fake import FakeListLLM
|
from langchain.llms.fake import FakeListLLM
|
||||||
from langflow.graph import Graph
|
from langflow.graph import Graph
|
||||||
from langflow.graph.vertex.types import (
|
from langflow.graph.edge.base import Edge
|
||||||
FileToolVertex,
|
|
||||||
LLMVertex,
|
|
||||||
ToolkitVertex,
|
|
||||||
)
|
|
||||||
from langflow.processing.process import get_result_and_thought
|
|
||||||
from langflow.utils.payload import get_root_node
|
|
||||||
from langflow.graph.graph.utils import (
|
from langflow.graph.graph.utils import (
|
||||||
find_last_node,
|
find_last_node,
|
||||||
|
process_flow,
|
||||||
set_new_target_handle,
|
set_new_target_handle,
|
||||||
ungroup_node,
|
ungroup_node,
|
||||||
process_flow,
|
|
||||||
update_source_handle,
|
update_source_handle,
|
||||||
update_target_handle,
|
update_target_handle,
|
||||||
update_template,
|
update_template,
|
||||||
)
|
)
|
||||||
|
from langflow.graph.utils import UnbuiltObject
|
||||||
|
from langflow.graph.vertex.base import Vertex
|
||||||
|
from langflow.graph.vertex.types import FileToolVertex, LLMVertex, ToolkitVertex
|
||||||
|
from langflow.processing.process import get_result_and_thought
|
||||||
|
from langflow.utils.payload import get_root_node
|
||||||
|
|
||||||
# Test cases for the graph module
|
# Test cases for the graph module
|
||||||
|
|
||||||
|
|
@ -232,29 +230,32 @@ def test_build_params(basic_graph):
|
||||||
assert "memory" in root.params
|
assert "memory" in root.params
|
||||||
|
|
||||||
|
|
||||||
def test_build(basic_graph):
|
@pytest.mark.asyncio
|
||||||
|
async def test_build(basic_graph):
|
||||||
"""Test Node's build method"""
|
"""Test Node's build method"""
|
||||||
assert_agent_was_built(basic_graph)
|
await assert_agent_was_built(basic_graph)
|
||||||
|
|
||||||
|
|
||||||
def assert_agent_was_built(graph):
|
async def assert_agent_was_built(graph):
|
||||||
"""Assert that the agent was built"""
|
"""Assert that the agent was built"""
|
||||||
assert isinstance(graph, Graph)
|
assert isinstance(graph, Graph)
|
||||||
# Now we test the build method
|
# Now we test the build method
|
||||||
# Build the Agent
|
# Build the Agent
|
||||||
result = graph.build()
|
result = await graph.build()
|
||||||
# The agent should be a AgentExecutor
|
# The agent should be a AgentExecutor
|
||||||
assert isinstance(result, Chain)
|
assert isinstance(result, Chain)
|
||||||
|
|
||||||
|
|
||||||
def test_llm_node_build(basic_graph):
|
@pytest.mark.asyncio
|
||||||
|
async def test_llm_node_build(basic_graph):
|
||||||
llm_node = get_node_by_type(basic_graph, LLMVertex)
|
llm_node = get_node_by_type(basic_graph, LLMVertex)
|
||||||
assert llm_node is not None
|
assert llm_node is not None
|
||||||
built_object = llm_node.build()
|
built_object = await llm_node.build()
|
||||||
assert built_object is not None
|
assert built_object is not UnbuiltObject()
|
||||||
|
|
||||||
|
|
||||||
def test_toolkit_node_build(client, openapi_graph):
|
@pytest.mark.asyncio
|
||||||
|
async def test_toolkit_node_build(client, openapi_graph):
|
||||||
# Write a file to the disk
|
# Write a file to the disk
|
||||||
file_path = "api-with-examples.yaml"
|
file_path = "api-with-examples.yaml"
|
||||||
with open(file_path, "w") as f:
|
with open(file_path, "w") as f:
|
||||||
|
|
@ -262,36 +263,31 @@ def test_toolkit_node_build(client, openapi_graph):
|
||||||
|
|
||||||
toolkit_node = get_node_by_type(openapi_graph, ToolkitVertex)
|
toolkit_node = get_node_by_type(openapi_graph, ToolkitVertex)
|
||||||
assert toolkit_node is not None
|
assert toolkit_node is not None
|
||||||
built_object = toolkit_node.build()
|
built_object = await toolkit_node.build()
|
||||||
assert built_object is not None
|
assert built_object is not UnbuiltObject
|
||||||
# Remove the file
|
# Remove the file
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
assert not Path(file_path).exists()
|
assert not Path(file_path).exists()
|
||||||
|
|
||||||
|
|
||||||
def test_file_tool_node_build(client, openapi_graph):
|
@pytest.mark.asyncio
|
||||||
|
async def test_file_tool_node_build(client, openapi_graph):
|
||||||
file_path = "api-with-examples.yaml"
|
file_path = "api-with-examples.yaml"
|
||||||
with open(file_path, "w") as f:
|
with open(file_path, "w") as f:
|
||||||
f.write("openapi: 3.0.0")
|
f.write("openapi: 3.0.0")
|
||||||
|
|
||||||
assert Path(file_path).exists()
|
assert Path(file_path).exists()
|
||||||
file_tool_node = get_node_by_type(openapi_graph, FileToolVertex)
|
file_tool_node = get_node_by_type(openapi_graph, FileToolVertex)
|
||||||
assert file_tool_node is not None
|
assert file_tool_node is not UnbuiltObject
|
||||||
built_object = file_tool_node.build()
|
built_object = await file_tool_node.build()
|
||||||
assert built_object is not None
|
assert built_object is not UnbuiltObject
|
||||||
# Remove the file
|
# Remove the file
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
assert not Path(file_path).exists()
|
assert not Path(file_path).exists()
|
||||||
|
|
||||||
|
|
||||||
# def test_wrapper_node_build(openapi_graph):
|
@pytest.mark.asyncio
|
||||||
# wrapper_node = get_node_by_type(openapi_graph, WrapperVertex)
|
async def test_get_result_and_thought(basic_graph):
|
||||||
# assert wrapper_node is not None
|
|
||||||
# built_object = wrapper_node.build()
|
|
||||||
# assert built_object is not None
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_result_and_thought(basic_graph):
|
|
||||||
"""Test the get_result_and_thought method"""
|
"""Test the get_result_and_thought method"""
|
||||||
responses = [
|
responses = [
|
||||||
"Final Answer: I am a response",
|
"Final Answer: I am a response",
|
||||||
|
|
@ -303,7 +299,7 @@ def test_get_result_and_thought(basic_graph):
|
||||||
assert llm_node is not None
|
assert llm_node is not None
|
||||||
llm_node._built_object = FakeListLLM(responses=responses)
|
llm_node._built_object = FakeListLLM(responses=responses)
|
||||||
llm_node._built = True
|
llm_node._built = True
|
||||||
langchain_object = basic_graph.build()
|
langchain_object = await basic_graph.build()
|
||||||
# assert all nodes are built
|
# assert all nodes are built
|
||||||
assert all(node._built for node in basic_graph.nodes)
|
assert all(node._built for node in basic_graph.nodes)
|
||||||
# now build again and check if FakeListLLM was used
|
# now build again and check if FakeListLLM was used
|
||||||
|
|
@ -486,27 +482,29 @@ def test_update_source_handle():
|
||||||
assert updated_edge["data"]["sourceHandle"]["id"] == "last_node"
|
assert updated_edge["data"]["sourceHandle"]["id"] == "last_node"
|
||||||
|
|
||||||
|
|
||||||
def test_pickle_graph(json_vector_store):
|
@pytest.mark.asyncio
|
||||||
|
async def test_pickle_graph(json_vector_store):
|
||||||
loaded_json = json.loads(json_vector_store)
|
loaded_json = json.loads(json_vector_store)
|
||||||
graph = Graph.from_payload(loaded_json)
|
graph = Graph.from_payload(loaded_json)
|
||||||
assert isinstance(graph, Graph)
|
assert isinstance(graph, Graph)
|
||||||
first_result = graph.build()
|
first_result = await graph.build()
|
||||||
assert isinstance(first_result, AgentExecutor)
|
assert isinstance(first_result, AgentExecutor)
|
||||||
pickled = pickle.dumps(graph)
|
pickled = pickle.dumps(graph)
|
||||||
assert pickled is not None
|
assert pickled is not UnbuiltObject
|
||||||
unpickled = pickle.loads(pickled)
|
unpickled = pickle.loads(pickled)
|
||||||
assert unpickled is not None
|
assert unpickled is not UnbuiltObject
|
||||||
result = unpickled.build()
|
result = await unpickled.build()
|
||||||
assert isinstance(result, AgentExecutor)
|
assert isinstance(result, AgentExecutor)
|
||||||
|
|
||||||
|
|
||||||
def test_pickle_each_vertex(json_vector_store):
|
@pytest.mark.asyncio
|
||||||
|
async def test_pickle_each_vertex(json_vector_store):
|
||||||
loaded_json = json.loads(json_vector_store)
|
loaded_json = json.loads(json_vector_store)
|
||||||
graph = Graph.from_payload(loaded_json)
|
graph = Graph.from_payload(loaded_json)
|
||||||
assert isinstance(graph, Graph)
|
assert isinstance(graph, Graph)
|
||||||
for vertex in graph.nodes:
|
for vertex in graph.nodes:
|
||||||
vertex.build()
|
await vertex.build()
|
||||||
pickled = pickle.dumps(vertex)
|
pickled = pickle.dumps(vertex)
|
||||||
assert pickled is not None
|
assert pickled is not UnbuiltObject
|
||||||
unpickled = pickle.loads(pickled)
|
unpickled = pickle.loads(pickled)
|
||||||
assert unpickled is not None
|
assert unpickled is not UnbuiltObject
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import pytest
|
||||||
from langflow.processing.process import process_tweaks
|
from langflow.processing.process import process_tweaks
|
||||||
from langflow.services.deps import get_session_service
|
from langflow.services.deps import get_session_service
|
||||||
|
|
||||||
|
|
@ -197,39 +198,42 @@ def test_tweak_not_in_template():
|
||||||
assert result == graph_data
|
assert result == graph_data
|
||||||
|
|
||||||
|
|
||||||
def test_load_langchain_object_with_cached_session(client, basic_graph_data):
|
@pytest.mark.asyncio
|
||||||
|
async def test_load_langchain_object_with_cached_session(client, basic_graph_data):
|
||||||
# Provide a non-existent session_id
|
# Provide a non-existent session_id
|
||||||
session_service = get_session_service()
|
session_service = get_session_service()
|
||||||
session_id1 = "non-existent-session-id"
|
session_id1 = "non-existent-session-id"
|
||||||
graph1, artifacts1 = session_service.load_session(session_id1, basic_graph_data)
|
graph1, artifacts1 = await session_service.load_session(session_id1, basic_graph_data)
|
||||||
# Use the new session_id to get the langchain_object again
|
# Use the new session_id to get the langchain_object again
|
||||||
graph2, artifacts2 = session_service.load_session(session_id1, basic_graph_data)
|
graph2, artifacts2 = await session_service.load_session(session_id1, basic_graph_data)
|
||||||
|
|
||||||
assert graph1 == graph2
|
assert graph1 == graph2
|
||||||
assert artifacts1 == artifacts2
|
assert artifacts1 == artifacts2
|
||||||
|
|
||||||
|
|
||||||
def test_load_langchain_object_with_no_cached_session(client, basic_graph_data):
|
@pytest.mark.asyncio
|
||||||
|
async def test_load_langchain_object_with_no_cached_session(client, basic_graph_data):
|
||||||
# Provide a non-existent session_id
|
# Provide a non-existent session_id
|
||||||
session_service = get_session_service()
|
session_service = get_session_service()
|
||||||
session_id1 = "non-existent-session-id"
|
session_id1 = "non-existent-session-id"
|
||||||
session_id = session_service.build_key(session_id1, basic_graph_data)
|
session_id = session_service.build_key(session_id1, basic_graph_data)
|
||||||
graph1, artifacts1 = session_service.load_session(session_id, basic_graph_data)
|
graph1, artifacts1 = await session_service.load_session(session_id, basic_graph_data)
|
||||||
# Clear the cache
|
# Clear the cache
|
||||||
session_service.clear_session(session_id)
|
session_service.clear_session(session_id)
|
||||||
# Use the new session_id to get the langchain_object again
|
# Use the new session_id to get the langchain_object again
|
||||||
graph2, artifacts2 = session_service.load_session(session_id, basic_graph_data)
|
graph2, artifacts2 = await session_service.load_session(session_id, basic_graph_data)
|
||||||
|
|
||||||
assert id(graph1) != id(graph2)
|
assert id(graph1) != id(graph2)
|
||||||
# Since the cache was cleared, objects should be different
|
# Since the cache was cleared, objects should be different
|
||||||
|
|
||||||
|
|
||||||
def test_load_langchain_object_without_session_id(client, basic_graph_data):
|
@pytest.mark.asyncio
|
||||||
|
async def test_load_langchain_object_without_session_id(client, basic_graph_data):
|
||||||
# Provide a non-existent session_id
|
# Provide a non-existent session_id
|
||||||
session_service = get_session_service()
|
session_service = get_session_service()
|
||||||
session_id1 = None
|
session_id1 = None
|
||||||
graph1, artifacts1 = session_service.load_session(session_id1, basic_graph_data)
|
graph1, artifacts1 = await session_service.load_session(session_id1, basic_graph_data)
|
||||||
# Use the new session_id to get the langchain_object again
|
# Use the new session_id to get the langchain_object again
|
||||||
graph2, artifacts2 = session_service.load_session(session_id1, basic_graph_data)
|
graph2, artifacts2 = await session_service.load_session(session_id1, basic_graph_data)
|
||||||
|
|
||||||
assert graph1 == graph2
|
assert graph1 == graph2
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue