fix: General Fixes and Improvements from v1.0.19.2 Fixes Branch (#4411)

* fix: update assistants client import (#4150)

* remove unnecessary patch

* remove unnecessary patch

* compatible release operator

* chore: add opensearch-py dependency (#4134)

Add opensearch-py dependency to pyproject.toml

* patch version

* lock

* lock some packages to speed up pip install

* langflow-base version

* fix: fix retrieverTool component (#4201)

♻️ (RetrieverTool.py): refactor build method signature to accept additional keyword arguments for future extensibility

* Fixed save modal not exiting

* fix: object has no attribute 'set_event_manager' (#4200)

* 🐛 (base.py): fix AttributeError by checking if custom_component has set_event_manager method before calling it

* 📝 (base.py): Import Component from langflow.custom to improve code readability and maintainability
♻️ (base.py): Refactor code to use isinstance() method for checking if custom_component is an instance of Component

* Refactor: Eliminate Global Variables for Improved Code Maintainability_fix_release (#4208)

Refactor: Eliminate Global Variables for Improved Code Maintainability

- Replaced global variables with local variables or class attributes.
- Enhanced code readability and reduced potential side effects.

* fix: Update example (#4204)

update example

* fix: avoids error NameError: name 'MAX_NUMBER_OF_FIELDS' is not defin… (#4203)

fix: avoids error NameError: name 'MAX_NUMBER_OF_FIELDS' is not defined and fixes build method

Co-authored-by: Edwin Jose <edwin.jose@datastax.com>

* fix: unexpected keyword argument 'code' -> SQLExecutor and SQLDatabase (#4230)

🔧 (SQLDatabase.py): update build method signature to accept additional keyword arguments for future extensibility
🔧 (SQLExecutor.py): update method signature to accept additional keyword arguments for future extensibility

* lock httptools to 0.6.4

* Move ChatInput import to within flow_component fixture in conftest.py

* Simplify error message formatting in test cases for data components

* Add readme to dockerfile

* build: dockerfile with entrypoint (#4062)

Adds a dockerfile with an entrypoint for use with Datastax Langflow

* fixes the leading v for checking out commits correctly

* fixes on more version checkout for docker build

* ✨ (authContext.tsx): Add functionality to fetch global variables on authentication
🔧 (api.tsx): Replace universal-cookie import with react-cookie for consistency
🔧 (authStore.ts): Replace universal-cookie import with react-cookie for consistency
🔧 (use-get-global-variables.ts): Add check to only fetch global variables if user is authenticated
✨ (use-get-mutation-global-variables.ts): Add mutation function to fetch and update global variables
🔧 (authStore.ts): Replace universal-cookie import with react-cookie for consistency

* [autofix.ci] apply automated fixes

* revert changes to workflows

* upgrade lockfile

* update pyproject versions

* update lockfile again

* ⬆️ (pyproject.toml): upgrade langflow-base dependency to version 0.0.99

* ⬆️ (pyproject.toml): downgrade version from 0.0.99 to 0.0.97 to align with project requirements and dependencies.

* ⬆️ (pyproject.toml): downgrade langflow-base dependency version from 0.0.99 to 0.0.97 to resolve compatibility issues

* ⬆️ (uv.lock): downgrade langchain-core package version from 0.3.15 to 0.3.12 to resolve compatibility issues with dependencies

* ⬆️ (pyproject.toml): upgrade langflow-base dependency to version 0.0.99 to utilize the latest features and improvements

---------

Co-authored-by: Sebastián Estévez <estevezsebastian@gmail.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
Co-authored-by: Lucas Oliveira <lucas.edu.oli@hotmail.com>
Co-authored-by: Edwin Jose <edwin.jose@datastax.com>
Co-authored-by: anovazzi1 <otavio2204@gmail.com>
Co-authored-by: João <38133825+joaoguilhermeS@users.noreply.github.com>
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Cristhian Zanforlin Lousa 2024-11-06 14:31:36 -03:00 • committed by GitHub
commit 73df2182de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1228 additions and 1559 deletions

View file

@ -12,7 +12,7 @@ packages = ["src/backend/langflow"]
[project] [project]
name = "langflow" name = "langflow"
version = "1.0.19" version = "1.0.19.post2"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
requires-python = ">=3.10,<3.13" requires-python = ">=3.10,<3.13"
license = "MIT" license = "MIT"
@ -31,7 +31,7 @@ maintainers = [
# Define your main dependencies here # Define your main dependencies here
dependencies = [ dependencies = [
"langflow-base==0.0.97", "langflow-base==0.0.99",
"beautifulsoup4>=4.12.2", "beautifulsoup4>=4.12.2",
"google-search-results>=2.4.1", "google-search-results>=2.4.1",
"google-api-python-client>=2.130.0", "google-api-python-client>=2.130.0",
@ -68,6 +68,8 @@ dependencies = [
"litellm>=1.44.0", "litellm>=1.44.0",
"chromadb>=0.4", "chromadb>=0.4",
"zep-python>=2.0.0", "zep-python>=2.0.0",
"langchain-groq>=0.1.9",
"langchain-pinecone>=0.1.3",
"youtube-transcript-api>=0.6.2", "youtube-transcript-api>=0.6.2",
"markdown>=3.7", "markdown>=3.7",
"upstash-vector>=0.5.0", "upstash-vector>=0.5.0",
@ -75,6 +77,8 @@ dependencies = [
"kubernetes>=30.1.0", "kubernetes>=30.1.0",
"firecrawl-py>=0.0.16", "firecrawl-py>=0.0.16",
"json-repair>=0.25.2", "json-repair>=0.25.2",
"langchain-google-calendar-tools>=0.0.1",
"langchain-milvus>=0.1.1",
"langwatch==0.1.16", "langwatch==0.1.16",
"langsmith~=0.1.136", "langsmith~=0.1.136",
"yfinance>=0.2.40", "yfinance>=0.2.40",
@ -107,6 +111,7 @@ dependencies = [
"langchain-milvus>=0.1.1", "langchain-milvus>=0.1.1",
"langchain-google-community~=2.0.1", "langchain-google-community~=2.0.1",
"langchain-elasticsearch>=0.2.0", "langchain-elasticsearch>=0.2.0",
"opensearch-py>=2.7.1",
"langchain-ollama>=0.2.0", "langchain-ollama>=0.2.0",
"pymupdf~=1.24.13", "pymupdf~=1.24.13",
"sqlalchemy[aiosqlite,postgresql_psycopg2binary,postgresql_psycopgbinary]>=2.0.36" "sqlalchemy[aiosqlite,postgresql_psycopg2binary,postgresql_psycopgbinary]>=2.0.36"

View file

@ -1,7 +1,5 @@
from typing import Any from typing import Any
from astra_assistants import patch
from openai import OpenAI
from openai.lib.streaming import AssistantEventHandler from openai.lib.streaming import AssistantEventHandler
from langflow.base.astra_assistants.util import get_patched_openai_client from langflow.base.astra_assistants.util import get_patched_openai_client
@ -63,8 +61,6 @@ class AssistantsRun(ComponentWithCache):
outputs = [Output(display_name="Assistant Response", name="assistant_response", method="process_inputs")] outputs = [Output(display_name="Assistant Response", name="assistant_response", method="process_inputs")]
def process_inputs(self) -> Message: def process_inputs(self) -> Message:
patch(OpenAI())
text = "" text = ""
if self.thread_id is None: if self.thread_id is None:

View file

@ -7,13 +7,12 @@ from langflow.io import Output
from langflow.schema import Data from langflow.schema import Data
from langflow.schema.dotdict import dotdict from langflow.schema.dotdict import dotdict
MAX_NUMBER_OF_FIELDS = 15
class CreateDataComponent(Component): class CreateDataComponent(Component):
display_name: str = "Create Data" display_name: str = "Create Data"
description: str = "Dynamically create a Data with a specified number of fields." description: str = "Dynamically create a Data with a specified number of fields."
name: str = "CreateData" name: str = "CreateData"
MAX_FIELDS = 15 # Define a constant for maximum number of fields
inputs = [ inputs = [
IntInput( IntInput(
@ -22,7 +21,7 @@ class CreateDataComponent(Component):
info="Number of fields to be added to the record.", info="Number of fields to be added to the record.",
real_time_refresh=True, real_time_refresh=True,
value=1, value=1,
range_spec=RangeSpec(min=1, max=15, step=1, step_type="int"), range_spec=RangeSpec(min=1, max=MAX_FIELDS, step=1, step_type="int"),
), ),
MessageTextInput( MessageTextInput(
name="text_key", name="text_key",
@ -50,10 +49,11 @@ class CreateDataComponent(Component):
except ValueError: except ValueError:
return build_config return build_config
existing_fields = {} existing_fields = {}
if field_value_int > MAX_NUMBER_OF_FIELDS: if field_value_int > self.MAX_FIELDS:
build_config["number_of_fields"]["value"] = MAX_NUMBER_OF_FIELDS build_config["number_of_fields"]["value"] = self.MAX_FIELDS
msg = ( msg = (
f"Number of fields cannot exceed {MAX_NUMBER_OF_FIELDS}. Try using a Component to combine two Data." f"Number of fields cannot exceed {self.MAX_FIELDS}. "
"Please adjust the number of fields to be within the allowed limit."
) )
raise ValueError(msg) raise ValueError(msg)
if len(build_config) > len(default_keys): if len(build_config) > len(default_keys):

View file

@ -44,7 +44,9 @@ class SQLExecutorComponent(CustomComponent):
include_columns: bool = False, include_columns: bool = False,
passthrough: bool = False, passthrough: bool = False,
add_error: bool = False, add_error: bool = False,
**kwargs,
) -> Text: ) -> Text:
_ = kwargs
error = None error = None
try: try:
database = SQLDatabase.from_uri(database_url) database = SQLDatabase.from_uri(database_url)

View file

@ -2,25 +2,30 @@ from typing import Any
from langflow.custom import Component from langflow.custom import Component
from langflow.field_typing.range_spec import RangeSpec from langflow.field_typing.range_spec import RangeSpec
from langflow.inputs.inputs import BoolInput, DataInput, DictInput, IntInput, MessageTextInput from langflow.inputs.inputs import (
BoolInput,
DataInput,
DictInput,
IntInput,
MessageTextInput,
)
from langflow.io import Output from langflow.io import Output
from langflow.schema import Data from langflow.schema import Data
from langflow.schema.dotdict import dotdict from langflow.schema.dotdict import dotdict
MAX_NUMBER_OF_FIELDS = 15
class UpdateDataComponent(Component): class UpdateDataComponent(Component):
display_name: str = "Update data" display_name: str = "Update data"
description: str = "Dynamically update or append data with the specified fields." description: str = "Dynamically update or append data with the specified fields."
name: str = "UpdateData" name: str = "UpdateData"
MAX_FIELDS = 15 # Define a constant for maximum number of fields
inputs = [ inputs = [
DataInput( DataInput(
name="old_data", name="old_data",
display_name="Data", display_name="Data",
info="The record to update.", info="The record to update.",
is_list=False, is_list=True, # Changed to True to handle list of Data objects
), ),
IntInput( IntInput(
name="number_of_fields", name="number_of_fields",
@ -28,7 +33,7 @@ class UpdateDataComponent(Component):
info="Number of fields to be added to the record.", info="Number of fields to be added to the record.",
real_time_refresh=True, real_time_refresh=True,
value=0, value=0,
range_spec=RangeSpec(min=1, max=15, step=1, step_type="int"), range_spec=RangeSpec(min=1, max=MAX_FIELDS, step=1, step_type="int"),
), ),
MessageTextInput( MessageTextInput(
name="text_key", name="text_key",
@ -49,24 +54,37 @@ class UpdateDataComponent(Component):
] ]
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None): def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
"""Update the build configuration when the number of fields changes.
Args:
build_config (dotdict): The current build configuration.
field_value (Any): The new value for the field.
field_name (Optional[str]): The name of the field being updated.
"""
if field_name == "number_of_fields": if field_name == "number_of_fields":
default_keys = ["code", "_type", "number_of_fields", "text_key", "old_data", "text_key_validator"] default_keys = {
"code",
"_type",
"number_of_fields",
"text_key",
"old_data",
"text_key_validator",
}
try: try:
field_value_int = int(field_value) field_value_int = int(field_value)
except ValueError: except ValueError:
return build_config return build_config
existing_fields = {}
if field_value_int > MAX_NUMBER_OF_FIELDS: if field_value_int > self.MAX_FIELDS:
build_config["number_of_fields"]["value"] = MAX_NUMBER_OF_FIELDS build_config["number_of_fields"]["value"] = self.MAX_FIELDS
msg = ( msg = f"Number of fields cannot exceed {self.MAX_FIELDS}. " "Try using a Component to combine two Data."
f"Number of fields cannot exceed {MAX_NUMBER_OF_FIELDS}. Try using a Component to combine two Data."
)
raise ValueError(msg) raise ValueError(msg)
if len(build_config) > len(default_keys):
# back up the existing template fields existing_fields = {}
for key in build_config.copy(): # Back up the existing template fields
if key not in default_keys: for key in list(build_config.keys()):
existing_fields[key] = build_config.pop(key) if key not in default_keys:
existing_fields[key] = build_config.pop(key)
for i in range(1, field_value_int + 1): for i in range(1, field_value_int + 1):
key = f"field_{i}_key" key = f"field_{i}_key"
@ -85,30 +103,55 @@ class UpdateDataComponent(Component):
build_config["number_of_fields"]["value"] = field_value_int build_config["number_of_fields"]["value"] = field_value_int
return build_config return build_config
async def build_data(self) -> Data: async def build_data(self) -> Data | list[Data]:
"""Build the updated data by combining the old data with new fields."""
new_data = self.get_data() new_data = self.get_data()
self.old_data.data.update(new_data) if isinstance(self.old_data, list):
if self.text_key: for data_item in self.old_data:
self.old_data.text_key = self.text_key if not isinstance(data_item, Data):
self.status = self.old_data continue # Skip invalid items
self.validate_text_key(self.old_data) data_item.data.update(new_data)
return self.old_data if self.text_key:
data_item.text_key = self.text_key
self.validate_text_key(data_item)
self.status = self.old_data
return self.old_data # Returns List[Data]
if isinstance(self.old_data, Data):
self.old_data.data.update(new_data)
if self.text_key:
self.old_data.text_key = self.text_key
self.status = self.old_data
self.validate_text_key(self.old_data)
return self.old_data # Returns Data
msg = "old_data is not a Data object or list of Data objects."
raise ValueError(msg)
def get_data(self): def get_data(self):
"""Function to get the Data from the attributes.""" """Function to get the Data from the attributes."""
data = {} data = {}
for value_dict in self._attributes.values(): default_keys = {
if isinstance(value_dict, dict): "code",
# Check if the value of the value_dict is a Data "_type",
_value_dict = { "number_of_fields",
key: value.get_text() if isinstance(value, Data) else value for key, value in value_dict.items() "text_key",
} "old_data",
data.update(_value_dict) "text_key_validator",
}
for attr_name, attr_value in self._attributes.items():
if attr_name in default_keys:
continue # Skip default attributes
if isinstance(attr_value, dict):
for key, value in attr_value.items():
data[key] = value.get_text() if isinstance(value, Data) else value
elif isinstance(attr_value, Data):
data[attr_name] = attr_value.get_text()
else:
data[attr_name] = attr_value
return data return data
def validate_text_key(self, data: Data) -> None: def validate_text_key(self, data: Data) -> None:
"""This function validates that the Text Key is one of the keys in the Data.""" """This function validates that the Text Key is one of the keys in the Data."""
data_keys = data.data.keys() data_keys = data.data.keys()
if self.text_key not in data_keys and self.text_key != "": if self.text_key and self.text_key not in data_keys:
msg = f"Text Key: {self.text_key} not found in the Data keys: {','.join(data_keys)}" msg = f"Text Key: '{self.text_key}' not found in the Data keys: " f"{', '.join(data_keys)}"
raise ValueError(msg) raise ValueError(msg)

View file

@ -21,12 +21,8 @@ class RetrieverToolComponent(CustomComponent):
"description": {"display_name": "Description", "info": "Description of the tool"}, "description": {"display_name": "Description", "info": "Description of the tool"},
} }
def build( def build(self, retriever: BaseRetriever, name: str, description: str, **kwargs) -> Tool:
self, _ = kwargs
retriever: BaseRetriever,
name: str,
description: str,
) -> Tool:
return create_retriever_tool( return create_retriever_tool(
retriever=retriever, retriever=retriever,
name=name, name=name,

View file

@ -6,7 +6,7 @@
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "URL", "dataType": "URL",
"id": "URL-46k0m", "id": "URL-v3FkJ",
"name": "data", "name": "data",
"output_types": [ "output_types": [
"Data" "Data"
@ -14,25 +14,25 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "data", "fieldName": "data",
"id": "ParseData-jUQRS", "id": "ParseData-qffHj",
"inputTypes": [ "inputTypes": [
"Data" "Data"
], ],
"type": "other" "type": "other"
} }
}, },
"id": "reactflow__edge-URL-46k0m{œdataTypeœ:œURLœ,œidœ:œURL-46k0mœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-ParseData-jUQRS{œfieldNameœ:œdataœ,œidœ:œParseData-jUQRSœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}", "id": "reactflow__edge-URL-v3FkJ{œdataTypeœ:œURLœ,œidœ:œURL-v3FkJœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-ParseData-qffHj{œfieldNameœ:œdataœ,œidœ:œParseData-qffHjœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
"source": "URL-46k0m", "source": "URL-v3FkJ",
"sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-46k0mœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}", "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-v3FkJœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}",
"target": "ParseData-jUQRS", "target": "ParseData-qffHj",
"targetHandle": "{œfieldNameœ: œdataœ, œidœ: œParseData-jUQRSœ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}" "targetHandle": "{œfieldNameœ: œdataœ, œidœ: œParseData-qffHjœ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}"
}, },
{ {
"className": "", "className": "",
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "ParseData", "dataType": "ParseData",
"id": "ParseData-jUQRS", "id": "ParseData-qffHj",
"name": "text", "name": "text",
"output_types": [ "output_types": [
"Message" "Message"
@ -40,7 +40,7 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "references", "fieldName": "references",
"id": "Prompt-Pf4QQ", "id": "Prompt-ztjwI",
"inputTypes": [ "inputTypes": [
"Message", "Message",
"Text" "Text"
@ -48,18 +48,18 @@
"type": "str" "type": "str"
} }
}, },
"id": "reactflow__edge-ParseData-jUQRS{œdataTypeœ:œParseDataœ,œidœ:œParseData-jUQRSœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-Pf4QQ{œfieldNameœ:œreferencesœ,œidœ:œPrompt-Pf4QQœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", "id": "reactflow__edge-ParseData-qffHj{œdataTypeœ:œParseDataœ,œidœ:œParseData-qffHjœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-ztjwI{œfieldNameœ:œreferencesœ,œidœ:œPrompt-ztjwIœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
"source": "ParseData-jUQRS", "source": "ParseData-qffHj",
"sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-jUQRSœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", "sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-qffHjœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
"target": "Prompt-Pf4QQ", "target": "Prompt-ztjwI",
"targetHandle": "{œfieldNameœ: œreferencesœ, œidœ: œPrompt-Pf4QQœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" "targetHandle": "{œfieldNameœ: œreferencesœ, œidœ: œPrompt-ztjwIœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
}, },
{ {
"className": "", "className": "",
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "TextInput", "dataType": "TextInput",
"id": "TextInput-slCbp", "id": "TextInput-A7goL",
"name": "text", "name": "text",
"output_types": [ "output_types": [
"Message" "Message"
@ -67,7 +67,7 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "instructions", "fieldName": "instructions",
"id": "Prompt-Pf4QQ", "id": "Prompt-ztjwI",
"inputTypes": [ "inputTypes": [
"Message", "Message",
"Text" "Text"
@ -75,18 +75,18 @@
"type": "str" "type": "str"
} }
}, },
"id": "reactflow__edge-TextInput-slCbp{œdataTypeœ:œTextInputœ,œidœ:œTextInput-slCbpœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-Pf4QQ{œfieldNameœ:œinstructionsœ,œidœ:œPrompt-Pf4QQœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", "id": "reactflow__edge-TextInput-A7goL{œdataTypeœ:œTextInputœ,œidœ:œTextInput-A7goLœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-ztjwI{œfieldNameœ:œinstructionsœ,œidœ:œPrompt-ztjwIœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
"source": "TextInput-slCbp", "source": "TextInput-A7goL",
"sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-slCbpœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", "sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-A7goLœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
"target": "Prompt-Pf4QQ", "target": "Prompt-ztjwI",
"targetHandle": "{œfieldNameœ: œinstructionsœ, œidœ: œPrompt-Pf4QQœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" "targetHandle": "{œfieldNameœ: œinstructionsœ, œidœ: œPrompt-ztjwIœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
}, },
{ {
"className": "", "className": "",
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "Prompt", "dataType": "Prompt",
"id": "Prompt-Pf4QQ", "id": "Prompt-ztjwI",
"name": "prompt", "name": "prompt",
"output_types": [ "output_types": [
"Message" "Message"
@ -94,25 +94,25 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "OpenAIModel-o0Gr0", "id": "OpenAIModel-Y3GUP",
"inputTypes": [ "inputTypes": [
"Message" "Message"
], ],
"type": "str" "type": "str"
} }
}, },
"id": "reactflow__edge-Prompt-Pf4QQ{œdataTypeœ:œPromptœ,œidœ:œPrompt-Pf4QQœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-o0Gr0{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-o0Gr0œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "id": "reactflow__edge-Prompt-ztjwI{œdataTypeœ:œPromptœ,œidœ:œPrompt-ztjwIœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-Y3GUP{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Y3GUPœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
"source": "Prompt-Pf4QQ", "source": "Prompt-ztjwI",
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-Pf4QQœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-ztjwIœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
"target": "OpenAIModel-o0Gr0", "target": "OpenAIModel-Y3GUP",
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-o0Gr0œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-Y3GUPœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
}, },
{ {
"className": "", "className": "",
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "OpenAIModel", "dataType": "OpenAIModel",
"id": "OpenAIModel-o0Gr0", "id": "OpenAIModel-Y3GUP",
"name": "text_output", "name": "text_output",
"output_types": [ "output_types": [
"Message" "Message"
@ -120,18 +120,18 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "ChatOutput-eIVde", "id": "ChatOutput-Y7FC3",
"inputTypes": [ "inputTypes": [
"Message" "Message"
], ],
"type": "str" "type": "str"
} }
}, },
"id": "reactflow__edge-OpenAIModel-o0Gr0{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-o0Gr0œ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-eIVde{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-eIVdeœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "id": "reactflow__edge-OpenAIModel-Y3GUP{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Y3GUPœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-Y7FC3{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Y7FC3œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
"source": "OpenAIModel-o0Gr0", "source": "OpenAIModel-Y3GUP",
"sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-o0Gr0œ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-Y3GUPœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
"target": "ChatOutput-eIVde", "target": "ChatOutput-Y7FC3",
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-eIVdeœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-Y7FC3œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
} }
], ],
"nodes": [ "nodes": [
@ -139,7 +139,7 @@
"data": { "data": {
"description": "Fetch content from one or more URLs.", "description": "Fetch content from one or more URLs.",
"display_name": "URL", "display_name": "URL",
"id": "URL-46k0m", "id": "URL-v3FkJ",
"node": { "node": {
"base_classes": [ "base_classes": [
"Data" "Data"
@ -252,8 +252,8 @@
"type": "URL" "type": "URL"
}, },
"dragging": false, "dragging": false,
"height": 397, "height": 465,
"id": "URL-46k0m", "id": "URL-v3FkJ",
"position": { "position": {
"x": 220.79156431407534, "x": 220.79156431407534,
"y": 498.8186168722667 "y": 498.8186168722667
@ -270,7 +270,7 @@
"data": { "data": {
"description": "Convert Data into plain text following a specified template.", "description": "Convert Data into plain text following a specified template.",
"display_name": "Parse Data", "display_name": "Parse Data",
"id": "ParseData-jUQRS", "id": "ParseData-qffHj",
"node": { "node": {
"base_classes": [ "base_classes": [
"Message" "Message"
@ -387,8 +387,8 @@
"type": "ParseData" "type": "ParseData"
}, },
"dragging": false, "dragging": false,
"height": 378, "height": 353,
"id": "ParseData-jUQRS", "id": "ParseData-qffHj",
"position": { "position": {
"x": 754.3607306709101, "x": 754.3607306709101,
"y": 736.8516961537598 "y": 736.8516961537598
@ -405,7 +405,7 @@
"data": { "data": {
"description": "Create a prompt template with dynamic variables.", "description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt", "display_name": "Prompt",
"id": "Prompt-Pf4QQ", "id": "Prompt-ztjwI",
"node": { "node": {
"base_classes": [ "base_classes": [
"Message" "Message"
@ -533,8 +533,8 @@
"type": "Prompt" "type": "Prompt"
}, },
"dragging": false, "dragging": false,
"height": 502, "height": 477,
"id": "Prompt-Pf4QQ", "id": "Prompt-ztjwI",
"position": { "position": {
"x": 1368.0633591447076, "x": 1368.0633591447076,
"y": 467.19448061224284 "y": 467.19448061224284
@ -551,7 +551,7 @@
"data": { "data": {
"description": "Get text inputs from the Playground.", "description": "Get text inputs from the Playground.",
"display_name": "Instructions", "display_name": "Instructions",
"id": "TextInput-slCbp", "id": "TextInput-A7goL",
"node": { "node": {
"base_classes": [ "base_classes": [
"Message" "Message"
@ -568,6 +568,7 @@
], ],
"frozen": false, "frozen": false,
"icon": "type", "icon": "type",
"metadata": {},
"output_types": [], "output_types": [],
"outputs": [ "outputs": [
{ {
@ -601,7 +602,7 @@
"show": true, "show": true,
"title_case": false, "title_case": false,
"type": "code", "type": "code",
"value": "from langflow.base.io.text import TextComponent\nfrom langflow.io import MultilineInput, Output\nfrom langflow.schema.message import Message\n\n\nclass TextInputComponent(TextComponent):\n display_name = \"Text Input\"\n description = \"Get text inputs from the Playground.\"\n icon = \"type\"\n name = \"TextInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Text to be passed as input.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text\", method=\"text_response\"),\n ]\n\n def text_response(self) -> Message:\n message = Message(\n text=self.input_value,\n )\n return message\n" "value": "from langflow.base.io.text import TextComponent\nfrom langflow.io import MultilineInput, Output\nfrom langflow.schema.message import Message\n\n\nclass TextInputComponent(TextComponent):\n display_name = \"Text Input\"\n description = \"Get text inputs from the Playground.\"\n icon = \"type\"\n name = \"TextInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Text to be passed as input.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text\", method=\"text_response\"),\n ]\n\n def text_response(self) -> Message:\n return Message(\n text=self.input_value,\n )\n"
}, },
"input_value": { "input_value": {
"_input_type": "MultilineInput", "_input_type": "MultilineInput",
@ -630,8 +631,8 @@
"type": "TextInput" "type": "TextInput"
}, },
"dragging": false, "dragging": false,
"height": 302, "height": 289,
"id": "TextInput-slCbp", "id": "TextInput-A7goL",
"position": { "position": {
"x": 743.7338453293725, "x": 743.7338453293725,
"y": 301.58775454952183 "y": 301.58775454952183
@ -648,7 +649,7 @@
"data": { "data": {
"description": "Display a chat message in the Playground.", "description": "Display a chat message in the Playground.",
"display_name": "Chat Output", "display_name": "Chat Output",
"id": "ChatOutput-eIVde", "id": "ChatOutput-Y7FC3",
"node": { "node": {
"base_classes": [ "base_classes": [
"Message" "Message"
@ -889,8 +890,8 @@
"type": "ChatOutput" "type": "ChatOutput"
}, },
"dragging": false, "dragging": false,
"height": 302, "height": 289,
"id": "ChatOutput-eIVde", "id": "ChatOutput-Y7FC3",
"position": { "position": {
"x": 2449.3489426461606, "x": 2449.3489426461606,
"y": 571.2449700910389 "y": 571.2449700910389
@ -907,7 +908,7 @@
"data": { "data": {
"description": "Generates text using OpenAI LLMs.", "description": "Generates text using OpenAI LLMs.",
"display_name": "OpenAI", "display_name": "OpenAI",
"id": "OpenAIModel-o0Gr0", "id": "OpenAIModel-Y3GUP",
"node": { "node": {
"base_classes": [ "base_classes": [
"LanguageModel", "LanguageModel",
@ -1223,8 +1224,8 @@
"type": "OpenAIModel" "type": "OpenAIModel"
}, },
"dragging": false, "dragging": false,
"height": 605, "height": 587,
"id": "OpenAIModel-o0Gr0", "id": "OpenAIModel-Y3GUP",
"position": { "position": {
"x": 1950.3830456413473, "x": 1950.3830456413473,
"y": 380.8161704718418 "y": 380.8161704718418
@ -1239,18 +1240,18 @@
} }
], ],
"viewport": { "viewport": {
"x": -314.93585938343927, "x": -46.935859383438924,
"y": 168.32720664181306, "y": 110.83424076132906,
"zoom": 0.5205627295612754 "zoom": 0.5205627295612754
} }
}, },
"description": "This flow can be used to create a blog post following instructions from the user, using two other blogs as reference.", "description": "This flow can be used to create a blog post following instructions from the user, using two other blogs as reference.",
"endpoint_name": null, "endpoint_name": null,
"icon": "FileText", "id": "5e9b5662-0985-4d40-8ffe-b7f42fa86421",
"id": "92d42e25-91d4-4108-8187-92fc53fc9778",
"is_component": false, "is_component": false,
"last_tested_version": "1.0.17", "last_tested_version": "1.0.19.post1",
"name": "Blog Writer", "name": "Blog Writer",
"icon": "FileText",
"tags": [ "tags": [
"chatbots" "chatbots"
] ]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -16,7 +16,6 @@ from asgi_lifespan import LifespanManager
from dotenv import load_dotenv from dotenv import load_dotenv
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from httpx import ASGITransport, AsyncClient from httpx import ASGITransport, AsyncClient
from langflow.components.inputs import ChatInput
from langflow.graph import Graph from langflow.graph import Graph
from langflow.initial_setup.setup import STARTER_FOLDER_NAME from langflow.initial_setup.setup import STARTER_FOLDER_NAME
from langflow.services.auth.utils import get_password_hash from langflow.services.auth.utils import get_password_hash
@ -531,6 +530,8 @@ async def added_webhook_test(client, json_webhook_test, logged_in_headers):
@pytest.fixture @pytest.fixture
async def flow_component(client: AsyncClient, logged_in_headers): async def flow_component(client: AsyncClient, logged_in_headers):
from langflow.components.inputs import ChatInput
chat_input = ChatInput() chat_input = ChatInput()
graph = Graph(start=chat_input, end=chat_input) graph = Graph(start=chat_input, end=chat_input)
graph_dict = graph.dump(name="Chat Input Component") graph_dict = graph.dump(name="Chat Input Component")

View file

@ -48,9 +48,7 @@ def test_update_build_config_exceed_limit(create_data_component):
"value": False, "value": False,
}, },
} }
with pytest.raises( with pytest.raises(ValueError, match="Number of fields cannot exceed 15."):
ValueError, match="Number of fields cannot exceed 15. Try using a Component to combine two Data."
):
create_data_component.update_build_config(build_config, 16, "number_of_fields") create_data_component.update_build_config(build_config, 16, "number_of_fields")

View file

@ -48,9 +48,7 @@ def test_update_build_config_exceed_limit(update_data_component):
"value": False, "value": False,
}, },
} }
with pytest.raises( with pytest.raises(ValueError, match="Number of fields cannot exceed 15."):
ValueError, match="Number of fields cannot exceed 15. Try using a Component to combine two Data."
):
update_data_component.update_build_config(build_config, 16, "number_of_fields") update_data_component.update_build_config(build_config, 16, "number_of_fields")
@ -94,6 +92,9 @@ def test_validate_text_key_valid(update_data_component):
def test_validate_text_key_invalid(update_data_component): def test_validate_text_key_invalid(update_data_component):
data = Data(data={"key1": "value1", "key2": "value2"}, text_key="key1") data = Data(data={"key1": "value1", "key2": "value2"}, text_key="key1")
update_data_component.text_key = "invalid_key" update_data_component.text_key = "invalid_key"
with pytest.raises(ValueError) as exc_info: # noqa: PT011
with pytest.raises(ValueError, match="Text Key: invalid_key not found in the Data keys: key1,key2"):
update_data_component.validate_text_key(data) update_data_component.validate_text_key(data)
expected_error_message = (
f"Text Key: '{update_data_component.text_key}' not found in the Data keys: {', '.join(data.data.keys())}"
)
assert str(exc_info.value) == expected_error_message

2456
uv.lock generated

File diff suppressed because it is too large Load diff