parent
60b1927cc5
commit
0b39554795
57 changed files with 351 additions and 359 deletions
|
|
@ -584,8 +584,8 @@ async def custom_component(
|
||||||
if raw_code.frontend_node is not None:
|
if raw_code.frontend_node is not None:
|
||||||
built_frontend_node = component_instance.post_code_processing(built_frontend_node, raw_code.frontend_node)
|
built_frontend_node = component_instance.post_code_processing(built_frontend_node, raw_code.frontend_node)
|
||||||
|
|
||||||
_type = get_instance_name(component_instance)
|
type_ = get_instance_name(component_instance)
|
||||||
return CustomComponentResponse(data=built_frontend_node, type=_type)
|
return CustomComponentResponse(data=built_frontend_node, type=type_)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/custom_component/update", status_code=HTTPStatus.OK)
|
@router.post("/custom_component/update", status_code=HTTPStatus.OK)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ async def create_variable(
|
||||||
name=variable.name,
|
name=variable.name,
|
||||||
value=variable.value,
|
value=variable.value,
|
||||||
default_fields=variable.default_fields or [],
|
default_fields=variable.default_fields or [],
|
||||||
_type=variable.type or GENERIC_TYPE,
|
type_=variable.type or GENERIC_TYPE,
|
||||||
session=session,
|
session=session,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -174,16 +174,16 @@ class BaseCrewComponent(Component):
|
||||||
self,
|
self,
|
||||||
) -> Callable:
|
) -> Callable:
|
||||||
def step_callback(agent_output: AgentFinish | list[tuple[AgentAction, str]]) -> None:
|
def step_callback(agent_output: AgentFinish | list[tuple[AgentAction, str]]) -> None:
|
||||||
_id = self._vertex.id if self._vertex else self.display_name
|
id_ = self._vertex.id if self._vertex else self.display_name
|
||||||
if isinstance(agent_output, AgentFinish):
|
if isinstance(agent_output, AgentFinish):
|
||||||
messages = agent_output.messages
|
messages = agent_output.messages
|
||||||
self.log(cast("dict", messages[0].to_json()), name=f"Finish (Agent: {_id})")
|
self.log(cast("dict", messages[0].to_json()), name=f"Finish (Agent: {id_})")
|
||||||
elif isinstance(agent_output, list):
|
elif isinstance(agent_output, list):
|
||||||
_messages_dict = {f"Action {i}": action.messages for i, (action, _) in enumerate(agent_output)}
|
messages_dict_ = {f"Action {i}": action.messages for i, (action, _) in enumerate(agent_output)}
|
||||||
# Serialize the messages with to_json() to avoid issues with circular references
|
# Serialize the messages with to_json() to avoid issues with circular references
|
||||||
serializable_dict = {k: [m.to_json() for m in v] for k, v in _messages_dict.items()}
|
serializable_dict = {k: [m.to_json() for m in v] for k, v in messages_dict_.items()}
|
||||||
messages_dict = {k: v[0] if len(v) == 1 else v for k, v in serializable_dict.items()}
|
messages_dict = {k: v[0] if len(v) == 1 else v for k, v in serializable_dict.items()}
|
||||||
self.log(messages_dict, name=f"Step (Agent: {_id})")
|
self.log(messages_dict, name=f"Step (Agent: {id_})")
|
||||||
|
|
||||||
return step_callback
|
return step_callback
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,15 +108,15 @@ def partition_file_to_data(file_path: str, *, silent_errors: bool) -> Data | Non
|
||||||
|
|
||||||
|
|
||||||
def read_text_file(file_path: str) -> str:
|
def read_text_file(file_path: str) -> str:
|
||||||
_file_path = Path(file_path)
|
file_path_ = Path(file_path)
|
||||||
raw_data = _file_path.read_bytes()
|
raw_data = file_path_.read_bytes()
|
||||||
result = chardet.detect(raw_data)
|
result = chardet.detect(raw_data)
|
||||||
encoding = result["encoding"]
|
encoding = result["encoding"]
|
||||||
|
|
||||||
if encoding in {"Windows-1252", "Windows-1254", "MacRoman"}:
|
if encoding in {"Windows-1252", "Windows-1254", "MacRoman"}:
|
||||||
encoding = "utf-8"
|
encoding = "utf-8"
|
||||||
|
|
||||||
return _file_path.read_text(encoding=encoding)
|
return file_path_.read_text(encoding=encoding)
|
||||||
|
|
||||||
|
|
||||||
def read_docx_file(file_path: str) -> str:
|
def read_docx_file(file_path: str) -> str:
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ class ChatComponent(Component):
|
||||||
def get_properties_from_source_component(self):
|
def get_properties_from_source_component(self):
|
||||||
if hasattr(self, "_vertex") and hasattr(self._vertex, "incoming_edges") and self._vertex.incoming_edges:
|
if hasattr(self, "_vertex") and hasattr(self._vertex, "incoming_edges") and self._vertex.incoming_edges:
|
||||||
source_id = self._vertex.incoming_edges[0].source_id
|
source_id = self._vertex.incoming_edges[0].source_id
|
||||||
_source_vertex = self.graph.get_vertex(source_id)
|
source_vertex = self.graph.get_vertex(source_id)
|
||||||
component = _source_vertex.custom_component
|
component = source_vertex.custom_component
|
||||||
source = component.display_name
|
source = component.display_name
|
||||||
icon = component.icon
|
icon = component.icon
|
||||||
possible_attributes = ["model_name", "model_id", "model"]
|
possible_attributes = ["model_name", "model_id", "model"]
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,12 @@ if TYPE_CHECKING:
|
||||||
TOOL_TYPES_SET = {"Tool", "BaseTool", "StructuredTool"}
|
TOOL_TYPES_SET = {"Tool", "BaseTool", "StructuredTool"}
|
||||||
|
|
||||||
|
|
||||||
def _get_input_type(_input: InputTypes):
|
def _get_input_type(input_: InputTypes):
|
||||||
if _input.input_types:
|
if input_.input_types:
|
||||||
if len(_input.input_types) == 1:
|
if len(input_.input_types) == 1:
|
||||||
return _input.input_types[0]
|
return input_.input_types[0]
|
||||||
return " | ".join(_input.input_types)
|
return " | ".join(input_.input_types)
|
||||||
return _input.field_type
|
return input_.field_type
|
||||||
|
|
||||||
|
|
||||||
def build_description(component: Component, output: Output) -> str:
|
def build_description(component: Component, output: Output) -> str:
|
||||||
|
|
|
||||||
|
|
@ -87,14 +87,14 @@ class StructuredOutputComponent(Component):
|
||||||
msg = "Output schema cannot be empty"
|
msg = "Output schema cannot be empty"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
_output_model = build_model_from_schema(self.output_schema)
|
output_model_ = build_model_from_schema(self.output_schema)
|
||||||
if self.multiple:
|
if self.multiple:
|
||||||
output_model = create_model(
|
output_model = create_model(
|
||||||
self.schema_name,
|
self.schema_name,
|
||||||
objects=(list[_output_model], Field(description=f"A list of {self.schema_name}.")), # type: ignore[valid-type]
|
objects=(list[output_model_], Field(description=f"A list of {self.schema_name}.")), # type: ignore[valid-type]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
output_model = _output_model
|
output_model = output_model_
|
||||||
try:
|
try:
|
||||||
llm_with_structured_output = cast("LanguageModel", self.llm).with_structured_output(schema=output_model) # type: ignore[valid-type, attr-defined]
|
llm_with_structured_output = cast("LanguageModel", self.llm).with_structured_output(schema=output_model) # type: ignore[valid-type, attr-defined]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,9 @@ class ChatInput(ChatComponent):
|
||||||
]
|
]
|
||||||
|
|
||||||
async def message_response(self) -> Message:
|
async def message_response(self) -> Message:
|
||||||
_background_color = self.background_color
|
background_color = self.background_color
|
||||||
_text_color = self.text_color
|
text_color = self.text_color
|
||||||
_icon = self.chat_icon
|
icon = self.chat_icon
|
||||||
|
|
||||||
message = await Message.create(
|
message = await Message.create(
|
||||||
text=self.input_value,
|
text=self.input_value,
|
||||||
|
|
@ -89,7 +89,7 @@ class ChatInput(ChatComponent):
|
||||||
sender_name=self.sender_name,
|
sender_name=self.sender_name,
|
||||||
session_id=self.session_id,
|
session_id=self.session_id,
|
||||||
files=self.files,
|
files=self.files,
|
||||||
properties={"background_color": _background_color, "text_color": _text_color, "icon": _icon},
|
properties={"background_color": background_color, "text_color": text_color, "icon": icon},
|
||||||
)
|
)
|
||||||
if self.session_id and isinstance(message, Message) and self.should_store_message:
|
if self.session_id and isinstance(message, Message) and self.should_store_message:
|
||||||
stored_message = await self.send_message(
|
stored_message = await self.send_message(
|
||||||
|
|
|
||||||
|
|
@ -123,8 +123,8 @@ class RunnableExecComponent(Component):
|
||||||
if self.use_stream:
|
if self.use_stream:
|
||||||
return self.astream_events(input_dict)
|
return self.astream_events(input_dict)
|
||||||
result = await self.runnable.ainvoke(input_dict)
|
result = await self.runnable.ainvoke(input_dict)
|
||||||
result_value, _status = self.get_output(result, self.input_key, self.output_key)
|
result_value, status_ = self.get_output(result, self.input_key, self.output_key)
|
||||||
status += _status
|
status += status_
|
||||||
status += f"\n\nOutput: {result_value}\n\nRaw Output: {result}"
|
status += f"\n\nOutput: {result_value}\n\nRaw Output: {result}"
|
||||||
self.status = status
|
self.status = status
|
||||||
return result_value
|
return result_value
|
||||||
|
|
|
||||||
|
|
@ -80,10 +80,10 @@ class ChatOutput(ChatComponent):
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:
|
def _build_source(self, id_: str | None, display_name: str | None, source: str | None) -> Source:
|
||||||
source_dict = {}
|
source_dict = {}
|
||||||
if _id:
|
if id_:
|
||||||
source_dict["id"] = _id
|
source_dict["id"] = id_
|
||||||
if display_name:
|
if display_name:
|
||||||
source_dict["display_name"] = display_name
|
source_dict["display_name"] = display_name
|
||||||
if source:
|
if source:
|
||||||
|
|
@ -91,20 +91,20 @@ class ChatOutput(ChatComponent):
|
||||||
return Source(**source_dict)
|
return Source(**source_dict)
|
||||||
|
|
||||||
async def message_response(self) -> Message:
|
async def message_response(self) -> Message:
|
||||||
_source, _icon, _display_name, _source_id = self.get_properties_from_source_component()
|
source, icon, display_name, source_id = self.get_properties_from_source_component()
|
||||||
_background_color = self.background_color
|
background_color = self.background_color
|
||||||
_text_color = self.text_color
|
text_color = self.text_color
|
||||||
if self.chat_icon:
|
if self.chat_icon:
|
||||||
_icon = self.chat_icon
|
icon = self.chat_icon
|
||||||
message = self.input_value if isinstance(self.input_value, Message) else Message(text=self.input_value)
|
message = self.input_value if isinstance(self.input_value, Message) else Message(text=self.input_value)
|
||||||
message.sender = self.sender
|
message.sender = self.sender
|
||||||
message.sender_name = self.sender_name
|
message.sender_name = self.sender_name
|
||||||
message.session_id = self.session_id
|
message.session_id = self.session_id
|
||||||
message.flow_id = self.graph.flow_id if hasattr(self, "graph") else None
|
message.flow_id = self.graph.flow_id if hasattr(self, "graph") else None
|
||||||
message.properties.source = self._build_source(_source_id, _display_name, _source)
|
message.properties.source = self._build_source(source_id, display_name, source)
|
||||||
message.properties.icon = _icon
|
message.properties.icon = icon
|
||||||
message.properties.background_color = _background_color
|
message.properties.background_color = background_color
|
||||||
message.properties.text_color = _text_color
|
message.properties.text_color = text_color
|
||||||
if self.session_id and isinstance(message, Message) and self.should_store_message:
|
if self.session_id and isinstance(message, Message) and self.should_store_message:
|
||||||
stored_message = await self.send_message(
|
stored_message = await self.send_message(
|
||||||
message,
|
message,
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,10 @@ class CreateDataComponent(Component):
|
||||||
for value_dict in self._attributes.values():
|
for value_dict in self._attributes.values():
|
||||||
if isinstance(value_dict, dict):
|
if isinstance(value_dict, dict):
|
||||||
# Check if the value of the value_dict is a Data
|
# Check if the value of the value_dict is a Data
|
||||||
_value_dict = {
|
value_dict_ = {
|
||||||
key: value.get_text() if isinstance(value, Data) else value for key, value in value_dict.items()
|
key: value.get_text() if isinstance(value, Data) else value for key, value in value_dict.items()
|
||||||
}
|
}
|
||||||
data.update(_value_dict)
|
data.update(value_dict_)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def validate_text_key(self) -> None:
|
def validate_text_key(self) -> None:
|
||||||
|
|
|
||||||
|
|
@ -94,9 +94,9 @@ class AstraDBToolComponent(LCToolComponent):
|
||||||
if self._cached_collection:
|
if self._cached_collection:
|
||||||
return self._cached_collection
|
return self._cached_collection
|
||||||
|
|
||||||
_cached_client = DataAPIClient(self.token)
|
cached_client = DataAPIClient(self.token)
|
||||||
_cached_db = _cached_client.get_database(self.api_endpoint, namespace=self.namespace)
|
cached_db = cached_client.get_database(self.api_endpoint, namespace=self.namespace)
|
||||||
self._cached_collection = _cached_db.get_collection(self.collection_name)
|
self._cached_collection = cached_db.get_collection(self.collection_name)
|
||||||
return self._cached_collection
|
return self._cached_collection
|
||||||
|
|
||||||
def create_args_schema(self) -> dict[str, BaseModel]:
|
def create_args_schema(self) -> dict[str, BaseModel]:
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ class PythonCodeStructuredTool(LCToolComponent):
|
||||||
return build_config
|
return build_config
|
||||||
|
|
||||||
async def build_tool(self) -> Tool:
|
async def build_tool(self) -> Tool:
|
||||||
_local_namespace = {} # type: ignore[var-annotated]
|
local_namespace = {} # type: ignore[var-annotated]
|
||||||
modules = self._find_imports(self.tool_code)
|
modules = self._find_imports(self.tool_code)
|
||||||
import_code = ""
|
import_code = ""
|
||||||
for module in modules["imports"]:
|
for module in modules["imports"]:
|
||||||
|
|
@ -154,7 +154,7 @@ class PythonCodeStructuredTool(LCToolComponent):
|
||||||
f"from {from_module.module} import {', '.join([alias.name for alias in from_module.names])}\n"
|
f"from {from_module.module} import {', '.join([alias.name for alias in from_module.names])}\n"
|
||||||
)
|
)
|
||||||
exec(import_code, globals())
|
exec(import_code, globals())
|
||||||
exec(self.tool_code, globals(), _local_namespace)
|
exec(self.tool_code, globals(), local_namespace)
|
||||||
|
|
||||||
class PythonCodeToolFunc:
|
class PythonCodeToolFunc:
|
||||||
params: dict = {}
|
params: dict = {}
|
||||||
|
|
@ -163,23 +163,23 @@ class PythonCodeStructuredTool(LCToolComponent):
|
||||||
for key, arg in kwargs.items():
|
for key, arg in kwargs.items():
|
||||||
if key not in PythonCodeToolFunc.params:
|
if key not in PythonCodeToolFunc.params:
|
||||||
PythonCodeToolFunc.params[key] = arg
|
PythonCodeToolFunc.params[key] = arg
|
||||||
return _local_namespace[self.tool_function](**PythonCodeToolFunc.params)
|
return local_namespace[self.tool_function](**PythonCodeToolFunc.params)
|
||||||
|
|
||||||
_globals = globals()
|
globals_ = globals()
|
||||||
_local = {}
|
local = {}
|
||||||
_local[self.tool_function] = PythonCodeToolFunc
|
local[self.tool_function] = PythonCodeToolFunc
|
||||||
_globals.update(_local)
|
globals_.update(local)
|
||||||
|
|
||||||
if isinstance(self.global_variables, list):
|
if isinstance(self.global_variables, list):
|
||||||
for data in self.global_variables:
|
for data in self.global_variables:
|
||||||
if isinstance(data, Data):
|
if isinstance(data, Data):
|
||||||
_globals.update(data.data)
|
globals_.update(data.data)
|
||||||
elif isinstance(self.global_variables, dict):
|
elif isinstance(self.global_variables, dict):
|
||||||
_globals.update(self.global_variables)
|
globals_.update(self.global_variables)
|
||||||
|
|
||||||
classes = json.loads(self._attributes["_classes"])
|
classes = json.loads(self._attributes["_classes"])
|
||||||
for class_dict in classes:
|
for class_dict in classes:
|
||||||
exec("\n".join(class_dict["code"]), _globals)
|
exec("\n".join(class_dict["code"]), globals_)
|
||||||
|
|
||||||
named_functions = json.loads(self._attributes["_functions"])
|
named_functions = json.loads(self._attributes["_functions"])
|
||||||
schema_fields = {}
|
schema_fields = {}
|
||||||
|
|
@ -199,8 +199,8 @@ class PythonCodeStructuredTool(LCToolComponent):
|
||||||
field_description = self._get_value(self._attributes[attr], str)
|
field_description = self._get_value(self._attributes[attr], str)
|
||||||
|
|
||||||
if field_annotation:
|
if field_annotation:
|
||||||
exec(f"temp_annotation_type = {field_annotation}", _globals)
|
exec(f"temp_annotation_type = {field_annotation}", globals_)
|
||||||
schema_annotation = _globals["temp_annotation_type"]
|
schema_annotation = globals_["temp_annotation_type"]
|
||||||
else:
|
else:
|
||||||
schema_annotation = Any
|
schema_annotation = Any
|
||||||
schema_fields[field_name] = (
|
schema_fields[field_name] = (
|
||||||
|
|
@ -211,15 +211,15 @@ class PythonCodeStructuredTool(LCToolComponent):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if "temp_annotation_type" in _globals:
|
if "temp_annotation_type" in globals_:
|
||||||
_globals.pop("temp_annotation_type")
|
globals_.pop("temp_annotation_type")
|
||||||
|
|
||||||
python_code_tool_schema = None
|
python_code_tool_schema = None
|
||||||
if schema_fields:
|
if schema_fields:
|
||||||
python_code_tool_schema = create_model("PythonCodeToolSchema", **schema_fields)
|
python_code_tool_schema = create_model("PythonCodeToolSchema", **schema_fields)
|
||||||
|
|
||||||
return StructuredTool.from_function(
|
return StructuredTool.from_function(
|
||||||
func=_local[self.tool_function].run,
|
func=local[self.tool_function].run,
|
||||||
args_schema=python_code_tool_schema,
|
args_schema=python_code_tool_schema,
|
||||||
name=self.tool_name,
|
name=self.tool_name,
|
||||||
description=self.tool_description,
|
description=self.tool_description,
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,8 @@ class PythonREPLToolComponent(LCToolComponent):
|
||||||
return global_dict
|
return global_dict
|
||||||
|
|
||||||
def build_tool(self) -> Tool:
|
def build_tool(self) -> Tool:
|
||||||
_globals = self.get_globals(self.global_imports)
|
globals_ = self.get_globals(self.global_imports)
|
||||||
python_repl = PythonREPL(_globals=_globals)
|
python_repl = PythonREPL(_globals=globals_)
|
||||||
|
|
||||||
def run_python_code(code: str) -> str:
|
def run_python_code(code: str) -> str:
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -121,10 +121,10 @@ class SearXNGToolComponent(LCToolComponent):
|
||||||
SearxSearch._headers = self.search_headers.copy()
|
SearxSearch._headers = self.search_headers.copy()
|
||||||
SearxSearch._max_results = self.max_results
|
SearxSearch._max_results = self.max_results
|
||||||
|
|
||||||
_globals = globals()
|
globals_ = globals()
|
||||||
_local = {}
|
local = {}
|
||||||
_local["SearxSearch"] = SearxSearch
|
local["SearxSearch"] = SearxSearch
|
||||||
_globals.update(_local)
|
globals_.update(local)
|
||||||
|
|
||||||
schema_fields = {
|
schema_fields = {
|
||||||
"query": (str, Field(..., description="The query to search for.")),
|
"query": (str, Field(..., description="The query to search for.")),
|
||||||
|
|
@ -137,7 +137,7 @@ class SearXNGToolComponent(LCToolComponent):
|
||||||
searx_search_schema = create_model("SearxSearchSchema", **schema_fields)
|
searx_search_schema = create_model("SearxSearchSchema", **schema_fields)
|
||||||
|
|
||||||
return StructuredTool.from_function(
|
return StructuredTool.from_function(
|
||||||
func=_local["SearxSearch"].search,
|
func=local["SearxSearch"].search,
|
||||||
args_schema=searx_search_schema,
|
args_schema=searx_search_schema,
|
||||||
name="searxng_search_tool",
|
name="searxng_search_tool",
|
||||||
description="A tool that searches for tools using SearXNG.\nThe available categories are: "
|
description="A tool that searches for tools using SearXNG.\nThe available categories are: "
|
||||||
|
|
|
||||||
|
|
@ -134,19 +134,19 @@ class ChromaVectorStoreComponent(LCVectorStoreComponent):
|
||||||
self.status = ""
|
self.status = ""
|
||||||
return
|
return
|
||||||
|
|
||||||
_stored_documents_without_id = []
|
stored_documents_without_id = []
|
||||||
if self.allow_duplicates:
|
if self.allow_duplicates:
|
||||||
stored_data = []
|
stored_data = []
|
||||||
else:
|
else:
|
||||||
stored_data = chroma_collection_to_data(vector_store.get(limit=self.limit))
|
stored_data = chroma_collection_to_data(vector_store.get(limit=self.limit))
|
||||||
for value in deepcopy(stored_data):
|
for value in deepcopy(stored_data):
|
||||||
del value.id
|
del value.id
|
||||||
_stored_documents_without_id.append(value)
|
stored_documents_without_id.append(value)
|
||||||
|
|
||||||
documents = []
|
documents = []
|
||||||
for _input in self.ingest_data or []:
|
for _input in self.ingest_data or []:
|
||||||
if isinstance(_input, Data):
|
if isinstance(_input, Data):
|
||||||
if _input not in _stored_documents_without_id:
|
if _input not in stored_documents_without_id:
|
||||||
documents.append(_input.to_lc_document())
|
documents.append(_input.to_lc_document())
|
||||||
else:
|
else:
|
||||||
msg = "Vector Store Inputs must be Data objects."
|
msg = "Vector Store Inputs must be Data objects."
|
||||||
|
|
|
||||||
|
|
@ -417,20 +417,20 @@ class Component(CustomComponent):
|
||||||
for index, output in enumerate(frontend_node["outputs"]):
|
for index, output in enumerate(frontend_node["outputs"]):
|
||||||
if isinstance(output, dict):
|
if isinstance(output, dict):
|
||||||
try:
|
try:
|
||||||
_output = Output(**output)
|
output_ = Output(**output)
|
||||||
self._set_output_return_type(_output)
|
self._set_output_return_type(output_)
|
||||||
_output_dict = _output.model_dump()
|
output_dict = output_.model_dump()
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
msg = f"Invalid output: {e}"
|
msg = f"Invalid output: {e}"
|
||||||
raise ValueError(msg) from e
|
raise ValueError(msg) from e
|
||||||
elif isinstance(output, Output):
|
elif isinstance(output, Output):
|
||||||
# we need to serialize it
|
# we need to serialize it
|
||||||
self._set_output_return_type(output)
|
self._set_output_return_type(output)
|
||||||
_output_dict = output.model_dump()
|
output_dict = output.model_dump()
|
||||||
else:
|
else:
|
||||||
msg = f"Invalid output type: {type(output)}"
|
msg = f"Invalid output type: {type(output)}"
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
frontend_node["outputs"][index] = _output_dict
|
frontend_node["outputs"][index] = output_dict
|
||||||
return frontend_node
|
return frontend_node
|
||||||
|
|
||||||
def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict: # noqa: ARG002
|
def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict: # noqa: ARG002
|
||||||
|
|
@ -557,7 +557,7 @@ class Component(CustomComponent):
|
||||||
return getattr(value, output.method)
|
return getattr(value, output.method)
|
||||||
|
|
||||||
def _process_connection_or_parameter(self, key, value) -> None:
|
def _process_connection_or_parameter(self, key, value) -> None:
|
||||||
_input = self._get_or_create_input(key)
|
input_ = self._get_or_create_input(key)
|
||||||
# We need to check if callable AND if it is a method from a class that inherits from Component
|
# We need to check if callable AND if it is a method from a class that inherits from Component
|
||||||
if isinstance(value, Component):
|
if isinstance(value, Component):
|
||||||
# We need to find the Output that can connect to an input of the current component
|
# We need to find the Output that can connect to an input of the current component
|
||||||
|
|
@ -570,7 +570,7 @@ class Component(CustomComponent):
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
msg = f"Method {value.__name__} is not a valid output of {value.__self__.__class__.__name__}"
|
msg = f"Method {value.__name__} is not a valid output of {value.__self__.__class__.__name__}"
|
||||||
raise ValueError(msg) from e
|
raise ValueError(msg) from e
|
||||||
self._connect_to_component(key, value, _input)
|
self._connect_to_component(key, value, input_)
|
||||||
else:
|
else:
|
||||||
self._set_parameter_or_attribute(key, value)
|
self._set_parameter_or_attribute(key, value)
|
||||||
|
|
||||||
|
|
@ -589,18 +589,18 @@ class Component(CustomComponent):
|
||||||
try:
|
try:
|
||||||
return self._inputs[key]
|
return self._inputs[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
_input = self._get_fallback_input(name=key, display_name=key)
|
input_ = self._get_fallback_input(name=key, display_name=key)
|
||||||
self._inputs[key] = _input
|
self._inputs[key] = input_
|
||||||
self.inputs.append(_input)
|
self.inputs.append(input_)
|
||||||
return _input
|
return input_
|
||||||
|
|
||||||
def _connect_to_component(self, key, value, _input) -> None:
|
def _connect_to_component(self, key, value, input_) -> None:
|
||||||
component = value.__self__
|
component = value.__self__
|
||||||
self._components.append(component)
|
self._components.append(component)
|
||||||
output = component.get_output_by_method(value)
|
output = component.get_output_by_method(value)
|
||||||
self._add_edge(component, key, output, _input)
|
self._add_edge(component, key, output, input_)
|
||||||
|
|
||||||
def _add_edge(self, component, key, output, _input) -> None:
|
def _add_edge(self, component, key, output, input_) -> None:
|
||||||
self._edges.append(
|
self._edges.append(
|
||||||
{
|
{
|
||||||
"source": component._id,
|
"source": component._id,
|
||||||
|
|
@ -615,8 +615,8 @@ class Component(CustomComponent):
|
||||||
"targetHandle": {
|
"targetHandle": {
|
||||||
"fieldName": key,
|
"fieldName": key,
|
||||||
"id": self._id,
|
"id": self._id,
|
||||||
"inputTypes": _input.input_types,
|
"inputTypes": input_.input_types,
|
||||||
"type": _input.field_type,
|
"type": input_.field_type,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -785,7 +785,7 @@ class Component(CustomComponent):
|
||||||
|
|
||||||
def set_attributes(self, params: dict) -> None:
|
def set_attributes(self, params: dict) -> None:
|
||||||
self._validate_inputs(params)
|
self._validate_inputs(params)
|
||||||
_attributes = {}
|
attributes = {}
|
||||||
for key, value in params.items():
|
for key, value in params.items():
|
||||||
if key in self.__dict__ and value != getattr(self, key):
|
if key in self.__dict__ and value != getattr(self, key):
|
||||||
msg = (
|
msg = (
|
||||||
|
|
@ -793,11 +793,11 @@ class Component(CustomComponent):
|
||||||
f"that is a reserved word and cannot be used."
|
f"that is a reserved word and cannot be used."
|
||||||
)
|
)
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
_attributes[key] = value
|
attributes[key] = value
|
||||||
for key, input_obj in self._inputs.items():
|
for key, input_obj in self._inputs.items():
|
||||||
if key not in _attributes and key not in self._attributes:
|
if key not in attributes and key not in self._attributes:
|
||||||
_attributes[key] = input_obj.value or None
|
attributes[key] = input_obj.value or None
|
||||||
self._attributes.update(_attributes)
|
self._attributes.update(attributes)
|
||||||
|
|
||||||
def _set_outputs(self, outputs: list[dict]) -> None:
|
def _set_outputs(self, outputs: list[dict]) -> None:
|
||||||
self.outputs = [Output(**output) for output in outputs]
|
self.outputs = [Output(**output) for output in outputs]
|
||||||
|
|
@ -826,10 +826,10 @@ class Component(CustomComponent):
|
||||||
inputs = self.get_trace_as_inputs()
|
inputs = self.get_trace_as_inputs()
|
||||||
metadata = self.get_trace_as_metadata()
|
metadata = self.get_trace_as_metadata()
|
||||||
async with self._tracing_service.trace_context(self, self.trace_name, inputs, metadata):
|
async with self._tracing_service.trace_context(self, self.trace_name, inputs, metadata):
|
||||||
_results, _artifacts = await self._build_results()
|
results, artifacts = await self._build_results()
|
||||||
self._tracing_service.set_outputs(self.trace_name, _results)
|
self._tracing_service.set_outputs(self.trace_name, results)
|
||||||
|
|
||||||
return _results, _artifacts
|
return results, artifacts
|
||||||
|
|
||||||
async def _build_without_tracing(self):
|
async def _build_without_tracing(self):
|
||||||
return await self._build_results()
|
return await self._build_results()
|
||||||
|
|
@ -864,8 +864,8 @@ class Component(CustomComponent):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def _build_results(self) -> tuple[dict, dict]:
|
async def _build_results(self) -> tuple[dict, dict]:
|
||||||
_results = {}
|
results = {}
|
||||||
_artifacts = {}
|
artifacts = {}
|
||||||
if hasattr(self, "_pre_run_setup"):
|
if hasattr(self, "_pre_run_setup"):
|
||||||
self._pre_run_setup()
|
self._pre_run_setup()
|
||||||
if hasattr(self, "outputs"):
|
if hasattr(self, "outputs"):
|
||||||
|
|
@ -885,7 +885,7 @@ class Component(CustomComponent):
|
||||||
self._current_output = output.name
|
self._current_output = output.name
|
||||||
method: Callable = getattr(self, output.method)
|
method: Callable = getattr(self, output.method)
|
||||||
if output.cache and output.value != UNDEFINED:
|
if output.cache and output.value != UNDEFINED:
|
||||||
_results[output.name] = output.value
|
results[output.name] = output.value
|
||||||
result = output.value
|
result = output.value
|
||||||
else:
|
else:
|
||||||
# If the method is asynchronous, we need to await it
|
# If the method is asynchronous, we need to await it
|
||||||
|
|
@ -900,7 +900,7 @@ class Component(CustomComponent):
|
||||||
and self._vertex.graph.flow_id is not None
|
and self._vertex.graph.flow_id is not None
|
||||||
):
|
):
|
||||||
result.set_flow_id(self._vertex.graph.flow_id)
|
result.set_flow_id(self._vertex.graph.flow_id)
|
||||||
_results[output.name] = result
|
results[output.name] = result
|
||||||
output.value = result
|
output.value = result
|
||||||
|
|
||||||
custom_repr = self.custom_repr()
|
custom_repr = self.custom_repr()
|
||||||
|
|
@ -927,15 +927,15 @@ class Component(CustomComponent):
|
||||||
artifact_type = get_artifact_type(artifact_value, result)
|
artifact_type = get_artifact_type(artifact_value, result)
|
||||||
raw, artifact_type = post_process_raw(raw, artifact_type)
|
raw, artifact_type = post_process_raw(raw, artifact_type)
|
||||||
artifact = {"repr": custom_repr, "raw": raw, "type": artifact_type}
|
artifact = {"repr": custom_repr, "raw": raw, "type": artifact_type}
|
||||||
_artifacts[output.name] = artifact
|
artifacts[output.name] = artifact
|
||||||
self._output_logs[output.name] = self._logs
|
self._output_logs[output.name] = self._logs
|
||||||
self._logs = []
|
self._logs = []
|
||||||
self._current_output = ""
|
self._current_output = ""
|
||||||
self._artifacts = _artifacts
|
self._artifacts = artifacts
|
||||||
self._results = _results
|
self._results = results
|
||||||
if self._tracing_service:
|
if self._tracing_service:
|
||||||
self._tracing_service.set_outputs(self.trace_name, _results)
|
self._tracing_service.set_outputs(self.trace_name, results)
|
||||||
return _results, _artifacts
|
return results, artifacts
|
||||||
|
|
||||||
def custom_repr(self):
|
def custom_repr(self):
|
||||||
if self.repr_value == "":
|
if self.repr_value == "":
|
||||||
|
|
|
||||||
|
|
@ -91,34 +91,34 @@ class DirectoryReader:
|
||||||
|
|
||||||
def read_file_content(self, file_path):
|
def read_file_content(self, file_path):
|
||||||
"""Read and return the content of a file."""
|
"""Read and return the content of a file."""
|
||||||
_file_path = Path(file_path)
|
file_path_ = Path(file_path)
|
||||||
if not _file_path.is_file():
|
if not file_path_.is_file():
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
with _file_path.open(encoding="utf-8") as file:
|
with file_path_.open(encoding="utf-8") as file:
|
||||||
# UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3069:
|
# UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3069:
|
||||||
# character maps to <undefined>
|
# character maps to <undefined>
|
||||||
return file.read()
|
return file.read()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
# This is happening in Windows, so we need to open the file in binary mode
|
# This is happening in Windows, so we need to open the file in binary mode
|
||||||
# The file is always just a python file, so we can safely read it as utf-8
|
# The file is always just a python file, so we can safely read it as utf-8
|
||||||
with _file_path.open("rb") as f:
|
with file_path_.open("rb") as f:
|
||||||
return f.read().decode("utf-8")
|
return f.read().decode("utf-8")
|
||||||
|
|
||||||
async def aread_file_content(self, file_path):
|
async def aread_file_content(self, file_path):
|
||||||
"""Read and return the content of a file."""
|
"""Read and return the content of a file."""
|
||||||
_file_path = Path(file_path)
|
file_path_ = Path(file_path)
|
||||||
if not _file_path.is_file():
|
if not file_path_.is_file():
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
async with async_open(_file_path, encoding="utf-8") as file:
|
async with async_open(file_path_, encoding="utf-8") as file:
|
||||||
# UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3069:
|
# UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3069:
|
||||||
# character maps to <undefined>
|
# character maps to <undefined>
|
||||||
return await file.read()
|
return await file.read()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
# This is happening in Windows, so we need to open the file in binary mode
|
# This is happening in Windows, so we need to open the file in binary mode
|
||||||
# The file is always just a python file, so we can safely read it as utf-8
|
# The file is always just a python file, so we can safely read it as utf-8
|
||||||
async with async_open(_file_path, "rb") as f:
|
async with async_open(file_path_, "rb") as f:
|
||||||
return (await f.read()).decode("utf-8")
|
return (await f.read()).decode("utf-8")
|
||||||
|
|
||||||
def get_files(self):
|
def get_files(self):
|
||||||
|
|
@ -227,16 +227,16 @@ class DirectoryReader:
|
||||||
logger.debug("-------------------- Building component menu list --------------------")
|
logger.debug("-------------------- Building component menu list --------------------")
|
||||||
|
|
||||||
for file_path in file_paths:
|
for file_path in file_paths:
|
||||||
_file_path = Path(file_path)
|
file_path_ = Path(file_path)
|
||||||
menu_name = _file_path.parent.name
|
menu_name = file_path_.parent.name
|
||||||
filename = _file_path.name
|
filename = file_path_.name
|
||||||
validation_result, result_content = self.process_file(file_path)
|
validation_result, result_content = self.process_file(file_path)
|
||||||
if not validation_result:
|
if not validation_result:
|
||||||
logger.error(f"Error while processing file {file_path}")
|
logger.error(f"Error while processing file {file_path}")
|
||||||
|
|
||||||
menu_result = self.find_menu(response, menu_name) or {
|
menu_result = self.find_menu(response, menu_name) or {
|
||||||
"name": menu_name,
|
"name": menu_name,
|
||||||
"path": str(_file_path.parent),
|
"path": str(file_path_.parent),
|
||||||
"components": [],
|
"components": [],
|
||||||
}
|
}
|
||||||
component_name = filename.split(".")[0]
|
component_name = filename.split(".")[0]
|
||||||
|
|
@ -307,16 +307,16 @@ class DirectoryReader:
|
||||||
results = await asyncio.gather(*tasks)
|
results = await asyncio.gather(*tasks)
|
||||||
|
|
||||||
for file_path, (validation_result, result_content) in zip(file_paths, results, strict=True):
|
for file_path, (validation_result, result_content) in zip(file_paths, results, strict=True):
|
||||||
_file_path = Path(file_path)
|
file_path_ = Path(file_path)
|
||||||
menu_name = _file_path.parent.name
|
menu_name = file_path_.parent.name
|
||||||
filename = _file_path.name
|
filename = file_path_.name
|
||||||
|
|
||||||
if not validation_result:
|
if not validation_result:
|
||||||
logger.error(f"Error while processing file {file_path}")
|
logger.error(f"Error while processing file {file_path}")
|
||||||
|
|
||||||
menu_result = self.find_menu(response, menu_name) or {
|
menu_result = self.find_menu(response, menu_name) or {
|
||||||
"name": menu_name,
|
"name": menu_name,
|
||||||
"path": str(_file_path.parent),
|
"path": str(file_path_.parent),
|
||||||
"components": [],
|
"components": [],
|
||||||
}
|
}
|
||||||
component_name = filename.split(".")[0]
|
component_name = filename.split(".")[0]
|
||||||
|
|
|
||||||
|
|
@ -44,15 +44,15 @@ def add_output_types(frontend_node: CustomComponentFrontendNode, return_types: l
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if return_type is str:
|
if return_type is str:
|
||||||
_return_type = "Text"
|
return_type_ = "Text"
|
||||||
elif hasattr(return_type, "__name__"):
|
elif hasattr(return_type, "__name__"):
|
||||||
_return_type = return_type.__name__
|
return_type_ = return_type.__name__
|
||||||
elif hasattr(return_type, "__class__"):
|
elif hasattr(return_type, "__class__"):
|
||||||
_return_type = return_type.__class__.__name__
|
return_type_ = return_type.__class__.__name__
|
||||||
else:
|
else:
|
||||||
_return_type = str(return_type)
|
return_type_ = str(return_type)
|
||||||
|
|
||||||
frontend_node.add_output_type(_return_type)
|
frontend_node.add_output_type(return_type_)
|
||||||
|
|
||||||
|
|
||||||
def reorder_fields(frontend_node: CustomComponentFrontendNode, field_order: list[str]) -> None:
|
def reorder_fields(frontend_node: CustomComponentFrontendNode, field_order: list[str]) -> None:
|
||||||
|
|
@ -200,7 +200,7 @@ def add_extra_fields(frontend_node, field_config, function_args) -> None:
|
||||||
"""Add extra fields to the frontend node."""
|
"""Add extra fields to the frontend node."""
|
||||||
if not function_args:
|
if not function_args:
|
||||||
return
|
return
|
||||||
_field_config = field_config.copy()
|
field_config_ = field_config.copy()
|
||||||
function_args_names = [arg["name"] for arg in function_args]
|
function_args_names = [arg["name"] for arg in function_args]
|
||||||
# If kwargs is in the function_args and not all field_config keys are in function_args
|
# If kwargs is in the function_args and not all field_config keys are in function_args
|
||||||
# then we need to add the extra fields
|
# then we need to add the extra fields
|
||||||
|
|
@ -214,7 +214,7 @@ def add_extra_fields(frontend_node, field_config, function_args) -> None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
field_name, field_type, field_value, field_required = get_field_properties(extra_field)
|
field_name, field_type, field_value, field_required = get_field_properties(extra_field)
|
||||||
config = _field_config.pop(field_name, {})
|
config = field_config_.pop(field_name, {})
|
||||||
frontend_node = add_new_custom_field(
|
frontend_node = add_new_custom_field(
|
||||||
frontend_node=frontend_node,
|
frontend_node=frontend_node,
|
||||||
field_name=field_name,
|
field_name=field_name,
|
||||||
|
|
@ -224,18 +224,18 @@ def add_extra_fields(frontend_node, field_config, function_args) -> None:
|
||||||
field_config=config,
|
field_config=config,
|
||||||
)
|
)
|
||||||
if "kwargs" in function_args_names and not all(key in function_args_names for key in field_config):
|
if "kwargs" in function_args_names and not all(key in function_args_names for key in field_config):
|
||||||
for field_name, config in _field_config.items():
|
for field_name, config in field_config_.items():
|
||||||
if "name" not in config or field_name == "code":
|
if "name" not in config or field_name == "code":
|
||||||
continue
|
continue
|
||||||
_config = config.model_dump() if isinstance(config, BaseModel) else config
|
config_ = config.model_dump() if isinstance(config, BaseModel) else config
|
||||||
_field_name, field_type, field_value, field_required = get_field_properties(extra_field=_config)
|
field_name_, field_type, field_value, field_required = get_field_properties(extra_field=config_)
|
||||||
frontend_node = add_new_custom_field(
|
frontend_node = add_new_custom_field(
|
||||||
frontend_node=frontend_node,
|
frontend_node=frontend_node,
|
||||||
field_name=_field_name,
|
field_name=field_name_,
|
||||||
field_type=field_type,
|
field_type=field_type,
|
||||||
field_value=field_value,
|
field_value=field_value,
|
||||||
field_required=field_required,
|
field_required=field_required,
|
||||||
field_config=_config,
|
field_config=config_,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,10 @@ class EventManager:
|
||||||
msg = "Event name must start with 'on_'"
|
msg = "Event name must start with 'on_'"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
if callback is None:
|
if callback is None:
|
||||||
_callback = partial(self.send_event, event_type=event_type)
|
callback_ = partial(self.send_event, event_type=event_type)
|
||||||
else:
|
else:
|
||||||
_callback = partial(callback, manager=self, event_type=event_type)
|
callback_ = partial(callback, manager=self, event_type=event_type)
|
||||||
self.events[name] = _callback
|
self.events[name] = callback_
|
||||||
|
|
||||||
def send_event(self, *, event_type: Literal["message", "error", "warning", "info", "token"], data: LoggableType):
|
def send_event(self, *, event_type: Literal["message", "error", "warning", "info", "token"], data: LoggableType):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ class SourceHandle(BaseModel):
|
||||||
|
|
||||||
@field_validator("name", mode="before")
|
@field_validator("name", mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_name(cls, v, _info):
|
def validate_name(cls, v, info):
|
||||||
if _info.data["data_type"] == "GroupNode":
|
if info.data["data_type"] == "GroupNode":
|
||||||
# 'OpenAIModel-u4iGV_text_output'
|
# 'OpenAIModel-u4iGV_text_output'
|
||||||
splits = v.split("_", 1)
|
splits = v.split("_", 1)
|
||||||
if len(splits) != 2: # noqa: PLR2004
|
if len(splits) != 2: # noqa: PLR2004
|
||||||
|
|
|
||||||
|
|
@ -1183,9 +1183,9 @@ class Graph:
|
||||||
for edge in vertex.edges:
|
for edge in vertex.edges:
|
||||||
for vid in [edge.source_id, edge.target_id]:
|
for vid in [edge.source_id, edge.target_id]:
|
||||||
if vid in self.vertex_map:
|
if vid in self.vertex_map:
|
||||||
_vertex = self.vertex_map[vid]
|
vertex_ = self.vertex_map[vid]
|
||||||
if not _vertex.frozen:
|
if not vertex_.frozen:
|
||||||
_vertex.build_params()
|
vertex_.build_params()
|
||||||
|
|
||||||
def _add_vertex(self, vertex: Vertex) -> None:
|
def _add_vertex(self, vertex: Vertex) -> None:
|
||||||
"""Adds a vertex to the graph."""
|
"""Adds a vertex to the graph."""
|
||||||
|
|
@ -1794,10 +1794,10 @@ class Graph:
|
||||||
|
|
||||||
def assert_streaming_sequence(self) -> None:
|
def assert_streaming_sequence(self) -> None:
|
||||||
for i in self.edges:
|
for i in self.edges:
|
||||||
_source = self.get_vertex(i.source_id)
|
source = self.get_vertex(i.source_id)
|
||||||
if "stream" in _source.params and _source.params["stream"] is True:
|
if "stream" in source.params and source.params["stream"] is True:
|
||||||
_target = self.get_vertex(i.target_id)
|
target = self.get_vertex(i.target_id)
|
||||||
if _target.vertex_type != "ChatOutput":
|
if target.vertex_type != "ChatOutput":
|
||||||
msg = (
|
msg = (
|
||||||
"Error: A 'streaming' vertex cannot be followed by a non-'chat output' vertex."
|
"Error: A 'streaming' vertex cannot be followed by a non-'chat output' vertex."
|
||||||
"Disable streaming to run the flow."
|
"Disable streaming to run the flow."
|
||||||
|
|
|
||||||
|
|
@ -162,15 +162,15 @@ def set_new_target_handle(proxy_id, new_edge, target_handle, node) -> None:
|
||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
new_edge["target"] = proxy_id
|
new_edge["target"] = proxy_id
|
||||||
_type = target_handle.get("type")
|
type_ = target_handle.get("type")
|
||||||
if _type is None:
|
if type_ is None:
|
||||||
msg = "The 'type' key must be present in target_handle."
|
msg = "The 'type' key must be present in target_handle."
|
||||||
raise KeyError(msg)
|
raise KeyError(msg)
|
||||||
|
|
||||||
field = target_handle["proxy"]["field"]
|
field = target_handle["proxy"]["field"]
|
||||||
new_target_handle = {
|
new_target_handle = {
|
||||||
"fieldName": field,
|
"fieldName": field,
|
||||||
"type": _type,
|
"type": type_,
|
||||||
"id": proxy_id,
|
"id": proxy_id,
|
||||||
}
|
}
|
||||||
if node["data"]["node"].get("flow"):
|
if node["data"]["node"].get("flow"):
|
||||||
|
|
|
||||||
|
|
@ -522,7 +522,7 @@ class Vertex:
|
||||||
stream_url = artifacts.get("stream_url")
|
stream_url = artifacts.get("stream_url")
|
||||||
files = [{"path": file} if isinstance(file, str) else file for file in artifacts.get("files", [])]
|
files = [{"path": file} if isinstance(file, str) else file for file in artifacts.get("files", [])]
|
||||||
component_id = self.id
|
component_id = self.id
|
||||||
_type = self.artifacts_type
|
type_ = self.artifacts_type
|
||||||
|
|
||||||
if isinstance(sender_name, Data | Message):
|
if isinstance(sender_name, Data | Message):
|
||||||
sender_name = sender_name.get_text()
|
sender_name = sender_name.get_text()
|
||||||
|
|
@ -536,7 +536,7 @@ class Vertex:
|
||||||
stream_url=stream_url,
|
stream_url=stream_url,
|
||||||
files=files,
|
files=files,
|
||||||
component_id=component_id,
|
component_id=component_id,
|
||||||
type=_type,
|
type=type_,
|
||||||
).model_dump(exclude_none=True)
|
).model_dump(exclude_none=True)
|
||||||
]
|
]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
|
||||||
|
|
@ -216,16 +216,16 @@ class InterfaceVertex(ComponentVertex):
|
||||||
if self.artifacts:
|
if self.artifacts:
|
||||||
# dump as a yaml string
|
# dump as a yaml string
|
||||||
if isinstance(self.artifacts, dict):
|
if isinstance(self.artifacts, dict):
|
||||||
_artifacts = [self.artifacts]
|
artifacts_ = [self.artifacts]
|
||||||
elif hasattr(self.artifacts, "data"):
|
elif hasattr(self.artifacts, "data"):
|
||||||
_artifacts = self.artifacts.data
|
artifacts_ = self.artifacts.data
|
||||||
else:
|
else:
|
||||||
_artifacts = self.artifacts
|
artifacts_ = self.artifacts
|
||||||
artifacts = []
|
artifacts = []
|
||||||
for artifact in _artifacts:
|
for artifact in artifacts_:
|
||||||
# artifacts = {k.title().replace("_", " "): v for k, v in self.artifacts.items() if v is not None}
|
# artifacts = {k.title().replace("_", " "): v for k, v in self.artifacts.items() if v is not None}
|
||||||
_artifact = {k.title().replace("_", " "): v for k, v in artifact.items() if v is not None}
|
artifact_ = {k.title().replace("_", " "): v for k, v in artifact.items() if v is not None}
|
||||||
artifacts.append(_artifact)
|
artifacts.append(artifact_)
|
||||||
return yaml.dump(artifacts, default_flow_style=False, allow_unicode=True)
|
return yaml.dump(artifacts, default_flow_style=False, allow_unicode=True)
|
||||||
return super().built_object_repr()
|
return super().built_object_repr()
|
||||||
|
|
||||||
|
|
@ -372,16 +372,16 @@ class InterfaceVertex(ComponentVertex):
|
||||||
complete_message = ""
|
complete_message = ""
|
||||||
if is_async:
|
if is_async:
|
||||||
async for message in iterator:
|
async for message in iterator:
|
||||||
_message = message.content if hasattr(message, "content") else message
|
message_ = message.content if hasattr(message, "content") else message
|
||||||
_message = _message.text if hasattr(_message, "text") else _message
|
message_ = message_.text if hasattr(message_, "text") else message_
|
||||||
yield _message
|
yield message_
|
||||||
complete_message += _message
|
complete_message += message_
|
||||||
else:
|
else:
|
||||||
for message in iterator:
|
for message in iterator:
|
||||||
_message = message.content if hasattr(message, "content") else message
|
message_ = message.content if hasattr(message, "content") else message
|
||||||
_message = _message.text if hasattr(_message, "text") else _message
|
message_ = message_.text if hasattr(message_, "text") else message_
|
||||||
yield _message
|
yield message_
|
||||||
complete_message += _message
|
complete_message += message_
|
||||||
|
|
||||||
files = self.params.get("files", [])
|
files = self.params.get("files", [])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,14 +44,14 @@ def data_to_text_list(template: str, data: Data | list[Data]) -> tuple[list[str]
|
||||||
if isinstance(data, (Data)):
|
if isinstance(data, (Data)):
|
||||||
data = [data]
|
data = [data]
|
||||||
# Check if there are any format strings in the template
|
# Check if there are any format strings in the template
|
||||||
_data = [
|
data_ = [
|
||||||
# If it is not a record, create one with the key "text"
|
# If it is not a record, create one with the key "text"
|
||||||
Data(text=value) if not isinstance(value, Data) else value
|
Data(text=value) if not isinstance(value, Data) else value
|
||||||
for value in data
|
for value in data
|
||||||
]
|
]
|
||||||
|
|
||||||
formatted_text = [template.format(data=value.data, **value.data) for value in _data]
|
formatted_text = [template.format(data=value.data, **value.data) for value in data_]
|
||||||
return formatted_text, _data
|
return formatted_text, data_
|
||||||
|
|
||||||
|
|
||||||
def data_to_text(template: str, data: Data | list[Data], sep: str = "\n") -> str:
|
def data_to_text(template: str, data: Data | list[Data], sep: str = "\n") -> str:
|
||||||
|
|
@ -83,13 +83,13 @@ def messages_to_text(template: str, messages: Message | list[Message]) -> str:
|
||||||
if isinstance(messages, (Message)):
|
if isinstance(messages, (Message)):
|
||||||
messages = [messages]
|
messages = [messages]
|
||||||
# Check if there are any format strings in the template
|
# Check if there are any format strings in the template
|
||||||
_messages = []
|
messages_ = []
|
||||||
for message in messages:
|
for message in messages:
|
||||||
# If it is not a message, create one with the key "text"
|
# If it is not a message, create one with the key "text"
|
||||||
if not isinstance(message, Message):
|
if not isinstance(message, Message):
|
||||||
msg = "All elements in the list must be of type Message."
|
msg = "All elements in the list must be of type Message."
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
_messages.append(message)
|
messages_.append(message)
|
||||||
|
|
||||||
formated_messages = [template.format(data=message.model_dump(), **message.model_dump()) for message in _messages]
|
formated_messages = [template.format(data=message.model_dump(), **message.model_dump()) for message in messages_]
|
||||||
return "\n".join(formated_messages)
|
return "\n".join(formated_messages)
|
||||||
|
|
|
||||||
|
|
@ -165,8 +165,8 @@ def update_new_output(data):
|
||||||
if "sourceHandle" in edge and "targetHandle" in edge:
|
if "sourceHandle" in edge and "targetHandle" in edge:
|
||||||
new_source_handle = scape_json_parse(edge["sourceHandle"])
|
new_source_handle = scape_json_parse(edge["sourceHandle"])
|
||||||
new_target_handle = scape_json_parse(edge["targetHandle"])
|
new_target_handle = scape_json_parse(edge["targetHandle"])
|
||||||
_id = new_source_handle["id"]
|
id_ = new_source_handle["id"]
|
||||||
source_node_index = next((index for (index, d) in enumerate(nodes) if d["id"] == _id), -1)
|
source_node_index = next((index for (index, d) in enumerate(nodes) if d["id"] == id_), -1)
|
||||||
source_node = nodes[source_node_index] if source_node_index != -1 else None
|
source_node = nodes[source_node_index] if source_node_index != -1 else None
|
||||||
|
|
||||||
if "baseClasses" in new_source_handle:
|
if "baseClasses" in new_source_handle:
|
||||||
|
|
@ -544,8 +544,8 @@ async def load_flows_from_directory() -> None:
|
||||||
msg = "Superuser not found in the database"
|
msg = "Superuser not found in the database"
|
||||||
raise NoResultFound(msg)
|
raise NoResultFound(msg)
|
||||||
user_id = user.id
|
user_id = user.id
|
||||||
_flows_path = Path(flows_path)
|
flows_path_ = Path(flows_path)
|
||||||
files = [f for f in _flows_path.iterdir() if f.is_file()]
|
files = [f for f in flows_path_.iterdir() if f.is_file()]
|
||||||
for file_path in files:
|
for file_path in files:
|
||||||
if file_path.suffix != ".json":
|
if file_path.suffix != ".json":
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -97,12 +97,12 @@ class StrInput(BaseInputMixin, ListableInputMixin, DatabaseLoadMixin, MetadataTr
|
||||||
"""Defines if the field will allow the user to open a text editor. Default is False."""
|
"""Defines if the field will allow the user to open a text editor. Default is False."""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_value(v: Any, _info):
|
def _validate_value(v: Any, info):
|
||||||
"""Validates the given value and returns the processed value.
|
"""Validates the given value and returns the processed value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
v (Any): The value to be validated.
|
v (Any): The value to be validated.
|
||||||
_info: Additional information about the input.
|
info: Additional information about the input.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The processed value.
|
The processed value.
|
||||||
|
|
@ -112,27 +112,27 @@ class StrInput(BaseInputMixin, ListableInputMixin, DatabaseLoadMixin, MetadataTr
|
||||||
"""
|
"""
|
||||||
if not isinstance(v, str) and v is not None:
|
if not isinstance(v, str) and v is not None:
|
||||||
# Keep the warning for now, but we should change it to an error
|
# Keep the warning for now, but we should change it to an error
|
||||||
if _info.data.get("input_types") and v.__class__.__name__ not in _info.data.get("input_types"):
|
if info.data.get("input_types") and v.__class__.__name__ not in info.data.get("input_types"):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
f"Invalid value type {type(v)} for input {_info.data.get('name')}. "
|
f"Invalid value type {type(v)} for input {info.data.get('name')}. "
|
||||||
f"Expected types: {_info.data.get('input_types')}",
|
f"Expected types: {info.data.get('input_types')}",
|
||||||
stacklevel=4,
|
stacklevel=4,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
f"Invalid value type {type(v)} for input {_info.data.get('name')}.",
|
f"Invalid value type {type(v)} for input {info.data.get('name')}.",
|
||||||
stacklevel=4,
|
stacklevel=4,
|
||||||
)
|
)
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@field_validator("value")
|
@field_validator("value")
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_value(cls, v: Any, _info):
|
def validate_value(cls, v: Any, info):
|
||||||
"""Validates the given value and returns the processed value.
|
"""Validates the given value and returns the processed value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
v (Any): The value to be validated.
|
v (Any): The value to be validated.
|
||||||
_info: Additional information about the input.
|
info: Additional information about the input.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The processed value.
|
The processed value.
|
||||||
|
|
@ -140,8 +140,8 @@ class StrInput(BaseInputMixin, ListableInputMixin, DatabaseLoadMixin, MetadataTr
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: If the value is not of a valid type or if the input is missing a required key.
|
ValueError: If the value is not of a valid type or if the input is missing a required key.
|
||||||
"""
|
"""
|
||||||
is_list = _info.data["is_list"]
|
is_list = info.data["is_list"]
|
||||||
return [cls._validate_value(vv, _info) for vv in v] if is_list else cls._validate_value(v, _info)
|
return [cls._validate_value(vv, info) for vv in v] if is_list else cls._validate_value(v, info)
|
||||||
|
|
||||||
|
|
||||||
class MessageInput(StrInput, InputTraceMixin):
|
class MessageInput(StrInput, InputTraceMixin):
|
||||||
|
|
@ -176,12 +176,12 @@ class MessageTextInput(StrInput, MetadataTraceMixin, InputTraceMixin, ToolModeMi
|
||||||
input_types: list[str] = ["Message"]
|
input_types: list[str] = ["Message"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_value(v: Any, _info):
|
def _validate_value(v: Any, info):
|
||||||
"""Validates the given value and returns the processed value.
|
"""Validates the given value and returns the processed value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
v (Any): The value to be validated.
|
v (Any): The value to be validated.
|
||||||
_info: Additional information about the input.
|
info: Additional information about the input.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The processed value.
|
The processed value.
|
||||||
|
|
@ -201,7 +201,7 @@ class MessageTextInput(StrInput, MetadataTraceMixin, InputTraceMixin, ToolModeMi
|
||||||
value = v.data[v.text_key]
|
value = v.data[v.text_key]
|
||||||
else:
|
else:
|
||||||
keys = ", ".join(v.data.keys())
|
keys = ", ".join(v.data.keys())
|
||||||
input_name = _info.data["name"]
|
input_name = info.data["name"]
|
||||||
msg = (
|
msg = (
|
||||||
f"The input to '{input_name}' must contain the key '{v.text_key}'."
|
f"The input to '{input_name}' must contain the key '{v.text_key}'."
|
||||||
f"You can set `text_key` to one of the following keys: {keys} "
|
f"You can set `text_key` to one of the following keys: {keys} "
|
||||||
|
|
@ -259,12 +259,12 @@ class SecretStrInput(BaseInputMixin, DatabaseLoadMixin):
|
||||||
|
|
||||||
@field_validator("value")
|
@field_validator("value")
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_value(cls, v: Any, _info):
|
def validate_value(cls, v: Any, info):
|
||||||
"""Validates the given value and returns the processed value.
|
"""Validates the given value and returns the processed value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
v (Any): The value to be validated.
|
v (Any): The value to be validated.
|
||||||
_info: Additional information about the input.
|
info: Additional information about the input.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The processed value.
|
The processed value.
|
||||||
|
|
@ -282,7 +282,7 @@ class SecretStrInput(BaseInputMixin, DatabaseLoadMixin):
|
||||||
value = v.data[v.text_key]
|
value = v.data[v.text_key]
|
||||||
else:
|
else:
|
||||||
keys = ", ".join(v.data.keys())
|
keys = ", ".join(v.data.keys())
|
||||||
input_name = _info.data["name"]
|
input_name = info.data["name"]
|
||||||
msg = (
|
msg = (
|
||||||
f"The input to '{input_name}' must contain the key '{v.text_key}'."
|
f"The input to '{input_name}' must contain the key '{v.text_key}'."
|
||||||
f"You can set `text_key` to one of the following keys: {keys} "
|
f"You can set `text_key` to one of the following keys: {keys} "
|
||||||
|
|
@ -294,7 +294,7 @@ class SecretStrInput(BaseInputMixin, DatabaseLoadMixin):
|
||||||
elif v is None:
|
elif v is None:
|
||||||
value = None
|
value = None
|
||||||
else:
|
else:
|
||||||
msg = f"Invalid value type `{type(v)}` for input `{_info.data['name']}`"
|
msg = f"Invalid value type `{type(v)}` for input `{info.data['name']}`"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
@ -313,12 +313,12 @@ class IntInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMixi
|
||||||
|
|
||||||
@field_validator("value")
|
@field_validator("value")
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_value(cls, v: Any, _info):
|
def validate_value(cls, v: Any, info):
|
||||||
"""Validates the given value and returns the processed value.
|
"""Validates the given value and returns the processed value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
v (Any): The value to be validated.
|
v (Any): The value to be validated.
|
||||||
_info: Additional information about the input.
|
info: Additional information about the input.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The processed value.
|
The processed value.
|
||||||
|
|
@ -327,7 +327,7 @@ class IntInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMixi
|
||||||
ValueError: If the value is not of a valid type or if the input is missing a required key.
|
ValueError: If the value is not of a valid type or if the input is missing a required key.
|
||||||
"""
|
"""
|
||||||
if v and not isinstance(v, int | float):
|
if v and not isinstance(v, int | float):
|
||||||
msg = f"Invalid value type {type(v)} for input {_info.data.get('name')}."
|
msg = f"Invalid value type {type(v)} for input {info.data.get('name')}."
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
if isinstance(v, float):
|
if isinstance(v, float):
|
||||||
v = int(v)
|
v = int(v)
|
||||||
|
|
@ -348,12 +348,12 @@ class FloatInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMi
|
||||||
|
|
||||||
@field_validator("value")
|
@field_validator("value")
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_value(cls, v: Any, _info):
|
def validate_value(cls, v: Any, info):
|
||||||
"""Validates the given value and returns the processed value.
|
"""Validates the given value and returns the processed value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
v (Any): The value to be validated.
|
v (Any): The value to be validated.
|
||||||
_info: Additional information about the input.
|
info: Additional information about the input.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The processed value.
|
The processed value.
|
||||||
|
|
@ -362,7 +362,7 @@ class FloatInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMi
|
||||||
ValueError: If the value is not of a valid type or if the input is missing a required key.
|
ValueError: If the value is not of a valid type or if the input is missing a required key.
|
||||||
"""
|
"""
|
||||||
if v and not isinstance(v, int | float):
|
if v and not isinstance(v, int | float):
|
||||||
msg = f"Invalid value type {type(v)} for input {_info.data.get('name')}."
|
msg = f"Invalid value type {type(v)} for input {info.data.get('name')}."
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
if isinstance(v, int):
|
if isinstance(v, int):
|
||||||
v = float(v)
|
v = float(v)
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,13 @@ from langflow.services.deps import get_settings_service
|
||||||
|
|
||||||
|
|
||||||
def load_file_into_dict(file_path: str) -> dict:
|
def load_file_into_dict(file_path: str) -> dict:
|
||||||
_file_path = Path(file_path)
|
file_path_ = Path(file_path)
|
||||||
if not _file_path.exists():
|
if not file_path_.exists():
|
||||||
msg = f"File not found: {file_path}"
|
msg = f"File not found: {file_path}"
|
||||||
raise FileNotFoundError(msg)
|
raise FileNotFoundError(msg)
|
||||||
|
|
||||||
# Files names are UUID, so we can't find the extension
|
# Files names are UUID, so we can't find the extension
|
||||||
with _file_path.open(encoding="utf-8") as file:
|
with file_path_.open(encoding="utf-8") as file:
|
||||||
try:
|
try:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
|
|
|
||||||
|
|
@ -148,8 +148,8 @@ def apply_tweaks(node: dict[str, Any], node_tweaks: dict[str, Any]) -> None:
|
||||||
template_data[tweak_name]["value"] = value
|
template_data[tweak_name]["value"] = value
|
||||||
elif isinstance(tweak_value, dict):
|
elif isinstance(tweak_value, dict):
|
||||||
for k, v in tweak_value.items():
|
for k, v in tweak_value.items():
|
||||||
_k = "file_path" if template_data[tweak_name]["type"] == "file" else k
|
k_ = "file_path" if template_data[tweak_name]["type"] == "file" else k
|
||||||
template_data[tweak_name][_k] = v
|
template_data[tweak_name][k_] = v
|
||||||
else:
|
else:
|
||||||
key = "file_path" if template_data[tweak_name]["type"] == "file" else "value"
|
key = "file_path" if template_data[tweak_name]["type"] == "file" else "value"
|
||||||
template_data[tweak_name][key] = tweak_value
|
template_data[tweak_name][key] = tweak_value
|
||||||
|
|
|
||||||
|
|
@ -53,13 +53,13 @@ def get_artifact_type(value, build_result=None) -> str:
|
||||||
|
|
||||||
|
|
||||||
def _to_list_of_dicts(raw):
|
def _to_list_of_dicts(raw):
|
||||||
_raw = []
|
raw_ = []
|
||||||
for item in raw:
|
for item in raw:
|
||||||
if hasattr(item, "dict") or hasattr(item, "model_dump"):
|
if hasattr(item, "dict") or hasattr(item, "model_dump"):
|
||||||
_raw.append(recursive_serialize_or_str(item))
|
raw_.append(recursive_serialize_or_str(item))
|
||||||
else:
|
else:
|
||||||
_raw.append(str(item))
|
raw_.append(str(item))
|
||||||
return _raw
|
return raw_
|
||||||
|
|
||||||
|
|
||||||
def post_process_raw(raw, artifact_type: str):
|
def post_process_raw(raw, artifact_type: str):
|
||||||
|
|
|
||||||
|
|
@ -92,9 +92,9 @@ def build_output_logs(vertex, result) -> dict:
|
||||||
payload = component_instance._artifacts
|
payload = component_instance._artifacts
|
||||||
output_result = payload.get(output["name"], {}).get("raw")
|
output_result = payload.get(output["name"], {}).get("raw")
|
||||||
message = get_message(output_result)
|
message = get_message(output_result)
|
||||||
_type = get_type(output_result)
|
type_ = get_type(output_result)
|
||||||
|
|
||||||
match _type:
|
match type_:
|
||||||
case LogType.STREAM if "stream_url" in message:
|
case LogType.STREAM if "stream_url" in message:
|
||||||
message = StreamURL(location=message["stream_url"])
|
message = StreamURL(location=message["stream_url"])
|
||||||
|
|
||||||
|
|
@ -112,6 +112,6 @@ def build_output_logs(vertex, result) -> dict:
|
||||||
message = message.to_dict(orient="records")
|
message = message.to_dict(orient="records")
|
||||||
message = [recursive_serialize_or_str(item) for item in message]
|
message = [recursive_serialize_or_str(item) for item in message]
|
||||||
name = output.get("name", f"output_{index}")
|
name = output.get("name", f"output_{index}")
|
||||||
outputs |= {name: OutputValue(message=message, type=_type).model_dump()}
|
outputs |= {name: OutputValue(message=message, type=type_).model_dump()}
|
||||||
|
|
||||||
return outputs
|
return outputs
|
||||||
|
|
|
||||||
|
|
@ -92,11 +92,11 @@ class CacheService(Subject, Service):
|
||||||
"image": "png",
|
"image": "png",
|
||||||
"pandas": "csv",
|
"pandas": "csv",
|
||||||
}
|
}
|
||||||
_extension = object_extensions[obj_type] if obj_type in object_extensions else type(obj).__name__.lower()
|
extension_ = object_extensions[obj_type] if obj_type in object_extensions else type(obj).__name__.lower()
|
||||||
self.current_cache[name] = {
|
self.current_cache[name] = {
|
||||||
"obj": obj,
|
"obj": obj,
|
||||||
"type": obj_type,
|
"type": obj_type,
|
||||||
"extension": extension or _extension,
|
"extension": extension or extension_,
|
||||||
}
|
}
|
||||||
self.notify()
|
self.notify()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -349,19 +349,19 @@ class Settings(BaseSettings):
|
||||||
logger.debug(f"Updating {key}")
|
logger.debug(f"Updating {key}")
|
||||||
if isinstance(getattr(self, key), list):
|
if isinstance(getattr(self, key), list):
|
||||||
# value might be a '[something]' string
|
# value might be a '[something]' string
|
||||||
_value = value
|
value_ = value
|
||||||
with contextlib.suppress(json.decoder.JSONDecodeError):
|
with contextlib.suppress(json.decoder.JSONDecodeError):
|
||||||
_value = orjson.loads(str(value))
|
value_ = orjson.loads(str(value))
|
||||||
if isinstance(_value, list):
|
if isinstance(value_, list):
|
||||||
for item in _value:
|
for item in value_:
|
||||||
_item = str(item) if isinstance(item, Path) else item
|
item_ = str(item) if isinstance(item, Path) else item
|
||||||
if _item not in getattr(self, key):
|
if item_ not in getattr(self, key):
|
||||||
getattr(self, key).append(_item)
|
getattr(self, key).append(item_)
|
||||||
logger.debug(f"Extended {key}")
|
logger.debug(f"Extended {key}")
|
||||||
else:
|
else:
|
||||||
_value = str(_value) if isinstance(_value, Path) else _value
|
value_ = str(value_) if isinstance(value_, Path) else value_
|
||||||
if _value not in getattr(self, key):
|
if value_ not in getattr(self, key):
|
||||||
getattr(self, key).append(_value)
|
getattr(self, key).append(value_)
|
||||||
logger.debug(f"Appended {key}")
|
logger.debug(f"Appended {key}")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
@ -393,11 +393,11 @@ def load_settings_from_yaml(file_path: str) -> Settings:
|
||||||
if "/" not in file_path:
|
if "/" not in file_path:
|
||||||
# Get current path
|
# Get current path
|
||||||
current_path = Path(__file__).resolve().parent
|
current_path = Path(__file__).resolve().parent
|
||||||
_file_path = Path(current_path) / file_path
|
file_path_ = Path(current_path) / file_path
|
||||||
else:
|
else:
|
||||||
_file_path = Path(file_path)
|
file_path_ = Path(file_path)
|
||||||
|
|
||||||
with _file_path.open(encoding="utf-8") as f:
|
with file_path_.open(encoding="utf-8") as f:
|
||||||
settings_dict = yaml.safe_load(f)
|
settings_dict = yaml.safe_load(f)
|
||||||
settings_dict = {k.upper(): v for k, v in settings_dict.items()}
|
settings_dict = {k.upper(): v for k, v in settings_dict.items()}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,11 @@ class SettingsService(Service):
|
||||||
if "/" not in file_path:
|
if "/" not in file_path:
|
||||||
# Get current path
|
# Get current path
|
||||||
current_path = Path(__file__).resolve().parent
|
current_path = Path(__file__).resolve().parent
|
||||||
_file_path = Path(current_path) / file_path
|
file_path_ = Path(current_path) / file_path
|
||||||
else:
|
else:
|
||||||
_file_path = Path(file_path)
|
file_path_ = Path(file_path)
|
||||||
|
|
||||||
with _file_path.open(encoding="utf-8") as f:
|
with file_path_.open(encoding="utf-8") as f:
|
||||||
settings_dict = yaml.safe_load(f)
|
settings_dict = yaml.safe_load(f)
|
||||||
settings_dict = {k.upper(): v for k, v in settings_dict.items()}
|
settings_dict = {k.upper(): v for k, v in settings_dict.items()}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -276,9 +276,7 @@ class ArizePhoenixTracer(BaseTracer):
|
||||||
def _convert_to_arize_phoenix_type(self, value):
|
def _convert_to_arize_phoenix_type(self, value):
|
||||||
"""Recursively converts a value to a Arize/Phoenix compatible type."""
|
"""Recursively converts a value to a Arize/Phoenix compatible type."""
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
for key, _value in value.copy().items():
|
value = {key: self._convert_to_arize_phoenix_type(val) for key, val in value.items()}
|
||||||
_value = self._convert_to_arize_phoenix_type(_value)
|
|
||||||
value[key] = _value
|
|
||||||
|
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
value = [self._convert_to_arize_phoenix_type(v) for v in value]
|
value = [self._convert_to_arize_phoenix_type(v) for v in value]
|
||||||
|
|
|
||||||
|
|
@ -78,15 +78,15 @@ class LangFuseTracer(BaseTracer):
|
||||||
if not self._ready:
|
if not self._ready:
|
||||||
return
|
return
|
||||||
|
|
||||||
_metadata: dict = {}
|
metadata_: dict = {}
|
||||||
_metadata |= {"trace_type": trace_type} if trace_type else {}
|
metadata_ |= {"trace_type": trace_type} if trace_type else {}
|
||||||
_metadata |= metadata or {}
|
metadata_ |= metadata or {}
|
||||||
|
|
||||||
_name = trace_name.removesuffix(f" ({trace_id})")
|
name = trace_name.removesuffix(f" ({trace_id})")
|
||||||
content_span = {
|
content_span = {
|
||||||
"name": _name,
|
"name": name,
|
||||||
"input": inputs,
|
"input": inputs,
|
||||||
"metadata": _metadata,
|
"metadata": metadata_,
|
||||||
"start_time": start_time,
|
"start_time": start_time,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,11 +110,11 @@ class LangFuseTracer(BaseTracer):
|
||||||
|
|
||||||
span = self.spans.get(trace_id, None)
|
span = self.spans.get(trace_id, None)
|
||||||
if span:
|
if span:
|
||||||
_output: dict = {}
|
output: dict = {}
|
||||||
_output |= outputs or {}
|
output |= outputs or {}
|
||||||
_output |= {"error": str(error)} if error else {}
|
output |= {"error": str(error)} if error else {}
|
||||||
_output |= {"logs": list(logs)} if logs else {}
|
output |= {"logs": list(logs)} if logs else {}
|
||||||
content = {"output": _output, "end_time": end_time}
|
content = {"output": output, "end_time": end_time}
|
||||||
span.update(**content)
|
span.update(**content)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -96,9 +96,7 @@ class LangSmithTracer(BaseTracer):
|
||||||
from langflow.schema.message import Message
|
from langflow.schema.message import Message
|
||||||
|
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
for key, _value in value.copy().items():
|
value = {key: self._convert_to_langchain_type(val) for key, val in value.items()}
|
||||||
_value = self._convert_to_langchain_type(_value)
|
|
||||||
value[key] = _value
|
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
value = [self._convert_to_langchain_type(v) for v in value]
|
value = [self._convert_to_langchain_type(v) for v in value]
|
||||||
elif isinstance(value, Message):
|
elif isinstance(value, Message):
|
||||||
|
|
|
||||||
|
|
@ -153,9 +153,7 @@ class LangWatchTracer(BaseTracer):
|
||||||
from langflow.schema.message import BaseMessage, Message
|
from langflow.schema.message import BaseMessage, Message
|
||||||
|
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
for key, _value in value.copy().items():
|
value = {key: self._convert_to_langwatch_type(val) for key, val in value.items()}
|
||||||
_value = self._convert_to_langwatch_type(_value)
|
|
||||||
value[key] = _value
|
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
value = [self._convert_to_langwatch_type(v) for v in value]
|
value = [self._convert_to_langwatch_type(v) for v in value]
|
||||||
elif isinstance(value, Message):
|
elif isinstance(value, Message):
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,7 @@ def convert_to_langchain_type(value):
|
||||||
from langflow.schema.message import Message
|
from langflow.schema.message import Message
|
||||||
|
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
for key, _value in value.copy().items():
|
value = {key: convert_to_langchain_type(val) for key, val in value.items()}
|
||||||
_value = convert_to_langchain_type(_value)
|
|
||||||
value[key] = _value
|
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
value = [convert_to_langchain_type(v) for v in value]
|
value = [convert_to_langchain_type(v) for v in value]
|
||||||
elif isinstance(value, Message):
|
elif isinstance(value, Message):
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class VariableService(Service):
|
||||||
value: str,
|
value: str,
|
||||||
*,
|
*,
|
||||||
default_fields: list[str],
|
default_fields: list[str],
|
||||||
_type: str,
|
type_: str,
|
||||||
session: AsyncSession,
|
session: AsyncSession,
|
||||||
) -> Variable:
|
) -> Variable:
|
||||||
"""Create a variable.
|
"""Create a variable.
|
||||||
|
|
@ -115,7 +115,7 @@ class VariableService(Service):
|
||||||
name: The name of the variable.
|
name: The name of the variable.
|
||||||
value: The value of the variable.
|
value: The value of the variable.
|
||||||
default_fields: The default fields of the variable.
|
default_fields: The default fields of the variable.
|
||||||
_type: The type of the variable.
|
type_: The type of the variable.
|
||||||
session: The database session.
|
session: The database session.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
|
||||||
|
|
@ -163,15 +163,15 @@ class KubernetesSecretService(VariableService, Service):
|
||||||
value: str,
|
value: str,
|
||||||
*,
|
*,
|
||||||
default_fields: list[str],
|
default_fields: list[str],
|
||||||
_type: str,
|
type_: str,
|
||||||
session: AsyncSession,
|
session: AsyncSession,
|
||||||
) -> Variable:
|
) -> Variable:
|
||||||
secret_name = encode_user_id(user_id)
|
secret_name = encode_user_id(user_id)
|
||||||
secret_key = name
|
secret_key = name
|
||||||
if _type == CREDENTIAL_TYPE:
|
if type_ == CREDENTIAL_TYPE:
|
||||||
secret_key = CREDENTIAL_TYPE + "_" + name
|
secret_key = CREDENTIAL_TYPE + "_" + name
|
||||||
else:
|
else:
|
||||||
_type = GENERIC_TYPE
|
type_ = GENERIC_TYPE
|
||||||
|
|
||||||
await asyncio.to_thread(
|
await asyncio.to_thread(
|
||||||
self.kubernetes_secrets.upsert_secret, secret_name=secret_name, data={secret_key: value}
|
self.kubernetes_secrets.upsert_secret, secret_name=secret_name, data={secret_key: value}
|
||||||
|
|
@ -179,7 +179,7 @@ class KubernetesSecretService(VariableService, Service):
|
||||||
|
|
||||||
variable_base = VariableCreate(
|
variable_base = VariableCreate(
|
||||||
name=name,
|
name=name,
|
||||||
type=_type,
|
type=type_,
|
||||||
value=auth_utils.encrypt_api_key(value, settings_service=self.settings_service),
|
value=auth_utils.encrypt_api_key(value, settings_service=self.settings_service),
|
||||||
default_fields=default_fields,
|
default_fields=default_fields,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -162,31 +162,31 @@ def encode_user_id(user_id: UUID | str) -> str:
|
||||||
return f"uuid-{str(user_id).lower()}"[:253]
|
return f"uuid-{str(user_id).lower()}"[:253]
|
||||||
|
|
||||||
# Convert string to lowercase
|
# Convert string to lowercase
|
||||||
_user_id = str(user_id).lower()
|
user_id_ = str(user_id).lower()
|
||||||
|
|
||||||
# If the user_id looks like an email, replace @ and . with allowed characters
|
# If the user_id looks like an email, replace @ and . with allowed characters
|
||||||
if "@" in _user_id or "." in _user_id:
|
if "@" in user_id_ or "." in user_id_:
|
||||||
_user_id = _user_id.replace("@", "-at-").replace(".", "-dot-")
|
user_id_ = user_id_.replace("@", "-at-").replace(".", "-dot-")
|
||||||
|
|
||||||
# Encode the user_id to base64
|
# Encode the user_id to base64
|
||||||
# encoded = base64.b64encode(user_id.encode("utf-8")).decode("utf-8")
|
# encoded = base64.b64encode(user_id.encode("utf-8")).decode("utf-8")
|
||||||
|
|
||||||
# Replace characters not allowed in Kubernetes names
|
# Replace characters not allowed in Kubernetes names
|
||||||
_user_id = _user_id.replace("+", "-").replace("/", "_").rstrip("=")
|
user_id_ = user_id_.replace("+", "-").replace("/", "_").rstrip("=")
|
||||||
|
|
||||||
# Ensure the name starts with an alphanumeric character
|
# Ensure the name starts with an alphanumeric character
|
||||||
if not _user_id[0].isalnum():
|
if not user_id_[0].isalnum():
|
||||||
_user_id = "a-" + _user_id
|
user_id_ = "a-" + user_id_
|
||||||
|
|
||||||
# Truncate to 253 characters (Kubernetes name length limit)
|
# Truncate to 253 characters (Kubernetes name length limit)
|
||||||
_user_id = _user_id[:253]
|
user_id_ = user_id_[:253]
|
||||||
|
|
||||||
if not all(c.isalnum() or c in "-_" for c in _user_id):
|
if not all(c.isalnum() or c in "-_" for c in user_id_):
|
||||||
msg = f"Invalid user_id: {_user_id}"
|
msg = f"Invalid user_id: {user_id_}"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
# Ensure the name ends with an alphanumeric character
|
# Ensure the name ends with an alphanumeric character
|
||||||
while not _user_id[-1].isalnum():
|
while not user_id_[-1].isalnum():
|
||||||
_user_id = _user_id[:-1]
|
user_id_ = user_id_[:-1]
|
||||||
|
|
||||||
return _user_id
|
return user_id_
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ class DatabaseVariableService(VariableService, Service):
|
||||||
name=var_name,
|
name=var_name,
|
||||||
value=value,
|
value=value,
|
||||||
default_fields=[],
|
default_fields=[],
|
||||||
_type=CREDENTIAL_TYPE,
|
type_=CREDENTIAL_TYPE,
|
||||||
session=session,
|
session=session,
|
||||||
)
|
)
|
||||||
logger.info(f"Processed {var_name} variable from environment.")
|
logger.info(f"Processed {var_name} variable from environment.")
|
||||||
|
|
@ -163,12 +163,12 @@ class DatabaseVariableService(VariableService, Service):
|
||||||
value: str,
|
value: str,
|
||||||
*,
|
*,
|
||||||
default_fields: Sequence[str] = (),
|
default_fields: Sequence[str] = (),
|
||||||
_type: str = GENERIC_TYPE,
|
type_: str = GENERIC_TYPE,
|
||||||
session: AsyncSession,
|
session: AsyncSession,
|
||||||
):
|
):
|
||||||
variable_base = VariableCreate(
|
variable_base = VariableCreate(
|
||||||
name=name,
|
name=name,
|
||||||
type=_type,
|
type=type_,
|
||||||
value=auth_utils.encrypt_api_key(value, settings_service=self.settings_service),
|
value=auth_utils.encrypt_api_key(value, settings_service=self.settings_service),
|
||||||
default_fields=list(default_fields),
|
default_fields=list(default_fields),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -192,10 +192,10 @@ class Output(BaseModel):
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return self.model_dump(by_alias=True, exclude_none=True)
|
return self.model_dump(by_alias=True, exclude_none=True)
|
||||||
|
|
||||||
def add_types(self, _type: list[Any]) -> None:
|
def add_types(self, type_: list[Any]) -> None:
|
||||||
if self.types is None:
|
if self.types is None:
|
||||||
self.types = []
|
self.types = []
|
||||||
self.types.extend([t for t in _type if t not in self.types])
|
self.types.extend([t for t in type_ if t not in self.types])
|
||||||
|
|
||||||
def set_selected(self) -> None:
|
def set_selected(self) -> None:
|
||||||
if not self.selected and self.types:
|
if not self.selected and self.types:
|
||||||
|
|
|
||||||
|
|
@ -50,14 +50,14 @@ class Template(BaseModel):
|
||||||
input_type = value.pop("_input_type", None)
|
input_type = value.pop("_input_type", None)
|
||||||
if input_type:
|
if input_type:
|
||||||
try:
|
try:
|
||||||
_input = instantiate_input(input_type, value)
|
input_ = instantiate_input(input_type, value)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = f"Error instantiating input {input_type}: {e}"
|
msg = f"Error instantiating input {input_type}: {e}"
|
||||||
raise ValueError(msg) from e
|
raise ValueError(msg) from e
|
||||||
else:
|
else:
|
||||||
_input = Input(**value)
|
input_ = Input(**value)
|
||||||
|
|
||||||
data["fields"].append(_input)
|
data["fields"].append(input_)
|
||||||
|
|
||||||
# Necessary for components with no inputs(?)
|
# Necessary for components with no inputs(?)
|
||||||
if "fields" not in data:
|
if "fields" not in data:
|
||||||
|
|
|
||||||
|
|
@ -40,29 +40,29 @@ def extract_uniont_types_from_generic_alias(return_type: GenericAlias) -> list:
|
||||||
return list(return_type.__args__)
|
return list(return_type.__args__)
|
||||||
|
|
||||||
|
|
||||||
def post_process_type(_type):
|
def post_process_type(type_):
|
||||||
"""Process the return type of a function.
|
"""Process the return type of a function.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
_type (Any): The return type of the function.
|
type_ (Any): The return type of the function.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Union[List[Any], Any]: The processed return type.
|
Union[List[Any], Any]: The processed return type.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if hasattr(_type, "__origin__") and _type.__origin__ in {list, list, SequenceABC}:
|
if hasattr(type_, "__origin__") and type_.__origin__ in {list, list, SequenceABC}:
|
||||||
_type = extract_inner_type_from_generic_alias(_type)
|
type_ = extract_inner_type_from_generic_alias(type_)
|
||||||
|
|
||||||
# If the return type is not a Union, then we just return it as a list
|
# If the return type is not a Union, then we just return it as a list
|
||||||
inner_type = _type[0] if isinstance(_type, list) else _type
|
inner_type = type_[0] if isinstance(type_, list) else type_
|
||||||
if (not hasattr(inner_type, "__origin__") or inner_type.__origin__ != Union) and (
|
if (not hasattr(inner_type, "__origin__") or inner_type.__origin__ != Union) and (
|
||||||
not hasattr(inner_type, "__class__") or inner_type.__class__.__name__ != "UnionType"
|
not hasattr(inner_type, "__class__") or inner_type.__class__.__name__ != "UnionType"
|
||||||
):
|
):
|
||||||
return _type if isinstance(_type, list) else [_type]
|
return type_ if isinstance(type_, list) else [type_]
|
||||||
# If the return type is a Union, then we need to parse it
|
# If the return type is a Union, then we need to parse it
|
||||||
_type = extract_union_types_from_generic_alias(_type)
|
type_ = extract_union_types_from_generic_alias(type_)
|
||||||
_type = set(chain.from_iterable([post_process_type(t) for t in _type]))
|
type_ = set(chain.from_iterable([post_process_type(t) for t in type_]))
|
||||||
return list(_type)
|
return list(type_)
|
||||||
|
|
||||||
|
|
||||||
def extract_union_types_from_generic_alias(return_type: GenericAlias) -> list:
|
def extract_union_types_from_generic_alias(return_type: GenericAlias) -> list:
|
||||||
|
|
|
||||||
|
|
@ -52,22 +52,22 @@ class ChatOutputResponse(BaseModel):
|
||||||
if not name:
|
if not name:
|
||||||
name = path.split("/")[-1]
|
name = path.split("/")[-1]
|
||||||
file["name"] = name
|
file["name"] = name
|
||||||
_type = file.get("type")
|
type_ = file.get("type")
|
||||||
if not _type:
|
if not type_:
|
||||||
# get the file type from the path
|
# get the file type from the path
|
||||||
extension = path.split(".")[-1]
|
extension = path.split(".")[-1]
|
||||||
file_types = set(TEXT_FILE_TYPES + IMG_FILE_TYPES)
|
file_types = set(TEXT_FILE_TYPES + IMG_FILE_TYPES)
|
||||||
if extension and extension in file_types:
|
if extension and extension in file_types:
|
||||||
_type = extension
|
type_ = extension
|
||||||
else:
|
else:
|
||||||
for file_type in file_types:
|
for file_type in file_types:
|
||||||
if file_type in path:
|
if file_type in path:
|
||||||
_type = file_type
|
type_ = file_type
|
||||||
break
|
break
|
||||||
if not _type:
|
if not type_:
|
||||||
msg = "File type is required."
|
msg = "File type is required."
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
file["type"] = _type
|
file["type"] = type_
|
||||||
|
|
||||||
return files
|
return files
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@ def build_template_from_function(name: str, type_to_loader_dict: dict, *, add_fu
|
||||||
|
|
||||||
for _type, v in type_to_loader_dict.items():
|
for _type, v in type_to_loader_dict.items():
|
||||||
if v.__annotations__["return"].__name__ == name:
|
if v.__annotations__["return"].__name__ == name:
|
||||||
_class = v.__annotations__["return"]
|
class_ = v.__annotations__["return"]
|
||||||
|
|
||||||
# Get the docstring
|
# Get the docstring
|
||||||
docs = parse(_class.__doc__)
|
docs = parse(class_.__doc__)
|
||||||
|
|
||||||
variables = {"_type": _type}
|
variables = {"_type": _type}
|
||||||
for class_field_items, value in _class.model_fields.items():
|
for class_field_items, value in class_.model_fields.items():
|
||||||
if class_field_items == "callback_manager":
|
if class_field_items == "callback_manager":
|
||||||
continue
|
continue
|
||||||
variables[class_field_items] = {}
|
variables[class_field_items] = {}
|
||||||
|
|
@ -49,7 +49,7 @@ def build_template_from_function(name: str, type_to_loader_dict: dict, *, add_fu
|
||||||
if name_ == "default_factory":
|
if name_ == "default_factory":
|
||||||
try:
|
try:
|
||||||
variables[class_field_items]["default"] = get_default_factory(
|
variables[class_field_items]["default"] = get_default_factory(
|
||||||
module=_class.__base__.__module__, function=value_
|
module=class_.__base__.__module__, function=value_
|
||||||
)
|
)
|
||||||
except Exception: # noqa: BLE001
|
except Exception: # noqa: BLE001
|
||||||
logger.opt(exception=True).debug(f"Error getting default factory for {value_}")
|
logger.opt(exception=True).debug(f"Error getting default factory for {value_}")
|
||||||
|
|
@ -60,7 +60,7 @@ def build_template_from_function(name: str, type_to_loader_dict: dict, *, add_fu
|
||||||
variables[class_field_items]["placeholder"] = docs.params.get(class_field_items, "")
|
variables[class_field_items]["placeholder"] = docs.params.get(class_field_items, "")
|
||||||
# Adding function to base classes to allow
|
# Adding function to base classes to allow
|
||||||
# the output to be a function
|
# the output to be a function
|
||||||
base_classes = get_base_classes(_class)
|
base_classes = get_base_classes(class_)
|
||||||
if add_function:
|
if add_function:
|
||||||
base_classes.append("Callable")
|
base_classes.append("Callable")
|
||||||
|
|
||||||
|
|
@ -88,15 +88,15 @@ def build_template_from_method(
|
||||||
|
|
||||||
for _type, v in type_to_cls_dict.items():
|
for _type, v in type_to_cls_dict.items():
|
||||||
if v.__name__ == class_name:
|
if v.__name__ == class_name:
|
||||||
_class = v
|
class_ = v
|
||||||
|
|
||||||
# Check if the method exists in this class
|
# Check if the method exists in this class
|
||||||
if not hasattr(_class, method_name):
|
if not hasattr(class_, method_name):
|
||||||
msg = f"Method {method_name} not found in class {class_name}"
|
msg = f"Method {method_name} not found in class {class_name}"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
# Get the method
|
# Get the method
|
||||||
method = getattr(_class, method_name)
|
method = getattr(class_, method_name)
|
||||||
|
|
||||||
# Get the docstring
|
# Get the docstring
|
||||||
docs = parse(method.__doc__)
|
docs = parse(method.__doc__)
|
||||||
|
|
@ -121,7 +121,7 @@ def build_template_from_method(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
base_classes = get_base_classes(_class)
|
base_classes = get_base_classes(class_)
|
||||||
|
|
||||||
# Adding function to base classes to allow the output to be a function
|
# Adding function to base classes to allow the output to be a function
|
||||||
if add_function:
|
if add_function:
|
||||||
|
|
@ -207,17 +207,17 @@ def format_dict(dictionary: dict[str, Any], class_name: str | None = None) -> di
|
||||||
if key == "_type":
|
if key == "_type":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
_type: str | type = get_type(value)
|
type_: str | type = get_type(value)
|
||||||
|
|
||||||
if "BaseModel" in str(_type):
|
if "BaseModel" in str(type_):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
_type = remove_optional_wrapper(_type)
|
type_ = remove_optional_wrapper(type_)
|
||||||
_type = check_list_type(_type, value)
|
type_ = check_list_type(type_, value)
|
||||||
_type = replace_mapping_with_dict(_type)
|
type_ = replace_mapping_with_dict(type_)
|
||||||
_type = get_type_from_union_literal(_type)
|
type_ = get_type_from_union_literal(type_)
|
||||||
|
|
||||||
value["type"] = get_formatted_type(key, _type)
|
value["type"] = get_formatted_type(key, type_)
|
||||||
value["show"] = should_show_field(value, key)
|
value["show"] = should_show_field(value, key)
|
||||||
value["password"] = is_password_field(key)
|
value["password"] = is_password_field(key)
|
||||||
value["multiline"] = is_multiline_field(key)
|
value["multiline"] = is_multiline_field(key)
|
||||||
|
|
@ -251,53 +251,53 @@ def get_type(value: Any) -> str | type:
|
||||||
The type value.
|
The type value.
|
||||||
"""
|
"""
|
||||||
# get "type" or "annotation" from the value
|
# get "type" or "annotation" from the value
|
||||||
_type = value.get("type") or value.get("annotation")
|
type_ = value.get("type") or value.get("annotation")
|
||||||
|
|
||||||
return _type if isinstance(_type, str) else _type.__name__
|
return type_ if isinstance(type_, str) else type_.__name__
|
||||||
|
|
||||||
|
|
||||||
def remove_optional_wrapper(_type: str | type) -> str:
|
def remove_optional_wrapper(type_: str | type) -> str:
|
||||||
"""Removes the 'Optional' wrapper from the type string.
|
"""Removes the 'Optional' wrapper from the type string.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The type string with the 'Optional' wrapper removed.
|
The type string with the 'Optional' wrapper removed.
|
||||||
"""
|
"""
|
||||||
if isinstance(_type, type):
|
if isinstance(type_, type):
|
||||||
_type = str(_type)
|
type_ = str(type_)
|
||||||
if "Optional" in _type:
|
if "Optional" in type_:
|
||||||
_type = _type.replace("Optional[", "")[:-1]
|
type_ = type_.replace("Optional[", "")[:-1]
|
||||||
|
|
||||||
return _type
|
return type_
|
||||||
|
|
||||||
|
|
||||||
def check_list_type(_type: str, value: dict[str, Any]) -> str:
|
def check_list_type(type_: str, value: dict[str, Any]) -> str:
|
||||||
"""Checks if the type is a list type and modifies the value accordingly.
|
"""Checks if the type is a list type and modifies the value accordingly.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The modified type string.
|
The modified type string.
|
||||||
"""
|
"""
|
||||||
if any(list_type in _type for list_type in ["List", "Sequence", "Set"]):
|
if any(list_type in type_ for list_type in ["List", "Sequence", "Set"]):
|
||||||
_type = _type.replace("List[", "").replace("Sequence[", "").replace("Set[", "")[:-1]
|
type_ = type_.replace("List[", "").replace("Sequence[", "").replace("Set[", "")[:-1]
|
||||||
value["list"] = True
|
value["list"] = True
|
||||||
else:
|
else:
|
||||||
value["list"] = False
|
value["list"] = False
|
||||||
|
|
||||||
return _type
|
return type_
|
||||||
|
|
||||||
|
|
||||||
def replace_mapping_with_dict(_type: str) -> str:
|
def replace_mapping_with_dict(type_: str) -> str:
|
||||||
"""Replaces 'Mapping' with 'dict' in the type string.
|
"""Replaces 'Mapping' with 'dict' in the type string.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The modified type string.
|
The modified type string.
|
||||||
"""
|
"""
|
||||||
if "Mapping" in _type:
|
if "Mapping" in type_:
|
||||||
_type = _type.replace("Mapping", "dict")
|
type_ = type_.replace("Mapping", "dict")
|
||||||
|
|
||||||
return _type
|
return type_
|
||||||
|
|
||||||
|
|
||||||
def get_formatted_type(key: str, _type: str) -> str:
|
def get_formatted_type(key: str, type_: str) -> str:
|
||||||
"""Formats the type value based on the given key.
|
"""Formats the type value based on the given key.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -309,7 +309,7 @@ def get_formatted_type(key: str, _type: str) -> str:
|
||||||
if key == "max_value_length":
|
if key == "max_value_length":
|
||||||
return "int"
|
return "int"
|
||||||
|
|
||||||
return _type
|
return type_
|
||||||
|
|
||||||
|
|
||||||
def should_show_field(value: dict[str, Any], key: str) -> bool:
|
def should_show_field(value: dict[str, Any], key: str) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -22,16 +22,16 @@ def get_version() -> str:
|
||||||
"langflow-nightly",
|
"langflow-nightly",
|
||||||
"langflow-base-nightly",
|
"langflow-base-nightly",
|
||||||
]
|
]
|
||||||
_version = None
|
version = None
|
||||||
for pkg_name in pkg_names:
|
for pkg_name in pkg_names:
|
||||||
with contextlib.suppress(ImportError, metadata.PackageNotFoundError):
|
with contextlib.suppress(ImportError, metadata.PackageNotFoundError):
|
||||||
_version = metadata.version(pkg_name)
|
version = metadata.version(pkg_name)
|
||||||
|
|
||||||
if _version is None:
|
if version is None:
|
||||||
msg = f"Package not found from options {pkg_names}"
|
msg = f"Package not found from options {pkg_names}"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
return _version
|
return version
|
||||||
|
|
||||||
|
|
||||||
def is_pre_release(v: str) -> bool:
|
def is_pre_release(v: str) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -222,13 +222,13 @@ def distributed_client_fixture(
|
||||||
monkeypatch.undo()
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
|
||||||
def get_graph(_type="basic"):
|
def get_graph(type_="basic"):
|
||||||
"""Get a graph from a json file."""
|
"""Get a graph from a json file."""
|
||||||
if _type == "basic":
|
if type_ == "basic":
|
||||||
path = pytest.BASIC_EXAMPLE_PATH
|
path = pytest.BASIC_EXAMPLE_PATH
|
||||||
elif _type == "complex":
|
elif type_ == "complex":
|
||||||
path = pytest.COMPLEX_EXAMPLE_PATH
|
path = pytest.COMPLEX_EXAMPLE_PATH
|
||||||
elif _type == "openapi":
|
elif type_ == "openapi":
|
||||||
path = pytest.OPENAPI_EXAMPLE_PATH
|
path = pytest.OPENAPI_EXAMPLE_PATH
|
||||||
|
|
||||||
with path.open(encoding="utf-8") as f:
|
with path.open(encoding="utf-8") as f:
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,10 @@ async def test_delete_api_key_route(client: AsyncClient, logged_in_headers, acti
|
||||||
"api_key": "string",
|
"api_key": "string",
|
||||||
"user_id": str(active_user.id),
|
"user_id": str(active_user.id),
|
||||||
}
|
}
|
||||||
_response = await client.post("api/v1/api_key/", json=basic_case, headers=logged_in_headers)
|
response_ = await client.post("api/v1/api_key/", json=basic_case, headers=logged_in_headers)
|
||||||
_id = _response.json()["id"]
|
id_ = response_.json()["id"]
|
||||||
|
|
||||||
response = await client.delete(f"api/v1/api_key/{_id}", headers=logged_in_headers)
|
response = await client.delete(f"api/v1/api_key/{id_}", headers=logged_in_headers)
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,9 @@ async def test_read_flow(client: AsyncClient, logged_in_headers):
|
||||||
"user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
"user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||||
"folder_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
"folder_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||||
}
|
}
|
||||||
_response = await client.post("api/v1/flows/", json=basic_case, headers=logged_in_headers)
|
response_ = await client.post("api/v1/flows/", json=basic_case, headers=logged_in_headers)
|
||||||
_id = _response.json()["id"]
|
id_ = response_.json()["id"]
|
||||||
response = await client.get(f"api/v1/flows/{_id}", headers=logged_in_headers)
|
response = await client.get(f"api/v1/flows/{id_}", headers=logged_in_headers)
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
|
@ -109,11 +109,11 @@ async def test_update_flow(client: AsyncClient, logged_in_headers):
|
||||||
"folder_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
"folder_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||||
}
|
}
|
||||||
basic_case["name"] = name
|
basic_case["name"] = name
|
||||||
_response = await client.post("api/v1/flows/", json=basic_case, headers=logged_in_headers)
|
response_ = await client.post("api/v1/flows/", json=basic_case, headers=logged_in_headers)
|
||||||
_id = _response.json()["id"]
|
id_ = response_.json()["id"]
|
||||||
|
|
||||||
basic_case["name"] = updated_name
|
basic_case["name"] = updated_name
|
||||||
response = await client.patch(f"api/v1/flows/{_id}", json=basic_case, headers=logged_in_headers)
|
response = await client.patch(f"api/v1/flows/{id_}", json=basic_case, headers=logged_in_headers)
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
assert isinstance(result, dict), "The result must be a dictionary"
|
assert isinstance(result, dict), "The result must be a dictionary"
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ async def test_read_folders(client: AsyncClient, logged_in_headers):
|
||||||
|
|
||||||
|
|
||||||
async def test_read_folder(client: AsyncClient, logged_in_headers, basic_case):
|
async def test_read_folder(client: AsyncClient, logged_in_headers, basic_case):
|
||||||
_response = await client.post("api/v1/folders/", json=basic_case, headers=logged_in_headers)
|
response_ = await client.post("api/v1/folders/", json=basic_case, headers=logged_in_headers)
|
||||||
_id = _response.json()["id"]
|
id_ = response_.json()["id"]
|
||||||
response = await client.get(f"api/v1/folders/{_id}", headers=logged_in_headers)
|
response = await client.get(f"api/v1/folders/{id_}", headers=logged_in_headers)
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
|
@ -51,9 +51,9 @@ async def test_read_folder(client: AsyncClient, logged_in_headers, basic_case):
|
||||||
async def test_update_folder(client: AsyncClient, logged_in_headers, basic_case):
|
async def test_update_folder(client: AsyncClient, logged_in_headers, basic_case):
|
||||||
update_case = basic_case.copy()
|
update_case = basic_case.copy()
|
||||||
update_case["name"] = "Updated Folder"
|
update_case["name"] = "Updated Folder"
|
||||||
_response = await client.post("api/v1/folders/", json=basic_case, headers=logged_in_headers)
|
response_ = await client.post("api/v1/folders/", json=basic_case, headers=logged_in_headers)
|
||||||
_id = _response.json()["id"]
|
id_ = response_.json()["id"]
|
||||||
response = await client.patch(f"api/v1/folders/{_id}", json=update_case, headers=logged_in_headers)
|
response = await client.patch(f"api/v1/folders/{id_}", json=update_case, headers=logged_in_headers)
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,10 @@ async def test_patch_user(client: AsyncClient, logged_in_headers_super_user):
|
||||||
name = "string"
|
name = "string"
|
||||||
updated_name = "string2"
|
updated_name = "string2"
|
||||||
basic_case = {"username": name, "password": "string"}
|
basic_case = {"username": name, "password": "string"}
|
||||||
_response = await client.post("api/v1/users/", json=basic_case)
|
response_ = await client.post("api/v1/users/", json=basic_case)
|
||||||
_id = _response.json()["id"]
|
id_ = response_.json()["id"]
|
||||||
basic_case["username"] = updated_name
|
basic_case["username"] = updated_name
|
||||||
response = await client.patch(f"api/v1/users/{_id}", json=basic_case, headers=logged_in_headers_super_user)
|
response = await client.patch(f"api/v1/users/{id_}", json=basic_case, headers=logged_in_headers_super_user)
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
|
@ -69,9 +69,9 @@ async def test_patch_user(client: AsyncClient, logged_in_headers_super_user):
|
||||||
|
|
||||||
|
|
||||||
async def test_reset_password(client: AsyncClient, logged_in_headers, active_user):
|
async def test_reset_password(client: AsyncClient, logged_in_headers, active_user):
|
||||||
_id = str(active_user.id)
|
id_ = str(active_user.id)
|
||||||
basic_case = {"username": "string", "password": "new_password"}
|
basic_case = {"username": "string", "password": "new_password"}
|
||||||
response = await client.patch(f"api/v1/users/{_id}/reset-password", json=basic_case, headers=logged_in_headers)
|
response = await client.patch(f"api/v1/users/{id_}/reset-password", json=basic_case, headers=logged_in_headers)
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
|
@ -88,9 +88,9 @@ async def test_reset_password(client: AsyncClient, logged_in_headers, active_use
|
||||||
|
|
||||||
async def test_delete_user(client: AsyncClient, logged_in_headers_super_user):
|
async def test_delete_user(client: AsyncClient, logged_in_headers_super_user):
|
||||||
basic_case = {"username": "string", "password": "string"}
|
basic_case = {"username": "string", "password": "string"}
|
||||||
_response = await client.post("api/v1/users/", json=basic_case)
|
response_ = await client.post("api/v1/users/", json=basic_case)
|
||||||
_id = _response.json()["id"]
|
id_ = response_.json()["id"]
|
||||||
response = await client.delete(f"api/v1/users/{_id}", headers=logged_in_headers_super_user)
|
response = await client.delete(f"api/v1/users/{id_}", headers=logged_in_headers_super_user)
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,8 @@ async def test_get_variable__typeerror(service, session: AsyncSession):
|
||||||
name = "name"
|
name = "name"
|
||||||
value = "value"
|
value = "value"
|
||||||
field = "session_id"
|
field = "session_id"
|
||||||
_type = CREDENTIAL_TYPE
|
type_ = CREDENTIAL_TYPE
|
||||||
await service.create_variable(user_id, name, value, _type=_type, session=session)
|
await service.create_variable(user_id, name, value, type_=type_, session=session)
|
||||||
|
|
||||||
with pytest.raises(TypeError) as exc:
|
with pytest.raises(TypeError) as exc:
|
||||||
await session.run_sync(_get_variable, service, user_id, name, field)
|
await session.run_sync(_get_variable, service, user_id, name, field)
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ def test_url_component():
|
||||||
url_component = data.URLComponent()
|
url_component = data.URLComponent()
|
||||||
url_component.set_attributes({"urls": ["https://langflow.org"]})
|
url_component.set_attributes({"urls": ["https://langflow.org"]})
|
||||||
# the url component can be used to load the contents of a website
|
# the url component can be used to load the contents of a website
|
||||||
_data = url_component.fetch_content()
|
data_ = url_component.fetch_content()
|
||||||
assert all(value.data for value in _data)
|
assert all(value.data for value in data_)
|
||||||
assert all(value.text for value in _data)
|
assert all(value.text for value in data_)
|
||||||
assert all(value.source for value in _data)
|
assert all(value.source for value in data_)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue