From 121b55231cf15451a62a86e4ba6ffba5ceeb58a1 Mon Sep 17 00:00:00 2001 From: ming Date: Tue, 11 Jun 2024 19:34:54 -0400 Subject: [PATCH 01/68] use Astra DB Application Token for all Astra token's display_name (#2143) --- .../base/langflow/components/vectorsearch/AstraDBSearch.py | 2 +- src/backend/base/langflow/components/vectorstores/AstraDB.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/components/vectorsearch/AstraDBSearch.py b/src/backend/base/langflow/components/vectorsearch/AstraDBSearch.py index 83ed42daf..107c6b80a 100644 --- a/src/backend/base/langflow/components/vectorsearch/AstraDBSearch.py +++ b/src/backend/base/langflow/components/vectorsearch/AstraDBSearch.py @@ -28,7 +28,7 @@ class AstraDBSearchComponent(LCVectorStoreComponent): "info": "The name of the collection within Astra DB where the vectors will be stored.", }, "token": { - "display_name": "Token", + "display_name": "Astra DB Application Token", "info": "Authentication token for accessing Astra DB.", "password": True, }, diff --git a/src/backend/base/langflow/components/vectorstores/AstraDB.py b/src/backend/base/langflow/components/vectorstores/AstraDB.py index cd4a06aea..4e15655ae 100644 --- a/src/backend/base/langflow/components/vectorstores/AstraDB.py +++ b/src/backend/base/langflow/components/vectorstores/AstraDB.py @@ -24,7 +24,7 @@ class AstraDBVectorStoreComponent(CustomComponent): "info": "The name of the collection within Astra DB where the vectors will be stored.", }, "token": { - "display_name": "Token", + "display_name": "Astra DB Application Token", "info": "Authentication token for accessing Astra DB.", "password": True, }, From 9261bc0a6f06531effee5ef0a4d03c0aa2b5f149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Wed, 12 Jun 2024 15:08:23 +0200 Subject: [PATCH 02/68] docker: add customizable port for frontend image (#2147) --- docker/frontend/nginx.conf | 2 +- docker/frontend/start-nginx.sh | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docker/frontend/nginx.conf b/docker/frontend/nginx.conf index d5ecfce43..593b1dcc8 100644 --- a/docker/frontend/nginx.conf +++ b/docker/frontend/nginx.conf @@ -7,7 +7,7 @@ server { gzip_vary on; gzip_disable "MSIE [4-6] \."; - listen 80; + listen __FRONTEND_PORT__; location / { root /usr/share/nginx/html; diff --git a/docker/frontend/start-nginx.sh b/docker/frontend/start-nginx.sh index 3607adf7d..6ef09745c 100644 --- a/docker/frontend/start-nginx.sh +++ b/docker/frontend/start-nginx.sh @@ -4,11 +4,20 @@ trap 'kill -TERM $PID' TERM INT if [ -z "$BACKEND_URL" ]; then BACKEND_URL="$1" fi +if [ -z "$FRONTEND_PORT" ]; then + FRONTEND_PORT="$2" +fi +if [ -z "$FRONTEND_PORT" ]; then + FRONTEND_PORT="80" +fi if [ -z "$BACKEND_URL" ]; then echo "BACKEND_URL must be set as an environment variable or as first parameter. (e.g. http://localhost:7860)" exit 1 fi +echo "BACKEND_URL: $BACKEND_URL" +echo "FRONTEND_PORT: $FRONTEND_PORT" sed -i "s|__BACKEND_URL__|$BACKEND_URL|g" /etc/nginx/conf.d/default.conf +sed -i "s|__FRONTEND_PORT__|$FRONTEND_PORT|g" /etc/nginx/conf.d/default.conf cat /etc/nginx/conf.d/default.conf From 968ec28fbc1e85f8786ff9d11f8ffe2bd5723241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Wed, 12 Jun 2024 15:09:46 +0200 Subject: [PATCH 03/68] docker: force python version to 3.12.3 (#2148) * docker: force python version to 3.12.3 * also fixes pydantic * remove ci build - too slow * use lock file for deps * use poetry.lock * use poetry.lock --- .github/workflows/docker_test.yml | 18 +--------------- docker/build_and_push.Dockerfile | 21 +++++++++++++++---- docker/build_and_push_base.Dockerfile | 4 +++- .../build_and_push_frontend.Dockerfile | 2 +- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/.github/workflows/docker_test.yml b/.github/workflows/docker_test.yml index 9be7beb00..07b826c58 100644 --- a/.github/workflows/docker_test.yml +++ b/.github/workflows/docker_test.yml @@ -24,6 +24,7 @@ env: jobs: test-docker: runs-on: ubuntu-latest + name: Test docker images steps: - uses: actions/checkout@v4 - name: Build image @@ -61,20 +62,3 @@ jobs: docker build -t langflowai/langflow-frontend:latest-dev \ -f docker/frontend/build_and_push_frontend.Dockerfile \ . - test-multi-arch-build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - id: qemu - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - push: false - file: ./docker/build_and_push.Dockerfile - platforms: "linux/amd64,linux/arm64/v8" - tags: langflowai/langflow:latest-dev diff --git a/docker/build_and_push.Dockerfile b/docker/build_and_push.Dockerfile index 43d9a0272..79f6ac8d7 100644 --- a/docker/build_and_push.Dockerfile +++ b/docker/build_and_push.Dockerfile @@ -7,8 +7,9 @@ # Used to build deps + create our virtual environment ################################ -# force platform to the current architecture to increase build speed time on multi-platform builds -FROM --platform=$BUILDPLATFORM python:3.12-slim as builder-base +# 1. use python:3.12.3-slim as the base image until https://github.com/pydantic/pydantic-core/issues/1292 gets resolved +# 2. do not add --platform=$BUILDPLATFORM because the pydantic binaries must be resolved for the final architecture +FROM python:3.12.3-slim as builder-base ENV PYTHONDONTWRITEBYTECODE=1 \ \ @@ -51,15 +52,27 @@ COPY pyproject.toml poetry.lock README.md ./ COPY src/ ./src COPY scripts/ ./scripts RUN python -m pip install requests --user && cd ./scripts && python update_dependencies.py + +# 1. Install the dependencies using the current poetry.lock file to create reproducible builds +# 2. Do not install dev dependencies +# 3. Install all the extras to ensure all optionals are installed as well +# 4. --sync to ensure nothing else is in the environment +# 5. Build the wheel and install "langflow" package (mainly for version) + +# Note: moving to build and installing the wheel will make the docker images not reproducible. RUN $POETRY_HOME/bin/poetry lock --no-update \ + # install current lock file with fixed dependencies versions \ + # do not install dev dependencies \ + && $POETRY_HOME/bin/poetry install --without dev --sync -E deploy -E couchbase -E cassio \ && $POETRY_HOME/bin/poetry build -f wheel \ - && $POETRY_HOME/bin/poetry run pip install dist/*.whl --force-reinstall + && $POETRY_HOME/bin/poetry run pip install dist/*.whl ################################ # RUNTIME # Setup user, utilities and copy the virtual environment only ################################ -FROM python:3.12-slim as runtime +# 1. use python:3.12.3-slim as the base image until https://github.com/pydantic/pydantic-core/issues/1292 gets resolved +FROM python:3.12.3-slim as runtime RUN apt-get -y update \ && apt-get install --no-install-recommends -y \ diff --git a/docker/build_and_push_base.Dockerfile b/docker/build_and_push_base.Dockerfile index f70a517da..916531df2 100644 --- a/docker/build_and_push_base.Dockerfile +++ b/docker/build_and_push_base.Dockerfile @@ -10,7 +10,9 @@ # PYTHON-BASE # Sets up all our shared environment variables ################################ -FROM python:3.12-slim as python-base + +# use python:3.12.3-slim as the base image until https://github.com/pydantic/pydantic-core/issues/1292 gets resolved +FROM python:3.12.3-slim as python-base # python ENV PYTHONUNBUFFERED=1 \ diff --git a/docker/frontend/build_and_push_frontend.Dockerfile b/docker/frontend/build_and_push_frontend.Dockerfile index 46c5ffdeb..55f570187 100644 --- a/docker/frontend/build_and_push_frontend.Dockerfile +++ b/docker/frontend/build_and_push_frontend.Dockerfile @@ -5,7 +5,7 @@ # BUILDER-BASE ################################ -# force platform to the current architecture to increase build speed time on multi-platform builds +# 1. force platform to the current architecture to increase build speed time on multi-platform builds FROM --platform=$BUILDPLATFORM node:lts-bookworm-slim as builder-base COPY src/frontend /frontend From 7fc92e2e9c63e6dfac31bf6960d7ce2ffb594b31 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Wed, 12 Jun 2024 10:59:22 -0300 Subject: [PATCH 04/68] =?UTF-8?q?=F0=9F=90=9B=20(flowSettingsModal):=20set?= =?UTF-8?q?=20isSaving=20to=20false=20on=20error=20to=20fix=20loading=20st?= =?UTF-8?q?ate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/modals/flowSettingsModal/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx index 022bd9aa0..f8d62b7fb 100644 --- a/src/frontend/src/modals/flowSettingsModal/index.tsx +++ b/src/frontend/src/modals/flowSettingsModal/index.tsx @@ -40,6 +40,7 @@ export default function FlowSettingsModal({ list: [err?.response?.data.detail ?? ""], }); console.error(err); + setIsSaving(false); }); } From b48caa0b82957b34749b09e443b1e83ceee589fb Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Wed, 12 Jun 2024 12:10:50 -0300 Subject: [PATCH 05/68] fix outdated component on memory conversation --- .../Langflow Memory Conversation.json | 351 ++++--------- .../VectorStore-RAG-Flows.json | 479 ++++-------------- 2 files changed, 218 insertions(+), 612 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json index d4c44a2ef..34a0ed884 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json @@ -5,176 +5,131 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], + "baseClasses": ["str", "Text", "object"], "dataType": "MemoryComponent", - "id": "MemoryComponent-cdA1J" + "id": "MemoryComponent-iDkC0" }, "targetHandle": { "fieldName": "context", - "id": "Prompt-ODkUx", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "id": "Prompt-19lCm", + "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], "type": "str" } }, - "id": "reactflow__edge-MemoryComponent-cdA1J{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}-Prompt-ODkUx{œfieldNameœ:œcontextœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-MemoryComponent-iDkC0{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-iDkC0œ}-Prompt-19lCm{œfieldNameœ:œcontextœ,œidœ:œPrompt-19lCmœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "selected": false, - "source": "MemoryComponent-cdA1J", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œMemoryComponentœ, œidœ: œMemoryComponent-cdA1Jœ}", + "source": "MemoryComponent-iDkC0", + "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œMemoryComponentœ, œidœ: œMemoryComponent-iDkC0œ}", "style": { "stroke": "#555" }, - "target": "Prompt-ODkUx", - "targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-ODkUxœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "target": "Prompt-19lCm", + "targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-19lCmœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" }, { - "className": "stroke-gray-900 stroke-connection", + "className": "", "data": { "sourceHandle": { - "baseClasses": [ - "Text", - "object", - "Record", - "str" - ], + "baseClasses": ["Text", "object", "Record", "str"], "dataType": "ChatInput", - "id": "ChatInput-t7F8v" + "id": "ChatInput-Z6G1Z" }, "targetHandle": { "fieldName": "user_message", - "id": "Prompt-ODkUx", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "id": "Prompt-19lCm", + "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], "type": "str" } }, - "id": "reactflow__edge-ChatInput-t7F8v{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-t7F8vœ}-Prompt-ODkUx{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ChatInput-Z6G1Z{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-Z6G1Zœ}-Prompt-19lCm{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-19lCmœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-t7F8v", - "sourceHandle": "{œbaseClassesœ: [œTextœ, œobjectœ, œRecordœ, œstrœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-t7F8vœ}", + "source": "ChatInput-Z6G1Z", + "sourceHandle": "{œbaseClassesœ: [œTextœ, œobjectœ, œRecordœ, œstrœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-Z6G1Zœ}", "style": { "stroke": "#555" }, - "target": "Prompt-ODkUx", - "targetHandle": "{œfieldNameœ: œuser_messageœ, œidœ: œPrompt-ODkUxœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "target": "Prompt-19lCm", + "targetHandle": "{œfieldNameœ: œuser_messageœ, œidœ: œPrompt-19lCmœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" }, { - "className": "stroke-gray-900 stroke-connection", + "className": "", "data": { "sourceHandle": { - "baseClasses": [ - "Text", - "str", - "object" - ], + "baseClasses": ["Text", "str", "object"], "dataType": "Prompt", - "id": "Prompt-ODkUx" + "id": "Prompt-19lCm" }, "targetHandle": { "fieldName": "input_value", - "id": "OpenAIModel-9RykF", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], + "id": "OpenAIModel-1v5Hz", + "inputTypes": ["Text", "Record", "Prompt"], "type": "str" } }, - "id": "reactflow__edge-Prompt-ODkUx{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-ODkUxœ}-OpenAIModel-9RykF{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-9RykFœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", - "source": "Prompt-ODkUx", - "sourceHandle": "{œbaseClassesœ: [œTextœ, œstrœ, œobjectœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-ODkUxœ}", + "id": "reactflow__edge-Prompt-19lCm{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-19lCmœ}-OpenAIModel-1v5Hz{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-1v5Hzœ,œinputTypesœ:[œTextœ,œRecordœ,œPromptœ],œtypeœ:œstrœ}", + "source": "Prompt-19lCm", + "sourceHandle": "{œbaseClassesœ: [œTextœ, œstrœ, œobjectœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-19lCmœ}", "style": { "stroke": "#555" }, - "target": "OpenAIModel-9RykF", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-9RykFœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" + "target": "OpenAIModel-1v5Hz", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-1v5Hzœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" }, { - "className": "stroke-gray-900 stroke-connection", + "className": "", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "object", - "Text" - ], + "baseClasses": ["str", "object", "Text"], "dataType": "OpenAIModel", - "id": "OpenAIModel-9RykF" + "id": "OpenAIModel-1v5Hz" }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-P1jEe", - "inputTypes": [ - "Text" - ], + "id": "ChatOutput-i56kN", + "inputTypes": ["Text"], "type": "str" } }, - "id": "reactflow__edge-OpenAIModel-9RykF{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-9RykFœ}-ChatOutput-P1jEe{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-P1jEeœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", - "source": "OpenAIModel-9RykF", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œobjectœ, œTextœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-9RykFœ}", + "id": "reactflow__edge-OpenAIModel-1v5Hz{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-1v5Hzœ}-ChatOutput-i56kN{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-i56kNœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", + "source": "OpenAIModel-1v5Hz", + "sourceHandle": "{œbaseClassesœ: [œstrœ, œobjectœ, œTextœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-1v5Hzœ}", "style": { "stroke": "#555" }, - "target": "ChatOutput-P1jEe", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-P1jEeœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" + "target": "ChatOutput-i56kN", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-i56kNœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" }, { - "className": "stroke-foreground stroke-connection", + "className": "", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], + "baseClasses": ["str", "Text", "object"], "dataType": "MemoryComponent", - "id": "MemoryComponent-cdA1J" + "id": "MemoryComponent-iDkC0" }, "targetHandle": { "fieldName": "input_value", - "id": "TextOutput-vrs6T", - "inputTypes": [ - "Record", - "Text" - ], + "id": "TextOutput-KuLNK", + "inputTypes": ["Record", "Text"], "type": "str" } }, - "id": "reactflow__edge-MemoryComponent-cdA1J{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}-TextOutput-vrs6T{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-vrs6Tœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", - "source": "MemoryComponent-cdA1J", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œMemoryComponentœ, œidœ: œMemoryComponent-cdA1Jœ}", + "id": "reactflow__edge-MemoryComponent-iDkC0{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-iDkC0œ}-TextOutput-KuLNK{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-KuLNKœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", + "source": "MemoryComponent-iDkC0", + "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œMemoryComponentœ, œidœ: œMemoryComponent-iDkC0œ}", "style": { "stroke": "#555" }, - "target": "TextOutput-vrs6T", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œTextOutput-vrs6Tœ, œinputTypesœ: [œRecordœ, œTextœ], œtypeœ: œstrœ}" + "target": "TextOutput-KuLNK", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œTextOutput-KuLNKœ, œinputTypesœ: [œRecordœ, œTextœ], œtypeœ: œstrœ}" } ], "nodes": [ { "data": { - "id": "ChatInput-t7F8v", + "id": "ChatInput-Z6G1Z", "node": { - "base_classes": [ - "Text", - "object", - "Record", - "str" - ], + "base_classes": ["Text", "object", "Record", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -190,10 +145,7 @@ "field_order": [], "frozen": false, "icon": "ChatInput", - "output_types": [ - "Message", - "Text" - ], + "output_types": ["Message", "Text"], "template": { "_type": "CustomComponent", "code": { @@ -241,17 +193,12 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -267,9 +214,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -289,9 +234,7 @@ "fileTypes": [], "file_path": "", "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -310,7 +253,7 @@ }, "dragging": false, "height": 469, - "id": "ChatInput-t7F8v", + "id": "ChatInput-Z6G1Z", "position": { "x": 1283.2700598313072, "y": 982.5953650473145 @@ -325,14 +268,9 @@ }, { "data": { - "id": "ChatOutput-P1jEe", + "id": "ChatOutput-i56kN", "node": { - "base_classes": [ - "Text", - "object", - "Record", - "str" - ], + "base_classes": ["Text", "object", "Record", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -348,10 +286,7 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" - ], + "output_types": ["Message", "Text"], "template": { "_type": "CustomComponent", "code": { @@ -379,9 +314,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, @@ -400,17 +333,12 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -426,9 +354,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -448,9 +374,7 @@ "fileTypes": [], "file_path": "", "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -469,7 +393,7 @@ }, "dragging": false, "height": 477, - "id": "ChatOutput-P1jEe", + "id": "ChatOutput-i56kN", "position": { "x": 3154.916355514023, "y": 851.051882666333 @@ -486,13 +410,9 @@ "data": { "description": "Retrieves stored chat messages given a specific Session ID.", "display_name": "Chat Memory", - "id": "MemoryComponent-cdA1J", + "id": "MemoryComponent-iDkC0", "node": { - "base_classes": [ - "str", - "Text", - "object" - ], + "base_classes": ["str", "Text", "object"], "beta": true, "custom_fields": { "n_messages": null, @@ -509,9 +429,7 @@ "field_order": [], "frozen": false, "icon": "history", - "output_types": [ - "Text" - ], + "output_types": ["Text"], "template": { "_type": "CustomComponent", "code": { @@ -558,17 +476,12 @@ "fileTypes": [], "file_path": "", "info": "Order of the messages.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "order", - "options": [ - "Ascending", - "Descending" - ], + "options": ["Ascending", "Descending"], "password": false, "placeholder": "", "required": false, @@ -584,9 +497,7 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, @@ -606,18 +517,12 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" - ], + "options": ["Machine", "User", "Machine and User"], "password": false, "placeholder": "", "required": false, @@ -633,9 +538,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -654,9 +557,7 @@ "fileTypes": [], "file_path": "", "info": "Session ID of the chat history.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -675,7 +576,7 @@ }, "dragging": false, "height": 489, - "id": "MemoryComponent-cdA1J", + "id": "MemoryComponent-iDkC0", "position": { "x": 1289.9606870058817, "y": 442.16804561053766 @@ -692,19 +593,12 @@ "data": { "description": "A component for creating prompt templates using dynamic variables.", "display_name": "Prompt", - "id": "Prompt-ODkUx", + "id": "Prompt-19lCm", "node": { - "base_classes": [ - "Text", - "str", - "object" - ], + "base_classes": ["Text", "str", "object"], "beta": false, "custom_fields": { - "template": [ - "context", - "user_message" - ] + "template": ["context", "user_message"] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", @@ -719,9 +613,7 @@ "is_input": null, "is_output": null, "name": "", - "output_types": [ - "Prompt" - ], + "output_types": ["Prompt"], "template": { "_type": "CustomComponent", "code": { @@ -775,9 +667,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -822,7 +712,7 @@ }, "dragging": false, "height": 477, - "id": "Prompt-ODkUx", + "id": "Prompt-19lCm", "position": { "x": 1894.594426342426, "y": 753.3797365481901 @@ -837,13 +727,9 @@ }, { "data": { - "id": "OpenAIModel-9RykF", + "id": "OpenAIModel-1v5Hz", "node": { - "base_classes": [ - "str", - "object", - "Text" - ], + "base_classes": ["str", "object", "Text"], "beta": false, "custom_fields": { "input_value": null, @@ -873,9 +759,7 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": [ - "Text" - ], + "output_types": ["Text"], "template": { "_type": "CustomComponent", "code": { @@ -903,11 +787,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], + "input_types": ["Text", "Record", "Prompt"], "list": false, "load_from_db": false, "multiline": false, @@ -964,9 +844,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -993,9 +871,7 @@ "fileTypes": [], "file_path": "", "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.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1014,9 +890,7 @@ "fileTypes": [], "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, @@ -1027,7 +901,7 @@ "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "stream": { "advanced": true, @@ -1055,9 +929,7 @@ "fileTypes": [], "file_path": "", "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1100,7 +972,7 @@ }, "dragging": false, "height": 563, - "id": "OpenAIModel-9RykF", + "id": "OpenAIModel-1v5Hz", "position": { "x": 2561.5850334731617, "y": 553.2745131130916 @@ -1115,13 +987,12 @@ }, { "data": { - "id": "TextOutput-vrs6T", + "description": "Display a text output in the Playground.", + "display_name": "Inspect Memory", + "edited": false, + "id": "TextOutput-KuLNK", "node": { - "base_classes": [ - "str", - "object", - "Text" - ], + "base_classes": ["object", "str", "Text"], "beta": false, "custom_fields": { "input_value": null, @@ -1130,13 +1001,12 @@ "description": "Display a text output in the Playground.", "display_name": "Inspect Memory", "documentation": "", + "edited": true, "field_formatters": {}, "field_order": [], "frozen": false, "icon": "type", - "output_types": [ - "Text" - ], + "output_types": ["Text"], "template": { "_type": "CustomComponent", "code": { @@ -1155,19 +1025,16 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextOutput(TextComponent):\n display_name = \"Text Output\"\n description = \"Display a text output in the Playground.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as output.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(self, input_value: Optional[Text] = \"\", record_template: str = \"\") -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n" + "value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextOutput(TextComponent):\n display_name = \"Text Output\"\n description = \"Display a text output in the Playground.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Text\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as output.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(self, input_value: Optional[Text] = \"\", record_template: Optional[str] = \"\") -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n" }, "input_value": { "advanced": false, - "display_name": "Value", + "display_name": "Text", "dynamic": false, "fileTypes": [], "file_path": "", "info": "Text or Record to be passed as output.", - "input_types": [ - "Record", - "Text" - ], + "input_types": ["Record", "Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1187,9 +1054,7 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, @@ -1208,7 +1073,7 @@ }, "dragging": false, "height": 289, - "id": "TextOutput-vrs6T", + "id": "TextOutput-KuLNK", "position": { "x": 1911.4785906252087, "y": 247.39079954376987 @@ -1231,8 +1096,8 @@ "description": "This project can be used as a starting point for building a Chat experience with user specific memory. You can set a different Session ID to start a new message history.", "icon": "🤖", "icon_bg_color": "#FFD700", - "id": "08d5cccf-d098-4367-b14b-1078429c9ed9", + "id": "edee50d3-a7e7-4cc7-9448-cc662300e8fb", "is_component": false, - "last_tested_version": "1.0.0a0", + "last_tested_version": "1.0.0a52", "name": "Memory Chatbot" -} \ No newline at end of file +} diff --git a/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json b/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json index c5698a12e..6247f0c5d 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json @@ -5,23 +5,14 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "Text", - "str" - ], + "baseClasses": ["object", "Text", "str"], "dataType": "TextOutput", "id": "TextOutput-BDknO" }, "targetHandle": { "fieldName": "context", "id": "Prompt-xeI6K", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], "type": "str" } }, @@ -39,24 +30,14 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Text", - "str", - "object", - "Record" - ], + "baseClasses": ["Text", "str", "object", "Record"], "dataType": "ChatInput", "id": "ChatInput-yxMKE" }, "targetHandle": { "fieldName": "question", "id": "Prompt-xeI6K", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], "type": "str" } }, @@ -74,22 +55,14 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "Text", - "str" - ], + "baseClasses": ["object", "Text", "str"], "dataType": "Prompt", "id": "Prompt-xeI6K" }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-EjXlN", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], + "inputTypes": ["Text", "Record", "Prompt"], "type": "str" } }, @@ -107,20 +80,14 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "Text", - "str" - ], + "baseClasses": ["object", "Text", "str"], "dataType": "OpenAIModel", "id": "OpenAIModel-EjXlN" }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-Q39I8", - "inputTypes": [ - "Text" - ], + "inputTypes": ["Text"], "type": "str" } }, @@ -138,19 +105,14 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Record" - ], + "baseClasses": ["Record"], "dataType": "File", "id": "File-t0a6a" }, "targetHandle": { "fieldName": "inputs", "id": "RecursiveCharacterTextSplitter-tR9QM", - "inputTypes": [ - "Document", - "Record" - ], + "inputTypes": ["Document", "Record"], "type": "Document" } }, @@ -168,9 +130,7 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Embeddings" - ], + "baseClasses": ["Embeddings"], "dataType": "OpenAIEmbeddings", "id": "OpenAIEmbeddings-ZlOk1" }, @@ -194,21 +154,14 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Text", - "str", - "object", - "Record" - ], + "baseClasses": ["Text", "str", "object", "Record"], "dataType": "ChatInput", "id": "ChatInput-yxMKE" }, "targetHandle": { "fieldName": "input_value", "id": "AstraDBSearch-41nRz", - "inputTypes": [ - "Text" - ], + "inputTypes": ["Text"], "type": "str" } }, @@ -225,9 +178,7 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Record" - ], + "baseClasses": ["Record"], "dataType": "RecursiveCharacterTextSplitter", "id": "RecursiveCharacterTextSplitter-tR9QM" }, @@ -252,9 +203,7 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Embeddings" - ], + "baseClasses": ["Embeddings"], "dataType": "OpenAIEmbeddings", "id": "OpenAIEmbeddings-9TPjc" }, @@ -279,19 +228,14 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Record" - ], + "baseClasses": ["Record"], "dataType": "AstraDBSearch", "id": "AstraDBSearch-41nRz" }, "targetHandle": { "fieldName": "input_value", "id": "TextOutput-BDknO", - "inputTypes": [ - "Record", - "Text" - ], + "inputTypes": ["Record", "Text"], "type": "str" } }, @@ -310,12 +254,7 @@ "data": { "id": "ChatInput-yxMKE", "node": { - "base_classes": [ - "Text", - "str", - "object", - "Record" - ], + "base_classes": ["Text", "str", "object", "Record"], "beta": false, "custom_fields": { "input_value": null, @@ -331,10 +270,7 @@ "field_order": [], "frozen": false, "icon": "ChatInput", - "output_types": [ - "Message", - "Text" - ], + "output_types": ["Message", "Text"], "template": { "_type": "CustomComponent", "code": { @@ -382,17 +318,12 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -408,9 +339,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -430,9 +359,7 @@ "fileTypes": [], "file_path": "", "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -462,11 +389,7 @@ "data": { "id": "TextOutput-BDknO", "node": { - "base_classes": [ - "object", - "Text", - "str" - ], + "base_classes": ["object", "Text", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -479,9 +402,7 @@ "field_order": [], "frozen": false, "icon": "type", - "output_types": [ - "Text" - ], + "output_types": ["Text"], "template": { "_type": "CustomComponent", "code": { @@ -509,10 +430,7 @@ "fileTypes": [], "file_path": "", "info": "Text or Record to be passed as output.", - "input_types": [ - "Record", - "Text" - ], + "input_types": ["Record", "Text"], "list": false, "load_from_db": false, "multiline": false, @@ -532,9 +450,7 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, @@ -570,9 +486,7 @@ "data": { "id": "OpenAIEmbeddings-ZlOk1", "node": { - "base_classes": [ - "Embeddings" - ], + "base_classes": ["Embeddings"], "beta": false, "custom_fields": { "allowed_special": null, @@ -604,9 +518,7 @@ "field_formatters": {}, "field_order": [], "frozen": false, - "output_types": [ - "Embeddings" - ], + "output_types": ["Embeddings"], "template": { "_type": "CustomComponent", "allowed_special": { @@ -616,9 +528,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -712,9 +622,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -734,9 +642,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -747,9 +653,7 @@ "show": true, "title_case": false, "type": "str", - "value": [ - "all" - ] + "value": ["all"] }, "embedding_ctx_length": { "advanced": true, @@ -796,9 +700,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -842,9 +744,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -863,9 +763,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, @@ -885,9 +783,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -906,9 +802,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -927,9 +821,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -948,9 +840,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1050,9 +940,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1083,11 +971,7 @@ "data": { "id": "OpenAIModel-EjXlN", "node": { - "base_classes": [ - "object", - "Text", - "str" - ], + "base_classes": ["object", "Text", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -1117,9 +1001,7 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": [ - "Text" - ], + "output_types": ["Text"], "template": { "_type": "CustomComponent", "code": { @@ -1147,11 +1029,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], + "input_types": ["Text", "Record", "Prompt"], "list": false, "load_from_db": false, "multiline": false, @@ -1208,9 +1086,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -1237,9 +1113,7 @@ "fileTypes": [], "file_path": "", "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.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1258,9 +1132,7 @@ "fileTypes": [], "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, @@ -1299,9 +1171,7 @@ "fileTypes": [], "file_path": "", "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1363,17 +1233,10 @@ "display_name": "Prompt", "id": "Prompt-xeI6K", "node": { - "base_classes": [ - "object", - "Text", - "str" - ], + "base_classes": ["object", "Text", "str"], "beta": false, "custom_fields": { - "template": [ - "context", - "question" - ] + "template": ["context", "question"] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", @@ -1388,9 +1251,7 @@ "is_input": null, "is_output": null, "name": "", - "output_types": [ - "Prompt" - ], + "output_types": ["Prompt"], "template": { "_type": "CustomComponent", "code": { @@ -1470,9 +1331,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1508,12 +1367,7 @@ "data": { "id": "ChatOutput-Q39I8", "node": { - "base_classes": [ - "object", - "Text", - "Record", - "str" - ], + "base_classes": ["object", "Text", "Record", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -1530,10 +1384,7 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" - ], + "output_types": ["Message", "Text"], "template": { "_type": "CustomComponent", "code": { @@ -1561,9 +1412,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, @@ -1582,17 +1431,12 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -1608,9 +1452,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1630,9 +1472,7 @@ "fileTypes": [], "file_path": "", "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1667,9 +1507,7 @@ "data": { "id": "File-t0a6a", "node": { - "base_classes": [ - "Record" - ], + "base_classes": ["Record"], "beta": false, "custom_fields": { "path": null, @@ -1682,9 +1520,7 @@ "field_order": [], "frozen": false, "icon": "file-text", - "output_types": [ - "Record" - ], + "output_types": ["Record"], "template": { "_type": "CustomComponent", "code": { @@ -1785,9 +1621,7 @@ "data": { "id": "RecursiveCharacterTextSplitter-tR9QM", "node": { - "base_classes": [ - "Record" - ], + "base_classes": ["Record"], "beta": false, "custom_fields": { "chunk_overlap": null, @@ -1801,9 +1635,7 @@ "field_formatters": {}, "field_order": [], "frozen": false, - "output_types": [ - "Record" - ], + "output_types": ["Record"], "template": { "_type": "CustomComponent", "chunk_overlap": { @@ -1869,10 +1701,7 @@ "fileTypes": [], "file_path": "", "info": "The texts to split.", - "input_types": [ - "Document", - "Record" - ], + "input_types": ["Document", "Record"], "list": true, "load_from_db": false, "multiline": false, @@ -1891,9 +1720,7 @@ "fileTypes": [], "file_path": "", "info": "The characters to split on.\nIf left empty defaults to [\"\\n\\n\", \"\\n\", \" \", \"\"].", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -1904,9 +1731,7 @@ "show": true, "title_case": false, "type": "str", - "value": [ - "" - ] + "value": [""] } } }, @@ -1931,9 +1756,7 @@ "data": { "id": "AstraDBSearch-41nRz", "node": { - "base_classes": [ - "Record" - ], + "base_classes": ["Record"], "beta": false, "custom_fields": { "api_endpoint": null, @@ -1968,9 +1791,7 @@ ], "frozen": false, "icon": "AstraDB", - "output_types": [ - "Record" - ], + "output_types": ["Record"], "template": { "_type": "CustomComponent", "api_endpoint": { @@ -1980,9 +1801,7 @@ "fileTypes": [], "file_path": "", "info": "API endpoint URL for the Astra DB service.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, @@ -2083,7 +1902,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import List, Optional\n\nfrom langflow.components.vectorstores.AstraDB import AstraDBVectorStoreComponent\nfrom langflow.components.vectorstores.base.model import LCVectorStoreComponent\nfrom langflow.field_typing import Embeddings, Text\nfrom langflow.schema import Record\n\n\nclass AstraDBSearchComponent(LCVectorStoreComponent):\n display_name = \"Astra DB Search\"\n description = \"Searches an existing Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"input_value\", \"embedding\"]\n\n def build_config(self):\n return {\n \"search_type\": {\n \"display_name\": \"Search Type\",\n \"options\": [\"Similarity\", \"MMR\"],\n },\n \"input_value\": {\n \"display_name\": \"Input Value\",\n \"info\": \"Input value to search\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of records to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing records.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n \"number_of_results\": {\n \"display_name\": \"Number of Results\",\n \"info\": \"Number of results to return.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n collection_name: str,\n input_value: Text,\n token: str,\n api_endpoint: str,\n search_type: str = \"Similarity\",\n number_of_results: int = 4,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> List[Record]:\n vector_store = AstraDBVectorStoreComponent().build(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n try:\n return self.search_with_vector_store(input_value, search_type, vector_store, k=number_of_results)\n except KeyError as e:\n if \"content\" in str(e):\n raise ValueError(\n \"You should ingest data through Langflow (or LangChain) to query it in Langflow. Your collection does not contain a field name 'content'.\"\n )\n else:\n raise e\n" + "value": "from typing import List, Optional\n\nfrom langflow.components.vectorstores.AstraDB import AstraDBVectorStoreComponent\nfrom langflow.components.vectorstores.base.model import LCVectorStoreComponent\nfrom langflow.field_typing import Embeddings, Text\nfrom langflow.schema import Record\n\n\nclass AstraDBSearchComponent(LCVectorStoreComponent):\n display_name = \"Astra DB Search\"\n description = \"Searches an existing Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"input_value\", \"embedding\"]\n\n def build_config(self):\n return {\n \"search_type\": {\n \"display_name\": \"Search Type\",\n \"options\": [\"Similarity\", \"MMR\"],\n },\n \"input_value\": {\n \"display_name\": \"Input Value\",\n \"info\": \"Input value to search\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Astra DB Application Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of records to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing records.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n \"number_of_results\": {\n \"display_name\": \"Number of Results\",\n \"info\": \"Number of results to return.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n collection_name: str,\n input_value: Text,\n token: str,\n api_endpoint: str,\n search_type: str = \"Similarity\",\n number_of_results: int = 4,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> List[Record]:\n vector_store = AstraDBVectorStoreComponent().build(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n try:\n return self.search_with_vector_store(input_value, search_type, vector_store, k=number_of_results)\n except KeyError as e:\n if \"content\" in str(e):\n raise ValueError(\n \"You should ingest data through Langflow (or LangChain) to query it in Langflow. Your collection does not contain a field name 'content'.\"\n )\n else:\n raise e\n" }, "collection_indexing_policy": { "advanced": true, @@ -2110,9 +1929,7 @@ "fileTypes": [], "file_path": "", "info": "The name of the collection within Astra DB where the vectors will be stored.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2150,9 +1967,7 @@ "fileTypes": [], "file_path": "", "info": "Input value to search", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2171,9 +1986,7 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to exclude from the indexing.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -2192,9 +2005,7 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to include in the indexing.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -2213,9 +2024,7 @@ "fileTypes": [], "file_path": "", "info": "Optional distance metric for vector comparisons in the vector store.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2234,9 +2043,7 @@ "fileTypes": [], "file_path": "", "info": "Optional namespace within Astra DB to use for the collection.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2293,17 +2100,12 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "search_type", - "options": [ - "Similarity", - "MMR" - ], + "options": ["Similarity", "MMR"], "password": false, "placeholder": "", "required": false, @@ -2319,18 +2121,12 @@ "fileTypes": [], "file_path": "", "info": "Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "setup_mode", - "options": [ - "Sync", - "Async", - "Off" - ], + "options": ["Sync", "Async", "Off"], "password": false, "placeholder": "", "required": false, @@ -2341,14 +2137,12 @@ }, "token": { "advanced": false, - "display_name": "Token", + "display_name": "Astra DB Application Token", "dynamic": false, "fileTypes": [], "file_path": "", "info": "Authentication token for accessing Astra DB.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, @@ -2384,9 +2178,7 @@ "data": { "id": "AstraDB-eUCSS", "node": { - "base_classes": [ - "VectorStore" - ], + "base_classes": ["VectorStore"], "beta": false, "custom_fields": { "api_endpoint": null, @@ -2419,10 +2211,7 @@ ], "frozen": false, "icon": "AstraDB", - "output_types": [ - "VectorStore", - "BaseRetriever" - ], + "output_types": ["VectorStore", "BaseRetriever"], "template": { "_type": "CustomComponent", "api_endpoint": { @@ -2432,9 +2221,7 @@ "fileTypes": [], "file_path": "", "info": "API endpoint URL for the Astra DB service.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, @@ -2535,7 +2322,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import List, Optional, Union\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Embeddings, VectorStore\nfrom langflow.schema import Record\nfrom langchain_core.retrievers import BaseRetriever\n\n\nclass AstraDBVectorStoreComponent(CustomComponent):\n display_name = \"Astra DB\"\n description = \"Builds or loads an Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"inputs\", \"embedding\"]\n\n def build_config(self):\n return {\n \"inputs\": {\n \"display_name\": \"Inputs\",\n \"info\": \"Optional list of records to be processed and stored in the vector store.\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of records to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing records.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n token: str,\n api_endpoint: str,\n collection_name: str,\n inputs: Optional[List[Record]] = None,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> Union[VectorStore, BaseRetriever]:\n try:\n from langchain_astradb import AstraDBVectorStore\n from langchain_astradb.utils.astradb import SetupMode\n except ImportError:\n raise ImportError(\n \"Could not import langchain Astra DB integration package. \"\n \"Please install it with `pip install langchain-astradb`.\"\n )\n\n try:\n setup_mode_value = SetupMode[setup_mode.upper()]\n except KeyError:\n raise ValueError(f\"Invalid setup mode: {setup_mode}\")\n if inputs:\n documents = [_input.to_lc_document() for _input in inputs]\n\n vector_store = AstraDBVectorStore.from_documents(\n documents=documents,\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n else:\n vector_store = AstraDBVectorStore(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n\n return vector_store\n return vector_store\n" + "value": "from typing import List, Optional, Union\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Embeddings, VectorStore\nfrom langflow.schema import Record\nfrom langchain_core.retrievers import BaseRetriever\n\n\nclass AstraDBVectorStoreComponent(CustomComponent):\n display_name = \"Astra DB\"\n description = \"Builds or loads an Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"inputs\", \"embedding\"]\n\n def build_config(self):\n return {\n \"inputs\": {\n \"display_name\": \"Inputs\",\n \"info\": \"Optional list of records to be processed and stored in the vector store.\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Astra DB Application Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of records to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing records.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n token: str,\n api_endpoint: str,\n collection_name: str,\n inputs: Optional[List[Record]] = None,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> Union[VectorStore, BaseRetriever]:\n try:\n from langchain_astradb import AstraDBVectorStore\n from langchain_astradb.utils.astradb import SetupMode\n except ImportError:\n raise ImportError(\n \"Could not import langchain Astra DB integration package. \"\n \"Please install it with `pip install langchain-astradb`.\"\n )\n\n try:\n setup_mode_value = SetupMode[setup_mode.upper()]\n except KeyError:\n raise ValueError(f\"Invalid setup mode: {setup_mode}\")\n if inputs:\n documents = [_input.to_lc_document() for _input in inputs]\n\n vector_store = AstraDBVectorStore.from_documents(\n documents=documents,\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n else:\n vector_store = AstraDBVectorStore(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n\n return vector_store\n return vector_store\n" }, "collection_indexing_policy": { "advanced": true, @@ -2562,9 +2349,7 @@ "fileTypes": [], "file_path": "", "info": "The name of the collection within Astra DB where the vectors will be stored.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2620,9 +2405,7 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to exclude from the indexing.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -2641,9 +2424,7 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to include in the indexing.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -2662,9 +2443,7 @@ "fileTypes": [], "file_path": "", "info": "Optional distance metric for vector comparisons in the vector store.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2683,9 +2462,7 @@ "fileTypes": [], "file_path": "", "info": "Optional namespace within Astra DB to use for the collection.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2723,18 +2500,12 @@ "fileTypes": [], "file_path": "", "info": "Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "setup_mode", - "options": [ - "Sync", - "Async", - "Off" - ], + "options": ["Sync", "Async", "Off"], "password": false, "placeholder": "", "required": false, @@ -2745,14 +2516,12 @@ }, "token": { "advanced": false, - "display_name": "Token", + "display_name": "Astra DB Application Token", "dynamic": false, "fileTypes": [], "file_path": "", "info": "Authentication token for accessing Astra DB.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, @@ -2788,9 +2557,7 @@ "data": { "id": "OpenAIEmbeddings-9TPjc", "node": { - "base_classes": [ - "Embeddings" - ], + "base_classes": ["Embeddings"], "beta": false, "custom_fields": { "allowed_special": null, @@ -2822,9 +2589,7 @@ "field_formatters": {}, "field_order": [], "frozen": false, - "output_types": [ - "Embeddings" - ], + "output_types": ["Embeddings"], "template": { "_type": "CustomComponent", "allowed_special": { @@ -2834,9 +2599,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2930,9 +2693,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2952,9 +2713,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2965,9 +2724,7 @@ "show": true, "title_case": false, "type": "str", - "value": [ - "all" - ] + "value": ["all"] }, "embedding_ctx_length": { "advanced": true, @@ -3014,9 +2771,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -3060,9 +2815,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3081,9 +2834,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, @@ -3103,9 +2854,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3124,9 +2873,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3145,9 +2892,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3166,9 +2911,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3268,9 +3011,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3313,4 +3054,4 @@ "is_component": false, "last_tested_version": "1.0.0a0", "name": "Vector Store RAG" -} \ No newline at end of file +} From e3d2b9ff28be07b1dca315bc717d672b5867be00 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Wed, 12 Jun 2024 12:26:36 -0300 Subject: [PATCH 06/68] chore: Update package versions in pyproject.toml and poetry.lock --- poetry.lock | 192 ++++++++++++++++---------------- pyproject.toml | 2 +- src/backend/base/poetry.lock | 12 +- src/backend/base/pyproject.toml | 2 +- 4 files changed, 104 insertions(+), 104 deletions(-) diff --git a/poetry.lock b/poetry.lock index 04e603184..0df7dac55 100644 --- a/poetry.lock +++ b/poetry.lock @@ -471,17 +471,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.123" +version = "1.34.124" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.123-py3-none-any.whl", hash = "sha256:56bec52d485d5670ce96d53ae7b2cd4ae4e8a705fb2298a21093cdd77d642331"}, - {file = "boto3-1.34.123.tar.gz", hash = "sha256:42b140fc850cf261ee4b1e8ef527fa071b1f1592a6d6a68d34b29f37cc46b4dd"}, + {file = "boto3-1.34.124-py3-none-any.whl", hash = "sha256:549d9735bbb7cf8e1a2f4b3b676be32946717c59f33c47234586835dc904d04e"}, + {file = "boto3-1.34.124.tar.gz", hash = "sha256:a91ee58fa54b170f17b2e144f038e155f92cf515f1c073ac2595e9ee45f125a8"}, ] [package.dependencies] -botocore = ">=1.34.123,<1.35.0" +botocore = ">=1.34.124,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -490,13 +490,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.123" +version = "1.34.124" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.123-py3-none-any.whl", hash = "sha256:8c34ada2a708c82e7174bff700611643db7ce2cb18f1130c35045c24310d299d"}, - {file = "botocore-1.34.123.tar.gz", hash = "sha256:a8577f6574600c4d159b5cd103ee05744a443d77f7778304e17307940b369c4f"}, + {file = "botocore-1.34.124-py3-none-any.whl", hash = "sha256:fede092c7f8f414f78f3e7991e47cb0cf2279d90c027606ad7cba79fa370b827"}, + {file = "botocore-1.34.124.tar.gz", hash = "sha256:3f0bf79c17d656acdfdb53581224f6a38867ff2829f7428c586198f67a90ea26"}, ] [package.dependencies] @@ -2438,8 +2438,8 @@ files = [ [package.dependencies] cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} greenlet = [ - {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, + {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, ] "zope.event" = "*" "zope.interface" = "*" @@ -2566,12 +2566,12 @@ files = [ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = [ - {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, + {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, ] grpcio-status = [ - {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, + {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, ] proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" @@ -2640,13 +2640,13 @@ httplib2 = ">=0.19.0" [[package]] name = "google-cloud-aiplatform" -version = "1.54.1" +version = "1.55.0" description = "Vertex AI API client library" optional = false python-versions = ">=3.8" files = [ - {file = "google-cloud-aiplatform-1.54.1.tar.gz", hash = "sha256:01c231961cc1a1a3b049ea3ef71fb11e77b2d56d632d020ce09e419b27ff77f2"}, - {file = "google_cloud_aiplatform-1.54.1-py2.py3-none-any.whl", hash = "sha256:43f70fcd572f15317d769e5a0e04cfb7c0e259ead3fe581d2fba4f203ace5617"}, + {file = "google-cloud-aiplatform-1.55.0.tar.gz", hash = "sha256:aa87cb6c49ae5fde87fb831ce8ad4a853c4656fe04babe505e9144c7a9e09c1a"}, + {file = "google_cloud_aiplatform-1.55.0-py2.py3-none-any.whl", hash = "sha256:c6cc76ca5537f4636a0c3f8c0288d2e0d2d86ef708e562d2654313e11d6ee46a"}, ] [package.dependencies] @@ -2667,9 +2667,9 @@ autologging = ["mlflow (>=1.27.0,<=2.1.1)"] cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] datasets = ["pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)"] endpoint = ["requests (>=2.28.1)"] -full = ["cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.109.1)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "nest-asyncio (>=1.0.0,<1.6.0)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] -langchain = ["langchain (>=0.1.16,<0.2)", "langchain-core (<0.2)", "langchain-google-vertexai (<2)"] -langchain-testing = ["absl-py", "cloudpickle (>=2.2.1,<4.0)", "langchain (>=0.1.16,<0.2)", "langchain-core (<0.2)", "langchain-google-vertexai (<2)", "pydantic (>=2.6.3,<3)", "pytest-xdist"] +full = ["cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.109.1)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "nest-asyncio (>=1.0.0,<1.6.0)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)"] +langchain = ["langchain (>=0.1.16,<0.3)", "langchain-core (<0.2)", "langchain-google-vertexai (<2)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)"] +langchain-testing = ["absl-py", "cloudpickle (>=2.2.1,<4.0)", "langchain (>=0.1.16,<0.3)", "langchain-core (<0.2)", "langchain-google-vertexai (<2)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "pytest-xdist"] lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"] metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"] pipelines = ["pyyaml (>=5.3.1,<7)"] @@ -2679,8 +2679,8 @@ private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] rapid-evaluation = ["nest-asyncio (>=1.0.0,<1.6.0)", "pandas (>=1.0.0,<2.2.0)"] ray = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "setuptools (<70.0.0)"] ray-testing = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pytest-xdist", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "ray[train] (==2.9.3)", "scikit-learn", "setuptools (<70.0.0)", "tensorflow", "torch (>=2.0.0,<2.1.0)", "xgboost", "xgboost-ray"] -reasoningengine = ["cloudpickle (>=2.2.1,<4.0)", "pydantic (>=2.6.3,<3)"] -tensorboard = ["tensorflow (>=2.3.0,<3.0.0dev)"] +reasoningengine = ["cloudpickle (>=2.2.1,<4.0)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)"] +tensorboard = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] testing = ["bigframes", "cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.109.1)", "google-api-core (>=2.11,<3.0.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "grpcio-testing", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "ipython", "kfp (>=2.6.0,<3.0.0)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "nest-asyncio (>=1.0.0,<1.6.0)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pyfakefs", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "requests-toolbelt (<1.0.0)", "scikit-learn", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (==2.13.0)", "tensorflow (==2.16.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "torch (>=2.0.0,<2.1.0)", "torch (>=2.2.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] vizier = ["google-vizier (>=0.1.6)"] xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] @@ -3025,13 +3025,13 @@ test = ["objgraph", "psutil"] [[package]] name = "groq" -version = "0.8.0" +version = "0.9.0" description = "The official Python library for the groq API" optional = false python-versions = ">=3.7" files = [ - {file = "groq-0.8.0-py3-none-any.whl", hash = "sha256:f5e4e892d45001241a930db451e633ca1f0007e3f749deaa5d7360062fcd61e3"}, - {file = "groq-0.8.0.tar.gz", hash = "sha256:37ceb2f706bd516d0bfcac8e89048a24b375172987a0d6bd9efb521c54f6deff"}, + {file = "groq-0.9.0-py3-none-any.whl", hash = "sha256:d0e46f4ad645504672bb09c8100af3ced3a7db0d5119dc13e4aca535fc455874"}, + {file = "groq-0.9.0.tar.gz", hash = "sha256:130ed5e35d3acfaab46b9e7a078eeaebf91052f4a9d71f86f87fb319b5fec332"}, ] [package.dependencies] @@ -3772,72 +3772,72 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jiter" -version = "0.4.1" +version = "0.4.2" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.8" files = [ - {file = "jiter-0.4.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3c2370cd8826b484f3fc6ed729cb58510ba24b4bc277c92323a57d35cf4df223"}, - {file = "jiter-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3587af23140a2eb282bba980010dae60f3b8b1579a034c5e869e9b94220a5972"}, - {file = "jiter-0.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df8788d34545d47de864032a78bae49a14b66b67196c73cd95f1c1e3081d9c73"}, - {file = "jiter-0.4.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91bf2d31e906a3ca26fc8ee0cb979e0e51b12aa7e83999c6afea047538f95e5c"}, - {file = "jiter-0.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8586e68702666b6acd919c65f718a09603adcfd8b4c7026bade2441d9e7bd34e"}, - {file = "jiter-0.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:639b766bde088546b5205fd31608502b5b42abee3294b43cc95c6ea8f9a257c3"}, - {file = "jiter-0.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cb32457296351c98da289d21a092a6c53c75beb80e7127c8e16224ee342c7c7"}, - {file = "jiter-0.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:583263bd81bce5426806cf27ba85e4b97746797fae13c71e50a8689e06e57f81"}, - {file = "jiter-0.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c0e413999a819ccef9b5fd22ef4b9b8c48a98e49da4d09b43ebce286d0d80e26"}, - {file = "jiter-0.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5e50468d5acfef335ba8bc3892bb304354c38ba18acb3f7ae428451e47136e49"}, - {file = "jiter-0.4.1-cp310-none-win32.whl", hash = "sha256:b2ac90b94dd717644c61c8ed0c2ec6e9505bd7314b91a1549680d7f1cb8f1da4"}, - {file = "jiter-0.4.1-cp310-none-win_amd64.whl", hash = "sha256:2509868b8dacf4f65d04b4d951d390f30f403a87a997a14e2db2d232c7a468a7"}, - {file = "jiter-0.4.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b56e4f2fa5767976f2332e9e067010ddfe1379b6456b5458123ba50657c33e02"}, - {file = "jiter-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f813b49db21c946aa010accc54b8e5c9d0007be252bda4738159fa6c65d6d396"}, - {file = "jiter-0.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2933c04ebd77b3e9cf34f80ba45c093739c687c9c5a4fd0a8c701a3bfd90940"}, - {file = "jiter-0.4.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b02ddd65513705ec38211ea48ffc0fde41aa46166d9f7706972daf97b57c8599"}, - {file = "jiter-0.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:88d06af883524e5429d75395bb4ee6ddeda4c30818b2f3e3b8f4afa2dd8f28c0"}, - {file = "jiter-0.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd333eca1090cf21e6359721eecbb2a7fe031cc4db3dd595081430b4a59371c5"}, - {file = "jiter-0.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdc90017cd22dca6b40f2f8518b38363e78aee3cb32f84e1cb08900a598ca91b"}, - {file = "jiter-0.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aedce5b11ca58853d46461e1880079836bfab4e132be2b7d2093ec193081bbc8"}, - {file = "jiter-0.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e55b2f4d2d5066979b0e0e58d85e3fffd0f6e6a0523aab7e0ce75950259387da"}, - {file = "jiter-0.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b3c85c586f1cd71c2a1e78756f6857119947b532379bd9be4338bf3dacf1e87f"}, - {file = "jiter-0.4.1-cp311-none-win32.whl", hash = "sha256:37875f56222f2bb61410e15196d9b91510ccca322c391f3d20c91d667130d15e"}, - {file = "jiter-0.4.1-cp311-none-win_amd64.whl", hash = "sha256:b71758befea8dbdc10e0fb40a776e085eed0e74afef42468ebb58562289e9190"}, - {file = "jiter-0.4.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:93a8869c18a3721e41d7adb289c5c71aea8887eb368a3411219a0afb62955cbe"}, - {file = "jiter-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0ffbc61349f2f27676d40d68e8ef83fc2a9dd2c1464962b1d1b1d8504bccbf85"}, - {file = "jiter-0.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f1f33e9fd4de4369a8d00fdf2571a8246a942095fb2a9d4cd25135ee675c85"}, - {file = "jiter-0.4.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d8f91a19eba23b4a1bb1e5b64c19cfdbf46604180e5dee40548b53ca13afd2d9"}, - {file = "jiter-0.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a60f8e495448d8e02d291fa9a8522cfe775a10210ba428994f383965e6f6e65"}, - {file = "jiter-0.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7387998c6585ce0f02ae4f5338fabf72b99494860c347f27bc34720290eafb15"}, - {file = "jiter-0.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7cbf41da6506b42db21a1a0befa48e16384591e84e80db002a826ccf07668f1"}, - {file = "jiter-0.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:793ae2499722b9fc31e300abd07418902512109bca17f617598a31a9e17bddce"}, - {file = "jiter-0.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:19f7953b8ada7ee109764ad91d4afb1a9f69b77cde0b890844744c513612dbf8"}, - {file = "jiter-0.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dcd3d6a142d7b267a8c5f1e28d02759e2e29343b095f6d8aaf463333a842e1f8"}, - {file = "jiter-0.4.1-cp312-none-win32.whl", hash = "sha256:fffdf137c3ab7f0c5facb7c478b57ad3e1eb9b149daff48687844de77b78ab70"}, - {file = "jiter-0.4.1-cp312-none-win_amd64.whl", hash = "sha256:fde004e47a801512c4167f188a6372960374fbd59e635753b3ee536e81953eb3"}, - {file = "jiter-0.4.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b429ba25e05ca28d5e7efa4249032746ac28ec6ad68017ed3ea009989c597911"}, - {file = "jiter-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:27df9925d0282c80bdd41613ace7cd799bd6355acdfe25cc48ec16843541999e"}, - {file = "jiter-0.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb68736a0e2b00eda83937c1937f999e8d7dab68820c04343ac2e2eb2c5c2193"}, - {file = "jiter-0.4.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c218458ac32ce0b495f013293867649b40c067a4d7533fa0d70a46f7194febae"}, - {file = "jiter-0.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ebead86e80e352753f6e6f78ca96c12d764a8dbbc7c4b25938ce657ab0e4e879"}, - {file = "jiter-0.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf58f878d43294bea400a9df86ef7796dd2e67969109bce22d337ca77372c69"}, - {file = "jiter-0.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba671e60570cd99b8ed83ce0d82703040dc34c793229ac607f09683ba1981163"}, - {file = "jiter-0.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef0bd8b68ad14f045544989b6ad3758bee6dc01f6924bce5b4fd7060b0a09b1b"}, - {file = "jiter-0.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7888f165a0fe285e015ee18cfcb8b5432c4fa389235b4c24c339ca0cc51ba979"}, - {file = "jiter-0.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d9c443b2a71a8c3ab6578f5faf7725ad5f63dbb92d87f820eec56de9da0560f"}, - {file = "jiter-0.4.1-cp38-none-win32.whl", hash = "sha256:6f618d1b04493bc9196e466ef59e0a6388eb85e936d1a61833449677643bbdd9"}, - {file = "jiter-0.4.1-cp38-none-win_amd64.whl", hash = "sha256:46b6364a0b2a81cc259768bda131e8528aa3af4312f23f7e10aa04d24f54bbb1"}, - {file = "jiter-0.4.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:6680785a9273a87e463c86a962042d620c00c7bb8100dde1a4c78b2184cdd613"}, - {file = "jiter-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:36b10d945b9ccd2e9f2720e37395daf9e63cfa47e5e0e2887c4931888f0800cd"}, - {file = "jiter-0.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78820599693bda34be17119abf9fad1f02e501b4816e47addbee9c5c768fb361"}, - {file = "jiter-0.4.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68697317170d8f851dfe978ba278b886e54e837ecd2a80c4a33ae780a0f19526"}, - {file = "jiter-0.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d126ffc3876cfc1fba6ae2be37f2532b5db593a96cf4b845724b50b44339c4fd"}, - {file = "jiter-0.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b082223f2e7e6f506d837df935f58f25cabf0a2b35902b4ec73fb561fbf2694a"}, - {file = "jiter-0.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13314287782782be8439dfafca50f13fcab18046227068a3a8e8d8ac888f092b"}, - {file = "jiter-0.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3da1346375605926f1ca4604d154ff41f5e3b933c6e01005e534bca2197d919f"}, - {file = "jiter-0.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9b67a97fbce3ec35ee97439c8b786393f71ecbe7458d5e9279d4c172772eac36"}, - {file = "jiter-0.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7b0f34544923bff0f3393aa3d60087686d86089c9361f6530bb5d19ebfb3db47"}, - {file = "jiter-0.4.1-cp39-none-win32.whl", hash = "sha256:b0c93ef95b896a4ce5edff23071e4dcad77c9e9262fcb6ca2b050f781e8335a9"}, - {file = "jiter-0.4.1-cp39-none-win_amd64.whl", hash = "sha256:3db5c83c8655ce031943b6f08434dac1a91e1477b0df452de0c44f3390a9b22c"}, - {file = "jiter-0.4.1.tar.gz", hash = "sha256:741851cf5f37cf3583f2a56829d734c9fd17334770c9a326e6d25291603d4278"}, + {file = "jiter-0.4.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c2b003ff58d14f5e182b875acd5177b2367245c19a03be9a2230535d296f7550"}, + {file = "jiter-0.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b48c77c25f094707731cd5bad6b776046846b60a27ee20efc8fadfb10a89415f"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f50ad6b172bde4d45f4d4ea10c49282a337b8bb735afc99763dfa55ea84a743"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95f6001e86f525fbbc9706db2078dc22be078b0950de55b92d37041930f5f940"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16646ef23b62b007de80460d303ebb2d81e355dac9389c787cec87cdd7ffef2f"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b4e847c13b0bf1255c711a92330e7a8cb8b5cdd1e37d7db309627bcdd3367ff"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c536589be60e4c5f2b20fadc4db7e9f55d4c9df3551f29ddf1c4a18dcc9dd54"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3b2763996167830889a854b4ded30bb90897f9b76be78069c50c3ec4540950e"}, + {file = "jiter-0.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:675e8ab98c99495091af6b6e9bf2b6353bcf81f25ab6ce27d36127e315b4505d"}, + {file = "jiter-0.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e48e43d9d999aaf55f53406b8846ff8cbe3e47ee4b9dc37e5a10a65ce760809f"}, + {file = "jiter-0.4.2-cp310-none-win32.whl", hash = "sha256:881b6e67c50bc36acb3570eda693763c8cd77d590940e06fa6d325d0da52ec1b"}, + {file = "jiter-0.4.2-cp310-none-win_amd64.whl", hash = "sha256:bb8f7b43259efc6add0d721ade2953e064b24e2026d26d979bc09ec080844cef"}, + {file = "jiter-0.4.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:24ad336ac47f274fa83f6fbedcabff9d3387c80f67c66b992688e6a8ba2c47e9"}, + {file = "jiter-0.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fc392a220095730afe365ce1516f2f88bb085a2fd29ea191be9c6e3c71713d9a"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1fdc408de36c81460896de0176f2f7b9f3574dcd35693a0b2c00f4ca34c98e4"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c10ad76722ee6a8c820b0db06a793c08b7d679e5201b9563015bd1e06c959a09"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbb46d1e9c82bba87f0cbda38413e49448a7df35b1e55917124bff9f38974a23"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:194e28ef4b5f3b61408cb2ee6b6dcbcdb0c9063d01b92b01345b7605692849f5"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0a447533eccd62748a727e058efa10a8d7cf1de8ffe1a4d705ecb41dad9090"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5f7704d7260bbb88cca3453951af739589132b26e896a3144fa2dae2263716d7"}, + {file = "jiter-0.4.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:01427458bc9550f2eda09d425755330e7d0eb09adce099577433bebf05d28d59"}, + {file = "jiter-0.4.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:159b8416879c0053b17c352f70b67b749ef5b2924c6154318ecf71918aab0905"}, + {file = "jiter-0.4.2-cp311-none-win32.whl", hash = "sha256:f2445234acfb79048ce1a0d5d0e181abb9afd9e4a29d8d9988fe26cc5773a81a"}, + {file = "jiter-0.4.2-cp311-none-win_amd64.whl", hash = "sha256:e15a65f233b6b0e5ac10ddf3b97ceb18aa9ffba096259961641d78b4ee321bd5"}, + {file = "jiter-0.4.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d61d59521aea9745447ce50f74d39a16ef74ec9d6477d9350d77e75a3d774ad2"}, + {file = "jiter-0.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eef607dc0acc251923427808dbd017f1998ae3c1a0430a261527aa5cbb3a942"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af6bf39954646e374fc47429c656372ac731a6a26b644158a5a84bcdbed33a47"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f509d23606e476852ee46a2b65b5c4ad3905f17424d9cc19c1dffa1c94ba3c6"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59672774daa44ee140aada0c781c82bee4d9ac5e522966186cfb6b3c217d8a51"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24a0458efac5afeca254cf557b8a654e17013075a69905c78f88d557f129d871"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8860766d1c293e75c1bb4e25b74fa987e3adf199cac3f5f9e6e49c2bebf092f"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a109f3281b72bbf4921fe43db1005c004a38559ca0b6c4985add81777dfe0a44"}, + {file = "jiter-0.4.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:faa7e667454b77ad2f0ef87db39f4944de759617aadf210ea2b73f26bb24755f"}, + {file = "jiter-0.4.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3512f8b00cafb6780b427cb6282800d2bf8277161d9c917830661bd4ed1d3528"}, + {file = "jiter-0.4.2-cp312-none-win32.whl", hash = "sha256:853b35d508ee5b66d06630473c1c0b7bb5e29bf4785c9d2202437116c94f7e21"}, + {file = "jiter-0.4.2-cp312-none-win_amd64.whl", hash = "sha256:4a3a8197784278eb8b24cb02c45e1cad67c2ce5b5b758adfb19b87f74bbdff9c"}, + {file = "jiter-0.4.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ca2a4d750aed3154b89f2efb148609fc985fad8db739460797aaf9b478acedda"}, + {file = "jiter-0.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0e6c304b3cc6896256727e1fb8991c7179a345eca8224e201795e9cacf4683b0"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cc34ac708ae1750d077e490321761ec4b9a055b994cbdd1d6fbd37099e4aa7b"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8c93383875ab8d2e4f760aaff335b4a12ff32d4f9cf49c4498d657734f611466"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce197ee044add576afca0955b42142dd0312639adb6ebadbdbe4277f2855614f"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a427716813ff65480ca5b5117cfa099f49b49cd38051f8609bd0d5493013ca0"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:479990218353356234669e70fac53e5eb6f739a10db25316171aede2c97d9364"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d35a91ec5ac74cf33234c431505299fa91c0a197c2dbafd47400aca7c69489d4"}, + {file = "jiter-0.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b27189847193708c94ad10ca0d891309342ae882725d2187cf5d2db02bde8d1b"}, + {file = "jiter-0.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76c255308cd1093fb411a03756b7bb220e48d4a98c30cbc79ed448bf3978e27d"}, + {file = "jiter-0.4.2-cp38-none-win32.whl", hash = "sha256:bb77438060bad49cc251941e6701b31138365c8a0ddaf10cdded2fcc6dd30701"}, + {file = "jiter-0.4.2-cp38-none-win_amd64.whl", hash = "sha256:ce858af19f7ce0d4b51c9f6c0c9d08f1e9dcef1986c5875efd0674a7054292ca"}, + {file = "jiter-0.4.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:6128838a2f357b3921b2a3242d5dc002ae4255ecc8f9f05c20d56d7d2d79c5ad"}, + {file = "jiter-0.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f2420cebb9ba856cb57dcab1d2d8def949b464b0db09c22a4e4dbd52fff7b200"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5d13d8128e853b320e00bb18bd4bb8b136cc0936091dc87633648fc688eb705"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eba5d6e54f149c508ba88677f97d3dc7dd75e9980d234bbac8027ac6db0763a3"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0fad5d64af0bc0545237419bf4150d8de56f0bd217434bdd1a59730327252bef"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d179e7bca89cf5719bd761dd37a341ff0f98199ecaa9c14af09792e47e977cc"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36353caee9f103d8ee7bda077f6400505b0f370e27eabcab33a33d21de12a2a6"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dd146c25bce576ca5db64fc7eccb8862af00f1f0e30108796953f12a53660e4c"}, + {file = "jiter-0.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:14b7c08cadbcd703041c66dc30e24e17de2f340281cac0e69374223ecf153aa4"}, + {file = "jiter-0.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a90f1a8b3d29aea198f8ea2b01148276ced8056e5103f32525266b3d880e65c9"}, + {file = "jiter-0.4.2-cp39-none-win32.whl", hash = "sha256:25b174997c780337b61ae57b1723455eecae9a17a9659044fd3c3b369190063f"}, + {file = "jiter-0.4.2-cp39-none-win_amd64.whl", hash = "sha256:bef62cea18521c5b99368147040c7e560c55098a35c93456f110678a2d34189a"}, + {file = "jiter-0.4.2.tar.gz", hash = "sha256:29b9d44f23f0c05f46d482f4ebf03213ee290d77999525d0975a17f875bf1eea"}, ] [[package]] @@ -4377,7 +4377,7 @@ six = "*" [[package]] name = "langflow-base" -version = "0.0.63" +version = "0.0.64" description = "A Python package with a built-in web application" optional = false python-versions = ">=3.10,<3.13" @@ -4458,13 +4458,13 @@ openai = ["openai (>=0.27.8)"] [[package]] name = "langsmith" -version = "0.1.76" +version = "0.1.77" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.76-py3-none-any.whl", hash = "sha256:4b8cb14f2233d9673ce9e6e3d545359946d9690a2c1457ab01e7459ec97b964e"}, - {file = "langsmith-0.1.76.tar.gz", hash = "sha256:5829f997495c0f9a39f91fe0a57e0cb702e8642e6948945f5bb9f46337db7732"}, + {file = "langsmith-0.1.77-py3-none-any.whl", hash = "sha256:2202cc21b1ed7e7b9e5d2af2694be28898afa048c09fdf09f620cbd9301755ae"}, + {file = "langsmith-0.1.77.tar.gz", hash = "sha256:4ace09077a9a4e412afeb4b517ca68e7de7b07f36e4792dc8236ac5207c0c0c7"}, ] [package.dependencies] @@ -4474,13 +4474,13 @@ requests = ">=2,<3" [[package]] name = "litellm" -version = "1.40.8" +version = "1.40.9" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.40.8-py3-none-any.whl", hash = "sha256:cd0c313423dad49224696c45ac02c574abcaed6666c597543c2318b3521f4320"}, - {file = "litellm-1.40.8.tar.gz", hash = "sha256:8878d2437ac50bcc6f39ded1729e2113eb5fee645fcebcd32fc241c529a21c00"}, + {file = "litellm-1.40.9-py3-none-any.whl", hash = "sha256:8a4107b9cce114d822de52cbc9bce56f8edc6620f19d0f2257e71834715fb366"}, + {file = "litellm-1.40.9.tar.gz", hash = "sha256:e0ea07d0b55001a6f60bba2b2ecd72d1f0dca07e656f63937adfdf45f31e5ad7"}, ] [package.dependencies] @@ -4491,7 +4491,7 @@ jinja2 = ">=3.1.2,<4.0.0" openai = ">=1.27.0" python-dotenv = ">=0.2.0" requests = ">=2.31.0,<3.0.0" -tiktoken = ">=0.4.0" +tiktoken = ">=0.7.0" tokenizers = "*" [package.extras] @@ -4543,8 +4543,8 @@ psutil = ">=5.9.1" pywin32 = {version = "*", markers = "platform_system == \"Windows\""} pyzmq = ">=25.0.0" requests = [ - {version = ">=2.26.0", markers = "python_version <= \"3.11\""}, {version = ">=2.32.2", markers = "python_version > \"3.11\""}, + {version = ">=2.26.0", markers = "python_version <= \"3.11\""}, ] tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} Werkzeug = ">=2.0.0" @@ -6012,9 +6012,9 @@ files = [ [package.dependencies] numpy = [ + {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, - {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -6824,13 +6824,13 @@ files = [ [[package]] name = "pydantic" -version = "2.7.3" +version = "2.7.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"}, - {file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"}, + {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, + {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index dbcdbfdff..e83fe9b8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow" -version = "1.0.0a52" +version = "1.0.0a53" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ diff --git a/src/backend/base/poetry.lock b/src/backend/base/poetry.lock index 6cd0f7cca..70bafac56 100644 --- a/src/backend/base/poetry.lock +++ b/src/backend/base/poetry.lock @@ -1275,13 +1275,13 @@ types-requests = ">=2.31.0.2,<3.0.0.0" [[package]] name = "langsmith" -version = "0.1.76" +version = "0.1.77" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.76-py3-none-any.whl", hash = "sha256:4b8cb14f2233d9673ce9e6e3d545359946d9690a2c1457ab01e7459ec97b964e"}, - {file = "langsmith-0.1.76.tar.gz", hash = "sha256:5829f997495c0f9a39f91fe0a57e0cb702e8642e6948945f5bb9f46337db7732"}, + {file = "langsmith-0.1.77-py3-none-any.whl", hash = "sha256:2202cc21b1ed7e7b9e5d2af2694be28898afa048c09fdf09f620cbd9301755ae"}, + {file = "langsmith-0.1.77.tar.gz", hash = "sha256:4ace09077a9a4e412afeb4b517ca68e7de7b07f36e4792dc8236ac5207c0c0c7"}, ] [package.dependencies] @@ -2083,13 +2083,13 @@ files = [ [[package]] name = "pydantic" -version = "2.7.3" +version = "2.7.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"}, - {file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"}, + {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, + {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, ] [package.dependencies] diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index e84674e1d..cadf3d6f3 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow-base" -version = "0.0.63" +version = "0.0.64" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ From 191fae732c85b755b2c3017775f1ab11c3e04f69 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Wed, 12 Jun 2024 12:43:35 -0300 Subject: [PATCH 07/68] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(GenericNode):=20ref?= =?UTF-8?q?actor=20to=20use=20optional=20chaining=20for=20template=20field?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ♻️ (count-handles): refactor to use optional chaining for template fields ♻️ (get-nodes-with-default-value): refactor to use optional chaining ♻️ (nodeToolbarComponent): refactor to use optional chaining for template fields 💡 (utils.ts): remove unnecessary trailing commas in function parameters to improve code readability and consistency --- .../src/CustomNodes/GenericNode/index.tsx | 40 +++++++++---------- .../src/CustomNodes/helpers/count-handles.ts | 6 +-- .../utils/get-nodes-with-default-value.ts | 6 +-- .../components/nodeToolbarComponent/index.tsx | 22 +++++----- src/frontend/src/utils/utils.ts | 28 ++++++------- 5 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 6838d3a42..44ef1a9d2 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -1,5 +1,6 @@ import emojiRegex from "emoji-regex"; import { useEffect, useMemo, useState } from "react"; +import { useHotkeys } from "react-hotkeys-hook"; import Markdown from "react-markdown"; import { NodeToolbar, useUpdateNodeInternals } from "reactflow"; import IconComponent from "../../components/genericIconComponent"; @@ -38,7 +39,6 @@ import useValidationStatusString from "../hooks/use-validation-status-string"; import getFieldTitle from "../utils/get-field-title"; import sortFields from "../utils/sort-fields"; import ParameterComponent from "./components/parameterComponent"; -import { useHotkeys } from "react-hotkeys-hook"; export default function GenericNode({ data, @@ -61,10 +61,10 @@ export default function GenericNode({ const setErrorData = useAlertStore((state) => state.setErrorData); const isDark = useDarkStore((state) => state.dark); const buildStatus = useFlowStore( - (state) => state.flowBuildStatus[data.id]?.status, + (state) => state.flowBuildStatus[data.id]?.status ); const lastRunTime = useFlowStore( - (state) => state.flowBuildStatus[data.id]?.timestamp, + (state) => state.flowBuildStatus[data.id]?.timestamp ); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); @@ -72,7 +72,7 @@ export default function GenericNode({ const [nodeName, setNodeName] = useState(data.node!.display_name); const [inputDescription, setInputDescription] = useState(false); const [nodeDescription, setNodeDescription] = useState( - data.node?.description!, + data.node?.description! ); const [isOutdated, setIsOutdated] = useState(false); const [validationStatus, setValidationStatus] = @@ -90,7 +90,7 @@ export default function GenericNode({ data.node!, setNode, setIsOutdated, - updateNodeInternals, + updateNodeInternals ); const name = nodeIconsLucide[data.type] ? data.type : types[data.type]; @@ -117,12 +117,12 @@ export default function GenericNode({ selected: boolean, showNode: boolean, buildStatus: BuildStatus | undefined, - validationStatus: VertexBuildTypeAPI | null, + validationStatus: VertexBuildTypeAPI | null ) => { const specificClassFromBuildStatus = getSpecificClassFromBuildStatus( buildStatus, validationStatus, - isDark, + isDark ); const baseBorderClass = getBaseBorderClass(selected); @@ -131,7 +131,7 @@ export default function GenericNode({ baseBorderClass, nodeSizeClass, "generic-node-div group/node", - specificClassFromBuildStatus, + specificClassFromBuildStatus ); return names; }; @@ -176,7 +176,7 @@ export default function GenericNode({ showNode, isEmoji, nodeIconFragment, - checkNodeIconFragment, + checkNodeIconFragment ); function countHandles(): void { @@ -309,7 +309,7 @@ export default function GenericNode({ selected, showNode, buildStatus, - validationStatus, + validationStatus )} > {data.node?.beta && showNode && ( @@ -411,8 +411,8 @@ export default function GenericNode({ .filter((templateField) => templateField.charAt(0) !== "_") .map( (templateField: string, idx) => - data.node!.template[templateField].show && - !data.node!.template[templateField].advanced && ( + data.node!.template[templateField]?.show && + !data.node!.template[templateField]?.advanced && ( - ), + ) )} { setInputDescription(true); @@ -670,8 +670,8 @@ export default function GenericNode({ .sort((a, b) => sortFields(a, b, data.node?.field_order ?? [])) .map((templateField: string, idx) => (
- {data.node!.template[templateField].show && - !data.node!.template[templateField].advanced ? ( + {data.node!.template[templateField]?.show && + !data.node!.template[templateField]?.advanced ? ( {" "} diff --git a/src/frontend/src/CustomNodes/helpers/count-handles.ts b/src/frontend/src/CustomNodes/helpers/count-handles.ts index ebfbfe01b..a7c8697ce 100644 --- a/src/frontend/src/CustomNodes/helpers/count-handles.ts +++ b/src/frontend/src/CustomNodes/helpers/count-handles.ts @@ -5,9 +5,9 @@ export function countHandlesFn(data: NodeDataType): number { .filter((templateField) => templateField.charAt(0) !== "_") .map((templateCamp) => { const { template } = data.node!; - if (template[templateCamp].input_types) return true; - if (!template[templateCamp].show) return false; - switch (template[templateCamp].type) { + if (template[templateCamp]?.input_types) return true; + if (!template[templateCamp]?.show) return false; + switch (template[templateCamp]?.type) { case "str": case "bool": case "float": diff --git a/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts b/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts index fb30a57a1..657526dda 100644 --- a/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts +++ b/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts @@ -11,10 +11,10 @@ export const getNodesWithDefaultValue = (flow) => { .filter( (templateField) => templateField.charAt(0) !== "_" && - node.data.node.template[templateField].show && + node.data.node.template[templateField]?.show && LANGFLOW_SUPPORTED_TYPES.has( - node.data.node.template[templateField].type, - ), + node.data.node.template[templateField]?.type + ) ) .map((n, i) => { arrNodesWithValues.push(node["id"]); diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index 1d0716d83..d61a114ed 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -57,17 +57,17 @@ export default function NodeToolbarComponent({ const nodeLength = Object.keys(data.node!.template).filter( (templateField) => templateField.charAt(0) !== "_" && - data.node?.template[templateField].show && - (data.node.template[templateField].type === "str" || - data.node.template[templateField].type === "bool" || - data.node.template[templateField].type === "float" || - data.node.template[templateField].type === "code" || - data.node.template[templateField].type === "prompt" || - data.node.template[templateField].type === "file" || - data.node.template[templateField].type === "Any" || - data.node.template[templateField].type === "int" || - data.node.template[templateField].type === "dict" || - data.node.template[templateField].type === "NestedDict") + data.node?.template[templateField]?.show && + (data.node.template[templateField]?.type === "str" || + data.node.template[templateField]?.type === "bool" || + data.node.template[templateField]?.type === "float" || + data.node.template[templateField]?.type === "code" || + data.node.template[templateField]?.type === "prompt" || + data.node.template[templateField]?.type === "file" || + data.node.template[templateField]?.type === "Any" || + data.node.template[templateField]?.type === "int" || + data.node.template[templateField]?.type === "dict" || + data.node.template[templateField]?.type === "NestedDict") ).length; const hasStore = useStoreStore((state) => state.hasStore); diff --git a/src/frontend/src/utils/utils.ts b/src/frontend/src/utils/utils.ts index 5d99927a5..5a1f6621f 100644 --- a/src/frontend/src/utils/utils.ts +++ b/src/frontend/src/utils/utils.ts @@ -56,7 +56,7 @@ export function normalCaseToSnakeCase(str: string): string { export function toTitleCase( str: string | undefined, - isNodeField?: boolean, + isNodeField?: boolean ): string { if (!str) return ""; let result = str @@ -65,7 +65,7 @@ export function toTitleCase( if (isNodeField) return word; if (index === 0) { return checkUpperWords( - word[0].toUpperCase() + word.slice(1).toLowerCase(), + word[0].toUpperCase() + word.slice(1).toLowerCase() ); } return checkUpperWords(word.toLowerCase()); @@ -78,7 +78,7 @@ export function toTitleCase( if (isNodeField) return word; if (index === 0) { return checkUpperWords( - word[0].toUpperCase() + word.slice(1).toLowerCase(), + word[0].toUpperCase() + word.slice(1).toLowerCase() ); } return checkUpperWords(word.toLowerCase()); @@ -182,7 +182,7 @@ export function checkLocalStorageKey(key: string): boolean { export function IncrementObjectKey( object: object, - key: string, + key: string ): { newKey: string; increment: number } { let count = 1; const type = removeCountFromString(key); @@ -217,7 +217,7 @@ export function groupByFamily( data: APIDataType, baseClasses: string, left: boolean, - flow?: NodeType[], + flow?: NodeType[] ): groupedObjType[] { const baseClassesSet = new Set(baseClasses.split("\n")); let arrOfPossibleInputs: Array<{ @@ -237,13 +237,13 @@ export function groupByFamily( const checkBaseClass = (template: TemplateVariableType) => { return ( - template.type && - template.show && + template?.type && + template?.show && ((!excludeTypes.has(template.type) && baseClassesSet.has(template.type)) || - (template.input_types && - template.input_types.some((inputType) => - baseClassesSet.has(inputType), + (template?.input_types && + template?.input_types.some((inputType) => + baseClassesSet.has(inputType) ))) ); }; @@ -263,7 +263,7 @@ export function groupByFamily( hasBaseClassInBaseClasses: foundNode?.hasBaseClassInBaseClasses || nodeData.node!.base_classes.some((baseClass) => - baseClassesSet.has(baseClass), + baseClassesSet.has(baseClass) ), //seta como anterior ou verifica se o node tem base class displayName: nodeData.node?.display_name, }); @@ -280,10 +280,10 @@ export function groupByFamily( if (!foundNode) { foundNode = { hasBaseClassInTemplate: Object.values(node!.template).some( - checkBaseClass, + checkBaseClass ), hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) => - baseClassesSet.has(baseClass), + baseClassesSet.has(baseClass) ), displayName: node?.display_name, }; @@ -355,7 +355,7 @@ export function isTimeStampString(str: string): boolean { export function extractColumnsFromRows( rows: object[], mode: "intersection" | "union", - excludeColumns?: Array, + excludeColumns?: Array ): (ColDef | ColGroupDef)[] { let columnsKeys: { [key: string]: ColDef | ColGroupDef } = {}; if (rows.length === 0) { From fdf44f6bc835c5d914e665abc08b8641c92f3874 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Wed, 12 Jun 2024 13:05:31 -0300 Subject: [PATCH 08/68] Refactor chatView component and improve error handling (#2144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor chatView component to handle artifacts in chat messages, fixing bug on streamed messages after refresh * Improve error handling and error messages in CustomNodes hooks to provide more informative and user-friendly error messages. This ensures that users are aware of any unexpected errors that occur while updating or adding components, and prompts them to try again. This change also aligns with recent improvements in error handling across the codebase. * ♻️ (utils.ts): remove unnecessary trailing commas in function parameters * ✨ (frontend): add constants for error messages in CustomNodes hooks ♻️ (frontend): refactor error handling in CustomNodes hooks to use constants * feat: Add constants for error messages in CustomNodes hooks Refactor error handling in CustomNodes hooks to use constants for error messages. This improves code readability and maintainability, ensuring consistent error messages are displayed to users. This change aligns with recent improvements in error handling across the codebase. * refactor: Improve error handling and error messages in CustomNodes hooks * refactor: Improve error handling and error messages in CustomNodes hooks * refactor: Improve error handling and error messages in CustomNodes hooks * refactor: Add missing click event in codeAreaModalComponent.spec.ts * refactor: Update cURL code generation to use icon-Copy test ID for button click * refactor: Improve error handling and error messages in CustomNodes hooks * refactor: Remove unnecessary click event in logs.spec.ts * refactor: Fix XPath selectors in textInputOutput.spec.ts * fix python api test * refactor: Update cURL code generation to use icon-Copy test ID for button click * fix save component * refactor: Update API key selection in userSettings.spec.ts * refactor: Update promptModalComponent.spec.ts * refactor: Update chatInputOutput.spec.ts for zoom out functionality * refactor: Add return_message option to ChatInput build_config * fix streaming * update example * update example name * refactor: Update chatInputOutputUser.spec.ts test to improve readability and maintainability --------- Co-authored-by: cristhianzl Co-authored-by: italojohnny --- .langchain.db-shm | Bin 0 -> 32768 bytes .langchain.db-wal | 0 .../langflow/components/inputs/ChatInput.py | 6 +- .../base/langflow/graph/vertex/types.py | 9 +- .../Basic Prompting (Hello, world!).json | 340 ++++---- src/backend/base/langflow/langflow-pre.db-shm | Bin 0 -> 32768 bytes src/backend/base/langflow/langflow-pre.db-wal | Bin 0 -> 700428 bytes src/frontend/package-lock.json | 794 +++++++----------- .../src/CustomNodes/GenericNode/index.tsx | 30 +- .../hooks/use-fetch-data-on-mount.tsx | 10 +- .../hooks/use-handle-new-value.tsx | 9 +- .../hooks/use-handle-refresh-buttons.tsx | 10 +- .../addNewVariableButton.tsx | 5 +- src/frontend/src/constants/constants.ts | 5 + .../IOModal/components/chatView/index.tsx | 18 +- .../utils/get-nodes-with-default-value.ts | 4 +- .../components/nodeToolbarComponent/index.tsx | 34 +- src/frontend/src/types/api/index.ts | 1 + src/frontend/src/utils/buildUtils.ts | 5 +- src/frontend/src/utils/utils.ts | 20 +- .../tests/end-to-end/chatInputOutput.spec.ts | 66 +- .../end-to-end/chatInputOutputUser.spec.ts | 13 +- .../end-to-end/codeAreaModalComponent.spec.ts | 1 + 23 files changed, 629 insertions(+), 751 deletions(-) create mode 100644 .langchain.db-shm create mode 100644 .langchain.db-wal create mode 100644 src/backend/base/langflow/langflow-pre.db-shm create mode 100644 src/backend/base/langflow/langflow-pre.db-wal diff --git a/.langchain.db-shm b/.langchain.db-shm new file mode 100644 index 0000000000000000000000000000000000000000..fe9ac2845eca6fe6da8a63cd096d9cf9e24ece10 GIT binary patch literal 32768 zcmeIuAr62r3 Union[Message, Text]: return super().build_with_record( sender=sender, diff --git a/src/backend/base/langflow/graph/vertex/types.py b/src/backend/base/langflow/graph/vertex/types.py index ba91a2597..5c698e144 100644 --- a/src/backend/base/langflow/graph/vertex/types.py +++ b/src/backend/base/langflow/graph/vertex/types.py @@ -1,5 +1,5 @@ import json -from typing import AsyncIterator, Dict, Iterator, List +from typing import AsyncIterator, Dict, Iterator, List, Generator import yaml from langchain_core.messages import AIMessage, AIMessageChunk @@ -99,11 +99,12 @@ 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, Message): - if isinstance(message, (AsyncIterator, Iterator)): + elif isinstance(self._built_object, (Message, Generator)): + if isinstance(message, (AsyncIterator, Iterator, Generator)): stream_url = self.build_stream_url() message = "" - self._built_object.text = message + if hasattr(self._built_object, "text"): + self._built_object.text = message else: message = self._built_object.text elif not isinstance(self._built_object, str): diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json index bd4fe5952..3895eebde 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json @@ -2,100 +2,72 @@ "data": { "edges": [ { - "className": "stroke-gray-900 stroke-connection", + "className": "", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "Text", - "str" - ], + "baseClasses": ["object", "Text", "str"], "dataType": "OpenAIModel", - "id": "OpenAIModel-k39HS" + "id": "OpenAIModel-NDBjF" }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-njtka", - "inputTypes": [ - "Text" - ], + "id": "ChatOutput-JkVmc", + "inputTypes": ["Text"], "type": "str" } }, - "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": "OpenAIModel-k39HS", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œTextœ, œstrœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-k39HSœ}", + "id": "reactflow__edge-OpenAIModel-NDBjF{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-NDBjFœ}-ChatOutput-JkVmc{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-JkVmcœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", + "source": "OpenAIModel-NDBjF", + "sourceHandle": "{œbaseClassesœ: [œobjectœ, œTextœ, œstrœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-NDBjFœ}", "style": { "stroke": "#555" }, - "target": "ChatOutput-njtka", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-njtkaœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" + "target": "ChatOutput-JkVmc", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-JkVmcœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" }, { - "className": "stroke-gray-900 stroke-connection", + "className": "", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "str", - "Text" - ], + "baseClasses": ["object", "str", "Text"], "dataType": "Prompt", - "id": "Prompt-uxBqP" + "id": "Prompt-WSII4" }, "targetHandle": { "fieldName": "input_value", - "id": "OpenAIModel-k39HS", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], + "id": "OpenAIModel-NDBjF", + "inputTypes": ["Text", "Record", "Prompt"], "type": "str" } }, - "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": "Prompt-uxBqP", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-uxBqPœ}", + "id": "reactflow__edge-Prompt-WSII4{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-WSII4œ}-OpenAIModel-NDBjF{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-NDBjFœ,œinputTypesœ:[œTextœ,œRecordœ,œPromptœ],œtypeœ:œstrœ}", + "source": "Prompt-WSII4", + "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-WSII4œ}", "style": { "stroke": "#555" }, - "target": "OpenAIModel-k39HS", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-k39HSœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" + "target": "OpenAIModel-NDBjF", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-NDBjFœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" }, { - "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "Record", - "str", - "Text" - ], + "baseClasses": ["Message", "object", "str", "Text"], "dataType": "ChatInput", - "id": "ChatInput-P3fgL" + "id": "ChatInput-kltLA" }, "targetHandle": { "fieldName": "user_input", - "id": "Prompt-uxBqP", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "id": "Prompt-WSII4", + "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], "type": "str" } }, - "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œ}", - "source": "ChatInput-P3fgL", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œRecordœ, œstrœ, œTextœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-P3fgLœ}", - "style": { - "stroke": "#555" - }, - "target": "Prompt-uxBqP", - "targetHandle": "{œfieldNameœ: œuser_inputœ, œidœ: œPrompt-uxBqPœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-ChatInput-kltLA{œbaseClassesœ:[œMessageœ,œobjectœ,œstrœ,œTextœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-kltLAœ}-Prompt-WSII4{œfieldNameœ:œuser_inputœ,œidœ:œPrompt-WSII4œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "source": "ChatInput-kltLA", + "sourceHandle": "{œbaseClassesœ: [œMessageœ, œobjectœ, œstrœ, œTextœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-kltLAœ}", + "target": "Prompt-WSII4", + "targetHandle": "{œfieldNameœ: œuser_inputœ, œidœ: œPrompt-WSII4œ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" } ], "nodes": [ @@ -103,18 +75,12 @@ "data": { "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", - "id": "Prompt-uxBqP", + "id": "Prompt-WSII4", "node": { - "base_classes": [ - "object", - "str", - "Text" - ], + "base_classes": ["object", "str", "Text"], "beta": false, "custom_fields": { - "template": [ - "user_input" - ] + "template": ["user_input"] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", @@ -129,9 +95,7 @@ "is_input": null, "is_output": null, "name": "", - "output_types": [ - "Prompt" - ], + "output_types": ["Prompt"], "template": { "_type": "CustomComponent", "code": { @@ -159,9 +123,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -205,17 +167,17 @@ "type": "Prompt" }, "dragging": false, - "height": 383, - "id": "Prompt-uxBqP", + "height": 419, + "id": "Prompt-WSII4", "position": { - "x": 53.588791333410654, - "y": -107.07318910019967 + "x": 18.562420355453696, + "y": -284.15095348876025 }, "positionAbsolute": { - "x": 53.588791333410654, - "y": -107.07318910019967 + "x": 18.562420355453696, + "y": -284.15095348876025 }, - "selected": true, + "selected": false, "type": "genericNode", "width": 384 }, @@ -223,13 +185,9 @@ "data": { "description": "Generates text using OpenAI LLMs.", "display_name": "OpenAI", - "id": "OpenAIModel-k39HS", + "id": "OpenAIModel-NDBjF", "node": { - "base_classes": [ - "object", - "Text", - "str" - ], + "base_classes": ["object", "Text", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -259,9 +217,7 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": [ - "Text" - ], + "output_types": ["Text"], "template": { "_type": "CustomComponent", "code": { @@ -289,11 +245,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], + "input_types": ["Text", "Record", "Prompt"], "list": false, "load_from_db": false, "multiline": false, @@ -350,9 +302,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -370,7 +320,7 @@ "show": true, "title_case": false, "type": "str", - "value": "gpt-3.5-turbo" + "value": "gpt-4o" }, "openai_api_base": { "advanced": true, @@ -379,9 +329,7 @@ "fileTypes": [], "file_path": "", "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.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -400,11 +348,9 @@ "fileTypes": [], "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, - "load_from_db": true, + "load_from_db": false, "multiline": false, "name": "openai_api_key", "password": true, @@ -413,7 +359,7 @@ "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "stream": { "advanced": true, @@ -432,7 +378,7 @@ "show": true, "title_case": false, "type": "bool", - "value": true + "value": false }, "system_message": { "advanced": true, @@ -441,9 +387,7 @@ "fileTypes": [], "file_path": "", "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -485,8 +429,8 @@ "type": "OpenAIModel" }, "dragging": false, - "height": 563, - "id": "OpenAIModel-k39HS", + "height": 571, + "id": "OpenAIModel-NDBjF", "position": { "x": 634.8148772766217, "y": 27.035057029045305 @@ -501,14 +445,9 @@ }, { "data": { - "id": "ChatOutput-njtka", + "id": "ChatOutput-JkVmc", "node": { - "base_classes": [ - "Record", - "Text", - "str", - "object" - ], + "base_classes": ["Record", "Text", "str", "object"], "beta": false, "custom_fields": { "input_value": null, @@ -525,10 +464,7 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" - ], + "output_types": ["Message", "Text"], "template": { "_type": "CustomComponent", "code": { @@ -556,9 +492,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, @@ -577,17 +511,12 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -603,9 +532,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -625,9 +552,7 @@ "fileTypes": [], "file_path": "", "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -644,15 +569,15 @@ "type": "ChatOutput" }, "dragging": false, - "height": 383, - "id": "ChatOutput-njtka", + "height": 391, + "id": "ChatOutput-JkVmc", "position": { - "x": 1193.250417197867, - "y": 71.88476890163852 + "x": 1183.52086970399, + "y": -21.518887039580306 }, "positionAbsolute": { - "x": 1193.250417197867, - "y": 71.88476890163852 + "x": 1183.52086970399, + "y": -21.518887039580306 }, "selected": false, "type": "genericNode", @@ -660,18 +585,14 @@ }, { "data": { - "id": "ChatInput-P3fgL", + "id": "ChatInput-kltLA", "node": { - "base_classes": [ - "object", - "Record", - "str", - "Text" - ], + "base_classes": ["Message", "object", "str", "Text"], "beta": false, "custom_fields": { + "files": null, "input_value": null, - "return_record": null, + "return_message": null, "sender": null, "sender_name": null, "session_id": null @@ -683,10 +604,7 @@ "field_order": [], "frozen": false, "icon": "ChatInput", - "output_types": [ - "Message", - "Text" - ], + "output_types": ["Message", "Text"], "template": { "_type": "CustomComponent", "code": { @@ -705,7 +623,49 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" + "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" + }, + "files": { + "advanced": true, + "display_name": "Files", + "dynamic": false, + "fileTypes": [ + ".txt", + ".md", + ".mdx", + ".csv", + ".json", + ".yaml", + ".yml", + ".xml", + ".html", + ".htm", + ".pdf", + ".docx", + ".py", + ".sh", + ".sql", + ".js", + ".ts", + ".tsx", + ".jpg", + ".jpeg", + ".png", + ".bmp" + ], + "file_path": "", + "info": "Files to be sent with the message.", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "files", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "file", + "value": "" }, "input_value": { "advanced": false, @@ -725,7 +685,26 @@ "show": true, "title_case": false, "type": "str", - "value": "hi" + "value": "what do you see?" + }, + "return_message": { + "advanced": true, + "display_name": "Return Record", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "return_message", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "bool", + "value": true }, "sender": { "advanced": true, @@ -734,17 +713,12 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -754,15 +728,13 @@ "value": "User" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -782,9 +754,7 @@ "fileTypes": [], "file_path": "", "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -801,30 +771,30 @@ "type": "ChatInput" }, "dragging": false, - "height": 375, - "id": "ChatInput-P3fgL", + "height": 289, + "id": "ChatInput-kltLA", "position": { - "x": -495.2223093083827, - "y": -232.56998443685862 + "x": -560.3246254009209, + "y": -435.0506368105706 }, "positionAbsolute": { - "x": -495.2223093083827, - "y": -232.56998443685862 + "x": -560.3246254009209, + "y": -435.0506368105706 }, - "selected": false, + "selected": true, "type": "genericNode", "width": 384 } ], "viewport": { - "x": 260.58251815500563, - "y": 318.2261172111936, - "zoom": 0.43514115784696294 + "x": 223.38563623650703, + "y": 271.96191180648566, + "zoom": 0.5138985141032123 } }, "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: \" ", - "id": "c091a57f-43a7-4a5e-b352-035ae8d8379c", + "id": "ad43b14f-6ec7-496f-9564-aad928603084", "is_component": false, - "last_tested_version": "1.0.0a4", + "last_tested_version": "1.0.0a52", "name": "Basic Prompting (Hello, World)" -} \ No newline at end of file +} diff --git a/src/backend/base/langflow/langflow-pre.db-shm b/src/backend/base/langflow/langflow-pre.db-shm new file mode 100644 index 0000000000000000000000000000000000000000..e77ab53ece30e9457154ec69a6e96f32330d8acf GIT binary patch literal 32768 zcmeI5M^hA06oro<=7Mj57p91u){QW+uH1F%P z9#xmWJSdsdvij4qvX41cX%6#&+JS2U`SZ_Z{qUbZ*c+*&9qCCjNG2IT29qIVC>chw zNH!Txa>xjhOY%rQDIkSp6d6s%kO^cWDI$}|WHNYD zmXj4^C0Ru_kTP|8RJRFik)2l+*Qlk3Sq6}ds~ zlAk1_J}{1KBV}Ye*-3Vha#BG~lh5QkN#*a5t>VgQBtEn0&D3eQJ_G+Qbjg4W$bbyU zfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih z49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbwqwEpkCiHBi0WfC*a%)k@ltb(yN zkc$h(Td0SH#@ToX22Qnb`)f1J?07C-I%+!5aT=!EUTUh7@0fyUNZ zTeXj_++^XjL7iE6Zje&Mxq;5u;e1g-u4rPNGUx8-**IfRa5kz9Qg$R0mG*<~f*9z6 zok8B(uL^P1en`l>rdaLz+{1C!^_qcmDvw5?svk?HW0%ob*Njp%p^>=cARRB!W?19; zR&#L9pynLBFi6>vfjDMA=q`wXt|$-kRqa^MhFV!)74YwTi8xtL>`lb6!Hiogvoh1`+ z$DKl+RH<)$mBf=)5jN?CeGyjpfB6{P`2R<~cuvJ8U(BH)Yy-Wq-+s`Y40vQ<0p57j z$;&oV@zBe59`xA)hjYd@rH(P{z zG3>6^&J46esj~$Mn@+(}gV`tHzCp_0ktFNurSULhdu)k^ZEBAW4D`VP2kAtPcEHvI z9e)}g`EK%gc*U9e;-K#)i_So4*cP3_zN^~6`T}_@#+r`Dv1l81&t&VH?O`uZpoM<{ DuY8%Z literal 0 HcmV?d00001 diff --git a/src/backend/base/langflow/langflow-pre.db-wal b/src/backend/base/langflow/langflow-pre.db-wal new file mode 100644 index 0000000000000000000000000000000000000000..746a1fa81882d7af6763e805909d6861b4239a95 GIT binary patch literal 700428 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 literal 0 HcmV?d00001 diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 7e7c3bbba..5ef96bea8 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -115,9 +115,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", - "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", "dev": true }, "node_modules/@alloc/quick-lru": { @@ -157,32 +157,12 @@ "nun": "bin/nun.mjs" } }, - "node_modules/@axiomhq/js": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@axiomhq/js/-/js-1.0.0-rc.3.tgz", - "integrity": "sha512-Zm10TczcMLounWqC42nMkXQ7XKLqjzLrd5ia022oBKDUZqAFVg2y9d1quQVNV4FlXyg9MKDdfMjpKQRmzEGaog==", - "dependencies": { - "fetch-retry": "^6.0.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@axiomhq/js/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dependencies": { - "@babel/highlight": "^7.24.2", + "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" }, "engines": { @@ -190,28 +170,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", - "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", + "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", - "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", + "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.24.5", - "@babel/helpers": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helpers": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -227,11 +207,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", - "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", + "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", "dependencies": { - "@babel/types": "^7.24.5", + "@babel/types": "^7.24.7", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -241,12 +221,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", + "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", + "@babel/compat-data": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -256,57 +236,61 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "dependencies": { + "@babel/types": "^7.24.7" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dependencies": { - "@babel/types": "^7.24.0" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", - "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", + "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.24.3", - "@babel/helper-simple-access": "^7.24.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "@babel/helper-validator-identifier": "^7.24.5" + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -316,70 +300,70 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", - "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", "dependencies": { - "@babel/types": "^7.24.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", - "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dependencies": { - "@babel/types": "^7.24.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", + "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", + "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", - "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", + "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", - "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -389,9 +373,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", - "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -400,9 +384,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", - "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -411,31 +395,31 @@ } }, "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", - "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", + "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", "dependencies": { - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/types": "^7.24.5", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -444,52 +428,18 @@ } }, "node_modules/@babel/types": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", - "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", + "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", "dependencies": { - "@babel/helper-string-parser": "^7.24.1", - "@babel/helper-validator-identifier": "^7.24.5", + "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@clack/core": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.3.4.tgz", - "integrity": "sha512-H4hxZDXgHtWTwV3RAVenqcC4VbJZNegbBjlPvzOzCouXtS2y3sDvlO3IsbrPNWuLWPPlYVYPghQdSF64683Ldw==", - "dependencies": { - "picocolors": "^1.0.0", - "sisteransi": "^1.0.5" - } - }, - "node_modules/@clack/prompts": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.7.0.tgz", - "integrity": "sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA==", - "bundleDependencies": [ - "is-unicode-supported" - ], - "dependencies": { - "@clack/core": "^0.3.3", - "is-unicode-supported": "*", - "picocolors": "^1.0.0", - "sisteransi": "^1.0.5" - } - }, - "node_modules/@clack/prompts/node_modules/is-unicode-supported": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -851,9 +801,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", + "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -979,9 +929,9 @@ } }, "node_modules/@hookform/resolvers": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.4.2.tgz", - "integrity": "sha512-1m9uAVIO8wVf7VCDAGsuGA0t6Z3m6jVGAN50HkV9vYLl0yixKK/Z1lr01vaRvYCkIKGoy1noVRxMzQYb4y/j1Q==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.6.0.tgz", + "integrity": "sha512-UBcpyOX3+RR+dNnqBd0lchXpoL8p4xC21XP8H6Meb8uve5Br1GCnmg0PcBoKKqPKgGu9GHQ/oygcmPrQhetwqw==", "peerDependencies": { "react-hook-form": "^7.0.0" } @@ -990,6 +940,7 @@ "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^2.0.2", @@ -1039,6 +990,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true }, "node_modules/@isaacs/cliui": { @@ -1068,17 +1020,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -1114,22 +1055,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -1205,25 +1130,6 @@ "node": ">=10" } }, - "node_modules/@million/install": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@million/install/-/install-0.0.3.tgz", - "integrity": "sha512-yK8NgP+73+Tby/bmQ12B96bJ7RjsLazLtFgBed1Fg1WfTCCpHTILq79yQMSD/OWgm1tt1NYV4ELaTRM6wZOeAg==", - "dependencies": { - "@antfu/ni": "^0.21.12", - "@axiomhq/js": "1.0.0-rc.3", - "@babel/core": "^7.24.5", - "@babel/types": "^7.23.6", - "@clack/prompts": "^0.7.0", - "cli-high": "^0.4.1", - "diff": "^5.1.0", - "posthog-node": "^3.6.3", - "xycolors": "^0.1.1" - }, - "bin": { - "install": "bin/index.js" - } - }, "node_modules/@million/lint": { "version": "0.0.73", "resolved": "https://registry.npmjs.org/@million/lint/-/lint-0.0.73.tgz", @@ -3372,14 +3278,14 @@ } }, "node_modules/@swc/core": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.5.7.tgz", - "integrity": "sha512-U4qJRBefIJNJDRCCiVtkfa/hpiZ7w0R6kASea+/KLp+vkus3zcLSB8Ub8SvKgTIxjWpwsKcZlPf5nrv4ls46SQ==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.5.28.tgz", + "integrity": "sha512-muCdNIqOTURUgYeyyOLYE3ShL8SZO6dw6bhRm6dCvxWzCZOncPc5fB0kjcPXTML+9KJoHL7ks5xg+vsQK+v6ig==", "dev": true, "hasInstallScript": true, "dependencies": { - "@swc/counter": "^0.1.2", - "@swc/types": "0.1.7" + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.8" }, "engines": { "node": ">=10" @@ -3389,19 +3295,19 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.5.7", - "@swc/core-darwin-x64": "1.5.7", - "@swc/core-linux-arm-gnueabihf": "1.5.7", - "@swc/core-linux-arm64-gnu": "1.5.7", - "@swc/core-linux-arm64-musl": "1.5.7", - "@swc/core-linux-x64-gnu": "1.5.7", - "@swc/core-linux-x64-musl": "1.5.7", - "@swc/core-win32-arm64-msvc": "1.5.7", - "@swc/core-win32-ia32-msvc": "1.5.7", - "@swc/core-win32-x64-msvc": "1.5.7" + "@swc/core-darwin-arm64": "1.5.28", + "@swc/core-darwin-x64": "1.5.28", + "@swc/core-linux-arm-gnueabihf": "1.5.28", + "@swc/core-linux-arm64-gnu": "1.5.28", + "@swc/core-linux-arm64-musl": "1.5.28", + "@swc/core-linux-x64-gnu": "1.5.28", + "@swc/core-linux-x64-musl": "1.5.28", + "@swc/core-win32-arm64-msvc": "1.5.28", + "@swc/core-win32-ia32-msvc": "1.5.28", + "@swc/core-win32-x64-msvc": "1.5.28" }, "peerDependencies": { - "@swc/helpers": "^0.5.0" + "@swc/helpers": "*" }, "peerDependenciesMeta": { "@swc/helpers": { @@ -3410,9 +3316,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.5.7.tgz", - "integrity": "sha512-bZLVHPTpH3h6yhwVl395k0Mtx8v6CGhq5r4KQdAoPbADU974Mauz1b6ViHAJ74O0IVE5vyy7tD3OpkQxL/vMDQ==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.5.28.tgz", + "integrity": "sha512-sP6g63ybzIdOWNDbn51tyHN8EMt7Mb4RMeHQEsXB7wQfDvzhpWB+AbfK6Gs3Q8fwP/pmWIrWW9csKOc1K2Mmkg==", "cpu": [ "arm64" ], @@ -3426,9 +3332,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.5.7.tgz", - "integrity": "sha512-RpUyu2GsviwTc2qVajPL0l8nf2vKj5wzO3WkLSHAHEJbiUZk83NJrZd1RVbEknIMO7+Uyjh54hEh8R26jSByaw==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.5.28.tgz", + "integrity": "sha512-Bd/agp/g7QocQG5AuorOzSC78t8OzeN+pCN/QvJj1CvPhvppjJw6e1vAbOR8vO2vvGi2pvtf3polrYQStJtSiA==", "cpu": [ "x64" ], @@ -3442,9 +3348,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.5.7.tgz", - "integrity": "sha512-cTZWTnCXLABOuvWiv6nQQM0hP6ZWEkzdgDvztgHI/+u/MvtzJBN5lBQ2lue/9sSFYLMqzqff5EHKlFtrJCA9dQ==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.5.28.tgz", + "integrity": "sha512-Wr3TwPGIveS9/OBWm0r9VAL8wkCR0zQn46J8K01uYCmVhUNK3Muxjs0vQBZaOrGu94mqbj9OXY+gB3W7aDvGdA==", "cpu": [ "arm" ], @@ -3458,9 +3364,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.5.7.tgz", - "integrity": "sha512-hoeTJFBiE/IJP30Be7djWF8Q5KVgkbDtjySmvYLg9P94bHg9TJPSQoC72tXx/oXOgXvElDe/GMybru0UxhKx4g==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.5.28.tgz", + "integrity": "sha512-8G1ZwVTuLgTAVTMPD+M97eU6WeiRIlGHwKZ5fiJHPBcz1xqIC7jQcEh7XBkobkYoU5OILotls3gzjRt8CMNyDQ==", "cpu": [ "arm64" ], @@ -3474,9 +3380,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.5.7.tgz", - "integrity": "sha512-+NDhK+IFTiVK1/o7EXdCeF2hEzCiaRSrb9zD7X2Z7inwWlxAntcSuzZW7Y6BRqGQH89KA91qYgwbnjgTQ22PiQ==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.5.28.tgz", + "integrity": "sha512-0Ajdzb5Fzvz+XUbN5ESeHAz9aHHSYiQcm+vmsDi0TtPHmsalfnqEPZmnK0zPALPJPLQP2dDo4hELeDg3/c3xgA==", "cpu": [ "arm64" ], @@ -3490,9 +3396,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.5.7.tgz", - "integrity": "sha512-25GXpJmeFxKB+7pbY7YQLhWWjkYlR+kHz5I3j9WRl3Lp4v4UD67OGXwPe+DIcHqcouA1fhLhsgHJWtsaNOMBNg==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.5.28.tgz", + "integrity": "sha512-ueQ9VejnQUM2Pt+vT0IAKoF4vYBWUP6n1KHGdILpoGe3LuafQrqu7RoyQ15C7/AYii7hAeNhTFdf6gLbg8cjFg==", "cpu": [ "x64" ], @@ -3506,9 +3412,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.5.7.tgz", - "integrity": "sha512-0VN9Y5EAPBESmSPPsCJzplZHV26akC0sIgd3Hc/7S/1GkSMoeuVL+V9vt+F/cCuzr4VidzSkqftdP3qEIsXSpg==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.5.28.tgz", + "integrity": "sha512-G5th8Mg0az8CbY4GQt9/m5hg2Y0kGIwvQBeVACuLQB6q2Y4txzdiTpjmFqUUhEvvl7Klyx1IHvNhfXs3zpt7PA==", "cpu": [ "x64" ], @@ -3522,9 +3428,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.5.7.tgz", - "integrity": "sha512-RtoNnstBwy5VloNCvmvYNApkTmuCe4sNcoYWpmY7C1+bPR+6SOo8im1G6/FpNem8AR5fcZCmXHWQ+EUmRWJyuA==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.5.28.tgz", + "integrity": "sha512-JezwCGavZ7CkNXx4yInI4kpb71L0zxzxA9BFlmnsGKEEjVQcKc3hFpmIzfFVs+eotlBUwDNb0+Yo9m6Cb7lllA==", "cpu": [ "arm64" ], @@ -3538,9 +3444,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.5.7.tgz", - "integrity": "sha512-Xm0TfvcmmspvQg1s4+USL3x8D+YPAfX2JHygvxAnCJ0EHun8cm2zvfNBcsTlnwYb0ybFWXXY129aq1wgFC9TpQ==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.5.28.tgz", + "integrity": "sha512-q8tW5J4RkOkl7vYShnWS//VAb2Ngolfm9WOMaF2GRJUr2Y/Xeb/+cNjdsNOqea2BzW049D5vdP7XPmir3/zUZw==", "cpu": [ "ia32" ], @@ -3554,9 +3460,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.5.7.tgz", - "integrity": "sha512-tp43WfJLCsKLQKBmjmY/0vv1slVywR5Q4qKjF5OIY8QijaEW7/8VwPyUyVoJZEnDgv9jKtUTG5PzqtIYPZGnyg==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.5.28.tgz", + "integrity": "sha512-jap6EiB3wG1YE1hyhNr9KLPpH4PGm+5tVMfN0l7fgKtV0ikgpcEN/YF94tru+z5m2HovqYW009+Evq9dcVGmpg==", "cpu": [ "x64" ], @@ -3576,9 +3482,9 @@ "dev": true }, "node_modules/@swc/types": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.7.tgz", - "integrity": "sha512-scHWahbHF0eyj3JsxG9CFJgFdFNaVQCNAimBlT6PzS3n/HptxqREjsm4OH6AN3lYcffZYSPxXW8ua2BEHp0lJQ==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.8.tgz", + "integrity": "sha512-RNFA3+7OJFNYY78x0FYwi1Ow+iF1eF5WvmfY1nXPOEH4R2p/D4Cr1vzje7dNAI2aLFqpv8Wyz4oKSWqIZArpQA==", "dev": true, "dependencies": { "@swc/counter": "^0.1.3" @@ -3656,11 +3562,11 @@ } }, "node_modules/@tanstack/react-virtual": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.5.0.tgz", - "integrity": "sha512-rtvo7KwuIvqK9zb0VZ5IL7fiJAEnG+0EiFZz8FUOs+2mhGqdGmjKIaT1XU7Zq0eFqL0jonLlhbayJI/J2SA/Bw==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.5.1.tgz", + "integrity": "sha512-jIsuhfgy8GqA67PdWqg73ZB2LFE+HD9hjWL1L6ifEIZVyZVAKpYmgUG4WsKQ005aEyImJmbuimPiEvc57IY0Aw==", "dependencies": { - "@tanstack/virtual-core": "3.5.0" + "@tanstack/virtual-core": "3.5.1" }, "funding": { "type": "github", @@ -3672,9 +3578,9 @@ } }, "node_modules/@tanstack/virtual-core": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.5.0.tgz", - "integrity": "sha512-KnPRCkQTyqhanNC0K63GBG3wA8I+D1fQuVnAvcBF8f13akOKeQp1gSbu6f77zCxhEk727iV5oQnbHLYzHrECLg==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.5.1.tgz", + "integrity": "sha512-046+AUSiDru/V9pajE1du8WayvBKeCvJ2NmKPy/mR8/SbKKrqmSbj7LJBfXE+nSq4f5TBXvnCzu0kcYebI9WdQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -4370,9 +4276,9 @@ } }, "node_modules/@types/lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==", + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==", "dev": true }, "node_modules/@types/mathjax": { @@ -4394,9 +4300,9 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "16.18.97", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.97.tgz", - "integrity": "sha512-4muilE1Lbfn57unR+/nT9AFjWk0MtWi5muwCEJqnOvfRQDbSfLCUdN7vCIg8TYuaANfhLOV85ve+FNpiUsbSRg==", + "version": "16.18.98", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.98.tgz", + "integrity": "sha512-fpiC20NvLpTLAzo3oVBKIqBGR6Fx/8oAK/SSf7G+fydnXMY1x4x9RZ6sBXhqKlCU21g2QapUsbLlhv3+a7wS+Q==", "devOptional": true }, "node_modules/@types/prop-types": { @@ -4405,9 +4311,9 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { - "version": "18.3.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.2.tgz", - "integrity": "sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==", + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4482,9 +4388,9 @@ "optional": true }, "node_modules/ace-builds": { - "version": "1.34.0", - "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.34.0.tgz", - "integrity": "sha512-ZQqoV76wl4guDE5zvEnxCDrvy9gzLAwu7eD4ikYj/Gdlb4+qRLbb+aOFVnweZZRsHh089V0WVaw2NMNuiiEdTw==" + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.35.0.tgz", + "integrity": "sha512-bwDKqjqNccC/MSujqnYTeAS5dIR8UmGLP0R90mvsJY0FRC8NUWBSTfj34+EIzo2NWc/gV8IZTqv4fXaiZJpCtA==" }, "node_modules/acorn": { "version": "8.11.3", @@ -4962,9 +4868,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", + "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", "funding": [ { "type": "opencollective", @@ -4980,10 +4886,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "update-browserslist-db": "^1.0.16" }, "bin": { "browserslist": "cli.js" @@ -5104,9 +5010,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001621", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz", - "integrity": "sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==", + "version": "1.0.30001632", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", + "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==", "funding": [ { "type": "opencollective", @@ -5259,24 +5165,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-high": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cli-high/-/cli-high-0.4.1.tgz", - "integrity": "sha512-fPbkILfCVG6mfxbsJbJZQsKE5JmfgJNlfBxap52XHXjLwPDhs/OMFVFrNR7bqiavZt2r5A80+wiRHzJyzWqwEQ==", - "hasInstallScript": true, - "dependencies": { - "@clack/prompts": "^0.7.0", - "sugar-high": "^0.6.1", - "xycolors": "^0.1.1", - "yargs": "^17.7.2" - }, - "bin": { - "cli-high": "bin/index.js" - }, - "funding": { - "url": "https://github.com/sponsors/xinyao27" - } - }, "node_modules/cli-spinners": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", @@ -5293,19 +5181,6 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -5637,9 +5512,9 @@ } }, "node_modules/daisyui": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.11.1.tgz", - "integrity": "sha512-obT9CUbQdW6eoHwSeT5VwaRrWlwrM4OT5qlfdJ0oQlSIEYhwnEl2+L2fwu5PioLbitwuMdYC2X8I1cyy8Pf6LQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.12.2.tgz", + "integrity": "sha512-ed3EFwPRLN+9+/MYPRB1pYjk6plRCBMobfBdSeB3voAS81KdL2pCKtbwJfUUpDdOnJ0F8T6oRdVX02P6UCD0Hg==", "dev": true, "dependencies": { "css-selector-tokenizer": "^0.8", @@ -5700,9 +5575,9 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dependencies": { "ms": "2.1.2" }, @@ -5959,9 +5834,9 @@ } }, "node_modules/dompurify": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.4.tgz", - "integrity": "sha512-2gnshi6OshmuKil8rMZuQCGiUF3cUxHY3NGDzUAdUx/NPEe5DVnO8BDoAQouvgwnx0R/+a6jUn36Z0FSdq8vww==" + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz", + "integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==" }, "node_modules/dot-case": { "version": "3.0.4", @@ -5989,9 +5864,9 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/electron-to-chromium": { - "version": "1.4.780", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.780.tgz", - "integrity": "sha512-NPtACGFe7vunRYzvYqVRhQvsDrTevxpgDKxG/Vcbe0BTNOY+5+/2mOXSw2ls7ToNbE5Bf/+uQbjTxcmwMozpCw==" + "version": "1.4.799", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.799.tgz", + "integrity": "sha512-3D3DwWkRTzrdEpntY0hMLYwj7SeBk1138CkPE8sBDSj3WzrzOiG2rHm3luw8jucpf+WiyLBCZyU9lMHyQI9M9Q==" }, "node_modules/emoji-regex": { "version": "10.3.0", @@ -6726,11 +6601,6 @@ "node": "^12.20 || >= 14.13" } }, - "node_modules/fetch-retry": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/fetch-retry/-/fetch-retry-6.0.0.tgz", - "integrity": "sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==" - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -6885,9 +6755,9 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.0.tgz", + "integrity": "sha512-CrWQNaEl1/6WeZoarcM9LHupTo3RpZO2Pdk1vktwzPiQTsJnAKJmm3TACKeG5UZbWDfaH2AbvYxzP96y0MT7fA==", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -6956,9 +6826,9 @@ } }, "node_modules/framer-motion": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.6.tgz", - "integrity": "sha512-XUrjjBt57e5YoHQtjwc3eNchFBuHvIgN/cS8SC4oIaAn2J/0+bLanUxXizidJKZVeHJam/JrmMnPRjYMglVn5g==", + "version": "11.2.10", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.10.tgz", + "integrity": "sha512-/gr3PLZUVFCc86a9MqCUboVrALscrdluzTb3yew+2/qKBU8CX6nzs918/SRBRCqaPbx0TZP10CB6yFgK2C5cYQ==", "dependencies": { "tslib": "^2.4.0" }, @@ -7087,14 +6957,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -7135,6 +6997,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "devOptional": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -8072,9 +7935,9 @@ } }, "node_modules/jackspeak": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", - "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", + "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -8268,9 +8131,9 @@ } }, "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "bin": { "jiti": "bin/jiti.js" } @@ -9508,20 +9371,19 @@ } }, "node_modules/million": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/million/-/million-3.1.1.tgz", - "integrity": "sha512-vmI3lyA3IN4QKiB0/M3uDef3lZZvgVUokWtLkc5NvxEnykY+TdSR6xatMMNDJmzMHTneayIOpc/eQu4d9Z/r2w==", + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/million/-/million-3.1.11.tgz", + "integrity": "sha512-6Vh1s0da0PzSqbbp9Zd8yMTIkOWnvBU4vNJCMHTZPXaY3fZ5h+N7s5croS/RBgjJIHz3WQZnvyNBQz7gQ6cqJg==", "dependencies": { "@babel/core": "^7.23.7", "@babel/types": "^7.23.6", - "@million/install": "^0.0.3", "@rollup/pluginutils": "^5.1.0", "kleur": "^4.1.5", "undici": "^6.3.0", "unplugin": "^1.6.0" }, "bin": { - "million": "packages/cli/dist/index.js" + "million": "cli.js" }, "funding": { "url": "https://github.com/sponsors/aidenybai" @@ -9690,9 +9552,9 @@ } }, "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", "optional": true }, "node_modules/nanoid": { @@ -10051,7 +9913,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-4.0.0.tgz", "integrity": "sha512-4Ispi9I9qYGO4lueiLDhe4q4iK5ERK8reLsuzH6BPaXn53EGaua8H66PXIFGrW897hwjXp+pVLrm/DLxN0RF0A==", - "license": "MIT", "engines": { "node": ">=12" } @@ -10437,9 +10298,9 @@ } }, "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "engines": { "node": ">=14" }, @@ -11477,14 +11338,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -11560,6 +11413,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "devOptional": true, "dependencies": { "glob": "^7.1.3" @@ -12369,15 +12223,15 @@ } }, "node_modules/sucrase/node_modules/glob": { - "version": "10.3.16", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", - "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.11.0" + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" @@ -12390,18 +12244,13 @@ } }, "node_modules/sucrase/node_modules/minipass": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", - "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } }, - "node_modules/sugar-high": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/sugar-high/-/sugar-high-0.6.1.tgz", - "integrity": "sha512-kg1qMW7WwJcueXIlHkChL/p2EWY3gf8rQmP6n5nUq2TWVqatqDTMLvViS9WgAjgyTKH5/3/b8sRwWPOOAo1zMA==" - }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -12444,9 +12293,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz", + "integrity": "sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -12699,9 +12548,9 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, "node_modules/type-check": { "version": "0.4.0", @@ -12741,9 +12590,9 @@ } }, "node_modules/ua-parser-js": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.38.tgz", + "integrity": "sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==", "dev": true, "funding": [ { @@ -12764,9 +12613,9 @@ } }, "node_modules/undici": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.1.tgz", - "integrity": "sha512-/0BWqR8rJNRysS5lqVmfc7eeOErcOP4tZpATVjJOojjHZ71gSYVAtFhEmadcIjwMIUehh5NFyKGsXCnXIajtbA==", + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.2.tgz", + "integrity": "sha512-o/MQLTwRm9IVhOqhZ0NQ9oXax1ygPjw6Vs+Vq/4QRjbOAC3B1GCHy7TYxxbExKlb7bzDRzt9vBWU6BDz0RFfYg==", "engines": { "node": ">=18.17" } @@ -13573,9 +13422,9 @@ } }, "node_modules/webpack-virtual-modules": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz", - "integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==" + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==" }, "node_modules/whatwg-encoding": { "version": "2.0.0", @@ -13706,16 +13555,16 @@ } }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -13768,35 +13617,62 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dependencies": { - "color-name": "~1.1.4" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } }, "node_modules/wrappy": { "version": "1.0.2", @@ -13853,32 +13729,15 @@ "node": ">=0.4" } }, - "node_modules/xycolors": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/xycolors/-/xycolors-0.1.1.tgz", - "integrity": "sha512-BbRKWpz/87nNH4lXp6TbBFUT0QipzmJI7ksQpSpBb3ny8mGJgkiKk36bIr8VqfyTEhasEBsfbp/Cum37fIHnjA==", - "hasInstallScript": true, - "funding": { - "url": "https://github.com/sponsors/xinyao27" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yaml": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", - "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", "bin": { "yaml": "bin.mjs" }, @@ -13886,31 +13745,6 @@ "node": ">= 14" } }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 44ef1a9d2..2119b3f4d 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -61,10 +61,10 @@ export default function GenericNode({ const setErrorData = useAlertStore((state) => state.setErrorData); const isDark = useDarkStore((state) => state.dark); const buildStatus = useFlowStore( - (state) => state.flowBuildStatus[data.id]?.status + (state) => state.flowBuildStatus[data.id]?.status, ); const lastRunTime = useFlowStore( - (state) => state.flowBuildStatus[data.id]?.timestamp + (state) => state.flowBuildStatus[data.id]?.timestamp, ); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); @@ -72,7 +72,7 @@ export default function GenericNode({ const [nodeName, setNodeName] = useState(data.node!.display_name); const [inputDescription, setInputDescription] = useState(false); const [nodeDescription, setNodeDescription] = useState( - data.node?.description! + data.node?.description!, ); const [isOutdated, setIsOutdated] = useState(false); const [validationStatus, setValidationStatus] = @@ -90,7 +90,7 @@ export default function GenericNode({ data.node!, setNode, setIsOutdated, - updateNodeInternals + updateNodeInternals, ); const name = nodeIconsLucide[data.type] ? data.type : types[data.type]; @@ -117,12 +117,12 @@ export default function GenericNode({ selected: boolean, showNode: boolean, buildStatus: BuildStatus | undefined, - validationStatus: VertexBuildTypeAPI | null + validationStatus: VertexBuildTypeAPI | null, ) => { const specificClassFromBuildStatus = getSpecificClassFromBuildStatus( buildStatus, validationStatus, - isDark + isDark, ); const baseBorderClass = getBaseBorderClass(selected); @@ -131,7 +131,7 @@ export default function GenericNode({ baseBorderClass, nodeSizeClass, "generic-node-div group/node", - specificClassFromBuildStatus + specificClassFromBuildStatus, ); return names; }; @@ -176,7 +176,7 @@ export default function GenericNode({ showNode, isEmoji, nodeIconFragment, - checkNodeIconFragment + checkNodeIconFragment, ); function countHandles(): void { @@ -309,7 +309,7 @@ export default function GenericNode({ selected, showNode, buildStatus, - validationStatus + validationStatus, )} > {data.node?.beta && showNode && ( @@ -457,7 +457,7 @@ export default function GenericNode({ } title={getFieldTitle( data.node?.template!, - templateField + templateField, )} info={data.node?.template[templateField].info} name={templateField} @@ -485,7 +485,7 @@ export default function GenericNode({ proxy={data.node?.template[templateField].proxy} showNode={showNode} /> - ) + ), )} { setInputDescription(true); @@ -713,13 +713,13 @@ export default function GenericNode({ } title={getFieldTitle( data.node?.template!, - templateField + templateField, )} info={data.node?.template[templateField].info} name={templateField} tooltipTitle={ data.node?.template[templateField].input_types?.join( - "\n" + "\n", ) ?? data.node?.template[templateField].type } required={data.node!.template[templateField].required} @@ -746,7 +746,7 @@ export default function GenericNode({
    {" "} diff --git a/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx b/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx index be239df8a..f203a059a 100644 --- a/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx @@ -1,5 +1,9 @@ import { cloneDeep } from "lodash"; import { useEffect } from "react"; +import { + ERROR_UPDATING_COMPONENT, + TITLE_ERROR_UPDATING_COMPONENT, +} from "../../constants/constants"; import useAlertStore from "../../stores/alertStore"; import { ResponseErrorDetailAPI } from "../../types/api"; @@ -39,8 +43,10 @@ const useFetchDataOnMount = ( let responseError = error as ResponseErrorDetailAPI; setErrorData({ - title: "Error while updating the Component", - list: [responseError?.response?.data?.detail ?? "Unknown error"], + title: TITLE_ERROR_UPDATING_COMPONENT, + list: [ + responseError?.response?.data?.detail ?? ERROR_UPDATING_COMPONENT, + ], }); } setIsLoading(false); diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx b/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx index e917970ca..7be08491c 100644 --- a/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx @@ -1,4 +1,8 @@ import { cloneDeep } from "lodash"; +import { + ERROR_UPDATING_COMPONENT, + TITLE_ERROR_UPDATING_COMPONENT, +} from "../../constants/constants"; import useAlertStore from "../../stores/alertStore"; import { ResponseErrorTypeAPI } from "../../types/api"; @@ -43,9 +47,10 @@ const useHandleOnNewValue = ( } catch (error) { let responseError = error as ResponseErrorTypeAPI; setErrorData({ - title: "Error while updating the Component", + title: TITLE_ERROR_UPDATING_COMPONENT, list: [ - responseError?.response?.data?.detail.error ?? "Unknown error", + responseError?.response?.data?.detail.error ?? + ERROR_UPDATING_COMPONENT, ], }); } diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx b/src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx index 4696aa994..8d9e1a694 100644 --- a/src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx @@ -1,4 +1,8 @@ import { cloneDeep } from "lodash"; +import { + ERROR_UPDATING_COMPONENT, + TITLE_ERROR_UPDATING_COMPONENT, +} from "../../constants/constants"; import useAlertStore from "../../stores/alertStore"; import { ResponseErrorDetailAPI } from "../../types/api"; import { handleUpdateValues } from "../../utils/parameterUtils"; @@ -25,8 +29,10 @@ const useHandleRefreshButtonPress = (setIsLoading, setNode, renderTooltips) => { let responseError = error as ResponseErrorDetailAPI; setErrorData({ - title: "Error while updating the Component", - list: [responseError?.response?.data?.detail ?? "Unknown error"], + title: TITLE_ERROR_UPDATING_COMPONENT, + list: [ + responseError?.response?.data?.detail ?? ERROR_UPDATING_COMPONENT, + ], }); } setIsLoading(false); diff --git a/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx b/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx index 89535aa68..ae23c34ac 100644 --- a/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx +++ b/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx @@ -70,7 +70,10 @@ export default function AddNewVariableButton({ let responseError = error as ResponseErrorDetailAPI; setErrorData({ title: "Error creating variable", - list: [responseError?.response?.data?.detail ?? "Unknown error"], + list: [ + responseError?.response?.data?.detail ?? + "An unexpected error occurred while adding a new variable. Please try again.", + ], }); }); } diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index 67c986bce..07dface8e 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -837,3 +837,8 @@ export const MAX_BATCH_SIZE = 50; export const MODAL_CLASSES = "nopan nodelete nodrag noundo nocopy fixed inset-0 bottom-0 left-0 right-0 top-0 z-50 overflow-auto bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"; + +export const ERROR_UPDATING_COMPONENT = + "An unexpected error occurred while updating the Component. Please try again."; +export const TITLE_ERROR_UPDATING_COMPONENT = + "Error while updating the Component"; diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 2e57fb2cb..a71cd8b82 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -62,14 +62,20 @@ export default function ChatView({ const chatMessages: ChatMessageType[] = chatOutputResponses .sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)) // - .filter((output) => output.data.message) + .filter( + (output) => + output.data.message || (!output.data.message && output.artifacts), + ) .map((output, index) => { try { console.log("output:", output); const { sender, message, sender_name, stream_url, files } = - output.data.message; - console.log("output.data.message:", output.data.message); - console.log("output.data.message.files:", output.data.message.files); + output.data.message?.message || + (output.data.message?.files ?? []).length > 0 || + output.data.message?.stream_url + ? output.data.message + : output.artifacts; + console.log(output); const is_ai = sender === "Machine" || sender === null || sender === undefined; return { @@ -134,7 +140,7 @@ export default function ChatView({ function updateChat( chat: ChatMessageType, message: string, - stream_url?: string + stream_url?: string, ) { // if (message === "") return; chat.message = message; @@ -164,7 +170,7 @@ export default function ChatView({ setIsDragging, setFiles, currentFlowId, - setErrorData + setErrorData, ); return ( diff --git a/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts b/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts index 657526dda..4cd3763c6 100644 --- a/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts +++ b/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts @@ -13,8 +13,8 @@ export const getNodesWithDefaultValue = (flow) => { templateField.charAt(0) !== "_" && node.data.node.template[templateField]?.show && LANGFLOW_SUPPORTED_TYPES.has( - node.data.node.template[templateField]?.type - ) + node.data.node.template[templateField]?.type, + ), ) .map((n, i) => { arrNodesWithValues.push(node["id"]); diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index d61a114ed..20d282f72 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -50,7 +50,7 @@ export default function NodeToolbarComponent({ const [showconfirmShare, setShowconfirmShare] = useState(false); const [showOverrideModal, setShowOverrideModal] = useState(false); const [flowComponent, setFlowComponent] = useState( - createFlowComponent(cloneDeep(data), version) + createFlowComponent(cloneDeep(data), version), ); const preventDefault = true; const isMac = navigator.platform.toUpperCase().includes("MAC"); @@ -67,7 +67,7 @@ export default function NodeToolbarComponent({ data.node.template[templateField]?.type === "Any" || data.node.template[templateField]?.type === "int" || data.node.template[templateField]?.type === "dict" || - data.node.template[templateField]?.type === "NestedDict") + data.node.template[templateField]?.type === "NestedDict"), ).length; const hasStore = useStoreStore((state) => state.hasStore); @@ -220,7 +220,7 @@ export default function NodeToolbarComponent({ const updateNodeInternals = useUpdateNodeInternals(); const setLastCopiedSelection = useFlowStore( - (state) => state.setLastCopiedSelection + (state) => state.setLastCopiedSelection, ); const setSuccessData = useAlertStore((state) => state.setSuccessData); @@ -294,7 +294,7 @@ export default function NodeToolbarComponent({ nodes, edges, setNodes, - setEdges + setEdges, ); break; case "override": @@ -321,14 +321,14 @@ export default function NodeToolbarComponent({ y: 10, paneX: nodes.find((node) => node.id === data.id)?.position.x, paneY: nodes.find((node) => node.id === data.id)?.position.y, - } + }, ); break; } }; const isSaved = flows.some((flow) => - Object.values(flow).includes(data.node?.display_name!) + Object.values(flow).includes(data.node?.display_name!), ); function displayShortcut({ @@ -346,7 +346,7 @@ export default function NodeToolbarComponent({ } }); const filteredShortcut = fixedShortcut.filter( - (key) => !key.toLowerCase().includes("shift") + (key) => !key.toLowerCase().includes("shift"), ); let shortcutWPlus: string[] = []; if (!hasShift) shortcutWPlus = filteredShortcut.join("+").split(" "); @@ -370,7 +370,7 @@ export default function NodeToolbarComponent({ const setNode = useFlowStore((state) => state.setNode); const handleOnNewValue = ( - newValue: string | string[] | boolean | Object[] + newValue: string | string[] | boolean | Object[], ): void => { if (data.node!.template[name].value !== newValue) { takeSnapshot(); @@ -426,8 +426,8 @@ export default function NodeToolbarComponent({ name.split(" ")[0].toLowerCase() === "code" - )! + ({ name }) => name.split(" ")[0].toLowerCase() === "code", + )!, )} side="top" > @@ -446,8 +446,8 @@ export default function NodeToolbarComponent({ name.split(" ")[0].toLowerCase() === "advanced" - )! + ({ name }) => name.split(" ")[0].toLowerCase() === "advanced", + )!, )} side="top" > @@ -486,14 +486,14 @@ export default function NodeToolbarComponent({ name.split(" ")[0].toLowerCase() === "freeze" - )! + ({ name }) => name.split(" ")[0].toLowerCase() === "freeze", + )!, )} side="top" > @@ -542,7 +542,7 @@ export default function NodeToolbarComponent({
    ).response?.data?.detail ?? "Unknown Error"], + [ + (error as AxiosError).response?.data?.detail ?? + "An unexpected error occurred while building the Component. Please try again.", + ], verticesIds.map((id) => ({ id })), ); stopBuild(); diff --git a/src/frontend/src/utils/utils.ts b/src/frontend/src/utils/utils.ts index 5a1f6621f..302d9e0a7 100644 --- a/src/frontend/src/utils/utils.ts +++ b/src/frontend/src/utils/utils.ts @@ -56,7 +56,7 @@ export function normalCaseToSnakeCase(str: string): string { export function toTitleCase( str: string | undefined, - isNodeField?: boolean + isNodeField?: boolean, ): string { if (!str) return ""; let result = str @@ -65,7 +65,7 @@ export function toTitleCase( if (isNodeField) return word; if (index === 0) { return checkUpperWords( - word[0].toUpperCase() + word.slice(1).toLowerCase() + word[0].toUpperCase() + word.slice(1).toLowerCase(), ); } return checkUpperWords(word.toLowerCase()); @@ -78,7 +78,7 @@ export function toTitleCase( if (isNodeField) return word; if (index === 0) { return checkUpperWords( - word[0].toUpperCase() + word.slice(1).toLowerCase() + word[0].toUpperCase() + word.slice(1).toLowerCase(), ); } return checkUpperWords(word.toLowerCase()); @@ -182,7 +182,7 @@ export function checkLocalStorageKey(key: string): boolean { export function IncrementObjectKey( object: object, - key: string + key: string, ): { newKey: string; increment: number } { let count = 1; const type = removeCountFromString(key); @@ -217,7 +217,7 @@ export function groupByFamily( data: APIDataType, baseClasses: string, left: boolean, - flow?: NodeType[] + flow?: NodeType[], ): groupedObjType[] { const baseClassesSet = new Set(baseClasses.split("\n")); let arrOfPossibleInputs: Array<{ @@ -243,7 +243,7 @@ export function groupByFamily( baseClassesSet.has(template.type)) || (template?.input_types && template?.input_types.some((inputType) => - baseClassesSet.has(inputType) + baseClassesSet.has(inputType), ))) ); }; @@ -263,7 +263,7 @@ export function groupByFamily( hasBaseClassInBaseClasses: foundNode?.hasBaseClassInBaseClasses || nodeData.node!.base_classes.some((baseClass) => - baseClassesSet.has(baseClass) + baseClassesSet.has(baseClass), ), //seta como anterior ou verifica se o node tem base class displayName: nodeData.node?.display_name, }); @@ -280,10 +280,10 @@ export function groupByFamily( if (!foundNode) { foundNode = { hasBaseClassInTemplate: Object.values(node!.template).some( - checkBaseClass + checkBaseClass, ), hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) => - baseClassesSet.has(baseClass) + baseClassesSet.has(baseClass), ), displayName: node?.display_name, }; @@ -355,7 +355,7 @@ export function isTimeStampString(str: string): boolean { export function extractColumnsFromRows( rows: object[], mode: "intersection" | "union", - excludeColumns?: Array + excludeColumns?: Array, ): (ColDef | ColGroupDef)[] { let columnsKeys: { [key: string]: ColDef | ColGroupDef } = {}; if (rows.length === 0) { diff --git a/src/frontend/tests/end-to-end/chatInputOutput.spec.ts b/src/frontend/tests/end-to-end/chatInputOutput.spec.ts index 66044f25f..c34c953ec 100644 --- a/src/frontend/tests/end-to-end/chatInputOutput.spec.ts +++ b/src/frontend/tests/end-to-end/chatInputOutput.spec.ts @@ -28,27 +28,55 @@ test("chat_io_teste", async ({ page }) => { ); await page.getByTestId("blank-flow").click(); - await page.waitForTimeout(2000); + await page.waitForTimeout(3000); + await page.getByTestId("extended-disclosure").click(); + await page.getByPlaceholder("Search").click(); + await page.getByPlaceholder("Search").fill("chat output"); + await page.waitForTimeout(1000); - // Create the DataTransfer and File - const dataTransfer = await page.evaluateHandle((data) => { - const dt = new DataTransfer(); - // Convert the buffer to a hex array - const file = new File([data], "ChatTest.json", { - type: "application/json", - }); - dt.items.add(file); - return dt; - }, jsonContent); + await page + .getByTestId("outputsChat Output") + .dragTo(page.locator('//*[@id="react-flow-id"]')); + await page.mouse.up(); + await page.mouse.down(); + + await page.getByPlaceholder("Search").click(); + await page.getByPlaceholder("Search").fill("chat input"); + await page.waitForTimeout(1000); + + await page + .getByTestId("inputsChat Input") + .dragTo(page.locator('//*[@id="react-flow-id"]')); + await page.mouse.up(); + await page.mouse.down(); + + await page.getByTitle("fit view").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + + // Click and hold on the first element + await page + .locator( + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[2]/div/div[2]/div[10]/button/div/div', + ) + .hover(); + await page.mouse.down(); + + // Move to the second element + await page + .locator( + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[1]/div/div[2]/div[4]/div/button/div/div', + ) + .hover(); + + // Release the mouse + await page.mouse.up(); - // Now dispatch - await page.dispatchEvent( - '//*[@id="react-flow-id"]/div[1]/div[1]/div', - "drop", - { - dataTransfer, - }, - ); await page.getByLabel("fit view").click(); await page.getByText("Playground", { exact: true }).click(); await page.getByPlaceholder("Send a message...").click(); diff --git a/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts b/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts index 1b27fa11f..6950ca230 100644 --- a/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts +++ b/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts @@ -56,10 +56,15 @@ test("user must interact with chat with Input/Output", async ({ page }) => { .getByTestId("textarea-input_value") .nth(1) .fill( - "testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!" + "testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!", ); + await page.getByText("Playground", { exact: true }).last().click(); await page.getByTestId("icon-LucideSend").click(); await page.getByText("Close", { exact: true }).click(); + await page.getByText("Chat Input", { exact: true }).click(); + await page.getByTestId("advanced-button-modal").click(); + await page.getByTestId("showsender_name").click(); + await page.getByText("Save Changes", { exact: true }).click(); await page .getByTestId("popover-anchor-input-sender_name") @@ -87,9 +92,9 @@ test("user must interact with chat with Input/Output", async ({ page }) => { await page .getByText( "testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!", - { exact: true } + { exact: true }, ) - .isVisible() + .isVisible(), ); }); @@ -199,7 +204,7 @@ test("user must be able to send an image on chat", async ({ page }) => { const jsonContent = readFileSync( "src/frontend/tests/end-to-end/assets/chain.png", - "utf-8" + "utf-8", ); // Create the DataTransfer and File diff --git a/src/frontend/tests/end-to-end/codeAreaModalComponent.spec.ts b/src/frontend/tests/end-to-end/codeAreaModalComponent.spec.ts index 577c138a3..6d9ac2d02 100644 --- a/src/frontend/tests/end-to-end/codeAreaModalComponent.spec.ts +++ b/src/frontend/tests/end-to-end/codeAreaModalComponent.spec.ts @@ -40,6 +40,7 @@ test("CodeAreaModalComponent", async ({ page }) => { await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); await page.getByTestId("div-generic-node").click(); + await page.getByTestId("code-button-modal").click(); const wCode = From 20405ffc0154f0b089c2304cfb792fba792ade4e Mon Sep 17 00:00:00 2001 From: italojohnny Date: Wed, 12 Jun 2024 15:20:24 -0300 Subject: [PATCH 09/68] change default openai model to gpt3.5 --- .../base/langflow/components/model_specs/ChatOpenAISpecs.py | 2 +- .../starter_projects/Basic Prompting (Hello, world!).json | 2 +- .../initial_setup/starter_projects/Langflow Blog Writter.json | 2 +- .../initial_setup/starter_projects/Langflow Document QA.json | 2 +- .../starter_projects/Langflow Memory Conversation.json | 2 +- .../starter_projects/Langflow Prompt Chaining.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/backend/base/langflow/components/model_specs/ChatOpenAISpecs.py b/src/backend/base/langflow/components/model_specs/ChatOpenAISpecs.py index 7358762bc..76974a00f 100644 --- a/src/backend/base/langflow/components/model_specs/ChatOpenAISpecs.py +++ b/src/backend/base/langflow/components/model_specs/ChatOpenAISpecs.py @@ -53,7 +53,7 @@ class ChatOpenAIComponent(CustomComponent): self, max_tokens: Optional[int] = 0, model_kwargs: NestedDict = {}, - model_name: str = "gpt-4o", + model_name: str = "gpt-3.5-turbo", openai_api_base: Optional[str] = None, openai_api_key: Optional[str] = None, temperature: float = 0.7, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json index 3895eebde..b03836d2a 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json @@ -320,7 +320,7 @@ "show": true, "title_case": false, "type": "str", - "value": "gpt-4o" + "value": "gpt-3.5-turbo" }, "openai_api_base": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json index a41e4499c..044ff92b0 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json @@ -723,7 +723,7 @@ "show": true, "title_case": false, "type": "str", - "value": "gpt-3.5-turbo-0125" + "value": "gpt-3.5-turbo" }, "openai_api_base": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json index 0a62bbad4..d3ecd268b 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json @@ -851,7 +851,7 @@ "show": true, "title_case": false, "type": "str", - "value": "gpt-4-turbo-preview" + "value": "gpt-3.5-turbo" }, "openai_api_base": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json index 34a0ed884..17707debc 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json @@ -862,7 +862,7 @@ "show": true, "title_case": false, "type": "str", - "value": "gpt-4-1106-preview" + "value": "gpt-3.5-turbo" }, "openai_api_base": { "advanced": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json index ba68a1cb7..e1319c936 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json @@ -1165,7 +1165,7 @@ "show": true, "title_case": false, "type": "str", - "value": "gpt-4-turbo-preview" + "value": "gpt-3.5-turbo" }, "openai_api_base": { "advanced": true, From ca0f5112bc99d4645cf2e57ea46edec2efddc4fc Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Wed, 12 Jun 2024 15:47:03 -0300 Subject: [PATCH 10/68] refactor: Fix condition for required fields in MessageModel --- src/backend/base/langflow/services/monitor/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/base/langflow/services/monitor/schema.py b/src/backend/base/langflow/services/monitor/schema.py index 4491fd0c5..a409f8337 100644 --- a/src/backend/base/langflow/services/monitor/schema.py +++ b/src/backend/base/langflow/services/monitor/schema.py @@ -99,7 +99,7 @@ class MessageModel(DefaultModel): @classmethod def from_message(cls, message: Message, flow_id: Optional[str] = None): # first check if the record has all the required fields - if not message.text or not message.sender or not message.sender_name: + if message.text is None or not message.sender or not message.sender_name: raise ValueError("The message does not have the required fields 'sender' and 'sender_name' in the data.") return cls( sender=message.sender, From 5c9af97bb17c4d1d81deb9d50e587a2c4e335349 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Wed, 12 Jun 2024 15:47:15 -0300 Subject: [PATCH 11/68] Refactor chatView component to handle artifacts in chat messages --- .../IOModal/components/chatView/index.tsx | 75 +++---------------- 1 file changed, 11 insertions(+), 64 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index a71cd8b82..736d21a6f 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -36,12 +36,6 @@ export default function ChatView({ const outputTypes = outputs.map((obj) => obj.type); const updateFlowPool = useFlowStore((state) => state.updateFlowPool); - // useEffect(() => { - // if (!outputTypes.includes("ChatOutput")) { - // setNoticeData({ title: NOCHATOUTPUT_NOTICE_ALERT }); - // } - // }, []); - //build chat history useEffect(() => { const chatOutputResponses: VertexBuildTypeAPI[] = []; @@ -69,13 +63,17 @@ export default function ChatView({ .map((output, index) => { try { console.log("output:", output); + + const messageOutput = output.data.message; + const hasMessageValue = + messageOutput?.message || + messageOutput?.message === "" || + (messageOutput?.files ?? []).length > 0 || + messageOutput?.stream_url; + const { sender, message, sender_name, stream_url, files } = - output.data.message?.message || - (output.data.message?.files ?? []).length > 0 || - output.data.message?.stream_url - ? output.data.message - : output.artifacts; - console.log(output); + hasMessageValue ? output.data.message : output.artifacts; + const is_ai = sender === "Machine" || sender === null || sender === undefined; return { @@ -88,6 +86,7 @@ export default function ChatView({ }; } catch (e) { console.error(e); + debugger; return { isSend: false, message: "Error parsing message", @@ -142,26 +141,12 @@ export default function ChatView({ message: string, stream_url?: string, ) { - // if (message === "") return; chat.message = message; - // chat is one of the chatHistory updateFlowPool(chat.componentId, { message, sender_name: chat.sender_name ?? "Bot", sender: chat.isSend ? "User" : "Machine", }); - // setChatHistory((oldChatHistory) => { - // const index = oldChatHistory.findIndex((ch) => ch.id === chat.id); - // if (index === -1) return oldChatHistory; - // let newChatHistory = _.cloneDeep(oldChatHistory); - // newChatHistory = [ - // ...newChatHistory.slice(0, index), - // chat, - // ...newChatHistory.slice(index + 1), - // ]; - // console.log("newChatHistory:", newChatHistory); - // return newChatHistory; - // }); } const [files, setFiles] = useState([]); const [isDragging, setIsDragging] = useState(false); @@ -196,44 +181,6 @@ export default function ChatView({ aria-hidden="true" /> - {/* */}
    {chatHistory?.length > 0 ? ( From 12e36bc5d5856b9ee9010ba7ba1bac0ceedff20a Mon Sep 17 00:00:00 2001 From: italojohnny Date: Wed, 12 Jun 2024 16:37:31 -0300 Subject: [PATCH 12/68] remove tmp sqlite files from version control --- src/backend/base/langflow/langflow-pre.db-shm | Bin 32768 -> 0 bytes src/backend/base/langflow/langflow-pre.db-wal | Bin 700428 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/backend/base/langflow/langflow-pre.db-shm delete mode 100644 src/backend/base/langflow/langflow-pre.db-wal diff --git a/src/backend/base/langflow/langflow-pre.db-shm b/src/backend/base/langflow/langflow-pre.db-shm deleted file mode 100644 index e77ab53ece30e9457154ec69a6e96f32330d8acf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI5M^hA06oro<=7Mj57p91u){QW+uH1F%P z9#xmWJSdsdvij4qvX41cX%6#&+JS2U`SZ_Z{qUbZ*c+*&9qCCjNG2IT29qIVC>chw zNH!Txa>xjhOY%rQDIkSp6d6s%kO^cWDI$}|WHNYD zmXj4^C0Ru_kTP|8RJRFik)2l+*Qlk3Sq6}ds~ zlAk1_J}{1KBV}Ye*-3Vha#BG~lh5QkN#*a5t>VgQBtEn0&D3eQJ_G+Qbjg4W$bbyU zfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih z49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbwqwEpkCiHBi0WfC*a%)k@ltb(yN zkc$h(Td0SH#@ToX22Qnb`)f1J?07C-I%+!5aT=!EUTUh7@0fyUNZ zTeXj_++^XjL7iE6Zje&Mxq;5u;e1g-u4rPNGUx8-**IfRa5kz9Qg$R0mG*<~f*9z6 zok8B(uL^P1en`l>rdaLz+{1C!^_qcmDvw5?svk?HW0%ob*Njp%p^>=cARRB!W?19; zR&#L9pynLBFi6>vfjDMA=q`wXt|$-kRqa^MhFV!)74YwTi8xtL>`lb6!Hiogvoh1`+ z$DKl+RH<)$mBf=)5jN?CeGyjpfB6{P`2R<~cuvJ8U(BH)Yy-Wq-+s`Y40vQ<0p57j z$;&oV@zBe59`xA)hjYd@rH(P{z zG3>6^&J46esj~$Mn@+(}gV`tHzCp_0ktFNurSULhdu)k^ZEBAW4D`VP2kAtPcEHvI z9e)}g`EK%gc*U9e;-K#)i_So4*cP3_zN^~6`T}_@#+r`Dv1l81&t&VH?O`uZpoM<{ DuY8%Z diff --git a/src/backend/base/langflow/langflow-pre.db-wal b/src/backend/base/langflow/langflow-pre.db-wal deleted file mode 100644 index 746a1fa81882d7af6763e805909d6861b4239a95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 700428 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 From 716bd2f65b2257d4bb41f972506c662cfb91d2c1 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Wed, 12 Jun 2024 17:50:51 -0300 Subject: [PATCH 13/68] Refactor code to handle artifacts in chat messages and improve error handling --- src/frontend/src/App.tsx | 1 - .../src/components/headerComponent/index.tsx | 5 +++-- src/frontend/src/contexts/authContext.tsx | 12 +++++++----- src/frontend/src/pages/LoginPage/index.tsx | 3 +-- .../components/ProfilePictureForm/index.tsx | 1 - 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 4a60f453f..04d48103b 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -80,7 +80,6 @@ export default function App() { login(user["access_token"]); setUserData(user); setAutoLogin(true); - setLoading(false); fetchAllData(); } }) diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx index 8a2b9a2cf..ef6b8938d 100644 --- a/src/frontend/src/components/headerComponent/index.tsx +++ b/src/frontend/src/components/headerComponent/index.tsx @@ -36,6 +36,7 @@ export default function Header(): JSX.Element { const location = useLocation(); const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext); + const navigate = useNavigate(); const removeFlow = useFlowsManagerStore((store) => store.removeFlow); const hasStore = useStoreStore((state) => state.hasStore); @@ -202,7 +203,7 @@ export default function Header(): JSX.Element { 0, BACKEND_URL.length - 1, )}${BASE_URL_API}files/profile_pictures/${ - userData?.profile_image ?? "Space/046-rocket.png" + userData?.profile_image ?? "Space/046-rocket.svg" }` ?? profileCircle } className="h-7 w-7 shrink-0 focus-visible:outline-0" @@ -220,7 +221,7 @@ export default function Header(): JSX.Element { 0, BACKEND_URL.length - 1, )}${BASE_URL_API}files/profile_pictures/${ - userData?.profile_image + userData?.profile_image ?? "Space/046-rocket.svg" }` ?? profileCircle } className="h-5 w-5 focus-visible:outline-0 " diff --git a/src/frontend/src/contexts/authContext.tsx b/src/frontend/src/contexts/authContext.tsx index 43416f373..fbb7f8f1e 100644 --- a/src/frontend/src/contexts/authContext.tsx +++ b/src/frontend/src/contexts/authContext.tsx @@ -3,6 +3,7 @@ import { useNavigate } from "react-router-dom"; import Cookies from "universal-cookie"; import { getLoggedUser, requestLogout } from "../controllers/API"; import useAlertStore from "../stores/alertStore"; +import { useFolderStore } from "../stores/foldersStore"; import { Users } from "../types/api"; import { AuthContextType } from "../types/contexts/auth"; @@ -30,18 +31,19 @@ export function AuthProvider({ children }): React.ReactElement { const navigate = useNavigate(); const cookies = new Cookies(); const [accessToken, setAccessToken] = useState( - cookies.get("access_token_lf") ?? null + cookies.get("access_token_lf") ?? null, ); const [isAuthenticated, setIsAuthenticated] = useState( - !!cookies.get("access_token_lf") + !!cookies.get("access_token_lf"), ); const [isAdmin, setIsAdmin] = useState(false); const [userData, setUserData] = useState(null); const [autoLogin, setAutoLogin] = useState(false); const setLoading = useAlertStore((state) => state.setLoading); const [apiKey, setApiKey] = useState( - cookies.get("apikey_tkn_lflw") + cookies.get("apikey_tkn_lflw"), ); + const getFoldersApi = useFolderStore((state) => state.getFoldersApi); useEffect(() => { const storedAccessToken = cookies.get("access_token_lf"); @@ -59,11 +61,11 @@ export function AuthProvider({ children }): React.ReactElement { function getUser() { getLoggedUser() - .then((user) => { + .then(async (user) => { setUserData(user); - setLoading(false); const isSuperUser = user!.is_superuser; setIsAdmin(isSuperUser); + await getFoldersApi(true); }) .catch((error) => { setLoading(false); diff --git a/src/frontend/src/pages/LoginPage/index.tsx b/src/frontend/src/pages/LoginPage/index.tsx index 2d9ea59c9..826b2a931 100644 --- a/src/frontend/src/pages/LoginPage/index.tsx +++ b/src/frontend/src/pages/LoginPage/index.tsx @@ -20,8 +20,7 @@ export default function LoginPage(): JSX.Element { useState(CONTROL_LOGIN_STATE); const { password, username } = inputState; - const { login, isAuthenticated, setUserData, setIsAdmin } = - useContext(AuthContext); + const { login } = useContext(AuthContext); const navigate = useNavigate(); const setErrorData = useAlertStore((state) => state.setErrorData); diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/index.tsx index 908cea255..0536b2b7b 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/index.tsx @@ -47,7 +47,6 @@ const ProfilePictureFormComponent = ({ } return prev; }); - setLoading(false); }); } }) From 34ca9a6a007d5c9130d5f4f0858d55687dbba0b0 Mon Sep 17 00:00:00 2001 From: italojohnny Date: Wed, 12 Jun 2024 19:22:49 -0300 Subject: [PATCH 14/68] Revert "Merge branch 'dev' of https://github.com/langflow-ai/langflow into dev" This reverts commit 32f67f8dba774b752451e8585d139204a318b2aa, reversing changes made to 716bd2f65b2257d4bb41f972506c662cfb91d2c1. --- src/backend/base/langflow/langflow-pre.db-shm | Bin 0 -> 32768 bytes src/backend/base/langflow/langflow-pre.db-wal | Bin 0 -> 700428 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/backend/base/langflow/langflow-pre.db-shm create mode 100644 src/backend/base/langflow/langflow-pre.db-wal diff --git a/src/backend/base/langflow/langflow-pre.db-shm b/src/backend/base/langflow/langflow-pre.db-shm new file mode 100644 index 0000000000000000000000000000000000000000..e77ab53ece30e9457154ec69a6e96f32330d8acf GIT binary patch literal 32768 zcmeI5M^hA06oro<=7Mj57p91u){QW+uH1F%P z9#xmWJSdsdvij4qvX41cX%6#&+JS2U`SZ_Z{qUbZ*c+*&9qCCjNG2IT29qIVC>chw zNH!Txa>xjhOY%rQDIkSp6d6s%kO^cWDI$}|WHNYD zmXj4^C0Ru_kTP|8RJRFik)2l+*Qlk3Sq6}ds~ zlAk1_J}{1KBV}Ye*-3Vha#BG~lh5QkN#*a5t>VgQBtEn0&D3eQJ_G+Qbjg4W$bbyU zfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih z49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbwqwEpkCiHBi0WfC*a%)k@ltb(yN zkc$h(Td0SH#@ToX22Qnb`)f1J?07C-I%+!5aT=!EUTUh7@0fyUNZ zTeXj_++^XjL7iE6Zje&Mxq;5u;e1g-u4rPNGUx8-**IfRa5kz9Qg$R0mG*<~f*9z6 zok8B(uL^P1en`l>rdaLz+{1C!^_qcmDvw5?svk?HW0%ob*Njp%p^>=cARRB!W?19; zR&#L9pynLBFi6>vfjDMA=q`wXt|$-kRqa^MhFV!)74YwTi8xtL>`lb6!Hiogvoh1`+ z$DKl+RH<)$mBf=)5jN?CeGyjpfB6{P`2R<~cuvJ8U(BH)Yy-Wq-+s`Y40vQ<0p57j z$;&oV@zBe59`xA)hjYd@rH(P{z zG3>6^&J46esj~$Mn@+(}gV`tHzCp_0ktFNurSULhdu)k^ZEBAW4D`VP2kAtPcEHvI z9e)}g`EK%gc*U9e;-K#)i_So4*cP3_zN^~6`T}_@#+r`Dv1l81&t&VH?O`uZpoM<{ DuY8%Z literal 0 HcmV?d00001 diff --git a/src/backend/base/langflow/langflow-pre.db-wal b/src/backend/base/langflow/langflow-pre.db-wal new file mode 100644 index 0000000000000000000000000000000000000000..746a1fa81882d7af6763e805909d6861b4239a95 GIT binary patch literal 700428 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 literal 0 HcmV?d00001 From 45963ffce07aade5af26302f854a78c2c5690c68 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Wed, 12 Jun 2024 19:43:41 -0300 Subject: [PATCH 15/68] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(index.tsx):=20remov?= =?UTF-8?q?e=20unnecessary=20trailing=20commas=20to=20improve=20code=20rea?= =?UTF-8?q?dability=20=F0=9F=90=9B=20(index.tsx):=20fix=20conditional=20re?= =?UTF-8?q?ndering=20of=20Download=20option=20in=20toolbar=20=E2=9C=A8=20(?= =?UTF-8?q?ProfilePictureForm):=20add=20setLoading(false)=20after=20updati?= =?UTF-8?q?ng=20state=20to=20ensure=20loading=20state=20is=20reset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/nodeToolbarComponent/index.tsx | 27 ++++++++++--------- .../components/ProfilePictureForm/index.tsx | 1 + 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index 20d282f72..8d217b2b2 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -625,7 +625,7 @@ export default function NodeToolbarComponent({ /> )} - {(!hasStore || !hasApiKey || !validApiKey) && ( + {/* {(!hasStore || !hasApiKey || !validApiKey) && ( - )} + )} */} - - obj.name === "Download")?.shortcut! - } - value={"Download"} - icon={"Download"} - dataTestId="download-button-modal" - /> - + {(!hasStore || !hasApiKey || !validApiKey) && ( + + obj.name === "Download") + ?.shortcut! + } + value={"Download"} + icon={"Download"} + dataTestId="download-button-modal" + /> + + )} Date: Thu, 13 Jun 2024 01:04:13 -0300 Subject: [PATCH 16/68] Improve makefile [edited] (#2141) * ignore sqlite tmp files * update many dependencies to be optional * improve makefile help messagem * chore: Update langflow-base dependency to use local path for development * change postgres to mandatory dependency * improve makefile readability * Revert update many dependencies to be optional This reverts commit 80780565df5bc63632e6971054670cee467542f9. --------- Co-authored-by: ogabrielluiz --- .gitignore | 2 ++ Makefile | 75 ++++++++++++++++++++++++++++++++++++----------------- poetry.lock | 1 - 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index d87a65c53..9e5e12603 100644 --- a/.gitignore +++ b/.gitignore @@ -180,6 +180,8 @@ coverage.xml local_settings.py db.sqlite3 db.sqlite3-journal +*.db-shm +*.db-wal # Flask stuff: instance/ diff --git a/Makefile b/Makefile index 4592caf9b..88487d312 100644 --- a/Makefile +++ b/Makefile @@ -9,17 +9,21 @@ open_browser ?= true path = src/backend/base/langflow/frontend workers ?= 1 + codespell: @poetry install --with spelling poetry run codespell --toml pyproject.toml + fix_codespell: @poetry install --with spelling poetry run codespell --toml pyproject.toml --write + setup_poetry: pipx install poetry + add: @echo 'Adding dependencies' ifdef devel @@ -34,45 +38,54 @@ ifdef base cd src/backend/base && poetry add $(base) endif + init: @echo 'Installing backend dependencies' make install_backend @echo 'Installing frontend dependencies' make install_frontend -coverage: + +coverage: ## run the tests and generate a coverage report poetry run pytest --cov \ --cov-config=.coveragerc \ --cov-report xml \ --cov-report term-missing:skip-covered \ --cov-report lcov:coverage/lcov-pytest.info + # allow passing arguments to pytest -tests: +tests: ## run the tests poetry run pytest tests --instafail -ra -n auto -m "not api_key_required" $(args) -format: +format: ## run code formatters poetry run ruff check . --fix poetry run ruff format . cd src/frontend && npm run format -lint: + +lint: ## run linters poetry run mypy --namespace-packages -p "langflow" -install_frontend: + +install_frontend: ## install the frontend dependencies cd src/frontend && npm install + install_frontendci: cd src/frontend && npm ci + install_frontendc: cd src/frontend && rm -rf node_modules package-lock.json && npm install + run_frontend: @-kill -9 `lsof -t -i:3000` cd src/frontend && npm start + tests_frontend: ifeq ($(UI), true) cd src/frontend && npx playwright test --ui --project=chromium @@ -80,6 +93,7 @@ else cd src/frontend && npx playwright test --project=chromium endif + run_cli: @echo 'Running the CLI' @make install_frontend > /dev/null @@ -93,6 +107,7 @@ else @make start host=$(host) port=$(port) log_level=$(log_level) endif + run_cli_debug: @echo 'Running the CLI in debug mode' @make install_frontend > /dev/null @@ -106,6 +121,7 @@ else @make start host=$(host) port=$(port) log_level=debug endif + start: @echo 'Running the CLI' @@ -116,30 +132,34 @@ else endif - setup_devcontainer: make init make build_frontend poetry run langflow --path src/frontend/build + setup_env: @sh ./scripts/setup/update_poetry.sh 1.8.2 @sh ./scripts/setup/setup_env.sh -frontend: + +frontend: ## run the frontend in development mode make install_frontend make run_frontend + frontendc: make install_frontendc make run_frontend + install_backend: @echo 'Installing backend dependencies' @poetry install @poetry run pre-commit install -backend: + +backend: ## run the backend in development mode @echo 'Setting up the environment' @make setup_env make install_backend @@ -152,6 +172,7 @@ else poetry run uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --env-file .env --loop asyncio --workers $(workers) endif + build_and_run: @echo 'Removing dist folder' @make setup_env @@ -161,18 +182,21 @@ build_and_run: poetry run pip install dist/*.tar.gz poetry run langflow run + build_and_install: @echo 'Removing dist folder' rm -rf dist rm -rf src/backend/base/dist make build && poetry run pip install dist/*.whl && pip install src/backend/base/dist/*.whl --force-reinstall -build_frontend: + +build_frontend: ## build the frontend static files cd src/frontend && CI='' npm run build rm -rf src/backend/base/langflow/frontend cp -r src/frontend/build src/backend/base/langflow/frontend -build: + +build: ## build the frontend static files and package the project @echo 'Building the project' @make setup_env ifdef base @@ -185,13 +209,16 @@ ifdef main make build_langflow endif + build_langflow_base: cd src/backend/base && poetry build rm -rf src/backend/base/langflow/frontend + build_langflow_backup: poetry lock && poetry build + build_langflow: cd ./scripts && poetry run python update_dependencies.py poetry lock @@ -201,7 +228,8 @@ ifdef restore mv poetry.lock.bak poetry.lock endif -dev: + +dev: ## run the project in development mode with docker compose make install_frontend ifeq ($(build),1) @echo 'Running docker compose up with build' @@ -211,25 +239,30 @@ else docker compose $(if $(debug),-f docker-compose.debug.yml) up endif + lock_base: cd src/backend/base && poetry lock lock_langflow: poetry lock + lock: # Run both in parallel @echo 'Locking dependencies' cd src/backend/base && poetry lock poetry lock + publish_base: cd src/backend/base && poetry publish + publish_langflow: poetry publish -publish: + +publish: ## build the frontend static files and package the project and publish it to PyPI @echo 'Publishing the project' ifdef base make publish_base @@ -239,17 +272,11 @@ ifdef main make publish_langflow endif -help: + +help: ## show this help message @echo '----' - @echo 'format - run code formatters' - @echo 'lint - run linters' - @echo 'install_frontend - install the frontend dependencies' - @echo 'build_frontend - build the frontend static files' - @echo 'run_frontend - run the frontend in development mode' - @echo 'run_backend - run the backend in development mode' - @echo 'build - build the frontend static files and package the project' - @echo 'publish - build the frontend static files and package the project and publish it to PyPI' - @echo 'dev - run the project in development mode with docker compose' - @echo 'tests - run the tests' - @echo 'coverage - run the tests and generate a coverage report' + @echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | \ + sed -e 's/:.*##\s*/:/' \ + -e 's/^\(.\+\):\(.*\)/\\x1b[36mmake \1\\x1b[m:\2/' | \ + column -c2 -t -s :']]')" @echo '----' diff --git a/poetry.lock b/poetry.lock index 0df7dac55..5fa13ff03 100644 --- a/poetry.lock +++ b/poetry.lock @@ -7498,7 +7498,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, From 061809a94e9f877f47c82da4d8e1fa851233ddd8 Mon Sep 17 00:00:00 2001 From: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com> Date: Wed, 12 Jun 2024 21:05:05 -0700 Subject: [PATCH 17/68] [feat] add embedding dimensions parameters to openai models (#2146) Add embedding dimensions parameters to openai models --- .../components/embeddings/AzureOpenAIEmbeddings.py | 8 ++++++++ .../langflow/components/embeddings/OpenAIEmbeddings.py | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/src/backend/base/langflow/components/embeddings/AzureOpenAIEmbeddings.py b/src/backend/base/langflow/components/embeddings/AzureOpenAIEmbeddings.py index dd40d64d5..4fca09762 100644 --- a/src/backend/base/langflow/components/embeddings/AzureOpenAIEmbeddings.py +++ b/src/backend/base/langflow/components/embeddings/AzureOpenAIEmbeddings.py @@ -1,3 +1,4 @@ +from typing import Optional from langchain_core.embeddings import Embeddings from langchain_openai import AzureOpenAIEmbeddings from pydantic.v1 import SecretStr @@ -44,6 +45,11 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent): "password": True, }, "code": {"show": False}, + "dimensions": { + "display_name": "Dimensions", + "info": "The number of dimensions the resulting output embeddings should have. Only supported by certain models.", + "advanced": True, + }, } def build( @@ -52,6 +58,7 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent): azure_deployment: str, api_version: str, api_key: str, + dimensions: Optional[int] = None, ) -> Embeddings: if api_key: azure_api_key = SecretStr(api_key) @@ -63,6 +70,7 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent): azure_deployment=azure_deployment, api_version=api_version, api_key=azure_api_key, + dimensions=dimensions, ) except Exception as e: diff --git a/src/backend/base/langflow/components/embeddings/OpenAIEmbeddings.py b/src/backend/base/langflow/components/embeddings/OpenAIEmbeddings.py index 2ff78d562..4813fc793 100644 --- a/src/backend/base/langflow/components/embeddings/OpenAIEmbeddings.py +++ b/src/backend/base/langflow/components/embeddings/OpenAIEmbeddings.py @@ -84,6 +84,11 @@ class OpenAIEmbeddingsComponent(CustomComponent): "advanced": True, }, "tiktoken_enable": {"display_name": "TikToken Enable", "advanced": True}, + "dimensions": { + "display_name": "Dimensions", + "info": "The number of dimensions the resulting output embeddings should have. Only supported by certain models.", + "advanced": True, + }, } def build( @@ -109,6 +114,7 @@ class OpenAIEmbeddingsComponent(CustomComponent): skip_empty: bool = False, tiktoken_enable: bool = True, tiktoken_model_name: Optional[str] = None, + dimensions: Optional[int] = None, ) -> Embeddings: # This is to avoid errors with Vector Stores (e.g Chroma) if disallowed_special == ["all"]: @@ -140,4 +146,5 @@ class OpenAIEmbeddingsComponent(CustomComponent): show_progress_bar=show_progress_bar, skip_empty=skip_empty, tiktoken_model_name=tiktoken_model_name, + dimensions=dimensions, ) From e98e3f53876bba975285c2392281da801a5e3baf Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 01:08:28 -0300 Subject: [PATCH 18/68] chore: Update package versions in pyproject.toml files --- poetry.lock | 48 +++++++++++++++++---------------- pyproject.toml | 2 +- src/backend/base/poetry.lock | 7 ++--- src/backend/base/pyproject.toml | 2 +- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5fa13ff03..29144c27b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -471,17 +471,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.124" +version = "1.34.125" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.124-py3-none-any.whl", hash = "sha256:549d9735bbb7cf8e1a2f4b3b676be32946717c59f33c47234586835dc904d04e"}, - {file = "boto3-1.34.124.tar.gz", hash = "sha256:a91ee58fa54b170f17b2e144f038e155f92cf515f1c073ac2595e9ee45f125a8"}, + {file = "boto3-1.34.125-py3-none-any.whl", hash = "sha256:116d9eb3c26cf313a2e1e44ef704d1f98f9eb18e7628695d07b01b44a8683544"}, + {file = "boto3-1.34.125.tar.gz", hash = "sha256:31c4a5e4d6f9e6116be61ff654b424ddbd1afcdefe0e8b870c4796f9108eb1c6"}, ] [package.dependencies] -botocore = ">=1.34.124,<1.35.0" +botocore = ">=1.34.125,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -490,13 +490,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.124" +version = "1.34.125" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.124-py3-none-any.whl", hash = "sha256:fede092c7f8f414f78f3e7991e47cb0cf2279d90c027606ad7cba79fa370b827"}, - {file = "botocore-1.34.124.tar.gz", hash = "sha256:3f0bf79c17d656acdfdb53581224f6a38867ff2829f7428c586198f67a90ea26"}, + {file = "botocore-1.34.125-py3-none-any.whl", hash = "sha256:71e97e7d2c088f1188ba6976441b5857a5425acd4aaa31b45d13119c9cb86424"}, + {file = "botocore-1.34.125.tar.gz", hash = "sha256:d2882be011ad5b16e7ab4a96360b5b66a0a7e175c1ea06dbf2de473c0a0a33d8"}, ] [package.dependencies] @@ -2092,18 +2092,18 @@ zstandard = ["zstandard"] [[package]] name = "filelock" -version = "3.14.0" +version = "3.15.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, - {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, + {file = "filelock-3.15.1-py3-none-any.whl", hash = "sha256:71b3102950e91dfc1bb4209b64be4dc8854f40e5f534428d8684f953ac847fac"}, + {file = "filelock-3.15.1.tar.gz", hash = "sha256:58a2549afdf9e02e10720eaa4d4470f56386d7a6f72edd7d0596337af8ed7ad8"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] @@ -2438,8 +2438,8 @@ files = [ [package.dependencies] cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} greenlet = [ - {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, + {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, ] "zope.event" = "*" "zope.interface" = "*" @@ -2566,12 +2566,12 @@ files = [ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = [ - {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] grpcio-status = [ - {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" @@ -4348,16 +4348,17 @@ extended-testing = ["beautifulsoup4 (>=4.12.3,<5.0.0)", "lxml (>=4.9.3,<6.0)"] [[package]] name = "langchainhub" -version = "0.1.18" +version = "0.1.20" description = "The LangChain Hub API client" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchainhub-0.1.18-py3-none-any.whl", hash = "sha256:11501f15e7f34715ecc8892587daa35c6f2a3005e1f2926c9bcabd31fc2c100c"}, - {file = "langchainhub-0.1.18.tar.gz", hash = "sha256:f2d0d8bf3abe4ca5e70511d8220bdc9ccea28d5267bcfd0e5ef9c53bd5bd3bad"}, + {file = "langchainhub-0.1.20-py3-none-any.whl", hash = "sha256:b3cbb5b2d7d6f9c3f89748bcc74424d8030ed4ebca58b5f44e0b6d9f111e33eb"}, + {file = "langchainhub-0.1.20.tar.gz", hash = "sha256:499fa8958233071f35750987f325005d16241bebd455163955b607c195c37f25"}, ] [package.dependencies] +packaging = ">=23.2,<25" requests = ">=2,<3" types-requests = ">=2.31.0.2,<3.0.0.0" @@ -4377,7 +4378,7 @@ six = "*" [[package]] name = "langflow-base" -version = "0.0.64" +version = "0.0.65" description = "A Python package with a built-in web application" optional = false python-versions = ">=3.10,<3.13" @@ -4543,8 +4544,8 @@ psutil = ">=5.9.1" pywin32 = {version = "*", markers = "platform_system == \"Windows\""} pyzmq = ">=25.0.0" requests = [ - {version = ">=2.32.2", markers = "python_version > \"3.11\""}, {version = ">=2.26.0", markers = "python_version <= \"3.11\""}, + {version = ">=2.32.2", markers = "python_version > \"3.11\""}, ] tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} Werkzeug = ">=2.0.0" @@ -5666,13 +5667,13 @@ sympy = "*" [[package]] name = "openai" -version = "1.33.0" +version = "1.34.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.33.0-py3-none-any.whl", hash = "sha256:621163b56570897ab8389d187f686a53d4771fd6ce95d481c0a9611fe8bc4229"}, - {file = "openai-1.33.0.tar.gz", hash = "sha256:1169211a7b326ecbc821cafb427c29bfd0871f9a3e0947dd9e51acb3b0f1df78"}, + {file = "openai-1.34.0-py3-none-any.whl", hash = "sha256:018623c2f795424044675c6230fa3bfbf98d9e0aab45d8fd116f2efb2cfb6b7e"}, + {file = "openai-1.34.0.tar.gz", hash = "sha256:95c8e2da4acd6958e626186957d656597613587195abd0fb2527566a93e76770"}, ] [package.dependencies] @@ -6012,9 +6013,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -7498,6 +7499,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, diff --git a/pyproject.toml b/pyproject.toml index e83fe9b8e..2d1ad64f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow" -version = "1.0.0a53" +version = "1.0.0a54" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ diff --git a/src/backend/base/poetry.lock b/src/backend/base/poetry.lock index 70bafac56..e6d42312d 100644 --- a/src/backend/base/poetry.lock +++ b/src/backend/base/poetry.lock @@ -1260,16 +1260,17 @@ extended-testing = ["beautifulsoup4 (>=4.12.3,<5.0.0)", "lxml (>=4.9.3,<6.0)"] [[package]] name = "langchainhub" -version = "0.1.18" +version = "0.1.20" description = "The LangChain Hub API client" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchainhub-0.1.18-py3-none-any.whl", hash = "sha256:11501f15e7f34715ecc8892587daa35c6f2a3005e1f2926c9bcabd31fc2c100c"}, - {file = "langchainhub-0.1.18.tar.gz", hash = "sha256:f2d0d8bf3abe4ca5e70511d8220bdc9ccea28d5267bcfd0e5ef9c53bd5bd3bad"}, + {file = "langchainhub-0.1.20-py3-none-any.whl", hash = "sha256:b3cbb5b2d7d6f9c3f89748bcc74424d8030ed4ebca58b5f44e0b6d9f111e33eb"}, + {file = "langchainhub-0.1.20.tar.gz", hash = "sha256:499fa8958233071f35750987f325005d16241bebd455163955b607c195c37f25"}, ] [package.dependencies] +packaging = ">=23.2,<25" requests = ">=2,<3" types-requests = ">=2.31.0.2,<3.0.0.0" diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index cadf3d6f3..1dd16933a 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow-base" -version = "0.0.64" +version = "0.0.65" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ From 84df4fd8e4976a8dfb41923c338a50c95cf0c539 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 09:33:03 -0300 Subject: [PATCH 19/68] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(authContext.tsx):?= =?UTF-8?q?=20remove=20unused=20import=20and=20comment=20out=20unused=20co?= =?UTF-8?q?de=20=F0=9F=90=9B=20(flowSettingsModal):=20fix=20potential=20nu?= =?UTF-8?q?ll=20reference=20error=20in=20useEffect=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/contexts/authContext.tsx | 5 ++--- src/frontend/src/modals/flowSettingsModal/index.tsx | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/contexts/authContext.tsx b/src/frontend/src/contexts/authContext.tsx index fbb7f8f1e..b37816571 100644 --- a/src/frontend/src/contexts/authContext.tsx +++ b/src/frontend/src/contexts/authContext.tsx @@ -3,7 +3,6 @@ import { useNavigate } from "react-router-dom"; import Cookies from "universal-cookie"; import { getLoggedUser, requestLogout } from "../controllers/API"; import useAlertStore from "../stores/alertStore"; -import { useFolderStore } from "../stores/foldersStore"; import { Users } from "../types/api"; import { AuthContextType } from "../types/contexts/auth"; @@ -43,7 +42,7 @@ export function AuthProvider({ children }): React.ReactElement { const [apiKey, setApiKey] = useState( cookies.get("apikey_tkn_lflw"), ); - const getFoldersApi = useFolderStore((state) => state.getFoldersApi); + // const getFoldersApi = useFolderStore((state) => state.getFoldersApi); useEffect(() => { const storedAccessToken = cookies.get("access_token_lf"); @@ -65,7 +64,7 @@ export function AuthProvider({ children }): React.ReactElement { setUserData(user); const isSuperUser = user!.is_superuser; setIsAdmin(isSuperUser); - await getFoldersApi(true); + // await getFoldersApi(true); }) .catch((error) => { setLoading(false); diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx index f8d62b7fb..912097a01 100644 --- a/src/frontend/src/modals/flowSettingsModal/index.tsx +++ b/src/frontend/src/modals/flowSettingsModal/index.tsx @@ -18,7 +18,7 @@ export default function FlowSettingsModal({ useEffect(() => { setName(currentFlow!.name); setDescription(currentFlow!.description); - }, [currentFlow!.name, currentFlow!.description, open]); + }, [currentFlow?.name, currentFlow?.description, open]); const [name, setName] = useState(currentFlow!.name); const [description, setDescription] = useState(currentFlow!.description); From 80634f95479164b240e3662f32d3cfb26f6cd4b0 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 09:52:20 -0300 Subject: [PATCH 20/68] =?UTF-8?q?=F0=9F=90=9B=20(index.tsx):=20remove=20de?= =?UTF-8?q?bugger=20statement=20to=20clean=20up=20code=20=E2=99=BB?= =?UTF-8?q?=EF=B8=8F=20(index.tsx):=20remove=20unnecessary=20trailing=20co?= =?UTF-8?q?mmas=20for=20consistency=20=F0=9F=90=9B=20(foldersStore.tsx):?= =?UTF-8?q?=20add=20optional=20chaining=20to=20handle=20potential=20undefi?= =?UTF-8?q?ned=20responses=20=E2=99=BB=EF=B8=8F=20(foldersStore.tsx):=20re?= =?UTF-8?q?move=20unnecessary=20trailing=20commas=20for=20consistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/modals/IOModal/components/chatView/index.tsx | 1 - src/frontend/src/stores/foldersStore.tsx | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 736d21a6f..3c3ae0fbf 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -86,7 +86,6 @@ export default function ChatView({ }; } catch (e) { console.error(e); - debugger; return { isSend: false, message: "Error parsing message", diff --git a/src/frontend/src/stores/foldersStore.tsx b/src/frontend/src/stores/foldersStore.tsx index e72610987..b7299d192 100644 --- a/src/frontend/src/stores/foldersStore.tsx +++ b/src/frontend/src/stores/foldersStore.tsx @@ -16,11 +16,11 @@ export const useFolderStore = create((set, get) => ({ get().setLoading(true); getFolders().then( (res) => { - const foldersWithoutStarterProjects = res.filter( + const foldersWithoutStarterProjects = res?.filter( (folder) => folder.name !== STARTER_FOLDER_NAME, ); - const starterProjects = res.find( + const starterProjects = res?.find( (folder) => folder.name === STARTER_FOLDER_NAME, ); From 34b6153fed85bbfa5d94777e2631283c177c3fa4 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 13 Jun 2024 06:52:32 -0700 Subject: [PATCH 21/68] chore: Update persist_directory parameter to handle None value in ChromaSearch and Chroma components (#2157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: Update persist_directory parameter to handle None value in ChromaSearch and Chroma components * 🐛 (test_endpoints.py): fix assertion to check for correct key name in output results for chat and any input types --- .../base/langflow/components/vectorsearch/ChromaSearch.py | 4 ++-- src/backend/base/langflow/components/vectorstores/Chroma.py | 3 +-- tests/test_endpoints.py | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/backend/base/langflow/components/vectorsearch/ChromaSearch.py b/src/backend/base/langflow/components/vectorsearch/ChromaSearch.py index 228e100e4..3d4687522 100644 --- a/src/backend/base/langflow/components/vectorsearch/ChromaSearch.py +++ b/src/backend/base/langflow/components/vectorsearch/ChromaSearch.py @@ -3,7 +3,6 @@ from typing import List, Optional import chromadb from chromadb.config import Settings from langchain_chroma import Chroma - from langflow.components.vectorstores.base.model import LCVectorStoreComponent from langflow.field_typing import Embeddings, Text from langflow.schema import Record @@ -104,10 +103,11 @@ class ChromaSearchComponent(LCVectorStoreComponent): client = chromadb.HttpClient(settings=chroma_settings) if index_directory: index_directory = self.resolve_path(index_directory) + vector_store = Chroma( embedding_function=embedding, collection_name=collection_name, - persist_directory=index_directory, + persist_directory=index_directory or None, client=client, ) diff --git a/src/backend/base/langflow/components/vectorstores/Chroma.py b/src/backend/base/langflow/components/vectorstores/Chroma.py index 6001b119c..f7080d2fc 100644 --- a/src/backend/base/langflow/components/vectorstores/Chroma.py +++ b/src/backend/base/langflow/components/vectorstores/Chroma.py @@ -7,7 +7,6 @@ from langchain_chroma import Chroma from langchain_core.embeddings import Embeddings from langchain_core.retrievers import BaseRetriever from langchain_core.vectorstores import VectorStore - from langflow.base.vectorstores.utils import chroma_collection_to_records from langflow.custom import CustomComponent from langflow.schema import Record @@ -107,7 +106,7 @@ class ChromaComponent(CustomComponent): index_directory = self.resolve_path(index_directory) chroma = Chroma( - persist_directory=index_directory, + persist_directory=index_directory or None, client=client, embedding_function=embedding, collection_name=collection_name, diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index d7c8a4ef2..1f1dcd5df 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -597,7 +597,7 @@ def test_successful_run_with_input_type_chat(client, starter_project, created_ap chat_input_outputs = [output for output in outputs_dict.get("outputs") if "ChatInput" in output.get("component_id")] assert len(chat_input_outputs) == 1 # Now we check if the input_value is correct - assert all([output.get("results").get("result") == "value1" for output in chat_input_outputs]), chat_input_outputs + assert all([output.get("results").get("text") == "value1" for output in chat_input_outputs]), chat_input_outputs def test_successful_run_with_input_type_any(client, starter_project, created_api_key): @@ -631,7 +631,7 @@ def test_successful_run_with_input_type_any(client, starter_project, created_api ] assert len(any_input_outputs) == 1 # Now we check if the input_value is correct - assert all([output.get("results").get("result") == "value1" for output in any_input_outputs]), any_input_outputs + assert all([output.get("results").get("text") == "value1" for output in any_input_outputs]), any_input_outputs @pytest.mark.api_key_required From 18b95c5092320e014cf353bb73be3e78acc945df Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 13 Jun 2024 07:27:15 -0700 Subject: [PATCH 22/68] chore: Update duckdb dependency to version 1.0.0 and improve MonitorService code (#2158) --- poetry.lock | 103 +++++++++--------- .../base/langflow/services/monitor/service.py | 27 +++-- src/backend/base/poetry.lock | 103 +++++++++--------- src/backend/base/pyproject.toml | 2 +- 4 files changed, 116 insertions(+), 119 deletions(-) diff --git a/poetry.lock b/poetry.lock index 29144c27b..4701d9fb2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1767,58 +1767,57 @@ weaviate = ["weaviate-client (>=3.26.1,<3.27.0)", "weaviate-client (>=4.5.4,<4.6 [[package]] name = "duckdb" -version = "0.10.3" +version = "1.0.0" description = "DuckDB in-process database" optional = false python-versions = ">=3.7.0" files = [ - {file = "duckdb-0.10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd25cc8d001c09a19340739ba59d33e12a81ab285b7a6bed37169655e1cefb31"}, - {file = "duckdb-0.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f9259c637b917ca0f4c63887e8d9b35ec248f5d987c886dfc4229d66a791009"}, - {file = "duckdb-0.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b48f5f1542f1e4b184e6b4fc188f497be8b9c48127867e7d9a5f4a3e334f88b0"}, - {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e327f7a3951ea154bb56e3fef7da889e790bd9a67ca3c36afc1beb17d3feb6d6"}, - {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d8b20ed67da004b4481973f4254fd79a0e5af957d2382eac8624b5c527ec48c"}, - {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d37680b8d7be04e4709db3a66c8b3eb7ceba2a5276574903528632f2b2cc2e60"}, - {file = "duckdb-0.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d34b86d6a2a6dfe8bb757f90bfe7101a3bd9e3022bf19dbddfa4b32680d26a9"}, - {file = "duckdb-0.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:73b1cb283ca0f6576dc18183fd315b4e487a545667ffebbf50b08eb4e8cdc143"}, - {file = "duckdb-0.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d917dde19fcec8cadcbef1f23946e85dee626ddc133e1e3f6551f15a61a03c61"}, - {file = "duckdb-0.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46757e0cf5f44b4cb820c48a34f339a9ccf83b43d525d44947273a585a4ed822"}, - {file = "duckdb-0.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:338c14d8ac53ac4aa9ec03b6f1325ecfe609ceeb72565124d489cb07f8a1e4eb"}, - {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:651fcb429602b79a3cf76b662a39e93e9c3e6650f7018258f4af344c816dab72"}, - {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3ae3c73b98b6215dab93cc9bc936b94aed55b53c34ba01dec863c5cab9f8e25"}, - {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56429b2cfe70e367fb818c2be19f59ce2f6b080c8382c4d10b4f90ba81f774e9"}, - {file = "duckdb-0.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b46c02c2e39e3676b1bb0dc7720b8aa953734de4fd1b762e6d7375fbeb1b63af"}, - {file = "duckdb-0.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:bcd460feef56575af2c2443d7394d405a164c409e9794a4d94cb5fdaa24a0ba4"}, - {file = "duckdb-0.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e229a7c6361afbb0d0ab29b1b398c10921263c52957aefe3ace99b0426fdb91e"}, - {file = "duckdb-0.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:732b1d3b6b17bf2f32ea696b9afc9e033493c5a3b783c292ca4b0ee7cc7b0e66"}, - {file = "duckdb-0.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5380d4db11fec5021389fb85d614680dc12757ef7c5881262742250e0b58c75"}, - {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:468a4e0c0b13c55f84972b1110060d1b0f854ffeb5900a178a775259ec1562db"}, - {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fa1e7ff8d18d71defa84e79f5c86aa25d3be80d7cb7bc259a322de6d7cc72da"}, - {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed1063ed97c02e9cf2e7fd1d280de2d1e243d72268330f45344c69c7ce438a01"}, - {file = "duckdb-0.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:22f2aad5bb49c007f3bfcd3e81fdedbc16a2ae41f2915fc278724ca494128b0c"}, - {file = "duckdb-0.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:8f9e2bb00a048eb70b73a494bdc868ce7549b342f7ffec88192a78e5a4e164bd"}, - {file = "duckdb-0.10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6c2fc49875b4b54e882d68703083ca6f84b27536d57d623fc872e2f502b1078"}, - {file = "duckdb-0.10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a66c125d0c30af210f7ee599e7821c3d1a7e09208196dafbf997d4e0cfcb81ab"}, - {file = "duckdb-0.10.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99dd7a1d901149c7a276440d6e737b2777e17d2046f5efb0c06ad3b8cb066a6"}, - {file = "duckdb-0.10.3-cp37-cp37m-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ec3bbdb209e6095d202202893763e26c17c88293b88ef986b619e6c8b6715bd"}, - {file = "duckdb-0.10.3-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:2b3dec4ef8ed355d7b7230b40950b30d0def2c387a2e8cd7efc80b9d14134ecf"}, - {file = "duckdb-0.10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:04129f94fb49bba5eea22f941f0fb30337f069a04993048b59e2811f52d564bc"}, - {file = "duckdb-0.10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d75d67024fc22c8edfd47747c8550fb3c34fb1cbcbfd567e94939ffd9c9e3ca7"}, - {file = "duckdb-0.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f3796e9507c02d0ddbba2e84c994fae131da567ce3d9cbb4cbcd32fadc5fbb26"}, - {file = "duckdb-0.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:78e539d85ebd84e3e87ec44d28ad912ca4ca444fe705794e0de9be3dd5550c11"}, - {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a99b67ac674b4de32073e9bc604b9c2273d399325181ff50b436c6da17bf00a"}, - {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1209a354a763758c4017a1f6a9f9b154a83bed4458287af9f71d84664ddb86b6"}, - {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b735cea64aab39b67c136ab3a571dbf834067f8472ba2f8bf0341bc91bea820"}, - {file = "duckdb-0.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:816ffb9f758ed98eb02199d9321d592d7a32a6cb6aa31930f4337eb22cfc64e2"}, - {file = "duckdb-0.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:1631184b94c3dc38b13bce4045bf3ae7e1b0ecbfbb8771eb8d751d8ffe1b59b3"}, - {file = "duckdb-0.10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fb98c35fc8dd65043bc08a2414dd9f59c680d7e8656295b8969f3f2061f26c52"}, - {file = "duckdb-0.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e75c9f5b6a92b2a6816605c001d30790f6d67ce627a2b848d4d6040686efdf9"}, - {file = "duckdb-0.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae786eddf1c2fd003466e13393b9348a44b6061af6fe7bcb380a64cac24e7df7"}, - {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9387da7b7973707b0dea2588749660dd5dd724273222680e985a2dd36787668"}, - {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:538f943bf9fa8a3a7c4fafa05f21a69539d2c8a68e557233cbe9d989ae232899"}, - {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6930608f35025a73eb94252964f9f19dd68cf2aaa471da3982cf6694866cfa63"}, - {file = "duckdb-0.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:03bc54a9cde5490918aad82d7d2a34290e3dfb78d5b889c6626625c0f141272a"}, - {file = "duckdb-0.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:372b6e3901d85108cafe5df03c872dfb6f0dbff66165a0cf46c47246c1957aa0"}, - {file = "duckdb-0.10.3.tar.gz", hash = "sha256:c5bd84a92bc708d3a6adffe1f554b94c6e76c795826daaaf482afc3d9c636971"}, + {file = "duckdb-1.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4a8ce2d1f9e1c23b9bab3ae4ca7997e9822e21563ff8f646992663f66d050211"}, + {file = "duckdb-1.0.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:19797670f20f430196e48d25d082a264b66150c264c1e8eae8e22c64c2c5f3f5"}, + {file = "duckdb-1.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b71c342090fe117b35d866a91ad6bffce61cd6ff3e0cff4003f93fc1506da0d8"}, + {file = "duckdb-1.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25dd69f44ad212c35ae2ea736b0e643ea2b70f204b8dff483af1491b0e2a4cec"}, + {file = "duckdb-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8da5f293ecb4f99daa9a9352c5fd1312a6ab02b464653a0c3a25ab7065c45d4d"}, + {file = "duckdb-1.0.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3207936da9967ddbb60644ec291eb934d5819b08169bc35d08b2dedbe7068c60"}, + {file = "duckdb-1.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1128d6c9c33e883b1f5df6b57c1eb46b7ab1baf2650912d77ee769aaa05111f9"}, + {file = "duckdb-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:02310d263474d0ac238646677feff47190ffb82544c018b2ff732a4cb462c6ef"}, + {file = "duckdb-1.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:75586791ab2702719c284157b65ecefe12d0cca9041da474391896ddd9aa71a4"}, + {file = "duckdb-1.0.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:83bb415fc7994e641344f3489e40430ce083b78963cb1057bf714ac3a58da3ba"}, + {file = "duckdb-1.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:bee2e0b415074e84c5a2cefd91f6b5ebeb4283e7196ba4ef65175a7cef298b57"}, + {file = "duckdb-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa5a4110d2a499312609544ad0be61e85a5cdad90e5b6d75ad16b300bf075b90"}, + {file = "duckdb-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa389e6a382d4707b5f3d1bc2087895925ebb92b77e9fe3bfb23c9b98372fdc"}, + {file = "duckdb-1.0.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ede6f5277dd851f1a4586b0c78dc93f6c26da45e12b23ee0e88c76519cbdbe0"}, + {file = "duckdb-1.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0b88cdbc0d5c3e3d7545a341784dc6cafd90fc035f17b2f04bf1e870c68456e5"}, + {file = "duckdb-1.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd1693cdd15375156f7fff4745debc14e5c54928589f67b87fb8eace9880c370"}, + {file = "duckdb-1.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c65a7fe8a8ce21b985356ee3ec0c3d3b3b2234e288e64b4cfb03356dbe6e5583"}, + {file = "duckdb-1.0.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:e5a8eda554379b3a43b07bad00968acc14dd3e518c9fbe8f128b484cf95e3d16"}, + {file = "duckdb-1.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:a1b6acdd54c4a7b43bd7cb584975a1b2ff88ea1a31607a2b734b17960e7d3088"}, + {file = "duckdb-1.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a677bb1b6a8e7cab4a19874249d8144296e6e39dae38fce66a80f26d15e670df"}, + {file = "duckdb-1.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:752e9d412b0a2871bf615a2ede54be494c6dc289d076974eefbf3af28129c759"}, + {file = "duckdb-1.0.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3aadb99d098c5e32d00dc09421bc63a47134a6a0de9d7cd6abf21780b678663c"}, + {file = "duckdb-1.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83b7091d4da3e9301c4f9378833f5ffe934fb1ad2b387b439ee067b2c10c8bb0"}, + {file = "duckdb-1.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:6a8058d0148b544694cb5ea331db44f6c2a00a7b03776cc4dd1470735c3d5ff7"}, + {file = "duckdb-1.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e40cb20e5ee19d44bc66ec99969af791702a049079dc5f248c33b1c56af055f4"}, + {file = "duckdb-1.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7bce1bc0de9af9f47328e24e6e7e39da30093179b1c031897c042dd94a59c8e"}, + {file = "duckdb-1.0.0-cp37-cp37m-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8355507f7a04bc0a3666958f4414a58e06141d603e91c0fa5a7c50e49867fb6d"}, + {file = "duckdb-1.0.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:39f1a46f5a45ad2886dc9b02ce5b484f437f90de66c327f86606d9ba4479d475"}, + {file = "duckdb-1.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d29ba477b27ae41676b62c8fae8d04ee7cbe458127a44f6049888231ca58fa"}, + {file = "duckdb-1.0.0-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:1bea713c1925918714328da76e79a1f7651b2b503511498ccf5e007a7e67d49e"}, + {file = "duckdb-1.0.0-cp38-cp38-macosx_12_0_universal2.whl", hash = "sha256:bfe67f3bcf181edbf6f918b8c963eb060e6aa26697d86590da4edc5707205450"}, + {file = "duckdb-1.0.0-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:dbc6093a75242f002be1d96a6ace3fdf1d002c813e67baff52112e899de9292f"}, + {file = "duckdb-1.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba1881a2b11c507cee18f8fd9ef10100be066fddaa2c20fba1f9a664245cd6d8"}, + {file = "duckdb-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:445d0bb35087c522705c724a75f9f1c13f1eb017305b694d2686218d653c8142"}, + {file = "duckdb-1.0.0-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:224553432e84432ffb9684f33206572477049b371ce68cc313a01e214f2fbdda"}, + {file = "duckdb-1.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d3914032e47c4e76636ad986d466b63fdea65e37be8a6dfc484ed3f462c4fde4"}, + {file = "duckdb-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:af9128a2eb7e1bb50cd2c2020d825fb2946fdad0a2558920cd5411d998999334"}, + {file = "duckdb-1.0.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:dd2659a5dbc0df0de68f617a605bf12fe4da85ba24f67c08730984a0892087e8"}, + {file = "duckdb-1.0.0-cp39-cp39-macosx_12_0_universal2.whl", hash = "sha256:ac5a4afb0bc20725e734e0b2c17e99a274de4801aff0d4e765d276b99dad6d90"}, + {file = "duckdb-1.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:2c5a53bee3668d6e84c0536164589d5127b23d298e4c443d83f55e4150fafe61"}, + {file = "duckdb-1.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b980713244d7708b25ee0a73de0c65f0e5521c47a0e907f5e1b933d79d972ef6"}, + {file = "duckdb-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cbd4f9fe7b7a56eff96c3f4d6778770dd370469ca2212eddbae5dd63749db5"}, + {file = "duckdb-1.0.0-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed228167c5d49888c5ef36f6f9cbf65011c2daf9dcb53ea8aa7a041ce567b3e4"}, + {file = "duckdb-1.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46d8395fbcea7231fd5032a250b673cc99352fef349b718a23dea2c0dd2b8dec"}, + {file = "duckdb-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:6ad1fc1a4d57e7616944166a5f9417bdbca1ea65c490797e3786e3a42e162d8a"}, + {file = "duckdb-1.0.0.tar.gz", hash = "sha256:a2a059b77bc7d5b76ae9d88e267372deff19c291048d59450c431e166233d453"}, ] [[package]] @@ -4392,7 +4391,7 @@ bcrypt = "4.0.1" cachetools = "^5.3.1" cryptography = "^42.0.5" docstring-parser = "^0.15" -duckdb = "^0.10.2" +duckdb = "^1.0.0" emoji = "^2.12.0" fastapi = "^0.111.0" gunicorn = "^22.0.0" @@ -6935,13 +6934,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-settings" -version = "2.3.2" +version = "2.3.3" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_settings-2.3.2-py3-none-any.whl", hash = "sha256:ae06e44349e4c7bff8d57aff415dfd397ae75c217a098d54e9e6990ad7594ac7"}, - {file = "pydantic_settings-2.3.2.tar.gz", hash = "sha256:05d33003c74c2cd585de97b59eb17b6ed67181bc8a3ce594d74b5d24e4df7323"}, + {file = "pydantic_settings-2.3.3-py3-none-any.whl", hash = "sha256:e4ed62ad851670975ec11285141db888fd24947f9440bd4380d7d8788d4965de"}, + {file = "pydantic_settings-2.3.3.tar.gz", hash = "sha256:87fda838b64b5039b970cd47c3e8a1ee460ce136278ff672980af21516f6e6ce"}, ] [package.dependencies] diff --git a/src/backend/base/langflow/services/monitor/service.py b/src/backend/base/langflow/services/monitor/service.py index 949877f4c..92c642e20 100644 --- a/src/backend/base/langflow/services/monitor/service.py +++ b/src/backend/base/langflow/services/monitor/service.py @@ -1,14 +1,13 @@ from datetime import datetime from pathlib import Path -from typing import TYPE_CHECKING, Optional, Union, List +from typing import TYPE_CHECKING, List, Optional, Union import duckdb -from loguru import logger -from platformdirs import user_cache_dir - from langflow.services.base import Service from langflow.services.monitor.schema import MessageModel, TransactionModel, VertexBuildModel from langflow.services.monitor.utils import add_row_to_table, drop_and_create_table_if_schema_mismatch +from loguru import logger +from platformdirs import user_cache_dir if TYPE_CHECKING: from langflow.services.settings.manager import SettingsService @@ -32,8 +31,8 @@ class MonitorService(Service): except Exception as e: logger.exception(f"Error initializing monitor service: {e}") - def exec_query(self, query: str): - with duckdb.connect(str(self.db_path)) as conn: + def exec_query(self, query: str, read_only: bool = False): + with duckdb.connect(str(self.db_path), read_only=read_only) as conn: return conn.execute(query).df() def to_df(self, table_name): @@ -55,7 +54,7 @@ class MonitorService(Service): raise ValueError(f"Unknown table name: {table_name}") # Connect to DuckDB and add the row - with duckdb.connect(str(self.db_path)) as conn: + with duckdb.connect(str(self.db_path), read_only=False) as conn: add_row_to_table(conn, table_name, model, data) def load_table_as_dataframe(self, table_name): @@ -89,7 +88,7 @@ class MonitorService(Service): if order_by: query += f" ORDER BY {order_by}" - with duckdb.connect(str(self.db_path)) as conn: + with duckdb.connect(str(self.db_path), read_only=True) as conn: df = conn.execute(query).df() return df.to_dict(orient="records") @@ -99,13 +98,13 @@ class MonitorService(Service): if flow_id: query += f" WHERE flow_id = '{flow_id}'" - with duckdb.connect(str(self.db_path)) as conn: + with duckdb.connect(str(self.db_path), read_only=False) as conn: conn.execute(query) def delete_messages_session(self, session_id: str): query = f"DELETE FROM messages WHERE session_id = '{session_id}'" - return self.exec_query(query) + return self.exec_query(query, read_only=False) def delete_messages(self, message_ids: Union[List[int], str]): if isinstance(message_ids, list): @@ -119,14 +118,14 @@ class MonitorService(Service): query = f"DELETE FROM messages WHERE index IN ({ids_str})" - return self.exec_query(query) + return self.exec_query(query, read_only=False) def update_message(self, message_id: str, **kwargs): query = ( f"""UPDATE messages SET {', '.join(f"{k} = '{v}'" for k, v in kwargs.items())} WHERE index = {message_id}""" ) - return self.exec_query(query) + return self.exec_query(query, read_only=False) def add_message(self, message: MessageModel): self.add_row("messages", message) @@ -162,7 +161,7 @@ class MonitorService(Service): if limit is not None: query += f" LIMIT {limit}" - with duckdb.connect(str(self.db_path)) as conn: + with duckdb.connect(str(self.db_path), read_only=True) as conn: df = conn.execute(query).df() return df @@ -193,7 +192,7 @@ class MonitorService(Service): if order_by: query += f" ORDER BY {order_by} DESC" - with duckdb.connect(str(self.db_path)) as conn: + with duckdb.connect(str(self.db_path), read_only=True) as conn: df = conn.execute(query).df() return df.to_dict(orient="records") diff --git a/src/backend/base/poetry.lock b/src/backend/base/poetry.lock index e6d42312d..ab30d8f08 100644 --- a/src/backend/base/poetry.lock +++ b/src/backend/base/poetry.lock @@ -578,58 +578,57 @@ files = [ [[package]] name = "duckdb" -version = "0.10.3" +version = "1.0.0" description = "DuckDB in-process database" optional = false python-versions = ">=3.7.0" files = [ - {file = "duckdb-0.10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd25cc8d001c09a19340739ba59d33e12a81ab285b7a6bed37169655e1cefb31"}, - {file = "duckdb-0.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f9259c637b917ca0f4c63887e8d9b35ec248f5d987c886dfc4229d66a791009"}, - {file = "duckdb-0.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b48f5f1542f1e4b184e6b4fc188f497be8b9c48127867e7d9a5f4a3e334f88b0"}, - {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e327f7a3951ea154bb56e3fef7da889e790bd9a67ca3c36afc1beb17d3feb6d6"}, - {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d8b20ed67da004b4481973f4254fd79a0e5af957d2382eac8624b5c527ec48c"}, - {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d37680b8d7be04e4709db3a66c8b3eb7ceba2a5276574903528632f2b2cc2e60"}, - {file = "duckdb-0.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d34b86d6a2a6dfe8bb757f90bfe7101a3bd9e3022bf19dbddfa4b32680d26a9"}, - {file = "duckdb-0.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:73b1cb283ca0f6576dc18183fd315b4e487a545667ffebbf50b08eb4e8cdc143"}, - {file = "duckdb-0.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d917dde19fcec8cadcbef1f23946e85dee626ddc133e1e3f6551f15a61a03c61"}, - {file = "duckdb-0.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46757e0cf5f44b4cb820c48a34f339a9ccf83b43d525d44947273a585a4ed822"}, - {file = "duckdb-0.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:338c14d8ac53ac4aa9ec03b6f1325ecfe609ceeb72565124d489cb07f8a1e4eb"}, - {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:651fcb429602b79a3cf76b662a39e93e9c3e6650f7018258f4af344c816dab72"}, - {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3ae3c73b98b6215dab93cc9bc936b94aed55b53c34ba01dec863c5cab9f8e25"}, - {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56429b2cfe70e367fb818c2be19f59ce2f6b080c8382c4d10b4f90ba81f774e9"}, - {file = "duckdb-0.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b46c02c2e39e3676b1bb0dc7720b8aa953734de4fd1b762e6d7375fbeb1b63af"}, - {file = "duckdb-0.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:bcd460feef56575af2c2443d7394d405a164c409e9794a4d94cb5fdaa24a0ba4"}, - {file = "duckdb-0.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e229a7c6361afbb0d0ab29b1b398c10921263c52957aefe3ace99b0426fdb91e"}, - {file = "duckdb-0.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:732b1d3b6b17bf2f32ea696b9afc9e033493c5a3b783c292ca4b0ee7cc7b0e66"}, - {file = "duckdb-0.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5380d4db11fec5021389fb85d614680dc12757ef7c5881262742250e0b58c75"}, - {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:468a4e0c0b13c55f84972b1110060d1b0f854ffeb5900a178a775259ec1562db"}, - {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fa1e7ff8d18d71defa84e79f5c86aa25d3be80d7cb7bc259a322de6d7cc72da"}, - {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed1063ed97c02e9cf2e7fd1d280de2d1e243d72268330f45344c69c7ce438a01"}, - {file = "duckdb-0.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:22f2aad5bb49c007f3bfcd3e81fdedbc16a2ae41f2915fc278724ca494128b0c"}, - {file = "duckdb-0.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:8f9e2bb00a048eb70b73a494bdc868ce7549b342f7ffec88192a78e5a4e164bd"}, - {file = "duckdb-0.10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6c2fc49875b4b54e882d68703083ca6f84b27536d57d623fc872e2f502b1078"}, - {file = "duckdb-0.10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a66c125d0c30af210f7ee599e7821c3d1a7e09208196dafbf997d4e0cfcb81ab"}, - {file = "duckdb-0.10.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99dd7a1d901149c7a276440d6e737b2777e17d2046f5efb0c06ad3b8cb066a6"}, - {file = "duckdb-0.10.3-cp37-cp37m-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ec3bbdb209e6095d202202893763e26c17c88293b88ef986b619e6c8b6715bd"}, - {file = "duckdb-0.10.3-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:2b3dec4ef8ed355d7b7230b40950b30d0def2c387a2e8cd7efc80b9d14134ecf"}, - {file = "duckdb-0.10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:04129f94fb49bba5eea22f941f0fb30337f069a04993048b59e2811f52d564bc"}, - {file = "duckdb-0.10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d75d67024fc22c8edfd47747c8550fb3c34fb1cbcbfd567e94939ffd9c9e3ca7"}, - {file = "duckdb-0.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f3796e9507c02d0ddbba2e84c994fae131da567ce3d9cbb4cbcd32fadc5fbb26"}, - {file = "duckdb-0.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:78e539d85ebd84e3e87ec44d28ad912ca4ca444fe705794e0de9be3dd5550c11"}, - {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a99b67ac674b4de32073e9bc604b9c2273d399325181ff50b436c6da17bf00a"}, - {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1209a354a763758c4017a1f6a9f9b154a83bed4458287af9f71d84664ddb86b6"}, - {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b735cea64aab39b67c136ab3a571dbf834067f8472ba2f8bf0341bc91bea820"}, - {file = "duckdb-0.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:816ffb9f758ed98eb02199d9321d592d7a32a6cb6aa31930f4337eb22cfc64e2"}, - {file = "duckdb-0.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:1631184b94c3dc38b13bce4045bf3ae7e1b0ecbfbb8771eb8d751d8ffe1b59b3"}, - {file = "duckdb-0.10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fb98c35fc8dd65043bc08a2414dd9f59c680d7e8656295b8969f3f2061f26c52"}, - {file = "duckdb-0.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e75c9f5b6a92b2a6816605c001d30790f6d67ce627a2b848d4d6040686efdf9"}, - {file = "duckdb-0.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae786eddf1c2fd003466e13393b9348a44b6061af6fe7bcb380a64cac24e7df7"}, - {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9387da7b7973707b0dea2588749660dd5dd724273222680e985a2dd36787668"}, - {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:538f943bf9fa8a3a7c4fafa05f21a69539d2c8a68e557233cbe9d989ae232899"}, - {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6930608f35025a73eb94252964f9f19dd68cf2aaa471da3982cf6694866cfa63"}, - {file = "duckdb-0.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:03bc54a9cde5490918aad82d7d2a34290e3dfb78d5b889c6626625c0f141272a"}, - {file = "duckdb-0.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:372b6e3901d85108cafe5df03c872dfb6f0dbff66165a0cf46c47246c1957aa0"}, - {file = "duckdb-0.10.3.tar.gz", hash = "sha256:c5bd84a92bc708d3a6adffe1f554b94c6e76c795826daaaf482afc3d9c636971"}, + {file = "duckdb-1.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4a8ce2d1f9e1c23b9bab3ae4ca7997e9822e21563ff8f646992663f66d050211"}, + {file = "duckdb-1.0.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:19797670f20f430196e48d25d082a264b66150c264c1e8eae8e22c64c2c5f3f5"}, + {file = "duckdb-1.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b71c342090fe117b35d866a91ad6bffce61cd6ff3e0cff4003f93fc1506da0d8"}, + {file = "duckdb-1.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25dd69f44ad212c35ae2ea736b0e643ea2b70f204b8dff483af1491b0e2a4cec"}, + {file = "duckdb-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8da5f293ecb4f99daa9a9352c5fd1312a6ab02b464653a0c3a25ab7065c45d4d"}, + {file = "duckdb-1.0.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3207936da9967ddbb60644ec291eb934d5819b08169bc35d08b2dedbe7068c60"}, + {file = "duckdb-1.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1128d6c9c33e883b1f5df6b57c1eb46b7ab1baf2650912d77ee769aaa05111f9"}, + {file = "duckdb-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:02310d263474d0ac238646677feff47190ffb82544c018b2ff732a4cb462c6ef"}, + {file = "duckdb-1.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:75586791ab2702719c284157b65ecefe12d0cca9041da474391896ddd9aa71a4"}, + {file = "duckdb-1.0.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:83bb415fc7994e641344f3489e40430ce083b78963cb1057bf714ac3a58da3ba"}, + {file = "duckdb-1.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:bee2e0b415074e84c5a2cefd91f6b5ebeb4283e7196ba4ef65175a7cef298b57"}, + {file = "duckdb-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa5a4110d2a499312609544ad0be61e85a5cdad90e5b6d75ad16b300bf075b90"}, + {file = "duckdb-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa389e6a382d4707b5f3d1bc2087895925ebb92b77e9fe3bfb23c9b98372fdc"}, + {file = "duckdb-1.0.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ede6f5277dd851f1a4586b0c78dc93f6c26da45e12b23ee0e88c76519cbdbe0"}, + {file = "duckdb-1.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0b88cdbc0d5c3e3d7545a341784dc6cafd90fc035f17b2f04bf1e870c68456e5"}, + {file = "duckdb-1.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd1693cdd15375156f7fff4745debc14e5c54928589f67b87fb8eace9880c370"}, + {file = "duckdb-1.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c65a7fe8a8ce21b985356ee3ec0c3d3b3b2234e288e64b4cfb03356dbe6e5583"}, + {file = "duckdb-1.0.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:e5a8eda554379b3a43b07bad00968acc14dd3e518c9fbe8f128b484cf95e3d16"}, + {file = "duckdb-1.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:a1b6acdd54c4a7b43bd7cb584975a1b2ff88ea1a31607a2b734b17960e7d3088"}, + {file = "duckdb-1.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a677bb1b6a8e7cab4a19874249d8144296e6e39dae38fce66a80f26d15e670df"}, + {file = "duckdb-1.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:752e9d412b0a2871bf615a2ede54be494c6dc289d076974eefbf3af28129c759"}, + {file = "duckdb-1.0.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3aadb99d098c5e32d00dc09421bc63a47134a6a0de9d7cd6abf21780b678663c"}, + {file = "duckdb-1.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83b7091d4da3e9301c4f9378833f5ffe934fb1ad2b387b439ee067b2c10c8bb0"}, + {file = "duckdb-1.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:6a8058d0148b544694cb5ea331db44f6c2a00a7b03776cc4dd1470735c3d5ff7"}, + {file = "duckdb-1.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e40cb20e5ee19d44bc66ec99969af791702a049079dc5f248c33b1c56af055f4"}, + {file = "duckdb-1.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7bce1bc0de9af9f47328e24e6e7e39da30093179b1c031897c042dd94a59c8e"}, + {file = "duckdb-1.0.0-cp37-cp37m-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8355507f7a04bc0a3666958f4414a58e06141d603e91c0fa5a7c50e49867fb6d"}, + {file = "duckdb-1.0.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:39f1a46f5a45ad2886dc9b02ce5b484f437f90de66c327f86606d9ba4479d475"}, + {file = "duckdb-1.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d29ba477b27ae41676b62c8fae8d04ee7cbe458127a44f6049888231ca58fa"}, + {file = "duckdb-1.0.0-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:1bea713c1925918714328da76e79a1f7651b2b503511498ccf5e007a7e67d49e"}, + {file = "duckdb-1.0.0-cp38-cp38-macosx_12_0_universal2.whl", hash = "sha256:bfe67f3bcf181edbf6f918b8c963eb060e6aa26697d86590da4edc5707205450"}, + {file = "duckdb-1.0.0-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:dbc6093a75242f002be1d96a6ace3fdf1d002c813e67baff52112e899de9292f"}, + {file = "duckdb-1.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba1881a2b11c507cee18f8fd9ef10100be066fddaa2c20fba1f9a664245cd6d8"}, + {file = "duckdb-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:445d0bb35087c522705c724a75f9f1c13f1eb017305b694d2686218d653c8142"}, + {file = "duckdb-1.0.0-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:224553432e84432ffb9684f33206572477049b371ce68cc313a01e214f2fbdda"}, + {file = "duckdb-1.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d3914032e47c4e76636ad986d466b63fdea65e37be8a6dfc484ed3f462c4fde4"}, + {file = "duckdb-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:af9128a2eb7e1bb50cd2c2020d825fb2946fdad0a2558920cd5411d998999334"}, + {file = "duckdb-1.0.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:dd2659a5dbc0df0de68f617a605bf12fe4da85ba24f67c08730984a0892087e8"}, + {file = "duckdb-1.0.0-cp39-cp39-macosx_12_0_universal2.whl", hash = "sha256:ac5a4afb0bc20725e734e0b2c17e99a274de4801aff0d4e765d276b99dad6d90"}, + {file = "duckdb-1.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:2c5a53bee3668d6e84c0536164589d5127b23d298e4c443d83f55e4150fafe61"}, + {file = "duckdb-1.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b980713244d7708b25ee0a73de0c65f0e5521c47a0e907f5e1b933d79d972ef6"}, + {file = "duckdb-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cbd4f9fe7b7a56eff96c3f4d6778770dd370469ca2212eddbae5dd63749db5"}, + {file = "duckdb-1.0.0-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed228167c5d49888c5ef36f6f9cbf65011c2daf9dcb53ea8aa7a041ce567b3e4"}, + {file = "duckdb-1.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46d8395fbcea7231fd5032a250b673cc99352fef349b718a23dea2c0dd2b8dec"}, + {file = "duckdb-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:6ad1fc1a4d57e7616944166a5f9417bdbca1ea65c490797e3786e3a42e162d8a"}, + {file = "duckdb-1.0.0.tar.gz", hash = "sha256:a2a059b77bc7d5b76ae9d88e267372deff19c291048d59450c431e166233d453"}, ] [[package]] @@ -2194,13 +2193,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-settings" -version = "2.3.2" +version = "2.3.3" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_settings-2.3.2-py3-none-any.whl", hash = "sha256:ae06e44349e4c7bff8d57aff415dfd397ae75c217a098d54e9e6990ad7594ac7"}, - {file = "pydantic_settings-2.3.2.tar.gz", hash = "sha256:05d33003c74c2cd585de97b59eb17b6ed67181bc8a3ce594d74b5d24e4df7323"}, + {file = "pydantic_settings-2.3.3-py3-none-any.whl", hash = "sha256:e4ed62ad851670975ec11285141db888fd24947f9440bd4380d7d8788d4965de"}, + {file = "pydantic_settings-2.3.3.tar.gz", hash = "sha256:87fda838b64b5039b970cd47c3e8a1ee460ce136278ff672980af21516f6e6ce"}, ] [package.dependencies] @@ -3245,4 +3244,4 @@ local = [] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "48a7355a7096e763b75315d0704bed8f4d8134a33553e62bc305a686b9e72803" +content-hash = "3c83d086a178cebd83473758459c4f026907268dbf8c9ce45bc4bfb1f340e9a5" diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index 1dd16933a..1c026b983 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -52,7 +52,7 @@ docstring-parser = "^0.15" python-jose = "^3.3.0" pandas = "2.2.0" multiprocess = "^0.70.14" -duckdb = "^0.10.2" +duckdb = "^1.0.0" python-socketio = "^5.11.0" python-docx = "^1.1.0" jq = { version = "^1.7.0", markers = "sys_platform != 'win32'" } From 8fef4b2d3a4f12ed134f9df4d1102095062996e1 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 11:30:20 -0300 Subject: [PATCH 23/68] chore: Update langflow-base and langflow package versions to 0.0.66 and 1.0.0a55 respectively --- poetry.lock | 2 +- pyproject.toml | 2 +- src/backend/base/pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4701d9fb2..f248ce18b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4377,7 +4377,7 @@ six = "*" [[package]] name = "langflow-base" -version = "0.0.65" +version = "0.0.66" description = "A Python package with a built-in web application" optional = false python-versions = ">=3.10,<3.13" diff --git a/pyproject.toml b/pyproject.toml index 2d1ad64f9..3b660249d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow" -version = "1.0.0a54" +version = "1.0.0a55" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index 1c026b983..03f3f70f1 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow-base" -version = "0.0.65" +version = "0.0.66" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ From ea305b824c99bde0df2817f5513564f4d13d3653 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 13 Jun 2024 12:01:02 -0300 Subject: [PATCH 24/68] chore: Update displayOutputPreview logic in ParameterComponent --- .../GenericNode/components/parameterComponent/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 28d6779e7..99b28b7bf 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -91,7 +91,8 @@ export default function ParameterComponent({ const displayOutputPreview = !!flowPool[data.id] && - flowPool[data.id][flowPool[data.id].length - 1]?.valid; + flowPool[data.id][flowPool[data.id].length - 1]?.valid && + flowPool[data.id][flowPool[data.id].length - 1]?.data?.logs[0]?.message; const unknownOutput = !!( flowPool[data.id] && From d2ae69135685f6180560cd89c101969f2657fe73 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 13 Jun 2024 08:01:38 -0700 Subject: [PATCH 25/68] chore: Update schema.py and service.py to handle file serialization and deserialization (#2159) Refactor the MessageModel class in schema.py to include methods for validating and serializing the 'files' field. This ensures that the 'files' field can be properly handled when it is a string or a list. In service.py, update the query in the MonitorService class to include the 'files' field when retrieving messages from the database. This ensures that the 'files' field is included in the query results. --- src/backend/base/langflow/services/monitor/schema.py | 10 +++++++++- src/backend/base/langflow/services/monitor/service.py | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/services/monitor/schema.py b/src/backend/base/langflow/services/monitor/schema.py index a409f8337..22ccb2bb4 100644 --- a/src/backend/base/langflow/services/monitor/schema.py +++ b/src/backend/base/langflow/services/monitor/schema.py @@ -91,9 +91,17 @@ class MessageModel(DefaultModel): files: list[str] = [] @field_validator("files", mode="before") + @classmethod def validate_files(cls, v): if isinstance(v, str): - return json.loads(v) + v = json.loads(v) + return v + + @field_serializer("files") + @classmethod + def serialize_files(cls, v): + if isinstance(v, list): + return json.dumps(v) return v @classmethod diff --git a/src/backend/base/langflow/services/monitor/service.py b/src/backend/base/langflow/services/monitor/service.py index 92c642e20..c7d898d11 100644 --- a/src/backend/base/langflow/services/monitor/service.py +++ b/src/backend/base/langflow/services/monitor/service.py @@ -140,7 +140,7 @@ class MonitorService(Service): order: Optional[str] = "DESC", limit: Optional[int] = None, ): - query = "SELECT index, flow_id, sender_name, sender, session_id, text, timestamp FROM messages" + query = "SELECT index, flow_id, sender_name, sender, session_id, text, files, timestamp FROM messages" conditions = [] if sender: conditions.append(f"sender = '{sender}'") From 6dbc2c76e4713467fd8c2d6d733bc2b3320ce692 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 12:05:45 -0300 Subject: [PATCH 26/68] Update projects --- .langchain.db-shm | Bin 32768 -> 0 bytes .langchain.db-wal | 0 .../Langflow Document QA.json | 2 +- .../Langflow Memory Conversation.json | 237 +++++++-- .../VectorStore-RAG-Flows.json | 477 ++++++++++++++---- src/backend/base/langflow/langflow-pre.db-shm | Bin 32768 -> 0 bytes src/backend/base/langflow/langflow-pre.db-wal | Bin 700428 -> 0 bytes 7 files changed, 557 insertions(+), 159 deletions(-) delete mode 100644 .langchain.db-shm delete mode 100644 .langchain.db-wal delete mode 100644 src/backend/base/langflow/langflow-pre.db-shm delete mode 100644 src/backend/base/langflow/langflow-pre.db-wal diff --git a/.langchain.db-shm b/.langchain.db-shm deleted file mode 100644 index fe9ac2845eca6fe6da8a63cd096d9cf9e24ece10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeIuAr62r3 Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" + "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" }, "input_value": { "advanced": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json index 17707debc..df360b08c 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json @@ -5,14 +5,23 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["str", "Text", "object"], + "baseClasses": [ + "str", + "Text", + "object" + ], "dataType": "MemoryComponent", "id": "MemoryComponent-iDkC0" }, "targetHandle": { "fieldName": "context", "id": "Prompt-19lCm", - "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], + "inputTypes": [ + "Document", + "BaseOutputParser", + "Record", + "Text" + ], "type": "str" } }, @@ -30,14 +39,24 @@ "className": "", "data": { "sourceHandle": { - "baseClasses": ["Text", "object", "Record", "str"], + "baseClasses": [ + "Text", + "object", + "Record", + "str" + ], "dataType": "ChatInput", "id": "ChatInput-Z6G1Z" }, "targetHandle": { "fieldName": "user_message", "id": "Prompt-19lCm", - "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], + "inputTypes": [ + "Document", + "BaseOutputParser", + "Record", + "Text" + ], "type": "str" } }, @@ -55,14 +74,22 @@ "className": "", "data": { "sourceHandle": { - "baseClasses": ["Text", "str", "object"], + "baseClasses": [ + "Text", + "str", + "object" + ], "dataType": "Prompt", "id": "Prompt-19lCm" }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-1v5Hz", - "inputTypes": ["Text", "Record", "Prompt"], + "inputTypes": [ + "Text", + "Record", + "Prompt" + ], "type": "str" } }, @@ -79,14 +106,20 @@ "className": "", "data": { "sourceHandle": { - "baseClasses": ["str", "object", "Text"], + "baseClasses": [ + "str", + "object", + "Text" + ], "dataType": "OpenAIModel", "id": "OpenAIModel-1v5Hz" }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-i56kN", - "inputTypes": ["Text"], + "inputTypes": [ + "Text" + ], "type": "str" } }, @@ -103,14 +136,21 @@ "className": "", "data": { "sourceHandle": { - "baseClasses": ["str", "Text", "object"], + "baseClasses": [ + "str", + "Text", + "object" + ], "dataType": "MemoryComponent", "id": "MemoryComponent-iDkC0" }, "targetHandle": { "fieldName": "input_value", "id": "TextOutput-KuLNK", - "inputTypes": ["Record", "Text"], + "inputTypes": [ + "Record", + "Text" + ], "type": "str" } }, @@ -129,7 +169,12 @@ "data": { "id": "ChatInput-Z6G1Z", "node": { - "base_classes": ["Text", "object", "Record", "str"], + "base_classes": [ + "Text", + "object", + "Record", + "str" + ], "beta": false, "custom_fields": { "input_value": null, @@ -145,7 +190,10 @@ "field_order": [], "frozen": false, "icon": "ChatInput", - "output_types": ["Message", "Text"], + "output_types": [ + "Message", + "Text" + ], "template": { "_type": "CustomComponent", "code": { @@ -164,7 +212,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" + "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" }, "input_value": { "advanced": false, @@ -193,12 +241,17 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "password": false, "placeholder": "", "required": false, @@ -214,7 +267,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -234,7 +289,9 @@ "fileTypes": [], "file_path": "", "info": "If provided, the message will be stored in the memory.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -270,7 +327,12 @@ "data": { "id": "ChatOutput-i56kN", "node": { - "base_classes": ["Text", "object", "Record", "str"], + "base_classes": [ + "Text", + "object", + "Record", + "str" + ], "beta": false, "custom_fields": { "input_value": null, @@ -286,7 +348,10 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": ["Message", "Text"], + "output_types": [ + "Message", + "Text" + ], "template": { "_type": "CustomComponent", "code": { @@ -314,7 +379,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -333,12 +400,17 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "password": false, "placeholder": "", "required": false, @@ -354,7 +426,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -374,7 +448,9 @@ "fileTypes": [], "file_path": "", "info": "If provided, the message will be stored in the memory.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -412,7 +488,11 @@ "display_name": "Chat Memory", "id": "MemoryComponent-iDkC0", "node": { - "base_classes": ["str", "Text", "object"], + "base_classes": [ + "str", + "Text", + "object" + ], "beta": true, "custom_fields": { "n_messages": null, @@ -429,7 +509,9 @@ "field_order": [], "frozen": false, "icon": "history", - "output_types": ["Text"], + "output_types": [ + "Text" + ], "template": { "_type": "CustomComponent", "code": { @@ -476,12 +558,17 @@ "fileTypes": [], "file_path": "", "info": "Order of the messages.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "password": false, "placeholder": "", "required": false, @@ -497,7 +584,9 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -517,12 +606,18 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "password": false, "placeholder": "", "required": false, @@ -538,7 +633,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -557,7 +654,9 @@ "fileTypes": [], "file_path": "", "info": "Session ID of the chat history.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -595,10 +694,17 @@ "display_name": "Prompt", "id": "Prompt-19lCm", "node": { - "base_classes": ["Text", "str", "object"], + "base_classes": [ + "Text", + "str", + "object" + ], "beta": false, "custom_fields": { - "template": ["context", "user_message"] + "template": [ + "context", + "user_message" + ] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", @@ -613,7 +719,9 @@ "is_input": null, "is_output": null, "name": "", - "output_types": ["Prompt"], + "output_types": [ + "Prompt" + ], "template": { "_type": "CustomComponent", "code": { @@ -667,7 +775,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -729,7 +839,11 @@ "data": { "id": "OpenAIModel-1v5Hz", "node": { - "base_classes": ["str", "object", "Text"], + "base_classes": [ + "str", + "object", + "Text" + ], "beta": false, "custom_fields": { "input_value": null, @@ -759,7 +873,9 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": ["Text"], + "output_types": [ + "Text" + ], "template": { "_type": "CustomComponent", "code": { @@ -787,7 +903,11 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text", "Record", "Prompt"], + "input_types": [ + "Text", + "Record", + "Prompt" + ], "list": false, "load_from_db": false, "multiline": false, @@ -844,7 +964,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, @@ -871,7 +993,9 @@ "fileTypes": [], "file_path": "", "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.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -890,7 +1014,9 @@ "fileTypes": [], "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": true, "multiline": false, @@ -929,7 +1055,9 @@ "fileTypes": [], "file_path": "", "info": "System message to pass to the model.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -992,7 +1120,11 @@ "edited": false, "id": "TextOutput-KuLNK", "node": { - "base_classes": ["object", "str", "Text"], + "base_classes": [ + "object", + "str", + "Text" + ], "beta": false, "custom_fields": { "input_value": null, @@ -1006,7 +1138,9 @@ "field_order": [], "frozen": false, "icon": "type", - "output_types": ["Text"], + "output_types": [ + "Text" + ], "template": { "_type": "CustomComponent", "code": { @@ -1034,7 +1168,10 @@ "fileTypes": [], "file_path": "", "info": "Text or Record to be passed as output.", - "input_types": ["Record", "Text"], + "input_types": [ + "Record", + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -1054,7 +1191,9 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1100,4 +1239,4 @@ "is_component": false, "last_tested_version": "1.0.0a52", "name": "Memory Chatbot" -} +} \ No newline at end of file diff --git a/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json b/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json index 6247f0c5d..c9745183d 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json @@ -5,14 +5,23 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["object", "Text", "str"], + "baseClasses": [ + "object", + "Text", + "str" + ], "dataType": "TextOutput", "id": "TextOutput-BDknO" }, "targetHandle": { "fieldName": "context", "id": "Prompt-xeI6K", - "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], + "inputTypes": [ + "Document", + "BaseOutputParser", + "Record", + "Text" + ], "type": "str" } }, @@ -30,14 +39,24 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["Text", "str", "object", "Record"], + "baseClasses": [ + "Text", + "str", + "object", + "Record" + ], "dataType": "ChatInput", "id": "ChatInput-yxMKE" }, "targetHandle": { "fieldName": "question", "id": "Prompt-xeI6K", - "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], + "inputTypes": [ + "Document", + "BaseOutputParser", + "Record", + "Text" + ], "type": "str" } }, @@ -55,14 +74,22 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["object", "Text", "str"], + "baseClasses": [ + "object", + "Text", + "str" + ], "dataType": "Prompt", "id": "Prompt-xeI6K" }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-EjXlN", - "inputTypes": ["Text", "Record", "Prompt"], + "inputTypes": [ + "Text", + "Record", + "Prompt" + ], "type": "str" } }, @@ -80,14 +107,20 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["object", "Text", "str"], + "baseClasses": [ + "object", + "Text", + "str" + ], "dataType": "OpenAIModel", "id": "OpenAIModel-EjXlN" }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-Q39I8", - "inputTypes": ["Text"], + "inputTypes": [ + "Text" + ], "type": "str" } }, @@ -105,14 +138,19 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["Record"], + "baseClasses": [ + "Record" + ], "dataType": "File", "id": "File-t0a6a" }, "targetHandle": { "fieldName": "inputs", "id": "RecursiveCharacterTextSplitter-tR9QM", - "inputTypes": ["Document", "Record"], + "inputTypes": [ + "Document", + "Record" + ], "type": "Document" } }, @@ -130,7 +168,9 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["Embeddings"], + "baseClasses": [ + "Embeddings" + ], "dataType": "OpenAIEmbeddings", "id": "OpenAIEmbeddings-ZlOk1" }, @@ -154,14 +194,21 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["Text", "str", "object", "Record"], + "baseClasses": [ + "Text", + "str", + "object", + "Record" + ], "dataType": "ChatInput", "id": "ChatInput-yxMKE" }, "targetHandle": { "fieldName": "input_value", "id": "AstraDBSearch-41nRz", - "inputTypes": ["Text"], + "inputTypes": [ + "Text" + ], "type": "str" } }, @@ -178,7 +225,9 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["Record"], + "baseClasses": [ + "Record" + ], "dataType": "RecursiveCharacterTextSplitter", "id": "RecursiveCharacterTextSplitter-tR9QM" }, @@ -203,7 +252,9 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["Embeddings"], + "baseClasses": [ + "Embeddings" + ], "dataType": "OpenAIEmbeddings", "id": "OpenAIEmbeddings-9TPjc" }, @@ -228,14 +279,19 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["Record"], + "baseClasses": [ + "Record" + ], "dataType": "AstraDBSearch", "id": "AstraDBSearch-41nRz" }, "targetHandle": { "fieldName": "input_value", "id": "TextOutput-BDknO", - "inputTypes": ["Record", "Text"], + "inputTypes": [ + "Record", + "Text" + ], "type": "str" } }, @@ -254,7 +310,12 @@ "data": { "id": "ChatInput-yxMKE", "node": { - "base_classes": ["Text", "str", "object", "Record"], + "base_classes": [ + "Text", + "str", + "object", + "Record" + ], "beta": false, "custom_fields": { "input_value": null, @@ -270,7 +331,10 @@ "field_order": [], "frozen": false, "icon": "ChatInput", - "output_types": ["Message", "Text"], + "output_types": [ + "Message", + "Text" + ], "template": { "_type": "CustomComponent", "code": { @@ -289,7 +353,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" + "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" }, "input_value": { "advanced": false, @@ -318,12 +382,17 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "password": false, "placeholder": "", "required": false, @@ -339,7 +408,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -359,7 +430,9 @@ "fileTypes": [], "file_path": "", "info": "If provided, the message will be stored in the memory.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -389,7 +462,11 @@ "data": { "id": "TextOutput-BDknO", "node": { - "base_classes": ["object", "Text", "str"], + "base_classes": [ + "object", + "Text", + "str" + ], "beta": false, "custom_fields": { "input_value": null, @@ -402,7 +479,9 @@ "field_order": [], "frozen": false, "icon": "type", - "output_types": ["Text"], + "output_types": [ + "Text" + ], "template": { "_type": "CustomComponent", "code": { @@ -430,7 +509,10 @@ "fileTypes": [], "file_path": "", "info": "Text or Record to be passed as output.", - "input_types": ["Record", "Text"], + "input_types": [ + "Record", + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -450,7 +532,9 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -486,7 +570,9 @@ "data": { "id": "OpenAIEmbeddings-ZlOk1", "node": { - "base_classes": ["Embeddings"], + "base_classes": [ + "Embeddings" + ], "beta": false, "custom_fields": { "allowed_special": null, @@ -518,7 +604,9 @@ "field_formatters": {}, "field_order": [], "frozen": false, - "output_types": ["Embeddings"], + "output_types": [ + "Embeddings" + ], "template": { "_type": "CustomComponent", "allowed_special": { @@ -528,7 +616,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -576,7 +666,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Dict, List, Optional\n\nfrom langchain_openai.embeddings.base import OpenAIEmbeddings\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Embeddings, NestedDict\n\n\nclass OpenAIEmbeddingsComponent(CustomComponent):\n display_name = \"OpenAI Embeddings\"\n description = \"Generate embeddings using OpenAI models.\"\n\n def build_config(self):\n return {\n \"allowed_special\": {\n \"display_name\": \"Allowed Special\",\n \"advanced\": True,\n \"field_type\": \"str\",\n \"is_list\": True,\n },\n \"default_headers\": {\n \"display_name\": \"Default Headers\",\n \"advanced\": True,\n \"field_type\": \"dict\",\n },\n \"default_query\": {\n \"display_name\": \"Default Query\",\n \"advanced\": True,\n \"field_type\": \"NestedDict\",\n },\n \"disallowed_special\": {\n \"display_name\": \"Disallowed Special\",\n \"advanced\": True,\n \"field_type\": \"str\",\n \"is_list\": True,\n },\n \"chunk_size\": {\"display_name\": \"Chunk Size\", \"advanced\": True},\n \"client\": {\"display_name\": \"Client\", \"advanced\": True},\n \"deployment\": {\"display_name\": \"Deployment\", \"advanced\": True},\n \"embedding_ctx_length\": {\n \"display_name\": \"Embedding Context Length\",\n \"advanced\": True,\n },\n \"max_retries\": {\"display_name\": \"Max Retries\", \"advanced\": True},\n \"model\": {\n \"display_name\": \"Model\",\n \"advanced\": False,\n \"options\": [\n \"text-embedding-3-small\",\n \"text-embedding-3-large\",\n \"text-embedding-ada-002\",\n ],\n },\n \"model_kwargs\": {\"display_name\": \"Model Kwargs\", \"advanced\": True},\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"password\": True,\n \"advanced\": True,\n },\n \"openai_api_key\": {\"display_name\": \"OpenAI API Key\", \"password\": True},\n \"openai_api_type\": {\n \"display_name\": \"OpenAI API Type\",\n \"advanced\": True,\n \"password\": True,\n },\n \"openai_api_version\": {\n \"display_name\": \"OpenAI API Version\",\n \"advanced\": True,\n },\n \"openai_organization\": {\n \"display_name\": \"OpenAI Organization\",\n \"advanced\": True,\n },\n \"openai_proxy\": {\"display_name\": \"OpenAI Proxy\", \"advanced\": True},\n \"request_timeout\": {\"display_name\": \"Request Timeout\", \"advanced\": True},\n \"show_progress_bar\": {\n \"display_name\": \"Show Progress Bar\",\n \"advanced\": True,\n },\n \"skip_empty\": {\"display_name\": \"Skip Empty\", \"advanced\": True},\n \"tiktoken_model_name\": {\n \"display_name\": \"TikToken Model Name\",\n \"advanced\": True,\n },\n \"tiktoken_enable\": {\"display_name\": \"TikToken Enable\", \"advanced\": True},\n }\n\n def build(\n self,\n openai_api_key: str,\n default_headers: Optional[Dict[str, str]] = None,\n default_query: Optional[NestedDict] = {},\n allowed_special: List[str] = [],\n disallowed_special: List[str] = [\"all\"],\n chunk_size: int = 1000,\n deployment: str = \"text-embedding-ada-002\",\n embedding_ctx_length: int = 8191,\n max_retries: int = 6,\n model: str = \"text-embedding-ada-002\",\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n openai_api_type: Optional[str] = None,\n openai_api_version: Optional[str] = None,\n openai_organization: Optional[str] = None,\n openai_proxy: Optional[str] = None,\n request_timeout: Optional[float] = None,\n show_progress_bar: bool = False,\n skip_empty: bool = False,\n tiktoken_enable: bool = True,\n tiktoken_model_name: Optional[str] = None,\n ) -> Embeddings:\n # This is to avoid errors with Vector Stores (e.g Chroma)\n if disallowed_special == [\"all\"]:\n disallowed_special = \"all\" # type: ignore\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n return OpenAIEmbeddings(\n tiktoken_enabled=tiktoken_enable,\n default_headers=default_headers,\n default_query=default_query,\n allowed_special=set(allowed_special),\n disallowed_special=\"all\",\n chunk_size=chunk_size,\n deployment=deployment,\n embedding_ctx_length=embedding_ctx_length,\n max_retries=max_retries,\n model=model,\n model_kwargs=model_kwargs,\n base_url=openai_api_base,\n api_key=api_key,\n openai_api_type=openai_api_type,\n api_version=openai_api_version,\n organization=openai_organization,\n openai_proxy=openai_proxy,\n timeout=request_timeout,\n show_progress_bar=show_progress_bar,\n skip_empty=skip_empty,\n tiktoken_model_name=tiktoken_model_name,\n )\n" + "value": "from typing import Dict, List, Optional\n\nfrom langchain_openai.embeddings.base import OpenAIEmbeddings\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Embeddings, NestedDict\n\n\nclass OpenAIEmbeddingsComponent(CustomComponent):\n display_name = \"OpenAI Embeddings\"\n description = \"Generate embeddings using OpenAI models.\"\n\n def build_config(self):\n return {\n \"allowed_special\": {\n \"display_name\": \"Allowed Special\",\n \"advanced\": True,\n \"field_type\": \"str\",\n \"is_list\": True,\n },\n \"default_headers\": {\n \"display_name\": \"Default Headers\",\n \"advanced\": True,\n \"field_type\": \"dict\",\n },\n \"default_query\": {\n \"display_name\": \"Default Query\",\n \"advanced\": True,\n \"field_type\": \"NestedDict\",\n },\n \"disallowed_special\": {\n \"display_name\": \"Disallowed Special\",\n \"advanced\": True,\n \"field_type\": \"str\",\n \"is_list\": True,\n },\n \"chunk_size\": {\"display_name\": \"Chunk Size\", \"advanced\": True},\n \"client\": {\"display_name\": \"Client\", \"advanced\": True},\n \"deployment\": {\"display_name\": \"Deployment\", \"advanced\": True},\n \"embedding_ctx_length\": {\n \"display_name\": \"Embedding Context Length\",\n \"advanced\": True,\n },\n \"max_retries\": {\"display_name\": \"Max Retries\", \"advanced\": True},\n \"model\": {\n \"display_name\": \"Model\",\n \"advanced\": False,\n \"options\": [\n \"text-embedding-3-small\",\n \"text-embedding-3-large\",\n \"text-embedding-ada-002\",\n ],\n },\n \"model_kwargs\": {\"display_name\": \"Model Kwargs\", \"advanced\": True},\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"password\": True,\n \"advanced\": True,\n },\n \"openai_api_key\": {\"display_name\": \"OpenAI API Key\", \"password\": True},\n \"openai_api_type\": {\n \"display_name\": \"OpenAI API Type\",\n \"advanced\": True,\n \"password\": True,\n },\n \"openai_api_version\": {\n \"display_name\": \"OpenAI API Version\",\n \"advanced\": True,\n },\n \"openai_organization\": {\n \"display_name\": \"OpenAI Organization\",\n \"advanced\": True,\n },\n \"openai_proxy\": {\"display_name\": \"OpenAI Proxy\", \"advanced\": True},\n \"request_timeout\": {\"display_name\": \"Request Timeout\", \"advanced\": True},\n \"show_progress_bar\": {\n \"display_name\": \"Show Progress Bar\",\n \"advanced\": True,\n },\n \"skip_empty\": {\"display_name\": \"Skip Empty\", \"advanced\": True},\n \"tiktoken_model_name\": {\n \"display_name\": \"TikToken Model Name\",\n \"advanced\": True,\n },\n \"tiktoken_enable\": {\"display_name\": \"TikToken Enable\", \"advanced\": True},\n \"dimensions\": {\n \"display_name\": \"Dimensions\",\n \"info\": \"The number of dimensions the resulting output embeddings should have. Only supported by certain models.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n openai_api_key: str,\n default_headers: Optional[Dict[str, str]] = None,\n default_query: Optional[NestedDict] = {},\n allowed_special: List[str] = [],\n disallowed_special: List[str] = [\"all\"],\n chunk_size: int = 1000,\n deployment: str = \"text-embedding-ada-002\",\n embedding_ctx_length: int = 8191,\n max_retries: int = 6,\n model: str = \"text-embedding-ada-002\",\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n openai_api_type: Optional[str] = None,\n openai_api_version: Optional[str] = None,\n openai_organization: Optional[str] = None,\n openai_proxy: Optional[str] = None,\n request_timeout: Optional[float] = None,\n show_progress_bar: bool = False,\n skip_empty: bool = False,\n tiktoken_enable: bool = True,\n tiktoken_model_name: Optional[str] = None,\n dimensions: Optional[int] = None,\n ) -> Embeddings:\n # This is to avoid errors with Vector Stores (e.g Chroma)\n if disallowed_special == [\"all\"]:\n disallowed_special = \"all\" # type: ignore\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n return OpenAIEmbeddings(\n tiktoken_enabled=tiktoken_enable,\n default_headers=default_headers,\n default_query=default_query,\n allowed_special=set(allowed_special),\n disallowed_special=\"all\",\n chunk_size=chunk_size,\n deployment=deployment,\n embedding_ctx_length=embedding_ctx_length,\n max_retries=max_retries,\n model=model,\n model_kwargs=model_kwargs,\n base_url=openai_api_base,\n api_key=api_key,\n openai_api_type=openai_api_type,\n api_version=openai_api_version,\n organization=openai_organization,\n openai_proxy=openai_proxy,\n timeout=request_timeout,\n show_progress_bar=show_progress_bar,\n skip_empty=skip_empty,\n tiktoken_model_name=tiktoken_model_name,\n dimensions=dimensions,\n )\n" }, "default_headers": { "advanced": true, @@ -622,7 +712,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -642,7 +734,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -653,7 +747,9 @@ "show": true, "title_case": false, "type": "str", - "value": ["all"] + "value": [ + "all" + ] }, "embedding_ctx_length": { "advanced": true, @@ -700,7 +796,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, @@ -744,7 +842,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -763,7 +863,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": true, "multiline": false, @@ -783,7 +885,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -802,7 +906,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -821,7 +927,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -840,7 +948,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -940,7 +1050,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -971,7 +1083,11 @@ "data": { "id": "OpenAIModel-EjXlN", "node": { - "base_classes": ["object", "Text", "str"], + "base_classes": [ + "object", + "Text", + "str" + ], "beta": false, "custom_fields": { "input_value": null, @@ -1001,7 +1117,9 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": ["Text"], + "output_types": [ + "Text" + ], "template": { "_type": "CustomComponent", "code": { @@ -1029,7 +1147,11 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text", "Record", "Prompt"], + "input_types": [ + "Text", + "Record", + "Prompt" + ], "list": false, "load_from_db": false, "multiline": false, @@ -1086,7 +1208,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, @@ -1113,7 +1237,9 @@ "fileTypes": [], "file_path": "", "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.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -1132,7 +1258,9 @@ "fileTypes": [], "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": true, "multiline": false, @@ -1171,7 +1299,9 @@ "fileTypes": [], "file_path": "", "info": "System message to pass to the model.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -1233,10 +1363,17 @@ "display_name": "Prompt", "id": "Prompt-xeI6K", "node": { - "base_classes": ["object", "Text", "str"], + "base_classes": [ + "object", + "Text", + "str" + ], "beta": false, "custom_fields": { - "template": ["context", "question"] + "template": [ + "context", + "question" + ] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", @@ -1251,7 +1388,9 @@ "is_input": null, "is_output": null, "name": "", - "output_types": ["Prompt"], + "output_types": [ + "Prompt" + ], "template": { "_type": "CustomComponent", "code": { @@ -1331,7 +1470,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -1367,7 +1508,12 @@ "data": { "id": "ChatOutput-Q39I8", "node": { - "base_classes": ["object", "Text", "Record", "str"], + "base_classes": [ + "object", + "Text", + "Record", + "str" + ], "beta": false, "custom_fields": { "input_value": null, @@ -1384,7 +1530,10 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": ["Message", "Text"], + "output_types": [ + "Message", + "Text" + ], "template": { "_type": "CustomComponent", "code": { @@ -1412,7 +1561,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1431,12 +1582,17 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "password": false, "placeholder": "", "required": false, @@ -1452,7 +1608,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -1472,7 +1630,9 @@ "fileTypes": [], "file_path": "", "info": "If provided, the message will be stored in the memory.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -1507,7 +1667,9 @@ "data": { "id": "File-t0a6a", "node": { - "base_classes": ["Record"], + "base_classes": [ + "Record" + ], "beta": false, "custom_fields": { "path": null, @@ -1520,7 +1682,9 @@ "field_order": [], "frozen": false, "icon": "file-text", - "output_types": ["Record"], + "output_types": [ + "Record" + ], "template": { "_type": "CustomComponent", "code": { @@ -1621,7 +1785,9 @@ "data": { "id": "RecursiveCharacterTextSplitter-tR9QM", "node": { - "base_classes": ["Record"], + "base_classes": [ + "Record" + ], "beta": false, "custom_fields": { "chunk_overlap": null, @@ -1635,7 +1801,9 @@ "field_formatters": {}, "field_order": [], "frozen": false, - "output_types": ["Record"], + "output_types": [ + "Record" + ], "template": { "_type": "CustomComponent", "chunk_overlap": { @@ -1701,7 +1869,10 @@ "fileTypes": [], "file_path": "", "info": "The texts to split.", - "input_types": ["Document", "Record"], + "input_types": [ + "Document", + "Record" + ], "list": true, "load_from_db": false, "multiline": false, @@ -1720,7 +1891,9 @@ "fileTypes": [], "file_path": "", "info": "The characters to split on.\nIf left empty defaults to [\"\\n\\n\", \"\\n\", \" \", \"\"].", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, @@ -1731,7 +1904,9 @@ "show": true, "title_case": false, "type": "str", - "value": [""] + "value": [ + "" + ] } } }, @@ -1756,7 +1931,9 @@ "data": { "id": "AstraDBSearch-41nRz", "node": { - "base_classes": ["Record"], + "base_classes": [ + "Record" + ], "beta": false, "custom_fields": { "api_endpoint": null, @@ -1791,7 +1968,9 @@ ], "frozen": false, "icon": "AstraDB", - "output_types": ["Record"], + "output_types": [ + "Record" + ], "template": { "_type": "CustomComponent", "api_endpoint": { @@ -1801,7 +1980,9 @@ "fileTypes": [], "file_path": "", "info": "API endpoint URL for the Astra DB service.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": true, "multiline": false, @@ -1929,7 +2110,9 @@ "fileTypes": [], "file_path": "", "info": "The name of the collection within Astra DB where the vectors will be stored.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -1967,7 +2150,9 @@ "fileTypes": [], "file_path": "", "info": "Input value to search", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -1986,7 +2171,9 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to exclude from the indexing.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, @@ -2005,7 +2192,9 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to include in the indexing.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, @@ -2024,7 +2213,9 @@ "fileTypes": [], "file_path": "", "info": "Optional distance metric for vector comparisons in the vector store.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2043,7 +2234,9 @@ "fileTypes": [], "file_path": "", "info": "Optional namespace within Astra DB to use for the collection.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2100,12 +2293,17 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, "name": "search_type", - "options": ["Similarity", "MMR"], + "options": [ + "Similarity", + "MMR" + ], "password": false, "placeholder": "", "required": false, @@ -2121,12 +2319,18 @@ "fileTypes": [], "file_path": "", "info": "Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, "name": "setup_mode", - "options": ["Sync", "Async", "Off"], + "options": [ + "Sync", + "Async", + "Off" + ], "password": false, "placeholder": "", "required": false, @@ -2142,7 +2346,9 @@ "fileTypes": [], "file_path": "", "info": "Authentication token for accessing Astra DB.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": true, "multiline": false, @@ -2178,7 +2384,9 @@ "data": { "id": "AstraDB-eUCSS", "node": { - "base_classes": ["VectorStore"], + "base_classes": [ + "VectorStore" + ], "beta": false, "custom_fields": { "api_endpoint": null, @@ -2211,7 +2419,10 @@ ], "frozen": false, "icon": "AstraDB", - "output_types": ["VectorStore", "BaseRetriever"], + "output_types": [ + "VectorStore", + "BaseRetriever" + ], "template": { "_type": "CustomComponent", "api_endpoint": { @@ -2221,7 +2432,9 @@ "fileTypes": [], "file_path": "", "info": "API endpoint URL for the Astra DB service.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": true, "multiline": false, @@ -2349,7 +2562,9 @@ "fileTypes": [], "file_path": "", "info": "The name of the collection within Astra DB where the vectors will be stored.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2405,7 +2620,9 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to exclude from the indexing.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, @@ -2424,7 +2641,9 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to include in the indexing.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, @@ -2443,7 +2662,9 @@ "fileTypes": [], "file_path": "", "info": "Optional distance metric for vector comparisons in the vector store.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2462,7 +2683,9 @@ "fileTypes": [], "file_path": "", "info": "Optional namespace within Astra DB to use for the collection.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2500,12 +2723,18 @@ "fileTypes": [], "file_path": "", "info": "Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, "name": "setup_mode", - "options": ["Sync", "Async", "Off"], + "options": [ + "Sync", + "Async", + "Off" + ], "password": false, "placeholder": "", "required": false, @@ -2521,7 +2750,9 @@ "fileTypes": [], "file_path": "", "info": "Authentication token for accessing Astra DB.", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": true, "multiline": false, @@ -2557,7 +2788,9 @@ "data": { "id": "OpenAIEmbeddings-9TPjc", "node": { - "base_classes": ["Embeddings"], + "base_classes": [ + "Embeddings" + ], "beta": false, "custom_fields": { "allowed_special": null, @@ -2589,7 +2822,9 @@ "field_formatters": {}, "field_order": [], "frozen": false, - "output_types": ["Embeddings"], + "output_types": [ + "Embeddings" + ], "template": { "_type": "CustomComponent", "allowed_special": { @@ -2599,7 +2834,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2647,7 +2884,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Dict, List, Optional\n\nfrom langchain_openai.embeddings.base import OpenAIEmbeddings\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Embeddings, NestedDict\n\n\nclass OpenAIEmbeddingsComponent(CustomComponent):\n display_name = \"OpenAI Embeddings\"\n description = \"Generate embeddings using OpenAI models.\"\n\n def build_config(self):\n return {\n \"allowed_special\": {\n \"display_name\": \"Allowed Special\",\n \"advanced\": True,\n \"field_type\": \"str\",\n \"is_list\": True,\n },\n \"default_headers\": {\n \"display_name\": \"Default Headers\",\n \"advanced\": True,\n \"field_type\": \"dict\",\n },\n \"default_query\": {\n \"display_name\": \"Default Query\",\n \"advanced\": True,\n \"field_type\": \"NestedDict\",\n },\n \"disallowed_special\": {\n \"display_name\": \"Disallowed Special\",\n \"advanced\": True,\n \"field_type\": \"str\",\n \"is_list\": True,\n },\n \"chunk_size\": {\"display_name\": \"Chunk Size\", \"advanced\": True},\n \"client\": {\"display_name\": \"Client\", \"advanced\": True},\n \"deployment\": {\"display_name\": \"Deployment\", \"advanced\": True},\n \"embedding_ctx_length\": {\n \"display_name\": \"Embedding Context Length\",\n \"advanced\": True,\n },\n \"max_retries\": {\"display_name\": \"Max Retries\", \"advanced\": True},\n \"model\": {\n \"display_name\": \"Model\",\n \"advanced\": False,\n \"options\": [\n \"text-embedding-3-small\",\n \"text-embedding-3-large\",\n \"text-embedding-ada-002\",\n ],\n },\n \"model_kwargs\": {\"display_name\": \"Model Kwargs\", \"advanced\": True},\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"password\": True,\n \"advanced\": True,\n },\n \"openai_api_key\": {\"display_name\": \"OpenAI API Key\", \"password\": True},\n \"openai_api_type\": {\n \"display_name\": \"OpenAI API Type\",\n \"advanced\": True,\n \"password\": True,\n },\n \"openai_api_version\": {\n \"display_name\": \"OpenAI API Version\",\n \"advanced\": True,\n },\n \"openai_organization\": {\n \"display_name\": \"OpenAI Organization\",\n \"advanced\": True,\n },\n \"openai_proxy\": {\"display_name\": \"OpenAI Proxy\", \"advanced\": True},\n \"request_timeout\": {\"display_name\": \"Request Timeout\", \"advanced\": True},\n \"show_progress_bar\": {\n \"display_name\": \"Show Progress Bar\",\n \"advanced\": True,\n },\n \"skip_empty\": {\"display_name\": \"Skip Empty\", \"advanced\": True},\n \"tiktoken_model_name\": {\n \"display_name\": \"TikToken Model Name\",\n \"advanced\": True,\n },\n \"tiktoken_enable\": {\"display_name\": \"TikToken Enable\", \"advanced\": True},\n }\n\n def build(\n self,\n openai_api_key: str,\n default_headers: Optional[Dict[str, str]] = None,\n default_query: Optional[NestedDict] = {},\n allowed_special: List[str] = [],\n disallowed_special: List[str] = [\"all\"],\n chunk_size: int = 1000,\n deployment: str = \"text-embedding-ada-002\",\n embedding_ctx_length: int = 8191,\n max_retries: int = 6,\n model: str = \"text-embedding-ada-002\",\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n openai_api_type: Optional[str] = None,\n openai_api_version: Optional[str] = None,\n openai_organization: Optional[str] = None,\n openai_proxy: Optional[str] = None,\n request_timeout: Optional[float] = None,\n show_progress_bar: bool = False,\n skip_empty: bool = False,\n tiktoken_enable: bool = True,\n tiktoken_model_name: Optional[str] = None,\n ) -> Embeddings:\n # This is to avoid errors with Vector Stores (e.g Chroma)\n if disallowed_special == [\"all\"]:\n disallowed_special = \"all\" # type: ignore\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n return OpenAIEmbeddings(\n tiktoken_enabled=tiktoken_enable,\n default_headers=default_headers,\n default_query=default_query,\n allowed_special=set(allowed_special),\n disallowed_special=\"all\",\n chunk_size=chunk_size,\n deployment=deployment,\n embedding_ctx_length=embedding_ctx_length,\n max_retries=max_retries,\n model=model,\n model_kwargs=model_kwargs,\n base_url=openai_api_base,\n api_key=api_key,\n openai_api_type=openai_api_type,\n api_version=openai_api_version,\n organization=openai_organization,\n openai_proxy=openai_proxy,\n timeout=request_timeout,\n show_progress_bar=show_progress_bar,\n skip_empty=skip_empty,\n tiktoken_model_name=tiktoken_model_name,\n )\n" + "value": "from typing import Dict, List, Optional\n\nfrom langchain_openai.embeddings.base import OpenAIEmbeddings\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Embeddings, NestedDict\n\n\nclass OpenAIEmbeddingsComponent(CustomComponent):\n display_name = \"OpenAI Embeddings\"\n description = \"Generate embeddings using OpenAI models.\"\n\n def build_config(self):\n return {\n \"allowed_special\": {\n \"display_name\": \"Allowed Special\",\n \"advanced\": True,\n \"field_type\": \"str\",\n \"is_list\": True,\n },\n \"default_headers\": {\n \"display_name\": \"Default Headers\",\n \"advanced\": True,\n \"field_type\": \"dict\",\n },\n \"default_query\": {\n \"display_name\": \"Default Query\",\n \"advanced\": True,\n \"field_type\": \"NestedDict\",\n },\n \"disallowed_special\": {\n \"display_name\": \"Disallowed Special\",\n \"advanced\": True,\n \"field_type\": \"str\",\n \"is_list\": True,\n },\n \"chunk_size\": {\"display_name\": \"Chunk Size\", \"advanced\": True},\n \"client\": {\"display_name\": \"Client\", \"advanced\": True},\n \"deployment\": {\"display_name\": \"Deployment\", \"advanced\": True},\n \"embedding_ctx_length\": {\n \"display_name\": \"Embedding Context Length\",\n \"advanced\": True,\n },\n \"max_retries\": {\"display_name\": \"Max Retries\", \"advanced\": True},\n \"model\": {\n \"display_name\": \"Model\",\n \"advanced\": False,\n \"options\": [\n \"text-embedding-3-small\",\n \"text-embedding-3-large\",\n \"text-embedding-ada-002\",\n ],\n },\n \"model_kwargs\": {\"display_name\": \"Model Kwargs\", \"advanced\": True},\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"password\": True,\n \"advanced\": True,\n },\n \"openai_api_key\": {\"display_name\": \"OpenAI API Key\", \"password\": True},\n \"openai_api_type\": {\n \"display_name\": \"OpenAI API Type\",\n \"advanced\": True,\n \"password\": True,\n },\n \"openai_api_version\": {\n \"display_name\": \"OpenAI API Version\",\n \"advanced\": True,\n },\n \"openai_organization\": {\n \"display_name\": \"OpenAI Organization\",\n \"advanced\": True,\n },\n \"openai_proxy\": {\"display_name\": \"OpenAI Proxy\", \"advanced\": True},\n \"request_timeout\": {\"display_name\": \"Request Timeout\", \"advanced\": True},\n \"show_progress_bar\": {\n \"display_name\": \"Show Progress Bar\",\n \"advanced\": True,\n },\n \"skip_empty\": {\"display_name\": \"Skip Empty\", \"advanced\": True},\n \"tiktoken_model_name\": {\n \"display_name\": \"TikToken Model Name\",\n \"advanced\": True,\n },\n \"tiktoken_enable\": {\"display_name\": \"TikToken Enable\", \"advanced\": True},\n \"dimensions\": {\n \"display_name\": \"Dimensions\",\n \"info\": \"The number of dimensions the resulting output embeddings should have. Only supported by certain models.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n openai_api_key: str,\n default_headers: Optional[Dict[str, str]] = None,\n default_query: Optional[NestedDict] = {},\n allowed_special: List[str] = [],\n disallowed_special: List[str] = [\"all\"],\n chunk_size: int = 1000,\n deployment: str = \"text-embedding-ada-002\",\n embedding_ctx_length: int = 8191,\n max_retries: int = 6,\n model: str = \"text-embedding-ada-002\",\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n openai_api_type: Optional[str] = None,\n openai_api_version: Optional[str] = None,\n openai_organization: Optional[str] = None,\n openai_proxy: Optional[str] = None,\n request_timeout: Optional[float] = None,\n show_progress_bar: bool = False,\n skip_empty: bool = False,\n tiktoken_enable: bool = True,\n tiktoken_model_name: Optional[str] = None,\n dimensions: Optional[int] = None,\n ) -> Embeddings:\n # This is to avoid errors with Vector Stores (e.g Chroma)\n if disallowed_special == [\"all\"]:\n disallowed_special = \"all\" # type: ignore\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n return OpenAIEmbeddings(\n tiktoken_enabled=tiktoken_enable,\n default_headers=default_headers,\n default_query=default_query,\n allowed_special=set(allowed_special),\n disallowed_special=\"all\",\n chunk_size=chunk_size,\n deployment=deployment,\n embedding_ctx_length=embedding_ctx_length,\n max_retries=max_retries,\n model=model,\n model_kwargs=model_kwargs,\n base_url=openai_api_base,\n api_key=api_key,\n openai_api_type=openai_api_type,\n api_version=openai_api_version,\n organization=openai_organization,\n openai_proxy=openai_proxy,\n timeout=request_timeout,\n show_progress_bar=show_progress_bar,\n skip_empty=skip_empty,\n tiktoken_model_name=tiktoken_model_name,\n dimensions=dimensions,\n )\n" }, "default_headers": { "advanced": true, @@ -2693,7 +2930,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2713,7 +2952,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2724,7 +2965,9 @@ "show": true, "title_case": false, "type": "str", - "value": ["all"] + "value": [ + "all" + ] }, "embedding_ctx_length": { "advanced": true, @@ -2771,7 +3014,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": true, "load_from_db": false, "multiline": false, @@ -2815,7 +3060,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2834,7 +3081,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": true, "multiline": false, @@ -2854,7 +3103,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2873,7 +3124,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2892,7 +3145,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -2911,7 +3166,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -3011,7 +3268,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text"], + "input_types": [ + "Text" + ], "list": false, "load_from_db": false, "multiline": false, @@ -3054,4 +3313,4 @@ "is_component": false, "last_tested_version": "1.0.0a0", "name": "Vector Store RAG" -} +} \ No newline at end of file diff --git a/src/backend/base/langflow/langflow-pre.db-shm b/src/backend/base/langflow/langflow-pre.db-shm deleted file mode 100644 index e77ab53ece30e9457154ec69a6e96f32330d8acf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI5M^hA06oro<=7Mj57p91u){QW+uH1F%P z9#xmWJSdsdvij4qvX41cX%6#&+JS2U`SZ_Z{qUbZ*c+*&9qCCjNG2IT29qIVC>chw zNH!Txa>xjhOY%rQDIkSp6d6s%kO^cWDI$}|WHNYD zmXj4^C0Ru_kTP|8RJRFik)2l+*Qlk3Sq6}ds~ zlAk1_J}{1KBV}Ye*-3Vha#BG~lh5QkN#*a5t>VgQBtEn0&D3eQJ_G+Qbjg4W$bbyU zfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbyUfDFih z49I{C$bbyUfDFih49I{C$bbyUfDFih49I{C$bbwqwEpkCiHBi0WfC*a%)k@ltb(yN zkc$h(Td0SH#@ToX22Qnb`)f1J?07C-I%+!5aT=!EUTUh7@0fyUNZ zTeXj_++^XjL7iE6Zje&Mxq;5u;e1g-u4rPNGUx8-**IfRa5kz9Qg$R0mG*<~f*9z6 zok8B(uL^P1en`l>rdaLz+{1C!^_qcmDvw5?svk?HW0%ob*Njp%p^>=cARRB!W?19; zR&#L9pynLBFi6>vfjDMA=q`wXt|$-kRqa^MhFV!)74YwTi8xtL>`lb6!Hiogvoh1`+ z$DKl+RH<)$mBf=)5jN?CeGyjpfB6{P`2R<~cuvJ8U(BH)Yy-Wq-+s`Y40vQ<0p57j z$;&oV@zBe59`xA)hjYd@rH(P{z zG3>6^&J46esj~$Mn@+(}gV`tHzCp_0ktFNurSULhdu)k^ZEBAW4D`VP2kAtPcEHvI z9e)}g`EK%gc*U9e;-K#)i_So4*cP3_zN^~6`T}_@#+r`Dv1l81&t&VH?O`uZpoM<{ DuY8%Z diff --git a/src/backend/base/langflow/langflow-pre.db-wal b/src/backend/base/langflow/langflow-pre.db-wal deleted file mode 100644 index 746a1fa81882d7af6763e805909d6861b4239a95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 700428 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 From 045cb38cd3400220c10610e0cc0052c40259b4c4 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 12:12:24 -0300 Subject: [PATCH 27/68] Update Memory Example --- .../Langflow Memory Conversation.json | 2316 ++++++++--------- 1 file changed, 1079 insertions(+), 1237 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json index df360b08c..55733f720 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json @@ -1,1242 +1,1084 @@ { - "data": { - "edges": [ - { - "className": "stroke-gray-900 stroke-connection", - "data": { - "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], - "dataType": "MemoryComponent", - "id": "MemoryComponent-iDkC0" - }, - "targetHandle": { - "fieldName": "context", - "id": "Prompt-19lCm", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-MemoryComponent-iDkC0{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-iDkC0œ}-Prompt-19lCm{œfieldNameœ:œcontextœ,œidœ:œPrompt-19lCmœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "selected": false, - "source": "MemoryComponent-iDkC0", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œMemoryComponentœ, œidœ: œMemoryComponent-iDkC0œ}", - "style": { - "stroke": "#555" - }, - "target": "Prompt-19lCm", - "targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-19lCmœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" - }, - { - "className": "", - "data": { - "sourceHandle": { - "baseClasses": [ - "Text", - "object", - "Record", - "str" - ], - "dataType": "ChatInput", - "id": "ChatInput-Z6G1Z" - }, - "targetHandle": { - "fieldName": "user_message", - "id": "Prompt-19lCm", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-ChatInput-Z6G1Z{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-Z6G1Zœ}-Prompt-19lCm{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-19lCmœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "selected": false, - "source": "ChatInput-Z6G1Z", - "sourceHandle": "{œbaseClassesœ: [œTextœ, œobjectœ, œRecordœ, œstrœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-Z6G1Zœ}", - "style": { - "stroke": "#555" - }, - "target": "Prompt-19lCm", - "targetHandle": "{œfieldNameœ: œuser_messageœ, œidœ: œPrompt-19lCmœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" - }, - { - "className": "", - "data": { - "sourceHandle": { - "baseClasses": [ - "Text", - "str", - "object" - ], - "dataType": "Prompt", - "id": "Prompt-19lCm" - }, - "targetHandle": { - "fieldName": "input_value", - "id": "OpenAIModel-1v5Hz", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], - "type": "str" - } - }, - "id": "reactflow__edge-Prompt-19lCm{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-19lCmœ}-OpenAIModel-1v5Hz{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-1v5Hzœ,œinputTypesœ:[œTextœ,œRecordœ,œPromptœ],œtypeœ:œstrœ}", - "source": "Prompt-19lCm", - "sourceHandle": "{œbaseClassesœ: [œTextœ, œstrœ, œobjectœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-19lCmœ}", - "style": { - "stroke": "#555" - }, - "target": "OpenAIModel-1v5Hz", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-1v5Hzœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" - }, - { - "className": "", - "data": { - "sourceHandle": { - "baseClasses": [ - "str", - "object", - "Text" - ], - "dataType": "OpenAIModel", - "id": "OpenAIModel-1v5Hz" - }, - "targetHandle": { - "fieldName": "input_value", - "id": "ChatOutput-i56kN", - "inputTypes": [ - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-OpenAIModel-1v5Hz{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-1v5Hzœ}-ChatOutput-i56kN{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-i56kNœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", - "source": "OpenAIModel-1v5Hz", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œobjectœ, œTextœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-1v5Hzœ}", - "style": { - "stroke": "#555" - }, - "target": "ChatOutput-i56kN", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-i56kNœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" - }, - { - "className": "", - "data": { - "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], - "dataType": "MemoryComponent", - "id": "MemoryComponent-iDkC0" - }, - "targetHandle": { - "fieldName": "input_value", - "id": "TextOutput-KuLNK", - "inputTypes": [ - "Record", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-MemoryComponent-iDkC0{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-iDkC0œ}-TextOutput-KuLNK{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-KuLNKœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", - "source": "MemoryComponent-iDkC0", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œMemoryComponentœ, œidœ: œMemoryComponent-iDkC0œ}", - "style": { - "stroke": "#555" - }, - "target": "TextOutput-KuLNK", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œTextOutput-KuLNKœ, œinputTypesœ: [œRecordœ, œTextœ], œtypeœ: œstrœ}" - } - ], - "nodes": [ - { - "data": { - "id": "ChatInput-Z6G1Z", - "node": { - "base_classes": [ - "Text", - "object", - "Record", - "str" - ], - "beta": false, - "custom_fields": { - "input_value": null, - "return_record": null, - "sender": null, - "sender_name": null, - "session_id": null - }, - "description": "Get chat inputs from the Playground.", - "display_name": "Chat Input", - "documentation": "", - "field_formatters": {}, - "field_order": [], - "frozen": false, - "icon": "ChatInput", - "output_types": [ - "Message", - "Text" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" - }, - "input_value": { - "advanced": false, - "display_name": "Text", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "input_value", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, - "sender": { - "advanced": true, - "display_name": "Sender Type", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, - "name": "sender", - "options": [ - "Machine", - "User" - ], - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "User" - }, - "sender_name": { - "advanced": false, - "display_name": "Sender Name", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "sender_name", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "User" - }, - "session_id": { - "advanced": false, - "display_name": "Session ID", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "session_id", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "MySessionID" - } - } - }, - "type": "ChatInput" - }, - "dragging": false, - "height": 469, - "id": "ChatInput-Z6G1Z", - "position": { - "x": 1283.2700598313072, - "y": 982.5953650473145 - }, - "positionAbsolute": { - "x": 1283.2700598313072, - "y": 982.5953650473145 - }, - "selected": false, - "type": "genericNode", - "width": 384 - }, - { - "data": { - "id": "ChatOutput-i56kN", - "node": { - "base_classes": [ - "Text", - "object", - "Record", - "str" - ], - "beta": false, - "custom_fields": { - "input_value": null, - "return_record": null, - "sender": null, - "sender_name": null, - "session_id": null - }, - "description": "Display a chat message in the Playground.", - "display_name": "Chat Output", - "documentation": "", - "field_formatters": {}, - "field_order": [], - "frozen": false, - "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" - }, - "input_value": { - "advanced": false, - "display_name": "Text", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "input_value", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str" - }, - "sender": { - "advanced": true, - "display_name": "Sender Type", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, - "name": "sender", - "options": [ - "Machine", - "User" - ], - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "Machine" - }, - "sender_name": { - "advanced": false, - "display_name": "Sender Name", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "sender_name", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "AI" - }, - "session_id": { - "advanced": false, - "display_name": "Session ID", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "session_id", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "MySessionID" - } - } - }, - "type": "ChatOutput" - }, - "dragging": false, - "height": 477, - "id": "ChatOutput-i56kN", - "position": { - "x": 3154.916355514023, - "y": 851.051882666333 - }, - "positionAbsolute": { - "x": 3154.916355514023, - "y": 851.051882666333 - }, - "selected": false, - "type": "genericNode", - "width": 384 - }, - { - "data": { - "description": "Retrieves stored chat messages given a specific Session ID.", - "display_name": "Chat Memory", - "id": "MemoryComponent-iDkC0", - "node": { - "base_classes": [ - "str", - "Text", - "object" - ], - "beta": true, - "custom_fields": { - "n_messages": null, - "order": null, - "record_template": null, - "sender": null, - "sender_name": null, - "session_id": null - }, - "description": "Retrieves stored chat messages given a specific Session ID.", - "display_name": "Chat Memory", - "documentation": "", - "field_formatters": {}, - "field_order": [], - "frozen": false, - "icon": "history", - "output_types": [ - "Text" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.memory.memory import BaseMemoryComponent\nfrom langflow.field_typing import Text\nfrom langflow.helpers.record import messages_to_text\nfrom langflow.memory import get_messages\nfrom langflow.schema.message import Message\n\n\nclass MemoryComponent(BaseMemoryComponent):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages given a specific Session ID.\"\n beta: bool = True\n icon = \"history\"\n\n def build_config(self):\n return {\n \"sender\": {\n \"options\": [\"Machine\", \"User\", \"Machine and User\"],\n \"display_name\": \"Sender Type\",\n },\n \"sender_name\": {\"display_name\": \"Sender Name\", \"advanced\": True},\n \"n_messages\": {\n \"display_name\": \"Number of Messages\",\n \"info\": \"Number of messages to retrieve.\",\n },\n \"session_id\": {\n \"display_name\": \"Session ID\",\n \"info\": \"Session ID of the chat history.\",\n \"input_types\": [\"Text\"],\n },\n \"order\": {\n \"options\": [\"Ascending\", \"Descending\"],\n \"display_name\": \"Order\",\n \"info\": \"Order of the messages.\",\n \"advanced\": True,\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def get_messages(self, **kwargs) -> list[Message]:\n # Validate kwargs by checking if it contains the correct keys\n if \"sender\" not in kwargs:\n kwargs[\"sender\"] = None\n if \"sender_name\" not in kwargs:\n kwargs[\"sender_name\"] = None\n if \"session_id\" not in kwargs:\n kwargs[\"session_id\"] = None\n if \"limit\" not in kwargs:\n kwargs[\"limit\"] = 5\n if \"order\" not in kwargs:\n kwargs[\"order\"] = \"Descending\"\n\n kwargs[\"order\"] = \"DESC\" if kwargs[\"order\"] == \"Descending\" else \"ASC\"\n if kwargs[\"sender\"] == \"Machine and User\":\n kwargs[\"sender\"] = None\n return get_messages(**kwargs)\n\n def build(\n self,\n sender: Optional[str] = \"Machine and User\",\n sender_name: Optional[str] = None,\n session_id: Optional[str] = None,\n n_messages: int = 5,\n order: Optional[str] = \"Descending\",\n record_template: Optional[str] = \"{sender_name}: {text}\",\n ) -> Text:\n messages = self.get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n messages_str = messages_to_text(template=record_template or \"\", messages=messages)\n self.status = messages_str\n return messages_str\n" - }, - "n_messages": { - "advanced": false, - "display_name": "Number of Messages", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Number of messages to retrieve.", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "n_messages", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "int", - "value": 5 - }, - "order": { - "advanced": true, - "display_name": "Order", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Order of the messages.", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, - "name": "order", - "options": [ - "Ascending", - "Descending" - ], - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "Descending" - }, - "record_template": { - "advanced": true, - "display_name": "Record Template", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "record_template", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "{sender_name}: {text}" - }, - "sender": { - "advanced": false, - "display_name": "Sender Type", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, - "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" - ], - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "Machine and User" - }, - "sender_name": { - "advanced": true, - "display_name": "Sender Name", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "sender_name", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str" - }, - "session_id": { - "advanced": false, - "display_name": "Session ID", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Session ID of the chat history.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "session_id", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "MySessionID" - } - } - }, - "type": "MemoryComponent" - }, - "dragging": false, - "height": 489, - "id": "MemoryComponent-iDkC0", - "position": { - "x": 1289.9606870058817, - "y": 442.16804561053766 - }, - "positionAbsolute": { - "x": 1289.9606870058817, - "y": 442.16804561053766 - }, - "selected": false, - "type": "genericNode", - "width": 384 - }, - { - "data": { - "description": "A component for creating prompt templates using dynamic variables.", - "display_name": "Prompt", - "id": "Prompt-19lCm", - "node": { - "base_classes": [ - "Text", - "str", - "object" - ], - "beta": false, - "custom_fields": { - "template": [ - "context", - "user_message" - ] - }, - "description": "Create a prompt template with dynamic variables.", - "display_name": "Prompt", - "documentation": "", - "error": null, - "field_formatters": {}, - "field_order": [], - "frozen": false, - "full_path": null, - "icon": "prompts", - "is_composition": null, - "is_input": null, - "is_output": null, - "name": "", - "output_types": [ - "Prompt" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" - }, - "context": { - "advanced": false, - "display_name": "context", - "dynamic": false, - "field_type": "str", - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "context", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, - "template": { - "advanced": false, - "display_name": "Template", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "template", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "prompt", - "value": "{context}\n\nUser: {user_message}\nAI: " - }, - "user_message": { - "advanced": false, - "display_name": "user_message", - "dynamic": false, - "field_type": "str", - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "user_message", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - } - } - }, - "type": "Prompt" - }, - "dragging": false, - "height": 477, - "id": "Prompt-19lCm", - "position": { - "x": 1894.594426342426, - "y": 753.3797365481901 - }, - "positionAbsolute": { - "x": 1894.594426342426, - "y": 753.3797365481901 - }, - "selected": false, - "type": "genericNode", - "width": 384 - }, - { - "data": { - "id": "OpenAIModel-1v5Hz", - "node": { - "base_classes": [ - "str", - "object", - "Text" - ], - "beta": false, - "custom_fields": { - "input_value": null, - "max_tokens": null, - "model_kwargs": null, - "model_name": null, - "openai_api_base": null, - "openai_api_key": null, - "stream": null, - "system_message": null, - "temperature": null - }, - "description": "Generates text using OpenAI LLMs.", - "display_name": "OpenAI", - "documentation": "", - "field_formatters": {}, - "field_order": [ - "max_tokens", - "model_kwargs", - "model_name", - "openai_api_base", - "openai_api_key", - "temperature", - "input_value", - "system_message", - "stream" - ], - "frozen": false, - "icon": "OpenAI", - "output_types": [ - "Text" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" - }, - "input_value": { - "advanced": false, - "display_name": "Input", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "input_value", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "str" - }, - "max_tokens": { - "advanced": true, - "display_name": "Max Tokens", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "max_tokens", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "int", - "value": 256 - }, - "model_kwargs": { - "advanced": true, - "display_name": "Model Kwargs", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "model_kwargs", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "NestedDict", - "value": {} - }, - "model_name": { - "advanced": false, - "display_name": "Model Name", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, - "name": "model_name", - "options": [ - "gpt-4o", - "gpt-4-turbo", - "gpt-4-turbo-preview", - "gpt-3.5-turbo", - "gpt-3.5-turbo-0125" - ], - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "gpt-3.5-turbo" - }, - "openai_api_base": { - "advanced": true, - "display_name": "OpenAI API Base", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "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.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "openai_api_base", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str" - }, - "openai_api_key": { - "advanced": false, - "display_name": "OpenAI API Key", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": true, - "multiline": false, - "name": "openai_api_key", - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, - "stream": { - "advanced": true, - "display_name": "Stream", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Stream the response from the model. Streaming works only in Chat.", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "stream", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "bool", - "value": false - }, - "system_message": { - "advanced": true, - "display_name": "System Message", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "system_message", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str" - }, - "temperature": { - "advanced": false, - "display_name": "Temperature", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "temperature", - "password": false, - "placeholder": "", - "rangeSpec": { - "max": 1, - "min": -1, - "step": 0.1, - "step_type": "float" + "id": "321b1bab-8691-42da-9689-1f12b5d2a48b", + "data": { + "nodes": [ + { + "data": { + "id": "ChatInput-Z9Rn6", + "node": { + "base_classes": [ + "Text", + "object", + "Record", + "str" + ], + "beta": false, + "custom_fields": { + "input_value": null, + "return_record": null, + "sender": null, + "sender_name": null, + "session_id": null + }, + "description": "Get chat inputs from the Playground.", + "display_name": "Chat Input", + "documentation": "", + "field_formatters": {}, + "field_order": [], + "frozen": false, + "icon": "ChatInput", + "output_types": [ + "Message", + "Text" + ], + "template": { + "_type": "CustomComponent", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" + }, + "input_value": { + "advanced": false, + "display_name": "Text", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "input_value", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "do you know his name?" + }, + "sender": { + "advanced": true, + "display_name": "Sender Type", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": true, + "load_from_db": false, + "multiline": false, + "name": "sender", + "options": [ + "Machine", + "User" + ], + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "User" + }, + "sender_name": { + "advanced": false, + "display_name": "Sender Name", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "sender_name", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "User" + }, + "session_id": { + "advanced": false, + "display_name": "Session ID", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "If provided, the message will be stored in the memory.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "session_id", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "MySessionID" + } + } + }, + "type": "ChatInput" }, - "required": false, - "show": true, - "title_case": false, - "type": "float", - "value": "0.2" - } - } - }, - "type": "OpenAIModel" - }, - "dragging": false, - "height": 563, - "id": "OpenAIModel-1v5Hz", - "position": { - "x": 2561.5850334731617, - "y": 553.2745131130916 - }, - "positionAbsolute": { - "x": 2561.5850334731617, - "y": 553.2745131130916 - }, - "selected": false, - "type": "genericNode", - "width": 384 - }, - { - "data": { - "description": "Display a text output in the Playground.", - "display_name": "Inspect Memory", - "edited": false, - "id": "TextOutput-KuLNK", - "node": { - "base_classes": [ - "object", - "str", - "Text" - ], - "beta": false, - "custom_fields": { - "input_value": null, - "record_template": null + "dragging": false, + "height": 477, + "id": "ChatInput-Z9Rn6", + "position": { + "x": 1283.2700598313072, + "y": 982.5953650473145 + }, + "positionAbsolute": { + "x": 1283.2700598313072, + "y": 982.5953650473145 + }, + "selected": false, + "type": "genericNode", + "width": 384 }, - "description": "Display a text output in the Playground.", - "display_name": "Inspect Memory", - "documentation": "", - "edited": true, - "field_formatters": {}, - "field_order": [], - "frozen": false, - "icon": "type", - "output_types": [ - "Text" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextOutput(TextComponent):\n display_name = \"Text Output\"\n description = \"Display a text output in the Playground.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Text\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as output.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(self, input_value: Optional[Text] = \"\", record_template: Optional[str] = \"\") -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n" - }, - "input_value": { - "advanced": false, - "display_name": "Text", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Text or Record to be passed as output.", - "input_types": [ - "Record", - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "input_value", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, - "record_template": { - "advanced": true, - "display_name": "Record Template", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "record_template", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - } + { + "data": { + "id": "ChatOutput-cVR7W", + "node": { + "base_classes": [ + "Text", + "object", + "Record", + "str" + ], + "beta": false, + "custom_fields": { + "input_value": null, + "return_record": null, + "sender": null, + "sender_name": null, + "session_id": null + }, + "description": "Display a chat message in the Playground.", + "display_name": "Chat Output", + "documentation": "", + "field_formatters": {}, + "field_order": [], + "frozen": false, + "icon": "ChatOutput", + "output_types": [ + "Message", + "Text" + ], + "template": { + "_type": "CustomComponent", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" + }, + "input_value": { + "advanced": false, + "display_name": "Text", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "input_value", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str" + }, + "sender": { + "advanced": true, + "display_name": "Sender Type", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": true, + "load_from_db": false, + "multiline": false, + "name": "sender", + "options": [ + "Machine", + "User" + ], + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "Machine" + }, + "sender_name": { + "advanced": false, + "display_name": "Sender Name", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "sender_name", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "AI" + }, + "session_id": { + "advanced": false, + "display_name": "Session ID", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "If provided, the message will be stored in the memory.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "session_id", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "MySessionID" + } + } + }, + "type": "ChatOutput" + }, + "dragging": false, + "height": 485, + "id": "ChatOutput-cVR7W", + "position": { + "x": 3154.916355514023, + "y": 851.051882666333 + }, + "positionAbsolute": { + "x": 3154.916355514023, + "y": 851.051882666333 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Retrieves stored chat messages given a specific Session ID.", + "display_name": "Chat Memory", + "id": "MemoryComponent-u6m5G", + "node": { + "base_classes": [ + "str", + "Text", + "object" + ], + "beta": true, + "custom_fields": { + "n_messages": null, + "order": null, + "record_template": null, + "sender": null, + "sender_name": null, + "session_id": null + }, + "description": "Retrieves stored chat messages given a specific Session ID.", + "display_name": "Chat Memory", + "documentation": "", + "field_formatters": {}, + "field_order": [], + "frozen": false, + "icon": "history", + "output_types": [ + "Text" + ], + "template": { + "_type": "CustomComponent", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from typing import Optional\n\nfrom langflow.base.memory.memory import BaseMemoryComponent\nfrom langflow.field_typing import Text\nfrom langflow.helpers.record import messages_to_text\nfrom langflow.memory import get_messages\nfrom langflow.schema.message import Message\n\n\nclass MemoryComponent(BaseMemoryComponent):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages given a specific Session ID.\"\n beta: bool = True\n icon = \"history\"\n\n def build_config(self):\n return {\n \"sender\": {\n \"options\": [\"Machine\", \"User\", \"Machine and User\"],\n \"display_name\": \"Sender Type\",\n },\n \"sender_name\": {\"display_name\": \"Sender Name\", \"advanced\": True},\n \"n_messages\": {\n \"display_name\": \"Number of Messages\",\n \"info\": \"Number of messages to retrieve.\",\n },\n \"session_id\": {\n \"display_name\": \"Session ID\",\n \"info\": \"Session ID of the chat history.\",\n \"input_types\": [\"Text\"],\n },\n \"order\": {\n \"options\": [\"Ascending\", \"Descending\"],\n \"display_name\": \"Order\",\n \"info\": \"Order of the messages.\",\n \"advanced\": True,\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def get_messages(self, **kwargs) -> list[Message]:\n # Validate kwargs by checking if it contains the correct keys\n if \"sender\" not in kwargs:\n kwargs[\"sender\"] = None\n if \"sender_name\" not in kwargs:\n kwargs[\"sender_name\"] = None\n if \"session_id\" not in kwargs:\n kwargs[\"session_id\"] = None\n if \"limit\" not in kwargs:\n kwargs[\"limit\"] = 5\n if \"order\" not in kwargs:\n kwargs[\"order\"] = \"Descending\"\n\n kwargs[\"order\"] = \"DESC\" if kwargs[\"order\"] == \"Descending\" else \"ASC\"\n if kwargs[\"sender\"] == \"Machine and User\":\n kwargs[\"sender\"] = None\n return get_messages(**kwargs)\n\n def build(\n self,\n sender: Optional[str] = \"Machine and User\",\n sender_name: Optional[str] = None,\n session_id: Optional[str] = None,\n n_messages: int = 5,\n order: Optional[str] = \"Descending\",\n record_template: Optional[str] = \"{sender_name}: {text}\",\n ) -> Text:\n messages = self.get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n messages_str = messages_to_text(template=record_template or \"\", messages=messages)\n self.status = messages_str\n return messages_str\n" + }, + "n_messages": { + "advanced": false, + "display_name": "Number of Messages", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "Number of messages to retrieve.", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "n_messages", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "int", + "value": 5 + }, + "order": { + "advanced": true, + "display_name": "Order", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "Order of the messages.", + "input_types": [ + "Text" + ], + "list": true, + "load_from_db": false, + "multiline": false, + "name": "order", + "options": [ + "Ascending", + "Descending" + ], + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "Descending" + }, + "record_template": { + "advanced": true, + "display_name": "Record Template", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "record_template", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "{sender_name}: {text}" + }, + "sender": { + "advanced": false, + "display_name": "Sender Type", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": true, + "load_from_db": false, + "multiline": false, + "name": "sender", + "options": [ + "Machine", + "User", + "Machine and User" + ], + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "Machine and User" + }, + "sender_name": { + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "sender_name", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str" + }, + "session_id": { + "advanced": false, + "display_name": "Session ID", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "Session ID of the chat history.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "session_id", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "MySessionID" + } + } + }, + "type": "MemoryComponent" + }, + "dragging": false, + "height": 505, + "id": "MemoryComponent-u6m5G", + "position": { + "x": 1289.9606870058817, + "y": 442.16804561053766 + }, + "positionAbsolute": { + "x": 1289.9606870058817, + "y": 442.16804561053766 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "id": "Prompt-kykM2", + "node": { + "template": { + "_type": "CustomComponent", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" + }, + "template": { + "advanced": false, + "display_name": "Template", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "template", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "prompt", + "value": "Previous messages:\n{Context}\n\nUser: {UserMessage}\nAI: " + }, + "Context": { + "field_type": "str", + "required": false, + "placeholder": "", + "list": false, + "show": true, + "multiline": true, + "value": "", + "fileTypes": [], + "file_path": "", + "password": false, + "name": "Context", + "display_name": "Context", + "advanced": false, + "input_types": [ + "Document", + "Message", + "Record", + "Text" + ], + "dynamic": false, + "info": "", + "load_from_db": false, + "title_case": false, + "type": "str" + }, + "UserMessage": { + "field_type": "str", + "required": false, + "placeholder": "", + "list": false, + "show": true, + "multiline": true, + "value": "", + "fileTypes": [], + "file_path": "", + "password": false, + "name": "UserMessage", + "display_name": "UserMessage", + "advanced": false, + "input_types": [ + "Document", + "Message", + "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": [ + "Context", + "UserMessage" + ] + }, + "output_types": [ + "Prompt" + ], + "full_path": null, + "field_formatters": {}, + "frozen": false, + "field_order": [], + "beta": false, + "error": null + }, + "type": "Prompt" + }, + "dragging": false, + "height": 513, + "id": "Prompt-kykM2", + "position": { + "x": 1890.2582485007167, + "y": 753.3797365481901 + }, + "positionAbsolute": { + "x": 1890.2582485007167, + "y": 753.3797365481901 + }, + "selected": true, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "OpenAIModel-Neuec", + "node": { + "base_classes": [ + "str", + "object", + "Text" + ], + "beta": false, + "custom_fields": { + "input_value": null, + "max_tokens": null, + "model_kwargs": null, + "model_name": null, + "openai_api_base": null, + "openai_api_key": null, + "stream": null, + "system_message": null, + "temperature": null + }, + "description": "Generates text using OpenAI LLMs.", + "display_name": "OpenAI", + "documentation": "", + "field_formatters": {}, + "field_order": [ + "max_tokens", + "model_kwargs", + "model_name", + "openai_api_base", + "openai_api_key", + "temperature", + "input_value", + "system_message", + "stream" + ], + "frozen": false, + "icon": "OpenAI", + "output_types": [ + "Text" + ], + "template": { + "_type": "CustomComponent", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" + }, + "input_value": { + "advanced": false, + "display_name": "Input", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text", + "Record", + "Prompt" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "input_value", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str" + }, + "max_tokens": { + "advanced": true, + "display_name": "Max Tokens", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "max_tokens", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "int", + "value": 256 + }, + "model_kwargs": { + "advanced": true, + "display_name": "Model Kwargs", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "model_kwargs", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "NestedDict", + "value": {} + }, + "model_name": { + "advanced": false, + "display_name": "Model Name", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": true, + "load_from_db": false, + "multiline": false, + "name": "model_name", + "options": [ + "gpt-4o", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-3.5-turbo", + "gpt-3.5-turbo-0125" + ], + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "gpt-3.5-turbo" + }, + "openai_api_base": { + "advanced": true, + "display_name": "OpenAI API Base", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "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.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "openai_api_base", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str" + }, + "openai_api_key": { + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "The OpenAI API Key to use for the OpenAI model.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": true, + "multiline": false, + "name": "openai_api_key", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "stream": { + "advanced": true, + "display_name": "Stream", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "Stream the response from the model. Streaming works only in Chat.", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "stream", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "bool", + "value": false + }, + "system_message": { + "advanced": true, + "display_name": "System Message", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "System message to pass to the model.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "system_message", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str" + }, + "temperature": { + "advanced": false, + "display_name": "Temperature", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "temperature", + "password": false, + "placeholder": "", + "rangeSpec": { + "max": 1, + "min": -1, + "step": 0.1, + "step_type": "float" + }, + "required": false, + "show": true, + "title_case": false, + "type": "float", + "value": "0.2" + } + } + }, + "type": "OpenAIModel" + }, + "dragging": false, + "height": 571, + "id": "OpenAIModel-Neuec", + "position": { + "x": 2561.5850334731617, + "y": 553.2745131130916 + }, + "positionAbsolute": { + "x": 2561.5850334731617, + "y": 553.2745131130916 + }, + "selected": false, + "type": "genericNode", + "width": 384 } - }, - "type": "TextOutput" - }, - "dragging": false, - "height": 289, - "id": "TextOutput-KuLNK", - "position": { - "x": 1911.4785906252087, - "y": 247.39079954376987 - }, - "positionAbsolute": { - "x": 1911.4785906252087, - "y": 247.39079954376987 - }, - "selected": false, - "type": "genericNode", - "width": 384 - } - ], - "viewport": { - "x": -569.862554459756, - "y": -42.08339711050985, - "zoom": 0.4868590524514978 - } - }, - "description": "This project can be used as a starting point for building a Chat experience with user specific memory. You can set a different Session ID to start a new message history.", - "icon": "🤖", - "icon_bg_color": "#FFD700", - "id": "edee50d3-a7e7-4cc7-9448-cc662300e8fb", - "is_component": false, - "last_tested_version": "1.0.0a52", - "name": "Memory Chatbot" + ], + "edges": [ + { + "className": "", + "data": { + "sourceHandle": { + "baseClasses": [ + "str", + "object", + "Text" + ], + "dataType": "OpenAIModel", + "id": "OpenAIModel-Neuec" + }, + "targetHandle": { + "fieldName": "input_value", + "id": "ChatOutput-cVR7W", + "inputTypes": [ + "Text" + ], + "type": "str" + } + }, + "id": "reactflow__edge-OpenAIModel-Neuec{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Neuecœ}-ChatOutput-cVR7W{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-cVR7Wœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", + "source": "OpenAIModel-Neuec", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Neuecœ}", + "style": { + "stroke": "#555" + }, + "target": "ChatOutput-cVR7W", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-cVR7Wœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" + }, + { + "source": "Prompt-kykM2", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-kykM2œ}", + "target": "OpenAIModel-Neuec", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Neuecœ,œinputTypesœ:[œTextœ,œRecordœ,œPromptœ],œtypeœ:œstrœ}", + "data": { + "targetHandle": { + "fieldName": "input_value", + "id": "OpenAIModel-Neuec", + "inputTypes": [ + "Text", + "Record", + "Prompt" + ], + "type": "str" + }, + "sourceHandle": { + "baseClasses": [ + "object", + "str", + "Text" + ], + "dataType": "Prompt", + "id": "Prompt-kykM2" + } + }, + "id": "reactflow__edge-Prompt-kykM2{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-kykM2œ}-OpenAIModel-Neuec{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Neuecœ,œinputTypesœ:[œTextœ,œRecordœ,œPromptœ],œtypeœ:œstrœ}", + "className": "" + }, + { + "source": "ChatInput-Z9Rn6", + "sourceHandle": "{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-Z9Rn6œ}", + "target": "Prompt-kykM2", + "targetHandle": "{œfieldNameœ:œUserMessageœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "data": { + "targetHandle": { + "fieldName": "UserMessage", + "id": "Prompt-kykM2", + "inputTypes": [ + "Document", + "Message", + "Record", + "Text" + ], + "type": "str" + }, + "sourceHandle": { + "baseClasses": [ + "Text", + "object", + "Record", + "str" + ], + "dataType": "ChatInput", + "id": "ChatInput-Z9Rn6" + } + }, + "id": "reactflow__edge-ChatInput-Z9Rn6{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-Z9Rn6œ}-Prompt-kykM2{œfieldNameœ:œUserMessageœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" + }, + { + "source": "MemoryComponent-u6m5G", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-u6m5Gœ}", + "target": "Prompt-kykM2", + "targetHandle": "{œfieldNameœ:œContextœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "data": { + "targetHandle": { + "fieldName": "Context", + "id": "Prompt-kykM2", + "inputTypes": [ + "Document", + "Message", + "Record", + "Text" + ], + "type": "str" + }, + "sourceHandle": { + "baseClasses": [ + "str", + "Text", + "object" + ], + "dataType": "MemoryComponent", + "id": "MemoryComponent-u6m5G" + } + }, + "id": "reactflow__edge-MemoryComponent-u6m5G{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-u6m5Gœ}-Prompt-kykM2{œfieldNameœ:œContextœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" + } + ], + "viewport": { + "x": -511.79726701119625, + "y": 48.514712353620894, + "zoom": 0.4612356948928673 + } + }, + "description": "This project can be used as a starting point for building a Chat experience with user specific memory. You can set a different Session ID to start a new message history.", + "name": "Memory Chatbot", + "last_tested_version": "1.0.0a54", + "is_component": false } \ No newline at end of file From 90798a91ea3c15a42c4fd8eb895e5673356101e6 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 12:13:27 -0300 Subject: [PATCH 28/68] Format code --- src/frontend/src/App.tsx | 6 +-- .../components/parameterComponent/index.tsx | 22 ++++---- .../tooltipRenderComponent/index.tsx | 2 +- .../src/CustomNodes/GenericNode/index.tsx | 30 +++++------ .../helpers/get-class-from-build-status.ts | 2 +- .../hooks/use-check-code-validity.tsx | 2 +- .../hooks/use-fetch-data-on-mount.tsx | 2 +- .../hooks/use-handle-new-value.tsx | 2 +- .../hooks/use-handle-node-class.tsx | 2 +- .../src/CustomNodes/hooks/use-icon-render.tsx | 4 +- .../CustomNodes/hooks/use-icons-status.tsx | 4 +- .../hooks/use-update-node-code.tsx | 4 +- .../src/CustomNodes/utils/get-field-title.tsx | 2 +- .../src/alerts/alertDropDown/index.tsx | 6 +-- .../src/components/ImageViewer/index.tsx | 14 ++--- .../folderAccordionComponent/index.tsx | 2 +- .../components/accordionComponent/index.tsx | 4 +- .../addNewVariableButton.tsx | 6 +-- .../components/dragCardComponent/index.tsx | 4 +- .../src/components/cardComponent/index.tsx | 18 +++---- .../components/cardsWrapComponent/index.tsx | 2 +- .../src/components/chatComponent/index.tsx | 8 +-- .../components/codeAreaComponent/index.tsx | 2 +- .../components/csvOutputComponent/index.tsx | 2 +- .../src/components/dictComponent/index.tsx | 2 +- .../components/dropdownComponent/index.tsx | 4 +- .../editFlowSettingsComponent/index.tsx | 2 +- .../components/genericIconComponent/index.tsx | 6 +-- .../components/menuBar/index.tsx | 4 +- .../src/components/headerComponent/index.tsx | 6 +-- .../horizontalScrollFadeComponent/index.tsx | 4 +- .../components/popover/index.tsx | 16 +++--- .../components/popoverObject/index.tsx | 24 ++++----- .../src/components/inputComponent/index.tsx | 12 ++--- .../components/inputFileComponent/index.tsx | 4 +- .../components/inputGlobalComponent/index.tsx | 8 +-- .../components/inputListComponent/index.tsx | 2 +- .../components/sideBarButtons/index.tsx | 2 +- .../components/sideBarFolderButtons/index.tsx | 18 +++---- .../hooks/use-on-file-drop.tsx | 10 ++-- .../components/TableOptions/index.tsx | 4 +- .../components/tableAutoCellRender/index.tsx | 2 +- .../components/tableNodeCellRender/index.tsx | 6 +-- .../src/components/tableComponent/index.tsx | 11 ++-- .../tagsSelectorComponent/index.tsx | 2 +- src/frontend/src/components/ui/accordion.tsx | 6 +-- src/frontend/src/components/ui/alert.tsx | 2 +- src/frontend/src/components/ui/badge.tsx | 2 +- src/frontend/src/components/ui/button.tsx | 6 +-- src/frontend/src/components/ui/card.tsx | 4 +- src/frontend/src/components/ui/checkbox.tsx | 4 +- .../src/components/ui/custom-accordion.tsx | 4 +- src/frontend/src/components/ui/form.tsx | 8 +-- .../src/components/ui/select-custom.tsx | 6 +-- src/frontend/src/components/ui/toggle.tsx | 2 +- src/frontend/src/components/ui/tooltip.tsx | 4 +- src/frontend/src/contexts/authContext.tsx | 6 +-- src/frontend/src/controllers/API/api.tsx | 10 ++-- .../API/helpers/check-duplicate-requests.ts | 2 +- src/frontend/src/controllers/API/index.ts | 52 +++++++++---------- src/frontend/src/icons/Groq/index.tsx | 2 +- src/frontend/src/icons/Streamlit/index.tsx | 2 +- src/frontend/src/index.tsx | 4 +- .../BundleModal/hooks/submit-folder.tsx | 4 +- .../components/csvSelect/index.tsx | 2 +- .../IOModal/components/IOFieldView/index.tsx | 4 +- .../components/SessionView/hooks/index.tsx | 2 +- .../IOModal/components/SessionView/index.tsx | 2 +- .../components/buttonSendWrapper/index.tsx | 4 +- .../components/textAreaWrapper/index.tsx | 6 +-- .../chatInput/hooks/use-drag-and-drop.tsx | 2 +- .../hooks/use-handle-file-change.tsx | 2 +- .../components/chatView/chatInput/index.tsx | 2 +- .../components/chatView/chatMessage/index.tsx | 16 +++--- .../chatView/fileComponent/index.tsx | 2 +- .../fileComponent/utils/handle-download.tsx | 4 +- .../utils/format-file-name.tsx | 2 +- .../IOModal/components/chatView/index.tsx | 6 +-- src/frontend/src/modals/IOModal/index.tsx | 40 +++++++------- src/frontend/src/modals/apiModal/index.tsx | 18 +++---- .../utils/check-can-build-tweak-object.ts | 2 +- .../apiModal/utils/get-changes-types.ts | 2 +- .../modals/apiModal/utils/get-curl-code.tsx | 6 +-- .../utils/get-nodes-with-default-value.ts | 4 +- .../apiModal/utils/get-python-api-code.tsx | 2 +- .../modals/apiModal/utils/get-python-code.tsx | 2 +- .../src/modals/apiModal/utils/get-value.ts | 2 +- .../modals/apiModal/utils/get-widget-code.tsx | 2 +- .../src/modals/apiModal/utils/tabs-array.tsx | 2 +- src/frontend/src/modals/baseModal/index.tsx | 14 ++--- .../src/modals/dictAreaModal/index.tsx | 2 +- .../editNodeModal/hooks/use-column-defs.tsx | 4 +- .../src/modals/editNodeModal/index.tsx | 9 ++-- src/frontend/src/modals/exportModal/index.tsx | 6 +-- .../src/modals/flowLogsModal/index.tsx | 4 +- .../foldersModal/hooks/submit-folder.tsx | 4 +- .../src/modals/genericModal/index.tsx | 6 +-- .../src/modals/newFlowModal/index.tsx | 2 +- src/frontend/src/modals/shareModal/index.tsx | 6 +-- .../modals/shareModal/utils/get-tags-ids.tsx | 2 +- .../components/PageComponent/index.tsx | 36 ++++++------- .../PageComponent/utils/get-random-name.tsx | 2 +- .../SelectionMenuComponent/index.tsx | 2 +- .../extraSidebarComponent/index.tsx | 28 +++++----- .../components/nodeToolbarComponent/index.tsx | 34 ++++++------ .../toolbarSelectItem/index.tsx | 2 +- src/frontend/src/pages/FlowPage/index.tsx | 2 +- .../hooks/use-delete-multiple.tsx | 2 +- .../hooks/use-filtered-flows.tsx | 4 +- .../hooks/use-handle-duplicate.tsx | 8 +-- .../hooks/use-handle-export.tsx | 4 +- .../hooks/use-handle-select-all.tsx | 2 +- .../hooks/use-select-options-change.tsx | 4 +- .../hooks/use-selected-flows.tsx | 2 +- .../components/componentsComponent/index.tsx | 22 ++++---- .../components/headerComponent/index.tsx | 2 +- .../headerTabsSearchComponent/index.tsx | 2 +- .../components/inputSearchComponent/index.tsx | 2 +- .../pages/MainPage/pages/mainPage/index.tsx | 2 +- .../src/pages/MainPage/services/index.ts | 12 ++--- .../MainPage/utils/handle-download-folder.ts | 2 +- src/frontend/src/pages/Playground/index.tsx | 2 +- .../src/pages/ProfileSettingsPage/index.tsx | 4 +- src/frontend/src/pages/SettingsPage/index.tsx | 2 +- .../hooks/use-handle-delete-key.tsx | 2 +- .../SettingsPage/pages/ApiKeysPage/index.tsx | 4 +- .../components/PasswordForm/index.tsx | 2 +- .../hooks/use-preload-images.tsx | 10 ++-- .../profilePictureChooserComponent/index.tsx | 2 +- .../components/StoreApiKeyForm/index.tsx | 4 +- .../SettingsPage/pages/GeneralPage/index.tsx | 10 ++-- .../pages/GlobalVariablesPage/index.tsx | 6 +-- .../EditShortcutButton/index.tsx | 10 ++-- .../pages/ShortcutsPage/index.tsx | 2 +- .../pages/hooks/use-patch-profile-picture.tsx | 2 +- .../SettingsPage/pages/hooks/use-save-key.tsx | 4 +- .../hooks/use-remove-messages.tsx | 2 +- .../SettingsPage/pages/messagesPage/index.tsx | 4 +- src/frontend/src/pages/StorePage/index.tsx | 6 +-- src/frontend/src/routes.tsx | 10 ++-- src/frontend/src/stores/darkStore.ts | 2 +- src/frontend/src/stores/flowStore.ts | 50 +++++++++--------- src/frontend/src/stores/flowsManagerStore.ts | 28 +++++----- src/frontend/src/stores/foldersStore.tsx | 10 ++-- .../globalVariablesStore/globalVariables.ts | 2 +- src/frontend/src/stores/messagesStore.ts | 6 +-- src/frontend/src/types/components/index.ts | 8 +-- src/frontend/src/types/store/index.ts | 2 +- src/frontend/src/types/zustand/flow/index.ts | 14 ++--- .../src/types/zustand/flowsManager/index.ts | 8 +-- .../types/zustand/globalVariables/index.ts | 2 +- src/frontend/src/utils/buildUtils.ts | 18 +++---- src/frontend/src/utils/parameterUtils.ts | 4 +- src/frontend/src/utils/storeUtils.ts | 2 +- src/frontend/src/utils/utils.ts | 20 +++---- .../tests/end-to-end/chatInputOutput.spec.ts | 6 +-- .../end-to-end/chatInputOutputUser.spec.ts | 8 +-- .../tests/end-to-end/dragAndDrop.spec.ts | 4 +- .../end-to-end/dropdownComponent.spec.ts | 28 +++++----- .../end-to-end/fileUploadComponent.spec.ts | 4 +- .../tests/end-to-end/filterEdge.spec.ts | 28 +++++----- .../tests/end-to-end/floatComponent.spec.ts | 20 +++---- .../tests/end-to-end/flowSettings.spec.ts | 4 +- src/frontend/tests/end-to-end/folders.spec.ts | 4 +- .../tests/end-to-end/inputComponent.spec.ts | 28 +++++----- .../end-to-end/inputListComponent.spec.ts | 18 +++---- .../end-to-end/keyPairListComponent.spec.ts | 4 +- .../end-to-end/langflowShortcuts.spec.ts | 4 +- .../tests/end-to-end/nestedComponent.spec.ts | 42 +++++++-------- src/frontend/tests/end-to-end/store.spec.ts | 2 +- .../end-to-end/textAreaModalComponent.spec.ts | 2 +- .../tests/end-to-end/toggleComponent.spec.ts | 34 ++++++------ 172 files changed, 652 insertions(+), 656 deletions(-) diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 04d48103b..849244999 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -29,10 +29,10 @@ export default function App() { useTrackLastVisitedPath(); const removeFromTempNotificationList = useAlertStore( - (state) => state.removeFromTempNotificationList, + (state) => state.removeFromTempNotificationList ); const tempNotificationList = useAlertStore( - (state) => state.tempNotificationList, + (state) => state.tempNotificationList ); const [fetchError, setFetchError] = useState(false); const isLoading = useFlowsManagerStore((state) => state.isLoading); @@ -50,7 +50,7 @@ export default function App() { const refreshVersion = useDarkStore((state) => state.refreshVersion); const refreshStars = useDarkStore((state) => state.refreshStars); const setGlobalVariables = useGlobalVariablesStore( - (state) => state.setGlobalVariables, + (state) => state.setGlobalVariables ); const checkHasStore = useStoreStore((state) => state.checkHasStore); const navigate = useNavigate(); diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 99b28b7bf..8b76fc243 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -1,5 +1,6 @@ import { cloneDeep } from "lodash"; import { ReactNode, useEffect, useRef, useState } from "react"; +import { useHotkeys } from "react-hotkeys-hook"; import { Handle, Position, useUpdateNodeInternals } from "reactflow"; import CodeAreaComponent from "../../../../components/codeAreaComponent"; import DictComponent from "../../../../components/dictComponent"; @@ -24,6 +25,7 @@ import { import { Case } from "../../../../shared/components/caseComponent"; import useFlowStore from "../../../../stores/flowStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; +import { useShortcutsStore } from "../../../../stores/shortcuts"; import { useTypesStore } from "../../../../stores/typesStore"; import { APIClassType } from "../../../../types/api"; import { ParameterComponentType } from "../../../../types/components"; @@ -48,11 +50,9 @@ import useFetchDataOnMount from "../../../hooks/use-fetch-data-on-mount"; import useHandleOnNewValue from "../../../hooks/use-handle-new-value"; import useHandleNodeClass from "../../../hooks/use-handle-node-class"; import useHandleRefreshButtonPress from "../../../hooks/use-handle-refresh-buttons"; +import OutputModal from "../outputModal"; import TooltipRenderComponent from "../tooltipRenderComponent"; import { TEXT_FIELD_TYPES } from "./constants"; -import OutputModal from "../outputModal"; -import { useShortcutsStore } from "../../../../stores/shortcuts"; -import { useHotkeys } from "react-hotkeys-hook"; export default function ParameterComponent({ left, @@ -121,7 +121,7 @@ export default function ParameterComponent({ debouncedHandleUpdateValues, setNode, renderTooltips, - setIsLoading, + setIsLoading ); const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass( @@ -130,7 +130,7 @@ export default function ParameterComponent({ takeSnapshot, setNode, updateNodeInternals, - renderTooltips, + renderTooltips ); const { handleRefreshButtonPress: handleRefreshButtonPressHook } = @@ -139,7 +139,7 @@ export default function ParameterComponent({ let disabled = edges.some( (edge) => - edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id), + edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id) ) ?? false; const handleRefreshButtonPress = async (name, data) => { @@ -152,12 +152,12 @@ export default function ParameterComponent({ handleUpdateValues, setNode, renderTooltips, - setIsLoading, + setIsLoading ); const handleOnNewValue = async ( newValue: string | string[] | boolean | Object[], - skipSnapshot: boolean | undefined = false, + skipSnapshot: boolean | undefined = false ): Promise => { handleOnNewValueHook(newValue, skipSnapshot); }; @@ -239,7 +239,7 @@ export default function ParameterComponent({ className={classNames( left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ", "h-3 w-3 rounded-full border-2 bg-background", - !showNode ? "mt-0" : "", + !showNode ? "mt-0" : "" )} style={{ borderColor: color ?? nodeColors.unknown, @@ -309,7 +309,7 @@ export default function ParameterComponent({ "h-5 w-5 rounded-md", displayOutputPreview && !unknownOutput ? " hover:bg-secondary-foreground/5 hover:text-medium-indigo" - : " cursor-not-allowed text-muted-foreground", + : " cursor-not-allowed text-muted-foreground" )} name={"ScanEye"} /> @@ -359,7 +359,7 @@ export default function ParameterComponent({ } className={classNames( left ? "-ml-0.5" : "-mr-0.5", - "h-3 w-3 rounded-full border-2 bg-background", + "h-3 w-3 rounded-full border-2 bg-background" )} style={{ borderColor: color ?? nodeColors.unknown }} onClick={() => setFilterEdge(groupedEdge.current)} diff --git a/src/frontend/src/CustomNodes/GenericNode/components/tooltipRenderComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/tooltipRenderComponent/index.tsx index ed2760161..c76bc7293 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/tooltipRenderComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/tooltipRenderComponent/index.tsx @@ -24,7 +24,7 @@ const TooltipRenderComponent = ({ item, index, left }) => { 0 ? "mt-2 flex items-center" : "mt-3 flex items-center", + index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center" )} >
          state.setErrorData); const isDark = useDarkStore((state) => state.dark); const buildStatus = useFlowStore( - (state) => state.flowBuildStatus[data.id]?.status, + (state) => state.flowBuildStatus[data.id]?.status ); const lastRunTime = useFlowStore( - (state) => state.flowBuildStatus[data.id]?.timestamp, + (state) => state.flowBuildStatus[data.id]?.timestamp ); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); @@ -72,7 +72,7 @@ export default function GenericNode({ const [nodeName, setNodeName] = useState(data.node!.display_name); const [inputDescription, setInputDescription] = useState(false); const [nodeDescription, setNodeDescription] = useState( - data.node?.description!, + data.node?.description! ); const [isOutdated, setIsOutdated] = useState(false); const [validationStatus, setValidationStatus] = @@ -90,7 +90,7 @@ export default function GenericNode({ data.node!, setNode, setIsOutdated, - updateNodeInternals, + updateNodeInternals ); const name = nodeIconsLucide[data.type] ? data.type : types[data.type]; @@ -117,12 +117,12 @@ export default function GenericNode({ selected: boolean, showNode: boolean, buildStatus: BuildStatus | undefined, - validationStatus: VertexBuildTypeAPI | null, + validationStatus: VertexBuildTypeAPI | null ) => { const specificClassFromBuildStatus = getSpecificClassFromBuildStatus( buildStatus, validationStatus, - isDark, + isDark ); const baseBorderClass = getBaseBorderClass(selected); @@ -131,7 +131,7 @@ export default function GenericNode({ baseBorderClass, nodeSizeClass, "generic-node-div group/node", - specificClassFromBuildStatus, + specificClassFromBuildStatus ); return names; }; @@ -176,7 +176,7 @@ export default function GenericNode({ showNode, isEmoji, nodeIconFragment, - checkNodeIconFragment, + checkNodeIconFragment ); function countHandles(): void { @@ -309,7 +309,7 @@ export default function GenericNode({ selected, showNode, buildStatus, - validationStatus, + validationStatus )} > {data.node?.beta && showNode && ( @@ -457,7 +457,7 @@ export default function GenericNode({ } title={getFieldTitle( data.node?.template!, - templateField, + templateField )} info={data.node?.template[templateField].info} name={templateField} @@ -485,7 +485,7 @@ export default function GenericNode({ proxy={data.node?.template[templateField].proxy} showNode={showNode} /> - ), + ) )} { setInputDescription(true); @@ -713,13 +713,13 @@ export default function GenericNode({ } title={getFieldTitle( data.node?.template!, - templateField, + templateField )} info={data.node?.template[templateField].info} name={templateField} tooltipTitle={ data.node?.template[templateField].input_types?.join( - "\n", + "\n" ) ?? data.node?.template[templateField].type } required={data.node!.template[templateField].required} @@ -746,7 +746,7 @@ export default function GenericNode({
          {" "} diff --git a/src/frontend/src/CustomNodes/helpers/get-class-from-build-status.ts b/src/frontend/src/CustomNodes/helpers/get-class-from-build-status.ts index 710e91d15..cf251c40c 100644 --- a/src/frontend/src/CustomNodes/helpers/get-class-from-build-status.ts +++ b/src/frontend/src/CustomNodes/helpers/get-class-from-build-status.ts @@ -4,7 +4,7 @@ import { VertexBuildTypeAPI } from "../../types/api"; export const getSpecificClassFromBuildStatus = ( buildStatus: BuildStatus | undefined, validationStatus: VertexBuildTypeAPI | null, - isDark: boolean, + isDark: boolean ) => { let isInvalid = validationStatus && !validationStatus.valid; diff --git a/src/frontend/src/CustomNodes/hooks/use-check-code-validity.tsx b/src/frontend/src/CustomNodes/hooks/use-check-code-validity.tsx index ec4d586f6..3a49ef62f 100644 --- a/src/frontend/src/CustomNodes/hooks/use-check-code-validity.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-check-code-validity.tsx @@ -6,7 +6,7 @@ const useCheckCodeValidity = ( data: NodeDataType, templates: { [key: string]: any }, setIsOutdated: (value: boolean) => void, - types, + types ) => { useEffect(() => { // This one should run only once diff --git a/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx b/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx index f203a059a..d8545d72a 100644 --- a/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx @@ -13,7 +13,7 @@ const useFetchDataOnMount = ( handleUpdateValues, setNode, renderTooltips, - setIsLoading, + setIsLoading ) => { const setErrorData = useAlertStore((state) => state.setErrorData); diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx b/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx index 7be08491c..b41491f97 100644 --- a/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx @@ -14,7 +14,7 @@ const useHandleOnNewValue = ( debouncedHandleUpdateValues, setNode, renderTooltips, - setIsLoading, + setIsLoading ) => { const setErrorData = useAlertStore((state) => state.setErrorData); diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx b/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx index 412658d77..bdcf1f8cc 100644 --- a/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx @@ -6,7 +6,7 @@ const useHandleNodeClass = ( takeSnapshot, setNode, updateNodeInternals, - renderTooltips, + renderTooltips ) => { const handleNodeClass = (newNodeClass, code) => { if (!data.node) return; diff --git a/src/frontend/src/CustomNodes/hooks/use-icon-render.tsx b/src/frontend/src/CustomNodes/hooks/use-icon-render.tsx index 181b4f515..cc9e29c0e 100644 --- a/src/frontend/src/CustomNodes/hooks/use-icon-render.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-icon-render.tsx @@ -12,8 +12,8 @@ const useIconNodeRender = ( checkNodeIconFragment: ( iconColor: string, iconName: string, - iconClassName: string, - ) => JSX.Element, + iconClassName: string + ) => JSX.Element ) => { const iconNodeRender = useCallback(() => { const iconElement = data?.node?.icon; diff --git a/src/frontend/src/CustomNodes/hooks/use-icons-status.tsx b/src/frontend/src/CustomNodes/hooks/use-icons-status.tsx index 42e8b64b3..19c6112d5 100644 --- a/src/frontend/src/CustomNodes/hooks/use-icons-status.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-icons-status.tsx @@ -1,14 +1,12 @@ -import ForwardedIconComponent from "../../components/genericIconComponent"; import Checkmark from "../../components/ui/checkmark"; import Loading from "../../components/ui/loading"; import Xmark from "../../components/ui/xmark"; import { BuildStatus } from "../../constants/enums"; import { VertexBuildTypeAPI } from "../../types/api"; -import { cn } from "../../utils/utils"; const useIconStatus = ( buildStatus: BuildStatus | undefined, - validationStatus: VertexBuildTypeAPI | null, + validationStatus: VertexBuildTypeAPI | null ) => { const conditionSuccess = validationStatus && validationStatus.valid; const conditionError = diff --git a/src/frontend/src/CustomNodes/hooks/use-update-node-code.tsx b/src/frontend/src/CustomNodes/hooks/use-update-node-code.tsx index f1593597f..d919a4fa9 100644 --- a/src/frontend/src/CustomNodes/hooks/use-update-node-code.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-update-node-code.tsx @@ -7,7 +7,7 @@ const useUpdateNodeCode = ( dataNode: APIClassType, // Define YourNodeType according to your data structure setNode: (id: string, callback: (oldNode) => any) => void, setIsOutdated: (value: boolean) => void, - updateNodeInternals: (id: string) => void, + updateNodeInternals: (id: string) => void ) => { const updateNodeCode = useCallback( (newNodeClass: APIClassType, code: string, name: string) => { @@ -30,7 +30,7 @@ const useUpdateNodeCode = ( updateNodeInternals(dataId); }, - [dataId, dataNode, setNode, setIsOutdated, updateNodeInternals], + [dataId, dataNode, setNode, setIsOutdated, updateNodeInternals] ); return updateNodeCode; diff --git a/src/frontend/src/CustomNodes/utils/get-field-title.tsx b/src/frontend/src/CustomNodes/utils/get-field-title.tsx index e448c4f01..a00829a90 100644 --- a/src/frontend/src/CustomNodes/utils/get-field-title.tsx +++ b/src/frontend/src/CustomNodes/utils/get-field-title.tsx @@ -2,7 +2,7 @@ import { APITemplateType } from "../../types/api"; export default function getFieldTitle( template: APITemplateType, - templateField: string, + templateField: string ): string { return template[templateField].display_name ? template[templateField].display_name! diff --git a/src/frontend/src/alerts/alertDropDown/index.tsx b/src/frontend/src/alerts/alertDropDown/index.tsx index 05f42922d..6eff32fe2 100644 --- a/src/frontend/src/alerts/alertDropDown/index.tsx +++ b/src/frontend/src/alerts/alertDropDown/index.tsx @@ -16,13 +16,13 @@ export default function AlertDropdown({ }: AlertDropdownType): JSX.Element { const notificationList = useAlertStore((state) => state.notificationList); const clearNotificationList = useAlertStore( - (state) => state.clearNotificationList, + (state) => state.clearNotificationList ); const removeFromNotificationList = useAlertStore( - (state) => state.removeFromNotificationList, + (state) => state.removeFromNotificationList ); const setNotificationCenter = useAlertStore( - (state) => state.setNotificationCenter, + (state) => state.setNotificationCenter ); const [open, setOpen] = useState(false); diff --git a/src/frontend/src/components/ImageViewer/index.tsx b/src/frontend/src/components/ImageViewer/index.tsx index 8433962a7..dc7f41ad7 100644 --- a/src/frontend/src/components/ImageViewer/index.tsx +++ b/src/frontend/src/components/ImageViewer/index.tsx @@ -31,14 +31,14 @@ export default function ImageViewer({ image }) { const fullPageButton = document.getElementById("full-page-button"); zoomInButton!.addEventListener("click", () => - viewer.viewport.zoomBy(1.2), + viewer.viewport.zoomBy(1.2) ); zoomOutButton!.addEventListener("click", () => - viewer.viewport.zoomBy(0.8), + viewer.viewport.zoomBy(0.8) ); homeButton!.addEventListener("click", () => viewer.viewport.goHome()); fullPageButton!.addEventListener("click", () => - viewer.setFullScreen(true), + viewer.setFullScreen(true) ); // Optionally, you can set additional viewer options here @@ -47,16 +47,16 @@ export default function ImageViewer({ image }) { return () => { viewer.destroy(); zoomInButton!.removeEventListener("click", () => - viewer.viewport.zoomBy(1.2), + viewer.viewport.zoomBy(1.2) ); zoomOutButton!.removeEventListener("click", () => - viewer.viewport.zoomBy(0.8), + viewer.viewport.zoomBy(0.8) ); homeButton!.removeEventListener("click", () => - viewer.viewport.goHome(), + viewer.viewport.goHome() ); fullPageButton!.removeEventListener("click", () => - viewer.setFullScreen(true), + viewer.setFullScreen(true) ); }; } diff --git a/src/frontend/src/components/accordionComponent/composite/folderAccordionComponent/index.tsx b/src/frontend/src/components/accordionComponent/composite/folderAccordionComponent/index.tsx index d4fb95b5c..212a03fa2 100644 --- a/src/frontend/src/components/accordionComponent/composite/folderAccordionComponent/index.tsx +++ b/src/frontend/src/components/accordionComponent/composite/folderAccordionComponent/index.tsx @@ -15,7 +15,7 @@ export default function FolderAccordionComponent({ options, }: AccordionComponentType): JSX.Element { const [value, setValue] = useState( - open.length === 0 ? "" : getOpenAccordion(), + open.length === 0 ? "" : getOpenAccordion() ); function getOpenAccordion(): string { diff --git a/src/frontend/src/components/accordionComponent/index.tsx b/src/frontend/src/components/accordionComponent/index.tsx index c9c21b8b2..43a0aef79 100644 --- a/src/frontend/src/components/accordionComponent/index.tsx +++ b/src/frontend/src/components/accordionComponent/index.tsx @@ -17,7 +17,7 @@ export default function AccordionComponent({ sideBar, }: AccordionComponentType): JSX.Element { const [value, setValue] = useState( - open.length === 0 ? "" : getOpenAccordion(), + open.length === 0 ? "" : getOpenAccordion() ); function getOpenAccordion(): string { @@ -52,7 +52,7 @@ export default function AccordionComponent({ disabled={disabled} className={cn( sideBar ? "w-full bg-muted px-[0.75rem] py-[0.5rem]" : "ml-3", - disabled ? "cursor-not-allowed" : "cursor-pointer", + disabled ? "cursor-not-allowed" : "cursor-pointer" )} > {trigger} diff --git a/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx b/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx index ae23c34ac..d75bf625c 100644 --- a/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx +++ b/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx @@ -29,19 +29,19 @@ export default function AddNewVariableButton({ const setErrorData = useAlertStore((state) => state.setErrorData); const componentFields = useTypesStore((state) => state.ComponentFields); const unavaliableFields = new Set( - Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields)), + Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields)) ); const availableFields = () => { const fields = Array.from(componentFields).filter( - (field) => !unavaliableFields.has(field), + (field) => !unavaliableFields.has(field) ); return sortByName(fields); }; const addGlobalVariable = useGlobalVariablesStore( - (state) => state.addGlobalVariable, + (state) => state.addGlobalVariable ); function handleSaveVariable() { diff --git a/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx b/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx index 28674f3bc..54dbf4846 100644 --- a/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx @@ -10,7 +10,7 @@ export default function DragCardComponent({ data }: { data: storeComponent }) { draggable //TODO check color schema className={cn( - "group relative flex flex-col justify-between overflow-hidden transition-all hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#ffffff10]", + "group relative flex flex-col justify-between overflow-hidden transition-all hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#ffffff10]" )} >
          @@ -22,7 +22,7 @@ export default function DragCardComponent({ data }: { data: storeComponent }) { "visible flex-shrink-0", data.is_component ? "mx-0.5 h-6 w-6 text-component-icon" - : "h-7 w-7 flex-shrink-0 text-flow-icon", + : "h-7 w-7 flex-shrink-0 text-flow-icon" )} name={data.is_component ? "ToyBrick" : "Group"} /> diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index d169a598a..a364a2d45 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -60,11 +60,11 @@ export default function CollectionCardComponent({ const [loading, setLoading] = useState(false); const [loadingLike, setLoadingLike] = useState(false); const [liked_by_user, setLiked_by_user] = useState( - data?.liked_by_user ?? false, + data?.liked_by_user ?? false ); const [likes_count, setLikes_count] = useState(data?.liked_by_count ?? 0); const [downloads_count, setDownloads_count] = useState( - data?.downloads_count ?? 0, + data?.downloads_count ?? 0 ); const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); @@ -75,12 +75,12 @@ export default function CollectionCardComponent({ const [openPlayground, setOpenPlayground] = useState(false); const [openDelete, setOpenDelete] = useState(false); const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId, + (state) => state.setCurrentFlowId ); const [loadingPlayground, setLoadingPlayground] = useState(false); const selectedFlowsComponentsCards = useFlowsManagerStore( - (state) => state.selectedFlowsComponentsCards, + (state) => state.selectedFlowsComponentsCards ); const name = data.is_component ? "Component" : "Flow"; @@ -220,7 +220,7 @@ export default function CollectionCardComponent({ "group relative flex h-[11rem] flex-col justify-between overflow-hidden hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#5f5f5f0e]", disabled ? "pointer-events-none opacity-50" : "", onClick ? "cursor-pointer" : "", - isSelectedCard ? "border border-selected" : "", + isSelectedCard ? "border border-selected" : "" )} onClick={onClick} > @@ -233,7 +233,7 @@ export default function CollectionCardComponent({ "visible flex-shrink-0", data.is_component ? "mx-0.5 h-6 w-6 text-component-icon" - : "h-7 w-7 flex-shrink-0 text-flow-icon", + : "h-7 w-7 flex-shrink-0 text-flow-icon" )} name={data.is_component ? "ToyBrick" : "Group"} /> @@ -428,7 +428,7 @@ export default function CollectionCardComponent({ name="Trash2" className={cn( "h-5 w-5", - !authorized ? " text-ring" : "", + !authorized ? " text-ring" : "" )} /> @@ -463,7 +463,7 @@ export default function CollectionCardComponent({ liked_by_user ? "fill-destructive stroke-destructive" : "", - !authorized ? " text-ring" : "", + !authorized ? " text-ring" : "" )} /> @@ -501,7 +501,7 @@ export default function CollectionCardComponent({ } className={cn( loading ? "h-5 w-5 animate-spin" : "h-5 w-5", - !authorized ? " text-ring" : "", + !authorized ? " text-ring" : "" )} /> diff --git a/src/frontend/src/components/cardsWrapComponent/index.tsx b/src/frontend/src/components/cardsWrapComponent/index.tsx index c7ca01588..0de3f1a2f 100644 --- a/src/frontend/src/components/cardsWrapComponent/index.tsx +++ b/src/frontend/src/components/cardsWrapComponent/index.tsx @@ -65,7 +65,7 @@ export default function CardsWrapComponent({ "h-full w-full", isDragging ? "mb-36 flex flex-col items-center justify-center gap-4 text-2xl font-light" - : "", + : "" )} > {isDragging ? ( diff --git a/src/frontend/src/components/chatComponent/index.tsx b/src/frontend/src/components/chatComponent/index.tsx index fab9a88d7..3040f685c 100644 --- a/src/frontend/src/components/chatComponent/index.tsx +++ b/src/frontend/src/components/chatComponent/index.tsx @@ -65,7 +65,7 @@ export default function FlowToolbar(): JSX.Element { "relative inline-flex h-full w-full items-center justify-center gap-[4px] bg-muted px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-background hover:bg-hover ", !hasApiKey || !validApiKey || !hasStore ? " button-disable text-muted-foreground " - : "", + : "" )} > Share @@ -88,7 +88,7 @@ export default function FlowToolbar(): JSX.Element { hasStore, openShareModal, setOpenShareModal, - ], + ] ); return ( @@ -144,7 +144,7 @@ export default function FlowToolbar(): JSX.Element { >
          { if (disabled && myValue !== "") { diff --git a/src/frontend/src/components/csvOutputComponent/index.tsx b/src/frontend/src/components/csvOutputComponent/index.tsx index 0b9a9d76e..1d4e342de 100644 --- a/src/frontend/src/components/csvOutputComponent/index.tsx +++ b/src/frontend/src/components/csvOutputComponent/index.tsx @@ -67,7 +67,7 @@ function CsvOutputComponent({ if (file) { const { rowData: data, colDefs: columns } = convertCSVToData( file, - separator, + separator ); setRowData(data); setColDefs(columns); diff --git a/src/frontend/src/components/dictComponent/index.tsx b/src/frontend/src/components/dictComponent/index.tsx index 057ba599d..b0340bf1a 100644 --- a/src/frontend/src/components/dictComponent/index.tsx +++ b/src/frontend/src/components/dictComponent/index.tsx @@ -24,7 +24,7 @@ export default function DictComponent({
          1 && editNode ? "my-1" : "", - "flex w-full flex-col gap-3", + "flex w-full flex-col gap-3" )} > { diff --git a/src/frontend/src/components/dropdownComponent/index.tsx b/src/frontend/src/components/dropdownComponent/index.tsx index 4aa9dcf07..0fd981603 100644 --- a/src/frontend/src/components/dropdownComponent/index.tsx +++ b/src/frontend/src/components/dropdownComponent/index.tsx @@ -58,7 +58,7 @@ export default function Dropdown({ ? "dropdown-component-outline" : "dropdown-component-false-outline", "w-full justify-between font-normal", - editNode ? "input-edit-node" : "py-2", + editNode ? "input-edit-node" : "py-2" )} > @@ -106,7 +106,7 @@ export default function Dropdown({ name="Check" className={cn( "ml-auto h-4 w-4 text-primary", - value === option ? "opacity-100" : "opacity-0", + value === option ? "opacity-100" : "opacity-0" )} /> diff --git a/src/frontend/src/components/editFlowSettingsComponent/index.tsx b/src/frontend/src/components/editFlowSettingsComponent/index.tsx index 26bc138e3..3dd813965 100644 --- a/src/frontend/src/components/editFlowSettingsComponent/index.tsx +++ b/src/frontend/src/components/editFlowSettingsComponent/index.tsx @@ -99,7 +99,7 @@ export const EditFlowSettings: React.FC = ({ {description === "" ? "No description" : description} diff --git a/src/frontend/src/components/genericIconComponent/index.tsx b/src/frontend/src/components/genericIconComponent/index.tsx index 9f7c687a1..398ae6e7f 100644 --- a/src/frontend/src/components/genericIconComponent/index.tsx +++ b/src/frontend/src/components/genericIconComponent/index.tsx @@ -18,7 +18,7 @@ export const ForwardedIconComponent = memo( strokeWidth, id = "", }: IconComponentProps, - ref, + ref ) => { const [showFallback, setShowFallback] = useState(false); @@ -65,8 +65,8 @@ export const ForwardedIconComponent = memo( /> ); - }, - ), + } + ) ); export default ForwardedIconComponent; diff --git a/src/frontend/src/components/headerComponent/components/menuBar/index.tsx b/src/frontend/src/components/headerComponent/components/menuBar/index.tsx index 4965d37a6..83511c769 100644 --- a/src/frontend/src/components/headerComponent/components/menuBar/index.tsx +++ b/src/frontend/src/components/headerComponent/components/menuBar/index.tsx @@ -115,7 +115,7 @@ export const MenuBar = ({}: {}): JSX.Element => { title: UPLOAD_ERROR_ALERT, list: [error], }); - }, + } ); }} > @@ -201,7 +201,7 @@ export const MenuBar = ({}: {}): JSX.Element => { name={isBuilding || saveLoading ? "Loader2" : "CheckCircle2"} className={cn( "h-4 w-4", - isBuilding || saveLoading ? "animate-spin" : "animate-wiggle", + isBuilding || saveLoading ? "animate-spin" : "animate-wiggle" )} /> {printByBuildStatus()} diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx index ef6b8938d..9b87335be 100644 --- a/src/frontend/src/components/headerComponent/index.tsx +++ b/src/frontend/src/components/headerComponent/index.tsx @@ -59,7 +59,7 @@ export default function Header(): JSX.Element { const lastFlowVisitedIndex = routeHistory .reverse() .findIndex( - (path) => path.includes("/flow/") && path !== location.pathname, + (path) => path.includes("/flow/") && path !== location.pathname ); const lastFlowVisited = routeHistory[lastFlowVisitedIndex]; @@ -201,7 +201,7 @@ export default function Header(): JSX.Element { src={ `${BACKEND_URL.slice( 0, - BACKEND_URL.length - 1, + BACKEND_URL.length - 1 )}${BASE_URL_API}files/profile_pictures/${ userData?.profile_image ?? "Space/046-rocket.svg" }` ?? profileCircle @@ -219,7 +219,7 @@ export default function Header(): JSX.Element { src={ `${BACKEND_URL.slice( 0, - BACKEND_URL.length - 1, + BACKEND_URL.length - 1 )}${BASE_URL_API}files/profile_pictures/${ userData?.profile_image ?? "Space/046-rocket.svg" }` ?? profileCircle diff --git a/src/frontend/src/components/horizontalScrollFadeComponent/index.tsx b/src/frontend/src/components/horizontalScrollFadeComponent/index.tsx index e0bf48917..7e0c788a6 100644 --- a/src/frontend/src/components/horizontalScrollFadeComponent/index.tsx +++ b/src/frontend/src/components/horizontalScrollFadeComponent/index.tsx @@ -32,11 +32,11 @@ export default function HorizontalScrollFadeComponent({ fadeContainerRef.current.classList.toggle( "fade-left", - isScrollable && !atStart, + isScrollable && !atStart ); fadeContainerRef.current.classList.toggle( "fade-right", - isScrollable && !atEnd, + isScrollable && !atEnd ); }; diff --git a/src/frontend/src/components/inputComponent/components/popover/index.tsx b/src/frontend/src/components/inputComponent/components/popover/index.tsx index e93f69207..06be89d91 100644 --- a/src/frontend/src/components/inputComponent/components/popover/index.tsx +++ b/src/frontend/src/components/inputComponent/components/popover/index.tsx @@ -75,9 +75,9 @@ const CustomInputPopover = ({ (selectedOption !== "" || !onChange) && setSelectedOption ? selectedOption : (selectedOptions?.length !== 0 || !onChange) && - setSelectedOptions - ? selectedOptions?.join(", ") - : value + setSelectedOptions + ? selectedOptions?.join(", ") + : value } autoFocus={autoFocus} disabled={disabled} @@ -103,7 +103,7 @@ const CustomInputPopover = ({ (password && !(setSelectedOption || setSelectedOptions)) ? "pr-8" : "", - className!, + className! )} placeholder={password && editNode ? "Key" : placeholder} onChange={handleInputChange} @@ -141,15 +141,15 @@ const CustomInputPopover = ({ onSelect={(currentValue) => { setSelectedOption && setSelectedOption( - currentValue === selectedOption ? "" : currentValue, + currentValue === selectedOption ? "" : currentValue ); setSelectedOptions && setSelectedOptions( selectedOptions?.includes(currentValue) ? selectedOptions.filter( - (item) => item !== currentValue, + (item) => item !== currentValue ) - : [...selectedOptions, currentValue], + : [...selectedOptions, currentValue] ); !setSelectedOptions && setShowOptions(false); }} @@ -162,7 +162,7 @@ const CustomInputPopover = ({ selectedOption === option || selectedOptions?.includes(option) ? "opacity-100" - : "opacity-0", + : "opacity-0" )} >
          diff --git a/src/frontend/src/components/inputComponent/components/popoverObject/index.tsx b/src/frontend/src/components/inputComponent/components/popoverObject/index.tsx index e907ec239..d43022845 100644 --- a/src/frontend/src/components/inputComponent/components/popoverObject/index.tsx +++ b/src/frontend/src/components/inputComponent/components/popoverObject/index.tsx @@ -60,14 +60,14 @@ const CustomInputPopoverObject = ({ ? options.find((option) => option.id === selectedOption)?.name || "" : (selectedOptions?.length !== 0 || !onChange) && - setSelectedOptions - ? selectedOptions - .map( - (optionId) => - options.find((option) => option.id === optionId)?.name, - ) - .join(", ") - : value + setSelectedOptions + ? selectedOptions + .map( + (optionId) => + options.find((option) => option.id === optionId)?.name + ) + .join(", ") + : value } autoFocus={autoFocus} disabled={disabled} @@ -115,15 +115,15 @@ const CustomInputPopoverObject = ({ onSelect={(currentValue) => { setSelectedOption && setSelectedOption( - currentValue === selectedOption ? "" : currentValue, + currentValue === selectedOption ? "" : currentValue ); setSelectedOptions && setSelectedOptions( selectedOptions?.includes(currentValue) ? selectedOptions.filter( - (item) => item !== currentValue, + (item) => item !== currentValue ) - : [...selectedOptions, currentValue], + : [...selectedOptions, currentValue] ); !setSelectedOptions && setShowOptions(false); }} @@ -136,7 +136,7 @@ const CustomInputPopoverObject = ({ selectedOption === option.id || selectedOptions?.includes(option.id) ? "opacity-100" - : "opacity-0", + : "opacity-0" )} >
          diff --git a/src/frontend/src/components/inputComponent/index.tsx b/src/frontend/src/components/inputComponent/index.tsx index c4a2453e2..c9f9a4170 100644 --- a/src/frontend/src/components/inputComponent/index.tsx +++ b/src/frontend/src/components/inputComponent/index.tsx @@ -72,7 +72,7 @@ export default function InputComponent({ editNode ? " input-edit-node " : "", password && editNode ? "pr-8" : "", password && !editNode ? "pr-10" : "", - className!, + className! )} placeholder={password && editNode ? "Key" : placeholder} onChange={(e) => { @@ -155,7 +155,7 @@ export default function InputComponent({ @@ -85,7 +85,7 @@ export default function TableOptions({ name="Trash2" className={cn( "h-5 w-5 text-primary transition-all", - !hasSelection ? "" : "hover:text-status-red ", + !hasSelection ? "" : "hover:text-status-red " )} /> diff --git a/src/frontend/src/components/tableComponent/components/tableAutoCellRender/index.tsx b/src/frontend/src/components/tableComponent/components/tableAutoCellRender/index.tsx index 903068c84..dd886e4fa 100644 --- a/src/frontend/src/components/tableComponent/components/tableAutoCellRender/index.tsx +++ b/src/frontend/src/components/tableComponent/components/tableAutoCellRender/index.tsx @@ -30,7 +30,7 @@ export default function TableAutoCellRender({ variant="outline" size="sq" className={cn( - "min-w-min bg-success-background text-success-foreground hover:bg-success-background", + "min-w-min bg-success-background text-success-foreground hover:bg-success-background" )} > {value} diff --git a/src/frontend/src/components/tableComponent/components/tableNodeCellRender/index.tsx b/src/frontend/src/components/tableComponent/components/tableNodeCellRender/index.tsx index fb24786f7..925848d82 100644 --- a/src/frontend/src/components/tableComponent/components/tableNodeCellRender/index.tsx +++ b/src/frontend/src/components/tableComponent/components/tableNodeCellRender/index.tsx @@ -72,8 +72,8 @@ export default function TableNodeCellRender({ ...id, proxy: templateData.proxy, } - : id, - ), + : id + ) ) ?? false; function getCellType() { switch (templateData.type) { @@ -144,7 +144,7 @@ export default function TableNodeCellRender({
          1 ? "my-3" : "", + templateValue?.length > 1 ? "my-3" : "" )} > ; @@ -36,7 +35,7 @@ const TableComponent = forwardRef< alertDescription = DEFAULT_TABLE_ALERT_MSG, ...props }, - ref, + ref ) => { let colDef = props.columnDefs.map((col, index) => { let newCol = { @@ -112,7 +111,7 @@ const TableComponent = forwardRef< }; const onColumnMoved = (params) => { const updatedColumnDefs = makeLastColumnNonResizable( - params.columnApi.getAllGridColumns().map((col) => col.getColDef()), + params.columnApi.getAllGridColumns().map((col) => col.getColDef()) ); params.api.setGridOption("columnDefs", updatedColumnDefs); if (props.onColumnMoved) props.onColumnMoved(params); @@ -136,7 +135,7 @@ const TableComponent = forwardRef< className={cn( dark ? "ag-theme-quartz-dark" : "ag-theme-quartz", "ag-theme-shadcn flex h-full flex-col", - "relative", + "relative" )} // applying the grid theme > source.includes("column"))) { localStorage.setItem( storeReference, - JSON.stringify(realRef.current?.api?.getColumnState()), + JSON.stringify(realRef.current?.api?.getColumnState()) ); setColumnStateChange(true); } @@ -176,7 +175,7 @@ const TableComponent = forwardRef< )}
          ); - }, + } ); export default TableComponent; diff --git a/src/frontend/src/components/tagsSelectorComponent/index.tsx b/src/frontend/src/components/tagsSelectorComponent/index.tsx index 4e3e181fc..6f84ae7c3 100644 --- a/src/frontend/src/components/tagsSelectorComponent/index.tsx +++ b/src/frontend/src/components/tagsSelectorComponent/index.tsx @@ -48,7 +48,7 @@ export function TagsSelector({ className={cn( selectedTags.some((category) => category === tag.name) ? "min-w-min bg-beta-foreground text-background hover:bg-beta-foreground" - : "", + : "" )} > {tag.name} diff --git a/src/frontend/src/components/ui/accordion.tsx b/src/frontend/src/components/ui/accordion.tsx index 39578095b..403b07996 100644 --- a/src/frontend/src/components/ui/accordion.tsx +++ b/src/frontend/src/components/ui/accordion.tsx @@ -34,7 +34,7 @@ const AccordionTrigger = React.forwardRef<
          svg]:rotate-180", - className, + className )} > {children} @@ -46,7 +46,7 @@ const AccordionTrigger = React.forwardRef< @@ -64,7 +64,7 @@ const AccordionContent = React.forwardRef< ref={ref} className={cn( "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm", - className, + className )} {...props} > diff --git a/src/frontend/src/components/ui/alert.tsx b/src/frontend/src/components/ui/alert.tsx index 05c5b7fa7..a584cea26 100644 --- a/src/frontend/src/components/ui/alert.tsx +++ b/src/frontend/src/components/ui/alert.tsx @@ -15,7 +15,7 @@ const alertVariants = cva( defaultVariants: { variant: "default", }, - }, + } ); const Alert = React.forwardRef< diff --git a/src/frontend/src/components/ui/badge.tsx b/src/frontend/src/components/ui/badge.tsx index 6eea7971a..8f8d1f6aa 100644 --- a/src/frontend/src/components/ui/badge.tsx +++ b/src/frontend/src/components/ui/badge.tsx @@ -27,7 +27,7 @@ const badgeVariants = cva( defaultVariants: { variant: "default", }, - }, + } ); export interface BadgeProps diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index ec3265bea..76f2316a0 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -35,7 +35,7 @@ const buttonVariants = cva( variant: "default", size: "default", }, - }, + } ); export interface ButtonProps @@ -65,7 +65,7 @@ const Button = React.forwardRef( children, ...props }, - ref, + ref ) => { const Comp = asChild ? Slot : "button"; let newChildren = children; @@ -97,7 +97,7 @@ const Button = React.forwardRef( ); - }, + } ); Button.displayName = "Button"; diff --git a/src/frontend/src/components/ui/card.tsx b/src/frontend/src/components/ui/card.tsx index a558fb645..d986c5530 100644 --- a/src/frontend/src/components/ui/card.tsx +++ b/src/frontend/src/components/ui/card.tsx @@ -9,7 +9,7 @@ const Card = React.forwardRef< ref={ref} className={cn( "flex flex-col justify-between rounded-lg border bg-muted text-card-foreground shadow-sm transition-all", - className, + className )} {...props} /> @@ -36,7 +36,7 @@ const CardTitle = React.forwardRef< ref={ref} className={cn( "text-base font-semibold leading-tight tracking-tight", - className, + className )} {...props} /> diff --git a/src/frontend/src/components/ui/checkbox.tsx b/src/frontend/src/components/ui/checkbox.tsx index 1c3d1e4fe..2c48fde84 100644 --- a/src/frontend/src/components/ui/checkbox.tsx +++ b/src/frontend/src/components/ui/checkbox.tsx @@ -13,7 +13,7 @@ const Checkbox = React.forwardRef< ref={ref} className={cn( "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground", - className, + className )} {...props} > @@ -37,7 +37,7 @@ const CheckBoxDiv = ({ className={cn( className, "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", - checked ? "bg-primary text-primary-foreground" : "", + checked ? "bg-primary text-primary-foreground" : "" )} > {checked && ( diff --git a/src/frontend/src/components/ui/custom-accordion.tsx b/src/frontend/src/components/ui/custom-accordion.tsx index 507649ca5..9ce39b8a7 100644 --- a/src/frontend/src/components/ui/custom-accordion.tsx +++ b/src/frontend/src/components/ui/custom-accordion.tsx @@ -24,7 +24,7 @@ const AccordionTrigger = React.forwardRef<
          svg]:rotate-180", - className, + className )} > {children} @@ -43,7 +43,7 @@ const AccordionContent = React.forwardRef< ref={ref} className={cn( "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden border-[1px] text-sm data-[state=open]:rounded-b-md data-[state=open]:border-t-0 data-[state=open]:bg-muted", - className, + className )} {...props} > diff --git a/src/frontend/src/components/ui/form.tsx b/src/frontend/src/components/ui/form.tsx index 69789b6f3..dcfd39449 100644 --- a/src/frontend/src/components/ui/form.tsx +++ b/src/frontend/src/components/ui/form.tsx @@ -16,18 +16,18 @@ const Form = FormProvider; type FormFieldContextValue< TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath, + TName extends FieldPath = FieldPath > = { name: TName; }; const FormFieldContext = React.createContext( - {} as FormFieldContextValue, + {} as FormFieldContextValue ); const FormField = < TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath, + TName extends FieldPath = FieldPath >({ ...props }: ControllerProps) => { @@ -66,7 +66,7 @@ type FormItemContextValue = { }; const FormItemContext = React.createContext( - {} as FormItemContextValue, + {} as FormItemContextValue ); const FormItem = React.forwardRef< diff --git a/src/frontend/src/components/ui/select-custom.tsx b/src/frontend/src/components/ui/select-custom.tsx index 82ba540fe..86a7bf3ac 100644 --- a/src/frontend/src/components/ui/select-custom.tsx +++ b/src/frontend/src/components/ui/select-custom.tsx @@ -36,7 +36,7 @@ const SelectContent = React.forwardRef< "relative z-50 min-w-[14rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", - className, + className )} position={position} {...props} @@ -45,7 +45,7 @@ const SelectContent = React.forwardRef< className={cn( "p-1", position === "popper" && - "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]", + "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]" )} > {children} @@ -75,7 +75,7 @@ const SelectItem = React.forwardRef< ref={ref} className={cn( "relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-3 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", - className, + className )} {...props} > diff --git a/src/frontend/src/components/ui/toggle.tsx b/src/frontend/src/components/ui/toggle.tsx index f41840dce..08ebb1380 100644 --- a/src/frontend/src/components/ui/toggle.tsx +++ b/src/frontend/src/components/ui/toggle.tsx @@ -25,7 +25,7 @@ const toggleVariants = cva( variant: "default", size: "default", }, - }, + } ); const Toggle = React.forwardRef< diff --git a/src/frontend/src/components/ui/tooltip.tsx b/src/frontend/src/components/ui/tooltip.tsx index 3ee8b9c7a..7f81bc3bd 100644 --- a/src/frontend/src/components/ui/tooltip.tsx +++ b/src/frontend/src/components/ui/tooltip.tsx @@ -20,7 +20,7 @@ const TooltipContent = React.forwardRef< sideOffset={sideOffset} className={cn( "z-45 overflow-y-auto rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", - className, + className )} {...props} /> @@ -37,7 +37,7 @@ const TooltipContentWithoutPortal = React.forwardRef< sideOffset={sideOffset} className={cn( "z-45 overflow-y-auto rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", - className, + className )} {...props} /> diff --git a/src/frontend/src/contexts/authContext.tsx b/src/frontend/src/contexts/authContext.tsx index b37816571..1817447f1 100644 --- a/src/frontend/src/contexts/authContext.tsx +++ b/src/frontend/src/contexts/authContext.tsx @@ -30,17 +30,17 @@ export function AuthProvider({ children }): React.ReactElement { const navigate = useNavigate(); const cookies = new Cookies(); const [accessToken, setAccessToken] = useState( - cookies.get("access_token_lf") ?? null, + cookies.get("access_token_lf") ?? null ); const [isAuthenticated, setIsAuthenticated] = useState( - !!cookies.get("access_token_lf"), + !!cookies.get("access_token_lf") ); const [isAdmin, setIsAdmin] = useState(false); const [userData, setUserData] = useState(null); const [autoLogin, setAutoLogin] = useState(false); const setLoading = useAlertStore((state) => state.setLoading); const [apiKey, setApiKey] = useState( - cookies.get("apikey_tkn_lflw"), + cookies.get("apikey_tkn_lflw") ); // const getFoldersApi = useFolderStore((state) => state.getFoldersApi); diff --git a/src/frontend/src/controllers/API/api.tsx b/src/frontend/src/controllers/API/api.tsx index b8035e74e..2a305cf5a 100644 --- a/src/frontend/src/controllers/API/api.tsx +++ b/src/frontend/src/controllers/API/api.tsx @@ -48,7 +48,7 @@ function ApiInterceptor() { } await clearBuildVerticesState(error); return Promise.reject(error); - }, + } ); const isAuthorizedURL = (url) => { @@ -65,10 +65,10 @@ function ApiInterceptor() { const parsedURL = new URL(url); const isDomainAllowed = authorizedDomains.some( - (domain) => parsedURL.origin === new URL(domain).origin, + (domain) => parsedURL.origin === new URL(domain).origin ); const isEndpointAllowed = authorizedEndpoints.some((endpoint) => - parsedURL.pathname.includes(endpoint), + parsedURL.pathname.includes(endpoint) ); return isDomainAllowed || isEndpointAllowed; @@ -101,7 +101,7 @@ function ApiInterceptor() { }, (error) => { return Promise.reject(error); - }, + } ); return () => { @@ -133,7 +133,7 @@ function ApiInterceptor() { if (error?.config?.headers) { delete error.config.headers["Authorization"]; error.config.headers["Authorization"] = `Bearer ${cookies.get( - "access_token_lf", + "access_token_lf" )}`; const response = await axios.request(error.config); return response; diff --git a/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts b/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts index 79a47c7a7..6486de541 100644 --- a/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts +++ b/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts @@ -8,7 +8,7 @@ export function checkDuplicateRequestAndStoreRequest(config) { const currentTime = Date.now(); const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) => - config?.url!.includes(request), + config?.url!.includes(request) ); if ( diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 91068832e..07a9cd06d 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -63,7 +63,7 @@ export async function sendAll(data: sendAllProps) { } export async function postValidateCode( - code: string, + code: string ): Promise> { return await api.post(`${BASE_URL_API}validate/code`, { code }); } @@ -78,7 +78,7 @@ export async function postValidateCode( export async function postValidatePrompt( name: string, template: string, - frontend_node: APIClassType, + frontend_node: APIClassType ): Promise> { return api.post(`${BASE_URL_API}validate/prompt`, { name, @@ -151,7 +151,7 @@ export async function saveFlowToDatabase(newFlow: { * @throws Will throw an error if the update fails. */ export async function updateFlowInDatabase( - updatedFlow: FlowType, + updatedFlow: FlowType ): Promise { try { const response = await api.patch(`${BASE_URL_API}flows/${updatedFlow.id}`, { @@ -329,7 +329,7 @@ export async function getHealth() { * */ export async function getBuildStatus( - flowId: string, + flowId: string ): Promise> { return await api.get(`${BASE_URL_API}build/${flowId}/status`); } @@ -342,7 +342,7 @@ export async function getBuildStatus( * */ export async function postBuildInit( - flow: FlowType, + flow: FlowType ): Promise> { return await api.post(`${BASE_URL_API}build/init/${flow.id}`, flow); } @@ -358,7 +358,7 @@ export async function postBuildInit( */ export async function uploadFile( file: File, - id: string, + id: string ): Promise> { const formData = new FormData(); formData.append("file", file); @@ -380,7 +380,7 @@ export async function getProfilePictures(): Promise> { // let template = apiClass.template; return await api.post(`${BASE_URL_API}custom_component`, { @@ -393,7 +393,7 @@ export async function postCustomComponentUpdate( code: string, template: APITemplateType, field: string, - field_value: any, + field_value: any ): Promise> { return await api.post(`${BASE_URL_API}custom_component/update`, { code, @@ -415,7 +415,7 @@ export async function onLogin(user: LoginType) { headers: { "Content-Type": "application/x-www-form-urlencoded", }, - }, + } ); if (response.status === 200) { @@ -477,11 +477,11 @@ export async function addUser(user: UserInputType): Promise> { export async function getUsersPage( skip: number, - limit: number, + limit: number ): Promise> { try { const res = await api.get( - `${BASE_URL_API}users/?skip=${skip}&limit=${limit}`, + `${BASE_URL_API}users/?skip=${skip}&limit=${limit}` ); if (res.status === 200) { return res.data; @@ -518,7 +518,7 @@ export async function resetPassword(user_id: string, user: resetPasswordType) { try { const res = await api.patch( `${BASE_URL_API}users/${user_id}/reset-password`, - user, + user ); if (res.status === 200) { return res.data; @@ -592,7 +592,7 @@ export async function saveFlowStore( last_tested_version?: string; }, tags: string[], - publicFlow = false, + publicFlow = false ): Promise { try { const response = await api.post(`${BASE_URL_API}store/components/`, { @@ -721,7 +721,7 @@ export async function postStoreComponents(component: Component) { export async function getComponent(component_id: string) { try { const res = await api.get( - `${BASE_URL_API}store/components/${component_id}`, + `${BASE_URL_API}store/components/${component_id}` ); if (res.status === 200) { return res.data; @@ -736,7 +736,7 @@ export async function searchComponent( page?: number | null, limit?: number | null, status?: string | null, - tags?: string[], + tags?: string[] ): Promise { try { let url = `${BASE_URL_API}store/components/`; @@ -848,7 +848,7 @@ export async function updateFlowStore( }, tags: string[], publicFlow = false, - id: string, + id: string ): Promise { try { const response = await api.patch(`${BASE_URL_API}store/components/${id}`, { @@ -932,7 +932,7 @@ export async function deleteGlobalVariable(id: string) { export async function updateGlobalVariable( name: string, value: string, - id: string, + id: string ) { try { const response = api.patch(`${BASE_URL_API}variables/${id}`, { @@ -951,7 +951,7 @@ export async function getVerticesOrder( startNodeId?: string | null, stopNodeId?: string | null, nodes?: Node[], - Edges?: Edge[], + Edges?: Edge[] ): Promise> { // nodeId is optional and is a query parameter // if nodeId is not provided, the API will return all vertices @@ -971,7 +971,7 @@ export async function getVerticesOrder( return await api.post( `${BASE_URL_API}build/${flowId}/vertices`, data, - config, + config ); } @@ -979,7 +979,7 @@ export async function postBuildVertex( flowId: string, vertexId: string, input_value: string, - files?: string[], + files?: string[] ): Promise> { // input_value is optional and is a query parameter let data = {}; @@ -991,7 +991,7 @@ export async function postBuildVertex( } return await api.post( `${BASE_URL_API}build/${flowId}/vertices/${vertexId}`, - data, + data ); } @@ -1015,7 +1015,7 @@ export async function getFlowPool({ } export async function deleteFlowPool( - flowId: string, + flowId: string ): Promise> { const config = {}; config["params"] = { flow_id: flowId }; @@ -1029,7 +1029,7 @@ export async function deleteFlowPool( * @returns A promise that resolves to an array of AxiosResponse objects representing the delete responses. */ export async function multipleDeleteFlowsComponents( - flowIds: string[], + flowIds: string[] ): Promise[]> { const batches: string[][] = []; @@ -1052,7 +1052,7 @@ export async function multipleDeleteFlowsComponents( // Execute all delete requests const responses: Promise>[] = batches.map((batch) => - deleteBatch(batch), + deleteBatch(batch) ); // Return the responses after all requests are completed @@ -1062,7 +1062,7 @@ export async function multipleDeleteFlowsComponents( export async function getTransactionTable( id: string, mode: "intersection" | "union", - params = {}, + params = {} ): Promise<{ rows: Array; columns: Array }> { const config = {}; config["params"] = { flow_id: id }; @@ -1078,7 +1078,7 @@ export async function getMessagesTable( mode: "intersection" | "union", id?: string, excludedFields?: string[], - params = {}, + params = {} ): Promise<{ rows: Array; columns: Array }> { const config = {}; if (id) { diff --git a/src/frontend/src/icons/Groq/index.tsx b/src/frontend/src/icons/Groq/index.tsx index 6c4283d29..291696154 100644 --- a/src/frontend/src/icons/Groq/index.tsx +++ b/src/frontend/src/icons/Groq/index.tsx @@ -4,5 +4,5 @@ import SvgGroqLogo from "./GroqLogo"; export const GroqIcon = forwardRef>( (props, ref) => { return ; - }, + } ); diff --git a/src/frontend/src/icons/Streamlit/index.tsx b/src/frontend/src/icons/Streamlit/index.tsx index 1a4c55119..6b3391021 100644 --- a/src/frontend/src/icons/Streamlit/index.tsx +++ b/src/frontend/src/icons/Streamlit/index.tsx @@ -4,5 +4,5 @@ import SvgStreamlit from "./SvgStreamlit"; export const Streamlit = forwardRef>( (props, ref) => { return ; - }, + } ); diff --git a/src/frontend/src/index.tsx b/src/frontend/src/index.tsx index 006d93ff2..78a57298f 100644 --- a/src/frontend/src/index.tsx +++ b/src/frontend/src/index.tsx @@ -11,11 +11,11 @@ import "./style/applies.css"; import "./style/classes.css"; const root = ReactDOM.createRoot( - document.getElementById("root") as HTMLElement, + document.getElementById("root") as HTMLElement ); root.render( - , + ); reportWebVitals(); diff --git a/src/frontend/src/modals/BundleModal/hooks/submit-folder.tsx b/src/frontend/src/modals/BundleModal/hooks/submit-folder.tsx index cb12ca7cb..1347a323b 100644 --- a/src/frontend/src/modals/BundleModal/hooks/submit-folder.tsx +++ b/src/frontend/src/modals/BundleModal/hooks/submit-folder.tsx @@ -27,7 +27,7 @@ const useFolderSubmit = (setOpen, folderToEdit) => { getFoldersApi(true); setOpen(false); } - }, + } ); } else { addFolder(data).then( @@ -42,7 +42,7 @@ const useFolderSubmit = (setOpen, folderToEdit) => { setErrorData({ title: `Error creating folder.`, }); - }, + } ); } }; diff --git a/src/frontend/src/modals/IOModal/components/IOFieldView/components/csvSelect/index.tsx b/src/frontend/src/modals/IOModal/components/IOFieldView/components/csvSelect/index.tsx index 5adb156ef..1438237b7 100644 --- a/src/frontend/src/modals/IOModal/components/IOFieldView/components/csvSelect/index.tsx +++ b/src/frontend/src/modals/IOModal/components/IOFieldView/components/csvSelect/index.tsx @@ -29,7 +29,7 @@ export default function CsvSelect({ node, handleChangeSelect }): JSX.Element { {separator} - ), + ) )} diff --git a/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx b/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx index 20bae05c6..1ee08100e 100644 --- a/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx @@ -50,7 +50,7 @@ export default function IOFieldView({ .results.result ?? ""; console.log( - (flowPool[node!.id] ?? [])[(flowPool[node!.id]?.length ?? 1) - 1]?.data, + (flowPool[node!.id] ?? [])[(flowPool[node!.id]?.length ?? 1) - 1]?.data ); function handleOutputType() { @@ -257,7 +257,7 @@ export default function IOFieldView({ rows={ Array.isArray(flowPoolNode?.data?.artifacts) ? flowPoolNode?.data?.artifacts?.map( - (artifact) => artifact.data, + (artifact) => artifact.data ) ?? [] : [flowPoolNode?.data?.artifacts] } diff --git a/src/frontend/src/modals/IOModal/components/SessionView/hooks/index.tsx b/src/frontend/src/modals/IOModal/components/SessionView/hooks/index.tsx index e8e638def..738ea9977 100644 --- a/src/frontend/src/modals/IOModal/components/SessionView/hooks/index.tsx +++ b/src/frontend/src/modals/IOModal/components/SessionView/hooks/index.tsx @@ -10,7 +10,7 @@ const useRemoveSession = (setSuccessData, setErrorData) => { await deleteMessagesFn( messages .filter((msg) => msg.session_id === session_id) - .map((msg) => msg.index), + .map((msg) => msg.index) ); deleteSession(session_id); setSuccessData({ diff --git a/src/frontend/src/modals/IOModal/components/SessionView/index.tsx b/src/frontend/src/modals/IOModal/components/SessionView/index.tsx index edabbe252..a7fc91b03 100644 --- a/src/frontend/src/modals/IOModal/components/SessionView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/SessionView/index.tsx @@ -17,7 +17,7 @@ export default function SessionView({ rows }: { rows: Array }) { setSelectedRows, setSuccessData, setErrorData, - selectedRows, + selectedRows ); const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData); diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/buttonSendWrapper/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/buttonSendWrapper/index.tsx index bf61b2fb9..bf0600a5d 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/buttonSendWrapper/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/buttonSendWrapper/index.tsx @@ -28,8 +28,8 @@ const ButtonSendWrapper = ({ noInput ? "bg-high-indigo text-background" : chatValue === "" - ? "text-primary" - : "bg-chat-send text-background", + ? "text-primary" + : "bg-chat-send text-background" )} disabled={lockChat || saveLoading} onClick={(): void => send()} diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/textAreaWrapper/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/textAreaWrapper/index.tsx index 29f88453a..3d41e51f4 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/textAreaWrapper/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/textAreaWrapper/index.tsx @@ -18,7 +18,7 @@ const TextAreaWrapper = ({ }) => { const getPlaceholderText = ( isDragging: boolean, - noInput: boolean, + noInput: boolean ): string => { if (isDragging) { return "Drop here"; @@ -33,8 +33,8 @@ const TextAreaWrapper = ({ lockChat || saveLoading ? "form-modal-lock-true bg-input" : noInput - ? "form-modal-no-input bg-input" - : "form-modal-lock-false bg-background"; + ? "form-modal-no-input bg-input" + : "form-modal-lock-false bg-background"; const fileClass = files.length > 0 diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatInput/hooks/use-drag-and-drop.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatInput/hooks/use-drag-and-drop.tsx index c85617ab2..141754cbd 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatInput/hooks/use-drag-and-drop.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatInput/hooks/use-drag-and-drop.tsx @@ -9,7 +9,7 @@ const useDragAndDrop = ( setIsDragging, setFiles, currentFlowId, - setErrorData, + setErrorData ) => { const dragOver = (e) => { e.preventDefault(); diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatInput/hooks/use-handle-file-change.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatInput/hooks/use-handle-file-change.tsx index 6316bf0c0..060481cd5 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatInput/hooks/use-handle-file-change.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatInput/hooks/use-handle-file-change.tsx @@ -9,7 +9,7 @@ const snErrorTxt = "png, jpg, jpeg"; export const useHandleFileChange = (setFiles, currentFlowId) => { const setErrorData = useAlertStore((state) => state.setErrorData); const handleFileChange = async ( - event: React.ChangeEvent, + event: React.ChangeEvent ) => { const fileInput = event.target; const file = fileInput.files?.[0]; diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatInput/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatInput/index.tsx index a3c070a5f..4311f0ed2 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatInput/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatInput/index.tsx @@ -116,7 +116,7 @@ export default function ChatInput({ key={file.id} onDelete={() => { setFiles((prev: FilePreviewType[]) => - prev.filter((f) => f.id !== file.id), + prev.filter((f) => f.id !== file.id) ); // TODO: delete file on backend }} diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx index 1d4b11667..d6451d1ae 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx @@ -115,19 +115,19 @@ export default function ChatMessage({
          ), - [chat.message, chatMessage], + [chat.message, chatMessage] )}
          @@ -292,7 +292,7 @@ dark:prose-invert" parts.push( {chat.message[match[1]]} - , + ); } diff --git a/src/frontend/src/modals/IOModal/components/chatView/fileComponent/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/fileComponent/index.tsx index a034b1c40..7b56cdcc0 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/fileComponent/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/fileComponent/index.tsx @@ -29,7 +29,7 @@ export default function FileCard({ const imgSrc = `${BACKEND_URL.slice( 0, - BACKEND_URL.length - 1, + BACKEND_URL.length - 1 )}${BASE_URL_API}files/images/${content}`; console.log(imgSrc); diff --git a/src/frontend/src/modals/IOModal/components/chatView/fileComponent/utils/handle-download.tsx b/src/frontend/src/modals/IOModal/components/chatView/fileComponent/utils/handle-download.tsx index a4be91d50..e82bf3167 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/fileComponent/utils/handle-download.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/fileComponent/utils/handle-download.tsx @@ -20,8 +20,8 @@ export default async function handleDownload({ const response = await fetch( `${BACKEND_URL.slice( 0, - BACKEND_URL.length - 1, - )}${BASE_URL_API}files/download/${content}`, + BACKEND_URL.length - 1 + )}${BASE_URL_API}files/download/${content}` ); if (!response.ok) { throw new Error("Network response was not ok"); diff --git a/src/frontend/src/modals/IOModal/components/chatView/filePreviewChat/utils/format-file-name.tsx b/src/frontend/src/modals/IOModal/components/chatView/filePreviewChat/utils/format-file-name.tsx index 94258b6af..b474f8294 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/filePreviewChat/utils/format-file-name.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/filePreviewChat/utils/format-file-name.tsx @@ -1,6 +1,6 @@ export default function formatFileName( name: string, - numberToTruncate: number = 25, + numberToTruncate: number = 25 ): string { if (name[numberToTruncate] === undefined) { return name; diff --git a/src/frontend/src/modals/IOModal/components/chatView/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/index.tsx index 736d21a6f..d4d4c8080 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/index.tsx @@ -58,7 +58,7 @@ export default function ChatView({ // .filter( (output) => - output.data.message || (!output.data.message && output.artifacts), + output.data.message || (!output.data.message && output.artifacts) ) .map((output, index) => { try { @@ -139,7 +139,7 @@ export default function ChatView({ function updateChat( chat: ChatMessageType, message: string, - stream_url?: string, + stream_url?: string ) { chat.message = message; updateFlowPool(chat.componentId, { @@ -155,7 +155,7 @@ export default function ChatView({ setIsDragging, setFiles, currentFlowId, - setErrorData, + setErrorData ); return ( diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index 2730dceb9..f1dc14182 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -36,25 +36,25 @@ export default function IOModal({ const allNodes = useFlowStore((state) => state.nodes); const setMessages = useMessagesStore((state) => state.setMessages); const inputs = useFlowStore((state) => state.inputs).filter( - (input) => input.type !== "ChatInput", + (input) => input.type !== "ChatInput" ); const chatInput = useFlowStore((state) => state.inputs).find( - (input) => input.type === "ChatInput", + (input) => input.type === "ChatInput" ); const outputs = useFlowStore((state) => state.outputs).filter( - (output) => output.type !== "ChatOutput", + (output) => output.type !== "ChatOutput" ); const chatOutput = useFlowStore((state) => state.outputs).find( - (output) => output.type === "ChatOutput", + (output) => output.type === "ChatOutput" ); const nodes = useFlowStore((state) => state.nodes).filter( (node) => inputs.some((input) => input.id === node.id) || - outputs.some((output) => output.id === node.id), + outputs.some((output) => output.id === node.id) ); const haveChat = chatInput || chatOutput; const [selectedTab, setSelectedTab] = useState( - inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0, + inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0 ); const setErrorData = useAlertStore((state) => state.setErrorData); const setSuccessData = useAlertStore((state) => state.setSuccessData); @@ -131,7 +131,7 @@ export default function IOModal({ const { handleRemoveSession } = useRemoveSession( setSuccessData, - setErrorData, + setErrorData ); useEffect(() => { @@ -155,7 +155,7 @@ export default function IOModal({ ({ rows, columns }) => { setMessages(rows); setColumns(columns); - }, + } ); } }, [open]); @@ -194,7 +194,7 @@ export default function IOModal({
          {nodes .filter((node) => - inputs.some((input) => input.id === node.id), + inputs.some((input) => input.id === node.id) ) .map((node, index) => { const input = inputs.find( - (input) => input.id === node.id, + (input) => input.id === node.id )!; return (
          {nodes .filter((node) => - outputs.some((output) => output.id === node.id), + outputs.some((output) => output.id === node.id) ) .map((node, index) => { const output = outputs.find( - (output) => output.id === node.id, + (output) => output.id === node.id )!; const textOutputValue = (flowPool[node!.id] ?? [])[ @@ -439,7 +439,7 @@ export default function IOModal({
          @@ -458,7 +458,7 @@ export default function IOModal({
          {inputs.some( - (input) => input.id === selectedViewField.id, + (input) => input.id === selectedViewField.id ) && ( )} {outputs.some( - (output) => output.id === selectedViewField.id, + (output) => output.id === selectedViewField.id ) && ( )} {sessions.some( - (session) => session === selectedViewField.id, + (session) => session === selectedViewField.id ) && ( - message.session_id === selectedViewField.id, + message.session_id === selectedViewField.id )} /> )} @@ -493,7 +493,7 @@ export default function IOModal({
          {haveChat ? ( @@ -525,7 +525,7 @@ export default function IOModal({ "h-4 w-4", isBuilding ? "animate-spin" - : "fill-current text-medium-indigo", + : "fill-current text-medium-indigo" )} /> ), diff --git a/src/frontend/src/modals/apiModal/index.tsx b/src/frontend/src/modals/apiModal/index.tsx index ead038ad2..19f506459 100644 --- a/src/frontend/src/modals/apiModal/index.tsx +++ b/src/frontend/src/modals/apiModal/index.tsx @@ -39,7 +39,7 @@ const ApiModal = forwardRef( open?: boolean; setOpen?: (a: boolean | ((o?: boolean) => boolean)) => void; }, - ref, + ref ) => { const tweak = useTweaksStore((state) => state.tweak); const addTweaks = useTweaksStore((state) => state.setTweak); @@ -57,18 +57,18 @@ const ApiModal = forwardRef( flow?.id, autoLogin, tweak, - flow?.endpoint_name, + flow?.endpoint_name ); const curl_run_code = getCurlRunCode( flow?.id, autoLogin, tweak, - flow?.endpoint_name, + flow?.endpoint_name ); const curl_webhook_code = getCurlWebhookCode( flow?.id, autoLogin, - flow?.endpoint_name, + flow?.endpoint_name ); const pythonCode = getPythonCode(flow?.name, tweak); const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin); @@ -83,7 +83,7 @@ const ApiModal = forwardRef( pythonCode, ]; const [tabs, setTabs] = useState( - createTabsArray(codesArray, includeWebhook), + createTabsArray(codesArray, includeWebhook) ); const canShowTweaks = @@ -132,7 +132,7 @@ const ApiModal = forwardRef( buildTweakObject( nodeId, element.data.node.template[templateField].value, - element.data.node.template[templateField], + element.data.node.template[templateField] ); } }); @@ -149,7 +149,7 @@ const ApiModal = forwardRef( async function buildTweakObject( tw: string, changes: string | string[] | boolean | number | Object[] | Object, - template: TemplateVariableType, + template: TemplateVariableType ) { changes = getChangesType(changes, template); @@ -191,7 +191,7 @@ const ApiModal = forwardRef( flow?.id, autoLogin, cloneTweak, - flow?.endpoint_name, + flow?.endpoint_name ); const pythonCode = getPythonCode(flow?.name, cloneTweak); const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin); @@ -235,7 +235,7 @@ const ApiModal = forwardRef( ); - }, + } ); export default ApiModal; diff --git a/src/frontend/src/modals/apiModal/utils/check-can-build-tweak-object.ts b/src/frontend/src/modals/apiModal/utils/check-can-build-tweak-object.ts index 5a859cae8..592a812c4 100644 --- a/src/frontend/src/modals/apiModal/utils/check-can-build-tweak-object.ts +++ b/src/frontend/src/modals/apiModal/utils/check-can-build-tweak-object.ts @@ -6,7 +6,7 @@ export const checkCanBuildTweakObject = (element, templateField) => { templateField.charAt(0) !== "_" && element.data.node.template[templateField].show && LANGFLOW_SUPPORTED_TYPES.has( - element.data.node.template[templateField].type, + element.data.node.template[templateField].type ) && templateField !== "code" ); diff --git a/src/frontend/src/modals/apiModal/utils/get-changes-types.ts b/src/frontend/src/modals/apiModal/utils/get-changes-types.ts index e8e912ff3..7e295e358 100644 --- a/src/frontend/src/modals/apiModal/utils/get-changes-types.ts +++ b/src/frontend/src/modals/apiModal/utils/get-changes-types.ts @@ -3,7 +3,7 @@ import { convertArrayToObj } from "../../../utils/reactflowUtils"; export const getChangesType = ( changes: string | string[] | boolean | number | Object[] | Object, - template: TemplateVariableType, + template: TemplateVariableType ) => { if (typeof changes === "string" && template.type === "float") { changes = parseFloat(changes); diff --git a/src/frontend/src/modals/apiModal/utils/get-curl-code.tsx b/src/frontend/src/modals/apiModal/utils/get-curl-code.tsx index c17118d1a..40257d73d 100644 --- a/src/frontend/src/modals/apiModal/utils/get-curl-code.tsx +++ b/src/frontend/src/modals/apiModal/utils/get-curl-code.tsx @@ -8,14 +8,14 @@ export function getCurlRunCode( flowId: string, isAuth: boolean, tweaksBuildedObject, - endpointName?: string, + endpointName?: string ): string { const tweaksObject = tweaksBuildedObject[0]; // show the endpoint name in the curl command if it exists return `curl -X POST \\ "${window.location.protocol}//${window.location.host}/api/v1/run/${ - endpointName || flowId - }?stream=false" \\ + endpointName || flowId + }?stream=false" \\ -H 'Content-Type: application/json'\\${ !isAuth ? `\n -H 'x-api-key: '\\` : "" } diff --git a/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts b/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts index 4cd3763c6..657526dda 100644 --- a/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts +++ b/src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts @@ -13,8 +13,8 @@ export const getNodesWithDefaultValue = (flow) => { templateField.charAt(0) !== "_" && node.data.node.template[templateField]?.show && LANGFLOW_SUPPORTED_TYPES.has( - node.data.node.template[templateField]?.type, - ), + node.data.node.template[templateField]?.type + ) ) .map((n, i) => { arrNodesWithValues.push(node["id"]); diff --git a/src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx b/src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx index 9aa946746..a876b18c3 100644 --- a/src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx +++ b/src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx @@ -10,7 +10,7 @@ export default function getPythonApiCode( flowId: string, isAuth: boolean, tweaksBuildedObject: any[], - endpointName?: string, + endpointName?: string ): string { let tweaksString = "{}"; if (tweaksBuildedObject && tweaksBuildedObject.length > 0) { diff --git a/src/frontend/src/modals/apiModal/utils/get-python-code.tsx b/src/frontend/src/modals/apiModal/utils/get-python-code.tsx index 734e16bbe..846a4c6e9 100644 --- a/src/frontend/src/modals/apiModal/utils/get-python-code.tsx +++ b/src/frontend/src/modals/apiModal/utils/get-python-code.tsx @@ -6,7 +6,7 @@ */ export default function getPythonCode( flowName: string, - tweaksBuildedObject: any[], + tweaksBuildedObject: any[] ): string { let tweaksString = "{}"; if (tweaksBuildedObject && tweaksBuildedObject.length > 0) { diff --git a/src/frontend/src/modals/apiModal/utils/get-value.ts b/src/frontend/src/modals/apiModal/utils/get-value.ts index df8e5bdde..8db7666bf 100644 --- a/src/frontend/src/modals/apiModal/utils/get-value.ts +++ b/src/frontend/src/modals/apiModal/utils/get-value.ts @@ -5,7 +5,7 @@ export const getValue = ( value: string, node: NodeType, template: TemplateVariableType, - tweak: Object[], + tweak: Object[] ) => { let returnValue = value ?? ""; diff --git a/src/frontend/src/modals/apiModal/utils/get-widget-code.tsx b/src/frontend/src/modals/apiModal/utils/get-widget-code.tsx index 0d3d02f91..a05dc192a 100644 --- a/src/frontend/src/modals/apiModal/utils/get-widget-code.tsx +++ b/src/frontend/src/modals/apiModal/utils/get-widget-code.tsx @@ -6,7 +6,7 @@ export default function getWidgetCode( flowId: string, flowName: string, - isAuth: boolean, + isAuth: boolean ): string { return ` diff --git a/src/frontend/src/modals/apiModal/utils/tabs-array.tsx b/src/frontend/src/modals/apiModal/utils/tabs-array.tsx index deed73332..adc15f932 100644 --- a/src/frontend/src/modals/apiModal/utils/tabs-array.tsx +++ b/src/frontend/src/modals/apiModal/utils/tabs-array.tsx @@ -1,7 +1,7 @@ export function createTabsArray( codes, includeWebhookCurl = false, - includeTweaks = false, + includeTweaks = false ) { const tabs = [ { diff --git a/src/frontend/src/modals/baseModal/index.tsx b/src/frontend/src/modals/baseModal/index.tsx index 3ee7788af..834c367b3 100644 --- a/src/frontend/src/modals/baseModal/index.tsx +++ b/src/frontend/src/modals/baseModal/index.tsx @@ -37,7 +37,7 @@ const Content: React.FC = ({ children, overflowHidden }) => {
          {children} @@ -122,7 +122,7 @@ interface BaseModalProps { React.ReactElement, React.ReactElement, React.ReactElement?, - React.ReactElement?, + React.ReactElement? ]; open?: boolean; setOpen?: (open: boolean) => void; @@ -158,16 +158,16 @@ function BaseModal({ onSubmit, }: BaseModalProps) { const headerChild = React.Children.toArray(children).find( - (child) => (child as React.ReactElement).type === Header, + (child) => (child as React.ReactElement).type === Header ); const triggerChild = React.Children.toArray(children).find( - (child) => (child as React.ReactElement).type === Trigger, + (child) => (child as React.ReactElement).type === Trigger ); const ContentChild = React.Children.toArray(children).find( - (child) => (child as React.ReactElement).type === Content, + (child) => (child as React.ReactElement).type === Content ); const ContentFooter = React.Children.toArray(children).find( - (child) => (child as React.ReactElement).type === Footer, + (child) => (child as React.ReactElement).type === Footer ); let { minWidth, height } = switchCaseModalSize(size); @@ -189,7 +189,7 @@ function BaseModal({ const contentClasses = cn( minWidth, height, - "flex flex-col duration-300 overflow-hidden", + "flex flex-col duration-300 overflow-hidden" ); //UPDATE COLORS AND STYLE CLASSSES diff --git a/src/frontend/src/modals/dictAreaModal/index.tsx b/src/frontend/src/modals/dictAreaModal/index.tsx index afcab7857..77887f478 100644 --- a/src/frontend/src/modals/dictAreaModal/index.tsx +++ b/src/frontend/src/modals/dictAreaModal/index.tsx @@ -4,6 +4,7 @@ import "ace-builds/src-noconflict/mode-python"; import "ace-builds/src-noconflict/theme-github"; import "ace-builds/src-noconflict/theme-twilight"; // import "ace-builds/webpack-resolver"; +import { cloneDeep } from "lodash"; import { useEffect, useState } from "react"; import JsonView from "react18-json-view"; import "react18-json-view/src/dark.css"; @@ -12,7 +13,6 @@ import IconComponent from "../../components/genericIconComponent"; import { CODE_DICT_DIALOG_SUBTITLE } from "../../constants/constants"; import { useDarkStore } from "../../stores/darkStore"; import BaseModal from "../baseModal"; -import { cloneDeep } from "lodash"; export default function DictAreaModal({ children, diff --git a/src/frontend/src/modals/editNodeModal/hooks/use-column-defs.tsx b/src/frontend/src/modals/editNodeModal/hooks/use-column-defs.tsx index 14263a9c6..83590f409 100644 --- a/src/frontend/src/modals/editNodeModal/hooks/use-column-defs.tsx +++ b/src/frontend/src/modals/editNodeModal/hooks/use-column-defs.tsx @@ -8,7 +8,7 @@ const useColumnDefs = ( handleOnNewValue: (newValue: any, name: string) => void, handleOnChangeDb: (value: boolean, key: string) => void, changeAdvanced: (n: string) => void, - open: boolean, + open: boolean ) => { const columnDefs: ColDef[] = useMemo( () => [ @@ -76,7 +76,7 @@ const useColumnDefs = ( cellClass: "no-border", }, ], - [open, myData], + [open, myData] ); return columnDefs; diff --git a/src/frontend/src/modals/editNodeModal/index.tsx b/src/frontend/src/modals/editNodeModal/index.tsx index b8e6a11a5..05a3a4354 100644 --- a/src/frontend/src/modals/editNodeModal/index.tsx +++ b/src/frontend/src/modals/editNodeModal/index.tsx @@ -1,6 +1,6 @@ import { ColDef, GridApi } from "ag-grid-community"; +import { cloneDeep } from "lodash"; import { forwardRef, useEffect, useRef, useState } from "react"; -import IconComponent from "../../components/genericIconComponent"; import TableComponent from "../../components/tableComponent"; import { Badge } from "../../components/ui/badge"; import { useDarkStore } from "../../stores/darkStore"; @@ -9,7 +9,6 @@ import { NodeDataType } from "../../types/flow"; import BaseModal from "../baseModal"; import useColumnDefs from "./hooks/use-column-defs"; import useRowData from "./hooks/use-row-data"; -import { cloneDeep } from "lodash"; const EditNodeModal = forwardRef( ( @@ -26,7 +25,7 @@ const EditNodeModal = forwardRef( // setOpenWDoubleClick: (open: boolean) => void; data: NodeDataType; }, - ref, + ref ) => { const myData = useRef(cloneDeep(data)); @@ -54,7 +53,7 @@ const EditNodeModal = forwardRef( handleOnNewValue, handleOnChangeDb, changeAdvanced, - open, + open ); const [gridApi, setGridApi] = useState(null); @@ -118,7 +117,7 @@ const EditNodeModal = forwardRef( /> ); - }, + } ); export default EditNodeModal; diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx index b7e7e0f9d..9ed28def8 100644 --- a/src/frontend/src/modals/exportModal/index.tsx +++ b/src/frontend/src/modals/exportModal/index.tsx @@ -45,7 +45,7 @@ const ExportModal = forwardRef( is_component: false, }, name!, - description, + description ); setNoticeData({ title: API_WARNING_NOTICE_ALERT, @@ -61,7 +61,7 @@ const ExportModal = forwardRef( is_component: false, }), name!, - description, + description ); setOpen(false); }} @@ -102,6 +102,6 @@ const ExportModal = forwardRef( ); - }, + } ); export default ExportModal; diff --git a/src/frontend/src/modals/flowLogsModal/index.tsx b/src/frontend/src/modals/flowLogsModal/index.tsx index 1ba42f9eb..840de14ca 100644 --- a/src/frontend/src/modals/flowLogsModal/index.tsx +++ b/src/frontend/src/modals/flowLogsModal/index.tsx @@ -37,7 +37,7 @@ export default function FlowLogsModal({ const { columns, rows } = data; setColumns(columns.map((col) => ({ ...col, editable: true }))); setRows(rows); - }, + } ); } @@ -47,7 +47,7 @@ export default function FlowLogsModal({ .some((template) => template["stream"] && template["stream"].value); console.log( haStream, - nodes.map((nodes) => (nodes.data as NodeDataType).node!.template), + nodes.map((nodes) => (nodes.data as NodeDataType).node!.template) ); if (haStream) { setNoticeData({ diff --git a/src/frontend/src/modals/foldersModal/hooks/submit-folder.tsx b/src/frontend/src/modals/foldersModal/hooks/submit-folder.tsx index 71a8ac944..c35d7c450 100644 --- a/src/frontend/src/modals/foldersModal/hooks/submit-folder.tsx +++ b/src/frontend/src/modals/foldersModal/hooks/submit-folder.tsx @@ -33,7 +33,7 @@ const useFolderSubmit = (setOpen, folderToEdit) => { getFoldersApi(true); setOpen(false); } - }, + } ); } else { addFolder(data).then( @@ -49,7 +49,7 @@ const useFolderSubmit = (setOpen, folderToEdit) => { setErrorData({ title: `Error creating folder.`, }); - }, + } ); } }; diff --git a/src/frontend/src/modals/genericModal/index.tsx b/src/frontend/src/modals/genericModal/index.tsx index 326be58db..250192dc9 100644 --- a/src/frontend/src/modals/genericModal/index.tsx +++ b/src/frontend/src/modals/genericModal/index.tsx @@ -83,7 +83,7 @@ export default function GenericModal({ } const filteredWordsHighlight = matches.filter( - (word) => !invalid_chars.includes(word), + (word) => !invalid_chars.includes(word) ); setWordsHighlight(filteredWordsHighlight); @@ -134,7 +134,7 @@ export default function GenericModal({ // to the first key of the custom_fields object if (field_name === "") { field_name = Array.isArray( - apiReturn.data?.frontend_node?.custom_fields?.[""], + apiReturn.data?.frontend_node?.custom_fields?.[""] ) ? apiReturn.data?.frontend_node?.custom_fields?.[""][0] ?? "" : apiReturn.data?.frontend_node?.custom_fields?.[""] ?? ""; @@ -209,7 +209,7 @@ export default function GenericModal({
          {type === TypeModal.PROMPT && isEdit && !readonly ? ( diff --git a/src/frontend/src/modals/newFlowModal/index.tsx b/src/frontend/src/modals/newFlowModal/index.tsx index f9d1f98b2..7e64ed5dc 100644 --- a/src/frontend/src/modals/newFlowModal/index.tsx +++ b/src/frontend/src/modals/newFlowModal/index.tsx @@ -32,7 +32,7 @@ export default function NewFlowModal({ key={0} flow={ examples.find( - (e) => e.name == "Basic Prompting (Hello, World)", + (e) => e.name == "Basic Prompting (Hello, World)" )! } /> diff --git a/src/frontend/src/modals/shareModal/index.tsx b/src/frontend/src/modals/shareModal/index.tsx index ac6832082..0bc66092a 100644 --- a/src/frontend/src/modals/shareModal/index.tsx +++ b/src/frontend/src/modals/shareModal/index.tsx @@ -131,14 +131,14 @@ export default function ShareModal({ title: "Error sharing " + is_component ? "component" : "flow", list: [err["response"]["data"]["detail"]], }); - }, + } ); else updateFlowStore( flow!, getTagsIds(selectedTags, tags), sharePublic, - unavaliableNames.find((e) => e.name === name)!.id, + unavaliableNames.find((e) => e.name === name)!.id ).then(successShare, (err) => { setErrorData({ title: "Error sharing " + is_component ? "component" : "flow", @@ -205,7 +205,7 @@ export default function ShareModal({ setOpen={internalSetOpen} onSubmit={() => { const isNameAvailable = !unavaliableNames.some( - (element) => element.name === name, + (element) => element.name === name ); if (isNameAvailable) { diff --git a/src/frontend/src/modals/shareModal/utils/get-tags-ids.tsx b/src/frontend/src/modals/shareModal/utils/get-tags-ids.tsx index 364d310a5..1eb1c650e 100644 --- a/src/frontend/src/modals/shareModal/utils/get-tags-ids.tsx +++ b/src/frontend/src/modals/shareModal/utils/get-tags-ids.tsx @@ -1,6 +1,6 @@ export default function getTagsIds( tags: string[], - tagListId: { name: string; id: string }[], + tagListId: { name: string; id: string }[] ) { return tags .map((tag) => tagListId.find((tagObj) => tagObj.name === tag))! diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 43e6a320c..797fb0368 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -62,19 +62,19 @@ export default function Page({ const preventDefault = true; const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const autoSaveCurrentFlow = useFlowsManagerStore( - (state) => state.autoSaveCurrentFlow, + (state) => state.autoSaveCurrentFlow ); const types = useTypesStore((state) => state.types); const templates = useTypesStore((state) => state.templates); const setFilterEdge = useFlowStore((state) => state.setFilterEdge); const reactFlowWrapper = useRef(null); const [showCanvas, setSHowCanvas] = useState( - Object.keys(templates).length > 0 && Object.keys(types).length > 0, + Object.keys(templates).length > 0 && Object.keys(types).length > 0 ); const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance); const setReactFlowInstance = useFlowStore( - (state) => state.setReactFlowInstance, + (state) => state.setReactFlowInstance ); const nodes = useFlowStore((state) => state.nodes); const edges = useFlowStore((state) => state.edges); @@ -91,10 +91,10 @@ export default function Page({ const paste = useFlowStore((state) => state.paste); const resetFlow = useFlowStore((state) => state.resetFlow); const lastCopiedSelection = useFlowStore( - (state) => state.lastCopiedSelection, + (state) => state.lastCopiedSelection ); const setLastCopiedSelection = useFlowStore( - (state) => state.setLastCopiedSelection, + (state) => state.setLastCopiedSelection ); const onConnect = useFlowStore((state) => state.onConnect); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); @@ -117,7 +117,7 @@ export default function Page({ clonedSelection!, clonedNodes, clonedEdges, - getRandomName(), + getRandomName() ); const newGroupNode = generateNodeFromFlow(newFlow, getNodeId); const newEdges = reconnectEdges(newGroupNode, removedEdges); @@ -125,8 +125,8 @@ export default function Page({ ...clonedNodes.filter( (oldNodes) => !clonedSelection?.nodes.some( - (selectionNode) => selectionNode.id === oldNodes.id, - ), + (selectionNode) => selectionNode.id === oldNodes.id + ) ), newGroupNode, ]); @@ -136,8 +136,8 @@ export default function Page({ !clonedSelection!.nodes.some( (selectionNode) => selectionNode.id === oldEdge.target || - selectionNode.id === oldEdge.source, - ), + selectionNode.id === oldEdge.source + ) ), ...newEdges, ]); @@ -213,7 +213,7 @@ export default function Page({ { x: position.current.x, y: position.current.y, - }, + } ); } } @@ -297,7 +297,7 @@ export default function Page({ useEffect(() => { setSHowCanvas( - Object.keys(templates).length > 0 && Object.keys(types).length > 0, + Object.keys(templates).length > 0 && Object.keys(types).length > 0 ); }, [templates, types]); @@ -306,7 +306,7 @@ export default function Page({ takeSnapshot(); onConnect(params); }, - [takeSnapshot, onConnect], + [takeSnapshot, onConnect] ); const onNodeDragStart: NodeDragHandler = useCallback(() => { @@ -347,7 +347,7 @@ export default function Page({ // Extract the data from the drag event and parse it as a JSON object const data: { type: string; node?: APIClassType } = JSON.parse( - event.dataTransfer.getData("nodedata"), + event.dataTransfer.getData("nodedata") ); const newId = getNodeId(data.type); @@ -363,7 +363,7 @@ export default function Page({ }; paste( { nodes: [newNode], edges: [] }, - { x: event.clientX, y: event.clientY }, + { x: event.clientX, y: event.clientY } ); } else if (event.dataTransfer.types.some((types) => types === "Files")) { takeSnapshot(); @@ -392,7 +392,7 @@ export default function Page({ } }, // Specify dependencies for useCallback - [getNodeId, setNodes, takeSnapshot, paste], + [getNodeId, setNodes, takeSnapshot, paste] ); const onEdgeUpdateStart = useCallback(() => { @@ -408,7 +408,7 @@ export default function Page({ setEdges((els) => updateEdge(oldEdge, newConnection, els)); } }, - [setEdges], + [setEdges] ); const onEdgeUpdateEnd = useCallback((_, edge: Edge): void => { @@ -441,7 +441,7 @@ export default function Page({ (flow: OnSelectionChangeParams): void => { setLastSelection(flow); }, - [], + [] ); const onPaneClick = useCallback((flow) => { diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/utils/get-random-name.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/utils/get-random-name.tsx index d3bcf4d55..a58b83ec3 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/utils/get-random-name.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/utils/get-random-name.tsx @@ -5,7 +5,7 @@ import { toTitleCase } from "../../../../../utils/utils"; export default function getRandomName( retry: number = 0, noSpace: boolean = false, - maxRetries: number = 3, + maxRetries: number = 3 ): string { const left: string[] = ADJECTIVES; const right: string[] = NOUNS; diff --git a/src/frontend/src/pages/FlowPage/components/SelectionMenuComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/SelectionMenuComponent/index.tsx index 7b8193caa..8734d54f8 100644 --- a/src/frontend/src/pages/FlowPage/components/SelectionMenuComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/SelectionMenuComponent/index.tsx @@ -13,7 +13,7 @@ export default function SelectionMenu({ const [disable, setDisable] = useState( lastSelection && edges.length > 0 ? validateSelection(lastSelection!, edges).length > 0 - : false, + : false ); const [isOpen, setIsOpen] = useState(false); const [isTransitioning, setIsTransitioning] = useState(false); diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx index 696f9898a..d193d2e6b 100644 --- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx @@ -41,7 +41,7 @@ export default function ExtraSidebar(): JSX.Element { const [search, setSearch] = useState(""); function onDragStart( event: React.DragEvent, - data: { type: string; node?: APIClassType }, + data: { type: string; node?: APIClassType } ): void { //start drag event var crt = event.currentTarget.cloneNode(true); @@ -67,7 +67,7 @@ export default function ExtraSidebar(): JSX.Element { let keys = Object.keys(data[d]).filter( (nd) => nd.toLowerCase().includes(e.toLowerCase()) || - data[d][nd].display_name?.toLowerCase().includes(e.toLowerCase()), + data[d][nd].display_name?.toLowerCase().includes(e.toLowerCase()) ); keys.forEach((element) => { ret[d][element] = data[d][element]; @@ -134,7 +134,7 @@ export default function ExtraSidebar(): JSX.Element { if (filtered.some((x) => x !== "")) { let keys = Object.keys(dataClone[d]).filter((nd) => - filtered.includes(nd), + filtered.includes(nd) ); Object.keys(dataClone[d]).forEach((element) => { if (!keys.includes(element)) { @@ -171,7 +171,7 @@ export default function ExtraSidebar(): JSX.Element { if (filtered.some((x) => x !== "")) { let keys = Object.keys(dataClone[d]).filter((nd) => - filtered.includes(nd), + filtered.includes(nd) ); Object.keys(dataClone[d]).forEach((element) => { if (!keys.includes(element)) { @@ -200,7 +200,7 @@ export default function ExtraSidebar(): JSX.Element { "extra-side-bar-buttons gap-[4px] text-sm font-semibold", !hasApiKey || !validApiKey || !hasStore ? "button-disable cursor-default text-muted-foreground" - : "", + : "" )} > Share ), - [hasApiKey, validApiKey, currentFlow, hasStore], + [hasApiKey, validApiKey, currentFlow, hasStore] ); const ExportMemo = useMemo( @@ -227,7 +227,7 @@ export default function ExtraSidebar(): JSX.Element { ), - [], + [] ); const getIcon = useMemo(() => { @@ -311,8 +311,8 @@ export default function ExtraSidebar(): JSX.Element { .sort((a, b) => sensitiveSort( dataFilter[SBSectionName][a].display_name, - dataFilter[SBSectionName][b].display_name, - ), + dataFilter[SBSectionName][b].display_name + ) ) .map((SBItemName: string, index) => ( ) : (
          - ), + ) )}{" "} sensitiveSort( dataFilter[SBSectionName][a].display_name, - dataFilter[SBSectionName][b].display_name, - ), + dataFilter[SBSectionName][b].display_name + ) ) .map((SBItemName: string, index) => ( ) : (
          - ), + ) )}
          diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index 8d217b2b2..bcf9e7018 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -50,7 +50,7 @@ export default function NodeToolbarComponent({ const [showconfirmShare, setShowconfirmShare] = useState(false); const [showOverrideModal, setShowOverrideModal] = useState(false); const [flowComponent, setFlowComponent] = useState( - createFlowComponent(cloneDeep(data), version), + createFlowComponent(cloneDeep(data), version) ); const preventDefault = true; const isMac = navigator.platform.toUpperCase().includes("MAC"); @@ -67,7 +67,7 @@ export default function NodeToolbarComponent({ data.node.template[templateField]?.type === "Any" || data.node.template[templateField]?.type === "int" || data.node.template[templateField]?.type === "dict" || - data.node.template[templateField]?.type === "NestedDict"), + data.node.template[templateField]?.type === "NestedDict") ).length; const hasStore = useStoreStore((state) => state.hasStore); @@ -220,7 +220,7 @@ export default function NodeToolbarComponent({ const updateNodeInternals = useUpdateNodeInternals(); const setLastCopiedSelection = useFlowStore( - (state) => state.setLastCopiedSelection, + (state) => state.setLastCopiedSelection ); const setSuccessData = useAlertStore((state) => state.setSuccessData); @@ -294,7 +294,7 @@ export default function NodeToolbarComponent({ nodes, edges, setNodes, - setEdges, + setEdges ); break; case "override": @@ -321,14 +321,14 @@ export default function NodeToolbarComponent({ y: 10, paneX: nodes.find((node) => node.id === data.id)?.position.x, paneY: nodes.find((node) => node.id === data.id)?.position.y, - }, + } ); break; } }; const isSaved = flows.some((flow) => - Object.values(flow).includes(data.node?.display_name!), + Object.values(flow).includes(data.node?.display_name!) ); function displayShortcut({ @@ -346,7 +346,7 @@ export default function NodeToolbarComponent({ } }); const filteredShortcut = fixedShortcut.filter( - (key) => !key.toLowerCase().includes("shift"), + (key) => !key.toLowerCase().includes("shift") ); let shortcutWPlus: string[] = []; if (!hasShift) shortcutWPlus = filteredShortcut.join("+").split(" "); @@ -370,7 +370,7 @@ export default function NodeToolbarComponent({ const setNode = useFlowStore((state) => state.setNode); const handleOnNewValue = ( - newValue: string | string[] | boolean | Object[], + newValue: string | string[] | boolean | Object[] ): void => { if (data.node!.template[name].value !== newValue) { takeSnapshot(); @@ -426,8 +426,8 @@ export default function NodeToolbarComponent({ name.split(" ")[0].toLowerCase() === "code", - )!, + ({ name }) => name.split(" ")[0].toLowerCase() === "code" + )! )} side="top" > @@ -446,8 +446,8 @@ export default function NodeToolbarComponent({ name.split(" ")[0].toLowerCase() === "advanced", - )!, + ({ name }) => name.split(" ")[0].toLowerCase() === "advanced" + )! )} side="top" > @@ -486,14 +486,14 @@ export default function NodeToolbarComponent({ name.split(" ")[0].toLowerCase() === "freeze", - )!, + ({ name }) => name.split(" ")[0].toLowerCase() === "freeze" + )! )} side="top" > @@ -542,7 +542,7 @@ export default function NodeToolbarComponent({
          !key.toLowerCase().includes("shift"), + (key) => !key.toLowerCase().includes("shift") ); let shortcutWPlus: string[] = []; if (!hasShift) shortcutWPlus = filteredShortcut.join("+").split(" "); diff --git a/src/frontend/src/pages/FlowPage/index.tsx b/src/frontend/src/pages/FlowPage/index.tsx index 49fdb1eca..e5a67bd35 100644 --- a/src/frontend/src/pages/FlowPage/index.tsx +++ b/src/frontend/src/pages/FlowPage/index.tsx @@ -10,7 +10,7 @@ import ExtraSidebar from "./components/extraSidebarComponent"; export default function FlowPage({ view }: { view?: boolean }): JSX.Element { const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId, + (state) => state.setCurrentFlowId ); const version = useDarkStore((state) => state.version); const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage); diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-delete-multiple.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-delete-multiple.tsx index 395088193..af78db50d 100644 --- a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-delete-multiple.tsx +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-delete-multiple.tsx @@ -9,7 +9,7 @@ const useDeleteMultipleFlows = ( myCollectionId, getFolderById, setSuccessData, - setErrorData, + setErrorData ) => { const handleDeleteMultiple = useCallback(() => { removeFlow(selectedFlowsComponentsCards) diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-filtered-flows.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-filtered-flows.tsx index 96b1757ff..b391d9c8e 100644 --- a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-filtered-flows.tsx +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-filtered-flows.tsx @@ -4,7 +4,7 @@ import { useEffect } from "react"; const useFilteredFlows = ( flowsFromFolder, searchFlowsComponents, - setAllFlows, + setAllFlows ) => { useEffect(() => { const newFlows = cloneDeep(flowsFromFolder || []); @@ -13,7 +13,7 @@ const useFilteredFlows = ( f.name.toLowerCase().includes(searchFlowsComponents.toLowerCase()) || f.description .toLowerCase() - .includes(searchFlowsComponents.toLowerCase()), + .includes(searchFlowsComponents.toLowerCase()) ); if (searchFlowsComponents === "") { diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-duplicate.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-duplicate.tsx index fc49fc0d1..97e966b91 100644 --- a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-duplicate.tsx +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-duplicate.tsx @@ -12,16 +12,16 @@ const useDuplicateFlows = ( setSuccessData, setSelectedFlowsComponentsCards, handleSelectAll, - cardTypes, + cardTypes ) => { const handleDuplicate = useCallback(() => { Promise.all( selectedFlowsComponentsCards.map((selectedFlow) => addFlow( true, - allFlows.find((flow) => flow.id === selectedFlow), - ), - ), + allFlows.find((flow) => flow.id === selectedFlow) + ) + ) ).then(() => { resetFilter(); getFoldersApi(true); diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-export.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-export.tsx index bd742045b..446525f13 100644 --- a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-export.tsx +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-export.tsx @@ -9,7 +9,7 @@ const useExportFlows = ( setSuccessData, setSelectedFlowsComponentsCards, handleSelectAll, - cardTypes, + cardTypes ) => { const handleExport = useCallback(() => { selectedFlowsComponentsCards.forEach((selectedFlowId) => { @@ -25,7 +25,7 @@ const useExportFlows = ( is_component: false, }), selectedFlow.name, - selectedFlow.description, + selectedFlow.description ); } }); diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-select-all.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-select-all.tsx index a81515e0a..5b20a4ddc 100644 --- a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-select-all.tsx +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-handle-select-all.tsx @@ -16,7 +16,7 @@ const useSelectAll = (flowsFromFolder, getValues, setValue) => { setValue(key, false); }); }, - [flowsFromFolder, getValues, setValue], + [flowsFromFolder, getValues, setValue] ); return { handleSelectAll }; diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-select-options-change.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-select-options-change.tsx index 56dc204c7..b19ec4f4f 100644 --- a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-select-options-change.tsx +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-select-options-change.tsx @@ -5,7 +5,7 @@ const useSelectOptionsChange = ( setErrorData, setOpenDelete, handleDuplicate, - handleExport, + handleExport ) => { const handleSelectOptionsChange = useCallback( (action) => { @@ -31,7 +31,7 @@ const useSelectOptionsChange = ( setOpenDelete, handleDuplicate, handleExport, - ], + ] ); return { handleSelectOptionsChange }; diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-selected-flows.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-selected-flows.tsx index b6f00934e..aa70b025f 100644 --- a/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-selected-flows.tsx +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/hooks/use-selected-flows.tsx @@ -2,7 +2,7 @@ import { useEffect } from "react"; const useSelectedFlows = ( entireFormValues, - setSelectedFlowsComponentsCards, + setSelectedFlowsComponentsCards ) => { useEffect(() => { if (!entireFormValues || Object.keys(entireFormValues).length === 0) return; diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx index af13e2bb4..81d2aa18d 100644 --- a/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx @@ -40,22 +40,22 @@ export default function ComponentsComponent({ const allFlows = useFlowsManagerStore((state) => state.allFlows); const flowsFromFolder = useFolderStore( - (state) => state.selectedFolder?.flows, + (state) => state.selectedFolder?.flows ); const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const [openDelete, setOpenDelete] = useState(false); const searchFlowsComponents = useFlowsManagerStore( - (state) => state.searchFlowsComponents, + (state) => state.searchFlowsComponents ); const setSelectedFlowsComponentsCards = useFlowsManagerStore( - (state) => state.setSelectedFlowsComponentsCards, + (state) => state.setSelectedFlowsComponentsCards ); const selectedFlowsComponentsCards = useFlowsManagerStore( - (state) => state.selectedFlowsComponentsCards, + (state) => state.selectedFlowsComponentsCards ); const [handleFileDrop] = useFileDrop(uploadFlow, type)!; @@ -109,7 +109,7 @@ export default function ComponentsComponent({ const { handleSelectAll } = useSelectAll( flowsFromFolder, getValues, - setValue, + setValue ); const { handleDuplicate } = useDuplicateFlows( @@ -124,7 +124,7 @@ export default function ComponentsComponent({ setSuccessData, setSelectedFlowsComponentsCards, handleSelectAll, - cardTypes, + cardTypes ); const version = useDarkStore((state) => state.version); @@ -138,7 +138,7 @@ export default function ComponentsComponent({ setSuccessData, setSelectedFlowsComponentsCards, handleSelectAll, - cardTypes, + cardTypes ); const { handleSelectOptionsChange } = useSelectOptionsChange( @@ -146,7 +146,7 @@ export default function ComponentsComponent({ setErrorData, setOpenDelete, handleDuplicate, - handleExport, + handleExport ); const { handleDeleteMultiple } = useDeleteMultipleFlows( @@ -158,21 +158,21 @@ export default function ComponentsComponent({ myCollectionId, getFolderById, setSuccessData, - setErrorData, + setErrorData ); useSelectedFlows(entireFormValues, setSelectedFlowsComponentsCards); const descriptionModal = useDescriptionModal( selectedFlowsComponentsCards, - type, + type ); const getTotalRowsCount = () => { if (type === "all") return allFlows?.length; return allFlows?.filter( - (f) => (f.is_component ?? false) === (type === "component"), + (f) => (f.is_component ?? false) === (type === "component") )?.length; }; diff --git a/src/frontend/src/pages/MainPage/components/headerComponent/index.tsx b/src/frontend/src/pages/MainPage/components/headerComponent/index.tsx index 151cdd902..26999fbd5 100644 --- a/src/frontend/src/pages/MainPage/components/headerComponent/index.tsx +++ b/src/frontend/src/pages/MainPage/components/headerComponent/index.tsx @@ -107,7 +107,7 @@ const HeaderComponent = ({ name="Trash2" className={cn( "h-5 w-5 text-primary transition-all", - disableFunctions ? "" : "hover:text-status-red", + disableFunctions ? "" : "hover:text-status-red" )} /> diff --git a/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx b/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx index 2c1b9cd49..17916f05b 100644 --- a/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx +++ b/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx @@ -20,7 +20,7 @@ const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => { const [inputValue, setInputValue] = useState(""); const setSearchFlowsComponents = useFlowsManagerStore( - (state) => state.setSearchFlowsComponents, + (state) => state.setSearchFlowsComponents ); const handleDownloadFolder = () => { diff --git a/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/inputSearchComponent/index.tsx b/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/inputSearchComponent/index.tsx index 59809425e..8619cb2d5 100644 --- a/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/inputSearchComponent/index.tsx +++ b/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/inputSearchComponent/index.tsx @@ -23,7 +23,7 @@ const InputSearchComponent = ({ const pagePath = window.location.pathname; const allFlows = useFlowsManagerStore((state) => state.allFlows); const searchFlowsComponents = useFlowsManagerStore( - (state) => state.searchFlowsComponents, + (state) => state.searchFlowsComponents ); const disableInputSearch = diff --git a/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx b/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx index ce2b653a2..0daa61257 100644 --- a/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx +++ b/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx @@ -16,7 +16,7 @@ import useDropdownOptions from "../../hooks/use-dropdown-options"; export default function HomePage(): JSX.Element { const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId, + (state) => state.setCurrentFlowId ); const location = useLocation(); diff --git a/src/frontend/src/pages/MainPage/services/index.ts b/src/frontend/src/pages/MainPage/services/index.ts index 3e1286d5e..fbfc34389 100644 --- a/src/frontend/src/pages/MainPage/services/index.ts +++ b/src/frontend/src/pages/MainPage/services/index.ts @@ -30,12 +30,12 @@ export async function addFolder(data: AddFolderType): Promise { export async function updateFolder( body: FolderType, - folderId: string, + folderId: string ): Promise { try { const response = await api.patch( `${BASE_URL_API}folders/${folderId}`, - body, + body ); return response?.data; } catch (error) { @@ -68,7 +68,7 @@ export async function downloadFlowsFromFolders(folderId: string): Promise<{ }> { try { const response = await api.get( - `${BASE_URL_API}folders/download/${folderId}`, + `${BASE_URL_API}folders/download/${folderId}` ); if (response?.status !== 200) { throw new Error(`HTTP error! status: ${response?.status}`); @@ -82,7 +82,7 @@ export async function downloadFlowsFromFolders(folderId: string): Promise<{ } export async function uploadFlowsFromFolders( - flows: FormData, + flows: FormData ): Promise { try { const response = await api.post(`${BASE_URL_API}folders/upload/`, flows); @@ -99,11 +99,11 @@ export async function uploadFlowsFromFolders( export async function moveFlowToFolder( flowId: string, - folderId: string, + folderId: string ): Promise { try { const response = await api.patch( - `${BASE_URL_API}folders/move_to_folder/${flowId}/${folderId}`, + `${BASE_URL_API}folders/move_to_folder/${flowId}/${folderId}` ); return response?.data; } catch (error) { diff --git a/src/frontend/src/pages/MainPage/utils/handle-download-folder.ts b/src/frontend/src/pages/MainPage/utils/handle-download-folder.ts index 6817622d0..d6a8e957c 100644 --- a/src/frontend/src/pages/MainPage/utils/handle-download-folder.ts +++ b/src/frontend/src/pages/MainPage/utils/handle-download-folder.ts @@ -11,7 +11,7 @@ export function handleDownloadFolderFn(folderId: string) { data.folder_description = folder?.description || ""; const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( - JSON.stringify(data), + JSON.stringify(data) )}`; const link = document.createElement("a"); diff --git a/src/frontend/src/pages/Playground/index.tsx b/src/frontend/src/pages/Playground/index.tsx index 4ea3cc506..d26147df6 100644 --- a/src/frontend/src/pages/Playground/index.tsx +++ b/src/frontend/src/pages/Playground/index.tsx @@ -11,7 +11,7 @@ export default function PlaygroundPage() { const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const getFlowById = useFlowsManagerStore((state) => state.getFlowById); const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId, + (state) => state.setCurrentFlowId ); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); diff --git a/src/frontend/src/pages/ProfileSettingsPage/index.tsx b/src/frontend/src/pages/ProfileSettingsPage/index.tsx index 5cedc5841..c1635dc9e 100644 --- a/src/frontend/src/pages/ProfileSettingsPage/index.tsx +++ b/src/frontend/src/pages/ProfileSettingsPage/index.tsx @@ -24,11 +24,11 @@ import { gradients } from "../../utils/styleUtils"; import GradientChooserComponent from "../SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent"; export default function ProfileSettingsPage(): JSX.Element { const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId, + (state) => state.setCurrentFlowId ); const [inputState, setInputState] = useState( - CONTROL_PATCH_USER_STATE, + CONTROL_PATCH_USER_STATE ); // set null id diff --git a/src/frontend/src/pages/SettingsPage/index.tsx b/src/frontend/src/pages/SettingsPage/index.tsx index 2e8f49cfc..e6eb41b2f 100644 --- a/src/frontend/src/pages/SettingsPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/index.tsx @@ -8,7 +8,7 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore"; export default function SettingsPage(): JSX.Element { const pathname = location.pathname; const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId, + (state) => state.setCurrentFlowId ); useEffect(() => { setCurrentFlowId(""); diff --git a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/hooks/use-handle-delete-key.tsx b/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/hooks/use-handle-delete-key.tsx index 74d5dae99..01b4b7415 100644 --- a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/hooks/use-handle-delete-key.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/hooks/use-handle-delete-key.tsx @@ -10,7 +10,7 @@ const useDeleteApiKeys = ( selectedRows, resetFilter, setSuccessData, - setErrorData, + setErrorData ) => { const handleDeleteKey = () => { Promise.all(selectedRows.map((selectedRow) => deleteApiKey(selectedRow))) diff --git a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx index af5280f72..e5b224a58 100644 --- a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx @@ -25,7 +25,7 @@ export default function ApiKeysPage() { userData, setLoadingKeys, keysList, - setUserId, + setUserId ); function resetFilter() { @@ -36,7 +36,7 @@ export default function ApiKeysPage() { selectedRows, resetFilter, setSuccessData, - setErrorData, + setErrorData ); const columnDefs = getColumnDefs(); diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/PasswordForm/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/PasswordForm/index.tsx index 28edb2f09..d99752e7b 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/PasswordForm/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/PasswordForm/index.tsx @@ -17,7 +17,7 @@ type PasswordFormComponentProps = { handlePatchPassword: ( password: string, cnfPassword: string, - handleInput: any, + handleInput: any ) => void; }; const PasswordFormComponent = ({ diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/hooks/use-preload-images.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/hooks/use-preload-images.tsx index 988af6ea9..1948c9482 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/hooks/use-preload-images.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/hooks/use-preload-images.tsx @@ -14,8 +14,8 @@ const usePreloadImages = (profilePictures, setImagesLoaded) => { img.src = src; img.onload = resolve; img.onerror = resolve; - }), - ), + }) + ) ); }; @@ -26,9 +26,9 @@ const usePreloadImages = (profilePictures, setImagesLoaded) => { Object.keys(profilePictures).flatMap((folder) => profilePictures[folder].map((path) => imageArray.push( - `${firstUrl}${BASE_URL_API}files/profile_pictures/${folder}/${path}`, - ), - ), + `${firstUrl}${BASE_URL_API}files/profile_pictures/${folder}/${path}` + ) + ) ); preloadImages(imageArray).then(() => { diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/index.tsx index 408bd8ee1..edfe58a16 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/index.tsx @@ -58,7 +58,7 @@ export default function ProfilePictureChooserComponent({ key={idx} src={`${BACKEND_URL.slice( 0, - BACKEND_URL.length - 1, + BACKEND_URL.length - 1 )}${BASE_URL_API}files/profile_pictures/${ folder + "/" + path }`} diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/StoreApiKeyForm/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/StoreApiKeyForm/index.tsx index 5bc5ea38b..1fbf34024 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/StoreApiKeyForm/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/StoreApiKeyForm/index.tsx @@ -47,8 +47,8 @@ const StoreApiKeyFormComponent = ({ {(hasApiKey && !validApiKey ? INVALID_API_KEY : !hasApiKey - ? NO_API_KEY - : "") + INSERT_API_KEY} + ? NO_API_KEY + : "") + INSERT_API_KEY} diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx index 239103777..917d61714 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx @@ -21,13 +21,13 @@ import StoreApiKeyFormComponent from "./components/StoreApiKeyForm"; export default function GeneralPage() { const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId, + (state) => state.setCurrentFlowId ); const { scrollId } = useParams(); const [inputState, setInputState] = useState( - CONTROL_PATCH_USER_STATE, + CONTROL_PATCH_USER_STATE ); const { autoLogin } = useContext(AuthContext); @@ -48,7 +48,7 @@ export default function GeneralPage() { const { handlePatchPassword } = usePatchPassword( userData, setSuccessData, - setErrorData, + setErrorData ); const { handleGetProfilePictures } = useGetProfilePictures(setErrorData); @@ -57,7 +57,7 @@ export default function GeneralPage() { setSuccessData, setErrorData, userData, - setUserData, + setUserData ); useScrollToElement(scrollId, setCurrentFlowId); @@ -67,7 +67,7 @@ export default function GeneralPage() { setErrorData, setHasApiKey, setValidApiKey, - setLoadingApiKey, + setLoadingApiKey ); function handleInput({ diff --git a/src/frontend/src/pages/SettingsPage/pages/GlobalVariablesPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GlobalVariablesPage/index.tsx index 6f0dac836..ba9c465d7 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GlobalVariablesPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GlobalVariablesPage/index.tsx @@ -14,13 +14,13 @@ import { useGlobalVariablesStore } from "../../../../stores/globalVariablesStore export default function GlobalVariablesPage() { const globalVariablesEntries = useGlobalVariablesStore( - (state) => state.globalVariablesEntries, + (state) => state.globalVariablesEntries ); const removeGlobalVariable = useGlobalVariablesStore( - (state) => state.removeGlobalVariable, + (state) => state.removeGlobalVariable ); const globalVariables = useGlobalVariablesStore( - (state) => state.globalVariables, + (state) => state.globalVariables ); const setErrorData = useAlertStore((state) => state.setErrorData); const getVariableId = useGlobalVariablesStore((state) => state.getVariableId); diff --git a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx index e2e49e815..894969f52 100644 --- a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx @@ -31,7 +31,7 @@ export default function EditShortcutButton({ ? defaultShortcuts.find( (s) => s.name.split(" ")[0].toLowerCase().toLowerCase() === - shortcut[0]?.split(" ")[0].toLowerCase(), + shortcut[0]?.split(" ")[0].toLowerCase() )?.shortcut : ""; const [key, setKey] = useState(null); @@ -50,7 +50,7 @@ export default function EditShortcutButton({ } const setUniqueShortcut = useShortcutsStore( - (state) => state.updateUniqueShortcut, + (state) => state.updateUniqueShortcut ); function editCombination(): void { @@ -74,7 +74,7 @@ export default function EditShortcutButton({ setShortcuts(newCombination); localStorage.setItem( "langflow-shortcuts", - JSON.stringify(newCombination), + JSON.stringify(newCombination) ); setKey(null); setOpen(false); @@ -116,7 +116,7 @@ export default function EditShortcutButton({ const keysArr = keys.split(" "); let hasNewKey = false; return keysArr.some( - (k) => k.toLowerCase().trim() === keyToCompare.toLowerCase().trim(), + (k) => k.toLowerCase().trim() === keyToCompare.toLowerCase().trim() ); } @@ -137,7 +137,7 @@ export default function EditShortcutButton({ if (checkForKeys(key, fixedKey)) return; } setKey((oldKey) => - getFixedCombination({ oldKey: oldKey!, key: fixedKey }), + getFixedCombination({ oldKey: oldKey!, key: fixedKey }) ); } diff --git a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx index 5e1228c7d..90e442897 100644 --- a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx @@ -40,7 +40,7 @@ export default function ShortcutsPage() { const combinationToEdit = shortcuts.filter((s) => s.name === selectedRows[0]); const [open, setOpen] = useState(false); const updateUniqueShortcut = useShortcutsStore( - (state) => state.updateUniqueShortcut, + (state) => state.updateUniqueShortcut ); function handleRestore() { diff --git a/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-profile-picture.tsx b/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-profile-picture.tsx index 584300fdf..537c579af 100644 --- a/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-profile-picture.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-profile-picture.tsx @@ -9,7 +9,7 @@ const usePatchProfilePicture = ( setSuccessData, setErrorData, currentUserData, - setUserData, + setUserData ) => { const handlePatchProfilePicture = async (profile_picture) => { try { diff --git a/src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx b/src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx index cf5871a26..bdd105fef 100644 --- a/src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx @@ -11,7 +11,7 @@ const useSaveKey = ( setErrorData, setHasApiKey, setValidApiKey, - setLoadingApiKey, + setLoadingApiKey ) => { const { storeApiKey } = useContext(AuthContext); @@ -35,7 +35,7 @@ const useSaveKey = ( setHasApiKey(false); setValidApiKey(false); setLoadingApiKey(false); - }, + } ); } }; diff --git a/src/frontend/src/pages/SettingsPage/pages/messagesPage/hooks/use-remove-messages.tsx b/src/frontend/src/pages/SettingsPage/pages/messagesPage/hooks/use-remove-messages.tsx index d7f4d5202..f4de52cbb 100644 --- a/src/frontend/src/pages/SettingsPage/pages/messagesPage/hooks/use-remove-messages.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/messagesPage/hooks/use-remove-messages.tsx @@ -5,7 +5,7 @@ const useRemoveMessages = ( setSelectedRows, setSuccessData, setErrorData, - selectedRows, + selectedRows ) => { const deleteMessages = useMessagesStore((state) => state.removeMessages); diff --git a/src/frontend/src/pages/SettingsPage/pages/messagesPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/messagesPage/index.tsx index 8b6f2a0a0..5b1766d8c 100644 --- a/src/frontend/src/pages/SettingsPage/pages/messagesPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/messagesPage/index.tsx @@ -26,7 +26,7 @@ export default function MessagesPage() { setSelectedRows, setSuccessData, setErrorData, - selectedRows, + selectedRows ); const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData); @@ -63,7 +63,7 @@ export default function MessagesPage() { overlayNoRowsTemplate="No data available" onSelectionChanged={(event: SelectionChangedEvent) => { setSelectedRows( - event.api.getSelectedRows().map((row) => row.index), + event.api.getSelectedRows().map((row) => row.index) ); }} rowSelection="multiple" diff --git a/src/frontend/src/pages/StorePage/index.tsx b/src/frontend/src/pages/StorePage/index.tsx index 718888c66..80b842db7 100644 --- a/src/frontend/src/pages/StorePage/index.tsx +++ b/src/frontend/src/pages/StorePage/index.tsx @@ -46,7 +46,7 @@ export default function StorePage(): JSX.Element { const setErrorData = useAlertStore((state) => state.setErrorData); const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId, + (state) => state.setCurrentFlowId ); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); const [loading, setLoading] = useState(true); @@ -143,7 +143,7 @@ export default function StorePage(): JSX.Element { setTotalRowsCount( filteredCategories?.length === 0 ? Number(res?.count ?? 0) - : res?.results?.length ?? 0, + : res?.results?.length ?? 0 ); } }) @@ -183,7 +183,7 @@ export default function StorePage(): JSX.Element { disabled={loading} className={cn( `${!validApiKey ? "animate-pulse border-error" : ""}`, - loading ? "cursor-not-allowed" : "", + loading ? "cursor-not-allowed" : "" )} variant="primary" onClick={() => { diff --git a/src/frontend/src/routes.tsx b/src/frontend/src/routes.tsx index a1997e510..5623f0871 100644 --- a/src/frontend/src/routes.tsx +++ b/src/frontend/src/routes.tsx @@ -11,25 +11,25 @@ import MessagesPage from "./pages/SettingsPage/pages/messagesPage"; const AdminPage = lazy(() => import("./pages/AdminPage")); const LoginAdminPage = lazy(() => import("./pages/AdminPage/LoginPage")); const ApiKeysPage = lazy( - () => import("./pages/SettingsPage/pages/ApiKeysPage"), + () => import("./pages/SettingsPage/pages/ApiKeysPage") ); const DeleteAccountPage = lazy(() => import("./pages/DeleteAccountPage")); const FlowPage = lazy(() => import("./pages/FlowPage")); const LoginPage = lazy(() => import("./pages/LoginPage")); const MyCollectionComponent = lazy( - () => import("./pages/MainPage/components/myCollectionComponent"), + () => import("./pages/MainPage/components/myCollectionComponent") ); const HomePage = lazy(() => import("./pages/MainPage/pages/mainPage")); const PlaygroundPage = lazy(() => import("./pages/Playground")); const SettingsPage = lazy(() => import("./pages/SettingsPage")); const GeneralPage = lazy( - () => import("./pages/SettingsPage/pages/GeneralPage"), + () => import("./pages/SettingsPage/pages/GeneralPage") ); const GlobalVariablesPage = lazy( - () => import("./pages/SettingsPage/pages/GlobalVariablesPage"), + () => import("./pages/SettingsPage/pages/GlobalVariablesPage") ); const ShortcutsPage = lazy( - () => import("./pages/SettingsPage/pages/ShortcutsPage"), + () => import("./pages/SettingsPage/pages/ShortcutsPage") ); const SignUp = lazy(() => import("./pages/SignUpPage")); const StorePage = lazy(() => import("./pages/StorePage")); diff --git a/src/frontend/src/stores/darkStore.ts b/src/frontend/src/stores/darkStore.ts index 8c9c10951..79f5518da 100644 --- a/src/frontend/src/stores/darkStore.ts +++ b/src/frontend/src/stores/darkStore.ts @@ -36,7 +36,7 @@ export const useDarkStore = create((set, get) => ({ window.localStorage.setItem("githubStars", res?.toString() ?? "0"); window.localStorage.setItem( "githubStarsLastUpdated", - new Date().toString(), + new Date().toString() ); set(() => ({ stars: res, lastUpdated: new Date() })); }); diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index 99aea0515..6d1c2ad5c 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -74,7 +74,7 @@ const useFlowStore = create((set, get) => ({ updateFlowPool: ( nodeId: string, data: VertexBuildTypeAPI | ChatOutputType | ChatInputType, - buildId?: string, + buildId?: string ) => { let newFlowPool = cloneDeep({ ...get().flowPool }); if (!newFlowPool[nodeId]) { @@ -167,7 +167,7 @@ const useFlowStore = create((set, get) => ({ flowsManager.autoSaveCurrentFlow( newChange, newEdges, - get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 }, + get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 } ); } }, @@ -183,7 +183,7 @@ const useFlowStore = create((set, get) => ({ flowsManager.autoSaveCurrentFlow( get().nodes, newChange, - get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 }, + get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 } ); } }, @@ -201,7 +201,7 @@ const useFlowStore = create((set, get) => ({ return newChange; } return node; - }), + }) ); }, getNode: (id: string) => { @@ -212,8 +212,8 @@ const useFlowStore = create((set, get) => ({ get().nodes.filter((node) => typeof nodeId === "string" ? node.id !== nodeId - : !nodeId.includes(node.id), - ), + : !nodeId.includes(node.id) + ) ); }, deleteEdge: (edgeId) => { @@ -221,8 +221,8 @@ const useFlowStore = create((set, get) => ({ get().edges.filter((edge) => typeof edgeId === "string" ? edge.id !== edgeId - : !edgeId.includes(edge.id), - ), + : !edgeId.includes(edge.id) + ) ); }, paste: (selection, position) => { @@ -288,7 +288,7 @@ const useFlowStore = create((set, get) => ({ let source = idsMap[edge.source]; let target = idsMap[edge.target]; const sourceHandleObject: sourceHandleType = scapeJSONParse( - edge.sourceHandle!, + edge.sourceHandle! ); let sourceHandle = scapedJSONStringfy({ ...sourceHandleObject, @@ -298,7 +298,7 @@ const useFlowStore = create((set, get) => ({ edge.data.sourceHandle = sourceHandleObject; const targetHandleObject: targetHandleType = scapeJSONParse( - edge.targetHandle!, + edge.targetHandle! ); let targetHandle = scapedJSONStringfy({ ...targetHandleObject, @@ -317,7 +317,7 @@ const useFlowStore = create((set, get) => ({ data: cloneDeep(edge.data), selected: false, }, - newEdges.map((edge) => ({ ...edge, selected: false })), + newEdges.map((edge) => ({ ...edge, selected: false })) ); }); get().setEdges(newEdges); @@ -336,10 +336,10 @@ const useFlowStore = create((set, get) => ({ }); const newNodes = get().nodes.filter( - (node) => !nodesIdsSelected.includes(node.id), + (node) => !nodesIdsSelected.includes(node.id) ); const newEdges = get().edges.filter( - (edge) => !edgesIdsSelected.includes(edge.id), + (edge) => !edgesIdsSelected.includes(edge.id) ); set({ nodes: newNodes, edges: newEdges }); @@ -397,7 +397,7 @@ const useFlowStore = create((set, get) => ({ // style: { stroke: "#555" }, // className: "stroke-foreground stroke-connection", }, - oldEdges, + oldEdges ); return newEdges; @@ -407,7 +407,7 @@ const useFlowStore = create((set, get) => ({ .autoSaveCurrentFlow( get().nodes, newEdges, - get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 }, + get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 } ); }, unselectAll: () => { @@ -442,7 +442,7 @@ const useFlowStore = create((set, get) => ({ function validateSubgraph(nodes: string[]) { const errorsObjs = validateNodes( get().nodes.filter((node) => nodes.includes(node.id)), - get().edges, + get().edges ); const errors = errorsObjs.map((obj) => obj.errors).flat(); @@ -461,13 +461,13 @@ const useFlowStore = create((set, get) => ({ function handleBuildUpdate( vertexBuildData: VertexBuildTypeAPI, status: BuildStatus, - runId: string, + runId: string ) { if (vertexBuildData && vertexBuildData.inactivated_vertices) { get().removeFromVerticesBuild(vertexBuildData.inactivated_vertices); get().updateBuildStatus( vertexBuildData.inactivated_vertices, - BuildStatus.INACTIVE, + BuildStatus.INACTIVE ); } @@ -483,14 +483,14 @@ const useFlowStore = create((set, get) => ({ // next_vertices_ids should be next_vertices_ids without the inactivated vertices const next_vertices_ids = vertexBuildData.next_vertices_ids.filter( - (id) => !vertexBuildData.inactivated_vertices?.includes(id), + (id) => !vertexBuildData.inactivated_vertices?.includes(id) ); const top_level_vertices = vertexBuildData.top_level_vertices.filter( - (vertex) => !vertexBuildData.inactivated_vertices?.includes(vertex), + (vertex) => !vertexBuildData.inactivated_vertices?.includes(vertex) ); const nextVertices: VertexLayerElementType[] = zip( next_vertices_ids, - top_level_vertices, + top_level_vertices ).map(([id, reference]) => ({ id: id!, reference })); const newLayers = [ @@ -512,7 +512,7 @@ const useFlowStore = create((set, get) => ({ get().addDataToFlowPool( { ...vertexBuildData, run_id: runId }, - vertexBuildData.id, + vertexBuildData.id ); useFlowStore.getState().updateBuildStatus([vertexBuildData.id], status); @@ -521,7 +521,7 @@ const useFlowStore = create((set, get) => ({ const newFlowBuildStatus = { ...get().flowBuildStatus }; // filter out the vertices that are not status const verticesToUpdate = verticesIds?.filter( - (id) => newFlowBuildStatus[id]?.status !== BuildStatus.BUILT, + (id) => newFlowBuildStatus[id]?.status !== BuildStatus.BUILT ); if (verticesToUpdate) { @@ -591,7 +591,7 @@ const useFlowStore = create((set, get) => ({ verticesLayers: VertexLayerElementType[][]; runId: string; verticesToRun: string[]; - } | null, + } | null ) => { set({ verticesBuild: vertices }); }, @@ -616,7 +616,7 @@ const useFlowStore = create((set, get) => ({ // that are going to be built verticesIds: get().verticesBuild!.verticesIds.filter( // keep the vertices that are not in the list of vertices to remove - (vertex) => !vertices.includes(vertex), + (vertex) => !vertices.includes(vertex) ), }, }); diff --git a/src/frontend/src/stores/flowsManagerStore.ts b/src/frontend/src/stores/flowsManagerStore.ts index 393681b72..d6bc8338e 100644 --- a/src/frontend/src/stores/flowsManagerStore.ts +++ b/src/frontend/src/stores/flowsManagerStore.ts @@ -87,12 +87,12 @@ const useFlowsManagerStore = create((set, get) => ({ if (dbData) { const { data, flows } = processFlows(dbData); const examples = flows.filter( - (flow) => flow.folder_id === starterFolderId, + (flow) => flow.folder_id === starterFolderId ); get().setExamples(examples); const flowsWithoutStarterFolder = flows.filter( - (flow) => flow.folder_id !== starterFolderId, + (flow) => flow.folder_id !== starterFolderId ); get().setFlows(flowsWithoutStarterFolder); @@ -120,7 +120,7 @@ const useFlowsManagerStore = create((set, get) => ({ if (get().currentFlow) { get().saveFlow( { ...get().currentFlow!, data: { nodes, edges, viewport } }, - true, + true ); } }, @@ -146,7 +146,7 @@ const useFlowsManagerStore = create((set, get) => ({ return updatedFlow; } return flow; - }), + }) ); //update tabs state @@ -195,7 +195,7 @@ const useFlowsManagerStore = create((set, get) => ({ flow?: FlowType, override?: boolean, position?: XYPosition, - fromDragAndDrop?: boolean, + fromDragAndDrop?: boolean ): Promise => { if (newProject) { let flowData = flow @@ -211,7 +211,7 @@ const useFlowsManagerStore = create((set, get) => ({ const newFlow = createNewFlow( flowData!, flow!, - folder_id || my_collection_id!, + folder_id || my_collection_id! ); const { id } = await saveFlowToDatabase(newFlow); newFlow.id = id; @@ -234,7 +234,7 @@ const useFlowsManagerStore = create((set, get) => ({ const newFlow = createNewFlow( flowData!, flow!, - folder_id || my_collection_id!, + folder_id || my_collection_id! ); const newName = addVersionToDuplicates(newFlow, get().flows); @@ -270,7 +270,7 @@ const useFlowsManagerStore = create((set, get) => ({ .getState() .paste( { nodes: flow!.data!.nodes, edges: flow!.data!.edges }, - position ?? { x: 10, y: 10 }, + position ?? { x: 10, y: 10 } ); } }, @@ -280,7 +280,7 @@ const useFlowsManagerStore = create((set, get) => ({ multipleDeleteFlowsComponents(id) .then(() => { const { data, flows } = processFlows( - get().flows.filter((flow) => !id.includes(flow.id)), + get().flows.filter((flow) => !id.includes(flow.id)) ); get().setFlows(flows); set({ isLoading: false }); @@ -300,7 +300,7 @@ const useFlowsManagerStore = create((set, get) => ({ deleteFlowFromDatabase(id) .then(() => { const { data, flows } = processFlows( - get().flows.filter((flow) => flow.id !== id), + get().flows.filter((flow) => flow.id !== id) ); get().setFlows(flows); set({ isLoading: false }); @@ -322,7 +322,7 @@ const useFlowsManagerStore = create((set, get) => ({ return new Promise((resolve) => { let componentFlow = get().flows.find( (componentFlow) => - componentFlow.is_component && componentFlow.name === key, + componentFlow.is_component && componentFlow.name === key ); if (componentFlow) { @@ -370,7 +370,7 @@ const useFlowsManagerStore = create((set, get) => ({ fileData, undefined, position, - true, + true ); resolve(id); } @@ -411,7 +411,7 @@ const useFlowsManagerStore = create((set, get) => ({ return get().addFlow( true, createFlowComponent(component, useDarkStore.getState().version), - override, + override ); }, takeSnapshot: () => { @@ -432,7 +432,7 @@ const useFlowsManagerStore = create((set, get) => ({ if (pastLength > 0) { past[currentFlowId] = past[currentFlowId].slice( pastLength - defaultOptions.maxHistorySize + 1, - pastLength, + pastLength ); past[currentFlowId].push(newState); diff --git a/src/frontend/src/stores/foldersStore.tsx b/src/frontend/src/stores/foldersStore.tsx index e72610987..25247ce76 100644 --- a/src/frontend/src/stores/foldersStore.tsx +++ b/src/frontend/src/stores/foldersStore.tsx @@ -17,18 +17,18 @@ export const useFolderStore = create((set, get) => ({ getFolders().then( (res) => { const foldersWithoutStarterProjects = res.filter( - (folder) => folder.name !== STARTER_FOLDER_NAME, + (folder) => folder.name !== STARTER_FOLDER_NAME ); const starterProjects = res.find( - (folder) => folder.name === STARTER_FOLDER_NAME, + (folder) => folder.name === STARTER_FOLDER_NAME ); set({ starterProjectId: starterProjects!.id ?? "" }); set({ folders: foldersWithoutStarterProjects }); const myCollectionId = res?.find( - (f) => f.name === DEFAULT_FOLDER, + (f) => f.name === DEFAULT_FOLDER )?.id; set({ myCollectionId }); @@ -45,7 +45,7 @@ export const useFolderStore = create((set, get) => ({ set({ folders: [] }); get().setLoading(false); reject(error); - }, + } ); } }); @@ -65,7 +65,7 @@ export const useFolderStore = create((set, get) => ({ }, () => { get().setLoadingById(false); - }, + } ); } }, diff --git a/src/frontend/src/stores/globalVariablesStore/globalVariables.ts b/src/frontend/src/stores/globalVariablesStore/globalVariables.ts index 708f7ec09..c07813858 100644 --- a/src/frontend/src/stores/globalVariablesStore/globalVariables.ts +++ b/src/frontend/src/stores/globalVariablesStore/globalVariables.ts @@ -45,5 +45,5 @@ export const useGlobalVariablesStore = create( getVariableId: (name) => { return get().globalVariables[name]?.id; }, - }), + }) ); diff --git a/src/frontend/src/stores/messagesStore.ts b/src/frontend/src/stores/messagesStore.ts index 349a1c447..c6bf2042b 100644 --- a/src/frontend/src/stores/messagesStore.ts +++ b/src/frontend/src/stores/messagesStore.ts @@ -5,7 +5,7 @@ export const useMessagesStore = create((set, get) => ({ deleteSession: (id) => { set((state) => { const updatedMessages = state.messages.filter( - (msg) => msg.session_id !== id, + (msg) => msg.session_id !== id ); return { messages: updatedMessages }; }); @@ -29,7 +29,7 @@ export const useMessagesStore = create((set, get) => ({ updateMessage: (message) => { set(() => ({ messages: get().messages.map((msg) => - msg.index === message.index ? message : msg, + msg.index === message.index ? message : msg ), })); }, @@ -41,7 +41,7 @@ export const useMessagesStore = create((set, get) => ({ try { set((state) => { const updatedMessages = state.messages.filter( - (msg) => !ids.includes(msg.index), + (msg) => !ids.includes(msg.index) ); get().setMessages(updatedMessages); resolve(updatedMessages); diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 99f890ebf..bb4b6d74e 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -492,7 +492,7 @@ export type ChatInputType = { isDragging: boolean; files: FilePreviewType[]; setFiles: ( - files: FilePreviewType[] | ((prev: FilePreviewType[]) => FilePreviewType[]), + files: FilePreviewType[] | ((prev: FilePreviewType[]) => FilePreviewType[]) ) => void; chatValue: string; inputRef: { @@ -595,7 +595,7 @@ export type chatMessagePropsType = { updateChat: ( chat: ChatMessageType, message: string, - stream_url?: string, + stream_url?: string ) => void; }; @@ -687,12 +687,12 @@ export type codeTabsPropsType = { value: string, node: NodeType, template: TemplateVariableType, - tweak: tweakType, + tweak: tweakType ) => string; buildTweakObject?: ( tw: string, changes: string | string[] | boolean | number | Object[] | Object, - template: TemplateVariableType, + template: TemplateVariableType ) => Promise; }; activeTweaks?: boolean; diff --git a/src/frontend/src/types/store/index.ts b/src/frontend/src/types/store/index.ts index 54b7cb478..a3b26cb64 100644 --- a/src/frontend/src/types/store/index.ts +++ b/src/frontend/src/types/store/index.ts @@ -48,7 +48,7 @@ export type shortcutsStoreType = { shortcut: string; }>; setShortcuts: ( - newShortcuts: Array<{ name: string; shortcut: string }>, + newShortcuts: Array<{ name: string; shortcut: string }> ) => void; getShortcutsFromStorage: () => void; }; diff --git a/src/frontend/src/types/zustand/flow/index.ts b/src/frontend/src/types/zustand/flow/index.ts index 7449f7bc1..e8b88e8d3 100644 --- a/src/frontend/src/types/zustand/flow/index.ts +++ b/src/frontend/src/types/zustand/flow/index.ts @@ -86,7 +86,7 @@ export type FlowStoreType = { state: | FlowState | undefined - | ((oldState: FlowState | undefined) => FlowState), + | ((oldState: FlowState | undefined) => FlowState) ) => void; nodes: Node[]; edges: Edge[]; @@ -94,11 +94,11 @@ export type FlowStoreType = { onEdgesChange: OnEdgesChange; setNodes: ( update: Node[] | ((oldState: Node[]) => Node[]), - skipSave?: boolean, + skipSave?: boolean ) => void; setEdges: ( update: Edge[] | ((oldState: Edge[]) => Edge[]), - skipSave?: boolean, + skipSave?: boolean ) => void; setNode: (id: string, update: Node | ((oldState: Node) => Node)) => void; getNode: (id: string) => Node | undefined; @@ -106,12 +106,12 @@ export type FlowStoreType = { deleteEdge: (edgeId: string | Array) => void; paste: ( selection: { nodes: any; edges: any }, - position: { x: number; y: number; paneX?: number; paneY?: number }, + position: { x: number; y: number; paneX?: number; paneY?: number } ) => void; lastCopiedSelection: { nodes: any; edges: any } | null; setLastCopiedSelection: ( newSelection: { nodes: any; edges: any } | null, - isCrop?: boolean, + isCrop?: boolean ) => void; cleanFlow: () => void; setFilterEdge: (newState) => void; @@ -138,7 +138,7 @@ export type FlowStoreType = { verticesLayers: VertexLayerElementType[][]; runId: string; verticesToRun: string[]; - } | null, + } | null ) => void; addToVerticesBuild: (vertices: string[]) => void; removeFromVerticesBuild: (vertices: string[]) => void; @@ -156,7 +156,7 @@ export type FlowStoreType = { updateFlowPool: ( nodeId: string, data: VertexBuildTypeAPI | ChatOutputType | ChatInputType, - buildId?: string, + buildId?: string ) => void; getNodePosition: (nodeId: string) => { x: number; y: number }; }; diff --git a/src/frontend/src/types/zustand/flowsManager/index.ts b/src/frontend/src/types/zustand/flowsManager/index.ts index aaafebb7b..e0770626e 100644 --- a/src/frontend/src/types/zustand/flowsManager/index.ts +++ b/src/frontend/src/types/zustand/flowsManager/index.ts @@ -17,12 +17,12 @@ export type FlowsManagerStoreType = { saveFlow: (flow: FlowType, silent?: boolean) => Promise | undefined; saveFlowDebounce: ( flow: FlowType, - silent?: boolean, + silent?: boolean ) => Promise | undefined; autoSaveCurrentFlow: ( nodes: Node[], edges: Edge[], - viewport: Viewport, + viewport: Viewport ) => void; uploadFlows: () => Promise; uploadFlow: ({ @@ -41,13 +41,13 @@ export type FlowsManagerStoreType = { flow?: FlowType, override?: boolean, position?: XYPosition, - fromDragAndDrop?: boolean, + fromDragAndDrop?: boolean ) => Promise; deleteComponent: (key: string) => Promise; removeFlow: (id: string | string[]) => Promise; saveComponent: ( component: any, - override: boolean, + override: boolean ) => Promise; undo: () => void; redo: () => void; diff --git a/src/frontend/src/types/zustand/globalVariables/index.ts b/src/frontend/src/types/zustand/globalVariables/index.ts index 4b178088c..90817780f 100644 --- a/src/frontend/src/types/zustand/globalVariables/index.ts +++ b/src/frontend/src/types/zustand/globalVariables/index.ts @@ -21,7 +21,7 @@ export type GlobalVariablesStore = { id: string, type?: string, default_fields?: string[], - value?: string, + value?: string ) => void; removeGlobalVariable: (name: string) => Promise; getVariableId: (name: string) => string | undefined; diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 5d70e1d12..4d0a3e147 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -17,7 +17,7 @@ type BuildVerticesParams = { onBuildUpdate?: ( data: VertexBuildTypeAPI, status: BuildStatus, - buildId: string, + buildId: string ) => void; // Replace any with the actual type if it's not any onBuildComplete?: (allNodesValid: boolean) => void; onBuildError?: (title, list, idList: VertexLayerElementType[]) => void; @@ -55,7 +55,7 @@ export async function updateVerticesOrder( startNodeId?: string | null, stopNodeId?: string | null, nodes?: Node[], - edges?: Edge[], + edges?: Edge[] ): Promise<{ verticesLayers: VertexLayerElementType[][]; verticesIds: string[]; @@ -71,7 +71,7 @@ export async function updateVerticesOrder( startNodeId, stopNodeId, nodes, - edges, + edges ); } catch (error: any) { setErrorData({ @@ -128,7 +128,7 @@ export async function buildVertices({ startNodeId, stopNodeId, nodes, - edges, + edges ); if (onValidateNodes) { try { @@ -190,14 +190,14 @@ export async function buildVertices({ onBuildUpdate( getInactiveVertexData(element.id), BuildStatus.INACTIVE, - runId, + runId ); } if (element.reference) { onBuildUpdate( getInactiveVertexData(element.reference), BuildStatus.INACTIVE, - runId, + runId ); } buildResults.push(false); @@ -223,7 +223,7 @@ export async function buildVertices({ if (stop) { return; } - }), + }) ); // Once the current layer is built, move to the next layer currentLayerIndex += 1; @@ -268,7 +268,7 @@ async function buildVertex({ onBuildError!( "Error Building Component", buildData.data.logs.map((log) => log.message), - verticesIds.map((id) => ({ id })), + verticesIds.map((id) => ({ id })) ); stopBuild(); } @@ -283,7 +283,7 @@ async function buildVertex({ (error as AxiosError).response?.data?.detail ?? "An unexpected error occurred while building the Component. Please try again.", ], - verticesIds.map((id) => ({ id })), + verticesIds.map((id) => ({ id })) ); stopBuild(); } diff --git a/src/frontend/src/utils/parameterUtils.ts b/src/frontend/src/utils/parameterUtils.ts index d1d53dde7..a1d2f2154 100644 --- a/src/frontend/src/utils/parameterUtils.ts +++ b/src/frontend/src/utils/parameterUtils.ts @@ -21,7 +21,7 @@ export const handleUpdateValues = async (name: string, data: NodeDataType) => { code, template, name, - data.node?.template[name]?.value, + data.node?.template[name]?.value ); if (res.status === 200 && data.node?.template) { return res.data.template; @@ -34,5 +34,5 @@ export const handleUpdateValues = async (name: string, data: NodeDataType) => { export const debouncedHandleUpdateValues = debounce( handleUpdateValues, - SAVE_DEBOUNCE_TIME, + SAVE_DEBOUNCE_TIME ); diff --git a/src/frontend/src/utils/storeUtils.ts b/src/frontend/src/utils/storeUtils.ts index c8391b211..637e4331a 100644 --- a/src/frontend/src/utils/storeUtils.ts +++ b/src/frontend/src/utils/storeUtils.ts @@ -7,7 +7,7 @@ export default function cloneFLowWithParent( flow: FlowType, parent: string, is_component: boolean, - keepId = false, + keepId = false ) { let childFLow = cloneDeep(flow); childFLow.parent = parent; diff --git a/src/frontend/src/utils/utils.ts b/src/frontend/src/utils/utils.ts index 302d9e0a7..5a1f6621f 100644 --- a/src/frontend/src/utils/utils.ts +++ b/src/frontend/src/utils/utils.ts @@ -56,7 +56,7 @@ export function normalCaseToSnakeCase(str: string): string { export function toTitleCase( str: string | undefined, - isNodeField?: boolean, + isNodeField?: boolean ): string { if (!str) return ""; let result = str @@ -65,7 +65,7 @@ export function toTitleCase( if (isNodeField) return word; if (index === 0) { return checkUpperWords( - word[0].toUpperCase() + word.slice(1).toLowerCase(), + word[0].toUpperCase() + word.slice(1).toLowerCase() ); } return checkUpperWords(word.toLowerCase()); @@ -78,7 +78,7 @@ export function toTitleCase( if (isNodeField) return word; if (index === 0) { return checkUpperWords( - word[0].toUpperCase() + word.slice(1).toLowerCase(), + word[0].toUpperCase() + word.slice(1).toLowerCase() ); } return checkUpperWords(word.toLowerCase()); @@ -182,7 +182,7 @@ export function checkLocalStorageKey(key: string): boolean { export function IncrementObjectKey( object: object, - key: string, + key: string ): { newKey: string; increment: number } { let count = 1; const type = removeCountFromString(key); @@ -217,7 +217,7 @@ export function groupByFamily( data: APIDataType, baseClasses: string, left: boolean, - flow?: NodeType[], + flow?: NodeType[] ): groupedObjType[] { const baseClassesSet = new Set(baseClasses.split("\n")); let arrOfPossibleInputs: Array<{ @@ -243,7 +243,7 @@ export function groupByFamily( baseClassesSet.has(template.type)) || (template?.input_types && template?.input_types.some((inputType) => - baseClassesSet.has(inputType), + baseClassesSet.has(inputType) ))) ); }; @@ -263,7 +263,7 @@ export function groupByFamily( hasBaseClassInBaseClasses: foundNode?.hasBaseClassInBaseClasses || nodeData.node!.base_classes.some((baseClass) => - baseClassesSet.has(baseClass), + baseClassesSet.has(baseClass) ), //seta como anterior ou verifica se o node tem base class displayName: nodeData.node?.display_name, }); @@ -280,10 +280,10 @@ export function groupByFamily( if (!foundNode) { foundNode = { hasBaseClassInTemplate: Object.values(node!.template).some( - checkBaseClass, + checkBaseClass ), hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) => - baseClassesSet.has(baseClass), + baseClassesSet.has(baseClass) ), displayName: node?.display_name, }; @@ -355,7 +355,7 @@ export function isTimeStampString(str: string): boolean { export function extractColumnsFromRows( rows: object[], mode: "intersection" | "union", - excludeColumns?: Array, + excludeColumns?: Array ): (ColDef | ColGroupDef)[] { let columnsKeys: { [key: string]: ColDef | ColGroupDef } = {}; if (rows.length === 0) { diff --git a/src/frontend/tests/end-to-end/chatInputOutput.spec.ts b/src/frontend/tests/end-to-end/chatInputOutput.spec.ts index c34c953ec..17cc25efb 100644 --- a/src/frontend/tests/end-to-end/chatInputOutput.spec.ts +++ b/src/frontend/tests/end-to-end/chatInputOutput.spec.ts @@ -24,7 +24,7 @@ test("chat_io_teste", async ({ page }) => { const jsonContent = readFileSync( "src/frontend/tests/end-to-end/assets/ChatTest.json", - "utf-8", + "utf-8" ); await page.getByTestId("blank-flow").click(); @@ -62,7 +62,7 @@ test("chat_io_teste", async ({ page }) => { // Click and hold on the first element await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[2]/div/div[2]/div[10]/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[2]/div/div[2]/div[10]/button/div/div' ) .hover(); await page.mouse.down(); @@ -70,7 +70,7 @@ test("chat_io_teste", async ({ page }) => { // Move to the second element await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[1]/div/div[2]/div[4]/div/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[1]/div/div[2]/div[4]/div/button/div/div' ) .hover(); diff --git a/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts b/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts index 6950ca230..89a066415 100644 --- a/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts +++ b/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts @@ -56,7 +56,7 @@ test("user must interact with chat with Input/Output", async ({ page }) => { .getByTestId("textarea-input_value") .nth(1) .fill( - "testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!", + "testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!" ); await page.getByText("Playground", { exact: true }).last().click(); await page.getByTestId("icon-LucideSend").click(); @@ -92,9 +92,9 @@ test("user must interact with chat with Input/Output", async ({ page }) => { await page .getByText( "testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!", - { exact: true }, + { exact: true } ) - .isVisible(), + .isVisible() ); }); @@ -204,7 +204,7 @@ test("user must be able to send an image on chat", async ({ page }) => { const jsonContent = readFileSync( "src/frontend/tests/end-to-end/assets/chain.png", - "utf-8", + "utf-8" ); // Create the DataTransfer and File diff --git a/src/frontend/tests/end-to-end/dragAndDrop.spec.ts b/src/frontend/tests/end-to-end/dragAndDrop.spec.ts index 03d11aaf7..3e760901d 100644 --- a/src/frontend/tests/end-to-end/dragAndDrop.spec.ts +++ b/src/frontend/tests/end-to-end/dragAndDrop.spec.ts @@ -27,7 +27,7 @@ test.describe("drag and drop test", () => { // Read your file into a buffer. const jsonContent = readFileSync( "src/frontend/tests/end-to-end/assets/collection.json", - "utf-8", + "utf-8" ); // Create the DataTransfer and File @@ -47,7 +47,7 @@ test.describe("drag and drop test", () => { "drop", { dataTransfer, - }, + } ); const genericNoda = page.getByTestId("div-generic-node"); diff --git a/src/frontend/tests/end-to-end/dropdownComponent.spec.ts b/src/frontend/tests/end-to-end/dropdownComponent.spec.ts index 6c0a7dd9e..0152ca7ed 100644 --- a/src/frontend/tests/end-to-end/dropdownComponent.spec.ts +++ b/src/frontend/tests/end-to-end/dropdownComponent.spec.ts @@ -80,32 +80,32 @@ test("dropDownComponent", async ({ page }) => { await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showendpoint_url"]').click(); expect( - await page.locator('//*[@id="showendpoint_url"]').isChecked(), + await page.locator('//*[@id="showendpoint_url"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showendpoint_url"]').click(); expect( - await page.locator('//*[@id="showendpoint_url"]').isChecked(), + await page.locator('//*[@id="showendpoint_url"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showregion_name"]').click(); expect( - await page.locator('//*[@id="showregion_name"]').isChecked(), + await page.locator('//*[@id="showregion_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showregion_name"]').click(); expect( - await page.locator('//*[@id="showregion_name"]').isChecked(), + await page.locator('//*[@id="showregion_name"]').isChecked() ).toBeTruthy(); // showmodel_id @@ -115,7 +115,7 @@ test("dropDownComponent", async ({ page }) => { // showmodel_id await page.locator('//*[@id="showmodel_id"]').click(); expect( - await page.locator('//*[@id="showmodel_id"]').isChecked(), + await page.locator('//*[@id="showmodel_id"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showcache"]').click(); @@ -126,32 +126,32 @@ test("dropDownComponent", async ({ page }) => { await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showendpoint_url"]').click(); expect( - await page.locator('//*[@id="showendpoint_url"]').isChecked(), + await page.locator('//*[@id="showendpoint_url"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showendpoint_url"]').click(); expect( - await page.locator('//*[@id="showendpoint_url"]').isChecked(), + await page.locator('//*[@id="showendpoint_url"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showregion_name"]').click(); expect( - await page.locator('//*[@id="showregion_name"]').isChecked(), + await page.locator('//*[@id="showregion_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showregion_name"]').click(); expect( - await page.locator('//*[@id="showregion_name"]').isChecked(), + await page.locator('//*[@id="showregion_name"]').isChecked() ).toBeTruthy(); // showmodel_id @@ -161,7 +161,7 @@ test("dropDownComponent", async ({ page }) => { // showmodel_id await page.locator('//*[@id="showmodel_id"]').click(); expect( - await page.locator('//*[@id="showmodel_id"]').isChecked(), + await page.locator('//*[@id="showmodel_id"]').isChecked() ).toBeTruthy(); await page.getByTestId("dropdown-edit-model_id").click(); diff --git a/src/frontend/tests/end-to-end/fileUploadComponent.spec.ts b/src/frontend/tests/end-to-end/fileUploadComponent.spec.ts index 00f761dea..93b471c43 100644 --- a/src/frontend/tests/end-to-end/fileUploadComponent.spec.ts +++ b/src/frontend/tests/end-to-end/fileUploadComponent.spec.ts @@ -64,7 +64,7 @@ test("dropDownComponent", async ({ page }) => { // Click and hold on the first element await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[1]/div/div[2]/div[6]/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[1]/div/div[2]/div[6]/button/div/div' ) .hover(); await page.mouse.down(); @@ -72,7 +72,7 @@ test("dropDownComponent", async ({ page }) => { // Move to the second element await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[2]/div/div[2]/div[3]/div/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[2]/div/div[2]/div[3]/div/button/div/div' ) .hover(); diff --git a/src/frontend/tests/end-to-end/filterEdge.spec.ts b/src/frontend/tests/end-to-end/filterEdge.spec.ts index 7edbc0436..9cb5a567e 100644 --- a/src/frontend/tests/end-to-end/filterEdge.spec.ts +++ b/src/frontend/tests/end-to-end/filterEdge.spec.ts @@ -40,7 +40,7 @@ test("LLMChain - Tooltip", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[3]/div/button/div/div', + '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[3]/div/button/div/div' ) .hover() .then(async () => { @@ -60,17 +60,17 @@ test("LLMChain - Tooltip", async ({ page }) => { await page.getByTitle("zoom out").click(); await page .locator( - '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[4]/div/button/div/div', + '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[4]/div/button/div/div' ) .hover() .then(async () => { await expect( - page.getByTestId("tooltip-Model Specs").first(), + page.getByTestId("tooltip-Model Specs").first() ).toBeVisible(); await page.waitForTimeout(2000); await expect( - page.getByTestId("tooltip-Model Specs").first(), + page.getByTestId("tooltip-Model Specs").first() ).toBeVisible(); await page.getByTestId("icon-Search").click(); @@ -81,12 +81,12 @@ test("LLMChain - Tooltip", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[5]/div/button/div/div', + '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[5]/div/button/div/div' ) .hover() .then(async () => { await expect( - page.getByTestId("empty-tooltip-filter").first(), + page.getByTestId("empty-tooltip-filter").first() ).toBeVisible(); }); }); @@ -113,7 +113,7 @@ test("LLMChain - Filter", async ({ page }) => { await page.waitForTimeout(1000); await page.getByTestId( - "input-list-plus-btn-edit_metadata_indexing_include-2", + "input-list-plus-btn-edit_metadata_indexing_include-2" ); await page.getByTestId("blank-flow").click(); @@ -136,7 +136,7 @@ test("LLMChain - Filter", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[4]/div/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[4]/div/button/div/div' ) .click(); @@ -149,14 +149,14 @@ test("LLMChain - Filter", async ({ page }) => { await expect(page.getByTestId("model_specsChatOpenAI")).toBeVisible(); await expect(page.getByTestId("model_specsChatVertexAI")).toBeVisible(); await expect( - page.getByTestId("model_specsGoogle Generative AI"), + page.getByTestId("model_specsGoogle Generative AI") ).toBeVisible(); await expect( - page.getByTestId("model_specsHugging Face Inference API"), + page.getByTestId("model_specsHugging Face Inference API") ).toBeVisible(); await expect(page.getByTestId("model_specsOllama")).toBeVisible(); await expect( - page.getByTestId("model_specsQianfanChatEndpoint"), + page.getByTestId("model_specsQianfanChatEndpoint") ).toBeVisible(); await expect(page.getByTestId("model_specsQianfanLLMEndpoint")).toBeVisible(); await expect(page.getByTestId("model_specsVertexAI")).toBeVisible(); @@ -168,7 +168,7 @@ test("LLMChain - Filter", async ({ page }) => { await expect(page.getByTestId("model_specsAmazon Bedrock")).not.toBeVisible(); await expect(page.getByTestId("modelsAzure OpenAI")).not.toBeVisible(); await expect( - page.getByTestId("model_specsAzureChatOpenAI"), + page.getByTestId("model_specsAzureChatOpenAI") ).not.toBeVisible(); await expect(page.getByTestId("model_specsChatAnthropic")).not.toBeVisible(); await expect(page.getByTestId("model_specsChatLiteLLM")).not.toBeVisible(); @@ -178,13 +178,13 @@ test("LLMChain - Filter", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div' ) .click(); await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div' ) .click(); diff --git a/src/frontend/tests/end-to-end/floatComponent.spec.ts b/src/frontend/tests/end-to-end/floatComponent.spec.ts index e172a92e3..0577a62b4 100644 --- a/src/frontend/tests/end-to-end/floatComponent.spec.ts +++ b/src/frontend/tests/end-to-end/floatComponent.spec.ts @@ -71,27 +71,27 @@ test("FloatComponent", async ({ page }) => { await page.getByTestId("showmirostat").click(); expect( - await page.locator('//*[@id="showmirostat"]').isChecked(), + await page.locator('//*[@id="showmirostat"]').isChecked() ).toBeTruthy(); await page.getByTestId("showmirostat_eta").click(); expect( - await page.locator('//*[@id="showmirostat_eta"]').isChecked(), + await page.locator('//*[@id="showmirostat_eta"]').isChecked() ).toBeTruthy(); await page.getByTestId("showmirostat_eta").click(); expect( - await page.locator('//*[@id="showmirostat_eta"]').isChecked(), + await page.locator('//*[@id="showmirostat_eta"]').isChecked() ).toBeFalsy(); await page.getByTestId("showmirostat_tau").click(); expect( - await page.locator('//*[@id="showmirostat_tau"]').isChecked(), + await page.locator('//*[@id="showmirostat_tau"]').isChecked() ).toBeTruthy(); await page.getByTestId("showmirostat_tau").click(); expect( - await page.locator('//*[@id="showmirostat_tau"]').isChecked(), + await page.locator('//*[@id="showmirostat_tau"]').isChecked() ).toBeFalsy(); await page.getByTestId("showmodel").click(); @@ -114,22 +114,22 @@ test("FloatComponent", async ({ page }) => { await page.getByTestId("shownum_thread").click(); expect( - await page.locator('//*[@id="shownum_thread"]').isChecked(), + await page.locator('//*[@id="shownum_thread"]').isChecked() ).toBeTruthy(); await page.getByTestId("shownum_thread").click(); expect( - await page.locator('//*[@id="shownum_thread"]').isChecked(), + await page.locator('//*[@id="shownum_thread"]').isChecked() ).toBeFalsy(); await page.getByTestId("showrepeat_last_n").click(); expect( - await page.locator('//*[@id="showrepeat_last_n"]').isChecked(), + await page.locator('//*[@id="showrepeat_last_n"]').isChecked() ).toBeTruthy(); await page.getByTestId("showrepeat_last_n").click(); expect( - await page.locator('//*[@id="showrepeat_last_n"]').isChecked(), + await page.locator('//*[@id="showrepeat_last_n"]').isChecked() ).toBeFalsy(); await page.getByText("Save Changes", { exact: true }).click(); @@ -145,7 +145,7 @@ test("FloatComponent", async ({ page }) => { // showtemperature await page.locator('//*[@id="showtemperature"]').click(); expect( - await page.locator('//*[@id="showtemperature"]').isChecked(), + await page.locator('//*[@id="showtemperature"]').isChecked() ).toBeTruthy(); await page.getByText("Save Changes", { exact: true }).click(); diff --git a/src/frontend/tests/end-to-end/flowSettings.spec.ts b/src/frontend/tests/end-to-end/flowSettings.spec.ts index 25807ed88..a2e9f25c0 100644 --- a/src/frontend/tests/end-to-end/flowSettings.spec.ts +++ b/src/frontend/tests/end-to-end/flowSettings.spec.ts @@ -29,7 +29,7 @@ test("flowSettings", async ({ page }) => { await page .getByPlaceholder("Flow name") .fill( - "Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test", + "Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test" ); await page.getByText("Character limit reached").isVisible(); @@ -41,7 +41,7 @@ test("flowSettings", async ({ page }) => { await page .getByPlaceholder("Flow description") .fill( - "Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test", + "Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test" ); await page.getByTestId("save-flow-settings").click(); diff --git a/src/frontend/tests/end-to-end/folders.spec.ts b/src/frontend/tests/end-to-end/folders.spec.ts index e0f694910..384816b3f 100644 --- a/src/frontend/tests/end-to-end/folders.spec.ts +++ b/src/frontend/tests/end-to-end/folders.spec.ts @@ -58,7 +58,7 @@ test("add folder by drag and drop", async ({ page }) => { const jsonContent = readFileSync( "src/frontend/tests/end-to-end/assets/collection.json", - "utf-8", + "utf-8" ); // Create the DataTransfer and File @@ -78,7 +78,7 @@ test("add folder by drag and drop", async ({ page }) => { "drop", { dataTransfer, - }, + } ); await page.getByText("Getting Started").first().isVisible(); diff --git a/src/frontend/tests/end-to-end/inputComponent.spec.ts b/src/frontend/tests/end-to-end/inputComponent.spec.ts index 3e6936b43..d944cffe5 100644 --- a/src/frontend/tests/end-to-end/inputComponent.spec.ts +++ b/src/frontend/tests/end-to-end/inputComponent.spec.ts @@ -60,69 +60,69 @@ test("InputComponent", async ({ page }) => { expect( await page .locator('//*[@id="showchroma_server_cors_allow_origins"]') - .isChecked(), + .isChecked() ).toBeTruthy(); await page.locator('//*[@id="showchroma_server_grpc_port"]').click(); expect( - await page.locator('//*[@id="showchroma_server_grpc_port"]').isChecked(), + await page.locator('//*[@id="showchroma_server_grpc_port"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showchroma_server_host"]').click(); expect( - await page.locator('//*[@id="showchroma_server_host"]').isChecked(), + await page.locator('//*[@id="showchroma_server_host"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showchroma_server_http_port"]').click(); expect( - await page.locator('//*[@id="showchroma_server_http_port"]').isChecked(), + await page.locator('//*[@id="showchroma_server_http_port"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showchroma_server_ssl_enabled"]').click(); expect( - await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked(), + await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showcollection_name"]').click(); expect( - await page.locator('//*[@id="showcollection_name"]').isChecked(), + await page.locator('//*[@id="showcollection_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showindex_directory"]').click(); expect( - await page.locator('//*[@id="showindex_directory"]').isChecked(), + await page.locator('//*[@id="showindex_directory"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showchroma_server_cors_allow_origins"]').click(); expect( await page .locator('//*[@id="showchroma_server_cors_allow_origins"]') - .isChecked(), + .isChecked() ).toBeFalsy(); await page.locator('//*[@id="showchroma_server_grpc_port"]').click(); expect( - await page.locator('//*[@id="showchroma_server_grpc_port"]').isChecked(), + await page.locator('//*[@id="showchroma_server_grpc_port"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showchroma_server_host"]').click(); expect( - await page.locator('//*[@id="showchroma_server_host"]').isChecked(), + await page.locator('//*[@id="showchroma_server_host"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showchroma_server_http_port"]').click(); expect( - await page.locator('//*[@id="showchroma_server_http_port"]').isChecked(), + await page.locator('//*[@id="showchroma_server_http_port"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showchroma_server_ssl_enabled"]').click(); expect( - await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked(), + await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showindex_directory"]').click(); expect( - await page.locator('//*[@id="showindex_directory"]').isChecked(), + await page.locator('//*[@id="showindex_directory"]').isChecked() ).toBeTruthy(); let valueEditNode = await page @@ -152,7 +152,7 @@ test("InputComponent", async ({ page }) => { await page.locator('//*[@id="showcollection_name"]').click(); expect( - await page.locator('//*[@id="showcollection_name"]').isChecked(), + await page.locator('//*[@id="showcollection_name"]').isChecked() ).toBeTruthy(); await page.getByText("Save Changes", { exact: true }).click(); diff --git a/src/frontend/tests/end-to-end/inputListComponent.spec.ts b/src/frontend/tests/end-to-end/inputListComponent.spec.ts index 74c527892..6414c92db 100644 --- a/src/frontend/tests/end-to-end/inputListComponent.spec.ts +++ b/src/frontend/tests/end-to-end/inputListComponent.spec.ts @@ -41,19 +41,19 @@ test("InputListComponent", async ({ page }) => { await page.getByTestId("edit-button-modal").click(); expect( - await page.getByTestId("showmetadata_indexing_exclude").isChecked(), + await page.getByTestId("showmetadata_indexing_exclude").isChecked() ).toBeFalsy(); await page.getByTestId("showmetadata_indexing_exclude").click(); expect( - await page.getByTestId("showmetadata_indexing_exclude").isChecked(), + await page.getByTestId("showmetadata_indexing_exclude").isChecked() ).toBeTruthy(); expect( - await page.getByTestId("showmetadata_indexing_include").isChecked(), + await page.getByTestId("showmetadata_indexing_include").isChecked() ).toBeFalsy(); await page.getByTestId("showmetadata_indexing_include").click(); expect( - await page.getByTestId("showmetadata_indexing_include").isChecked(), + await page.getByTestId("showmetadata_indexing_include").isChecked() ).toBeTruthy(); await page @@ -93,7 +93,7 @@ test("InputListComponent", async ({ page }) => { .click(); const plusButtonLocator = page.getByTestId( - "input-list-plus-btn_metadata_indexing_include-1", + "input-list-plus-btn_metadata_indexing_include-1" ); const elementCount = await plusButtonLocator?.count(); @@ -164,12 +164,12 @@ test("InputListComponent", async ({ page }) => { .click(); const plusButtonLocatorEdit0 = await page.getByTestId( - "input-list-plus-btn-edit_metadata_indexing_include-0", + "input-list-plus-btn-edit_metadata_indexing_include-0" ); const elementCountEdit0 = await plusButtonLocatorEdit0?.count(); const plusButtonLocatorEdit2 = await page.getByTestId( - "input-list-plus-btn-edit_metadata_indexing_include-2", + "input-list-plus-btn-edit_metadata_indexing_include-2" ); const elementCountEdit2 = await plusButtonLocatorEdit2?.count(); @@ -178,13 +178,13 @@ test("InputListComponent", async ({ page }) => { } const minusButtonLocatorEdit1 = await page.getByTestId( - "input-list-minus-btn-edit_metadata_indexing_include-1", + "input-list-minus-btn-edit_metadata_indexing_include-1" ); const elementCountMinusEdit1 = await minusButtonLocatorEdit1?.count(); const minusButtonLocatorEdit2 = await page.getByTestId( - "input-list-minus-btn-edit_metadata_indexing_include-2", + "input-list-minus-btn-edit_metadata_indexing_include-2" ); const elementCountMinusEdit2 = await minusButtonLocatorEdit2?.count(); diff --git a/src/frontend/tests/end-to-end/keyPairListComponent.spec.ts b/src/frontend/tests/end-to-end/keyPairListComponent.spec.ts index 31a4521ae..165dd685b 100644 --- a/src/frontend/tests/end-to-end/keyPairListComponent.spec.ts +++ b/src/frontend/tests/end-to-end/keyPairListComponent.spec.ts @@ -81,7 +81,7 @@ test("KeypairListComponent", async ({ page }) => { expect(await page.locator('//*[@id="showcache"]').isChecked()).toBeFalsy(); await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeFalsy(); await page.getByText("Save Changes", { exact: true }).click(); @@ -96,7 +96,7 @@ test("KeypairListComponent", async ({ page }) => { await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showcache"]').click(); expect(await page.locator('//*[@id="showcache"]').isChecked()).toBeTruthy(); diff --git a/src/frontend/tests/end-to-end/langflowShortcuts.spec.ts b/src/frontend/tests/end-to-end/langflowShortcuts.spec.ts index 42b8744d1..58615dd55 100644 --- a/src/frontend/tests/end-to-end/langflowShortcuts.spec.ts +++ b/src/frontend/tests/end-to-end/langflowShortcuts.spec.ts @@ -61,7 +61,7 @@ test("LangflowShortcuts", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div[2]/div/div[1]/div/div[1]/div/div/div[1]', + '//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div[2]/div/div[1]/div/div[1]/div/div/div[1]' ) .click(); await page.keyboard.press("Backspace"); @@ -84,7 +84,7 @@ test("LangflowShortcuts", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div[2]/div/div[1]/div/div[1]/div/div/div[1]', + '//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div[2]/div/div[1]/div/div[1]/div/div/div[1]' ) .click(); await page.keyboard.press("Backspace"); diff --git a/src/frontend/tests/end-to-end/nestedComponent.spec.ts b/src/frontend/tests/end-to-end/nestedComponent.spec.ts index 2df9c1b7b..3cd319576 100644 --- a/src/frontend/tests/end-to-end/nestedComponent.spec.ts +++ b/src/frontend/tests/end-to-end/nestedComponent.spec.ts @@ -41,7 +41,7 @@ test("NestedComponent", async ({ page }) => { await page.locator('//*[@id="showpool_threads"]').click(); expect( - await page.locator('//*[@id="showpool_threads"]').isChecked(), + await page.locator('//*[@id="showpool_threads"]').isChecked() ).toBeTruthy(); //showtext_key @@ -53,140 +53,140 @@ test("NestedComponent", async ({ page }) => { await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeFalsy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeFalsy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeFalsy(); // showindex_name await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeTruthy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeTruthy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeTruthy(); // showindex_name await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeFalsy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeFalsy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeFalsy(); // showindex_name await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeTruthy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeTruthy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeTruthy(); // showindex_name await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeFalsy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeFalsy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeFalsy(); // showindex_name await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeTruthy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeTruthy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeTruthy(); //showpool_threads await page.locator('//*[@id="showpool_threads"]').click(); expect( - await page.locator('//*[@id="showpool_threads"]').isChecked(), + await page.locator('//*[@id="showpool_threads"]').isChecked() ).toBeFalsy(); //showtext_key await page.locator('//*[@id="showtext_key"]').click(); expect( - await page.locator('//*[@id="showtext_key"]').isChecked(), + await page.locator('//*[@id="showtext_key"]').isChecked() ).toBeTruthy(); await page.getByText("Save Changes", { exact: true }).click(); diff --git a/src/frontend/tests/end-to-end/store.spec.ts b/src/frontend/tests/end-to-end/store.spec.ts index 8e443ecdd..13963d698 100644 --- a/src/frontend/tests/end-to-end/store.spec.ts +++ b/src/frontend/tests/end-to-end/store.spec.ts @@ -262,7 +262,7 @@ test("should share component with share button", async ({ page }) => { await page.getByText("Set workflow status to public").isVisible(); await page .getByText( - "Attention: API keys in specified fields are automatically removed upon sharing.", + "Attention: API keys in specified fields are automatically removed upon sharing." ) .isVisible(); await page.getByText("Export").first().isVisible(); diff --git a/src/frontend/tests/end-to-end/textAreaModalComponent.spec.ts b/src/frontend/tests/end-to-end/textAreaModalComponent.spec.ts index 9443586e6..fa4248a63 100644 --- a/src/frontend/tests/end-to-end/textAreaModalComponent.spec.ts +++ b/src/frontend/tests/end-to-end/textAreaModalComponent.spec.ts @@ -51,7 +51,7 @@ test("TextAreaModalComponent", async ({ page }) => { await page .getByTestId("textarea-text") .fill( - "test test test test test test test test test test test !@#%*)( 123456789101010101010101111111111 !!!!!!!!!!", + "test test test test test test test test test test test !@#%*)( 123456789101010101010101111111111 !!!!!!!!!!" ); await page.getByTestId("textarea-text-ExternalLink").click(); diff --git a/src/frontend/tests/end-to-end/toggleComponent.spec.ts b/src/frontend/tests/end-to-end/toggleComponent.spec.ts index 58d909af4..5a8d9a36a 100644 --- a/src/frontend/tests/end-to-end/toggleComponent.spec.ts +++ b/src/frontend/tests/end-to-end/toggleComponent.spec.ts @@ -45,7 +45,7 @@ test("ToggleComponent", async ({ page }) => { await page.locator('//*[@id="showload_hidden"]').click(); expect( - await page.locator('//*[@id="showload_hidden"]').isChecked(), + await page.locator('//*[@id="showload_hidden"]').isChecked() ).toBeTruthy(); await page.getByText("Save Changes", { exact: true }).click(); @@ -81,12 +81,12 @@ test("ToggleComponent", async ({ page }) => { await page.locator('//*[@id="showload_hidden"]').click(); expect( - await page.locator('//*[@id="showload_hidden"]').isChecked(), + await page.locator('//*[@id="showload_hidden"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showmax_concurrency"]').click(); expect( - await page.locator('//*[@id="showmax_concurrency"]').isChecked(), + await page.locator('//*[@id="showmax_concurrency"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showpath"]').click(); @@ -94,22 +94,22 @@ test("ToggleComponent", async ({ page }) => { await page.locator('//*[@id="showrecursive"]').click(); expect( - await page.locator('//*[@id="showrecursive"]').isChecked(), + await page.locator('//*[@id="showrecursive"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showsilent_errors"]').click(); expect( - await page.locator('//*[@id="showsilent_errors"]').isChecked(), + await page.locator('//*[@id="showsilent_errors"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showuse_multithreading"]').click(); expect( - await page.locator('//*[@id="showuse_multithreading"]').isChecked(), + await page.locator('//*[@id="showuse_multithreading"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showmax_concurrency"]').click(); expect( - await page.locator('//*[@id="showmax_concurrency"]').isChecked(), + await page.locator('//*[@id="showmax_concurrency"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showpath"]').click(); @@ -117,17 +117,17 @@ test("ToggleComponent", async ({ page }) => { await page.locator('//*[@id="showrecursive"]').click(); expect( - await page.locator('//*[@id="showrecursive"]').isChecked(), + await page.locator('//*[@id="showrecursive"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showsilent_errors"]').click(); expect( - await page.locator('//*[@id="showsilent_errors"]').isChecked(), + await page.locator('//*[@id="showsilent_errors"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showuse_multithreading"]').click(); expect( - await page.locator('//*[@id="showuse_multithreading"]').isChecked(), + await page.locator('//*[@id="showuse_multithreading"]').isChecked() ).toBeFalsy(); await page.getByText("Save Changes", { exact: true }).click(); @@ -144,38 +144,38 @@ test("ToggleComponent", async ({ page }) => { await page.locator('//*[@id="showload_hidden"]').click(); expect( - await page.locator('//*[@id="showload_hidden"]').isChecked(), + await page.locator('//*[@id="showload_hidden"]').isChecked() ).toBeTruthy(); expect( - await page.getByTestId("toggle-edit-load_hidden").isChecked(), + await page.getByTestId("toggle-edit-load_hidden").isChecked() ).toBeTruthy(); await page.getByText("Save Changes", { exact: true }).click(); await page.getByTestId("toggle-load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle-load_hidden").isChecked() ).toBeFalsy(); await page.getByTestId("toggle-load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle-load_hidden").isChecked() ).toBeTruthy(); await page.getByTestId("toggle-load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle-load_hidden").isChecked() ).toBeFalsy(); await page.getByTestId("toggle-load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle-load_hidden").isChecked() ).toBeTruthy(); await page.getByTestId("toggle-load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle-load_hidden").isChecked() ).toBeFalsy(); } }); From 627c094907d223b33b1148bce1ed6b798ea78341 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 12:19:39 -0300 Subject: [PATCH 29/68] chore: Remove simple-git-hooks from package.json --- src/frontend/package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/frontend/package.json b/src/frontend/package.json index efcdd4b43..7f1d95664 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -85,9 +85,6 @@ "format": "npx prettier --write \"{tests,src}/**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore", "type-check": "tsc --noEmit --pretty --project tsconfig.json && vite" }, - "simple-git-hooks": { - "pre-commit": "npx pretty-quick --staged" - }, "eslintConfig": { "extends": [ "react-app", @@ -138,4 +135,4 @@ "ua-parser-js": "^1.0.37", "vite": "^4.5.2" } -} +} \ No newline at end of file From 383ac6e1af89c7d1e0415d7e5cff38969c6da3ba Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 12:19:56 -0300 Subject: [PATCH 30/68] Update memory example --- .../starter_projects/Langflow Memory Conversation.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json index 55733f720..1d1300e67 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json @@ -864,7 +864,7 @@ "show": true, "title_case": false, "type": "str", - "value": "" + "value": "OPENAI_API_KEY" }, "stream": { "advanced": true, @@ -1039,7 +1039,8 @@ "id": "ChatInput-Z9Rn6" } }, - "id": "reactflow__edge-ChatInput-Z9Rn6{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-Z9Rn6œ}-Prompt-kykM2{œfieldNameœ:œUserMessageœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" + "id": "reactflow__edge-ChatInput-Z9Rn6{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-Z9Rn6œ}-Prompt-kykM2{œfieldNameœ:œUserMessageœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "className": "" }, { "source": "MemoryComponent-u6m5G", @@ -1068,12 +1069,13 @@ "id": "MemoryComponent-u6m5G" } }, - "id": "reactflow__edge-MemoryComponent-u6m5G{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-u6m5Gœ}-Prompt-kykM2{œfieldNameœ:œContextœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" + "id": "reactflow__edge-MemoryComponent-u6m5G{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-u6m5Gœ}-Prompt-kykM2{œfieldNameœ:œContextœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "className": "" } ], "viewport": { "x": -511.79726701119625, - "y": 48.514712353620894, + "y": 49.514712353620894, "zoom": 0.4612356948928673 } }, From abb0f818433936c2e905deb875c8060722dbd615 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 12:21:19 -0300 Subject: [PATCH 31/68] chore: Remove pretty-quick from package.json and package-lock.json --- src/frontend/package-lock.json | 169 --------------------------------- src/frontend/package.json | 1 - 2 files changed, 170 deletions(-) diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 5ef96bea8..b4b86cfd1 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -105,7 +105,6 @@ "prettier": "^2.8.8", "prettier-plugin-organize-imports": "^3.2.3", "prettier-plugin-tailwindcss": "^0.3.0", - "pretty-quick": "^3.1.3", "simple-git-hooks": "^2.11.1", "tailwindcss": "^3.3.3", "tailwindcss-dotted-background": "^1.1.0", @@ -9956,15 +9955,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -10512,165 +10502,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pretty-quick": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.3.1.tgz", - "integrity": "sha512-3b36UXfYQ+IXXqex6mCca89jC8u0mYLqFAN5eTQKoXO6oCQYcIVYZEB/5AlBHI7JPYygReM2Vv6Vom/Gln7fBg==", - "dev": true, - "dependencies": { - "execa": "^4.1.0", - "find-up": "^4.1.0", - "ignore": "^5.3.0", - "mri": "^1.2.0", - "picocolors": "^1.0.0", - "picomatch": "^3.0.1", - "tslib": "^2.6.2" - }, - "bin": { - "pretty-quick": "dist/cli.js" - }, - "engines": { - "node": ">=10.13" - }, - "peerDependencies": { - "prettier": "^2.0.0" - } - }, - "node_modules/pretty-quick/node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/pretty-quick/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-quick/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-quick/node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "engines": { - "node": ">=8.12.0" - } - }, - "node_modules/pretty-quick/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-quick/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-quick/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-quick/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-quick/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-quick/node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", diff --git a/src/frontend/package.json b/src/frontend/package.json index 7f1d95664..ca1717f26 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -127,7 +127,6 @@ "prettier": "^2.8.8", "prettier-plugin-organize-imports": "^3.2.3", "prettier-plugin-tailwindcss": "^0.3.0", - "pretty-quick": "^3.1.3", "simple-git-hooks": "^2.11.1", "tailwindcss": "^3.3.3", "tailwindcss-dotted-background": "^1.1.0", From a6c9fe84670f23b62e8b9b86d00226e6c61a1d94 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 12:23:46 -0300 Subject: [PATCH 32/68] Update memory example params --- .../Langflow Memory Conversation.json | 2160 ++++++++--------- 1 file changed, 1080 insertions(+), 1080 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json index 1d1300e67..d1bd09650 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json @@ -1,1086 +1,1086 @@ { - "id": "321b1bab-8691-42da-9689-1f12b5d2a48b", - "data": { - "nodes": [ - { - "data": { - "id": "ChatInput-Z9Rn6", - "node": { - "base_classes": [ - "Text", - "object", - "Record", - "str" - ], - "beta": false, - "custom_fields": { - "input_value": null, - "return_record": null, - "sender": null, - "sender_name": null, - "session_id": null - }, - "description": "Get chat inputs from the Playground.", - "display_name": "Chat Input", - "documentation": "", - "field_formatters": {}, - "field_order": [], - "frozen": false, - "icon": "ChatInput", - "output_types": [ - "Message", - "Text" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" - }, - "input_value": { - "advanced": false, - "display_name": "Text", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "input_value", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "do you know his name?" - }, - "sender": { - "advanced": true, - "display_name": "Sender Type", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, - "name": "sender", - "options": [ - "Machine", - "User" - ], - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "User" - }, - "sender_name": { - "advanced": false, - "display_name": "Sender Name", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "sender_name", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "User" - }, - "session_id": { - "advanced": false, - "display_name": "Session ID", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "session_id", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "MySessionID" - } - } - }, - "type": "ChatInput" - }, - "dragging": false, - "height": 477, - "id": "ChatInput-Z9Rn6", - "position": { - "x": 1283.2700598313072, - "y": 982.5953650473145 - }, - "positionAbsolute": { - "x": 1283.2700598313072, - "y": 982.5953650473145 - }, - "selected": false, - "type": "genericNode", - "width": 384 + "data": { + "edges": [ + { + "className": "", + "data": { + "sourceHandle": { + "baseClasses": [ + "str", + "object", + "Text" + ], + "dataType": "OpenAIModel", + "id": "OpenAIModel-Neuec" + }, + "targetHandle": { + "fieldName": "input_value", + "id": "ChatOutput-cVR7W", + "inputTypes": [ + "Text" + ], + "type": "str" + } + }, + "id": "reactflow__edge-OpenAIModel-Neuec{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Neuecœ}-ChatOutput-cVR7W{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-cVR7Wœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", + "source": "OpenAIModel-Neuec", + "sourceHandle": "{œbaseClassesœ: [œstrœ, œobjectœ, œTextœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-Neuecœ}", + "style": { + "stroke": "#555" + }, + "target": "ChatOutput-cVR7W", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-cVR7Wœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "baseClasses": [ + "object", + "str", + "Text" + ], + "dataType": "Prompt", + "id": "Prompt-kykM2" + }, + "targetHandle": { + "fieldName": "input_value", + "id": "OpenAIModel-Neuec", + "inputTypes": [ + "Text", + "Record", + "Prompt" + ], + "type": "str" + } + }, + "id": "reactflow__edge-Prompt-kykM2{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-kykM2œ}-OpenAIModel-Neuec{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Neuecœ,œinputTypesœ:[œTextœ,œRecordœ,œPromptœ],œtypeœ:œstrœ}", + "source": "Prompt-kykM2", + "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-kykM2œ}", + "target": "OpenAIModel-Neuec", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-Neuecœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "baseClasses": [ + "Text", + "object", + "Record", + "str" + ], + "dataType": "ChatInput", + "id": "ChatInput-Z9Rn6" + }, + "targetHandle": { + "fieldName": "UserMessage", + "id": "Prompt-kykM2", + "inputTypes": [ + "Document", + "Message", + "Record", + "Text" + ], + "type": "str" + } + }, + "id": "reactflow__edge-ChatInput-Z9Rn6{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-Z9Rn6œ}-Prompt-kykM2{œfieldNameœ:œUserMessageœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "source": "ChatInput-Z9Rn6", + "sourceHandle": "{œbaseClassesœ: [œTextœ, œobjectœ, œRecordœ, œstrœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-Z9Rn6œ}", + "target": "Prompt-kykM2", + "targetHandle": "{œfieldNameœ: œUserMessageœ, œidœ: œPrompt-kykM2œ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "baseClasses": [ + "str", + "Text", + "object" + ], + "dataType": "MemoryComponent", + "id": "MemoryComponent-u6m5G" + }, + "targetHandle": { + "fieldName": "Context", + "id": "Prompt-kykM2", + "inputTypes": [ + "Document", + "Message", + "Record", + "Text" + ], + "type": "str" + } + }, + "id": "reactflow__edge-MemoryComponent-u6m5G{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-u6m5Gœ}-Prompt-kykM2{œfieldNameœ:œContextœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "source": "MemoryComponent-u6m5G", + "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œMemoryComponentœ, œidœ: œMemoryComponent-u6m5Gœ}", + "target": "Prompt-kykM2", + "targetHandle": "{œfieldNameœ: œContextœ, œidœ: œPrompt-kykM2œ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + } + ], + "nodes": [ + { + "data": { + "id": "ChatInput-Z9Rn6", + "node": { + "base_classes": [ + "Text", + "object", + "Record", + "str" + ], + "beta": false, + "custom_fields": { + "input_value": null, + "return_record": null, + "sender": null, + "sender_name": null, + "session_id": null }, - { - "data": { - "id": "ChatOutput-cVR7W", - "node": { - "base_classes": [ - "Text", - "object", - "Record", - "str" - ], - "beta": false, - "custom_fields": { - "input_value": null, - "return_record": null, - "sender": null, - "sender_name": null, - "session_id": null - }, - "description": "Display a chat message in the Playground.", - "display_name": "Chat Output", - "documentation": "", - "field_formatters": {}, - "field_order": [], - "frozen": false, - "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" - }, - "input_value": { - "advanced": false, - "display_name": "Text", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "input_value", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str" - }, - "sender": { - "advanced": true, - "display_name": "Sender Type", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, - "name": "sender", - "options": [ - "Machine", - "User" - ], - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "Machine" - }, - "sender_name": { - "advanced": false, - "display_name": "Sender Name", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "sender_name", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "AI" - }, - "session_id": { - "advanced": false, - "display_name": "Session ID", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "session_id", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "MySessionID" - } - } - }, - "type": "ChatOutput" - }, - "dragging": false, - "height": 485, - "id": "ChatOutput-cVR7W", - "position": { - "x": 3154.916355514023, - "y": 851.051882666333 - }, - "positionAbsolute": { - "x": 3154.916355514023, - "y": 851.051882666333 - }, - "selected": false, - "type": "genericNode", - "width": 384 - }, - { - "data": { - "description": "Retrieves stored chat messages given a specific Session ID.", - "display_name": "Chat Memory", - "id": "MemoryComponent-u6m5G", - "node": { - "base_classes": [ - "str", - "Text", - "object" - ], - "beta": true, - "custom_fields": { - "n_messages": null, - "order": null, - "record_template": null, - "sender": null, - "sender_name": null, - "session_id": null - }, - "description": "Retrieves stored chat messages given a specific Session ID.", - "display_name": "Chat Memory", - "documentation": "", - "field_formatters": {}, - "field_order": [], - "frozen": false, - "icon": "history", - "output_types": [ - "Text" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.memory.memory import BaseMemoryComponent\nfrom langflow.field_typing import Text\nfrom langflow.helpers.record import messages_to_text\nfrom langflow.memory import get_messages\nfrom langflow.schema.message import Message\n\n\nclass MemoryComponent(BaseMemoryComponent):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages given a specific Session ID.\"\n beta: bool = True\n icon = \"history\"\n\n def build_config(self):\n return {\n \"sender\": {\n \"options\": [\"Machine\", \"User\", \"Machine and User\"],\n \"display_name\": \"Sender Type\",\n },\n \"sender_name\": {\"display_name\": \"Sender Name\", \"advanced\": True},\n \"n_messages\": {\n \"display_name\": \"Number of Messages\",\n \"info\": \"Number of messages to retrieve.\",\n },\n \"session_id\": {\n \"display_name\": \"Session ID\",\n \"info\": \"Session ID of the chat history.\",\n \"input_types\": [\"Text\"],\n },\n \"order\": {\n \"options\": [\"Ascending\", \"Descending\"],\n \"display_name\": \"Order\",\n \"info\": \"Order of the messages.\",\n \"advanced\": True,\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def get_messages(self, **kwargs) -> list[Message]:\n # Validate kwargs by checking if it contains the correct keys\n if \"sender\" not in kwargs:\n kwargs[\"sender\"] = None\n if \"sender_name\" not in kwargs:\n kwargs[\"sender_name\"] = None\n if \"session_id\" not in kwargs:\n kwargs[\"session_id\"] = None\n if \"limit\" not in kwargs:\n kwargs[\"limit\"] = 5\n if \"order\" not in kwargs:\n kwargs[\"order\"] = \"Descending\"\n\n kwargs[\"order\"] = \"DESC\" if kwargs[\"order\"] == \"Descending\" else \"ASC\"\n if kwargs[\"sender\"] == \"Machine and User\":\n kwargs[\"sender\"] = None\n return get_messages(**kwargs)\n\n def build(\n self,\n sender: Optional[str] = \"Machine and User\",\n sender_name: Optional[str] = None,\n session_id: Optional[str] = None,\n n_messages: int = 5,\n order: Optional[str] = \"Descending\",\n record_template: Optional[str] = \"{sender_name}: {text}\",\n ) -> Text:\n messages = self.get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n messages_str = messages_to_text(template=record_template or \"\", messages=messages)\n self.status = messages_str\n return messages_str\n" - }, - "n_messages": { - "advanced": false, - "display_name": "Number of Messages", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Number of messages to retrieve.", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "n_messages", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "int", - "value": 5 - }, - "order": { - "advanced": true, - "display_name": "Order", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Order of the messages.", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, - "name": "order", - "options": [ - "Ascending", - "Descending" - ], - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "Descending" - }, - "record_template": { - "advanced": true, - "display_name": "Record Template", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "record_template", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "{sender_name}: {text}" - }, - "sender": { - "advanced": false, - "display_name": "Sender Type", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, - "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" - ], - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "Machine and User" - }, - "sender_name": { - "advanced": true, - "display_name": "Sender Name", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "sender_name", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str" - }, - "session_id": { - "advanced": false, - "display_name": "Session ID", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Session ID of the chat history.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "session_id", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "MySessionID" - } - } - }, - "type": "MemoryComponent" - }, - "dragging": false, - "height": 505, - "id": "MemoryComponent-u6m5G", - "position": { - "x": 1289.9606870058817, - "y": 442.16804561053766 - }, - "positionAbsolute": { - "x": 1289.9606870058817, - "y": 442.16804561053766 - }, - "selected": false, - "type": "genericNode", - "width": 384 - }, - { - "data": { - "description": "Create a prompt template with dynamic variables.", - "display_name": "Prompt", - "id": "Prompt-kykM2", - "node": { - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" - }, - "template": { - "advanced": false, - "display_name": "Template", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "template", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "prompt", - "value": "Previous messages:\n{Context}\n\nUser: {UserMessage}\nAI: " - }, - "Context": { - "field_type": "str", - "required": false, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "Context", - "display_name": "Context", - "advanced": false, - "input_types": [ - "Document", - "Message", - "Record", - "Text" - ], - "dynamic": false, - "info": "", - "load_from_db": false, - "title_case": false, - "type": "str" - }, - "UserMessage": { - "field_type": "str", - "required": false, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "UserMessage", - "display_name": "UserMessage", - "advanced": false, - "input_types": [ - "Document", - "Message", - "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": [ - "Context", - "UserMessage" - ] - }, - "output_types": [ - "Prompt" - ], - "full_path": null, - "field_formatters": {}, - "frozen": false, - "field_order": [], - "beta": false, - "error": null - }, - "type": "Prompt" - }, - "dragging": false, - "height": 513, - "id": "Prompt-kykM2", - "position": { - "x": 1890.2582485007167, - "y": 753.3797365481901 - }, - "positionAbsolute": { - "x": 1890.2582485007167, - "y": 753.3797365481901 - }, - "selected": true, - "type": "genericNode", - "width": 384 - }, - { - "data": { - "id": "OpenAIModel-Neuec", - "node": { - "base_classes": [ - "str", - "object", - "Text" - ], - "beta": false, - "custom_fields": { - "input_value": null, - "max_tokens": null, - "model_kwargs": null, - "model_name": null, - "openai_api_base": null, - "openai_api_key": null, - "stream": null, - "system_message": null, - "temperature": null - }, - "description": "Generates text using OpenAI LLMs.", - "display_name": "OpenAI", - "documentation": "", - "field_formatters": {}, - "field_order": [ - "max_tokens", - "model_kwargs", - "model_name", - "openai_api_base", - "openai_api_key", - "temperature", - "input_value", - "system_message", - "stream" - ], - "frozen": false, - "icon": "OpenAI", - "output_types": [ - "Text" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" - }, - "input_value": { - "advanced": false, - "display_name": "Input", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "input_value", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "str" - }, - "max_tokens": { - "advanced": true, - "display_name": "Max Tokens", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "max_tokens", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "int", - "value": 256 - }, - "model_kwargs": { - "advanced": true, - "display_name": "Model Kwargs", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "model_kwargs", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "NestedDict", - "value": {} - }, - "model_name": { - "advanced": false, - "display_name": "Model Name", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, - "name": "model_name", - "options": [ - "gpt-4o", - "gpt-4-turbo", - "gpt-4-turbo-preview", - "gpt-3.5-turbo", - "gpt-3.5-turbo-0125" - ], - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "gpt-3.5-turbo" - }, - "openai_api_base": { - "advanced": true, - "display_name": "OpenAI API Base", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "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.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "openai_api_base", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str" - }, - "openai_api_key": { - "advanced": false, - "display_name": "OpenAI API Key", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": true, - "multiline": false, - "name": "openai_api_key", - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "str", - "value": "OPENAI_API_KEY" - }, - "stream": { - "advanced": true, - "display_name": "Stream", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Stream the response from the model. Streaming works only in Chat.", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "stream", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "bool", - "value": false - }, - "system_message": { - "advanced": true, - "display_name": "System Message", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "system_message", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str" - }, - "temperature": { - "advanced": false, - "display_name": "Temperature", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "temperature", - "password": false, - "placeholder": "", - "rangeSpec": { - "max": 1, - "min": -1, - "step": 0.1, - "step_type": "float" - }, - "required": false, - "show": true, - "title_case": false, - "type": "float", - "value": "0.2" - } - } - }, - "type": "OpenAIModel" - }, - "dragging": false, - "height": 571, - "id": "OpenAIModel-Neuec", - "position": { - "x": 2561.5850334731617, - "y": 553.2745131130916 - }, - "positionAbsolute": { - "x": 2561.5850334731617, - "y": 553.2745131130916 - }, - "selected": false, - "type": "genericNode", - "width": 384 + "description": "Get chat inputs from the Playground.", + "display_name": "Chat Input", + "documentation": "", + "field_formatters": {}, + "field_order": [], + "frozen": false, + "icon": "ChatInput", + "output_types": [ + "Message", + "Text" + ], + "template": { + "_type": "CustomComponent", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" + }, + "input_value": { + "advanced": false, + "display_name": "Text", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "input_value", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "do you know his name?" + }, + "sender": { + "advanced": true, + "display_name": "Sender Type", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": true, + "load_from_db": false, + "multiline": false, + "name": "sender", + "options": [ + "Machine", + "User" + ], + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "User" + }, + "sender_name": { + "advanced": false, + "display_name": "Sender Name", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "sender_name", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "User" + }, + "session_id": { + "advanced": false, + "display_name": "Session ID", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "If provided, the message will be stored in the memory.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "session_id", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "MySessionID" + } } - ], - "edges": [ - { - "className": "", - "data": { - "sourceHandle": { - "baseClasses": [ - "str", - "object", - "Text" - ], - "dataType": "OpenAIModel", - "id": "OpenAIModel-Neuec" - }, - "targetHandle": { - "fieldName": "input_value", - "id": "ChatOutput-cVR7W", - "inputTypes": [ - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-OpenAIModel-Neuec{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Neuecœ}-ChatOutput-cVR7W{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-cVR7Wœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", - "source": "OpenAIModel-Neuec", - "sourceHandle": "{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Neuecœ}", - "style": { - "stroke": "#555" - }, - "target": "ChatOutput-cVR7W", - "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-cVR7Wœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" + }, + "type": "ChatInput" + }, + "dragging": false, + "height": 477, + "id": "ChatInput-Z9Rn6", + "position": { + "x": 1283.2700598313072, + "y": 982.5953650473145 + }, + "positionAbsolute": { + "x": 1283.2700598313072, + "y": 982.5953650473145 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "ChatOutput-cVR7W", + "node": { + "base_classes": [ + "Text", + "object", + "Record", + "str" + ], + "beta": false, + "custom_fields": { + "input_value": null, + "return_record": null, + "sender": null, + "sender_name": null, + "session_id": null }, - { - "source": "Prompt-kykM2", - "sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-kykM2œ}", - "target": "OpenAIModel-Neuec", - "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Neuecœ,œinputTypesœ:[œTextœ,œRecordœ,œPromptœ],œtypeœ:œstrœ}", - "data": { - "targetHandle": { - "fieldName": "input_value", - "id": "OpenAIModel-Neuec", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], - "type": "str" - }, - "sourceHandle": { - "baseClasses": [ - "object", - "str", - "Text" - ], - "dataType": "Prompt", - "id": "Prompt-kykM2" - } - }, - "id": "reactflow__edge-Prompt-kykM2{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-kykM2œ}-OpenAIModel-Neuec{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Neuecœ,œinputTypesœ:[œTextœ,œRecordœ,œPromptœ],œtypeœ:œstrœ}", - "className": "" - }, - { - "source": "ChatInput-Z9Rn6", - "sourceHandle": "{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-Z9Rn6œ}", - "target": "Prompt-kykM2", - "targetHandle": "{œfieldNameœ:œUserMessageœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "data": { - "targetHandle": { - "fieldName": "UserMessage", - "id": "Prompt-kykM2", - "inputTypes": [ - "Document", - "Message", - "Record", - "Text" - ], - "type": "str" - }, - "sourceHandle": { - "baseClasses": [ - "Text", - "object", - "Record", - "str" - ], - "dataType": "ChatInput", - "id": "ChatInput-Z9Rn6" - } - }, - "id": "reactflow__edge-ChatInput-Z9Rn6{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-Z9Rn6œ}-Prompt-kykM2{œfieldNameœ:œUserMessageœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "className": "" - }, - { - "source": "MemoryComponent-u6m5G", - "sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-u6m5Gœ}", - "target": "Prompt-kykM2", - "targetHandle": "{œfieldNameœ:œContextœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "data": { - "targetHandle": { - "fieldName": "Context", - "id": "Prompt-kykM2", - "inputTypes": [ - "Document", - "Message", - "Record", - "Text" - ], - "type": "str" - }, - "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], - "dataType": "MemoryComponent", - "id": "MemoryComponent-u6m5G" - } - }, - "id": "reactflow__edge-MemoryComponent-u6m5G{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-u6m5Gœ}-Prompt-kykM2{œfieldNameœ:œContextœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "className": "" + "description": "Display a chat message in the Playground.", + "display_name": "Chat Output", + "documentation": "", + "field_formatters": {}, + "field_order": [], + "frozen": false, + "icon": "ChatOutput", + "output_types": [ + "Message", + "Text" + ], + "template": { + "_type": "CustomComponent", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" + }, + "input_value": { + "advanced": false, + "display_name": "Text", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "input_value", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str" + }, + "sender": { + "advanced": true, + "display_name": "Sender Type", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": true, + "load_from_db": false, + "multiline": false, + "name": "sender", + "options": [ + "Machine", + "User" + ], + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "Machine" + }, + "sender_name": { + "advanced": false, + "display_name": "Sender Name", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "sender_name", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "AI" + }, + "session_id": { + "advanced": false, + "display_name": "Session ID", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "If provided, the message will be stored in the memory.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "session_id", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "MySessionID" + } } - ], - "viewport": { - "x": -511.79726701119625, - "y": 49.514712353620894, - "zoom": 0.4612356948928673 - } - }, - "description": "This project can be used as a starting point for building a Chat experience with user specific memory. You can set a different Session ID to start a new message history.", - "name": "Memory Chatbot", - "last_tested_version": "1.0.0a54", - "is_component": false + }, + "type": "ChatOutput" + }, + "dragging": false, + "height": 485, + "id": "ChatOutput-cVR7W", + "position": { + "x": 3154.916355514023, + "y": 851.051882666333 + }, + "positionAbsolute": { + "x": 3154.916355514023, + "y": 851.051882666333 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Retrieves stored chat messages given a specific Session ID.", + "display_name": "Chat Memory", + "id": "MemoryComponent-u6m5G", + "node": { + "base_classes": [ + "str", + "Text", + "object" + ], + "beta": true, + "custom_fields": { + "n_messages": null, + "order": null, + "record_template": null, + "sender": null, + "sender_name": null, + "session_id": null + }, + "description": "Retrieves stored chat messages given a specific Session ID.", + "display_name": "Chat Memory", + "documentation": "", + "field_formatters": {}, + "field_order": [], + "frozen": false, + "icon": "history", + "output_types": [ + "Text" + ], + "template": { + "_type": "CustomComponent", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from typing import Optional\n\nfrom langflow.base.memory.memory import BaseMemoryComponent\nfrom langflow.field_typing import Text\nfrom langflow.helpers.record import messages_to_text\nfrom langflow.memory import get_messages\nfrom langflow.schema.message import Message\n\n\nclass MemoryComponent(BaseMemoryComponent):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages given a specific Session ID.\"\n beta: bool = True\n icon = \"history\"\n\n def build_config(self):\n return {\n \"sender\": {\n \"options\": [\"Machine\", \"User\", \"Machine and User\"],\n \"display_name\": \"Sender Type\",\n },\n \"sender_name\": {\"display_name\": \"Sender Name\", \"advanced\": True},\n \"n_messages\": {\n \"display_name\": \"Number of Messages\",\n \"info\": \"Number of messages to retrieve.\",\n },\n \"session_id\": {\n \"display_name\": \"Session ID\",\n \"info\": \"Session ID of the chat history.\",\n \"input_types\": [\"Text\"],\n },\n \"order\": {\n \"options\": [\"Ascending\", \"Descending\"],\n \"display_name\": \"Order\",\n \"info\": \"Order of the messages.\",\n \"advanced\": True,\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def get_messages(self, **kwargs) -> list[Message]:\n # Validate kwargs by checking if it contains the correct keys\n if \"sender\" not in kwargs:\n kwargs[\"sender\"] = None\n if \"sender_name\" not in kwargs:\n kwargs[\"sender_name\"] = None\n if \"session_id\" not in kwargs:\n kwargs[\"session_id\"] = None\n if \"limit\" not in kwargs:\n kwargs[\"limit\"] = 5\n if \"order\" not in kwargs:\n kwargs[\"order\"] = \"Descending\"\n\n kwargs[\"order\"] = \"DESC\" if kwargs[\"order\"] == \"Descending\" else \"ASC\"\n if kwargs[\"sender\"] == \"Machine and User\":\n kwargs[\"sender\"] = None\n return get_messages(**kwargs)\n\n def build(\n self,\n sender: Optional[str] = \"Machine and User\",\n sender_name: Optional[str] = None,\n session_id: Optional[str] = None,\n n_messages: int = 5,\n order: Optional[str] = \"Descending\",\n record_template: Optional[str] = \"{sender_name}: {text}\",\n ) -> Text:\n messages = self.get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n messages_str = messages_to_text(template=record_template or \"\", messages=messages)\n self.status = messages_str\n return messages_str\n" + }, + "n_messages": { + "advanced": false, + "display_name": "Number of Messages", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "Number of messages to retrieve.", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "n_messages", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "int", + "value": 5 + }, + "order": { + "advanced": true, + "display_name": "Order", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "Order of the messages.", + "input_types": [ + "Text" + ], + "list": true, + "load_from_db": false, + "multiline": false, + "name": "order", + "options": [ + "Ascending", + "Descending" + ], + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "Descending" + }, + "record_template": { + "advanced": true, + "display_name": "Record Template", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "record_template", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "{sender_name}: {text}" + }, + "sender": { + "advanced": false, + "display_name": "Sender Type", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": true, + "load_from_db": false, + "multiline": false, + "name": "sender", + "options": [ + "Machine", + "User", + "Machine and User" + ], + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "Machine and User" + }, + "sender_name": { + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "sender_name", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str" + }, + "session_id": { + "advanced": false, + "display_name": "Session ID", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "Session ID of the chat history.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "session_id", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "MySessionID" + } + } + }, + "type": "MemoryComponent" + }, + "dragging": false, + "height": 505, + "id": "MemoryComponent-u6m5G", + "position": { + "x": 1289.9606870058817, + "y": 442.16804561053766 + }, + "positionAbsolute": { + "x": 1289.9606870058817, + "y": 442.16804561053766 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "id": "Prompt-kykM2", + "node": { + "base_classes": [ + "object", + "str", + "Text" + ], + "beta": false, + "custom_fields": { + "template": [ + "Context", + "UserMessage" + ] + }, + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "documentation": "", + "error": null, + "field_formatters": {}, + "field_order": [], + "frozen": false, + "full_path": null, + "icon": "prompts", + "is_composition": null, + "is_input": null, + "is_output": null, + "name": "", + "output_types": [ + "Prompt" + ], + "template": { + "Context": { + "advanced": false, + "display_name": "Context", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Document", + "Message", + "Record", + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "Context", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "UserMessage": { + "advanced": false, + "display_name": "UserMessage", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Document", + "Message", + "Record", + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "UserMessage", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "_type": "CustomComponent", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" + }, + "template": { + "advanced": false, + "display_name": "Template", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "template", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "prompt", + "value": "Previous messages:\n{Context}\n\nUser: {UserMessage}\nAI: " + } + } + }, + "type": "Prompt" + }, + "dragging": false, + "height": 513, + "id": "Prompt-kykM2", + "position": { + "x": 1890.2582485007167, + "y": 753.3797365481901 + }, + "positionAbsolute": { + "x": 1890.2582485007167, + "y": 753.3797365481901 + }, + "selected": true, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "OpenAIModel-Neuec", + "node": { + "base_classes": [ + "str", + "object", + "Text" + ], + "beta": false, + "custom_fields": { + "input_value": null, + "max_tokens": null, + "model_kwargs": null, + "model_name": null, + "openai_api_base": null, + "openai_api_key": null, + "stream": null, + "system_message": null, + "temperature": null + }, + "description": "Generates text using OpenAI LLMs.", + "display_name": "OpenAI", + "documentation": "", + "field_formatters": {}, + "field_order": [ + "max_tokens", + "model_kwargs", + "model_name", + "openai_api_base", + "openai_api_key", + "temperature", + "input_value", + "system_message", + "stream" + ], + "frozen": false, + "icon": "OpenAI", + "output_types": [ + "Text" + ], + "template": { + "_type": "CustomComponent", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" + }, + "input_value": { + "advanced": false, + "display_name": "Input", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text", + "Record", + "Prompt" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "input_value", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str" + }, + "max_tokens": { + "advanced": true, + "display_name": "Max Tokens", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "max_tokens", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "int", + "value": 256 + }, + "model_kwargs": { + "advanced": true, + "display_name": "Model Kwargs", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "model_kwargs", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "NestedDict", + "value": {} + }, + "model_name": { + "advanced": false, + "display_name": "Model Name", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Text" + ], + "list": true, + "load_from_db": false, + "multiline": false, + "name": "model_name", + "options": [ + "gpt-4o", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-3.5-turbo", + "gpt-3.5-turbo-0125" + ], + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "gpt-3.5-turbo" + }, + "openai_api_base": { + "advanced": true, + "display_name": "OpenAI API Base", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "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.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "openai_api_base", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str" + }, + "openai_api_key": { + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "The OpenAI API Key to use for the OpenAI model.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": true, + "multiline": false, + "name": "openai_api_key", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "OPENAI_API_KEY" + }, + "stream": { + "advanced": true, + "display_name": "Stream", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "Stream the response from the model. Streaming works only in Chat.", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "stream", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "bool", + "value": false + }, + "system_message": { + "advanced": true, + "display_name": "System Message", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "System message to pass to the model.", + "input_types": [ + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": false, + "name": "system_message", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str" + }, + "temperature": { + "advanced": false, + "display_name": "Temperature", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": false, + "name": "temperature", + "password": false, + "placeholder": "", + "rangeSpec": { + "max": 1, + "min": -1, + "step": 0.1, + "step_type": "float" + }, + "required": false, + "show": true, + "title_case": false, + "type": "float", + "value": "0.2" + } + } + }, + "type": "OpenAIModel" + }, + "dragging": false, + "height": 571, + "id": "OpenAIModel-Neuec", + "position": { + "x": 2561.5850334731617, + "y": 553.2745131130916 + }, + "positionAbsolute": { + "x": 2561.5850334731617, + "y": 553.2745131130916 + }, + "selected": false, + "type": "genericNode", + "width": 384 + } + ], + "viewport": { + "x": -511.79726701119625, + "y": 49.514712353620894, + "zoom": 0.4612356948928673 + } + }, + "description": "This project can be used as a starting point for building a Chat experience with user specific memory. You can set a different Session ID to start a new message history.", + "id": "321b1bab-8691-42da-9689-1f12b5d2a48b", + "is_component": false, + "last_tested_version": "1.0.0a54", + "name": "Memory Chatbot" } \ No newline at end of file From 5f14aece1a5cbbdc450b168f6b4dd56eb3605b74 Mon Sep 17 00:00:00 2001 From: italojohnny Date: Thu, 13 Jun 2024 12:30:20 -0300 Subject: [PATCH 33/68] bugfix --- src/backend/base/langflow/graph/utils.py | 3 +-- src/backend/base/langflow/interface/initialize/loading.py | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/graph/utils.py b/src/backend/base/langflow/graph/utils.py index 06b7ca90a..89db4f0aa 100644 --- a/src/backend/base/langflow/graph/utils.py +++ b/src/backend/base/langflow/graph/utils.py @@ -65,9 +65,8 @@ def serialize_field(value): return value -def get_artifact_type(custom_component, build_result) -> str: +def get_artifact_type(value, build_result) -> str: result = ArtifactType.UNKNOWN - value = custom_component.repr_value match value: case Record(): result = ArtifactType.RECORD diff --git a/src/backend/base/langflow/interface/initialize/loading.py b/src/backend/base/langflow/interface/initialize/loading.py index afe653e42..67403c2fe 100644 --- a/src/backend/base/langflow/interface/initialize/loading.py +++ b/src/backend/base/langflow/interface/initialize/loading.py @@ -132,11 +132,12 @@ async def instantiate_custom_component(params, user_id, vertex, fallback_to_env_ raw = custom_component.repr_value if hasattr(raw, "data") and raw is not None: raw = raw.data - elif hasattr(raw, "model_dump") and raw is not None: raw = raw.model_dump() + if raw is None and isinstance(build_result, (dict, Record, str)): + raw = build_result.data if isinstance(build_result, Record) else build_result - artifact_type = get_artifact_type(custom_component, build_result) + artifact_type = get_artifact_type(custom_component.repr_value or raw, build_result) raw = post_process_raw(raw, artifact_type) artifact = {"repr": custom_repr, "raw": raw, "type": artifact_type} return custom_component, build_result, artifact From 7313e5807b73dcfd87d0da29b9277ddfd59a7b30 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 16:08:58 -0300 Subject: [PATCH 34/68] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(foldersStore.tsx):?= =?UTF-8?q?=20remove=20redundant=20loadingById=20state=20and=20setLoadingB?= =?UTF-8?q?yId=20function=20=F0=9F=90=9B=20(foldersStore.tsx):=20fix=20fal?= =?UTF-8?q?lback=20to=20getFoldersApi=20on=20getFolderById=20failure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/stores/foldersStore.tsx | 15 ++++++--------- src/frontend/src/types/zustand/folders/index.ts | 2 -- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/frontend/src/stores/foldersStore.tsx b/src/frontend/src/stores/foldersStore.tsx index 190fc3212..ce858c3be 100644 --- a/src/frontend/src/stores/foldersStore.tsx +++ b/src/frontend/src/stores/foldersStore.tsx @@ -17,18 +17,18 @@ export const useFolderStore = create((set, get) => ({ getFolders().then( (res) => { const foldersWithoutStarterProjects = res?.filter( - (folder) => folder.name !== STARTER_FOLDER_NAME + (folder) => folder.name !== STARTER_FOLDER_NAME, ); const starterProjects = res?.find( - (folder) => folder.name === STARTER_FOLDER_NAME + (folder) => folder.name === STARTER_FOLDER_NAME, ); set({ starterProjectId: starterProjects!.id ?? "" }); set({ folders: foldersWithoutStarterProjects }); const myCollectionId = res?.find( - (f) => f.name === DEFAULT_FOLDER + (f) => f.name === DEFAULT_FOLDER, )?.id; set({ myCollectionId }); @@ -45,7 +45,7 @@ export const useFolderStore = create((set, get) => ({ set({ folders: [] }); get().setLoading(false); reject(error); - } + }, ); } }); @@ -54,24 +54,21 @@ export const useFolderStore = create((set, get) => ({ loading: false, setLoading: (loading) => set(() => ({ loading: loading })), getFolderById: (id) => { - get().setLoadingById(true); if (id) { getFolderById(id).then( (res) => { const setAllFlows = useFlowsManagerStore.getState().setAllFlows; setAllFlows(res.flows); set({ selectedFolder: res }); - get().setLoadingById(false); }, () => { - get().setLoadingById(false); - } + get().getFoldersApi(true); + }, ); } }, selectedFolder: null, loadingById: false, - setLoadingById: (loading) => set(() => ({ loadingById: loading })), getMyCollectionFolder: () => { const folders = get().folders; const myCollectionId = folders?.find((f) => f.name === DEFAULT_FOLDER)?.id; diff --git a/src/frontend/src/types/zustand/folders/index.ts b/src/frontend/src/types/zustand/folders/index.ts index 2c5b2b96b..5e41677b2 100644 --- a/src/frontend/src/types/zustand/folders/index.ts +++ b/src/frontend/src/types/zustand/folders/index.ts @@ -8,8 +8,6 @@ export type FoldersStoreType = { setLoading: (loading: boolean) => void; selectedFolder: FolderType | null; getFolderById: (id: string) => void; - loadingById: boolean; - setLoadingById: (loading: boolean) => void; getMyCollectionFolder: () => void; myCollectionFlows: FolderType | null; myCollectionId: string | null; From 5de41ecb3a924c3f638380558ccf7d9b664d7ce9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 13 Jun 2024 13:36:48 -0700 Subject: [PATCH 35/68] =?UTF-8?q?=F0=9F=90=9B=20(service.py):=20Fix=20hand?= =?UTF-8?q?ling=20of=20postgres=20dialect=20in=20database=20URL=20to=20pre?= =?UTF-8?q?vent=20NoSuchModuleError=20and=20provide=20a=20warning=20messag?= =?UTF-8?q?e=20for=20users=20to=20update=20the=20database=20URL.=20(#2161)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/services/database/service.py | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/backend/base/langflow/services/database/service.py b/src/backend/base/langflow/services/database/service.py index db5b63c70..ceeaf3e38 100644 --- a/src/backend/base/langflow/services/database/service.py +++ b/src/backend/base/langflow/services/database/service.py @@ -6,24 +6,21 @@ 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 sqlalchemy.engine import Engine -from sqlalchemy import event -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 event, inspect +from sqlalchemy.engine import Engine +from sqlalchemy.exc import OperationalError +from sqlmodel import Session, SQLModel, create_engine, select, text if TYPE_CHECKING: - from sqlalchemy.engine import Engine - from langflow.services.settings.service import SettingsService + from sqlalchemy.engine import Engine class DatabaseService(Service): @@ -48,12 +45,23 @@ class DatabaseService(Service): connect_args = {"check_same_thread": False} else: connect_args = {} - return create_engine( - self.database_url, - connect_args=connect_args, - pool_size=self.settings_service.settings.pool_size, - max_overflow=self.settings_service.settings.max_overflow, - ) + try: + return create_engine( + self.database_url, + connect_args=connect_args, + pool_size=self.settings_service.settings.pool_size, + max_overflow=self.settings_service.settings.max_overflow, + ) + except sa.exc.NoSuchModuleError as exc: + # sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres + if "postgres" in str(exc) and not self.database_url.startswith("postgresql"): + # https://stackoverflow.com/questions/62688256/sqlalchemy-exc-nosuchmoduleerror-cant-load-plugin-sqlalchemy-dialectspostgre + self.database_url = self.database_url.replace("postgres://", "postgresql://") + logger.warning( + "Fixed postgres dialect in database URL. Replacing postgres:// with postgresql://. To avoid this warning, update the database URL." + ) + return self._create_engine() + raise RuntimeError("Error creating database engine") from exc @event.listens_for(Engine, "connect") def on_connection(dbapi_connection, connection_record): From bc50e010d4b88dc15c390b05aa74402c3cb8d101 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:43:35 -0300 Subject: [PATCH 36/68] feat: Add Case component to App for conditional rendering --- src/frontend/src/App.tsx | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 849244999..720ffb6b2 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -18,21 +18,21 @@ import { autoLogin, getGlobalVariables, getHealth } from "./controllers/API"; import { setupAxiosDefaults } from "./controllers/API/utils"; import useTrackLastVisitedPath from "./hooks/use-track-last-visited-path"; import Router from "./routes"; +import { Case } from "./shared/components/caseComponent"; import useAlertStore from "./stores/alertStore"; import { useDarkStore } from "./stores/darkStore"; import useFlowsManagerStore from "./stores/flowsManagerStore"; import { useFolderStore } from "./stores/foldersStore"; import { useGlobalVariablesStore } from "./stores/globalVariablesStore/globalVariables"; import { useStoreStore } from "./stores/storeStore"; -import { useTypesStore } from "./stores/typesStore"; export default function App() { useTrackLastVisitedPath(); const removeFromTempNotificationList = useAlertStore( - (state) => state.removeFromTempNotificationList + (state) => state.removeFromTempNotificationList, ); const tempNotificationList = useAlertStore( - (state) => state.tempNotificationList + (state) => state.tempNotificationList, ); const [fetchError, setFetchError] = useState(false); const isLoading = useFlowsManagerStore((state) => state.isLoading); @@ -43,21 +43,18 @@ export default function App() { const { isAuthenticated, login, setUserData, setAutoLogin, getUser } = useContext(AuthContext); - const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows); const setLoading = useAlertStore((state) => state.setLoading); const fetchApiData = useStoreStore((state) => state.fetchApiData); - const getTypes = useTypesStore((state) => state.getTypes); const refreshVersion = useDarkStore((state) => state.refreshVersion); const refreshStars = useDarkStore((state) => state.refreshStars); const setGlobalVariables = useGlobalVariablesStore( - (state) => state.setGlobalVariables + (state) => state.setGlobalVariables, ); const checkHasStore = useStoreStore((state) => state.checkHasStore); const navigate = useNavigate(); const dark = useDarkStore((state) => state.dark); - const getFoldersApi = useFolderStore((state) => state.getFoldersApi); - const loadingFolders = useFolderStore((state) => state.loading); + const isLoadingFolders = useFolderStore((state) => state.isLoadingFolders); const [isLoadingHealth, setIsLoadingHealth] = useState(false); @@ -115,13 +112,13 @@ export default function App() { if (isAuthenticated) { try { await setupAxiosDefaults(); - await getFoldersApi(); - await getTypes(); - await refreshFlows(); + const res = await getGlobalVariables(); setGlobalVariables(res); + checkHasStore(); fetchApiData(); + resolve(); } catch (error) { console.error("Failed to fetch data:", error); @@ -174,6 +171,8 @@ export default function App() { } }; + const isLoadingApplication = isLoading || isLoadingFolders; + return ( //need parent component with width and height
          @@ -196,15 +195,15 @@ export default function App() { > } - {isLoading || loadingFolders ? ( +
          - ) : ( - <> - - - )} +
          + + + +
          From 75c13f4f20e9d17e48a222c9f183c391babf7705 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:44:06 -0300 Subject: [PATCH 37/68] chore: Refactor loadingFolders variable name in SidebarNav component --- src/frontend/src/components/sidebarComponent/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/components/sidebarComponent/index.tsx b/src/frontend/src/components/sidebarComponent/index.tsx index efec5da1e..8cdbe5907 100644 --- a/src/frontend/src/components/sidebarComponent/index.tsx +++ b/src/frontend/src/components/sidebarComponent/index.tsx @@ -28,7 +28,7 @@ export default function SidebarNav({ }: SidebarNavProps) { const location = useLocation(); const pathname = location.pathname; - const loadingFolders = useFolderStore((state) => state.loading); + const loadingFolders = useFolderStore((state) => state.isLoadingFolders); const folders = useFolderStore((state) => state.folders); const pathValues = ["folder", "components", "flows", "all"]; From 0bbecc9bab4c428818021d99b5ae138494ecec96 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:44:12 -0300 Subject: [PATCH 38/68] refactor: Refresh folders after adding a new folder in SideBarFoldersButtonsComponent --- .../components/sideBarFolderButtons/index.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx b/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx index 234eb9304..1b538ba20 100644 --- a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx +++ b/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx @@ -31,14 +31,14 @@ const SideBarFoldersButtonsComponent = ({ const [foldersNames, setFoldersNames] = useState({}); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); const [editFolders, setEditFolderName] = useState( - folders.map((obj) => ({ name: obj.name, edit: false })) + folders.map((obj) => ({ name: obj.name, edit: false })), ); const uploadFolder = useFolderStore((state) => state.uploadFolder); const currentFolder = pathname.split("/"); const urlWithoutPath = pathname.split("/").length < 4; const myCollectionId = useFolderStore((state) => state.myCollectionId); - const getFoldersApi = useFolderStore((state) => state.getFoldersApi); const folderIdDragging = useFolderStore((state) => state.folderIdDragging); + const refreshFolders = useFolderStore((state) => state.refreshFolders); const checkPathName = (itemId: string) => { if (urlWithoutPath && itemId === myCollectionId) { @@ -58,7 +58,7 @@ const SideBarFoldersButtonsComponent = ({ const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop( folderId, - handleFolderChange + handleFolderChange, ); const handleUploadFlowsToFolder = () => { @@ -85,8 +85,8 @@ const SideBarFoldersButtonsComponent = ({ function addNewFolder() { addFolder({ name: "New Folder", parent_id: null, description: "" }).then( (res) => { - getFoldersApi(true); - } + refreshFolders(); + }, ); } @@ -132,7 +132,7 @@ const SideBarFoldersButtonsComponent = ({ <> {folders.map((item, index) => { const editFolderName = editFolders?.filter( - (folder) => folder.name === item.name + (folder) => folder.name === item.name, )[0]; return (
          handleChangeFolder!(item.id!)} > @@ -218,7 +218,7 @@ const SideBarFoldersButtonsComponent = ({ folders.map((obj) => ({ name: obj.name, edit: false, - })) + })), ); } if (e.key === "Enter") { @@ -251,10 +251,10 @@ const SideBarFoldersButtonsComponent = ({ }; const updatedFolder = await updateFolder( body, - item.id! + item.id!, ); const updateFolders = folders.filter( - (f) => f.name !== item.name + (f) => f.name !== item.name, ); setFolders([...updateFolders, updatedFolder]); setFoldersNames({}); @@ -262,7 +262,7 @@ const SideBarFoldersButtonsComponent = ({ folders.map((obj) => ({ name: obj.name, edit: false, - })) + })), ); } else { setFoldersNames((old) => ({ From 9cd3bd751e1e93fad764775e91178303e57be7b2 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:44:21 -0300 Subject: [PATCH 39/68] chore: Update use-on-file-drop.tsx to use refreshFolders instead of getFoldersApi --- .../sidebarComponent/hooks/use-on-file-drop.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx b/src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx index f10733468..c75bf4bec 100644 --- a/src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx +++ b/src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx @@ -12,11 +12,11 @@ import { addVersionToDuplicates } from "../../../utils/reactflowUtils"; const useFileDrop = (folderId, folderChangeCallback) => { const setFolderDragging = useFolderStore((state) => state.setFolderDragging); const setFolderIdDragging = useFolderStore( - (state) => state.setFolderIdDragging + (state) => state.setFolderIdDragging, ); const setErrorData = useAlertStore((state) => state.setErrorData); - const getFoldersApi = useFolderStore((state) => state.getFoldersApi); + const refreshFolders = useFolderStore((state) => state.refreshFolders); const flows = useFlowsManagerStore((state) => state.flows); const triggerFolderChange = (folderId) => { @@ -45,7 +45,7 @@ const useFileDrop = (folderId, folderChangeCallback) => { | React.DragEvent | React.DragEvent | React.DragEvent, - folderId: string + folderId: string, ) => { e.preventDefault(); @@ -60,7 +60,7 @@ const useFileDrop = (folderId, folderChangeCallback) => { | React.DragEvent | React.DragEvent | React.DragEvent, - folderId: string + folderId: string, ) => { if (e.dataTransfer.types.some((types) => types === "Files")) { setFolderDragging(true); @@ -73,7 +73,7 @@ const useFileDrop = (folderId, folderChangeCallback) => { e: | React.DragEvent | React.DragEvent - | React.DragEvent + | React.DragEvent, ) => { e.preventDefault(); if (e.target === e.currentTarget) { @@ -87,7 +87,7 @@ const useFileDrop = (folderId, folderChangeCallback) => { | React.DragEvent | React.DragEvent | React.DragEvent, - folderId: string + folderId: string, ) => { if (e?.dataTransfer?.getData("flow")) { const data = JSON.parse(e?.dataTransfer?.getData("flow")); @@ -118,7 +118,7 @@ const useFileDrop = (folderId, folderChangeCallback) => { setFolderIdDragging(""); updateFlowInDatabase(updatedFlow).then(() => { - getFoldersApi(true); + refreshFolders(); triggerFolderChange(folderId); }); }; @@ -129,7 +129,7 @@ const useFileDrop = (folderId, folderChangeCallback) => { setFolderDragging(false); setFolderIdDragging(""); uploadFlowsFromFolders(formData).then(() => { - getFoldersApi(true); + refreshFolders(); triggerFolderChange(folderId); }); }; From 8980b66bec682933baefc5e407d1e44b30c3a1fd Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:44:28 -0300 Subject: [PATCH 40/68] refactor: Update TableComponent to use optional chaining for hiding overlay --- src/frontend/src/components/tableComponent/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/frontend/src/components/tableComponent/index.tsx b/src/frontend/src/components/tableComponent/index.tsx index 126d4f3f2..e956d8795 100644 --- a/src/frontend/src/components/tableComponent/index.tsx +++ b/src/frontend/src/components/tableComponent/index.tsx @@ -35,7 +35,7 @@ const TableComponent = forwardRef< alertDescription = DEFAULT_TABLE_ALERT_MSG, ...props }, - ref + ref, ) => { let colDef = props.columnDefs.map((col, index) => { let newCol = { @@ -105,13 +105,13 @@ const TableComponent = forwardRef< } }, 50); setTimeout(() => { - realRef.current.api.hideOverlay(); + realRef?.current?.api?.hideOverlay(); }, 1000); if (props.onGridReady) props.onGridReady(params); }; const onColumnMoved = (params) => { const updatedColumnDefs = makeLastColumnNonResizable( - params.columnApi.getAllGridColumns().map((col) => col.getColDef()) + params.columnApi.getAllGridColumns().map((col) => col.getColDef()), ); params.api.setGridOption("columnDefs", updatedColumnDefs); if (props.onColumnMoved) props.onColumnMoved(params); @@ -135,7 +135,7 @@ const TableComponent = forwardRef< className={cn( dark ? "ag-theme-quartz-dark" : "ag-theme-quartz", "ag-theme-shadcn flex h-full flex-col", - "relative" + "relative", )} // applying the grid theme > source.includes("column"))) { localStorage.setItem( storeReference, - JSON.stringify(realRef.current?.api?.getColumnState()) + JSON.stringify(realRef.current?.api?.getColumnState()), ); setColumnStateChange(true); } @@ -175,7 +175,7 @@ const TableComponent = forwardRef< )}
          ); - } + }, ); export default TableComponent; From 4358ff52554e62beda6b288713189ae466d178e5 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:44:36 -0300 Subject: [PATCH 41/68] chore: Update authContext to useFolderStore for getting folders --- src/frontend/src/contexts/authContext.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/contexts/authContext.tsx b/src/frontend/src/contexts/authContext.tsx index 1817447f1..bd5c2d02c 100644 --- a/src/frontend/src/contexts/authContext.tsx +++ b/src/frontend/src/contexts/authContext.tsx @@ -3,6 +3,7 @@ import { useNavigate } from "react-router-dom"; import Cookies from "universal-cookie"; import { getLoggedUser, requestLogout } from "../controllers/API"; import useAlertStore from "../stores/alertStore"; +import { useFolderStore } from "../stores/foldersStore"; import { Users } from "../types/api"; import { AuthContextType } from "../types/contexts/auth"; @@ -30,19 +31,20 @@ export function AuthProvider({ children }): React.ReactElement { const navigate = useNavigate(); const cookies = new Cookies(); const [accessToken, setAccessToken] = useState( - cookies.get("access_token_lf") ?? null + cookies.get("access_token_lf") ?? null, ); const [isAuthenticated, setIsAuthenticated] = useState( - !!cookies.get("access_token_lf") + !!cookies.get("access_token_lf"), ); const [isAdmin, setIsAdmin] = useState(false); const [userData, setUserData] = useState(null); const [autoLogin, setAutoLogin] = useState(false); const setLoading = useAlertStore((state) => state.setLoading); const [apiKey, setApiKey] = useState( - cookies.get("apikey_tkn_lflw") + cookies.get("apikey_tkn_lflw"), ); - // const getFoldersApi = useFolderStore((state) => state.getFoldersApi); + + const getFoldersApi = useFolderStore((state) => state.getFoldersApi); useEffect(() => { const storedAccessToken = cookies.get("access_token_lf"); @@ -64,7 +66,8 @@ export function AuthProvider({ children }): React.ReactElement { setUserData(user); const isSuperUser = user!.is_superuser; setIsAdmin(isSuperUser); - // await getFoldersApi(true); + + getFoldersApi(true, true); }) .catch((error) => { setLoading(false); From b9e1a3c0c55fb6a1937499b6e3aa6dffdcd30f54 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:44:46 -0300 Subject: [PATCH 42/68] fix: Handle duplicate requests in ApiInterceptor --- src/frontend/src/controllers/API/api.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/controllers/API/api.tsx b/src/frontend/src/controllers/API/api.tsx index 2a305cf5a..0ac3efa6b 100644 --- a/src/frontend/src/controllers/API/api.tsx +++ b/src/frontend/src/controllers/API/api.tsx @@ -48,7 +48,7 @@ function ApiInterceptor() { } await clearBuildVerticesState(error); return Promise.reject(error); - } + }, ); const isAuthorizedURL = (url) => { @@ -65,10 +65,10 @@ function ApiInterceptor() { const parsedURL = new URL(url); const isDomainAllowed = authorizedDomains.some( - (domain) => parsedURL.origin === new URL(domain).origin + (domain) => parsedURL.origin === new URL(domain).origin, ); const isEndpointAllowed = authorizedEndpoints.some((endpoint) => - parsedURL.pathname.includes(endpoint) + parsedURL.pathname.includes(endpoint), ); return isDomainAllowed || isEndpointAllowed; @@ -87,6 +87,7 @@ function ApiInterceptor() { if (!checkRequest) { controller.abort("Duplicate Request"); + console.error("Duplicate Request"); } const accessToken = cookies.get("access_token_lf"); @@ -101,7 +102,7 @@ function ApiInterceptor() { }, (error) => { return Promise.reject(error); - } + }, ); return () => { @@ -133,7 +134,7 @@ function ApiInterceptor() { if (error?.config?.headers) { delete error.config.headers["Authorization"]; error.config.headers["Authorization"] = `Bearer ${cookies.get( - "access_token_lf" + "access_token_lf", )}`; const response = await axios.request(error.config); return response; From a49cd49eee35f310e26059695f1e0b3481898c87 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:45:02 -0300 Subject: [PATCH 43/68] refactor: Remove console.log statement in getMessagesTable function --- src/frontend/src/controllers/API/index.ts | 54 +++++++++++------------ 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 07a9cd06d..104b815b0 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -63,7 +63,7 @@ export async function sendAll(data: sendAllProps) { } export async function postValidateCode( - code: string + code: string, ): Promise> { return await api.post(`${BASE_URL_API}validate/code`, { code }); } @@ -78,7 +78,7 @@ export async function postValidateCode( export async function postValidatePrompt( name: string, template: string, - frontend_node: APIClassType + frontend_node: APIClassType, ): Promise> { return api.post(`${BASE_URL_API}validate/prompt`, { name, @@ -151,7 +151,7 @@ export async function saveFlowToDatabase(newFlow: { * @throws Will throw an error if the update fails. */ export async function updateFlowInDatabase( - updatedFlow: FlowType + updatedFlow: FlowType, ): Promise { try { const response = await api.patch(`${BASE_URL_API}flows/${updatedFlow.id}`, { @@ -329,7 +329,7 @@ export async function getHealth() { * */ export async function getBuildStatus( - flowId: string + flowId: string, ): Promise> { return await api.get(`${BASE_URL_API}build/${flowId}/status`); } @@ -342,7 +342,7 @@ export async function getBuildStatus( * */ export async function postBuildInit( - flow: FlowType + flow: FlowType, ): Promise> { return await api.post(`${BASE_URL_API}build/init/${flow.id}`, flow); } @@ -358,7 +358,7 @@ export async function postBuildInit( */ export async function uploadFile( file: File, - id: string + id: string, ): Promise> { const formData = new FormData(); formData.append("file", file); @@ -380,7 +380,7 @@ export async function getProfilePictures(): Promise> { // let template = apiClass.template; return await api.post(`${BASE_URL_API}custom_component`, { @@ -393,7 +393,7 @@ export async function postCustomComponentUpdate( code: string, template: APITemplateType, field: string, - field_value: any + field_value: any, ): Promise> { return await api.post(`${BASE_URL_API}custom_component/update`, { code, @@ -415,7 +415,7 @@ export async function onLogin(user: LoginType) { headers: { "Content-Type": "application/x-www-form-urlencoded", }, - } + }, ); if (response.status === 200) { @@ -477,11 +477,11 @@ export async function addUser(user: UserInputType): Promise> { export async function getUsersPage( skip: number, - limit: number + limit: number, ): Promise> { try { const res = await api.get( - `${BASE_URL_API}users/?skip=${skip}&limit=${limit}` + `${BASE_URL_API}users/?skip=${skip}&limit=${limit}`, ); if (res.status === 200) { return res.data; @@ -518,7 +518,7 @@ export async function resetPassword(user_id: string, user: resetPasswordType) { try { const res = await api.patch( `${BASE_URL_API}users/${user_id}/reset-password`, - user + user, ); if (res.status === 200) { return res.data; @@ -592,7 +592,7 @@ export async function saveFlowStore( last_tested_version?: string; }, tags: string[], - publicFlow = false + publicFlow = false, ): Promise { try { const response = await api.post(`${BASE_URL_API}store/components/`, { @@ -721,7 +721,7 @@ export async function postStoreComponents(component: Component) { export async function getComponent(component_id: string) { try { const res = await api.get( - `${BASE_URL_API}store/components/${component_id}` + `${BASE_URL_API}store/components/${component_id}`, ); if (res.status === 200) { return res.data; @@ -736,7 +736,7 @@ export async function searchComponent( page?: number | null, limit?: number | null, status?: string | null, - tags?: string[] + tags?: string[], ): Promise { try { let url = `${BASE_URL_API}store/components/`; @@ -848,7 +848,7 @@ export async function updateFlowStore( }, tags: string[], publicFlow = false, - id: string + id: string, ): Promise { try { const response = await api.patch(`${BASE_URL_API}store/components/${id}`, { @@ -932,7 +932,7 @@ export async function deleteGlobalVariable(id: string) { export async function updateGlobalVariable( name: string, value: string, - id: string + id: string, ) { try { const response = api.patch(`${BASE_URL_API}variables/${id}`, { @@ -951,7 +951,7 @@ export async function getVerticesOrder( startNodeId?: string | null, stopNodeId?: string | null, nodes?: Node[], - Edges?: Edge[] + Edges?: Edge[], ): Promise> { // nodeId is optional and is a query parameter // if nodeId is not provided, the API will return all vertices @@ -971,7 +971,7 @@ export async function getVerticesOrder( return await api.post( `${BASE_URL_API}build/${flowId}/vertices`, data, - config + config, ); } @@ -979,7 +979,7 @@ export async function postBuildVertex( flowId: string, vertexId: string, input_value: string, - files?: string[] + files?: string[], ): Promise> { // input_value is optional and is a query parameter let data = {}; @@ -991,7 +991,7 @@ export async function postBuildVertex( } return await api.post( `${BASE_URL_API}build/${flowId}/vertices/${vertexId}`, - data + data, ); } @@ -1015,7 +1015,7 @@ export async function getFlowPool({ } export async function deleteFlowPool( - flowId: string + flowId: string, ): Promise> { const config = {}; config["params"] = { flow_id: flowId }; @@ -1029,7 +1029,7 @@ export async function deleteFlowPool( * @returns A promise that resolves to an array of AxiosResponse objects representing the delete responses. */ export async function multipleDeleteFlowsComponents( - flowIds: string[] + flowIds: string[], ): Promise[]> { const batches: string[][] = []; @@ -1052,7 +1052,7 @@ export async function multipleDeleteFlowsComponents( // Execute all delete requests const responses: Promise>[] = batches.map((batch) => - deleteBatch(batch) + deleteBatch(batch), ); // Return the responses after all requests are completed @@ -1062,7 +1062,7 @@ export async function multipleDeleteFlowsComponents( export async function getTransactionTable( id: string, mode: "intersection" | "union", - params = {} + params = {}, ): Promise<{ rows: Array; columns: Array }> { const config = {}; config["params"] = { flow_id: id }; @@ -1078,7 +1078,7 @@ export async function getMessagesTable( mode: "intersection" | "union", id?: string, excludedFields?: string[], - params = {} + params = {}, ): Promise<{ rows: Array; columns: Array }> { const config = {}; if (id) { @@ -1091,8 +1091,6 @@ export async function getMessagesTable( const rowsOrganized = rows.data; - console.log(rowsOrganized); - const columns = extractColumnsFromRows(rowsOrganized, mode, excludedFields); const sessions = new Set(); rowsOrganized.forEach((row) => { From e66286662e7ec990146b9c3ab94e2307057d6615 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:45:09 -0300 Subject: [PATCH 44/68] refactor: Improve duplicate request handling in checkDuplicateRequestAndStoreRequest function --- .../controllers/API/helpers/check-duplicate-requests.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts b/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts index 6486de541..85c6608ac 100644 --- a/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts +++ b/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts @@ -4,11 +4,12 @@ export function checkDuplicateRequestAndStoreRequest(config) { const lastUrl = localStorage.getItem("lastUrlCalled"); const lastMethodCalled = localStorage.getItem("lastMethodCalled"); const lastRequestTime = localStorage.getItem("lastRequestTime"); + const lastCurrentUrl = localStorage.getItem("lastCurrentUrl"); + const currentUrl = window.location.pathname; const currentTime = Date.now(); - const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) => - config?.url!.includes(request) + config?.url!.includes(request), ); if ( @@ -17,7 +18,8 @@ export function checkDuplicateRequestAndStoreRequest(config) { lastMethodCalled === config.method && lastMethodCalled === "get" && // Assuming you want to check only for GET requests lastRequestTime && - currentTime - parseInt(lastRequestTime, 10) < 800 + currentTime - parseInt(lastRequestTime, 10) < 300 && + lastCurrentUrl === currentUrl ) { return false; } @@ -25,6 +27,7 @@ export function checkDuplicateRequestAndStoreRequest(config) { localStorage.setItem("lastUrlCalled", config.url ?? ""); localStorage.setItem("lastMethodCalled", config.method ?? ""); localStorage.setItem("lastRequestTime", currentTime.toString()); + localStorage.setItem("lastCurrentUrl", currentUrl); return true; } From 68201bb0d500e95e48c1962fc7f842e179c8ac45 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:45:16 -0300 Subject: [PATCH 45/68] feat: Add setLoading to LoginAdminPage for displaying loading state during login --- src/frontend/src/pages/AdminPage/LoginPage/index.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/frontend/src/pages/AdminPage/LoginPage/index.tsx b/src/frontend/src/pages/AdminPage/LoginPage/index.tsx index b1772baa5..0c7e073df 100644 --- a/src/frontend/src/pages/AdminPage/LoginPage/index.tsx +++ b/src/frontend/src/pages/AdminPage/LoginPage/index.tsx @@ -19,6 +19,7 @@ export default function LoginAdminPage() { const [inputState, setInputState] = useState(CONTROL_LOGIN_STATE); const { login, isAuthenticated, setUserData } = useContext(AuthContext); + const setLoading = useAlertStore((state) => state.setLoading); const { password, username } = inputState; const setErrorData = useAlertStore((state) => state.setErrorData); @@ -35,6 +36,10 @@ export default function LoginAdminPage() { }; onLogin(user) .then((user) => { + console.log("admin page"); + + setLoading(true); + login(user.access_token); navigate("/admin/"); }) From a1cd3be2373d8e590fb4d075a693f95e334bc3ba Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:45:27 -0300 Subject: [PATCH 46/68] feat: Add useFlowsManagerStore to LoginPage for managing loading state during login --- src/frontend/src/pages/LoginPage/index.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/frontend/src/pages/LoginPage/index.tsx b/src/frontend/src/pages/LoginPage/index.tsx index 826b2a931..d25d1c2e1 100644 --- a/src/frontend/src/pages/LoginPage/index.tsx +++ b/src/frontend/src/pages/LoginPage/index.tsx @@ -9,6 +9,7 @@ import { CONTROL_LOGIN_STATE } from "../../constants/constants"; import { AuthContext } from "../../contexts/authContext"; import { onLogin } from "../../controllers/API"; import useAlertStore from "../../stores/alertStore"; +import useFlowsManagerStore from "../../stores/flowsManagerStore"; import { LoginType } from "../../types/api"; import { inputHandlerEventType, @@ -23,6 +24,7 @@ export default function LoginPage(): JSX.Element { const { login } = useContext(AuthContext); const navigate = useNavigate(); const setErrorData = useAlertStore((state) => state.setErrorData); + const setLoading = useFlowsManagerStore((state) => state.setIsLoading); function handleInput({ target: { name, value }, @@ -37,6 +39,9 @@ export default function LoginPage(): JSX.Element { }; onLogin(user) .then((user) => { + console.log("login page"); + + setLoading(true); login(user.access_token); navigate("/"); }) From 6450b52c402137e2d55573a36c74e539b6699ecc Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:45:41 -0300 Subject: [PATCH 47/68] chore: Remove unused code in HeaderTabsSearchComponent --- .../headerTabsSearchComponent/index.tsx | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx b/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx index 17916f05b..c6ceb44c7 100644 --- a/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx +++ b/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx @@ -1,39 +1,19 @@ import { useState } from "react"; -import { useLocation } from "react-router-dom"; -import useAlertStore from "../../../../../../stores/alertStore"; import useFlowsManagerStore from "../../../../../../stores/flowsManagerStore"; -import { useFolderStore } from "../../../../../../stores/foldersStore"; -import { handleDownloadFolderFn } from "../../../../utils/handle-download-folder"; import InputSearchComponent from "../inputSearchComponent"; import TabsSearchComponent from "../tabsComponent"; type HeaderTabsSearchComponentProps = {}; const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => { - const location = useLocation(); - const myCollectionId = useFolderStore((state) => state.myCollectionId); - const folderId = location?.state?.folderId || myCollectionId; const isLoading = useFlowsManagerStore((state) => state.isLoading); const [tabActive, setTabActive] = useState("Flows"); - const setErrorData = useAlertStore((state) => state.setErrorData); - const allFlows = useFlowsManagerStore((state) => state.allFlows); const [inputValue, setInputValue] = useState(""); const setSearchFlowsComponents = useFlowsManagerStore( - (state) => state.setSearchFlowsComponents + (state) => state.setSearchFlowsComponents, ); - const handleDownloadFolder = () => { - if (allFlows.length === 0) { - setErrorData({ - title: "Folder is empty", - list: [], - }); - return; - } - handleDownloadFolderFn(folderId); - }; - return ( <>
          From 5462aeee3e3b3128dc08bfa3c4ea7a22ea246de6 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:45:50 -0300 Subject: [PATCH 48/68] refactor: Update useDeleteFolder hook to use refreshFolders instead of getFoldersApi --- src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx b/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx index 257f1b6d2..239bf26f3 100644 --- a/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx +++ b/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx @@ -2,11 +2,12 @@ import useAlertStore from "../../../stores/alertStore"; import { useFolderStore } from "../../../stores/foldersStore"; import { deleteFolder, getFolderById } from "../services"; -const useDeleteFolder = ({ navigate, getFoldersApi }) => { +const useDeleteFolder = ({ navigate }) => { const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const folderToEdit = useFolderStore((state) => state.folderToEdit); const myCollectionId = useFolderStore((state) => state.myCollectionId); + const refreshFolders = useFolderStore((state) => state.refreshFolders); const handleDeleteFolder = () => { deleteFolder(folderToEdit?.id!) @@ -15,7 +16,7 @@ const useDeleteFolder = ({ navigate, getFoldersApi }) => { title: "Folder deleted successfully.", }); getFolderById(myCollectionId!); - getFoldersApi(true); + refreshFolders(); navigate("/all"); }) .catch((err) => { From 9dd56b04126eb7efe98b40722b6671c70393b6f7 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:45:58 -0300 Subject: [PATCH 49/68] refactor: Update MainPage component to remove unused code and improve folder deletion handling --- .../src/pages/MainPage/pages/mainPage/index.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx b/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx index 0daa61257..6b4892d60 100644 --- a/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx +++ b/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx @@ -16,7 +16,7 @@ import useDropdownOptions from "../../hooks/use-dropdown-options"; export default function HomePage(): JSX.Element { const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId + (state) => state.setCurrentFlowId, ); const location = useLocation(); @@ -25,16 +25,9 @@ export default function HomePage(): JSX.Element { const [openFolderModal, setOpenFolderModal] = useState(false); const [openDeleteFolderModal, setOpenDeleteFolderModal] = useState(false); const is_component = pathname === "/components"; - const getFoldersApi = useFolderStore((state) => state.getFoldersApi); const setFolderToEdit = useFolderStore((state) => state.setFolderToEdit); const navigate = useNavigate(); - useEffect(() => { - setTimeout(() => { - getFoldersApi(); - }, 300); - }, []); - useEffect(() => { setCurrentFlowId(""); }, [pathname]); @@ -45,7 +38,7 @@ export default function HomePage(): JSX.Element { is_component, }); - const { handleDeleteFolder } = useDeleteFolder({ navigate, getFoldersApi }); + const { handleDeleteFolder } = useDeleteFolder({ navigate }); return ( <> From 33b3a9ee7ee6e1801608e82cd8d277a8ebf6d33e Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:46:06 -0300 Subject: [PATCH 50/68] refactor: Update getFoldersApi to handle startup application and improve code readability --- src/frontend/src/stores/foldersStore.tsx | 78 +++++++++++++++++------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/src/frontend/src/stores/foldersStore.tsx b/src/frontend/src/stores/foldersStore.tsx index ce858c3be..01db44247 100644 --- a/src/frontend/src/stores/foldersStore.tsx +++ b/src/frontend/src/stores/foldersStore.tsx @@ -7,15 +7,16 @@ import { } from "../pages/MainPage/services"; import { FoldersStoreType } from "../types/zustand/folders"; import useFlowsManagerStore from "./flowsManagerStore"; +import { useTypesStore } from "./typesStore"; export const useFolderStore = create((set, get) => ({ folders: [], - getFoldersApi: (refetch = false) => { + getFoldersApi: (refetch = false, startupApplication: boolean = false) => { return new Promise((resolve, reject) => { if (get()?.folders.length === 0 || refetch === true) { - get().setLoading(true); + get().setIsLoadingFolders(true); getFolders().then( - (res) => { + async (res) => { const foldersWithoutStarterProjects = res?.filter( (folder) => folder.name !== STARTER_FOLDER_NAME, ); @@ -33,38 +34,73 @@ export const useFolderStore = create((set, get) => ({ set({ myCollectionId }); - if (refetch === true) { - useFlowsManagerStore.getState().refreshFlows(); - useFlowsManagerStore.getState().setAllFlows; - } + const { refreshFlows } = useFlowsManagerStore.getState(); + const { getTypes } = useTypesStore.getState(); + const { setIsLoadingFolders } = get(); + + if (refetch) { + if (startupApplication) { + await refreshFlows(); + await getTypes(); + } else { + refreshFlows(); + getTypes(); + } + } + setIsLoadingFolders(false); - get().setLoading(false); resolve(); }, (error) => { set({ folders: [] }); - get().setLoading(false); + get().setIsLoadingFolders(false); reject(error); }, ); } }); }, - setFolders: (folders) => set(() => ({ folders: folders })), - loading: false, - setLoading: (loading) => set(() => ({ loading: loading })), - getFolderById: (id) => { - if (id) { - getFolderById(id).then( - (res) => { - const setAllFlows = useFlowsManagerStore.getState().setAllFlows; - setAllFlows(res.flows); - set({ selectedFolder: res }); + refreshFolders: () => { + return new Promise((resolve, reject) => { + getFolders().then( + async (res) => { + const foldersWithoutStarterProjects = res?.filter( + (folder) => folder.name !== STARTER_FOLDER_NAME, + ); + + const starterProjects = res?.find( + (folder) => folder.name === STARTER_FOLDER_NAME, + ); + + set({ starterProjectId: starterProjects!.id ?? "" }); + set({ folders: foldersWithoutStarterProjects }); + + const myCollectionId = res?.find( + (f) => f.name === DEFAULT_FOLDER, + )?.id; + + set({ myCollectionId }); + + resolve(); }, - () => { - get().getFoldersApi(true); + (error) => { + set({ folders: [] }); + get().setIsLoadingFolders(false); + reject(error); }, ); + }); + }, + setFolders: (folders) => set(() => ({ folders: folders })), + isLoadingFolders: false, + setIsLoadingFolders: (isLoadingFolders) => set(() => ({ isLoadingFolders })), + getFolderById: (id) => { + if (id) { + getFolderById(id).then((res) => { + const setAllFlows = useFlowsManagerStore.getState().setAllFlows; + setAllFlows(res.flows); + set({ selectedFolder: res }); + }); } }, selectedFolder: null, From 41fc8461079b7987364e6e5e6a4eb8a1dd4ea734 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 13 Jun 2024 17:46:14 -0300 Subject: [PATCH 51/68] refactor: Update FoldersStoreType to use isLoadingFolders instead of loading --- src/frontend/src/types/zustand/folders/index.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/types/zustand/folders/index.ts b/src/frontend/src/types/zustand/folders/index.ts index 5e41677b2..495d65b87 100644 --- a/src/frontend/src/types/zustand/folders/index.ts +++ b/src/frontend/src/types/zustand/folders/index.ts @@ -2,10 +2,13 @@ import { FolderType } from "../../../pages/MainPage/entities"; export type FoldersStoreType = { folders: FolderType[]; - getFoldersApi: (refetch?: boolean) => Promise; + getFoldersApi: ( + refetch?: boolean, + startupApplication?: boolean, + ) => Promise; setFolders: (folders: FolderType[]) => void; - loading: boolean; - setLoading: (loading: boolean) => void; + isLoadingFolders: boolean; + setIsLoadingFolders: (isLoadingFolders: boolean) => void; selectedFolder: FolderType | null; getFolderById: (id: string) => void; getMyCollectionFolder: () => void; @@ -23,4 +26,5 @@ export type FoldersStoreType = { setFolderIdDragging: (id: string) => void; starterProjectId: string; setStarterProjectId: (id: string) => void; + refreshFolders: () => void; }; From d921865e4584a0593284c681cdeaa7ceab1f1bbd Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 18:33:48 -0300 Subject: [PATCH 52/68] chore: Update dependencies to latest versions --- poetry.lock | 28 ++++++++++++++-------------- pyproject.toml | 2 +- src/backend/base/pyproject.toml | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/poetry.lock b/poetry.lock index f248ce18b..b39fc9d0b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -471,17 +471,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.125" +version = "1.34.126" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.125-py3-none-any.whl", hash = "sha256:116d9eb3c26cf313a2e1e44ef704d1f98f9eb18e7628695d07b01b44a8683544"}, - {file = "boto3-1.34.125.tar.gz", hash = "sha256:31c4a5e4d6f9e6116be61ff654b424ddbd1afcdefe0e8b870c4796f9108eb1c6"}, + {file = "boto3-1.34.126-py3-none-any.whl", hash = "sha256:7f676daef674fe74f34ce4063228eccc6e60c811f574720e31f230296c4bf29a"}, + {file = "boto3-1.34.126.tar.gz", hash = "sha256:7e8418b47dd43954a9088d504541bed8a42b6d06e712d02befba134c1c4d7c6d"}, ] [package.dependencies] -botocore = ">=1.34.125,<1.35.0" +botocore = ">=1.34.126,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -490,13 +490,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.125" +version = "1.34.126" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.125-py3-none-any.whl", hash = "sha256:71e97e7d2c088f1188ba6976441b5857a5425acd4aaa31b45d13119c9cb86424"}, - {file = "botocore-1.34.125.tar.gz", hash = "sha256:d2882be011ad5b16e7ab4a96360b5b66a0a7e175c1ea06dbf2de473c0a0a33d8"}, + {file = "botocore-1.34.126-py3-none-any.whl", hash = "sha256:7eff883c638fe30e0b036789df32d851e093d12544615a3b90062b42ac85bdbc"}, + {file = "botocore-1.34.126.tar.gz", hash = "sha256:7a8ccb6a7c02456757a984a3a44331b6f51c94cb8b9b287cd045122fd177a4b0"}, ] [package.dependencies] @@ -4377,7 +4377,7 @@ six = "*" [[package]] name = "langflow-base" -version = "0.0.66" +version = "0.0.67" description = "A Python package with a built-in web application" optional = false python-versions = ">=3.10,<3.13" @@ -4474,13 +4474,13 @@ requests = ">=2,<3" [[package]] name = "litellm" -version = "1.40.9" +version = "1.40.10" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.40.9-py3-none-any.whl", hash = "sha256:8a4107b9cce114d822de52cbc9bce56f8edc6620f19d0f2257e71834715fb366"}, - {file = "litellm-1.40.9.tar.gz", hash = "sha256:e0ea07d0b55001a6f60bba2b2ecd72d1f0dca07e656f63937adfdf45f31e5ad7"}, + {file = "litellm-1.40.10-py3-none-any.whl", hash = "sha256:46b77c49593f4e5e7bd9c1291f5896549b6ff6ebdad457af3ce0f4937bcfc17d"}, + {file = "litellm-1.40.10.tar.gz", hash = "sha256:cdfc86f1de60491cd85155d98d64beec019c35dd0d8c8784037655cb7c0bdddc"}, ] [package.dependencies] @@ -7818,13 +7818,13 @@ websockets = ">=11,<13" [[package]] name = "redis" -version = "5.0.5" +version = "5.0.6" description = "Python client for Redis database and key-value store" optional = true python-versions = ">=3.7" files = [ - {file = "redis-5.0.5-py3-none-any.whl", hash = "sha256:30b47d4ebb6b7a0b9b40c1275a19b87bb6f46b3bed82a89012cf56dea4024ada"}, - {file = "redis-5.0.5.tar.gz", hash = "sha256:3417688621acf6ee368dec4a04dd95881be24efd34c79f00d31f62bb528800ae"}, + {file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"}, + {file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index 3b660249d..e1158fe30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow" -version = "1.0.0a55" +version = "1.0.0a56" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index 03f3f70f1..0dd32d331 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow-base" -version = "0.0.66" +version = "0.0.67" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ From c818c4759411567d7041d0fda788b1cca565f9b3 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 18:47:37 -0300 Subject: [PATCH 53/68] chore: Update Docker build workflow to include base and component images --- .github/workflows/docker-build.yml | 85 ++++++++++++++++-------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index f63920703..f2492eacf 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -23,14 +23,24 @@ env: POETRY_VERSION: "1.8.2" jobs: - docker_build: - name: Build Docker Image + setup: runs-on: ubuntu-latest + outputs: + base_tags: ${{ steps.set-vars.outputs.base_tags }} + main_tags: ${{ steps.set-vars.outputs.main_tags }} + steps: + - uses: actions/checkout@v4 + - name: Set Dockerfile and Tags + id: set-vars + run: | + echo "::set-output name=base_tags::langflowai/langflow:base-${{ inputs.version }}" + echo "::set-output name=main_tags::langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" + + build_base: + runs-on: ubuntu-latest + needs: setup steps: - uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - id: qemu - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub @@ -38,56 +48,51 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Set Dockerfile and Tags - id: set-vars - run: | - if [ "${{ inputs.release_type }}" == "base" ]; then - echo "DOCKERFILE=./docker/build_and_push_base.Dockerfile" >> $GITHUB_ENV - echo "TAGS=langflowai/langflow:base-${{ inputs.version }}" >> $GITHUB_ENV - else - echo "DOCKERFILE=./docker/build_and_push.Dockerfile" >> $GITHUB_ENV - echo "TAGS=langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" >> $GITHUB_ENV - fi - - name: Build and push + - name: Build and push Base Image uses: docker/build-push-action@v5 with: context: . push: true platforms: "linux/amd64,linux/arm64/v8" - file: ${{ env.DOCKERFILE }} - tags: ${{ env.TAGS }} - - name: Wait for Docker Hub to propagate - run: sleep 120 - - name: Build and push (backend) - if: ${{ inputs.release_type == 'main' }} + file: ./docker/build_and_push_base.Dockerfile + tags: ${{ needs.setup.outputs.base_tags }} + + build_components: + runs-on: ubuntu-latest + needs: build_base + strategy: + matrix: + component: [backend, frontend] + include: + - component: backend + dockerfile: ./docker/build_and_push_backend.Dockerfile + tags: langflowai/langflow-backend:${{ inputs.version }},langflowai/langflow-backend:1.0-alpha + - component: frontend + dockerfile: ./docker/frontend/build_and_push_frontend.Dockerfile + tags: langflowai/langflow-frontend:${{ inputs.version }},langflowai/langflow-frontend:1.0-alpha + steps: + - uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push ${{ matrix.component }} uses: docker/build-push-action@v5 with: context: . push: true platforms: "linux/amd64,linux/arm64/v8" - file: ./docker/build_and_push_backend.Dockerfile - build-args: | - LANGFLOW_IMAGE=langflowai/langflow:${{ inputs.version }} - tags: | - langflowai/langflow-backend:${{ inputs.version }} - langflowai/langflow-backend:1.0-alpha - - name: Build and push (frontend) - if: ${{ inputs.release_type == 'main' }} - uses: docker/build-push-action@v5 - with: - context: . - push: true - file: ./docker/frontend/build_and_push_frontend.Dockerfile - platforms: "linux/amd64,linux/arm64/v8" - tags: | - langflowai/langflow-frontend:${{ inputs.version }} - langflowai/langflow-frontend:1.0-alpha + file: ${{ matrix.dockerfile }} + tags: ${{ matrix.tags }} restart-space: name: Restart HuggingFace Spaces if: ${{ inputs.release_type == 'main' }} runs-on: ubuntu-latest - needs: docker_build + needs: build_base strategy: matrix: python-version: From 05ceb29dec511f4ebfa4024006df9e27cf849ed0 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Thu, 13 Jun 2024 18:48:43 -0300 Subject: [PATCH 54/68] chore: Update Docker build workflow to include base and component images --- .github/workflows/docker-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index f2492eacf..e4f53bac4 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -58,6 +58,7 @@ jobs: tags: ${{ needs.setup.outputs.base_tags }} build_components: + if: ${{ inputs.release_type == 'main' }} runs-on: ubuntu-latest needs: build_base strategy: From 5f87e10127512b656adc36fc3d21ba2ca68c7adf Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 08:19:38 -0300 Subject: [PATCH 55/68] =?UTF-8?q?=F0=9F=94=A7=20(docker-build.yml):=20upda?= =?UTF-8?q?te=20setting=20output=20variables=20to=20use=20a=20file=20for?= =?UTF-8?q?=20better=20readability=20and=20maintainability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-build.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index e4f53bac4..4741f4117 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -33,9 +33,8 @@ jobs: - name: Set Dockerfile and Tags id: set-vars run: | - echo "::set-output name=base_tags::langflowai/langflow:base-${{ inputs.version }}" - echo "::set-output name=main_tags::langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" - + echo "base_tags=langflowai/langflow:base-${{ inputs.version }}" >> $GITHUB_OUTPUT + echo "main_tags=langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" >> $GITHUB_OUTPUT build_base: runs-on: ubuntu-latest needs: setup From 5300b9cc08c81c1b7f08004e33057c39a60c0bd3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 14 Jun 2024 04:24:52 -0700 Subject: [PATCH 56/68] Improve error handling and logging in get_current_user_by_jwt function (#2165) chore: Improve error handling and logging in get_current_user_by_jwt function --- .../base/langflow/services/auth/utils.py | 65 +++++++++++-------- tests/test_user.py | 3 +- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/backend/base/langflow/services/auth/utils.py b/src/backend/base/langflow/services/auth/utils.py index f62d3ce4f..ebea58e7a 100644 --- a/src/backend/base/langflow/services/auth/utils.py +++ b/src/backend/base/langflow/services/auth/utils.py @@ -1,14 +1,13 @@ +import warnings from datetime import datetime, timedelta, timezone from typing import Annotated, Coroutine, Optional, Union from uuid import UUID -import warnings from cryptography.fernet import Fernet from fastapi import Depends, HTTPException, Security, status from fastapi.security import APIKeyHeader, APIKeyQuery, OAuth2PasswordBearer - - from jose import JWTError, jwt +from loguru import logger from sqlmodel import Session from starlette.websockets import WebSocket @@ -92,44 +91,58 @@ async def get_current_user_by_jwt( ) -> User: settings_service = get_settings_service() - credentials_exception = HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Could not validate credentials", - headers={"WWW-Authenticate": "Bearer"}, - ) - if isinstance(token, Coroutine): token = await token - if settings_service.auth_settings.SECRET_KEY.get_secret_value() is None: - raise credentials_exception + secret_key = settings_service.auth_settings.SECRET_KEY.get_secret_value() + if secret_key is None: + logger.error("Secret key is not set in settings.") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + # Careful not to leak sensitive information + detail="Authentication failure: Verify authentication settings.", + headers={"WWW-Authenticate": "Bearer"}, + ) try: - # Ignore warning about datetime.utcnow with warnings.catch_warnings(): warnings.simplefilter("ignore") - - payload = jwt.decode( - token, - settings_service.auth_settings.SECRET_KEY.get_secret_value(), - algorithms=[settings_service.auth_settings.ALGORITHM], - ) - user_id: UUID = payload.get("sub") # type: ignore - token_type: str = payload.get("type") # type: ignore + payload = jwt.decode(token, secret_key, algorithms=[settings_service.auth_settings.ALGORITHM]) + user_id: UUID = payload.get("sub") + token_type: str = payload.get("type") if expires := payload.get("exp", None): expires_datetime = datetime.fromtimestamp(expires, timezone.utc) - # TypeError: can't compare offset-naive and offset-aware datetimes if datetime.now(timezone.utc) > expires_datetime: - raise credentials_exception + logger.info("Token expired for user ID: %s", user_id) + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Token has expired.", + headers={"WWW-Authenticate": "Bearer"}, + ) if user_id is None or token_type: - raise credentials_exception + logger.info("Invalid token payload: %s", payload) + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid token details.", + headers={"WWW-Authenticate": "Bearer"}, + ) except JWTError as e: - raise credentials_exception from e + logger.error("JWT decoding error: %s", str(e)) + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) from e - user = get_user_by_id(db, user_id) # type: ignore + user = get_user_by_id(db, user_id) if user is None or not user.is_active: - raise credentials_exception + logger.info("User not found or inactive: %s", user_id) + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="User not found or is inactive.", + headers={"WWW-Authenticate": "Bearer"}, + ) return user diff --git a/tests/test_user.py b/tests/test_user.py index 5f894e801..759ce6aaf 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -1,6 +1,7 @@ from datetime import datetime import pytest + from langflow.services.auth.utils import create_super_user, get_password_hash from langflow.services.database.models.user import UserUpdate from langflow.services.database.models.user.model import User @@ -95,7 +96,7 @@ def test_data_consistency_after_update(client, active_user, logged_in_headers, s # Fetch the updated user from the database response = client.get("/api/v1/users/whoami", headers=logged_in_headers) assert response.status_code == 401, response.json() - assert response.json()["detail"] == "Could not validate credentials" + assert response.json()["detail"] == "User not found or is inactive." def test_data_consistency_after_delete(client, test_user, super_user_headers): From 3738a32b92b4a9c45f6df1e8496f23d3b3cba1a2 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 08:36:28 -0300 Subject: [PATCH 57/68] chore: Update Docker build workflow to include LANGFLOW_IMAGE build argument --- .github/workflows/docker-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 4741f4117..c5ea5f6f7 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -85,6 +85,8 @@ jobs: context: . push: true platforms: "linux/amd64,linux/arm64/v8" + build-args: | + LANGFLOW_IMAGE=langflowai/langflow:${{ inputs.version }} file: ${{ matrix.dockerfile }} tags: ${{ matrix.tags }} From 38c721f38734651392778b475716e65ca806c83e Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 08:39:42 -0300 Subject: [PATCH 58/68] chore: Bump version of langflow and langflow-base packages --- pyproject.toml | 2 +- src/backend/base/pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e1158fe30..c6fbaff06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow" -version = "1.0.0a56" +version = "1.0.0a57" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index 0dd32d331..a833470a2 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langflow-base" -version = "0.0.67" +version = "0.0.68" description = "A Python package with a built-in web application" authors = ["Langflow "] maintainers = [ From a7e83baa35323c81477edf090f8af092262a2927 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 08:53:15 -0300 Subject: [PATCH 59/68] chore: Bump langchain and langchain-core versions to 0.2.4 and 0.2.6 respectively --- poetry.lock | 31 +++++++++++++++++-------------- src/backend/base/poetry.lock | 31 +++++++++++++++++-------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/poetry.lock b/poetry.lock index b39fc9d0b..31864a0c5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4081,22 +4081,25 @@ adal = ["adal (>=1.0.2)"] [[package]] name = "langchain" -version = "0.2.3" +version = "0.2.4" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"}, - {file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"}, + {file = "langchain-0.2.4-py3-none-any.whl", hash = "sha256:a04813215c30f944df006031e2febde872af8fab628dcee825d969e07b6cd621"}, + {file = "langchain-0.2.4.tar.gz", hash = "sha256:e704b5b06222d5eba2d02c76f891321d1bac8952ed54e093831b2bdabf99dcd5"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} -langchain-core = ">=0.2.0,<0.3.0" +langchain-core = ">=0.2.6,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0" langsmith = ">=0.1.17,<0.2.0" -numpy = ">=1,<2" +numpy = [ + {version = ">=1,<2", markers = "python_version < \"3.12\""}, + {version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""}, +] pydantic = ">=1,<3" PyYAML = ">=5.3" requests = ">=2,<3" @@ -4192,19 +4195,19 @@ tenacity = ">=8.1.0,<9.0.0" [[package]] name = "langchain-core" -version = "0.2.5" +version = "0.2.6" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"}, - {file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"}, + {file = "langchain_core-0.2.6-py3-none-any.whl", hash = "sha256:90521c9fc95d8f925e0d2e2d952382676aea6d3f8de611eda1b1810874c31e5d"}, + {file = "langchain_core-0.2.6.tar.gz", hash = "sha256:9f0e38da722a558a6e95b6d86de01bd92e84558c47ac8ba599f02eab70a1c873"}, ] [package.dependencies] jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.66,<0.2.0" -packaging = ">=23.2,<24.0" +langsmith = ">=0.1.75,<0.2.0" +packaging = ">=23.2,<25" pydantic = ">=1,<3" PyYAML = ">=5.3" tenacity = ">=8.1.0,<9.0.0" @@ -4377,7 +4380,7 @@ six = "*" [[package]] name = "langflow-base" -version = "0.0.67" +version = "0.0.68" description = "A Python package with a built-in web application" optional = false python-versions = ">=3.10,<3.13" @@ -4474,13 +4477,13 @@ requests = ">=2,<3" [[package]] name = "litellm" -version = "1.40.10" +version = "1.40.12" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.40.10-py3-none-any.whl", hash = "sha256:46b77c49593f4e5e7bd9c1291f5896549b6ff6ebdad457af3ce0f4937bcfc17d"}, - {file = "litellm-1.40.10.tar.gz", hash = "sha256:cdfc86f1de60491cd85155d98d64beec019c35dd0d8c8784037655cb7c0bdddc"}, + {file = "litellm-1.40.12-py3-none-any.whl", hash = "sha256:42f1648507f29c60543ba5fdf35d38fc161694da043b201508225bae50d3328c"}, + {file = "litellm-1.40.12.tar.gz", hash = "sha256:366bb9c3694b9ef59b3d073bb37ff9ca175ab4090dc187b0a11d2b21db3a6a5d"}, ] [package.dependencies] diff --git a/src/backend/base/poetry.lock b/src/backend/base/poetry.lock index ab30d8f08..8f57c7d7b 100644 --- a/src/backend/base/poetry.lock +++ b/src/backend/base/poetry.lock @@ -1158,22 +1158,25 @@ files = [ [[package]] name = "langchain" -version = "0.2.3" +version = "0.2.4" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"}, - {file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"}, + {file = "langchain-0.2.4-py3-none-any.whl", hash = "sha256:a04813215c30f944df006031e2febde872af8fab628dcee825d969e07b6cd621"}, + {file = "langchain-0.2.4.tar.gz", hash = "sha256:e704b5b06222d5eba2d02c76f891321d1bac8952ed54e093831b2bdabf99dcd5"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} -langchain-core = ">=0.2.0,<0.3.0" +langchain-core = ">=0.2.6,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0" langsmith = ">=0.1.17,<0.2.0" -numpy = ">=1,<2" +numpy = [ + {version = ">=1,<2", markers = "python_version < \"3.12\""}, + {version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""}, +] pydantic = ">=1,<3" PyYAML = ">=5.3" requests = ">=2,<3" @@ -1205,19 +1208,19 @@ tenacity = ">=8.1.0,<9.0.0" [[package]] name = "langchain-core" -version = "0.2.5" +version = "0.2.6" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"}, - {file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"}, + {file = "langchain_core-0.2.6-py3-none-any.whl", hash = "sha256:90521c9fc95d8f925e0d2e2d952382676aea6d3f8de611eda1b1810874c31e5d"}, + {file = "langchain_core-0.2.6.tar.gz", hash = "sha256:9f0e38da722a558a6e95b6d86de01bd92e84558c47ac8ba599f02eab70a1c873"}, ] [package.dependencies] jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.66,<0.2.0" -packaging = ">=23.2,<24.0" +langsmith = ">=0.1.75,<0.2.0" +packaging = ">=23.2,<25" pydantic = ">=1,<3" PyYAML = ">=5.3" tenacity = ">=8.1.0,<9.0.0" @@ -1859,13 +1862,13 @@ files = [ [[package]] name = "packaging" -version = "23.2" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] From df634de53f56af5e1fe8838685518c2ca8e6b095 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 08:55:33 -0300 Subject: [PATCH 60/68] chore: Improve error handling and logging in get_current_user_by_jwt function --- src/backend/base/langflow/services/auth/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/services/auth/utils.py b/src/backend/base/langflow/services/auth/utils.py index ebea58e7a..d0ece7562 100644 --- a/src/backend/base/langflow/services/auth/utils.py +++ b/src/backend/base/langflow/services/auth/utils.py @@ -113,7 +113,7 @@ async def get_current_user_by_jwt( if expires := payload.get("exp", None): expires_datetime = datetime.fromtimestamp(expires, timezone.utc) if datetime.now(timezone.utc) > expires_datetime: - logger.info("Token expired for user ID: %s", user_id) + logger.info("Token expired for user") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Token has expired.", @@ -121,14 +121,14 @@ async def get_current_user_by_jwt( ) if user_id is None or token_type: - logger.info("Invalid token payload: %s", payload) + logger.info(f"Invalid token payload. Token type: {token_type}") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token details.", headers={"WWW-Authenticate": "Bearer"}, ) except JWTError as e: - logger.error("JWT decoding error: %s", str(e)) + logger.error(f"JWT decoding error: {e}") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", @@ -137,7 +137,7 @@ async def get_current_user_by_jwt( user = get_user_by_id(db, user_id) if user is None or not user.is_active: - logger.info("User not found or inactive: %s", user_id) + logger.info("User not found or inactive.") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found or is inactive.", From 81be804eeead4442ba1c9c5eaade0e7cc78287b6 Mon Sep 17 00:00:00 2001 From: italojohnny Date: Fri, 14 Jun 2024 09:14:27 -0300 Subject: [PATCH 61/68] fix .json files after merge conflict --- .../Basic Prompting (Hello, world!).json | 389 ++++---- .../Langflow Blog Writter.json | 383 ++++---- .../Langflow Document QA.json | 761 +++++++--------- .../Langflow Memory Conversation.json | 656 ++++++-------- .../Langflow Prompt Chaining.json | 640 ++++++------- .../VectorStore-RAG-Flows.json | 844 ++++++++---------- 6 files changed, 1630 insertions(+), 2043 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json index a27880ebf..283249015 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json @@ -2,73 +2,79 @@ "data": { "edges": [ { - "className": "", + "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["object", "Text", "str"], "dataType": "OpenAIModel", - "id": "OpenAIModel-NDBjF" + "id": "OpenAIModel-k39HS", + "name": "text_output", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-JkVmc", - "inputTypes": ["Text"], + "id": "ChatOutput-njtka", + "inputTypes": ["Text", "Message"], "type": "str" } }, - "id": "reactflow__edge-OpenAIModel-NDBjF{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-NDBjFœ}-ChatOutput-JkVmc{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-JkVmcœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", - "source": "OpenAIModel-NDBjF", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œTextœ, œstrœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-NDBjFœ}", + "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": "OpenAIModel-k39HS", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-k39HSœ, œoutput_typesœ: [œTextœ], œnameœ: œtext_outputœ}", "style": { "stroke": "#555" }, - "target": "ChatOutput-JkVmc", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-JkVmcœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" - }, - { - "className": "", - "data": { - "sourceHandle": { - "baseClasses": ["object", "str", "Text"], - "dataType": "Prompt", - "id": "Prompt-WSII4" - }, - "targetHandle": { - "fieldName": "input_value", - "id": "OpenAIModel-NDBjF", - "inputTypes": ["Text", "Record", "Prompt"], - "type": "str" - } - }, - "id": "reactflow__edge-Prompt-WSII4{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-WSII4œ}-OpenAIModel-NDBjF{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-NDBjFœ,œinputTypesœ:[œTextœ,œRecordœ,œPromptœ],œtypeœ:œstrœ}", - "source": "Prompt-WSII4", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-WSII4œ}", - "style": { - "stroke": "#555" - }, - "target": "OpenAIModel-NDBjF", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-NDBjFœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" + "target": "ChatOutput-njtka", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-njtkaœ, œinputTypesœ: [œTextœ, œMessageœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": ["Message", "object", "str", "Text"], - "dataType": "ChatInput", - "id": "ChatInput-kltLA" + "dataType": "Prompt", + "id": "Prompt-uxBqP", + "name": "prompt", + "output_types": ["Prompt"] }, "targetHandle": { - "fieldName": "user_input", - "id": "Prompt-WSII4", - "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], + "fieldName": "input_value", + "id": "OpenAIModel-k39HS", + "inputTypes": ["Text", "Data", "Prompt"], "type": "str" } }, - "id": "reactflow__edge-ChatInput-kltLA{œbaseClassesœ:[œMessageœ,œobjectœ,œstrœ,œTextœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-kltLAœ}-Prompt-WSII4{œfieldNameœ:œuser_inputœ,œidœ:œPrompt-WSII4œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "source": "ChatInput-kltLA", - "sourceHandle": "{œbaseClassesœ: [œMessageœ, œobjectœ, œstrœ, œTextœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-kltLAœ}", - "target": "Prompt-WSII4", - "targetHandle": "{œfieldNameœ: œuser_inputœ, œidœ: œPrompt-WSII4œ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "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": "Prompt-uxBqP", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-uxBqPœ, œoutput_typesœ: [œPromptœ], œnameœ: œpromptœ}", + "style": { + "stroke": "#555" + }, + "target": "OpenAIModel-k39HS", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-k39HSœ, œinputTypesœ: [œTextœ, œDataœ, œPromptœ], œtypeœ: œstrœ}" + }, + { + "className": "stroke-gray-900 stroke-connection", + "data": { + "sourceHandle": { + "dataType": "ChatInput", + "id": "ChatInput-P3fgL", + "name": "message", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "user_input", + "id": "Prompt-uxBqP", + "inputTypes": ["Document", "Message", "Record", "Text"], + "type": "str" + } + }, + "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œ}", + "source": "ChatInput-P3fgL", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-P3fgLœ, œoutput_typesœ: [œMessageœ], œnameœ: œmessageœ}", + "style": { + "stroke": "#555" + }, + "target": "Prompt-uxBqP", + "targetHandle": "{œfieldNameœ: œuser_inputœ, œidœ: œPrompt-uxBqPœ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" } ], "nodes": [ @@ -76,7 +82,7 @@ "data": { "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", - "id": "Prompt-WSII4", + "id": "Prompt-uxBqP", "node": { "base_classes": ["object", "str", "Text"], "beta": false, @@ -96,9 +102,29 @@ "is_input": null, "is_output": null, "name": "", - "output_types": ["Prompt"], + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Prompt", + "method": "build_prompt", + "name": "prompt", + "selected": "Prompt", + "types": ["Prompt"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Text", + "method": "format_prompt", + "name": "text", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + } + ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -115,7 +141,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" + "value": "from langflow.custom import Component\nfrom langflow.field_typing.prompt import Prompt\nfrom langflow.inputs import PromptInput\nfrom langflow.template import Output\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n Output(display_name=\"Text\", name=\"text\", method=\"format_prompt\"),\n ]\n\n async def format_prompt(self) -> str:\n prompt = await self.build_prompt()\n formatted_text = prompt.format_text()\n self.status = formatted_text\n return formatted_text\n\n async def build_prompt(\n self,\n ) -> Prompt:\n kwargs = {k: v for k, v in self._arguments.items() if k != \"template\"}\n prompt = await Prompt.from_template_and_variables(self.template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" }, "template": { "advanced": false, @@ -145,12 +171,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "input_types": ["Document", "Message", "Record", "Text"], "list": false, "load_from_db": false, "multiline": true, @@ -168,17 +189,17 @@ "type": "Prompt" }, "dragging": false, - "height": 419, - "id": "Prompt-WSII4", + "height": 383, + "id": "Prompt-uxBqP", "position": { - "x": 18.562420355453696, - "y": -284.15095348876025 + "x": 53.588791333410654, + "y": -107.07318910019967 }, "positionAbsolute": { - "x": 18.562420355453696, - "y": -284.15095348876025 + "x": 53.588791333410654, + "y": -107.07318910019967 }, - "selected": false, + "selected": true, "type": "genericNode", "width": 384 }, @@ -186,7 +207,7 @@ "data": { "description": "Generates text using OpenAI LLMs.", "display_name": "OpenAI", - "id": "OpenAIModel-NDBjF", + "id": "OpenAIModel-k39HS", "node": { "base_classes": ["object", "Text", "str"], "beta": false, @@ -218,9 +239,29 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": ["Text"], + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Text", + "method": "text_response", + "name": "text_output", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Language Model", + "method": "build_model", + "name": "model_output", + "selected": "BaseLanguageModel", + "types": ["BaseLanguageModel"], + "value": "__UNDEFINED__" + } + ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -237,7 +278,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" + "value": "from langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import BaseLanguageModel, Text\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, FloatInput, SecretStrInput, StrInput\nfrom langflow.inputs.inputs import IntInput\nfrom langflow.template import Output\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n StrInput(name=\"input_value\", display_name=\"Input\", input_types=[\"Text\", \"Data\", \"Prompt\"]),\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n 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.\",\n ),\n SecretStrInput(\n name=\"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 value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n BoolInput(name=\"stream\", display_name=\"Stream\", info=STREAM_INFO_TEXT, advanced=True),\n StrInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"System message to pass to the model.\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text_output\", method=\"text_response\"),\n Output(display_name=\"Language Model\", name=\"model_output\", method=\"build_model\"),\n ]\n\n def text_response(self) -> Text:\n input_value = self.input_value\n stream = self.stream\n system_message = self.system_message\n output = self.build_model()\n result = self.get_chat_result(output, stream, input_value, system_message)\n self.status = result\n return result\n\n def build_model(self) -> BaseLanguageModel:\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs or {},\n model=model_name or None,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n )\n return output\n" }, "input_value": { "advanced": false, @@ -246,17 +287,18 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Text", "Record", "Prompt"], + "input_types": ["Text", "Data", "Prompt"], "list": false, "load_from_db": false, "multiline": false, "name": "input_value", "password": false, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "max_tokens": { "advanced": true, @@ -265,6 +307,7 @@ "fileTypes": [], "file_path": "", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -274,8 +317,8 @@ "required": false, "show": true, "title_case": false, - "type": "int", - "value": 256 + "type": "str", + "value": "" }, "model_kwargs": { "advanced": true, @@ -284,6 +327,7 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -293,8 +337,8 @@ "required": false, "show": true, "title_case": false, - "type": "NestedDict", - "value": {} + "type": "str", + "value": "" }, "model_name": { "advanced": false, @@ -340,7 +384,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "openai_api_key": { "advanced": false, @@ -351,16 +396,16 @@ "info": "The OpenAI API Key to use for the OpenAI model.", "input_types": ["Text"], "list": false, - "load_from_db": false, + "load_from_db": true, "multiline": false, "name": "openai_api_key", "password": true, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "type": "str", - "value": "" + "value": "OPENAI_API_KEY" }, "stream": { "advanced": true, @@ -369,6 +414,7 @@ "fileTypes": [], "file_path": "", "info": "Stream the response from the model. Streaming works only in Chat.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -378,7 +424,7 @@ "required": false, "show": true, "title_case": false, - "type": "bool", + "type": "str", "value": false }, "system_message": { @@ -398,7 +444,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "temperature": { "advanced": false, @@ -407,22 +454,17 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, "name": "temperature", "password": false, "placeholder": "", - "rangeSpec": { - "max": 1, - "min": -1, - "step": 0.1, - "step_type": "float" - }, "required": false, "show": true, "title_case": false, - "type": "float", + "type": "str", "value": 0.1 } } @@ -430,8 +472,8 @@ "type": "OpenAIModel" }, "dragging": false, - "height": 571, - "id": "OpenAIModel-NDBjF", + "height": 563, + "id": "OpenAIModel-k39HS", "position": { "x": 634.8148772766217, "y": 27.035057029045305 @@ -446,7 +488,7 @@ }, { "data": { - "id": "ChatOutput-JkVmc", + "id": "ChatOutput-njtka", "node": { "base_classes": ["Record", "Text", "str", "object"], "beta": false, @@ -465,9 +507,20 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": ["Message", "Text"], + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } + ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -484,7 +537,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput, DropdownInput, MultilineInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n input_types=[\"Text\", \"Message\"],\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"Machine\",\n advanced=True,\n info=\"Type of sender.\",\n ),\n StrInput(name=\"sender_name\", display_name=\"Sender Name\", info=\"Name of the sender.\", value=\"AI\", advanced=True),\n StrInput(name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True),\n BoolInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n if isinstance(self.input_value, Message):\n message = self.input_value\n else:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -492,8 +545,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": ["Text"], + "info": "Message to be passed as output.", + "input_types": ["Text", "Message"], "list": false, "load_from_db": false, "multiline": true, @@ -503,7 +556,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "sender": { "advanced": true, @@ -511,7 +565,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", + "info": "Type of sender.", "input_types": ["Text"], "list": true, "load_from_db": false, @@ -527,12 +581,12 @@ "value": "Machine" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", + "info": "Name of the sender.", "input_types": ["Text"], "list": false, "load_from_db": false, @@ -552,7 +606,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", + "info": "Session ID for the message.", "input_types": ["Text"], "list": false, "load_from_db": false, @@ -563,22 +617,23 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" } } }, "type": "ChatOutput" }, "dragging": false, - "height": 391, - "id": "ChatOutput-JkVmc", + "height": 383, + "id": "ChatOutput-njtka", "position": { - "x": 1183.52086970399, - "y": -21.518887039580306 + "x": 1193.250417197867, + "y": 71.88476890163852 }, "positionAbsolute": { - "x": 1183.52086970399, - "y": -21.518887039580306 + "x": 1193.250417197867, + "y": 71.88476890163852 }, "selected": false, "type": "genericNode", @@ -586,14 +641,13 @@ }, { "data": { - "id": "ChatInput-kltLA", + "id": "ChatInput-P3fgL", "node": { - "base_classes": ["Message", "object", "str", "Text"], + "base_classes": ["object", "Record", "str", "Text"], "beta": false, "custom_fields": { - "files": null, "input_value": null, - "return_message": null, + "return_record": null, "sender": null, "sender_name": null, "session_id": null @@ -605,9 +659,20 @@ "field_order": [], "frozen": false, "icon": "ChatInput", - "output_types": ["Message", "Text"], + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } + ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -624,49 +689,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" - }, - "files": { - "advanced": true, - "display_name": "Files", - "dynamic": false, - "fileTypes": [ - ".txt", - ".md", - ".mdx", - ".csv", - ".json", - ".yaml", - ".yml", - ".xml", - ".html", - ".htm", - ".pdf", - ".docx", - ".py", - ".sh", - ".sql", - ".js", - ".ts", - ".tsx", - ".jpg", - ".jpeg", - ".png", - ".bmp" - ], - "file_path": "", - "info": "Files to be sent with the message.", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "files", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "file", - "value": "" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import DropdownInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\n\n inputs = [\n StrInput(\n name=\"input_value\",\n display_name=\"Text\",\n multiline=True,\n input_types=[],\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"User\",\n info=\"Type of sender.\",\n advanced=True,\n ),\n StrInput(\n name=\"sender_name\",\n type=str,\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=\"User\",\n advanced=True,\n ),\n StrInput(\n name=\"session_id\", type=str, display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, (Message, str)) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -674,7 +697,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", + "info": "Message to be passed as input.", "input_types": [], "list": false, "load_from_db": false, @@ -686,26 +709,7 @@ "show": true, "title_case": false, "type": "str", - "value": "what do you see?" - }, - "return_message": { - "advanced": true, - "display_name": "Return Record", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "return_message", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "bool", - "value": true + "value": "" }, "sender": { "advanced": true, @@ -713,7 +717,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", + "info": "Type of sender.", "input_types": ["Text"], "list": true, "load_from_db": false, @@ -734,7 +738,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", + "info": "Name of the sender.", "input_types": ["Text"], "list": false, "load_from_db": false, @@ -754,7 +758,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", + "info": "Session ID for the message.", "input_types": ["Text"], "list": false, "load_from_db": false, @@ -765,37 +769,38 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" } } }, "type": "ChatInput" }, "dragging": false, - "height": 289, - "id": "ChatInput-kltLA", + "height": 375, + "id": "ChatInput-P3fgL", "position": { - "x": -560.3246254009209, - "y": -435.0506368105706 + "x": -495.2223093083827, + "y": -232.56998443685862 }, "positionAbsolute": { - "x": -560.3246254009209, - "y": -435.0506368105706 + "x": -495.2223093083827, + "y": -232.56998443685862 }, - "selected": true, + "selected": false, "type": "genericNode", "width": 384 } ], "viewport": { - "x": 223.38563623650703, - "y": 271.96191180648566, - "zoom": 0.5138985141032123 + "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: \" ", - "id": "ad43b14f-6ec7-496f-9564-aad928603084", + "id": "c091a57f-43a7-4a5e-b352-035ae8d8379c", "is_component": false, - "last_tested_version": "1.0.0a52", + "last_tested_version": "1.0.0a4", "name": "Basic Prompting (Hello, World)" } diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json index 200e625c5..4810c2b28 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json @@ -5,28 +5,22 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Record" - ], "dataType": "URL", - "id": "URL-HYPkR" + "id": "URL-HYPkR", + "name": "record", + "output_types": [] }, "targetHandle": { "fieldName": "reference_2", "id": "Prompt-Rse03", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], "type": "str" } }, "id": "reactflow__edge-URL-HYPkR{œbaseClassesœ:[œRecordœ],œdataTypeœ:œURLœ,œidœ:œURL-HYPkRœ}-Prompt-Rse03{œfieldNameœ:œreference_2œ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "selected": false, "source": "URL-HYPkR", - "sourceHandle": "{œbaseClassesœ: [œRecordœ], œdataTypeœ: œURLœ, œidœ: œURL-HYPkRœ}", + "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-HYPkRœ, œoutput_typesœ: [], œnameœ: œrecordœ}", "style": { "stroke": "#555" }, @@ -37,57 +31,46 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], "dataType": "OpenAIModel", - "id": "OpenAIModel-gi29P" + "id": "OpenAIModel-gi29P", + "name": "text_output", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-JPlxl", - "inputTypes": [ - "Text" - ], + "inputTypes": ["Text", "Message"], "type": "str" } }, "id": "reactflow__edge-OpenAIModel-gi29P{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-gi29Pœ}-ChatOutput-JPlxl{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-JPlxlœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "source": "OpenAIModel-gi29P", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-gi29Pœ}", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-gi29Pœ, œoutput_typesœ: [œTextœ], œnameœ: œtext_outputœ}", "style": { "stroke": "#555" }, "target": "ChatOutput-JPlxl", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-JPlxlœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-JPlxlœ, œinputTypesœ: [œTextœ, œMessageœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Record" - ], "dataType": "URL", - "id": "URL-2cX90" + "id": "URL-2cX90", + "name": "record", + "output_types": [] }, "targetHandle": { "fieldName": "reference_1", "id": "Prompt-Rse03", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], "type": "str" } }, "id": "reactflow__edge-URL-2cX90{œbaseClassesœ:[œRecordœ],œdataTypeœ:œURLœ,œidœ:œURL-2cX90œ}-Prompt-Rse03{œfieldNameœ:œreference_1œ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "source": "URL-2cX90", - "sourceHandle": "{œbaseClassesœ: [œRecordœ], œdataTypeœ: œURLœ, œidœ: œURL-2cX90œ}", + "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-2cX90œ, œoutput_typesœ: [], œnameœ: œrecordœ}", "style": { "stroke": "#555" }, @@ -98,29 +81,21 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "Text", - "str" - ], "dataType": "TextInput", - "id": "TextInput-og8Or" + "id": "TextInput-og8Or", + "name": "Text", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "instructions", "id": "Prompt-Rse03", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"], "type": "str" } }, "id": "reactflow__edge-TextInput-og8Or{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œTextInputœ,œidœ:œTextInput-og8Orœ}-Prompt-Rse03{œfieldNameœ:œinstructionsœ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "source": "TextInput-og8Or", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œTextœ, œstrœ], œdataTypeœ: œTextInputœ, œidœ: œTextInput-og8Orœ}", + "sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-og8Orœ, œoutput_typesœ: [œTextœ], œnameœ: œTextœ}", "style": { "stroke": "#555" }, @@ -131,34 +106,27 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "Text", - "str" - ], "dataType": "Prompt", - "id": "Prompt-Rse03" + "id": "Prompt-Rse03", + "name": "prompt", + "output_types": ["Prompt"] }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-gi29P", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], + "inputTypes": ["Text", "Data", "Prompt"], "type": "str" } }, "id": "reactflow__edge-Prompt-Rse03{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-Rse03œ}-OpenAIModel-gi29P{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-gi29Pœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "selected": false, "source": "Prompt-Rse03", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œTextœ, œstrœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-Rse03œ}", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-Rse03œ, œoutput_typesœ: [œPromptœ], œnameœ: œpromptœ}", "style": { "stroke": "#555" }, "target": "OpenAIModel-gi29P", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-gi29Pœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-gi29Pœ, œinputTypesœ: [œTextœ, œDataœ, œPromptœ], œtypeœ: œstrœ}" } ], "nodes": [ @@ -168,18 +136,10 @@ "display_name": "Prompt", "id": "Prompt-Rse03", "node": { - "base_classes": [ - "object", - "Text", - "str" - ], + "base_classes": ["object", "Text", "str"], "beta": false, "custom_fields": { - "template": [ - "reference_1", - "reference_2", - "instructions" - ] + "template": ["reference_1", "reference_2", "instructions"] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", @@ -194,11 +154,29 @@ "is_input": null, "is_output": null, "name": "", - "output_types": [ - "Prompt" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Prompt", + "method": "build_prompt", + "name": "prompt", + "selected": "Prompt", + "types": ["Prompt"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Text", + "method": "format_prompt", + "name": "text", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -215,7 +193,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" + "value": "from langflow.custom import Component\nfrom langflow.field_typing.prompt import Prompt\nfrom langflow.inputs import PromptInput\nfrom langflow.template import Output\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n Output(display_name=\"Text\", name=\"text\", method=\"format_prompt\"),\n ]\n\n async def format_prompt(self) -> str:\n prompt = await self.build_prompt()\n formatted_text = prompt.format_text()\n self.status = formatted_text\n return formatted_text\n\n async def build_prompt(\n self,\n ) -> Prompt:\n kwargs = {k: v for k, v in self._arguments.items() if k != \"template\"}\n prompt = await Prompt.from_template_and_variables(self.template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" }, "instructions": { "advanced": false, @@ -302,9 +280,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -340,9 +316,7 @@ "data": { "id": "URL-HYPkR", "node": { - "base_classes": [ - "Record" - ], + "base_classes": ["Record"], "beta": false, "custom_fields": { "urls": null @@ -354,11 +328,20 @@ "field_order": [], "frozen": false, "icon": "layout-template", - "output_types": [ - "Record" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "method": "fetch_content", + "name": "data", + "selected": "Data", + "types": ["Data"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -375,31 +358,22 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Any, Dict\n\nfrom langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import CustomComponent\nfrom langflow.schema import Record\n\n\nclass URLComponent(CustomComponent):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n def build_config(self) -> Dict[str, Any]:\n return {\n \"urls\": {\"display_name\": \"URL\"},\n }\n\n def build(\n self,\n urls: list[str],\n ) -> list[Record]:\n loader = WebBaseLoader(web_paths=[url for url in urls if url])\n docs = loader.load()\n records = self.to_records(docs)\n self.status = records\n return records\n" + "value": "from langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.inputs import StrInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n inputs = [\n StrInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, separated by commas.\",\n value=\"\",\n is_list=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n ]\n\n def fetch_content(self) -> Data:\n urls = [url.strip() for url in self.urls if url.strip()]\n loader = WebBaseLoader(web_paths=urls)\n docs = loader.load()\n data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n" }, "urls": { "advanced": false, - "display_name": "URL", + "display_name": "URLs", "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Enter one or more URLs, separated by commas.", "list": true, "load_from_db": false, - "multiline": false, "name": "urls", - "password": false, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "type": "str", - "value": [ - "https://www.promptingguide.ai/techniques/prompt_chaining" - ] + "value": "" } } }, @@ -424,12 +398,7 @@ "data": { "id": "ChatOutput-JPlxl", "node": { - "base_classes": [ - "Text", - "Record", - "object", - "str" - ], + "base_classes": ["Text", "Record", "object", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -446,12 +415,20 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -468,7 +445,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput, DropdownInput, MultilineInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n input_types=[\"Text\", \"Message\"],\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"Machine\",\n advanced=True,\n info=\"Type of sender.\",\n ),\n StrInput(name=\"sender_name\", display_name=\"Sender Name\", info=\"Name of the sender.\", value=\"AI\", advanced=True),\n StrInput(name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True),\n BoolInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n if isinstance(self.input_value, Message):\n message = self.input_value\n else:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -476,10 +453,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Message to be passed as output.", + "input_types": ["Text", "Message"], "list": false, "load_from_db": false, "multiline": true, @@ -489,7 +464,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "sender": { "advanced": true, @@ -497,18 +473,13 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Type of sender.", + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -518,15 +489,13 @@ "value": "Machine" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Name of the sender.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -545,10 +514,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "info": "Session ID for the message.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -558,7 +525,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" } } }, @@ -578,11 +546,7 @@ "data": { "id": "OpenAIModel-gi29P", "node": { - "base_classes": [ - "str", - "Text", - "object" - ], + "base_classes": ["str", "Text", "object"], "beta": false, "custom_fields": { "input_value": null, @@ -612,11 +576,29 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": [ - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Text", + "method": "text_response", + "name": "text_output", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Language Model", + "method": "build_model", + "name": "model_output", + "selected": "BaseLanguageModel", + "types": ["BaseLanguageModel"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -633,7 +615,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" + "value": "from langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import BaseLanguageModel, Text\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, FloatInput, SecretStrInput, StrInput\nfrom langflow.inputs.inputs import IntInput\nfrom langflow.template import Output\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n StrInput(name=\"input_value\", display_name=\"Input\", input_types=[\"Text\", \"Data\", \"Prompt\"]),\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n 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.\",\n ),\n SecretStrInput(\n name=\"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 value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n BoolInput(name=\"stream\", display_name=\"Stream\", info=STREAM_INFO_TEXT, advanced=True),\n StrInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"System message to pass to the model.\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text_output\", method=\"text_response\"),\n Output(display_name=\"Language Model\", name=\"model_output\", method=\"build_model\"),\n ]\n\n def text_response(self) -> Text:\n input_value = self.input_value\n stream = self.stream\n system_message = self.system_message\n output = self.build_model()\n result = self.get_chat_result(output, stream, input_value, system_message)\n self.status = result\n return result\n\n def build_model(self) -> BaseLanguageModel:\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs or {},\n model=model_name or None,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n )\n return output\n" }, "input_value": { "advanced": false, @@ -642,21 +624,18 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], + "input_types": ["Text", "Data", "Prompt"], "list": false, "load_from_db": false, "multiline": false, "name": "input_value", "password": false, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "max_tokens": { "advanced": true, @@ -665,6 +644,7 @@ "fileTypes": [], "file_path": "", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -674,8 +654,8 @@ "required": false, "show": true, "title_case": false, - "type": "int", - "value": "1024" + "type": "str", + "value": "" }, "model_kwargs": { "advanced": true, @@ -684,6 +664,7 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -693,8 +674,8 @@ "required": false, "show": true, "title_case": false, - "type": "NestedDict", - "value": {} + "type": "str", + "value": "" }, "model_name": { "advanced": false, @@ -703,9 +684,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -732,9 +711,7 @@ "fileTypes": [], "file_path": "", "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.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -744,7 +721,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "openai_api_key": { "advanced": false, @@ -753,16 +731,14 @@ "fileTypes": [], "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, "name": "openai_api_key", "password": true, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "type": "str", @@ -775,6 +751,7 @@ "fileTypes": [], "file_path": "", "info": "Stream the response from the model. Streaming works only in Chat.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -784,8 +761,8 @@ "required": false, "show": true, "title_case": false, - "type": "bool", - "value": true + "type": "str", + "value": false }, "system_message": { "advanced": true, @@ -794,9 +771,7 @@ "fileTypes": [], "file_path": "", "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -806,7 +781,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "temperature": { "advanced": false, @@ -815,23 +791,18 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, "name": "temperature", "password": false, "placeholder": "", - "rangeSpec": { - "max": 1, - "min": -1, - "step": 0.1, - "step_type": "float" - }, "required": false, "show": true, "title_case": false, - "type": "float", - "value": "0.1" + "type": "str", + "value": 0.1 } } }, @@ -856,9 +827,7 @@ "data": { "id": "URL-2cX90", "node": { - "base_classes": [ - "Record" - ], + "base_classes": ["Record"], "beta": false, "custom_fields": { "urls": null @@ -870,11 +839,20 @@ "field_order": [], "frozen": false, "icon": "layout-template", - "output_types": [ - "Record" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "method": "fetch_content", + "name": "data", + "selected": "Data", + "types": ["Data"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -891,31 +869,22 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Any, Dict\n\nfrom langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import CustomComponent\nfrom langflow.schema import Record\n\n\nclass URLComponent(CustomComponent):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n def build_config(self) -> Dict[str, Any]:\n return {\n \"urls\": {\"display_name\": \"URL\"},\n }\n\n def build(\n self,\n urls: list[str],\n ) -> list[Record]:\n loader = WebBaseLoader(web_paths=[url for url in urls if url])\n docs = loader.load()\n records = self.to_records(docs)\n self.status = records\n return records\n" + "value": "from langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.inputs import StrInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n inputs = [\n StrInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, separated by commas.\",\n value=\"\",\n is_list=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n ]\n\n def fetch_content(self) -> Data:\n urls = [url.strip() for url in self.urls if url.strip()]\n loader = WebBaseLoader(web_paths=urls)\n docs = loader.load()\n data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n" }, "urls": { "advanced": false, - "display_name": "URL", + "display_name": "URLs", "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Enter one or more URLs, separated by commas.", "list": true, "load_from_db": false, - "multiline": false, "name": "urls", - "password": false, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "type": "str", - "value": [ - "https://www.promptingguide.ai/introduction/basics" - ] + "value": "" } } }, @@ -940,11 +909,7 @@ "data": { "id": "TextInput-og8Or", "node": { - "base_classes": [ - "object", - "Text", - "str" - ], + "base_classes": ["object", "Text", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -957,8 +922,13 @@ "field_order": [], "frozen": false, "icon": "type", - "output_types": [ - "Text" + "output_types": ["Text"], + "outputs": [ + { + "name": "Text", + "selected": "Text", + "types": ["Text"] + } ], "template": { "_type": "CustomComponent", @@ -987,10 +957,7 @@ "fileTypes": [], "file_path": "", "info": "Text or Record to be passed as input.", - "input_types": [ - "Record", - "Text" - ], + "input_types": ["Record", "Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1010,9 +977,7 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json index 97e56a5ba..48b474fc0 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json @@ -2,157 +2,107 @@ "data": { "edges": [ { - "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "Record", - "Text", - "object" - ], - "dataType": "ChatInput", - "id": "ChatInput-MsSJ9" - }, - "targetHandle": { - "fieldName": "Question", - "id": "Prompt-tHwPf", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-ChatInput-MsSJ9{œbaseClassesœ:[œstrœ,œRecordœ,œTextœ,œobjectœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-MsSJ9œ}-Prompt-tHwPf{œfieldNameœ:œQuestionœ,œidœ:œPrompt-tHwPfœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "source": "ChatInput-MsSJ9", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œRecordœ, œTextœ, œobjectœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-MsSJ9œ}", - "style": { - "stroke": "#555" - }, - "target": "Prompt-tHwPf", - "targetHandle": "{œfieldNameœ: œQuestionœ, œidœ: œPrompt-tHwPfœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" - }, - { - "className": "stroke-gray-900 stroke-connection", - "data": { - "sourceHandle": { - "baseClasses": [ - "Record" - ], "dataType": "File", - "id": "File-6TEsD" + "id": "File-BzIs2", + "name": "data", + "output_types": ["Data"] }, "targetHandle": { "fieldName": "Document", - "id": "Prompt-tHwPf", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "id": "Prompt-9DNZG", + "inputTypes": ["Document", "Message", "Data", "Text"], "type": "str" } }, - "id": "reactflow__edge-File-6TEsD{œbaseClassesœ:[œRecordœ],œdataTypeœ:œFileœ,œidœ:œFile-6TEsDœ}-Prompt-tHwPf{œfieldNameœ:œDocumentœ,œidœ:œPrompt-tHwPfœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "source": "File-6TEsD", - "sourceHandle": "{œbaseClassesœ: [œRecordœ], œdataTypeœ: œFileœ, œidœ: œFile-6TEsDœ}", - "style": { - "stroke": "#555" - }, - "target": "Prompt-tHwPf", - "targetHandle": "{œfieldNameœ: œDocumentœ, œidœ: œPrompt-tHwPfœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-File-BzIs2{œdataTypeœ:œFileœ,œidœ:œFile-BzIs2œ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-Prompt-9DNZG{œfieldNameœ:œDocumentœ,œidœ:œPrompt-9DNZGœ,œinputTypesœ:[œDocumentœ,œMessageœ,œDataœ,œTextœ],œtypeœ:œstrœ}", + "source": "File-BzIs2", + "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-BzIs2œ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}", + "target": "Prompt-9DNZG", + "targetHandle": "{œfieldNameœ: œDocumentœ, œidœ: œPrompt-9DNZGœ, œinputTypesœ: [œDocumentœ, œMessageœ, œDataœ, œTextœ], œtypeœ: œstrœ}" + }, + { + "data": { + "sourceHandle": { + "dataType": "ChatInput", + "id": "ChatInput-27Usy", + "name": "message", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "Question", + "id": "Prompt-9DNZG", + "inputTypes": ["Document", "Message", "Data", "Text"], + "type": "str" + } + }, + "id": "reactflow__edge-ChatInput-27Usy{œdataTypeœ:œChatInputœ,œidœ:œChatInput-27Usyœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-9DNZG{œfieldNameœ:œQuestionœ,œidœ:œPrompt-9DNZGœ,œinputTypesœ:[œDocumentœ,œMessageœ,œDataœ,œTextœ],œtypeœ:œstrœ}", + "source": "ChatInput-27Usy", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-27Usyœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-9DNZG", + "targetHandle": "{œfieldNameœ: œQuestionœ, œidœ: œPrompt-9DNZGœ, œinputTypesœ: [œDocumentœ, œMessageœ, œDataœ, œTextœ], œtypeœ: œstrœ}" }, { - "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "str", - "Text" - ], "dataType": "Prompt", - "id": "Prompt-tHwPf" + "id": "Prompt-9DNZG", + "name": "prompt", + "output_types": ["Prompt"] }, "targetHandle": { "fieldName": "input_value", - "id": "OpenAIModel-Bt067", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], + "id": "OpenAIModel-8b6nG", + "inputTypes": ["Text", "Data", "Prompt"], "type": "str" } }, - "id": "reactflow__edge-Prompt-tHwPf{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-tHwPfœ}-OpenAIModel-Bt067{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Bt067œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", - "source": "Prompt-tHwPf", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-tHwPfœ}", - "style": { - "stroke": "#555" - }, - "target": "OpenAIModel-Bt067", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-Bt067œ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-Prompt-9DNZG{œdataTypeœ:œPromptœ,œidœ:œPrompt-9DNZGœ,œnameœ:œpromptœ,œoutput_typesœ:[œPromptœ]}-OpenAIModel-8b6nG{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-8b6nGœ,œinputTypesœ:[œTextœ,œDataœ,œPromptœ],œtypeœ:œstrœ}", + "source": "Prompt-9DNZG", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-9DNZGœ, œnameœ: œpromptœ, œoutput_typesœ: [œPromptœ]}", + "target": "OpenAIModel-8b6nG", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-8b6nGœ, œinputTypesœ: [œTextœ, œDataœ, œPromptœ], œtypeœ: œstrœ}" }, { - "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "str", - "Text" - ], "dataType": "OpenAIModel", - "id": "OpenAIModel-Bt067" + "id": "OpenAIModel-8b6nG", + "name": "text_output", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-F5Awj", - "inputTypes": [ - "Text" - ], + "id": "ChatOutput-y4SCS", + "inputTypes": ["Text", "Message"], "type": "str" } }, - "id": "reactflow__edge-OpenAIModel-Bt067{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Bt067œ}-ChatOutput-F5Awj{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-F5Awjœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", - "source": "OpenAIModel-Bt067", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-Bt067œ}", - "style": { - "stroke": "#555" - }, - "target": "ChatOutput-F5Awj", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-F5Awjœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-OpenAIModel-8b6nG{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-8b6nGœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œTextœ]}-ChatOutput-y4SCS{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-y4SCSœ,œinputTypesœ:[œTextœ,œMessageœ],œtypeœ:œstrœ}", + "source": "OpenAIModel-8b6nG", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-8b6nGœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œTextœ]}", + "target": "ChatOutput-y4SCS", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-y4SCSœ, œinputTypesœ: [œTextœ, œMessageœ], œtypeœ: œstrœ}" } ], "nodes": [ { "data": { - "description": "A component for creating prompt templates using dynamic variables.", + "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", - "id": "Prompt-tHwPf", + "id": "Prompt-9DNZG", "node": { - "base_classes": [ - "object", - "str", - "Text" - ], + "base_classes": ["object", "str", "Text"], "beta": false, + "conditional_paths": [], "custom_fields": { - "template": [ - "Document", - "Question" - ] + "template": ["Document", "Question"] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", "documentation": "", "error": null, - "field_formatters": {}, "field_order": [], "frozen": false, "full_path": null, @@ -161,9 +111,28 @@ "is_input": null, "is_output": null, "name": "", - "output_types": [ - "Prompt" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Prompt", + "method": "build_prompt", + "name": "prompt", + "selected": "Prompt", + "types": ["Prompt"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Text", + "method": "format_prompt", + "name": "text", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + } ], + "pinned": false, "template": { "Document": { "advanced": false, @@ -173,12 +142,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "input_types": ["Document", "Message", "Data", "Text"], "list": false, "load_from_db": false, "multiline": true, @@ -199,12 +163,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "input_types": ["Document", "Message", "Data", "Text"], "list": false, "load_from_db": false, "multiline": true, @@ -217,7 +176,7 @@ "type": "str", "value": "" }, - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -234,7 +193,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" + "value": "from langflow.custom import Component\nfrom langflow.field_typing.prompt import Prompt\nfrom langflow.inputs import PromptInput\nfrom langflow.template import Output\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n Output(display_name=\"Text\", name=\"text\", method=\"format_prompt\"),\n ]\n\n async def format_prompt(self) -> str:\n prompt = await self.build_prompt()\n formatted_text = prompt.format_text()\n self.status = formatted_text\n return formatted_text\n\n async def build_prompt(\n self,\n ) -> Prompt:\n kwargs = {k: v for k, v in self._arguments.items() if k != \"template\"}\n prompt = await Prompt.from_template_and_variables(self.template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" }, "template": { "advanced": false, @@ -243,9 +202,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -263,8 +220,8 @@ "type": "Prompt" }, "dragging": false, - "height": 479, - "id": "Prompt-tHwPf", + "height": 573, + "id": "Prompt-9DNZG", "position": { "x": 585.7906101139403, "y": 117.52115876762832 @@ -279,124 +236,9 @@ }, { "data": { - "id": "File-6TEsD", + "id": "ChatInput-27Usy", "node": { - "base_classes": [ - "Record" - ], - "beta": false, - "custom_fields": { - "path": null, - "silent_errors": null - }, - "description": "A generic file loader.", - "display_name": "Files", - "documentation": "", - "field_formatters": {}, - "field_order": [], - "frozen": false, - "output_types": [ - "Record" - ], - "template": { - "_type": "CustomComponent", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from pathlib import Path\nfrom typing import Any, Dict\n\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parse_text_file_to_record\nfrom langflow.interface.custom.custom_component import CustomComponent\nfrom langflow.schema import Record\n\n\nclass FileComponent(CustomComponent):\n display_name = \"Files\"\n description = \"A generic file loader.\"\n\n def build_config(self) -> Dict[str, Any]:\n return {\n \"path\": {\n \"display_name\": \"Path\",\n \"field_type\": \"file\",\n \"file_types\": TEXT_FILE_TYPES,\n \"info\": f\"Supported file types: {', '.join(TEXT_FILE_TYPES)}\",\n },\n \"silent_errors\": {\n \"display_name\": \"Silent Errors\",\n \"advanced\": True,\n \"info\": \"If true, errors will not raise an exception.\",\n },\n }\n\n def load_file(self, path: str, silent_errors: bool = False) -> Record:\n resolved_path = self.resolve_path(path)\n path_obj = Path(resolved_path)\n extension = path_obj.suffix[1:].lower()\n if extension == \"doc\":\n raise ValueError(\"doc files are not supported. Please save as .docx\")\n if extension not in TEXT_FILE_TYPES:\n raise ValueError(f\"Unsupported file type: {extension}\")\n record = parse_text_file_to_record(resolved_path, silent_errors)\n self.status = record if record else \"No data\"\n return record or Record()\n\n def build(\n self,\n path: str,\n silent_errors: bool = False,\n ) -> Record:\n record = self.load_file(path, silent_errors)\n self.status = record\n return record\n" - }, - "path": { - "advanced": false, - "display_name": "Path", - "dynamic": false, - "fileTypes": [ - ".txt", - ".md", - ".mdx", - ".csv", - ".json", - ".yaml", - ".yml", - ".xml", - ".html", - ".htm", - ".pdf", - ".docx" - ], - "info": "Supported file types: txt, md, mdx, csv, json, yaml, yml, xml, html, htm, pdf, docx", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "path", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "file", - "value": "" - }, - "silent_errors": { - "advanced": true, - "display_name": "Silent Errors", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "If true, errors will not raise an exception.", - "list": false, - "load_from_db": false, - "multiline": false, - "name": "silent_errors", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "bool", - "value": false - } - } - }, - "type": "File" - }, - "dragging": false, - "height": 282, - "id": "File-6TEsD", - "position": { - "x": -18.636536329280602, - "y": 3.951948774836353 - }, - "positionAbsolute": { - "x": -18.636536329280602, - "y": 3.951948774836353 - }, - "selected": false, - "type": "genericNode", - "width": 384 - }, - { - "data": { - "id": "ChatInput-MsSJ9", - "node": { - "base_classes": [ - "str", - "Record", - "Text", - "object" - ], + "base_classes": ["str", "Record", "Text", "object"], "beta": false, "custom_fields": { "input_value": null, @@ -412,12 +254,20 @@ "field_order": [], "frozen": false, "icon": "ChatInput", - "output_types": [ - "Message", - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -434,7 +284,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import DropdownInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\n\n inputs = [\n StrInput(\n name=\"input_value\",\n display_name=\"Text\",\n multiline=True,\n input_types=[],\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"User\",\n info=\"Type of sender.\",\n advanced=True,\n ),\n StrInput(\n name=\"sender_name\",\n type=str,\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=\"User\",\n advanced=True,\n ),\n StrInput(\n name=\"session_id\", type=str, display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, (Message, str)) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -442,7 +292,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", + "info": "Message to be passed as input.", "input_types": [], "list": false, "load_from_db": false, @@ -462,18 +312,13 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Type of sender.", + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -483,15 +328,13 @@ "value": "User" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Name of the sender.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -510,10 +353,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "info": "Session ID for the message.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -523,37 +364,33 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" } } }, "type": "ChatInput" }, "dragging": false, - "height": 377, - "id": "ChatInput-MsSJ9", + "height": 301, + "id": "ChatInput-27Usy", "position": { - "x": -28.80036300619821, + "x": -38.501719080514135, "y": 379.81180230285355 }, "positionAbsolute": { - "x": -28.80036300619821, + "x": -38.501719080514135, "y": 379.81180230285355 }, - "selected": true, + "selected": false, "type": "genericNode", "width": 384 }, { "data": { - "id": "ChatOutput-F5Awj", + "id": "ChatOutput-y4SCS", "node": { - "base_classes": [ - "str", - "Record", - "Text", - "object" - ], + "base_classes": ["str", "Record", "Text", "object"], "beta": false, "custom_fields": { "input_value": null, @@ -569,12 +406,20 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -591,7 +436,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput, DropdownInput, MultilineInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n input_types=[\"Text\", \"Message\"],\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"Machine\",\n advanced=True,\n info=\"Type of sender.\",\n ),\n StrInput(name=\"sender_name\", display_name=\"Sender Name\", info=\"Name of the sender.\", value=\"AI\", advanced=True),\n StrInput(name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True),\n BoolInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n if isinstance(self.input_value, Message):\n message = self.input_value\n else:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -599,10 +444,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Message to be passed as output.", + "input_types": ["Text", "Message"], "list": false, "load_from_db": false, "multiline": true, @@ -612,7 +455,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "sender": { "advanced": true, @@ -620,18 +464,13 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Type of sender.", + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -641,15 +480,13 @@ "value": "Machine" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Name of the sender.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -668,10 +505,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "info": "Session ID for the message.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -681,15 +516,16 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" } } }, "type": "ChatOutput" }, "dragging": false, - "height": 385, - "id": "ChatOutput-F5Awj", + "height": 309, + "id": "ChatOutput-y4SCS", "position": { "x": 1733.3012915204283, "y": 168.76098809939327 @@ -704,47 +540,36 @@ }, { "data": { - "id": "OpenAIModel-Bt067", + "description": "A generic file loader.", + "display_name": "File", + "id": "File-BzIs2", "node": { - "base_classes": [ - "object", - "str", - "Text" - ], + "base_classes": ["Data"], "beta": false, - "custom_fields": { - "input_value": null, - "max_tokens": null, - "model_kwargs": null, - "model_name": null, - "openai_api_base": null, - "openai_api_key": null, - "stream": null, - "system_message": null, - "temperature": null - }, - "description": "Generates text using OpenAI LLMs.", - "display_name": "OpenAI", + "conditional_paths": [], + "custom_fields": {}, + "description": "A generic file loader.", + "display_name": "File", "documentation": "", - "field_formatters": {}, - "field_order": [ - "max_tokens", - "model_kwargs", - "model_name", - "openai_api_base", - "openai_api_key", - "temperature", - "input_value", - "system_message", - "stream" - ], + "edited": true, + "field_order": ["path", "silent_errors"], "frozen": false, - "icon": "OpenAI", - "output_types": [ - "Text" + "icon": "file-text", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "method": "load_file", + "name": "data", + "selected": "Data", + "types": ["Data"], + "value": "__UNDEFINED__" + } ], + "pinned": false, "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -761,82 +586,189 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" + "value": "from pathlib import Path\n\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parse_text_file_to_data\nfrom langflow.custom import Component\nfrom langflow.inputs import BoolInput, FileInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\n\nclass FileComponent(Component):\n display_name = \"File\"\n description = \"A generic file loader.\"\n icon = \"file-text\"\n\n inputs = [\n FileInput(\n name=\"path\",\n display_name=\"Path\",\n file_types=TEXT_FILE_TYPES,\n info=f\"Supported file types: {', '.join(TEXT_FILE_TYPES)}\",\n ),\n BoolInput(\n name=\"silent_errors\",\n display_name=\"Silent Errors\",\n advanced=True,\n info=\"If true, errors will not raise an exception.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"load_file\"),\n ]\n\n def load_file(self) -> Data:\n if not self.path:\n raise ValueError(\"Please, upload a file to use this component.\")\n resolved_path = self.resolve_path(self.path)\n silent_errors = self.silent_errors\n\n extension = Path(resolved_path).suffix[1:].lower()\n\n if extension == \"doc\":\n raise ValueError(\"doc files are not supported. Please save as .docx\")\n if extension not in TEXT_FILE_TYPES:\n raise ValueError(f\"Unsupported file type: {extension}\")\n\n data = parse_text_file_to_data(resolved_path, silent_errors)\n self.status = data if data else \"No data\"\n return data or Data()\n" }, - "input_value": { + "path": { "advanced": false, - "display_name": "Input", + "display_name": "Path", "dynamic": false, + "fileTypes": [ + "txt", + "md", + "mdx", + "csv", + "json", + "yaml", + "yml", + "xml", + "html", + "htm", + "pdf", + "docx", + "py", + "sh", + "sql", + "js", + "ts", + "tsx" + ], + "file_path": "cd558bbb-10b7-4c22-a7ad-3739f26b4bd7/Climate Prediction Updated.json", + "info": "Supported file types: txt, md, mdx, csv, json, yaml, yml, xml, html, htm, pdf, docx, py, sh, sql, js, ts, tsx", + "list": false, + "name": "path", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "file", + "value": "" + }, + "silent_errors": { + "advanced": true, + "display_name": "Silent Errors", + "dynamic": false, + "info": "If true, errors will not raise an exception.", + "list": false, + "name": "silent_errors", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "bool", + "value": false + } + } + }, + "type": "File" + }, + "dragging": false, + "height": 301, + "id": "File-BzIs2", + "position": { + "x": -44.56084223565597, + "y": 39.0475820447775 + }, + "positionAbsolute": { + "x": -44.56084223565597, + "y": 39.0475820447775 + }, + "selected": true, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "OpenAIModel-8b6nG", + "node": { + "base_classes": ["BaseLanguageModel", "Text"], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Generates text using OpenAI LLMs.", + "display_name": "OpenAI", + "documentation": "", + "field_order": [ + "input_value", + "max_tokens", + "model_kwargs", + "model_name", + "openai_api_base", + "openai_api_key", + "temperature", + "stream", + "system_message" + ], + "frozen": false, + "icon": "OpenAI", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Text", + "method": "text_response", + "name": "text_output", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Language Model", + "method": "build_model", + "name": "model_output", + "selected": "BaseLanguageModel", + "types": ["BaseLanguageModel"], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "code": { + "advanced": true, + "dynamic": true, "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], "list": false, "load_from_db": false, - "multiline": false, - "name": "input_value", + "multiline": true, + "name": "code", "password": false, "placeholder": "", "required": true, "show": true, "title_case": false, - "type": "str" + "type": "code", + "value": "from langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import BaseLanguageModel, Text\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, FloatInput, SecretStrInput, StrInput\nfrom langflow.inputs.inputs import IntInput\nfrom langflow.template import Output\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n StrInput(name=\"input_value\", display_name=\"Input\", input_types=[\"Text\", \"Data\", \"Prompt\"]),\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n 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.\",\n ),\n SecretStrInput(\n name=\"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 value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n BoolInput(name=\"stream\", display_name=\"Stream\", info=STREAM_INFO_TEXT, advanced=True),\n StrInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"System message to pass to the model.\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text_output\", method=\"text_response\"),\n Output(display_name=\"Language Model\", name=\"model_output\", method=\"build_model\"),\n ]\n\n def text_response(self) -> Text:\n input_value = self.input_value\n stream = self.stream\n system_message = self.system_message\n output = self.build_model()\n result = self.get_chat_result(output, stream, input_value, system_message)\n self.status = result\n return result\n\n def build_model(self) -> BaseLanguageModel:\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs or {},\n model=model_name or None,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n )\n return output\n" + }, + "input_value": { + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "", + "input_types": ["Text", "Data", "Prompt"], + "list": false, + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" }, "max_tokens": { "advanced": true, "display_name": "Max Tokens", "dynamic": false, - "fileTypes": [], - "file_path": "", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", "list": false, - "load_from_db": false, - "multiline": false, "name": "max_tokens", - "password": false, "placeholder": "", "required": false, "show": true, "title_case": false, - "type": "int", - "value": 256 + "type": "int" }, "model_kwargs": { "advanced": true, "display_name": "Model Kwargs", "dynamic": false, - "fileTypes": [], - "file_path": "", "info": "", "list": false, - "load_from_db": false, - "multiline": false, "name": "model_kwargs", - "password": false, "placeholder": "", "required": false, "show": true, "title_case": false, - "type": "NestedDict", - "value": {} + "type": "dict" }, "model_name": { "advanced": false, "display_name": "Model Name", "dynamic": false, - "fileTypes": [], - "file_path": "", "info": "", - "input_types": [ - "Text" - ], - "list": true, - "load_from_db": false, - "multiline": false, "name": "model_name", "options": [ "gpt-4o", @@ -845,7 +777,6 @@ "gpt-3.5-turbo", "gpt-3.5-turbo-0125" ], - "password": false, "placeholder": "", "required": false, "show": true, @@ -857,104 +788,70 @@ "advanced": true, "display_name": "OpenAI API Base", "dynamic": false, - "fileTypes": [], - "file_path": "", "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.", - "input_types": [ - "Text" - ], "list": false, "load_from_db": false, - "multiline": false, "name": "openai_api_base", - "password": false, "placeholder": "", "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "openai_api_key": { "advanced": false, "display_name": "OpenAI API Key", "dynamic": false, - "fileTypes": [], - "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], - "list": false, + "input_types": ["Text"], "load_from_db": true, - "multiline": false, "name": "openai_api_key", "password": true, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "type": "str", "value": "OPENAI_API_KEY" }, "stream": { - "advanced": false, + "advanced": true, "display_name": "Stream", "dynamic": false, - "fileTypes": [], - "file_path": "", "info": "Stream the response from the model. Streaming works only in Chat.", "list": false, - "load_from_db": false, - "multiline": false, "name": "stream", - "password": false, "placeholder": "", "required": false, "show": true, "title_case": false, "type": "bool", - "value": true + "value": false }, "system_message": { "advanced": true, "display_name": "System Message", "dynamic": false, - "fileTypes": [], - "file_path": "", "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], "list": false, "load_from_db": false, - "multiline": false, "name": "system_message", - "password": false, "placeholder": "", "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "temperature": { "advanced": false, "display_name": "Temperature", "dynamic": false, - "fileTypes": [], - "file_path": "", "info": "", "list": false, - "load_from_db": false, - "multiline": false, "name": "temperature", - "password": false, "placeholder": "", - "rangeSpec": { - "max": 1, - "min": -1, - "step": 0.1, - "step_type": "float" - }, "required": false, "show": true, "title_case": false, @@ -966,15 +863,15 @@ "type": "OpenAIModel" }, "dragging": false, - "height": 642, - "id": "OpenAIModel-Bt067", + "height": 623, + "id": "OpenAIModel-8b6nG", "position": { - "x": 1137.6078582863759, - "y": -14.41920034020356 + "x": 1141.7303854551026, + "y": -51.19892217231286 }, "positionAbsolute": { - "x": 1137.6078582863759, - "y": -14.41920034020356 + "x": 1141.7303854551026, + "y": -51.19892217231286 }, "selected": false, "type": "genericNode", @@ -982,14 +879,14 @@ } ], "viewport": { - "x": 352.20899206064655, - "y": 56.054900898593075, - "zoom": 0.9023391400011 + "x": 198.5801484914205, + "y": 244.8736279905512, + "zoom": 0.5335671198494703 } }, "description": "This flow integrates PDF reading with a language model to answer document-specific questions. Ideal for small-scale texts, it facilitates direct queries with immediate insights.", - "id": "fecbce42-6f11-454c-8ab2-db6eddbbbb0f", + "id": "cd558bbb-10b7-4c22-a7ad-3739f26b4bd7", "is_component": false, - "last_tested_version": "1.0.0a0", + "last_tested_version": "1.0.0a52", "name": "Document QA" } diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json index 6c5b563a5..f8617e1ae 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json @@ -2,162 +2,139 @@ "data": { "edges": [ { - "className": "", + "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "object", - "Text" - ], - "dataType": "OpenAIModel", - "id": "OpenAIModel-Neuec" + "dataType": "MemoryComponent", + "id": "MemoryComponent-cdA1J", + "name": "text", + "output_types": ["Text"] }, "targetHandle": { - "fieldName": "input_value", - "id": "ChatOutput-cVR7W", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "fieldName": "context", + "id": "Prompt-ODkUx", + "inputTypes": ["Document", "Message", "Record", "Text"], "type": "str" } }, - "id": "reactflow__edge-OpenAIModel-Neuec{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Neuecœ}-ChatOutput-cVR7W{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-cVR7Wœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", - "source": "OpenAIModel-Neuec", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œobjectœ, œTextœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-Neuecœ}", + "id": "reactflow__edge-MemoryComponent-cdA1J{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}-Prompt-ODkUx{œfieldNameœ:œcontextœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "selected": false, + "source": "MemoryComponent-cdA1J", + "sourceHandle": "{œdataTypeœ: œMemoryComponentœ, œidœ: œMemoryComponent-cdA1Jœ, œoutput_typesœ: [œTextœ], œnameœ: œtextœ}", "style": { "stroke": "#555" }, - "target": "ChatOutput-cVR7W", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-cVR7Wœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" + "target": "Prompt-ODkUx", + "targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-ODkUxœ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" }, { - "className": "", + "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "str", - "Text" - ], + "dataType": "ChatInput", + "id": "ChatInput-t7F8v", + "name": "message", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "user_message", + "id": "Prompt-ODkUx", + "inputTypes": ["Document", "Message", "Record", "Text"], "type": "str" } }, "id": "reactflow__edge-ChatInput-t7F8v{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-t7F8vœ}-Prompt-ODkUx{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "selected": false, "source": "ChatInput-t7F8v", - "sourceHandle": "{œbaseClassesœ: [œTextœ, œobjectœ, œRecordœ, œstrœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-t7F8vœ}", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-t7F8vœ, œoutput_typesœ: [œMessageœ], œnameœ: œmessageœ}", "style": { "stroke": "#555" }, "target": "Prompt-ODkUx", - "targetHandle": "{œfieldNameœ: œuser_messageœ, œidœ: œPrompt-ODkUxœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œuser_messageœ, œidœ: œPrompt-ODkUxœ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "str", - "Text" - ], "dataType": "Prompt", - "id": "Prompt-kykM2" + "id": "Prompt-ODkUx", + "name": "prompt", + "output_types": ["Prompt"] }, "targetHandle": { "fieldName": "input_value", - "id": "OpenAIModel-Neuec", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], + "id": "OpenAIModel-9RykF", + "inputTypes": ["Text", "Data", "Prompt"], "type": "str" } }, - "id": "reactflow__edge-Prompt-kykM2{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-kykM2œ}-OpenAIModel-Neuec{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Neuecœ,œinputTypesœ:[œTextœ,œRecordœ,œPromptœ],œtypeœ:œstrœ}", - "source": "Prompt-kykM2", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-kykM2œ}", - "target": "OpenAIModel-Neuec", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-Neuecœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-Prompt-ODkUx{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-ODkUxœ}-OpenAIModel-9RykF{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-9RykFœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", + "source": "Prompt-ODkUx", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-ODkUxœ, œoutput_typesœ: [œPromptœ], œnameœ: œpromptœ}", + "style": { + "stroke": "#555" + }, + "target": "OpenAIModel-9RykF", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-9RykFœ, œinputTypesœ: [œTextœ, œDataœ, œPromptœ], œtypeœ: œstrœ}" }, { - "className": "", + "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Text", - "object", - "Record", - "str" - ], - "dataType": "ChatInput", - "id": "ChatInput-Z9Rn6" + "dataType": "OpenAIModel", + "id": "OpenAIModel-9RykF", + "name": "text_output", + "output_types": ["Text"] }, "targetHandle": { - "fieldName": "UserMessage", - "id": "Prompt-kykM2", - "inputTypes": [ - "Document", - "Message", - "Record", - "Text" - ], + "fieldName": "input_value", + "id": "ChatOutput-P1jEe", + "inputTypes": ["Text", "Message"], "type": "str" } }, - "id": "reactflow__edge-ChatInput-Z9Rn6{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-Z9Rn6œ}-Prompt-kykM2{œfieldNameœ:œUserMessageœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "source": "ChatInput-Z9Rn6", - "sourceHandle": "{œbaseClassesœ: [œTextœ, œobjectœ, œRecordœ, œstrœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-Z9Rn6œ}", - "target": "Prompt-kykM2", - "targetHandle": "{œfieldNameœ: œUserMessageœ, œidœ: œPrompt-kykM2œ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-OpenAIModel-9RykF{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-9RykFœ}-ChatOutput-P1jEe{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-P1jEeœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", + "source": "OpenAIModel-9RykF", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-9RykFœ, œoutput_typesœ: [œTextœ], œnameœ: œtext_outputœ}", + "style": { + "stroke": "#555" + }, + "target": "ChatOutput-P1jEe", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-P1jEeœ, œinputTypesœ: [œTextœ, œMessageœ], œtypeœ: œstrœ}" }, { - "className": "", + "className": "stroke-foreground stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], "dataType": "MemoryComponent", - "id": "MemoryComponent-u6m5G" + "id": "MemoryComponent-cdA1J", + "name": "text", + "output_types": ["Text"] }, "targetHandle": { - "fieldName": "Context", - "id": "Prompt-kykM2", - "inputTypes": [ - "Document", - "Message", - "Record", - "Text" - ], + "fieldName": "input_value", + "id": "TextOutput-vrs6T", + "inputTypes": ["Record", "Text"], "type": "str" } }, - "id": "reactflow__edge-MemoryComponent-u6m5G{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-u6m5Gœ}-Prompt-kykM2{œfieldNameœ:œContextœ,œidœ:œPrompt-kykM2œ,œinputTypesœ:[œDocumentœ,œMessageœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", - "source": "MemoryComponent-u6m5G", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œMemoryComponentœ, œidœ: œMemoryComponent-u6m5Gœ}", - "target": "Prompt-kykM2", - "targetHandle": "{œfieldNameœ: œContextœ, œidœ: œPrompt-kykM2œ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-MemoryComponent-cdA1J{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}-TextOutput-vrs6T{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-vrs6Tœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", + "source": "MemoryComponent-cdA1J", + "sourceHandle": "{œdataTypeœ: œMemoryComponentœ, œidœ: œMemoryComponent-cdA1Jœ, œoutput_typesœ: [œTextœ], œnameœ: œtextœ}", + "style": { + "stroke": "#555" + }, + "target": "TextOutput-vrs6T", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œTextOutput-vrs6Tœ, œinputTypesœ: [œRecordœ, œTextœ], œtypeœ: œstrœ}" } ], "nodes": [ { "data": { - "id": "ChatInput-Z9Rn6", + "id": "ChatInput-t7F8v", "node": { - "base_classes": [ - "Text", - "object", - "Record", - "str" - ], + "base_classes": ["Text", "object", "Record", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -173,12 +150,20 @@ "field_order": [], "frozen": false, "icon": "ChatInput", - "output_types": [ - "Message", - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -195,7 +180,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import DropdownInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\n\n inputs = [\n StrInput(\n name=\"input_value\",\n display_name=\"Text\",\n multiline=True,\n input_types=[],\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"User\",\n info=\"Type of sender.\",\n advanced=True,\n ),\n StrInput(\n name=\"sender_name\",\n type=str,\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=\"User\",\n advanced=True,\n ),\n StrInput(\n name=\"session_id\", type=str, display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, (Message, str)) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -203,7 +188,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", + "info": "Message to be passed as input.", "input_types": [], "list": false, "load_from_db": false, @@ -215,7 +200,7 @@ "show": true, "title_case": false, "type": "str", - "value": "do you know his name?" + "value": "" }, "sender": { "advanced": true, @@ -223,18 +208,13 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Type of sender.", + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -244,15 +224,13 @@ "value": "User" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Name of the sender.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -266,15 +244,13 @@ "value": "User" }, "session_id": { - "advanced": false, + "advanced": true, "display_name": "Session ID", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "info": "Session ID for the message.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -285,15 +261,15 @@ "show": true, "title_case": false, "type": "str", - "value": "MySessionID" + "value": "" } } }, "type": "ChatInput" }, "dragging": false, - "height": 477, - "id": "ChatInput-Z9Rn6", + "height": 469, + "id": "ChatInput-t7F8v", "position": { "x": 1283.2700598313072, "y": 982.5953650473145 @@ -308,14 +284,9 @@ }, { "data": { - "id": "ChatOutput-cVR7W", + "id": "ChatOutput-P1jEe", "node": { - "base_classes": [ - "Text", - "object", - "Record", - "str" - ], + "base_classes": ["Text", "object", "Record", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -331,12 +302,20 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -353,7 +332,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput, DropdownInput, MultilineInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n input_types=[\"Text\", \"Message\"],\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"Machine\",\n advanced=True,\n info=\"Type of sender.\",\n ),\n StrInput(name=\"sender_name\", display_name=\"Sender Name\", info=\"Name of the sender.\", value=\"AI\", advanced=True),\n StrInput(name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True),\n BoolInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n if isinstance(self.input_value, Message):\n message = self.input_value\n else:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -361,10 +340,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Message to be passed as output.", + "input_types": ["Text", "Message"], "list": false, "load_from_db": false, "multiline": true, @@ -374,7 +351,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "sender": { "advanced": true, @@ -382,18 +360,13 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Type of sender.", + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -403,15 +376,13 @@ "value": "Machine" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Name of the sender.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -425,15 +396,13 @@ "value": "AI" }, "session_id": { - "advanced": false, + "advanced": true, "display_name": "Session ID", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "info": "Session ID for the message.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -444,15 +413,15 @@ "show": true, "title_case": false, "type": "str", - "value": "MySessionID" + "value": "" } } }, "type": "ChatOutput" }, "dragging": false, - "height": 485, - "id": "ChatOutput-cVR7W", + "height": 477, + "id": "ChatOutput-P1jEe", "position": { "x": 3154.916355514023, "y": 851.051882666333 @@ -469,13 +438,9 @@ "data": { "description": "Retrieves stored chat messages given a specific Session ID.", "display_name": "Chat Memory", - "id": "MemoryComponent-u6m5G", + "id": "MemoryComponent-cdA1J", "node": { - "base_classes": [ - "str", - "Text", - "object" - ], + "base_classes": ["str", "Text", "object"], "beta": true, "custom_fields": { "n_messages": null, @@ -492,8 +457,18 @@ "field_order": [], "frozen": false, "icon": "history", - "output_types": [ - "Text" + "output_types": ["Text"], + "outputs": [ + { + "cache": true, + "display_name": "Text", + "hidden": null, + "method": null, + "name": "text", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + } ], "template": { "_type": "CustomComponent", @@ -513,7 +488,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.memory.memory import BaseMemoryComponent\nfrom langflow.field_typing import Text\nfrom langflow.helpers.record import messages_to_text\nfrom langflow.memory import get_messages\nfrom langflow.schema.message import Message\n\n\nclass MemoryComponent(BaseMemoryComponent):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages given a specific Session ID.\"\n beta: bool = True\n icon = \"history\"\n\n def build_config(self):\n return {\n \"sender\": {\n \"options\": [\"Machine\", \"User\", \"Machine and User\"],\n \"display_name\": \"Sender Type\",\n },\n \"sender_name\": {\"display_name\": \"Sender Name\", \"advanced\": True},\n \"n_messages\": {\n \"display_name\": \"Number of Messages\",\n \"info\": \"Number of messages to retrieve.\",\n },\n \"session_id\": {\n \"display_name\": \"Session ID\",\n \"info\": \"Session ID of the chat history.\",\n \"input_types\": [\"Text\"],\n },\n \"order\": {\n \"options\": [\"Ascending\", \"Descending\"],\n \"display_name\": \"Order\",\n \"info\": \"Order of the messages.\",\n \"advanced\": True,\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def get_messages(self, **kwargs) -> list[Message]:\n # Validate kwargs by checking if it contains the correct keys\n if \"sender\" not in kwargs:\n kwargs[\"sender\"] = None\n if \"sender_name\" not in kwargs:\n kwargs[\"sender_name\"] = None\n if \"session_id\" not in kwargs:\n kwargs[\"session_id\"] = None\n if \"limit\" not in kwargs:\n kwargs[\"limit\"] = 5\n if \"order\" not in kwargs:\n kwargs[\"order\"] = \"Descending\"\n\n kwargs[\"order\"] = \"DESC\" if kwargs[\"order\"] == \"Descending\" else \"ASC\"\n if kwargs[\"sender\"] == \"Machine and User\":\n kwargs[\"sender\"] = None\n return get_messages(**kwargs)\n\n def build(\n self,\n sender: Optional[str] = \"Machine and User\",\n sender_name: Optional[str] = None,\n session_id: Optional[str] = None,\n n_messages: int = 5,\n order: Optional[str] = \"Descending\",\n record_template: Optional[str] = \"{sender_name}: {text}\",\n ) -> Text:\n messages = self.get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n messages_str = messages_to_text(template=record_template or \"\", messages=messages)\n self.status = messages_str\n return messages_str\n" + "value": "from typing import Optional\n\nfrom langflow.base.memory.memory import BaseMemoryComponent\nfrom langflow.field_typing import Text\nfrom langflow.helpers.data import messages_to_text\nfrom langflow.memory import get_messages\nfrom langflow.schema.message import Message\n\n\nclass MemoryComponent(BaseMemoryComponent):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages given a specific Session ID.\"\n beta: bool = True\n icon = \"history\"\n\n def build_config(self):\n return {\n \"sender\": {\n \"options\": [\"Machine\", \"User\", \"Machine and User\"],\n \"display_name\": \"Sender Type\",\n },\n \"sender_name\": {\"display_name\": \"Sender Name\", \"advanced\": True},\n \"n_messages\": {\n \"display_name\": \"Number of Messages\",\n \"info\": \"Number of messages to retrieve.\",\n },\n \"session_id\": {\n \"display_name\": \"Session ID\",\n \"info\": \"Session ID of the chat history.\",\n \"input_types\": [\"Text\"],\n },\n \"order\": {\n \"options\": [\"Ascending\", \"Descending\"],\n \"display_name\": \"Order\",\n \"info\": \"Order of the messages.\",\n \"advanced\": True,\n },\n \"data_template\": {\n \"display_name\": \"Data Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n \"advanced\": True,\n },\n }\n\n def get_messages(self, **kwargs) -> list[Message]:\n # Validate kwargs by checking if it contains the correct keys\n if \"sender\" not in kwargs:\n kwargs[\"sender\"] = None\n if \"sender_name\" not in kwargs:\n kwargs[\"sender_name\"] = None\n if \"session_id\" not in kwargs:\n kwargs[\"session_id\"] = None\n if \"limit\" not in kwargs:\n kwargs[\"limit\"] = 5\n if \"order\" not in kwargs:\n kwargs[\"order\"] = \"Descending\"\n\n kwargs[\"order\"] = \"DESC\" if kwargs[\"order\"] == \"Descending\" else \"ASC\"\n if kwargs[\"sender\"] == \"Machine and User\":\n kwargs[\"sender\"] = None\n return get_messages(**kwargs)\n\n def build(\n self,\n sender: Optional[str] = \"Machine and User\",\n sender_name: Optional[str] = None,\n session_id: Optional[str] = None,\n n_messages: int = 5,\n order: Optional[str] = \"Descending\",\n data_template: Optional[str] = \"{sender_name}: {text}\",\n ) -> Text:\n messages = self.get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n messages_str = messages_to_text(template=data_template or \"\", messages=messages)\n self.status = messages_str\n return messages_str\n" }, "n_messages": { "advanced": false, @@ -541,17 +516,12 @@ "fileTypes": [], "file_path": "", "info": "Order of the messages.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "order", - "options": [ - "Ascending", - "Descending" - ], + "options": ["Ascending", "Descending"], "password": false, "placeholder": "", "required": false, @@ -560,28 +530,6 @@ "type": "str", "value": "Descending" }, - "record_template": { - "advanced": true, - "display_name": "Record Template", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "record_template", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "{sender_name}: {text}" - }, "sender": { "advanced": false, "display_name": "Sender Type", @@ -589,18 +537,12 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" - ], + "options": ["Machine", "User", "Machine and User"], "password": false, "placeholder": "", "required": false, @@ -616,9 +558,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -637,9 +577,7 @@ "fileTypes": [], "file_path": "", "info": "Session ID of the chat history.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -657,8 +595,8 @@ "type": "MemoryComponent" }, "dragging": false, - "height": 505, - "id": "MemoryComponent-u6m5G", + "height": 489, + "id": "MemoryComponent-cdA1J", "position": { "x": 1289.9606870058817, "y": 442.16804561053766 @@ -673,21 +611,14 @@ }, { "data": { - "description": "Create a prompt template with dynamic variables.", + "description": "A component for creating prompt templates using dynamic variables.", "display_name": "Prompt", - "id": "Prompt-kykM2", + "id": "Prompt-ODkUx", "node": { - "base_classes": [ - "object", - "str", - "Text" - ], + "base_classes": ["Text", "str", "object"], "beta": false, "custom_fields": { - "template": [ - "Context", - "UserMessage" - ] + "template": ["context", "user_message"] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", @@ -702,72 +633,35 @@ "is_input": null, "is_output": null, "name": "", - "output_types": [ - "Prompt" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Prompt", + "method": "build_prompt", + "name": "prompt", + "selected": "Prompt", + "types": ["Prompt"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Text", + "method": "format_prompt", + "name": "text", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + } ], "template": { - "Context": { - "advanced": false, - "display_name": "Context", - "dynamic": false, - "field_type": "str", - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Document", - "Message", - "Record", - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "Context", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, - "UserMessage": { - "advanced": false, - "display_name": "UserMessage", - "dynamic": false, - "field_type": "str", - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Document", - "Message", - "Record", - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "UserMessage", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], "list": false, "load_from_db": false, "multiline": true, @@ -778,22 +672,37 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" + "value": "from langflow.custom import Component\nfrom langflow.field_typing.prompt import Prompt\nfrom langflow.inputs import PromptInput\nfrom langflow.template import Output\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n Output(display_name=\"Text\", name=\"text\", method=\"format_prompt\"),\n ]\n\n async def format_prompt(self) -> str:\n prompt = await self.build_prompt()\n formatted_text = prompt.format_text()\n self.status = formatted_text\n return formatted_text\n\n async def build_prompt(\n self,\n ) -> Prompt:\n kwargs = {k: v for k, v in self._arguments.items() if k != \"template\"}\n prompt = await Prompt.from_template_and_variables(self.template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" }, - "template": { + "context": { "advanced": false, - "display_name": "Template", + "display_name": "context", "dynamic": false, "field_type": "str", "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "input_types": ["Document", "Message", "Record", "Text"], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "context", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "template": { + "advanced": false, + "display_name": "Template", + "dynamic": false, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -804,36 +713,53 @@ "show": true, "title_case": false, "type": "prompt", - "value": "Previous messages:\n{Context}\n\nUser: {UserMessage}\nAI: " + "value": "{context}\n\nUser: {user_message}\nAI: " + }, + "user_message": { + "advanced": false, + "display_name": "user_message", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": ["Document", "Message", "Record", "Text"], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "user_message", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" } } }, "type": "Prompt" }, "dragging": false, - "height": 513, - "id": "Prompt-kykM2", + "height": 477, + "id": "Prompt-ODkUx", "position": { - "x": 1890.2582485007167, + "x": 1894.594426342426, "y": 753.3797365481901 }, "positionAbsolute": { - "x": 1890.2582485007167, + "x": 1894.594426342426, "y": 753.3797365481901 }, - "selected": true, + "selected": false, "type": "genericNode", "width": 384 }, { "data": { - "id": "OpenAIModel-Neuec", + "id": "OpenAIModel-9RykF", "node": { - "base_classes": [ - "str", - "object", - "Text" - ], + "base_classes": ["str", "object", "Text"], "beta": false, "custom_fields": { "input_value": null, @@ -863,11 +789,29 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": [ - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Text", + "method": "text_response", + "name": "text_output", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Language Model", + "method": "build_model", + "name": "model_output", + "selected": "BaseLanguageModel", + "types": ["BaseLanguageModel"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -884,7 +828,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" + "value": "from langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import BaseLanguageModel, Text\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, FloatInput, SecretStrInput, StrInput\nfrom langflow.inputs.inputs import IntInput\nfrom langflow.template import Output\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n StrInput(name=\"input_value\", display_name=\"Input\", input_types=[\"Text\", \"Data\", \"Prompt\"]),\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n 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.\",\n ),\n SecretStrInput(\n name=\"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 value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n BoolInput(name=\"stream\", display_name=\"Stream\", info=STREAM_INFO_TEXT, advanced=True),\n StrInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"System message to pass to the model.\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text_output\", method=\"text_response\"),\n Output(display_name=\"Language Model\", name=\"model_output\", method=\"build_model\"),\n ]\n\n def text_response(self) -> Text:\n input_value = self.input_value\n stream = self.stream\n system_message = self.system_message\n output = self.build_model()\n result = self.get_chat_result(output, stream, input_value, system_message)\n self.status = result\n return result\n\n def build_model(self) -> BaseLanguageModel:\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs or {},\n model=model_name or None,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n )\n return output\n" }, "input_value": { "advanced": false, @@ -893,21 +837,18 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], + "input_types": ["Text", "Data", "Prompt"], "list": false, "load_from_db": false, "multiline": false, "name": "input_value", "password": false, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "max_tokens": { "advanced": true, @@ -916,6 +857,7 @@ "fileTypes": [], "file_path": "", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -925,8 +867,8 @@ "required": false, "show": true, "title_case": false, - "type": "int", - "value": 256 + "type": "str", + "value": "" }, "model_kwargs": { "advanced": true, @@ -935,6 +877,7 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -944,8 +887,8 @@ "required": false, "show": true, "title_case": false, - "type": "NestedDict", - "value": {} + "type": "str", + "value": "" }, "model_name": { "advanced": false, @@ -954,9 +897,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -983,9 +924,7 @@ "fileTypes": [], "file_path": "", "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.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -995,7 +934,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "openai_api_key": { "advanced": false, @@ -1004,16 +944,14 @@ "fileTypes": [], "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, "name": "openai_api_key", "password": true, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "type": "str", @@ -1026,6 +964,7 @@ "fileTypes": [], "file_path": "", "info": "Stream the response from the model. Streaming works only in Chat.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1035,7 +974,7 @@ "required": false, "show": true, "title_case": false, - "type": "bool", + "type": "str", "value": false }, "system_message": { @@ -1045,9 +984,7 @@ "fileTypes": [], "file_path": "", "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1057,7 +994,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "temperature": { "advanced": false, @@ -1066,31 +1004,26 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, "name": "temperature", "password": false, "placeholder": "", - "rangeSpec": { - "max": 1, - "min": -1, - "step": 0.1, - "step_type": "float" - }, "required": false, "show": true, "title_case": false, - "type": "float", - "value": "0.2" + "type": "str", + "value": 0.1 } } }, "type": "OpenAIModel" }, "dragging": false, - "height": 571, - "id": "OpenAIModel-Neuec", + "height": 563, + "id": "OpenAIModel-9RykF", "position": { "x": 2561.5850334731617, "y": 553.2745131130916 @@ -1107,11 +1040,7 @@ "data": { "id": "TextOutput-vrs6T", "node": { - "base_classes": [ - "str", - "object", - "Text" - ], + "base_classes": ["str", "object", "Text"], "beta": false, "custom_fields": { "input_value": null, @@ -1124,9 +1053,7 @@ "field_order": [], "frozen": false, "icon": "type", - "output_types": [ - "Text" - ], + "output_types": ["Text"], "template": { "_type": "CustomComponent", "code": { @@ -1154,10 +1081,7 @@ "fileTypes": [], "file_path": "", "info": "Text or Record to be passed as output.", - "input_types": [ - "Record", - "Text" - ], + "input_types": ["Record", "Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1177,9 +1101,7 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, @@ -1213,14 +1135,16 @@ } ], "viewport": { - "x": -511.79726701119625, - "y": 49.514712353620894, - "zoom": 0.4612356948928673 + "x": -569.862554459756, + "y": -42.08339711050985, + "zoom": 0.4868590524514978 } }, "description": "This project can be used as a starting point for building a Chat experience with user specific memory. You can set a different Session ID to start a new message history.", - "id": "321b1bab-8691-42da-9689-1f12b5d2a48b", + "icon": "🤖", + "icon_bg_color": "#FFD700", + "id": "08d5cccf-d098-4367-b14b-1078429c9ed9", "is_component": false, - "last_tested_version": "1.0.0a54", + "last_tested_version": "1.0.0a0", "name": "Memory Chatbot" } diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json index 4e0327636..81c6c3c3a 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json @@ -5,60 +5,46 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], "dataType": "TextInput", - "id": "TextInput-sptaH" + "id": "TextInput-sptaH", + "name": "text", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "document", "id": "Prompt-amqBu", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "inputTypes": ["Document", "Message", "Record", "Text"], "type": "str" } }, "id": "reactflow__edge-TextInput-sptaH{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œTextInputœ,œidœ:œTextInput-sptaHœ}-Prompt-amqBu{œfieldNameœ:œdocumentœ,œidœ:œPrompt-amqBuœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "source": "TextInput-sptaH", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œTextInputœ, œidœ: œTextInput-sptaHœ}", + "sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-sptaHœ, œoutput_typesœ: [œTextœ], œnameœ: œtextœ}", "style": { "stroke": "#555" }, "target": "Prompt-amqBu", - "targetHandle": "{œfieldNameœ: œdocumentœ, œidœ: œPrompt-amqBuœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œdocumentœ, œidœ: œPrompt-amqBuœ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "str", - "Text" - ], "dataType": "Prompt", - "id": "Prompt-amqBu" + "id": "Prompt-amqBu", + "name": "text", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "input_value", "id": "TextOutput-2MS4a", - "inputTypes": [ - "Record", - "Text" - ], + "inputTypes": ["Record", "Text"], "type": "str" } }, "id": "reactflow__edge-Prompt-amqBu{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-amqBuœ}-TextOutput-2MS4a{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-2MS4aœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", "source": "Prompt-amqBu", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-amqBuœ}", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-amqBuœ, œoutput_typesœ: [œTextœ], œnameœ: œtextœ}", "style": { "stroke": "#555" }, @@ -69,122 +55,96 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "str", - "Text" - ], "dataType": "Prompt", - "id": "Prompt-amqBu" + "id": "Prompt-amqBu", + "name": "prompt", + "output_types": ["Prompt"] }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-uYXZJ", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], + "inputTypes": ["Text", "Data", "Prompt"], "type": "str" } }, "id": "reactflow__edge-Prompt-amqBu{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-amqBuœ}-OpenAIModel-uYXZJ{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-uYXZJœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "source": "Prompt-amqBu", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-amqBuœ}", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-amqBuœ, œoutput_typesœ: [œPromptœ], œnameœ: œpromptœ}", "style": { "stroke": "#555" }, "target": "OpenAIModel-uYXZJ", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-uYXZJœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-uYXZJœ, œinputTypesœ: [œTextœ, œDataœ, œPromptœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], "dataType": "OpenAIModel", - "id": "OpenAIModel-uYXZJ" + "id": "OpenAIModel-uYXZJ", + "name": "text_output", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "summary", "id": "Prompt-gTNiz", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "inputTypes": ["Document", "Message", "Record", "Text"], "type": "str" } }, "id": "reactflow__edge-OpenAIModel-uYXZJ{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uYXZJœ}-Prompt-gTNiz{œfieldNameœ:œsummaryœ,œidœ:œPrompt-gTNizœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "source": "OpenAIModel-uYXZJ", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-uYXZJœ}", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-uYXZJœ, œoutput_typesœ: [œTextœ], œnameœ: œtext_outputœ}", "style": { "stroke": "#555" }, "target": "Prompt-gTNiz", - "targetHandle": "{œfieldNameœ: œsummaryœ, œidœ: œPrompt-gTNizœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œsummaryœ, œidœ: œPrompt-gTNizœ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], "dataType": "OpenAIModel", - "id": "OpenAIModel-uYXZJ" + "id": "OpenAIModel-uYXZJ", + "name": "text_output", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-EJkG3", - "inputTypes": [ - "Text" - ], + "inputTypes": ["Text", "Message"], "type": "str" } }, "id": "reactflow__edge-OpenAIModel-uYXZJ{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uYXZJœ}-ChatOutput-EJkG3{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-EJkG3œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "source": "OpenAIModel-uYXZJ", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-uYXZJœ}", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-uYXZJœ, œoutput_typesœ: [œTextœ], œnameœ: œtext_outputœ}", "style": { "stroke": "#555" }, "target": "ChatOutput-EJkG3", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-EJkG3œ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-EJkG3œ, œinputTypesœ: [œTextœ, œMessageœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "str", - "Text" - ], "dataType": "Prompt", - "id": "Prompt-gTNiz" + "id": "Prompt-gTNiz", + "name": "text", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "input_value", "id": "TextOutput-MUDOR", - "inputTypes": [ - "Record", - "Text" - ], + "inputTypes": ["Record", "Text"], "type": "str" } }, "id": "reactflow__edge-Prompt-gTNiz{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-gTNizœ}-TextOutput-MUDOR{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-MUDORœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", "source": "Prompt-gTNiz", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-gTNizœ}", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-gTNizœ, œoutput_typesœ: [œTextœ], œnameœ: œtextœ}", "style": { "stroke": "#555" }, @@ -195,63 +155,51 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "str", - "Text" - ], "dataType": "Prompt", - "id": "Prompt-gTNiz" + "id": "Prompt-gTNiz", + "name": "prompt", + "output_types": ["Prompt"] }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-XawYB", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], + "inputTypes": ["Text", "Data", "Prompt"], "type": "str" } }, "id": "reactflow__edge-Prompt-gTNiz{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-gTNizœ}-OpenAIModel-XawYB{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-XawYBœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "source": "Prompt-gTNiz", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œstrœ, œTextœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-gTNizœ}", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-gTNizœ, œoutput_typesœ: [œPromptœ], œnameœ: œpromptœ}", "style": { "stroke": "#555" }, "target": "OpenAIModel-XawYB", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-XawYBœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-XawYBœ, œinputTypesœ: [œTextœ, œDataœ, œPromptœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "str", - "Text", - "object" - ], "dataType": "OpenAIModel", - "id": "OpenAIModel-XawYB" + "id": "OpenAIModel-XawYB", + "name": "text_output", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-DNmvg", - "inputTypes": [ - "Text" - ], + "inputTypes": ["Text", "Message"], "type": "str" } }, "id": "reactflow__edge-OpenAIModel-XawYB{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-XawYBœ}-ChatOutput-DNmvg{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-DNmvgœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "source": "OpenAIModel-XawYB", - "sourceHandle": "{œbaseClassesœ: [œstrœ, œTextœ, œobjectœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-XawYBœ}", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-XawYBœ, œoutput_typesœ: [œTextœ], œnameœ: œtext_outputœ}", "style": { "stroke": "#555" }, "target": "ChatOutput-DNmvg", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-DNmvgœ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-DNmvgœ, œinputTypesœ: [œTextœ, œMessageœ], œtypeœ: œstrœ}" } ], "nodes": [ @@ -261,16 +209,10 @@ "display_name": "Prompt", "id": "Prompt-amqBu", "node": { - "base_classes": [ - "object", - "str", - "Text" - ], + "base_classes": ["object", "str", "Text"], "beta": false, "custom_fields": { - "template": [ - "document" - ] + "template": ["document"] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", @@ -285,11 +227,29 @@ "is_input": null, "is_output": null, "name": "", - "output_types": [ - "Prompt" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Prompt", + "method": "build_prompt", + "name": "prompt", + "selected": "Prompt", + "types": ["Prompt"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Text", + "method": "format_prompt", + "name": "text", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -306,7 +266,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" + "value": "from langflow.custom import Component\nfrom langflow.field_typing.prompt import Prompt\nfrom langflow.inputs import PromptInput\nfrom langflow.template import Output\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n Output(display_name=\"Text\", name=\"text\", method=\"format_prompt\"),\n ]\n\n async def format_prompt(self) -> str:\n prompt = await self.build_prompt()\n formatted_text = prompt.format_text()\n self.status = formatted_text\n return formatted_text\n\n async def build_prompt(\n self,\n ) -> Prompt:\n kwargs = {k: v for k, v in self._arguments.items() if k != \"template\"}\n prompt = await Prompt.from_template_and_variables(self.template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" }, "document": { "advanced": false, @@ -316,12 +276,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "input_types": ["Document", "Message", "Record", "Text"], "list": false, "load_from_db": false, "multiline": true, @@ -341,9 +296,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -381,16 +334,10 @@ "display_name": "Prompt", "id": "Prompt-gTNiz", "node": { - "base_classes": [ - "object", - "str", - "Text" - ], + "base_classes": ["object", "str", "Text"], "beta": false, "custom_fields": { - "template": [ - "summary" - ] + "template": ["summary"] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", @@ -405,11 +352,29 @@ "is_input": null, "is_output": null, "name": "", - "output_types": [ - "Prompt" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Prompt", + "method": "build_prompt", + "name": "prompt", + "selected": "Prompt", + "types": ["Prompt"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Text", + "method": "format_prompt", + "name": "text", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -426,7 +391,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" + "value": "from langflow.custom import Component\nfrom langflow.field_typing.prompt import Prompt\nfrom langflow.inputs import PromptInput\nfrom langflow.template import Output\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n Output(display_name=\"Text\", name=\"text\", method=\"format_prompt\"),\n ]\n\n async def format_prompt(self) -> str:\n prompt = await self.build_prompt()\n formatted_text = prompt.format_text()\n self.status = formatted_text\n return formatted_text\n\n async def build_prompt(\n self,\n ) -> Prompt:\n kwargs = {k: v for k, v in self._arguments.items() if k != \"template\"}\n prompt = await Prompt.from_template_and_variables(self.template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" }, "summary": { "advanced": false, @@ -436,12 +401,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "input_types": ["Document", "Message", "Record", "Text"], "list": false, "load_from_db": false, "multiline": true, @@ -461,9 +421,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -495,12 +453,7 @@ "data": { "id": "ChatOutput-EJkG3", "node": { - "base_classes": [ - "object", - "Record", - "Text", - "str" - ], + "base_classes": ["object", "Record", "Text", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -517,12 +470,20 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -539,7 +500,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput, DropdownInput, MultilineInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n input_types=[\"Text\", \"Message\"],\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"Machine\",\n advanced=True,\n info=\"Type of sender.\",\n ),\n StrInput(name=\"sender_name\", display_name=\"Sender Name\", info=\"Name of the sender.\", value=\"AI\", advanced=True),\n StrInput(name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True),\n BoolInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n if isinstance(self.input_value, Message):\n message = self.input_value\n else:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -547,10 +508,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Message to be passed as output.", + "input_types": ["Text", "Message"], "list": false, "load_from_db": false, "multiline": true, @@ -560,7 +519,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "sender": { "advanced": true, @@ -568,18 +528,13 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Type of sender.", + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -589,15 +544,13 @@ "value": "Machine" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Name of the sender.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -608,7 +561,7 @@ "show": true, "title_case": false, "type": "str", - "value": "Summarizer" + "value": "AI" }, "session_id": { "advanced": true, @@ -616,10 +569,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "info": "Session ID for the message.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -629,7 +580,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" } } }, @@ -650,12 +602,7 @@ "data": { "id": "ChatOutput-DNmvg", "node": { - "base_classes": [ - "object", - "Record", - "Text", - "str" - ], + "base_classes": ["object", "Record", "Text", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -672,12 +619,20 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -694,7 +649,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput, DropdownInput, MultilineInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n input_types=[\"Text\", \"Message\"],\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"Machine\",\n advanced=True,\n info=\"Type of sender.\",\n ),\n StrInput(name=\"sender_name\", display_name=\"Sender Name\", info=\"Name of the sender.\", value=\"AI\", advanced=True),\n StrInput(name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True),\n BoolInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n if isinstance(self.input_value, Message):\n message = self.input_value\n else:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -702,10 +657,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Message to be passed as output.", + "input_types": ["Text", "Message"], "list": false, "load_from_db": false, "multiline": true, @@ -715,7 +668,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "sender": { "advanced": true, @@ -723,18 +677,13 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Type of sender.", + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -744,15 +693,13 @@ "value": "Machine" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Name of the sender.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -763,7 +710,7 @@ "show": true, "title_case": false, "type": "str", - "value": "Question Generator" + "value": "AI" }, "session_id": { "advanced": true, @@ -771,10 +718,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "info": "Session ID for the message.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -784,7 +729,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" } } }, @@ -804,11 +750,7 @@ "data": { "id": "TextInput-sptaH", "node": { - "base_classes": [ - "str", - "Text", - "object" - ], + "base_classes": ["str", "Text", "object"], "beta": false, "custom_fields": { "input_value": null, @@ -821,11 +763,20 @@ "field_order": [], "frozen": false, "icon": "type", - "output_types": [ - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Text", + "method": "text_response", + "name": "text", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -842,7 +793,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextInput(TextComponent):\n display_name = \"Text Input\"\n description = \"Get text inputs from the Playground.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Text\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as input.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Optional[Text] = \"\",\n record_template: Optional[str] = \"\",\n ) -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n" + "value": "from langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\nfrom langflow.inputs import MultilineInput, StrInput\nfrom langflow.template import Output\n\n\nclass TextInput(TextComponent):\n display_name = \"Text Input\"\n description = \"Get text inputs from the Playground.\"\n icon = \"type\"\n\n inputs = [\n StrInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Text to be passed as input.\",\n ),\n MultilineInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n advanced=True,\n value=\"{text}\",\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text\", method=\"text_response\"),\n ]\n\n def text_response(self) -> Text:\n return self.build(input_value=self.input_value, data_template=self.data_template)\n" }, "input_value": { "advanced": false, @@ -850,11 +801,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "Text or Record to be passed as input.", - "input_types": [ - "Record", - "Text" - ], + "info": "Text to be passed as input.", + "input_types": ["Data", "Text"], "list": false, "load_from_db": false, "multiline": false, @@ -865,28 +813,6 @@ "show": true, "title_case": false, "type": "str", - "value": "Revolutionary Nano-Battery Technology Unveiled In a groundbreaking announcement yesterday, researchers from the fictional Tech Innovations Institute revealed the development of a new nano-battery technology that promises to revolutionize energy storage. The new battery, dubbed the \"EnerGCell\", uses advanced nanomaterials to achieve unprecedented efficiency and storage capacities. According to lead researcher Dr. Ada Byron, the EnerGCell can store up to ten times more energy than the best lithium-ion batteries available today, while charging in just a fraction of the time. \"We're talking about charging your electric vehicle in just five minutes for a range of over 1,000 miles,\" Dr. Byron stated during the press conference. The technology behind the EnerGCell involves a complex arrangement of nanostructured electrodes that allow for rapid ion transfer and extremely high energy density. This breakthrough was achieved after a decade of research into nanomaterials and their applications in energy storage. The implications of this technology are vast, promising to accelerate the adoption of renewable energy by making it more practical and affordable to store wind and solar power. It could also lead to significant advancements in electric vehicles, mobile devices, and any other technology that relies on batteries. Despite the excitement, some experts are calling for patience, noting that the EnerGCell is still in its early stages of development and may take several years before it's commercially available. However, the potential impact of such a technology on the environment and the global economy is undeniable. Tech Innovations Institute plans to continue refining the EnerGCell and begin pilot projects with select partners in the coming year. If successful, this nano-battery technology could indeed be the breakthrough needed to usher in a new era of clean energy and technology." - }, - "record_template": { - "advanced": true, - "display_name": "Record Template", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "record_template", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", "value": "" } } @@ -912,11 +838,7 @@ "data": { "id": "TextOutput-2MS4a", "node": { - "base_classes": [ - "str", - "Text", - "object" - ], + "base_classes": ["str", "Text", "object"], "beta": false, "custom_fields": { "input_value": null, @@ -929,9 +851,7 @@ "field_order": [], "frozen": false, "icon": "type", - "output_types": [ - "Text" - ], + "output_types": ["Text"], "template": { "_type": "CustomComponent", "code": { @@ -959,10 +879,7 @@ "fileTypes": [], "file_path": "", "info": "Text or Record to be passed as output.", - "input_types": [ - "Record", - "Text" - ], + "input_types": ["Record", "Text"], "list": false, "load_from_db": false, "multiline": false, @@ -982,9 +899,7 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, @@ -1020,11 +935,7 @@ "data": { "id": "OpenAIModel-uYXZJ", "node": { - "base_classes": [ - "str", - "Text", - "object" - ], + "base_classes": ["str", "Text", "object"], "beta": false, "custom_fields": { "input_value": null, @@ -1054,11 +965,29 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": [ - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Text", + "method": "text_response", + "name": "text_output", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Language Model", + "method": "build_model", + "name": "model_output", + "selected": "BaseLanguageModel", + "types": ["BaseLanguageModel"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -1075,7 +1004,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" + "value": "from langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import BaseLanguageModel, Text\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, FloatInput, SecretStrInput, StrInput\nfrom langflow.inputs.inputs import IntInput\nfrom langflow.template import Output\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n StrInput(name=\"input_value\", display_name=\"Input\", input_types=[\"Text\", \"Data\", \"Prompt\"]),\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n 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.\",\n ),\n SecretStrInput(\n name=\"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 value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n BoolInput(name=\"stream\", display_name=\"Stream\", info=STREAM_INFO_TEXT, advanced=True),\n StrInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"System message to pass to the model.\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text_output\", method=\"text_response\"),\n Output(display_name=\"Language Model\", name=\"model_output\", method=\"build_model\"),\n ]\n\n def text_response(self) -> Text:\n input_value = self.input_value\n stream = self.stream\n system_message = self.system_message\n output = self.build_model()\n result = self.get_chat_result(output, stream, input_value, system_message)\n self.status = result\n return result\n\n def build_model(self) -> BaseLanguageModel:\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs or {},\n model=model_name or None,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n )\n return output\n" }, "input_value": { "advanced": false, @@ -1084,21 +1013,18 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], + "input_types": ["Text", "Data", "Prompt"], "list": false, "load_from_db": false, "multiline": false, "name": "input_value", "password": false, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "max_tokens": { "advanced": true, @@ -1107,6 +1033,7 @@ "fileTypes": [], "file_path": "", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1116,8 +1043,8 @@ "required": false, "show": true, "title_case": false, - "type": "int", - "value": 256 + "type": "str", + "value": "" }, "model_kwargs": { "advanced": true, @@ -1126,6 +1053,7 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1135,8 +1063,8 @@ "required": false, "show": true, "title_case": false, - "type": "NestedDict", - "value": {} + "type": "str", + "value": "" }, "model_name": { "advanced": false, @@ -1145,9 +1073,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -1174,9 +1100,7 @@ "fileTypes": [], "file_path": "", "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.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1186,7 +1110,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "openai_api_key": { "advanced": false, @@ -1195,16 +1120,14 @@ "fileTypes": [], "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, "name": "openai_api_key", "password": true, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "type": "str", @@ -1217,6 +1140,7 @@ "fileTypes": [], "file_path": "", "info": "Stream the response from the model. Streaming works only in Chat.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1226,7 +1150,7 @@ "required": false, "show": true, "title_case": false, - "type": "bool", + "type": "str", "value": false }, "system_message": { @@ -1236,9 +1160,7 @@ "fileTypes": [], "file_path": "", "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1248,7 +1170,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "temperature": { "advanced": false, @@ -1257,22 +1180,17 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, "name": "temperature", "password": false, "placeholder": "", - "rangeSpec": { - "max": 1, - "min": -1, - "step": 0.1, - "step_type": "float" - }, "required": false, "show": true, "title_case": false, - "type": "float", + "type": "str", "value": 0.1 } } @@ -1298,11 +1216,7 @@ "data": { "id": "TextOutput-MUDOR", "node": { - "base_classes": [ - "str", - "Text", - "object" - ], + "base_classes": ["str", "Text", "object"], "beta": false, "custom_fields": { "input_value": null, @@ -1315,9 +1229,7 @@ "field_order": [], "frozen": false, "icon": "type", - "output_types": [ - "Text" - ], + "output_types": ["Text"], "template": { "_type": "CustomComponent", "code": { @@ -1345,10 +1257,7 @@ "fileTypes": [], "file_path": "", "info": "Text or Record to be passed as output.", - "input_types": [ - "Record", - "Text" - ], + "input_types": ["Record", "Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1368,9 +1277,7 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, @@ -1406,11 +1313,7 @@ "data": { "id": "OpenAIModel-XawYB", "node": { - "base_classes": [ - "str", - "Text", - "object" - ], + "base_classes": ["str", "Text", "object"], "beta": false, "custom_fields": { "input_value": null, @@ -1440,11 +1343,29 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": [ - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Text", + "method": "text_response", + "name": "text_output", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Language Model", + "method": "build_model", + "name": "model_output", + "selected": "BaseLanguageModel", + "types": ["BaseLanguageModel"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -1461,7 +1382,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" + "value": "from langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import BaseLanguageModel, Text\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, FloatInput, SecretStrInput, StrInput\nfrom langflow.inputs.inputs import IntInput\nfrom langflow.template import Output\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n StrInput(name=\"input_value\", display_name=\"Input\", input_types=[\"Text\", \"Data\", \"Prompt\"]),\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n 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.\",\n ),\n SecretStrInput(\n name=\"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 value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n BoolInput(name=\"stream\", display_name=\"Stream\", info=STREAM_INFO_TEXT, advanced=True),\n StrInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"System message to pass to the model.\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text_output\", method=\"text_response\"),\n Output(display_name=\"Language Model\", name=\"model_output\", method=\"build_model\"),\n ]\n\n def text_response(self) -> Text:\n input_value = self.input_value\n stream = self.stream\n system_message = self.system_message\n output = self.build_model()\n result = self.get_chat_result(output, stream, input_value, system_message)\n self.status = result\n return result\n\n def build_model(self) -> BaseLanguageModel:\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs or {},\n model=model_name or None,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n )\n return output\n" }, "input_value": { "advanced": false, @@ -1470,21 +1391,18 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], + "input_types": ["Text", "Data", "Prompt"], "list": false, "load_from_db": false, "multiline": false, "name": "input_value", "password": false, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "max_tokens": { "advanced": true, @@ -1493,6 +1411,7 @@ "fileTypes": [], "file_path": "", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1502,8 +1421,8 @@ "required": false, "show": true, "title_case": false, - "type": "int", - "value": 256 + "type": "str", + "value": "" }, "model_kwargs": { "advanced": true, @@ -1512,6 +1431,7 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1521,8 +1441,8 @@ "required": false, "show": true, "title_case": false, - "type": "NestedDict", - "value": {} + "type": "str", + "value": "" }, "model_name": { "advanced": false, @@ -1531,9 +1451,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -1551,7 +1469,7 @@ "show": true, "title_case": false, "type": "str", - "value": "gpt-4-turbo-preview" + "value": "gpt-4o" }, "openai_api_base": { "advanced": true, @@ -1560,9 +1478,7 @@ "fileTypes": [], "file_path": "", "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.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1572,7 +1488,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "openai_api_key": { "advanced": false, @@ -1581,20 +1498,18 @@ "fileTypes": [], "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, - "load_from_db": false, + "load_from_db": true, "multiline": false, "name": "openai_api_key", "password": true, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "type": "str", - "value": "" + "value": "OPENAI_API_KEY" }, "stream": { "advanced": true, @@ -1603,6 +1518,7 @@ "fileTypes": [], "file_path": "", "info": "Stream the response from the model. Streaming works only in Chat.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1612,7 +1528,7 @@ "required": false, "show": true, "title_case": false, - "type": "bool", + "type": "str", "value": false }, "system_message": { @@ -1622,9 +1538,7 @@ "fileTypes": [], "file_path": "", "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1634,7 +1548,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "temperature": { "advanced": false, @@ -1643,22 +1558,17 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, "name": "temperature", "password": false, "placeholder": "", - "rangeSpec": { - "max": 1, - "min": -1, - "step": 0.1, - "step_type": "float" - }, "required": false, "show": true, "title_case": false, - "type": "float", + "type": "str", "value": 0.1 } } diff --git a/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json b/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json index 97b21a559..088a13d24 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json @@ -5,174 +5,140 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "Text", - "str" - ], "dataType": "TextOutput", - "id": "TextOutput-BDknO" + "id": "TextOutput-BDknO", + "name": "Text", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "context", "id": "Prompt-xeI6K", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "inputTypes": ["Document", "Message", "Record", "Text"], "type": "str" } }, "id": "reactflow__edge-TextOutput-BDknO{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œTextOutputœ,œidœ:œTextOutput-BDknOœ}-Prompt-xeI6K{œfieldNameœ:œcontextœ,œidœ:œPrompt-xeI6Kœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "selected": false, "source": "TextOutput-BDknO", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œTextœ, œstrœ], œdataTypeœ: œTextOutputœ, œidœ: œTextOutput-BDknOœ}", + "sourceHandle": "{œdataTypeœ: œTextOutputœ, œidœ: œTextOutput-BDknOœ, œoutput_typesœ: [œTextœ], œnameœ: œTextœ}", "style": { "stroke": "#555" }, "target": "Prompt-xeI6K", - "targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-xeI6Kœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-xeI6Kœ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Text", - "str", - "object", - "Record" - ], "dataType": "ChatInput", - "id": "ChatInput-yxMKE" + "id": "ChatInput-yxMKE", + "name": "message", + "output_types": ["Message"] }, "targetHandle": { "fieldName": "question", "id": "Prompt-xeI6K", - "inputTypes": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "inputTypes": ["Document", "Message", "Record", "Text"], "type": "str" } }, "id": "reactflow__edge-ChatInput-yxMKE{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ,œRecordœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-yxMKEœ}-Prompt-xeI6K{œfieldNameœ:œquestionœ,œidœ:œPrompt-xeI6Kœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "selected": false, "source": "ChatInput-yxMKE", - "sourceHandle": "{œbaseClassesœ: [œTextœ, œstrœ, œobjectœ, œRecordœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-yxMKEœ}", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-yxMKEœ, œoutput_typesœ: [œMessageœ], œnameœ: œmessageœ}", "style": { "stroke": "#555" }, "target": "Prompt-xeI6K", - "targetHandle": "{œfieldNameœ: œquestionœ, œidœ: œPrompt-xeI6Kœ, œinputTypesœ: [œDocumentœ, œBaseOutputParserœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œquestionœ, œidœ: œPrompt-xeI6Kœ, œinputTypesœ: [œDocumentœ, œMessageœ, œRecordœ, œTextœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "Text", - "str" - ], "dataType": "Prompt", - "id": "Prompt-xeI6K" + "id": "Prompt-xeI6K", + "name": "prompt", + "output_types": ["Prompt"] }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-EjXlN", - "inputTypes": [ - "Text", - "Record", - "Prompt" - ], + "inputTypes": ["Text", "Data", "Prompt"], "type": "str" } }, "id": "reactflow__edge-Prompt-xeI6K{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-xeI6Kœ}-OpenAIModel-EjXlN{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-EjXlNœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "selected": false, "source": "Prompt-xeI6K", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œTextœ, œstrœ], œdataTypeœ: œPromptœ, œidœ: œPrompt-xeI6Kœ}", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-xeI6Kœ, œoutput_typesœ: [œPromptœ], œnameœ: œpromptœ}", "style": { "stroke": "#555" }, "target": "OpenAIModel-EjXlN", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-EjXlNœ, œinputTypesœ: [œTextœ, œRecordœ, œPromptœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-EjXlNœ, œinputTypesœ: [œTextœ, œDataœ, œPromptœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "object", - "Text", - "str" - ], "dataType": "OpenAIModel", - "id": "OpenAIModel-EjXlN" + "id": "OpenAIModel-EjXlN", + "name": "text_output", + "output_types": ["Text"] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-Q39I8", - "inputTypes": [ - "Text" - ], + "inputTypes": ["Text", "Message"], "type": "str" } }, "id": "reactflow__edge-OpenAIModel-EjXlN{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-EjXlNœ}-ChatOutput-Q39I8{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Q39I8œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "selected": false, "source": "OpenAIModel-EjXlN", - "sourceHandle": "{œbaseClassesœ: [œobjectœ, œTextœ, œstrœ], œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-EjXlNœ}", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-EjXlNœ, œoutput_typesœ: [œTextœ], œnameœ: œtext_outputœ}", "style": { "stroke": "#555" }, "target": "ChatOutput-Q39I8", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-Q39I8œ, œinputTypesœ: [œTextœ], œtypeœ: œstrœ}" + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-Q39I8œ, œinputTypesœ: [œTextœ, œMessageœ], œtypeœ: œstrœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Record" - ], "dataType": "File", - "id": "File-t0a6a" + "id": "File-t0a6a", + "name": "record", + "output_types": [] }, "targetHandle": { "fieldName": "inputs", "id": "RecursiveCharacterTextSplitter-tR9QM", - "inputTypes": [ - "Document", - "Record" - ], + "inputTypes": ["Document", "Data"], "type": "Document" } }, "id": "reactflow__edge-File-t0a6a{œbaseClassesœ:[œRecordœ],œdataTypeœ:œFileœ,œidœ:œFile-t0a6aœ}-RecursiveCharacterTextSplitter-tR9QM{œfieldNameœ:œinputsœ,œidœ:œRecursiveCharacterTextSplitter-tR9QMœ,œinputTypesœ:[œDocumentœ,œRecordœ],œtypeœ:œDocumentœ}", "selected": false, "source": "File-t0a6a", - "sourceHandle": "{œbaseClassesœ: [œRecordœ], œdataTypeœ: œFileœ, œidœ: œFile-t0a6aœ}", + "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-t0a6aœ, œoutput_typesœ: [], œnameœ: œrecordœ}", "style": { "stroke": "#555" }, "target": "RecursiveCharacterTextSplitter-tR9QM", - "targetHandle": "{œfieldNameœ: œinputsœ, œidœ: œRecursiveCharacterTextSplitter-tR9QMœ, œinputTypesœ: [œDocumentœ, œRecordœ], œtypeœ: œDocumentœ}" + "targetHandle": "{œfieldNameœ: œinputsœ, œidœ: œRecursiveCharacterTextSplitter-tR9QMœ, œinputTypesœ: [œDocumentœ, œDataœ], œtypeœ: œDocumentœ}" }, { "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Embeddings" - ], "dataType": "OpenAIEmbeddings", - "id": "OpenAIEmbeddings-ZlOk1" + "id": "OpenAIEmbeddings-ZlOk1", + "name": "embeddings", + "output_types": ["Embeddings"] }, "targetHandle": { "fieldName": "embedding", @@ -183,7 +149,7 @@ }, "id": "reactflow__edge-OpenAIEmbeddings-ZlOk1{œbaseClassesœ:[œEmbeddingsœ],œdataTypeœ:œOpenAIEmbeddingsœ,œidœ:œOpenAIEmbeddings-ZlOk1œ}-AstraDBSearch-41nRz{œfieldNameœ:œembeddingœ,œidœ:œAstraDBSearch-41nRzœ,œinputTypesœ:null,œtypeœ:œEmbeddingsœ}", "source": "OpenAIEmbeddings-ZlOk1", - "sourceHandle": "{œbaseClassesœ: [œEmbeddingsœ], œdataTypeœ: œOpenAIEmbeddingsœ, œidœ: œOpenAIEmbeddings-ZlOk1œ}", + "sourceHandle": "{œdataTypeœ: œOpenAIEmbeddingsœ, œidœ: œOpenAIEmbeddings-ZlOk1œ, œoutput_typesœ: [œEmbeddingsœ], œnameœ: œembeddingsœ}", "style": { "stroke": "#555" }, @@ -194,27 +160,21 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Text", - "str", - "object", - "Record" - ], "dataType": "ChatInput", - "id": "ChatInput-yxMKE" + "id": "ChatInput-yxMKE", + "name": "message", + "output_types": ["Message"] }, "targetHandle": { "fieldName": "input_value", "id": "AstraDBSearch-41nRz", - "inputTypes": [ - "Text" - ], + "inputTypes": ["Text"], "type": "str" } }, "id": "reactflow__edge-ChatInput-yxMKE{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ,œRecordœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-yxMKEœ}-AstraDBSearch-41nRz{œfieldNameœ:œinput_valueœ,œidœ:œAstraDBSearch-41nRzœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "source": "ChatInput-yxMKE", - "sourceHandle": "{œbaseClassesœ: [œTextœ, œstrœ, œobjectœ, œRecordœ], œdataTypeœ: œChatInputœ, œidœ: œChatInput-yxMKEœ}", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-yxMKEœ, œoutput_typesœ: [œMessageœ], œnameœ: œmessageœ}", "style": { "stroke": "#555" }, @@ -225,11 +185,10 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Record" - ], "dataType": "RecursiveCharacterTextSplitter", - "id": "RecursiveCharacterTextSplitter-tR9QM" + "id": "RecursiveCharacterTextSplitter-tR9QM", + "name": "record", + "output_types": [] }, "targetHandle": { "fieldName": "inputs", @@ -241,7 +200,7 @@ "id": "reactflow__edge-RecursiveCharacterTextSplitter-tR9QM{œbaseClassesœ:[œRecordœ],œdataTypeœ:œRecursiveCharacterTextSplitterœ,œidœ:œRecursiveCharacterTextSplitter-tR9QMœ}-AstraDB-eUCSS{œfieldNameœ:œinputsœ,œidœ:œAstraDB-eUCSSœ,œinputTypesœ:null,œtypeœ:œRecordœ}", "selected": false, "source": "RecursiveCharacterTextSplitter-tR9QM", - "sourceHandle": "{œbaseClassesœ: [œRecordœ], œdataTypeœ: œRecursiveCharacterTextSplitterœ, œidœ: œRecursiveCharacterTextSplitter-tR9QMœ}", + "sourceHandle": "{œdataTypeœ: œRecursiveCharacterTextSplitterœ, œidœ: œRecursiveCharacterTextSplitter-tR9QMœ, œoutput_typesœ: [], œnameœ: œrecordœ}", "style": { "stroke": "#555" }, @@ -252,11 +211,10 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Embeddings" - ], "dataType": "OpenAIEmbeddings", - "id": "OpenAIEmbeddings-9TPjc" + "id": "OpenAIEmbeddings-9TPjc", + "name": "embeddings", + "output_types": ["Embeddings"] }, "targetHandle": { "fieldName": "embedding", @@ -268,7 +226,7 @@ "id": "reactflow__edge-OpenAIEmbeddings-9TPjc{œbaseClassesœ:[œEmbeddingsœ],œdataTypeœ:œOpenAIEmbeddingsœ,œidœ:œOpenAIEmbeddings-9TPjcœ}-AstraDB-eUCSS{œfieldNameœ:œembeddingœ,œidœ:œAstraDB-eUCSSœ,œinputTypesœ:null,œtypeœ:œEmbeddingsœ}", "selected": false, "source": "OpenAIEmbeddings-9TPjc", - "sourceHandle": "{œbaseClassesœ: [œEmbeddingsœ], œdataTypeœ: œOpenAIEmbeddingsœ, œidœ: œOpenAIEmbeddings-9TPjcœ}", + "sourceHandle": "{œdataTypeœ: œOpenAIEmbeddingsœ, œidœ: œOpenAIEmbeddings-9TPjcœ, œoutput_typesœ: [œEmbeddingsœ], œnameœ: œembeddingsœ}", "style": { "stroke": "#555" }, @@ -279,25 +237,21 @@ "className": "stroke-gray-900 stroke-connection", "data": { "sourceHandle": { - "baseClasses": [ - "Record" - ], "dataType": "AstraDBSearch", - "id": "AstraDBSearch-41nRz" + "id": "AstraDBSearch-41nRz", + "name": "record", + "output_types": [] }, "targetHandle": { "fieldName": "input_value", "id": "TextOutput-BDknO", - "inputTypes": [ - "Record", - "Text" - ], + "inputTypes": ["Record", "Text"], "type": "str" } }, "id": "reactflow__edge-AstraDBSearch-41nRz{œbaseClassesœ:[œRecordœ],œdataTypeœ:œAstraDBSearchœ,œidœ:œAstraDBSearch-41nRzœ}-TextOutput-BDknO{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-BDknOœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", "source": "AstraDBSearch-41nRz", - "sourceHandle": "{œbaseClassesœ: [œRecordœ], œdataTypeœ: œAstraDBSearchœ, œidœ: œAstraDBSearch-41nRzœ}", + "sourceHandle": "{œdataTypeœ: œAstraDBSearchœ, œidœ: œAstraDBSearch-41nRzœ, œoutput_typesœ: [], œnameœ: œrecordœ}", "style": { "stroke": "#555" }, @@ -310,12 +264,7 @@ "data": { "id": "ChatInput-yxMKE", "node": { - "base_classes": [ - "Text", - "str", - "object", - "Record" - ], + "base_classes": ["Text", "str", "object", "Record"], "beta": false, "custom_fields": { "input_value": null, @@ -331,12 +280,20 @@ "field_order": [], "frozen": false, "icon": "ChatInput", - "output_types": [ - "Message", - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -353,7 +310,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import Text\nfrom typing import Union\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\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\": \"Text\",\n \"multiline\": True,\n }\n build_config[\"return_message\"] = {\n \"display_name\": \"Return Record\",\n \"advanced\": 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 files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_message: Optional[bool] = True,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_message=return_message,\n )\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import DropdownInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\n\n inputs = [\n StrInput(\n name=\"input_value\",\n display_name=\"Text\",\n multiline=True,\n input_types=[],\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"User\",\n info=\"Type of sender.\",\n advanced=True,\n ),\n StrInput(\n name=\"sender_name\",\n type=str,\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=\"User\",\n advanced=True,\n ),\n StrInput(\n name=\"session_id\", type=str, display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, (Message, str)) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -361,7 +318,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", + "info": "Message to be passed as input.", "input_types": [], "list": false, "load_from_db": false, @@ -373,7 +330,7 @@ "show": true, "title_case": false, "type": "str", - "value": "what is a line" + "value": "" }, "sender": { "advanced": true, @@ -381,18 +338,13 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Type of sender.", + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -402,15 +354,13 @@ "value": "User" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Name of the sender.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -429,10 +379,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "info": "Session ID for the message.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -442,7 +390,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" } } }, @@ -462,11 +411,7 @@ "data": { "id": "TextOutput-BDknO", "node": { - "base_classes": [ - "object", - "Text", - "str" - ], + "base_classes": ["object", "Text", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -479,8 +424,13 @@ "field_order": [], "frozen": false, "icon": "type", - "output_types": [ - "Text" + "output_types": ["Text"], + "outputs": [ + { + "name": "Text", + "selected": "Text", + "types": ["Text"] + } ], "template": { "_type": "CustomComponent", @@ -509,10 +459,7 @@ "fileTypes": [], "file_path": "", "info": "Text or Record to be passed as output.", - "input_types": [ - "Record", - "Text" - ], + "input_types": ["Record", "Text"], "list": false, "load_from_db": false, "multiline": false, @@ -532,9 +479,7 @@ "fileTypes": [], "file_path": "", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": true, @@ -570,9 +515,7 @@ "data": { "id": "OpenAIEmbeddings-ZlOk1", "node": { - "base_classes": [ - "Embeddings" - ], + "base_classes": ["Embeddings"], "beta": false, "custom_fields": { "allowed_special": null, @@ -604,8 +547,18 @@ "field_formatters": {}, "field_order": [], "frozen": false, - "output_types": [ - "Embeddings" + "output_types": ["Embeddings"], + "outputs": [ + { + "cache": true, + "display_name": "Embeddings", + "hidden": null, + "method": null, + "name": "embeddings", + "selected": "Embeddings", + "types": ["Embeddings"], + "value": "__UNDEFINED__" + } ], "template": { "_type": "CustomComponent", @@ -616,10 +569,8 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], - "list": false, + "input_types": ["Text"], + "list": true, "load_from_db": false, "multiline": false, "name": "allowed_special", @@ -712,9 +663,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -734,10 +683,8 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], - "list": false, + "input_types": ["Text"], + "list": true, "load_from_db": false, "multiline": false, "name": "disallowed_special", @@ -747,9 +694,7 @@ "show": true, "title_case": false, "type": "str", - "value": [ - "all" - ] + "value": ["all"] }, "embedding_ctx_length": { "advanced": true, @@ -796,9 +741,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -842,9 +785,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -863,11 +804,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, - "load_from_db": true, + "load_from_db": false, "multiline": false, "name": "openai_api_key", "password": true, @@ -885,9 +824,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -906,9 +843,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -927,9 +862,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -948,9 +881,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1050,9 +981,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1083,11 +1012,7 @@ "data": { "id": "OpenAIModel-EjXlN", "node": { - "base_classes": [ - "object", - "Text", - "str" - ], + "base_classes": ["object", "Text", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -1117,11 +1042,29 @@ ], "frozen": false, "icon": "OpenAI", - "output_types": [ - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Text", + "method": "text_response", + "name": "text_output", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Language Model", + "method": "build_model", + "name": "model_output", + "selected": "BaseLanguageModel", + "types": ["BaseLanguageModel"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -1138,7 +1081,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\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\", \"input_types\": [\"Text\", \"Record\", \"Prompt\"]},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\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\": MODEL_NAMES,\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 = 0.1,\n model_name: str = \"gpt-3.5-turbo\",\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 if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n" + "value": "from langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import BaseLanguageModel, Text\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, FloatInput, SecretStrInput, StrInput\nfrom langflow.inputs.inputs import IntInput\nfrom langflow.template import Output\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n StrInput(name=\"input_value\", display_name=\"Input\", input_types=[\"Text\", \"Data\", \"Prompt\"]),\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n 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.\",\n ),\n SecretStrInput(\n name=\"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 value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n BoolInput(name=\"stream\", display_name=\"Stream\", info=STREAM_INFO_TEXT, advanced=True),\n StrInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"System message to pass to the model.\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text_output\", method=\"text_response\"),\n Output(display_name=\"Language Model\", name=\"model_output\", method=\"build_model\"),\n ]\n\n def text_response(self) -> Text:\n input_value = self.input_value\n stream = self.stream\n system_message = self.system_message\n output = self.build_model()\n result = self.get_chat_result(output, stream, input_value, system_message)\n self.status = result\n return result\n\n def build_model(self) -> BaseLanguageModel:\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs or {},\n model=model_name or None,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n )\n return output\n" }, "input_value": { "advanced": false, @@ -1147,21 +1090,18 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text", - "Record", - "Prompt" - ], + "input_types": ["Text", "Data", "Prompt"], "list": false, "load_from_db": false, "multiline": false, "name": "input_value", "password": false, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "max_tokens": { "advanced": true, @@ -1170,6 +1110,7 @@ "fileTypes": [], "file_path": "", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1179,8 +1120,8 @@ "required": false, "show": true, "title_case": false, - "type": "int", - "value": 256 + "type": "str", + "value": "" }, "model_kwargs": { "advanced": true, @@ -1189,6 +1130,7 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1198,8 +1140,8 @@ "required": false, "show": true, "title_case": false, - "type": "NestedDict", - "value": {} + "type": "str", + "value": "" }, "model_name": { "advanced": false, @@ -1208,9 +1150,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -1237,9 +1177,7 @@ "fileTypes": [], "file_path": "", "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.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1249,7 +1187,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "openai_api_key": { "advanced": false, @@ -1258,16 +1197,14 @@ "fileTypes": [], "file_path": "", "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": true, "multiline": false, "name": "openai_api_key", "password": true, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "type": "str", @@ -1280,6 +1217,7 @@ "fileTypes": [], "file_path": "", "info": "Stream the response from the model. Streaming works only in Chat.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1289,7 +1227,7 @@ "required": false, "show": true, "title_case": false, - "type": "bool", + "type": "str", "value": false }, "system_message": { @@ -1299,9 +1237,7 @@ "fileTypes": [], "file_path": "", "info": "System message to pass to the model.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1311,7 +1247,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "temperature": { "advanced": false, @@ -1320,22 +1257,17 @@ "fileTypes": [], "file_path": "", "info": "", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, "name": "temperature", "password": false, "placeholder": "", - "rangeSpec": { - "max": 1, - "min": -1, - "step": 0.1, - "step_type": "float" - }, "required": false, "show": true, "title_case": false, - "type": "float", + "type": "str", "value": 0.1 } } @@ -1363,17 +1295,10 @@ "display_name": "Prompt", "id": "Prompt-xeI6K", "node": { - "base_classes": [ - "object", - "Text", - "str" - ], + "base_classes": ["object", "Text", "str"], "beta": false, "custom_fields": { - "template": [ - "context", - "question" - ] + "template": ["context", "question"] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", @@ -1388,11 +1313,29 @@ "is_input": null, "is_output": null, "name": "", - "output_types": [ - "Prompt" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Prompt", + "method": "build_prompt", + "name": "prompt", + "selected": "Prompt", + "types": ["Prompt"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Text", + "method": "format_prompt", + "name": "text", + "selected": "Text", + "types": ["Text"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -1409,7 +1352,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.custom import CustomComponent\nfrom langflow.field_typing import TemplateField\nfrom langflow.field_typing.prompt import Prompt\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 async def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Prompt:\n prompt = await Prompt.from_template_and_variables(template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" + "value": "from langflow.custom import Component\nfrom langflow.field_typing.prompt import Prompt\nfrom langflow.inputs import PromptInput\nfrom langflow.template import Output\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n Output(display_name=\"Text\", name=\"text\", method=\"format_prompt\"),\n ]\n\n async def format_prompt(self) -> str:\n prompt = await self.build_prompt()\n formatted_text = prompt.format_text()\n self.status = formatted_text\n return formatted_text\n\n async def build_prompt(\n self,\n ) -> Prompt:\n kwargs = {k: v for k, v in self._arguments.items() if k != \"template\"}\n prompt = await Prompt.from_template_and_variables(self.template, kwargs)\n self.status = prompt.format_text()\n return prompt\n" }, "context": { "advanced": false, @@ -1419,12 +1362,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "input_types": ["Document", "Message", "Record", "Text"], "list": false, "load_from_db": false, "multiline": true, @@ -1445,12 +1383,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Document", - "BaseOutputParser", - "Record", - "Text" - ], + "input_types": ["Document", "Message", "Record", "Text"], "list": false, "load_from_db": false, "multiline": true, @@ -1470,9 +1403,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1508,12 +1439,7 @@ "data": { "id": "ChatOutput-Q39I8", "node": { - "base_classes": [ - "object", - "Text", - "Record", - "str" - ], + "base_classes": ["object", "Text", "Record", "str"], "beta": false, "custom_fields": { "input_value": null, @@ -1530,12 +1456,20 @@ "field_order": [], "frozen": false, "icon": "ChatOutput", - "output_types": [ - "Message", - "Text" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -1552,7 +1486,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\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 files: Optional[list[str]] = None,\n return_message: Optional[bool] = False,\n ) -> Union[Message, Text]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n files=files,\n return_message=return_message,\n )\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput, DropdownInput, MultilineInput, StrInput\nfrom langflow.schema.message import Message\nfrom langflow.template import Output\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n input_types=[\"Text\", \"Message\"],\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"Machine\",\n advanced=True,\n info=\"Type of sender.\",\n ),\n StrInput(name=\"sender_name\", display_name=\"Sender Name\", info=\"Name of the sender.\", value=\"AI\", advanced=True),\n StrInput(name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", advanced=True),\n BoolInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n if isinstance(self.input_value, Message):\n message = self.input_value\n else:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and isinstance(message.text, str):\n self.store_message(message)\n self.status = message\n return message\n" }, "input_value": { "advanced": false, @@ -1560,10 +1494,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Message to be passed as output.", + "input_types": ["Text", "Message"], "list": false, "load_from_db": false, "multiline": true, @@ -1573,7 +1505,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" }, "sender": { "advanced": true, @@ -1581,18 +1514,13 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Type of sender.", + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "sender", - "options": [ - "Machine", - "User" - ], + "options": ["Machine", "User"], "password": false, "placeholder": "", "required": false, @@ -1602,15 +1530,13 @@ "value": "Machine" }, "sender_name": { - "advanced": false, + "advanced": true, "display_name": "Sender Name", "dynamic": false, "fileTypes": [], "file_path": "", - "info": "", - "input_types": [ - "Text" - ], + "info": "Name of the sender.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1629,10 +1555,8 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "If provided, the message will be stored in the memory.", - "input_types": [ - "Text" - ], + "info": "Session ID for the message.", + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -1642,7 +1566,8 @@ "required": false, "show": true, "title_case": false, - "type": "str" + "type": "str", + "value": "" } } }, @@ -1667,9 +1592,7 @@ "data": { "id": "File-t0a6a", "node": { - "base_classes": [ - "Record" - ], + "base_classes": ["Record"], "beta": false, "custom_fields": { "path": null, @@ -1682,11 +1605,20 @@ "field_order": [], "frozen": false, "icon": "file-text", - "output_types": [ - "Record" + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "method": "load_file", + "name": "data", + "selected": "Data", + "types": ["Data"], + "value": "__UNDEFINED__" + } ], "template": { - "_type": "CustomComponent", + "_type": "Component", "code": { "advanced": true, "dynamic": true, @@ -1703,58 +1635,49 @@ "show": true, "title_case": false, "type": "code", - "value": "from pathlib import Path\nfrom typing import Any, Dict\n\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parse_text_file_to_record\nfrom langflow.custom import CustomComponent\nfrom langflow.schema import Record\n\n\nclass FileComponent(CustomComponent):\n display_name = \"File\"\n description = \"A generic file loader.\"\n icon = \"file-text\"\n\n def build_config(self) -> Dict[str, Any]:\n return {\n \"path\": {\n \"display_name\": \"Path\",\n \"field_type\": \"file\",\n \"file_types\": TEXT_FILE_TYPES,\n \"info\": f\"Supported file types: {', '.join(TEXT_FILE_TYPES)}\",\n },\n \"silent_errors\": {\n \"display_name\": \"Silent Errors\",\n \"advanced\": True,\n \"info\": \"If true, errors will not raise an exception.\",\n },\n }\n\n def load_file(self, path: str, silent_errors: bool = False) -> Record:\n resolved_path = self.resolve_path(path)\n path_obj = Path(resolved_path)\n extension = path_obj.suffix[1:].lower()\n if extension == \"doc\":\n raise ValueError(\"doc files are not supported. Please save as .docx\")\n if extension not in TEXT_FILE_TYPES:\n raise ValueError(f\"Unsupported file type: {extension}\")\n record = parse_text_file_to_record(resolved_path, silent_errors)\n self.status = record if record else \"No data\"\n return record or Record()\n\n def build(\n self,\n path: str,\n silent_errors: bool = False,\n ) -> Record:\n record = self.load_file(path, silent_errors)\n self.status = record\n return record\n" + "value": "from pathlib import Path\n\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parse_text_file_to_data\nfrom langflow.custom import Component\nfrom langflow.inputs import BoolInput, FileInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\n\nclass FileComponent(Component):\n display_name = \"File\"\n description = \"A generic file loader.\"\n icon = \"file-text\"\n\n inputs = [\n FileInput(\n name=\"path\",\n display_name=\"Path\",\n file_types=TEXT_FILE_TYPES,\n info=f\"Supported file types: {', '.join(TEXT_FILE_TYPES)}\",\n ),\n BoolInput(\n name=\"silent_errors\",\n display_name=\"Silent Errors\",\n advanced=True,\n info=\"If true, errors will not raise an exception.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"load_file\"),\n ]\n\n def load_file(self) -> Data:\n if not self.path:\n raise ValueError(\"Please, upload a file to use this component.\")\n resolved_path = self.resolve_path(self.path)\n silent_errors = self.silent_errors\n\n extension = Path(resolved_path).suffix[1:].lower()\n\n if extension == \"doc\":\n raise ValueError(\"doc files are not supported. Please save as .docx\")\n if extension not in TEXT_FILE_TYPES:\n raise ValueError(f\"Unsupported file type: {extension}\")\n\n data = parse_text_file_to_data(resolved_path, silent_errors)\n self.status = data if data else \"No data\"\n return data or Data()\n" }, "path": { "advanced": false, "display_name": "Path", "dynamic": false, "fileTypes": [ - ".txt", - ".md", - ".mdx", - ".csv", - ".json", - ".yaml", - ".yml", - ".xml", - ".html", - ".htm", - ".pdf", - ".docx", - ".py", - ".sh", - ".sql", - ".js", - ".ts", - ".tsx" + "txt", + "md", + "mdx", + "csv", + "json", + "yaml", + "yml", + "xml", + "html", + "htm", + "pdf", + "docx", + "py", + "sh", + "sql", + "js", + "ts", + "tsx" ], - "file_path": "51e2b78a-199b-4054-9f32-e288eef6924c/Langflow conversation.pdf", + "file_path": "", "info": "Supported file types: txt, md, mdx, csv, json, yaml, yml, xml, html, htm, pdf, docx, py, sh, sql, js, ts, tsx", "list": false, - "load_from_db": false, - "multiline": false, "name": "path", - "password": false, "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "type": "file", - "value": "" + "type": "file" }, "silent_errors": { "advanced": true, "display_name": "Silent Errors", "dynamic": false, - "fileTypes": [], - "file_path": "", "info": "If true, errors will not raise an exception.", "list": false, - "load_from_db": false, - "multiline": false, "name": "silent_errors", - "password": false, "placeholder": "", "required": false, "show": true, @@ -1785,9 +1708,7 @@ "data": { "id": "RecursiveCharacterTextSplitter-tR9QM", "node": { - "base_classes": [ - "Record" - ], + "base_classes": ["Record"], "beta": false, "custom_fields": { "chunk_overlap": null, @@ -1801,8 +1722,18 @@ "field_formatters": {}, "field_order": [], "frozen": false, - "output_types": [ - "Record" + "output_types": ["Data"], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "hidden": null, + "method": null, + "name": "data", + "selected": "Data", + "types": ["Data"], + "value": "__UNDEFINED__" + } ], "template": { "_type": "CustomComponent", @@ -1860,7 +1791,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import Optional\n\nfrom langchain_core.documents import Document\nfrom langchain_text_splitters import RecursiveCharacterTextSplitter\n\nfrom langflow.custom import CustomComponent\nfrom langflow.schema import Record\nfrom langflow.utils.util import build_loader_repr_from_records, unescape_string\n\n\nclass RecursiveCharacterTextSplitterComponent(CustomComponent):\n display_name: str = \"Recursive Character Text Splitter\"\n description: str = \"Split text into chunks of a specified length.\"\n documentation: str = \"https://docs.langflow.org/components/text-splitters#recursivecharactertextsplitter\"\n\n def build_config(self):\n return {\n \"inputs\": {\n \"display_name\": \"Input\",\n \"info\": \"The texts to split.\",\n \"input_types\": [\"Document\", \"Record\"],\n },\n \"separators\": {\n \"display_name\": \"Separators\",\n \"info\": 'The characters to split on.\\nIf left empty defaults to [\"\\\\n\\\\n\", \"\\\\n\", \" \", \"\"].',\n \"is_list\": True,\n },\n \"chunk_size\": {\n \"display_name\": \"Chunk Size\",\n \"info\": \"The maximum length of each chunk.\",\n \"field_type\": \"int\",\n \"value\": 1000,\n },\n \"chunk_overlap\": {\n \"display_name\": \"Chunk Overlap\",\n \"info\": \"The amount of overlap between chunks.\",\n \"field_type\": \"int\",\n \"value\": 200,\n },\n \"code\": {\"show\": False},\n }\n\n def build(\n self,\n inputs: list[Document],\n separators: Optional[list[str]] = None,\n chunk_size: Optional[int] = 1000,\n chunk_overlap: Optional[int] = 200,\n ) -> list[Record]:\n \"\"\"\n Split text into chunks of a specified length.\n\n Args:\n separators (list[str]): The characters to split on.\n chunk_size (int): The maximum length of each chunk.\n chunk_overlap (int): The amount of overlap between chunks.\n length_function (function): The function to use to calculate the length of the text.\n\n Returns:\n list[str]: The chunks of text.\n \"\"\"\n\n if separators == \"\":\n separators = None\n elif separators:\n # check if the separators list has escaped characters\n # if there are escaped characters, unescape them\n separators = [unescape_string(x) for x in separators]\n\n # Make sure chunk_size and chunk_overlap are ints\n if isinstance(chunk_size, str):\n chunk_size = int(chunk_size)\n if isinstance(chunk_overlap, str):\n chunk_overlap = int(chunk_overlap)\n splitter = RecursiveCharacterTextSplitter(\n separators=separators,\n chunk_size=chunk_size,\n chunk_overlap=chunk_overlap,\n )\n documents = []\n for _input in inputs:\n if isinstance(_input, Record):\n documents.append(_input.to_lc_document())\n else:\n documents.append(_input)\n docs = splitter.split_documents(documents)\n records = self.to_records(docs)\n self.repr_value = build_loader_repr_from_records(records)\n return records\n" + "value": "from typing import Optional\n\nfrom langchain_core.documents import Document\nfrom langchain_text_splitters import RecursiveCharacterTextSplitter\n\nfrom langflow.custom import CustomComponent\nfrom langflow.schema import Data\nfrom langflow.utils.util import build_loader_repr_from_data, unescape_string\n\n\nclass RecursiveCharacterTextSplitterComponent(CustomComponent):\n display_name: str = \"Recursive Character Text Splitter\"\n description: str = \"Split text into chunks of a specified length.\"\n documentation: str = \"https://docs.langflow.org/components/text-splitters#recursivecharactertextsplitter\"\n\n def build_config(self):\n return {\n \"inputs\": {\n \"display_name\": \"Input\",\n \"info\": \"The texts to split.\",\n \"input_types\": [\"Document\", \"Data\"],\n },\n \"separators\": {\n \"display_name\": \"Separators\",\n \"info\": 'The characters to split on.\\nIf left empty defaults to [\"\\\\n\\\\n\", \"\\\\n\", \" \", \"\"].',\n \"is_list\": True,\n },\n \"chunk_size\": {\n \"display_name\": \"Chunk Size\",\n \"info\": \"The maximum length of each chunk.\",\n \"field_type\": \"int\",\n \"value\": 1000,\n },\n \"chunk_overlap\": {\n \"display_name\": \"Chunk Overlap\",\n \"info\": \"The amount of overlap between chunks.\",\n \"field_type\": \"int\",\n \"value\": 200,\n },\n \"code\": {\"show\": False},\n }\n\n def build(\n self,\n inputs: list[Document],\n separators: Optional[list[str]] = None,\n chunk_size: Optional[int] = 1000,\n chunk_overlap: Optional[int] = 200,\n ) -> list[Data]:\n \"\"\"\n Split text into chunks of a specified length.\n\n Args:\n separators (list[str]): The characters to split on.\n chunk_size (int): The maximum length of each chunk.\n chunk_overlap (int): The amount of overlap between chunks.\n length_function (function): The function to use to calculate the length of the text.\n\n Returns:\n list[str]: The chunks of text.\n \"\"\"\n\n if separators == \"\":\n separators = None\n elif separators:\n # check if the separators list has escaped characters\n # if there are escaped characters, unescape them\n separators = [unescape_string(x) for x in separators]\n\n # Make sure chunk_size and chunk_overlap are ints\n if isinstance(chunk_size, str):\n chunk_size = int(chunk_size)\n if isinstance(chunk_overlap, str):\n chunk_overlap = int(chunk_overlap)\n splitter = RecursiveCharacterTextSplitter(\n separators=separators,\n chunk_size=chunk_size,\n chunk_overlap=chunk_overlap,\n )\n documents = []\n for _input in inputs:\n if isinstance(_input, Data):\n documents.append(_input.to_lc_document())\n else:\n documents.append(_input)\n docs = splitter.split_documents(documents)\n data = self.to_data(docs)\n self.repr_value = build_loader_repr_from_data(data)\n return data\n" }, "inputs": { "advanced": false, @@ -1869,10 +1800,7 @@ "fileTypes": [], "file_path": "", "info": "The texts to split.", - "input_types": [ - "Document", - "Record" - ], + "input_types": ["Document", "Data"], "list": true, "load_from_db": false, "multiline": false, @@ -1891,9 +1819,7 @@ "fileTypes": [], "file_path": "", "info": "The characters to split on.\nIf left empty defaults to [\"\\n\\n\", \"\\n\", \" \", \"\"].", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -1904,9 +1830,7 @@ "show": true, "title_case": false, "type": "str", - "value": [ - "" - ] + "value": [""] } } }, @@ -1931,9 +1855,7 @@ "data": { "id": "AstraDBSearch-41nRz", "node": { - "base_classes": [ - "Record" - ], + "base_classes": ["Record"], "beta": false, "custom_fields": { "api_endpoint": null, @@ -1968,8 +1890,18 @@ ], "frozen": false, "icon": "AstraDB", - "output_types": [ - "Record" + "output_types": ["Data"], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "hidden": null, + "method": null, + "name": "data", + "selected": "Data", + "types": ["Data"], + "value": "__UNDEFINED__" + } ], "template": { "_type": "CustomComponent", @@ -1980,11 +1912,9 @@ "fileTypes": [], "file_path": "", "info": "API endpoint URL for the Astra DB service.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, - "load_from_db": true, + "load_from_db": false, "multiline": false, "name": "api_endpoint", "password": false, @@ -2001,7 +1931,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "Optional number of records to process in a single batch.", + "info": "Optional number of data to process in a single batch.", "list": false, "load_from_db": false, "multiline": false, @@ -2055,7 +1985,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "Optional concurrency level for bulk insert operations that overwrite existing records.", + "info": "Optional concurrency level for bulk insert operations that overwrite existing data.", "list": false, "load_from_db": false, "multiline": false, @@ -2083,7 +2013,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import List, Optional\n\nfrom langflow.components.vectorstores.AstraDB import AstraDBVectorStoreComponent\nfrom langflow.components.vectorstores.base.model import LCVectorStoreComponent\nfrom langflow.field_typing import Embeddings, Text\nfrom langflow.schema import Record\n\n\nclass AstraDBSearchComponent(LCVectorStoreComponent):\n display_name = \"Astra DB Search\"\n description = \"Searches an existing Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"input_value\", \"embedding\"]\n\n def build_config(self):\n return {\n \"search_type\": {\n \"display_name\": \"Search Type\",\n \"options\": [\"Similarity\", \"MMR\"],\n },\n \"input_value\": {\n \"display_name\": \"Input Value\",\n \"info\": \"Input value to search\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Astra DB Application Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of records to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing records.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n \"number_of_results\": {\n \"display_name\": \"Number of Results\",\n \"info\": \"Number of results to return.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n collection_name: str,\n input_value: Text,\n token: str,\n api_endpoint: str,\n search_type: str = \"Similarity\",\n number_of_results: int = 4,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> List[Record]:\n vector_store = AstraDBVectorStoreComponent().build(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n try:\n return self.search_with_vector_store(input_value, search_type, vector_store, k=number_of_results)\n except KeyError as e:\n if \"content\" in str(e):\n raise ValueError(\n \"You should ingest data through Langflow (or LangChain) to query it in Langflow. Your collection does not contain a field name 'content'.\"\n )\n else:\n raise e\n" + "value": "from typing import List, Optional\n\nfrom langflow.components.vectorstores.AstraDB import AstraDBVectorStoreComponent\nfrom langflow.components.vectorstores.base.model import LCVectorStoreComponent\nfrom langflow.field_typing import Embeddings, Text\nfrom langflow.schema import Data\n\n\nclass AstraDBSearchComponent(LCVectorStoreComponent):\n display_name = \"Astra DB Search\"\n description = \"Searches an existing Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"input_value\", \"embedding\"]\n\n def build_config(self):\n return {\n \"search_type\": {\n \"display_name\": \"Search Type\",\n \"options\": [\"Similarity\", \"MMR\"],\n },\n \"input_value\": {\n \"display_name\": \"Input Value\",\n \"info\": \"Input value to search\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Astra DB Application Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of data to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing data.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n \"number_of_results\": {\n \"display_name\": \"Number of Results\",\n \"info\": \"Number of results to return.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n collection_name: str,\n input_value: Text,\n token: str,\n api_endpoint: str,\n search_type: str = \"Similarity\",\n number_of_results: int = 4,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> List[Data]:\n vector_store = AstraDBVectorStoreComponent().build(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n try:\n return self.search_with_vector_store(input_value, search_type, vector_store, k=number_of_results)\n except KeyError as e:\n if \"content\" in str(e):\n raise ValueError(\n \"You should ingest data through Langflow (or LangChain) to query it in Langflow. Your collection does not contain a field name 'content'.\"\n )\n else:\n raise e\n" }, "collection_indexing_policy": { "advanced": true, @@ -2110,9 +2040,7 @@ "fileTypes": [], "file_path": "", "info": "The name of the collection within Astra DB where the vectors will be stored.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2150,9 +2078,7 @@ "fileTypes": [], "file_path": "", "info": "Input value to search", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2171,9 +2097,7 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to exclude from the indexing.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -2192,9 +2116,7 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to include in the indexing.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -2213,9 +2135,7 @@ "fileTypes": [], "file_path": "", "info": "Optional distance metric for vector comparisons in the vector store.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2234,9 +2154,7 @@ "fileTypes": [], "file_path": "", "info": "Optional namespace within Astra DB to use for the collection.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2293,17 +2211,12 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "search_type", - "options": [ - "Similarity", - "MMR" - ], + "options": ["Similarity", "MMR"], "password": false, "placeholder": "", "required": false, @@ -2319,18 +2232,12 @@ "fileTypes": [], "file_path": "", "info": "Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "setup_mode", - "options": [ - "Sync", - "Async", - "Off" - ], + "options": ["Sync", "Async", "Off"], "password": false, "placeholder": "", "required": false, @@ -2346,11 +2253,9 @@ "fileTypes": [], "file_path": "", "info": "Authentication token for accessing Astra DB.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, - "load_from_db": true, + "load_from_db": false, "multiline": false, "name": "token", "password": true, @@ -2384,9 +2289,7 @@ "data": { "id": "AstraDB-eUCSS", "node": { - "base_classes": [ - "VectorStore" - ], + "base_classes": ["VectorStore"], "beta": false, "custom_fields": { "api_endpoint": null, @@ -2419,9 +2322,28 @@ ], "frozen": false, "icon": "AstraDB", - "output_types": [ - "VectorStore", - "BaseRetriever" + "output_types": ["VectorStore", "BaseRetriever"], + "outputs": [ + { + "cache": true, + "display_name": "VectorStore", + "hidden": null, + "method": null, + "name": "vectorstore", + "selected": "VectorStore", + "types": ["VectorStore"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "BaseRetriever", + "hidden": null, + "method": null, + "name": "baseretriever", + "selected": "BaseRetriever", + "types": ["BaseRetriever"], + "value": "__UNDEFINED__" + } ], "template": { "_type": "CustomComponent", @@ -2432,11 +2354,9 @@ "fileTypes": [], "file_path": "", "info": "API endpoint URL for the Astra DB service.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, - "load_from_db": true, + "load_from_db": false, "multiline": false, "name": "api_endpoint", "password": false, @@ -2453,7 +2373,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "Optional number of records to process in a single batch.", + "info": "Optional number of data to process in a single batch.", "list": false, "load_from_db": false, "multiline": false, @@ -2507,7 +2427,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "Optional concurrency level for bulk insert operations that overwrite existing records.", + "info": "Optional concurrency level for bulk insert operations that overwrite existing data.", "list": false, "load_from_db": false, "multiline": false, @@ -2535,7 +2455,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import List, Optional, Union\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Embeddings, VectorStore\nfrom langflow.schema import Record\nfrom langchain_core.retrievers import BaseRetriever\n\n\nclass AstraDBVectorStoreComponent(CustomComponent):\n display_name = \"Astra DB\"\n description = \"Builds or loads an Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"inputs\", \"embedding\"]\n\n def build_config(self):\n return {\n \"inputs\": {\n \"display_name\": \"Inputs\",\n \"info\": \"Optional list of records to be processed and stored in the vector store.\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Astra DB Application Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of records to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing records.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n token: str,\n api_endpoint: str,\n collection_name: str,\n inputs: Optional[List[Record]] = None,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> Union[VectorStore, BaseRetriever]:\n try:\n from langchain_astradb import AstraDBVectorStore\n from langchain_astradb.utils.astradb import SetupMode\n except ImportError:\n raise ImportError(\n \"Could not import langchain Astra DB integration package. \"\n \"Please install it with `pip install langchain-astradb`.\"\n )\n\n try:\n setup_mode_value = SetupMode[setup_mode.upper()]\n except KeyError:\n raise ValueError(f\"Invalid setup mode: {setup_mode}\")\n if inputs:\n documents = [_input.to_lc_document() for _input in inputs]\n\n vector_store = AstraDBVectorStore.from_documents(\n documents=documents,\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n else:\n vector_store = AstraDBVectorStore(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n\n return vector_store\n return vector_store\n" + "value": "from typing import List, Optional, Union\n\nfrom langchain_core.retrievers import BaseRetriever\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Embeddings, VectorStore\nfrom langflow.schema import Data\n\n\nclass AstraDBVectorStoreComponent(CustomComponent):\n display_name = \"Astra DB\"\n description = \"Builds or loads an Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"inputs\", \"embedding\"]\n\n def build_config(self):\n return {\n \"inputs\": {\n \"display_name\": \"Inputs\",\n \"info\": \"Optional list of data to be processed and stored in the vector store.\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Astra DB Application Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of data to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing data.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n token: str,\n api_endpoint: str,\n collection_name: str,\n inputs: Optional[List[Data]] = None,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> Union[VectorStore, BaseRetriever]:\n try:\n from langchain_astradb import AstraDBVectorStore\n from langchain_astradb.utils.astradb import SetupMode\n except ImportError:\n raise ImportError(\n \"Could not import langchain Astra DB integration package. \"\n \"Please install it with `pip install langchain-astradb`.\"\n )\n\n try:\n setup_mode_value = SetupMode[setup_mode.upper()]\n except KeyError:\n raise ValueError(f\"Invalid setup mode: {setup_mode}\")\n if inputs:\n documents = [_input.to_lc_document() for _input in inputs]\n\n vector_store = AstraDBVectorStore.from_documents(\n documents=documents,\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n else:\n vector_store = AstraDBVectorStore(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n\n return vector_store\n return vector_store\n" }, "collection_indexing_policy": { "advanced": true, @@ -2562,9 +2482,7 @@ "fileTypes": [], "file_path": "", "info": "The name of the collection within Astra DB where the vectors will be stored.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2601,7 +2519,7 @@ "dynamic": false, "fileTypes": [], "file_path": "", - "info": "Optional list of records to be processed and stored in the vector store.", + "info": "Optional list of data to be processed and stored in the vector store.", "list": true, "load_from_db": false, "multiline": false, @@ -2620,9 +2538,7 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to exclude from the indexing.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -2641,9 +2557,7 @@ "fileTypes": [], "file_path": "", "info": "Optional list of metadata fields to include in the indexing.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -2662,9 +2576,7 @@ "fileTypes": [], "file_path": "", "info": "Optional distance metric for vector comparisons in the vector store.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2683,9 +2595,7 @@ "fileTypes": [], "file_path": "", "info": "Optional namespace within Astra DB to use for the collection.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2723,18 +2633,12 @@ "fileTypes": [], "file_path": "", "info": "Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, "name": "setup_mode", - "options": [ - "Sync", - "Async", - "Off" - ], + "options": ["Sync", "Async", "Off"], "password": false, "placeholder": "", "required": false, @@ -2750,11 +2654,9 @@ "fileTypes": [], "file_path": "", "info": "Authentication token for accessing Astra DB.", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, - "load_from_db": true, + "load_from_db": false, "multiline": false, "name": "token", "password": true, @@ -2788,9 +2690,7 @@ "data": { "id": "OpenAIEmbeddings-9TPjc", "node": { - "base_classes": [ - "Embeddings" - ], + "base_classes": ["Embeddings"], "beta": false, "custom_fields": { "allowed_special": null, @@ -2822,8 +2722,18 @@ "field_formatters": {}, "field_order": [], "frozen": false, - "output_types": [ - "Embeddings" + "output_types": ["Embeddings"], + "outputs": [ + { + "cache": true, + "display_name": "Embeddings", + "hidden": null, + "method": null, + "name": "embeddings", + "selected": "Embeddings", + "types": ["Embeddings"], + "value": "__UNDEFINED__" + } ], "template": { "_type": "CustomComponent", @@ -2834,10 +2744,8 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], - "list": false, + "input_types": ["Text"], + "list": true, "load_from_db": false, "multiline": false, "name": "allowed_special", @@ -2930,9 +2838,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -2952,10 +2858,8 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], - "list": false, + "input_types": ["Text"], + "list": true, "load_from_db": false, "multiline": false, "name": "disallowed_special", @@ -2965,9 +2869,7 @@ "show": true, "title_case": false, "type": "str", - "value": [ - "all" - ] + "value": ["all"] }, "embedding_ctx_length": { "advanced": true, @@ -3014,9 +2916,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": true, "load_from_db": false, "multiline": false, @@ -3060,9 +2960,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3081,11 +2979,9 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, - "load_from_db": true, + "load_from_db": false, "multiline": false, "name": "openai_api_key", "password": true, @@ -3103,9 +2999,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3124,9 +3018,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3145,9 +3037,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3166,9 +3056,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, @@ -3268,9 +3156,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Text" - ], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, From 2b244b69e314799f2d023bd83428a9a3f608b390 Mon Sep 17 00:00:00 2001 From: italojohnny Date: Fri, 14 Jun 2024 09:30:38 -0300 Subject: [PATCH 62/68] fix .json files after merge conflict --- .../initial_setup/starter_projects/Langflow Blog Writter.json | 4 ++-- .../starter_projects/Langflow Prompt Chaining.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json index 4810c2b28..24b77b849 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json @@ -358,7 +358,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.inputs import StrInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n inputs = [\n StrInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, separated by commas.\",\n value=\"\",\n is_list=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n ]\n\n def fetch_content(self) -> Data:\n urls = [url.strip() for url in self.urls if url.strip()]\n loader = WebBaseLoader(web_paths=urls)\n docs = loader.load()\n data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n" + "value": "from langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.inputs import StrInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\nimport re\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n inputs = [\n StrInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, separated by commas.\",\n value=\"\",\n is_list=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"\n Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(http://|https://)?\" # http:// or https://\n r\"(([a-zA-Z0-9\\.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,}))\" # top-level domain\n r\"(:[0-9]{1,5})?\" # optional port\n r\"(\\/.*)?$\" # optional path\n )\n\n if not re.match(url_regex, string):\n raise ValueError(f\"Invalid URL: {string}\")\n\n return string\n\n def fetch_content(self) -> Data:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n loader = WebBaseLoader(web_paths=urls)\n docs = loader.load()\n data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n" }, "urls": { "advanced": false, @@ -869,7 +869,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.inputs import StrInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n inputs = [\n StrInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, separated by commas.\",\n value=\"\",\n is_list=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n ]\n\n def fetch_content(self) -> Data:\n urls = [url.strip() for url in self.urls if url.strip()]\n loader = WebBaseLoader(web_paths=urls)\n docs = loader.load()\n data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n" + "value": "from langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.inputs import StrInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\nimport re\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n inputs = [\n StrInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, separated by commas.\",\n value=\"\",\n is_list=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"\n Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(http://|https://)?\" # http:// or https://\n r\"(([a-zA-Z0-9\\.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,}))\" # top-level domain\n r\"(:[0-9]{1,5})?\" # optional port\n r\"(\\/.*)?$\" # optional path\n )\n\n if not re.match(url_regex, string):\n raise ValueError(f\"Invalid URL: {string}\")\n\n return string\n\n def fetch_content(self) -> Data:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n loader = WebBaseLoader(web_paths=urls)\n docs = loader.load()\n data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n" }, "urls": { "advanced": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json index 81c6c3c3a..8951eaa80 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json @@ -802,7 +802,7 @@ "fileTypes": [], "file_path": "", "info": "Text to be passed as input.", - "input_types": ["Data", "Text"], + "input_types": ["Text"], "list": false, "load_from_db": false, "multiline": false, From ec1a0fafe5be2864f71c17d5d33f9bcd5d82e396 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Fri, 14 Jun 2024 09:34:59 -0300 Subject: [PATCH 63/68] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(use-delete-folder.t?= =?UTF-8?q?sx):=20replace=20refreshFolders=20with=20getFoldersApi=20for=20?= =?UTF-8?q?better=20API=20call=20management?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx b/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx index 239bf26f3..1eb1c9c0c 100644 --- a/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx +++ b/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx @@ -8,6 +8,7 @@ const useDeleteFolder = ({ navigate }) => { const folderToEdit = useFolderStore((state) => state.folderToEdit); const myCollectionId = useFolderStore((state) => state.myCollectionId); const refreshFolders = useFolderStore((state) => state.refreshFolders); + const getFoldersApi = useFolderStore((state) => state.getFoldersApi); const handleDeleteFolder = () => { deleteFolder(folderToEdit?.id!) @@ -16,7 +17,7 @@ const useDeleteFolder = ({ navigate }) => { title: "Folder deleted successfully.", }); getFolderById(myCollectionId!); - refreshFolders(); + getFoldersApi(true); navigate("/all"); }) .catch((err) => { From 705311982c21ecae3b590af7648cef46de32b46e Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Fri, 14 Jun 2024 09:40:48 -0300 Subject: [PATCH 64/68] formatting files --- src/frontend/src/App.tsx | 6 +-- .../components/sideBarFolderButtons/index.tsx | 18 +++---- .../hooks/use-on-file-drop.tsx | 10 ++-- .../src/components/tableComponent/index.tsx | 10 ++-- src/frontend/src/contexts/authContext.tsx | 6 +-- src/frontend/src/controllers/API/api.tsx | 10 ++-- .../API/helpers/check-duplicate-requests.ts | 2 +- src/frontend/src/controllers/API/index.ts | 52 +++++++++---------- .../headerTabsSearchComponent/index.tsx | 2 +- .../pages/MainPage/pages/mainPage/index.tsx | 2 +- src/frontend/src/stores/foldersStore.tsx | 16 +++--- .../src/types/zustand/folders/index.ts | 2 +- 12 files changed, 68 insertions(+), 68 deletions(-) diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 720ffb6b2..b84c0d792 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -29,10 +29,10 @@ export default function App() { useTrackLastVisitedPath(); const removeFromTempNotificationList = useAlertStore( - (state) => state.removeFromTempNotificationList, + (state) => state.removeFromTempNotificationList ); const tempNotificationList = useAlertStore( - (state) => state.tempNotificationList, + (state) => state.tempNotificationList ); const [fetchError, setFetchError] = useState(false); const isLoading = useFlowsManagerStore((state) => state.isLoading); @@ -48,7 +48,7 @@ export default function App() { const refreshVersion = useDarkStore((state) => state.refreshVersion); const refreshStars = useDarkStore((state) => state.refreshStars); const setGlobalVariables = useGlobalVariablesStore( - (state) => state.setGlobalVariables, + (state) => state.setGlobalVariables ); const checkHasStore = useStoreStore((state) => state.checkHasStore); const navigate = useNavigate(); diff --git a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx b/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx index 1b538ba20..ff5c17a15 100644 --- a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx +++ b/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx @@ -31,7 +31,7 @@ const SideBarFoldersButtonsComponent = ({ const [foldersNames, setFoldersNames] = useState({}); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); const [editFolders, setEditFolderName] = useState( - folders.map((obj) => ({ name: obj.name, edit: false })), + folders.map((obj) => ({ name: obj.name, edit: false })) ); const uploadFolder = useFolderStore((state) => state.uploadFolder); const currentFolder = pathname.split("/"); @@ -58,7 +58,7 @@ const SideBarFoldersButtonsComponent = ({ const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop( folderId, - handleFolderChange, + handleFolderChange ); const handleUploadFlowsToFolder = () => { @@ -86,7 +86,7 @@ const SideBarFoldersButtonsComponent = ({ addFolder({ name: "New Folder", parent_id: null, description: "" }).then( (res) => { refreshFolders(); - }, + } ); } @@ -132,7 +132,7 @@ const SideBarFoldersButtonsComponent = ({ <> {folders.map((item, index) => { const editFolderName = editFolders?.filter( - (folder) => folder.name === item.name, + (folder) => folder.name === item.name )[0]; return (
          handleChangeFolder!(item.id!)} > @@ -218,7 +218,7 @@ const SideBarFoldersButtonsComponent = ({ folders.map((obj) => ({ name: obj.name, edit: false, - })), + })) ); } if (e.key === "Enter") { @@ -251,10 +251,10 @@ const SideBarFoldersButtonsComponent = ({ }; const updatedFolder = await updateFolder( body, - item.id!, + item.id! ); const updateFolders = folders.filter( - (f) => f.name !== item.name, + (f) => f.name !== item.name ); setFolders([...updateFolders, updatedFolder]); setFoldersNames({}); @@ -262,7 +262,7 @@ const SideBarFoldersButtonsComponent = ({ folders.map((obj) => ({ name: obj.name, edit: false, - })), + })) ); } else { setFoldersNames((old) => ({ diff --git a/src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx b/src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx index c75bf4bec..ab4b9d27f 100644 --- a/src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx +++ b/src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx @@ -12,7 +12,7 @@ import { addVersionToDuplicates } from "../../../utils/reactflowUtils"; const useFileDrop = (folderId, folderChangeCallback) => { const setFolderDragging = useFolderStore((state) => state.setFolderDragging); const setFolderIdDragging = useFolderStore( - (state) => state.setFolderIdDragging, + (state) => state.setFolderIdDragging ); const setErrorData = useAlertStore((state) => state.setErrorData); @@ -45,7 +45,7 @@ const useFileDrop = (folderId, folderChangeCallback) => { | React.DragEvent | React.DragEvent | React.DragEvent, - folderId: string, + folderId: string ) => { e.preventDefault(); @@ -60,7 +60,7 @@ const useFileDrop = (folderId, folderChangeCallback) => { | React.DragEvent | React.DragEvent | React.DragEvent, - folderId: string, + folderId: string ) => { if (e.dataTransfer.types.some((types) => types === "Files")) { setFolderDragging(true); @@ -73,7 +73,7 @@ const useFileDrop = (folderId, folderChangeCallback) => { e: | React.DragEvent | React.DragEvent - | React.DragEvent, + | React.DragEvent ) => { e.preventDefault(); if (e.target === e.currentTarget) { @@ -87,7 +87,7 @@ const useFileDrop = (folderId, folderChangeCallback) => { | React.DragEvent | React.DragEvent | React.DragEvent, - folderId: string, + folderId: string ) => { if (e?.dataTransfer?.getData("flow")) { const data = JSON.parse(e?.dataTransfer?.getData("flow")); diff --git a/src/frontend/src/components/tableComponent/index.tsx b/src/frontend/src/components/tableComponent/index.tsx index e956d8795..954d7d257 100644 --- a/src/frontend/src/components/tableComponent/index.tsx +++ b/src/frontend/src/components/tableComponent/index.tsx @@ -35,7 +35,7 @@ const TableComponent = forwardRef< alertDescription = DEFAULT_TABLE_ALERT_MSG, ...props }, - ref, + ref ) => { let colDef = props.columnDefs.map((col, index) => { let newCol = { @@ -111,7 +111,7 @@ const TableComponent = forwardRef< }; const onColumnMoved = (params) => { const updatedColumnDefs = makeLastColumnNonResizable( - params.columnApi.getAllGridColumns().map((col) => col.getColDef()), + params.columnApi.getAllGridColumns().map((col) => col.getColDef()) ); params.api.setGridOption("columnDefs", updatedColumnDefs); if (props.onColumnMoved) props.onColumnMoved(params); @@ -135,7 +135,7 @@ const TableComponent = forwardRef< className={cn( dark ? "ag-theme-quartz-dark" : "ag-theme-quartz", "ag-theme-shadcn flex h-full flex-col", - "relative", + "relative" )} // applying the grid theme > source.includes("column"))) { localStorage.setItem( storeReference, - JSON.stringify(realRef.current?.api?.getColumnState()), + JSON.stringify(realRef.current?.api?.getColumnState()) ); setColumnStateChange(true); } @@ -175,7 +175,7 @@ const TableComponent = forwardRef< )}
          ); - }, + } ); export default TableComponent; diff --git a/src/frontend/src/contexts/authContext.tsx b/src/frontend/src/contexts/authContext.tsx index bd5c2d02c..f6b81a235 100644 --- a/src/frontend/src/contexts/authContext.tsx +++ b/src/frontend/src/contexts/authContext.tsx @@ -31,17 +31,17 @@ export function AuthProvider({ children }): React.ReactElement { const navigate = useNavigate(); const cookies = new Cookies(); const [accessToken, setAccessToken] = useState( - cookies.get("access_token_lf") ?? null, + cookies.get("access_token_lf") ?? null ); const [isAuthenticated, setIsAuthenticated] = useState( - !!cookies.get("access_token_lf"), + !!cookies.get("access_token_lf") ); const [isAdmin, setIsAdmin] = useState(false); const [userData, setUserData] = useState(null); const [autoLogin, setAutoLogin] = useState(false); const setLoading = useAlertStore((state) => state.setLoading); const [apiKey, setApiKey] = useState( - cookies.get("apikey_tkn_lflw"), + cookies.get("apikey_tkn_lflw") ); const getFoldersApi = useFolderStore((state) => state.getFoldersApi); diff --git a/src/frontend/src/controllers/API/api.tsx b/src/frontend/src/controllers/API/api.tsx index 0ac3efa6b..6bd73f5f1 100644 --- a/src/frontend/src/controllers/API/api.tsx +++ b/src/frontend/src/controllers/API/api.tsx @@ -48,7 +48,7 @@ function ApiInterceptor() { } await clearBuildVerticesState(error); return Promise.reject(error); - }, + } ); const isAuthorizedURL = (url) => { @@ -65,10 +65,10 @@ function ApiInterceptor() { const parsedURL = new URL(url); const isDomainAllowed = authorizedDomains.some( - (domain) => parsedURL.origin === new URL(domain).origin, + (domain) => parsedURL.origin === new URL(domain).origin ); const isEndpointAllowed = authorizedEndpoints.some((endpoint) => - parsedURL.pathname.includes(endpoint), + parsedURL.pathname.includes(endpoint) ); return isDomainAllowed || isEndpointAllowed; @@ -102,7 +102,7 @@ function ApiInterceptor() { }, (error) => { return Promise.reject(error); - }, + } ); return () => { @@ -134,7 +134,7 @@ function ApiInterceptor() { if (error?.config?.headers) { delete error.config.headers["Authorization"]; error.config.headers["Authorization"] = `Bearer ${cookies.get( - "access_token_lf", + "access_token_lf" )}`; const response = await axios.request(error.config); return response; diff --git a/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts b/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts index 85c6608ac..ec3c74268 100644 --- a/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts +++ b/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts @@ -9,7 +9,7 @@ export function checkDuplicateRequestAndStoreRequest(config) { const currentUrl = window.location.pathname; const currentTime = Date.now(); const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) => - config?.url!.includes(request), + config?.url!.includes(request) ); if ( diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 104b815b0..481aa2135 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -63,7 +63,7 @@ export async function sendAll(data: sendAllProps) { } export async function postValidateCode( - code: string, + code: string ): Promise> { return await api.post(`${BASE_URL_API}validate/code`, { code }); } @@ -78,7 +78,7 @@ export async function postValidateCode( export async function postValidatePrompt( name: string, template: string, - frontend_node: APIClassType, + frontend_node: APIClassType ): Promise> { return api.post(`${BASE_URL_API}validate/prompt`, { name, @@ -151,7 +151,7 @@ export async function saveFlowToDatabase(newFlow: { * @throws Will throw an error if the update fails. */ export async function updateFlowInDatabase( - updatedFlow: FlowType, + updatedFlow: FlowType ): Promise { try { const response = await api.patch(`${BASE_URL_API}flows/${updatedFlow.id}`, { @@ -329,7 +329,7 @@ export async function getHealth() { * */ export async function getBuildStatus( - flowId: string, + flowId: string ): Promise> { return await api.get(`${BASE_URL_API}build/${flowId}/status`); } @@ -342,7 +342,7 @@ export async function getBuildStatus( * */ export async function postBuildInit( - flow: FlowType, + flow: FlowType ): Promise> { return await api.post(`${BASE_URL_API}build/init/${flow.id}`, flow); } @@ -358,7 +358,7 @@ export async function postBuildInit( */ export async function uploadFile( file: File, - id: string, + id: string ): Promise> { const formData = new FormData(); formData.append("file", file); @@ -380,7 +380,7 @@ export async function getProfilePictures(): Promise> { // let template = apiClass.template; return await api.post(`${BASE_URL_API}custom_component`, { @@ -393,7 +393,7 @@ export async function postCustomComponentUpdate( code: string, template: APITemplateType, field: string, - field_value: any, + field_value: any ): Promise> { return await api.post(`${BASE_URL_API}custom_component/update`, { code, @@ -415,7 +415,7 @@ export async function onLogin(user: LoginType) { headers: { "Content-Type": "application/x-www-form-urlencoded", }, - }, + } ); if (response.status === 200) { @@ -477,11 +477,11 @@ export async function addUser(user: UserInputType): Promise> { export async function getUsersPage( skip: number, - limit: number, + limit: number ): Promise> { try { const res = await api.get( - `${BASE_URL_API}users/?skip=${skip}&limit=${limit}`, + `${BASE_URL_API}users/?skip=${skip}&limit=${limit}` ); if (res.status === 200) { return res.data; @@ -518,7 +518,7 @@ export async function resetPassword(user_id: string, user: resetPasswordType) { try { const res = await api.patch( `${BASE_URL_API}users/${user_id}/reset-password`, - user, + user ); if (res.status === 200) { return res.data; @@ -592,7 +592,7 @@ export async function saveFlowStore( last_tested_version?: string; }, tags: string[], - publicFlow = false, + publicFlow = false ): Promise { try { const response = await api.post(`${BASE_URL_API}store/components/`, { @@ -721,7 +721,7 @@ export async function postStoreComponents(component: Component) { export async function getComponent(component_id: string) { try { const res = await api.get( - `${BASE_URL_API}store/components/${component_id}`, + `${BASE_URL_API}store/components/${component_id}` ); if (res.status === 200) { return res.data; @@ -736,7 +736,7 @@ export async function searchComponent( page?: number | null, limit?: number | null, status?: string | null, - tags?: string[], + tags?: string[] ): Promise { try { let url = `${BASE_URL_API}store/components/`; @@ -848,7 +848,7 @@ export async function updateFlowStore( }, tags: string[], publicFlow = false, - id: string, + id: string ): Promise { try { const response = await api.patch(`${BASE_URL_API}store/components/${id}`, { @@ -932,7 +932,7 @@ export async function deleteGlobalVariable(id: string) { export async function updateGlobalVariable( name: string, value: string, - id: string, + id: string ) { try { const response = api.patch(`${BASE_URL_API}variables/${id}`, { @@ -951,7 +951,7 @@ export async function getVerticesOrder( startNodeId?: string | null, stopNodeId?: string | null, nodes?: Node[], - Edges?: Edge[], + Edges?: Edge[] ): Promise> { // nodeId is optional and is a query parameter // if nodeId is not provided, the API will return all vertices @@ -971,7 +971,7 @@ export async function getVerticesOrder( return await api.post( `${BASE_URL_API}build/${flowId}/vertices`, data, - config, + config ); } @@ -979,7 +979,7 @@ export async function postBuildVertex( flowId: string, vertexId: string, input_value: string, - files?: string[], + files?: string[] ): Promise> { // input_value is optional and is a query parameter let data = {}; @@ -991,7 +991,7 @@ export async function postBuildVertex( } return await api.post( `${BASE_URL_API}build/${flowId}/vertices/${vertexId}`, - data, + data ); } @@ -1015,7 +1015,7 @@ export async function getFlowPool({ } export async function deleteFlowPool( - flowId: string, + flowId: string ): Promise> { const config = {}; config["params"] = { flow_id: flowId }; @@ -1029,7 +1029,7 @@ export async function deleteFlowPool( * @returns A promise that resolves to an array of AxiosResponse objects representing the delete responses. */ export async function multipleDeleteFlowsComponents( - flowIds: string[], + flowIds: string[] ): Promise[]> { const batches: string[][] = []; @@ -1052,7 +1052,7 @@ export async function multipleDeleteFlowsComponents( // Execute all delete requests const responses: Promise>[] = batches.map((batch) => - deleteBatch(batch), + deleteBatch(batch) ); // Return the responses after all requests are completed @@ -1062,7 +1062,7 @@ export async function multipleDeleteFlowsComponents( export async function getTransactionTable( id: string, mode: "intersection" | "union", - params = {}, + params = {} ): Promise<{ rows: Array; columns: Array }> { const config = {}; config["params"] = { flow_id: id }; @@ -1078,7 +1078,7 @@ export async function getMessagesTable( mode: "intersection" | "union", id?: string, excludedFields?: string[], - params = {}, + params = {} ): Promise<{ rows: Array; columns: Array }> { const config = {}; if (id) { diff --git a/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx b/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx index c6ceb44c7..b023216ab 100644 --- a/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx +++ b/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx @@ -11,7 +11,7 @@ const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => { const [inputValue, setInputValue] = useState(""); const setSearchFlowsComponents = useFlowsManagerStore( - (state) => state.setSearchFlowsComponents, + (state) => state.setSearchFlowsComponents ); return ( diff --git a/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx b/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx index 6b4892d60..39add5211 100644 --- a/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx +++ b/src/frontend/src/pages/MainPage/pages/mainPage/index.tsx @@ -16,7 +16,7 @@ import useDropdownOptions from "../../hooks/use-dropdown-options"; export default function HomePage(): JSX.Element { const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId, + (state) => state.setCurrentFlowId ); const location = useLocation(); diff --git a/src/frontend/src/stores/foldersStore.tsx b/src/frontend/src/stores/foldersStore.tsx index 01db44247..8e42026b4 100644 --- a/src/frontend/src/stores/foldersStore.tsx +++ b/src/frontend/src/stores/foldersStore.tsx @@ -18,18 +18,18 @@ export const useFolderStore = create((set, get) => ({ getFolders().then( async (res) => { const foldersWithoutStarterProjects = res?.filter( - (folder) => folder.name !== STARTER_FOLDER_NAME, + (folder) => folder.name !== STARTER_FOLDER_NAME ); const starterProjects = res?.find( - (folder) => folder.name === STARTER_FOLDER_NAME, + (folder) => folder.name === STARTER_FOLDER_NAME ); set({ starterProjectId: starterProjects!.id ?? "" }); set({ folders: foldersWithoutStarterProjects }); const myCollectionId = res?.find( - (f) => f.name === DEFAULT_FOLDER, + (f) => f.name === DEFAULT_FOLDER )?.id; set({ myCollectionId }); @@ -55,7 +55,7 @@ export const useFolderStore = create((set, get) => ({ set({ folders: [] }); get().setIsLoadingFolders(false); reject(error); - }, + } ); } }); @@ -65,18 +65,18 @@ export const useFolderStore = create((set, get) => ({ getFolders().then( async (res) => { const foldersWithoutStarterProjects = res?.filter( - (folder) => folder.name !== STARTER_FOLDER_NAME, + (folder) => folder.name !== STARTER_FOLDER_NAME ); const starterProjects = res?.find( - (folder) => folder.name === STARTER_FOLDER_NAME, + (folder) => folder.name === STARTER_FOLDER_NAME ); set({ starterProjectId: starterProjects!.id ?? "" }); set({ folders: foldersWithoutStarterProjects }); const myCollectionId = res?.find( - (f) => f.name === DEFAULT_FOLDER, + (f) => f.name === DEFAULT_FOLDER )?.id; set({ myCollectionId }); @@ -87,7 +87,7 @@ export const useFolderStore = create((set, get) => ({ set({ folders: [] }); get().setIsLoadingFolders(false); reject(error); - }, + } ); }); }, diff --git a/src/frontend/src/types/zustand/folders/index.ts b/src/frontend/src/types/zustand/folders/index.ts index 495d65b87..45c1e0bd9 100644 --- a/src/frontend/src/types/zustand/folders/index.ts +++ b/src/frontend/src/types/zustand/folders/index.ts @@ -4,7 +4,7 @@ export type FoldersStoreType = { folders: FolderType[]; getFoldersApi: ( refetch?: boolean, - startupApplication?: boolean, + startupApplication?: boolean ) => Promise; setFolders: (folders: FolderType[]) => void; isLoadingFolders: boolean; From 57a7b2baabd0fa39a8d45cfedfb604bf27ecde3d Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Fri, 14 Jun 2024 09:45:53 -0300 Subject: [PATCH 65/68] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(use-delete-folder.t?= =?UTF-8?q?sx):=20remove=20unused=20refreshFolders=20variable=20to=20clean?= =?UTF-8?q?=20up=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx b/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx index 1eb1c9c0c..0d093bc32 100644 --- a/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx +++ b/src/frontend/src/pages/MainPage/hooks/use-delete-folder.tsx @@ -7,7 +7,6 @@ const useDeleteFolder = ({ navigate }) => { const setErrorData = useAlertStore((state) => state.setErrorData); const folderToEdit = useFolderStore((state) => state.folderToEdit); const myCollectionId = useFolderStore((state) => state.myCollectionId); - const refreshFolders = useFolderStore((state) => state.refreshFolders); const getFoldersApi = useFolderStore((state) => state.getFoldersApi); const handleDeleteFolder = () => { From a9cb7b0f5ebfd8f0994ae0c93c1257216433164c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 14 Jun 2024 06:27:48 -0700 Subject: [PATCH 66/68] Update linting workflows for frontend and Python code (#2171) * chore: Update pre-commit hooks and dependencies * chore: Add linting workflow for frontend code * chore: Update linting workflow for Python code and include tests in pull requests * chore: Add Ruff style check workflow for Python code --- .github/workflows/lint-js.yml | 51 +++++++++++++++++++++ .github/workflows/{lint.yml => lint-py.yml} | 21 +++++---- .github/workflows/matchers/ruff.json | 14 ++++++ .github/workflows/style-check-py.yml | 39 ++++++++++++++++ .pre-commit-config.yaml | 26 ----------- 5 files changed, 115 insertions(+), 36 deletions(-) create mode 100644 .github/workflows/lint-js.yml rename .github/workflows/{lint.yml => lint-py.yml} (77%) create mode 100644 .github/workflows/matchers/ruff.json create mode 100644 .github/workflows/style-check-py.yml diff --git a/.github/workflows/lint-js.yml b/.github/workflows/lint-js.yml new file mode 100644 index 000000000..0590e8a38 --- /dev/null +++ b/.github/workflows/lint-js.yml @@ -0,0 +1,51 @@ +name: Lint Frontend + +on: + pull_request: + paths: + - "src/frontend/**" + +env: + NODE_VERSION: "21" + +jobs: + run-linters: + name: Run linters + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + id: setup-node + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Cache Node.js dependencies + uses: actions/cache@v4 + id: npm-cache + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('src/frontend/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install Node.js dependencies + run: | + cd src/frontend + npm ci + if: ${{ steps.setup-node.outputs.cache-hit != 'true' }} + + - name: Run linters + uses: wearerequired/lint-action@v1 + with: + github_token: ${{ secrets.github_token }} + auto_fix: true + git_email: "gabriel@langflow.org" + # Enable linters + eslint: true + prettier: true + prettier_args: '--write \"{tests,src}/**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore' + diff --git a/.github/workflows/lint.yml b/.github/workflows/lint-py.yml similarity index 77% rename from .github/workflows/lint.yml rename to .github/workflows/lint-py.yml index 3c5898369..5ab5541e3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint-py.yml @@ -1,17 +1,12 @@ -name: lint +name: Lint Python on: - push: - branches: [main] - paths: - - "poetry.lock" - - "pyproject.toml" - - "src/backend/**" pull_request: paths: - "poetry.lock" - "pyproject.toml" - "src/backend/**" + - "tests/**" env: POETRY_VERSION: "1.8.2" @@ -45,6 +40,12 @@ jobs: path: | ./.mypy_cache key: ${{ runner.os }}-mypy-${{ hashFiles('**/pyproject.toml') }} - - name: Lint check - run: | - make lint + - name: Run linters + uses: wearerequired/lint-action@v1 + with: + github_token: ${{ secrets.github_token }} + # Enable linters + git_email: "gabriel@langflow.org" + mypy: true + mypy_args: '--namespace-packages -p "langflow"' + diff --git a/.github/workflows/matchers/ruff.json b/.github/workflows/matchers/ruff.json new file mode 100644 index 000000000..53b21be23 --- /dev/null +++ b/.github/workflows/matchers/ruff.json @@ -0,0 +1,14 @@ +{ + "problemMatcher": [ + { + "owner": "ruff", + "pattern": [ + { + "regexp": "^(Would reformat): (.+)$", + "message": 1, + "file": 2 + } + ] + } + ] +} \ No newline at end of file diff --git a/.github/workflows/style-check-py.yml b/.github/workflows/style-check-py.yml new file mode 100644 index 000000000..4063cd48d --- /dev/null +++ b/.github/workflows/style-check-py.yml @@ -0,0 +1,39 @@ +name: Ruff Style Check + +on: + pull_request: + paths: + - "poetry.lock" + - "pyproject.toml" + - "src/backend/**" + - "tests/**" + +env: + POETRY_VERSION: "1.8.2" + +jobs: + lint: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: + - "3.12" + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }} + uses: "./.github/actions/poetry_caching" + with: + python-version: ${{ matrix.python-version }} + poetry-version: ${{ env.POETRY_VERSION }} + cache-key: ${{ runner.os }}-poetry-${{ env.POETRY_VERSION }}-${{ hashFiles('**/poetry.lock') }} + - name: Install Python dependencies + run: | + poetry env use ${{ matrix.python-version }} + poetry install + - name: Register problem matcher + run: echo "::add-matcher::.github/workflows/matchers/ruff.json" + - name: Run Ruff + run: poetry run ruff check --output-format=github . + - name: Run Ruff format + run: poetry run ruff format --check . + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d03df05b9..16599a199 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,5 @@ fail_fast: true repos: - - repo: https://github.com/pre-commit/mirrors-eslint - rev: "v9.1.1" - hooks: - - id: eslint - files: \.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx - types: [file] - args: ["--fix", "--no-warn-ignored"] - additional_dependencies: - - eslint@9.1.1 - - eslint-plugin-prettier - - eslint-config-prettier - - prettier - - eslint-plugin-react@latest - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 hooks: @@ -25,16 +12,3 @@ repos: args: - --fix=lf - id: trailing-whitespace - - repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. - rev: v0.4.2 - hooks: - # Run the linter. - - id: ruff - # Python - files: \.py$ - types: [file] - # Run the formatter. - - id: ruff-format - files: \.py$ - types: [file] From 244306961180f273f0be673ab016782a363bdb94 Mon Sep 17 00:00:00 2001 From: ogabrielluiz Date: Fri, 14 Jun 2024 12:33:18 -0300 Subject: [PATCH 67/68] chore: Update mypy command prefix to use "poetry run" --- .github/workflows/lint-py.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint-py.yml b/.github/workflows/lint-py.yml index 5ab5541e3..6182f91fc 100644 --- a/.github/workflows/lint-py.yml +++ b/.github/workflows/lint-py.yml @@ -48,4 +48,5 @@ jobs: git_email: "gabriel@langflow.org" mypy: true mypy_args: '--namespace-packages -p "langflow"' + mypy_command_prefix: "poetry run" From 71e8aa00cf33e84134352b535ed5841f4d1c1817 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Fri, 14 Jun 2024 13:23:03 -0300 Subject: [PATCH 68/68] =?UTF-8?q?=F0=9F=90=9B=20(utils.ts):=20add=20option?= =?UTF-8?q?al=20chaining=20to=20base=5Fclasses=20to=20prevent=20runtime=20?= =?UTF-8?q?errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/utils/utils.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/frontend/src/utils/utils.ts b/src/frontend/src/utils/utils.ts index 5a1f6621f..0a1f99692 100644 --- a/src/frontend/src/utils/utils.ts +++ b/src/frontend/src/utils/utils.ts @@ -56,7 +56,7 @@ export function normalCaseToSnakeCase(str: string): string { export function toTitleCase( str: string | undefined, - isNodeField?: boolean + isNodeField?: boolean, ): string { if (!str) return ""; let result = str @@ -65,7 +65,7 @@ export function toTitleCase( if (isNodeField) return word; if (index === 0) { return checkUpperWords( - word[0].toUpperCase() + word.slice(1).toLowerCase() + word[0].toUpperCase() + word.slice(1).toLowerCase(), ); } return checkUpperWords(word.toLowerCase()); @@ -78,7 +78,7 @@ export function toTitleCase( if (isNodeField) return word; if (index === 0) { return checkUpperWords( - word[0].toUpperCase() + word.slice(1).toLowerCase() + word[0].toUpperCase() + word.slice(1).toLowerCase(), ); } return checkUpperWords(word.toLowerCase()); @@ -182,7 +182,7 @@ export function checkLocalStorageKey(key: string): boolean { export function IncrementObjectKey( object: object, - key: string + key: string, ): { newKey: string; increment: number } { let count = 1; const type = removeCountFromString(key); @@ -217,7 +217,7 @@ export function groupByFamily( data: APIDataType, baseClasses: string, left: boolean, - flow?: NodeType[] + flow?: NodeType[], ): groupedObjType[] { const baseClassesSet = new Set(baseClasses.split("\n")); let arrOfPossibleInputs: Array<{ @@ -243,7 +243,7 @@ export function groupByFamily( baseClassesSet.has(template.type)) || (template?.input_types && template?.input_types.some((inputType) => - baseClassesSet.has(inputType) + baseClassesSet.has(inputType), ))) ); }; @@ -263,7 +263,7 @@ export function groupByFamily( hasBaseClassInBaseClasses: foundNode?.hasBaseClassInBaseClasses || nodeData.node!.base_classes.some((baseClass) => - baseClassesSet.has(baseClass) + baseClassesSet.has(baseClass), ), //seta como anterior ou verifica se o node tem base class displayName: nodeData.node?.display_name, }); @@ -280,10 +280,10 @@ export function groupByFamily( if (!foundNode) { foundNode = { hasBaseClassInTemplate: Object.values(node!.template).some( - checkBaseClass + checkBaseClass, ), - hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) => - baseClassesSet.has(baseClass) + hasBaseClassInBaseClasses: node!.base_classes?.some((baseClass) => + baseClassesSet.has(baseClass), ), displayName: node?.display_name, }; @@ -355,7 +355,7 @@ export function isTimeStampString(str: string): boolean { export function extractColumnsFromRows( rows: object[], mode: "intersection" | "union", - excludeColumns?: Array + excludeColumns?: Array, ): (ColDef | ColGroupDef)[] { let columnsKeys: { [key: string]: ColDef | ColGroupDef } = {}; if (rows.length === 0) {