merge fix
This commit is contained in:
commit
357029865f
72 changed files with 2282 additions and 2558 deletions
|
|
@ -2,25 +2,25 @@ import platform
|
|||
import socket
|
||||
import sys
|
||||
import time
|
||||
import webbrowser
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import click
|
||||
import httpx
|
||||
import typer
|
||||
from dotenv import load_dotenv
|
||||
from multiprocess import Process, cpu_count # type: ignore
|
||||
from rich import box
|
||||
from rich import print as rprint
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.table import Table
|
||||
|
||||
from langflow.main import setup_app
|
||||
from langflow.services.database.utils import session_getter
|
||||
from langflow.services.deps import get_db_service, get_settings_service
|
||||
from langflow.services.utils import initialize_services, initialize_settings_service
|
||||
from langflow.utils.logger import configure, logger
|
||||
from multiprocess import Process, cpu_count # type: ignore
|
||||
from packaging import version as pkg_version
|
||||
from rich import box
|
||||
from rich import print as rprint
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
|
|
@ -99,8 +99,12 @@ def update_settings(
|
|||
|
||||
@app.command()
|
||||
def run(
|
||||
host: str = typer.Option("127.0.0.1", help="Host to bind the server to.", envvar="LANGFLOW_HOST"),
|
||||
workers: int = typer.Option(1, help="Number of worker processes.", envvar="LANGFLOW_WORKERS"),
|
||||
host: str = typer.Option(
|
||||
"127.0.0.1", help="Host to bind the server to.", envvar="LANGFLOW_HOST"
|
||||
),
|
||||
workers: int = typer.Option(
|
||||
1, help="Number of worker processes.", envvar="LANGFLOW_WORKERS"
|
||||
),
|
||||
timeout: int = typer.Option(300, help="Worker timeout in seconds."),
|
||||
port: int = typer.Option(7860, help="Port to listen on.", envvar="LANGFLOW_PORT"),
|
||||
components_path: Optional[Path] = typer.Option(
|
||||
|
|
@ -108,11 +112,19 @@ def run(
|
|||
help="Path to the directory containing custom components.",
|
||||
envvar="LANGFLOW_COMPONENTS_PATH",
|
||||
),
|
||||
config: str = typer.Option(Path(__file__).parent / "config.yaml", help="Path to the configuration file."),
|
||||
config: str = typer.Option(
|
||||
Path(__file__).parent / "config.yaml", help="Path to the configuration file."
|
||||
),
|
||||
# .env file param
|
||||
env_file: Path = typer.Option(None, help="Path to the .env file containing environment variables."),
|
||||
log_level: str = typer.Option("critical", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"),
|
||||
log_file: Path = typer.Option("logs/langflow.log", help="Path to the log file.", envvar="LANGFLOW_LOG_FILE"),
|
||||
env_file: Path = typer.Option(
|
||||
None, help="Path to the .env file containing environment variables."
|
||||
),
|
||||
log_level: str = typer.Option(
|
||||
"critical", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"
|
||||
),
|
||||
log_file: Path = typer.Option(
|
||||
"logs/langflow.log", help="Path to the log file.", envvar="LANGFLOW_LOG_FILE"
|
||||
),
|
||||
cache: Optional[str] = typer.Option(
|
||||
envvar="LANGFLOW_LANGCHAIN_CACHE",
|
||||
help="Type of cache to use. (InMemoryCache, SQLiteCache)",
|
||||
|
|
@ -189,22 +201,30 @@ def run(
|
|||
else:
|
||||
# Run using gunicorn on Linux
|
||||
run_on_mac_or_linux(host, port, log_level, options, app, open_browser)
|
||||
if open_browser:
|
||||
click.launch(f"http://{host}:{port}")
|
||||
|
||||
|
||||
def run_on_mac_or_linux(host, port, log_level, options, app, open_browser=True):
|
||||
webapp_process = Process(target=run_langflow, args=(host, port, log_level, options, app))
|
||||
webapp_process.start()
|
||||
def wait_for_server_ready(host, port):
|
||||
"""
|
||||
Wait for the server to become ready by polling the health endpoint.
|
||||
"""
|
||||
status_code = 0
|
||||
while status_code != 200:
|
||||
try:
|
||||
status_code = httpx.get(f"http://{host}:{port}/health").status_code
|
||||
|
||||
except Exception:
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def run_on_mac_or_linux(host, port, log_level, options, app):
|
||||
webapp_process = Process(
|
||||
target=run_langflow, args=(host, port, log_level, options, app)
|
||||
)
|
||||
webapp_process.start()
|
||||
wait_for_server_ready(host, port)
|
||||
|
||||
print_banner(host, port)
|
||||
if open_browser:
|
||||
webbrowser.open(f"http://{host}:{port}")
|
||||
|
||||
|
||||
def run_on_windows(host, port, log_level, options, app):
|
||||
|
|
@ -245,40 +265,165 @@ def get_free_port(port):
|
|||
return port
|
||||
|
||||
|
||||
def print_banner(host, port):
|
||||
def version_is_prerelease(version: str):
|
||||
"""
|
||||
Check if a version is a pre-release version.
|
||||
"""
|
||||
return "a" in version or "b" in version or "rc" in version
|
||||
|
||||
|
||||
def get_letter_from_version(version: str):
|
||||
"""
|
||||
Get the letter from a pre-release version.
|
||||
"""
|
||||
if "a" in version:
|
||||
return "a"
|
||||
if "b" in version:
|
||||
return "b"
|
||||
if "rc" in version:
|
||||
return "rc"
|
||||
return None
|
||||
|
||||
|
||||
def build_new_version_notice(current_version: str, package_name: str):
|
||||
"""
|
||||
Build a new version notice.
|
||||
"""
|
||||
# The idea here is that we want to show a notice to the user
|
||||
# when a new version of Langflow is available.
|
||||
# The key is that if the version the user has is a pre-release
|
||||
# e.g 0.0.0a1, then we find the latest version that is pre-release
|
||||
# otherwise we find the latest stable version.
|
||||
# we will show the notice either way, but only if the version
|
||||
# the user has is not the latest version.
|
||||
if version_is_prerelease(current_version):
|
||||
# curl -s "https://pypi.org/pypi/langflow/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1
|
||||
# this command will give us the latest pre-release version
|
||||
package_info = httpx.get(f"https://pypi.org/pypi/{package_name}/json").json()
|
||||
# 4.0.0a1 or 4.0.0b1 or 4.0.0rc1
|
||||
# find which type of pre-release version we have
|
||||
# could be a1, b1, rc1
|
||||
# we want the a, b, or rc and the number
|
||||
suffix_letter = get_letter_from_version(current_version)
|
||||
number_version = current_version.split(suffix_letter)[0]
|
||||
latest_version = sorted(
|
||||
package_info["releases"].keys(),
|
||||
key=lambda x: x.split(suffix_letter)[-1] and number_version in x,
|
||||
)[-1]
|
||||
if version_is_prerelease(latest_version) and latest_version != current_version:
|
||||
return (
|
||||
True,
|
||||
f"A new pre-release version of {package_name} is available: {latest_version}",
|
||||
)
|
||||
else:
|
||||
latest_version = httpx.get(f"https://pypi.org/pypi/{package_name}/json").json()[
|
||||
"info"
|
||||
]["version"]
|
||||
if not version_is_prerelease(latest_version):
|
||||
return (
|
||||
False,
|
||||
f"A new version of {package_name} is available: {latest_version}",
|
||||
)
|
||||
return False, ""
|
||||
|
||||
|
||||
def is_prerelease(version: str) -> bool:
|
||||
return "a" in version or "b" in version or "rc" in version
|
||||
|
||||
|
||||
def fetch_latest_version(package_name: str, include_prerelease: bool) -> str:
|
||||
response = httpx.get(f"https://pypi.org/pypi/{package_name}/json")
|
||||
versions = response.json()["releases"].keys()
|
||||
valid_versions = [v for v in versions if include_prerelease or not is_prerelease(v)]
|
||||
if not valid_versions:
|
||||
return None # Handle case where no valid versions are found
|
||||
return max(valid_versions, key=lambda v: pkg_version.parse(v))
|
||||
|
||||
|
||||
def build_version_notice(current_version: str, package_name: str) -> str:
|
||||
latest_version = fetch_latest_version(package_name, is_prerelease(current_version))
|
||||
if latest_version and pkg_version.parse(current_version) < pkg_version.parse(
|
||||
latest_version
|
||||
):
|
||||
release_type = "pre-release" if is_prerelease(latest_version) else "version"
|
||||
return f"A new {release_type} of {package_name} is available: {latest_version}"
|
||||
return ""
|
||||
|
||||
|
||||
def generate_pip_command(package_names, is_pre_release):
|
||||
"""
|
||||
Generate the pip install command based on the packages and whether it's a pre-release.
|
||||
"""
|
||||
base_command = "pip install"
|
||||
if is_pre_release:
|
||||
return f"{base_command} {' '.join(package_names)} -U --pre"
|
||||
else:
|
||||
return f"{base_command} {' '.join(package_names)} -U"
|
||||
|
||||
|
||||
def stylize_text(text: str, to_style: str, is_prerelease: bool) -> str:
|
||||
color = "#42a7f5" if is_prerelease else "#6e42f5"
|
||||
# return "".join(f"[{color}]{char}[/]" for char in text)
|
||||
styled_text = f"[{color}]{to_style}[/]"
|
||||
return text.replace(to_style, styled_text)
|
||||
|
||||
|
||||
def print_banner(host: str, port: int):
|
||||
notices = []
|
||||
package_names = [] # Track package names for pip install instructions
|
||||
is_pre_release = False # Track if any package is a pre-release
|
||||
package_name = ""
|
||||
|
||||
try:
|
||||
from langflow.version import __version__
|
||||
from langflow.version import __version__ as langflow_version
|
||||
|
||||
version = __version__
|
||||
word = "Langflow"
|
||||
is_pre_release |= is_prerelease(langflow_version) # Update pre-release status
|
||||
notice = build_version_notice(langflow_version, "langflow")
|
||||
notice = stylize_text(notice, "langflow", is_pre_release)
|
||||
if notice:
|
||||
notices.append(notice)
|
||||
package_names.append("langflow")
|
||||
package_name = "Langflow"
|
||||
except ImportError:
|
||||
from importlib import metadata
|
||||
langflow_version = None
|
||||
|
||||
version = metadata.version("langflow-base")
|
||||
word = "Langflow Base"
|
||||
# Attempt to handle langflow-base similarly
|
||||
if langflow_version is None: # This means langflow.version was not imported
|
||||
try:
|
||||
from importlib import metadata
|
||||
|
||||
colors = ["#6e42f5"]
|
||||
langflow_base_version = metadata.version("langflow-base")
|
||||
is_pre_release |= is_prerelease(
|
||||
langflow_base_version
|
||||
) # Update pre-release status
|
||||
notice = build_version_notice(langflow_base_version, "langflow-base")
|
||||
notice = stylize_text(notice, "langflow-base", is_pre_release)
|
||||
if notice:
|
||||
notices.append(notice)
|
||||
package_names.append("langflow-base")
|
||||
package_name = "Langflow Base"
|
||||
except ImportError as e:
|
||||
logger.exception(e)
|
||||
raise e
|
||||
|
||||
styled_word = ""
|
||||
# Generate pip command based on the collected data
|
||||
pip_command = generate_pip_command(package_names, is_pre_release)
|
||||
|
||||
for i, char in enumerate(word):
|
||||
color = colors[i % len(colors)]
|
||||
styled_word += f"[{color}]{char}[/]"
|
||||
# Add pip install command to notices if any package needs an update
|
||||
if notices:
|
||||
notices.append(f"Run '{pip_command}' to update.")
|
||||
|
||||
# Title with emojis and gradient text
|
||||
title = (
|
||||
f"[bold]Welcome to :chains: {styled_word} v{version}[/bold]\n"
|
||||
f"Access [link=http://{host}:{port}]http://{host}:{port}[/link]"
|
||||
)
|
||||
info_text = (
|
||||
"Collaborate, and contribute at our "
|
||||
"[bold][link=https://github.com/logspace-ai/langflow]GitHub Repo[/link][/bold] :rocket:"
|
||||
styled_notices = [f"[bold]{notice}[/bold]" for notice in notices if notice]
|
||||
styled_package_name = stylize_text(
|
||||
package_name, package_name, any("pre-release" in notice for notice in notices)
|
||||
)
|
||||
|
||||
# Create a panel with the title and the info text, and a border around it
|
||||
panel = Panel(f"{title}\n{info_text}", box=box.ROUNDED, border_style="blue", expand=False)
|
||||
title = f"[bold]Welcome to :chains: {styled_package_name}[/bold]\n"
|
||||
info_text = "Collaborate, and contribute at our [bold][link=https://github.com/logspace-ai/langflow]GitHub Repo[/link][/bold] :rocket:"
|
||||
access_link = f"Access [link=http://{host}:{port}]http://{host}:{port}[/link]"
|
||||
|
||||
# Print the banner with a separator line before and after
|
||||
panel_content = "\n\n".join([title, *styled_notices, info_text, access_link])
|
||||
panel = Panel(panel_content, box=box.ROUNDED, border_style="blue", expand=False)
|
||||
rprint(panel)
|
||||
|
||||
|
||||
|
|
@ -314,8 +459,12 @@ def run_langflow(host, port, log_level, options, app):
|
|||
@app.command()
|
||||
def superuser(
|
||||
username: str = typer.Option(..., prompt=True, help="Username for the superuser."),
|
||||
password: str = typer.Option(..., prompt=True, hide_input=True, help="Password for the superuser."),
|
||||
log_level: str = typer.Option("error", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"),
|
||||
password: str = typer.Option(
|
||||
..., prompt=True, hide_input=True, help="Password for the superuser."
|
||||
),
|
||||
log_level: str = typer.Option(
|
||||
"error", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"
|
||||
),
|
||||
):
|
||||
"""
|
||||
Create a superuser.
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class ChatComponent(CustomComponent):
|
|||
input_value: Optional[Union[str, Record]] = None,
|
||||
session_id: Optional[str] = None,
|
||||
return_record: Optional[bool] = False,
|
||||
record_template: Optional[str] = "Text: {text}\nData: {data}",
|
||||
record_template: str = "Text: {text}\nData: {data}",
|
||||
) -> Union[Text, Record]:
|
||||
input_value_record: Optional[Record] = None
|
||||
if return_record:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Optional, Union
|
||||
from typing import Optional
|
||||
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
|
|
@ -27,7 +27,7 @@ class TextComponent(CustomComponent):
|
|||
|
||||
def build(
|
||||
self,
|
||||
input_value: Optional[Union[Text, Record]] = "",
|
||||
input_value: Optional[Text] = "",
|
||||
record_template: Optional[str] = "{text}",
|
||||
) -> Text:
|
||||
if isinstance(input_value, Record):
|
||||
|
|
|
|||
|
|
@ -94,14 +94,14 @@ class APIRequest(CustomComponent):
|
|||
self,
|
||||
method: str,
|
||||
urls: List[str],
|
||||
headers: Optional[Record] = None,
|
||||
_headers: Optional[Record] = None,
|
||||
body: Optional[Record] = None,
|
||||
timeout: int = 5,
|
||||
) -> List[Record]:
|
||||
if headers is None:
|
||||
if _headers is None:
|
||||
headers = {}
|
||||
else:
|
||||
headers = headers.data
|
||||
headers = _headers.data
|
||||
|
||||
bodies = []
|
||||
if body:
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ class CreateRecordComponent(CustomComponent):
|
|||
)
|
||||
build_config[field.name] = field.to_dict()
|
||||
|
||||
build_config["number_of_fields"]["value"] = field_value_int
|
||||
return build_config
|
||||
|
||||
def build_config(self):
|
||||
|
|
|
|||
|
|
@ -890,9 +890,9 @@ class Graph:
|
|||
raise ValueError(f"Source vertex {edge['source']} not found")
|
||||
if target is None:
|
||||
raise ValueError(f"Target vertex {edge['target']} not found")
|
||||
edge = ContractEdge(source, target, edge)
|
||||
new_edge = ContractEdge(source, target, edge)
|
||||
|
||||
edges.add(edge)
|
||||
edges.add(new_edge)
|
||||
|
||||
return list(edges)
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class RunnableVerticesManager:
|
|||
for v_id in set(next_runnable_vertices): # Use set to avoid duplicates
|
||||
self.update_vertex_run_state(v_id, is_runnable=False)
|
||||
self.remove_from_predecessors(v_id)
|
||||
await set_cache_coro(data=graph, lock=lock)
|
||||
await set_cache_coro(data=graph, lock=lock) # type: ignore
|
||||
return next_runnable_vertices
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import inspect
|
|||
import types
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING, Any, AsyncIterator, Callable, Dict, Iterator, List, Optional
|
||||
import os
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
|
@ -305,7 +306,7 @@ class Vertex:
|
|||
if file_path := field.get("file_path"):
|
||||
storage_service = get_storage_service()
|
||||
try:
|
||||
flow_id, file_name = file_path.split("/")
|
||||
flow_id, file_name = os.path.split(file_path)
|
||||
full_path = storage_service.build_full_path(flow_id, file_name)
|
||||
except ValueError as e:
|
||||
if "too many values to unpack" in str(e):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import TYPE_CHECKING, Any, Callable, Coroutine, List, Optional, Tuple, Union
|
||||
from typing import TYPE_CHECKING, Any, Callable, Coroutine, List, Optional, Tuple, Type, Union, cast
|
||||
|
||||
from pydantic.v1 import BaseModel, Field, create_model
|
||||
from sqlmodel import select
|
||||
|
|
@ -63,12 +63,14 @@ def find_flow(flow_name: str, user_id: str) -> Optional[str]:
|
|||
|
||||
|
||||
async def run_flow(
|
||||
inputs: Union[dict, List[dict]] = None,
|
||||
inputs: Optional[Union[dict, List[dict]]] = None,
|
||||
tweaks: Optional[dict] = None,
|
||||
flow_id: Optional[str] = None,
|
||||
flow_name: Optional[str] = None,
|
||||
user_id: Optional[str] = None,
|
||||
) -> Any:
|
||||
if user_id is None:
|
||||
raise ValueError("Session is invalid")
|
||||
graph = await load_flow(user_id, flow_id, flow_name, tweaks)
|
||||
|
||||
if inputs is None:
|
||||
|
|
@ -77,7 +79,7 @@ async def run_flow(
|
|||
inputs_components = []
|
||||
types = []
|
||||
for input_dict in inputs:
|
||||
inputs_list.append({INPUT_FIELD_NAME: input_dict.get("input_value")})
|
||||
inputs_list.append({INPUT_FIELD_NAME: cast(str, input_dict.get("input_value"))})
|
||||
inputs_components.append(input_dict.get("components", []))
|
||||
types.append(input_dict.get("type", []))
|
||||
|
||||
|
|
@ -138,12 +140,12 @@ async def flow_function({func_args}):
|
|||
"""
|
||||
|
||||
compiled_func = compile(func_body, "<string>", "exec")
|
||||
local_scope = {}
|
||||
local_scope: dict = {}
|
||||
exec(compiled_func, globals(), local_scope)
|
||||
return local_scope["flow_function"]
|
||||
|
||||
|
||||
def build_function_and_schema(flow_record: Record, graph: "Graph") -> Tuple[Callable, BaseModel]:
|
||||
def build_function_and_schema(flow_record: Record, graph: "Graph") -> Tuple[Callable, Type[BaseModel]]:
|
||||
"""
|
||||
Builds a dynamic function and schema for a given flow.
|
||||
|
||||
|
|
@ -178,7 +180,7 @@ def get_flow_inputs(graph: "Graph") -> List["Vertex"]:
|
|||
return inputs
|
||||
|
||||
|
||||
def build_schema_from_inputs(name: str, inputs: List[tuple[str, str, str]]) -> BaseModel:
|
||||
def build_schema_from_inputs(name: str, inputs: List["Vertex"]) -> Type[BaseModel]:
|
||||
"""
|
||||
Builds a schema from the given inputs.
|
||||
|
||||
|
|
@ -196,4 +198,4 @@ def build_schema_from_inputs(name: str, inputs: List[tuple[str, str, str]]) -> B
|
|||
field_name = input_.display_name.lower().replace(" ", "_")
|
||||
description = input_.description
|
||||
fields[field_name] = (str, Field(default="", description=description))
|
||||
return create_model(name, **fields)
|
||||
return create_model(name, **fields) # type: ignore
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from typing import Union
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.schema import Record
|
||||
|
|
@ -16,7 +17,7 @@ def docs_to_records(documents: list[Document]) -> list[Record]:
|
|||
return [Record.from_document(document) for document in documents]
|
||||
|
||||
|
||||
def records_to_text(template: str, records: list[Record]) -> str:
|
||||
def records_to_text(template: str, records: Union[Record, list[Record]]) -> str:
|
||||
"""
|
||||
Converts a list of Records to a list of texts.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,888 @@
|
|||
{
|
||||
"id": "c091a57f-43a7-4a5e-b352-035ae8d8379c",
|
||||
"data": {
|
||||
"nodes": [
|
||||
{
|
||||
"id": "Prompt-uxBqP",
|
||||
"type": "genericNode",
|
||||
"position": {
|
||||
"x": 53.588791333410654,
|
||||
"y": -107.07318910019967
|
||||
},
|
||||
"data": {
|
||||
"type": "Prompt",
|
||||
"node": {
|
||||
"template": {
|
||||
"code": {
|
||||
"type": "code",
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from langchain_core.prompts import PromptTemplate\n\nfrom langflow.field_typing import Prompt, TemplateField, Text\nfrom langflow.interface.custom.custom_component import CustomComponent\n\n\nclass PromptComponent(CustomComponent):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n\n def build_config(self):\n return {\n \"template\": TemplateField(display_name=\"Template\"),\n \"code\": TemplateField(advanced=True),\n }\n\n def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Text:\n from langflow.base.prompts.utils import dict_values_to_string\n\n prompt_template = PromptTemplate.from_template(Text(template))\n kwargs = dict_values_to_string(kwargs)\n kwargs = {k: \"\\n\".join(v) if isinstance(v, list) else v for k, v in kwargs.items()}\n try:\n formated_prompt = prompt_template.format(**kwargs)\n except Exception as exc:\n raise ValueError(f\"Error formatting prompt: {exc}\") from exc\n self.status = f'Prompt:\\n\"{formated_prompt}\"'\n return formated_prompt\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "code",
|
||||
"advanced": true,
|
||||
"dynamic": true,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"template": {
|
||||
"type": "prompt",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "Answer the user as if you were a pirate.\n\nUser: {user_input}\n\nAnswer: ",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "template",
|
||||
"display_name": "Template",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"_type": "CustomComponent",
|
||||
"user_input": {
|
||||
"field_type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "user_input",
|
||||
"display_name": "user_input",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"type": "str"
|
||||
}
|
||||
},
|
||||
"description": "Create a prompt template with dynamic variables.",
|
||||
"icon": "prompts",
|
||||
"is_input": null,
|
||||
"is_output": null,
|
||||
"is_composition": null,
|
||||
"base_classes": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"name": "",
|
||||
"display_name": "Prompt",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"template": [
|
||||
"user_input"
|
||||
]
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"full_path": null,
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
"beta": false,
|
||||
"error": null
|
||||
},
|
||||
"id": "Prompt-uxBqP",
|
||||
"description": "Create a prompt template with dynamic variables.",
|
||||
"display_name": "Prompt"
|
||||
},
|
||||
"selected": true,
|
||||
"width": 384,
|
||||
"height": 383,
|
||||
"dragging": false,
|
||||
"positionAbsolute": {
|
||||
"x": 53.588791333410654,
|
||||
"y": -107.07318910019967
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "OpenAIModel-k39HS",
|
||||
"type": "genericNode",
|
||||
"position": {
|
||||
"x": 634.8148772766217,
|
||||
"y": 27.035057029045305
|
||||
},
|
||||
"data": {
|
||||
"type": "OpenAIModel",
|
||||
"node": {
|
||||
"template": {
|
||||
"input_value": {
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "input_value",
|
||||
"display_name": "Input",
|
||||
"advanced": false,
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"code": {
|
||||
"type": "code",
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": [\n \"gpt-4-turbo-preview\",\n \"gpt-3.5-turbo\",\n \"gpt-4-0125-preview\",\n \"gpt-4-1106-preview\",\n \"gpt-4-vision-preview\",\n \"gpt-3.5-turbo-0125\",\n \"gpt-3.5-turbo-1106\",\n ],\n \"value\": \"gpt-4-turbo-preview\",\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str,\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=openai_api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "code",
|
||||
"advanced": true,
|
||||
"dynamic": true,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"max_tokens": {
|
||||
"type": "int",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": 256,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "max_tokens",
|
||||
"display_name": "Max Tokens",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"model_kwargs": {
|
||||
"type": "NestedDict",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": {},
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "model_kwargs",
|
||||
"display_name": "Model Kwargs",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"model_name": {
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"list": true,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "gpt-3.5-turbo",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-4-0125-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4-vision-preview",
|
||||
"gpt-3.5-turbo-0125",
|
||||
"gpt-3.5-turbo-1106"
|
||||
],
|
||||
"name": "model_name",
|
||||
"display_name": "Model Name",
|
||||
"advanced": false,
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"openai_api_base": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "openai_api_base",
|
||||
"display_name": "OpenAI API Base",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"openai_api_key": {
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": true,
|
||||
"name": "openai_api_key",
|
||||
"display_name": "OpenAI API Key",
|
||||
"advanced": false,
|
||||
"dynamic": false,
|
||||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"load_from_db": true,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"value": ""
|
||||
},
|
||||
"stream": {
|
||||
"type": "bool",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": true,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "stream",
|
||||
"display_name": "Stream",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "Stream the response from the model. Streaming works only in Chat.",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"system_message": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "system_message",
|
||||
"display_name": "System Message",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "System message to pass to the model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"temperature": {
|
||||
"type": "float",
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": 0.1,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "temperature",
|
||||
"display_name": "Temperature",
|
||||
"advanced": false,
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"rangeSpec": {
|
||||
"step_type": "float",
|
||||
"min": -1,
|
||||
"max": 1,
|
||||
"step": 0.1
|
||||
},
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"icon": "OpenAI",
|
||||
"base_classes": [
|
||||
"object",
|
||||
"Text",
|
||||
"str"
|
||||
],
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"input_value": null,
|
||||
"openai_api_key": null,
|
||||
"temperature": null,
|
||||
"model_name": null,
|
||||
"max_tokens": null,
|
||||
"model_kwargs": null,
|
||||
"openai_api_base": null,
|
||||
"stream": null,
|
||||
"system_message": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [
|
||||
"max_tokens",
|
||||
"model_kwargs",
|
||||
"model_name",
|
||||
"openai_api_base",
|
||||
"openai_api_key",
|
||||
"temperature",
|
||||
"input_value",
|
||||
"system_message",
|
||||
"stream"
|
||||
],
|
||||
"beta": false
|
||||
},
|
||||
"id": "OpenAIModel-k39HS",
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"display_name": "OpenAI"
|
||||
},
|
||||
"selected": false,
|
||||
"width": 384,
|
||||
"height": 563,
|
||||
"positionAbsolute": {
|
||||
"x": 634.8148772766217,
|
||||
"y": 27.035057029045305
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"id": "ChatOutput-njtka",
|
||||
"type": "genericNode",
|
||||
"position": {
|
||||
"x": 1193.250417197867,
|
||||
"y": 71.88476890163852
|
||||
},
|
||||
"data": {
|
||||
"type": "ChatOutput",
|
||||
"node": {
|
||||
"template": {
|
||||
"code": {
|
||||
"type": "code",
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Interaction Panel.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n ) -> Union[Text, Record]:\n return super().build(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template,\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "code",
|
||||
"advanced": true,
|
||||
"dynamic": true,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"input_value": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "input_value",
|
||||
"display_name": "Message",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"record_template": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "{text}",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "record_template",
|
||||
"display_name": "Record Template",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "In case of Message being a Record, this template will be used to convert it to text.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"return_record": {
|
||||
"type": "bool",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": false,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "return_record",
|
||||
"display_name": "Return Record",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "Return the message as a record containing the sender, sender_name, and session_id.",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"sender": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": true,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "Machine",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "AI",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "sender_name",
|
||||
"display_name": "Sender Name",
|
||||
"advanced": false,
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "session_id",
|
||||
"display_name": "Session ID",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a chat message in the Interaction Panel.",
|
||||
"icon": "ChatOutput",
|
||||
"base_classes": [
|
||||
"Record",
|
||||
"Text",
|
||||
"str",
|
||||
"object"
|
||||
],
|
||||
"display_name": "Chat Output",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"sender": null,
|
||||
"sender_name": null,
|
||||
"input_value": null,
|
||||
"session_id": null,
|
||||
"return_record": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
"beta": false
|
||||
},
|
||||
"id": "ChatOutput-njtka"
|
||||
},
|
||||
"selected": false,
|
||||
"width": 384,
|
||||
"height": 383,
|
||||
"positionAbsolute": {
|
||||
"x": 1193.250417197867,
|
||||
"y": 71.88476890163852
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"id": "ChatInput-P3fgL",
|
||||
"type": "genericNode",
|
||||
"position": {
|
||||
"x": -495.2223093083827,
|
||||
"y": -232.56998443685862
|
||||
},
|
||||
"data": {
|
||||
"type": "ChatInput",
|
||||
"node": {
|
||||
"template": {
|
||||
"code": {
|
||||
"type": "code",
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Interaction Panel.\"\n icon = \"ChatInput\"\n\n def build_config(self):\n build_config = super().build_config()\n build_config[\"input_value\"] = {\n \"input_types\": [],\n \"display_name\": \"Message\",\n \"multiline\": True,\n }\n\n return build_config\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "code",
|
||||
"advanced": true,
|
||||
"dynamic": true,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"input_value": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "input_value",
|
||||
"display_name": "Message",
|
||||
"advanced": false,
|
||||
"input_types": [],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"value": "hi"
|
||||
},
|
||||
"return_record": {
|
||||
"type": "bool",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": false,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "return_record",
|
||||
"display_name": "Return Record",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "Return the message as a record containing the sender, sender_name, and session_id.",
|
||||
"load_from_db": false,
|
||||
"title_case": false
|
||||
},
|
||||
"sender": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": true,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "User",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "User",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "sender_name",
|
||||
"display_name": "Sender Name",
|
||||
"advanced": false,
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": false,
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"name": "session_id",
|
||||
"display_name": "Session ID",
|
||||
"advanced": true,
|
||||
"dynamic": false,
|
||||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Get chat inputs from the Interaction Panel.",
|
||||
"icon": "ChatInput",
|
||||
"base_classes": [
|
||||
"object",
|
||||
"Record",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"display_name": "Chat Input",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"sender": null,
|
||||
"sender_name": null,
|
||||
"input_value": null,
|
||||
"session_id": null,
|
||||
"return_record": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
"beta": false
|
||||
},
|
||||
"id": "ChatInput-P3fgL"
|
||||
},
|
||||
"selected": false,
|
||||
"width": 384,
|
||||
"height": 375,
|
||||
"positionAbsolute": {
|
||||
"x": -495.2223093083827,
|
||||
"y": -232.56998443685862
|
||||
},
|
||||
"dragging": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "OpenAIModel-k39HS",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-k39HSœ}",
|
||||
"target": "ChatOutput-njtka",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-njtkaœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-njtka",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"Text",
|
||||
"str"
|
||||
],
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-k39HS"
|
||||
}
|
||||
},
|
||||
"style": {
|
||||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-OpenAIModel-k39HS{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-k39HSœ}-ChatOutput-njtka{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-njtkaœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
},
|
||||
{
|
||||
"source": "Prompt-uxBqP",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-uxBqPœ}",
|
||||
"target": "OpenAIModel-k39HS",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-k39HSœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-k39HS",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-uxBqP"
|
||||
}
|
||||
},
|
||||
"style": {
|
||||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-Prompt-uxBqP{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-uxBqPœ}-OpenAIModel-k39HS{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-k39HSœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
},
|
||||
{
|
||||
"source": "ChatInput-P3fgL",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œRecordœ,œstrœ,œTextœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-P3fgLœ}",
|
||||
"target": "Prompt-uxBqP",
|
||||
"targetHandle": "{œfieldNameœ:œuser_inputœ,œidœ:œPrompt-uxBqPœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "user_input",
|
||||
"id": "Prompt-uxBqP",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"Record",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"dataType": "ChatInput",
|
||||
"id": "ChatInput-P3fgL"
|
||||
}
|
||||
},
|
||||
"style": {
|
||||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-ChatInput-P3fgL{œbaseClassesœ:[œobjectœ,œRecordœ,œstrœ,œTextœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-P3fgLœ}-Prompt-uxBqP{œfieldNameœ:œuser_inputœ,œidœ:œPrompt-uxBqPœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
}
|
||||
],
|
||||
"viewport": {
|
||||
"x": 260.58251815500563,
|
||||
"y": 318.2261172111936,
|
||||
"zoom": 0.43514115784696294
|
||||
}
|
||||
},
|
||||
"description": "This flow will get you experimenting with the basics of the UI, the Chat and the Prompt component. \n\nTry changing the Template in it to see how the model behaves. \nYou can change it to this and a Text Input into the `type_of_person` variable : \"Answer the user as if you were a pirate.\n\nUser: {user_input}\n\nAnswer: \" ",
|
||||
"name": "Basic Prompting (Hello, world!)",
|
||||
"last_tested_version": "1.0.0a4",
|
||||
"is_component": false
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
from langflow.processing.load import load_flow_from_json # noqa: F401
|
||||
from langflow.processing.load import load_flow_from_json, run_flow_from_json # noqa: F401
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from pathlib import Path
|
|||
from typing import Optional
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import nest_asyncio
|
||||
import nest_asyncio # type: ignore
|
||||
import socketio # type: ignore
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class Record(BaseModel):
|
|||
data (dict, optional): Additional data associated with the record.
|
||||
"""
|
||||
|
||||
text_key: Optional[str] = "text"
|
||||
text_key: str = "text"
|
||||
data: dict = {}
|
||||
default_value: Optional[str] = ""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,45 @@
|
|||
import os
|
||||
import logging
|
||||
|
||||
from gunicorn import glogging
|
||||
from gunicorn.app.base import BaseApplication # type: ignore
|
||||
from uvicorn.workers import UvicornWorker
|
||||
|
||||
from langflow.utils.logger import InterceptHandler # type: ignore
|
||||
|
||||
|
||||
class LangflowUvicornWorker(UvicornWorker):
|
||||
CONFIG_KWARGS = {"loop": "asyncio"}
|
||||
|
||||
|
||||
class Logger(glogging.Logger):
|
||||
"""Implements and overrides the gunicorn logging interface.
|
||||
|
||||
This class inherits from the standard gunicorn logger and overrides it by
|
||||
replacing the handlers with `InterceptHandler` in order to route the
|
||||
gunicorn logs to loguru.
|
||||
"""
|
||||
|
||||
def __init__(self, cfg):
|
||||
super().__init__(cfg)
|
||||
logging.getLogger("gunicorn.error").handlers = [InterceptHandler()]
|
||||
logging.getLogger("gunicorn.access").handlers = [InterceptHandler()]
|
||||
|
||||
|
||||
class LangflowApplication(BaseApplication):
|
||||
def __init__(self, app, options=None):
|
||||
self.options = options or {}
|
||||
|
||||
self.options["worker_class"] = "langflow.server.LangflowUvicornWorker"
|
||||
self.options["loglevel"] = os.getenv("LANGFLOW_LOG_LEVEL", "error").lower()
|
||||
self.options["logger_class"] = Logger
|
||||
self.application = app
|
||||
super().__init__()
|
||||
|
||||
def load_config(self):
|
||||
config = {key: value for key, value in self.options.items() if key in self.cfg.settings and value is not None}
|
||||
config = {
|
||||
key: value
|
||||
for key, value in self.options.items()
|
||||
if key in self.cfg.settings and value is not None
|
||||
}
|
||||
for key, value in config.items():
|
||||
self.cfg.set(key.lower(), value)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
from datetime import datetime
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import command, util
|
||||
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.database import models # noqa
|
||||
from langflow.services.database.models.user.crud import get_user_by_username
|
||||
from langflow.services.database.utils import Result, TableResults
|
||||
from langflow.services.deps import get_settings_service
|
||||
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:
|
||||
from sqlalchemy.engine import Engine
|
||||
|
|
@ -37,7 +37,10 @@ class DatabaseService(Service):
|
|||
def _create_engine(self) -> "Engine":
|
||||
"""Create the engine for the database."""
|
||||
settings_service = get_settings_service()
|
||||
if settings_service.settings.DATABASE_URL and settings_service.settings.DATABASE_URL.startswith("sqlite"):
|
||||
if (
|
||||
settings_service.settings.DATABASE_URL
|
||||
and settings_service.settings.DATABASE_URL.startswith("sqlite")
|
||||
):
|
||||
connect_args = {"check_same_thread": False}
|
||||
else:
|
||||
connect_args = {}
|
||||
|
|
@ -49,7 +52,9 @@ class DatabaseService(Service):
|
|||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
if exc_type is not None: # If an exception has been raised
|
||||
logger.error(f"Session rollback because of exception: {exc_type.__name__} {exc_value}")
|
||||
logger.error(
|
||||
f"Session rollback because of exception: {exc_type.__name__} {exc_value}"
|
||||
)
|
||||
self._session.rollback()
|
||||
else:
|
||||
self._session.commit()
|
||||
|
|
@ -66,7 +71,9 @@ class DatabaseService(Service):
|
|||
settings_service = get_settings_service()
|
||||
if settings_service.auth_settings.AUTO_LOGIN:
|
||||
with Session(self.engine) as session:
|
||||
flows = session.exec(select(models.Flow).where(models.Flow.user_id is None)).all()
|
||||
flows = session.exec(
|
||||
select(models.Flow).where(models.Flow.user_id is None)
|
||||
).all()
|
||||
if flows:
|
||||
logger.debug("Migrating flows to default superuser")
|
||||
username = settings_service.auth_settings.SUPERUSER
|
||||
|
|
@ -96,14 +103,16 @@ class DatabaseService(Service):
|
|||
expected_columns = list(model.model_fields.keys())
|
||||
|
||||
try:
|
||||
available_columns = [col["name"] for col in inspector.get_columns(table)]
|
||||
available_columns = [
|
||||
col["name"] for col in inspector.get_columns(table)
|
||||
]
|
||||
except sa.exc.NoSuchTableError:
|
||||
logger.error(f"Missing table: {table}")
|
||||
logger.debug(f"Missing table: {table}")
|
||||
return False
|
||||
|
||||
for column in expected_columns:
|
||||
if column not in available_columns:
|
||||
logger.error(f"Missing column: {column} in table {table}")
|
||||
logger.debug(f"Missing column: {column} in table {table}")
|
||||
return False
|
||||
|
||||
for table in legacy_tables:
|
||||
|
|
@ -160,7 +169,9 @@ class DatabaseService(Service):
|
|||
buffer.write(f"{datetime.now().isoformat()}: Checking migrations\n")
|
||||
command.check(alembic_cfg)
|
||||
except Exception as exc:
|
||||
if isinstance(exc, (util.exc.CommandError, util.exc.AutogenerateDiffsDetected)):
|
||||
if isinstance(
|
||||
exc, (util.exc.CommandError, util.exc.AutogenerateDiffsDetected)
|
||||
):
|
||||
command.upgrade(alembic_cfg, "head")
|
||||
time.sleep(3)
|
||||
|
||||
|
|
@ -197,7 +208,10 @@ class DatabaseService(Service):
|
|||
# We will check that all models are in the database
|
||||
# and that the database is up to date with all columns
|
||||
sql_models = [models.Flow, models.User, models.ApiKey]
|
||||
return [TableResults(sql_model.__tablename__, self.check_table(sql_model)) for sql_model in sql_models]
|
||||
return [
|
||||
TableResults(sql_model.__tablename__, self.check_table(sql_model))
|
||||
for sql_model in sql_models
|
||||
]
|
||||
|
||||
def check_table(self, model):
|
||||
results = []
|
||||
|
|
@ -206,7 +220,9 @@ class DatabaseService(Service):
|
|||
expected_columns = list(model.__fields__.keys())
|
||||
available_columns = []
|
||||
try:
|
||||
available_columns = [col["name"] for col in inspector.get_columns(table_name)]
|
||||
available_columns = [
|
||||
col["name"] for col in inspector.get_columns(table_name)
|
||||
]
|
||||
results.append(Result(name=table_name, type="table", success=True))
|
||||
except sa.exc.NoSuchTableError:
|
||||
logger.error(f"Missing table: {table_name}")
|
||||
|
|
@ -237,7 +253,9 @@ class DatabaseService(Service):
|
|||
try:
|
||||
table.create(self.engine, checkfirst=True)
|
||||
except OperationalError as oe:
|
||||
logger.warning(f"Table {table} already exists, skipping. Exception: {oe}")
|
||||
logger.warning(
|
||||
f"Table {table} already exists, skipping. Exception: {oe}"
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(f"Error creating table {table}: {exc}")
|
||||
raise RuntimeError(f"Error creating table {table}") from exc
|
||||
|
|
@ -249,7 +267,9 @@ class DatabaseService(Service):
|
|||
if table not in table_names:
|
||||
logger.error("Something went wrong creating the database and tables.")
|
||||
logger.error("Please check your database settings.")
|
||||
raise RuntimeError("Something went wrong creating the database and tables.")
|
||||
raise RuntimeError(
|
||||
"Something went wrong creating the database and tables."
|
||||
)
|
||||
|
||||
logger.debug("Database and tables created successfully")
|
||||
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ class MonitorService(Service):
|
|||
if conditions:
|
||||
query += " WHERE " + " AND ".join(conditions)
|
||||
|
||||
if order_by:
|
||||
if order_by and order:
|
||||
# Make sure the order is from newest to oldest
|
||||
query += f" ORDER BY {order_by} {order.upper()}"
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ class StateService(Service):
|
|||
class InMemoryStateService(StateService):
|
||||
def __init__(self, settings_service: SettingsService):
|
||||
self.settings_service = settings_service
|
||||
self.states = {}
|
||||
self.observers = defaultdict(list)
|
||||
self.states: dict = {}
|
||||
self.observers: dict = defaultdict(list)
|
||||
self.lock = Lock()
|
||||
|
||||
def append_state(self, key, new_state, run_id: str):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
|
@ -25,7 +26,10 @@ def patching(record):
|
|||
|
||||
|
||||
def configure(log_level: Optional[str] = None, log_file: Optional[Path] = None):
|
||||
if os.getenv("LANGFLOW_LOG_LEVEL", "").upper() in VALID_LOG_LEVELS and log_level is None:
|
||||
if (
|
||||
os.getenv("LANGFLOW_LOG_LEVEL", "").upper() in VALID_LOG_LEVELS
|
||||
and log_level is None
|
||||
):
|
||||
log_level = os.getenv("LANGFLOW_LOG_LEVEL")
|
||||
if log_level is None:
|
||||
log_level = "ERROR"
|
||||
|
|
@ -67,3 +71,46 @@ def configure(log_level: Optional[str] = None, log_file: Optional[Path] = None):
|
|||
logger.debug(f"Logger set up with log level: {log_level}")
|
||||
if log_file:
|
||||
logger.debug(f"Log file: {log_file}")
|
||||
|
||||
setup_uvicorn_logger()
|
||||
setup_gunicorn_logger()
|
||||
|
||||
|
||||
def setup_uvicorn_logger():
|
||||
loggers = (
|
||||
logging.getLogger(name)
|
||||
for name in logging.root.manager.loggerDict
|
||||
if name.startswith("uvicorn.")
|
||||
)
|
||||
for uvicorn_logger in loggers:
|
||||
uvicorn_logger.handlers = []
|
||||
logging.getLogger("uvicorn").handlers = [InterceptHandler()]
|
||||
|
||||
|
||||
def setup_gunicorn_logger():
|
||||
logging.getLogger("gunicorn.error").handlers = [InterceptHandler()]
|
||||
logging.getLogger("gunicorn.access").handlers = [InterceptHandler()]
|
||||
|
||||
|
||||
class InterceptHandler(logging.Handler):
|
||||
"""
|
||||
Default handler from examples in loguru documentaion.
|
||||
See https://loguru.readthedocs.io/en/stable/overview.html#entirely-compatible-with-standard-logging
|
||||
"""
|
||||
|
||||
def emit(self, record):
|
||||
# Get corresponding Loguru level if it exists
|
||||
try:
|
||||
level = logger.level(record.levelname).name
|
||||
except ValueError:
|
||||
level = record.levelno
|
||||
|
||||
# Find caller from where originated the logged message
|
||||
frame, depth = logging.currentframe(), 2
|
||||
while frame.f_code.co_filename == logging.__file__:
|
||||
frame = frame.f_back
|
||||
depth += 1
|
||||
|
||||
logger.opt(depth=depth, exception=record.exc_info).log(
|
||||
level, record.getMessage()
|
||||
)
|
||||
|
|
|
|||
2035
src/backend/base/poetry.lock
generated
2035
src/backend/base/poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "langflow-base"
|
||||
version = "0.0.18"
|
||||
version = "0.0.21"
|
||||
description = "A Python package with a built-in web application"
|
||||
authors = ["Logspace <contact@logspace.ai>"]
|
||||
maintainers = [
|
||||
|
|
@ -27,19 +27,19 @@ langflow-base = "langflow.__main__:main"
|
|||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.10,<3.12"
|
||||
fastapi = "^0.109.0"
|
||||
fastapi = "^0.110.1"
|
||||
httpx = "*"
|
||||
uvicorn = "^0.27.0"
|
||||
uvicorn = "^0.29.0"
|
||||
gunicorn = "^21.2.0"
|
||||
langchain = "~0.1.0"
|
||||
sqlmodel = "^0.0.14"
|
||||
langchain = "~0.1.14"
|
||||
sqlmodel = "^0.0.16"
|
||||
loguru = "^0.7.1"
|
||||
rich = "^13.7.0"
|
||||
langchain-experimental = "*"
|
||||
pydantic = "^2.5.0"
|
||||
pydantic-settings = "^2.1.0"
|
||||
websockets = "*"
|
||||
typer = "^0.9.0"
|
||||
typer = "^0.12.0"
|
||||
cachetools = "^5.3.1"
|
||||
platformdirs = "^4.2.0"
|
||||
python-multipart = "^0.0.7"
|
||||
|
|
@ -57,37 +57,33 @@ python-socketio = "^5.11.0"
|
|||
python-docx = "^1.1.0"
|
||||
jq = { version = "^1.7.0", markers = "sys_platform != 'win32'" }
|
||||
pypdf = "^4.1.0"
|
||||
chromadb = "^0.4.24"
|
||||
langchain-anthropic = "^0.1.4"
|
||||
langchain-astradb = "^0.1.0"
|
||||
nest-asyncio = "^1.6.0"
|
||||
emoji = "^2.11.0"
|
||||
cryptography = "^42.0.5"
|
||||
langchain-openai = "^0.1.1"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest-asyncio = "^0.21.1"
|
||||
pytest-asyncio = "^0.23.1"
|
||||
types-redis = "^4.6.0.5"
|
||||
ipykernel = "^6.26.0"
|
||||
mypy = "^1.7.1"
|
||||
ruff = "^0.1.5"
|
||||
ruff = "^0.3.5"
|
||||
httpx = "*"
|
||||
pytest = "^7.4.2"
|
||||
pytest = "^8.1.1"
|
||||
types-requests = "^2.31.0"
|
||||
requests = "^2.31.0"
|
||||
pytest-cov = "^4.1.0"
|
||||
pandas-stubs = "^2.0.0.230412"
|
||||
types-pillow = "^9.5.0.2"
|
||||
pytest-cov = "^5.0.0"
|
||||
pandas-stubs = "^2.2.1.230412"
|
||||
types-pillow = "^10.2.0.0"
|
||||
types-pyyaml = "^6.0.12.8"
|
||||
types-python-jose = "^3.3.4.8"
|
||||
types-passlib = "^1.7.7.13"
|
||||
locust = "^2.16.1"
|
||||
pytest-mock = "^3.11.1"
|
||||
pytest-xdist = "^3.3.1"
|
||||
locust = "^2.24.1"
|
||||
pytest-mock = "^3.14.0"
|
||||
pytest-xdist = "^3.5.0"
|
||||
types-pywin32 = "^306.0.0.4"
|
||||
types-google-cloud-ndb = "^2.2.0.0"
|
||||
pytest-sugar = "^0.9.7"
|
||||
types-google-cloud-ndb = "^2.3.0.0"
|
||||
pytest-sugar = "^1.0.0"
|
||||
|
||||
|
||||
[tool.poetry.extras]
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export default function UndrawCardComponent({
|
|||
}}
|
||||
/>
|
||||
);
|
||||
case "Basic Prompting (Ahoy World!)":
|
||||
case "Basic Prompting (Hello, world!)":
|
||||
return (
|
||||
<BasicPrompt
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -34,11 +34,15 @@ export default function NewFlowModal({
|
|||
{/* {examples.map((example, idx) => {
|
||||
return <UndrawCardComponent key={idx} flow={example} />;
|
||||
})} */}
|
||||
{examples.find((e) => e.name == "Basic Prompting (Ahoy World!)") && (
|
||||
{examples.find(
|
||||
(e) => e.name == "Basic Prompting (Hello, world!)"
|
||||
) && (
|
||||
<UndrawCardComponent
|
||||
key={1}
|
||||
flow={
|
||||
examples.find((e) => e.name == "Basic Prompting (Ahoy World!)")!
|
||||
examples.find(
|
||||
(e) => e.name == "Basic Prompting (Hello, world!)"
|
||||
)!
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
|||
)}
|
||||
<a
|
||||
target={"_blank"}
|
||||
href="https://logspace.ai/"
|
||||
href="https://medium.com/logspace/langflow-datastax-better-together-1b7462cebc4d"
|
||||
className="logspace-page-icon"
|
||||
>
|
||||
{version && <div className="mt-1">⛓️ Langflow v{version}</div>}
|
||||
<div className={version ? "mt-2" : "mt-1"}>Created by Logspace</div>
|
||||
{version && <div className="mt-1">Langflow 🤝 DataStax</div>}
|
||||
<div className={version ? "mt-2" : "mt-1"}>⛓️ v{version}</div>
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -80,6 +80,15 @@ export type InputGlobalComponentType = {
|
|||
editNode?: boolean;
|
||||
};
|
||||
|
||||
export type InputGlobalComponentType = {
|
||||
disabled: boolean;
|
||||
onChange: (value: string) => void;
|
||||
setDb: (value: boolean) => void;
|
||||
name: string;
|
||||
data: NodeDataType;
|
||||
editNode?: boolean;
|
||||
};
|
||||
|
||||
export type KeyPairListComponentType = {
|
||||
value: any;
|
||||
onChange: (value: Object[]) => void;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { debounce } from "lodash";
|
||||
import { SAVE_DEBOUNCE_TIME } from "../constants/constants";
|
||||
import { postCustomComponentUpdate } from "../controllers/API";
|
||||
import { ResponseErrorTypeAPI } from "../types/api";
|
||||
import { NodeDataType } from "../types/flow";
|
||||
|
|
@ -38,4 +39,7 @@ export const handleUpdateValues = async (name: string, data: NodeDataType) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const debouncedHandleUpdateValues = debounce(handleUpdateValues, 200);
|
||||
export const debouncedHandleUpdateValues = debounce(
|
||||
handleUpdateValues,
|
||||
SAVE_DEBOUNCE_TIME
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { test } from "@playwright/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// await page.waitForTimeout(16000);
|
||||
// test.setTimeout(140000);
|
||||
});
|
||||
test.describe("Auto_login tests", () => {
|
||||
test("auto_login sign in", async ({ page }) => {
|
||||
await page.goto("http:localhost:3000/");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.waitForTimeout(2000);
|
||||
test.setTimeout(120000);
|
||||
});
|
||||
test("CodeAreaModalComponent", async ({ page }) => {
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// await page.waitForTimeout(3000);
|
||||
// test.setTimeout(120000);
|
||||
});
|
||||
test.describe("drag and drop test", () => {
|
||||
/// <reference lib="dom"/>
|
||||
test("drop collection", async ({ page }) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { Page, test } from "@playwright/test";
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// await page.waitForTimeout(6000);
|
||||
// test.setTimeout(120000);
|
||||
});
|
||||
|
||||
test.describe("Flow Page tests", () => {
|
||||
async function goToFlowPage(page: Page) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// await page.waitForTimeout(8000);
|
||||
// test.setTimeout(120000);
|
||||
});
|
||||
test("InputComponent", async ({ page }) => {
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// await page.waitForTimeout(20000);
|
||||
// test.setTimeout(120000);
|
||||
});
|
||||
test("KeypairListComponent", async ({ page }) => {
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import uaParser from "ua-parser-js";
|
||||
|
||||
test("LangflowShortcuts", async ({ page }) => {
|
||||
const getUA = await page.evaluate(() => navigator.userAgent);
|
||||
const userAgentInfo = uaParser(getUA);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// await page.waitForTimeout(12000);
|
||||
// test.setTimeout(120000);
|
||||
});
|
||||
test("NestedComponent", async ({ page }) => {
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// await page.waitForTimeout(13000);
|
||||
// test.setTimeout(120000);
|
||||
});
|
||||
test("PromptTemplateComponent", async ({ page }) => {
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { Page, expect, test } from "@playwright/test";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// await page.waitForTimeout(14000);
|
||||
// test.setTimeout(120000);
|
||||
});
|
||||
test.describe("save component tests", () => {
|
||||
async function saveComponent(page: Page, pattern: RegExp, n: number) {
|
||||
for (let i = 0; i < n; i++) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// await page.waitForTimeout(15000);
|
||||
// test.setTimeout(120000);
|
||||
});
|
||||
test("ToggleComponent", async ({ page }) => {
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue