Merge remote-tracking branch 'origin/cz/mergeAll' into fix/ui_adjustments
|
|
@ -86,6 +86,10 @@ def update_frontend_node_with_template_values(frontend_node, raw_frontend_node):
|
|||
|
||||
update_template_values(frontend_node["template"], raw_frontend_node["template"])
|
||||
|
||||
old_code = raw_frontend_node["template"]["code"]["value"]
|
||||
new_code = frontend_node["template"]["code"]["value"]
|
||||
frontend_node["edited"] = old_code != new_code
|
||||
|
||||
return frontend_node
|
||||
|
||||
|
||||
|
|
@ -204,16 +208,18 @@ def format_elapsed_time(elapsed_time: float) -> str:
|
|||
return f"{minutes} {minutes_unit}, {seconds} {seconds_unit}"
|
||||
|
||||
|
||||
async def build_and_cache_graph_from_db(
|
||||
flow_id: str,
|
||||
session: Session,
|
||||
chat_service: "ChatService",
|
||||
):
|
||||
async def build_and_cache_graph_from_db(flow_id: str, session: Session, chat_service: "ChatService"):
|
||||
"""Build and cache the graph."""
|
||||
flow: Optional[Flow] = session.get(Flow, flow_id)
|
||||
if not flow or not flow.data:
|
||||
raise ValueError("Invalid flow ID")
|
||||
graph = Graph.from_payload(flow.data, flow_id)
|
||||
for vertex_id in graph._has_session_id_vertices:
|
||||
vertex = graph.get_vertex(vertex_id)
|
||||
if vertex is None:
|
||||
raise ValueError(f"Vertex {vertex_id} not found")
|
||||
if not vertex._raw_params.get("session_id"):
|
||||
vertex.update_raw_params({"session_id": flow_id})
|
||||
await chat_service.set_cache(flow_id, graph)
|
||||
return graph
|
||||
|
||||
|
|
@ -317,3 +323,4 @@ def parse_exception(exc):
|
|||
if hasattr(exc, "body"):
|
||||
return exc.body["message"]
|
||||
return str(exc)
|
||||
return str(exc)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ from langflow.api.v1.schemas import (
|
|||
VertexBuildResponse,
|
||||
VerticesOrderResponse,
|
||||
)
|
||||
from langflow.schema.schema import Log
|
||||
from langflow.services.auth.utils import get_current_active_user
|
||||
from langflow.services.chat.service import ChatService
|
||||
from langflow.services.deps import get_chat_service, get_session, get_session_service
|
||||
|
|
@ -123,6 +124,7 @@ async def build_vertex(
|
|||
vertex_id: str,
|
||||
background_tasks: BackgroundTasks,
|
||||
inputs: Annotated[Optional[InputValueRequest], Body(embed=True)] = None,
|
||||
files: Optional[list[str]] = None,
|
||||
chat_service: "ChatService" = Depends(get_chat_service),
|
||||
current_user=Depends(get_current_active_user),
|
||||
):
|
||||
|
|
@ -159,6 +161,7 @@ async def build_vertex(
|
|||
else:
|
||||
graph = cache.get("result")
|
||||
vertex = graph.get_vertex(vertex_id)
|
||||
|
||||
try:
|
||||
lock = chat_service._cache_locks[flow_id_str]
|
||||
(
|
||||
|
|
@ -175,19 +178,25 @@ async def build_vertex(
|
|||
vertex_id=vertex_id,
|
||||
user_id=current_user.id,
|
||||
inputs_dict=inputs.model_dump() if inputs else {},
|
||||
files=files,
|
||||
)
|
||||
log_obj = Log(message=vertex.artifacts_raw, type=vertex.artifacts_type)
|
||||
result_data_response = ResultDataResponse(**result_dict.model_dump())
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception(f"Error building vertex: {exc}")
|
||||
params = format_exception_message(exc)
|
||||
valid = False
|
||||
log_obj = Log(message=params, type="error")
|
||||
result_data_response = ResultDataResponse(results={})
|
||||
artifacts = {}
|
||||
# If there's an error building the vertex
|
||||
# we need to clear the cache
|
||||
await chat_service.clear_cache(flow_id_str)
|
||||
|
||||
result_data_response.message = artifacts
|
||||
result_data_response.logs.append(log_obj)
|
||||
|
||||
# Log the vertex build
|
||||
if not vertex.will_stream:
|
||||
background_tasks.add_task(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import hashlib
|
|||
from http import HTTPStatus
|
||||
from io import BytesIO
|
||||
from uuid import UUID
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
|
@ -99,6 +100,47 @@ async def download_image(file_name: str, flow_id: UUID, storage_service: Storage
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/profile_pictures/{folder_name}/{file_name}")
|
||||
async def download_profile_picture(
|
||||
folder_name: str,
|
||||
file_name: str,
|
||||
storage_service: StorageService = Depends(get_storage_service),
|
||||
):
|
||||
try:
|
||||
extension = file_name.split(".")[-1]
|
||||
config_dir = get_storage_service().settings_service.settings.config_dir
|
||||
config_path = Path(config_dir)
|
||||
folder_path = config_path / 'profile_pictures' / folder_name
|
||||
content_type = build_content_type_from_extension(extension)
|
||||
file_content = await storage_service.get_file(flow_id=folder_path, file_name=file_name)
|
||||
return StreamingResponse(BytesIO(file_content), media_type=content_type)
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/profile_pictures/list")
|
||||
async def list_profile_pictures(storage_service: StorageService = Depends(get_storage_service)):
|
||||
try:
|
||||
config_dir = get_storage_service().settings_service.settings.config_dir
|
||||
config_path = Path(config_dir)
|
||||
|
||||
people_path = config_path / "profile_pictures/People"
|
||||
space_path = config_path / "profile_pictures/Space"
|
||||
|
||||
people = await storage_service.list_files(flow_id=people_path)
|
||||
space = await storage_service.list_files(flow_id=space_path)
|
||||
|
||||
files = [Path("People") / i for i in people]
|
||||
files += [Path("Space") / i for i in space]
|
||||
|
||||
return {"files": files}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
|
||||
@router.get("/list/{flow_id}")
|
||||
async def list_files(
|
||||
flow_id: UUID = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from loguru import logger
|
|||
from sqlmodel import Session, col, select
|
||||
|
||||
from langflow.api.utils import remove_api_keys, validate_is_component
|
||||
from langflow.api.v1.schemas import FlowListCreate, FlowListIds, FlowListRead
|
||||
from langflow.api.v1.schemas import FlowListCreate, FlowListRead
|
||||
from langflow.initial_setup.setup import STARTER_FOLDER_NAME
|
||||
from langflow.services.auth.utils import get_current_active_user
|
||||
from langflow.services.database.models.flow import Flow, FlowCreate, FlowRead, FlowUpdate
|
||||
|
|
@ -258,9 +258,9 @@ async def download_file(
|
|||
return FlowListRead(flows=flows)
|
||||
|
||||
|
||||
@router.post("/multiple_delete/")
|
||||
@router.delete("/")
|
||||
async def delete_multiple_flows(
|
||||
flow_ids: FlowListIds, user: User = Depends(get_current_active_user), db: Session = Depends(get_session)
|
||||
flow_ids: List[UUID], user: User = Depends(get_current_active_user), db: Session = Depends(get_session)
|
||||
):
|
||||
"""
|
||||
Delete multiple flows by their IDs.
|
||||
|
|
@ -274,9 +274,7 @@ async def delete_multiple_flows(
|
|||
|
||||
"""
|
||||
try:
|
||||
deleted_flows = db.exec(
|
||||
select(Flow).where(col(Flow.id).in_(flow_ids.flow_ids)).where(Flow.user_id == user.id)
|
||||
).all()
|
||||
deleted_flows = db.exec(select(Flow).where(col(Flow.id).in_(flow_ids)).where(Flow.user_id == user.id)).all()
|
||||
for flow in deleted_flows:
|
||||
db.delete(flow)
|
||||
db.commit()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from typing import List
|
||||
|
||||
from langflow.helpers.flow import generate_unique_flow_name
|
||||
from langflow.helpers.folders import generate_unique_folder_name
|
||||
import orjson
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, Response, UploadFile, status
|
||||
from sqlalchemy import or_, update
|
||||
|
|
@ -203,16 +205,9 @@ async def upload_file(
|
|||
if not data:
|
||||
raise HTTPException(status_code=400, detail="No flows found in the file")
|
||||
|
||||
folder_results = session.exec(
|
||||
select(Folder).where(
|
||||
Folder.name == data["folder_name"],
|
||||
Folder.user_id == current_user.id,
|
||||
)
|
||||
)
|
||||
existing_folder_names = [folder.name for folder in folder_results]
|
||||
folder_name = generate_unique_folder_name(data["folder_name"], current_user.id, session)
|
||||
|
||||
if existing_folder_names:
|
||||
data["folder_name"] = f"{data['folder_name']} ({len(existing_folder_names) + 1})"
|
||||
data["folder_name"] = folder_name
|
||||
|
||||
folder = FolderCreate(name=data["folder_name"], description=data["folder_description"])
|
||||
|
||||
|
|
@ -232,6 +227,8 @@ async def upload_file(
|
|||
raise HTTPException(status_code=400, detail="No flows found in the data")
|
||||
# Now we set the user_id for all flows
|
||||
for flow in flow_list.flows:
|
||||
flow_name = generate_unique_flow_name(flow.name, current_user.id, session)
|
||||
flow.name = flow_name
|
||||
flow.user_id = current_user.id
|
||||
flow.folder_id = new_folder.id
|
||||
|
||||
|
|
|
|||
|
|
@ -71,9 +71,7 @@ async def login_to_get_access_token(
|
|||
|
||||
@router.get("/auto_login")
|
||||
async def auto_login(
|
||||
response: Response,
|
||||
db: Session = Depends(get_session),
|
||||
settings_service=Depends(get_settings_service)
|
||||
response: Response, db: Session = Depends(get_session), settings_service=Depends(get_settings_service)
|
||||
):
|
||||
auth_settings = settings_service.auth_settings
|
||||
if settings_service.auth_settings.AUTO_LOGIN:
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query
|
|||
|
||||
from langflow.services.deps import get_monitor_service
|
||||
from langflow.services.monitor.schema import (
|
||||
MessageModelRequest,
|
||||
MessageModelResponse,
|
||||
TransactionModelResponse,
|
||||
VertexBuildMapModel,
|
||||
|
|
@ -66,6 +67,44 @@ async def get_messages(
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/messages", status_code=204)
|
||||
async def delete_messages(
|
||||
message_ids: List[int],
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
):
|
||||
try:
|
||||
monitor_service.delete_messages(message_ids=message_ids)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/messages/{message_id}", response_model=MessageModelResponse)
|
||||
async def update_message(
|
||||
message_id: int,
|
||||
message: MessageModelRequest,
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
):
|
||||
try:
|
||||
message_dict = message.model_dump(exclude_none=True)
|
||||
message_dict.pop("index", None)
|
||||
monitor_service.update_message(message_id=message_id, **message_dict)
|
||||
return MessageModelResponse(index=message_id, **message_dict)
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/messages/session/{session_id}", status_code=204)
|
||||
async def delete_messages_session(
|
||||
session_id: str,
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
):
|
||||
try:
|
||||
monitor_service.delete_messages_session(session_id=session_id)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/transactions", response_model=List[TransactionModelResponse])
|
||||
async def get_transactions(
|
||||
source: Optional[str] = Query(None),
|
||||
|
|
@ -79,6 +118,22 @@ async def get_transactions(
|
|||
dicts = monitor_service.get_transactions(
|
||||
source=source, target=target, status=status, order_by=order_by, flow_id=flow_id
|
||||
)
|
||||
return [TransactionModelResponse(**d) for d in dicts]
|
||||
result = []
|
||||
for d in dicts:
|
||||
d = TransactionModelResponse(
|
||||
index=d["index"],
|
||||
timestamp=d["timestamp"],
|
||||
vertex_id=d["vertex_id"],
|
||||
inputs=d["inputs"],
|
||||
outputs=d["outputs"],
|
||||
status=d["status"],
|
||||
error=d["error"],
|
||||
flow_id=d["flow_id"],
|
||||
source=d["vertex_id"],
|
||||
target=d["target_id"],
|
||||
)
|
||||
result.append(d)
|
||||
return result
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_serial
|
|||
from langflow.graph.schema import RunOutputs
|
||||
from langflow.schema import dotdict
|
||||
from langflow.schema.graph import Tweaks
|
||||
from langflow.schema.schema import InputType, OutputType
|
||||
from langflow.schema.schema import InputType, Log, OutputType
|
||||
from langflow.services.database.models.api_key.model import ApiKeyRead
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
from langflow.services.database.models.flow import FlowCreate, FlowRead
|
||||
|
|
@ -245,6 +245,8 @@ class VerticesOrderResponse(BaseModel):
|
|||
|
||||
class ResultDataResponse(BaseModel):
|
||||
results: Optional[Any] = Field(default_factory=dict)
|
||||
logs: List[Log | None] = Field(default_factory=list)
|
||||
message: Optional[Any] = Field(default_factory=dict)
|
||||
artifacts: Optional[Any] = Field(default_factory=dict)
|
||||
timedelta: Optional[float] = None
|
||||
duration: Optional[str] = None
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from langchain_core.runnables import Runnable
|
|||
from langflow.base.agents.utils import get_agents_list, records_to_messages
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Text, Tool
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class LCAgentComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from langchain_core.prompts import BasePromptTemplate, ChatPromptTemplate
|
|||
from langchain_core.tools import BaseTool
|
||||
from pydantic import BaseModel
|
||||
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
from .default_prompts import XML_AGENT_PROMPT
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ Constants:
|
|||
- FIELD_FORMAT_ATTRIBUTES: A list of attributes used for formatting fields.
|
||||
"""
|
||||
|
||||
import orjson
|
||||
|
||||
STREAM_INFO_TEXT = "Stream the response from the model. Streaming works only in Chat."
|
||||
|
||||
NODE_FORMAT_ATTRIBUTES = ["beta", "icon", "display_name", "description"]
|
||||
NODE_FORMAT_ATTRIBUTES = ["beta", "icon", "display_name", "description", "output_types"]
|
||||
|
||||
|
||||
FIELD_FORMAT_ATTRIBUTES = [
|
||||
|
|
@ -27,3 +29,5 @@ FIELD_FORMAT_ATTRIBUTES = [
|
|||
"refresh_button_text",
|
||||
"options",
|
||||
]
|
||||
|
||||
ORJSON_OPTIONS = orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS | orjson.OPT_OMIT_MICROSECONDS
|
||||
|
|
|
|||
|
|
@ -15,10 +15,25 @@ import shlex
|
|||
from collections import OrderedDict, namedtuple
|
||||
from http.cookies import SimpleCookie
|
||||
|
||||
from uncurl.api import parser # type: ignore
|
||||
|
||||
parser.add_argument("-x", "--proxy", default={})
|
||||
parser.add_argument("-U", "--proxy-user", default="")
|
||||
ParsedArgs = namedtuple(
|
||||
"ParsedArgs",
|
||||
[
|
||||
"command",
|
||||
"url",
|
||||
"data",
|
||||
"data_binary",
|
||||
"method",
|
||||
"headers",
|
||||
"compressed",
|
||||
"insecure",
|
||||
"user",
|
||||
"include",
|
||||
"silent",
|
||||
"proxy",
|
||||
"proxy_user",
|
||||
"cookies",
|
||||
],
|
||||
)
|
||||
|
||||
ParsedContext = namedtuple("ParsedContext", ["method", "url", "data", "headers", "cookies", "verify", "auth", "proxy"])
|
||||
|
||||
|
|
@ -27,24 +42,90 @@ def normalize_newlines(multiline_text):
|
|||
return multiline_text.replace(" \\\n", " ")
|
||||
|
||||
|
||||
def parse_curl_command(curl_command):
|
||||
tokens = shlex.split(normalize_newlines(curl_command))
|
||||
tokens = [token for token in tokens if token and token != " "]
|
||||
if "curl" not in tokens[0]:
|
||||
raise ValueError("Invalid curl command")
|
||||
args_template = {
|
||||
"command": None,
|
||||
"url": None,
|
||||
"data": None,
|
||||
"data_binary": None,
|
||||
"method": "get",
|
||||
"headers": [],
|
||||
"compressed": False,
|
||||
"insecure": False,
|
||||
"user": (),
|
||||
"include": False,
|
||||
"silent": False,
|
||||
"proxy": None,
|
||||
"proxy_user": None,
|
||||
"cookies": {},
|
||||
}
|
||||
args = args_template.copy()
|
||||
method_on_curl = None
|
||||
i = 0
|
||||
while i < len(tokens):
|
||||
token = tokens[i]
|
||||
if token == "-X":
|
||||
i += 1
|
||||
args["method"] = tokens[i].lower()
|
||||
method_on_curl = tokens[i].lower()
|
||||
elif token in ("-d", "--data"):
|
||||
i += 1
|
||||
args["data"] = tokens[i]
|
||||
elif token in ("-b", "--data-binary", "--data-raw"):
|
||||
i += 1
|
||||
args["data_binary"] = tokens[i]
|
||||
elif token in ("-H", "--header"):
|
||||
i += 1
|
||||
args["headers"].append(tokens[i])
|
||||
elif token == "--compressed":
|
||||
args["compressed"] = True
|
||||
elif token in ("-k", "--insecure"):
|
||||
args["insecure"] = True
|
||||
elif token in ("-u", "--user"):
|
||||
i += 1
|
||||
args["user"] = tuple(tokens[i].split(":"))
|
||||
elif token in ("-I", "--include"):
|
||||
args["include"] = True
|
||||
elif token in ("-s", "--silent"):
|
||||
args["silent"] = True
|
||||
elif token in ("-x", "--proxy"):
|
||||
i += 1
|
||||
args["proxy"] = tokens[i]
|
||||
elif token in ("-U", "--proxy-user"):
|
||||
i += 1
|
||||
args["proxy_user"] = tokens[i]
|
||||
elif not token.startswith("-"):
|
||||
if args["command"] is None:
|
||||
args["command"] = token
|
||||
else:
|
||||
args["url"] = token
|
||||
i += 1
|
||||
|
||||
args["method"] = method_on_curl or args["method"]
|
||||
|
||||
return ParsedArgs(**args)
|
||||
|
||||
|
||||
def parse_context(curl_command):
|
||||
method = "get"
|
||||
|
||||
tokens = shlex.split(normalize_newlines(curl_command))
|
||||
tokens = [token for token in tokens if token and token != " "]
|
||||
parsed_args = parser.parse_args(tokens)
|
||||
parsed_args: ParsedArgs = parse_curl_command(curl_command)
|
||||
|
||||
post_data = parsed_args.data or parsed_args.data_binary
|
||||
if post_data:
|
||||
method = "post"
|
||||
|
||||
if parsed_args.X:
|
||||
method = parsed_args.X.lower()
|
||||
if parsed_args.method:
|
||||
method = parsed_args.method.lower()
|
||||
|
||||
cookie_dict = OrderedDict()
|
||||
quoted_headers = OrderedDict()
|
||||
|
||||
for curl_header in parsed_args.header:
|
||||
for curl_header in parsed_args.headers:
|
||||
if curl_header.startswith(":"):
|
||||
occurrence = [m.start() for m in re.finditer(":", curl_header)]
|
||||
header_key, header_value = curl_header[: occurrence[1]], curl_header[occurrence[1] + 1 :]
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import json
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as ET
|
||||
from concurrent import futures
|
||||
from pathlib import Path
|
||||
from typing import Callable, List, Optional, Text
|
||||
|
||||
import chardet
|
||||
import orjson
|
||||
import yaml
|
||||
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
# Types of files that can be read simply by file.read()
|
||||
# and have 100% to be completely readable
|
||||
|
|
@ -31,6 +33,17 @@ TEXT_FILE_TYPES = [
|
|||
"tsx",
|
||||
]
|
||||
|
||||
IMG_FILE_TYPES = [
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"png",
|
||||
"bmp",
|
||||
]
|
||||
|
||||
|
||||
def normalize_text(text):
|
||||
return unicodedata.normalize("NFKD", text)
|
||||
|
||||
|
||||
def is_hidden(path: Path) -> bool:
|
||||
return path.name.startswith(".")
|
||||
|
|
@ -92,7 +105,10 @@ def read_text_file(file_path: str) -> str:
|
|||
with open(file_path, "rb") as f:
|
||||
raw_data = f.read()
|
||||
result = chardet.detect(raw_data)
|
||||
encoding = result['encoding']
|
||||
encoding = result["encoding"]
|
||||
|
||||
if encoding in ["Windows-1252", "Windows-1254"]:
|
||||
encoding = "utf-8"
|
||||
|
||||
with open(file_path, "r", encoding=encoding) as f:
|
||||
return f.read()
|
||||
|
|
@ -121,9 +137,15 @@ def parse_text_file_to_record(file_path: str, silent_errors: bool) -> Optional[R
|
|||
text = read_docx_file(file_path)
|
||||
else:
|
||||
text = read_text_file(file_path)
|
||||
|
||||
# if file is json, yaml, or xml, we can parse it
|
||||
if file_path.endswith(".json"):
|
||||
text = json.loads(text)
|
||||
text = orjson.loads(text)
|
||||
if isinstance(text, dict):
|
||||
text = {k: normalize_text(v) if isinstance(v, str) else v for k, v in text.items()}
|
||||
elif isinstance(text, list):
|
||||
text = [normalize_text(item) if isinstance(item, str) else item for item in text]
|
||||
|
||||
elif file_path.endswith(".yaml") or file_path.endswith(".yml"):
|
||||
text = yaml.safe_load(text)
|
||||
elif file_path.endswith(".xml"):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import List
|
||||
|
||||
from langflow.graph.schema import ResultData, RunOutputs
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
def build_records_from_run_outputs(run_outputs: RunOutputs) -> List[Record]:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
from typing import Optional, Union
|
||||
|
||||
from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
from langflow.memory import store_message
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class ChatComponent(CustomComponent):
|
||||
|
|
@ -15,7 +15,7 @@ class ChatComponent(CustomComponent):
|
|||
return {
|
||||
"input_value": {
|
||||
"input_types": ["Text"],
|
||||
"display_name": "Message",
|
||||
"display_name": "Text",
|
||||
"multiline": True,
|
||||
},
|
||||
"sender": {
|
||||
|
|
@ -40,98 +40,45 @@ class ChatComponent(CustomComponent):
|
|||
"info": "In case of Message being a Record, this template will be used to convert it to text.",
|
||||
"advanced": True,
|
||||
},
|
||||
"files": {
|
||||
"field_type": "file",
|
||||
"display_name": "Files",
|
||||
"file_types": TEXT_FILE_TYPES + IMG_FILE_TYPES,
|
||||
"info": "Files to be sent with the message.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def store_message(
|
||||
self,
|
||||
message: Union[str, Text, Record],
|
||||
session_id: Optional[str] = None,
|
||||
sender: Optional[str] = None,
|
||||
sender_name: Optional[str] = None,
|
||||
) -> list[Record]:
|
||||
records = store_message(
|
||||
message: Message,
|
||||
) -> list[Message]:
|
||||
messages = store_message(
|
||||
message,
|
||||
session_id=session_id,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
flow_id=self.graph.flow_id,
|
||||
)
|
||||
|
||||
self.status = records
|
||||
return records
|
||||
self.status = messages
|
||||
return messages
|
||||
|
||||
def build_with_record(
|
||||
self,
|
||||
sender: Optional[str] = "User",
|
||||
sender_name: Optional[str] = "User",
|
||||
input_value: Optional[Union[str, Record]] = None,
|
||||
input_value: Optional[Union[str, Record, Message]] = None,
|
||||
files: Optional[list[str]] = None,
|
||||
session_id: Optional[str] = None,
|
||||
return_record: Optional[bool] = False,
|
||||
record_template: str = "Text: {text}\nData: {data}",
|
||||
) -> Union[Text, Record]:
|
||||
input_value_record: Optional[Record] = None
|
||||
if return_record:
|
||||
if isinstance(input_value, Record):
|
||||
# Update the data of the record
|
||||
input_value.data["sender"] = sender
|
||||
input_value.data["sender_name"] = sender_name
|
||||
input_value.data["session_id"] = session_id
|
||||
else:
|
||||
input_value_record = Record(
|
||||
text=input_value,
|
||||
data={
|
||||
"sender": sender,
|
||||
"sender_name": sender_name,
|
||||
"session_id": session_id,
|
||||
},
|
||||
)
|
||||
elif isinstance(input_value, Record):
|
||||
input_value = records_to_text(template=record_template, records=input_value)
|
||||
if not input_value:
|
||||
input_value = ""
|
||||
if return_record and input_value_record:
|
||||
result: Union[Text, Record] = input_value_record
|
||||
else:
|
||||
result = input_value
|
||||
self.status = result
|
||||
if session_id and isinstance(result, (Record, str)):
|
||||
self.store_message(result, session_id, sender, sender_name)
|
||||
return result
|
||||
) -> Message:
|
||||
message: Message | None = None
|
||||
|
||||
def build_no_record(
|
||||
self,
|
||||
sender: Optional[str] = "User",
|
||||
sender_name: Optional[str] = "User",
|
||||
input_value: Optional[str] = None,
|
||||
session_id: Optional[str] = None,
|
||||
return_record: Optional[bool] = False,
|
||||
record_template: str = "Text: {text}\nData: {data}",
|
||||
) -> Union[Text, Record]:
|
||||
input_value_record: Optional[Record] = None
|
||||
if return_record:
|
||||
if isinstance(input_value, Record):
|
||||
# Update the data of the record
|
||||
input_value.data["sender"] = sender
|
||||
input_value.data["sender_name"] = sender_name
|
||||
input_value.data["session_id"] = session_id
|
||||
else:
|
||||
input_value_record = Record(
|
||||
text=input_value,
|
||||
data={
|
||||
"sender": sender,
|
||||
"sender_name": sender_name,
|
||||
"session_id": session_id,
|
||||
},
|
||||
)
|
||||
elif isinstance(input_value, Record):
|
||||
input_value = records_to_text(template=record_template, records=input_value)
|
||||
if not input_value:
|
||||
input_value = ""
|
||||
if return_record and input_value_record:
|
||||
result: Union[Text, Record] = input_value_record
|
||||
if isinstance(input_value, Record):
|
||||
# Update the data of the record
|
||||
message = Message.from_record(input_value)
|
||||
else:
|
||||
result = input_value
|
||||
self.status = result
|
||||
if session_id and isinstance(result, (Record, str)):
|
||||
self.store_message(result, session_id, sender, sender_name)
|
||||
return result
|
||||
message = Message(
|
||||
text=input_value, sender=sender, sender_name=sender_name, files=files, session_id=session_id
|
||||
)
|
||||
self.status = message
|
||||
if session_id and isinstance(message, Message):
|
||||
self.store_message(message)
|
||||
return message
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class TextComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class BaseMemoryComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import warnings
|
||||
from typing import Optional, Union
|
||||
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
|
|
@ -5,6 +6,7 @@ from langchain_core.language_models.llms import LLM
|
|||
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing.prompt import Prompt
|
||||
|
||||
|
||||
class LCModelComponent(CustomComponent):
|
||||
|
|
@ -53,19 +55,28 @@ class LCModelComponent(CustomComponent):
|
|||
key in response_metadata["token_usage"] for key in inner_openai_keys
|
||||
):
|
||||
token_usage = response_metadata["token_usage"]
|
||||
completion_tokens = token_usage["completion_tokens"]
|
||||
prompt_tokens = token_usage["prompt_tokens"]
|
||||
total_tokens = token_usage["total_tokens"]
|
||||
finish_reason = response_metadata["finish_reason"]
|
||||
status_message = f"Tokens:\nInput: {prompt_tokens}\nOutput: {completion_tokens}\nTotal Tokens: {total_tokens}\nStop Reason: {finish_reason}\nResponse: {content}"
|
||||
status_message = {
|
||||
"tokens": {
|
||||
"input": token_usage["prompt_tokens"],
|
||||
"output": token_usage["completion_tokens"],
|
||||
"total": token_usage["total_tokens"],
|
||||
"stop_reason": response_metadata["finish_reason"],
|
||||
"response": content,
|
||||
}
|
||||
}
|
||||
|
||||
elif all(key in response_metadata for key in anthropic_keys) and all(
|
||||
key in response_metadata["usage"] for key in inner_anthropic_keys
|
||||
):
|
||||
usage = response_metadata["usage"]
|
||||
input_tokens = usage["input_tokens"]
|
||||
output_tokens = usage["output_tokens"]
|
||||
stop_reason = response_metadata["stop_reason"]
|
||||
status_message = f"Tokens:\nInput: {input_tokens}\nOutput: {output_tokens}\nStop Reason: {stop_reason}\nResponse: {content}"
|
||||
status_message = {
|
||||
"tokens": {
|
||||
"input": usage["input_tokens"],
|
||||
"output": usage["output_tokens"],
|
||||
"stop_reason": response_metadata["stop_reason"],
|
||||
"response": content,
|
||||
}
|
||||
}
|
||||
else:
|
||||
status_message = f"Response: {content}"
|
||||
else:
|
||||
|
|
@ -73,7 +84,7 @@ class LCModelComponent(CustomComponent):
|
|||
return status_message
|
||||
|
||||
def get_chat_result(
|
||||
self, runnable: BaseChatModel, stream: bool, input_value: str, system_message: Optional[str] = None
|
||||
self, runnable: BaseChatModel, stream: bool, input_value: str | Prompt, system_message: Optional[str] = None
|
||||
):
|
||||
messages: list[Union[HumanMessage, SystemMessage]] = []
|
||||
if not input_value and not system_message:
|
||||
|
|
@ -81,11 +92,21 @@ class LCModelComponent(CustomComponent):
|
|||
if system_message:
|
||||
messages.append(SystemMessage(content=system_message))
|
||||
if input_value:
|
||||
messages.append(HumanMessage(content=input_value))
|
||||
if isinstance(input_value, Prompt):
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
if "prompt" in input_value:
|
||||
prompt = input_value.load_lc_prompt()
|
||||
runnable = prompt | runnable
|
||||
else:
|
||||
messages.append(input_value.to_lc_message())
|
||||
else:
|
||||
messages.append(HumanMessage(content=input_value))
|
||||
inputs = messages or {}
|
||||
if stream:
|
||||
return runnable.stream(messages)
|
||||
return runnable.stream(inputs)
|
||||
else:
|
||||
message = runnable.invoke(messages)
|
||||
message = runnable.invoke(inputs)
|
||||
result = message.content
|
||||
if isinstance(message, AIMessage):
|
||||
status_message = self.build_status_message(message)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from copy import deepcopy
|
||||
|
||||
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
def record_to_string(record: Record) -> str:
|
||||
|
|
@ -35,10 +35,14 @@ def dict_values_to_string(d: dict) -> dict:
|
|||
# it could be a list of records or documents or strings
|
||||
if isinstance(value, list):
|
||||
for i, item in enumerate(value):
|
||||
if isinstance(item, Record):
|
||||
if isinstance(item, Message):
|
||||
d_copy[key][i] = item.text
|
||||
elif isinstance(item, Record):
|
||||
d_copy[key][i] = record_to_string(item)
|
||||
elif isinstance(item, Document):
|
||||
d_copy[key][i] = document_to_string(item)
|
||||
elif isinstance(value, Message):
|
||||
d_copy[key] = value.text
|
||||
elif isinstance(value, Record):
|
||||
d_copy[key] = record_to_string(value)
|
||||
elif isinstance(value, Document):
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from langchain_core.prompts import ChatPromptTemplate
|
|||
|
||||
from langflow.base.agents.agent import LCAgentComponent
|
||||
from langflow.field_typing import BaseLanguageModel, Text, Tool
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class ToolCallingAgentComponent(LCAgentComponent):
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ from typing import List, Optional
|
|||
from langchain.agents import create_xml_agent
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
|
||||
|
||||
from langflow.base.agents.agent import LCAgentComponent
|
||||
from langflow.field_typing import BaseLanguageModel, Text, Tool
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class XMLAgentComponent(LCAgentComponent):
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from langchain_core.documents import Document
|
|||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel, BaseMemory, BaseRetriever, Text
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class RetrievalQAComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import uuid
|
|||
from typing import Any, Optional
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.dotdict import dotdict
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class WebhookComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ from langchain_core.prompts.chat import HumanMessagePromptTemplate, SystemMessag
|
|||
from langflow.base.agents.agent import LCAgentComponent
|
||||
from langflow.base.agents.utils import AGENTS, AgentSpec, get_agents_list
|
||||
from langflow.field_typing import BaseLanguageModel, Text, Tool
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.dotdict import dotdict
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class AgentComponent(LCAgentComponent):
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ from langflow.custom import CustomComponent
|
|||
from langflow.field_typing import Tool
|
||||
from langflow.graph.graph.base import Graph
|
||||
from langflow.helpers.flow import get_flow_inputs
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.dotdict import dotdict
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class FlowToolComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from typing import List, Optional
|
|||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.memory import get_messages, store_message
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class StoreMessageComponent(CustomComponent):
|
||||
|
|
@ -31,12 +31,11 @@ class StoreMessageComponent(CustomComponent):
|
|||
sender_name: Optional[str] = None,
|
||||
session_id: Optional[str] = None,
|
||||
message: str = "",
|
||||
) -> List[Record]:
|
||||
) -> List[Message]:
|
||||
store_message(
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
session_id=session_id,
|
||||
message=message,
|
||||
message=Message(
|
||||
text=message, sender=sender, sender_name=sender_name, flow_id=self.graph.flow_id, session_id=session_id
|
||||
)
|
||||
)
|
||||
|
||||
self.status = get_messages(session_id=session_id)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ from typing import Optional
|
|||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
from langflow.helpers.record import messages_to_text
|
||||
from langflow.memory import get_messages
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class MemoryComponent(BaseMemoryComponent):
|
||||
|
|
@ -43,7 +43,7 @@ class MemoryComponent(BaseMemoryComponent):
|
|||
},
|
||||
}
|
||||
|
||||
def get_messages(self, **kwargs) -> list[Record]:
|
||||
def get_messages(self, **kwargs) -> list[Message]:
|
||||
# Validate kwargs by checking if it contains the correct keys
|
||||
if "sender" not in kwargs:
|
||||
kwargs["sender"] = None
|
||||
|
|
@ -77,6 +77,6 @@ class MemoryComponent(BaseMemoryComponent):
|
|||
limit=n_messages,
|
||||
order=order,
|
||||
)
|
||||
messages_str = records_to_text(template=record_template or "", records=messages)
|
||||
messages_str = messages_to_text(template=record_template or "", messages=messages)
|
||||
self.status = messages_str
|
||||
return messages_str
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
from typing import Optional, Union
|
||||
from typing import Optional
|
||||
|
||||
from langflow.base.io.chat import ChatComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class ChatInput(ChatComponent):
|
||||
|
|
@ -14,7 +13,7 @@ class ChatInput(ChatComponent):
|
|||
build_config = super().build_config()
|
||||
build_config["input_value"] = {
|
||||
"input_types": [],
|
||||
"display_name": "Message",
|
||||
"display_name": "Text",
|
||||
"multiline": True,
|
||||
}
|
||||
|
||||
|
|
@ -25,13 +24,13 @@ class ChatInput(ChatComponent):
|
|||
sender: Optional[str] = "User",
|
||||
sender_name: Optional[str] = "User",
|
||||
input_value: Optional[str] = None,
|
||||
files: Optional[list[str]] = None,
|
||||
session_id: Optional[str] = None,
|
||||
return_record: Optional[bool] = False,
|
||||
) -> Union[Text, Record]:
|
||||
return super().build_no_record(
|
||||
) -> Message:
|
||||
return super().build_with_record(
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
input_value=input_value,
|
||||
files=files,
|
||||
session_id=session_id,
|
||||
return_record=return_record,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Prompt, TemplateField, Text
|
||||
from langflow.field_typing import TemplateField
|
||||
from langflow.field_typing.prompt import Prompt
|
||||
|
||||
|
||||
class PromptComponent(CustomComponent):
|
||||
|
|
@ -15,19 +14,11 @@ class PromptComponent(CustomComponent):
|
|||
"code": TemplateField(advanced=True),
|
||||
}
|
||||
|
||||
def build(
|
||||
async def build(
|
||||
self,
|
||||
template: Prompt,
|
||||
**kwargs,
|
||||
) -> Text:
|
||||
from langflow.base.prompts.utils import dict_values_to_string
|
||||
|
||||
prompt_template = PromptTemplate.from_template(Text(template))
|
||||
kwargs = dict_values_to_string(kwargs)
|
||||
kwargs = {k: "\n".join(v) if isinstance(v, list) else v for k, v in kwargs.items()}
|
||||
try:
|
||||
formated_prompt = prompt_template.format(**kwargs)
|
||||
except Exception as exc:
|
||||
raise ValueError(f"Error formatting prompt: {exc}") from exc
|
||||
self.status = f'Prompt:\n"{formated_prompt}"'
|
||||
return formated_prompt
|
||||
) -> Prompt:
|
||||
prompt = await Prompt.from_template_and_variables(template, kwargs)
|
||||
self.status = prompt.format_text()
|
||||
return prompt
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class TextInput(TextComponent):
|
|||
def build_config(self):
|
||||
return {
|
||||
"input_value": {
|
||||
"display_name": "Value",
|
||||
"display_name": "Text",
|
||||
"input_types": ["Record", "Text"],
|
||||
"info": "Text or Record to be passed as input.",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional
|
|||
from langchain_community.utilities.searchapi import SearchApiAPIWrapper
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langchain_astradb.chat_message_histories import AstraDBChatMessageHistory
|
|||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class AstraDBMessageReaderComponent(BaseMemoryComponent):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_astradb import AstraDBChatMessageHistory
|
||||
from langchain_core.messages import BaseMessage
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langchain_astradb import AstraDBChatMessageHistory
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class AstraDBMessageWriterComponent(BaseMemoryComponent):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langchain_community.chat_message_histories.zep import SearchScope, SearchTy
|
|||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class ZepMessageReaderComponent(BaseMemoryComponent):
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Optional
|
|||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from zep_python.langchain import ZepChatMessageHistory
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class AmazonBedrockComponent(LCModelComponent):
|
|||
"advanced": True,
|
||||
},
|
||||
"cache": {"display_name": "Cache"},
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"system_message": {
|
||||
"display_name": "System Message",
|
||||
"info": "System message to pass to the model.",
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class AnthropicLLM(LCModelComponent):
|
|||
"info": "Endpoint of the Anthropic API. Defaults to 'https://api.anthropic.com' if not specified.",
|
||||
},
|
||||
"code": {"show": False},
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"advanced": True,
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ class AzureChatOpenAIComponent(LCModelComponent):
|
|||
"info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
|
||||
},
|
||||
"code": {"show": False},
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"info": STREAM_INFO_TEXT,
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class QianfanChatEndpointComponent(LCModelComponent):
|
|||
"info": "Endpoint of the Qianfan LLM, required if custom model used.",
|
||||
},
|
||||
"code": {"show": False},
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"info": STREAM_INFO_TEXT,
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ class ChatLiteLLMModelComponent(LCModelComponent):
|
|||
"required": False,
|
||||
"default": False,
|
||||
},
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"info": STREAM_INFO_TEXT,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_cohere import ChatCohere
|
||||
from pydantic.v1 import SecretStr
|
||||
from langflow.field_typing import Text
|
||||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langchain_cohere import ChatCohere
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class CohereComponent(LCModelComponent):
|
||||
|
|
@ -42,7 +43,7 @@ class CohereComponent(LCModelComponent):
|
|||
"type": "float",
|
||||
"show": True,
|
||||
},
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"info": STREAM_INFO_TEXT,
|
||||
|
|
@ -69,3 +70,4 @@ class CohereComponent(LCModelComponent):
|
|||
temperature=temperature,
|
||||
)
|
||||
return self.get_chat_result(output, stream, input_value, system_message)
|
||||
return self.get_chat_result(output, stream, input_value, system_message)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ from typing import Optional
|
|||
|
||||
from langchain_community.chat_models.huggingface import ChatHuggingFace
|
||||
from langchain_community.llms.huggingface_endpoint import HuggingFaceEndpoint
|
||||
from langflow.field_typing import Text
|
||||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class HuggingFaceEndpointsComponent(LCModelComponent):
|
||||
|
|
@ -36,7 +37,7 @@ class HuggingFaceEndpointsComponent(LCModelComponent):
|
|||
"advanced": True,
|
||||
},
|
||||
"code": {"show": False},
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"info": STREAM_INFO_TEXT,
|
||||
|
|
@ -72,3 +73,4 @@ class HuggingFaceEndpointsComponent(LCModelComponent):
|
|||
raise ValueError("Could not connect to HuggingFace Endpoints API.") from e
|
||||
output = ChatHuggingFace(llm=llm)
|
||||
return self.get_chat_result(output, stream, input_value, system_message)
|
||||
return self.get_chat_result(output, stream, input_value, system_message)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class MistralAIModelComponent(LCModelComponent):
|
|||
|
||||
def build_config(self):
|
||||
return {
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"max_tokens": {
|
||||
"display_name": "Max Tokens",
|
||||
"advanced": True,
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ class ChatOllamaComponent(LCModelComponent):
|
|||
"info": "Template to use for generating text.",
|
||||
"advanced": True,
|
||||
},
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"info": STREAM_INFO_TEXT,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class OpenAIModelComponent(LCModelComponent):
|
|||
|
||||
def build_config(self):
|
||||
return {
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"max_tokens": {
|
||||
"display_name": "Max Tokens",
|
||||
"advanced": True,
|
||||
|
|
@ -79,7 +79,7 @@ class OpenAIModelComponent(LCModelComponent):
|
|||
input_value: Text,
|
||||
openai_api_key: str,
|
||||
temperature: float = 0.1,
|
||||
model_name: str = "gpt-4o",
|
||||
model_name: str = "gpt-3.5-turbo",
|
||||
max_tokens: Optional[int] = 256,
|
||||
model_kwargs: NestedDict = {},
|
||||
openai_api_base: Optional[str] = None,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
from typing import Optional
|
||||
|
||||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Text
|
||||
|
|
@ -74,7 +73,7 @@ class ChatVertexAIComponent(LCModelComponent):
|
|||
"value": False,
|
||||
"advanced": True,
|
||||
},
|
||||
"input_value": {"display_name": "Input"},
|
||||
"input_value": {"display_name": "Input", "input_types": ["Text", "Record", "Prompt"]},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"info": STREAM_INFO_TEXT,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
from typing import Optional, Union
|
||||
from typing import Optional
|
||||
|
||||
from langflow.base.io.chat import ChatComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class ChatOutput(ChatComponent):
|
||||
|
|
@ -16,14 +15,12 @@ class ChatOutput(ChatComponent):
|
|||
sender_name: Optional[str] = "AI",
|
||||
input_value: Optional[str] = None,
|
||||
session_id: Optional[str] = None,
|
||||
return_record: Optional[bool] = False,
|
||||
record_template: Optional[str] = "{text}",
|
||||
) -> Union[Text, Record]:
|
||||
files: Optional[list[str]] = None,
|
||||
) -> Message:
|
||||
return super().build_with_record(
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
input_value=input_value,
|
||||
session_id=session_id,
|
||||
return_record=return_record,
|
||||
record_template=record_template or "",
|
||||
files=files,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,18 @@ from langflow.custom import CustomComponent
|
|||
from langflow.schema import Record
|
||||
|
||||
|
||||
class RecordsOutput(CustomComponent):
|
||||
class RecordOutput(CustomComponent):
|
||||
display_name = "Records Output"
|
||||
description = "Display Records as a Table"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"input_value": {
|
||||
"display_name": "Records",
|
||||
"input_types": ["Record"],
|
||||
"info": "Record or Record list to be passed as input.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(self, input_value: Record) -> Record:
|
||||
return input_value
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class TextOutput(TextComponent):
|
|||
def build_config(self):
|
||||
return {
|
||||
"input_value": {
|
||||
"display_name": "Value",
|
||||
"display_name": "Text",
|
||||
"input_types": ["Record", "Text"],
|
||||
"info": "Text or Record to be passed as output.",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import List
|
|||
from langchain_text_splitters import CharacterTextSplitter
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
from langflow.utils.util import unescape_string
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import List, Optional
|
|||
from langchain_text_splitters import Language, RecursiveCharacterTextSplitter
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class LanguageRecursiveTextSplitterComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional
|
|||
from langchain_community.utilities.searchapi import SearchApiAPIWrapper
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.Redis import RedisComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Record
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
|
||||
class RedisSearchComponent(RedisComponent, LCVectorStoreComponent):
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.Weaviate import WeaviateVectorStoreComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Record
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
|
||||
class WeaviateSearchVectorStore(WeaviateVectorStoreComponent, LCVectorStoreComponent):
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
from .AstraDBSearch import AstraDBSearchComponent
|
||||
from .ChromaSearch import ChromaSearchComponent
|
||||
from .FAISSSearch import FAISSSearchComponent
|
||||
from .MongoDBAtlasVectorSearch import MongoDBAtlasSearchComponent
|
||||
from .PineconeSearch import PineconeSearchComponent
|
||||
from .QdrantSearch import QdrantSearchComponent
|
||||
from .RedisSearch import RedisSearchComponent
|
||||
from .SupabaseVectorStoreSearch import SupabaseSearchComponent
|
||||
from .VectaraSearch import VectaraSearchComponent
|
||||
from .WeaviateSearch import WeaviateSearchVectorStore
|
||||
from .pgvectorSearch import PGVectorSearchComponent
|
||||
from .Couchbase import CouchbaseSearchComponent # type: ignore
|
||||
|
||||
__all__ = [
|
||||
"AstraDBSearchComponent",
|
||||
"ChromaSearchComponent",
|
||||
"CouchbaseSearchComponent",
|
||||
"FAISSSearchComponent",
|
||||
"MongoDBAtlasSearchComponent",
|
||||
"PineconeSearchComponent",
|
||||
"QdrantSearchComponent",
|
||||
"RedisSearchComponent",
|
||||
"SupabaseSearchComponent",
|
||||
"VectaraSearchComponent",
|
||||
"WeaviateSearchVectorStore",
|
||||
"PGVectorSearchComponent",
|
||||
]
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
from typing import List
|
||||
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.pgvector import PGVectorComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Record
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
|
||||
class PGVectorSearchComponent(PGVectorComponent, LCVectorStoreComponent):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
from langchain_astradb import AstraDBVectorStore
|
||||
from langchain_astradb.utils.astradb import SetupMode
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings, VectorStore
|
||||
from langflow.schema import Record
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
|
||||
|
||||
class AstraDBVectorStoreComponent(CustomComponent):
|
||||
|
|
@ -156,3 +157,4 @@ class AstraDBVectorStoreComponent(CustomComponent):
|
|||
)
|
||||
|
||||
return vector_store
|
||||
return vector_store
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from langchain_core.retrievers import BaseRetriever
|
|||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class ChromaComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
from langchain_community.vectorstores import CouchbaseVectorStore
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings, VectorStore
|
||||
from langflow.schema import Record
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from couchbase.auth import PasswordAuthenticator # type: ignore
|
||||
from couchbase.cluster import Cluster # type: ignore
|
||||
from couchbase.options import ClusterOptions # type: ignore
|
||||
from langchain_community.vectorstores import CouchbaseVectorStore
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings, VectorStore
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class CouchbaseComponent(CustomComponent):
|
||||
display_name = "Couchbase"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langchain_core.vectorstores import VectorStore
|
|||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class FAISSComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langchain_community.vectorstores.mongodb_atlas import MongoDBAtlasVectorSea
|
|||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class MongoDBAtlasComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from langchain_pinecone.vectorstores import PineconeVectorStore
|
|||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class PineconeComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langchain_core.vectorstores import VectorStore
|
|||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class QdrantComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langchain_core.retrievers import BaseRetriever
|
|||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class RedisComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from supabase.client import Client, create_client
|
|||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class SupabaseComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langchain_core.retrievers import BaseRetriever
|
|||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class UpstashVectorStoreComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from langchain_core.vectorstores import VectorStore
|
|||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseRetriever
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class VectaraComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from langchain_core.retrievers import BaseRetriever
|
|||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class WeaviateVectorStoreComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
from .AstraDB import AstraDBVectorStoreComponent
|
||||
from .Chroma import ChromaComponent
|
||||
from .FAISS import FAISSComponent
|
||||
from .MongoDBAtlasVector import MongoDBAtlasComponent
|
||||
from .Pinecone import PineconeComponent
|
||||
from .Qdrant import QdrantComponent
|
||||
from .Redis import RedisComponent
|
||||
from .SupabaseVectorStore import SupabaseComponent
|
||||
from .Vectara import VectaraComponent
|
||||
from .Weaviate import WeaviateVectorStoreComponent
|
||||
from .pgvector import PGVectorComponent
|
||||
from .Couchbase import CouchbaseComponent
|
||||
|
||||
__all__ = [
|
||||
"AstraDBVectorStoreComponent",
|
||||
"ChromaComponent",
|
||||
"CouchbaseComponent",
|
||||
"FAISSComponent",
|
||||
"MongoDBAtlasComponent",
|
||||
"PineconeComponent",
|
||||
"QdrantComponent",
|
||||
"RedisComponent",
|
||||
"SupabaseComponent",
|
||||
"VectaraComponent",
|
||||
"WeaviateVectorStoreComponent",
|
||||
"base",
|
||||
"PGVectorComponent",
|
||||
]
|
||||
|
|
@ -6,7 +6,7 @@ from langchain_core.retrievers import BaseRetriever
|
|||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class PGVectorComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ class CodeParser:
|
|||
bases = self.execute_and_inspect_classes(self.code)
|
||||
except Exception as e:
|
||||
# If the code cannot be executed, return an empty list
|
||||
logger.exception(e)
|
||||
logger.debug(e)
|
||||
bases = []
|
||||
raise e
|
||||
return bases
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import yaml
|
|||
from cachetools import TTLCache, cachedmethod
|
||||
from langchain_core.documents import Document
|
||||
from pydantic import BaseModel
|
||||
|
||||
from langflow.custom.code_parser.utils import (
|
||||
extract_inner_type_from_generic_alias,
|
||||
extract_union_types_from_generic_alias,
|
||||
|
|
@ -378,13 +379,14 @@ class CustomComponent(Component):
|
|||
The variable for the current user with the specified name.
|
||||
"""
|
||||
|
||||
def get_variable(name: str):
|
||||
def get_variable(name: str, field: str):
|
||||
if hasattr(self, "_user_id") and not self._user_id:
|
||||
raise ValueError(f"User id is not set for {self.__class__.__name__}")
|
||||
variable_service = get_variable_service() # Get service instance
|
||||
# Retrieve and decrypt the variable by name for the current user
|
||||
with session_scope() as session:
|
||||
return variable_service.get_variable(user_id=self._user_id or "", name=name, session=session)
|
||||
user_id = self._user_id or ""
|
||||
return variable_service.get_variable(user_id=user_id, name=name, field=field, session=session)
|
||||
|
||||
return get_variable
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,8 @@ class DirectoryReader:
|
|||
component_tuple = (*build_component(component), component)
|
||||
components.append(component_tuple)
|
||||
except Exception as e:
|
||||
logger.error(f"Error while loading component { component['name']}: {e}")
|
||||
logger.debug(f"Error while loading component { component['name']}")
|
||||
logger.debug(e)
|
||||
continue
|
||||
items.append({"name": menu["name"], "path": menu["path"], "components": components})
|
||||
filtered = [menu for menu in items if menu["components"]]
|
||||
|
|
@ -266,8 +267,7 @@ class DirectoryReader:
|
|||
if validation_result:
|
||||
try:
|
||||
output_types = self.get_output_types_from_code(result_content)
|
||||
except Exception as exc:
|
||||
logger.exception(f"Error while getting output types from code: {str(exc)}")
|
||||
except Exception:
|
||||
output_types = [component_name_camelcase]
|
||||
else:
|
||||
output_types = [component_name_camelcase]
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ from .constants import (
|
|||
Embeddings,
|
||||
NestedDict,
|
||||
Object,
|
||||
Prompt,
|
||||
PromptTemplate,
|
||||
Text,
|
||||
TextSplitter,
|
||||
Tool,
|
||||
VectorStore,
|
||||
)
|
||||
from .prompt import Prompt
|
||||
from .range_spec import RangeSpec
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langchain.memory.chat_memory import BaseChatMemory
|
|||
from langchain_core.document_loaders import BaseLoader
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.language_models import BaseLLM, BaseLanguageModel
|
||||
from langchain_core.language_models import BaseLanguageModel, BaseLLM
|
||||
from langchain_core.memory import BaseMemory
|
||||
from langchain_core.output_parsers import BaseOutputParser
|
||||
from langchain_core.prompts import BasePromptTemplate, ChatPromptTemplate, PromptTemplate
|
||||
|
|
@ -15,6 +15,8 @@ from langchain_core.tools import Tool
|
|||
from langchain_core.vectorstores import VectorStore
|
||||
from langchain_text_splitters import TextSplitter
|
||||
|
||||
from langflow.field_typing.prompt import Prompt
|
||||
|
||||
# Type alias for more complex dicts
|
||||
NestedDict = Dict[str, Union[str, Dict]]
|
||||
|
||||
|
|
@ -27,10 +29,6 @@ class Data:
|
|||
pass
|
||||
|
||||
|
||||
class Prompt:
|
||||
pass
|
||||
|
||||
|
||||
class Code:
|
||||
pass
|
||||
|
||||
|
|
|
|||
41
src/backend/base/langflow/field_typing/prompt.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from langchain_core.load import load
|
||||
from langchain_core.messages import HumanMessage
|
||||
from langchain_core.prompts import BaseChatPromptTemplate, ChatPromptTemplate, PromptTemplate
|
||||
|
||||
from langflow.base.prompts.utils import dict_values_to_string
|
||||
from langflow.schema.message import Message
|
||||
from langflow.schema.record import Record
|
||||
|
||||
|
||||
class Prompt(Record):
|
||||
def load_lc_prompt(self):
|
||||
if "prompt" not in self:
|
||||
raise ValueError("Prompt is required.")
|
||||
return load(self.prompt)
|
||||
|
||||
@classmethod
|
||||
def from_lc_prompt(
|
||||
cls,
|
||||
prompt: BaseChatPromptTemplate,
|
||||
):
|
||||
prompt_json = prompt.to_json()
|
||||
return cls(prompt=prompt_json)
|
||||
|
||||
def format_text(self):
|
||||
prompt_template = PromptTemplate.from_template(self.template)
|
||||
variables_with_str_values = dict_values_to_string(self.variables)
|
||||
formatted_prompt = prompt_template.format(**variables_with_str_values)
|
||||
return formatted_prompt
|
||||
|
||||
@classmethod
|
||||
async def from_template_and_variables(cls, template: str, variables: dict):
|
||||
instance = cls(template=template, variables=variables)
|
||||
contents = [{"type": "text", "text": instance.format_text()}]
|
||||
# Get all Message instances from the kwargs
|
||||
for value in variables.values():
|
||||
if isinstance(value, Message):
|
||||
content_dicts = await value.get_file_content_dicts()
|
||||
contents.extend(content_dicts)
|
||||
prompt_template = ChatPromptTemplate.from_messages([HumanMessage(content=contents)])
|
||||
instance.prompt = prompt_template.to_json()
|
||||
return instance
|
||||
|
|
@ -710,6 +710,7 @@ class Graph:
|
|||
chat_service: ChatService,
|
||||
vertex_id: str,
|
||||
inputs_dict: Optional[Dict[str, str]] = None,
|
||||
files: Optional[list[str]] = None,
|
||||
user_id: Optional[str] = None,
|
||||
fallback_to_env_vars: bool = False,
|
||||
):
|
||||
|
|
@ -737,7 +738,9 @@ class Graph:
|
|||
# Check the cache for the vertex
|
||||
cached_result = await chat_service.get_cache(key=vertex.id)
|
||||
if isinstance(cached_result, CacheMiss):
|
||||
await vertex.build(user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars)
|
||||
await vertex.build(
|
||||
user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars, files=files
|
||||
)
|
||||
await chat_service.set_cache(key=vertex.id, data=vertex)
|
||||
else:
|
||||
cached_vertex = cached_result["result"]
|
||||
|
|
@ -751,7 +754,9 @@ class Graph:
|
|||
vertex.result.used_frozen_result = True
|
||||
|
||||
else:
|
||||
await vertex.build(user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars)
|
||||
await vertex.build(
|
||||
user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars, files=files
|
||||
)
|
||||
|
||||
if vertex.result is not None:
|
||||
params = f"{vertex._built_object_repr()}{params}"
|
||||
|
|
@ -764,11 +769,13 @@ class Graph:
|
|||
next_runnable_vertices, top_level_vertices = await self.get_next_and_top_level_vertices(
|
||||
lock, set_cache_coro, vertex
|
||||
)
|
||||
log_transaction(vertex, status="success")
|
||||
flow_id = self.flow_id
|
||||
log_transaction(flow_id, vertex, status="success")
|
||||
return next_runnable_vertices, top_level_vertices, result_dict, params, valid, artifacts, vertex
|
||||
except Exception as exc:
|
||||
logger.exception(f"Error building vertex: {exc}")
|
||||
log_transaction(vertex, status="failure", error=str(exc))
|
||||
flow_id = self.flow_id
|
||||
log_transaction(flow_id, vertex, status="failure", error=str(exc))
|
||||
raise exc
|
||||
|
||||
async def get_next_and_top_level_vertices(
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
from enum import Enum
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from pydantic import BaseModel, Field, field_serializer
|
||||
from pydantic import BaseModel, Field, field_serializer, model_validator
|
||||
|
||||
from langflow.graph.utils import serialize_field
|
||||
from langflow.schema.schema import Log, StreamURL
|
||||
from langflow.utils.schemas import ChatOutputResponse, ContainsEnumMeta
|
||||
|
||||
|
||||
class ResultData(BaseModel):
|
||||
results: Optional[Any] = Field(default_factory=dict)
|
||||
artifacts: Optional[Any] = Field(default_factory=dict)
|
||||
logs: Optional[List[dict]] = Field(default_factory=list)
|
||||
messages: Optional[list[ChatOutputResponse]] = Field(default_factory=list)
|
||||
timedelta: Optional[float] = None
|
||||
duration: Optional[str] = None
|
||||
|
|
@ -23,6 +25,24 @@ class ResultData(BaseModel):
|
|||
return {key: serialize_field(val) for key, val in value.items()}
|
||||
return serialize_field(value)
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def validate_model(cls, values):
|
||||
if not values.get("logs") and values.get("artifacts"):
|
||||
# Build the log from the artifacts
|
||||
message = values["artifacts"]
|
||||
|
||||
# ! Temporary fix
|
||||
if not isinstance(message, dict):
|
||||
message = {"message": message}
|
||||
|
||||
if "stream_url" in message and "type" in message:
|
||||
stream_url = StreamURL(location=message["stream_url"])
|
||||
values["logs"] = [Log(message=stream_url, type=message["type"])]
|
||||
elif "type" in message:
|
||||
values["logs"] = [Log(message=message, type=message["type"])]
|
||||
return values
|
||||
|
||||
|
||||
class InterfaceComponentTypes(str, Enum, metaclass=ContainsEnumMeta):
|
||||
# ChatInput and ChatOutput are the only ones that are
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
from typing import Any, Union
|
||||
from enum import Enum
|
||||
from typing import Any, Generator, Union
|
||||
|
||||
from langchain_core.documents import Document
|
||||
from pydantic import BaseModel
|
||||
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class UnbuiltObject:
|
||||
|
|
@ -14,6 +17,16 @@ class UnbuiltResult:
|
|||
pass
|
||||
|
||||
|
||||
class ArtifactType(str, Enum):
|
||||
TEXT = "text"
|
||||
RECORD = "record"
|
||||
OBJECT = "object"
|
||||
ARRAY = "array"
|
||||
STREAM = "stream"
|
||||
UNKNOWN = "unknown"
|
||||
MESSAGE = "message"
|
||||
|
||||
|
||||
def validate_prompt(prompt: str):
|
||||
"""Validate prompt."""
|
||||
if extract_input_variables_from_prompt(prompt):
|
||||
|
|
@ -50,3 +63,38 @@ def serialize_field(value):
|
|||
elif isinstance(value, str):
|
||||
return {"result": value}
|
||||
return value
|
||||
|
||||
|
||||
def get_artifact_type(custom_component, build_result) -> str:
|
||||
result = ArtifactType.UNKNOWN
|
||||
value = custom_component.repr_value
|
||||
match value:
|
||||
case Record():
|
||||
result = ArtifactType.RECORD
|
||||
|
||||
case str():
|
||||
result = ArtifactType.TEXT
|
||||
|
||||
case dict():
|
||||
result = ArtifactType.OBJECT
|
||||
|
||||
case list():
|
||||
result = ArtifactType.ARRAY
|
||||
|
||||
case Message():
|
||||
result = ArtifactType.MESSAGE
|
||||
|
||||
if result == ArtifactType.UNKNOWN:
|
||||
if isinstance(build_result, Generator):
|
||||
result = ArtifactType.STREAM
|
||||
elif isinstance(value, Message) and isinstance(value.text, Generator):
|
||||
result = ArtifactType.STREAM
|
||||
|
||||
return result.value
|
||||
|
||||
|
||||
def post_process_raw(raw, artifact_type: str):
|
||||
if artifact_type == ArtifactType.STREAM.value:
|
||||
raw = ""
|
||||
|
||||
return raw
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ import inspect
|
|||
import os
|
||||
import types
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING, Any, AsyncIterator, Callable, Dict, Iterator, List, Optional
|
||||
from typing import TYPE_CHECKING, Any, AsyncIterator, Callable, Dict, Iterator, List, Mapping, Optional
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from langflow.graph.schema import INPUT_COMPONENTS, OUTPUT_COMPONENTS, InterfaceComponentTypes, ResultData
|
||||
from langflow.graph.utils import UnbuiltObject, UnbuiltResult
|
||||
from langflow.graph.vertex.utils import log_transaction
|
||||
from langflow.graph.utils import ArtifactType, UnbuiltObject, UnbuiltResult
|
||||
from langflow.interface.initialize import loading
|
||||
from langflow.interface.listing import lazy_load_dict
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME
|
||||
from langflow.services.deps import get_storage_service
|
||||
from langflow.services.monitor.utils import log_transaction
|
||||
from langflow.utils.constants import DIRECT_TYPES
|
||||
from langflow.utils.schemas import ChatOutputResponse
|
||||
from langflow.utils.util import sync_to_async, unescape_string
|
||||
|
|
@ -63,6 +63,8 @@ class Vertex:
|
|||
self._built_result = None
|
||||
self._built = False
|
||||
self.artifacts: Dict[str, Any] = {}
|
||||
self.artifacts_raw: Any = None
|
||||
self.artifacts_type: Optional[str] = None
|
||||
self.steps: List[Callable] = [self._build]
|
||||
self.steps_ran: List[Callable] = []
|
||||
self.task_id: Optional[str] = None
|
||||
|
|
@ -371,7 +373,7 @@ class Vertex:
|
|||
self.load_from_db_fields = load_from_db_fields
|
||||
self._raw_params = params.copy()
|
||||
|
||||
def update_raw_params(self, new_params: Dict[str, str], overwrite: bool = False):
|
||||
def update_raw_params(self, new_params: Mapping[str, str | list[str]], overwrite: bool = False):
|
||||
"""
|
||||
Update the raw parameters of the vertex with the given new parameters.
|
||||
|
||||
|
|
@ -422,11 +424,14 @@ class Vertex:
|
|||
try:
|
||||
messages = [
|
||||
ChatOutputResponse(
|
||||
message=artifacts["message"],
|
||||
message=artifacts["text"],
|
||||
sender=artifacts.get("sender"),
|
||||
sender_name=artifacts.get("sender_name"),
|
||||
session_id=artifacts.get("session_id"),
|
||||
stream_url=artifacts.get("stream_url"),
|
||||
files=[{"path": file} if isinstance(file, str) else file for file in artifacts.get("files", [])],
|
||||
component_id=self.id,
|
||||
type=self.artifacts_type,
|
||||
).model_dump(exclude_none=True)
|
||||
]
|
||||
except KeyError:
|
||||
|
|
@ -439,12 +444,11 @@ class Vertex:
|
|||
# We need to set the artifacts to pass information
|
||||
# to the frontend
|
||||
self.set_artifacts()
|
||||
artifacts = self.artifacts
|
||||
artifacts = self.artifacts_raw
|
||||
if isinstance(artifacts, dict):
|
||||
messages = self.extract_messages_from_artifacts(artifacts)
|
||||
else:
|
||||
messages = []
|
||||
|
||||
result_dict = ResultData(
|
||||
results=result_dict,
|
||||
artifacts=artifacts,
|
||||
|
|
@ -525,12 +529,13 @@ class Vertex:
|
|||
Returns:
|
||||
The built result if use_result is True, else the built object.
|
||||
"""
|
||||
flow_id = self.graph.flow_id
|
||||
if not self._built:
|
||||
log_transaction(source=self, target=requester, flow_id=self.graph.flow_id, status="error")
|
||||
log_transaction(flow_id, vertex=self, target=requester, status="error")
|
||||
raise ValueError(f"Component {self.display_name} has not been built yet")
|
||||
|
||||
result = self._built_result if self.use_result else self._built_object
|
||||
log_transaction(source=self, target=requester, flow_id=self.graph.flow_id, status="success")
|
||||
log_transaction(flow_id, vertex=self, target=requester, status="success")
|
||||
return result
|
||||
|
||||
async def _build_vertex_and_update_params(self, key, vertex: "Vertex"):
|
||||
|
|
@ -624,6 +629,8 @@ class Vertex:
|
|||
self._built_object, self.artifacts = result
|
||||
elif len(result) == 3:
|
||||
self._custom_component, self._built_object, self.artifacts = result
|
||||
self.artifacts_raw = self.artifacts.get("raw", None)
|
||||
self.artifacts_type = self.artifacts.get("type", None) or ArtifactType.UNKNOWN.value
|
||||
else:
|
||||
self._built_object = result
|
||||
|
||||
|
|
@ -664,6 +671,7 @@ class Vertex:
|
|||
self,
|
||||
user_id=None,
|
||||
inputs: Optional[Dict[str, Any]] = None,
|
||||
files: Optional[list[str]] = None,
|
||||
requester: Optional["Vertex"] = None,
|
||||
**kwargs,
|
||||
) -> Any:
|
||||
|
|
@ -681,9 +689,14 @@ class Vertex:
|
|||
return await self.get_requester_result(requester)
|
||||
self._reset()
|
||||
|
||||
if self._is_chat_input() and inputs:
|
||||
inputs = {"input_value": inputs.get(INPUT_FIELD_NAME, "")}
|
||||
self.update_raw_params(inputs, overwrite=True)
|
||||
if self._is_chat_input() and (inputs or files):
|
||||
chat_input = {}
|
||||
if inputs:
|
||||
chat_input.update({"input_value": inputs.get(INPUT_FIELD_NAME, "")})
|
||||
if files:
|
||||
chat_input.update({"files": files})
|
||||
|
||||
self.update_raw_params(chat_input, overwrite=True)
|
||||
|
||||
# Run steps
|
||||
for step in self.steps:
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@ import json
|
|||
from typing import AsyncIterator, Dict, Iterator, List
|
||||
|
||||
import yaml
|
||||
from langchain_core.messages import AIMessage
|
||||
from langchain_core.messages import AIMessage, AIMessageChunk
|
||||
from loguru import logger
|
||||
|
||||
from langflow.graph.schema import CHAT_COMPONENTS, RECORDS_COMPONENTS, InterfaceComponentTypes
|
||||
from langflow.graph.utils import UnbuiltObject, serialize_field
|
||||
from langflow.graph.utils import ArtifactType, UnbuiltObject, serialize_field
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.message import Message
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME
|
||||
from langflow.services.monitor.utils import log_vertex_build
|
||||
from langflow.utils.schemas import ChatOutputResponse, RecordOutputResponse
|
||||
|
|
@ -83,10 +84,11 @@ class InterfaceVertex(Vertex):
|
|||
sender = self.params.get("sender", None)
|
||||
sender_name = self.params.get("sender_name", None)
|
||||
message = self.params.get(INPUT_FIELD_NAME, None)
|
||||
files = [{"path": file} if isinstance(file, str) else file for file in self.params.get("files", [])]
|
||||
if isinstance(message, str):
|
||||
message = unescape_string(message)
|
||||
stream_url = None
|
||||
if isinstance(self._built_object, AIMessage):
|
||||
if isinstance(self._built_object, (AIMessage, AIMessageChunk)):
|
||||
artifacts = ChatOutputResponse.from_message(
|
||||
self._built_object,
|
||||
sender=sender,
|
||||
|
|
@ -97,23 +99,27 @@ class InterfaceVertex(Vertex):
|
|||
# Turn the dict into a pleasing to
|
||||
# read JSON inside a code block
|
||||
message = dict_to_codeblock(self._built_object)
|
||||
elif isinstance(self._built_object, Record):
|
||||
message = self._built_object.text
|
||||
elif isinstance(message, (AsyncIterator, Iterator)):
|
||||
stream_url = self.build_stream_url()
|
||||
message = ""
|
||||
elif isinstance(self._built_object, Message):
|
||||
if isinstance(message, (AsyncIterator, Iterator)):
|
||||
stream_url = self.build_stream_url()
|
||||
message = ""
|
||||
self._built_object.text = message
|
||||
else:
|
||||
message = self._built_object.text
|
||||
elif not isinstance(self._built_object, str):
|
||||
message = str(self._built_object)
|
||||
# if the message is a generator or iterator
|
||||
# it means that it is a stream of messages
|
||||
else:
|
||||
message = self._built_object
|
||||
|
||||
artifact_type = ArtifactType.STREAM if stream_url is not None else ArtifactType.OBJECT
|
||||
artifacts = ChatOutputResponse(
|
||||
message=message,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
stream_url=stream_url,
|
||||
files=files,
|
||||
type=artifact_type,
|
||||
)
|
||||
|
||||
self.will_stream = stream_url is not None
|
||||
|
|
@ -195,6 +201,8 @@ class InterfaceVertex(Vertex):
|
|||
message=complete_message,
|
||||
sender=self.params.get("sender", ""),
|
||||
sender_name=self.params.get("sender_name", ""),
|
||||
files=[{"path": file} if isinstance(file, str) else file for file in self.params.get("files", [])],
|
||||
type=ArtifactType.OBJECT.value,
|
||||
).model_dump()
|
||||
self.params[INPUT_FIELD_NAME] = complete_message
|
||||
self._built_object = Record(text=complete_message, data=self.artifacts)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from langflow.services.deps import get_monitor_service
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
|
||||
|
|
@ -21,34 +17,3 @@ def build_clean_params(target: "Vertex") -> dict:
|
|||
if isinstance(value, list):
|
||||
params[key] = [item for item in value if isinstance(item, (str, int, bool, float, list, dict))]
|
||||
return params
|
||||
|
||||
|
||||
def log_transaction(source: "Vertex", target: "Vertex", flow_id, status, error=None):
|
||||
"""
|
||||
Logs a transaction between two vertices.
|
||||
|
||||
Args:
|
||||
source (Vertex): The source vertex of the transaction.
|
||||
target (Vertex): The target vertex of the transaction.
|
||||
status: The status of the transaction.
|
||||
error (Optional): Any error associated with the transaction.
|
||||
|
||||
Raises:
|
||||
Exception: If there is an error while logging the transaction.
|
||||
|
||||
"""
|
||||
try:
|
||||
monitor_service = get_monitor_service()
|
||||
clean_params = build_clean_params(target)
|
||||
data = {
|
||||
"source": source.vertex_type,
|
||||
"target": target.vertex_type,
|
||||
"target_args": clean_params,
|
||||
"timestamp": monitor_service.get_timestamp(),
|
||||
"status": status,
|
||||
"error": error,
|
||||
"flow_id": flow_id,
|
||||
}
|
||||
monitor_service.add_row(table_name="transactions", data=data)
|
||||
except Exception as e:
|
||||
logger.error(f"Error logging transaction: {e}")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
from .record import docs_to_records, records_to_text
|
||||
from .record import docs_to_records, records_to_text, messages_to_text
|
||||
|
||||
__all__ = ["docs_to_records", "records_to_text"]
|
||||
__all__ = ["docs_to_records", "records_to_text", "messages_to_text"]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ from pydantic.v1 import BaseModel, Field, create_model
|
|||
from sqlmodel import Session, select
|
||||
|
||||
from langflow.graph.schema import RunOutputs
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME, Record
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME
|
||||
from langflow.services.database.models.flow import Flow
|
||||
from langflow.services.deps import get_session, get_settings_service, session_scope
|
||||
|
||||
|
|
@ -90,7 +91,9 @@ async def run_flow(
|
|||
|
||||
fallback_to_env_vars = get_settings_service().settings.fallback_to_env_var
|
||||
|
||||
return await graph.arun(inputs_list, inputs_components=inputs_components, types=types, fallback_to_env_vars=fallback_to_env_vars)
|
||||
return await graph.arun(
|
||||
inputs_list, inputs_components=inputs_components, types=types, fallback_to_env_vars=fallback_to_env_vars
|
||||
)
|
||||
|
||||
|
||||
def generate_function_for_flow(
|
||||
|
|
@ -249,7 +252,7 @@ def get_flow_by_id_or_endpoint_name(
|
|||
flow = db.get(Flow, flow_id)
|
||||
except ValueError:
|
||||
endpoint_name = flow_id_or_name
|
||||
stmt = select(Flow).where(Flow.name == endpoint_name)
|
||||
stmt = select(Flow).where(Flow.endpoint_name == endpoint_name)
|
||||
if user_id:
|
||||
stmt = stmt.where(Flow.user_id == user_id)
|
||||
flow = db.exec(stmt).first()
|
||||
|
|
@ -257,3 +260,24 @@ def get_flow_by_id_or_endpoint_name(
|
|||
raise HTTPException(status_code=404, detail=f"Flow identifier {flow_id_or_name} not found")
|
||||
|
||||
return flow
|
||||
|
||||
|
||||
def generate_unique_flow_name(flow_name, user_id, session):
|
||||
original_name = flow_name
|
||||
n = 1
|
||||
while True:
|
||||
# Check if a flow with the given name exists
|
||||
existing_flow = session.exec(
|
||||
select(Flow).where(
|
||||
Flow.name == flow_name,
|
||||
Flow.user_id == user_id,
|
||||
)
|
||||
).first()
|
||||
|
||||
# If no flow with the given name exists, return the name
|
||||
if not existing_flow:
|
||||
return flow_name
|
||||
|
||||
# If a flow with the name already exists, append (n) to the name and increment n
|
||||
flow_name = f"{original_name} ({n})"
|
||||
n += 1
|
||||
|
|
|
|||
23
src/backend/base/langflow/helpers/folders.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
from langflow.services.database.models.folder.model import Folder
|
||||
from sqlalchemy import select
|
||||
|
||||
|
||||
def generate_unique_folder_name(folder_name, user_id, session):
|
||||
original_name = folder_name
|
||||
n = 1
|
||||
while True:
|
||||
# Check if a folder with the given name exists
|
||||
existing_folder = session.exec(
|
||||
select(Folder).where(
|
||||
Folder.name == folder_name,
|
||||
Folder.user_id == user_id,
|
||||
)
|
||||
).first()
|
||||
|
||||
# If no folder with the given name exists, return the name
|
||||
if not existing_folder:
|
||||
return folder_name
|
||||
|
||||
# If a folder with the name already exists, append (n) to the name and increment n
|
||||
folder_name = f"{original_name} ({n})"
|
||||
n += 1
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
from typing import Union
|
||||
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
def docs_to_records(documents: list[Document]) -> list[Record]:
|
||||
|
|
@ -27,7 +29,7 @@ def records_to_text(template: str, records: Union[Record, list[Record]]) -> str:
|
|||
Returns:
|
||||
list[str]: The converted list of texts.
|
||||
"""
|
||||
if isinstance(records, Record):
|
||||
if isinstance(records, (Record)):
|
||||
records = [records]
|
||||
# Check if there are any format strings in the template
|
||||
_records = []
|
||||
|
|
@ -39,3 +41,27 @@ def records_to_text(template: str, records: Union[Record, list[Record]]) -> str:
|
|||
|
||||
formated_records = [template.format(data=record.data, **record.data) for record in _records]
|
||||
return "\n".join(formated_records)
|
||||
|
||||
|
||||
def messages_to_text(template: str, messages: Union[Message, list[Message]]) -> str:
|
||||
"""
|
||||
Converts a list of Messages to a list of texts.
|
||||
|
||||
Args:
|
||||
messages (list[Message]): The list of Messages to convert.
|
||||
|
||||
Returns:
|
||||
list[str]: The converted list of texts.
|
||||
"""
|
||||
if isinstance(messages, (Message)):
|
||||
messages = [messages]
|
||||
# Check if there are any format strings in the template
|
||||
_messages = []
|
||||
for message in messages:
|
||||
# If it is not a message, create one with the key "text"
|
||||
if not isinstance(message, Message):
|
||||
raise ValueError("All elements in the list must be of type Message.")
|
||||
_messages.append(message)
|
||||
|
||||
formated_messages = [template.format(data=message.model_dump(), **message.model_dump()) for message in _messages]
|
||||
return "\n".join(formated_messages)
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 30 KiB |