From 90e89a6137ef8347e345d575ed1838155e558b5d Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:20:31 -0300 Subject: [PATCH 001/592] refactor: Update MessageBase text attribute based on isinstance check. --- .../langflow/services/database/models/message/model.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/services/database/models/message/model.py b/src/backend/base/langflow/services/database/models/message/model.py index 0f2f675beef..69e7458c16e 100644 --- a/src/backend/base/langflow/services/database/models/message/model.py +++ b/src/backend/base/langflow/services/database/models/message/model.py @@ -36,10 +36,16 @@ def from_message(cls, message: "Message", flow_id: str | UUID | None = None): timestamp = message.timestamp if not flow_id and message.flow_id: flow_id = message.flow_id + if not isinstance(message.text, str): + # If the text is not a string, it means it could be + # async iterator so we simply add it as an empty string + message_text = "" + else: + message_text = message.text return cls( sender=message.sender, sender_name=message.sender_name, - text=message.text, + text=message_text, session_id=message.session_id, files=message.files or [], timestamp=timestamp, From 9ca83a0396a8f7a4f8ba36fcc18388f7029336e4 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:20:53 -0300 Subject: [PATCH 002/592] feat: Add update_message function to update a message in the database. --- .../services/database/models/message/crud.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/backend/base/langflow/services/database/models/message/crud.py diff --git a/src/backend/base/langflow/services/database/models/message/crud.py b/src/backend/base/langflow/services/database/models/message/crud.py new file mode 100644 index 00000000000..bfaec781509 --- /dev/null +++ b/src/backend/base/langflow/services/database/models/message/crud.py @@ -0,0 +1,23 @@ +from uuid import UUID + +from sqlmodel import Session + +from langflow.services.database.models.message.model import MessageTable, MessageUpdate +from langflow.services.deps import get_session + + +def update_message(message_id: UUID, message: MessageUpdate | dict, session: Session | None): + if not session: + session = get_session() + if not isinstance(message, MessageUpdate): + message = MessageUpdate(**message) + + db_message = session.get(MessageTable, message_id) + if not db_message: + raise ValueError("Message not found") + message_dict = message.model_dump(exclude_unset=True, exclude_none=True) + db_message.sqlmodel_update(message_dict) + session.add(db_message) + session.commit() + session.refresh(db_message) + return db_message From a79282e0a2926284ec075acc714d990ad419867b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:21:18 -0300 Subject: [PATCH 003/592] refactor(chat): Update imports and remove unnecessary config method in ChatComponent. --- src/backend/base/langflow/base/io/chat.py | 44 ++--------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index b9ff4f3d1e9..ebdfffe6a3e 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -1,55 +1,17 @@ -from typing import Optional, Union +import asyncio +from typing import AsyncIterator, Iterator, Optional, Union -from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES from langflow.custom import Component from langflow.memory import store_message from langflow.schema import Data from langflow.schema.message import Message -from langflow.utils.constants import MESSAGE_SENDER_USER, MESSAGE_SENDER_AI +from langflow.services.database.models.message.crud import update_message class ChatComponent(Component): display_name = "Chat Component" description = "Use as base for chat components." - def build_config(self): - return { - "input_value": { - "input_types": ["Text"], - "display_name": "Text", - "multiline": True, - }, - "sender": { - "options": [MESSAGE_SENDER_AI, MESSAGE_SENDER_USER], - "display_name": "Sender Type", - "advanced": True, - }, - "sender_name": {"display_name": "Sender Name", "advanced": True}, - "session_id": { - "display_name": "Session ID", - "info": "If provided, the message will be stored in the memory.", - "advanced": True, - }, - "return_message": { - "display_name": "Return Message", - "info": "Return the message as a Message containing the sender, sender_name, and session_id.", - "advanced": True, - }, - "data_template": { - "display_name": "Data Template", - "multiline": True, - "info": "In case of Message being a Data, this template will be used to convert it to text.", - "advanced": True, - }, - "files": { - "field_type": "file", - "display_name": "Files", - "file_types": TEXT_FILE_TYPES + IMG_FILE_TYPES, - "info": "Files to be sent with the message.", - "advanced": True, - }, - } - # Keep this method for backward compatibility def store_message( self, From c71f6b4553d8922394ee8f4071ace819d623553e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:21:30 -0300 Subject: [PATCH 004/592] refactor: Add stream_message method to ChatComponent. --- src/backend/base/langflow/base/io/chat.py | 28 ++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index ebdfffe6a3e..6434dc60bec 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -21,10 +21,36 @@ def store_message( message, flow_id=self.graph.flow_id, ) - + if len(messages) > 1: + raise ValueError("Only one message can be stored at a time.") + stored_message = messages[0] + if hasattr(self, "_callback") and self._callback and stored_message.id: + if not isinstance(message.text, str): + complete_message = self._stream_message(message, stored_message.id) + update_message(message_id=stored_message.id, message=dict(text=complete_message)) + self.vertex._added_message = stored_message self.status = messages return messages + def _stream_message(self, message: Message, message_id: str): + iterator = message.text + if not isinstance(iterator, (AsyncIterator, Iterator)): + raise ValueError("The message must be an iterator or an async iterator.") + complete_message: str = "" + if isinstance(iterator, AsyncIterator): + iterator = asyncio.ensure_future(iterator.__anext__()) + for chunk in iterator: + complete_message += chunk + data = { + "text": complete_message, + "chunk": chunk, + "sender": message.sender, + "sender_name": message.sender_name, + "id": message_id, + } + self._callback("token", data) + return complete_message + def build_with_data( self, sender: Optional[str] = "User", From 37a5ac60f1e42297d9022e44ec957894240e2d97 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:21:43 -0300 Subject: [PATCH 005/592] refactor: Update method call in ChatOutput component. --- src/backend/base/langflow/components/outputs/ChatOutput.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/backend/base/langflow/components/outputs/ChatOutput.py b/src/backend/base/langflow/components/outputs/ChatOutput.py index 45972712c8b..df386a94d80 100644 --- a/src/backend/base/langflow/components/outputs/ChatOutput.py +++ b/src/backend/base/langflow/components/outputs/ChatOutput.py @@ -71,9 +71,8 @@ def message_response(self) -> Message: and isinstance(message.text, str) and self.should_store_message ): - store_message( + self.store_message( message, - flow_id=self.graph.flow_id, ) self.message.value = message From ff3eec55ac49304299601d0165a16349b11c7c9a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:22:02 -0300 Subject: [PATCH 006/592] feat: Add callback function to custom component and update build_results signature. --- .../base/langflow/custom/custom_component/component.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index da14c673c62..a28b5624350 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -8,6 +8,7 @@ import yaml from pydantic import BaseModel +from langflow.graph.graph.schema import CallbackFunction from langflow.graph.state.model import create_state_model from langflow.helpers.custom import format_type from langflow.schema.artifact import get_artifact_type, post_process_raw @@ -56,6 +57,7 @@ def __init__(self, **kwargs): self._parameters = inputs or {} self._edges: list[EdgeData] = [] self._components: list[Component] = [] + self._callback = None self._state_model = None self.set_attributes(self._parameters) self._output_logs = {} @@ -77,6 +79,9 @@ def __init__(self, **kwargs): self._set_output_types() self.set_class_code() + def set_callback(self, callback: CallbackFunction | None = None): + self._callback = callback + def _reset_all_output_values(self): for output in self.outputs: setattr(output, "value", UNDEFINED) @@ -597,7 +602,9 @@ async def _build_with_tracing(self): async def _build_without_tracing(self): return await self._build_results() - async def build_results(self): + async def build_results( + self, + ): if self._tracing_service: return await self._build_with_tracing() return await self._build_without_tracing() @@ -634,6 +641,7 @@ async def _build_results(self): result.set_flow_id(self._vertex.graph.flow_id) _results[output.name] = result output.value = result + custom_repr = self.custom_repr() if custom_repr is None and isinstance(result, (dict, Data, str)): custom_repr = result From 61e5d72b7401068b6c4968bf8962af4c48bf6256 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:22:13 -0300 Subject: [PATCH 007/592] feat: Add callback parameter to instantiate_class function. --- src/backend/base/langflow/interface/initialize/loading.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/interface/initialize/loading.py b/src/backend/base/langflow/interface/initialize/loading.py index f0ae915adc3..a5b1d4ba576 100644 --- a/src/backend/base/langflow/interface/initialize/loading.py +++ b/src/backend/base/langflow/interface/initialize/loading.py @@ -1,7 +1,7 @@ import inspect import os import warnings -from typing import TYPE_CHECKING, Any, Type +from typing import TYPE_CHECKING, Any, Callable, Type import orjson from loguru import logger @@ -20,6 +20,7 @@ async def instantiate_class( vertex: "Vertex", user_id=None, + callback: Callable | None = None, ) -> Any: """Instantiate class from module type and key, and params""" @@ -38,6 +39,7 @@ async def instantiate_class( _parameters=custom_params, _vertex=vertex, _tracing_service=get_tracing_service(), + _callback=callback, ) return custom_component, custom_params From ef8815c6cb9e761c65417ce1b9124fbcd343fb26 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:22:34 -0300 Subject: [PATCH 008/592] feat(graph): Add callback functions for sync and async operations. --- src/backend/base/langflow/graph/graph/schema.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/graph/graph/schema.py b/src/backend/base/langflow/graph/graph/schema.py index 4a1dcc3a3a3..20fcfc970c4 100644 --- a/src/backend/base/langflow/graph/graph/schema.py +++ b/src/backend/base/langflow/graph/graph/schema.py @@ -1,9 +1,10 @@ -from typing import TYPE_CHECKING, NamedTuple +from typing import TYPE_CHECKING, NamedTuple, Protocol, Union from typing_extensions import NotRequired, TypedDict from langflow.graph.edge.schema import EdgeData from langflow.graph.vertex.schema import NodeData +from langflow.schema.log import LoggableType if TYPE_CHECKING: from langflow.graph.schema import ResultData @@ -44,3 +45,14 @@ class OutputConfigDict(TypedDict): class StartConfigDict(TypedDict): output: OutputConfigDict + + +class SyncCallbackFunction(Protocol): + def __call__(self, log: LoggableType) -> None: ... + + +class AsyncCallbackFunction(Protocol): + async def __call__(self, log: LoggableType) -> None: ... + + +CallbackFunction = Union[SyncCallbackFunction, AsyncCallbackFunction] From bf2618d0989b8d24044af7abe1fc59cc0145b557 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:22:44 -0300 Subject: [PATCH 009/592] feat: Add callback function support to vertex build process. --- src/backend/base/langflow/graph/vertex/base.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index 29620b1ebf2..50e36cf839e 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -13,6 +13,7 @@ from loguru import logger from langflow.exceptions.component import ComponentBuildException +from langflow.graph.graph.schema import CallbackFunction from langflow.graph.schema import INPUT_COMPONENTS, OUTPUT_COMPONENTS, InterfaceComponentTypes, ResultData from langflow.graph.utils import UnbuiltObject, UnbuiltResult, log_transaction from langflow.graph.vertex.schema import NodeData @@ -457,6 +458,7 @@ async def _build( self, fallback_to_env_vars, user_id=None, + callback: CallbackFunction | None = None, ): """ Initiate the build process. @@ -468,12 +470,19 @@ async def _build( raise ValueError(f"Base type for vertex {self.display_name} not found") if not self._custom_component: - custom_component, custom_params = await initialize.loading.instantiate_class(user_id=user_id, vertex=self) + custom_component, custom_params = await initialize.loading.instantiate_class( + user_id=user_id, vertex=self, callback=callback + ) else: custom_component = self._custom_component + self._custom_component.set_callback(callback) custom_params = initialize.loading.get_params(self.params) - await self._build_results(custom_component, custom_params, fallback_to_env_vars) + await self._build_results( + custom_component=custom_component, + custom_params=custom_params, + fallback_to_env_vars=fallback_to_env_vars, + ) self._validate_built_object() @@ -761,6 +770,7 @@ async def build( inputs: dict[str, Any] | None = None, files: list[str] | None = None, requester: Optional["Vertex"] = None, + callback: CallbackFunction | None = None, **kwargs, ) -> Any: async with self._lock: @@ -790,9 +800,9 @@ async def build( for step in self.steps: if step not in self.steps_ran: if inspect.iscoroutinefunction(step): - await step(user_id=user_id, **kwargs) + await step(user_id=user_id, callback=callback, **kwargs) else: - step(user_id=user_id, **kwargs) + step(user_id=user_id, callback=callback, **kwargs) self.steps_ran.append(step) self._finalize_build() From e1fe1be369deafc6d0c78fb82a23d89c4244d5b2 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:22:51 -0300 Subject: [PATCH 010/592] feat: Add handling for added message in InterfaceVertex class. --- src/backend/base/langflow/graph/vertex/types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/base/langflow/graph/vertex/types.py b/src/backend/base/langflow/graph/vertex/types.py index 1e4128d53fd..99caa148004 100644 --- a/src/backend/base/langflow/graph/vertex/types.py +++ b/src/backend/base/langflow/graph/vertex/types.py @@ -201,6 +201,7 @@ def _finalize_build(self): class InterfaceVertex(ComponentVertex): def __init__(self, data: NodeData, graph): super().__init__(data, graph=graph) + self._added_message = None self.steps = [self._build, self._run] def build_stream_url(self): From 4c41950b87aa8b4c672ec5a8b29a1aaf9caf6a18 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:23:02 -0300 Subject: [PATCH 011/592] feat: Add callback support to Graph methods. --- src/backend/base/langflow/graph/graph/base.py | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index e29f6c1d57c..95d14f63b20 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -4,11 +4,11 @@ import uuid import warnings from collections import defaultdict, deque +from collections.abc import Generator from datetime import datetime, timezone from functools import partial from itertools import chain from typing import TYPE_CHECKING, Any, Optional -from collections.abc import Generator import nest_asyncio from loguru import logger @@ -18,7 +18,7 @@ from langflow.graph.edge.schema import EdgeData from langflow.graph.graph.constants import Finish, lazy_load_vertex_dict from langflow.graph.graph.runnable_vertices_manager import RunnableVerticesManager -from langflow.graph.graph.schema import GraphData, GraphDump, StartConfigDict, VertexBuildResult +from langflow.graph.graph.schema import CallbackFunction, GraphData, GraphDump, StartConfigDict, VertexBuildResult from langflow.graph.graph.state_manager import GraphStateManager from langflow.graph.graph.state_model import create_state_model_from_graph from langflow.graph.graph.utils import find_start_component_id, process_flow, should_continue, sort_up_to_vertex @@ -256,7 +256,12 @@ def add_component_edge(self, source_id: str, output_input_tuple: tuple[str, str] } self._add_edge(edge_data) - async def async_start(self, inputs: list[dict] | None = None, max_iterations: int | None = None): + async def async_start( + self, + inputs: list[dict] | None = None, + max_iterations: int | None = None, + callback: CallbackFunction | None = None, + ): if not self._prepared: raise ValueError("Graph not prepared. Call prepare() first.") # The idea is for this to return a generator that yields the result of @@ -270,7 +275,7 @@ async def async_start(self, inputs: list[dict] | None = None, max_iterations: in yielded_counts: dict[str, int] = defaultdict(int) while should_continue(yielded_counts, max_iterations): - result = await self.astep() + result = await self.astep(callback) yield result if hasattr(result, "vertex"): yielded_counts[result.vertex.id] += 1 @@ -292,13 +297,14 @@ def start( inputs: list[dict] | None = None, max_iterations: int | None = None, config: StartConfigDict | None = None, + callback: CallbackFunction | None = None, ) -> Generator: if config is not None: self.__apply_config(config) #! Change this ASAP nest_asyncio.apply() loop = asyncio.get_event_loop() - async_gen = self.async_start(inputs, max_iterations) + async_gen = self.async_start(inputs, max_iterations, callback) async_gen_task = asyncio.ensure_future(async_gen.__anext__()) while True: @@ -1153,6 +1159,7 @@ async def astep( inputs: Optional["InputValueRequest"] = None, files: list[str] | None = None, user_id: str | None = None, + callback: CallbackFunction | None = None, ): if not self._prepared: raise ValueError("Graph not prepared. Call prepare() first.") @@ -1168,6 +1175,7 @@ async def astep( files=files, get_cache=chat_service.get_cache, set_cache=chat_service.set_cache, + callback=callback, ) next_runnable_vertices = await self.get_next_runnable_vertices( @@ -1219,6 +1227,7 @@ async def build_vertex( files: list[str] | None = None, user_id: str | None = None, fallback_to_env_vars: bool = False, + callback: CallbackFunction | None = None, ) -> VertexBuildResult: """ Builds a vertex in the graph. @@ -1273,7 +1282,11 @@ async def build_vertex( if should_build: await vertex.build( - user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars, files=files + user_id=user_id, + inputs=inputs_dict, + fallback_to_env_vars=fallback_to_env_vars, + files=files, + callback=callback, ) if set_cache is not None: vertex_dict = { From d2a200a60ea0c59693be601906201387ab762d27 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:23:11 -0300 Subject: [PATCH 012/592] feat(chat): Add callback function to build_vertices function. --- src/backend/base/langflow/api/v1/chat.py | 30 ++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index b5b1af9c687..ad398be48e3 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -4,6 +4,8 @@ import traceback import typing import uuid +from collections.abc import Callable +from functools import partial from typing import TYPE_CHECKING, Annotated from fastapi import APIRouter, BackgroundTasks, Body, Depends, HTTPException @@ -33,6 +35,7 @@ ) from langflow.exceptions.component import ComponentBuildException from langflow.graph.graph.base import Graph +from langflow.graph.graph.schema import CallbackFunction from langflow.graph.utils import log_vertex_build from langflow.schema.schema import OutputValue from langflow.services.auth.utils import get_current_active_user @@ -204,7 +207,9 @@ async def build_graph_and_get_order() -> tuple[list[str], list[str], "Graph"]: logger.exception(exc) raise HTTPException(status_code=500, detail=str(exc)) from exc - async def _build_vertex(vertex_id: str, graph: "Graph") -> VertexBuildResponse: + async def _build_vertex( + vertex_id: str, graph: "Graph", callback: CallbackFunction | None = None + ) -> VertexBuildResponse: flow_id_str = str(flow_id) next_runnable_vertices = [] @@ -222,6 +227,7 @@ async def _build_vertex(vertex_id: str, graph: "Graph") -> VertexBuildResponse: files=files, get_cache=chat_service.get_cache, set_cache=chat_service.set_cache, + callback=callback, ) result_dict = vertex_build_result.result_dict params = vertex_build_result.params @@ -324,7 +330,11 @@ def send_event(event_type: str, value: dict, queue: asyncio.Queue) -> None: queue.put_nowait((event_id, str_data.encode("utf-8"), time.time())) async def build_vertices( - vertex_id: str, graph: "Graph", queue: asyncio.Queue, client_consumed_queue: asyncio.Queue + vertex_id: str, + graph: "Graph", + queue: asyncio.Queue, + client_consumed_queue: asyncio.Queue, + callback: CallbackFunction | None = None, ) -> None: build_task = asyncio.create_task(await asyncio.to_thread(_build_vertex, vertex_id, graph)) try: @@ -346,7 +356,9 @@ async def build_vertices( if vertex_build_response.next_vertices_ids: tasks = [] for next_vertex_id in vertex_build_response.next_vertices_ids: - task = asyncio.create_task(build_vertices(next_vertex_id, graph, queue, client_consumed_queue)) + task = asyncio.create_task( + build_vertices(next_vertex_id, graph, queue, client_consumed_queue, callback) + ) tasks.append(task) try: await asyncio.gather(*tasks) @@ -356,6 +368,14 @@ async def build_vertices( return async def event_generator(queue: asyncio.Queue, client_consumed_queue: asyncio.Queue) -> None: + async def send_event_wrapper(queue) -> Callable: + partial_send_event = partial(send_event, queue=queue) + + def send_event_callback(event, data): + partial_send_event(event, data) + + return send_event_callback + if not data: # using another thread since the DB query is I/O bound vertices_task = asyncio.create_task(await asyncio.to_thread(build_graph_and_get_order)) @@ -386,7 +406,9 @@ async def event_generator(queue: asyncio.Queue, client_consumed_queue: asyncio.Q tasks = [] for vertex_id in ids: - task = asyncio.create_task(build_vertices(vertex_id, graph, queue, client_consumed_queue)) + task = asyncio.create_task( + build_vertices(vertex_id, graph, queue, client_consumed_queue, send_event_wrapper(queue)) + ) tasks.append(task) try: await asyncio.gather(*tasks) From fc9c9dfbe64abfb366100a1e97475028eb6debba Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 17:32:49 -0300 Subject: [PATCH 013/592] refactor: Simplify update_message function and use session_scope for session management. --- .../services/database/models/message/crud.py | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/backend/base/langflow/services/database/models/message/crud.py b/src/backend/base/langflow/services/database/models/message/crud.py index bfaec781509..05c566fe9af 100644 --- a/src/backend/base/langflow/services/database/models/message/crud.py +++ b/src/backend/base/langflow/services/database/models/message/crud.py @@ -1,23 +1,19 @@ from uuid import UUID -from sqlmodel import Session - from langflow.services.database.models.message.model import MessageTable, MessageUpdate -from langflow.services.deps import get_session +from langflow.services.deps import session_scope -def update_message(message_id: UUID, message: MessageUpdate | dict, session: Session | None): - if not session: - session = get_session() +def update_message(message_id: UUID, message: MessageUpdate | dict): if not isinstance(message, MessageUpdate): message = MessageUpdate(**message) - - db_message = session.get(MessageTable, message_id) - if not db_message: - raise ValueError("Message not found") - message_dict = message.model_dump(exclude_unset=True, exclude_none=True) - db_message.sqlmodel_update(message_dict) - session.add(db_message) - session.commit() - session.refresh(db_message) - return db_message + with session_scope() as session: + db_message = session.get(MessageTable, message_id) + if not db_message: + raise ValueError("Message not found") + message_dict = message.model_dump(exclude_unset=True, exclude_none=True) + db_message.sqlmodel_update(message_dict) + session.add(db_message) + session.commit() + session.refresh(db_message) + return db_message From 5e9388b9a8998cb3fd0195fc806631bac619673c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 17:32:57 -0300 Subject: [PATCH 014/592] fix: Call set_callback method if available on custom component. --- src/backend/base/langflow/interface/initialize/loading.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/interface/initialize/loading.py b/src/backend/base/langflow/interface/initialize/loading.py index a5b1d4ba576..23396c3f0d8 100644 --- a/src/backend/base/langflow/interface/initialize/loading.py +++ b/src/backend/base/langflow/interface/initialize/loading.py @@ -39,8 +39,9 @@ async def instantiate_class( _parameters=custom_params, _vertex=vertex, _tracing_service=get_tracing_service(), - _callback=callback, ) + if hasattr(custom_component, "set_callback"): + custom_component.set_callback(callback) return custom_component, custom_params From ea9ad4e4079f5652667fd2f22095633b8760efd4 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 17:33:08 -0300 Subject: [PATCH 015/592] refactor(chat): Update chat message chunk handling and ID conversion. --- src/backend/base/langflow/base/io/chat.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index 6434dc60bec..c4926e4009d 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -40,13 +40,13 @@ def _stream_message(self, message: Message, message_id: str): if isinstance(iterator, AsyncIterator): iterator = asyncio.ensure_future(iterator.__anext__()) for chunk in iterator: - complete_message += chunk + complete_message += chunk.content data = { "text": complete_message, - "chunk": chunk, + "chunk": chunk.content, "sender": message.sender, "sender_name": message.sender_name, - "id": message_id, + "id": str(message_id), } self._callback("token", data) return complete_message From 54f35cd74df9566de5cc91f335e36840dd25693c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 17:33:20 -0300 Subject: [PATCH 016/592] feat: Add null check before setting cache in build_vertex_stream function. --- src/backend/base/langflow/api/v1/chat.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index ad398be48e3..10fee8a76b6 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -661,6 +661,7 @@ async def build_vertex_stream( flow_id_str = str(flow_id) async def stream_vertex(): + graph = None try: cache = await chat_service.get_cache(flow_id_str) if not cache: @@ -714,7 +715,8 @@ async def stream_vertex(): yield str(StreamData(event="error", data={"error": exc_message})) finally: logger.debug("Closing stream") - await chat_service.set_cache(flow_id_str, graph) + if graph: + await chat_service.set_cache(flow_id_str, graph) yield str(StreamData(event="close", data={"message": "Stream closed"})) return StreamingResponse(stream_vertex(), media_type="text/event-stream") From 69296313924f3dbd9cf11345f60793d9cb1a6234 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 17:33:35 -0300 Subject: [PATCH 017/592] refactor: Fix send_event_wrapper function and add callback parameter to _build_vertex function. --- src/backend/base/langflow/api/v1/chat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 10fee8a76b6..ea35b7941f7 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -336,7 +336,7 @@ async def build_vertices( client_consumed_queue: asyncio.Queue, callback: CallbackFunction | None = None, ) -> None: - build_task = asyncio.create_task(await asyncio.to_thread(_build_vertex, vertex_id, graph)) + build_task = asyncio.create_task(await asyncio.to_thread(_build_vertex, vertex_id, graph, callback)) try: await build_task except asyncio.CancelledError: @@ -368,7 +368,7 @@ async def build_vertices( return async def event_generator(queue: asyncio.Queue, client_consumed_queue: asyncio.Queue) -> None: - async def send_event_wrapper(queue) -> Callable: + def send_event_wrapper(queue) -> Callable: partial_send_event = partial(send_event, queue=queue) def send_event_callback(event, data): From d1f9c7532e23757d05154d6aee9354f2e3214cf2 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 17:33:46 -0300 Subject: [PATCH 018/592] refactor: Simplify conditional statement and import order in ChatOutput. --- .../base/langflow/components/outputs/ChatOutput.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/backend/base/langflow/components/outputs/ChatOutput.py b/src/backend/base/langflow/components/outputs/ChatOutput.py index df386a94d80..66f5a8b4e07 100644 --- a/src/backend/base/langflow/components/outputs/ChatOutput.py +++ b/src/backend/base/langflow/components/outputs/ChatOutput.py @@ -1,9 +1,8 @@ from langflow.base.io.chat import ChatComponent from langflow.inputs import BoolInput from langflow.io import DropdownInput, MessageTextInput, Output -from langflow.memory import store_message from langflow.schema.message import Message -from langflow.utils.constants import MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_AI +from langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER class ChatOutput(ChatComponent): @@ -65,12 +64,7 @@ def message_response(self) -> Message: sender_name=self.sender_name, session_id=self.session_id, ) - if ( - self.session_id - and isinstance(message, Message) - and isinstance(message.text, str) - and self.should_store_message - ): + if self.session_id and isinstance(message, Message) and self.should_store_message: self.store_message( message, ) From e4db7bc89468b1c0a3542a9ac7f3f04386329c4e Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 19:24:53 +0000 Subject: [PATCH 019/592] [autofix.ci] apply automated fixes From abaa23e8f29faaeae77a5ef1af137b8352863e5c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 20 Aug 2024 11:43:48 -0300 Subject: [PATCH 020/592] refactor: move log method to Component class. --- .../custom/custom_component/component.py | 21 ++++++++++++++++++- .../custom_component/custom_component.py | 18 +--------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index a28b5624350..4efb98efca3 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -1,7 +1,7 @@ import inspect +from collections.abc import Callable from copy import deepcopy from typing import TYPE_CHECKING, Any, ClassVar, get_type_hints -from collections.abc import Callable from uuid import UUID import nanoid # type: ignore @@ -13,6 +13,7 @@ from langflow.helpers.custom import format_type from langflow.schema.artifact import get_artifact_type, post_process_raw from langflow.schema.data import Data +from langflow.schema.log import LoggableType from langflow.schema.message import Message from langflow.services.tracing.schema import Log from langflow.template.field.base import UNDEFINED, Input, Output @@ -727,3 +728,21 @@ def get_project_name(self): if hasattr(self, "_tracing_service"): return self._tracing_service.project_name return "Langflow" + + def log(self, message: LoggableType | list[LoggableType], name: Optional[str] = None): + """ + Logs a message. + + Args: + message (LoggableType | list[LoggableType]): The message to log. + """ + if name is None: + name = f"Log {len(self._logs) + 1}" + log = Log(message=message, type=get_artifact_type(message), name=name) + self._logs.append(log) + if self._tracing_service and self._vertex: + self._tracing_service.add_log(trace_name=self.trace_name, log=log) + if self._callback is not None: + event_name = "log" + data = log.model_dump() + self._callback(event_name, data) diff --git a/src/backend/base/langflow/custom/custom_component/custom_component.py b/src/backend/base/langflow/custom/custom_component/custom_component.py index e3357c22ddd..a6c5836b1cb 100644 --- a/src/backend/base/langflow/custom/custom_component/custom_component.py +++ b/src/backend/base/langflow/custom/custom_component/custom_component.py @@ -1,6 +1,6 @@ +from collections.abc import Callable, Sequence from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, Optional -from collections.abc import Callable, Sequence import yaml from cachetools import TTLCache @@ -10,9 +10,7 @@ from langflow.custom.custom_component.base_component import BaseComponent from langflow.helpers.flow import list_flows, load_flow, run_flow from langflow.schema import Data -from langflow.schema.artifact import get_artifact_type from langflow.schema.dotdict import dotdict -from langflow.schema.log import LoggableType from langflow.schema.schema import OutputValue from langflow.services.deps import get_storage_service, get_variable_service, session_scope from langflow.services.storage.service import StorageService @@ -509,20 +507,6 @@ def build(self, *args: Any, **kwargs: Any) -> Any: """ raise NotImplementedError - def log(self, message: LoggableType | list[LoggableType], name: str | None = None): - """ - Logs a message. - - Args: - message (LoggableType | list[LoggableType]): The message to log. - """ - if name is None: - name = f"Log {len(self._logs) + 1}" - log = Log(message=message, type=get_artifact_type(message), name=name) - self._logs.append(log) - if self._tracing_service and self._vertex: - self._tracing_service.add_log(trace_name=self.trace_name, log=log) - def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict): """ This function is called after the code validation is done. From 9a8019f33a8e6c64e9800b9162337537157b1cb7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 20 Aug 2024 11:44:02 -0300 Subject: [PATCH 021/592] refactor: Simplify CallbackFunction definition. --- src/backend/base/langflow/graph/graph/schema.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/backend/base/langflow/graph/graph/schema.py b/src/backend/base/langflow/graph/graph/schema.py index 20fcfc970c4..90f30656ce2 100644 --- a/src/backend/base/langflow/graph/graph/schema.py +++ b/src/backend/base/langflow/graph/graph/schema.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, NamedTuple, Protocol, Union +from typing import TYPE_CHECKING, NamedTuple, Protocol from typing_extensions import NotRequired, TypedDict @@ -47,12 +47,5 @@ class StartConfigDict(TypedDict): output: OutputConfigDict -class SyncCallbackFunction(Protocol): - def __call__(self, log: LoggableType) -> None: ... - - -class AsyncCallbackFunction(Protocol): - async def __call__(self, log: LoggableType) -> None: ... - - -CallbackFunction = Union[SyncCallbackFunction, AsyncCallbackFunction] +class CallbackFunction(Protocol): + def __call__(self, event_name: str, log: LoggableType) -> None: ... From 441015e153c8e156581c7febe6ea4fcecea9b9f6 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 20 Aug 2024 12:11:47 -0300 Subject: [PATCH 022/592] feat: Initialize _current_output attribute in Component class. --- src/backend/base/langflow/custom/custom_component/component.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 4efb98efca3..3235b005f82 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -37,6 +37,7 @@ class Component(CustomComponent): outputs: list[Output] = [] code_class_base_inheritance: ClassVar[str] = "Component" _output_logs: dict[str, Log] = {} + _current_output: str = "" def __init__(self, **kwargs): # if key starts with _ it is a config @@ -58,6 +59,7 @@ def __init__(self, **kwargs): self._parameters = inputs or {} self._edges: list[EdgeData] = [] self._components: list[Component] = [] + self._current_output = "" self._callback = None self._state_model = None self.set_attributes(self._parameters) From 6fec2b31c6e730670ac1ee9dcb20cdbbc4c4d2e5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 20 Aug 2024 12:12:06 -0300 Subject: [PATCH 023/592] feat: store current output name in custom component during processing. --- src/backend/base/langflow/custom/custom_component/component.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 3235b005f82..dff533cbc4f 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -626,6 +626,7 @@ async def _build_results(self): ): if output.method is None: raise ValueError(f"Output {output.name} does not have a method defined.") + self._current_output = output.name method: Callable = getattr(self, output.method) if output.cache and output.value != UNDEFINED: _results[output.name] = output.value @@ -672,6 +673,7 @@ async def _build_results(self): _artifacts[output.name] = artifact self._output_logs[output.name] = self._logs self._logs = [] + self._current_output = "" self._artifacts = _artifacts self._results = _results if self._tracing_service: From b2119d29d62c81af3090ffeb2e79554163ec4cad Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 20 Aug 2024 12:12:14 -0300 Subject: [PATCH 024/592] feat: Add current output and component ID to log data. --- src/backend/base/langflow/custom/custom_component/component.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index dff533cbc4f..e1e8e9d5cc1 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -749,4 +749,6 @@ def log(self, message: LoggableType | list[LoggableType], name: Optional[str] = if self._callback is not None: event_name = "log" data = log.model_dump() + data["output"] = self._current_output + data["component_id"] = self._id self._callback(event_name, data) From 93a7802b86cbbf05eaf1cdd7a817e80439bde15c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 20 Aug 2024 13:02:45 -0300 Subject: [PATCH 025/592] fix: Add condition to check current output before invoking callback. --- src/backend/base/langflow/custom/custom_component/component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index e1e8e9d5cc1..42df8be2cab 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -746,7 +746,7 @@ def log(self, message: LoggableType | list[LoggableType], name: Optional[str] = self._logs.append(log) if self._tracing_service and self._vertex: self._tracing_service.add_log(trace_name=self.trace_name, log=log) - if self._callback is not None: + if self._callback is not None and self._current_output: event_name = "log" data = log.model_dump() data["output"] = self._current_output From 8766c437b2777613d5cbf81a015a15bcac0d1e52 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 20 Aug 2024 14:30:16 -0300 Subject: [PATCH 026/592] refactor: Update callback to log_callback in graph methods. --- src/backend/base/langflow/api/v1/chat.py | 8 ++++---- src/backend/base/langflow/base/io/chat.py | 4 ++-- .../custom/custom_component/component.py | 12 ++++++------ src/backend/base/langflow/graph/graph/base.py | 18 +++++++++--------- .../base/langflow/graph/graph/schema.py | 2 +- src/backend/base/langflow/graph/vertex/base.py | 16 ++++++++-------- .../langflow/interface/initialize/loading.py | 9 +++++---- 7 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index ea35b7941f7..f944451104b 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -35,7 +35,7 @@ ) from langflow.exceptions.component import ComponentBuildException from langflow.graph.graph.base import Graph -from langflow.graph.graph.schema import CallbackFunction +from langflow.graph.graph.schema import LogCallbackFunction from langflow.graph.utils import log_vertex_build from langflow.schema.schema import OutputValue from langflow.services.auth.utils import get_current_active_user @@ -208,7 +208,7 @@ async def build_graph_and_get_order() -> tuple[list[str], list[str], "Graph"]: raise HTTPException(status_code=500, detail=str(exc)) from exc async def _build_vertex( - vertex_id: str, graph: "Graph", callback: CallbackFunction | None = None + vertex_id: str, graph: "Graph", log_callback: LogCallbackFunction | None = None ) -> VertexBuildResponse: flow_id_str = str(flow_id) @@ -227,7 +227,7 @@ async def _build_vertex( files=files, get_cache=chat_service.get_cache, set_cache=chat_service.set_cache, - callback=callback, + log_callback=log_callback, ) result_dict = vertex_build_result.result_dict params = vertex_build_result.params @@ -334,7 +334,7 @@ async def build_vertices( graph: "Graph", queue: asyncio.Queue, client_consumed_queue: asyncio.Queue, - callback: CallbackFunction | None = None, + callback: LogCallbackFunction | None = None, ) -> None: build_task = asyncio.create_task(await asyncio.to_thread(_build_vertex, vertex_id, graph, callback)) try: diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index c4926e4009d..facdf3a1e7e 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -24,7 +24,7 @@ def store_message( if len(messages) > 1: raise ValueError("Only one message can be stored at a time.") stored_message = messages[0] - if hasattr(self, "_callback") and self._callback and stored_message.id: + if hasattr(self, "_callback") and self._log_callback and stored_message.id: if not isinstance(message.text, str): complete_message = self._stream_message(message, stored_message.id) update_message(message_id=stored_message.id, message=dict(text=complete_message)) @@ -48,7 +48,7 @@ def _stream_message(self, message: Message, message_id: str): "sender_name": message.sender_name, "id": str(message_id), } - self._callback("token", data) + self._log_callback("token", data) return complete_message def build_with_data( diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 42df8be2cab..90a11034922 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -8,7 +8,7 @@ import yaml from pydantic import BaseModel -from langflow.graph.graph.schema import CallbackFunction +from langflow.graph.graph.schema import LogCallbackFunction from langflow.graph.state.model import create_state_model from langflow.helpers.custom import format_type from langflow.schema.artifact import get_artifact_type, post_process_raw @@ -60,7 +60,7 @@ def __init__(self, **kwargs): self._edges: list[EdgeData] = [] self._components: list[Component] = [] self._current_output = "" - self._callback = None + self._log_callback: LogCallbackFunction | None = None self._state_model = None self.set_attributes(self._parameters) self._output_logs = {} @@ -82,8 +82,8 @@ def __init__(self, **kwargs): self._set_output_types() self.set_class_code() - def set_callback(self, callback: CallbackFunction | None = None): - self._callback = callback + def set_log_callback(self, callback: LogCallbackFunction | None = None): + self._log_callback = callback def _reset_all_output_values(self): for output in self.outputs: @@ -746,9 +746,9 @@ def log(self, message: LoggableType | list[LoggableType], name: Optional[str] = self._logs.append(log) if self._tracing_service and self._vertex: self._tracing_service.add_log(trace_name=self.trace_name, log=log) - if self._callback is not None and self._current_output: + if self._log_callback is not None and self._current_output: event_name = "log" data = log.model_dump() data["output"] = self._current_output data["component_id"] = self._id - self._callback(event_name, data) + self._log_callback(event_name, data) diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index 95d14f63b20..2fe1368877c 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -18,7 +18,7 @@ from langflow.graph.edge.schema import EdgeData from langflow.graph.graph.constants import Finish, lazy_load_vertex_dict from langflow.graph.graph.runnable_vertices_manager import RunnableVerticesManager -from langflow.graph.graph.schema import CallbackFunction, GraphData, GraphDump, StartConfigDict, VertexBuildResult +from langflow.graph.graph.schema import GraphData, GraphDump, LogCallbackFunction, StartConfigDict, VertexBuildResult from langflow.graph.graph.state_manager import GraphStateManager from langflow.graph.graph.state_model import create_state_model_from_graph from langflow.graph.graph.utils import find_start_component_id, process_flow, should_continue, sort_up_to_vertex @@ -260,7 +260,7 @@ async def async_start( self, inputs: list[dict] | None = None, max_iterations: int | None = None, - callback: CallbackFunction | None = None, + log_callback: LogCallbackFunction | None = None, ): if not self._prepared: raise ValueError("Graph not prepared. Call prepare() first.") @@ -275,7 +275,7 @@ async def async_start( yielded_counts: dict[str, int] = defaultdict(int) while should_continue(yielded_counts, max_iterations): - result = await self.astep(callback) + result = await self.astep(log_callback=log_callback) yield result if hasattr(result, "vertex"): yielded_counts[result.vertex.id] += 1 @@ -297,14 +297,14 @@ def start( inputs: list[dict] | None = None, max_iterations: int | None = None, config: StartConfigDict | None = None, - callback: CallbackFunction | None = None, + log_callback: LogCallbackFunction | None = None, ) -> Generator: if config is not None: self.__apply_config(config) #! Change this ASAP nest_asyncio.apply() loop = asyncio.get_event_loop() - async_gen = self.async_start(inputs, max_iterations, callback) + async_gen = self.async_start(inputs, max_iterations, log_callback) async_gen_task = asyncio.ensure_future(async_gen.__anext__()) while True: @@ -1159,7 +1159,7 @@ async def astep( inputs: Optional["InputValueRequest"] = None, files: list[str] | None = None, user_id: str | None = None, - callback: CallbackFunction | None = None, + log_callback: LogCallbackFunction | None = None, ): if not self._prepared: raise ValueError("Graph not prepared. Call prepare() first.") @@ -1175,7 +1175,7 @@ async def astep( files=files, get_cache=chat_service.get_cache, set_cache=chat_service.set_cache, - callback=callback, + log_callback=log_callback, ) next_runnable_vertices = await self.get_next_runnable_vertices( @@ -1227,7 +1227,7 @@ async def build_vertex( files: list[str] | None = None, user_id: str | None = None, fallback_to_env_vars: bool = False, - callback: CallbackFunction | None = None, + log_callback: LogCallbackFunction | None = None, ) -> VertexBuildResult: """ Builds a vertex in the graph. @@ -1286,7 +1286,7 @@ async def build_vertex( inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars, files=files, - callback=callback, + log_callback=log_callback, ) if set_cache is not None: vertex_dict = { diff --git a/src/backend/base/langflow/graph/graph/schema.py b/src/backend/base/langflow/graph/graph/schema.py index 90f30656ce2..62ad99a270a 100644 --- a/src/backend/base/langflow/graph/graph/schema.py +++ b/src/backend/base/langflow/graph/graph/schema.py @@ -47,5 +47,5 @@ class StartConfigDict(TypedDict): output: OutputConfigDict -class CallbackFunction(Protocol): +class LogCallbackFunction(Protocol): def __call__(self, event_name: str, log: LoggableType) -> None: ... diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index 50e36cf839e..084d9f1c930 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -1,10 +1,10 @@ import ast import asyncio import inspect +import json import os import traceback import types -import json from enum import Enum from typing import TYPE_CHECKING, Any, Optional from collections.abc import AsyncIterator, Callable, Iterator, Mapping @@ -13,7 +13,7 @@ from loguru import logger from langflow.exceptions.component import ComponentBuildException -from langflow.graph.graph.schema import CallbackFunction +from langflow.graph.graph.schema import LogCallbackFunction from langflow.graph.schema import INPUT_COMPONENTS, OUTPUT_COMPONENTS, InterfaceComponentTypes, ResultData from langflow.graph.utils import UnbuiltObject, UnbuiltResult, log_transaction from langflow.graph.vertex.schema import NodeData @@ -458,7 +458,7 @@ async def _build( self, fallback_to_env_vars, user_id=None, - callback: CallbackFunction | None = None, + log_callback: LogCallbackFunction | None = None, ): """ Initiate the build process. @@ -471,11 +471,11 @@ async def _build( if not self._custom_component: custom_component, custom_params = await initialize.loading.instantiate_class( - user_id=user_id, vertex=self, callback=callback + user_id=user_id, vertex=self, log_callback=log_callback ) else: custom_component = self._custom_component - self._custom_component.set_callback(callback) + self._custom_component.set_log_callback(log_callback) custom_params = initialize.loading.get_params(self.params) await self._build_results( @@ -770,7 +770,7 @@ async def build( inputs: dict[str, Any] | None = None, files: list[str] | None = None, requester: Optional["Vertex"] = None, - callback: CallbackFunction | None = None, + log_callback: LogCallbackFunction | None = None, **kwargs, ) -> Any: async with self._lock: @@ -800,9 +800,9 @@ async def build( for step in self.steps: if step not in self.steps_ran: if inspect.iscoroutinefunction(step): - await step(user_id=user_id, callback=callback, **kwargs) + await step(user_id=user_id, log_callback=log_callback, **kwargs) else: - step(user_id=user_id, callback=callback, **kwargs) + step(user_id=user_id, log_callback=log_callback, **kwargs) self.steps_ran.append(step) self._finalize_build() diff --git a/src/backend/base/langflow/interface/initialize/loading.py b/src/backend/base/langflow/interface/initialize/loading.py index 23396c3f0d8..08970761e6f 100644 --- a/src/backend/base/langflow/interface/initialize/loading.py +++ b/src/backend/base/langflow/interface/initialize/loading.py @@ -1,13 +1,14 @@ import inspect import os import warnings -from typing import TYPE_CHECKING, Any, Callable, Type +from typing import TYPE_CHECKING, Any, Type import orjson from loguru import logger from pydantic import PydanticDeprecatedSince20 from langflow.custom.eval import eval_custom_component_code +from langflow.graph.graph.schema import LogCallbackFunction from langflow.schema import Data from langflow.schema.artifact import get_artifact_type, post_process_raw from langflow.services.deps import get_tracing_service @@ -20,7 +21,7 @@ async def instantiate_class( vertex: "Vertex", user_id=None, - callback: Callable | None = None, + log_callback: LogCallbackFunction | None = None, ) -> Any: """Instantiate class from module type and key, and params""" @@ -40,8 +41,8 @@ async def instantiate_class( _vertex=vertex, _tracing_service=get_tracing_service(), ) - if hasattr(custom_component, "set_callback"): - custom_component.set_callback(callback) + if hasattr(custom_component, "set_log_callback"): + custom_component.set_log_callback(log_callback) return custom_component, custom_params From 5e25adfdc885a6a830c786c0d1e081b1a4a8c778 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 20 Aug 2024 14:30:31 -0300 Subject: [PATCH 027/592] feat: Add test for callback graph execution with log messages. --- .../unit/graph/graph/test_callback_graph.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/backend/tests/unit/graph/graph/test_callback_graph.py diff --git a/src/backend/tests/unit/graph/graph/test_callback_graph.py b/src/backend/tests/unit/graph/graph/test_callback_graph.py new file mode 100644 index 00000000000..009bfb80114 --- /dev/null +++ b/src/backend/tests/unit/graph/graph/test_callback_graph.py @@ -0,0 +1,42 @@ +from langflow.components.outputs.ChatOutput import ChatOutput +from langflow.custom.custom_component.component import Component +from langflow.graph.graph.base import Graph +from langflow.inputs.inputs import IntInput +from langflow.schema.message import Message +from langflow.template.field.base import Output + + +class LogComponent(Component): + name = "LogComponent" + inputs = [IntInput(name="times", value=1)] + outputs = [Output(name="call_log", method="call_log_method")] + + def call_log_method(self) -> Message: + for i in range(self.times): + self.log(f"This is log message {i}", name=f"Log {i}") + return Message(text="Log called") + + +def test_callback_graph(): + logs: list[tuple[str, dict]] = [] + + def mock_callback(event_name, data): + logs.append((event_name, data)) + + log_component = LogComponent(_id="log_component") + log_component.set(times=3) + chat_output = ChatOutput(_id="chat_output") + chat_output.set(sender_name=log_component.call_log_method) + graph = Graph(start=log_component, end=chat_output) + + results = list(graph.start(log_callback=mock_callback)) + assert len(results) == 3 + assert len(logs) == 3 + assert all(isinstance(log, tuple) for log in logs) + assert all(isinstance(log[1], dict) for log in logs) + assert logs[0][0] == "log" + assert logs[0][1]["name"] == "Log 0" + assert logs[1][0] == "log" + assert logs[1][1]["name"] == "Log 1" + assert logs[2][0] == "log" + assert logs[2][1]["name"] == "Log 2" From d2b4bf7551858d37c973a4e350d7731cf23df72c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 23 Aug 2024 17:37:58 -0300 Subject: [PATCH 028/592] update projects --- .../starter_projects/Basic Prompting (Hello, World).json | 2 +- .../langflow/initial_setup/starter_projects/Blog Writer.json | 2 +- .../langflow/initial_setup/starter_projects/Complex Agent.json | 2 +- .../langflow/initial_setup/starter_projects/Document QA.json | 2 +- .../initial_setup/starter_projects/Hierarchical Agent.json | 2 +- .../langflow/initial_setup/starter_projects/Memory Chatbot.json | 2 +- .../initial_setup/starter_projects/Sequential Agent.json | 2 +- .../initial_setup/starter_projects/Vector Store RAG.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json index c79bdb1070d..de1a10263d8 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json @@ -476,7 +476,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_AI\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json b/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json index 24651b62089..e2e2b375190 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json @@ -664,7 +664,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_AI\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json index f946f5b8ac4..4ddc58fed4c 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json @@ -1197,7 +1197,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_AI\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json b/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json index 5a8fe5dabb0..a3f005bcc86 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json @@ -554,7 +554,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_AI\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json index ce0a3442a36..9a2d65d0bfe 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json @@ -901,7 +901,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_AI\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json b/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json index 089b84c536e..2e8d96092f4 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json @@ -825,7 +825,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_AI\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json index 258af3ae731..3ee4adf7be1 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json @@ -911,7 +911,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_AI\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json index c62b2492c7c..48e89da1fd5 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json @@ -1289,7 +1289,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_AI\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, From 0817895cc7217e3fd59838aef0714b3f7a555adc Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 27 Aug 2024 14:46:52 -0300 Subject: [PATCH 029/592] fix(chat.py): fix condition to check if message text is a string before updating message text in the database --- src/backend/base/langflow/base/io/chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index facdf3a1e7e..7ea8568a2ea 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -24,7 +24,7 @@ def store_message( if len(messages) > 1: raise ValueError("Only one message can be stored at a time.") stored_message = messages[0] - if hasattr(self, "_callback") and self._log_callback and stored_message.id: + if hasattr(self, "_log_callback") and self._log_callback and stored_message.id: if not isinstance(message.text, str): complete_message = self._stream_message(message, stored_message.id) update_message(message_id=stored_message.id, message=dict(text=complete_message)) From b46f5367d054a1beed14d042f22a2ae25a2af318 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 27 Aug 2024 14:51:45 -0300 Subject: [PATCH 030/592] refactor(ChatOutput.py): update ChatOutput class to correctly store and assign the message value to ensure consistency and avoid potential bugs --- src/backend/base/langflow/components/outputs/ChatOutput.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/components/outputs/ChatOutput.py b/src/backend/base/langflow/components/outputs/ChatOutput.py index 66f5a8b4e07..e8e5e51c060 100644 --- a/src/backend/base/langflow/components/outputs/ChatOutput.py +++ b/src/backend/base/langflow/components/outputs/ChatOutput.py @@ -65,10 +65,11 @@ def message_response(self) -> Message: session_id=self.session_id, ) if self.session_id and isinstance(message, Message) and self.should_store_message: - self.store_message( + stored_message = self.store_message( message, ) - self.message.value = message + self.message.value = stored_message + message = stored_message self.status = message return message From 49b73d198beda7fe995a67005a2d8c3dba3c3d06 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 27 Aug 2024 14:51:57 -0300 Subject: [PATCH 031/592] refactor(chat.py): update return type of store_message method to return a single Message object instead of a list of Messages refactor(chat.py): update logic to correctly handle updating and returning a single stored message object instead of a list of messages --- src/backend/base/langflow/base/io/chat.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index 7ea8568a2ea..0143a1de3ea 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -16,7 +16,7 @@ class ChatComponent(Component): def store_message( self, message: Message, - ) -> list[Message]: + ) -> Message: messages = store_message( message, flow_id=self.graph.flow_id, @@ -27,10 +27,11 @@ def store_message( if hasattr(self, "_log_callback") and self._log_callback and stored_message.id: if not isinstance(message.text, str): complete_message = self._stream_message(message, stored_message.id) - update_message(message_id=stored_message.id, message=dict(text=complete_message)) + message_table = update_message(message_id=stored_message.id, message=dict(text=complete_message)) + stored_message = Message(**message_table.model_dump()) self.vertex._added_message = stored_message - self.status = messages - return messages + self.status = stored_message + return stored_message def _stream_message(self, message: Message, message_id: str): iterator = message.text From 1db1b0ea30dad9aec4369a5c714e7c37bdcff99c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 27 Aug 2024 14:52:18 -0300 Subject: [PATCH 032/592] update starter projects --- .../starter_projects/Basic Prompting (Hello, World).json | 2 +- .../langflow/initial_setup/starter_projects/Blog Writer.json | 2 +- .../langflow/initial_setup/starter_projects/Complex Agent.json | 2 +- .../langflow/initial_setup/starter_projects/Document QA.json | 2 +- .../initial_setup/starter_projects/Hierarchical Agent.json | 2 +- .../langflow/initial_setup/starter_projects/Memory Chatbot.json | 2 +- .../initial_setup/starter_projects/Sequential Agent.json | 2 +- .../initial_setup/starter_projects/Vector Store RAG.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json index de1a10263d8..3852c989598 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json @@ -476,7 +476,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json b/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json index e2e2b375190..8897dd0bd8a 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json @@ -664,7 +664,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json index 4ddc58fed4c..938973817ad 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json @@ -1197,7 +1197,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json b/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json index a3f005bcc86..891ea75d0f6 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json @@ -554,7 +554,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json index 9a2d65d0bfe..08bc9dfcfe3 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json @@ -901,7 +901,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json b/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json index 2e8d96092f4..4b888376138 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json @@ -825,7 +825,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json index 3ee4adf7be1..7494a9b4af6 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json @@ -911,7 +911,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json index 48e89da1fd5..0253773f0fe 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json @@ -1289,7 +1289,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n self.store_message(\n message,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, From 28d00c9db352db5e0d4400adf48ccc2b1248451a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 16:42:52 -0300 Subject: [PATCH 033/592] refactor(component.py): update type hint for name parameter in log method to be more explicit --- .../base/langflow/custom/custom_component/component.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 90a11034922..10573871046 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -6,8 +6,6 @@ import nanoid # type: ignore import yaml -from pydantic import BaseModel - from langflow.graph.graph.schema import LogCallbackFunction from langflow.graph.state.model import create_state_model from langflow.helpers.custom import format_type @@ -20,6 +18,7 @@ from langflow.template.frontend_node.custom_components import ComponentFrontendNode from langflow.utils.async_helpers import run_until_complete from langflow.utils.util import find_closest_match +from pydantic import BaseModel from .custom_component import CustomComponent @@ -729,11 +728,11 @@ def to_tool(self): return ComponentTool(component=self) def get_project_name(self): - if hasattr(self, "_tracing_service"): + if hasattr(self, "_tracing_service") and self._tracing_service: return self._tracing_service.project_name return "Langflow" - def log(self, message: LoggableType | list[LoggableType], name: Optional[str] = None): + def log(self, message: LoggableType | list[LoggableType], name: str | None = None): """ Logs a message. From 6c2876bafc3b978e19535fc1395eb6039ef25d69 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:10:45 -0300 Subject: [PATCH 034/592] feat: Add EventManager class for managing events and event registration --- src/backend/base/langflow/events/__init__.py | 0 .../base/langflow/events/event_manager.py | 38 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/backend/base/langflow/events/__init__.py create mode 100644 src/backend/base/langflow/events/event_manager.py diff --git a/src/backend/base/langflow/events/__init__.py b/src/backend/base/langflow/events/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/backend/base/langflow/events/event_manager.py b/src/backend/base/langflow/events/event_manager.py new file mode 100644 index 00000000000..c5a099b9db2 --- /dev/null +++ b/src/backend/base/langflow/events/event_manager.py @@ -0,0 +1,38 @@ +import asyncio +import json +import time +import uuid +from collections.abc import Callable +from functools import partial + + +class EventManager: + def __init__(self, queue: asyncio.Queue): + self.queue = queue + self.events: dict[str, Callable] = {} + + def register_event(self, name: str, event_type: str): + self.events[name] = partial(self.send_event, event_type) + + def send_event(self, event_type: str, data: dict): + json_data = {"event": event_type, "data": data} + event_id = uuid.uuid4() + str_data = json.dumps(json_data) + "\n\n" + self.queue.put_nowait((event_id, str_data.encode("utf-8"), time.time())) + + def noop(self, data: dict): + pass + + def __getattr__(self, name: str) -> Callable[[dict], None]: + return self.events.get(name, self.noop) + + +def create_default_event_manager(queue): + manager = EventManager(queue) + manager.register_event("on_token", "token") + manager.register_event("on_vertices_sorted", "vertices_sorted") + manager.register_event("on_error", "error") + manager.register_event("on_end", "end") + manager.register_event("on_message", "message") + manager.register_event("on_end_vertex", "end_vertex") + return manager From ad37fc3551d63905d7f15e8db7c3c5fc2fd49663 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:19:45 -0300 Subject: [PATCH 035/592] refactor: Update log_callback to event_manager in custom component classes --- .../langflow/custom/custom_component/component.py | 11 ++++++----- src/backend/base/langflow/graph/vertex/base.py | 2 +- .../base/langflow/interface/initialize/loading.py | 8 ++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 10573871046..e51a02317bc 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -6,7 +6,9 @@ import nanoid # type: ignore import yaml -from langflow.graph.graph.schema import LogCallbackFunction +from pydantic import BaseModel + +from langflow.events.event_manager import EventManager from langflow.graph.state.model import create_state_model from langflow.helpers.custom import format_type from langflow.schema.artifact import get_artifact_type, post_process_raw @@ -18,7 +20,6 @@ from langflow.template.frontend_node.custom_components import ComponentFrontendNode from langflow.utils.async_helpers import run_until_complete from langflow.utils.util import find_closest_match -from pydantic import BaseModel from .custom_component import CustomComponent @@ -59,7 +60,7 @@ def __init__(self, **kwargs): self._edges: list[EdgeData] = [] self._components: list[Component] = [] self._current_output = "" - self._log_callback: LogCallbackFunction | None = None + self._event_manager: EventManager | None = None self._state_model = None self.set_attributes(self._parameters) self._output_logs = {} @@ -81,8 +82,8 @@ def __init__(self, **kwargs): self._set_output_types() self.set_class_code() - def set_log_callback(self, callback: LogCallbackFunction | None = None): - self._log_callback = callback + def set_event_manager(self, event_manager: EventManager | None = None): + self._event_manager = event_manager def _reset_all_output_values(self): for output in self.outputs: diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index 084d9f1c930..f030cf49316 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -475,7 +475,7 @@ async def _build( ) else: custom_component = self._custom_component - self._custom_component.set_log_callback(log_callback) + self._custom_component.set_event_manager(event_manager) custom_params = initialize.loading.get_params(self.params) await self._build_results( diff --git a/src/backend/base/langflow/interface/initialize/loading.py b/src/backend/base/langflow/interface/initialize/loading.py index 08970761e6f..36f6cf21d58 100644 --- a/src/backend/base/langflow/interface/initialize/loading.py +++ b/src/backend/base/langflow/interface/initialize/loading.py @@ -8,7 +8,7 @@ from pydantic import PydanticDeprecatedSince20 from langflow.custom.eval import eval_custom_component_code -from langflow.graph.graph.schema import LogCallbackFunction +from langflow.events.event_manager import EventManager from langflow.schema import Data from langflow.schema.artifact import get_artifact_type, post_process_raw from langflow.services.deps import get_tracing_service @@ -21,7 +21,7 @@ async def instantiate_class( vertex: "Vertex", user_id=None, - log_callback: LogCallbackFunction | None = None, + event_manager: EventManager | None = None, ) -> Any: """Instantiate class from module type and key, and params""" @@ -41,8 +41,8 @@ async def instantiate_class( _vertex=vertex, _tracing_service=get_tracing_service(), ) - if hasattr(custom_component, "set_log_callback"): - custom_component.set_log_callback(log_callback) + if hasattr(custom_component, "set_event_manager"): + custom_component.set_event_manager(event_manager) return custom_component, custom_params From 06ced1130fe6d5527593e8e6811897b8bbc19a78 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:21:12 -0300 Subject: [PATCH 036/592] refactor(component.py): rename _log_callback to _event_manager and update method call to on_log for better clarity and consistency --- .../base/langflow/custom/custom_component/component.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index e51a02317bc..603b662c5a1 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -746,9 +746,9 @@ def log(self, message: LoggableType | list[LoggableType], name: str | None = Non self._logs.append(log) if self._tracing_service and self._vertex: self._tracing_service.add_log(trace_name=self.trace_name, log=log) - if self._log_callback is not None and self._current_output: + if self._event_manager is not None and self._current_output: event_name = "log" data = log.model_dump() data["output"] = self._current_output data["component_id"] = self._id - self._log_callback(event_name, data) + self._event_manager.on_log(event_name, data) From 31898ea2390d0c33e92278766a5b4a9cfeed23ee Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:21:29 -0300 Subject: [PATCH 037/592] refactor(chat.py): rename _log_callback method to _event_manager.on_token for clarity and consistency in method naming --- src/backend/base/langflow/base/io/chat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index 0143a1de3ea..daedeb4f989 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -24,7 +24,7 @@ def store_message( if len(messages) > 1: raise ValueError("Only one message can be stored at a time.") stored_message = messages[0] - if hasattr(self, "_log_callback") and self._log_callback and stored_message.id: + if hasattr(self, "_event_manager") and self._event_manager and stored_message.id: if not isinstance(message.text, str): complete_message = self._stream_message(message, stored_message.id) message_table = update_message(message_id=stored_message.id, message=dict(text=complete_message)) @@ -49,7 +49,7 @@ def _stream_message(self, message: Message, message_id: str): "sender_name": message.sender_name, "id": str(message_id), } - self._log_callback("token", data) + self._event_manager.on_token(data) return complete_message def build_with_data( From b2c4bf47734946ed14d4668797392ce84d167889 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:21:51 -0300 Subject: [PATCH 038/592] refactor: Rename log_callback to event_manager for clarity and consistency --- src/backend/base/langflow/graph/graph/base.py | 19 ++++++++++--------- .../unit/graph/graph/test_callback_graph.py | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index 2fe1368877c..de2477d7f7d 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -13,12 +13,13 @@ import nest_asyncio from loguru import logger +from langflow.events.event_manager import EventManager from langflow.exceptions.component import ComponentBuildException from langflow.graph.edge.base import CycleEdge from langflow.graph.edge.schema import EdgeData from langflow.graph.graph.constants import Finish, lazy_load_vertex_dict from langflow.graph.graph.runnable_vertices_manager import RunnableVerticesManager -from langflow.graph.graph.schema import GraphData, GraphDump, LogCallbackFunction, StartConfigDict, VertexBuildResult +from langflow.graph.graph.schema import GraphData, GraphDump, StartConfigDict, VertexBuildResult from langflow.graph.graph.state_manager import GraphStateManager from langflow.graph.graph.state_model import create_state_model_from_graph from langflow.graph.graph.utils import find_start_component_id, process_flow, should_continue, sort_up_to_vertex @@ -260,7 +261,7 @@ async def async_start( self, inputs: list[dict] | None = None, max_iterations: int | None = None, - log_callback: LogCallbackFunction | None = None, + event_manager: EventManager | None = None, ): if not self._prepared: raise ValueError("Graph not prepared. Call prepare() first.") @@ -275,7 +276,7 @@ async def async_start( yielded_counts: dict[str, int] = defaultdict(int) while should_continue(yielded_counts, max_iterations): - result = await self.astep(log_callback=log_callback) + result = await self.astep(event_manager=event_manager) yield result if hasattr(result, "vertex"): yielded_counts[result.vertex.id] += 1 @@ -297,14 +298,14 @@ def start( inputs: list[dict] | None = None, max_iterations: int | None = None, config: StartConfigDict | None = None, - log_callback: LogCallbackFunction | None = None, + event_manager: EventManager | None = None, ) -> Generator: if config is not None: self.__apply_config(config) #! Change this ASAP nest_asyncio.apply() loop = asyncio.get_event_loop() - async_gen = self.async_start(inputs, max_iterations, log_callback) + async_gen = self.async_start(inputs, max_iterations, event_manager) async_gen_task = asyncio.ensure_future(async_gen.__anext__()) while True: @@ -1159,7 +1160,7 @@ async def astep( inputs: Optional["InputValueRequest"] = None, files: list[str] | None = None, user_id: str | None = None, - log_callback: LogCallbackFunction | None = None, + event_manager: EventManager | None = None, ): if not self._prepared: raise ValueError("Graph not prepared. Call prepare() first.") @@ -1175,7 +1176,7 @@ async def astep( files=files, get_cache=chat_service.get_cache, set_cache=chat_service.set_cache, - log_callback=log_callback, + event_manager=event_manager, ) next_runnable_vertices = await self.get_next_runnable_vertices( @@ -1227,7 +1228,7 @@ async def build_vertex( files: list[str] | None = None, user_id: str | None = None, fallback_to_env_vars: bool = False, - log_callback: LogCallbackFunction | None = None, + event_manager: EventManager | None = None, ) -> VertexBuildResult: """ Builds a vertex in the graph. @@ -1286,7 +1287,7 @@ async def build_vertex( inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars, files=files, - log_callback=log_callback, + event_manager=event_manager, ) if set_cache is not None: vertex_dict = { diff --git a/src/backend/tests/unit/graph/graph/test_callback_graph.py b/src/backend/tests/unit/graph/graph/test_callback_graph.py index 009bfb80114..54436618a16 100644 --- a/src/backend/tests/unit/graph/graph/test_callback_graph.py +++ b/src/backend/tests/unit/graph/graph/test_callback_graph.py @@ -29,7 +29,7 @@ def mock_callback(event_name, data): chat_output.set(sender_name=log_component.call_log_method) graph = Graph(start=log_component, end=chat_output) - results = list(graph.start(log_callback=mock_callback)) + results = list(graph.start(event_manager=mock_callback)) assert len(results) == 3 assert len(logs) == 3 assert all(isinstance(log, tuple) for log in logs) From e88c1457b239dec5af238f5559a91737f0f70674 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:22:04 -0300 Subject: [PATCH 039/592] refactor: Update Vertex class to use EventManager instead of log_callback for better clarity and consistency --- src/backend/base/langflow/graph/vertex/base.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index f030cf49316..85e9d089f03 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -5,15 +5,15 @@ import os import traceback import types +from collections.abc import AsyncIterator, Callable, Iterator, Mapping from enum import Enum from typing import TYPE_CHECKING, Any, Optional -from collections.abc import AsyncIterator, Callable, Iterator, Mapping import pandas as pd from loguru import logger +from langflow.events.event_manager import EventManager from langflow.exceptions.component import ComponentBuildException -from langflow.graph.graph.schema import LogCallbackFunction from langflow.graph.schema import INPUT_COMPONENTS, OUTPUT_COMPONENTS, InterfaceComponentTypes, ResultData from langflow.graph.utils import UnbuiltObject, UnbuiltResult, log_transaction from langflow.graph.vertex.schema import NodeData @@ -458,7 +458,7 @@ async def _build( self, fallback_to_env_vars, user_id=None, - log_callback: LogCallbackFunction | None = None, + event_manager: EventManager | None = None, ): """ Initiate the build process. @@ -471,7 +471,7 @@ async def _build( if not self._custom_component: custom_component, custom_params = await initialize.loading.instantiate_class( - user_id=user_id, vertex=self, log_callback=log_callback + user_id=user_id, vertex=self, event_manager=event_manager ) else: custom_component = self._custom_component @@ -770,7 +770,7 @@ async def build( inputs: dict[str, Any] | None = None, files: list[str] | None = None, requester: Optional["Vertex"] = None, - log_callback: LogCallbackFunction | None = None, + event_manager: EventManager | None = None, **kwargs, ) -> Any: async with self._lock: @@ -800,9 +800,9 @@ async def build( for step in self.steps: if step not in self.steps_ran: if inspect.iscoroutinefunction(step): - await step(user_id=user_id, log_callback=log_callback, **kwargs) + await step(user_id=user_id, event_manager=event_manager, **kwargs) else: - step(user_id=user_id, log_callback=log_callback, **kwargs) + step(user_id=user_id, event_manager=event_manager, **kwargs) self.steps_ran.append(step) self._finalize_build() From 2d29f00983cf08c4b6840906824552469506447b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:22:33 -0300 Subject: [PATCH 040/592] refactor: update build_flow to use EventManager --- src/backend/base/langflow/api/v1/chat.py | 57 ++++++++---------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index f944451104b..f1ddaedee13 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -4,8 +4,6 @@ import traceback import typing import uuid -from collections.abc import Callable -from functools import partial from typing import TYPE_CHECKING, Annotated from fastapi import APIRouter, BackgroundTasks, Body, Depends, HTTPException @@ -33,9 +31,9 @@ VertexBuildResponse, VerticesOrderResponse, ) +from langflow.events.event_manager import EventManager, create_default_event_manager from langflow.exceptions.component import ComponentBuildException from langflow.graph.graph.base import Graph -from langflow.graph.graph.schema import LogCallbackFunction from langflow.graph.utils import log_vertex_build from langflow.schema.schema import OutputValue from langflow.services.auth.utils import get_current_active_user @@ -207,9 +205,7 @@ async def build_graph_and_get_order() -> tuple[list[str], list[str], "Graph"]: logger.exception(exc) raise HTTPException(status_code=500, detail=str(exc)) from exc - async def _build_vertex( - vertex_id: str, graph: "Graph", log_callback: LogCallbackFunction | None = None - ) -> VertexBuildResponse: + async def _build_vertex(vertex_id: str, graph: "Graph", event_manager: "EventManager") -> VertexBuildResponse: flow_id_str = str(flow_id) next_runnable_vertices = [] @@ -227,7 +223,7 @@ async def _build_vertex( files=files, get_cache=chat_service.get_cache, set_cache=chat_service.set_cache, - log_callback=log_callback, + event_manager=event_manager, ) result_dict = vertex_build_result.result_dict params = vertex_build_result.params @@ -322,21 +318,13 @@ async def _build_vertex( message = parse_exception(exc) raise HTTPException(status_code=500, detail=message) from exc - def send_event(event_type: str, value: dict, queue: asyncio.Queue) -> None: - json_data = {"event": event_type, "data": value} - event_id = uuid.uuid4() - logger.debug(f"sending event {event_id}: {event_type}") - str_data = json.dumps(json_data) + "\n\n" - queue.put_nowait((event_id, str_data.encode("utf-8"), time.time())) - async def build_vertices( vertex_id: str, graph: "Graph", - queue: asyncio.Queue, client_consumed_queue: asyncio.Queue, - callback: LogCallbackFunction | None = None, + event_manager: "EventManager", ) -> None: - build_task = asyncio.create_task(await asyncio.to_thread(_build_vertex, vertex_id, graph, callback)) + build_task = asyncio.create_task(await asyncio.to_thread(_build_vertex, vertex_id, graph, event_manager)) try: await build_task except asyncio.CancelledError: @@ -350,14 +338,14 @@ async def build_vertices( build_data = json.loads(vertex_build_response_json) except Exception as exc: raise ValueError(f"Error serializing vertex build response: {exc}") from exc - send_event("end_vertex", {"build_data": build_data}, queue) + event_manager.on_end_vertex({"build_data": build_data}) await client_consumed_queue.get() if vertex_build_response.valid: if vertex_build_response.next_vertices_ids: tasks = [] for next_vertex_id in vertex_build_response.next_vertices_ids: task = asyncio.create_task( - build_vertices(next_vertex_id, graph, queue, client_consumed_queue, callback) + build_vertices(next_vertex_id, graph, client_consumed_queue, event_manager) ) tasks.append(task) try: @@ -367,15 +355,7 @@ async def build_vertices( task.cancel() return - async def event_generator(queue: asyncio.Queue, client_consumed_queue: asyncio.Queue) -> None: - def send_event_wrapper(queue) -> Callable: - partial_send_event = partial(send_event, queue=queue) - - def send_event_callback(event, data): - partial_send_event(event, data) - - return send_event_callback - + async def event_generator(event_manager: EventManager, client_consumed_queue: asyncio.Queue) -> None: if not data: # using another thread since the DB query is I/O bound vertices_task = asyncio.create_task(await asyncio.to_thread(build_graph_and_get_order)) @@ -386,9 +366,9 @@ def send_event_callback(event, data): return except Exception as e: if isinstance(e, HTTPException): - send_event("error", {"error": str(e.detail), "statusCode": e.status_code}, queue) + event_manager.on_error({"error": str(e.detail), "statusCode": e.status_code}) raise e - send_event("error", {"error": str(e)}, queue) + event_manager.on_error({"error": str(e)}) raise e ids, vertices_to_run, graph = vertices_task.result() @@ -397,18 +377,16 @@ def send_event_callback(event, data): ids, vertices_to_run, graph = await build_graph_and_get_order() except Exception as e: if isinstance(e, HTTPException): - send_event("error", {"error": str(e.detail), "statusCode": e.status_code}, queue) + event_manager.on_error({"error": str(e.detail), "statusCode": e.status_code}) raise e - send_event("error", {"error": str(e)}, queue) + event_manager.on_error({"error": str(e)}) raise e - send_event("vertices_sorted", {"ids": ids, "to_run": vertices_to_run}, queue) + event_manager.on_vertices_sorted({"ids": ids, "to_run": vertices_to_run}) await client_consumed_queue.get() tasks = [] for vertex_id in ids: - task = asyncio.create_task( - build_vertices(vertex_id, graph, queue, client_consumed_queue, send_event_wrapper(queue)) - ) + task = asyncio.create_task(build_vertices(vertex_id, graph, client_consumed_queue, event_manager)) tasks.append(task) try: await asyncio.gather(*tasks) @@ -417,8 +395,8 @@ def send_event_callback(event, data): for task in tasks: task.cancel() return - send_event("end", {}, queue) - await queue.put((None, None, time.time)) + event_manager.on_end({}) + await event_manager.queue.put((None, None, time.time)) async def consume_and_yield(queue: asyncio.Queue, client_consumed_queue: asyncio.Queue) -> typing.AsyncGenerator: while True: @@ -435,7 +413,8 @@ async def consume_and_yield(queue: asyncio.Queue, client_consumed_queue: asyncio asyncio_queue: asyncio.Queue = asyncio.Queue() asyncio_queue_client_consumed: asyncio.Queue = asyncio.Queue() - main_task = asyncio.create_task(event_generator(asyncio_queue, asyncio_queue_client_consumed)) + event_manager = create_default_event_manager(asyncio_queue) + main_task = asyncio.create_task(event_generator(event_manager, asyncio_queue_client_consumed)) def on_disconnect(): logger.debug("Client disconnected, closing tasks") From d8320deffe74e8ba4a1c24c0900269515991b958 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:52:51 -0300 Subject: [PATCH 041/592] refactor: Update EventManager class to use Protocol for event callbacks --- src/backend/base/langflow/events/event_manager.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/events/event_manager.py b/src/backend/base/langflow/events/event_manager.py index c5a099b9db2..8bacb9c20c4 100644 --- a/src/backend/base/langflow/events/event_manager.py +++ b/src/backend/base/langflow/events/event_manager.py @@ -5,11 +5,19 @@ from collections.abc import Callable from functools import partial +from typing_extensions import Protocol + +from langflow.schema.log import LoggableType + + +class EventCallback(Protocol): + def __call__(self, event_type: str, data: LoggableType): ... + class EventManager: def __init__(self, queue: asyncio.Queue): self.queue = queue - self.events: dict[str, Callable] = {} + self.events: dict[str, EventCallback] = {} def register_event(self, name: str, event_type: str): self.events[name] = partial(self.send_event, event_type) From b630bba1427cbe5875436026cb237f85fc656669 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:55:15 -0300 Subject: [PATCH 042/592] if event_type is not passed, it uses the default send_event --- src/backend/base/langflow/events/event_manager.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/events/event_manager.py b/src/backend/base/langflow/events/event_manager.py index 8bacb9c20c4..315e29ca746 100644 --- a/src/backend/base/langflow/events/event_manager.py +++ b/src/backend/base/langflow/events/event_manager.py @@ -19,8 +19,11 @@ def __init__(self, queue: asyncio.Queue): self.queue = queue self.events: dict[str, EventCallback] = {} - def register_event(self, name: str, event_type: str): - self.events[name] = partial(self.send_event, event_type) + def register_event(self, name: str, event_type: str | None = None): + if event_type is None: + self.events[name] = self.send_event + else: + self.events[name] = partial(self.send_event, event_type) def send_event(self, event_type: str, data: dict): json_data = {"event": event_type, "data": data} From c72bd9bececf81fe6b3b8b3de119e5323555e579 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:56:12 -0300 Subject: [PATCH 043/592] Add method to register event functions in EventManager - Introduced `register_event_function` method to allow passing custom event functions. - Updated `noop` method to accept `event_type` parameter. - Adjusted `__getattr__` to return `EventCallback` type. --- src/backend/base/langflow/events/event_manager.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/backend/base/langflow/events/event_manager.py b/src/backend/base/langflow/events/event_manager.py index 315e29ca746..6771b318c0c 100644 --- a/src/backend/base/langflow/events/event_manager.py +++ b/src/backend/base/langflow/events/event_manager.py @@ -2,7 +2,6 @@ import json import time import uuid -from collections.abc import Callable from functools import partial from typing_extensions import Protocol @@ -25,16 +24,19 @@ def register_event(self, name: str, event_type: str | None = None): else: self.events[name] = partial(self.send_event, event_type) + def register_event_function(self, name: str, event_function: EventCallback): + self.events[name] = event_function + def send_event(self, event_type: str, data: dict): json_data = {"event": event_type, "data": data} event_id = uuid.uuid4() str_data = json.dumps(json_data) + "\n\n" self.queue.put_nowait((event_id, str_data.encode("utf-8"), time.time())) - def noop(self, data: dict): + def noop(self, event_type: str, data: dict): pass - def __getattr__(self, name: str) -> Callable[[dict], None]: + def __getattr__(self, name: str) -> EventCallback: return self.events.get(name, self.noop) From 2c95daf619d2d93077861c87861739badfe9f15d Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:56:37 -0300 Subject: [PATCH 044/592] update test_callback_graph --- .../tests/unit/graph/graph/test_callback_graph.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/backend/tests/unit/graph/graph/test_callback_graph.py b/src/backend/tests/unit/graph/graph/test_callback_graph.py index 54436618a16..cd20226827b 100644 --- a/src/backend/tests/unit/graph/graph/test_callback_graph.py +++ b/src/backend/tests/unit/graph/graph/test_callback_graph.py @@ -1,5 +1,8 @@ +import asyncio + from langflow.components.outputs.ChatOutput import ChatOutput from langflow.custom.custom_component.component import Component +from langflow.events.event_manager import EventManager from langflow.graph.graph.base import Graph from langflow.inputs.inputs import IntInput from langflow.schema.message import Message @@ -20,8 +23,11 @@ def call_log_method(self) -> Message: def test_callback_graph(): logs: list[tuple[str, dict]] = [] - def mock_callback(event_name, data): - logs.append((event_name, data)) + def mock_callback(event_type: str, data: dict): + logs.append((event_type, data)) + + event_manager = EventManager(queue=asyncio.Queue()) + event_manager.register_event_function("on_log", mock_callback) log_component = LogComponent(_id="log_component") log_component.set(times=3) @@ -29,7 +35,7 @@ def mock_callback(event_name, data): chat_output.set(sender_name=log_component.call_log_method) graph = Graph(start=log_component, end=chat_output) - results = list(graph.start(event_manager=mock_callback)) + results = list(graph.start(event_manager=event_manager)) assert len(results) == 3 assert len(logs) == 3 assert all(isinstance(log, tuple) for log in logs) From 8834512198b74e6db1f645ae2d288f9d6bcd09fe Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 28 Aug 2024 19:56:47 -0300 Subject: [PATCH 045/592] Add unit tests for EventManager in test_event_manager.py - Added tests for event registration, including default event type, empty string names, and specific event types. - Added tests for custom event functions and unregistered event access. - Added tests for event sending, including JSON formatting, empty data, and large payloads. - Added tests for handling JSON serialization errors and the noop function. --- .../tests/unit/events/test_event_manager.py | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 src/backend/tests/unit/events/test_event_manager.py diff --git a/src/backend/tests/unit/events/test_event_manager.py b/src/backend/tests/unit/events/test_event_manager.py new file mode 100644 index 00000000000..45820e2a0f1 --- /dev/null +++ b/src/backend/tests/unit/events/test_event_manager.py @@ -0,0 +1,180 @@ +import asyncio +import json +from functools import partial +from unittest.mock import Mock, patch + +import pytest + +from langflow.events.event_manager import EventManager + + +@pytest.fixture +def client(): + pass + + +class TestEventManager: + # Registering an event without specifying an event type should default to using send_event + def test_register_event_without_event_type_defaults_to_send_event(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + event_manager.register_event("test_event") + + assert "test_event" in event_manager.events + assert event_manager.events["test_event"] == event_manager.send_event + + # Registering an event with an empty string as the name + def test_register_event_with_empty_string_name(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + event_manager.register_event("") + + assert "" in event_manager.events + assert event_manager.events[""] == event_manager.send_event + + # Registering an event with a specific event type should use a partial function of send_event + def test_register_event_with_specific_event_type_uses_partial_function(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + event_manager.register_event("test_event", "specific_type") + + assert "test_event" in event_manager.events + assert isinstance(event_manager.events["test_event"], partial) + + # Registering a custom event function should store it correctly in the events dictionary using a mock with the correct import + def test_register_custom_event_function_stored_correctly_with_mock_with_mock_import(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + custom_event_function = Mock() + event_manager.register_event_function("custom_event", custom_event_function) + + assert "custom_event" in event_manager.events + assert event_manager.events["custom_event"] == custom_event_function + + # Accessing an unregistered event should return the noop function + def test_accessing_unregistered_event_returns_noop_function(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + + result = event_manager.unregistered_event + + assert result == event_manager.noop + + # Accessing a registered event should return the corresponding function + def test_accessing_registered_event_returns_corresponding_function(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + event_manager.register_event("test_event") + + assert "test_event" in event_manager.events + assert event_manager.events["test_event"] == event_manager.send_event + + # Sending an event should correctly format the event data as JSON and add it to the queue + def test_send_event_correctly_formats_data_and_adds_to_queue(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + event_manager.register_event("test_event") + + event_type = "test_type" + data = {"key": "value"} + event_manager.send_event(event_type, data) + + event_id, str_data, timestamp = queue.get_nowait() + decoded_data = json.loads(str_data.decode("utf-8")) + + assert decoded_data["event"] == event_type + assert decoded_data["data"] == data + + # Accessing an event with a name that has not been registered + def test_accessing_unregistered_event(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + + result = event_manager.unknown_event + + assert result == event_manager.noop + + # Asserting the registration of a partial function for an event with an empty string as the event type + def test_assert_partial_function_for_empty_string_event_type(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + event_manager.register_event("test_event", "") + + assert "test_event" in event_manager.events + assert ( + isinstance(event_manager.events["test_event"], partial) + and event_manager.events["test_event"].func == event_manager.send_event + and event_manager.events["test_event"].args == ("",) + ) + + # Sending an event with an empty dictionary as data + def test_sending_event_with_empty_data(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + event_manager.register_event("test_event") + + event_manager.send_event("test_event", {}) + + # Check if the event was sent with the correct data + assert not queue.empty() + event_id, data, timestamp = queue.get_nowait() + decoded_data = json.loads(data.decode("utf-8")) + assert decoded_data["event"] == "test_event" + assert decoded_data["data"] == {} + + # Registering an event with None as the event type + def test_register_event_with_none_event_type_defaults_to_send_event(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + event_manager.register_event("test_event", None) + + assert "test_event" in event_manager.events + assert event_manager.events["test_event"] == event_manager.send_event + + # Registering multiple events with the same name should overwrite the previous event + def test_registering_multiple_events_overwrite_previous_fixed(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + event_manager.register_event("test_event", "type1") + event_manager.register_event("test_event", "type2") + + assert "test_event" in event_manager.events + assert event_manager.events["test_event"].args == ("type2",) + + # The queue should handle events with large data payloads + def test_handle_large_data_payloads(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + event_manager.register_event("test_event") + + large_data = {"key": "value" * 1000} # Creating a large data payload + event_manager.send_event("test_event", large_data) + + event_id, str_data, timestamp = queue.get_nowait() + decoded_data = json.loads(str_data.decode("utf-8")) + + assert decoded_data["event"] == "test_event" + assert decoded_data["data"] == large_data + + # The noop function should handle any data without raising exceptions + def test_noop_handles_any_data(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + data = {"key": "value"} + + event_manager.noop(data) + # No exceptions should be raised + + # Implementing the Recommended Fix for handling JSON serialization error during event sending + def mock_json_dumps_error(self, *args, **kwargs): + raise ValueError("Mock JSON serialization error") + + def test_send_event_json_serialization_error_with_patch_fixed_replica(self): + queue = asyncio.Queue() + event_manager = EventManager(queue) + with patch("json.dumps", side_effect=self.mock_json_dumps_error): + try: + event_manager.send_event("test_event", {"key": "value"}) + except Exception: + pass + assert queue.empty() # Queue should be empty due to error handling From d375bd46c1ea103e04dc68fad1f0862dd1d66635 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:22:44 -0300 Subject: [PATCH 046/592] feat: Add callback function support to vertex build process. --- src/backend/base/langflow/graph/vertex/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index 85e9d089f03..49313e7cead 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -14,6 +14,7 @@ from langflow.events.event_manager import EventManager from langflow.exceptions.component import ComponentBuildException +from langflow.graph.graph.schema import CallbackFunction from langflow.graph.schema import INPUT_COMPONENTS, OUTPUT_COMPONENTS, InterfaceComponentTypes, ResultData from langflow.graph.utils import UnbuiltObject, UnbuiltResult, log_transaction from langflow.graph.vertex.schema import NodeData From bfce547f971fc730d9770166cbb76653a8c2d0f3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:23:02 -0300 Subject: [PATCH 047/592] feat: Add callback support to Graph methods. --- src/backend/base/langflow/graph/graph/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index de2477d7f7d..69a46cfaaf1 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -19,7 +19,7 @@ from langflow.graph.edge.schema import EdgeData from langflow.graph.graph.constants import Finish, lazy_load_vertex_dict from langflow.graph.graph.runnable_vertices_manager import RunnableVerticesManager -from langflow.graph.graph.schema import GraphData, GraphDump, StartConfigDict, VertexBuildResult +from langflow.graph.graph.schema import CallbackFunction, GraphData, GraphDump, StartConfigDict, VertexBuildResult from langflow.graph.graph.state_manager import GraphStateManager from langflow.graph.graph.state_model import create_state_model_from_graph from langflow.graph.graph.utils import find_start_component_id, process_flow, should_continue, sort_up_to_vertex From 108d09ad1a4a824568ee98ba8d43554c756fd197 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 19 Aug 2024 16:23:11 -0300 Subject: [PATCH 048/592] feat(chat): Add callback function to build_vertices function. --- src/backend/base/langflow/api/v1/chat.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index f1ddaedee13..88447157240 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -34,6 +34,7 @@ from langflow.events.event_manager import EventManager, create_default_event_manager from langflow.exceptions.component import ComponentBuildException from langflow.graph.graph.base import Graph +from langflow.graph.graph.schema import CallbackFunction from langflow.graph.utils import log_vertex_build from langflow.schema.schema import OutputValue from langflow.services.auth.utils import get_current_active_user From 578f1a580f085993e36ffe83d33a86d51ba35313 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 19:24:53 +0000 Subject: [PATCH 049/592] [autofix.ci] apply automated fixes From ef24c227ff30db11cfa111ed2460488e0163a3d2 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 20 Aug 2024 14:30:16 -0300 Subject: [PATCH 050/592] refactor: Update callback to log_callback in graph methods. --- src/backend/base/langflow/api/v1/chat.py | 1 - src/backend/base/langflow/graph/graph/base.py | 2 +- src/backend/base/langflow/graph/vertex/base.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 88447157240..f1ddaedee13 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -34,7 +34,6 @@ from langflow.events.event_manager import EventManager, create_default_event_manager from langflow.exceptions.component import ComponentBuildException from langflow.graph.graph.base import Graph -from langflow.graph.graph.schema import CallbackFunction from langflow.graph.utils import log_vertex_build from langflow.schema.schema import OutputValue from langflow.services.auth.utils import get_current_active_user diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index 69a46cfaaf1..de2477d7f7d 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -19,7 +19,7 @@ from langflow.graph.edge.schema import EdgeData from langflow.graph.graph.constants import Finish, lazy_load_vertex_dict from langflow.graph.graph.runnable_vertices_manager import RunnableVerticesManager -from langflow.graph.graph.schema import CallbackFunction, GraphData, GraphDump, StartConfigDict, VertexBuildResult +from langflow.graph.graph.schema import GraphData, GraphDump, StartConfigDict, VertexBuildResult from langflow.graph.graph.state_manager import GraphStateManager from langflow.graph.graph.state_model import create_state_model_from_graph from langflow.graph.graph.utils import find_start_component_id, process_flow, should_continue, sort_up_to_vertex diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index 49313e7cead..85e9d089f03 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -14,7 +14,6 @@ from langflow.events.event_manager import EventManager from langflow.exceptions.component import ComponentBuildException -from langflow.graph.graph.schema import CallbackFunction from langflow.graph.schema import INPUT_COMPONENTS, OUTPUT_COMPONENTS, InterfaceComponentTypes, ResultData from langflow.graph.utils import UnbuiltObject, UnbuiltResult, log_transaction from langflow.graph.vertex.schema import NodeData From ffc30941f157ccab9cca912b1f57ecef6a0ab2c8 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 23 Aug 2024 18:58:38 -0300 Subject: [PATCH 051/592] fetching data from messages and builds at the same time, need to remove duplicates --- .../components/fileCardWrapper/index.tsx | 22 +++++--- .../components/chatView/chatMessage/index.tsx | 4 +- .../IOModal/components/chatView/index.tsx | 52 ++++++++++++------- src/frontend/src/types/chat/index.ts | 6 ++- src/frontend/src/types/messages/index.ts | 2 +- src/frontend/src/utils/utils.ts | 10 ++++ 6 files changed, 66 insertions(+), 30 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/fileCardWrapper/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/fileCardWrapper/index.tsx index 3a3467b62b9..8e84677163a 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/fileCardWrapper/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/fileCardWrapper/index.tsx @@ -5,16 +5,26 @@ import formatFileName from "../../../filePreviewChat/utils/format-file-name"; export default function FileCardWrapper({ index, - name, - type, path, }: { index: number; - name: string; - type: string; - path: string; + path: { path: string; type: string; name: string } | string; }) { const [show, setShow] = useState(true); + let name: string = ""; + let type: string = ""; + let pathString: string = ""; + if (typeof path === "string") { + name = path.split("/").pop() || ""; + type = path.split(".").pop() || ""; + pathString = path; + } + else { + name = path.name; + type = path.type; + pathString = path.path; + } + return (
- +
); } diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx index e1739be6196..c1b446f05b9 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx @@ -349,9 +349,7 @@ export default function ChatMessage({ return ( ); })} diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index fd6bb270a97..c5ed9370ea7 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -17,10 +17,11 @@ import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import { VertexBuildTypeAPI } from "../../../../types/api"; import { ChatMessageType } from "../../../../types/chat"; import { FilePreviewType, chatViewProps } from "../../../../types/components"; -import { classNames } from "../../../../utils/utils"; +import { classNames, removeDuplicatesBasedOnAttribute } from "../../../../utils/utils"; import ChatInput from "./chatInput"; import useDragAndDrop from "./chatInput/hooks/use-drag-and-drop"; import ChatMessage from "./chatMessage"; +import { useMessagesStore } from "@/stores/messagesStore"; export default function ChatView({ sendMessage, @@ -34,6 +35,7 @@ export default function ChatView({ const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); const messagesRef = useRef(null); const [chatHistory, setChatHistory] = useState([]); + const messages = useMessagesStore((state) => state.messages); const inputTypes = inputs.map((obj) => obj.type); const inputIds = inputs.map((obj) => obj.id); @@ -59,24 +61,15 @@ export default function ChatView({ } } }); - const chatMessages: ChatMessageType[] = chatOutputResponses - .sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)) - // + const messagesFromPool: ChatMessageType[] = chatOutputResponses .filter( (output) => - output.data.message || (!output.data.message && output.artifacts), + output.data.message!==undefined ) .map((output, index) => { try { - const messageOutput = output.data.message; - const hasMessageValue = - messageOutput?.message || - messageOutput?.message === "" || - (messageOutput?.files ?? []).length > 0 || - messageOutput?.stream_url; - - const { sender, message, sender_name, stream_url, files } = - hasMessageValue ? output.data.message : output.artifacts; + const messageOutput = output.data.message!; + const { sender, message, sender_name, stream_url, files } = messageOutput const is_ai = sender === "Machine" || sender === null || sender === undefined; @@ -84,9 +77,9 @@ export default function ChatView({ isSend: !is_ai, message, sender_name, - componentId: output.id, stream_url: stream_url, files, + timestamp: output.timestamp, }; } catch (e) { console.error(e); @@ -95,11 +88,34 @@ export default function ChatView({ message: "Error parsing message", sender_name: "Error", componentId: output.id, + timestamp: output.timestamp, }; } }); - setChatHistory(chatMessages); - }, [flowPool]); + const messagesFromMessagesStore:ChatMessageType[] = messages.filter(message=>message.flow_id===currentFlowId) + .map((message) => { + let files = message.files; + //HANDLE THE "[]" case + if(typeof files === "string") { + files = JSON.parse(files); + } + return { + isSend: message.sender === "User", + message: message.text, + sender_name: message.sender_name, + files: files, + id: message.id, + timestamp: message.timestamp, + }; + }); + console.log(messagesFromMessagesStore) + console.log(messagesFromPool) + const finalChatHistory = [...messagesFromPool, ...messagesFromMessagesStore]; + // this function will remove duplicates from the chat history based on the timestamp + const filteredChatHistory = removeDuplicatesBasedOnAttribute(finalChatHistory, "timestamp").sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)); + setChatHistory(filteredChatHistory); + + }, [flowPool,messages]); useEffect(() => { if (messagesRef.current) { messagesRef.current.scrollTop = messagesRef.current.scrollHeight; @@ -146,7 +162,7 @@ export default function ChatView({ stream_url?: string, ) { chat.message = message; - updateFlowPool(chat.componentId, { + if(chat.componentId) updateFlowPool(chat.componentId, { message, sender_name: chat.sender_name ?? "Bot", sender: chat.isSend ? "User" : "Machine", diff --git a/src/frontend/src/types/chat/index.ts b/src/frontend/src/types/chat/index.ts index 72ebb537063..8b9d304455b 100644 --- a/src/frontend/src/types/chat/index.ts +++ b/src/frontend/src/types/chat/index.ts @@ -6,10 +6,12 @@ export type ChatMessageType = { template?: string; isSend: boolean; thought?: string; - files?: Array<{ path: string; type: string; name: string }>; + files?: Array<{ path: string; type: string; name: string }|string>; prompt?: string; chatKey?: string; - componentId: string; + componentId?: string; + id?: string; + timestamp:string; stream_url?: string | null; sender_name?: string; }; diff --git a/src/frontend/src/types/messages/index.ts b/src/frontend/src/types/messages/index.ts index 9b19e148979..7e4a3eb8aae 100644 --- a/src/frontend/src/types/messages/index.ts +++ b/src/frontend/src/types/messages/index.ts @@ -1,7 +1,7 @@ type Message = { artifacts: Record; flow_id: string; - message: string; + text: string; sender: string; sender_name: string; session_id: string; diff --git a/src/frontend/src/utils/utils.ts b/src/frontend/src/utils/utils.ts index b0e58ae0085..49041958f4a 100644 --- a/src/frontend/src/utils/utils.ts +++ b/src/frontend/src/utils/utils.ts @@ -621,3 +621,13 @@ export function addPlusSignes(array: string[]): string[] { return "+" + key; }); } + +export function removeDuplicatesBasedOnAttribute(arr: T[],attribute:string): T[] { + const seen = new Set(); + const filteredChatHistory = arr.filter((item) => { + const duplicate = seen.has(item[attribute]); + seen.add(item[attribute]); + return !duplicate; + }); + return filteredChatHistory; +} From 07e6991a3ae6f96b956673bd6ac8699dd5fb02bd Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Mon, 26 Aug 2024 09:52:11 -0300 Subject: [PATCH 052/592] refactor: Sort chat history by timestamp in ChatView component --- .../src/modals/IOModal/components/chatView/index.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index c5ed9370ea7..913803c24cf 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -108,12 +108,11 @@ export default function ChatView({ timestamp: message.timestamp, }; }); - console.log(messagesFromMessagesStore) - console.log(messagesFromPool) - const finalChatHistory = [...messagesFromPool, ...messagesFromMessagesStore]; + const finalChatHistory = [...messagesFromPool, ...messagesFromMessagesStore].sort((a, b) => { + return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(); + }) // this function will remove duplicates from the chat history based on the timestamp - const filteredChatHistory = removeDuplicatesBasedOnAttribute(finalChatHistory, "timestamp").sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)); - setChatHistory(filteredChatHistory); + setChatHistory(finalChatHistory); }, [flowPool,messages]); useEffect(() => { From 8247d2ef0b33816009156498ea1e1ec70ae39454 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 23 Aug 2024 10:30:42 -0300 Subject: [PATCH 053/592] fix: update serialization and improve error handling (#3516) * feat(utils): add support for V1BaseModel in serialize_field Add support for V1BaseModel instances in the serialize_field function by checking for a "to_json" method. If the method is not present, return the attribute values as a dictionary. * refactor: Update field serializer function and error handling in build_flow function --- src/backend/base/langflow/api/v1/chat.py | 3 ++- src/backend/base/langflow/api/v1/schemas.py | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index f1ddaedee13..cf52ca993d3 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -327,7 +327,8 @@ async def build_vertices( build_task = asyncio.create_task(await asyncio.to_thread(_build_vertex, vertex_id, graph, event_manager)) try: await build_task - except asyncio.CancelledError: + except asyncio.CancelledError as exc: + logger.exception(exc) build_task.cancel() return diff --git a/src/backend/base/langflow/api/v1/schemas.py b/src/backend/base/langflow/api/v1/schemas.py index 959042b343a..da27a3a7a3b 100644 --- a/src/backend/base/langflow/api/v1/schemas.py +++ b/src/backend/base/langflow/api/v1/schemas.py @@ -267,6 +267,13 @@ def serialize_results(cls, v): return {key: serialize_field(val) for key, val in v.items()} return serialize_field(v) + @field_serializer("results") + @classmethod + def serialize_results(cls, v): + if isinstance(v, dict): + return {key: serialize_field(val) for key, val in v.items()} + return serialize_field(v) + class VertexBuildResponse(BaseModel): id: str | None = None From f13399f36784831c7ce4a5c42d991beb9d13d7b8 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Mon, 26 Aug 2024 16:06:34 -0300 Subject: [PATCH 054/592] remove use memo to prevent bugs --- .../IOModal/components/chatView/chatMessage/index.tsx | 7 +------ .../src/modals/IOModal/components/chatView/index.tsx | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx index c1b446f05b9..5b501107d4d 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx @@ -26,7 +26,6 @@ export default function ChatMessage({ updateChat, setLockChat, }: chatMessagePropsType): JSX.Element { - const [showFile, setShowFile] = useState(true); const convert = new Convert({ newline: true }); const [hidden, setHidden] = useState(true); const template = chat.template; @@ -182,9 +181,7 @@ export default function ChatMessage({ } className="flex w-full flex-col" > - {useMemo( - () => - chatMessage === "" && lockChat ? ( + {chatMessage === "" && lockChat ? ( - ), - [chat.message, chatMessage], )} diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 913803c24cf..434df0840d6 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -109,7 +109,7 @@ export default function ChatView({ }; }); const finalChatHistory = [...messagesFromPool, ...messagesFromMessagesStore].sort((a, b) => { - return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(); + return (new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()); }) // this function will remove duplicates from the chat history based on the timestamp setChatHistory(finalChatHistory); From ec528600ace980f1a21d5bb51d1045fea612c452 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 27 Aug 2024 18:53:56 -0300 Subject: [PATCH 055/592] feat: add updateMessagePartial method to MessagesStoreType --- src/frontend/src/types/zustand/messages/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/src/types/zustand/messages/index.ts b/src/frontend/src/types/zustand/messages/index.ts index 38e66c99066..09fa9b0d6ad 100644 --- a/src/frontend/src/types/zustand/messages/index.ts +++ b/src/frontend/src/types/zustand/messages/index.ts @@ -7,6 +7,7 @@ export type MessagesStoreType = { addMessage: (message: Message) => void; removeMessage: (message: Message) => void; updateMessage: (message: Message) => void; + updateMessagePartial: (message: Partial) => void; clearMessages: () => void; removeMessages: (ids: string[]) => void; columns: Array; From 21259d7c2e3bc17ba7e0ea1bed7fb365d3ca81b2 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 27 Aug 2024 19:00:42 -0300 Subject: [PATCH 056/592] feat: update message partially in MessagesStoreType This commit adds the `updateMessagePartial` method to the `MessagesStoreType` in `messagesStore.ts`. This method allows updating a specific message by merging the changes with the existing message object. --- src/frontend/src/stores/messagesStore.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/frontend/src/stores/messagesStore.ts b/src/frontend/src/stores/messagesStore.ts index cb817febce5..3b6f2ac5fbf 100644 --- a/src/frontend/src/stores/messagesStore.ts +++ b/src/frontend/src/stores/messagesStore.ts @@ -33,6 +33,13 @@ export const useMessagesStore = create((set, get) => ({ ), })); }, + updateMessagePartial: (message) => { + set(() => ({ + messages: get().messages.map((msg) => + msg.id === message.id ? { ...msg, ...message } : msg, + ), + })); + }, clearMessages: () => { set(() => ({ messages: [] })); }, From ea460d0d964d3328d1349b821c39bb782a94681e Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 27 Aug 2024 19:09:44 -0300 Subject: [PATCH 057/592] feat: add log callback for start message in ChatComponent --- src/backend/base/langflow/base/io/chat.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index daedeb4f989..980cb2e7d34 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -40,6 +40,15 @@ def _stream_message(self, message: Message, message_id: str): complete_message: str = "" if isinstance(iterator, AsyncIterator): iterator = asyncio.ensure_future(iterator.__anext__()) + self._log_callback("start_message", {"text": "", + "sender": message.sender, + "sender_name": message.sender_name, + "id": str(message_id), + "timestamp": message.timestamp, + "flow_id": self.graph.flow_id, + "session_id": message.session_id, + "files": message.files, + }) for chunk in iterator: complete_message += chunk.content data = { From 46ad5302b243c4af590346556ed7bfbc05031ff9 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 27 Aug 2024 19:13:06 -0300 Subject: [PATCH 058/592] feat: update log_callback name --- src/backend/base/langflow/base/io/chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index 980cb2e7d34..169e15f7440 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -40,7 +40,7 @@ def _stream_message(self, message: Message, message_id: str): complete_message: str = "" if isinstance(iterator, AsyncIterator): iterator = asyncio.ensure_future(iterator.__anext__()) - self._log_callback("start_message", {"text": "", + self._log_callback("message", {"text": "", "sender": message.sender, "sender_name": message.sender_name, "id": str(message_id), From f493af6a68f79014b3c7115c3b0fdee843bc6729 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 27 Aug 2024 20:12:11 -0300 Subject: [PATCH 059/592] feat: add log_callback for message in ChatComponent that are not streaming --- src/backend/base/langflow/base/io/chat.py | 9 --------- .../src/modals/IOModal/components/chatView/index.tsx | 2 +- src/frontend/src/utils/buildUtils.ts | 8 ++++++++ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index 169e15f7440..daedeb4f989 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -40,15 +40,6 @@ def _stream_message(self, message: Message, message_id: str): complete_message: str = "" if isinstance(iterator, AsyncIterator): iterator = asyncio.ensure_future(iterator.__anext__()) - self._log_callback("message", {"text": "", - "sender": message.sender, - "sender_name": message.sender_name, - "id": str(message_id), - "timestamp": message.timestamp, - "flow_id": self.graph.flow_id, - "session_id": message.session_id, - "files": message.files, - }) for chunk in iterator: complete_message += chunk.content data = { diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 434df0840d6..14126ce6047 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -108,7 +108,7 @@ export default function ChatView({ timestamp: message.timestamp, }; }); - const finalChatHistory = [...messagesFromPool, ...messagesFromMessagesStore].sort((a, b) => { + const finalChatHistory = [...messagesFromMessagesStore].sort((a, b) => { return (new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()); }) // this function will remove duplicates from the chat history based on the timestamp diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index c86165bcc9c..74e7181ed38 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -10,6 +10,7 @@ import { VertexBuildTypeAPI } from "../types/api"; import { isErrorLogType } from "../types/utils/typeCheckingUtils"; import { VertexLayerElementType } from "../types/zustand/flow"; import { tryParseJson } from "./utils"; +import { useMessagesStore } from "@/stores/messagesStore"; type BuildVerticesParams = { setLockChat?: (lock: boolean) => void; @@ -187,6 +188,7 @@ export async function buildFlowVertices({ onBuildStart(ids.map((id) => ({ id: id, reference: id }))); ids.forEach((id) => verticesStartTimeMs.set(id, Date.now())); }; + console.log(type, data); switch (type) { case "vertices_sorted": { const verticesToRun = data.to_run; @@ -265,6 +267,12 @@ export async function buildFlowVertices({ } return true; } + case "message": { + //adds a message to the messsage table + console.log(data); + useMessagesStore.getState().addMessage(data); + return true; + } case "end": { const allNodesValid = buildResults.every((result) => result); onBuildComplete!(allNodesValid); From ea11475b4a2375a96f099a334e275c564215675d Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Wed, 28 Aug 2024 00:05:20 -0300 Subject: [PATCH 060/592] refactor: remove console.log statement in buildFlowVertices function --- src/frontend/src/utils/buildUtils.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 74e7181ed38..386e526c4bb 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -188,7 +188,6 @@ export async function buildFlowVertices({ onBuildStart(ids.map((id) => ({ id: id, reference: id }))); ids.forEach((id) => verticesStartTimeMs.set(id, Date.now())); }; - console.log(type, data); switch (type) { case "vertices_sorted": { const verticesToRun = data.to_run; @@ -273,6 +272,11 @@ export async function buildFlowVertices({ useMessagesStore.getState().addMessage(data); return true; } + case "token": { + // console.log(data.text); + useMessagesStore.getState().updateMessagePartial(data); + return true; + } case "end": { const allNodesValid = buildResults.every((result) => result); onBuildComplete!(allNodesValid); From d5397d6cbfbc9d2a156ac038518471c0856e8c87 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Wed, 28 Aug 2024 11:17:08 -0300 Subject: [PATCH 061/592] refactor: store message in ChatInput after updating flow_id This commit refactors the `ChatInput` component by moving the logic to store the message after updating the `flow_id` property. This ensures that the message is properly stored in the correct flow. The previous implementation had the logic to store the message before updating the `flow_id`, which could lead to incorrect storage of messages. This change improves the reliability and accuracy of message storage in the `ChatInput` component. --- .../langflow/components/inputs/ChatInput.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/backend/base/langflow/components/inputs/ChatInput.py b/src/backend/base/langflow/components/inputs/ChatInput.py index 28aa220a0f2..a5985c1b951 100644 --- a/src/backend/base/langflow/components/inputs/ChatInput.py +++ b/src/backend/base/langflow/components/inputs/ChatInput.py @@ -70,17 +70,12 @@ def message_response(self) -> Message: files=self.files, ) - if ( - self.session_id - and isinstance(message, Message) - and isinstance(message.text, str) - and self.should_store_message - ): - store_message( - message, - flow_id=self.graph.flow_id, - ) - self.message.value = message + + self.store_message( + message, + flow_id=self.graph.flow_id, + ) + self.message.value = message self.status = message return message From 67f7f728a1ed09ebfa7b322a7014737b80cdc93b Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Wed, 28 Aug 2024 11:19:25 -0300 Subject: [PATCH 062/592] refactor: move message storage logic in ChatInput after updating flow_id --- src/backend/base/langflow/components/inputs/ChatInput.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/base/langflow/components/inputs/ChatInput.py b/src/backend/base/langflow/components/inputs/ChatInput.py index a5985c1b951..a5416a8408f 100644 --- a/src/backend/base/langflow/components/inputs/ChatInput.py +++ b/src/backend/base/langflow/components/inputs/ChatInput.py @@ -73,7 +73,6 @@ def message_response(self) -> Message: self.store_message( message, - flow_id=self.graph.flow_id, ) self.message.value = message From 7e9ccdcbbb0d4fc1b4e34c317ee67ad09ece6930 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Wed, 28 Aug 2024 11:35:42 -0300 Subject: [PATCH 063/592] refactor: update ChatComponent to use stored_message.id instead of self.graph.flow_id Update the `ChatComponent` class in `chat.py` to use the `stored_message.id` property instead of `self.graph.flow_id` when logging a message. This ensures that the correct message ID is used for logging purposes. The previous implementation used the flow ID, which could lead to incorrect logging. This change improves the accuracy of message logging in the `ChatComponent`. --- src/frontend/src/utils/buildUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 386e526c4bb..89685bd4312 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -273,7 +273,7 @@ export async function buildFlowVertices({ return true; } case "token": { - // console.log(data.text); + console.log(data.id); useMessagesStore.getState().updateMessagePartial(data); return true; } From 604fbd2f14d2d3daea08c00185e38939fe9ea951 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Wed, 28 Aug 2024 20:08:27 -0300 Subject: [PATCH 064/592] refactor: remove unused code and console.log statements --- src/frontend/src/modals/IOModal/index.tsx | 2 +- src/frontend/src/utils/buildUtils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index caa10f9bb8e..3ea3abc6661 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -142,7 +142,7 @@ export default function IOModal({ setLockChat(false); }); } - refetch(); + // refetch(); setLockChat(false); if (chatInput) { setNode(chatInput.id, (node: NodeType) => { diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 89685bd4312..30680359408 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -273,7 +273,7 @@ export async function buildFlowVertices({ return true; } case "token": { - console.log(data.id); + console.log(data.text); useMessagesStore.getState().updateMessagePartial(data); return true; } From c3576bcb9cd43a23d709af18d07798be7636dae0 Mon Sep 17 00:00:00 2001 From: italojohnny Date: Mon, 2 Sep 2024 18:55:13 -0300 Subject: [PATCH 065/592] raw: temp serializer fix --- src/backend/base/langflow/base/io/chat.py | 3 +++ src/backend/base/langflow/events/event_manager.py | 14 +++++++++++--- src/backend/base/langflow/schema/message.py | 6 +++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index daedeb4f989..94d4311b043 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -30,6 +30,9 @@ def store_message( message_table = update_message(message_id=stored_message.id, message=dict(text=complete_message)) stored_message = Message(**message_table.model_dump()) self.vertex._added_message = stored_message + + self._event_manager.on_message(stored_message) + self.status = stored_message return stored_message diff --git a/src/backend/base/langflow/events/event_manager.py b/src/backend/base/langflow/events/event_manager.py index 6771b318c0c..7c5156a5b80 100644 --- a/src/backend/base/langflow/events/event_manager.py +++ b/src/backend/base/langflow/events/event_manager.py @@ -5,7 +5,7 @@ from functools import partial from typing_extensions import Protocol - +from langflow.schema.message import Message from langflow.schema.log import LoggableType @@ -28,9 +28,17 @@ def register_event_function(self, name: str, event_function: EventCallback): self.events[name] = event_function def send_event(self, event_type: str, data: dict): + + if isinstance(data, Message): + data = data.model_dump() + json_data = {"event": event_type, "data": data} - event_id = uuid.uuid4() - str_data = json.dumps(json_data) + "\n\n" + event_id = str(uuid.uuid4()) + try: + str_data = json.dumps(json_data, default=str) + "\n\n" + except: + import pdb; pdb.set_trace() + print('-') self.queue.put_nowait((event_id, str_data.encode("utf-8"), time.time())) def noop(self, event_type: str, data: dict): diff --git a/src/backend/base/langflow/schema/message.py b/src/backend/base/langflow/schema/message.py index 53d7f014138..17be844a716 100644 --- a/src/backend/base/langflow/schema/message.py +++ b/src/backend/base/langflow/schema/message.py @@ -59,7 +59,11 @@ def validate_flow_id(cls, value): def serialize_flow_id(value): if isinstance(value, str): return UUID(value) - return value + return str(value) + + @field_serializer("timestamp") + def serialize_timestamp(value): + return datetime.strptime(value, "%Y-%m-%d %H:%M:%S") @field_validator("files", mode="before") @classmethod From b299a9e34e4b33cebe01fe26ed67cef2617ad52f Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 3 Sep 2024 12:58:16 -0300 Subject: [PATCH 066/592] streaming working but the message comes in one shot --- src/backend/base/langflow/base/io/chat.py | 3 +-- src/frontend/src/utils/buildUtils.ts | 7 ++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index 94d4311b043..5648d354a73 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -25,14 +25,13 @@ def store_message( raise ValueError("Only one message can be stored at a time.") stored_message = messages[0] if hasattr(self, "_event_manager") and self._event_manager and stored_message.id: + self._event_manager.on_message(stored_message) if not isinstance(message.text, str): complete_message = self._stream_message(message, stored_message.id) message_table = update_message(message_id=stored_message.id, message=dict(text=complete_message)) stored_message = Message(**message_table.model_dump()) self.vertex._added_message = stored_message - self._event_manager.on_message(stored_message) - self.status = stored_message return stored_message diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 30680359408..37580e25b51 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -268,12 +268,13 @@ export async function buildFlowVertices({ } case "message": { //adds a message to the messsage table - console.log(data); - useMessagesStore.getState().addMessage(data); + console.log(data.data); + useMessagesStore.getState().addMessage(data.data); return true; } case "token": { - console.log(data.text); + console.log(data); + // await one second before sending the next token useMessagesStore.getState().updateMessagePartial(data); return true; } From a4825c29f7ed2b94d07fc3600dd7d11091f7dfe6 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 3 Sep 2024 13:02:43 -0300 Subject: [PATCH 067/592] refactor: optimize message update in useMessagesStore Improve the efficiency of updating messages in the `useMessagesStore` function of `messagesStore.ts`. Instead of iterating through the entire message list, this refactor searches for the message to update by iterating backwards from the end. This approach allows for faster message retrieval and update. The code has been modified to use a for loop and break out of the loop once the message is found. This change enhances the performance of the message update process. --- src/frontend/src/stores/messagesStore.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/stores/messagesStore.ts b/src/frontend/src/stores/messagesStore.ts index 3b6f2ac5fbf..93fd0e239fb 100644 --- a/src/frontend/src/stores/messagesStore.ts +++ b/src/frontend/src/stores/messagesStore.ts @@ -34,11 +34,19 @@ export const useMessagesStore = create((set, get) => ({ })); }, updateMessagePartial: (message) => { - set(() => ({ - messages: get().messages.map((msg) => - msg.id === message.id ? { ...msg, ...message } : msg, - ), - })); + // search for the message and update it + // look for the message list backwards to find the message faster + for (let i = get().messages.length - 1; i >= 0; i--) { + if (get().messages[i].id === message.id) { + set((state) => { + const updatedMessages = [...state.messages]; + updatedMessages[i] = { ...updatedMessages[i], ...message }; + return { messages: updatedMessages }; + }); + break; + } + } + }, clearMessages: () => { set(() => ({ messages: [] })); From c9a8f19631283131ce5f41babd74dbc36d5d8eac Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 3 Sep 2024 13:22:46 -0300 Subject: [PATCH 068/592] Refactor `serialize_flow_id` method to correctly handle UUID serialization in `message.py` --- src/backend/base/langflow/schema/message.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/base/langflow/schema/message.py b/src/backend/base/langflow/schema/message.py index 17be844a716..a3ddf70fed0 100644 --- a/src/backend/base/langflow/schema/message.py +++ b/src/backend/base/langflow/schema/message.py @@ -57,9 +57,9 @@ def validate_flow_id(cls, value): @field_serializer("flow_id") def serialize_flow_id(value): - if isinstance(value, str): - return UUID(value) - return str(value) + if isinstance(value, UUID): + return str(value) + return value @field_serializer("timestamp") def serialize_timestamp(value): From a18d02c0c6f3316cbe01e8430b8df7bda36f961d Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 3 Sep 2024 13:22:54 -0300 Subject: [PATCH 069/592] Refactor `send_event` method to use `jsonable_encoder` for data serialization --- .../base/langflow/events/event_manager.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/backend/base/langflow/events/event_manager.py b/src/backend/base/langflow/events/event_manager.py index 7c5156a5b80..fc96dee6cf9 100644 --- a/src/backend/base/langflow/events/event_manager.py +++ b/src/backend/base/langflow/events/event_manager.py @@ -4,8 +4,9 @@ import uuid from functools import partial +from fastapi.encoders import jsonable_encoder from typing_extensions import Protocol -from langflow.schema.message import Message + from langflow.schema.log import LoggableType @@ -28,17 +29,12 @@ def register_event_function(self, name: str, event_function: EventCallback): self.events[name] = event_function def send_event(self, event_type: str, data: dict): + jsonable_data = jsonable_encoder(data) - if isinstance(data, Message): - data = data.model_dump() - - json_data = {"event": event_type, "data": data} + json_data = {"event": event_type, "data": jsonable_data} event_id = str(uuid.uuid4()) - try: - str_data = json.dumps(json_data, default=str) + "\n\n" - except: - import pdb; pdb.set_trace() - print('-') + str_data = json.dumps(json_data, default=str) + "\n\n" + self.queue.put_nowait((event_id, str_data.encode("utf-8"), time.time())) def noop(self, event_type: str, data: dict): From 5fe292e91d2ef1ba2773e2abb0df428e600a8af4 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 3 Sep 2024 14:07:30 -0300 Subject: [PATCH 070/592] refactor: optimize message update in useMessagesStore --- src/frontend/src/stores/messagesStore.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/frontend/src/stores/messagesStore.ts b/src/frontend/src/stores/messagesStore.ts index 93fd0e239fb..95afc295587 100644 --- a/src/frontend/src/stores/messagesStore.ts +++ b/src/frontend/src/stores/messagesStore.ts @@ -36,17 +36,16 @@ export const useMessagesStore = create((set, get) => ({ updateMessagePartial: (message) => { // search for the message and update it // look for the message list backwards to find the message faster - for (let i = get().messages.length - 1; i >= 0; i--) { - if (get().messages[i].id === message.id) { - set((state) => { - const updatedMessages = [...state.messages]; + set((state) => { + const updatedMessages = [...state.messages]; + for (let i = get().messages.length - 1; i >= 0; i--) { + if (get().messages[i].id === message.id) { updatedMessages[i] = { ...updatedMessages[i], ...message }; - return { messages: updatedMessages }; - }); - break; + break; + } } - } - + return { messages: updatedMessages }; + }); }, clearMessages: () => { set(() => ({ messages: [] })); From 8420ec56dc7207defbdab100bf792d1ecf8bb55d Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 3 Sep 2024 14:08:07 -0300 Subject: [PATCH 071/592] streaming working with timeout --- .../IOModal/components/chatView/chatMessage/index.tsx | 8 ++++++-- .../src/modals/IOModal/components/chatView/index.tsx | 2 +- src/frontend/src/utils/buildUtils.ts | 6 ++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx index 5b501107d4d..ba227096833 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx @@ -33,13 +33,17 @@ export default function ChatMessage({ const [streamUrl, setStreamUrl] = useState(chat.stream_url); // We need to check if message is not undefined because // we need to run .toString() on it - const chatMessageString = chat.message ? chat.message.toString() : ""; - const [chatMessage, setChatMessage] = useState(chatMessageString); + const [chatMessage, setChatMessage] = useState(chat.message ? chat.message.toString() : ""); const [isStreaming, setIsStreaming] = useState(false); const eventSource = useRef(undefined); const setErrorData = useAlertStore((state) => state.setErrorData); const chatMessageRef = useRef(chatMessage); + useEffect(() => { + const chatMessageString = chat.message ? chat.message.toString() : ""; + setChatMessage(chatMessageString); + },[chat]); + // Sync ref with state useEffect(() => { chatMessageRef.current = chatMessage; diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 14126ce6047..5f390671a52 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -111,7 +111,7 @@ export default function ChatView({ const finalChatHistory = [...messagesFromMessagesStore].sort((a, b) => { return (new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()); }) - // this function will remove duplicates from the chat history based on the timestamp + setChatHistory(finalChatHistory); }, [flowPool,messages]); diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 37580e25b51..d83895ef717 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -11,6 +11,7 @@ import { isErrorLogType } from "../types/utils/typeCheckingUtils"; import { VertexLayerElementType } from "../types/zustand/flow"; import { tryParseJson } from "./utils"; import { useMessagesStore } from "@/stores/messagesStore"; +import { timeStamp } from "console"; type BuildVerticesParams = { setLockChat?: (lock: boolean) => void; @@ -273,8 +274,9 @@ export async function buildFlowVertices({ return true; } case "token": { - console.log(data); - // await one second before sending the next token + // console.log(data); + // await one milisencond so the streaming can work + await new Promise((resolve) => setTimeout(resolve, 1)); useMessagesStore.getState().updateMessagePartial(data); return true; } From 6d321e993990e005ec4a4b0a65833c4961df8d33 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 3 Sep 2024 14:12:36 -0300 Subject: [PATCH 072/592] refactor: update buildUtils.ts to use data instead of data.data in addMessage function --- src/frontend/src/utils/buildUtils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index d83895ef717..a4e8ddf6901 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -269,8 +269,7 @@ export async function buildFlowVertices({ } case "message": { //adds a message to the messsage table - console.log(data.data); - useMessagesStore.getState().addMessage(data.data); + useMessagesStore.getState().addMessage(data); return true; } case "token": { From 7b1b8f8c132eca926342ff703fd87a98478bb4f2 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 3 Sep 2024 15:33:12 -0300 Subject: [PATCH 073/592] version with reactState for chatHistory --- src/frontend/src/modals/IOModal/components/chatView/index.tsx | 2 +- src/frontend/src/utils/buildUtils.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 5f390671a52..041501d6f20 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -267,7 +267,7 @@ export default function ChatView({ lockChat={lockChat} chat={chat} lastMessage={chatHistory.length - 1 === index ? true : false} - key={`${chat.componentId}-${index}`} + key={`${chat.id}-${index}`} updateChat={updateChat} /> )) diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index a4e8ddf6901..cbac47192e4 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -273,8 +273,7 @@ export async function buildFlowVertices({ return true; } case "token": { - // console.log(data); - // await one milisencond so the streaming can work + // await one milisencond so we avoid react batched updates await new Promise((resolve) => setTimeout(resolve, 1)); useMessagesStore.getState().updateMessagePartial(data); return true; From 49129ebac11aaafd919c30d86bef11390db69757 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 3 Sep 2024 17:20:35 -0300 Subject: [PATCH 074/592] refactor: update on_message method in ChatComponent --- src/backend/base/langflow/base/io/chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index 5648d354a73..21dba362a8e 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -25,12 +25,12 @@ def store_message( raise ValueError("Only one message can be stored at a time.") stored_message = messages[0] if hasattr(self, "_event_manager") and self._event_manager and stored_message.id: - self._event_manager.on_message(stored_message) if not isinstance(message.text, str): complete_message = self._stream_message(message, stored_message.id) message_table = update_message(message_id=stored_message.id, message=dict(text=complete_message)) stored_message = Message(**message_table.model_dump()) self.vertex._added_message = stored_message + self._event_manager.on_message(data=stored_message.model_dump()) self.status = stored_message return stored_message From b598231a6920c328bc1a4cc72317abaa2d01c9fd Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 3 Sep 2024 17:41:08 -0300 Subject: [PATCH 075/592] refactor: update on_message method in ChatComponent --- src/backend/base/langflow/base/io/chat.py | 2 +- src/backend/base/langflow/events/event_manager.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index b6875e1f7c8..e4cabb8a237 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -26,12 +26,12 @@ def store_message( raise ValueError("Only one message can be stored at a time.") stored_message = messages[0] if hasattr(self, "_event_manager") and self._event_manager and stored_message.id: + self._event_manager.on_message(data=stored_message.data) if not isinstance(message.text, str): complete_message = self._stream_message(message, stored_message.id) message_table = update_message(message_id=stored_message.id, message=dict(text=complete_message)) stored_message = Message(**message_table.model_dump()) self.vertex._added_message = stored_message - self._event_manager.on_message(data=stored_message.model_dump()) self.status = stored_message return stored_message diff --git a/src/backend/base/langflow/events/event_manager.py b/src/backend/base/langflow/events/event_manager.py index 121d46544a3..2919490b6ed 100644 --- a/src/backend/base/langflow/events/event_manager.py +++ b/src/backend/base/langflow/events/event_manager.py @@ -8,7 +8,7 @@ from typing_extensions import Protocol from langflow.schema.log import LoggableType - +from fastapi.encoders import jsonable_encoder class EventCallback(Protocol): def __call__(self, *, manager: "EventManager", event_type: str, data: LoggableType): ... @@ -46,7 +46,8 @@ def register_event(self, name: str, event_type: str, callback: EventCallback | N self.events[name] = _callback def send_event(self, *, event_type: str, data: LoggableType): - json_data = {"event": event_type, "data": data} + jsonable_data = jsonable_encoder(data) + json_data = {"event": event_type, "data": jsonable_data} event_id = uuid.uuid4() str_data = json.dumps(json_data) + "\n\n" self.queue.put_nowait((event_id, str_data.encode("utf-8"), time.time())) From 560de167daac09e5fb23590c380e0e17c03c0427 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 6 Sep 2024 14:56:07 -0300 Subject: [PATCH 076/592] refactor: Remove unused dependency in package-lock.json --- src/frontend/package-lock.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 5dddee9ed1a..493e02b23a3 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -13072,12 +13072,6 @@ "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", "license": "(MIT AND Zlib)" }, - "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "license": "(MIT AND Zlib)" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", From c27c73aad06d1fcc591d4e859150f00943b814c3 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 6 Sep 2024 15:10:23 -0300 Subject: [PATCH 077/592] Refactor chatView component and add hiddenSession prop --- .../IOModal/components/chatView/index.tsx | 1 + src/frontend/src/modals/IOModal/index.tsx | 76 +++++++++++-------- src/frontend/src/types/components/index.ts | 1 + 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 041501d6f20..0ab12deb4e0 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -29,6 +29,7 @@ export default function ChatView({ setChatValue, lockChat, setLockChat, + hiddenSession, }: chatViewProps): JSX.Element { const { flowPool, outputs, inputs, CleanFlowPool } = useFlowStore(); const { setErrorData } = useAlertStore(); diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index e87916d892f..4a41fdbffe7 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -359,14 +359,7 @@ export default function IOModal({ return (
{ - event.stopPropagation(); - setSelectedViewField({ - id: session, - type: "Session", - }); - }} + className="file-component-accordion-div" >
@@ -383,6 +376,48 @@ export default function IOModal({
+ + - {/*
- - f_session?.session_id === session, - ) - ? "Active Session" - : "Inactive Session" - } - > -
- f_session?.session_id === session, - ) - ? "bg-status-green" - : "bg-slate-500", - )} - >
-
-
*/}
diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index bce079119cf..be8bd6d0b23 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -851,6 +851,7 @@ export type chatViewProps = { setChatValue: (value: string) => void; lockChat: boolean; setLockChat: (lock: boolean) => void; + hiddenSession?: boolean; }; export type IOFileInputProps = { From 35d3aa47b341be59cf0e8cb963c3fda934a34f3b Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 6 Sep 2024 16:54:59 -0300 Subject: [PATCH 078/592] Refactor chatView component and update hiddenSessions prop --- .../src/modals/IOModal/components/chatView/index.tsx | 2 +- src/frontend/src/modals/IOModal/index.tsx | 9 +++++++-- src/frontend/src/types/components/index.ts | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 0ab12deb4e0..524011c31fe 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -29,7 +29,7 @@ export default function ChatView({ setChatValue, lockChat, setLockChat, - hiddenSession, + hiddenSessions, }: chatViewProps): JSX.Element { const { flowPool, outputs, inputs, CleanFlowPool } = useFlowStore(); const { setErrorData } = useAlertStore(); diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index 4a41fdbffe7..98de7d3a15d 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -62,6 +62,7 @@ export default function IOModal({ const deleteSession = useMessagesStore((state) => state.deleteSession); const { mutate: deleteSessionFunction } = useDeleteMessages(); + const [hiddenSessions, setHiddenSessions] = useState([]); function handleDeleteSession(session_id: string) { deleteSessionFunction( @@ -169,9 +170,12 @@ export default function IOModal({ sessions.add(row.session_id); }); setSessions(Array.from(sessions)); - sessions; }, [messages]); + useEffect(() => { + setHiddenSessions(sessions); + }, []); + return (
@@ -521,6 +525,7 @@ export default function IOModal({ setChatValue={setChatValue} lockChat={lockChat} setLockChat={setLockChat} + hiddenSessions={[]} /> ) : ( diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index be8bd6d0b23..67c8d71fd9c 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -851,7 +851,7 @@ export type chatViewProps = { setChatValue: (value: string) => void; lockChat: boolean; setLockChat: (lock: boolean) => void; - hiddenSession?: boolean; + hiddenSessions?: string[]; }; export type IOFileInputProps = { From 4283600989c1039d0765410478748bb092748267 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Mon, 9 Sep 2024 14:57:56 -0300 Subject: [PATCH 079/592] Refactor chatView component to use visibleSessions prop instead of hiddenSessions --- .../IOModal/components/chatView/index.tsx | 52 ++----------------- src/frontend/src/modals/IOModal/index.tsx | 17 ++++-- src/frontend/src/types/components/index.ts | 2 +- 3 files changed, 16 insertions(+), 55 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 524011c31fe..a2067b93d3d 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -29,7 +29,7 @@ export default function ChatView({ setChatValue, lockChat, setLockChat, - hiddenSessions, + visibleSessions }: chatViewProps): JSX.Element { const { flowPool, outputs, inputs, CleanFlowPool } = useFlowStore(); const { setErrorData } = useAlertStore(); @@ -47,53 +47,7 @@ export default function ChatView({ //build chat history useEffect(() => { - const chatOutputResponses: VertexBuildTypeAPI[] = []; - outputIds.forEach((outputId) => { - if (outputId.includes("ChatOutput")) { - if (flowPool[outputId] && flowPool[outputId].length > 0) { - chatOutputResponses.push(...flowPool[outputId]); - } - } - }); - inputIds.forEach((inputId) => { - if (inputId.includes("ChatInput")) { - if (flowPool[inputId] && flowPool[inputId].length > 0) { - chatOutputResponses.push(...flowPool[inputId]); - } - } - }); - const messagesFromPool: ChatMessageType[] = chatOutputResponses - .filter( - (output) => - output.data.message!==undefined - ) - .map((output, index) => { - try { - const messageOutput = output.data.message!; - const { sender, message, sender_name, stream_url, files } = messageOutput - - const is_ai = - sender === "Machine" || sender === null || sender === undefined; - return { - isSend: !is_ai, - message, - sender_name, - stream_url: stream_url, - files, - timestamp: output.timestamp, - }; - } catch (e) { - console.error(e); - return { - isSend: false, - message: "Error parsing message", - sender_name: "Error", - componentId: output.id, - timestamp: output.timestamp, - }; - } - }); - const messagesFromMessagesStore:ChatMessageType[] = messages.filter(message=>message.flow_id===currentFlowId) + const messagesFromMessagesStore:ChatMessageType[] = messages.filter(message=>message.flow_id===currentFlowId && (visibleSessions?.includes(message.session_id)??true)) .map((message) => { let files = message.files; //HANDLE THE "[]" case @@ -115,7 +69,7 @@ export default function ChatView({ setChatHistory(finalChatHistory); - }, [flowPool,messages]); + }, [flowPool,messages,visibleSessions]); useEffect(() => { if (messagesRef.current) { messagesRef.current.scrollTop = messagesRef.current.scrollHeight; diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index 98de7d3a15d..99500b871a1 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -62,7 +62,7 @@ export default function IOModal({ const deleteSession = useMessagesStore((state) => state.deleteSession); const { mutate: deleteSessionFunction } = useDeleteMessages(); - const [hiddenSessions, setHiddenSessions] = useState([]); + const [visibleSessions, setvisibleSessions] = useState([]); function handleDeleteSession(session_id: string) { deleteSessionFunction( @@ -170,10 +170,11 @@ export default function IOModal({ sessions.add(row.session_id); }); setSessions(Array.from(sessions)); + setvisibleSessions(Array.from(sessions)); }, [messages]); useEffect(() => { - setHiddenSessions(sessions); + }, []); return ( @@ -384,7 +385,13 @@ export default function IOModal({ unstyled size="icon" onClick={(e) => { - + e.preventDefault(); + e.stopPropagation(); + setvisibleSessions((prev) => + prev.includes(session) + ? prev.filter((item) => item !== session) + : [...prev, session], + ); }} >
@@ -525,7 +532,7 @@ export default function IOModal({ setChatValue={setChatValue} lockChat={lockChat} setLockChat={setLockChat} - hiddenSessions={[]} + visibleSessions={visibleSessions} /> ) : ( diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 67c8d71fd9c..5c63021d6d9 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -851,7 +851,7 @@ export type chatViewProps = { setChatValue: (value: string) => void; lockChat: boolean; setLockChat: (lock: boolean) => void; - hiddenSessions?: string[]; + visibleSessions?: string[]; }; export type IOFileInputProps = { From 1d54fdbbfaba8556496c45bf588300fdcde4ccad Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Mon, 9 Sep 2024 15:10:47 -0300 Subject: [PATCH 080/592] Refactor IOModal component to remove redundant code --- src/frontend/src/modals/IOModal/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index 99500b871a1..b871af87932 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -170,7 +170,6 @@ export default function IOModal({ sessions.add(row.session_id); }); setSessions(Array.from(sessions)); - setvisibleSessions(Array.from(sessions)); }, [messages]); useEffect(() => { From 3dfbe66a40742605e8f43cb23fb553ce9caa4bca Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Mon, 9 Sep 2024 15:58:15 -0300 Subject: [PATCH 081/592] Refactor chatView component to include focusChat prop --- .../modals/IOModal/components/chatView/index.tsx | 5 +++-- src/frontend/src/modals/IOModal/index.tsx | 14 ++++++++++++++ src/frontend/src/types/components/index.ts | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index a2067b93d3d..0a1c1e20af4 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -29,7 +29,8 @@ export default function ChatView({ setChatValue, lockChat, setLockChat, - visibleSessions + visibleSessions, + focusChat }: chatViewProps): JSX.Element { const { flowPool, outputs, inputs, CleanFlowPool } = useFlowStore(); const { setErrorData } = useAlertStore(); @@ -82,7 +83,7 @@ export default function ChatView({ if (ref.current) { ref.current.focus(); } - }, []); + }, [focusChat]); function clearChat(): void { setChatHistory([]); diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index b871af87932..a8ddb83bd06 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -27,6 +27,7 @@ import BaseModal from "../baseModal"; import IOFieldView from "./components/IOFieldView"; import SessionView from "./components/SessionView"; import ChatView from "./components/chatView"; +import ShortUniqueId from "short-unique-id"; export default function IOModal({ children, @@ -114,6 +115,8 @@ export default function IOModal({ const [sessions, setSessions] = useState([]); const messages = useMessagesStore((state) => state.messages); const flowPool = useFlowStore((state) => state.flowPool); + const uuid = new ShortUniqueId(); + const [sessionId, setSessionId] = useState(); const { refetch } = useGetMessagesQuery( { @@ -461,6 +464,16 @@ export default function IOModal({ No memories available. )} + {sessions.length>0 &&
+ +
} @@ -526,6 +539,7 @@ export default function IOModal({ > {haveChat ? ( void; visibleSessions?: string[]; + focusChat?: string; }; export type IOFileInputProps = { From 314747d3d88bc3615a3e6b0e66c95fbdda485800 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Mon, 9 Sep 2024 16:06:00 -0300 Subject: [PATCH 082/592] Refactor chatView component to include focusChat prop and trigger focus on chat when new session is set --- src/frontend/src/modals/IOModal/components/chatView/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 0a1c1e20af4..f4384f5798a 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -83,6 +83,7 @@ export default function ChatView({ if (ref.current) { ref.current.focus(); } + // trigger focus on chat when new session is set }, [focusChat]); function clearChat(): void { From ecabdb118e054bf68a7f607d194a1e810726d99d Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 10 Sep 2024 13:08:59 -0300 Subject: [PATCH 083/592] Refactor IOModal component to update visible sessions when new session is added --- src/frontend/src/modals/IOModal/index.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index 6d3a7304ee5..eb90e581210 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -173,7 +173,16 @@ export default function IOModal({ .forEach((row) => { sessions.add(row.session_id); }); - setSessions(Array.from(sessions)); + setSessions((prev)=>{ + if(prev.length [ + ...prev, + Array.from(sessions)[Array.from(sessions).length - 1], + ]) + } + return Array.from(sessions); + }); }, [messages]); const setPlaygroundScrollBehaves = useUtilityStore( From a360615279fce3af9e236ddedbdf8775fae0f657 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 12 Sep 2024 17:08:34 -0300 Subject: [PATCH 084/592] feat: Add session parameter to buildFlowVertices function --- src/frontend/src/utils/buildUtils.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index cbac47192e4..c8881e8330d 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -34,6 +34,7 @@ type BuildVerticesParams = { nodes?: Node[]; edges?: Edge[]; logBuilds?: boolean; + session?: string; }; function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI { @@ -153,6 +154,7 @@ export async function buildFlowVertices({ edges, logBuilds, setLockChat, + session, }: BuildVerticesParams) { let url = `${BASE_URL_API}build/${flowId}/flow?`; if (startNodeId) { @@ -177,6 +179,9 @@ export async function buildFlowVertices({ edges, }; } + if(session) { + postData["session"] = session; + } const buildResults: Array = []; From 16ec7f2f596d4d3f164a3dda3a8de6ed29fe6f8f Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 12 Sep 2024 17:08:59 -0300 Subject: [PATCH 085/592] feat: Add someFlowTemplateFields function Add the someFlowTemplateFields function to the reactflowUtils module. This function checks if any of the nodes in the provided array have template fields that pass a given validation function. --- src/frontend/src/utils/reactflowUtils.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index dff2e843699..141887c74ea 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -1712,3 +1712,12 @@ export function checkOldComponents({ nodes }: { nodes: any[] }) { ), ); } + + +export function someFlowTemplateFields({nodes}:{nodes:NodeType[]},validateFn:(field:InputFieldType)=>boolean):boolean{ + return nodes.some((node)=>{ + return Object.keys(node.data.node?.template??{}).some((field)=>{ + return validateFn((node.data.node?.template??{})[field]) + }) + }) +} From 5dbdf573361231bf11a89e2eb97c564437592177 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 12 Sep 2024 17:09:09 -0300 Subject: [PATCH 086/592] feat: Add session parameter to buildFlowVertices function --- src/frontend/src/stores/flowStore.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index 6fa47d09396..c81bf03d471 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -496,6 +496,7 @@ const useFlowStore = create((set, get) => ({ files, silent, setLockChat, + session }: { startNodeId?: string; stopNodeId?: string; @@ -503,6 +504,7 @@ const useFlowStore = create((set, get) => ({ files?: string[]; silent?: boolean; setLockChat?: (lock: boolean) => void; + session?: string; }) => { get().setIsBuilding(true); get().setLockChat(true); @@ -606,6 +608,7 @@ const useFlowStore = create((set, get) => ({ useFlowStore.getState().updateBuildStatus([vertexBuildData.id], status); } await buildFlowVerticesWithFallback({ + session, input_value, files, flowId: currentFlow!.id, From 631468f560d8b03b09a864e552f93b43e1b11c2f Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 12 Sep 2024 17:09:15 -0300 Subject: [PATCH 087/592] feat: Add session parameter to buildFlowVertices function --- src/frontend/src/types/zustand/flow/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/src/types/zustand/flow/index.ts b/src/frontend/src/types/zustand/flow/index.ts index ac4705d8ebf..0c1e10915bd 100644 --- a/src/frontend/src/types/zustand/flow/index.ts +++ b/src/frontend/src/types/zustand/flow/index.ts @@ -130,6 +130,7 @@ export type FlowStoreType = { files, silent, setLockChat, + session }: { setLockChat?: (lock: boolean) => void; startNodeId?: string; @@ -137,6 +138,7 @@ export type FlowStoreType = { input_value?: string; files?: string[]; silent?: boolean; + session?: string; }) => Promise; getFlow: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport }; updateVerticesBuild: ( From 18af9810f12ca45159b4d4599b741740d6c870d9 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 12 Sep 2024 17:09:37 -0300 Subject: [PATCH 088/592] update Session logic on ioModal --- src/frontend/src/modals/IOModal/index.tsx | 39 +++++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index eb90e581210..2252456d150 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -29,6 +29,7 @@ import IOFieldView from "./components/IOFieldView"; import SessionView from "./components/SessionView"; import ChatView from "./components/chatView"; import ShortUniqueId from "short-unique-id"; +import { someFlowTemplateFields } from "@/utils/reactflowUtils"; export default function IOModal({ children, @@ -60,6 +61,7 @@ export default function IOModal({ inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0, ); const setErrorData = useAlertStore((state) => state.setErrorData); + const setNoticeData = useAlertStore((state) => state.setNoticeData); const setSuccessData = useAlertStore((state) => state.setSuccessData); const deleteSession = useMessagesStore((state) => state.deleteSession); @@ -117,7 +119,8 @@ export default function IOModal({ const messages = useMessagesStore((state) => state.messages); const flowPool = useFlowStore((state) => state.flowPool); const uuid = new ShortUniqueId(); - const [sessionId, setSessionId] = useState(); + const [sessionId, setSessionId] = useState(currentFlowId); + const [SessionInFlow, setSessionInFlow] = useState(false); const { refetch } = useGetMessagesQuery( { @@ -127,6 +130,15 @@ export default function IOModal({ { enabled: open }, ); + useEffect(() => { + const hasSectionInFlow =someFlowTemplateFields({nodes:allNodes},(Field)=>Field.display_name?.toLocaleLowerCase()==="session id" && Field.value) + setSessionInFlow(hasSectionInFlow) + //if there is no session in the flow components we should not allow the user to see multiple sessions at a time + if(!hasSectionInFlow && visibleSessions.length>1){ + setvisibleSessions([]) + } + }, [allNodes]); + async function sendMessage({ repeat = 1, files, @@ -138,12 +150,23 @@ export default function IOModal({ setIsBuilding(true); setLockChat(true); setChatValue(""); + const runSession = visibleSessions.length>1?undefined:sessionId; + // check for multiple sessions view without session id in flow + if(!SessionInFlow && !runSession){ + setNoticeData({ + title:"Multiple sessions are supported only when a session id is set inside components, otherwise only the default session will be used.", + }) + if(sessions.includes(currentFlowId)){ + setvisibleSessions([currentFlowId]) + } + } for (let i = 0; i < repeat; i++) { await buildFlow({ input_value: chatValue, startNodeId: chatInput?.id, files: files, silent: true, + session: runSession, setLockChat, }).catch((err) => { console.error(err); @@ -185,6 +208,17 @@ export default function IOModal({ }); }, [messages]); + useEffect(() => { + if(visibleSessions.length===0 && sessions.length>0) + { + setSessionId(uuid.randomUUID(8)) + } + else if(visibleSessions.length===1) + { + setSessionId(visibleSessions[0]) + } + }, [visibleSessions]); + const setPlaygroundScrollBehaves = useUtilityStore( (state) => state.setPlaygroundScrollBehaves, ); @@ -408,7 +442,7 @@ export default function IOModal({ setvisibleSessions((prev) => prev.includes(session) ? prev.filter((item) => item !== session) - : [...prev, session], + : (SessionInFlow?[...prev, session]:[session]), ); }} > @@ -484,7 +518,6 @@ export default function IOModal({ -
{chatHistory?.length > 0 ? ( chatHistory.map((chat, index) => ( From 35cbea4d29e2d51fb83946c7c5688a6819a70421 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 13 Sep 2024 11:19:17 -0300 Subject: [PATCH 090/592] Refactor Vertex class: Inject session_id if provided in inputs --- src/backend/base/langflow/graph/vertex/base.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index 76d357063bb..28530aa7674 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -781,9 +781,14 @@ async def build( # and we are just getting the result for the requester return await self.get_requester_result(requester) self._reset() - + # inject session_id if it is not None + if "session" in inputs and inputs.get("session") is not None and self.has_session_id: + session_id_value = self.get_value_from_template_dict("session_id") + if session_id_value =='': + self.update_raw_params({"session_id": inputs.get("session")}, overwrite=True) if self._is_chat_input() and (inputs or files): chat_input = {} + # aqui paizao fica na sua if inputs: chat_input.update({"input_value": inputs.get(INPUT_FIELD_NAME, "")}) if files: From 5f9e6a0f486998678bfab04166ea2907b5a91600 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 13 Sep 2024 11:19:45 -0300 Subject: [PATCH 091/592] Refactor build_flow function: Set default session if inputs are empty --- src/backend/base/langflow/api/v1/chat.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 5990807f3ce..86e64003f4f 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -6,7 +6,7 @@ import uuid from typing import TYPE_CHECKING, Annotated -from fastapi import APIRouter, BackgroundTasks, Body, Depends, HTTPException +from fastapi import APIRouter, BackgroundTasks, Body, Depends, HTTPException, Request from fastapi.responses import StreamingResponse from loguru import logger from starlette.background import BackgroundTask @@ -153,6 +153,8 @@ async def build_flow( telemetry_service: "TelemetryService" = Depends(get_telemetry_service), session=Depends(get_session), ): + if not inputs: + inputs = InputValueRequest(session=str(flow_id)) async def build_graph_and_get_order() -> tuple[list[str], list[str], "Graph"]: start_time = time.perf_counter() components_count = None From 95ccbdb96ac6c95b059e73e6dc99080e693df903 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 13 Sep 2024 11:19:54 -0300 Subject: [PATCH 092/592] Refactor InputValueRequest schema: Add session parameter --- src/backend/base/langflow/api/v1/schemas.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backend/base/langflow/api/v1/schemas.py b/src/backend/base/langflow/api/v1/schemas.py index da27a3a7a3b..498e704e7fa 100644 --- a/src/backend/base/langflow/api/v1/schemas.py +++ b/src/backend/base/langflow/api/v1/schemas.py @@ -296,6 +296,7 @@ class VerticesBuiltResponse(BaseModel): class InputValueRequest(BaseModel): components: list[str] | None = [] input_value: str | None = None + session: str | None = None type: InputType | None = Field( "any", description="Defines on which components the input value should be applied. 'any' applies to all input components.", @@ -308,9 +309,12 @@ class InputValueRequest(BaseModel): { "components": ["components_id", "Component Name"], "input_value": "input_value", + "session": "session_id", }, {"components": ["Component Name"], "input_value": "input_value"}, {"input_value": "input_value"}, + {"components": ["Component Name"], "input_value": "input_value","session": "session_id"}, + {"input_value": "input_value","session": "session_id"}, {"type": "chat", "input_value": "input_value"}, {"type": "json", "input_value": '{"key": "value"}'}, ] From 4c42d481b6c2f7e087767747cc9db650efe15caf Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 13 Sep 2024 11:20:12 -0300 Subject: [PATCH 093/592] Refactor IOModal component: Update session logic --- src/frontend/src/modals/IOModal/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index 2252456d150..bcf5cdcfe21 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -211,7 +211,7 @@ export default function IOModal({ useEffect(() => { if(visibleSessions.length===0 && sessions.length>0) { - setSessionId(uuid.randomUUID(8)) + setSessionId(`Session ${uuid.randomUUID(4)}`) } else if(visibleSessions.length===1) { @@ -448,7 +448,7 @@ export default function IOModal({ >
Date: Fri, 13 Sep 2024 11:20:22 -0300 Subject: [PATCH 094/592] Refactor buildFlowVertices function: Update input handling --- src/frontend/src/utils/buildUtils.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index c8881e8330d..c0a56c4d063 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -156,6 +156,7 @@ export async function buildFlowVertices({ setLockChat, session, }: BuildVerticesParams) { + const inputs = {}; let url = `${BASE_URL_API}build/${flowId}/flow?`; if (startNodeId) { url = `${url}&start_component_id=${startNodeId}`; @@ -167,9 +168,6 @@ export async function buildFlowVertices({ url = `${url}&log_builds=${logBuilds}`; } const postData = {}; - if (typeof input_value !== "undefined") { - postData["inputs"] = { input_value: input_value }; - } if (files) { postData["files"] = files; } @@ -179,10 +177,17 @@ export async function buildFlowVertices({ edges, }; } - if(session) { - postData["session"] = session; + if (typeof input_value !== "undefined") { + inputs["input_value"] = input_value; + } + if(session){ + inputs["session"] = session; + } + if(Object.keys(inputs).length > 0){ + postData["inputs"] = inputs; } + const buildResults: Array = []; const verticesStartTimeMs: Map = new Map(); From ab7ee2ab50bef16d8b5dfc4597a1aa1eab97169d Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 13 Sep 2024 11:50:38 -0300 Subject: [PATCH 095/592] Refactor MessagesStoreType in zustand/messages/index.ts: Remove unused columns property and setColumns method --- src/frontend/src/types/zustand/messages/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/frontend/src/types/zustand/messages/index.ts b/src/frontend/src/types/zustand/messages/index.ts index 09fa9b0d6ad..6a4051def72 100644 --- a/src/frontend/src/types/zustand/messages/index.ts +++ b/src/frontend/src/types/zustand/messages/index.ts @@ -10,7 +10,5 @@ export type MessagesStoreType = { updateMessagePartial: (message: Partial) => void; clearMessages: () => void; removeMessages: (ids: string[]) => void; - columns: Array; - setColumns: (columns: Array) => void; deleteSession: (id: string) => void; }; From ed86d88abc94b0a8b066036d93efe0dc8ebc901e Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 13 Sep 2024 11:50:44 -0300 Subject: [PATCH 096/592] Refactor MessagesStoreType: Remove unused columns property and setColumns method --- src/frontend/src/stores/messagesStore.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/frontend/src/stores/messagesStore.ts b/src/frontend/src/stores/messagesStore.ts index 95afc295587..a9a5b9a32b7 100644 --- a/src/frontend/src/stores/messagesStore.ts +++ b/src/frontend/src/stores/messagesStore.ts @@ -10,10 +10,6 @@ export const useMessagesStore = create((set, get) => ({ return { messages: updatedMessages }; }); }, - columns: [], - setColumns: (columns) => { - set(() => ({ columns: columns })); - }, messages: [], setMessages: (messages) => { set(() => ({ messages: messages })); From 3211b222db052e09d89ac147f10cf5adad55b2ea Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 13 Sep 2024 11:51:08 -0300 Subject: [PATCH 097/592] Refactor SessionView component: Update columns extraction logic --- .../src/modals/IOModal/components/SessionView/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/SessionView/index.tsx b/src/frontend/src/modals/IOModal/components/SessionView/index.tsx index 9dc3002828c..404f2cdb47d 100644 --- a/src/frontend/src/modals/IOModal/components/SessionView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/SessionView/index.tsx @@ -10,7 +10,7 @@ import { useMemo, useState } from "react"; import TableComponent from "../../../../components/tableComponent"; import useAlertStore from "../../../../stores/alertStore"; import { useMessagesStore } from "../../../../stores/messagesStore"; -import { messagesSorter } from "../../../../utils/utils"; +import { extractColumnsFromRows, messagesSorter } from "../../../../utils/utils"; export default function SessionView({ session, @@ -19,12 +19,12 @@ export default function SessionView({ session?: string; id?: string; }) { - const columns = useMessagesStore((state) => state.columns); const messages = useMessagesStore((state) => state.messages); const setErrorData = useAlertStore((state) => state.setErrorData); const setSuccessData = useAlertStore((state) => state.setSuccessData); const updateMessage = useMessagesStore((state) => state.updateMessage); const deleteMessagesStore = useMessagesStore((state) => state.removeMessages); + const columns = extractColumnsFromRows(messages, "intersection"); const isFetching = useIsFetching({ queryKey: ["useGetMessagesQuery"], exact: false, From 1580757d8fd05d42a5e12f4732689e59fc514d4b Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 13 Sep 2024 11:51:16 -0300 Subject: [PATCH 098/592] Refactor ChatView component: Remove unused variables --- src/frontend/src/modals/IOModal/components/chatView/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 6549d5f3741..b510544c855 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -40,8 +40,6 @@ export default function ChatView({ const messages = useMessagesStore((state) => state.messages); const inputTypes = inputs.map((obj) => obj.type); - const inputIds = inputs.map((obj) => obj.id); - const outputIds = outputs.map((obj) => obj.id); const updateFlowPool = useFlowStore((state) => state.updateFlowPool); const [id, setId] = useState(""); const { mutate: mutateDeleteFlowPool } = useDeleteBuilds(); From 6a0b8ef0b3b26e62165228f3f8b2dbd42235532a Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 13 Sep 2024 11:51:21 -0300 Subject: [PATCH 099/592] Refactor useGetMessagesQuery: Remove unused setColumns method --- .../src/controllers/API/queries/messages/use-get-messages.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/src/controllers/API/queries/messages/use-get-messages.ts b/src/frontend/src/controllers/API/queries/messages/use-get-messages.ts index 380d22ed71d..c0be11627fd 100644 --- a/src/frontend/src/controllers/API/queries/messages/use-get-messages.ts +++ b/src/frontend/src/controllers/API/queries/messages/use-get-messages.ts @@ -40,7 +40,6 @@ export const useGetMessagesQuery: useQueryFunctionType< const data = await getMessagesFn(id, params); const columns = extractColumnsFromRows(data.data, mode, excludedFields); useMessagesStore.getState().setMessages(data.data); - useMessagesStore.getState().setColumns(columns); return { rows: data, columns }; }; From 4571a8b27cf1d369d560b7f27e07bfd7b578c693 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 13 Sep 2024 13:40:23 -0300 Subject: [PATCH 100/592] Refactor RenderIcons component: Set default value for filteredShortcut prop to prevent bug --- src/frontend/src/components/renderIconComponent/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/components/renderIconComponent/index.tsx b/src/frontend/src/components/renderIconComponent/index.tsx index d64426b402b..1b23da848b4 100644 --- a/src/frontend/src/components/renderIconComponent/index.tsx +++ b/src/frontend/src/components/renderIconComponent/index.tsx @@ -3,7 +3,7 @@ import { addPlusSignes, cn, sortShortcuts } from "@/utils/utils"; import RenderKey from "./components/renderKey"; export default function RenderIcons({ - filteredShortcut, + filteredShortcut=[], tableRender = false, }: { filteredShortcut: string[]; From de4f3fc78585cbc36c7994270ce015d10c074ca9 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Wed, 18 Sep 2024 15:49:55 -0300 Subject: [PATCH 101/592] create edit message component for chat view --- .../components/editMessageField/index.tsx | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/editMessageField/index.tsx diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/editMessageField/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/editMessageField/index.tsx new file mode 100644 index 00000000000..1f6ad3fd0af --- /dev/null +++ b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/editMessageField/index.tsx @@ -0,0 +1,45 @@ +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { useEffect, useRef, useState } from "react"; + +export default function EditMessageField({ + message: initialMessage, + onEdit, + onCancel, +}: { + message: string; + onEdit: (message: string) => void; + onCancel: () => void; +}) { + const [message, setMessage] = useState(initialMessage); + const textareaRef = useRef(null); + const adjustTextareaHeight = () => { + if (textareaRef.current) { + textareaRef.current.style.height = "auto"; + textareaRef.current.style.height = `${textareaRef.current.scrollHeight+3}px`; + } + }; + useEffect(() => { + adjustTextareaHeight(); + }, []); + + return ( +
+