diff --git a/pyproject.toml b/pyproject.toml index 094772c7eaa..14b342f44d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,7 +98,7 @@ dependencies = [ "yfinance>=0.2.40", "langchain-google-community~=2.0.1", "wolframalpha>=5.1.3", - "astra-assistants~=2.2.2", + "astra-assistants[tools]~=2.2.5", "composio-langchain==0.5.9", "spider-client>=0.0.27", "nltk>=3.9.1", diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 64e9be0c882..eff041f1b74 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -40,6 +40,7 @@ from langflow.graph.graph.base import Graph from langflow.graph.utils import log_vertex_build from langflow.schema.schema import OutputValue +from langflow.services.cache.utils import CacheMiss from langflow.services.chat.service import ChatService from langflow.services.deps import get_chat_service, get_session, get_telemetry_service from langflow.services.telemetry.schema import ComponentPayload, PlaygroundPayload @@ -493,7 +494,7 @@ async def build_vertex( error_message = None try: cache = await chat_service.get_cache(flow_id_str) - if not cache: + if isinstance(cache, CacheMiss): # If there's no cache logger.warning(f"No cache found for {flow_id_str}. Building graph starting at {vertex_id}") graph: Graph = await build_graph_from_db( @@ -621,7 +622,7 @@ async def _stream_vertex(flow_id: str, vertex_id: str, chat_service: ChatService yield str(StreamData(event="error", data={"error": str(exc)})) return - if not cache: + if isinstance(cache, CacheMiss): # If there's no cache msg = f"No cache found for {flow_id}." logger.error(msg) diff --git a/src/backend/base/langflow/base/astra_assistants/util.py b/src/backend/base/langflow/base/astra_assistants/util.py index c2ea1262e6c..fd36a7e037e 100644 --- a/src/backend/base/langflow/base/astra_assistants/util.py +++ b/src/backend/base/langflow/base/astra_assistants/util.py @@ -10,6 +10,8 @@ from astra_assistants import OpenAIWithDefaultKey, patch from astra_assistants.tools.tool_interface import ToolInterface +from langflow.services.cache.utils import CacheMiss + client_lock = threading.Lock() client = None @@ -17,7 +19,7 @@ def get_patched_openai_client(shared_component_cache): os.environ["ASTRA_ASSISTANTS_QUIET"] = "true" client = shared_component_cache.get("client") - if client is None: + if isinstance(client, CacheMiss): client = patch(OpenAIWithDefaultKey()) shared_component_cache.set("client", client) return client diff --git a/src/backend/base/langflow/components/models/huggingface.py b/src/backend/base/langflow/components/models/huggingface.py index 5c3ae585e77..e65c5ed454d 100644 --- a/src/backend/base/langflow/components/models/huggingface.py +++ b/src/backend/base/langflow/components/models/huggingface.py @@ -8,7 +8,7 @@ from langflow.base.models.model import LCModelComponent from langflow.field_typing import LanguageModel from langflow.inputs.inputs import HandleInput -from langflow.io import DictInput, DropdownInput, IntInput, SecretStrInput, StrInput +from langflow.io import DictInput, DropdownInput, FloatInput, IntInput, SecretStrInput, StrInput class HuggingFaceEndpointsComponent(LCModelComponent): @@ -20,6 +20,45 @@ class HuggingFaceEndpointsComponent(LCModelComponent): inputs = [ *LCModelComponent._base_inputs, StrInput(name="model_id", display_name="Model ID", value="openai-community/gpt2"), + IntInput( + name="max_new_tokens", display_name="Max New Tokens", value=512, info="Maximum number of generated tokens" + ), + IntInput( + name="top_k", + display_name="Top K", + advanced=True, + info="The number of highest probability vocabulary tokens to keep for top-k-filtering", + ), + FloatInput( + name="top_p", + display_name="Top P", + value=0.95, + advanced=True, + info=( + "If set to < 1, only the smallest set of most probable tokens with " + "probabilities that add up to `top_p` or higher are kept for generation" + ), + ), + FloatInput( + name="typical_p", + display_name="Typical P", + value=0.95, + advanced=True, + info="Typical Decoding mass.", + ), + FloatInput( + name="temperature", + display_name="Temperature", + value=0.8, + advanced=True, + info="The value used to module the logits distribution", + ), + FloatInput( + name="repetition_penalty", + display_name="Repetition Penalty", + info="The parameter for repetition penalty. 1.0 means no penalty.", + advanced=True, + ), StrInput( name="inference_endpoint", display_name="Inference Endpoint", diff --git a/src/backend/base/langflow/components/prototypes/sub_flow.py b/src/backend/base/langflow/components/prototypes/sub_flow.py index a62a0bea148..7c30647afd9 100644 --- a/src/backend/base/langflow/components/prototypes/sub_flow.py +++ b/src/backend/base/langflow/components/prototypes/sub_flow.py @@ -90,7 +90,7 @@ def add_inputs_to_build_config(self, inputs_vertex: list[Vertex], build_config: async def generate_results(self) -> list[Data]: tweaks: dict = {} for field in self._attributes: - if field != "flow_name": + if field != "flow_name" and "|" in field: [node, name] = field.split("|") if node not in tweaks: tweaks[node] = {} diff --git a/src/backend/base/langflow/components/vectorstores/astradb.py b/src/backend/base/langflow/components/vectorstores/astradb.py index 34687a838b2..eac794ba4b8 100644 --- a/src/backend/base/langflow/components/vectorstores/astradb.py +++ b/src/backend/base/langflow/components/vectorstores/astradb.py @@ -1,5 +1,6 @@ import os +import orjson from astrapy.admin import parse_api_endpoint from loguru import logger @@ -116,6 +117,7 @@ class AstraVectorStoreComponent(LCVectorStoreComponent): display_name="Metric", info="Optional distance metric for vector comparisons in the vector store.", options=["cosine", "dot_product", "euclidean"], + value="cosine", advanced=True, ), IntInput( @@ -145,8 +147,8 @@ class AstraVectorStoreComponent(LCVectorStoreComponent): DropdownInput( name="setup_mode", display_name="Setup Mode", - info="Configuration mode for setting up the vector store, with options like 'Sync', 'Async', or 'Off'.", - options=["Sync", "Async", "Off"], + info="Configuration mode for setting up the vector store, with options like 'Sync' or 'Off'.", + options=["Sync", "Off"], advanced=True, value="Sync", ), @@ -160,18 +162,21 @@ class AstraVectorStoreComponent(LCVectorStoreComponent): name="metadata_indexing_include", display_name="Metadata Indexing Include", info="Optional list of metadata fields to include in the indexing.", + is_list=True, advanced=True, ), StrInput( name="metadata_indexing_exclude", display_name="Metadata Indexing Exclude", info="Optional list of metadata fields to exclude from the indexing.", + is_list=True, advanced=True, ), StrInput( name="collection_indexing_policy", display_name="Collection Indexing Policy", - info="Optional dictionary defining the indexing policy for the collection.", + info='Optional JSON string for the "indexing" field of the collection. ' + "See https://docs.datastax.com/en/astra-db-serverless/api-reference/collections.html#the-indexing-option", advanced=True, ), IntInput( @@ -401,31 +406,27 @@ def build_vector_store(self, vectorize_options=None): ), } - vector_store_kwargs = { - **embedding_dict, - "collection_name": self.collection_name, - "token": self.token, - "api_endpoint": self.api_endpoint, - "namespace": self.namespace or None, - "environment": parse_api_endpoint(self.api_endpoint).environment, - "metric": self.metric or None, - "batch_size": self.batch_size or None, - "bulk_insert_batch_concurrency": self.bulk_insert_batch_concurrency or None, - "bulk_insert_overwrite_concurrency": self.bulk_insert_overwrite_concurrency or None, - "bulk_delete_concurrency": self.bulk_delete_concurrency or None, - "setup_mode": setup_mode_value, - "pre_delete_collection": self.pre_delete_collection or False, - } - - if self.metadata_indexing_include: - vector_store_kwargs["metadata_indexing_include"] = self.metadata_indexing_include - elif self.metadata_indexing_exclude: - vector_store_kwargs["metadata_indexing_exclude"] = self.metadata_indexing_exclude - elif self.collection_indexing_policy: - vector_store_kwargs["collection_indexing_policy"] = self.collection_indexing_policy - try: - vector_store = AstraDBVectorStore(**vector_store_kwargs) + vector_store = AstraDBVectorStore( + collection_name=self.collection_name, + token=self.token, + api_endpoint=self.api_endpoint, + namespace=self.namespace or None, + environment=parse_api_endpoint(self.api_endpoint).environment, + metric=self.metric, + batch_size=self.batch_size or None, + bulk_insert_batch_concurrency=self.bulk_insert_batch_concurrency or None, + bulk_insert_overwrite_concurrency=self.bulk_insert_overwrite_concurrency or None, + bulk_delete_concurrency=self.bulk_delete_concurrency or None, + setup_mode=setup_mode_value, + pre_delete_collection=self.pre_delete_collection, + metadata_indexing_include=[s for s in self.metadata_indexing_include if s], + metadata_indexing_exclude=[s for s in self.metadata_indexing_exclude if s], + collection_indexing_policy=orjson.dumps(self.collection_indexing_policy) + if self.collection_indexing_policy + else None, + **embedding_dict, + ) except Exception as e: msg = f"Error initializing AstraDBVectorStore: {e}" raise ValueError(msg) from e diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index b2343c6c456..cc4d64e9d19 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -1,6 +1,7 @@ from __future__ import annotations import ast +import asyncio import inspect from copy import deepcopy from textwrap import dedent @@ -506,11 +507,10 @@ def __call__(self, **kwargs): async def _run(self): # Resolve callable inputs for key, _input in self._inputs.items(): - if callable(_input.value): - result = _input.value() - if inspect.iscoroutine(result): - result = await result - self._inputs[key].value = result + if asyncio.iscoroutinefunction(_input.value): + self._inputs[key].value = await _input.value() + elif callable(_input.value): + self._inputs[key].value = await asyncio.to_thread(_input.value) self.set_attributes({}) @@ -718,10 +718,11 @@ async def _build_results(self): _results[output.name] = output.value result = output.value else: - result = method() # If the method is asynchronous, we need to await it if inspect.iscoroutinefunction(method): - result = await result + result = await method() + else: + result = await asyncio.to_thread(method) if ( self._vertex is not None and isinstance(result, Message) diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index d1ea4c5bc23..d1a0400ab82 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -707,58 +707,6 @@ async def _run( return vertex_outputs - def run( - self, - inputs: list[dict[str, str]], - *, - input_components: list[list[str]] | None = None, - types: list[InputType | None] | None = None, - outputs: list[str] | None = None, - session_id: str | None = None, - stream: bool = False, - fallback_to_env_vars: bool = False, - ) -> list[RunOutputs]: - """Run the graph with the given inputs and return the outputs. - - Args: - inputs (Dict[str, str]): A dictionary of input values. - input_components (Optional[list[str]]): A list of input components. - types (Optional[list[str]]): A list of types. - outputs (Optional[list[str]]): A list of output components. - session_id (Optional[str]): The session ID. - stream (bool): Whether to stream the outputs. - fallback_to_env_vars (bool): Whether to fallback to environment variables. - - Returns: - List[RunOutputs]: A list of RunOutputs objects representing the outputs. - """ - # run the async function in a sync way - # this could be used in a FastAPI endpoint - # so we should take care of the event loop - coro = self.arun( - inputs=inputs, - inputs_components=input_components, - types=types, - outputs=outputs, - session_id=session_id, - stream=stream, - fallback_to_env_vars=fallback_to_env_vars, - ) - - try: - # Attempt to get the running event loop; if none, an exception is raised - loop = asyncio.get_running_loop() - except RuntimeError: - # If there's no running event loop, use asyncio.run - return asyncio.run(coro) - - # If the event loop is closed, use asyncio.run - if loop.is_closed(): - return asyncio.run(coro) - - # If there's an existing, open event loop, use it to run the async function - return loop.run_until_complete(coro) - async def arun( self, inputs: list[dict[str, str]], @@ -1356,7 +1304,7 @@ async def build_vertex( if get_cache is not None: cached_result = await get_cache(key=vertex.id) else: - cached_result = None + cached_result = CacheMiss() if isinstance(cached_result, CacheMiss): should_build = True else: diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index 375e54c6d6d..38a56f150c2 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -814,10 +814,7 @@ async def build( # Run steps for step in self.steps: if step not in self.steps_ran: - if inspect.iscoroutinefunction(step): - await step(user_id=user_id, event_manager=event_manager, **kwargs) - else: - step(user_id=user_id, event_manager=event_manager, **kwargs) + await step(user_id=user_id, event_manager=event_manager, **kwargs) self.steps_ran.append(step) self.finalize_build() diff --git a/src/backend/base/langflow/initial_setup/setup.py b/src/backend/base/langflow/initial_setup/setup.py index 14deb499f25..5afaf44b12e 100644 --- a/src/backend/base/langflow/initial_setup/setup.py +++ b/src/backend/base/langflow/initial_setup/setup.py @@ -3,7 +3,6 @@ import shutil import time from collections import defaultdict -from collections.abc import Awaitable from copy import deepcopy from datetime import datetime, timezone from pathlib import Path @@ -600,12 +599,7 @@ def find_existing_flow(session, flow_id, flow_endpoint_name): return None -async def create_or_update_starter_projects(get_all_components_coro: Awaitable[dict]) -> None: - try: - all_types_dict = await get_all_components_coro - except Exception: - logger.exception("Error loading components") - raise +def create_or_update_starter_projects(all_types_dict: dict) -> None: with session_scope() as session: new_folder = create_starter_folder(session) starter_projects = load_starter_projects() diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json index 4d7e25e89a7..2db19d965c6 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json @@ -683,13 +683,13 @@ "show": true, "title_case": false, "type": "code", - "value": "import os\n\nfrom astrapy.admin import parse_api_endpoint\nfrom loguru import logger\n\nfrom langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store\nfrom langflow.helpers import docs_to_data\nfrom langflow.inputs import DictInput, FloatInput, MessageTextInput\nfrom langflow.io import (\n BoolInput,\n DataInput,\n DropdownInput,\n HandleInput,\n IntInput,\n MultilineInput,\n SecretStrInput,\n StrInput,\n)\nfrom langflow.schema import Data\n\n\nclass AstraVectorStoreComponent(LCVectorStoreComponent):\n display_name: str = \"Astra DB\"\n description: str = \"Implementation of Vector Store using Astra DB with search capabilities\"\n documentation: str = \"https://docs.langflow.org/starter-projects-vector-store-rag\"\n name = \"AstraDB\"\n icon: str = \"AstraDB\"\n\n VECTORIZE_PROVIDERS_MAPPING = {\n \"Azure OpenAI\": [\"azureOpenAI\", [\"text-embedding-3-small\", \"text-embedding-3-large\", \"text-embedding-ada-002\"]],\n \"Hugging Face - Dedicated\": [\"huggingfaceDedicated\", [\"endpoint-defined-model\"]],\n \"Hugging Face - Serverless\": [\n \"huggingface\",\n [\n \"sentence-transformers/all-MiniLM-L6-v2\",\n \"intfloat/multilingual-e5-large\",\n \"intfloat/multilingual-e5-large-instruct\",\n \"BAAI/bge-small-en-v1.5\",\n \"BAAI/bge-base-en-v1.5\",\n \"BAAI/bge-large-en-v1.5\",\n ],\n ],\n \"Jina AI\": [\n \"jinaAI\",\n [\n \"jina-embeddings-v2-base-en\",\n \"jina-embeddings-v2-base-de\",\n \"jina-embeddings-v2-base-es\",\n \"jina-embeddings-v2-base-code\",\n \"jina-embeddings-v2-base-zh\",\n ],\n ],\n \"Mistral AI\": [\"mistral\", [\"mistral-embed\"]],\n \"NVIDIA\": [\"nvidia\", [\"NV-Embed-QA\"]],\n \"OpenAI\": [\"openai\", [\"text-embedding-3-small\", \"text-embedding-3-large\", \"text-embedding-ada-002\"]],\n \"Upstage\": [\"upstageAI\", [\"solar-embedding-1-large\"]],\n \"Voyage AI\": [\n \"voyageAI\",\n [\"voyage-large-2-instruct\", \"voyage-law-2\", \"voyage-code-2\", \"voyage-large-2\", \"voyage-2\"],\n ],\n }\n\n inputs = [\n SecretStrInput(\n name=\"token\",\n display_name=\"Astra DB Application Token\",\n info=\"Authentication token for accessing Astra DB.\",\n value=\"ASTRA_DB_APPLICATION_TOKEN\",\n required=True,\n advanced=os.getenv(\"ASTRA_ENHANCED\", \"false\").lower() == \"true\",\n ),\n SecretStrInput(\n name=\"api_endpoint\",\n display_name=\"Database\" if os.getenv(\"ASTRA_ENHANCED\", \"false\").lower() == \"true\" else \"API Endpoint\",\n info=\"API endpoint URL for the Astra DB service.\",\n value=\"ASTRA_DB_API_ENDPOINT\",\n required=True,\n ),\n StrInput(\n name=\"collection_name\",\n display_name=\"Collection Name\",\n info=\"The name of the collection within Astra DB where the vectors will be stored.\",\n required=True,\n ),\n MultilineInput(\n name=\"search_input\",\n display_name=\"Search Input\",\n ),\n DataInput(\n name=\"ingest_data\",\n display_name=\"Ingest Data\",\n is_list=True,\n ),\n StrInput(\n name=\"namespace\",\n display_name=\"Namespace\",\n info=\"Optional namespace within Astra DB to use for the collection.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"embedding_service\",\n display_name=\"Embedding Model or Astra Vectorize\",\n info=\"Determines whether to use Astra Vectorize for the collection.\",\n options=[\"Embedding Model\", \"Astra Vectorize\"],\n real_time_refresh=True,\n value=\"Embedding Model\",\n ),\n HandleInput(\n name=\"embedding\",\n display_name=\"Embedding Model\",\n input_types=[\"Embeddings\"],\n info=\"Allows an embedding model configuration.\",\n ),\n DropdownInput(\n name=\"metric\",\n display_name=\"Metric\",\n info=\"Optional distance metric for vector comparisons in the vector store.\",\n options=[\"cosine\", \"dot_product\", \"euclidean\"],\n advanced=True,\n ),\n IntInput(\n name=\"batch_size\",\n display_name=\"Batch Size\",\n info=\"Optional number of data to process in a single batch.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_insert_batch_concurrency\",\n display_name=\"Bulk Insert Batch Concurrency\",\n info=\"Optional concurrency level for bulk insert operations.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_insert_overwrite_concurrency\",\n display_name=\"Bulk Insert Overwrite Concurrency\",\n info=\"Optional concurrency level for bulk insert operations that overwrite existing data.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_delete_concurrency\",\n display_name=\"Bulk Delete Concurrency\",\n info=\"Optional concurrency level for bulk delete operations.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"setup_mode\",\n display_name=\"Setup Mode\",\n info=\"Configuration mode for setting up the vector store, with options like 'Sync', 'Async', or 'Off'.\",\n options=[\"Sync\", \"Async\", \"Off\"],\n advanced=True,\n value=\"Sync\",\n ),\n BoolInput(\n name=\"pre_delete_collection\",\n display_name=\"Pre Delete Collection\",\n info=\"Boolean flag to determine whether to delete the collection before creating a new one.\",\n advanced=True,\n ),\n StrInput(\n name=\"metadata_indexing_include\",\n display_name=\"Metadata Indexing Include\",\n info=\"Optional list of metadata fields to include in the indexing.\",\n advanced=True,\n ),\n StrInput(\n name=\"metadata_indexing_exclude\",\n display_name=\"Metadata Indexing Exclude\",\n info=\"Optional list of metadata fields to exclude from the indexing.\",\n advanced=True,\n ),\n StrInput(\n name=\"collection_indexing_policy\",\n display_name=\"Collection Indexing Policy\",\n info=\"Optional dictionary defining the indexing policy for the collection.\",\n advanced=True,\n ),\n IntInput(\n name=\"number_of_results\",\n display_name=\"Number of Results\",\n info=\"Number of results to return.\",\n advanced=True,\n value=4,\n ),\n DropdownInput(\n name=\"search_type\",\n display_name=\"Search Type\",\n info=\"Search type to use\",\n options=[\"Similarity\", \"Similarity with score threshold\", \"MMR (Max Marginal Relevance)\"],\n value=\"Similarity\",\n advanced=True,\n ),\n FloatInput(\n name=\"search_score_threshold\",\n display_name=\"Search Score Threshold\",\n info=\"Minimum similarity score threshold for search results. \"\n \"(when using 'Similarity with score threshold')\",\n value=0,\n advanced=True,\n ),\n DictInput(\n name=\"search_filter\",\n display_name=\"Search Metadata Filter\",\n info=\"Optional dictionary of filters to apply to the search query.\",\n advanced=True,\n is_list=True,\n ),\n ]\n\n def insert_in_dict(self, build_config, field_name, new_parameters):\n # Insert the new key-value pair after the found key\n for new_field_name, new_parameter in new_parameters.items():\n # Get all the items as a list of tuples (key, value)\n items = list(build_config.items())\n\n # Find the index of the key to insert after\n idx = len(items)\n for i, (key, _value) in enumerate(items):\n if key == field_name:\n idx = i + 1\n break\n\n items.insert(idx, (new_field_name, new_parameter))\n\n # Clear the original dictionary and update with the modified items\n build_config.clear()\n build_config.update(items)\n\n return build_config\n\n def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n if field_name == \"embedding_service\":\n if field_value == \"Astra Vectorize\":\n for field in [\"embedding\"]:\n if field in build_config:\n del build_config[field]\n\n new_parameter = DropdownInput(\n name=\"provider\",\n display_name=\"Vectorize Provider\",\n options=self.VECTORIZE_PROVIDERS_MAPPING.keys(),\n value=\"\",\n required=True,\n real_time_refresh=True,\n ).to_dict()\n\n self.insert_in_dict(build_config, \"embedding_service\", {\"provider\": new_parameter})\n else:\n for field in [\n \"provider\",\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if field in build_config:\n del build_config[field]\n\n new_parameter = HandleInput(\n name=\"embedding\",\n display_name=\"Embedding Model\",\n input_types=[\"Embeddings\"],\n info=\"Allows an embedding model configuration.\",\n ).to_dict()\n\n self.insert_in_dict(build_config, \"embedding_service\", {\"embedding\": new_parameter})\n\n elif field_name == \"provider\":\n for field in [\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if field in build_config:\n del build_config[field]\n\n model_options = self.VECTORIZE_PROVIDERS_MAPPING[field_value][1]\n\n new_parameter_0 = DropdownInput(\n name=\"z_00_model_name\",\n display_name=\"Model Name\",\n info=\"The embedding model to use for the selected provider. Each provider has a different set of \"\n \"models available (full list at \"\n \"https://docs.datastax.com/en/astra-db-serverless/databases/embedding-generation.html):\\n\\n\"\n f\"{', '.join(model_options)}\",\n options=model_options,\n required=True,\n ).to_dict()\n\n new_parameter_1 = DictInput(\n name=\"z_01_model_parameters\",\n display_name=\"Model Parameters\",\n is_list=True,\n ).to_dict()\n\n new_parameter_2 = MessageTextInput(\n name=\"z_02_api_key_name\",\n display_name=\"API Key name\",\n info=\"The name of the embeddings provider API key stored on Astra. \"\n \"If set, it will override the 'ProviderKey' in the authentication parameters.\",\n ).to_dict()\n\n new_parameter_3 = SecretStrInput(\n name=\"z_03_provider_api_key\",\n display_name=\"Provider API Key\",\n info=\"An alternative to the Astra Authentication that passes an API key for the provider \"\n \"with each request to Astra DB. \"\n \"This may be used when Vectorize is configured for the collection, \"\n \"but no corresponding provider secret is stored within Astra's key management system.\",\n ).to_dict()\n\n new_parameter_4 = DictInput(\n name=\"z_04_authentication\",\n display_name=\"Authentication parameters\",\n is_list=True,\n ).to_dict()\n\n self.insert_in_dict(\n build_config,\n \"provider\",\n {\n \"z_00_model_name\": new_parameter_0,\n \"z_01_model_parameters\": new_parameter_1,\n \"z_02_api_key_name\": new_parameter_2,\n \"z_03_provider_api_key\": new_parameter_3,\n \"z_04_authentication\": new_parameter_4,\n },\n )\n\n return build_config\n\n def build_vectorize_options(self, **kwargs):\n for attribute in [\n \"provider\",\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if not hasattr(self, attribute):\n setattr(self, attribute, None)\n\n # Fetch values from kwargs if any self.* attributes are None\n provider_value = self.VECTORIZE_PROVIDERS_MAPPING.get(self.provider, [None])[0] or kwargs.get(\"provider\")\n authentication = {**(self.z_04_authentication or kwargs.get(\"z_04_authentication\", {}))}\n\n api_key_name = self.z_02_api_key_name or kwargs.get(\"z_02_api_key_name\")\n provider_key = self.z_03_provider_api_key or kwargs.get(\"z_03_provider_api_key\")\n if api_key_name:\n authentication[\"providerKey\"] = api_key_name\n\n return {\n # must match astrapy.info.CollectionVectorServiceOptions\n \"collection_vector_service_options\": {\n \"provider\": provider_value,\n \"modelName\": self.z_00_model_name or kwargs.get(\"z_00_model_name\"),\n \"authentication\": authentication,\n \"parameters\": self.z_01_model_parameters or kwargs.get(\"z_01_model_parameters\", {}),\n },\n \"collection_embedding_api_key\": provider_key,\n }\n\n @check_cached_vector_store\n def build_vector_store(self, vectorize_options=None):\n try:\n from langchain_astradb import AstraDBVectorStore\n from langchain_astradb.utils.astradb import SetupMode\n except ImportError as e:\n msg = (\n \"Could not import langchain Astra DB integration package. \"\n \"Please install it with `pip install langchain-astradb`.\"\n )\n raise ImportError(msg) from e\n\n try:\n if not self.setup_mode:\n self.setup_mode = self._inputs[\"setup_mode\"].options[0]\n\n setup_mode_value = SetupMode[self.setup_mode.upper()]\n except KeyError as e:\n msg = f\"Invalid setup mode: {self.setup_mode}\"\n raise ValueError(msg) from e\n\n if self.embedding:\n embedding_dict = {\"embedding\": self.embedding}\n else:\n from astrapy.info import CollectionVectorServiceOptions\n\n dict_options = vectorize_options or self.build_vectorize_options()\n dict_options[\"authentication\"] = {\n k: v for k, v in dict_options.get(\"authentication\", {}).items() if k and v\n }\n dict_options[\"parameters\"] = {k: v for k, v in dict_options.get(\"parameters\", {}).items() if k and v}\n\n embedding_dict = {\n \"collection_vector_service_options\": CollectionVectorServiceOptions.from_dict(\n dict_options.get(\"collection_vector_service_options\", {})\n ),\n }\n\n vector_store_kwargs = {\n **embedding_dict,\n \"collection_name\": self.collection_name,\n \"token\": self.token,\n \"api_endpoint\": self.api_endpoint,\n \"namespace\": self.namespace or None,\n \"environment\": parse_api_endpoint(self.api_endpoint).environment,\n \"metric\": self.metric or None,\n \"batch_size\": self.batch_size or None,\n \"bulk_insert_batch_concurrency\": self.bulk_insert_batch_concurrency or None,\n \"bulk_insert_overwrite_concurrency\": self.bulk_insert_overwrite_concurrency or None,\n \"bulk_delete_concurrency\": self.bulk_delete_concurrency or None,\n \"setup_mode\": setup_mode_value,\n \"pre_delete_collection\": self.pre_delete_collection or False,\n }\n\n if self.metadata_indexing_include:\n vector_store_kwargs[\"metadata_indexing_include\"] = self.metadata_indexing_include\n elif self.metadata_indexing_exclude:\n vector_store_kwargs[\"metadata_indexing_exclude\"] = self.metadata_indexing_exclude\n elif self.collection_indexing_policy:\n vector_store_kwargs[\"collection_indexing_policy\"] = self.collection_indexing_policy\n\n try:\n vector_store = AstraDBVectorStore(**vector_store_kwargs)\n except Exception as e:\n msg = f\"Error initializing AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n\n self._add_documents_to_vector_store(vector_store)\n\n return vector_store\n\n def _add_documents_to_vector_store(self, vector_store) -> None:\n documents = []\n for _input in self.ingest_data or []:\n if isinstance(_input, Data):\n documents.append(_input.to_lc_document())\n else:\n msg = \"Vector Store Inputs must be Data objects.\"\n raise TypeError(msg)\n\n if documents:\n logger.debug(f\"Adding {len(documents)} documents to the Vector Store.\")\n try:\n vector_store.add_documents(documents)\n except Exception as e:\n msg = f\"Error adding documents to AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n else:\n logger.debug(\"No documents to add to the Vector Store.\")\n\n def _map_search_type(self) -> str:\n if self.search_type == \"Similarity with score threshold\":\n return \"similarity_score_threshold\"\n if self.search_type == \"MMR (Max Marginal Relevance)\":\n return \"mmr\"\n return \"similarity\"\n\n def _build_search_args(self):\n args = {\n \"k\": self.number_of_results,\n \"score_threshold\": self.search_score_threshold,\n }\n\n if self.search_filter:\n clean_filter = {k: v for k, v in self.search_filter.items() if k and v}\n if len(clean_filter) > 0:\n args[\"filter\"] = clean_filter\n return args\n\n def search_documents(self, vector_store=None) -> list[Data]:\n if not vector_store:\n vector_store = self.build_vector_store()\n\n logger.debug(f\"Search input: {self.search_input}\")\n logger.debug(f\"Search type: {self.search_type}\")\n logger.debug(f\"Number of results: {self.number_of_results}\")\n\n if self.search_input and isinstance(self.search_input, str) and self.search_input.strip():\n try:\n search_type = self._map_search_type()\n search_args = self._build_search_args()\n\n docs = vector_store.search(query=self.search_input, search_type=search_type, **search_args)\n except Exception as e:\n msg = f\"Error performing search in AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n\n logger.debug(f\"Retrieved documents: {len(docs)}\")\n\n data = docs_to_data(docs)\n logger.debug(f\"Converted documents to data: {len(data)}\")\n self.status = data\n return data\n logger.debug(\"No search input provided. Skipping search.\")\n return []\n\n def get_retriever_kwargs(self):\n search_args = self._build_search_args()\n return {\n \"search_type\": self._map_search_type(),\n \"search_kwargs\": search_args,\n }\n" + "value": "import os\n\nimport orjson\nfrom astrapy.admin import parse_api_endpoint\nfrom loguru import logger\n\nfrom langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store\nfrom langflow.helpers import docs_to_data\nfrom langflow.inputs import DictInput, FloatInput, MessageTextInput\nfrom langflow.io import (\n BoolInput,\n DataInput,\n DropdownInput,\n HandleInput,\n IntInput,\n MultilineInput,\n SecretStrInput,\n StrInput,\n)\nfrom langflow.schema import Data\n\n\nclass AstraVectorStoreComponent(LCVectorStoreComponent):\n display_name: str = \"Astra DB\"\n description: str = \"Implementation of Vector Store using Astra DB with search capabilities\"\n documentation: str = \"https://docs.langflow.org/starter-projects-vector-store-rag\"\n name = \"AstraDB\"\n icon: str = \"AstraDB\"\n\n VECTORIZE_PROVIDERS_MAPPING = {\n \"Azure OpenAI\": [\"azureOpenAI\", [\"text-embedding-3-small\", \"text-embedding-3-large\", \"text-embedding-ada-002\"]],\n \"Hugging Face - Dedicated\": [\"huggingfaceDedicated\", [\"endpoint-defined-model\"]],\n \"Hugging Face - Serverless\": [\n \"huggingface\",\n [\n \"sentence-transformers/all-MiniLM-L6-v2\",\n \"intfloat/multilingual-e5-large\",\n \"intfloat/multilingual-e5-large-instruct\",\n \"BAAI/bge-small-en-v1.5\",\n \"BAAI/bge-base-en-v1.5\",\n \"BAAI/bge-large-en-v1.5\",\n ],\n ],\n \"Jina AI\": [\n \"jinaAI\",\n [\n \"jina-embeddings-v2-base-en\",\n \"jina-embeddings-v2-base-de\",\n \"jina-embeddings-v2-base-es\",\n \"jina-embeddings-v2-base-code\",\n \"jina-embeddings-v2-base-zh\",\n ],\n ],\n \"Mistral AI\": [\"mistral\", [\"mistral-embed\"]],\n \"NVIDIA\": [\"nvidia\", [\"NV-Embed-QA\"]],\n \"OpenAI\": [\"openai\", [\"text-embedding-3-small\", \"text-embedding-3-large\", \"text-embedding-ada-002\"]],\n \"Upstage\": [\"upstageAI\", [\"solar-embedding-1-large\"]],\n \"Voyage AI\": [\n \"voyageAI\",\n [\"voyage-large-2-instruct\", \"voyage-law-2\", \"voyage-code-2\", \"voyage-large-2\", \"voyage-2\"],\n ],\n }\n\n inputs = [\n SecretStrInput(\n name=\"token\",\n display_name=\"Astra DB Application Token\",\n info=\"Authentication token for accessing Astra DB.\",\n value=\"ASTRA_DB_APPLICATION_TOKEN\",\n required=True,\n advanced=os.getenv(\"ASTRA_ENHANCED\", \"false\").lower() == \"true\",\n ),\n SecretStrInput(\n name=\"api_endpoint\",\n display_name=\"Database\" if os.getenv(\"ASTRA_ENHANCED\", \"false\").lower() == \"true\" else \"API Endpoint\",\n info=\"API endpoint URL for the Astra DB service.\",\n value=\"ASTRA_DB_API_ENDPOINT\",\n required=True,\n ),\n StrInput(\n name=\"collection_name\",\n display_name=\"Collection Name\",\n info=\"The name of the collection within Astra DB where the vectors will be stored.\",\n required=True,\n ),\n MultilineInput(\n name=\"search_input\",\n display_name=\"Search Input\",\n ),\n DataInput(\n name=\"ingest_data\",\n display_name=\"Ingest Data\",\n is_list=True,\n ),\n StrInput(\n name=\"namespace\",\n display_name=\"Namespace\",\n info=\"Optional namespace within Astra DB to use for the collection.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"embedding_service\",\n display_name=\"Embedding Model or Astra Vectorize\",\n info=\"Determines whether to use Astra Vectorize for the collection.\",\n options=[\"Embedding Model\", \"Astra Vectorize\"],\n real_time_refresh=True,\n value=\"Embedding Model\",\n ),\n HandleInput(\n name=\"embedding\",\n display_name=\"Embedding Model\",\n input_types=[\"Embeddings\"],\n info=\"Allows an embedding model configuration.\",\n ),\n DropdownInput(\n name=\"metric\",\n display_name=\"Metric\",\n info=\"Optional distance metric for vector comparisons in the vector store.\",\n options=[\"cosine\", \"dot_product\", \"euclidean\"],\n value=\"cosine\",\n advanced=True,\n ),\n IntInput(\n name=\"batch_size\",\n display_name=\"Batch Size\",\n info=\"Optional number of data to process in a single batch.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_insert_batch_concurrency\",\n display_name=\"Bulk Insert Batch Concurrency\",\n info=\"Optional concurrency level for bulk insert operations.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_insert_overwrite_concurrency\",\n display_name=\"Bulk Insert Overwrite Concurrency\",\n info=\"Optional concurrency level for bulk insert operations that overwrite existing data.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_delete_concurrency\",\n display_name=\"Bulk Delete Concurrency\",\n info=\"Optional concurrency level for bulk delete operations.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"setup_mode\",\n display_name=\"Setup Mode\",\n info=\"Configuration mode for setting up the vector store, with options like 'Sync' or 'Off'.\",\n options=[\"Sync\", \"Off\"],\n advanced=True,\n value=\"Sync\",\n ),\n BoolInput(\n name=\"pre_delete_collection\",\n display_name=\"Pre Delete Collection\",\n info=\"Boolean flag to determine whether to delete the collection before creating a new one.\",\n advanced=True,\n ),\n StrInput(\n name=\"metadata_indexing_include\",\n display_name=\"Metadata Indexing Include\",\n info=\"Optional list of metadata fields to include in the indexing.\",\n is_list=True,\n advanced=True,\n ),\n StrInput(\n name=\"metadata_indexing_exclude\",\n display_name=\"Metadata Indexing Exclude\",\n info=\"Optional list of metadata fields to exclude from the indexing.\",\n is_list=True,\n advanced=True,\n ),\n StrInput(\n name=\"collection_indexing_policy\",\n display_name=\"Collection Indexing Policy\",\n info='Optional JSON string for the \"indexing\" field of the collection. '\n \"See https://docs.datastax.com/en/astra-db-serverless/api-reference/collections.html#the-indexing-option\",\n advanced=True,\n ),\n IntInput(\n name=\"number_of_results\",\n display_name=\"Number of Results\",\n info=\"Number of results to return.\",\n advanced=True,\n value=4,\n ),\n DropdownInput(\n name=\"search_type\",\n display_name=\"Search Type\",\n info=\"Search type to use\",\n options=[\"Similarity\", \"Similarity with score threshold\", \"MMR (Max Marginal Relevance)\"],\n value=\"Similarity\",\n advanced=True,\n ),\n FloatInput(\n name=\"search_score_threshold\",\n display_name=\"Search Score Threshold\",\n info=\"Minimum similarity score threshold for search results. \"\n \"(when using 'Similarity with score threshold')\",\n value=0,\n advanced=True,\n ),\n DictInput(\n name=\"search_filter\",\n display_name=\"Search Metadata Filter\",\n info=\"Optional dictionary of filters to apply to the search query.\",\n advanced=True,\n is_list=True,\n ),\n ]\n\n def insert_in_dict(self, build_config, field_name, new_parameters):\n # Insert the new key-value pair after the found key\n for new_field_name, new_parameter in new_parameters.items():\n # Get all the items as a list of tuples (key, value)\n items = list(build_config.items())\n\n # Find the index of the key to insert after\n idx = len(items)\n for i, (key, _value) in enumerate(items):\n if key == field_name:\n idx = i + 1\n break\n\n items.insert(idx, (new_field_name, new_parameter))\n\n # Clear the original dictionary and update with the modified items\n build_config.clear()\n build_config.update(items)\n\n return build_config\n\n def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n if field_name == \"embedding_service\":\n if field_value == \"Astra Vectorize\":\n for field in [\"embedding\"]:\n if field in build_config:\n del build_config[field]\n\n new_parameter = DropdownInput(\n name=\"provider\",\n display_name=\"Vectorize Provider\",\n options=self.VECTORIZE_PROVIDERS_MAPPING.keys(),\n value=\"\",\n required=True,\n real_time_refresh=True,\n ).to_dict()\n\n self.insert_in_dict(build_config, \"embedding_service\", {\"provider\": new_parameter})\n else:\n for field in [\n \"provider\",\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if field in build_config:\n del build_config[field]\n\n new_parameter = HandleInput(\n name=\"embedding\",\n display_name=\"Embedding Model\",\n input_types=[\"Embeddings\"],\n info=\"Allows an embedding model configuration.\",\n ).to_dict()\n\n self.insert_in_dict(build_config, \"embedding_service\", {\"embedding\": new_parameter})\n\n elif field_name == \"provider\":\n for field in [\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if field in build_config:\n del build_config[field]\n\n model_options = self.VECTORIZE_PROVIDERS_MAPPING[field_value][1]\n\n new_parameter_0 = DropdownInput(\n name=\"z_00_model_name\",\n display_name=\"Model Name\",\n info=\"The embedding model to use for the selected provider. Each provider has a different set of \"\n \"models available (full list at \"\n \"https://docs.datastax.com/en/astra-db-serverless/databases/embedding-generation.html):\\n\\n\"\n f\"{', '.join(model_options)}\",\n options=model_options,\n required=True,\n ).to_dict()\n\n new_parameter_1 = DictInput(\n name=\"z_01_model_parameters\",\n display_name=\"Model Parameters\",\n is_list=True,\n ).to_dict()\n\n new_parameter_2 = MessageTextInput(\n name=\"z_02_api_key_name\",\n display_name=\"API Key name\",\n info=\"The name of the embeddings provider API key stored on Astra. \"\n \"If set, it will override the 'ProviderKey' in the authentication parameters.\",\n ).to_dict()\n\n new_parameter_3 = SecretStrInput(\n name=\"z_03_provider_api_key\",\n display_name=\"Provider API Key\",\n info=\"An alternative to the Astra Authentication that passes an API key for the provider \"\n \"with each request to Astra DB. \"\n \"This may be used when Vectorize is configured for the collection, \"\n \"but no corresponding provider secret is stored within Astra's key management system.\",\n ).to_dict()\n\n new_parameter_4 = DictInput(\n name=\"z_04_authentication\",\n display_name=\"Authentication parameters\",\n is_list=True,\n ).to_dict()\n\n self.insert_in_dict(\n build_config,\n \"provider\",\n {\n \"z_00_model_name\": new_parameter_0,\n \"z_01_model_parameters\": new_parameter_1,\n \"z_02_api_key_name\": new_parameter_2,\n \"z_03_provider_api_key\": new_parameter_3,\n \"z_04_authentication\": new_parameter_4,\n },\n )\n\n return build_config\n\n def build_vectorize_options(self, **kwargs):\n for attribute in [\n \"provider\",\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if not hasattr(self, attribute):\n setattr(self, attribute, None)\n\n # Fetch values from kwargs if any self.* attributes are None\n provider_value = self.VECTORIZE_PROVIDERS_MAPPING.get(self.provider, [None])[0] or kwargs.get(\"provider\")\n authentication = {**(self.z_04_authentication or kwargs.get(\"z_04_authentication\", {}))}\n\n api_key_name = self.z_02_api_key_name or kwargs.get(\"z_02_api_key_name\")\n provider_key = self.z_03_provider_api_key or kwargs.get(\"z_03_provider_api_key\")\n if api_key_name:\n authentication[\"providerKey\"] = api_key_name\n\n return {\n # must match astrapy.info.CollectionVectorServiceOptions\n \"collection_vector_service_options\": {\n \"provider\": provider_value,\n \"modelName\": self.z_00_model_name or kwargs.get(\"z_00_model_name\"),\n \"authentication\": authentication,\n \"parameters\": self.z_01_model_parameters or kwargs.get(\"z_01_model_parameters\", {}),\n },\n \"collection_embedding_api_key\": provider_key,\n }\n\n @check_cached_vector_store\n def build_vector_store(self, vectorize_options=None):\n try:\n from langchain_astradb import AstraDBVectorStore\n from langchain_astradb.utils.astradb import SetupMode\n except ImportError as e:\n msg = (\n \"Could not import langchain Astra DB integration package. \"\n \"Please install it with `pip install langchain-astradb`.\"\n )\n raise ImportError(msg) from e\n\n try:\n if not self.setup_mode:\n self.setup_mode = self._inputs[\"setup_mode\"].options[0]\n\n setup_mode_value = SetupMode[self.setup_mode.upper()]\n except KeyError as e:\n msg = f\"Invalid setup mode: {self.setup_mode}\"\n raise ValueError(msg) from e\n\n if self.embedding:\n embedding_dict = {\"embedding\": self.embedding}\n else:\n from astrapy.info import CollectionVectorServiceOptions\n\n dict_options = vectorize_options or self.build_vectorize_options()\n dict_options[\"authentication\"] = {\n k: v for k, v in dict_options.get(\"authentication\", {}).items() if k and v\n }\n dict_options[\"parameters\"] = {k: v for k, v in dict_options.get(\"parameters\", {}).items() if k and v}\n\n embedding_dict = {\n \"collection_vector_service_options\": CollectionVectorServiceOptions.from_dict(\n dict_options.get(\"collection_vector_service_options\", {})\n ),\n }\n\n try:\n vector_store = AstraDBVectorStore(\n collection_name=self.collection_name,\n token=self.token,\n api_endpoint=self.api_endpoint,\n namespace=self.namespace or None,\n environment=parse_api_endpoint(self.api_endpoint).environment,\n metric=self.metric,\n batch_size=self.batch_size or None,\n bulk_insert_batch_concurrency=self.bulk_insert_batch_concurrency or None,\n bulk_insert_overwrite_concurrency=self.bulk_insert_overwrite_concurrency or None,\n bulk_delete_concurrency=self.bulk_delete_concurrency or None,\n setup_mode=setup_mode_value,\n pre_delete_collection=self.pre_delete_collection,\n metadata_indexing_include=[s for s in self.metadata_indexing_include if s],\n metadata_indexing_exclude=[s for s in self.metadata_indexing_exclude if s],\n collection_indexing_policy=orjson.dumps(self.collection_indexing_policy)\n if self.collection_indexing_policy\n else None,\n **embedding_dict,\n )\n except Exception as e:\n msg = f\"Error initializing AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n\n self._add_documents_to_vector_store(vector_store)\n\n return vector_store\n\n def _add_documents_to_vector_store(self, vector_store) -> None:\n documents = []\n for _input in self.ingest_data or []:\n if isinstance(_input, Data):\n documents.append(_input.to_lc_document())\n else:\n msg = \"Vector Store Inputs must be Data objects.\"\n raise TypeError(msg)\n\n if documents:\n logger.debug(f\"Adding {len(documents)} documents to the Vector Store.\")\n try:\n vector_store.add_documents(documents)\n except Exception as e:\n msg = f\"Error adding documents to AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n else:\n logger.debug(\"No documents to add to the Vector Store.\")\n\n def _map_search_type(self) -> str:\n if self.search_type == \"Similarity with score threshold\":\n return \"similarity_score_threshold\"\n if self.search_type == \"MMR (Max Marginal Relevance)\":\n return \"mmr\"\n return \"similarity\"\n\n def _build_search_args(self):\n args = {\n \"k\": self.number_of_results,\n \"score_threshold\": self.search_score_threshold,\n }\n\n if self.search_filter:\n clean_filter = {k: v for k, v in self.search_filter.items() if k and v}\n if len(clean_filter) > 0:\n args[\"filter\"] = clean_filter\n return args\n\n def search_documents(self, vector_store=None) -> list[Data]:\n if not vector_store:\n vector_store = self.build_vector_store()\n\n logger.debug(f\"Search input: {self.search_input}\")\n logger.debug(f\"Search type: {self.search_type}\")\n logger.debug(f\"Number of results: {self.number_of_results}\")\n\n if self.search_input and isinstance(self.search_input, str) and self.search_input.strip():\n try:\n search_type = self._map_search_type()\n search_args = self._build_search_args()\n\n docs = vector_store.search(query=self.search_input, search_type=search_type, **search_args)\n except Exception as e:\n msg = f\"Error performing search in AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n\n logger.debug(f\"Retrieved documents: {len(docs)}\")\n\n data = docs_to_data(docs)\n logger.debug(f\"Converted documents to data: {len(data)}\")\n self.status = data\n return data\n logger.debug(\"No search input provided. Skipping search.\")\n return []\n\n def get_retriever_kwargs(self):\n search_args = self._build_search_args()\n return {\n \"search_type\": self._map_search_type(),\n \"search_kwargs\": search_args,\n }\n" }, "collection_indexing_policy": { "advanced": true, "display_name": "Collection Indexing Policy", "dynamic": false, - "info": "Optional dictionary defining the indexing policy for the collection.", + "info": "Optional JSON string for the \"indexing\" field of the collection. See https://docs.datastax.com/en/astra-db-serverless/api-reference/collections.html#the-indexing-option", "list": false, "load_from_db": false, "name": "collection_indexing_policy", @@ -780,7 +780,7 @@ "display_name": "Metadata Indexing Exclude", "dynamic": false, "info": "Optional list of metadata fields to exclude from the indexing.", - "list": false, + "list": true, "load_from_db": false, "name": "metadata_indexing_exclude", "placeholder": "", @@ -796,7 +796,7 @@ "display_name": "Metadata Indexing Include", "dynamic": false, "info": "Optional list of metadata fields to include in the indexing.", - "list": false, + "list": true, "load_from_db": false, "name": "metadata_indexing_include", "placeholder": "", @@ -946,11 +946,10 @@ "advanced": true, "display_name": "Setup Mode", "dynamic": false, - "info": "Configuration mode for setting up the vector store, with options like 'Sync', 'Async', or 'Off'.", + "info": "Configuration mode for setting up the vector store, with options like 'Sync' or 'Off'.", "name": "setup_mode", "options": [ "Sync", - "Async", "Off" ], "placeholder": "", @@ -1948,13 +1947,13 @@ "show": true, "title_case": false, "type": "code", - "value": "import os\n\nfrom astrapy.admin import parse_api_endpoint\nfrom loguru import logger\n\nfrom langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store\nfrom langflow.helpers import docs_to_data\nfrom langflow.inputs import DictInput, FloatInput, MessageTextInput\nfrom langflow.io import (\n BoolInput,\n DataInput,\n DropdownInput,\n HandleInput,\n IntInput,\n MultilineInput,\n SecretStrInput,\n StrInput,\n)\nfrom langflow.schema import Data\n\n\nclass AstraVectorStoreComponent(LCVectorStoreComponent):\n display_name: str = \"Astra DB\"\n description: str = \"Implementation of Vector Store using Astra DB with search capabilities\"\n documentation: str = \"https://docs.langflow.org/starter-projects-vector-store-rag\"\n name = \"AstraDB\"\n icon: str = \"AstraDB\"\n\n VECTORIZE_PROVIDERS_MAPPING = {\n \"Azure OpenAI\": [\"azureOpenAI\", [\"text-embedding-3-small\", \"text-embedding-3-large\", \"text-embedding-ada-002\"]],\n \"Hugging Face - Dedicated\": [\"huggingfaceDedicated\", [\"endpoint-defined-model\"]],\n \"Hugging Face - Serverless\": [\n \"huggingface\",\n [\n \"sentence-transformers/all-MiniLM-L6-v2\",\n \"intfloat/multilingual-e5-large\",\n \"intfloat/multilingual-e5-large-instruct\",\n \"BAAI/bge-small-en-v1.5\",\n \"BAAI/bge-base-en-v1.5\",\n \"BAAI/bge-large-en-v1.5\",\n ],\n ],\n \"Jina AI\": [\n \"jinaAI\",\n [\n \"jina-embeddings-v2-base-en\",\n \"jina-embeddings-v2-base-de\",\n \"jina-embeddings-v2-base-es\",\n \"jina-embeddings-v2-base-code\",\n \"jina-embeddings-v2-base-zh\",\n ],\n ],\n \"Mistral AI\": [\"mistral\", [\"mistral-embed\"]],\n \"NVIDIA\": [\"nvidia\", [\"NV-Embed-QA\"]],\n \"OpenAI\": [\"openai\", [\"text-embedding-3-small\", \"text-embedding-3-large\", \"text-embedding-ada-002\"]],\n \"Upstage\": [\"upstageAI\", [\"solar-embedding-1-large\"]],\n \"Voyage AI\": [\n \"voyageAI\",\n [\"voyage-large-2-instruct\", \"voyage-law-2\", \"voyage-code-2\", \"voyage-large-2\", \"voyage-2\"],\n ],\n }\n\n inputs = [\n SecretStrInput(\n name=\"token\",\n display_name=\"Astra DB Application Token\",\n info=\"Authentication token for accessing Astra DB.\",\n value=\"ASTRA_DB_APPLICATION_TOKEN\",\n required=True,\n advanced=os.getenv(\"ASTRA_ENHANCED\", \"false\").lower() == \"true\",\n ),\n SecretStrInput(\n name=\"api_endpoint\",\n display_name=\"Database\" if os.getenv(\"ASTRA_ENHANCED\", \"false\").lower() == \"true\" else \"API Endpoint\",\n info=\"API endpoint URL for the Astra DB service.\",\n value=\"ASTRA_DB_API_ENDPOINT\",\n required=True,\n ),\n StrInput(\n name=\"collection_name\",\n display_name=\"Collection Name\",\n info=\"The name of the collection within Astra DB where the vectors will be stored.\",\n required=True,\n ),\n MultilineInput(\n name=\"search_input\",\n display_name=\"Search Input\",\n ),\n DataInput(\n name=\"ingest_data\",\n display_name=\"Ingest Data\",\n is_list=True,\n ),\n StrInput(\n name=\"namespace\",\n display_name=\"Namespace\",\n info=\"Optional namespace within Astra DB to use for the collection.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"embedding_service\",\n display_name=\"Embedding Model or Astra Vectorize\",\n info=\"Determines whether to use Astra Vectorize for the collection.\",\n options=[\"Embedding Model\", \"Astra Vectorize\"],\n real_time_refresh=True,\n value=\"Embedding Model\",\n ),\n HandleInput(\n name=\"embedding\",\n display_name=\"Embedding Model\",\n input_types=[\"Embeddings\"],\n info=\"Allows an embedding model configuration.\",\n ),\n DropdownInput(\n name=\"metric\",\n display_name=\"Metric\",\n info=\"Optional distance metric for vector comparisons in the vector store.\",\n options=[\"cosine\", \"dot_product\", \"euclidean\"],\n advanced=True,\n ),\n IntInput(\n name=\"batch_size\",\n display_name=\"Batch Size\",\n info=\"Optional number of data to process in a single batch.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_insert_batch_concurrency\",\n display_name=\"Bulk Insert Batch Concurrency\",\n info=\"Optional concurrency level for bulk insert operations.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_insert_overwrite_concurrency\",\n display_name=\"Bulk Insert Overwrite Concurrency\",\n info=\"Optional concurrency level for bulk insert operations that overwrite existing data.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_delete_concurrency\",\n display_name=\"Bulk Delete Concurrency\",\n info=\"Optional concurrency level for bulk delete operations.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"setup_mode\",\n display_name=\"Setup Mode\",\n info=\"Configuration mode for setting up the vector store, with options like 'Sync', 'Async', or 'Off'.\",\n options=[\"Sync\", \"Async\", \"Off\"],\n advanced=True,\n value=\"Sync\",\n ),\n BoolInput(\n name=\"pre_delete_collection\",\n display_name=\"Pre Delete Collection\",\n info=\"Boolean flag to determine whether to delete the collection before creating a new one.\",\n advanced=True,\n ),\n StrInput(\n name=\"metadata_indexing_include\",\n display_name=\"Metadata Indexing Include\",\n info=\"Optional list of metadata fields to include in the indexing.\",\n advanced=True,\n ),\n StrInput(\n name=\"metadata_indexing_exclude\",\n display_name=\"Metadata Indexing Exclude\",\n info=\"Optional list of metadata fields to exclude from the indexing.\",\n advanced=True,\n ),\n StrInput(\n name=\"collection_indexing_policy\",\n display_name=\"Collection Indexing Policy\",\n info=\"Optional dictionary defining the indexing policy for the collection.\",\n advanced=True,\n ),\n IntInput(\n name=\"number_of_results\",\n display_name=\"Number of Results\",\n info=\"Number of results to return.\",\n advanced=True,\n value=4,\n ),\n DropdownInput(\n name=\"search_type\",\n display_name=\"Search Type\",\n info=\"Search type to use\",\n options=[\"Similarity\", \"Similarity with score threshold\", \"MMR (Max Marginal Relevance)\"],\n value=\"Similarity\",\n advanced=True,\n ),\n FloatInput(\n name=\"search_score_threshold\",\n display_name=\"Search Score Threshold\",\n info=\"Minimum similarity score threshold for search results. \"\n \"(when using 'Similarity with score threshold')\",\n value=0,\n advanced=True,\n ),\n DictInput(\n name=\"search_filter\",\n display_name=\"Search Metadata Filter\",\n info=\"Optional dictionary of filters to apply to the search query.\",\n advanced=True,\n is_list=True,\n ),\n ]\n\n def insert_in_dict(self, build_config, field_name, new_parameters):\n # Insert the new key-value pair after the found key\n for new_field_name, new_parameter in new_parameters.items():\n # Get all the items as a list of tuples (key, value)\n items = list(build_config.items())\n\n # Find the index of the key to insert after\n idx = len(items)\n for i, (key, _value) in enumerate(items):\n if key == field_name:\n idx = i + 1\n break\n\n items.insert(idx, (new_field_name, new_parameter))\n\n # Clear the original dictionary and update with the modified items\n build_config.clear()\n build_config.update(items)\n\n return build_config\n\n def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n if field_name == \"embedding_service\":\n if field_value == \"Astra Vectorize\":\n for field in [\"embedding\"]:\n if field in build_config:\n del build_config[field]\n\n new_parameter = DropdownInput(\n name=\"provider\",\n display_name=\"Vectorize Provider\",\n options=self.VECTORIZE_PROVIDERS_MAPPING.keys(),\n value=\"\",\n required=True,\n real_time_refresh=True,\n ).to_dict()\n\n self.insert_in_dict(build_config, \"embedding_service\", {\"provider\": new_parameter})\n else:\n for field in [\n \"provider\",\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if field in build_config:\n del build_config[field]\n\n new_parameter = HandleInput(\n name=\"embedding\",\n display_name=\"Embedding Model\",\n input_types=[\"Embeddings\"],\n info=\"Allows an embedding model configuration.\",\n ).to_dict()\n\n self.insert_in_dict(build_config, \"embedding_service\", {\"embedding\": new_parameter})\n\n elif field_name == \"provider\":\n for field in [\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if field in build_config:\n del build_config[field]\n\n model_options = self.VECTORIZE_PROVIDERS_MAPPING[field_value][1]\n\n new_parameter_0 = DropdownInput(\n name=\"z_00_model_name\",\n display_name=\"Model Name\",\n info=\"The embedding model to use for the selected provider. Each provider has a different set of \"\n \"models available (full list at \"\n \"https://docs.datastax.com/en/astra-db-serverless/databases/embedding-generation.html):\\n\\n\"\n f\"{', '.join(model_options)}\",\n options=model_options,\n required=True,\n ).to_dict()\n\n new_parameter_1 = DictInput(\n name=\"z_01_model_parameters\",\n display_name=\"Model Parameters\",\n is_list=True,\n ).to_dict()\n\n new_parameter_2 = MessageTextInput(\n name=\"z_02_api_key_name\",\n display_name=\"API Key name\",\n info=\"The name of the embeddings provider API key stored on Astra. \"\n \"If set, it will override the 'ProviderKey' in the authentication parameters.\",\n ).to_dict()\n\n new_parameter_3 = SecretStrInput(\n name=\"z_03_provider_api_key\",\n display_name=\"Provider API Key\",\n info=\"An alternative to the Astra Authentication that passes an API key for the provider \"\n \"with each request to Astra DB. \"\n \"This may be used when Vectorize is configured for the collection, \"\n \"but no corresponding provider secret is stored within Astra's key management system.\",\n ).to_dict()\n\n new_parameter_4 = DictInput(\n name=\"z_04_authentication\",\n display_name=\"Authentication parameters\",\n is_list=True,\n ).to_dict()\n\n self.insert_in_dict(\n build_config,\n \"provider\",\n {\n \"z_00_model_name\": new_parameter_0,\n \"z_01_model_parameters\": new_parameter_1,\n \"z_02_api_key_name\": new_parameter_2,\n \"z_03_provider_api_key\": new_parameter_3,\n \"z_04_authentication\": new_parameter_4,\n },\n )\n\n return build_config\n\n def build_vectorize_options(self, **kwargs):\n for attribute in [\n \"provider\",\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if not hasattr(self, attribute):\n setattr(self, attribute, None)\n\n # Fetch values from kwargs if any self.* attributes are None\n provider_value = self.VECTORIZE_PROVIDERS_MAPPING.get(self.provider, [None])[0] or kwargs.get(\"provider\")\n authentication = {**(self.z_04_authentication or kwargs.get(\"z_04_authentication\", {}))}\n\n api_key_name = self.z_02_api_key_name or kwargs.get(\"z_02_api_key_name\")\n provider_key = self.z_03_provider_api_key or kwargs.get(\"z_03_provider_api_key\")\n if api_key_name:\n authentication[\"providerKey\"] = api_key_name\n\n return {\n # must match astrapy.info.CollectionVectorServiceOptions\n \"collection_vector_service_options\": {\n \"provider\": provider_value,\n \"modelName\": self.z_00_model_name or kwargs.get(\"z_00_model_name\"),\n \"authentication\": authentication,\n \"parameters\": self.z_01_model_parameters or kwargs.get(\"z_01_model_parameters\", {}),\n },\n \"collection_embedding_api_key\": provider_key,\n }\n\n @check_cached_vector_store\n def build_vector_store(self, vectorize_options=None):\n try:\n from langchain_astradb import AstraDBVectorStore\n from langchain_astradb.utils.astradb import SetupMode\n except ImportError as e:\n msg = (\n \"Could not import langchain Astra DB integration package. \"\n \"Please install it with `pip install langchain-astradb`.\"\n )\n raise ImportError(msg) from e\n\n try:\n if not self.setup_mode:\n self.setup_mode = self._inputs[\"setup_mode\"].options[0]\n\n setup_mode_value = SetupMode[self.setup_mode.upper()]\n except KeyError as e:\n msg = f\"Invalid setup mode: {self.setup_mode}\"\n raise ValueError(msg) from e\n\n if self.embedding:\n embedding_dict = {\"embedding\": self.embedding}\n else:\n from astrapy.info import CollectionVectorServiceOptions\n\n dict_options = vectorize_options or self.build_vectorize_options()\n dict_options[\"authentication\"] = {\n k: v for k, v in dict_options.get(\"authentication\", {}).items() if k and v\n }\n dict_options[\"parameters\"] = {k: v for k, v in dict_options.get(\"parameters\", {}).items() if k and v}\n\n embedding_dict = {\n \"collection_vector_service_options\": CollectionVectorServiceOptions.from_dict(\n dict_options.get(\"collection_vector_service_options\", {})\n ),\n }\n\n vector_store_kwargs = {\n **embedding_dict,\n \"collection_name\": self.collection_name,\n \"token\": self.token,\n \"api_endpoint\": self.api_endpoint,\n \"namespace\": self.namespace or None,\n \"environment\": parse_api_endpoint(self.api_endpoint).environment,\n \"metric\": self.metric or None,\n \"batch_size\": self.batch_size or None,\n \"bulk_insert_batch_concurrency\": self.bulk_insert_batch_concurrency or None,\n \"bulk_insert_overwrite_concurrency\": self.bulk_insert_overwrite_concurrency or None,\n \"bulk_delete_concurrency\": self.bulk_delete_concurrency or None,\n \"setup_mode\": setup_mode_value,\n \"pre_delete_collection\": self.pre_delete_collection or False,\n }\n\n if self.metadata_indexing_include:\n vector_store_kwargs[\"metadata_indexing_include\"] = self.metadata_indexing_include\n elif self.metadata_indexing_exclude:\n vector_store_kwargs[\"metadata_indexing_exclude\"] = self.metadata_indexing_exclude\n elif self.collection_indexing_policy:\n vector_store_kwargs[\"collection_indexing_policy\"] = self.collection_indexing_policy\n\n try:\n vector_store = AstraDBVectorStore(**vector_store_kwargs)\n except Exception as e:\n msg = f\"Error initializing AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n\n self._add_documents_to_vector_store(vector_store)\n\n return vector_store\n\n def _add_documents_to_vector_store(self, vector_store) -> None:\n documents = []\n for _input in self.ingest_data or []:\n if isinstance(_input, Data):\n documents.append(_input.to_lc_document())\n else:\n msg = \"Vector Store Inputs must be Data objects.\"\n raise TypeError(msg)\n\n if documents:\n logger.debug(f\"Adding {len(documents)} documents to the Vector Store.\")\n try:\n vector_store.add_documents(documents)\n except Exception as e:\n msg = f\"Error adding documents to AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n else:\n logger.debug(\"No documents to add to the Vector Store.\")\n\n def _map_search_type(self) -> str:\n if self.search_type == \"Similarity with score threshold\":\n return \"similarity_score_threshold\"\n if self.search_type == \"MMR (Max Marginal Relevance)\":\n return \"mmr\"\n return \"similarity\"\n\n def _build_search_args(self):\n args = {\n \"k\": self.number_of_results,\n \"score_threshold\": self.search_score_threshold,\n }\n\n if self.search_filter:\n clean_filter = {k: v for k, v in self.search_filter.items() if k and v}\n if len(clean_filter) > 0:\n args[\"filter\"] = clean_filter\n return args\n\n def search_documents(self, vector_store=None) -> list[Data]:\n if not vector_store:\n vector_store = self.build_vector_store()\n\n logger.debug(f\"Search input: {self.search_input}\")\n logger.debug(f\"Search type: {self.search_type}\")\n logger.debug(f\"Number of results: {self.number_of_results}\")\n\n if self.search_input and isinstance(self.search_input, str) and self.search_input.strip():\n try:\n search_type = self._map_search_type()\n search_args = self._build_search_args()\n\n docs = vector_store.search(query=self.search_input, search_type=search_type, **search_args)\n except Exception as e:\n msg = f\"Error performing search in AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n\n logger.debug(f\"Retrieved documents: {len(docs)}\")\n\n data = docs_to_data(docs)\n logger.debug(f\"Converted documents to data: {len(data)}\")\n self.status = data\n return data\n logger.debug(\"No search input provided. Skipping search.\")\n return []\n\n def get_retriever_kwargs(self):\n search_args = self._build_search_args()\n return {\n \"search_type\": self._map_search_type(),\n \"search_kwargs\": search_args,\n }\n" + "value": "import os\n\nimport orjson\nfrom astrapy.admin import parse_api_endpoint\nfrom loguru import logger\n\nfrom langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store\nfrom langflow.helpers import docs_to_data\nfrom langflow.inputs import DictInput, FloatInput, MessageTextInput\nfrom langflow.io import (\n BoolInput,\n DataInput,\n DropdownInput,\n HandleInput,\n IntInput,\n MultilineInput,\n SecretStrInput,\n StrInput,\n)\nfrom langflow.schema import Data\n\n\nclass AstraVectorStoreComponent(LCVectorStoreComponent):\n display_name: str = \"Astra DB\"\n description: str = \"Implementation of Vector Store using Astra DB with search capabilities\"\n documentation: str = \"https://docs.langflow.org/starter-projects-vector-store-rag\"\n name = \"AstraDB\"\n icon: str = \"AstraDB\"\n\n VECTORIZE_PROVIDERS_MAPPING = {\n \"Azure OpenAI\": [\"azureOpenAI\", [\"text-embedding-3-small\", \"text-embedding-3-large\", \"text-embedding-ada-002\"]],\n \"Hugging Face - Dedicated\": [\"huggingfaceDedicated\", [\"endpoint-defined-model\"]],\n \"Hugging Face - Serverless\": [\n \"huggingface\",\n [\n \"sentence-transformers/all-MiniLM-L6-v2\",\n \"intfloat/multilingual-e5-large\",\n \"intfloat/multilingual-e5-large-instruct\",\n \"BAAI/bge-small-en-v1.5\",\n \"BAAI/bge-base-en-v1.5\",\n \"BAAI/bge-large-en-v1.5\",\n ],\n ],\n \"Jina AI\": [\n \"jinaAI\",\n [\n \"jina-embeddings-v2-base-en\",\n \"jina-embeddings-v2-base-de\",\n \"jina-embeddings-v2-base-es\",\n \"jina-embeddings-v2-base-code\",\n \"jina-embeddings-v2-base-zh\",\n ],\n ],\n \"Mistral AI\": [\"mistral\", [\"mistral-embed\"]],\n \"NVIDIA\": [\"nvidia\", [\"NV-Embed-QA\"]],\n \"OpenAI\": [\"openai\", [\"text-embedding-3-small\", \"text-embedding-3-large\", \"text-embedding-ada-002\"]],\n \"Upstage\": [\"upstageAI\", [\"solar-embedding-1-large\"]],\n \"Voyage AI\": [\n \"voyageAI\",\n [\"voyage-large-2-instruct\", \"voyage-law-2\", \"voyage-code-2\", \"voyage-large-2\", \"voyage-2\"],\n ],\n }\n\n inputs = [\n SecretStrInput(\n name=\"token\",\n display_name=\"Astra DB Application Token\",\n info=\"Authentication token for accessing Astra DB.\",\n value=\"ASTRA_DB_APPLICATION_TOKEN\",\n required=True,\n advanced=os.getenv(\"ASTRA_ENHANCED\", \"false\").lower() == \"true\",\n ),\n SecretStrInput(\n name=\"api_endpoint\",\n display_name=\"Database\" if os.getenv(\"ASTRA_ENHANCED\", \"false\").lower() == \"true\" else \"API Endpoint\",\n info=\"API endpoint URL for the Astra DB service.\",\n value=\"ASTRA_DB_API_ENDPOINT\",\n required=True,\n ),\n StrInput(\n name=\"collection_name\",\n display_name=\"Collection Name\",\n info=\"The name of the collection within Astra DB where the vectors will be stored.\",\n required=True,\n ),\n MultilineInput(\n name=\"search_input\",\n display_name=\"Search Input\",\n ),\n DataInput(\n name=\"ingest_data\",\n display_name=\"Ingest Data\",\n is_list=True,\n ),\n StrInput(\n name=\"namespace\",\n display_name=\"Namespace\",\n info=\"Optional namespace within Astra DB to use for the collection.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"embedding_service\",\n display_name=\"Embedding Model or Astra Vectorize\",\n info=\"Determines whether to use Astra Vectorize for the collection.\",\n options=[\"Embedding Model\", \"Astra Vectorize\"],\n real_time_refresh=True,\n value=\"Embedding Model\",\n ),\n HandleInput(\n name=\"embedding\",\n display_name=\"Embedding Model\",\n input_types=[\"Embeddings\"],\n info=\"Allows an embedding model configuration.\",\n ),\n DropdownInput(\n name=\"metric\",\n display_name=\"Metric\",\n info=\"Optional distance metric for vector comparisons in the vector store.\",\n options=[\"cosine\", \"dot_product\", \"euclidean\"],\n value=\"cosine\",\n advanced=True,\n ),\n IntInput(\n name=\"batch_size\",\n display_name=\"Batch Size\",\n info=\"Optional number of data to process in a single batch.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_insert_batch_concurrency\",\n display_name=\"Bulk Insert Batch Concurrency\",\n info=\"Optional concurrency level for bulk insert operations.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_insert_overwrite_concurrency\",\n display_name=\"Bulk Insert Overwrite Concurrency\",\n info=\"Optional concurrency level for bulk insert operations that overwrite existing data.\",\n advanced=True,\n ),\n IntInput(\n name=\"bulk_delete_concurrency\",\n display_name=\"Bulk Delete Concurrency\",\n info=\"Optional concurrency level for bulk delete operations.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"setup_mode\",\n display_name=\"Setup Mode\",\n info=\"Configuration mode for setting up the vector store, with options like 'Sync' or 'Off'.\",\n options=[\"Sync\", \"Off\"],\n advanced=True,\n value=\"Sync\",\n ),\n BoolInput(\n name=\"pre_delete_collection\",\n display_name=\"Pre Delete Collection\",\n info=\"Boolean flag to determine whether to delete the collection before creating a new one.\",\n advanced=True,\n ),\n StrInput(\n name=\"metadata_indexing_include\",\n display_name=\"Metadata Indexing Include\",\n info=\"Optional list of metadata fields to include in the indexing.\",\n is_list=True,\n advanced=True,\n ),\n StrInput(\n name=\"metadata_indexing_exclude\",\n display_name=\"Metadata Indexing Exclude\",\n info=\"Optional list of metadata fields to exclude from the indexing.\",\n is_list=True,\n advanced=True,\n ),\n StrInput(\n name=\"collection_indexing_policy\",\n display_name=\"Collection Indexing Policy\",\n info='Optional JSON string for the \"indexing\" field of the collection. '\n \"See https://docs.datastax.com/en/astra-db-serverless/api-reference/collections.html#the-indexing-option\",\n advanced=True,\n ),\n IntInput(\n name=\"number_of_results\",\n display_name=\"Number of Results\",\n info=\"Number of results to return.\",\n advanced=True,\n value=4,\n ),\n DropdownInput(\n name=\"search_type\",\n display_name=\"Search Type\",\n info=\"Search type to use\",\n options=[\"Similarity\", \"Similarity with score threshold\", \"MMR (Max Marginal Relevance)\"],\n value=\"Similarity\",\n advanced=True,\n ),\n FloatInput(\n name=\"search_score_threshold\",\n display_name=\"Search Score Threshold\",\n info=\"Minimum similarity score threshold for search results. \"\n \"(when using 'Similarity with score threshold')\",\n value=0,\n advanced=True,\n ),\n DictInput(\n name=\"search_filter\",\n display_name=\"Search Metadata Filter\",\n info=\"Optional dictionary of filters to apply to the search query.\",\n advanced=True,\n is_list=True,\n ),\n ]\n\n def insert_in_dict(self, build_config, field_name, new_parameters):\n # Insert the new key-value pair after the found key\n for new_field_name, new_parameter in new_parameters.items():\n # Get all the items as a list of tuples (key, value)\n items = list(build_config.items())\n\n # Find the index of the key to insert after\n idx = len(items)\n for i, (key, _value) in enumerate(items):\n if key == field_name:\n idx = i + 1\n break\n\n items.insert(idx, (new_field_name, new_parameter))\n\n # Clear the original dictionary and update with the modified items\n build_config.clear()\n build_config.update(items)\n\n return build_config\n\n def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n if field_name == \"embedding_service\":\n if field_value == \"Astra Vectorize\":\n for field in [\"embedding\"]:\n if field in build_config:\n del build_config[field]\n\n new_parameter = DropdownInput(\n name=\"provider\",\n display_name=\"Vectorize Provider\",\n options=self.VECTORIZE_PROVIDERS_MAPPING.keys(),\n value=\"\",\n required=True,\n real_time_refresh=True,\n ).to_dict()\n\n self.insert_in_dict(build_config, \"embedding_service\", {\"provider\": new_parameter})\n else:\n for field in [\n \"provider\",\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if field in build_config:\n del build_config[field]\n\n new_parameter = HandleInput(\n name=\"embedding\",\n display_name=\"Embedding Model\",\n input_types=[\"Embeddings\"],\n info=\"Allows an embedding model configuration.\",\n ).to_dict()\n\n self.insert_in_dict(build_config, \"embedding_service\", {\"embedding\": new_parameter})\n\n elif field_name == \"provider\":\n for field in [\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if field in build_config:\n del build_config[field]\n\n model_options = self.VECTORIZE_PROVIDERS_MAPPING[field_value][1]\n\n new_parameter_0 = DropdownInput(\n name=\"z_00_model_name\",\n display_name=\"Model Name\",\n info=\"The embedding model to use for the selected provider. Each provider has a different set of \"\n \"models available (full list at \"\n \"https://docs.datastax.com/en/astra-db-serverless/databases/embedding-generation.html):\\n\\n\"\n f\"{', '.join(model_options)}\",\n options=model_options,\n required=True,\n ).to_dict()\n\n new_parameter_1 = DictInput(\n name=\"z_01_model_parameters\",\n display_name=\"Model Parameters\",\n is_list=True,\n ).to_dict()\n\n new_parameter_2 = MessageTextInput(\n name=\"z_02_api_key_name\",\n display_name=\"API Key name\",\n info=\"The name of the embeddings provider API key stored on Astra. \"\n \"If set, it will override the 'ProviderKey' in the authentication parameters.\",\n ).to_dict()\n\n new_parameter_3 = SecretStrInput(\n name=\"z_03_provider_api_key\",\n display_name=\"Provider API Key\",\n info=\"An alternative to the Astra Authentication that passes an API key for the provider \"\n \"with each request to Astra DB. \"\n \"This may be used when Vectorize is configured for the collection, \"\n \"but no corresponding provider secret is stored within Astra's key management system.\",\n ).to_dict()\n\n new_parameter_4 = DictInput(\n name=\"z_04_authentication\",\n display_name=\"Authentication parameters\",\n is_list=True,\n ).to_dict()\n\n self.insert_in_dict(\n build_config,\n \"provider\",\n {\n \"z_00_model_name\": new_parameter_0,\n \"z_01_model_parameters\": new_parameter_1,\n \"z_02_api_key_name\": new_parameter_2,\n \"z_03_provider_api_key\": new_parameter_3,\n \"z_04_authentication\": new_parameter_4,\n },\n )\n\n return build_config\n\n def build_vectorize_options(self, **kwargs):\n for attribute in [\n \"provider\",\n \"z_00_model_name\",\n \"z_01_model_parameters\",\n \"z_02_api_key_name\",\n \"z_03_provider_api_key\",\n \"z_04_authentication\",\n ]:\n if not hasattr(self, attribute):\n setattr(self, attribute, None)\n\n # Fetch values from kwargs if any self.* attributes are None\n provider_value = self.VECTORIZE_PROVIDERS_MAPPING.get(self.provider, [None])[0] or kwargs.get(\"provider\")\n authentication = {**(self.z_04_authentication or kwargs.get(\"z_04_authentication\", {}))}\n\n api_key_name = self.z_02_api_key_name or kwargs.get(\"z_02_api_key_name\")\n provider_key = self.z_03_provider_api_key or kwargs.get(\"z_03_provider_api_key\")\n if api_key_name:\n authentication[\"providerKey\"] = api_key_name\n\n return {\n # must match astrapy.info.CollectionVectorServiceOptions\n \"collection_vector_service_options\": {\n \"provider\": provider_value,\n \"modelName\": self.z_00_model_name or kwargs.get(\"z_00_model_name\"),\n \"authentication\": authentication,\n \"parameters\": self.z_01_model_parameters or kwargs.get(\"z_01_model_parameters\", {}),\n },\n \"collection_embedding_api_key\": provider_key,\n }\n\n @check_cached_vector_store\n def build_vector_store(self, vectorize_options=None):\n try:\n from langchain_astradb import AstraDBVectorStore\n from langchain_astradb.utils.astradb import SetupMode\n except ImportError as e:\n msg = (\n \"Could not import langchain Astra DB integration package. \"\n \"Please install it with `pip install langchain-astradb`.\"\n )\n raise ImportError(msg) from e\n\n try:\n if not self.setup_mode:\n self.setup_mode = self._inputs[\"setup_mode\"].options[0]\n\n setup_mode_value = SetupMode[self.setup_mode.upper()]\n except KeyError as e:\n msg = f\"Invalid setup mode: {self.setup_mode}\"\n raise ValueError(msg) from e\n\n if self.embedding:\n embedding_dict = {\"embedding\": self.embedding}\n else:\n from astrapy.info import CollectionVectorServiceOptions\n\n dict_options = vectorize_options or self.build_vectorize_options()\n dict_options[\"authentication\"] = {\n k: v for k, v in dict_options.get(\"authentication\", {}).items() if k and v\n }\n dict_options[\"parameters\"] = {k: v for k, v in dict_options.get(\"parameters\", {}).items() if k and v}\n\n embedding_dict = {\n \"collection_vector_service_options\": CollectionVectorServiceOptions.from_dict(\n dict_options.get(\"collection_vector_service_options\", {})\n ),\n }\n\n try:\n vector_store = AstraDBVectorStore(\n collection_name=self.collection_name,\n token=self.token,\n api_endpoint=self.api_endpoint,\n namespace=self.namespace or None,\n environment=parse_api_endpoint(self.api_endpoint).environment,\n metric=self.metric,\n batch_size=self.batch_size or None,\n bulk_insert_batch_concurrency=self.bulk_insert_batch_concurrency or None,\n bulk_insert_overwrite_concurrency=self.bulk_insert_overwrite_concurrency or None,\n bulk_delete_concurrency=self.bulk_delete_concurrency or None,\n setup_mode=setup_mode_value,\n pre_delete_collection=self.pre_delete_collection,\n metadata_indexing_include=[s for s in self.metadata_indexing_include if s],\n metadata_indexing_exclude=[s for s in self.metadata_indexing_exclude if s],\n collection_indexing_policy=orjson.dumps(self.collection_indexing_policy)\n if self.collection_indexing_policy\n else None,\n **embedding_dict,\n )\n except Exception as e:\n msg = f\"Error initializing AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n\n self._add_documents_to_vector_store(vector_store)\n\n return vector_store\n\n def _add_documents_to_vector_store(self, vector_store) -> None:\n documents = []\n for _input in self.ingest_data or []:\n if isinstance(_input, Data):\n documents.append(_input.to_lc_document())\n else:\n msg = \"Vector Store Inputs must be Data objects.\"\n raise TypeError(msg)\n\n if documents:\n logger.debug(f\"Adding {len(documents)} documents to the Vector Store.\")\n try:\n vector_store.add_documents(documents)\n except Exception as e:\n msg = f\"Error adding documents to AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n else:\n logger.debug(\"No documents to add to the Vector Store.\")\n\n def _map_search_type(self) -> str:\n if self.search_type == \"Similarity with score threshold\":\n return \"similarity_score_threshold\"\n if self.search_type == \"MMR (Max Marginal Relevance)\":\n return \"mmr\"\n return \"similarity\"\n\n def _build_search_args(self):\n args = {\n \"k\": self.number_of_results,\n \"score_threshold\": self.search_score_threshold,\n }\n\n if self.search_filter:\n clean_filter = {k: v for k, v in self.search_filter.items() if k and v}\n if len(clean_filter) > 0:\n args[\"filter\"] = clean_filter\n return args\n\n def search_documents(self, vector_store=None) -> list[Data]:\n if not vector_store:\n vector_store = self.build_vector_store()\n\n logger.debug(f\"Search input: {self.search_input}\")\n logger.debug(f\"Search type: {self.search_type}\")\n logger.debug(f\"Number of results: {self.number_of_results}\")\n\n if self.search_input and isinstance(self.search_input, str) and self.search_input.strip():\n try:\n search_type = self._map_search_type()\n search_args = self._build_search_args()\n\n docs = vector_store.search(query=self.search_input, search_type=search_type, **search_args)\n except Exception as e:\n msg = f\"Error performing search in AstraDBVectorStore: {e}\"\n raise ValueError(msg) from e\n\n logger.debug(f\"Retrieved documents: {len(docs)}\")\n\n data = docs_to_data(docs)\n logger.debug(f\"Converted documents to data: {len(data)}\")\n self.status = data\n return data\n logger.debug(\"No search input provided. Skipping search.\")\n return []\n\n def get_retriever_kwargs(self):\n search_args = self._build_search_args()\n return {\n \"search_type\": self._map_search_type(),\n \"search_kwargs\": search_args,\n }\n" }, "collection_indexing_policy": { "advanced": true, "display_name": "Collection Indexing Policy", "dynamic": false, - "info": "Optional dictionary defining the indexing policy for the collection.", + "info": "Optional JSON string for the \"indexing\" field of the collection. See https://docs.datastax.com/en/astra-db-serverless/api-reference/collections.html#the-indexing-option", "list": false, "load_from_db": false, "name": "collection_indexing_policy", @@ -2045,7 +2044,7 @@ "display_name": "Metadata Indexing Exclude", "dynamic": false, "info": "Optional list of metadata fields to exclude from the indexing.", - "list": false, + "list": true, "load_from_db": false, "name": "metadata_indexing_exclude", "placeholder": "", @@ -2061,7 +2060,7 @@ "display_name": "Metadata Indexing Include", "dynamic": false, "info": "Optional list of metadata fields to include in the indexing.", - "list": false, + "list": true, "load_from_db": false, "name": "metadata_indexing_include", "placeholder": "", @@ -2211,11 +2210,10 @@ "advanced": true, "display_name": "Setup Mode", "dynamic": false, - "info": "Configuration mode for setting up the vector store, with options like 'Sync', 'Async', or 'Off'.", + "info": "Configuration mode for setting up the vector store, with options like 'Sync' or 'Off'.", "name": "setup_mode", "options": [ "Sync", - "Async", "Off" ], "placeholder": "", diff --git a/src/backend/base/langflow/load/load.py b/src/backend/base/langflow/load/load.py index 038ffc2d6ef..1cbc7b4f345 100644 --- a/src/backend/base/langflow/load/load.py +++ b/src/backend/base/langflow/load/load.py @@ -1,8 +1,8 @@ +import asyncio import json from pathlib import Path from dotenv import load_dotenv -from loguru import logger from langflow.graph import Graph from langflow.graph.schema import RunOutputs @@ -69,7 +69,7 @@ def load_flow_from_json( return Graph.from_payload(graph_data) -def run_flow_from_json( +async def arun_flow_from_json( flow: Path | str | dict, input_value: str, *, @@ -106,17 +106,11 @@ def run_flow_from_json( Returns: List[RunOutputs]: A list of RunOutputs objects representing the results of running the flow. """ - # Set all streaming to false - try: - import nest_asyncio - - nest_asyncio.apply() - except Exception: # noqa: BLE001 - logger.opt(exception=True).warning("Could not apply nest_asyncio") if tweaks is None: tweaks = {} tweaks["stream"] = False - graph = load_flow_from_json( + graph = await asyncio.to_thread( + load_flow_from_json, flow=flow, tweaks=tweaks, log_level=log_level, @@ -125,7 +119,7 @@ def run_flow_from_json( cache=cache, disable_logs=disable_logs, ) - return run_graph( + return await run_graph( graph=graph, session_id=session_id, input_value=input_value, @@ -134,3 +128,43 @@ def run_flow_from_json( output_component=output_component, fallback_to_env_vars=fallback_to_env_vars, ) + + +def run_flow_from_json( + flow: Path | str | dict, + input_value: str, + *, + session_id: str | None = None, + tweaks: dict | None = None, + input_type: str = "chat", + output_type: str = "chat", + output_component: str | None = None, + log_level: str | None = None, + log_file: str | None = None, + env_file: str | None = None, + cache: str | None = None, + disable_logs: bool | None = True, + fallback_to_env_vars: bool = False, +) -> list[RunOutputs]: + coro = arun_flow_from_json( + flow, + input_value, + session_id=session_id, + tweaks=tweaks, + input_type=input_type, + output_type=output_type, + output_component=output_component, + log_level=log_level, + log_file=log_file, + env_file=env_file, + cache=cache, + disable_logs=disable_logs, + fallback_to_env_vars=fallback_to_env_vars, + ) + + try: + loop = asyncio.get_running_loop() + except RuntimeError: + return asyncio.run(coro) + + return loop.run_until_complete(coro) diff --git a/src/backend/base/langflow/main.py b/src/backend/base/langflow/main.py index 507ac180d2f..d3f5ae981e7 100644 --- a/src/backend/base/langflow/main.py +++ b/src/backend/base/langflow/main.py @@ -8,7 +8,6 @@ from pathlib import Path from urllib.parse import urlencode -import nest_asyncio from fastapi import FastAPI, HTTPException, Request, Response, status from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, JSONResponse @@ -87,28 +86,25 @@ async def dispatch(self, request: Request, call_next): return response -telemetry_service_tasks = set() - - def get_lifespan(*, fix_migration=False, version=None): + def _initialize(): + initialize_services(fix_migration=fix_migration) + setup_llm_caching() + initialize_super_user_if_needed() + @asynccontextmanager async def lifespan(_app: FastAPI): - nest_asyncio.apply() # Startup message if version: rprint(f"[bold green]Starting Langflow v{version}...[/bold green]") else: rprint("[bold green]Starting Langflow...[/bold green]") try: - initialize_services(fix_migration=fix_migration) - setup_llm_caching() - initialize_super_user_if_needed() - task = asyncio.create_task(get_and_cache_all_types_dict(get_settings_service())) - await create_or_update_starter_projects(task) - telemetry_service_task = asyncio.create_task(get_telemetry_service().start()) - telemetry_service_tasks.add(telemetry_service_task) - telemetry_service_task.add_done_callback(telemetry_service_tasks.discard) - load_flows_from_directory() + await asyncio.to_thread(_initialize) + all_types_dict = await get_and_cache_all_types_dict(get_settings_service()) + await asyncio.to_thread(create_or_update_starter_projects, all_types_dict) + get_telemetry_service().start() + await asyncio.to_thread(load_flows_from_directory) yield except Exception as exc: if "langflow migration --fix" not in str(exc): diff --git a/src/backend/base/langflow/processing/process.py b/src/backend/base/langflow/processing/process.py index c8130302a88..07be9e4d5b0 100644 --- a/src/backend/base/langflow/processing/process.py +++ b/src/backend/base/langflow/processing/process.py @@ -58,7 +58,7 @@ async def run_graph_internal( return run_outputs, session_id_str -def run_graph( +async def run_graph( graph: Graph, input_value: str, input_type: str, @@ -104,9 +104,9 @@ def run_graph( components.append(input_value_request.components or []) inputs_list.append({INPUT_FIELD_NAME: input_value_request.input_value}) types.append(input_value_request.type) - return graph.run( + return await graph.arun( inputs_list, - input_components=components, + inputs_components=components, types=types, outputs=outputs or [], stream=False, diff --git a/src/backend/base/langflow/services/cache/base.py b/src/backend/base/langflow/services/cache/base.py index 7e1fcee0359..7444767834d 100644 --- a/src/backend/base/langflow/services/cache/base.py +++ b/src/backend/base/langflow/services/cache/base.py @@ -23,7 +23,7 @@ def get(self, key, lock: LockType | None = None): lock: A lock to use for the operation. Returns: - The value associated with the key, or None if the key is not found. + The value associated with the key, or CACHE_MISS if the key is not found. """ @abc.abstractmethod @@ -59,6 +59,17 @@ def delete(self, key, lock: LockType | None = None): def clear(self, lock: LockType | None = None): """Clear all items from the cache.""" + @abc.abstractmethod + def contains(self, key) -> bool: + """Check if the key is in the cache. + + Args: + key: The key of the item to check. + + Returns: + True if the key is in the cache, False otherwise. + """ + @abc.abstractmethod def __contains__(self, key) -> bool: """Check if the key is in the cache. @@ -110,7 +121,7 @@ async def get(self, key, lock: AsyncLockType | None = None): lock: A lock to use for the operation. Returns: - The value associated with the key, or None if the key is not found. + The value associated with the key, or CACHE_MISS if the key is not found. """ @abc.abstractmethod @@ -147,7 +158,7 @@ async def clear(self, lock: AsyncLockType | None = None): """Clear all items from the cache.""" @abc.abstractmethod - def __contains__(self, key) -> bool: + async def contains(self, key) -> bool: """Check if the key is in the cache. Args: diff --git a/src/backend/base/langflow/services/cache/disk.py b/src/backend/base/langflow/services/cache/disk.py index ed3412e327f..7b9eff33854 100644 --- a/src/backend/base/langflow/services/cache/disk.py +++ b/src/backend/base/langflow/services/cache/disk.py @@ -26,18 +26,18 @@ def __init__(self, cache_dir, max_size=None, expiration_time=3600) -> None: async def get(self, key, lock: asyncio.Lock | None = None): if not lock: async with self.lock: - return await self._get(key) + return await asyncio.to_thread(self._get, key) else: - return await self._get(key) + return await asyncio.to_thread(self._get, key) - async def _get(self, key): - item = await asyncio.to_thread(self.cache.get, key, default=None) + def _get(self, key): + item = self.cache.get(key, default=None) if item: if time.time() - item["time"] < self.expiration_time: - await asyncio.to_thread(self.cache.touch, key) # Refresh the expiry time + self.cache.touch(key) # Refresh the expiry time return pickle.loads(item["value"]) if isinstance(item["value"], bytes) else item["value"] logger.info(f"Cache item for key '{key}' has expired and will be deleted.") - await self._delete(key) # Log before deleting the expired item + self.cache.delete(key) # Log before deleting the expired item return CACHE_MISS async def set(self, key, value, lock: asyncio.Lock | None = None) -> None: @@ -81,14 +81,14 @@ async def upsert(self, key, value, lock: asyncio.Lock | None = None) -> None: await self._upsert(key, value) async def _upsert(self, key, value) -> None: - existing_value = await self.get(key) + existing_value = await asyncio.to_thread(self._get, key) if existing_value is not CACHE_MISS and isinstance(existing_value, dict) and isinstance(value, dict): existing_value.update(value) value = existing_value await self.set(key, value) - def __contains__(self, key) -> bool: - return asyncio.run(asyncio.to_thread(self.cache.__contains__, key)) + async def contains(self, key) -> bool: + return await asyncio.to_thread(self.cache.__contains__, key) async def teardown(self) -> None: # Clean up the cache directory diff --git a/src/backend/base/langflow/services/cache/service.py b/src/backend/base/langflow/services/cache/service.py index baec3dbd402..f917be6a57a 100644 --- a/src/backend/base/langflow/services/cache/service.py +++ b/src/backend/base/langflow/services/cache/service.py @@ -56,7 +56,7 @@ def get(self, key, lock: Union[threading.Lock, None] = None): # noqa: UP007 lock: A lock to use for the operation. Returns: - The value associated with the key, or None if the key is not found or the item has expired. + The value associated with the key, or CACHE_MISS if the key is not found or the item has expired. """ with lock or self._lock: return self._get_without_lock(key) @@ -70,7 +70,7 @@ def _get_without_lock(self, key): # Check if the value is pickled return pickle.loads(item["value"]) if isinstance(item["value"], bytes) else item["value"] self.delete(key) - return None + return CACHE_MISS def set(self, key, value, lock: Union[threading.Lock, None] = None) -> None: # noqa: UP007 """Add an item to the cache. @@ -105,7 +105,7 @@ def upsert(self, key, value, lock: Union[threading.Lock, None] = None) -> None: """ with lock or self._lock: existing_value = self._get_without_lock(key) - if existing_value is not None and isinstance(existing_value, dict) and isinstance(value, dict): + if existing_value is not CACHE_MISS and isinstance(existing_value, dict) and isinstance(value, dict): existing_value.update(value) value = existing_value @@ -139,10 +139,14 @@ def clear(self, lock: Union[threading.Lock, None] = None) -> None: # noqa: UP00 with lock or self._lock: self._cache.clear() - def __contains__(self, key) -> bool: + def contains(self, key) -> bool: """Check if the key is in the cache.""" return key in self._cache + def __contains__(self, key) -> bool: + """Check if the key is in the cache.""" + return self.contains(key) + def __getitem__(self, key): """Retrieve an item from the cache using the square bracket notation.""" return self.get(key) @@ -229,9 +233,9 @@ def is_connected(self) -> bool: @override async def get(self, key, lock=None): if key is None: - return None + return CACHE_MISS value = await self._client.get(str(key)) - return pickle.loads(value) if value else None + return pickle.loads(value) if value else CACHE_MISS @override async def set(self, key, value, lock=None) -> None: @@ -274,11 +278,11 @@ async def clear(self, lock=None) -> None: """Clear all items from the cache.""" await self._client.flushdb() - def __contains__(self, key) -> bool: + async def contains(self, key) -> bool: """Check if the key is in the cache.""" if key is None: return False - return bool(asyncio.run(self._client.exists(str(key)))) + return bool(await self._client.exists(str(key))) def __repr__(self) -> str: """Return a string representation of the RedisCache instance.""" @@ -364,5 +368,5 @@ async def _upsert(self, key, value) -> None: value = existing_value await self.set(key, value) - def __contains__(self, key) -> bool: + async def contains(self, key) -> bool: return key in self.cache diff --git a/src/backend/base/langflow/services/chat/service.py b/src/backend/base/langflow/services/chat/service.py index b6db599378f..2f73578eac9 100644 --- a/src/backend/base/langflow/services/chat/service.py +++ b/src/backend/base/langflow/services/chat/service.py @@ -4,7 +4,7 @@ from typing import Any from langflow.services.base import Service -from langflow.services.cache.base import AsyncBaseCacheService +from langflow.services.cache.base import AsyncBaseCacheService, CacheService from langflow.services.deps import get_cache_service @@ -16,60 +16,7 @@ class ChatService(Service): def __init__(self) -> None: self.async_cache_locks: dict[str, asyncio.Lock] = defaultdict(asyncio.Lock) self._sync_cache_locks: dict[str, RLock] = defaultdict(RLock) - self.cache_service = get_cache_service() - - def _get_lock(self, key: str): - """Retrieves the lock associated with the given key. - - Args: - key (str): The key to retrieve the lock for. - - Returns: - threading.Lock or asyncio.Lock: The lock associated with the given key. - """ - if isinstance(self.cache_service, AsyncBaseCacheService): - return self.async_cache_locks[key] - return self._sync_cache_locks[key] - - async def _perform_cache_operation( - self, operation: str, key: str, data: Any = None, lock: asyncio.Lock | None = None - ): - """Perform a cache operation based on the given operation type. - - Args: - operation (str): The type of cache operation to perform. Possible values are "upsert", "get", or "delete". - key (str): The key associated with the cache operation. - data (Any, optional): The data to be stored in the cache. Only applicable for "upsert" operation. - Defaults to None. - lock (Optional[asyncio.Lock], optional): The lock to be used for the cache operation. Defaults to None. - - Returns: - Any: The result of the cache operation. Only applicable for "get" operation. - - Raises: - None - - """ - lock = lock or self._get_lock(key) - if isinstance(self.cache_service, AsyncBaseCacheService): - if operation == "upsert": - await self.cache_service.upsert(str(key), data, lock=lock) - return None - if operation == "get": - return await self.cache_service.get(key, lock=lock) - if operation == "delete": - await self.cache_service.delete(key, lock=lock) - return None - return None - if operation == "upsert": - self.cache_service.upsert(str(key), data, lock=lock) - return None - if operation == "get": - return self.cache_service.get(key, lock=lock) - if operation == "delete": - self.cache_service.delete(key, lock=lock) - return None - return None + self.cache_service: CacheService | AsyncBaseCacheService = get_cache_service() async def set_cache(self, key: str, data: Any, lock: asyncio.Lock | None = None) -> bool: """Set the cache for a client. @@ -86,7 +33,12 @@ async def set_cache(self, key: str, data: Any, lock: asyncio.Lock | None = None) "result": data, "type": type(data), } - await self._perform_cache_operation("upsert", key, result_dict, lock) + if isinstance(self.cache_service, AsyncBaseCacheService): + await self.cache_service.upsert(str(key), result_dict, lock=lock or self.async_cache_locks[key]) + return await self.cache_service.contains(key) + await asyncio.to_thread( + self.cache_service.upsert, str(key), result_dict, lock=lock or self._sync_cache_locks[key] + ) return key in self.cache_service async def get_cache(self, key: str, lock: asyncio.Lock | None = None) -> Any: @@ -99,7 +51,9 @@ async def get_cache(self, key: str, lock: asyncio.Lock | None = None) -> Any: Returns: Any: The cached data. """ - return await self._perform_cache_operation("get", key, lock=lock or self._get_lock(key)) + if isinstance(self.cache_service, AsyncBaseCacheService): + return await self.cache_service.get(key, lock=lock or self.async_cache_locks[key]) + return await asyncio.to_thread(self.cache_service.get, key, lock=lock or self._sync_cache_locks[key]) async def clear_cache(self, key: str, lock: asyncio.Lock | None = None) -> None: """Clear the cache for a client. @@ -108,4 +62,6 @@ async def clear_cache(self, key: str, lock: asyncio.Lock | None = None) -> None: key (str): The cache key. lock (Optional[asyncio.Lock], optional): The lock to use for the cache operation. Defaults to None. """ - await self._perform_cache_operation("delete", key, lock=lock or self._get_lock(key)) + if isinstance(self.cache_service, AsyncBaseCacheService): + return await self.cache_service.delete(key, lock=lock or self.async_cache_locks[key]) + return await asyncio.to_thread(self.cache_service.delete, key, lock=lock or self._sync_cache_locks[key]) diff --git a/src/backend/base/langflow/services/deps.py b/src/backend/base/langflow/services/deps.py index 01bd841d5dc..40152a0175b 100644 --- a/src/backend/base/langflow/services/deps.py +++ b/src/backend/base/langflow/services/deps.py @@ -12,7 +12,7 @@ from sqlmodel import Session - from langflow.services.cache.service import CacheService + from langflow.services.cache.service import AsyncBaseCacheService, CacheService from langflow.services.chat.service import ChatService from langflow.services.database.service import DatabaseService from langflow.services.plugins.service import PluginService @@ -188,7 +188,7 @@ def session_scope() -> Generator[Session, None, None]: raise -def get_cache_service() -> CacheService: +def get_cache_service() -> CacheService | AsyncBaseCacheService: """Retrieves the cache service from the service manager. Returns: diff --git a/src/backend/base/langflow/services/manager.py b/src/backend/base/langflow/services/manager.py index 458c7a66ea3..8c66f1ae853 100644 --- a/src/backend/base/langflow/services/manager.py +++ b/src/backend/base/langflow/services/manager.py @@ -1,6 +1,5 @@ from __future__ import annotations -import asyncio import importlib import inspect from typing import TYPE_CHECKING @@ -95,9 +94,7 @@ async def teardown(self) -> None: continue logger.debug(f"Teardown service {service.name}") try: - result = service.teardown() - if asyncio.iscoroutine(result): - await result + await service.teardown() except Exception as exc: # noqa: BLE001 logger.exception(exc) self.services = {} diff --git a/src/backend/base/langflow/services/session/service.py b/src/backend/base/langflow/services/session/service.py index abd4e630a78..420ef9778f5 100644 --- a/src/backend/base/langflow/services/session/service.py +++ b/src/backend/base/langflow/services/session/service.py @@ -1,7 +1,9 @@ -from collections.abc import Coroutine +import asyncio from typing import TYPE_CHECKING from langflow.services.base import Service +from langflow.services.cache.base import AsyncBaseCacheService +from langflow.services.cache.utils import CacheMiss from langflow.services.session.utils import compute_dict_hash, session_id_generator if TYPE_CHECKING: @@ -12,20 +14,21 @@ class SessionService(Service): name = "session_service" def __init__(self, cache_service) -> None: - self.cache_service: CacheService = cache_service + self.cache_service: CacheService | AsyncBaseCacheService = cache_service async def load_session(self, key, flow_id: str, data_graph: dict | None = None): # Check if the data is cached - if key in self.cache_service: - result = self.cache_service.get(key) - if isinstance(result, Coroutine): - result = await result - return result + if isinstance(self.cache_service, AsyncBaseCacheService): + value = await self.cache_service.get(key) + else: + value = await asyncio.to_thread(self.cache_service.get, key) + if not isinstance(value, CacheMiss): + return value if key is None: key = self.generate_key(session_id=None, data_graph=data_graph) if data_graph is None: - return (None, None) + return None, None # If not cached, build the graph and cache it from langflow.graph.graph.base import Graph @@ -47,13 +50,13 @@ def generate_key(self, session_id, data_graph): return self.build_key(session_id, data_graph=data_graph) async def update_session(self, session_id, value) -> None: - result = self.cache_service.set(session_id, value) - # if it is a coroutine, await it - if isinstance(result, Coroutine): - await result + if isinstance(self.cache_service, AsyncBaseCacheService): + await self.cache_service.set(session_id, value) + else: + await asyncio.to_thread(self.cache_service.set, session_id, value) async def clear_session(self, session_id) -> None: - result = self.cache_service.delete(session_id) - # if it is a coroutine, await it - if isinstance(result, Coroutine): - await result + if isinstance(self.cache_service, AsyncBaseCacheService): + await self.cache_service.delete(session_id) + else: + await asyncio.to_thread(self.cache_service.delete, session_id) diff --git a/src/backend/base/langflow/services/socket/service.py b/src/backend/base/langflow/services/socket/service.py index d4ec60579fb..8f6e4400085 100644 --- a/src/backend/base/langflow/services/socket/service.py +++ b/src/backend/base/langflow/services/socket/service.py @@ -1,20 +1,18 @@ -from typing import TYPE_CHECKING, Any +from typing import Any import socketio from loguru import logger from langflow.services.base import Service +from langflow.services.cache.base import AsyncBaseCacheService, CacheService from langflow.services.deps import get_chat_service from langflow.services.socket.utils import build_vertex, get_vertices -if TYPE_CHECKING: - from langflow.services.cache.service import CacheService - class SocketIOService(Service): name = "socket_service" - def __init__(self, cache_service: "CacheService"): + def __init__(self, cache_service: CacheService | AsyncBaseCacheService): self.cache_service = cache_service def init(self, sio: socketio.AsyncServer) -> None: @@ -63,11 +61,14 @@ async def on_build_vertex(self, sid, flow_id, vertex_id) -> None: set_cache=self.set_cache, ) - def get_cache(self, sid: str) -> Any: + async def get_cache(self, sid: str) -> Any: """Get the cache for a client.""" - return self.cache_service.get(sid) + value = self.cache_service.get(sid) + if isinstance(self.cache_service, AsyncBaseCacheService): + return await value + return value - def set_cache(self, sid: str, build_result: Any) -> bool: + async def set_cache(self, sid: str, build_result: Any) -> bool: """Set the cache for a client.""" # client_id is the flow id but that already exists in the cache # so we need to change it to something else @@ -76,5 +77,8 @@ def set_cache(self, sid: str, build_result: Any) -> bool: "result": build_result, "type": type(build_result), } - self.cache_service.upsert(sid, result_dict) + result = self.cache_service.upsert(sid, result_dict) + if isinstance(self.cache_service, AsyncBaseCacheService): + await result + return await self.cache_service.contains(sid) return sid in self.cache_service diff --git a/src/backend/base/langflow/services/socket/utils.py b/src/backend/base/langflow/services/socket/utils.py index d754638014c..723acd48dd5 100644 --- a/src/backend/base/langflow/services/socket/utils.py +++ b/src/backend/base/langflow/services/socket/utils.py @@ -50,7 +50,7 @@ async def build_vertex( set_cache: Callable, ) -> None: try: - cache = get_cache(flow_id) + cache = await get_cache(flow_id) graph = cache.get("result") if not isinstance(graph, Graph): @@ -86,7 +86,7 @@ async def build_vertex( valid = False result_dict = ResultDataResponse(results={}) artifacts = {} - set_cache(flow_id, graph) + await set_cache(flow_id, graph) log_vertex_build( flow_id=flow_id, vertex_id=vertex_id, diff --git a/src/backend/base/langflow/services/telemetry/service.py b/src/backend/base/langflow/services/telemetry/service.py index cb396981f5b..0c43cee96ce 100644 --- a/src/backend/base/langflow/services/telemetry/service.py +++ b/src/backend/base/langflow/services/telemetry/service.py @@ -1,7 +1,6 @@ from __future__ import annotations import asyncio -import contextlib import os import platform from datetime import datetime, timezone @@ -112,7 +111,7 @@ async def log_package_playground(self, payload: PlaygroundPayload) -> None: async def log_package_component(self, payload: ComponentPayload) -> None: await self._queue_event((self.send_telemetry_data, payload, "component")) - async def start(self) -> None: + def start(self) -> None: if self.running or self.do_not_track: return try: @@ -131,6 +130,15 @@ async def flush(self) -> None: except Exception: # noqa: BLE001 logger.exception("Error flushing logs") + async def _cancel_task(self, task: asyncio.Task, cancel_msg: str) -> None: + task.cancel(cancel_msg) + try: + await task + except asyncio.CancelledError: + current_task = asyncio.current_task() + if current_task and current_task.cancelling() > 0: + raise + async def stop(self) -> None: if self.do_not_track or self._stopping: return @@ -140,9 +148,9 @@ async def stop(self) -> None: await self.flush() self.running = False if self.worker_task: - self.worker_task.cancel() - with contextlib.suppress(asyncio.CancelledError): - await self.worker_task + await self._cancel_task(self.worker_task, "Cancel telemetry worker task") + if self.log_package_version_task: + await self._cancel_task(self.log_package_version_task, "Cancel telemetry log package version task") await self.client.aclose() except Exception: # noqa: BLE001 logger.exception("Error stopping tracing service") diff --git a/src/backend/tests/.test_durations b/src/backend/tests/.test_durations index d87ab9d19ea..0057ca8cb73 100644 --- a/src/backend/tests/.test_durations +++ b/src/backend/tests/.test_durations @@ -61,23 +61,39 @@ "src/backend/tests/test_webhook.py::test_webhook_endpoint": 8.848518459000388, "src/backend/tests/test_webhook.py::test_webhook_flow_on_run_endpoint": 4.675444458000584, "src/backend/tests/test_webhook.py::test_webhook_with_random_payload": 5.161753501000476, - "src/backend/tests/unit/api/test_api_utils.py::test_get_outdated_components": 2.8084017080254853, - "src/backend/tests/unit/api/test_api_utils.py::test_get_suggestion_message": 5.118682334024925, - "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable": 3.0856673330417834, + "src/backend/tests/unit/api/test_api_utils.py::test_get_outdated_components": 0.000613333992077969, + "src/backend/tests/unit/api/test_api_utils.py::test_get_suggestion_message": 0.0010302500013494864, + "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable": 6.459080125001492, "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__Exception": 5.891528583015315, "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__HTTPException": 2.8841335409670137, + "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__exception": 3.036611793009797, + "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__httpexception": 2.84643145899463, "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__variable_name_alread_exists": 3.690157334029209, - "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__variable_name_and_value_cannot_be_empty": 2.877465248981025, - "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__variable_name_cannot_be_empty": 3.4102128740632907, - "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__variable_value_cannot_be_empty": 2.7537577909533866, - "src/backend/tests/unit/api/v1/test_variable.py::test_delete_variable": 2.491162959020585, + "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__variable_name_already_exists": 2.858093374001328, + "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__variable_name_and_value_cannot_be_empty": 2.7468443339894293, + "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__variable_name_cannot_be_empty": 2.610517124994658, + "src/backend/tests/unit/api/v1/test_variable.py::test_create_variable__variable_value_cannot_be_empty": 2.439285208005458, + "src/backend/tests/unit/api/v1/test_variable.py::test_delete_variable": 2.6817042070033494, "src/backend/tests/unit/api/v1/test_variable.py::test_delete_variable__Exception": 3.1565893749939278, - "src/backend/tests/unit/api/v1/test_variable.py::test_read_variables": 3.6610397080075927, - "src/backend/tests/unit/api/v1/test_variable.py::test_read_variables__": 3.7497400419670157, - "src/backend/tests/unit/api/v1/test_variable.py::test_read_variables__empty": 3.0430358340381645, - "src/backend/tests/unit/api/v1/test_variable.py::test_update_variable": 2.8299104999750853, + "src/backend/tests/unit/api/v1/test_variable.py::test_delete_variable__exception": 3.1838819590047933, + "src/backend/tests/unit/api/v1/test_variable.py::test_read_variables": 2.054674957995303, + "src/backend/tests/unit/api/v1/test_variable.py::test_read_variables__": 2.762839665010688, + "src/backend/tests/unit/api/v1/test_variable.py::test_read_variables__empty": 6.563063166991924, + "src/backend/tests/unit/api/v1/test_variable.py::test_update_variable": 2.924393498993595, "src/backend/tests/unit/api/v1/test_variable.py::test_update_variable__Exception": 3.202228542009834, + "src/backend/tests/unit/api/v1/test_variable.py::test_update_variable__exception": 2.7966553330188617, + "src/backend/tests/unit/base/load/test_load.py::test_run_flow_from_json_params": 0.0005931660125497729, "src/backend/tests/unit/base/tools/test_component_tool.py::test_component_tool": 0.04467487393412739, + "src/backend/tests/unit/base/tools/test_component_toolkit.py::test_component_tool": 0.004378668003482744, + "src/backend/tests/unit/components/helpers/test_structured_output_component.py::TestStructuredOutputComponent::test_correctly_builds_output_model": 0.008082624975941144, + "src/backend/tests/unit/components/helpers/test_structured_output_component.py::TestStructuredOutputComponent::test_empty_output_schema": 0.0015336249925894663, + "src/backend/tests/unit/components/helpers/test_structured_output_component.py::TestStructuredOutputComponent::test_handles_multiple_outputs": 0.0017795409949030727, + "src/backend/tests/unit/components/helpers/test_structured_output_component.py::TestStructuredOutputComponent::test_invalid_llm_config": 0.0018090419907821342, + "src/backend/tests/unit/components/helpers/test_structured_output_component.py::TestStructuredOutputComponent::test_invalid_output_schema_type": 0.0015345420106314123, + "src/backend/tests/unit/components/helpers/test_structured_output_component.py::TestStructuredOutputComponent::test_large_input_value": 0.0019746669859159738, + "src/backend/tests/unit/components/helpers/test_structured_output_component.py::TestStructuredOutputComponent::test_nested_output_schema": 0.0024899589916458353, + "src/backend/tests/unit/components/helpers/test_structured_output_component.py::TestStructuredOutputComponent::test_raises_value_error_for_unsupported_language_model": 0.0015592920099152252, + "src/backend/tests/unit/components/helpers/test_structured_output_component.py::TestStructuredOutputComponent::test_successful_structured_output_generation_with_patch_with_config": 0.003882082979544066, "src/backend/tests/unit/components/models/test_ChatOllama_component.py::test_build_model": 0.0020211669616401196, "src/backend/tests/unit/components/models/test_ChatOllama_component.py::test_get_model_failure": 0.0068002091138623655, "src/backend/tests/unit/components/models/test_ChatOllama_component.py::test_get_model_success": 0.015780292043928057, @@ -85,439 +101,541 @@ "src/backend/tests/unit/components/models/test_ChatOllama_component.py::test_update_build_config_mirostat_disabled": 0.0013394170091487467, "src/backend/tests/unit/components/models/test_ChatOllama_component.py::test_update_build_config_mirostat_enabled": 0.0016756660188548267, "src/backend/tests/unit/components/models/test_ChatOllama_component.py::test_update_build_config_model_name": 0.0062951669679023325, - "src/backend/tests/unit/components/prompts/test_prompt_component.py::TestPromptComponent::test_post_code_processing": 0.01907300011953339, + "src/backend/tests/unit/components/models/test_chatollama_component.py::test_build_model": 0.002432957000564784, + "src/backend/tests/unit/components/models/test_chatollama_component.py::test_get_model_failure": 0.014791373978368938, + "src/backend/tests/unit/components/models/test_chatollama_component.py::test_get_model_success": 0.017748498998116702, + "src/backend/tests/unit/components/models/test_chatollama_component.py::test_update_build_config_keep_alive": 0.0020072909974260256, + "src/backend/tests/unit/components/models/test_chatollama_component.py::test_update_build_config_mirostat_disabled": 0.0025072919961530715, + "src/backend/tests/unit/components/models/test_chatollama_component.py::test_update_build_config_mirostat_enabled": 0.008608500997070223, + "src/backend/tests/unit/components/models/test_chatollama_component.py::test_update_build_config_model_name": 0.016517708005267195, + "src/backend/tests/unit/components/prompts/test_prompt_component.py::TestPromptComponent::test_post_code_processing": 0.0018564589991001412, + "src/backend/tests/unit/components/prototypes/test_create_data_component.py::test_build_data": 0.0009333339839940891, + "src/backend/tests/unit/components/prototypes/test_create_data_component.py::test_get_data": 0.0005440840031951666, + "src/backend/tests/unit/components/prototypes/test_create_data_component.py::test_update_build_config": 0.0009354579960927367, + "src/backend/tests/unit/components/prototypes/test_create_data_component.py::test_update_build_config_exceed_limit": 0.0007642909768037498, + "src/backend/tests/unit/components/prototypes/test_create_data_component.py::test_validate_text_key_invalid": 0.0048938330000964925, + "src/backend/tests/unit/components/prototypes/test_create_data_component.py::test_validate_text_key_valid": 0.000822375004645437, + "src/backend/tests/unit/components/prototypes/test_update_data_component.py::test_build_data": 0.0024513340031262487, + "src/backend/tests/unit/components/prototypes/test_update_data_component.py::test_get_data": 0.0008190829976228997, + "src/backend/tests/unit/components/prototypes/test_update_data_component.py::test_update_build_config": 0.0012493320100475103, + "src/backend/tests/unit/components/prototypes/test_update_data_component.py::test_update_build_config_exceed_limit": 0.0026447090203873813, + "src/backend/tests/unit/components/prototypes/test_update_data_component.py::test_validate_text_key_invalid": 0.0007135839841794223, + "src/backend/tests/unit/components/prototypes/test_update_data_component.py::test_validate_text_key_valid": 0.000633457995718345, + "src/backend/tests/unit/components/tools/test_python_repl_tool.py::test_python_repl_tool_template": 0.011029833985958248, + "src/backend/tests/unit/components/tools/test_yfinance_tool.py::test_yfinance_tool_template": 0.009737292028148659, "src/backend/tests/unit/custom/component/test_component_to_tool.py::test_component_to_tool": 0.019733334018383175, - "src/backend/tests/unit/custom/custom_component/test_component.py::test_set_component": 0.002436416980344802, - "src/backend/tests/unit/custom/custom_component/test_component.py::test_set_invalid_output": 0.0008760430500842631, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_accessing_non_registered_callback": 0.0003252510214224458, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_accessing_non_registered_event_callback_with_recommended_fix": 0.002420708944555372, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_accessing_registered_event_callback": 0.00048387504648417234, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_event_id_uniqueness_with_await": 0.014276833040639758, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_handling_large_number_of_events": 0.0026765000075101852, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_performance_impact_frequent_registrations": 0.0014237920404411852, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_queue_receives_correct_event_data_format": 0.004706457955762744, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_register_event_with_empty_name": 0.00039654201827943325, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_register_event_with_invalid_name_fixed": 0.0022748749470338225, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_register_event_with_valid_name_and_callback_with_mock_callback": 0.00042608199873939157, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_register_event_with_valid_name_and_no_callback": 0.0002793750027194619, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_register_event_without_event_type_argument_fixed": 0.0004962499951943755, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_sending_event_with_complex_data": 0.015433623921126127, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_sending_event_with_none_data": 0.0003140009939670563, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_sending_event_with_valid_type_and_data_asyncio_plugin": 0.004175833077169955, - "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_thread_safety_accessing_events_dictionary": 0.002862458990421146, - "src/backend/tests/unit/exceptions/test_api.py::test_api_exception": 5.165536917047575, - "src/backend/tests/unit/exceptions/test_api.py::test_api_exception_no_flow": 3.1444325000047684, - "src/backend/tests/unit/graph/edge/test_edge_base.py::test_edge_raises_error_on_invalid_target_handle": 0.01743029203498736, - "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_model_and_assign_values_fails": 0.003823124978225678, - "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_model_with_fields_from_kwargs": 0.0009365829755552113, - "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_model_with_invalid_callable": 0.00458308303495869, - "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_model_with_valid_return_type_annotations": 0.0014327499084174633, - "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_with_multiple_components": 0.003126667987089604, - "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_with_pydantic_field": 0.002614291966892779, - "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_default_model_name_to_state": 0.0033909990452229977, - "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_graph_functional_start_state_update": 0.38284662598744035, - "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_handle_empty_kwargs_gracefully": 0.0026077500078827143, + "src/backend/tests/unit/custom/component/test_component_to_tool.py::test_component_to_tool_has_no_component_as_tool": 0.006827708013588563, + "src/backend/tests/unit/custom/component/test_component_to_tool.py::test_component_to_toolkit": 0.004191375002847053, + "src/backend/tests/unit/custom/component/test_componet_set_functionality.py::test_set_with_message_text_input_list": 0.00036783299583476037, + "src/backend/tests/unit/custom/component/test_componet_set_functionality.py::test_set_with_mixed_list_input": 0.001263248996110633, + "src/backend/tests/unit/custom/custom_component/test_component.py::test_set_component": 0.0025628319999668747, + "src/backend/tests/unit/custom/custom_component/test_component.py::test_set_invalid_output": 0.001577167000505142, + "src/backend/tests/unit/custom/custom_component/test_component.py::test_set_required_inputs": 0.0007535420154454187, + "src/backend/tests/unit/custom/custom_component/test_component.py::test_set_required_inputs_various_components": 0.005984875999274664, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_accessing_non_registered_callback": 0.00027879200933966786, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_accessing_non_registered_event_callback_with_recommended_fix": 0.0009840830025495961, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_accessing_registered_event_callback": 0.004149834014242515, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_event_id_uniqueness_with_await": 0.000860916989040561, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_handling_large_number_of_events": 0.0012658749910769984, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_performance_impact_frequent_registrations": 0.0014525000005960464, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_queue_receives_correct_event_data_format": 0.004754083027364686, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_register_event_with_empty_name": 0.00027983300969935954, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_register_event_with_invalid_name_fixed": 0.0009289159934269264, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_register_event_with_valid_name_and_callback_with_mock_callback": 0.00038820799090899527, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_register_event_with_valid_name_and_no_callback": 0.00035366599331609905, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_register_event_without_event_type_argument_fixed": 0.00039058399852365255, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_sending_event_with_complex_data": 0.003258166994783096, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_sending_event_with_none_data": 0.000255541002843529, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_sending_event_with_valid_type_and_data_asyncio_plugin": 0.0021823329880135134, + "src/backend/tests/unit/events/test_event_manager.py::TestEventManager::test_thread_safety_accessing_events_dictionary": 0.0013184999988880008, + "src/backend/tests/unit/exceptions/test_api.py::test_api_exception": 0.0014990839990787208, + "src/backend/tests/unit/exceptions/test_api.py::test_api_exception_no_flow": 0.0002953749935841188, + "src/backend/tests/unit/graph/edge/test_edge_base.py::test_edge_raises_error_on_invalid_target_handle": 0.02953520698065404, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_model_and_assign_values_fails": 0.0014010409940965474, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_model_with_fields_from_kwargs": 0.002258125998196192, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_model_with_invalid_callable": 0.0003716670034918934, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_model_with_valid_return_type_annotations": 0.0018959159933729097, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_with_multiple_components": 0.0025144999963231385, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_create_with_pydantic_field": 0.0013957919873064384, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_default_model_name_to_state": 0.0006555419968208298, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_graph_functional_start_state_update": 0.021681665995856747, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_handle_empty_kwargs_gracefully": 0.0004747079947264865, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_raise_typeerror_for_invalid_field_type_in_tuple": 0.0004467510007089004, "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_raise_valueerror_for_invalid_field_type_in_tuple": 0.00342700001783669, - "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_raise_valueerror_for_unsupported_value_types": 0.001125834067352116, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph": 0.0028614590410143137, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional": 0.002848291944246739, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_async_start": 0.00851887499447912, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_start": 0.006822708004619926, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_start_end": 0.31068545795278624, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_not_prepared": 0.003954542044084519, + "src/backend/tests/unit/graph/graph/state/test_state_model.py::TestCreateStateModel::test_raise_valueerror_for_unsupported_value_types": 0.0003228330024285242, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph": 0.007216000012704171, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional": 0.027125124004669487, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_async_start": 0.007453917001839727, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_start": 0.006435709015931934, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_start_end": 0.00986600000760518, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_not_prepared": 0.004827625016332604, "src/backend/tests/unit/graph/graph/test_base.py::test_graph_set_with_invalid_component": 0.0009155830484814942, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_set_with_valid_component": 0.0013159169466234744, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_with_edge": 0.0030836249934509397, - "src/backend/tests/unit/graph/graph/test_callback_graph.py::test_callback_graph": 0.004803291987627745, - "src/backend/tests/unit/graph/graph/test_cycles.py::test_cycle_in_graph": 0.020792666997294873, - "src/backend/tests/unit/graph/graph/test_cycles.py::test_cycle_in_graph_max_iterations": 0.0154116649646312, - "src/backend/tests/unit/graph/graph/test_graph_state_model.py::test_graph_functional_start_graph_state_update": 0.014615208026953042, - "src/backend/tests/unit/graph/graph/test_graph_state_model.py::test_graph_state_model": 0.019434958929196, - "src/backend/tests/unit/graph/graph/test_graph_state_model.py::test_graph_state_model_json_schema": 0.00020879099611192942, - "src/backend/tests/unit/graph/graph/test_graph_state_model.py::test_graph_state_model_serialization": 0.30240879103075713, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_add_to_vertices_being_run": 0.0002751240390352905, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_are_all_predecessors_fulfilled": 0.0003926260396838188, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_are_all_predecessors_fulfilled__wrong": 0.00041854201117530465, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_build_run_map": 0.0003674989566206932, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict": 0.00048262596828863025, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_run_map__bad_case": 0.00037833303213119507, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_run_predecessors__bad_case": 0.0004080839571543038, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_vertices_being_run__bad_case": 0.00028962595388293266, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_vertices_to_run__bad_case": 0.00034329190384596586, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable": 0.0006714990013279021, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_is_active": 0.00042558403220027685, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_run_predecessors": 0.0013484579976648092, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_vertices_to_run": 0.0008156669791787863, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_pickle": 0.0026231249794363976, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_remove_from_predecessors": 0.0008892920450307429, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_remove_vertex_from_runnables": 0.0002857920480892062, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_to_dict": 0.00039079098496586084, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_run_state": 0.0026797510217875242, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_vertex_run_state": 0.0002868340234272182, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_vertex_run_state__bad_case": 0.0003107090014964342, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_detects_cycles_in_simple_graph": 0.00028504099464043975, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_disconnected_components": 0.0005705829826183617, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_duplicate_edges": 0.0002827919670380652, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_identifies_multiple_cycles": 0.0006248340359888971, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_large_graphs_efficiency": 0.0006118330638855696, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_mixed_data_types_in_edges": 0.0002896260120905936, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_multiple_edges_between_same_nodes": 0.00031320902053266764, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_no_cycles_present": 0.0005720839835703373, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_nodes_with_no_incoming_edges": 0.0009030828950926661, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_nodes_with_no_outgoing_edges": 0.0002746249665506184, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_self_loops": 0.0006457920535467565, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_single_node_no_edges": 0.00040495797293260694, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_detects_cycle_in_simple_graph": 0.0002679579774849117, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_disconnected_components": 0.00047562498366460204, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_duplicate_edges": 0.0003092490369454026, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_empty_edges_list": 0.0005007500294595957, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_identifies_first_cycle": 0.00026495999190956354, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_large_graph_efficiency": 0.0015956249553710222, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_multiple_cycles": 0.0002763339434750378, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_multiple_edges_between_same_nodes": 0.000241084024310112, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_nodes_with_no_outgoing_edges": 0.0016497920732945204, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_returns_none_when_no_cycle": 0.00026058393996208906, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_self_loop_cycle": 0.0002930000191554427, - "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_single_node_no_edges": 0.0005835830233991146, - "src/backend/tests/unit/graph/graph/test_utils.py::test_get_successors_a": 0.0002936659730039537, - "src/backend/tests/unit/graph/graph/test_utils.py::test_get_successors_z": 0.0003981670015491545, - "src/backend/tests/unit/graph/graph/test_utils.py::test_has_cycle": 0.00029833399457857013, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_a": 0.0004433330032043159, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_g": 0.0002774589229375124, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_h": 0.00027199997566640377, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_invalid_vertex": 0.0033761669765226543, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_m": 0.00028033298440277576, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_n_is_start": 0.0017444159602746367, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_t": 0.00028278998797759414, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_x": 0.0003054169355891645, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_z": 0.0003299160161986947, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_set_with_valid_component": 0.00021208298858255148, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_with_edge": 0.007440209010383114, + "src/backend/tests/unit/graph/graph/test_callback_graph.py::test_callback_graph": 0.005031000007875264, + "src/backend/tests/unit/graph/graph/test_cycles.py::test_cycle_in_graph": 0.014751875976799056, + "src/backend/tests/unit/graph/graph/test_cycles.py::test_cycle_in_graph_max_iterations": 0.010304541006917134, + "src/backend/tests/unit/graph/graph/test_cycles.py::test_that_outputs_cache_is_set_to_false_in_cycle": 0.008758458992815576, + "src/backend/tests/unit/graph/graph/test_graph_state_model.py::test_graph_functional_start_graph_state_update": 0.02893937600310892, + "src/backend/tests/unit/graph/graph/test_graph_state_model.py::test_graph_state_model": 0.022187833004863933, + "src/backend/tests/unit/graph/graph/test_graph_state_model.py::test_graph_state_model_json_schema": 0.00014870801533106714, + "src/backend/tests/unit/graph/graph/test_graph_state_model.py::test_graph_state_model_serialization": 0.0091743749944726, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_add_to_vertices_being_run": 0.0002863750123651698, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_are_all_predecessors_fulfilled": 0.0016801239835331216, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_are_all_predecessors_fulfilled__wrong": 0.0003180409985361621, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_build_run_map": 0.0003421669971430674, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict": 0.000252666010055691, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_run_map__bad_case": 0.00032991799525916576, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_run_predecessors__bad_case": 0.0002584999747341499, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_vertices_being_run__bad_case": 0.0002657489967532456, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_vertices_to_run__bad_case": 0.00031679200765211135, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable": 0.00030975100526120514, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_is_active": 0.00029416699544526637, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_run_predecessors": 0.0002981260040542111, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_vertices_to_run": 0.00027700100326910615, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_pickle": 0.0004706249892478809, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_remove_from_predecessors": 0.00030187499942258, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_remove_vertex_from_runnables": 0.0003077080036746338, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_to_dict": 0.00030195899307727814, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_run_state": 0.0009988340025302023, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_vertex_run_state": 0.0005493749922607094, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_vertex_run_state__bad_case": 0.0009784170251805335, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_detects_cycles_in_simple_graph": 0.0002591660158941522, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_disconnected_components": 0.00024408299941569567, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_duplicate_edges": 0.00032054202165454626, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_identifies_multiple_cycles": 0.0003361249982845038, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_large_graphs_efficiency": 0.0006353759963531047, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_mixed_data_types_in_edges": 0.0004426649829838425, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_multiple_edges_between_same_nodes": 0.00023762500495649874, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_no_cycles_present": 0.00045362499076873064, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_nodes_with_no_incoming_edges": 0.00028583398670889437, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_nodes_with_no_outgoing_edges": 0.00022262497805058956, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_self_loops": 0.0002409169974271208, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindAllCycleEdges::test_single_node_no_edges": 0.0002518740075174719, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_detects_cycle_in_simple_graph": 0.00027979098376818, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_disconnected_components": 0.0005520419799722731, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_duplicate_edges": 0.00024108400975819677, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_empty_edges_list": 0.0005072490021120757, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_identifies_first_cycle": 0.00026816700119525194, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_large_graph_efficiency": 0.0009095829882426187, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_multiple_cycles": 0.00021704200480598956, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_multiple_edges_between_same_nodes": 0.0002363330131629482, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_nodes_with_no_outgoing_edges": 0.0002569590142229572, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_returns_none_when_no_cycle": 0.00137962399458047, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_self_loop_cycle": 0.000247417003265582, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleEdge::test_single_node_no_edges": 0.0004968340072082356, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_correctly_identify_and_return_vertices_in_single_cycle": 0.0003238339995732531, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_detect_cycles_simple_graph": 0.0009623739897506312, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_duplicate_edges_fixed_fixed": 0.0003650419967016205, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_empty_edges": 0.00026845700631383806, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_large_graphs_efficiently": 0.00037829198117833585, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_no_outgoing_edges": 0.00032545800786465406, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_self_loops": 0.0004827920056413859, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_single_cycle": 0.0004672919894801453, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_two_inputs_in_cycle[0]": 0.0003818330151261762, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_two_inputs_in_cycle[1]": 0.0005016250070184469, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_two_inputs_in_cycle[2]": 0.000504001000081189, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_two_inputs_in_cycle[3]": 0.0010670000046957284, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_handle_two_inputs_in_cycle[4]": 0.00037191798037383705, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_no_cycles_empty_list": 0.0002690419933060184, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_no_modification_of_input_edges_list": 0.00038037498597986996, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_non_string_vertex_ids": 0.00039325098623521626, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_process_disconnected_components": 0.00048516599053982645, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_return_vertices_involved_in_multiple_cycles": 0.000401416007662192, + "src/backend/tests/unit/graph/graph/test_utils.py::TestFindCycleVertices::test_single_vertex_no_edges": 0.00025691698829177767, + "src/backend/tests/unit/graph/graph/test_utils.py::test_get_successors_a": 0.00031374899845104665, + "src/backend/tests/unit/graph/graph/test_utils.py::test_get_successors_z": 0.00026387599064037204, + "src/backend/tests/unit/graph/graph/test_utils.py::test_has_cycle": 0.00024429100449196994, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_a": 0.00032716698478907347, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_g": 0.0035800429905066267, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_h": 0.0020107919990550727, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_invalid_vertex": 0.00041204200533684343, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_m": 0.0013786249910481274, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_n_is_start": 0.0003265839914092794, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_t": 0.00029279298905748874, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_x": 0.000256291008554399, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_z": 0.0002878749946830794, "src/backend/tests/unit/graph/test_graph.py::test_build_edges": 0.001086625037714839, "src/backend/tests/unit/graph/test_graph.py::test_build_nodes": 0.0012113330303691328, "src/backend/tests/unit/graph/test_graph.py::test_build_params": 0.00745550001738593, "src/backend/tests/unit/graph/test_graph.py::test_circular_dependencies": 0.0011518750106915832, - "src/backend/tests/unit/graph/test_graph.py::test_find_last_node": 0.0008732929709367454, + "src/backend/tests/unit/graph/test_graph.py::test_find_last_node": 0.0008025429997360334, "src/backend/tests/unit/graph/test_graph.py::test_get_node": 3.6276886249543168, "src/backend/tests/unit/graph/test_graph.py::test_get_node_neighbors_basic": 0.0015942919999361038, "src/backend/tests/unit/graph/test_graph.py::test_get_root_vertex": 0.00336533400695771, "src/backend/tests/unit/graph/test_graph.py::test_get_vertices_with_target": 0.0015001240535639226, "src/backend/tests/unit/graph/test_graph.py::test_graph_structure": 3.660518125980161, - "src/backend/tests/unit/graph/test_graph.py::test_invalid_node_types": 0.0033974149846471846, + "src/backend/tests/unit/graph/test_graph.py::test_invalid_node_types": 0.014299875008873641, "src/backend/tests/unit/graph/test_graph.py::test_matched_type": 0.0011828330461867154, "src/backend/tests/unit/graph/test_graph.py::test_pickle_graph": 0.025576499931048602, - "src/backend/tests/unit/graph/test_graph.py::test_process_flow": 0.0011497500236146152, - "src/backend/tests/unit/graph/test_graph.py::test_process_flow_one_group": 0.0018960839952342212, - "src/backend/tests/unit/graph/test_graph.py::test_process_flow_vector_store_grouped": 0.005742707988247275, - "src/backend/tests/unit/graph/test_graph.py::test_set_new_target_handle": 0.0002932510105893016, - "src/backend/tests/unit/graph/test_graph.py::test_ungroup_node": 0.0010475010494701564, - "src/backend/tests/unit/graph/test_graph.py::test_update_source_handle": 0.000287000962998718, - "src/backend/tests/unit/graph/test_graph.py::test_update_target_handle_proxy": 0.0003938330337405205, - "src/backend/tests/unit/graph/test_graph.py::test_update_template": 0.00037104199873283505, + "src/backend/tests/unit/graph/test_graph.py::test_process_flow": 0.001123667010688223, + "src/backend/tests/unit/graph/test_graph.py::test_process_flow_one_group": 0.002319873994565569, + "src/backend/tests/unit/graph/test_graph.py::test_process_flow_vector_store_grouped": 0.002715042981435545, + "src/backend/tests/unit/graph/test_graph.py::test_serialize_graph": 0.03731829300522804, + "src/backend/tests/unit/graph/test_graph.py::test_set_new_target_handle": 0.0002179180009989068, + "src/backend/tests/unit/graph/test_graph.py::test_ungroup_node": 0.0009937919967342168, + "src/backend/tests/unit/graph/test_graph.py::test_update_source_handle": 0.00021954097610432655, + "src/backend/tests/unit/graph/test_graph.py::test_update_target_handle_proxy": 0.00023383200459647924, + "src/backend/tests/unit/graph/test_graph.py::test_update_template": 0.0006267920107347891, "src/backend/tests/unit/graph/test_graph.py::test_validate_edges": 0.0010510420543141663, - "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot": 2.745265916979406, - "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot_dump_components_and_edges": 2.7694530828739516, - "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot_dump_structure": 2.6900041250046343, - "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag": 0.09931745805079117, - "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_add": 0.046793167013674974, - "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_dump": 0.015337915974669158, - "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_dump_components_and_edges": 0.3383241659612395, - "src/backend/tests/unit/inputs/test_inputs.py::test_bool_input_invalid": 0.0002828749711625278, - "src/backend/tests/unit/inputs/test_inputs.py::test_bool_input_valid": 0.0003182090003974736, - "src/backend/tests/unit/inputs/test_inputs.py::test_data_input_valid": 0.00039245799416676164, - "src/backend/tests/unit/inputs/test_inputs.py::test_dict_input_invalid": 0.00027891702484339476, - "src/backend/tests/unit/inputs/test_inputs.py::test_dict_input_valid": 0.0002850420423783362, - "src/backend/tests/unit/inputs/test_inputs.py::test_dropdown_input_invalid": 0.00026658392744138837, - "src/backend/tests/unit/inputs/test_inputs.py::test_dropdown_input_valid": 0.0002708340180106461, - "src/backend/tests/unit/inputs/test_inputs.py::test_file_input_valid": 0.0003541659680195153, - "src/backend/tests/unit/inputs/test_inputs.py::test_float_input_invalid": 0.001792833034414798, - "src/backend/tests/unit/inputs/test_inputs.py::test_float_input_valid": 0.00028895906871184707, - "src/backend/tests/unit/inputs/test_inputs.py::test_handle_input_invalid": 0.00028858205769211054, - "src/backend/tests/unit/inputs/test_inputs.py::test_handle_input_valid": 0.0002573339152149856, - "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_comprehensive": 0.0004499579663388431, - "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_invalid": 0.0004248740151524544, - "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_valid": 0.0003302929690107703, - "src/backend/tests/unit/inputs/test_inputs.py::test_int_input_invalid": 0.00048291601706296206, - "src/backend/tests/unit/inputs/test_inputs.py::test_int_input_valid": 0.00032804306829348207, - "src/backend/tests/unit/inputs/test_inputs.py::test_message_text_input_invalid": 0.0003255010233260691, - "src/backend/tests/unit/inputs/test_inputs.py::test_message_text_input_valid": 0.0005122079746797681, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_input_invalid": 0.0003666250850073993, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_input_valid": 0.0003393329679965973, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_secret_input_invalid": 0.0013413759879767895, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_secret_input_valid": 0.0009220420033670962, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiselect_input_invalid": 0.00031925097573548555, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiselect_input_valid": 0.0004229999613016844, - "src/backend/tests/unit/inputs/test_inputs.py::test_nested_dict_input_invalid": 0.0005164990434423089, - "src/backend/tests/unit/inputs/test_inputs.py::test_nested_dict_input_valid": 0.0006655830075033009, - "src/backend/tests/unit/inputs/test_inputs.py::test_prompt_input_valid": 0.0003657920169644058, - "src/backend/tests/unit/inputs/test_inputs.py::test_secret_str_input_invalid": 0.00035770906833931804, - "src/backend/tests/unit/inputs/test_inputs.py::test_secret_str_input_valid": 0.0007077500340528786, - "src/backend/tests/unit/inputs/test_inputs.py::test_str_input_invalid": 0.0011320829507894814, - "src/backend/tests/unit/inputs/test_inputs.py::test_str_input_valid": 0.0003163329674862325, - "src/backend/tests/unit/inputs/test_inputs.py::test_table_input_invalid": 0.0003552910056896508, - "src/backend/tests/unit/inputs/test_inputs.py::test_table_input_valid": 0.00033895799424499273, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_complex_nested_structures_handling": 0.002644082938786596, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_default_values_assignment": 0.0005547509645111859, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_default_values_for_non_required_fields": 0.0005426649586297572, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_empty_list_of_inputs": 0.0004508750280365348, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_field_types_conversion": 0.000591459043789655, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_fields_creation_with_correct_types_and_attributes": 0.0010405409848317504, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_correctly_accesses_descriptions_recommended_fix": 0.0006468749779742211, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_create_model_from_valid_schema": 0.0009404990123584867, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_handle_empty_schema": 0.0004204170254524797, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_handle_large_schemas_efficiently": 0.0006854169914731756, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_handles_multiple_fields_fixed_with_instance_check": 0.000743875018088147, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_manages_unknown_field_types": 0.00032945799466688186, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_nested_list_and_dict_types_handling": 0.0008280000038212165, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_no_duplicate_field_names_fixed_fixed": 0.0013690840132767335, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_process_schema_missing_optional_keys_updated": 0.001131124998209998, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_raises_error_for_invalid_input_different_exception_with_specific_exception": 0.00028408200887497514, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_returns_valid_model_class": 0.001225708008860238, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_schema_fields_with_none_default": 0.0006880419823573902, + "src/backend/tests/unit/helpers/test_base_model_from_schema.py::TestBuildModelFromSchema::test_supports_single_and_multiple_type_annotations": 0.0009820420091273263, + "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot": 0.03888849999930244, + "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot_dump_components_and_edges": 0.013488709009834565, + "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot_dump_structure": 0.012505375008913688, + "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag": 0.15770391499972902, + "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_add": 0.09183391700207721, + "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_dump": 0.051857166006811894, + "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_dump_components_and_edges": 0.05073120800079778, + "src/backend/tests/unit/inputs/test_inputs.py::test_bool_input_invalid": 0.00042454199865460396, + "src/backend/tests/unit/inputs/test_inputs.py::test_bool_input_valid": 0.0006222910160431638, + "src/backend/tests/unit/inputs/test_inputs.py::test_code_input_valid": 0.0002798320056172088, + "src/backend/tests/unit/inputs/test_inputs.py::test_data_input_valid": 0.0003379589907126501, + "src/backend/tests/unit/inputs/test_inputs.py::test_dict_input_invalid": 0.00028512599237728864, + "src/backend/tests/unit/inputs/test_inputs.py::test_dict_input_valid": 0.0002797069901134819, + "src/backend/tests/unit/inputs/test_inputs.py::test_dropdown_input_invalid": 0.00023374997545033693, + "src/backend/tests/unit/inputs/test_inputs.py::test_dropdown_input_valid": 0.0002424179983790964, + "src/backend/tests/unit/inputs/test_inputs.py::test_file_input_valid": 0.0003238749923184514, + "src/backend/tests/unit/inputs/test_inputs.py::test_float_input_invalid": 0.00027679000049829483, + "src/backend/tests/unit/inputs/test_inputs.py::test_float_input_valid": 0.0002347080153413117, + "src/backend/tests/unit/inputs/test_inputs.py::test_handle_input_invalid": 0.0003875840047840029, + "src/backend/tests/unit/inputs/test_inputs.py::test_handle_input_valid": 0.0003749999887077138, + "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_comprehensive": 0.0004925010143779218, + "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_invalid": 0.0005071670020697638, + "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_valid": 0.00023137500102166086, + "src/backend/tests/unit/inputs/test_inputs.py::test_int_input_invalid": 0.00023924899869598448, + "src/backend/tests/unit/inputs/test_inputs.py::test_int_input_valid": 0.0002771660074358806, + "src/backend/tests/unit/inputs/test_inputs.py::test_message_text_input_invalid": 0.0002607509959489107, + "src/backend/tests/unit/inputs/test_inputs.py::test_message_text_input_valid": 0.00033233298745471984, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_input_invalid": 0.00028641699464060366, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_input_valid": 0.00025187499704770744, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_secret_input_invalid": 0.0003962079936172813, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_secret_input_valid": 0.0007522089872509241, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiselect_input_invalid": 0.0005655839922837913, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiselect_input_valid": 0.000393084017559886, + "src/backend/tests/unit/inputs/test_inputs.py::test_nested_dict_input_invalid": 0.0005445830029202625, + "src/backend/tests/unit/inputs/test_inputs.py::test_nested_dict_input_valid": 0.00025858302251435816, + "src/backend/tests/unit/inputs/test_inputs.py::test_prompt_input_valid": 0.00031541698263026774, + "src/backend/tests/unit/inputs/test_inputs.py::test_secret_str_input_invalid": 0.0003497080033412203, + "src/backend/tests/unit/inputs/test_inputs.py::test_secret_str_input_valid": 0.00026641700242180377, + "src/backend/tests/unit/inputs/test_inputs.py::test_str_input_invalid": 0.0002740829950198531, + "src/backend/tests/unit/inputs/test_inputs.py::test_str_input_valid": 0.00025004199414979666, + "src/backend/tests/unit/inputs/test_inputs.py::test_table_input_invalid": 0.00029595798696391284, + "src/backend/tests/unit/inputs/test_inputs.py::test_table_input_valid": 0.00044508300197776407, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_complex_nested_structures_handling": 0.0009323329868493602, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_default_values_assignment": 0.0007065010140649974, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_default_values_for_non_required_fields": 0.0011114159860881045, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_empty_list_of_inputs": 0.0004522090021055192, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_field_types_conversion": 0.000897249992704019, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_fields_creation_with_correct_types_and_attributes": 0.0014027920115040615, "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_invalid_field_types_handling": 0.0005195839912630618, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_is_list_attribute_processing": 0.0009998760651797056, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_is_list_handling": 0.0007378349546343088, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_missing_attributes_handling": 0.0005290409899316728, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_missing_optional_attributes": 0.0005764180677942932, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_mixed_required_optional_fields_processing": 0.0008241240284405649, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_multiple_input_types": 0.0010676659876480699, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_non_standard_field_types_handling": 0.0006119169993326068, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_none_default_value_handling": 0.0007848759996704757, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_options_attribute_processing": 0.0009355409420095384, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_options_handling": 0.0017233749967999756, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_passing_input_type_directly": 0.0006622910150326788, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_schema_model_creation": 0.0006008749478496611, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_single_input_type_conversion": 0.0006804990116506815, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_single_input_type_replica": 0.0006408749613910913, - "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_special_characters_in_names_handling": 0.000593625009059906, - "src/backend/tests/unit/io/test_io_schema.py::test_create_input_schema": 0.002895458950661123, - "src/backend/tests/unit/schema/test_schema_message.py::test_message_async_prompt_serialization": 0.4139872090308927, - "src/backend/tests/unit/schema/test_schema_message.py::test_message_prompt_serialization": 0.003387250064406544, - "src/backend/tests/unit/services/variable/test_service.py::test_create_variable": 0.005690833961125463, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_is_list_attribute_processing": 0.0011251249961787835, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_is_list_handling": 0.0005297500174492598, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_missing_attributes_handling": 0.00054600001021754, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_missing_optional_attributes": 0.000713790999725461, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_mixed_required_optional_fields_processing": 0.0008094989898381755, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_multiple_input_types": 0.0006732500187354162, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_non_standard_field_types_handling": 0.0007644159777555615, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_none_default_value_handling": 0.0005729590047849342, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_options_attribute_processing": 0.0007577919895993546, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_options_handling": 0.0005486660083988681, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_passing_input_type_directly": 0.000253000995144248, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_schema_model_creation": 0.0006212080188561231, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_single_input_type_conversion": 0.0009867499902611598, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_single_input_type_replica": 0.0005574600072577596, + "src/backend/tests/unit/io/test_io_schema.py::TestCreateInputSchema::test_special_characters_in_names_handling": 0.001630875005503185, + "src/backend/tests/unit/io/test_io_schema.py::test_create_input_schema": 0.002724208010477014, + "src/backend/tests/unit/io/test_table_schema.py::TestColumn::test_create_column_with_valid_formatter": 0.00273924799694214, + "src/backend/tests/unit/io/test_table_schema.py::TestColumn::test_create_column_without_display_name": 0.0004777080030180514, + "src/backend/tests/unit/io/test_table_schema.py::TestColumn::test_create_with_type_instead_of_formatter": 0.00023699901066720486, + "src/backend/tests/unit/io/test_table_schema.py::TestColumn::test_default_sortable_filterable": 0.0003669170109787956, + "src/backend/tests/unit/io/test_table_schema.py::TestColumn::test_description_and_default": 0.00022525002714246511, + "src/backend/tests/unit/io/test_table_schema.py::TestColumn::test_formatter_explicitly_set_to_enum": 0.00023816697648726404, + "src/backend/tests/unit/io/test_table_schema.py::TestColumn::test_formatter_none_when_not_provided": 0.0002403760008746758, + "src/backend/tests/unit/io/test_table_schema.py::TestColumn::test_formatter_set_based_on_value": 0.001065042000846006, + "src/backend/tests/unit/io/test_table_schema.py::TestColumn::test_invalid_formatter_raises_value_error": 0.0003882509918184951, + "src/backend/tests/unit/schema/test_schema_message.py::test_message_async_prompt_serialization": 0.00209424999775365, + "src/backend/tests/unit/schema/test_schema_message.py::test_message_prompt_serialization": 0.0012432500079739839, + "src/backend/tests/unit/services/variable/test_service.py::test_create_variable": 0.004593417004798539, "src/backend/tests/unit/services/variable/test_service.py::test_delete_varaible_by_id": 0.0060262500192038715, - "src/backend/tests/unit/services/variable/test_service.py::test_delete_variable": 0.005094875057693571, + "src/backend/tests/unit/services/variable/test_service.py::test_delete_variable": 0.0056892079883255064, "src/backend/tests/unit/services/variable/test_service.py::test_delete_variable__ValueError": 0.0035743750049732625, + "src/backend/tests/unit/services/variable/test_service.py::test_delete_variable__valueerror": 0.008015125000383705, + "src/backend/tests/unit/services/variable/test_service.py::test_delete_variable_by_id": 0.0287587490020087, "src/backend/tests/unit/services/variable/test_service.py::test_delete_variable_by_id__ValueError": 0.27340612601256, - "src/backend/tests/unit/services/variable/test_service.py::test_get_variable": 0.0322585420217365, + "src/backend/tests/unit/services/variable/test_service.py::test_delete_variable_by_id__valueerror": 0.004210916013107635, + "src/backend/tests/unit/services/variable/test_service.py::test_get_variable": 0.007395416992949322, "src/backend/tests/unit/services/variable/test_service.py::test_get_variable__TypeError": 0.00458791694836691, "src/backend/tests/unit/services/variable/test_service.py::test_get_variable__ValueError": 0.003811584028881043, + "src/backend/tests/unit/services/variable/test_service.py::test_get_variable__typeerror": 0.00520645797951147, + "src/backend/tests/unit/services/variable/test_service.py::test_get_variable__valueerror": 0.004982833022950217, + "src/backend/tests/unit/services/variable/test_service.py::test_initialize_user_variables__create_and_update": 0.05702541599748656, "src/backend/tests/unit/services/variable/test_service.py::test_initialize_user_variables__donkey": 0.0002315010060556233, - "src/backend/tests/unit/services/variable/test_service.py::test_initialize_user_variables__not_found_variable": 0.04281383304623887, - "src/backend/tests/unit/services/variable/test_service.py::test_initialize_user_variables__skipping_environment_variable_storage": 0.3182707919040695, - "src/backend/tests/unit/services/variable/test_service.py::test_list_variables": 0.006724000093527138, - "src/backend/tests/unit/services/variable/test_service.py::test_list_variables__empty": 0.004544168012216687, - "src/backend/tests/unit/services/variable/test_service.py::test_update_variable": 0.322743374039419, + "src/backend/tests/unit/services/variable/test_service.py::test_initialize_user_variables__not_found_variable": 0.027411707997089252, + "src/backend/tests/unit/services/variable/test_service.py::test_initialize_user_variables__skipping_environment_variable_storage": 0.007381542003713548, + "src/backend/tests/unit/services/variable/test_service.py::test_list_variables": 0.03627604100620374, + "src/backend/tests/unit/services/variable/test_service.py::test_list_variables__empty": 0.004159414995228872, + "src/backend/tests/unit/services/variable/test_service.py::test_update_variable": 0.006257750006625429, "src/backend/tests/unit/services/variable/test_service.py::test_update_variable__ValueError": 0.0036237920285202563, - "src/backend/tests/unit/services/variable/test_service.py::test_update_variable_fields": 0.005188334034755826, - "src/backend/tests/unit/test_api_key.py::test_create_api_key": 3.842014041962102, - "src/backend/tests/unit/test_api_key.py::test_delete_api_key": 2.8479771240381524, - "src/backend/tests/unit/test_api_key.py::test_get_api_keys": 5.180979499069508, + "src/backend/tests/unit/services/variable/test_service.py::test_update_variable__valueerror": 0.0037017079739598557, + "src/backend/tests/unit/services/variable/test_service.py::test_update_variable_fields": 0.005448331998195499, + "src/backend/tests/unit/test_api_key.py::test_create_api_key": 2.742826249988866, + "src/backend/tests/unit/test_api_key.py::test_delete_api_key": 2.8074388330132933, + "src/backend/tests/unit/test_api_key.py::test_get_api_keys": 6.625337666002451, "src/backend/tests/unit/test_cache.py::test_build_graph": 1.1988659180001378, - "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow": 12.902665957051795, - "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow_from_request_data": 11.015627957996912, - "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow_with_frozen_path": 7.392927417007741, - "src/backend/tests/unit/test_cli.py::test_components_path": 3.066763209120836, - "src/backend/tests/unit/test_cli.py::test_superuser": 2.517449541017413, - "src/backend/tests/unit/test_custom_component.py::test_build_config_field_keys": 0.21923858404625207, - "src/backend/tests/unit/test_custom_component.py::test_build_config_field_value_keys": 0.22493916703388095, - "src/backend/tests/unit/test_custom_component.py::test_build_config_field_values_dict": 0.22050262603443116, - "src/backend/tests/unit/test_custom_component.py::test_build_config_fields_dict": 0.2320227089803666, - "src/backend/tests/unit/test_custom_component.py::test_build_config_has_fields": 0.24524295894661918, - "src/backend/tests/unit/test_custom_component.py::test_build_config_no_code": 0.00026833300944417715, - "src/backend/tests/unit/test_custom_component.py::test_build_config_return_type": 0.339174666965846, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_get_tree": 0.000570459000300616, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_init": 0.0005881660035811365, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_ann_assign": 0.0002855409402400255, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_arg_no_annotation": 0.0003049989463761449, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_arg_with_annotation": 0.0002652510302141309, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_assign": 0.00028533401200547814, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_callable_details_no_args": 0.0002887090086005628, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_classes": 0.0005051260814070702, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_classes_raises": 0.0003716680221259594, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_function_def_init": 0.0003388329641893506, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_function_def_not_init": 0.0011131670325994492, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_functions": 0.0003978340537287295, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_global_vars": 0.0020412079175002873, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_imports_import": 0.00039212603587657213, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_imports_importfrom": 0.0023357909522019327, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_syntax_error": 0.0015684169484302402, - "src/backend/tests/unit/test_custom_component.py::test_component_code_null_error": 0.0003358739777468145, - "src/backend/tests/unit/test_custom_component.py::test_component_get_code_tree": 0.0010483330697752535, - "src/backend/tests/unit/test_custom_component.py::test_component_get_code_tree_syntax_error": 0.000570415984839201, - "src/backend/tests/unit/test_custom_component.py::test_component_get_function_valid": 0.000449958024546504, - "src/backend/tests/unit/test_custom_component.py::test_component_init": 0.00030837394297122955, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_build_not_implemented": 0.00040358398109674454, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_build_template_config": 0.0005803339881822467, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_class_template_validation_no_code": 0.000299291976261884, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_code_tree_syntax_error": 0.0003827909822575748, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function": 0.0003200420760549605, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_args": 0.001189417962450534, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_args_no_args": 0.000778250047005713, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_return_type": 0.0008200830779969692, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_return_type_no_return_type": 0.0005066260346211493, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_valid": 0.00028941791970282793, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_main_class_name": 0.0007971250452101231, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_main_class_name_no_main_class": 0.0003335429937578738, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_init": 0.00028658407973125577, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_multiple_outputs": 0.23506933404132724, + "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow": 7.302524667000398, + "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow_from_request_data": 11.276845540996874, + "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow_with_frozen_path": 6.094088374986313, + "src/backend/tests/unit/test_cli.py::test_components_path": 2.7196251249843044, + "src/backend/tests/unit/test_cli.py::test_superuser": 0.41284320899285376, + "src/backend/tests/unit/test_custom_component.py::test_build_config_field_keys": 0.00034166700788773596, + "src/backend/tests/unit/test_custom_component.py::test_build_config_field_value_keys": 0.00034308299655094743, + "src/backend/tests/unit/test_custom_component.py::test_build_config_field_values_dict": 0.0002791250008158386, + "src/backend/tests/unit/test_custom_component.py::test_build_config_fields_dict": 0.001094749997719191, + "src/backend/tests/unit/test_custom_component.py::test_build_config_has_fields": 0.0002571240038378164, + "src/backend/tests/unit/test_custom_component.py::test_build_config_no_code": 0.000267458992311731, + "src/backend/tests/unit/test_custom_component.py::test_build_config_return_type": 0.0002676250005606562, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_get_tree": 0.0004345829947851598, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_init": 0.0003053749824175611, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_ann_assign": 0.0003006249899044633, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_arg_no_annotation": 0.00026441700174473226, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_arg_with_annotation": 0.0002427909930702299, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_assign": 0.0005063330027041957, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_callable_details_no_args": 0.00026870898727793247, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_classes": 0.0004775419947691262, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_classes_raises": 0.0008237510046456009, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_function_def_init": 0.0004109169967705384, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_function_def_not_init": 0.0004714989918284118, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_functions": 0.000632501018117182, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_global_vars": 0.00030583298939745873, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_imports_import": 0.0003995000006398186, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_imports_importfrom": 0.00031070799741428345, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_syntax_error": 0.002313543009222485, + "src/backend/tests/unit/test_custom_component.py::test_component_code_null_error": 0.0002887090086005628, + "src/backend/tests/unit/test_custom_component.py::test_component_get_code_tree": 0.0031612500024493784, + "src/backend/tests/unit/test_custom_component.py::test_component_get_code_tree_syntax_error": 0.0006448339991038665, + "src/backend/tests/unit/test_custom_component.py::test_component_get_function_valid": 0.0002910000184783712, + "src/backend/tests/unit/test_custom_component.py::test_component_init": 0.0005167909985175356, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_build_not_implemented": 0.0003232910094084218, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_build_template_config": 0.0011781659995904192, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_class_template_validation_no_code": 0.0002872909972211346, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_code_tree_syntax_error": 0.0010349989897804335, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function": 0.0006846679898444563, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_args": 0.0013231259945314378, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_args_no_args": 0.0006097919977037236, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_return_type": 0.0006717090000165626, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_return_type_no_return_type": 0.000703833022271283, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_valid": 0.0005436670035123825, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_main_class_name": 0.0006051230011507869, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_main_class_name_no_main_class": 0.00034770899219438434, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_init": 0.00023249999503605068, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_multiple_outputs": 0.004873125013546087, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_subclass_from_lctoolcomponent": 0.003097583001363091, "src/backend/tests/unit/test_custom_component.py::test_list_flows_flow_objects": 1.981454541994026, "src/backend/tests/unit/test_custom_component.py::test_list_flows_return_type": 0.36947908403817564, - "src/backend/tests/unit/test_custom_component_with_client.py::test_list_flows_flow_objects": 4.498874542012345, - "src/backend/tests/unit/test_data_class.py::test_add_method_for_integers": 0.00033433298813179135, - "src/backend/tests/unit/test_data_class.py::test_add_method_for_strings": 0.0003061249735765159, - "src/backend/tests/unit/test_data_class.py::test_add_method_with_non_overlapping_keys": 0.00044004200026392937, - "src/backend/tests/unit/test_data_class.py::test_conversion_from_document": 0.0002981260186061263, - "src/backend/tests/unit/test_data_class.py::test_conversion_to_document": 0.00037058297311887145, - "src/backend/tests/unit/test_data_class.py::test_custom_attribute_get_set_del": 0.00030108302598819137, - "src/backend/tests/unit/test_data_class.py::test_custom_attribute_setting_and_getting": 0.00029091595206409693, - "src/backend/tests/unit/test_data_class.py::test_data_initialization": 0.0006093750707805157, - "src/backend/tests/unit/test_data_class.py::test_deep_copy": 0.00027412595227360725, - "src/backend/tests/unit/test_data_class.py::test_dir_includes_data_keys": 0.00036174996057525277, - "src/backend/tests/unit/test_data_class.py::test_dir_reflects_attribute_deletion": 0.0003001660224981606, - "src/backend/tests/unit/test_data_class.py::test_get_text_with_empty_data": 0.00027429102919995785, - "src/backend/tests/unit/test_data_class.py::test_get_text_with_none_data": 0.0002610410447232425, - "src/backend/tests/unit/test_data_class.py::test_get_text_with_text_key": 0.0002877499791793525, - "src/backend/tests/unit/test_data_class.py::test_get_text_without_text_key": 0.00039620796451345086, - "src/backend/tests/unit/test_data_class.py::test_str_and_dir_methods": 0.00043566705426201224, - "src/backend/tests/unit/test_data_class.py::test_validate_data_with_extra_keys": 0.0003074160194955766, - "src/backend/tests/unit/test_data_components.py::test_build_with_multiple_urls": 0.015901083999779075, - "src/backend/tests/unit/test_data_components.py::test_directory_component_build_with_multithreading": 0.0019602919346652925, - "src/backend/tests/unit/test_data_components.py::test_directory_without_mocks": 0.08891058299923316, - "src/backend/tests/unit/test_data_components.py::test_failed_request": 0.01442495797527954, - "src/backend/tests/unit/test_data_components.py::test_parse_curl": 0.0009977090521715581, - "src/backend/tests/unit/test_data_components.py::test_successful_get_request": 0.010583875002339482, - "src/backend/tests/unit/test_data_components.py::test_timeout": 0.00837341690203175, - "src/backend/tests/unit/test_data_components.py::test_url_component": 0.24645916698500514, - "src/backend/tests/unit/test_database.py::test_create_flow": 5.127251083904412, - "src/backend/tests/unit/test_database.py::test_create_flow_with_invalid_data": 5.224326541996561, - "src/backend/tests/unit/test_database.py::test_create_flows": 3.537977208965458, - "src/backend/tests/unit/test_database.py::test_delete_flow": 2.998433125030715, - "src/backend/tests/unit/test_database.py::test_delete_flows": 4.002111000008881, - "src/backend/tests/unit/test_database.py::test_delete_flows_with_transaction_and_build": 5.505517375015188, - "src/backend/tests/unit/test_database.py::test_delete_nonexistent_flow": 4.838145291025285, - "src/backend/tests/unit/test_database.py::test_download_file": 3.6044614590355195, - "src/backend/tests/unit/test_database.py::test_get_nonexistent_flow": 3.5974671249859966, - "src/backend/tests/unit/test_database.py::test_load_flows": 2.4904161670128815, + "src/backend/tests/unit/test_custom_component_with_client.py::test_feature_flags_add_toolkit_output": 2.445131874992512, + "src/backend/tests/unit/test_custom_component_with_client.py::test_list_flows_flow_objects": 6.366708082990954, + "src/backend/tests/unit/test_custom_component_with_client.py::test_list_flows_return_type": 6.1017331250041025, + "src/backend/tests/unit/test_data_class.py::test_add_method_for_integers": 0.0008926670125219971, + "src/backend/tests/unit/test_data_class.py::test_add_method_for_strings": 0.0005702919879695401, + "src/backend/tests/unit/test_data_class.py::test_add_method_with_non_overlapping_keys": 0.00077945897646714, + "src/backend/tests/unit/test_data_class.py::test_conversion_from_document": 0.0009000010031741112, + "src/backend/tests/unit/test_data_class.py::test_conversion_to_document": 0.000492624007165432, + "src/backend/tests/unit/test_data_class.py::test_custom_attribute_get_set_del": 0.000315251003485173, + "src/backend/tests/unit/test_data_class.py::test_custom_attribute_setting_and_getting": 0.0006850829959148541, + "src/backend/tests/unit/test_data_class.py::test_data_initialization": 0.0014634580147685483, + "src/backend/tests/unit/test_data_class.py::test_deep_copy": 0.0012726260029012337, + "src/backend/tests/unit/test_data_class.py::test_dir_includes_data_keys": 0.00048404099652543664, + "src/backend/tests/unit/test_data_class.py::test_dir_reflects_attribute_deletion": 0.0002809169964166358, + "src/backend/tests/unit/test_data_class.py::test_get_text_with_empty_data": 0.0002680830075405538, + "src/backend/tests/unit/test_data_class.py::test_get_text_with_none_data": 0.00025637597718741745, + "src/backend/tests/unit/test_data_class.py::test_get_text_with_text_key": 0.0003513750125421211, + "src/backend/tests/unit/test_data_class.py::test_get_text_without_text_key": 0.0004122499958612025, + "src/backend/tests/unit/test_data_class.py::test_str_and_dir_methods": 0.0007797069847583771, + "src/backend/tests/unit/test_data_class.py::test_validate_data_with_extra_keys": 0.0002761679934337735, + "src/backend/tests/unit/test_data_components.py::test_build_with_multiple_urls": 0.025597874977393076, + "src/backend/tests/unit/test_data_components.py::test_directory_component_build_with_multithreading": 0.007956333996844478, + "src/backend/tests/unit/test_data_components.py::test_directory_without_mocks": 0.11932349899143446, + "src/backend/tests/unit/test_data_components.py::test_failed_request": 0.043825083994306624, + "src/backend/tests/unit/test_data_components.py::test_parse_curl": 0.0012990839895792305, + "src/backend/tests/unit/test_data_components.py::test_successful_get_request": 0.022733542005880736, + "src/backend/tests/unit/test_data_components.py::test_timeout": 0.019111124987830408, + "src/backend/tests/unit/test_data_components.py::test_url_component": 0.22873466702003498, + "src/backend/tests/unit/test_database.py::test_create_flow": 6.703404999992927, + "src/backend/tests/unit/test_database.py::test_create_flow_with_invalid_data": 2.73376258200733, + "src/backend/tests/unit/test_database.py::test_create_flows": 6.39252566799405, + "src/backend/tests/unit/test_database.py::test_delete_flow": 2.728197958000237, + "src/backend/tests/unit/test_database.py::test_delete_flows": 6.607315500019467, + "src/backend/tests/unit/test_database.py::test_delete_flows_with_transaction_and_build": 3.976848082995275, + "src/backend/tests/unit/test_database.py::test_delete_folder_with_flows_with_transaction_and_build": 3.8092600429954473, + "src/backend/tests/unit/test_database.py::test_delete_nonexistent_flow": 2.8013894179894123, + "src/backend/tests/unit/test_database.py::test_download_file": 2.715675707993796, + "src/backend/tests/unit/test_database.py::test_get_flows_from_folder_pagination": 2.6655857499863487, + "src/backend/tests/unit/test_database.py::test_get_flows_from_folder_pagination_with_params": 2.913716540992027, + "src/backend/tests/unit/test_database.py::test_get_nonexistent_flow": 2.6375617910089204, + "src/backend/tests/unit/test_database.py::test_load_flows": 2.0784470409998903, "src/backend/tests/unit/test_database.py::test_migrate_transactions": 3.3142859160434455, "src/backend/tests/unit/test_database.py::test_migrate_transactions_no_duckdb": 4.5406213329406455, - "src/backend/tests/unit/test_database.py::test_read_flow": 5.121038749057334, - "src/backend/tests/unit/test_database.py::test_read_flows": 3.660002124030143, - "src/backend/tests/unit/test_database.py::test_read_only_starter_projects": 3.6530590419424698, - "src/backend/tests/unit/test_database.py::test_sqlite_pragmas": 3.192982708045747, - "src/backend/tests/unit/test_database.py::test_update_flow": 3.5030196660081856, - "src/backend/tests/unit/test_database.py::test_update_flow_idempotency": 2.922055457078386, - "src/backend/tests/unit/test_database.py::test_update_nonexistent_flow": 3.74330650101183, - "src/backend/tests/unit/test_database.py::test_upload_file": 2.987421375059057, - "src/backend/tests/unit/test_endpoints.py::test_build_vertex_invalid_flow_id": 3.191926500061527, - "src/backend/tests/unit/test_endpoints.py::test_build_vertex_invalid_vertex_id": 5.576961666985881, - "src/backend/tests/unit/test_endpoints.py::test_get_all": 3.0514157909783535, - "src/backend/tests/unit/test_endpoints.py::test_get_vertices": 5.105153874028474, - "src/backend/tests/unit/test_endpoints.py::test_get_vertices_flow_not_found": 3.7256477079354227, - "src/backend/tests/unit/test_endpoints.py::test_invalid_flow_id": 3.5856346240034327, - "src/backend/tests/unit/test_endpoints.py::test_invalid_prompt": 2.686517208989244, - "src/backend/tests/unit/test_endpoints.py::test_invalid_run_with_input_type_chat": 3.052263376011979, - "src/backend/tests/unit/test_endpoints.py::test_post_validate_code": 3.117296125041321, - "src/backend/tests/unit/test_endpoints.py::test_starter_projects": 2.871985792007763, - "src/backend/tests/unit/test_endpoints.py::test_successful_run_no_payload": 7.301038249977864, - "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_input_type_any": 10.727296584052965, + "src/backend/tests/unit/test_database.py::test_read_flow": 2.868339541993919, + "src/backend/tests/unit/test_database.py::test_read_flows": 2.770004666992463, + "src/backend/tests/unit/test_database.py::test_read_flows_components_only": 3.0139556240319507, + "src/backend/tests/unit/test_database.py::test_read_flows_components_only_paginated": 7.084283374992083, + "src/backend/tests/unit/test_database.py::test_read_flows_custom_page_size": 3.6366116249992047, + "src/backend/tests/unit/test_database.py::test_read_flows_invalid_page": 3.3711534589965595, + "src/backend/tests/unit/test_database.py::test_read_flows_invalid_size": 3.680850126009318, + "src/backend/tests/unit/test_database.py::test_read_flows_no_pagination_params": 3.527583750008489, + "src/backend/tests/unit/test_database.py::test_read_flows_pagination_with_flows": 3.3972291670070263, + "src/backend/tests/unit/test_database.py::test_read_flows_pagination_with_params": 2.730520750017604, + "src/backend/tests/unit/test_database.py::test_read_flows_pagination_without_params": 2.8355551669956185, + "src/backend/tests/unit/test_database.py::test_read_folder": 2.7866254580003442, + "src/backend/tests/unit/test_database.py::test_read_folder_with_component_filter": 2.7470500000054017, + "src/backend/tests/unit/test_database.py::test_read_folder_with_flows": 3.0035516250209184, + "src/backend/tests/unit/test_database.py::test_read_folder_with_pagination": 2.671652207020088, + "src/backend/tests/unit/test_database.py::test_read_folder_with_search": 6.826817126013339, + "src/backend/tests/unit/test_database.py::test_read_nonexistent_folder": 2.4916570399946067, + "src/backend/tests/unit/test_database.py::test_read_only_starter_projects": 2.7018470829934813, + "src/backend/tests/unit/test_database.py::test_sqlite_pragmas": 6.04876904199773, + "src/backend/tests/unit/test_database.py::test_update_flow": 2.9653350829757983, + "src/backend/tests/unit/test_database.py::test_update_flow_idempotency": 6.656303208990721, + "src/backend/tests/unit/test_database.py::test_update_nonexistent_flow": 2.5573415829858277, + "src/backend/tests/unit/test_database.py::test_upload_file": 2.797283834006521, + "src/backend/tests/unit/test_endpoints.py::test_build_vertex_invalid_flow_id": 2.9302696250088047, + "src/backend/tests/unit/test_endpoints.py::test_build_vertex_invalid_vertex_id": 5.0664555830007885, + "src/backend/tests/unit/test_endpoints.py::test_get_all": 2.803566291986499, + "src/backend/tests/unit/test_endpoints.py::test_get_vertices": 4.591521250011283, + "src/backend/tests/unit/test_endpoints.py::test_get_vertices_flow_not_found": 2.915230041995528, + "src/backend/tests/unit/test_endpoints.py::test_invalid_flow_id": 2.053172583007836, + "src/backend/tests/unit/test_endpoints.py::test_invalid_prompt": 2.2492985830031103, + "src/backend/tests/unit/test_endpoints.py::test_invalid_run_with_input_type_chat": 2.204873708978994, + "src/backend/tests/unit/test_endpoints.py::test_post_validate_code": 2.2824806649878155, + "src/backend/tests/unit/test_endpoints.py::test_starter_projects": 2.8886309579975205, + "src/backend/tests/unit/test_endpoints.py::test_successful_run_no_payload": 3.3445361249760026, + "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_input_type_any": 2.349476457995479, "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_input_type_chat": 6.699964084022213, - "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_input_type_text": 6.435781583015341, - "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_output_type_any": 2.60208841599524, - "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_output_type_debug": 7.752158374991268, - "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_output_type_text": 15.427432750933804, - "src/backend/tests/unit/test_endpoints.py::test_valid_prompt": 2.756689875095617, - "src/backend/tests/unit/test_endpoints.py::test_various_prompts[The weather is {weather} today.-expected_input_variables1]": 2.8440902908914723, - "src/backend/tests/unit/test_endpoints.py::test_various_prompts[This prompt has no variables.-expected_input_variables2]": 3.201118874945678, - "src/backend/tests/unit/test_endpoints.py::test_various_prompts[{a}, {b}, and {c} are variables.-expected_input_variables3]": 2.482491874950938, - "src/backend/tests/unit/test_endpoints.py::test_various_prompts[{color} is my favorite color.-expected_input_variables0]": 2.9541310409549624, - "src/backend/tests/unit/test_experimental_components.py::test_python_function_component": 0.0005039580864831805, - "src/backend/tests/unit/test_files.py::test_delete_file": 2.8763427079538815, - "src/backend/tests/unit/test_files.py::test_download_file": 3.294751874054782, - "src/backend/tests/unit/test_files.py::test_file_operations": 2.8885202910169028, - "src/backend/tests/unit/test_files.py::test_list_files": 3.0035152910277247, - "src/backend/tests/unit/test_files.py::test_upload_file": 3.253328792983666, - "src/backend/tests/unit/test_frontend_nodes.py::test_frontend_node_to_dict": 0.0030099580180831254, - "src/backend/tests/unit/test_frontend_nodes.py::test_template_field_defaults": 0.00038308301009237766, - "src/backend/tests/unit/test_frontend_nodes.py::test_template_to_dict": 0.0011379160569049418, - "src/backend/tests/unit/test_helper_components.py::test_data_as_text_component": 0.0020189170027151704, - "src/backend/tests/unit/test_helper_components.py::test_uuid_generator_component": 0.005951832048594952, - "src/backend/tests/unit/test_initial_setup.py::test_create_or_update_starter_projects": 2.4876928760204464, - "src/backend/tests/unit/test_initial_setup.py::test_get_project_data": 2.4432279990869574, - "src/backend/tests/unit/test_initial_setup.py::test_load_starter_projects": 2.868139374011662, - "src/backend/tests/unit/test_initial_setup.py::test_refresh_starter_projects": 5.7177311679697596, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_create_secret": 1.9582560420385562, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_delete_secret": 2.389675415935926, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_email_address": 2.3270480830105953, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_encode_string": 2.155345457023941, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_encode_uuid": 2.7869142910349183, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_ends_with_non_alphanumeric": 1.9956887899897993, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_get_secret": 2.3893265000660904, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_long_string": 2.5059052080614492, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_starts_with_non_alphanumeric": 2.6645933760446496, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_uuid_case_insensitivity": 2.3025795420981012, + "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_input_type_text": 2.700779123988468, + "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_output_type_any": 2.3004004169924883, + "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_output_type_debug": 3.672783749992959, + "src/backend/tests/unit/test_endpoints.py::test_successful_run_with_output_type_text": 2.6621182080125436, + "src/backend/tests/unit/test_endpoints.py::test_valid_prompt": 5.999147291993722, + "src/backend/tests/unit/test_endpoints.py::test_various_prompts[The weather is {weather} today.-expected_input_variables1]": 2.1486775009980192, + "src/backend/tests/unit/test_endpoints.py::test_various_prompts[This prompt has no variables.-expected_input_variables2]": 2.0907279579841997, + "src/backend/tests/unit/test_endpoints.py::test_various_prompts[{a}, {b}, and {c} are variables.-expected_input_variables3]": 2.3965142930101138, + "src/backend/tests/unit/test_endpoints.py::test_various_prompts[{color} is my favorite color.-expected_input_variables0]": 1.988381125003798, + "src/backend/tests/unit/test_experimental_components.py::test_python_function_component": 0.002817584987496957, + "src/backend/tests/unit/test_files.py::test_delete_file": 4.913786668010289, + "src/backend/tests/unit/test_files.py::test_download_file": 3.8658866670011776, + "src/backend/tests/unit/test_files.py::test_file_operations": 3.1674791669938713, + "src/backend/tests/unit/test_files.py::test_list_files": 3.314151166996453, + "src/backend/tests/unit/test_files.py::test_upload_file": 5.353316916982294, + "src/backend/tests/unit/test_frontend_nodes.py::test_frontend_node_to_dict": 0.0011902500118594617, + "src/backend/tests/unit/test_frontend_nodes.py::test_template_field_defaults": 0.00040308300231117755, + "src/backend/tests/unit/test_frontend_nodes.py::test_template_to_dict": 0.0005205829947954044, + "src/backend/tests/unit/test_helper_components.py::test_data_as_text_component": 0.0009841659921221435, + "src/backend/tests/unit/test_helper_components.py::test_uuid_generator_component": 0.003648749989224598, + "src/backend/tests/unit/test_initial_setup.py::test_create_or_update_starter_projects": 2.4345767510094447, + "src/backend/tests/unit/test_initial_setup.py::test_get_project_data": 0.0335117919894401, + "src/backend/tests/unit/test_initial_setup.py::test_load_starter_projects": 0.012142167004640214, + "src/backend/tests/unit/test_initial_setup.py::test_refresh_starter_projects": 5.426571749994764, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_create_secret": 0.002603082000860013, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_delete_secret": 0.001129208001657389, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_email_address": 0.00032104300044011325, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_encode_string": 0.0005734579899581149, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_encode_uuid": 0.000326291992678307, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_ends_with_non_alphanumeric": 0.00022949899721425027, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_get_secret": 0.0030760820081923157, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_long_string": 0.0019510830024955794, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_starts_with_non_alphanumeric": 0.00026229101058561355, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_uuid_case_insensitivity": 0.0006137930031400174, "src/backend/tests/unit/test_loading.py::test_load_flow_from_json": 1.2976477909833193, - "src/backend/tests/unit/test_loading.py::test_load_flow_from_json_object": 0.008532750012818724, + "src/backend/tests/unit/test_loading.py::test_load_flow_from_json_object": 0.05305879100342281, "src/backend/tests/unit/test_loading.py::test_load_flow_from_json_with_tweaks": 0.005636290996335447, - "src/backend/tests/unit/test_logger.py::test_enabled": 0.00033791596069931984, - "src/backend/tests/unit/test_logger.py::test_get_after_timestamp": 0.00033074902603402734, - "src/backend/tests/unit/test_logger.py::test_get_before_timestamp": 0.0003540000761859119, - "src/backend/tests/unit/test_logger.py::test_get_last_n": 0.0010117499623447657, - "src/backend/tests/unit/test_logger.py::test_init_default": 0.0012091670650988817, - "src/backend/tests/unit/test_logger.py::test_init_with_env_variable": 0.0006678340141661465, - "src/backend/tests/unit/test_logger.py::test_len": 0.0004001249326393008, - "src/backend/tests/unit/test_logger.py::test_max_size": 0.00028612592723220587, - "src/backend/tests/unit/test_logger.py::test_write": 0.00045541703002527356, - "src/backend/tests/unit/test_logger.py::test_write_overflow": 0.00032337504671886563, - "src/backend/tests/unit/test_login.py::test_login_successful": 2.795972707972396, - "src/backend/tests/unit/test_login.py::test_login_unsuccessful_wrong_password": 2.452121749985963, - "src/backend/tests/unit/test_login.py::test_login_unsuccessful_wrong_username": 2.353854291024618, - "src/backend/tests/unit/test_messages.py::test_add_messages": 1.966487625031732, - "src/backend/tests/unit/test_messages.py::test_add_messagetables": 2.361863334022928, - "src/backend/tests/unit/test_messages.py::test_convert_to_langchain[convert_to_langchain_type]": 2.4220113739720546, - "src/backend/tests/unit/test_messages.py::test_convert_to_langchain[message]": 2.848500917025376, - "src/backend/tests/unit/test_messages.py::test_delete_messages": 2.3732009579543956, - "src/backend/tests/unit/test_messages.py::test_get_messages": 2.8447752079810016, - "src/backend/tests/unit/test_messages.py::test_store_message": 2.08129950100556, + "src/backend/tests/unit/test_logger.py::test_enabled": 0.0009046249761013314, + "src/backend/tests/unit/test_logger.py::test_get_after_timestamp": 0.0010347500065108761, + "src/backend/tests/unit/test_logger.py::test_get_before_timestamp": 0.0007574170012958348, + "src/backend/tests/unit/test_logger.py::test_get_last_n": 0.00028300099074840546, + "src/backend/tests/unit/test_logger.py::test_init_default": 0.0002609999937703833, + "src/backend/tests/unit/test_logger.py::test_init_with_env_variable": 0.0006650000141235068, + "src/backend/tests/unit/test_logger.py::test_len": 0.0002499170077499002, + "src/backend/tests/unit/test_logger.py::test_max_size": 0.0003244580002501607, + "src/backend/tests/unit/test_logger.py::test_write": 0.0003003750025527552, + "src/backend/tests/unit/test_logger.py::test_write_overflow": 0.0002600839943625033, + "src/backend/tests/unit/test_login.py::test_login_successful": 2.902008124990971, + "src/backend/tests/unit/test_login.py::test_login_unsuccessful_wrong_password": 2.7796240000025136, + "src/backend/tests/unit/test_login.py::test_login_unsuccessful_wrong_username": 2.493468248998397, + "src/backend/tests/unit/test_messages.py::test_add_messages": 1.6197930010093842, + "src/backend/tests/unit/test_messages.py::test_add_messagetables": 2.3668873319984414, + "src/backend/tests/unit/test_messages.py::test_convert_to_langchain[convert_to_langchain_type]": 0.0003520840109558776, + "src/backend/tests/unit/test_messages.py::test_convert_to_langchain[message]": 0.0006598339969059452, + "src/backend/tests/unit/test_messages.py::test_delete_messages": 2.3060955419932725, + "src/backend/tests/unit/test_messages.py::test_get_messages": 1.8678351669950644, + "src/backend/tests/unit/test_messages.py::test_store_message": 2.1468691249756375, "src/backend/tests/unit/test_messages_endpoints.py::test_delete_messages": 3.083023541024886, "src/backend/tests/unit/test_messages_endpoints.py::test_delete_messages_session": 2.9022462490247563, + "src/backend/tests/unit/test_messages_endpoints.py::test_no_messages_found_with_given_session_id": 2.5029219170100987, + "src/backend/tests/unit/test_messages_endpoints.py::test_successfully_update_session_id": 3.395022416996653, "src/backend/tests/unit/test_messages_endpoints.py::test_update_message": 2.7309321249485947, "src/backend/tests/unit/test_messages_endpoints.py::test_update_message_not_found": 2.71192433295073, - "src/backend/tests/unit/test_process.py::test_load_langchain_object_with_cached_session": 2.4839380000485107, + "src/backend/tests/unit/test_process.py::test_load_langchain_object_with_cached_session": 0.00957645900780335, "src/backend/tests/unit/test_process.py::test_load_langchain_object_with_no_cached_session": 2.9178847920848057, "src/backend/tests/unit/test_process.py::test_load_langchain_object_without_session_id": 2.8941064990358427, - "src/backend/tests/unit/test_process.py::test_multiple_tweaks": 2.5230258330120705, - "src/backend/tests/unit/test_process.py::test_no_tweaks": 2.4845038329949602, - "src/backend/tests/unit/test_process.py::test_single_tweak": 2.4502264168695547, - "src/backend/tests/unit/test_process.py::test_tweak_no_node_id": 2.7300714169978164, - "src/backend/tests/unit/test_process.py::test_tweak_not_in_template": 3.3537612090585753, - "src/backend/tests/unit/test_schema.py::TestInput::test_field_type_str": 0.0005948750185780227, - "src/backend/tests/unit/test_schema.py::TestInput::test_field_type_type": 0.00042970903450623155, - "src/backend/tests/unit/test_schema.py::TestInput::test_input_to_dict": 0.0010768340434879065, - "src/backend/tests/unit/test_schema.py::TestInput::test_invalid_field_type": 0.0010708340560086071, - "src/backend/tests/unit/test_schema.py::TestInput::test_post_process_type_function": 0.0012357080704532564, - "src/backend/tests/unit/test_schema.py::TestInput::test_serialize_field_type": 0.00046479201409965754, - "src/backend/tests/unit/test_schema.py::TestInput::test_validate_type_class": 0.0006184999947436154, - "src/backend/tests/unit/test_schema.py::TestInput::test_validate_type_string": 0.00040066702058538795, - "src/backend/tests/unit/test_schema.py::TestOutput::test_output_add_types": 0.0004017909523099661, - "src/backend/tests/unit/test_schema.py::TestOutput::test_output_default": 0.00041379104368388653, - "src/backend/tests/unit/test_schema.py::TestOutput::test_output_set_selected": 0.00044112600153312087, - "src/backend/tests/unit/test_schema.py::TestOutput::test_output_to_dict": 0.0003872510278597474, - "src/backend/tests/unit/test_schema.py::TestOutput::test_output_validate_display_name": 0.0005081669660285115, - "src/backend/tests/unit/test_schema.py::TestOutput::test_output_validate_model": 0.0014921249821782112, - "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_custom_type": 0.00036487600300461054, - "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_int_type": 0.0005036659422330558, - "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_list_custom_type": 0.001383666938636452, - "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_list_int_type": 0.0003718330408446491, - "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_union_custom_type": 0.0003910410450771451, - "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_union_type": 0.0020738329621963203, - "src/backend/tests/unit/test_setup_superuser.py::test_teardown_superuser_default_superuser": 2.970154915994499, - "src/backend/tests/unit/test_setup_superuser.py::test_teardown_superuser_no_default_superuser": 2.52261962502962, - "src/backend/tests/unit/test_telemetry.py::test_gauge": 3.1998873339616694, - "src/backend/tests/unit/test_telemetry.py::test_gauge_with_counter_method": 2.5295122920069844, - "src/backend/tests/unit/test_telemetry.py::test_gauge_with_historgram_method": 2.5291368339094333, - "src/backend/tests/unit/test_telemetry.py::test_gauge_with_up_down_counter_method": 2.441234875994269, - "src/backend/tests/unit/test_telemetry.py::test_increment_counter": 2.5292067090049386, - "src/backend/tests/unit/test_telemetry.py::test_increment_counter_empty_label": 2.843116457981523, - "src/backend/tests/unit/test_telemetry.py::test_increment_counter_missing_mandatory_label": 2.4626984579954296, - "src/backend/tests/unit/test_telemetry.py::test_increment_counter_unregisted_metric": 2.6637080420041457, - "src/backend/tests/unit/test_telemetry.py::test_init": 2.430767166952137, - "src/backend/tests/unit/test_telemetry.py::test_missing_labels": 2.2437128739547916, - "src/backend/tests/unit/test_telemetry.py::test_multithreaded_singleton": 2.9899119589827023, - "src/backend/tests/unit/test_telemetry.py::test_multithreaded_singleton_race_condition": 2.874815374962054, - "src/backend/tests/unit/test_telemetry.py::test_opentelementry_singleton": 2.434948291920591, - "src/backend/tests/unit/test_template.py::test_build_template_from_function": 0.0023095000069588423, - "src/backend/tests/unit/test_template.py::test_get_base_classes": 0.0009337090305052698, - "src/backend/tests/unit/test_template.py::test_get_default_factory": 0.00046029197983443737, + "src/backend/tests/unit/test_process.py::test_multiple_tweaks": 0.001178542006528005, + "src/backend/tests/unit/test_process.py::test_no_tweaks": 0.00038279099680949, + "src/backend/tests/unit/test_process.py::test_single_tweak": 0.0003569579857867211, + "src/backend/tests/unit/test_process.py::test_tweak_no_node_id": 0.00031458398734685034, + "src/backend/tests/unit/test_process.py::test_tweak_not_in_template": 0.0002716250019147992, + "src/backend/tests/unit/test_schema.py::TestInput::test_field_type_str": 0.0003131239936919883, + "src/backend/tests/unit/test_schema.py::TestInput::test_field_type_type": 0.00030450100894086063, + "src/backend/tests/unit/test_schema.py::TestInput::test_input_to_dict": 0.0004160410026088357, + "src/backend/tests/unit/test_schema.py::TestInput::test_invalid_field_type": 0.00042341598600614816, + "src/backend/tests/unit/test_schema.py::TestInput::test_post_process_type_function": 0.0014711670082760975, + "src/backend/tests/unit/test_schema.py::TestInput::test_serialize_field_type": 0.001243667007656768, + "src/backend/tests/unit/test_schema.py::TestInput::test_validate_type_class": 0.00025591599114704877, + "src/backend/tests/unit/test_schema.py::TestInput::test_validate_type_string": 0.0018165000074077398, + "src/backend/tests/unit/test_schema.py::TestOutput::test_output_add_types": 0.0008704580104677007, + "src/backend/tests/unit/test_schema.py::TestOutput::test_output_default": 0.0009019580029416829, + "src/backend/tests/unit/test_schema.py::TestOutput::test_output_set_selected": 0.0002717920142458752, + "src/backend/tests/unit/test_schema.py::TestOutput::test_output_to_dict": 0.00038420798955485225, + "src/backend/tests/unit/test_schema.py::TestOutput::test_output_validate_display_name": 0.0002256250154459849, + "src/backend/tests/unit/test_schema.py::TestOutput::test_output_validate_model": 0.00037212498136796057, + "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_custom_type": 0.0021775009954581037, + "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_int_type": 0.00021445899619720876, + "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_list_custom_type": 0.001444459005142562, + "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_list_int_type": 0.0004171250038780272, + "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_union_custom_type": 0.00034558399056550115, + "src/backend/tests/unit/test_schema.py::TestPostProcessType::test_union_type": 0.0002598340070107952, + "src/backend/tests/unit/test_setup_superuser.py::test_teardown_superuser_default_superuser": 0.0014707069785799831, + "src/backend/tests/unit/test_setup_superuser.py::test_teardown_superuser_no_default_superuser": 0.004468208004254848, + "src/backend/tests/unit/test_telemetry.py::test_gauge": 0.04031450102047529, + "src/backend/tests/unit/test_telemetry.py::test_gauge_with_counter_method": 0.03398229199228808, + "src/backend/tests/unit/test_telemetry.py::test_gauge_with_historgram_method": 0.012796249007806182, + "src/backend/tests/unit/test_telemetry.py::test_gauge_with_up_down_counter_method": 0.02348929199797567, + "src/backend/tests/unit/test_telemetry.py::test_increment_counter": 0.024323291014297865, + "src/backend/tests/unit/test_telemetry.py::test_increment_counter_empty_label": 0.01421995900454931, + "src/backend/tests/unit/test_telemetry.py::test_increment_counter_missing_mandatory_label": 0.01877875000354834, + "src/backend/tests/unit/test_telemetry.py::test_increment_counter_unregisted_metric": 0.012985084002139047, + "src/backend/tests/unit/test_telemetry.py::test_init": 0.02585883297433611, + "src/backend/tests/unit/test_telemetry.py::test_missing_labels": 0.010578041998087429, + "src/backend/tests/unit/test_telemetry.py::test_multithreaded_singleton": 0.020110293000470847, + "src/backend/tests/unit/test_telemetry.py::test_multithreaded_singleton_race_condition": 0.04233933298382908, + "src/backend/tests/unit/test_telemetry.py::test_opentelementry_singleton": 0.009367083010147326, + "src/backend/tests/unit/test_template.py::test_build_template_from_function": 0.004148293010075577, + "src/backend/tests/unit/test_template.py::test_get_base_classes": 0.001759457984007895, + "src/backend/tests/unit/test_template.py::test_get_default_factory": 0.0006392920040525496, "src/backend/tests/unit/test_user.py::test_add_user": 3.429326084034983, "src/backend/tests/unit/test_user.py::test_data_consistency_after_delete": 3.084409792034421, "src/backend/tests/unit/test_user.py::test_data_consistency_after_update": 4.112100625992753, - "src/backend/tests/unit/test_user.py::test_deactivated_user_cannot_access": 3.187602208054159, + "src/backend/tests/unit/test_user.py::test_deactivated_user_cannot_access": 2.17174604201864, "src/backend/tests/unit/test_user.py::test_deactivated_user_cannot_login": 2.550756209064275, "src/backend/tests/unit/test_user.py::test_delete_user": 3.7109769160160795, "src/backend/tests/unit/test_user.py::test_delete_user_wrong_id": 3.291543999046553, @@ -528,24 +646,82 @@ "src/backend/tests/unit/test_user.py::test_patch_user": 3.110160624026321, "src/backend/tests/unit/test_user.py::test_patch_user_wrong_id": 3.0659845010377467, "src/backend/tests/unit/test_user.py::test_read_all_users": 2.8889535000780597, - "src/backend/tests/unit/test_user.py::test_user_waiting_for_approval": 2.578539165959228, - "src/backend/tests/unit/test_validate_code.py::test_create_function": 0.0017862499225884676, - "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_function": 0.0009927490027621388, - "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_module": 0.0012502089375630021, - "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_schema": 0.002157249953597784, - "src/backend/tests/unit/test_validate_code.py::test_execute_function_success": 0.0005792500451207161, - "src/backend/tests/unit/test_validate_code.py::test_validate_code": 0.001845582912210375, - "src/backend/tests/unit/test_version.py::test_compute_main": 2.6987011659657583, - "src/backend/tests/unit/test_version.py::test_version": 2.45842487498885, - "src/backend/tests/unit/test_webhook.py::test_webhook_endpoint": 16.56281495897565, - "src/backend/tests/unit/test_webhook.py::test_webhook_flow_on_run_endpoint": 8.454458000021987, - "src/backend/tests/unit/test_webhook.py::test_webhook_with_random_payload": 7.648270000063349, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol::password@host-protocol::password@host]": 0.0005409579607658088, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pa:ss:word@host-protocol:user:pa:ss:word@host]": 0.0005487080197781324, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pa@ss@word@host-protocol:user:pa%40ss%40word@host]": 0.0005739580374211073, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pass@word@host-protocol:user:pass%40word@host]": 0.0005274589639157057, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:password@-protocol:user:password@]": 0.00048216601135209203, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:password@host-protocol:user:password@host]": 0.0006742500117979944, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user@host-protocol:user@host]": 0.0006230419385246933, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[user:password@host-user:password@host]": 0.0008930000476539135 + "src/backend/tests/unit/test_user.py::test_user_waiting_for_approval": 2.536705748992972, + "src/backend/tests/unit/test_validate_code.py::test_create_function": 0.0013515419996110722, + "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_function": 0.0018152910051867366, + "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_module": 0.0031597080087522045, + "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_schema": 0.0014733760035596788, + "src/backend/tests/unit/test_validate_code.py::test_execute_function_success": 0.0007328320061787963, + "src/backend/tests/unit/test_validate_code.py::test_validate_code": 0.0006066659698262811, + "src/backend/tests/unit/test_version.py::test_compute_main": 0.002204290998633951, + "src/backend/tests/unit/test_version.py::test_version": 0.0002888750022975728, + "src/backend/tests/unit/test_webhook.py::test_webhook_endpoint": 2.277943458990194, + "src/backend/tests/unit/test_webhook.py::test_webhook_flow_on_run_endpoint": 4.702883917008876, + "src/backend/tests/unit/test_webhook.py::test_webhook_with_random_payload": 1.9362829589954345, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol::password@host-protocol::password@host]": 0.0021194580185692757, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pa:ss:word@host-protocol:user:pa:ss:word@host]": 0.00029479099612217396, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pa@ss@word@host-protocol:user:pa%40ss%40word@host]": 0.00036020799598190933, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pass@word@host-protocol:user:pass%40word@host]": 0.0018152500124415383, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:password@-protocol:user:password@]": 0.0012781660188920796, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:password@host-protocol:user:password@host]": 0.000649124980554916, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user@host-protocol:user@host]": 0.0008907499868655577, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[user:password@host-user:password@host]": 0.0002650820097187534, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[-]": 0.0007548329886049032, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[/home/user/\\ndocu\\nments/file.txt-/home/user/\\\\ndocu\\\\nments/file.txt]": 0.0003840830031549558, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[/home/user/docu\\n\\nments/file.txt-/home/user/docu\\\\n\\\\nments/file.txt]": 0.000284498994005844, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[/home/user/docu\\nments/file.txt-/home/user/docu\\\\nments/file.txt]": 0.0002952510112663731, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[/home/user/documents/\\n-/home/user/documents/\\\\n]": 0.00032479199580848217, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[/home/user/documents/file.txt-/home/user/documents/file.txt]": 0.003706041010445915, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[/home/user/my-\\ndocs/special_file!.pdf-/home/user/my-\\\\ndocs/special_file!.pdf]": 0.0008384159882552922, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[C:/Users\\\\Documents/file.txt-C:/Users\\\\Documents/file.txt]": 0.0027259999769739807, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[C:\\\\Users\\\\Documents\\\\-C:\\\\Users\\\\Documents\\\\]": 0.00036058299883734435, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[C:\\\\Users\\\\Documents\\\\file.txt-C:\\\\Users\\\\Documents\\\\file.txt]": 0.0003219590143999085, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[C:\\\\Users\\\\\\nDocuments\\\\file.txt-C:\\\\Users\\\\\\\\nDocuments\\\\file.txt]": 0.0004588750016409904, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[\\\\\\\\server\\\\share\\\\file.txt-\\\\\\\\server\\\\share\\\\file.txt]": 0.00033083301968872547, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[\\n/home/user/documents/-\\\\n/home/user/documents/]": 0.0003133750142296776, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path[\\n\\n\\n-\\\\n\\\\n\\\\n]": 0.0024994170089485124, + "src/backend/tests/unit/utils/test_format_directory_path.py::test_format_directory_path_type": 0.0006627909897360951, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path[-]": 0.00036491699574980885, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path[/home/user/\\ndocu\\nments/file.txt-/home/user/\\\\ndocu\\\\nments/file.txt]": 0.0012690840085269883, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path[/home/user/docu\\n\\nments/file.txt-/home/user/docu\\\\n\\\\nments/file.txt]": 0.0009298330114688724, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path[/home/user/docu\\nments/file.txt-/home/user/docu\\\\nments/file.txt]": 0.00037158500344958156, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path[/home/user/documents/\\n-/home/user/documents/\\\\n]": 0.0055610839917790145, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path[/home/user/documents/file.txt-/home/user/documents/file.txt]": 0.0007864989893278107, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path[/home/user/my-\\ndocs/special_file!.pdf-/home/user/my-\\\\ndocs/special_file!.pdf]": 0.0002928329922724515, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path[C:\\\\Users\\\\\\nDocuments\\\\file.txt-C:\\\\Users\\\\\\\\nDocuments\\\\file.txt]": 0.0006068350048735738, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path[\\n/home/user/documents/-\\\\n/home/user/documents/]": 0.0008881250105332583, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path[\\n\\n\\n-\\\\n\\\\n\\\\n]": 0.00032625001040287316, + "src/backend/tests/unit/utils/test_rewrite_file_path.py::test_format_directory_path_type": 0.0007176249928306788, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_negative_max_length": 0.00024045900499913841, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[-5-]": 0.00035358300374355167, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[12345-3-12345]": 0.000382417012588121, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[3.141592653589793-4-3.141592653589793]": 0.0003810000198427588, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[None-5-None]": 0.0003037490096176043, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[True-2-True]": 0.0003895000118063763, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[\\u3053\\u3093\\u306b\\u3061\\u306f-3-\\u3053\\u3093\\u306b...]": 0.0016692509962012991, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[a-1-a]": 0.0007649170001968741, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-10-aaaaaaaaaa...]": 0.0003159589978167787, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[exact-5-exact]": 0.00043825000466313213, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[long string-7-long st...]": 0.0006490829982794821, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_non_dict_list[short string-20-short string]": 0.00042245900840498507, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_none_max_length": 0.0003397920081624761, + "src/backend/tests/unit/utils/test_truncate_long_strings.py::test_truncate_long_strings_zero_max_length": 0.00033637401065789163, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings[input_data0-10-expected0]": 0.0003074170235777274, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings[input_data1-5-expected1]": 0.0024352499895030633, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings[input_data2-7-expected2]": 0.00054187499335967, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings[input_data3-8-expected3]": 0.00039250000554602593, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings[input_data4-10-expected4]": 0.0005182919994695112, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings[input_data5-10-expected5]": 0.001439957006368786, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings[input_data6-10-expected6]": 0.002114584029186517, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings[input_data7-5-expected7]": 0.0013511670113075525, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings[input_data8-3-expected8]": 0.0008633749966975302, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings[input_data9-10-expected9]": 0.00467070699960459, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings_default_max_length": 0.0008027509902603924, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings_in_place_modification": 0.000868667004397139, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings_invalid_input": 0.0009479999862378463, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings_negative_max_length": 0.0017517930100439116, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings_no_modification": 0.0004700419958680868, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings_small_max_length": 0.0008286249794764444, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings_type_preservation": 0.0003270010056439787, + "src/backend/tests/unit/utils/test_truncate_long_strings_on_objects.py::test_truncate_long_strings_zero_max_length": 0.0008695819997228682 } \ No newline at end of file diff --git a/src/backend/tests/unit/base/tools/test_component_toolkit.py b/src/backend/tests/unit/base/tools/test_component_toolkit.py index 67c81dae474..cdde2257744 100644 --- a/src/backend/tests/unit/base/tools/test_component_toolkit.py +++ b/src/backend/tests/unit/base/tools/test_component_toolkit.py @@ -81,7 +81,7 @@ def test_component_tool(): @pytest.mark.api_key_required -@pytest.mark.usefixtures("_add_toolkit_output", "client") +@pytest.mark.usefixtures("_add_toolkit_output") def test_component_tool_with_api_key(): chat_output = ChatOutput() openai_llm = OpenAIModelComponent() diff --git a/src/backend/tests/unit/components/models/test_huggingface.py b/src/backend/tests/unit/components/models/test_huggingface.py new file mode 100644 index 00000000000..6bd313e4ff4 --- /dev/null +++ b/src/backend/tests/unit/components/models/test_huggingface.py @@ -0,0 +1,31 @@ +from langflow.inputs.inputs import DictInput, DropdownInput, FloatInput, HandleInput, IntInput, SecretStrInput, StrInput + +from src.backend.base.langflow.components.models.huggingface import HuggingFaceEndpointsComponent + + +def test_huggingface_inputs(): + component = HuggingFaceEndpointsComponent() + inputs = component.inputs + + # Define expected input types and their names + expected_inputs = { + "model_id": StrInput, + "max_new_tokens": IntInput, + "top_k": IntInput, + "top_p": FloatInput, + "typical_p": FloatInput, + "temperature": FloatInput, + "repetition_penalty": FloatInput, + "inference_endpoint": StrInput, + "task": DropdownInput, + "huggingfacehub_api_token": SecretStrInput, + "model_kwargs": DictInput, + "retry_attempts": IntInput, + "output_parser": HandleInput, + } + + # Check if all expected inputs are present + for name, input_type in expected_inputs.items(): + assert any( + isinstance(inp, input_type) and inp.name == name for inp in inputs + ), f"Missing or incorrect input: {name}" diff --git a/src/backend/tests/unit/graph/edge/test_edge_base.py b/src/backend/tests/unit/graph/edge/test_edge_base.py index 19037447aad..a3e212c07b3 100644 --- a/src/backend/tests/unit/graph/edge/test_edge_base.py +++ b/src/backend/tests/unit/graph/edge/test_edge_base.py @@ -6,7 +6,6 @@ from langflow.graph import Graph -@pytest.mark.usefixtures("client") def test_edge_raises_error_on_invalid_target_handle(): template = """Answer the user as if you were a pirate. diff --git a/src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py b/src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py index 024433335e4..83203209ba6 100644 --- a/src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py +++ b/src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py @@ -43,7 +43,6 @@ def memory_chatbot_graph(): return graph -@pytest.mark.usefixtures("client") def test_memory_chatbot(memory_chatbot_graph): # Now we run step by step expected_order = deque(["chat_input", "chat_memory", "prompt", "openai", "chat_output"]) diff --git a/src/backend/tests/unit/test_cli.py b/src/backend/tests/unit/test_cli.py index 796935adb57..ee40633a181 100644 --- a/src/backend/tests/unit/test_cli.py +++ b/src/backend/tests/unit/test_cli.py @@ -11,10 +11,10 @@ def default_settings(): ] -@pytest.mark.usefixtures("client") def test_components_path(runner, default_settings, tmp_path): # create a "components" folder temp_dir = tmp_path / "components" + temp_dir.mkdir(exist_ok=True) result = runner.invoke( app, @@ -25,7 +25,6 @@ def test_components_path(runner, default_settings, tmp_path): assert str(temp_dir) in settings_service.settings.components_path -@pytest.mark.usefixtures("client") def test_superuser(runner): result = runner.invoke(app, ["superuser"], input="admin\nadmin\n") assert result.exit_code == 0, result.stdout diff --git a/src/backend/tests/unit/test_database.py b/src/backend/tests/unit/test_database.py index 7055971ab73..bc4983fe2f7 100644 --- a/src/backend/tests/unit/test_database.py +++ b/src/backend/tests/unit/test_database.py @@ -632,7 +632,6 @@ async def test_load_flows(client: TestClient): assert response.json()["folder_id"] is not None -@pytest.mark.usefixtures("client") def test_sqlite_pragmas(): db_service = get_db_service() diff --git a/uv.lock b/uv.lock index 4ba1f2ae218..9d0a7bfe4b5 100644 --- a/uv.lock +++ b/uv.lock @@ -241,7 +241,7 @@ wheels = [ [[package]] name = "astra-assistants" -version = "2.2.2" +version = "2.2.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -256,9 +256,16 @@ dependencies = [ { name = "tree-sitter" }, { name = "tree-sitter-python" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/a1/2a0baaa257b4de12dccf2c18e1cfad29d3f860321f11ad500d334254f67d/astra_assistants-2.2.2.tar.gz", hash = "sha256:523758c87599ad17d083c99dce77ecd2eda17f967f43c3269e6cd85751ee15a6", size = 67554 } +sdist = { url = "https://files.pythonhosted.org/packages/71/2c/13922eca2cae8a950cd0d18955e7ee5c993944afe0eab10379cf94131bac/astra_assistants-2.2.5.tar.gz", hash = "sha256:6735646ea61420e4a9d9a5204c3cc69e6420b15d58ee16ff4240eca9ceaf127a", size = 67657 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/2f/d69cfe69dfe5bf37d17fa5b47e2ecd001195d04b2a25702968cb32a1e8e4/astra_assistants-2.2.2-py3-none-any.whl", hash = "sha256:0a99968ec7b913f30152d3942488a91ffd207dd4f3333d2af18417c3f2b0a800", size = 78332 }, + { url = "https://files.pythonhosted.org/packages/8d/26/165b492b4f4242510af640b8357d65384203dddd62f902083afaaf8b5938/astra_assistants-2.2.5-py3-none-any.whl", hash = "sha256:475193aa43d9bf8c5f9c9f0eb7e116f495d9345225a2fa25aff9bd21043c3788", size = 78408 }, +] + +[package.optional-dependencies] +tools = [ + { name = "astrapy" }, + { name = "e2b" }, + { name = "e2b-code-interpreter" }, ] [[package]] @@ -3567,7 +3574,7 @@ version = "1.0.19" source = { editable = "." } dependencies = [ { name = "assemblyai" }, - { name = "astra-assistants" }, + { name = "astra-assistants", extra = ["tools"] }, { name = "beautifulsoup4" }, { name = "boto3" }, { name = "certifi" }, @@ -3704,7 +3711,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "assemblyai", specifier = ">=0.33.0" }, - { name = "astra-assistants", specifier = "~=2.2.2" }, + { name = "astra-assistants", extras = ["tools"], specifier = "~=2.2.5" }, { name = "beautifulsoup4", specifier = ">=4.12.2" }, { name = "boto3", specifier = "~=1.34.162" }, { name = "cassio", marker = "extra == 'cassio'", specifier = ">=0.1.7" },