Remove console.log

This commit is contained in:
igorrCarvalho 2024-02-28 18:10:48 -03:00
commit 25d3b96600
88 changed files with 1621 additions and 1797 deletions

View file

@ -3,7 +3,15 @@ name: lint
on: on:
push: push:
branches: [main] branches: [main]
paths:
- "poetry.lock"
- "pyproject.toml"
- "src/backend/**"
pull_request: pull_request:
paths:
- "poetry.lock"
- "pyproject.toml"
- "src/backend/**"
env: env:
POETRY_VERSION: "1.7.0" POETRY_VERSION: "1.7.0"

View file

@ -3,8 +3,16 @@ name: test
on: on:
push: push:
branches: [main] branches: [main]
paths:
- "poetry.lock"
- "pyproject.toml"
- "src/backend/**"
pull_request: pull_request:
branches: [dev] branches: [dev]
paths:
- "poetry.lock"
- "pyproject.toml"
- "src/backend/**"
env: env:
POETRY_VERSION: "1.5.0" POETRY_VERSION: "1.5.0"

View file

@ -1,10 +1,11 @@
import time import time
import uuid import uuid
from typing import TYPE_CHECKING, Optional from typing import TYPE_CHECKING, Annotated, Optional
from fastapi import ( from fastapi import (
APIRouter, APIRouter,
BackgroundTasks, BackgroundTasks,
Body,
Depends, Depends,
HTTPException, HTTPException,
WebSocket, WebSocket,
@ -21,6 +22,7 @@ from langflow.api.utils import (
format_exception_message, format_exception_message,
) )
from langflow.api.v1.schemas import ( from langflow.api.v1.schemas import (
InputValueRequest,
ResultDataResponse, ResultDataResponse,
StreamData, StreamData,
VertexBuildResponse, VertexBuildResponse,
@ -32,8 +34,9 @@ from langflow.services.auth.utils import (
get_current_user_for_websocket, get_current_user_for_websocket,
) )
from langflow.services.chat.service import ChatService from langflow.services.chat.service import ChatService
from langflow.services.deps import get_chat_service, get_session from langflow.services.deps import get_chat_service, get_session, get_session_service
from langflow.services.monitor.utils import log_vertex_build from langflow.services.monitor.utils import log_vertex_build
from langflow.services.session.service import SessionService
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.graph.vertex.types import ChatVertex from langflow.graph.vertex.types import ChatVertex
@ -138,10 +141,12 @@ async def build_vertex(
flow_id: str, flow_id: str,
vertex_id: str, vertex_id: str,
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
inputs: Annotated[InputValueRequest, Body(embed=True)] = None,
chat_service: "ChatService" = Depends(get_chat_service), chat_service: "ChatService" = Depends(get_chat_service),
current_user=Depends(get_current_active_user), current_user=Depends(get_current_active_user),
): ):
"""Build a vertex instead of the entire graph.""" """Build a vertex instead of the entire graph."""
{"inputs": {"input_value": "some value"}}
start_time = time.perf_counter() start_time = time.perf_counter()
try: try:
start_time = time.perf_counter() start_time = time.perf_counter()
@ -162,7 +167,8 @@ async def build_vertex(
vertex = graph.get_vertex(vertex_id) vertex = graph.get_vertex(vertex_id)
try: try:
if not vertex.pinned or not vertex._built: if not vertex.pinned or not vertex._built:
await vertex.build(user_id=current_user.id) inputs_dict = inputs.model_dump() if inputs else {}
await vertex.build(user_id=current_user.id, inputs=inputs_dict)
if vertex.result is not None: if vertex.result is not None:
params = vertex._built_object_repr() params = vertex._built_object_repr()
@ -175,7 +181,7 @@ async def build_vertex(
result_data_response = ResultDataResponse(**result_dict.model_dump()) result_data_response = ResultDataResponse(**result_dict.model_dump())
except Exception as exc: except Exception as exc:
logger.error(f"Error building vertex: {exc}") logger.exception(f"Error building vertex: {exc}")
params = format_exception_message(exc) params = format_exception_message(exc)
valid = False valid = False
result_data_response = ResultDataResponse(results={}) result_data_response = ResultDataResponse(results={})
@ -185,15 +191,16 @@ async def build_vertex(
chat_service.clear_cache(flow_id) chat_service.clear_cache(flow_id)
# Log the vertex build # Log the vertex build
background_tasks.add_task( if not vertex.will_stream:
log_vertex_build, background_tasks.add_task(
flow_id=flow_id, log_vertex_build,
vertex_id=vertex_id, flow_id=flow_id,
valid=valid, vertex_id=vertex_id,
params=params, valid=valid,
data=result_data_response, params=params,
artifacts=artifacts, data=result_data_response,
) artifacts=artifacts,
)
timedelta = time.perf_counter() - start_time timedelta = time.perf_counter() - start_time
duration = format_elapsed_time(timedelta) duration = format_elapsed_time(timedelta)
@ -226,39 +233,56 @@ async def build_vertex(
async def build_vertex_stream( async def build_vertex_stream(
flow_id: str, flow_id: str,
vertex_id: str, vertex_id: str,
session_id: Optional[str] = None,
chat_service: "ChatService" = Depends(get_chat_service), chat_service: "ChatService" = Depends(get_chat_service),
session_service: "SessionService" = Depends(get_session_service),
): ):
"""Build a vertex instead of the entire graph.""" """Build a vertex instead of the entire graph."""
try: try:
async def stream_vertex(): async def stream_vertex():
try: try:
cache = chat_service.get_cache(flow_id) if not session_id:
if not cache: cache = chat_service.get_cache(flow_id)
# If there's no cache if not cache:
raise ValueError(f"No cache found for {flow_id}.") # If there's no cache
raise ValueError(f"No cache found for {flow_id}.")
else:
graph = cache.get("result")
else: else:
graph = cache.get("result") session_data = await session_service.load_session(session_id)
graph, artifacts = session_data if session_data else (None, None)
if not graph:
raise ValueError(f"No graph found for {flow_id}.")
vertex: "ChatVertex" = graph.get_vertex(vertex_id) vertex: "ChatVertex" = graph.get_vertex(vertex_id)
if not hasattr(vertex, "stream"): if not hasattr(vertex, "stream"):
raise ValueError(f"Vertex {vertex_id} does not support streaming") raise ValueError(f"Vertex {vertex_id} does not support streaming")
if not vertex.pinned or not vertex._built: if isinstance(vertex._built_result, str) and vertex._built_result:
stream_data = StreamData(
event="message",
data={"message": f"Streaming vertex {vertex_id}"},
)
yield str(stream_data)
stream_data = StreamData(
event="message",
data={"chunk": vertex._built_result},
)
yield str(stream_data)
elif not vertex.pinned or not vertex._built:
logger.debug(f"Streaming vertex {vertex_id}") logger.debug(f"Streaming vertex {vertex_id}")
stream_data = StreamData( stream_data = StreamData(
event="message", event="message",
data={"message": f"Streaming vertex {vertex_id}"}, data={"message": f"Streaming vertex {vertex_id}"},
) )
yield str(stream_data) yield str(stream_data)
number_of_chunks = 0
async for chunk in vertex.stream(): async for chunk in vertex.stream():
stream_data = StreamData( stream_data = StreamData(
event="message", event="message",
data={"chunk": chunk}, data={"chunk": chunk},
) )
number_of_chunks += 1
yield str(stream_data) yield str(stream_data)
logger.debug(f"Number of chunks: {number_of_chunks}")
elif vertex.result is not None: elif vertex.result is not None:
stream_data = StreamData( stream_data = StreamData(
event="message", event="message",

View file

@ -228,6 +228,7 @@ async def run_flow_with_caching(
flow_id: str, flow_id: str,
inputs: Optional[Union[List[dict], dict]] = None, inputs: Optional[Union[List[dict], dict]] = None,
tweaks: Optional[dict] = None, tweaks: Optional[dict] = None,
stream: Annotated[bool, Body(embed=True)] = False, # noqa: F821
session_id: Annotated[Union[None, str], Body(embed=True)] = None, # noqa: F821 session_id: Annotated[Union[None, str], Body(embed=True)] = None, # noqa: F821
api_key_user: User = Depends(api_key_security), api_key_user: User = Depends(api_key_security),
session_service: SessionService = Depends(get_session_service), session_service: SessionService = Depends(get_session_service),
@ -239,13 +240,14 @@ async def run_flow_with_caching(
task_result: Any = None task_result: Any = None
if not graph: if not graph:
raise ValueError("Graph not found in the session") raise ValueError("Graph not found in the session")
task_result = await run_graph( task_result, session_id = await run_graph(
graph=graph, graph=graph,
flow_id=flow_id, flow_id=flow_id,
session_id=session_id, session_id=session_id,
inputs=inputs, inputs=inputs,
artifacts=artifacts, artifacts=artifacts,
session_service=session_service, session_service=session_service,
stream=stream,
) )
else: else:
@ -263,13 +265,14 @@ async def run_flow_with_caching(
raise ValueError(f"Flow {flow_id} has no data") raise ValueError(f"Flow {flow_id} has no data")
graph_data = flow.data graph_data = flow.data
graph_data = process_tweaks(graph_data, tweaks) graph_data = process_tweaks(graph_data, tweaks)
task_result = await run_graph( task_result, session_id = await run_graph(
graph=graph_data, graph=graph_data,
flow_id=flow_id, flow_id=flow_id,
session_id=session_id, session_id=session_id,
inputs=inputs, inputs=inputs,
artifacts={}, artifacts={},
session_service=session_service, session_service=session_service,
stream=stream,
) )
return RunResponse(outputs=task_result, session_id=session_id) return RunResponse(outputs=task_result, session_id=session_id)

View file

@ -261,3 +261,7 @@ class VertexBuildResponse(BaseModel):
class VerticesBuiltResponse(BaseModel): class VerticesBuiltResponse(BaseModel):
vertices: List[VertexBuildResponse] vertices: List[VertexBuildResponse]
class InputValueRequest(BaseModel):
input_value: str

View file

@ -16,7 +16,8 @@ from langflow.field_typing.range_spec import RangeSpec
class ConversationalAgent(CustomComponent): class ConversationalAgent(CustomComponent):
display_name: str = "OpenAI Conversational Agent" display_name: str = "OpenAI Conversational Agent"
description: str = "Conversational Agent that can use OpenAI's function calling API" description: str = "Conversational Agent that can use OpenAI's function calling API"
icon = "OpenAI"
def build_config(self): def build_config(self):
openai_function_models = [ openai_function_models = [
"gpt-4-turbo-preview", "gpt-4-turbo-preview",

View file

@ -20,7 +20,10 @@ class RetrievalQAComponent(CustomComponent):
"input_key": {"display_name": "Input Key", "advanced": True}, "input_key": {"display_name": "Input Key", "advanced": True},
"output_key": {"display_name": "Output Key", "advanced": True}, "output_key": {"display_name": "Output Key", "advanced": True},
"return_source_documents": {"display_name": "Return Source Documents"}, "return_source_documents": {"display_name": "Return Source Documents"},
"inputs": {"display_name": "Input", "input_types": ["Text", "Document"]}, "input_value": {
"display_name": "Input",
"input_types": ["Text", "Document"],
},
} }
def build( def build(

View file

@ -1,6 +1,6 @@
from concurrent import futures from concurrent import futures
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List from typing import Any, Dict, List, Optional
from langflow import CustomComponent from langflow import CustomComponent
from langflow.schema import Record from langflow.schema import Record
@ -12,21 +12,30 @@ class GatherRecordsComponent(CustomComponent):
def build_config(self) -> Dict[str, Any]: def build_config(self) -> Dict[str, Any]:
return { return {
"path": {"display_name": "Path"},
"types": {
"display_name": "Types",
"info": "File types to load. Leave empty to load all types.",
},
"depth": {"display_name": "Depth", "info": "Depth to search for files."},
"max_concurrency": {"display_name": "Max Concurrency", "advanced": True},
"load_hidden": { "load_hidden": {
"display_name": "Load Hidden Files", "display_name": "Load Hidden",
"value": False,
"advanced": True, "advanced": True,
"info": "If true, hidden files will be loaded.",
}, },
"max_concurrency": { "recursive": {
"display_name": "Max Concurrency", "display_name": "Recursive",
"value": 10,
"advanced": True, "advanced": True,
"info": "If true, the search will be recursive.",
},
"silent_errors": {
"display_name": "Silent Errors",
"advanced": True,
"info": "If true, errors will not raise an exception.",
}, },
"path": {"display_name": "Local Directory"},
"recursive": {"display_name": "Recursive", "value": True, "advanced": True},
"use_multithreading": { "use_multithreading": {
"display_name": "Use Multithreading", "display_name": "Use Multithreading",
"value": True,
"advanced": True, "advanced": True,
}, },
} }
@ -61,7 +70,9 @@ class GatherRecordsComponent(CustomComponent):
glob = "**/*" if recursive else "*" glob = "**/*" if recursive else "*"
paths = walk_level(path_obj, depth) if depth else path_obj.glob(glob) paths = walk_level(path_obj, depth) if depth else path_obj.glob(glob)
file_paths = [str(p) for p in paths if p.is_file() and match_types(p) and is_not_hidden(p)] file_paths = [
str(p) for p in paths if p.is_file() and match_types(p) and is_not_hidden(p)
]
return file_paths return file_paths
@ -91,13 +102,20 @@ class GatherRecordsComponent(CustomComponent):
use_multithreading: bool, use_multithreading: bool,
) -> List[Record]: ) -> List[Record]:
if use_multithreading: if use_multithreading:
records = self.parallel_load_records(file_paths, silent_errors, max_concurrency) records = self.parallel_load_records(
file_paths, silent_errors, max_concurrency
)
else: else:
records = [self.parse_file_to_record(file_path, silent_errors) for file_path in file_paths] records = [
self.parse_file_to_record(file_path, silent_errors)
for file_path in file_paths
]
records = list(filter(None, records)) records = list(filter(None, records))
return records return records
def parallel_load_records(self, file_paths: List[str], silent_errors: bool, max_concurrency: int) -> List[Record]: def parallel_load_records(
self, file_paths: List[str], silent_errors: bool, max_concurrency: int
) -> List[Record]:
with futures.ThreadPoolExecutor(max_workers=max_concurrency) as executor: with futures.ThreadPoolExecutor(max_workers=max_concurrency) as executor:
loaded_files = executor.map( loaded_files = executor.map(
lambda file_path: self.parse_file_to_record(file_path, silent_errors), lambda file_path: self.parse_file_to_record(file_path, silent_errors),
@ -108,7 +126,7 @@ class GatherRecordsComponent(CustomComponent):
def build( def build(
self, self,
path: str, path: str,
types: List[str] = None, types: Optional[List[str]] = None,
depth: int = 0, depth: int = 0,
max_concurrency: int = 2, max_concurrency: int = 2,
load_hidden: bool = False, load_hidden: bool = False,
@ -116,14 +134,23 @@ class GatherRecordsComponent(CustomComponent):
silent_errors: bool = False, silent_errors: bool = False,
use_multithreading: bool = True, use_multithreading: bool = True,
) -> List[Record]: ) -> List[Record]:
if types is None:
types = []
resolved_path = self.resolve_path(path) resolved_path = self.resolve_path(path)
file_paths = self.retrieve_file_paths(resolved_path, types, load_hidden, recursive, depth) file_paths = self.retrieve_file_paths(
resolved_path, types, load_hidden, recursive, depth
)
loaded_records = [] loaded_records = []
if use_multithreading: if use_multithreading:
loaded_records = self.parallel_load_records(file_paths, silent_errors, max_concurrency) loaded_records = self.parallel_load_records(
file_paths, silent_errors, max_concurrency
)
else: else:
loaded_records = [self.parse_file_to_record(file_path, silent_errors) for file_path in file_paths] loaded_records = [
self.parse_file_to_record(file_path, silent_errors)
for file_path in file_paths
]
loaded_records = list(filter(None, loaded_records)) loaded_records = list(filter(None, loaded_records))
self.status = loaded_records self.status = loaded_records
return loaded_records return loaded_records

View file

@ -9,6 +9,7 @@ class HuggingFaceEmbeddingsComponent(CustomComponent):
documentation = ( documentation = (
"https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/sentence_transformers" "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/sentence_transformers"
) )
icon="HuggingFace"
def build_config(self): def build_config(self):
return { return {

View file

@ -9,6 +9,8 @@ class HuggingFaceInferenceAPIEmbeddingsComponent(CustomComponent):
display_name = "HuggingFaceInferenceAPIEmbeddings" display_name = "HuggingFaceInferenceAPIEmbeddings"
description = "HuggingFace sentence_transformers embedding models, API version." description = "HuggingFace sentence_transformers embedding models, API version."
documentation = "https://github.com/huggingface/text-embeddings-inference" documentation = "https://github.com/huggingface/text-embeddings-inference"
icon="HuggingFace"
def build_config(self): def build_config(self):
return { return {

View file

@ -1,7 +1,7 @@
from typing import Optional, Union from typing import Optional, Union
from langflow.components.io.base.chat import ChatComponent
from langflow.field_typing import Text from langflow.field_typing import Text
from langflow.io.schema import ChatComponent
from langflow.schema import Record from langflow.schema import Record

View file

@ -1,7 +1,7 @@
from typing import Optional, Union from typing import Optional, Union
from langflow.components.io.base.chat import ChatComponent
from langflow.field_typing import Text from langflow.field_typing import Text
from langflow.io.schema import ChatComponent
from langflow.schema import Record from langflow.schema import Record

View file

@ -12,7 +12,7 @@ class MessageHistoryComponent(CustomComponent):
def build_config(self): def build_config(self):
return { return {
"sender": { "sender": {
"options": ["Machine", "User"], "options": ["Machine", "User", "Machine and User"],
"display_name": "Sender Type", "display_name": "Sender Type",
}, },
"sender_name": {"display_name": "Sender Name"}, "sender_name": {"display_name": "Sender Name"},
@ -38,6 +38,8 @@ class MessageHistoryComponent(CustomComponent):
session_id: Optional[str] = None, session_id: Optional[str] = None,
n_messages: int = 5, n_messages: int = 5,
) -> List[Record]: ) -> List[Record]:
if sender == "Machine and User":
sender = None
messages = get_messages( messages = get_messages(
sender=sender, sender=sender,
sender_name=sender_name, sender_name=sender_name,

View file

@ -9,11 +9,11 @@ class TextInput(CustomComponent):
description = "Used to pass text input to the next component." description = "Used to pass text input to the next component."
field_config = { field_config = {
"value": {"display_name": "Value", "multiline": True}, "input_value": {"display_name": "Value", "multiline": True},
} }
def build(self, value: Optional[str] = "") -> Text: def build(self, input_value: Optional[str] = "") -> Text:
self.status = value self.status = input_value
if not value: if not input_value:
value = "" input_value = ""
return value return input_value

View file

@ -10,6 +10,8 @@ from langflow import CustomComponent
class AmazonBedrockComponent(CustomComponent): class AmazonBedrockComponent(CustomComponent):
display_name: str = "Amazon Bedrock" display_name: str = "Amazon Bedrock"
description: str = "LLM model from Amazon Bedrock." description: str = "LLM model from Amazon Bedrock."
icon = "Amazon"
def build_config(self): def build_config(self):
return { return {

View file

@ -10,6 +10,7 @@ from langflow import CustomComponent
class AnthropicLLM(CustomComponent): class AnthropicLLM(CustomComponent):
display_name: str = "AnthropicLLM" display_name: str = "AnthropicLLM"
description: str = "Anthropic Chat&Completion large language models." description: str = "Anthropic Chat&Completion large language models."
icon ="Anthropic"
def build_config(self): def build_config(self):
return { return {

View file

@ -10,6 +10,8 @@ from langflow.field_typing import BaseLanguageModel, NestedDict
class AnthropicComponent(CustomComponent): class AnthropicComponent(CustomComponent):
display_name = "Anthropic" display_name = "Anthropic"
description = "Anthropic large language models." description = "Anthropic large language models."
icon ="Anthropic"
def build_config(self): def build_config(self):
return { return {

View file

@ -9,6 +9,7 @@ class ChatAnthropicComponent(CustomComponent):
display_name = "ChatAnthropic" display_name = "ChatAnthropic"
description = "`Anthropic` chat large language models." description = "`Anthropic` chat large language models."
documentation = "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/anthropic" documentation = "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/anthropic"
icon ="Anthropic"
def build_config(self): def build_config(self):
return { return {

View file

@ -9,6 +9,7 @@ from langflow.field_typing import BaseLanguageModel, NestedDict
class ChatOpenAIComponent(CustomComponent): class ChatOpenAIComponent(CustomComponent):
display_name = "ChatOpenAI" display_name = "ChatOpenAI"
description = "`OpenAI` Chat large language models API." description = "`OpenAI` Chat large language models API."
icon = "OpenAI"
def build_config(self): def build_config(self):
return { return {

View file

@ -10,6 +10,8 @@ from langflow.field_typing import BaseLanguageModel
class ChatVertexAIComponent(CustomComponent): class ChatVertexAIComponent(CustomComponent):
display_name = "ChatVertexAI" display_name = "ChatVertexAI"
description = "`Vertex AI` Chat large language models API." description = "`Vertex AI` Chat large language models API."
icon="VertexAI"
def build_config(self): def build_config(self):
return { return {

View file

@ -7,6 +7,7 @@ class CohereComponent(CustomComponent):
display_name = "Cohere" display_name = "Cohere"
description = "Cohere large language models." description = "Cohere large language models."
documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/cohere" documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/cohere"
icon = "Cohere"
def build_config(self): def build_config(self):
return { return {

View file

@ -10,6 +10,7 @@ class GoogleGenerativeAIComponent(CustomComponent):
display_name: str = "Google Generative AI" display_name: str = "Google Generative AI"
description: str = "A component that uses Google Generative AI to generate text." description: str = "A component that uses Google Generative AI to generate text."
documentation: str = "http://docs.langflow.org/components/custom" documentation: str = "http://docs.langflow.org/components/custom"
icon = "Google"
def build_config(self): def build_config(self):
return { return {

View file

@ -8,6 +8,8 @@ from langflow import CustomComponent
class HuggingFaceEndpointsComponent(CustomComponent): class HuggingFaceEndpointsComponent(CustomComponent):
display_name: str = "Hugging Face Inference API" display_name: str = "Hugging Face Inference API"
description: str = "LLM model from Hugging Face Inference API." description: str = "LLM model from Hugging Face Inference API."
icon="HuggingFace"
def build_config(self): def build_config(self):
return { return {

View file

@ -7,6 +7,7 @@ from langchain_community.llms.vertexai import VertexAI
class VertexAIComponent(CustomComponent): class VertexAIComponent(CustomComponent):
display_name = "VertexAI" display_name = "VertexAI"
description = "Google Vertex AI large language models" description = "Google Vertex AI large language models"
icon="VertexAI"
def build_config(self): def build_config(self):
return { return {

View file

@ -2,13 +2,14 @@ from typing import Optional
from langchain_community.chat_models.bedrock import BedrockChat from langchain_community.chat_models.bedrock import BedrockChat
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
from langflow.field_typing import Text from langflow.field_typing import Text
class AmazonBedrockComponent(CustomComponent): class AmazonBedrockComponent(LCModelComponent):
display_name: str = "Amazon Bedrock Model" display_name: str = "Amazon Bedrock Model"
description: str = "Generate text using LLM model from Amazon Bedrock." description: str = "Generate text using LLM model from Amazon Bedrock."
icon = "Amazon"
def build_config(self): def build_config(self):
return { return {
@ -34,7 +35,11 @@ class AmazonBedrockComponent(CustomComponent):
"model_kwargs": {"display_name": "Model Kwargs"}, "model_kwargs": {"display_name": "Model Kwargs"},
"cache": {"display_name": "Cache"}, "cache": {"display_name": "Cache"},
"code": {"advanced": True}, "code": {"advanced": True},
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -47,6 +52,7 @@ class AmazonBedrockComponent(CustomComponent):
endpoint_url: Optional[str] = None, endpoint_url: Optional[str] = None,
streaming: bool = False, streaming: bool = False,
cache: Optional[bool] = None, cache: Optional[bool] = None,
stream: bool = False,
) -> Text: ) -> Text:
try: try:
output = BedrockChat( output = BedrockChat(
@ -60,7 +66,5 @@ class AmazonBedrockComponent(CustomComponent):
) # type: ignore ) # type: ignore
except Exception as e: except Exception as e:
raise ValueError("Could not connect to AmazonBedrock API.") from e raise ValueError("Could not connect to AmazonBedrock API.") from e
message = output.invoke(input_value)
result = message.content if hasattr(message, "content") else message return self.get_result(output=output, stream=stream, input_value=input_value)
self.status = result
return result

View file

@ -3,15 +3,16 @@ from typing import Optional
from langchain_community.chat_models.anthropic import ChatAnthropic from langchain_community.chat_models.anthropic import ChatAnthropic
from pydantic.v1 import SecretStr from pydantic.v1 import SecretStr
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
from langflow.field_typing import Text from langflow.field_typing import Text
class AnthropicLLM(CustomComponent): class AnthropicLLM(LCModelComponent):
display_name: str = "AnthropicModel" display_name: str = "AnthropicModel"
description: str = ( description: str = (
"Generate text using Anthropic Chat&Completion large language models." "Generate text using Anthropic Chat&Completion large language models."
) )
icon = "Anthropic"
def build_config(self): def build_config(self):
return { return {
@ -49,7 +50,11 @@ class AnthropicLLM(CustomComponent):
"info": "Endpoint of the Anthropic API. Defaults to 'https://api.anthropic.com' if not specified.", "info": "Endpoint of the Anthropic API. Defaults to 'https://api.anthropic.com' if not specified.",
}, },
"code": {"show": False}, "code": {"show": False},
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -60,6 +65,7 @@ class AnthropicLLM(CustomComponent):
max_tokens: Optional[int] = None, max_tokens: Optional[int] = None,
temperature: Optional[float] = None, temperature: Optional[float] = None,
api_endpoint: Optional[str] = None, api_endpoint: Optional[str] = None,
stream: bool = False,
) -> Text: ) -> Text:
# Set default API endpoint if not provided # Set default API endpoint if not provided
if not api_endpoint: if not api_endpoint:
@ -77,7 +83,5 @@ class AnthropicLLM(CustomComponent):
) )
except Exception as e: except Exception as e:
raise ValueError("Could not connect to Anthropic API.") from e raise ValueError("Could not connect to Anthropic API.") from e
message = output.invoke(input_value)
result = message.content if hasattr(message, "content") else message return self.get_result(output=output, stream=stream, input_value=input_value)
self.status = result
return result

View file

@ -3,16 +3,17 @@ from typing import Optional
from langchain.llms.base import BaseLanguageModel from langchain.llms.base import BaseLanguageModel
from langchain_openai import AzureChatOpenAI from langchain_openai import AzureChatOpenAI
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
class AzureChatOpenAIComponent(CustomComponent): class AzureChatOpenAIComponent(LCModelComponent):
display_name: str = "AzureOpenAI Model" display_name: str = "AzureOpenAI Model"
description: str = "Generate text using LLM model from Azure OpenAI." description: str = "Generate text using LLM model from Azure OpenAI."
documentation: str = ( documentation: str = (
"https://python.langchain.com/docs/integrations/llms/azure_openai" "https://python.langchain.com/docs/integrations/llms/azure_openai"
) )
beta = False beta = False
icon = "Azure"
AZURE_OPENAI_MODELS = [ AZURE_OPENAI_MODELS = [
"gpt-35-turbo", "gpt-35-turbo",
@ -73,7 +74,11 @@ class AzureChatOpenAIComponent(CustomComponent):
"info": "Maximum number of tokens to generate.", "info": "Maximum number of tokens to generate.",
}, },
"code": {"show": False}, "code": {"show": False},
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -86,6 +91,7 @@ class AzureChatOpenAIComponent(CustomComponent):
api_version: str, api_version: str,
temperature: float = 0.7, temperature: float = 0.7,
max_tokens: Optional[int] = 1000, max_tokens: Optional[int] = 1000,
stream: bool = False,
) -> BaseLanguageModel: ) -> BaseLanguageModel:
try: try:
output = AzureChatOpenAI( output = AzureChatOpenAI(
@ -99,7 +105,5 @@ class AzureChatOpenAIComponent(CustomComponent):
) )
except Exception as e: except Exception as e:
raise ValueError("Could not connect to AzureOpenAI API.") from e raise ValueError("Could not connect to AzureOpenAI API.") from e
message = output.invoke(input_value)
result = message.content if hasattr(message, "content") else message return self.get_result(output=output, stream=stream, input_value=input_value)
self.status = result
return result

View file

@ -3,16 +3,17 @@ from typing import Optional
from langchain_community.chat_models.baidu_qianfan_endpoint import QianfanChatEndpoint from langchain_community.chat_models.baidu_qianfan_endpoint import QianfanChatEndpoint
from pydantic.v1 import SecretStr from pydantic.v1 import SecretStr
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
from langflow.field_typing import Text from langflow.field_typing import Text
class QianfanChatEndpointComponent(CustomComponent): class QianfanChatEndpointComponent(LCModelComponent):
display_name: str = "QianfanChat Model" display_name: str = "QianfanChat Model"
description: str = ( description: str = (
"Generate text using Baidu Qianfan chat models. Get more detail from " "Generate text using Baidu Qianfan chat models. Get more detail from "
"https://python.langchain.com/docs/integrations/chat/baidu_qianfan_endpoint." "https://python.langchain.com/docs/integrations/chat/baidu_qianfan_endpoint."
) )
icon = "BaiduQianfan"
def build_config(self): def build_config(self):
return { return {
@ -68,7 +69,11 @@ class QianfanChatEndpointComponent(CustomComponent):
"info": "Endpoint of the Qianfan LLM, required if custom model used.", "info": "Endpoint of the Qianfan LLM, required if custom model used.",
}, },
"code": {"show": False}, "code": {"show": False},
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -81,6 +86,7 @@ class QianfanChatEndpointComponent(CustomComponent):
temperature: Optional[float] = None, temperature: Optional[float] = None,
penalty_score: Optional[float] = None, penalty_score: Optional[float] = None,
endpoint: Optional[str] = None, endpoint: Optional[str] = None,
stream: bool = False,
) -> Text: ) -> Text:
try: try:
output = QianfanChatEndpoint( # type: ignore output = QianfanChatEndpoint( # type: ignore
@ -94,7 +100,5 @@ class QianfanChatEndpointComponent(CustomComponent):
) )
except Exception as e: except Exception as e:
raise ValueError("Could not connect to Baidu Qianfan API.") from e raise ValueError("Could not connect to Baidu Qianfan API.") from e
message = output.invoke(input_value)
result = message.content if hasattr(message, "content") else message return self.get_result(output=output, stream=stream, input_value=input_value)
self.status = result
return result

View file

@ -2,11 +2,11 @@ from typing import Dict, Optional
from langchain_community.llms.ctransformers import CTransformers from langchain_community.llms.ctransformers import CTransformers
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
from langflow.field_typing import Text from langflow.field_typing import Text
class CTransformersComponent(CustomComponent): class CTransformersComponent(LCModelComponent):
display_name = "CTransformersModel" display_name = "CTransformersModel"
description = "Generate text using CTransformers LLM models" description = "Generate text using CTransformers LLM models"
documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/ctransformers" documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/ctransformers"
@ -28,7 +28,11 @@ class CTransformersComponent(CustomComponent):
"field_type": "dict", "field_type": "dict",
"value": '{"top_k":40,"top_p":0.95,"temperature":0.8,"repetition_penalty":1.1,"last_n_tokens":64,"seed":-1,"max_new_tokens":256,"stop":"","stream":"False","reset":"True","batch_size":8,"threads":-1,"context_length":-1,"gpu_layers":0}', "value": '{"top_k":40,"top_p":0.95,"temperature":0.8,"repetition_penalty":1.1,"last_n_tokens":64,"seed":-1,"max_new_tokens":256,"stop":"","stream":"False","reset":"True","batch_size":8,"threads":-1,"context_length":-1,"gpu_layers":0}',
}, },
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -38,11 +42,10 @@ class CTransformersComponent(CustomComponent):
input_value: str, input_value: str,
model_type: str, model_type: str,
config: Optional[Dict] = None, config: Optional[Dict] = None,
stream: Optional[bool] = False,
) -> Text: ) -> Text:
output = CTransformers( output = CTransformers(
model=model, model_file=model_file, model_type=model_type, config=config model=model, model_file=model_file, model_type=model_type, config=config
) )
message = output.invoke(input_value)
result = message.content if hasattr(message, "content") else message return self.get_result(output=output, stream=stream, input_value=input_value)
self.status = result
return result

View file

@ -1,14 +1,16 @@
from langchain_community.chat_models.cohere import ChatCohere from langchain_community.chat_models.cohere import ChatCohere
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
from langflow.field_typing import Text from langflow.field_typing import Text
class CohereComponent(CustomComponent): class CohereComponent(LCModelComponent):
display_name = "CohereModel" display_name = "CohereModel"
description = "Generate text using Cohere large language models." description = "Generate text using Cohere large language models."
documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/cohere" documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/cohere"
icon = "Cohere"
def build_config(self): def build_config(self):
return { return {
"cohere_api_key": { "cohere_api_key": {
@ -28,7 +30,11 @@ class CohereComponent(CustomComponent):
"type": "float", "type": "float",
"show": True, "show": True,
}, },
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -37,14 +43,11 @@ class CohereComponent(CustomComponent):
input_value: str, input_value: str,
max_tokens: int = 256, max_tokens: int = 256,
temperature: float = 0.75, temperature: float = 0.75,
stream: bool = False,
) -> Text: ) -> Text:
output = ChatCohere( output = ChatCohere(
cohere_api_key=cohere_api_key, cohere_api_key=cohere_api_key,
max_tokens=max_tokens, max_tokens=max_tokens,
temperature=temperature, temperature=temperature,
) )
message = output.invoke(input_value) return self.get_result(output=output, stream=stream, input_value=input_value)
result = message.content if hasattr(message, "content") else message
self.status = result
return result
return result

View file

@ -1,16 +1,17 @@
from typing import Optional from typing import Optional
from langchain_google_genai import ChatGoogleGenerativeAI # type: ignore from langchain_google_genai import ChatGoogleGenerativeAI
from pydantic.v1.types import SecretStr from pydantic.v1 import SecretStr
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
from langflow.field_typing import RangeSpec, Text from langflow.field_typing import RangeSpec, Text
class GoogleGenerativeAIComponent(CustomComponent): class GoogleGenerativeAIComponent(LCModelComponent):
display_name: str = "Google Generative AIModel" display_name: str = "Google Generative AIModel"
description: str = "Generate text using Google Generative AI to generate text." description: str = "Generate text using Google Generative AI to generate text."
documentation: str = "http://docs.langflow.org/components/custom" icon = "GoogleGenerativeAI"
icon = "Google"
def build_config(self): def build_config(self):
return { return {
@ -50,7 +51,11 @@ class GoogleGenerativeAIComponent(CustomComponent):
"code": { "code": {
"advanced": True, "advanced": True,
}, },
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input", "info": "The input to the model."},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -63,6 +68,7 @@ class GoogleGenerativeAIComponent(CustomComponent):
top_k: Optional[int] = None, top_k: Optional[int] = None,
top_p: Optional[float] = None, top_p: Optional[float] = None,
n: Optional[int] = 1, n: Optional[int] = 1,
stream: bool = False,
) -> Text: ) -> Text:
output = ChatGoogleGenerativeAI( output = ChatGoogleGenerativeAI(
model=model, model=model,
@ -73,7 +79,4 @@ class GoogleGenerativeAIComponent(CustomComponent):
n=n or 1, n=n or 1,
google_api_key=SecretStr(google_api_key), google_api_key=SecretStr(google_api_key),
) )
message = output.invoke(input_value) return self.get_result(output=output, stream=stream, input_value=input_value)
result = message.content if hasattr(message, "content") else message
self.status = result
return result

View file

@ -3,13 +3,14 @@ from typing import Optional
from langchain_community.chat_models.huggingface import ChatHuggingFace from langchain_community.chat_models.huggingface import ChatHuggingFace
from langchain_community.llms.huggingface_endpoint import HuggingFaceEndpoint from langchain_community.llms.huggingface_endpoint import HuggingFaceEndpoint
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
from langflow.field_typing import Text from langflow.field_typing import Text
class HuggingFaceEndpointsComponent(CustomComponent): class HuggingFaceEndpointsComponent(LCModelComponent):
display_name: str = "Hugging Face Inference API models" display_name: str = "Hugging Face Inference API models"
description: str = "Generate text using LLM model from Hugging Face Inference API." description: str = "Generate text using LLM model from Hugging Face Inference API."
icon = "HuggingFace"
def build_config(self): def build_config(self):
return { return {
@ -24,7 +25,11 @@ class HuggingFaceEndpointsComponent(CustomComponent):
"field_type": "code", "field_type": "code",
}, },
"code": {"show": False}, "code": {"show": False},
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -34,6 +39,7 @@ class HuggingFaceEndpointsComponent(CustomComponent):
task: str = "text2text-generation", task: str = "text2text-generation",
huggingfacehub_api_token: Optional[str] = None, huggingfacehub_api_token: Optional[str] = None,
model_kwargs: Optional[dict] = None, model_kwargs: Optional[dict] = None,
stream: bool = False,
) -> Text: ) -> Text:
try: try:
llm = HuggingFaceEndpoint( llm = HuggingFaceEndpoint(
@ -45,7 +51,4 @@ class HuggingFaceEndpointsComponent(CustomComponent):
except Exception as e: except Exception as e:
raise ValueError("Could not connect to HuggingFace Endpoints API.") from e raise ValueError("Could not connect to HuggingFace Endpoints API.") from e
output = ChatHuggingFace(llm=llm) output = ChatHuggingFace(llm=llm)
message = output.invoke(input_value)alue) return self.get_result(output=output, stream=stream, input_value=input_value)
result = message.content if hasattr(message, "content") else message
self.status = result
return result

View file

@ -2,11 +2,11 @@ from typing import Any, Dict, List, Optional
from langchain_community.llms.llamacpp import LlamaCpp from langchain_community.llms.llamacpp import LlamaCpp
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
from langflow.field_typing import Text from langflow.field_typing import Text
class LlamaCppComponent(CustomComponent): class LlamaCppComponent(LCModelComponent):
display_name = "LlamaCppModel" display_name = "LlamaCppModel"
description = "Generate text using llama.cpp model." description = "Generate text using llama.cpp model."
documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/llamacpp" documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/llamacpp"
@ -56,7 +56,11 @@ class LlamaCppComponent(CustomComponent):
"use_mmap": {"display_name": "Use Mmap", "advanced": True}, "use_mmap": {"display_name": "Use Mmap", "advanced": True},
"verbose": {"display_name": "Verbose", "advanced": True}, "verbose": {"display_name": "Verbose", "advanced": True},
"vocab_only": {"display_name": "Vocab Only", "advanced": True}, "vocab_only": {"display_name": "Vocab Only", "advanced": True},
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -97,6 +101,7 @@ class LlamaCppComponent(CustomComponent):
use_mmap: Optional[bool] = True, use_mmap: Optional[bool] = True,
verbose: bool = True, verbose: bool = True,
vocab_only: bool = False, vocab_only: bool = False,
stream: bool = False,
) -> Text: ) -> Text:
output = LlamaCpp( output = LlamaCpp(
model_path=model_path, model_path=model_path,
@ -135,9 +140,5 @@ class LlamaCppComponent(CustomComponent):
verbose=verbose, verbose=verbose,
vocab_only=vocab_only, vocab_only=vocab_only,
) )
message = output.invoke(input_value)
result = message.content if hasattr(message, "content") else message return self.get_result(output=output, stream=stream, input_value=input_value)
self.status = result
return result
self.status = result
return result

View file

@ -3,17 +3,19 @@ from typing import Any, Dict, List, Optional
# from langchain_community.chat_models import ChatOllama # from langchain_community.chat_models import ChatOllama
from langchain_community.chat_models import ChatOllama from langchain_community.chat_models import ChatOllama
from langflow.components.models.base.model import LCModelComponent
# from langchain.chat_models import ChatOllama # from langchain.chat_models import ChatOllama
from langflow import CustomComponent
from langflow.field_typing import Text from langflow.field_typing import Text
# whe When a callback component is added to Langflow, the comment must be uncommented. # whe When a callback component is added to Langflow, the comment must be uncommented.
# from langchain.callbacks.manager import CallbackManager # from langchain.callbacks.manager import CallbackManager
class ChatOllamaComponent(CustomComponent): class ChatOllamaComponent(LCModelComponent):
display_name = "ChatOllamaModel" display_name = "ChatOllamaModel"
description = "Generate text using Local LLM for chat with Ollama." description = "Generate text using Local LLM for chat with Ollama."
icon = "Ollama"
def build_config(self) -> dict: def build_config(self) -> dict:
return { return {
@ -164,7 +166,11 @@ class ChatOllamaComponent(CustomComponent):
"info": "Template to use for generating text.", "info": "Template to use for generating text.",
"advanced": True, "advanced": True,
}, },
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -197,6 +203,7 @@ class ChatOllamaComponent(CustomComponent):
timeout: Optional[int] = None, timeout: Optional[int] = None,
top_k: Optional[int] = None, top_k: Optional[int] = None,
top_p: Optional[int] = None, top_p: Optional[int] = None,
stream: Optional[bool] = False,
) -> Text: ) -> Text:
if not base_url: if not base_url:
base_url = "http://localhost:11434" base_url = "http://localhost:11434"
@ -250,7 +257,5 @@ class ChatOllamaComponent(CustomComponent):
output = ChatOllama(**llm_params) # type: ignore output = ChatOllama(**llm_params) # type: ignore
except Exception as e: except Exception as e:
raise ValueError("Could not initialize Ollama LLM.") from e raise ValueError("Could not initialize Ollama LLM.") from e
message = output.invoke(input_value)
result = message.content if hasattr(message, "content") else message return self.get_result(output=output, stream=stream, input_value=input_value)
self.status = result
return result

View file

@ -2,17 +2,18 @@ from typing import Optional
from langchain_openai import ChatOpenAI from langchain_openai import ChatOpenAI
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
from langflow.field_typing import NestedDict, Text from langflow.field_typing import NestedDict, Text
class OpenAIModelComponent(CustomComponent): class OpenAIModelComponent(LCModelComponent):
display_name = "OpenAI Model" display_name = "OpenAI Model"
description = "Generates text using OpenAI's models." description = "Generates text using OpenAI's models."
icon = "OpenAI"
def build_config(self): def build_config(self):
return { return {
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"max_tokens": { "max_tokens": {
"display_name": "Max Tokens", "display_name": "Max Tokens",
"advanced": False, "advanced": False,
@ -57,6 +58,10 @@ class OpenAIModelComponent(CustomComponent):
"required": False, "required": False,
"value": 0.7, "value": 0.7,
}, },
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -68,10 +73,11 @@ class OpenAIModelComponent(CustomComponent):
openai_api_base: Optional[str] = None, openai_api_base: Optional[str] = None,
openai_api_key: Optional[str] = None, openai_api_key: Optional[str] = None,
temperature: float = 0.7, temperature: float = 0.7,
stream: Optional[bool] = False,
) -> Text: ) -> Text:
if not openai_api_base: if not openai_api_base:
openai_api_base = "https://api.openai.com/v1" openai_api_base = "https://api.openai.com/v1"
model = ChatOpenAI( output = ChatOpenAI(
max_tokens=max_tokens, max_tokens=max_tokens,
model_kwargs=model_kwargs, model_kwargs=model_kwargs,
model=model_name, model=model_name,
@ -80,7 +86,4 @@ class OpenAIModelComponent(CustomComponent):
temperature=temperature, temperature=temperature,
) )
message = model.invoke(input_value) return self.get_result(output=output, stream=stream, input_value=input_value)
result = message.content if hasattr(message, "content") else message
self.status = result
return result

View file

@ -2,13 +2,15 @@ from typing import List, Optional
from langchain_core.messages.base import BaseMessage from langchain_core.messages.base import BaseMessage
from langflow import CustomComponent from langflow.components.models.base.model import LCModelComponent
from langflow.field_typing import Text from langflow.field_typing import Text
class ChatVertexAIComponent(CustomComponent): class ChatVertexAIComponent(LCModelComponent):
display_name = "ChatVertexAIModel" display_name = "ChatVertexAIModel"
description = "Generate text using Vertex AI Chat large language models API." description = "Generate text using Vertex AI Chat large language models API."
icon="VertexAI"
def build_config(self): def build_config(self):
return { return {
@ -57,7 +59,11 @@ class ChatVertexAIComponent(CustomComponent):
"value": False, "value": False,
"advanced": True, "advanced": True,
}, },
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
} }
def build( def build(
@ -73,6 +79,7 @@ class ChatVertexAIComponent(CustomComponent):
top_k: int = 40, top_k: int = 40,
top_p: float = 0.95, top_p: float = 0.95,
verbose: bool = False, verbose: bool = False,
stream: bool = False,
) -> Text: ) -> Text:
try: try:
from langchain_google_vertexai import ChatVertexAI from langchain_google_vertexai import ChatVertexAI
@ -92,7 +99,5 @@ class ChatVertexAIComponent(CustomComponent):
top_p=top_p, top_p=top_p,
verbose=verbose, verbose=verbose,
) )
message = output.invoke(input_value)
result = message.content if hasattr(message, "content") else message return self.get_result(output=output, stream=stream, input_value=input_value)
self.status = result
return result

View file

@ -0,0 +1,28 @@
from langchain_core.runnables import Runnable
from langflow import CustomComponent
class LCModelComponent(CustomComponent):
display_name: str = "Model Name"
description: str = "Model Description"
def get_result(self, output: Runnable, stream: bool, input_value: str):
"""
Retrieves the result from the output of a Runnable object.
Args:
output (Runnable): The output object to retrieve the result from.
stream (bool): Indicates whether to use streaming or invocation mode.
input_value (str): The input value to pass to the output object.
Returns:
The result obtained from the output object.
"""
if stream:
result = output.stream(input_value)
else:
message = output.invoke(input_value)
result = message.content if hasattr(message, "content") else message
self.status = result
return result

View file

@ -9,6 +9,7 @@ from langflow import CustomComponent
class AmazonKendraRetrieverComponent(CustomComponent): class AmazonKendraRetrieverComponent(CustomComponent):
display_name: str = "Amazon Kendra Retriever" display_name: str = "Amazon Kendra Retriever"
description: str = "Retriever that uses the Amazon Kendra API." description: str = "Retriever that uses the Amazon Kendra API."
icon = "Amazon"
def build_config(self): def build_config(self):
return { return {

View file

@ -17,6 +17,8 @@ class VectaraSelfQueryRetriverComponent(CustomComponent):
description: str = "Implementation of Vectara Self Query Retriever" description: str = "Implementation of Vectara Self Query Retriever"
documentation = "https://python.langchain.com/docs/integrations/retrievers/self_query/vectara_self_query" documentation = "https://python.langchain.com/docs/integrations/retrievers/self_query/vectara_self_query"
beta = True beta = True
icon="Vectara"
field_config = { field_config = {
"code": {"show": True}, "code": {"show": True},

View file

@ -15,7 +15,7 @@ class RunnableExecComponent(CustomComponent):
"display_name": "Input Key", "display_name": "Input Key",
"info": "The key to use for the input.", "info": "The key to use for the input.",
}, },
"inputs": { "input_value": {
"display_name": "Inputs", "display_name": "Inputs",
"info": "The inputs to pass to the runnable.", "info": "The inputs to pass to the runnable.",
}, },

View file

@ -3,12 +3,12 @@ from typing import List, Optional
import chromadb # type: ignore import chromadb # type: ignore
from langchain_community.vectorstores.chroma import Chroma from langchain_community.vectorstores.chroma import Chroma
from langflow import CustomComponent from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.field_typing import Embeddings, Text from langflow.field_typing import Embeddings, Text
from langflow.schema import Record, docs_to_records from langflow.schema import Record
class ChromaSearchComponent(CustomComponent): class ChromaSearchComponent(LCVectorStoreComponent):
""" """
A custom component for implementing a Vector Store using Chroma. A custom component for implementing a Vector Store using Chroma.
""" """
@ -26,7 +26,7 @@ class ChromaSearchComponent(CustomComponent):
- dict: A dictionary containing the configuration options for the component. - dict: A dictionary containing the configuration options for the component.
""" """
return { return {
"inputs": {"display_name": "Input"}, "input_value": {"display_name": "Input"},
"search_type": { "search_type": {
"display_name": "Search Type", "display_name": "Search Type",
"options": ["Similarity", "MMR"], "options": ["Similarity", "MMR"],
@ -101,17 +101,11 @@ class ChromaSearchComponent(CustomComponent):
chroma_server_ssl_enabled=chroma_server_ssl_enabled, chroma_server_ssl_enabled=chroma_server_ssl_enabled,
) )
index_directory = self.resolve_path(index_directory) index_directory = self.resolve_path(index_directory)
chroma = Chroma( vector_store = Chroma(
embedding_function=embedding, embedding_function=embedding,
collection_name=collection_name, collection_name=collection_name,
persist_directory=index_directory, persist_directory=index_directory,
client_settings=chroma_settings, client_settings=chroma_settings,
) )
# Validate the inputs return self.search_with_vector_store(input_value, search_type, vector_store)
docs = []
if inputs and isinstance(inputs, str):
docs = chroma.search(query=inputs, search_type=search_type.lower())
else:
raise ValueError("Invalid inputs provided.")
return docs_to_records(docs)

View file

@ -3,24 +3,36 @@ from typing import List, Union
from langchain.schema import BaseRetriever from langchain.schema import BaseRetriever
from langchain_community.vectorstores import VectorStore from langchain_community.vectorstores import VectorStore
from langchain_community.vectorstores.faiss import FAISS from langchain_community.vectorstores.faiss import FAISS
from langflow import CustomComponent from langflow import CustomComponent
from langflow.field_typing import Document, Embeddings from langflow.field_typing import Document, Embeddings
class FAISSComponent(CustomComponent): class FAISSComponent(CustomComponent):
display_name = "FAISS" display_name = "FAISS"
description = "Construct FAISS wrapper from raw documents." description = "Ingest documents into FAISS Vector Store."
documentation = "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/faiss" documentation = "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/faiss"
def build_config(self): def build_config(self):
return { return {
"documents": {"display_name": "Documents"}, "documents": {"display_name": "Documents"},
"embedding": {"display_name": "Embedding"}, "embedding": {"display_name": "Embedding"},
"folder_path": {
"display_name": "Folder Path",
"info": "Path to save the FAISS index. It will be relative to where Langflow is running.",
},
"index_name": {"display_name": "Index Name"},
} }
def build( def build(
self, self,
embedding: Embeddings, embedding: Embeddings,
documents: List[Document], documents: List[Document],
folder_path: str,
index_name: str = "langflow_index",
) -> Union[VectorStore, FAISS, BaseRetriever]: ) -> Union[VectorStore, FAISS, BaseRetriever]:
return FAISS.from_documents(documents=documents, embedding=embedding) vector_store = FAISS.from_documents(documents=documents, embedding=embedding)
if not folder_path:
raise ValueError("Folder path is required to save the FAISS index.")
path = self.resolve_path(folder_path)
vector_store.save_local(str(path), index_name)

View file

@ -0,0 +1,45 @@
from typing import List
from langchain_community.vectorstores.faiss import FAISS
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.field_typing import Embeddings
from langflow.schema import Record
class FAISSSearchComponent(LCVectorStoreComponent):
display_name = "FAISS Search"
description = "Search a FAISS Vector Store for similar documents."
documentation = "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/faiss"
def build_config(self):
return {
"documents": {"display_name": "Documents"},
"embedding": {"display_name": "Embedding"},
"folder_path": {
"display_name": "Folder Path",
"info": "Path to save the FAISS index. It will be relative to where Langflow is running.",
},
"input_value": {"display_name": "Input"},
"index_name": {"display_name": "Index Name"},
}
def build(
self,
input_value: str,
embedding: Embeddings,
folder_path: str,
index_name: str = "langflow_index",
) -> List[Record]:
if not folder_path:
raise ValueError("Folder path is required to save the FAISS index.")
path = self.resolve_path(folder_path)
vector_store = FAISS.load_local(
folder_path=str(path), embeddings=embedding, index_name=index_name
)
if not vector_store:
raise ValueError("Failed to load the FAISS index.")
return self.search_with_vector_store(
vector_store=vector_store, input_value=input_value, search_type="similarity"
)

View file

@ -0,0 +1,57 @@
from typing import List, Optional
from langchain_community.vectorstores.mongodb_atlas import MongoDBAtlasVectorSearch
from langflow import CustomComponent
from langflow.field_typing import Document, Embeddings, NestedDict
class MongoDBAtlasComponent(CustomComponent):
display_name = "MongoDB Atlas"
description = (
"Construct a `MongoDB Atlas Vector Search` vector store from raw documents."
)
icon="MongoDB"
def build_config(self):
return {
"documents": {"display_name": "Documents"},
"embedding": {"display_name": "Embedding"},
"collection_name": {"display_name": "Collection Name"},
"db_name": {"display_name": "Database Name"},
"index_name": {"display_name": "Index Name"},
"mongodb_atlas_cluster_uri": {"display_name": "MongoDB Atlas Cluster URI"},
"search_kwargs": {"display_name": "Search Kwargs", "advanced": True},
}
def build(
self,
embedding: Embeddings,
documents: List[Document] = None,
collection_name: str = "",
db_name: str = "",
index_name: str = "",
mongodb_atlas_cluster_uri: str = "",
search_kwargs: Optional[NestedDict] = None,
) -> MongoDBAtlasVectorSearch:
search_kwargs = search_kwargs or {}
if documents:
vector_store = MongoDBAtlasVectorSearch.from_documents(
documents=documents,
embedding=embedding,
collection_name=collection_name,
db_name=db_name,
index_name=index_name,
mongodb_atlas_cluster_uri=mongodb_atlas_cluster_uri,
search_kwargs=search_kwargs,
)
else:
vector_store = MongoDBAtlasVectorSearch(
embedding=embedding,
collection_name=collection_name,
db_name=db_name,
index_name=index_name,
mongodb_atlas_cluster_uri=mongodb_atlas_cluster_uri,
search_kwargs=search_kwargs,
)
return vector_store

View file

@ -1,22 +1,22 @@
from typing import List, Optional from typing import List, Optional
from langchain_community.vectorstores import MongoDBAtlasVectorSearch from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.components.vectorstores.MongoDBAtlasVector import MongoDBAtlasComponent
from langflow import CustomComponent from langflow.field_typing import Embeddings, NestedDict
from langflow.field_typing import ( from langflow.schema import Record
Document,
Embeddings,
NestedDict,
)
class MongoDBAtlasComponent(CustomComponent): class MongoDBAtlasSearchComponent(MongoDBAtlasComponent, LCVectorStoreComponent):
display_name = "MongoDB Atlas" display_name = "MongoDB Atlas Search"
description = "Construct a `MongoDB Atlas Vector Search` vector store from raw documents." description = "Search a MongoDB Atlas Vector Store for similar documents."
def build_config(self): def build_config(self):
return { return {
"documents": {"display_name": "Documents"}, "search_type": {
"display_name": "Search Type",
"options": ["Similarity", "MMR"],
},
"input_value": {"display_name": "Input"},
"embedding": {"display_name": "Embedding"}, "embedding": {"display_name": "Embedding"},
"collection_name": {"display_name": "Collection Name"}, "collection_name": {"display_name": "Collection Name"},
"db_name": {"display_name": "Database Name"}, "db_name": {"display_name": "Database Name"},
@ -27,17 +27,16 @@ class MongoDBAtlasComponent(CustomComponent):
def build( def build(
self, self,
documents: List[Document], input_value: str,
search_type: str,
embedding: Embeddings, embedding: Embeddings,
collection_name: str = "", collection_name: str = "",
db_name: str = "", db_name: str = "",
index_name: str = "", index_name: str = "",
mongodb_atlas_cluster_uri: str = "", mongodb_atlas_cluster_uri: str = "",
search_kwargs: Optional[NestedDict] = None, search_kwargs: Optional[NestedDict] = None,
) -> MongoDBAtlasVectorSearch: ) -> List[Record]:
search_kwargs = search_kwargs or {} vector_store = super().build(
return MongoDBAtlasVectorSearch(
documents=documents,
embedding=embedding, embedding=embedding,
collection_name=collection_name, collection_name=collection_name,
db_name=db_name, db_name=db_name,
@ -45,3 +44,8 @@ class MongoDBAtlasComponent(CustomComponent):
mongodb_atlas_cluster_uri=mongodb_atlas_cluster_uri, mongodb_atlas_cluster_uri=mongodb_atlas_cluster_uri,
search_kwargs=search_kwargs, search_kwargs=search_kwargs,
) )
if not vector_store:
raise ValueError("Failed to create MongoDB Atlas Vector Store")
return self.search_with_vector_store(
vector_store=vector_store, input_value=input_value, search_type=search_type
)

View file

@ -5,6 +5,7 @@ import pinecone # type: ignore
from langchain.schema import BaseRetriever from langchain.schema import BaseRetriever
from langchain_community.vectorstores import VectorStore from langchain_community.vectorstores import VectorStore
from langchain_community.vectorstores.pinecone import Pinecone from langchain_community.vectorstores.pinecone import Pinecone
from langflow import CustomComponent from langflow import CustomComponent
from langflow.field_typing import Document, Embeddings from langflow.field_typing import Document, Embeddings
@ -12,6 +13,7 @@ from langflow.field_typing import Document, Embeddings
class PineconeComponent(CustomComponent): class PineconeComponent(CustomComponent):
display_name = "Pinecone" display_name = "Pinecone"
description = "Construct Pinecone wrapper from raw documents." description = "Construct Pinecone wrapper from raw documents."
icon = "Pinecone"
def build_config(self): def build_config(self):
return { return {
@ -19,10 +21,23 @@ class PineconeComponent(CustomComponent):
"embedding": {"display_name": "Embedding"}, "embedding": {"display_name": "Embedding"},
"index_name": {"display_name": "Index Name"}, "index_name": {"display_name": "Index Name"},
"namespace": {"display_name": "Namespace"}, "namespace": {"display_name": "Namespace"},
"pinecone_api_key": {"display_name": "Pinecone API Key", "default": "", "password": True, "required": True}, "pinecone_api_key": {
"pinecone_env": {"display_name": "Pinecone Environment", "default": "", "required": True}, "display_name": "Pinecone API Key",
"default": "",
"password": True,
"required": True,
},
"pinecone_env": {
"display_name": "Pinecone Environment",
"default": "",
"required": True,
},
"search_kwargs": {"display_name": "Search Kwargs", "default": "{}"}, "search_kwargs": {"display_name": "Search Kwargs", "default": "{}"},
"pool_threads": {"display_name": "Pool Threads", "default": 1, "advanced": True}, "pool_threads": {
"display_name": "Pool Threads",
"default": 1,
"advanced": True,
},
} }
def build( def build(

View file

@ -0,0 +1,70 @@
from typing import List, Optional
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.components.vectorstores.Pinecone import PineconeComponent
from langflow.field_typing import Embeddings
from langflow.schema import Record
class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
display_name = "Pinecone Search"
description = "Search a Pinecone Vector Store for similar documents."
icon = "Pinecone"
def build_config(self):
return {
"search_type": {
"display_name": "Search Type",
"options": ["Similarity", "MMR"],
},
"input_value": {"display_name": "Input"},
"embedding": {"display_name": "Embedding"},
"index_name": {"display_name": "Index Name"},
"namespace": {"display_name": "Namespace"},
"pinecone_api_key": {
"display_name": "Pinecone API Key",
"default": "",
"password": True,
"required": True,
},
"pinecone_env": {
"display_name": "Pinecone Environment",
"default": "",
"required": True,
},
"search_kwargs": {"display_name": "Search Kwargs", "default": "{}"},
"pool_threads": {
"display_name": "Pool Threads",
"default": 1,
"advanced": True,
},
}
def build(
self,
input_value: str,
embedding: Embeddings,
pinecone_env: str,
text_key: str = "text",
pool_threads: int = 4,
index_name: Optional[str] = None,
pinecone_api_key: Optional[str] = None,
namespace: Optional[str] = "default",
search_type: str = "similarity",
) -> List[Record]:
vector_store = super().build(
embedding=embedding,
pinecone_env=pinecone_env,
documents=[],
text_key=text_key,
pool_threads=pool_threads,
index_name=index_name,
pinecone_api_key=pinecone_api_key,
namespace=namespace,
)
if not vector_store:
raise ValueError("Failed to load the Pinecone index.")
return self.search_with_vector_store(
vector_store=vector_store, input_value=input_value, search_type=search_type
)

View file

@ -10,6 +10,7 @@ from langflow.field_typing import Document, Embeddings, NestedDict
class QdrantComponent(CustomComponent): class QdrantComponent(CustomComponent):
display_name = "Qdrant" display_name = "Qdrant"
description = "Construct Qdrant wrapper from a list of texts." description = "Construct Qdrant wrapper from a list of texts."
icon="Qdrant"
def build_config(self): def build_config(self):
return { return {

View file

@ -0,0 +1,93 @@
from typing import List, Optional
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.components.vectorstores.Qdrant import QdrantComponent
from langflow.field_typing import Embeddings, NestedDict
from langflow.schema import Record
class QdrantSearchComponent(QdrantComponent, LCVectorStoreComponent):
display_name = "Qdrant"
description = "Construct Qdrant wrapper from a list of texts."
icon="Qdrant"
def build_config(self):
return {
"search_type": {
"display_name": "Search Type",
"options": ["Similarity", "MMR"],
},
"input_value": {"display_name": "Input"},
"embedding": {"display_name": "Embedding"},
"api_key": {"display_name": "API Key", "password": True, "advanced": True},
"collection_name": {"display_name": "Collection Name"},
"content_payload_key": {
"display_name": "Content Payload Key",
"advanced": True,
},
"distance_func": {"display_name": "Distance Function", "advanced": True},
"grpc_port": {"display_name": "gRPC Port", "advanced": True},
"host": {"display_name": "Host", "advanced": True},
"https": {"display_name": "HTTPS", "advanced": True},
"location": {"display_name": "Location", "advanced": True},
"metadata_payload_key": {
"display_name": "Metadata Payload Key",
"advanced": True,
},
"path": {"display_name": "Path", "advanced": True},
"port": {"display_name": "Port", "advanced": True},
"prefer_grpc": {"display_name": "Prefer gRPC", "advanced": True},
"prefix": {"display_name": "Prefix", "advanced": True},
"search_kwargs": {"display_name": "Search Kwargs", "advanced": True},
"timeout": {"display_name": "Timeout", "advanced": True},
"url": {"display_name": "URL", "advanced": True},
}
def build(
self,
input_value: str,
embedding: Embeddings,
collection_name: str,
search_type: str = "similarity",
api_key: Optional[str] = None,
content_payload_key: str = "page_content",
distance_func: str = "Cosine",
grpc_port: int = 6334,
https: bool = False,
host: Optional[str] = None,
location: Optional[str] = None,
metadata_payload_key: str = "metadata",
path: Optional[str] = None,
port: Optional[int] = 6333,
prefer_grpc: bool = False,
prefix: Optional[str] = None,
search_kwargs: Optional[NestedDict] = None,
timeout: Optional[int] = None,
url: Optional[str] = None,
) -> List[Record]:
vector_store = super().build(
embedding=embedding,
collection_name=collection_name,
api_key=api_key,
content_payload_key=content_payload_key,
distance_func=distance_func,
grpc_port=grpc_port,
https=https,
host=host,
location=location,
metadata_payload_key=metadata_payload_key,
path=path,
port=port,
prefer_grpc=prefer_grpc,
prefix=prefix,
search_kwargs=search_kwargs,
timeout=timeout,
url=url,
)
if not vector_store:
raise ValueError("Failed to load the Qdrant index.")
return self.search_with_vector_store(
vector_store=vector_store, input_value=input_value, search_type=search_type
)

View file

@ -0,0 +1,77 @@
from typing import List, Optional
from langchain.embeddings.base import Embeddings
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.components.vectorstores.Redis import RedisComponent
from langflow.schema import Record
class RedisSearchComponent(RedisComponent, LCVectorStoreComponent):
"""
A custom component for implementing a Vector Store using Redis.
"""
display_name: str = "Redis Search"
description: str = "Search a Redis Vector Store for similar documents."
documentation = "https://python.langchain.com/docs/integrations/vectorstores/redis"
beta = True
def build_config(self):
"""
Builds the configuration for the component.
Returns:
- dict: A dictionary containing the configuration options for the component.
"""
return {
"search_type": {
"display_name": "Search Type",
"options": ["Similarity", "MMR"],
},
"input_value": {"display_name": "Input"},
"index_name": {"display_name": "Index Name", "value": "your_index"},
"code": {"show": False, "display_name": "Code"},
"documents": {"display_name": "Documents", "is_list": True},
"embedding": {"display_name": "Embedding"},
"schema": {"display_name": "Schema", "file_types": [".yaml"]},
"redis_server_url": {
"display_name": "Redis Server Connection String",
"advanced": False,
},
"redis_index_name": {"display_name": "Redis Index", "advanced": False},
}
def build(
self,
input_value: str,
search_type: str,
embedding: Embeddings,
redis_server_url: str,
redis_index_name: str,
schema: Optional[str] = None,
) -> List[Record]:
"""
Builds the Vector Store or BaseRetriever object.
Args:
- embedding (Embeddings): The embeddings to use for the Vector Store.
- documents (Optional[Document]): The documents to use for the Vector Store.
- redis_index_name (str): The name of the Redis index.
- redis_server_url (str): The URL for the Redis server.
Returns:
- VectorStore: The Vector Store object.
"""
vector_store = super().build(
embedding=embedding,
redis_server_url=redis_server_url,
redis_index_name=redis_index_name,
schema=schema,
)
if not vector_store:
raise ValueError("Failed to load the Redis index.")
return self.search_with_vector_store(
input_value=input_value, search_type=search_type, vector_store=vector_store
)

View file

@ -0,0 +1,50 @@
from typing import List
from langchain_community.vectorstores.supabase import SupabaseVectorStore
from supabase.client import Client, create_client
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.field_typing import Embeddings
from langflow.schema import Record
class SupabaseSearchComponent(LCVectorStoreComponent):
display_name = "Supabase Search"
description = "Search a Supabase Vector Store for similar documents."
icon="Supabase"
def build_config(self):
return {
"search_type": {
"display_name": "Search Type",
"options": ["Similarity", "MMR"],
},
"input_value": {"display_name": "Input"},
"embedding": {"display_name": "Embedding"},
"query_name": {"display_name": "Query Name"},
"search_kwargs": {"display_name": "Search Kwargs", "advanced": True},
"supabase_service_key": {"display_name": "Supabase Service Key"},
"supabase_url": {"display_name": "Supabase URL"},
"table_name": {"display_name": "Table Name", "advanced": True},
}
def build(
self,
input_value: str,
search_type: str,
embedding: Embeddings,
query_name: str = "",
supabase_service_key: str = "",
supabase_url: str = "",
table_name: str = "",
) -> List[Record]:
supabase: Client = create_client(
supabase_url, supabase_key=supabase_service_key
)
vector_store = SupabaseVectorStore(
client=supabase,
embedding=embedding,
table_name=table_name,
query_name=query_name,
)
return self.search_with_vector_store(input_value, search_type, vector_store)

View file

@ -8,13 +8,17 @@ from langchain_community.vectorstores.vectara import Vectara
from langchain_core.vectorstores import VectorStore from langchain_core.vectorstores import VectorStore
from langflow import CustomComponent from langflow import CustomComponent
from langflow.field_typing import BaseRetriever, Document from langflow.field_typing import BaseRetriever, Document
from langchain_community.vectorstores.vectara import Vectara
class VectaraComponent(CustomComponent): class VectaraComponent(CustomComponent):
display_name: str = "Vectara" display_name: str = "Vectara"
description: str = "Implementation of Vector Store using Vectara" description: str = "Implementation of Vector Store using Vectara"
documentation = "https://python.langchain.com/docs/integrations/vectorstores/vectara" documentation = (
"https://python.langchain.com/docs/integrations/vectorstores/vectara"
)
beta = True beta = True
icon="Vectara"
field_config = { field_config = {
"vectara_customer_id": { "vectara_customer_id": {
"display_name": "Vectara Customer ID", "display_name": "Vectara Customer ID",
@ -26,7 +30,10 @@ class VectaraComponent(CustomComponent):
"display_name": "Vectara API Key", "display_name": "Vectara API Key",
"password": True, "password": True,
}, },
"documents": {"display_name": "Documents", "info": "If provided, will be upserted to corpus (optional)"}, "documents": {
"display_name": "Documents",
"info": "If provided, will be upserted to corpus (optional)",
},
"files_url": { "files_url": {
"display_name": "Files Url", "display_name": "Files Url",
"info": "Make vectara object using url of files (optional)", "info": "Make vectara object using url of files (optional)",

View file

@ -0,0 +1,66 @@
from typing import List
from langchain_community.vectorstores.vectara import Vectara
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.components.vectorstores.Vectara import VectaraComponent
from langflow.schema import Record
class VectaraSearchComponent(VectaraComponent, LCVectorStoreComponent):
display_name: str = "Vectara Search"
description: str = "Search a Vectara Vector Store for similar documents."
documentation = (
"https://python.langchain.com/docs/integrations/vectorstores/vectara"
)
beta = True
icon="Vectara"
field_config = {
"search_type": {
"display_name": "Search Type",
"options": ["Similarity", "MMR"],
},
"input_value": {"display_name": "Input"},
"vectara_customer_id": {
"display_name": "Vectara Customer ID",
},
"vectara_corpus_id": {
"display_name": "Vectara Corpus ID",
},
"vectara_api_key": {
"display_name": "Vectara API Key",
"password": True,
},
"documents": {
"display_name": "Documents",
"info": "If provided, will be upserted to corpus (optional)",
},
"files_url": {
"display_name": "Files Url",
"info": "Make vectara object using url of files (optional)",
},
}
def build(
self,
input_value: str,
search_type: str,
vectara_customer_id: str,
vectara_corpus_id: str,
vectara_api_key: str,
) -> List[Record]:
source = "Langflow"
vector_store = Vectara(
vectara_customer_id=vectara_customer_id,
vectara_corpus_id=vectara_corpus_id,
vectara_api_key=vectara_api_key,
source=source,
)
if not vector_store:
raise ValueError("Failed to create Vectara Vector Store")
return self.search_with_vector_store(
vector_store=vector_store, input_value=input_value, search_type=search_type
)

View file

@ -8,10 +8,12 @@ from langchain_community.vectorstores import VectorStore, Weaviate
from langflow import CustomComponent from langflow import CustomComponent
class WeaviateVectorStore(CustomComponent): class WeaviateVectorStoreComponent(CustomComponent):
display_name: str = "Weaviate" display_name: str = "Weaviate"
description: str = "Implementation of Vector Store using Weaviate" description: str = "Implementation of Vector Store using Weaviate"
documentation = "https://python.langchain.com/docs/integrations/vectorstores/weaviate" documentation = (
"https://python.langchain.com/docs/integrations/vectorstores/weaviate"
)
beta = True beta = True
field_config = { field_config = {
"url": {"display_name": "Weaviate URL", "value": "http://localhost:8080"}, "url": {"display_name": "Weaviate URL", "value": "http://localhost:8080"},
@ -24,7 +26,12 @@ class WeaviateVectorStore(CustomComponent):
"display_name": "Index name", "display_name": "Index name",
"required": False, "required": False,
}, },
"text_key": {"display_name": "Text Key", "required": False, "advanced": True, "value": "text"}, "text_key": {
"display_name": "Text Key",
"required": False,
"advanced": True,
"value": "text",
},
"documents": {"display_name": "Documents", "is_list": True}, "documents": {"display_name": "Documents", "is_list": True},
"embedding": {"display_name": "Embedding"}, "embedding": {"display_name": "Embedding"},
"attributes": { "attributes": {
@ -34,7 +41,11 @@ class WeaviateVectorStore(CustomComponent):
"field_type": "str", "field_type": "str",
"advanced": True, "advanced": True,
}, },
"search_by_text": {"display_name": "Search By Text", "field_type": "bool", "advanced": True}, "search_by_text": {
"display_name": "Search By Text",
"field_type": "bool",
"advanced": True,
},
"code": {"show": False}, "code": {"show": False},
} }

View file

@ -0,0 +1,84 @@
from typing import List, Optional
from langchain.embeddings.base import Embeddings
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.components.vectorstores.Weaviate import WeaviateVectorStoreComponent
from langflow.schema import Record
class WeaviateSearchVectorStore(WeaviateVectorStoreComponent, LCVectorStoreComponent):
display_name: str = "Weaviate Search"
description: str = "Search a Weaviate Vector Store for similar documents."
documentation = (
"https://python.langchain.com/docs/integrations/vectorstores/weaviate"
)
beta = True
icon="Weaviate"
field_config = {
"search_type": {
"display_name": "Search Type",
"options": ["Similarity", "MMR"],
},
"input_value": {"display_name": "Input"},
"url": {"display_name": "Weaviate URL", "value": "http://localhost:8080"},
"api_key": {
"display_name": "API Key",
"password": True,
"required": False,
},
"index_name": {
"display_name": "Index name",
"required": False,
},
"text_key": {
"display_name": "Text Key",
"required": False,
"advanced": True,
"value": "text",
},
"documents": {"display_name": "Documents", "is_list": True},
"embedding": {"display_name": "Embedding"},
"attributes": {
"display_name": "Attributes",
"required": False,
"is_list": True,
"field_type": "str",
"advanced": True,
},
"search_by_text": {
"display_name": "Search By Text",
"field_type": "bool",
"advanced": True,
},
"code": {"show": False},
}
def build(
self,
input_value: str,
search_type: str,
url: str,
search_by_text: bool = False,
api_key: Optional[str] = None,
index_name: Optional[str] = None,
text_key: str = "text",
embedding: Optional[Embeddings] = None,
attributes: Optional[list] = None,
) -> List[Record]:
vector_store = super().build(
url=url,
api_key=api_key,
index_name=index_name,
text_key=text_key,
embedding=embedding,
attributes=attributes,
search_by_text=search_by_text,
)
if not vector_store:
raise ValueError("Failed to load the Weaviate index.")
return self.search_with_vector_store(
vector_store=vector_store, input_value=input_value, search_type=search_type
)

View file

@ -0,0 +1,41 @@
from typing import List
from langchain_core.vectorstores import VectorStore
from langflow import CustomComponent
from langflow.field_typing import Text
from langflow.schema import Record, docs_to_records
class LCVectorStoreComponent(CustomComponent):
display_name: str = "LC Vector Store"
description: str = "Search a LC Vector Store for similar documents."
beta: bool = True
def search_with_vector_store(
self, input_value: Text, search_type: str, vector_store: VectorStore
) -> List[Record]:
"""
Search for records in the vector store based on the input value and search type.
Args:
input_value (Text): The input value to search for.
search_type (str): The type of search to perform.
vector_store (VectorStore): The vector store to search in.
Returns:
List[Record]: A list of records matching the search criteria.
Raises:
ValueError: If invalid inputs are provided.
"""
docs = []
if input_value and isinstance(input_value, str):
docs = vector_store.search(
query=input_value, search_type=search_type.lower()
)
else:
raise ValueError("Invalid inputs provided.")
return docs_to_records(docs)

View file

@ -0,0 +1,73 @@
from typing import List, Optional
from langchain.embeddings.base import Embeddings
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.components.vectorstores.pgvector import PGVectorComponent
from langflow.schema import Record
class PGVectorSearchComponent(PGVectorComponent, LCVectorStoreComponent):
"""
A custom component for implementing a Vector Store using PostgreSQL.
"""
display_name: str = "PGVector Search"
description: str = "Search a PGVector Store for similar documents."
documentation = (
"https://python.langchain.com/docs/integrations/vectorstores/pgvector"
)
def build_config(self):
"""
Builds the configuration for the component.
Returns:
- dict: A dictionary containing the configuration options for the component.
"""
return {
"code": {"show": False},
"embedding": {"display_name": "Embedding"},
"search_type": {
"display_name": "Search Type",
"options": ["Similarity", "MMR"],
},
"pg_server_url": {
"display_name": "PostgreSQL Server Connection String",
"advanced": False,
},
"collection_name": {"display_name": "Table", "advanced": False},
"input_value": {"display_name": "Input"},
}
def build(
self,
input_value: str,
embedding: Embeddings,
pg_server_url: str,
collection_name: str,
search_type: Optional[str] = None,
) -> List[Record]:
"""
Builds the Vector Store or BaseRetriever object.
Args:
- input_value (str): The input value to search for.
- embedding (Embeddings): The embeddings to use for the Vector Store.
- collection_name (str): The name of the PG table.
- pg_server_url (str): The URL for the PG server.
Returns:
- VectorStore: The Vector Store object.
"""
try:
vector_store = super().build(
embedding=embedding,
pg_server_url=pg_server_url,
collection_name=collection_name,
)
except Exception as e:
raise RuntimeError(f"Failed to build PGVector: {e}")
return self.search_with_vector_store(
input_value=input_value, search_type=search_type, vector_store=vector_store
)

View file

@ -218,24 +218,7 @@ retrievers:
# https://github.com/supabase-community/supabase-py/issues/482 # https://github.com/supabase-community/supabase-py/issues/482
# ZepRetriever: # ZepRetriever:
# documentation: "https://python.langchain.com/docs/modules/data_connection/retrievers/integrations/zep_memorystore" # documentation: "https://python.langchain.com/docs/modules/data_connection/retrievers/integrations/zep_memorystore"
vectorstores:
# Chroma:
# documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/chroma"
Qdrant:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/qdrant"
FAISS:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/faiss"
Pinecone:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/pinecone"
ElasticsearchStore:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/elasticsearch"
SupabaseVectorStore:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/supabase"
MongoDBAtlasVectorSearch:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/mongodb_atlas"
# Requires docarray >=0.32.0 but langchain-serve requires jina 3.15.2 which doesn't support docarray >=0.32.0
# DocArrayInMemorySearch:
# documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/docarray_in_memory"
wrappers: wrappers:
RequestsWrapper: RequestsWrapper:
documentation: "" documentation: ""

View file

@ -73,7 +73,7 @@ class Graph:
if getattr(vertex, attribute): if getattr(vertex, attribute):
getattr(self, f"_{attribute}_vertices").append(vertex.id) getattr(self, f"_{attribute}_vertices").append(vertex.id)
async def _run(self, inputs: Dict[str, str]) -> List["ResultData"]: async def _run(self, inputs: Dict[str, str], stream: bool) -> List["ResultData"]:
"""Runs the graph with the given inputs.""" """Runs the graph with the given inputs."""
for vertex_id in self._is_input_vertices: for vertex_id in self._is_input_vertices:
vertex = self.get_vertex(vertex_id) vertex = self.get_vertex(vertex_id)
@ -91,10 +91,14 @@ class Graph:
vertex = self.get_vertex(vertex_id) vertex = self.get_vertex(vertex_id)
if vertex is None: if vertex is None:
raise ValueError(f"Vertex {vertex_id} not found") raise ValueError(f"Vertex {vertex_id} not found")
if not stream and hasattr(vertex, "consume_async_generator"):
await vertex.consume_async_generator()
outputs.append(vertex.result) outputs.append(vertex.result)
return outputs return outputs
async def run(self, inputs: Dict[str, Union[str, list[str]]]) -> List["ResultData"]: async def run(
self, inputs: Dict[str, Union[str, list[str]]], stream: bool
) -> List["ResultData"]:
"""Runs the graph with the given inputs.""" """Runs the graph with the given inputs."""
# inputs is {"message": "Hello, world!"} # inputs is {"message": "Hello, world!"}
@ -106,7 +110,9 @@ class Graph:
if not isinstance(inputs_values, list): if not isinstance(inputs_values, list):
inputs_values = [inputs_values] inputs_values = [inputs_values]
for input_value in inputs_values: for input_value in inputs_values:
run_outputs = await self._run({INPUT_FIELD_NAME: input_value}) run_outputs = await self._run(
{INPUT_FIELD_NAME: input_value}, stream=stream
)
logger.debug(f"Run outputs: {run_outputs}") logger.debug(f"Run outputs: {run_outputs}")
outputs.extend(run_outputs) outputs.extend(run_outputs)
return outputs return outputs

View file

@ -44,6 +44,7 @@ class Vertex:
) -> None: ) -> None:
# is_external means that the Vertex send or receives data from # is_external means that the Vertex send or receives data from
# an external source (e.g the chat) # an external source (e.g the chat)
self.will_stream = False
self.updated_raw_params = False self.updated_raw_params = False
self.id: str = data["id"] self.id: str = data["id"]
self.is_input = any( self.is_input = any(
@ -391,6 +392,8 @@ class Vertex:
ValueError: If any key in new_params is not found in self._raw_params. ValueError: If any key in new_params is not found in self._raw_params.
""" """
# First check if the input_value in _raw_params is not a vertex # First check if the input_value in _raw_params is not a vertex
if not new_params:
return
if any(isinstance(self._raw_params.get(key), Vertex) for key in new_params): if any(isinstance(self._raw_params.get(key), Vertex) for key in new_params):
return return
self._raw_params.update(new_params) self._raw_params.update(new_params)
@ -456,7 +459,7 @@ class Vertex:
await self._build_node_and_update_params(key, value, user_id) await self._build_node_and_update_params(key, value, user_id)
elif isinstance(value, list) and self._is_list_of_nodes(value): elif isinstance(value, list) and self._is_list_of_nodes(value):
await self._build_list_of_nodes_and_update_params(key, value, user_id) await self._build_list_of_nodes_and_update_params(key, value, user_id)
elif key not in self.params: elif key not in self.params or self.updated_raw_params:
self.params[key] = value self.params[key] = value
def _is_node(self, value): def _is_node(self, value):
@ -610,6 +613,7 @@ class Vertex:
async def build( async def build(
self, self,
user_id=None, user_id=None,
inputs: Optional[Dict[str, Any]] = None,
requester: Optional["Vertex"] = None, requester: Optional["Vertex"] = None,
**kwargs, **kwargs,
) -> Any: ) -> Any:
@ -622,6 +626,9 @@ class Vertex:
return self.get_requester_result(requester) return self.get_requester_result(requester)
self._reset() self._reset()
if self.is_input:
self.update_raw_params(inputs)
# Run steps # Run steps
for step in self.steps: for step in self.steps:
if step not in self.steps_ran: if step not in self.steps_ran:

View file

@ -11,7 +11,7 @@ from langflow.graph.utils import UnbuiltObject, flatten_list
from langflow.graph.vertex.base import StatefulVertex, StatelessVertex from langflow.graph.vertex.base import StatefulVertex, StatelessVertex
from langflow.interface.utils import extract_input_variables_from_prompt from langflow.interface.utils import extract_input_variables_from_prompt
from langflow.schema import Record from langflow.schema import Record
from langflow.services.monitor.utils import log_message from langflow.services.monitor.utils import log_vertex_build
from langflow.utils.schemas import ChatOutputResponse from langflow.utils.schemas import ChatOutputResponse
@ -394,6 +394,8 @@ class ChatVertex(StatelessVertex):
sender_name=sender_name, sender_name=sender_name,
stream_url=stream_url, stream_url=stream_url,
) )
self.will_stream = stream_url is not None
if artifacts: if artifacts:
self.artifacts = artifacts.model_dump() self.artifacts = artifacts.model_dump()
if isinstance(self._built_object, (AsyncIterator, Iterator)): if isinstance(self._built_object, (AsyncIterator, Iterator)):
@ -434,19 +436,25 @@ class ChatVertex(StatelessVertex):
self._built_result = complete_message self._built_result = complete_message
# Update artifacts with the message # Update artifacts with the message
# and remove the stream_url # and remove the stream_url
self._finalize_build()
logger.debug(f"Streamed message: {complete_message}") logger.debug(f"Streamed message: {complete_message}")
await log_message( await log_vertex_build(
sender=self.params.get("sender", ""), flow_id=self.graph.flow_id,
sender_name=self.params.get("sender_name", ""), vertex_id=self.id,
message=complete_message, valid=True,
session_id=self.params.get("session_id", ""), params=self._built_object_repr(),
data=self.result,
artifacts=self.artifacts, artifacts=self.artifacts,
) )
self._validate_built_object() self._validate_built_object()
self._built = True self._built = True
async def consume_async_generator(self):
async for _ in self.stream():
pass
class RoutingVertex(StatelessVertex): class RoutingVertex(StatelessVertex):
def __init__(self, data: Dict, graph): def __init__(self, data: Dict, graph):

View file

@ -1,6 +1,7 @@
import ast import ast
import os import os
import zlib import zlib
from pathlib import Path
from loguru import logger from loguru import logger
@ -79,9 +80,13 @@ class DirectoryReader:
except Exception as e: except Exception as e:
logger.error(f"Error while loading component: {e}") logger.error(f"Error while loading component: {e}")
continue continue
items.append({"name": menu["name"], "path": menu["path"], "components": components}) items.append(
{"name": menu["name"], "path": menu["path"], "components": components}
)
filtered = [menu for menu in items if menu["components"]] filtered = [menu for menu in items if menu["components"]]
logger.debug(f'Filtered components {"with errors" if with_errors else ""}: {len(filtered)}') logger.debug(
f'Filtered components {"with errors" if with_errors else ""}: {len(filtered)}'
)
return {"menu": filtered} return {"menu": filtered}
def validate_code(self, file_content): def validate_code(self, file_content):
@ -114,15 +119,24 @@ class DirectoryReader:
Walk through the directory path and return a list of all .py files. Walk through the directory path and return a list of all .py files.
""" """
if not (safe_path := self.get_safe_path()): if not (safe_path := self.get_safe_path()):
raise CustomComponentPathValueError(f"The path needs to start with '{self.base_path}'.") raise CustomComponentPathValueError(
f"The path needs to start with '{self.base_path}'."
)
file_list = [] file_list = []
for root, _, files in os.walk(safe_path): safe_path_obj = Path(safe_path)
file_list.extend( for file_path in safe_path_obj.rglob("*.py"):
os.path.join(root, filename) # The other condtion is that it should be
for filename in files # in the safe_path/[folder]/[file].py format
if filename.endswith(".py") and not filename.startswith("__") # any folders below [folder] will be ignored
) # basically the parent folder of the file should be a
# folder in the safe_path
if (
file_path.is_file()
and file_path.parent.parent == safe_path_obj
and not file_path.name.startswith("__")
):
file_list.append(str(file_path))
return file_list return file_list
def find_menu(self, response, menu_name): def find_menu(self, response, menu_name):
@ -159,7 +173,9 @@ class DirectoryReader:
for node in ast.walk(module): for node in ast.walk(module):
if isinstance(node, ast.FunctionDef): if isinstance(node, ast.FunctionDef):
for arg in node.args.args: for arg in node.args.args:
if self._is_type_hint_in_arg_annotation(arg.annotation, type_hint_name): if self._is_type_hint_in_arg_annotation(
arg.annotation, type_hint_name
):
return True return True
except SyntaxError: except SyntaxError:
# Returns False if the code is not valid Python # Returns False if the code is not valid Python
@ -177,14 +193,16 @@ class DirectoryReader:
and annotation.value.id == type_hint_name and annotation.value.id == type_hint_name
) )
def is_type_hint_used_but_not_imported(self, type_hint_name: str, code: str) -> bool: def is_type_hint_used_but_not_imported(
self, type_hint_name: str, code: str
) -> bool:
""" """
Check if a type hint is used but not imported in the given code. Check if a type hint is used but not imported in the given code.
""" """
try: try:
return self._is_type_hint_used_in_args(type_hint_name, code) and not self._is_type_hint_imported( return self._is_type_hint_used_in_args(
type_hint_name, code type_hint_name, code
) ) and not self._is_type_hint_imported(type_hint_name, code)
except SyntaxError: except SyntaxError:
# Returns True if there's something wrong with the code # Returns True if there's something wrong with the code
# TODO : Find a better way to handle this # TODO : Find a better way to handle this
@ -205,9 +223,9 @@ class DirectoryReader:
return False, "Syntax error" return False, "Syntax error"
elif not self.validate_build(file_content): elif not self.validate_build(file_content):
return False, "Missing build function" return False, "Missing build function"
elif self._is_type_hint_used_in_args("Optional", file_content) and not self._is_type_hint_imported( elif self._is_type_hint_used_in_args(
"Optional", file_content "Optional", file_content
): ) and not self._is_type_hint_imported("Optional", file_content):
return ( return (
False, False,
"Type hint 'Optional' is used but not imported in the code.", "Type hint 'Optional' is used but not imported in the code.",
@ -223,7 +241,9 @@ class DirectoryReader:
from the .py files in the directory. from the .py files in the directory.
""" """
response = {"menu": []} response = {"menu": []}
logger.debug("-------------------- Building component menu list --------------------") logger.debug(
"-------------------- Building component menu list --------------------"
)
for file_path in file_paths: for file_path in file_paths:
menu_name = os.path.basename(os.path.dirname(file_path)) menu_name = os.path.basename(os.path.dirname(file_path))
@ -243,7 +263,9 @@ class DirectoryReader:
# first check if it's already CamelCase # first check if it's already CamelCase
if "_" in component_name: if "_" in component_name:
component_name_camelcase = " ".join(word.title() for word in component_name.split("_")) component_name_camelcase = " ".join(
word.title() for word in component_name.split("_")
)
else: else:
component_name_camelcase = component_name component_name_camelcase = component_name
@ -251,7 +273,9 @@ class DirectoryReader:
try: try:
output_types = self.get_output_types_from_code(result_content) output_types = self.get_output_types_from_code(result_content)
except Exception as exc: except Exception as exc:
logger.exception(f"Error while getting output types from code: {str(exc)}") logger.exception(
f"Error while getting output types from code: {str(exc)}"
)
output_types = [component_name_camelcase] output_types = [component_name_camelcase]
else: else:
output_types = [component_name_camelcase] output_types = [component_name_camelcase]
@ -267,7 +291,9 @@ class DirectoryReader:
if menu_result not in response["menu"]: if menu_result not in response["menu"]:
response["menu"].append(menu_result) response["menu"].append(menu_result)
logger.debug("-------------------- Component menu list built --------------------") logger.debug(
"-------------------- Component menu list built --------------------"
)
return response return response
@staticmethod @staticmethod

View file

@ -1,11 +1,18 @@
from langflow.interface.custom.directory_reader import DirectoryReader
from langflow.template.frontend_node.custom_components import CustomComponentFrontendNode
from loguru import logger from loguru import logger
from langflow.interface.custom.directory_reader import DirectoryReader
from langflow.template.frontend_node.custom_components import (
CustomComponentFrontendNode,
)
def merge_nested_dicts_with_renaming(dict1, dict2): def merge_nested_dicts_with_renaming(dict1, dict2):
for key, value in dict2.items(): for key, value in dict2.items():
if key in dict1 and isinstance(value, dict) and isinstance(dict1.get(key), dict): if (
key in dict1
and isinstance(value, dict)
and isinstance(dict1.get(key), dict)
):
for sub_key, sub_value in value.items(): for sub_key, sub_value in value.items():
# if sub_key in dict1[key]: # if sub_key in dict1[key]:
# new_key = get_new_key(dict1[key], sub_key) # new_key = get_new_key(dict1[key], sub_key)
@ -62,7 +69,9 @@ def build_custom_component_list_from_path(path: str):
file_list = load_files_from_path(path) file_list = load_files_from_path(path)
reader = DirectoryReader(path, False) reader = DirectoryReader(path, False)
valid_components, invalid_components = build_and_validate_all_files(reader, file_list) valid_components, invalid_components = build_and_validate_all_files(
reader, file_list
)
valid_menu = build_valid_menu(valid_components) valid_menu = build_valid_menu(valid_components)
invalid_menu = build_invalid_menu(invalid_components) invalid_menu = build_invalid_menu(invalid_components)
@ -109,7 +118,9 @@ def build_invalid_menu_items(menu_item):
menu_items[component_name] = component_template menu_items[component_name] = component_template
logger.debug(f"Added {component_name} to invalid menu.") logger.debug(f"Added {component_name} to invalid menu.")
except Exception as exc: except Exception as exc:
logger.exception(f"Error while creating custom component [{component_name}]: {str(exc)}") logger.exception(
f"Error while creating custom component [{component_name}]: {str(exc)}"
)
return menu_items return menu_items
@ -136,12 +147,14 @@ def determine_component_name(component):
def build_menu_items(menu_item): def build_menu_items(menu_item):
"""Build menu items for a given menu.""" """Build menu items for a given menu."""
menu_items = {} menu_items = {}
logger.debug(f"Building menu items for {menu_item['name']}")
logger.debug(f"Loading {len(menu_item['components'])} components")
for component_name, component_template, component in menu_item["components"]: for component_name, component_template, component in menu_item["components"]:
try: try:
menu_items[component_name] = component_template menu_items[component_name] = component_template
logger.debug(f"Added {component_name} to valid menu.")
except Exception as exc: except Exception as exc:
logger.error(f"Error loading Component: {component['output_types']}") logger.error(f"Error loading Component: {component['output_types']}")
logger.exception(f"Error while building custom component {component['output_types']}: {exc}") logger.exception(
return menu_items f"Error while building custom component {component['output_types']}: {exc}"
)
return menu_items return menu_items

View file

@ -271,18 +271,26 @@ async def run_graph(
graph: Union["Graph", dict], graph: Union["Graph", dict],
flow_id: str, flow_id: str,
session_id: str, session_id: str,
stream: bool,
inputs: Optional[Union[dict, List[dict]]] = None, inputs: Optional[Union[dict, List[dict]]] = None,
artifacts: Optional[Dict[str, Any]] = None, artifacts: Optional[Dict[str, Any]] = None,
session_service: Optional[SessionService] = None, session_service: Optional[SessionService] = None,
): ):
"""Run the graph and generate the result""" """Run the graph and generate the result"""
if isinstance(graph, dict): if isinstance(graph, dict):
graph_data = graph
graph = Graph.from_payload(graph, flow_id=flow_id) graph = Graph.from_payload(graph, flow_id=flow_id)
else:
graph_data = graph._graph_data
if not session_id:
session_id = session_service.generate_key(
session_id=flow_id, data_graph=graph_data
)
outputs = await graph.run(inputs) outputs = await graph.run(inputs, stream=stream)
if session_id and session_service: if session_id and session_service:
session_service.update_session(session_id, (graph, artifacts)) session_service.update_session(session_id, (graph, artifacts))
return outputs return outputs, session_id
def validate_input( def validate_input(

View file

@ -5,16 +5,17 @@ from typing import TYPE_CHECKING
import sqlalchemy as sa import sqlalchemy as sa
from alembic import command, util from alembic import command, util
from alembic.config import Config from alembic.config import Config
from loguru import logger
from sqlalchemy import inspect
from sqlalchemy.exc import OperationalError
from sqlmodel import Session, SQLModel, create_engine, select, text
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.database import models # noqa from langflow.services.database import models # noqa
from langflow.services.database.models.user.crud import get_user_by_username from langflow.services.database.models.user.crud import get_user_by_username
from langflow.services.database.utils import Result, TableResults from langflow.services.database.utils import Result, TableResults
from langflow.services.deps import get_settings_service from langflow.services.deps import get_settings_service
from langflow.services.utils import teardown_superuser from langflow.services.utils import teardown_superuser
from loguru import logger
from sqlalchemy import inspect
from sqlalchemy.exc import OperationalError
from sqlmodel import Session, SQLModel, create_engine, select, text
if TYPE_CHECKING: if TYPE_CHECKING:
from sqlalchemy.engine import Engine from sqlalchemy.engine import Engine
@ -39,7 +40,7 @@ class DatabaseService(Service):
connect_args = {"check_same_thread": False} connect_args = {"check_same_thread": False}
else: else:
connect_args = {} connect_args = {}
return create_engine(self.database_url, connect_args=connect_args, max_overflow=-1) return create_engine(self.database_url, connect_args=connect_args)
def __enter__(self): def __enter__(self):
self._session = Session(self.engine) self._session = Session(self.engine)

View file

@ -8,7 +8,11 @@ import Checkmark from "../../components/ui/checkmark";
import Loading from "../../components/ui/loading"; import Loading from "../../components/ui/loading";
import { Textarea } from "../../components/ui/textarea"; import { Textarea } from "../../components/ui/textarea";
import Xmark from "../../components/ui/xmark"; import Xmark from "../../components/ui/xmark";
import { priorityFields, statusBuild, statusBuilding } from "../../constants/constants"; import {
priorityFields,
statusBuild,
statusBuilding,
} from "../../constants/constants";
import { BuildStatus } from "../../constants/enums"; import { BuildStatus } from "../../constants/enums";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import { useDarkStore } from "../../stores/darkStore"; import { useDarkStore } from "../../stores/darkStore";
@ -211,9 +215,7 @@ export default function GenericNode({
return "inactive-status"; return "inactive-status";
} }
if (buildStatus === BuildStatus.BUILT && isInvalid) { if (buildStatus === BuildStatus.BUILT && isInvalid) {
return isDark return isDark ? "built-invalid-status-dark" : "built-invalid-status";
? "built-invalid-status-dark"
: "built-invalid-status";
} else if (buildStatus === BuildStatus.BUILDING) { } else if (buildStatus === BuildStatus.BUILDING) {
return "building-status"; return "building-status";
} else { } else {
@ -296,7 +298,7 @@ export default function GenericNode({
<div <div
className={ className={
"generic-node-title-arrangement rounded-full" + "generic-node-title-arrangement rounded-full" +
(!showNode && " justify-center") (!showNode && " justify-center ")
} }
> >
{iconNodeRender()} {iconNodeRender()}
@ -332,21 +334,20 @@ export default function GenericNode({
) : ( ) : (
<ShadTooltip content={data.node?.display_name}> <ShadTooltip content={data.node?.display_name}>
<div className="group flex items-center gap-2.5"> <div className="group flex items-center gap-2.5">
<div
<div onDoubleClick={(event) => {
onDoubleClick={(event) => { if (nameEditable) {
if (nameEditable) { setInputName(true);
setInputName(true); }
} takeSnapshot();
takeSnapshot(); event.stopPropagation();
event.stopPropagation(); event.preventDefault();
event.preventDefault(); }}
}} data-testid={"title-" + data.node?.display_name}
data-testid={"title-" + data.node?.display_name} className="generic-node-tooltip-div text-primary"
className="generic-node-tooltip-div text-primary" >
> {data.node?.display_name}
{data.node?.display_name} </div>
</div>
{nameEditable && ( {nameEditable && (
<div <div
@ -465,7 +466,7 @@ export default function GenericNode({
if (buildStatus === BuildStatus.BUILDING || isBuilding) if (buildStatus === BuildStatus.BUILDING || isBuilding)
return; return;
setValidationStatus(null); setValidationStatus(null);
buildFlow(data.id); buildFlow({nodeId: data.id});
}} }}
> >
<div> <div>
@ -478,11 +479,11 @@ export default function GenericNode({
) : ( ) : (
<div className="max-h-96 overflow-auto"> <div className="max-h-96 overflow-auto">
{typeof validationStatus.params === "string" {typeof validationStatus.params === "string"
? (`${durationString}\n${validationStatus.params}` ? `${durationString}\n${validationStatus.params}`
.split("\n") .split("\n")
.map((line, index) => ( .map((line, index) => (
<div key={index}>{line}</div> <div key={index}>{line}</div>
))) ))
: durationString} : durationString}
</div> </div>
) )

View file

@ -1,3 +1,4 @@
import { Cross2Icon } from "@radix-ui/react-icons";
import { useState } from "react"; import { useState } from "react";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import { import {
@ -46,15 +47,15 @@ export default function AlertDropdown({
setTimeout(clearNotificationList, 100); setTimeout(clearNotificationList, 100);
}} }}
> >
<IconComponent name="Trash2" className="h-[1.1rem] w-[1.1rem]" /> <IconComponent name="Trash2" className="h-4 w-4" />
</button> </button>
<button <button
className="text-foreground hover:text-status-red" className="text-foreground opacity-70 hover:opacity-100"
onClick={() => { onClick={() => {
setOpen(false); setOpen(false);
}} }}
> >
<IconComponent name="X" className="h-5 w-5" /> <Cross2Icon className="h-4 w-4" />
</button> </button>
</div> </div>
</div> </div>

View file

@ -19,12 +19,12 @@ export default function IOInputField({
<Textarea <Textarea
className="w-full" className="w-full"
placeholder={"Enter text..."} placeholder={"Enter text..."}
value={node.data.node!.template["value"].value} value={node.data.node!.template["input_value"].value}
onChange={(e) => { onChange={(e) => {
e.target.value; e.target.value;
if (node) { if (node) {
let newNode = cloneDeep(node); let newNode = cloneDeep(node);
newNode.data.node!.template["value"].value = e.target.value; newNode.data.node!.template["input_value"].value = e.target.value;
setNode(node.id, newNode); setNode(node.id, newNode);
} }
}} }}
@ -49,12 +49,12 @@ export default function IOInputField({
<Textarea <Textarea
className="w-full custom-scroll" className="w-full custom-scroll"
placeholder={"Enter text..."} placeholder={"Enter text..."}
value={node.data.node!.template["value"]} value={node.data.node!.template["input_value"]}
onChange={(e) => { onChange={(e) => {
e.target.value; e.target.value;
if (node) { if (node) {
let newNode = cloneDeep(node); let newNode = cloneDeep(node);
newNode.data.node!.template["value"].value = e.target.value; newNode.data.node!.template["input_value"].value = e.target.value;
setNode(node.id, newNode); setNode(node.id, newNode);
} }
}} }}

View file

@ -30,12 +30,12 @@ export default function IOOutputView({
<Textarea <Textarea
className="w-full custom-scroll" className="w-full custom-scroll"
placeholder={"Enter text..."} placeholder={"Enter text..."}
value={node.data.node!.template["value"]} value={node.data.node!.template["input_value"]}
onChange={(e) => { onChange={(e) => {
e.target.value; e.target.value;
if (node) { if (node) {
let newNode = cloneDeep(node); let newNode = cloneDeep(node);
newNode.data.node!.template["value"].value = e.target.value; newNode.data.node!.template["input_value"].value = e.target.value;
setNode(node.id, newNode); setNode(node.id, newNode);
} }
}} }}

View file

@ -1,9 +1,13 @@
import { cloneDeep } from "lodash";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { CHAT_FORM_DIALOG_SUBTITLE, outputsModalTitle, textInputModalTitle } from "../../constants/constants"; import {
CHAT_FORM_DIALOG_SUBTITLE,
outputsModalTitle,
textInputModalTitle,
} from "../../constants/constants";
import BaseModal from "../../modals/baseModal"; import BaseModal from "../../modals/baseModal";
import useAlertStore from "../../stores/alertStore";
import useFlowStore from "../../stores/flowStore"; import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { updateVerticesOrder } from "../../utils/buildUtils";
import { cn } from "../../utils/utils"; import { cn } from "../../utils/utils";
import AccordionComponent from "../AccordionComponent"; import AccordionComponent from "../AccordionComponent";
import IOInputField from "../IOInputField"; import IOInputField from "../IOInputField";
@ -40,27 +44,30 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
{ type: string; id: string } | undefined { type: string; id: string } | undefined
>(undefined); >(undefined);
const { getNode, setNode, buildFlow, getFlow } = useFlowStore(); const buildFlow = useFlowStore((state) => state.buildFlow);
const { setErrorData } = useAlertStore();
const setIsBuilding = useFlowStore((state) => state.setIsBuilding); const setIsBuilding = useFlowStore((state) => state.setIsBuilding);
const [lockChat, setLockChat] = useState(false); const [lockChat, setLockChat] = useState(false);
const [chatValue, setChatValue] = useState(""); const [chatValue, setChatValue] = useState("");
const isBuilding = useFlowStore((state) => state.isBuilding); const isBuilding = useFlowStore((state) => state.isBuilding);
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
async function updateVertices() {
return updateVerticesOrder(currentFlow!.id, null);
}
useEffect(() => {
if (open) {
updateVertices();
}
}, [open, currentFlow]);
async function sendMessage(count = 1): Promise<void> { async function sendMessage(count = 1): Promise<void> {
if (isBuilding) return; if (isBuilding) return;
const { nodes, edges } = getFlow();
setIsBuilding(true); setIsBuilding(true);
setLockChat(true); setLockChat(true);
setChatValue(""); setChatValue("");
const chatInputNode = nodes.find((node) => node.id === chatInput?.id);
if (chatInputNode) {
let newNode = cloneDeep(chatInputNode);
newNode.data.node!.template["message"].value = chatValue;
setNode(chatInput!.id, newNode);
}
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
await buildFlow().catch((err) => { await buildFlow({ input_value: chatValue }).catch((err) => {
console.error(err); console.error(err);
setLockChat(false); setLockChat(false);
}); });
@ -104,7 +111,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
<Tabs <Tabs
value={selectedTab.toString()} value={selectedTab.toString()}
className={ className={
"flex h-full flex-col overflow-y-auto custom-scroll rounded-md border bg-muted text-center" "flex h-full flex-col overflow-y-auto rounded-md border bg-muted text-center custom-scroll"
} }
onValueChange={(value) => { onValueChange={(value) => {
setSelectedTab(Number(value)); setSelectedTab(Number(value));
@ -266,24 +273,27 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
{selectedViewField.type} {selectedViewField.type}
</div> </div>
<div className="h-full"> <div className="h-full">
{inputs.some( {inputs.some(
(input) => input.id === selectedViewField.id (input) => input.id === selectedViewField.id
) ? ( ) ? (
<IOInputField <IOInputField
inputType={selectedViewField.type!} inputType={selectedViewField.type!}
inputId={selectedViewField.id!} inputId={selectedViewField.id!}
/> />
) : ( ) : (
<IOOutputView <IOOutputView
outputType={selectedViewField.type!} outputType={selectedViewField.type!}
outputId={selectedViewField.id!} outputId={selectedViewField.id!}
/> />
)} )}
</div> </div>
</div> </div>
)} )}
<div <div
className={cn("flex w-full h-full",selectedViewField ? "hidden" : "")} className={cn(
"flex h-full w-full",
selectedViewField ? "hidden" : ""
)}
> >
<NewChatView <NewChatView
sendMessage={sendMessage} sendMessage={sendMessage}

View file

@ -23,8 +23,6 @@ export default function BuildTrigger({
const nodes = useFlowStore((state) => state.nodes); const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges); const edges = useFlowStore((state) => state.edges);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setFlowState = useFlowStore((state) => state.setFlowState);
const eventClick = isBuilding ? "pointer-events-none" : ""; const eventClick = isBuilding ? "pointer-events-none" : "";
const [progress, setProgress] = useState(0); const [progress, setProgress] = useState(0);
@ -47,7 +45,7 @@ export default function BuildTrigger({
setIsBuilding(true); setIsBuilding(true);
await enforceMinimumLoadingTime(startTime, minimumLoadingTime); await enforceMinimumLoadingTime(startTime, minimumLoadingTime);
await buildFlow(); await buildFlow({});
} catch (error) { } catch (error) {
console.error("Error:", error); console.error("Error:", error);
} finally { } finally {

View file

@ -4,6 +4,7 @@ import { Textarea } from "../../../components/ui/textarea";
import { chatInputType } from "../../../types/components"; import { chatInputType } from "../../../types/components";
import { classNames } from "../../../utils/utils"; import { classNames } from "../../../utils/utils";
import { chatInputPlaceholder, chatInputPlaceholderSend } from "../../../constants/constants"; import { chatInputPlaceholder, chatInputPlaceholderSend } from "../../../constants/constants";
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
export default function ChatInput({ export default function ChatInput({
lockChat, lockChat,
@ -14,20 +15,21 @@ export default function ChatInput({
noInput, noInput,
}: chatInputType): JSX.Element { }: chatInputType): JSX.Element {
const [repeat, setRepeat] = useState(1); const [repeat, setRepeat] = useState(1);
const saveLoading = useFlowsManagerStore((state) => state.saveLoading);
useEffect(() => { useEffect(() => {
if (!lockChat && inputRef.current) { if (!lockChat && inputRef.current) {
inputRef.current.focus(); inputRef.current.focus();
} }
}, [lockChat, inputRef]); }, [lockChat, inputRef]);
function handleChange(value: number) { /* function handleChange(value: number) {
console.log(value); console.log(value);
if (value > 0) { if (value > 0) {
setRepeat(value); setRepeat(value);
} else { } else {
setRepeat(1); setRepeat(1);
} }
} } */
useEffect(() => { useEffect(() => {
if (inputRef.current) { if (inputRef.current) {
@ -41,13 +43,13 @@ export default function ChatInput({
<div className="relative w-full"> <div className="relative w-full">
<Textarea <Textarea
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.key === "Enter" && !lockChat && !event.shiftKey) { if (event.key === "Enter" && !lockChat && !saveLoading && !event.shiftKey) {
sendMessage(repeat); sendMessage(repeat);
} }
}} }}
rows={1} rows={1}
ref={inputRef} ref={inputRef}
disabled={lockChat || noInput} disabled={lockChat || noInput || saveLoading}
style={{ style={{
resize: "none", resize: "none",
bottom: `${inputRef?.current?.scrollHeight}px`, bottom: `${inputRef?.current?.scrollHeight}px`,
@ -58,12 +60,12 @@ export default function ChatInput({
: "hidden" : "hidden"
}`, }`,
}} }}
value={lockChat ? "Thinking..." : chatValue} value={lockChat ? "Thinking..." : (saveLoading ? "Saving..." : chatValue)}
onChange={(event): void => { onChange={(event): void => {
setChatValue(event.target.value); setChatValue(event.target.value);
}} }}
className={classNames( className={classNames(
lockChat (lockChat || saveLoading)
? " form-modal-lock-true bg-input" ? " form-modal-lock-true bg-input"
: noInput : noInput
? "form-modal-no-input bg-input" ? "form-modal-no-input bg-input"
@ -87,10 +89,10 @@ export default function ChatInput({
? "text-primary" ? "text-primary"
: "bg-chat-send text-background" : "bg-chat-send text-background"
)} )}
disabled={lockChat} disabled={lockChat || saveLoading}
onClick={(): void => sendMessage(repeat)} onClick={(): void => sendMessage(repeat)}
> >
{lockChat ? ( {lockChat || saveLoading ? (
<IconComponent <IconComponent
name="Lock" name="Lock"
className="form-modal-lock-icon" className="form-modal-lock-icon"

View file

@ -1,5 +1,5 @@
import Convert from "ansi-to-html"; import Convert from "ansi-to-html";
import { useEffect, useMemo, useState, useRef } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import Markdown from "react-markdown"; import Markdown from "react-markdown";
import rehypeMathjax from "rehype-mathjax"; import rehypeMathjax from "rehype-mathjax";
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm";
@ -9,17 +9,17 @@ import Robot from "../../../assets/robot.png";
import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper"; import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper";
import CodeTabsComponent from "../../../components/codeTabsComponent"; import CodeTabsComponent from "../../../components/codeTabsComponent";
import IconComponent from "../../../components/genericIconComponent"; import IconComponent from "../../../components/genericIconComponent";
import useFlowStore from "../../../stores/flowStore";
import { chatMessagePropsType } from "../../../types/components"; import { chatMessagePropsType } from "../../../types/components";
import { classNames } from "../../../utils/utils"; import { classNames } from "../../../utils/utils";
import FileCard from "../fileComponent"; import FileCard from "../fileComponent";
import useFlowStore from "../../../stores/flowStore";
export default function ChatMessage({ export default function ChatMessage({
chat, chat,
lockChat, lockChat,
lastMessage, lastMessage,
updateChat, updateChat,
setLockChat setLockChat,
}: chatMessagePropsType): JSX.Element { }: chatMessagePropsType): JSX.Element {
const convert = new Convert({ newline: true }); const convert = new Convert({ newline: true });
const [hidden, setHidden] = useState(true); const [hidden, setHidden] = useState(true);
@ -40,8 +40,6 @@ export default function ChatMessage({
chatMessageRef.current = chatMessage; chatMessageRef.current = chatMessage;
}, [chatMessage]); }, [chatMessage]);
// The idea now is that chat.stream_url MAY be a URL if we should stream the output of the chat // The idea now is that chat.stream_url MAY be a URL if we should stream the output of the chat
// probably the message is empty when we have a stream_url // probably the message is empty when we have a stream_url
// what we need is to update the chat_message with the SSE data // what we need is to update the chat_message with the SSE data
@ -70,9 +68,7 @@ export default function ChatMessage({
}); });
}; };
useEffect(() => { useEffect(() => {
console.log("chatMessage", chatMessage);
if (streamUrl && !isStreaming) { if (streamUrl && !isStreaming) {
setLockChat(true); setLockChat(true);
streamChunks(streamUrl) streamChunks(streamUrl)
@ -92,8 +88,8 @@ export default function ChatMessage({
useEffect(() => { useEffect(() => {
return () => { return () => {
eventSource.current?.close(); eventSource.current?.close();
} };
}, []) }, []);
useEffect(() => { useEffect(() => {
const element = document.getElementById("last-chat-message"); const element = document.getElementById("last-chat-message");
@ -222,7 +218,7 @@ dark:prose-invert"
}, },
]} ]}
activeTab={"0"} activeTab={"0"}
setActiveTab={() => { }} setActiveTab={() => {}}
/> />
) : ( ) : (
<code className={className} {...props}> <code className={className} {...props}>
@ -279,33 +275,33 @@ dark:prose-invert"
<span className="prose text-primary word-break-break-word dark:prose-invert"> <span className="prose text-primary word-break-break-word dark:prose-invert">
{promptOpen {promptOpen
? template?.split("\n")?.map((line, index) => { ? template?.split("\n")?.map((line, index) => {
const regex = /{([^}]+)}/g; const regex = /{([^}]+)}/g;
let match; let match;
let parts: Array<JSX.Element | string> = []; let parts: Array<JSX.Element | string> = [];
let lastIndex = 0; let lastIndex = 0;
while ((match = regex.exec(line)) !== null) { while ((match = regex.exec(line)) !== null) {
// Push text up to the match // Push text up to the match
if (match.index !== lastIndex) { if (match.index !== lastIndex) {
parts.push(line.substring(lastIndex, match.index)); parts.push(line.substring(lastIndex, match.index));
} }
// Push div with matched text // Push div with matched text
if (chat.message[match[1]]) { if (chat.message[match[1]]) {
parts.push( parts.push(
<span className="chat-message-highlight"> <span className="chat-message-highlight">
{chat.message[match[1]]} {chat.message[match[1]]}
</span> </span>
); );
} }
// Update last index // Update last index
lastIndex = regex.lastIndex; lastIndex = regex.lastIndex;
} }
// Push text after the last match // Push text after the last match
if (lastIndex !== line.length) { if (lastIndex !== line.length) {
parts.push(line.substring(lastIndex)); parts.push(line.substring(lastIndex));
} }
return <p>{parts}</p>; return <p>{parts}</p>;
}) })
: chatMessage} : chatMessage}
</span> </span>
</> </>

View file

@ -1,6 +1,10 @@
import _ from "lodash";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import { NOCHATOUTPUT_NOTICE_ALERT } from "../../constants/alerts_constants";
import {
chatFirstInitialText,
chatSecondInitialText,
} from "../../constants/constants";
import { deleteFlowPool } from "../../controllers/API"; import { deleteFlowPool } from "../../controllers/API";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import useFlowStore from "../../stores/flowStore"; import useFlowStore from "../../stores/flowStore";
@ -14,8 +18,6 @@ import {
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import ChatInput from "./chatInput"; import ChatInput from "./chatInput";
import ChatMessage from "./chatMessage"; import ChatMessage from "./chatMessage";
import { INFO_MISSING_ALERT, NOCHATOUTPUT_NOTICE_ALERT } from "../../constants/alerts_constants";
import { chatFirstInitialText, chatSecondInitialText } from "../../constants/constants";
export default function NewChatView({ export default function NewChatView({
sendMessage, sendMessage,
@ -34,7 +36,7 @@ export default function NewChatView({
const inputIds = inputs.map((obj) => obj.id); const inputIds = inputs.map((obj) => obj.id);
const outputIds = outputs.map((obj) => obj.id); const outputIds = outputs.map((obj) => obj.id);
const outputTypes = outputs.map((obj) => obj.type); const outputTypes = outputs.map((obj) => obj.type);
const updateFlowPool = useFlowStore((state)=>state.updateFlowPool) const updateFlowPool = useFlowStore((state) => state.updateFlowPool);
useEffect(() => { useEffect(() => {
if (!outputTypes.includes("ChatOutput")) { if (!outputTypes.includes("ChatOutput")) {
@ -73,7 +75,7 @@ export default function NewChatView({
isSend: !is_ai, isSend: !is_ai,
message: message, message: message,
sender_name, sender_name,
componentId: output.id, componentId: output.id,
stream_url: stream_url, stream_url: stream_url,
}; };
} catch (e) { } catch (e) {
@ -120,22 +122,26 @@ export default function NewChatView({
chat: ChatMessageType, chat: ChatMessageType,
message: string, message: string,
stream_url?: string stream_url?: string
) { ) {
if (message === "") return; if (message === "") return;
chat.message = message; chat.message = message;
// chat is one of the chatHistory // chat is one of the chatHistory
updateFlowPool(chat.componentId,{message,sender_name:chat.sender_name??"Bot",sender:"Machine"}) updateFlowPool(chat.componentId, {
message,
sender_name: chat.sender_name ?? "Bot",
sender: chat.isSend ? "User" : "Machine",
});
// setChatHistory((oldChatHistory) => { // setChatHistory((oldChatHistory) => {
// const index = oldChatHistory.findIndex((ch) => ch.id === chat.id); // const index = oldChatHistory.findIndex((ch) => ch.id === chat.id);
// if (index === -1) return oldChatHistory; // if (index === -1) return oldChatHistory;
// let newChatHistory = _.cloneDeep(oldChatHistory); // let newChatHistory = _.cloneDeep(oldChatHistory);
// newChatHistory = [ // newChatHistory = [
// ...newChatHistory.slice(0, index), // ...newChatHistory.slice(0, index),
// chat, // chat,
// ...newChatHistory.slice(index + 1), // ...newChatHistory.slice(index + 1),
// ]; // ];
// console.log("newChatHistory:", newChatHistory); // console.log("newChatHistory:", newChatHistory);
// return newChatHistory; // return newChatHistory;
// }); // });
} }
@ -160,7 +166,7 @@ export default function NewChatView({
{chatHistory?.length > 0 ? ( {chatHistory?.length > 0 ? (
chatHistory.map((chat, index) => ( chatHistory.map((chat, index) => (
<ChatMessage <ChatMessage
setLockChat={setLockChat} setLockChat={setLockChat}
lockChat={lockChat} lockChat={lockChat}
chat={chat} chat={chat}
lastMessage={chatHistory.length - 1 === index ? true : false} lastMessage={chatHistory.length - 1 === index ? true : false}

View file

@ -123,7 +123,7 @@ function ApiInterceptor() {
async function clearBuildVerticesState(error) { async function clearBuildVerticesState(error) {
if (error?.response?.status === 500) { if (error?.response?.status === 500) {
const vertices = useFlowStore.getState().verticesBuild; const vertices = useFlowStore.getState().verticesBuild;
useFlowStore.getState().updateBuildStatus(vertices, BuildStatus.BUILT); useFlowStore.getState().updateBuildStatus(vertices?.verticesIds ?? [], BuildStatus.BUILT);
useFlowStore.getState().setIsBuilding(false); useFlowStore.getState().setIsBuilding(false);
} }
} }

View file

@ -869,9 +869,10 @@ export async function getVerticesOrder(
export async function postBuildVertex( export async function postBuildVertex(
flowId: string, flowId: string,
vertexId: string vertexId: string,
input_value: string,
): Promise<AxiosResponse<VertexBuildTypeAPI>> { ): Promise<AxiosResponse<VertexBuildTypeAPI>> {
return await api.post(`${BASE_URL_API}build/${flowId}/vertices/${vertexId}`); return await api.post(`${BASE_URL_API}build/${flowId}/vertices/${vertexId}`, input_value ? {inputs: {input_value: input_value}} : undefined);
} }
export async function downloadImage({ flowId, fileName }): Promise<any> { export async function downloadImage({ flowId, fileName }): Promise<any> {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 406 KiB

Before After
Before After

View file

@ -1,5 +1,6 @@
import _, { cloneDeep } from "lodash"; import _, { cloneDeep } from "lodash";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useUpdateNodeInternals } from "reactflow";
import ShadTooltip from "../../../../components/ShadTooltipComponent"; import ShadTooltip from "../../../../components/ShadTooltipComponent";
import CodeAreaComponent from "../../../../components/codeAreaComponent"; import CodeAreaComponent from "../../../../components/codeAreaComponent";
import IconComponent from "../../../../components/genericIconComponent"; import IconComponent from "../../../../components/genericIconComponent";
@ -26,7 +27,6 @@ import {
updateFlowPosition, updateFlowPosition,
} from "../../../../utils/reactflowUtils"; } from "../../../../utils/reactflowUtils";
import { classNames, cn } from "../../../../utils/utils"; import { classNames, cn } from "../../../../utils/utils";
import { useUpdateNodeInternals } from "reactflow";
export default function NodeToolbarComponent({ export default function NodeToolbarComponent({
data, data,
@ -94,11 +94,12 @@ export default function NodeToolbarComponent({
const handleModalWShortcut = useFlowStore(state => state.handleModalWShortcut); const handleModalWShortcut = useFlowStore(state => state.handleModalWShortcut);
useEffect(() => { useEffect(() => {
console.log(openCodeModalWShortcut)
setOpenModal(openCodeModalWShortcut) setOpenModal(openCodeModalWShortcut)
}, [openCodeModalWShortcut, handleModalWShortcut]) }, [openCodeModalWShortcut, handleModalWShortcut])
const setLastCopiedSelection = useFlowStore(state => state.setLastCopiedSelection); const setLastCopiedSelection = useFlowStore(
(state) => state.setLastCopiedSelection
);
useEffect(() => { useEffect(() => {
setFlowComponent(createFlowComponent(cloneDeep(data), version)); setFlowComponent(createFlowComponent(cloneDeep(data), version));
}, [ }, [
@ -153,8 +154,8 @@ export default function NodeToolbarComponent({
deleteNode(data.id); deleteNode(data.id);
break; break;
case "copy": case "copy":
const node = nodes.filter(node => node.id === data.id) const node = nodes.filter((node) => node.id === data.id);
setLastCopiedSelection({ nodes: _.cloneDeep(node), edges: [] }) setLastCopiedSelection({ nodes: _.cloneDeep(node), edges: [] });
} }
}; };
@ -242,7 +243,7 @@ export default function NodeToolbarComponent({
id={"code-input-node-toolbar-" + name} id={"code-input-node-toolbar-" + name}
/> />
</div> </div>
<IconComponent name="Code" className="h-4 w-4" /> <IconComponent name="TerminalSquare" className="h-4 w-4" />
</button> </button>
</ShadTooltip> </ShadTooltip>
) : ( ) : (
@ -380,13 +381,11 @@ export default function NodeToolbarComponent({
className="relative top-0.5 mr-2 h-4 w-4 " className="relative top-0.5 mr-2 h-4 w-4 "
/>{" "} />{" "}
<span className="">Copy</span>{" "} <span className="">Copy</span>{" "}
<IconComponent
<IconComponent name="Command"
name="Command" className="absolute right-[1.15rem] top-[0.65em] h-3.5 w-3.5 stroke-2"
className="absolute right-[1.15rem] top-[0.65em] h-3.5 w-3.5 stroke-2" ></IconComponent>
></IconComponent> <span className="absolute right-2 top-[0.5em]">C</span>
<span className="absolute right-2 top-[0.5em]">C</span>
</div> </div>
</SelectItem> </SelectItem>
{hasStore && ( {hasStore && (
@ -459,7 +458,7 @@ export default function NodeToolbarComponent({
<span> <span>
<IconComponent <IconComponent
name="Delete" name="Delete"
className="absolute right-2 top-2 h-4 w-4 text-red-400 stroke-2" className="absolute right-2 top-2 h-4 w-4 stroke-2 text-red-400"
></IconComponent> ></IconComponent>
</span> </span>
</div> </div>

View file

@ -9,9 +9,12 @@ import {
applyNodeChanges, applyNodeChanges,
} from "reactflow"; } from "reactflow";
import { create } from "zustand"; import { create } from "zustand";
import { FLOW_BUILD_SUCCESS_ALERT, MISSED_ERROR_ALERT } from "../constants/alerts_constants"; import {
FLOW_BUILD_SUCCESS_ALERT,
MISSED_ERROR_ALERT,
} from "../constants/alerts_constants";
import { BuildStatus } from "../constants/enums"; import { BuildStatus } from "../constants/enums";
import { getFlowPool, updateFlowInDatabase } from "../controllers/API"; import { getFlowPool } from "../controllers/API";
import { VertexBuildTypeAPI } from "../types/api"; import { VertexBuildTypeAPI } from "../types/api";
import { import {
NodeDataType, NodeDataType,
@ -19,7 +22,12 @@ import {
sourceHandleType, sourceHandleType,
targetHandleType, targetHandleType,
} from "../types/flow"; } from "../types/flow";
import { ChatOutputType, FlowPoolObjectType, FlowStoreType, chatInputType } from "../types/zustand/flow"; import {
ChatOutputType,
FlowPoolObjectType,
FlowStoreType,
chatInputType,
} from "../types/zustand/flow";
import { buildVertices } from "../utils/buildUtils"; import { buildVertices } from "../utils/buildUtils";
import { import {
cleanEdges, cleanEdges,
@ -69,23 +77,25 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
} }
get().setFlowPool(newFlowPool); get().setFlowPool(newFlowPool);
}, },
updateFlowPool:(nodeId:string,data:FlowPoolObjectType| ChatOutputType | chatInputType,buildId?:string)=>{ updateFlowPool: (
nodeId: string,
data: FlowPoolObjectType | ChatOutputType | chatInputType,
buildId?: string
) => {
let newFlowPool = cloneDeep({ ...get().flowPool }); let newFlowPool = cloneDeep({ ...get().flowPool });
if (!newFlowPool[nodeId]){ if (!newFlowPool[nodeId]) {
return; return;
} } else {
else { let index = newFlowPool[nodeId].length - 1;
let index = newFlowPool[nodeId].length-1; if (buildId) {
if(buildId){ index = newFlowPool[nodeId].findIndex((flow) => flow.id === buildId);
index = newFlowPool[nodeId].findIndex((flow)=>flow.id===buildId);
} }
//check if the data is a flowpool object //check if the data is a flowpool object
if((data as FlowPoolObjectType).data?.artifacts!==undefined){ if ((data as FlowPoolObjectType).data?.artifacts !== undefined) {
newFlowPool[nodeId][index] = (data as FlowPoolObjectType); newFlowPool[nodeId][index] = data as FlowPoolObjectType;
} }
//update data artifact //update data artifact
else else {
{
newFlowPool[nodeId][index].data.artifacts = data; newFlowPool[nodeId][index].data.artifacts = data;
} }
} }
@ -404,7 +414,13 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
}); });
}); });
}, },
buildFlow: async (nodeId?: string) => { buildFlow: async ({
nodeId,
input_value,
}: {
nodeId?: string;
input_value?: string;
}) => {
get().setIsBuilding(true); get().setIsBuilding(true);
const currentFlow = useFlowsManagerStore.getState().currentFlow; const currentFlow = useFlowsManagerStore.getState().currentFlow;
const setSuccessData = useAlertStore.getState().setSuccessData; const setSuccessData = useAlertStore.getState().setSuccessData;
@ -427,25 +443,19 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
function handleBuildUpdate( function handleBuildUpdate(
vertexBuildData: VertexBuildTypeAPI, vertexBuildData: VertexBuildTypeAPI,
status: BuildStatus, status: BuildStatus,
buildId:string buildId: string
) { ) {
if (vertexBuildData && vertexBuildData.inactive_vertices) { if (vertexBuildData && vertexBuildData.inactive_vertices) {
get().removeFromVerticesBuild(vertexBuildData.inactive_vertices); get().removeFromVerticesBuild(vertexBuildData.inactive_vertices);
} }
get().addDataToFlowPool({...vertexBuildData,buildId}, vertexBuildData.id); get().addDataToFlowPool(
{ ...vertexBuildData, buildId },
vertexBuildData.id
);
useFlowStore.getState().updateBuildStatus([vertexBuildData.id], status); useFlowStore.getState().updateBuildStatus([vertexBuildData.id], status);
} }
await updateFlowInDatabase({
data: {
nodes: get().nodes,
edges: get().edges,
viewport: get().reactFlowInstance?.getViewport()!,
},
id: currentFlow!.id,
name: currentFlow!.name,
description: currentFlow!.description,
});
await buildVertices({ await buildVertices({
input_value,
flowId: currentFlow!.id, flowId: currentFlow!.id,
nodeId, nodeId,
onGetOrderSuccess: () => { onGetOrderSuccess: () => {
@ -483,16 +493,22 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
viewport: get().reactFlowInstance?.getViewport()!, viewport: get().reactFlowInstance?.getViewport()!,
}; };
}, },
updateVerticesBuild: (vertices: string[]) => { updateVerticesBuild: (
vertices: { verticesIds: string[], verticesOrder: string[][], verticesLayers: string[][], runId: string } | null
) => {
set({ verticesBuild: vertices }); set({ verticesBuild: vertices });
}, },
verticesBuild: [], verticesBuild: null,
removeFromVerticesBuild: (vertices: string[]) => { removeFromVerticesBuild: (vertices: string[]) => {
const verticesBuild = get().verticesBuild;
if (!verticesBuild) return;
set({ set({
verticesBuild: get().verticesBuild.filter( verticesBuild: {
(vertex) => !vertices.includes(vertex) ...verticesBuild,
), verticesIds: get().verticesBuild!.verticesIds.filter(
(vertex) => !vertices.includes(vertex)
),
},
}); });
}, },
updateBuildStatus: (nodeIdList: string[], status: BuildStatus) => { updateBuildStatus: (nodeIdList: string[], status: BuildStatus) => {

View file

@ -83,6 +83,7 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
if (saveTimeoutId) { if (saveTimeoutId) {
clearTimeout(saveTimeoutId); clearTimeout(saveTimeoutId);
} }
set({ saveLoading: true });
// Set up a new timeout. // Set up a new timeout.
saveTimeoutId = setTimeout(() => { saveTimeoutId = setTimeout(() => {
if (get().currentFlow) { if (get().currentFlow) {
@ -92,7 +93,7 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
); );
} }
set({ saveLoading: true }); set({ saveLoading: true });
}, 1000); // Delay of 1000ms. }, 500); // Delay of 500ms because chat message depends on it.
}, },
saveFlow: (flow: FlowType, silent?: boolean) => { saveFlow: (flow: FlowType, silent?: boolean) => {
set({ saveLoading: true }); set({ saveLoading: true });

View file

@ -88,11 +88,11 @@ export type FlowStoreType = {
getFilterEdge: any[]; getFilterEdge: any[];
onConnect: (connection: Connection) => void; onConnect: (connection: Connection) => void;
unselectAll: () => void; unselectAll: () => void;
buildFlow: (nodeId?: string) => Promise<void>; buildFlow: ({nodeId, input_value}: {nodeId?: string, input_value?: string}) => Promise<void>;
getFlow: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport }; getFlow: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport };
updateVerticesBuild: (vertices: string[]) => void; updateVerticesBuild: (vertices: {verticesIds: string[], verticesLayers: string[][], verticesOrder: string[][], runId: string} | null) => void;
removeFromVerticesBuild: (vertices: string[]) => void; removeFromVerticesBuild: (vertices: string[]) => void;
verticesBuild: string[]; verticesBuild: {verticesIds: string[], verticesLayers: string[][], verticesOrder: string[][], runId: string} | null;
updateBuildStatus: (nodeId: string[], status: BuildStatus) => void; updateBuildStatus: (nodeId: string[], status: BuildStatus) => void;
revertBuiltStatusFromBuilding: () => void; revertBuiltStatusFromBuilding: () => void;
flowBuildStatus: { [key: string]: BuildStatus }; flowBuildStatus: { [key: string]: BuildStatus };

View file

@ -7,9 +7,14 @@ import { VertexBuildTypeAPI } from "../types/api";
type BuildVerticesParams = { type BuildVerticesParams = {
flowId: string; // Assuming FlowType is the type for your flow flowId: string; // Assuming FlowType is the type for your flow
input_value?: any; // Replace any with the actual type if it's not any
nodeId?: string | null; // Assuming nodeId is of type string, and it's optional nodeId?: string | null; // Assuming nodeId is of type string, and it's optional
onGetOrderSuccess?: () => void; onGetOrderSuccess?: () => void;
onBuildUpdate?: (data: VertexBuildTypeAPI, status: BuildStatus,buildId:string) => void; // Replace any with the actual type if it's not any onBuildUpdate?: (
data: VertexBuildTypeAPI,
status: BuildStatus,
buildId: string
) => void; // Replace any with the actual type if it's not any
onBuildComplete?: (allNodesValid: boolean) => void; onBuildComplete?: (allNodesValid: boolean) => void;
onBuildError?: (title, list, idList: string[]) => void; onBuildError?: (title, list, idList: string[]) => void;
onBuildStart?: (idList: string[]) => void; onBuildStart?: (idList: string[]) => void;
@ -34,8 +39,54 @@ function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
return inactiveVertexData; return inactiveVertexData;
} }
export async function updateVerticesOrder(flowId: string, nodeId: string | null): Promise<{ verticesLayers: string[][], verticesIds: string[], verticesOrder: string[][], runId: string }> {
return new Promise(async (resolve, reject) => {
const setErrorData = useAlertStore.getState().setErrorData;
let orderResponse;
try {
orderResponse = await getVerticesOrder(flowId, nodeId);
} catch (error: any) {
console.log(error);
setErrorData({
title: "Oops! Looks like you missed something",
list: [error.response?.data?.detail ?? "Unknown Error"],
});
useFlowStore.getState().setIsBuilding(false);
throw new Error("Invalid nodes");
}
let verticesOrder: Array<Array<string>> = orderResponse.data.ids;
const runId = orderResponse.data.run_id;
let verticesLayers: Array<Array<string>> = [];
if (nodeId) {
for (let i = 0; i < verticesOrder.length; i += 1) {
const innerArray = verticesOrder[i];
const idIndex = innerArray.indexOf(nodeId);
if (idIndex !== -1) {
// If there's a nodeId, we want to run just that component and not the entire layer
// because a layer contains dependencies for the next layer
// and we are stopping at the layer that contains the nodeId
verticesLayers.push([innerArray[idIndex]]);
break; // Stop searching after finding the first occurrence
}
// If the targetId is not found, include the entire inner array
verticesLayers.push(innerArray);
}
} else {
verticesLayers = verticesOrder;
}
const verticesIds = verticesLayers.flat();
useFlowStore
.getState()
.updateVerticesBuild({ verticesLayers, verticesIds, verticesOrder, runId });
resolve({ verticesLayers, verticesIds, verticesOrder, runId });
});
}
export async function buildVertices({ export async function buildVertices({
flowId, flowId,
input_value,
nodeId = null, nodeId = null,
onGetOrderSuccess, onGetOrderSuccess,
onBuildUpdate, onBuildUpdate,
@ -44,24 +95,18 @@ export async function buildVertices({
onBuildStart, onBuildStart,
validateNodes, validateNodes,
}: BuildVerticesParams) { }: BuildVerticesParams) {
const setErrorData = useAlertStore.getState().setErrorData; let verticesBuild = useFlowStore.getState().verticesBuild;
let orderResponse; if (!verticesBuild || nodeId) {
try { verticesBuild = await updateVerticesOrder(flowId, nodeId);
orderResponse = await getVerticesOrder(flowId, nodeId);
} catch (error:any) {
console.log(error);
setErrorData({
title: "Oops! Looks like you missed something",
list: [error.response?.data?.detail ?? "Unknown Error"],
});
useFlowStore.getState().setIsBuilding(false);
throw new Error("Invalid nodes");
} }
if (onGetOrderSuccess) onGetOrderSuccess(); const verticesIds = verticesBuild?.verticesIds!;
let verticesOrder: Array<Array<string>> = orderResponse.data.ids; const verticesLayers = verticesBuild?.verticesLayers!;
const runId = orderResponse.data.run_id; const verticesOrder = verticesBuild?.verticesOrder!;
let vertices_layers: Array<Array<string>> = []; const runId = verticesBuild?.runId!;
let stop = false; let stop = false;
if (onGetOrderSuccess) onGetOrderSuccess();
if (validateNodes) { if (validateNodes) {
try { try {
validateNodes(verticesOrder.flatMap((id) => id)); validateNodes(verticesOrder.flatMap((id) => id));
@ -69,48 +114,29 @@ export async function buildVertices({
return; return;
} }
} }
if (nodeId) {
for (let i = 0; i < verticesOrder.length; i += 1) {
const innerArray = verticesOrder[i];
const idIndex = innerArray.indexOf(nodeId);
if (idIndex !== -1) {
// If there's a nodeId, we want to run just that component and not the entire layer
// because a layer contains dependencies for the next layer
// and we are stopping at the layer that contains the nodeId
vertices_layers.push([innerArray[idIndex]]);
break; // Stop searching after finding the first occurrence
}
// If the targetId is not found, include the entire inner array
vertices_layers.push(innerArray);
}
} else {
vertices_layers = verticesOrder;
}
const verticesIds = vertices_layers.flat();
useFlowStore.getState().updateBuildStatus(verticesIds, BuildStatus.TO_BUILD); useFlowStore.getState().updateBuildStatus(verticesIds, BuildStatus.TO_BUILD);
useFlowStore.getState().updateVerticesBuild(verticesIds);
useFlowStore.getState().setIsBuilding(true); useFlowStore.getState().setIsBuilding(true);
// Set each vertex state to building // Set each vertex state to building
const buildResults: Array<boolean> = []; const buildResults: Array<boolean> = [];
for (const layer of vertices_layers) { for (const layer of verticesLayers) {
if (onBuildStart) onBuildStart(layer); if (onBuildStart) onBuildStart(layer);
for (const id of layer) { for (const id of layer) {
// Check if id is in the list of inactive nodes // Check if id is in the list of inactive nodes
if ( if (!verticesIds.includes(id) && onBuildUpdate) {
!useFlowStore.getState().verticesBuild.includes(id) &&
onBuildUpdate
) {
// If it is, skip building and set the state to inactive // If it is, skip building and set the state to inactive
onBuildUpdate(getInactiveVertexData(id), BuildStatus.INACTIVE,runId); onBuildUpdate(getInactiveVertexData(id), BuildStatus.INACTIVE, runId);
buildResults.push(false); buildResults.push(false);
continue; continue;
} }
await buildVertex({ await buildVertex({
flowId, flowId,
id, id,
onBuildUpdate:(data: VertexBuildTypeAPI, status: BuildStatus) => {if(onBuildUpdate) onBuildUpdate(data, status,runId)}, input_value,
onBuildUpdate: (data: VertexBuildTypeAPI, status: BuildStatus) => {
if (onBuildUpdate) onBuildUpdate(data, status, runId);
},
onBuildError, onBuildError,
verticesIds, verticesIds,
buildResults, buildResults,
@ -137,6 +163,7 @@ export async function buildVertices({
async function buildVertex({ async function buildVertex({
flowId, flowId,
id, id,
input_value,
onBuildUpdate, onBuildUpdate,
onBuildError, onBuildError,
verticesIds, verticesIds,
@ -145,6 +172,7 @@ async function buildVertex({
}: { }: {
flowId: string; flowId: string;
id: string; id: string;
input_value: string;
onBuildUpdate?: (data: any, status: BuildStatus) => void; onBuildUpdate?: (data: any, status: BuildStatus) => void;
onBuildError?: (title, list, idList: string[]) => void; onBuildError?: (title, list, idList: string[]) => void;
verticesIds: string[]; verticesIds: string[];
@ -152,7 +180,7 @@ async function buildVertex({
stopBuild: () => void; stopBuild: () => void;
}) { }) {
try { try {
const buildRes = await postBuildVertex(flowId, id); const buildRes = await postBuildVertex(flowId, id, input_value);
const buildData: VertexBuildTypeAPI = buildRes.data; const buildData: VertexBuildTypeAPI = buildRes.data;
if (onBuildUpdate) { if (onBuildUpdate) {
if (!buildData.valid) { if (!buildData.valid) {

View file

@ -219,7 +219,7 @@ export const nodeColors: { [char: string]: string } = {
wrappers: "#E6277A", wrappers: "#E6277A",
utilities: "#31A3CC", utilities: "#31A3CC",
output_parsers: "#E6A627", output_parsers: "#E6A627",
str: "#049524", str: "#31a3cc",
retrievers: "#e6b25a", retrievers: "#e6b25a",
unknown: "#9CA3AF", unknown: "#9CA3AF",
custom_components: "#ab11ab", custom_components: "#ab11ab",
@ -258,6 +258,7 @@ export const nodeIconsLucide: iconsType = {
Chroma: ChromaIcon, Chroma: ChromaIcon,
AirbyteJSONLoader: AirbyteIcon, AirbyteJSONLoader: AirbyteIcon,
AmazonBedrockEmbeddings: AWSIcon, AmazonBedrockEmbeddings: AWSIcon,
Amazon: AWSIcon,
Anthropic: AnthropicIcon, Anthropic: AnthropicIcon,
ChatAnthropic: AnthropicIcon, ChatAnthropic: AnthropicIcon,
BingSearchAPIWrapper: BingIcon, BingSearchAPIWrapper: BingIcon,
@ -270,13 +271,17 @@ export const nodeIconsLucide: iconsType = {
GoogleSearchAPIWrapper: GoogleIcon, GoogleSearchAPIWrapper: GoogleIcon,
GoogleSearchResults: GoogleIcon, GoogleSearchResults: GoogleIcon,
GoogleSearchRun: GoogleIcon, GoogleSearchRun: GoogleIcon,
Google: GoogleIcon,
HNLoader: HackerNewsIcon, HNLoader: HackerNewsIcon,
HuggingFaceHub: HuggingFaceIcon, HuggingFaceHub: HuggingFaceIcon,
HuggingFace: HuggingFaceIcon,
HuggingFaceEmbeddings: HuggingFaceIcon, HuggingFaceEmbeddings: HuggingFaceIcon,
IFixitLoader: IFixIcon, IFixitLoader: IFixIcon,
Meta: MetaIcon, Meta: MetaIcon,
Midjorney: MidjourneyIcon, Midjorney: MidjourneyIcon,
MongoDBAtlasVectorSearch: MongoDBIcon, MongoDBAtlasVectorSearch: MongoDBIcon,
MongoDB:MongoDBIcon,
MongoDBChatMessageHistory: MongoDBIcon,
NotionDirectoryLoader: NotionIcon, NotionDirectoryLoader: NotionIcon,
ChatOpenAI: OpenAiIcon, ChatOpenAI: OpenAiIcon,
AzureChatOpenAI: OpenAiIcon, AzureChatOpenAI: OpenAiIcon,
@ -289,6 +294,7 @@ export const nodeIconsLucide: iconsType = {
Searx: SearxIcon, Searx: SearxIcon,
SlackDirectoryLoader: SvgSlackIcon, SlackDirectoryLoader: SvgSlackIcon,
SupabaseVectorStore: SupabaseIcon, SupabaseVectorStore: SupabaseIcon,
Supabase: SupabaseIcon,
VertexAI: VertexAIIcon, VertexAI: VertexAIIcon,
ChatVertexAI: VertexAIIcon, ChatVertexAI: VertexAIIcon,
VertexAIEmbeddings: VertexAIIcon, VertexAIEmbeddings: VertexAIIcon,