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

View file

@ -1,7 +1,5 @@
from typing import Any
from astra_assistants import patch
from openai import OpenAI
from openai.lib.streaming import AssistantEventHandler
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")]
def process_inputs(self) -> Message:
patch(OpenAI())
text = ""
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.dotdict import dotdict
MAX_NUMBER_OF_FIELDS = 15
class CreateDataComponent(Component):
display_name: str = "Create Data"
description: str = "Dynamically create a Data with a specified number of fields."
name: str = "CreateData"
MAX_FIELDS = 15 # Define a constant for maximum number of fields
inputs = [
IntInput(
@ -22,7 +21,7 @@ class CreateDataComponent(Component):
info="Number of fields to be added to the record.",
real_time_refresh=True,
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(
name="text_key",
@ -50,10 +49,11 @@ class CreateDataComponent(Component):
except ValueError:
return build_config
existing_fields = {}
if field_value_int > MAX_NUMBER_OF_FIELDS:
build_config["number_of_fields"]["value"] = MAX_NUMBER_OF_FIELDS
if field_value_int > self.MAX_FIELDS:
build_config["number_of_fields"]["value"] = self.MAX_FIELDS
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)
if len(build_config) > len(default_keys):

View file

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

View file

@ -2,25 +2,30 @@ from typing import Any
from langflow.custom import Component
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.schema import Data
from langflow.schema.dotdict import dotdict
MAX_NUMBER_OF_FIELDS = 15
class UpdateDataComponent(Component):
display_name: str = "Update data"
description: str = "Dynamically update or append data with the specified fields."
name: str = "UpdateData"
MAX_FIELDS = 15 # Define a constant for maximum number of fields
inputs = [
DataInput(
name="old_data",
display_name="Data",
info="The record to update.",
is_list=False,
is_list=True, # Changed to True to handle list of Data objects
),
IntInput(
name="number_of_fields",
@ -28,7 +33,7 @@ class UpdateDataComponent(Component):
info="Number of fields to be added to the record.",
real_time_refresh=True,
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(
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):
"""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":
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:
field_value_int = int(field_value)
except ValueError:
return build_config
existing_fields = {}
if field_value_int > MAX_NUMBER_OF_FIELDS:
build_config["number_of_fields"]["value"] = MAX_NUMBER_OF_FIELDS
msg = (
f"Number of fields cannot exceed {MAX_NUMBER_OF_FIELDS}. Try using a Component to combine two Data."
)
if field_value_int > self.MAX_FIELDS:
build_config["number_of_fields"]["value"] = self.MAX_FIELDS
msg = f"Number of fields cannot exceed {self.MAX_FIELDS}. " "Try using a Component to combine two Data."
raise ValueError(msg)
if len(build_config) > len(default_keys):
# back up the existing template fields
for key in build_config.copy():
if key not in default_keys:
existing_fields[key] = build_config.pop(key)
existing_fields = {}
# Back up the existing template fields
for key in list(build_config.keys()):
if key not in default_keys:
existing_fields[key] = build_config.pop(key)
for i in range(1, field_value_int + 1):
key = f"field_{i}_key"
@ -85,30 +103,55 @@ class UpdateDataComponent(Component):
build_config["number_of_fields"]["value"] = field_value_int
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()
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
if isinstance(self.old_data, list):
for data_item in self.old_data:
if not isinstance(data_item, Data):
continue # Skip invalid items
data_item.data.update(new_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):
"""Function to get the Data from the attributes."""
data = {}
for value_dict in self._attributes.values():
if isinstance(value_dict, dict):
# Check if the value of the value_dict is a Data
_value_dict = {
key: value.get_text() if isinstance(value, Data) else value for key, value in value_dict.items()
}
data.update(_value_dict)
default_keys = {
"code",
"_type",
"number_of_fields",
"text_key",
"old_data",
"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
def validate_text_key(self, data: Data) -> None:
"""This function validates that the Text Key is one of the keys in the Data."""
data_keys = data.data.keys()
if self.text_key not in data_keys and self.text_key != "":
msg = f"Text Key: {self.text_key} not found in the Data keys: {','.join(data_keys)}"
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: " f"{', '.join(data_keys)}"
raise ValueError(msg)

View file

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

View file

@ -6,7 +6,7 @@
"data": {
"sourceHandle": {
"dataType": "URL",
"id": "URL-46k0m",
"id": "URL-v3FkJ",
"name": "data",
"output_types": [
"Data"
@ -14,25 +14,25 @@
},
"targetHandle": {
"fieldName": "data",
"id": "ParseData-jUQRS",
"id": "ParseData-qffHj",
"inputTypes": [
"Data"
],
"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œ}",
"source": "URL-46k0m",
"sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-46k0mœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}",
"target": "ParseData-jUQRS",
"targetHandle": "{œ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-v3FkJ",
"sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-v3FkJœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}",
"target": "ParseData-qffHj",
"targetHandle": "{œfieldNameœ: œdataœ, œidœ: œParseData-qffHjœ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}"
},
{
"className": "",
"data": {
"sourceHandle": {
"dataType": "ParseData",
"id": "ParseData-jUQRS",
"id": "ParseData-qffHj",
"name": "text",
"output_types": [
"Message"
@ -40,7 +40,7 @@
},
"targetHandle": {
"fieldName": "references",
"id": "Prompt-Pf4QQ",
"id": "Prompt-ztjwI",
"inputTypes": [
"Message",
"Text"
@ -48,18 +48,18 @@
"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œ}",
"source": "ParseData-jUQRS",
"sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-jUQRSœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
"target": "Prompt-Pf4QQ",
"targetHandle": "{œ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-qffHj",
"sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-qffHjœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
"target": "Prompt-ztjwI",
"targetHandle": "{œfieldNameœ: œreferencesœ, œidœ: œPrompt-ztjwIœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
},
{
"className": "",
"data": {
"sourceHandle": {
"dataType": "TextInput",
"id": "TextInput-slCbp",
"id": "TextInput-A7goL",
"name": "text",
"output_types": [
"Message"
@ -67,7 +67,7 @@
},
"targetHandle": {
"fieldName": "instructions",
"id": "Prompt-Pf4QQ",
"id": "Prompt-ztjwI",
"inputTypes": [
"Message",
"Text"
@ -75,18 +75,18 @@
"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œ}",
"source": "TextInput-slCbp",
"sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-slCbpœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
"target": "Prompt-Pf4QQ",
"targetHandle": "{œ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-A7goL",
"sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-A7goLœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
"target": "Prompt-ztjwI",
"targetHandle": "{œfieldNameœ: œinstructionsœ, œidœ: œPrompt-ztjwIœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
},
{
"className": "",
"data": {
"sourceHandle": {
"dataType": "Prompt",
"id": "Prompt-Pf4QQ",
"id": "Prompt-ztjwI",
"name": "prompt",
"output_types": [
"Message"
@ -94,25 +94,25 @@
},
"targetHandle": {
"fieldName": "input_value",
"id": "OpenAIModel-o0Gr0",
"id": "OpenAIModel-Y3GUP",
"inputTypes": [
"Message"
],
"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œ}",
"source": "Prompt-Pf4QQ",
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-Pf4QQœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
"target": "OpenAIModel-o0Gr0",
"targetHandle": "{œ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-ztjwI",
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-ztjwIœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
"target": "OpenAIModel-Y3GUP",
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-Y3GUPœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
},
{
"className": "",
"data": {
"sourceHandle": {
"dataType": "OpenAIModel",
"id": "OpenAIModel-o0Gr0",
"id": "OpenAIModel-Y3GUP",
"name": "text_output",
"output_types": [
"Message"
@ -120,18 +120,18 @@
},
"targetHandle": {
"fieldName": "input_value",
"id": "ChatOutput-eIVde",
"id": "ChatOutput-Y7FC3",
"inputTypes": [
"Message"
],
"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œ}",
"source": "OpenAIModel-o0Gr0",
"sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-o0Gr0œ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
"target": "ChatOutput-eIVde",
"targetHandle": "{œ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-Y3GUP",
"sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-Y3GUPœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
"target": "ChatOutput-Y7FC3",
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-Y7FC3œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
}
],
"nodes": [
@ -139,7 +139,7 @@
"data": {
"description": "Fetch content from one or more URLs.",
"display_name": "URL",
"id": "URL-46k0m",
"id": "URL-v3FkJ",
"node": {
"base_classes": [
"Data"
@ -252,8 +252,8 @@
"type": "URL"
},
"dragging": false,
"height": 397,
"id": "URL-46k0m",
"height": 465,
"id": "URL-v3FkJ",
"position": {
"x": 220.79156431407534,
"y": 498.8186168722667
@ -270,7 +270,7 @@
"data": {
"description": "Convert Data into plain text following a specified template.",
"display_name": "Parse Data",
"id": "ParseData-jUQRS",
"id": "ParseData-qffHj",
"node": {
"base_classes": [
"Message"
@ -387,8 +387,8 @@
"type": "ParseData"
},
"dragging": false,
"height": 378,
"id": "ParseData-jUQRS",
"height": 353,
"id": "ParseData-qffHj",
"position": {
"x": 754.3607306709101,
"y": 736.8516961537598
@ -405,7 +405,7 @@
"data": {
"description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt",
"id": "Prompt-Pf4QQ",
"id": "Prompt-ztjwI",
"node": {
"base_classes": [
"Message"
@ -533,8 +533,8 @@
"type": "Prompt"
},
"dragging": false,
"height": 502,
"id": "Prompt-Pf4QQ",
"height": 477,
"id": "Prompt-ztjwI",
"position": {
"x": 1368.0633591447076,
"y": 467.19448061224284
@ -551,7 +551,7 @@
"data": {
"description": "Get text inputs from the Playground.",
"display_name": "Instructions",
"id": "TextInput-slCbp",
"id": "TextInput-A7goL",
"node": {
"base_classes": [
"Message"
@ -568,6 +568,7 @@
],
"frozen": false,
"icon": "type",
"metadata": {},
"output_types": [],
"outputs": [
{
@ -601,7 +602,7 @@
"show": true,
"title_case": false,
"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_type": "MultilineInput",
@ -630,8 +631,8 @@
"type": "TextInput"
},
"dragging": false,
"height": 302,
"id": "TextInput-slCbp",
"height": 289,
"id": "TextInput-A7goL",
"position": {
"x": 743.7338453293725,
"y": 301.58775454952183
@ -648,7 +649,7 @@
"data": {
"description": "Display a chat message in the Playground.",
"display_name": "Chat Output",
"id": "ChatOutput-eIVde",
"id": "ChatOutput-Y7FC3",
"node": {
"base_classes": [
"Message"
@ -889,8 +890,8 @@
"type": "ChatOutput"
},
"dragging": false,
"height": 302,
"id": "ChatOutput-eIVde",
"height": 289,
"id": "ChatOutput-Y7FC3",
"position": {
"x": 2449.3489426461606,
"y": 571.2449700910389
@ -907,7 +908,7 @@
"data": {
"description": "Generates text using OpenAI LLMs.",
"display_name": "OpenAI",
"id": "OpenAIModel-o0Gr0",
"id": "OpenAIModel-Y3GUP",
"node": {
"base_classes": [
"LanguageModel",
@ -1223,8 +1224,8 @@
"type": "OpenAIModel"
},
"dragging": false,
"height": 605,
"id": "OpenAIModel-o0Gr0",
"height": 587,
"id": "OpenAIModel-Y3GUP",
"position": {
"x": 1950.3830456413473,
"y": 380.8161704718418
@ -1239,18 +1240,18 @@
}
],
"viewport": {
"x": -314.93585938343927,
"y": 168.32720664181306,
"x": -46.935859383438924,
"y": 110.83424076132906,
"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.",
"endpoint_name": null,
"icon": "FileText",
"id": "92d42e25-91d4-4108-8187-92fc53fc9778",
"id": "5e9b5662-0985-4d40-8ffe-b7f42fa86421",
"is_component": false,
"last_tested_version": "1.0.17",
"last_tested_version": "1.0.19.post1",
"name": "Blog Writer",
"icon": "FileText",
"tags": [
"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 fastapi.testclient import TestClient
from httpx import ASGITransport, AsyncClient
from langflow.components.inputs import ChatInput
from langflow.graph import Graph
from langflow.initial_setup.setup import STARTER_FOLDER_NAME
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
async def flow_component(client: AsyncClient, logged_in_headers):
from langflow.components.inputs import ChatInput
chat_input = ChatInput()
graph = Graph(start=chat_input, end=chat_input)
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,
},
}
with pytest.raises(
ValueError, match="Number of fields cannot exceed 15. Try using a Component to combine two Data."
):
with pytest.raises(ValueError, match="Number of fields cannot exceed 15."):
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,
},
}
with pytest.raises(
ValueError, match="Number of fields cannot exceed 15. Try using a Component to combine two Data."
):
with pytest.raises(ValueError, match="Number of fields cannot exceed 15."):
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):
data = Data(data={"key1": "value1", "key2": "value2"}, text_key="key1")
update_data_component.text_key = "invalid_key"
with pytest.raises(ValueError, match="Text Key: invalid_key not found in the Data keys: key1,key2"):
with pytest.raises(ValueError) as exc_info: # noqa: PT011
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