Merge branch 'main' into dependabot/github_actions/install-pinned/ruff-b52a71f70b28264686d57d1efef1ba845b9cec6c
This commit is contained in:
commit
4a13afc13b
231 changed files with 6516 additions and 3370 deletions
14
.github/workflows/auto-update.yml
vendored
Normal file
14
.github/workflows/auto-update.yml
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
name: Auto-update
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- main
|
||||
|
||||
jobs:
|
||||
Auto:
|
||||
name: Auto-update
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tibdex/auto-update@v2
|
||||
6
.github/workflows/docker-build.yml
vendored
6
.github/workflows/docker-build.yml
vendored
|
|
@ -11,7 +11,7 @@ on:
|
|||
pre_release:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
default: false
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
|
|
@ -86,10 +86,10 @@ jobs:
|
|||
include:
|
||||
- component: backend
|
||||
dockerfile: ./docker/build_and_push_backend.Dockerfile
|
||||
tags: langflowai/langflow-backend:${{ inputs.version }},langflowai/langflow-backend:1.0-alpha
|
||||
tags: ${{ inputs.pre_release == 'true' && format('langflowai/langflow-backend:{0}', inputs.version) || format('langflowai/langflow-backend:{0},langflowai/langflow-backend:latest', inputs.version) }}
|
||||
- component: frontend
|
||||
dockerfile: ./docker/frontend/build_and_push_frontend.Dockerfile
|
||||
tags: langflowai/langflow-frontend:${{ inputs.version }},langflowai/langflow-frontend:1.0-alpha
|
||||
tags: ${{ inputs.pre_release == 'true' && format('langflowai/langflow-frontend:{0}', inputs.version) || format('langflowai/langflow-frontend:{0},langflowai/langflow-frontend:latest', inputs.version) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Docker Buildx
|
||||
|
|
|
|||
10
.github/workflows/lint-js.yml
vendored
10
.github/workflows/lint-js.yml
vendored
|
|
@ -5,7 +5,7 @@ on:
|
|||
paths:
|
||||
- "src/frontend/**"
|
||||
merge_group:
|
||||
branches: [dev]
|
||||
types: [checks_requested]
|
||||
|
||||
env:
|
||||
NODE_VERSION: "21"
|
||||
|
|
@ -45,10 +45,6 @@ jobs:
|
|||
- name: Run Prettier
|
||||
run: |
|
||||
cd src/frontend
|
||||
npm run format
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: Apply Prettier formatting
|
||||
branch: ${{ github.head_ref }}
|
||||
npm run check-format
|
||||
|
||||
|
||||
|
|
|
|||
2
.github/workflows/python_test.yml
vendored
2
.github/workflows/python_test.yml
vendored
|
|
@ -41,7 +41,7 @@ jobs:
|
|||
poetry run python -m langflow run --host 127.0.0.1 --port 7860 --backend-only &
|
||||
SERVER_PID=$!
|
||||
# Wait for the server to start
|
||||
timeout 120 bash -c 'until curl -f http://127.0.0.1:7860/auto_login; do sleep 2; done' || (echo "Server did not start in time" && kill $SERVER_PID && exit 1)
|
||||
timeout 120 bash -c 'until curl -f http://127.0.0.1:7860/api/v1/auto_login; do sleep 5; done' || (echo "Server did not start in time" && kill $SERVER_PID && exit 1)
|
||||
# Terminate the server
|
||||
kill $SERVER_PID || (echo "Failed to terminate the server" && exit 1)
|
||||
sleep 10 # give the server some time to terminate
|
||||
|
|
|
|||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
|
@ -58,7 +58,7 @@ jobs:
|
|||
python -m langflow run --host 127.0.0.1 --port 7860 &
|
||||
SERVER_PID=$!
|
||||
# Wait for the server to start
|
||||
timeout 120 bash -c 'until curl -f http://127.0.0.1:7860/auto_login; do sleep 2; done' || (echo "Server did not start in time" && kill $SERVER_PID && exit 1)
|
||||
timeout 120 bash -c 'until curl -f http://127.0.0.1:7860/api/v1/auto_login; do sleep 2; done' || (echo "Server did not start in time" && kill $SERVER_PID && exit 1)
|
||||
# Terminate the server
|
||||
kill $SERVER_PID || (echo "Failed to terminate the server" && exit 1)
|
||||
sleep 10 # give the server some time to terminate
|
||||
|
|
@ -120,7 +120,7 @@ jobs:
|
|||
python -m langflow run --host 127.0.0.1 --port 7860 &
|
||||
SERVER_PID=$!
|
||||
# Wait for the server to start
|
||||
timeout 120 bash -c 'until curl -f http://127.0.0.1:7860/auto_login; do sleep 2; done' || (echo "Server did not start in time" && kill $SERVER_PID && exit 1)
|
||||
timeout 120 bash -c 'until curl -f http://127.0.0.1:7860/api/v1/auto_login; do sleep 2; done' || (echo "Server did not start in time" && kill $SERVER_PID && exit 1)
|
||||
# Terminate the server
|
||||
kill $SERVER_PID || (echo "Failed to terminate the server" && exit 1)
|
||||
sleep 10 # give the server some time to terminate
|
||||
|
|
|
|||
35
.github/workflows/typescript_test.yml
vendored
35
.github/workflows/typescript_test.yml
vendored
|
|
@ -4,13 +4,14 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: "Branch to run tests on"
|
||||
required: true
|
||||
description: "(Optional) Branch to checkout"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "1.8.2"
|
||||
POETRY_VERSION: "1.8.3"
|
||||
NODE_VERSION: "21"
|
||||
PYTHON_VERSION: "3.12"
|
||||
# Define the directory where Playwright browsers will be installed.
|
||||
|
|
@ -19,18 +20,23 @@ env:
|
|||
|
||||
jobs:
|
||||
setup-and-test:
|
||||
name: Run Frontend Tests on branch ${{ inputs.branch }}
|
||||
name: Run Playwright Tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shardIndex: [1, 2, 3, 4]
|
||||
shardTotal: [4]
|
||||
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
shardTotal: [10]
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
STORE_API_KEY: ${{ secrets.STORE_API_KEY }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.branch }}
|
||||
# If branch is passed as input, checkout that branch
|
||||
# else checkout the default branch
|
||||
ref: ${{ github.event.inputs.branch || github.ref }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
|
@ -52,14 +58,15 @@ jobs:
|
|||
cd src/frontend
|
||||
npm ci
|
||||
if: ${{ steps.setup-node.outputs.cache-hit != 'true' }}
|
||||
|
||||
- name: Cache playwright binaries
|
||||
uses: actions/cache@v4
|
||||
- name: Get Playwright version
|
||||
run: echo "PLAYWRIGHT_VERSION=$(jq '.devDependencies["@playwright/test"]' src/frontend/package.json -r)" >> $GITHUB_ENV
|
||||
- name: Cache Playwright binaries
|
||||
id: playwright-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright
|
||||
key: ${{ runner.os }}-playwright-${{ hashFiles('src/frontend/package-lock.json') }}
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-browsers-${{ runner.os }}-${{ env.PLAYWRIGHT_VERSION }}
|
||||
|
||||
- name: Install Frontend dependencies
|
||||
run: |
|
||||
cd src/frontend
|
||||
|
|
|
|||
53
Makefile
53
Makefile
|
|
@ -47,20 +47,22 @@ init:
|
|||
|
||||
|
||||
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
|
||||
@poetry run coverage run
|
||||
@poetry run coverage erase
|
||||
|
||||
|
||||
# allow passing arguments to pytest
|
||||
unit_tests:
|
||||
poetry run pytest --ignore=tests/integration --instafail -ra -n auto -m "not api_key_required" $(args)
|
||||
poetry run pytest \
|
||||
--ignore=tests/integration \
|
||||
--instafail -ra -n auto -m "not api_key_required" \
|
||||
$(args)
|
||||
|
||||
|
||||
integration_tests:
|
||||
poetry run pytest tests/integration --instafail -ra -n auto $(args)
|
||||
poetry run pytest tests/integration \
|
||||
--instafail -ra -n auto \
|
||||
$(args)
|
||||
|
||||
format: ## run code formatters
|
||||
poetry run ruff check . --fix
|
||||
|
|
@ -129,9 +131,20 @@ start:
|
|||
@echo 'Running the CLI'
|
||||
|
||||
ifeq ($(open_browser),false)
|
||||
@make install_backend && poetry run langflow run --path $(path) --log-level $(log_level) --host $(host) --port $(port) --env-file $(env) --no-open-browser
|
||||
@make install_backend && poetry run langflow run \
|
||||
--path $(path) \
|
||||
--log-level $(log_level) \
|
||||
--host $(host) \
|
||||
--port $(port) \
|
||||
--env-file $(env) \
|
||||
--no-open-browser
|
||||
else
|
||||
@make install_backend && poetry run langflow run --path $(path) --log-level $(log_level) --host $(host) --port $(port) --env-file $(env)
|
||||
@make install_backend && poetry run langflow run \
|
||||
--path $(path) \
|
||||
--log-level $(log_level) \
|
||||
--host $(host) \
|
||||
--port $(port) \
|
||||
--env-file $(env)
|
||||
endif
|
||||
|
||||
|
||||
|
|
@ -166,13 +179,27 @@ backend: ## run the backend in development mode
|
|||
@echo 'Setting up the environment'
|
||||
@make setup_env
|
||||
make install_backend
|
||||
@-kill -9 $(lsof -t -i:7860)
|
||||
@-kill -9 $$(lsof -t -i:7860)
|
||||
ifdef login
|
||||
@echo "Running backend autologin is $(login)";
|
||||
LANGFLOW_AUTO_LOGIN=$(login) poetry run uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --env-file .env --loop asyncio --workers $(workers)
|
||||
LANGFLOW_AUTO_LOGIN=$(login) poetry run uvicorn \
|
||||
--factory langflow.main:create_app \
|
||||
--host 0.0.0.0 \
|
||||
--port $(port) \
|
||||
--reload \
|
||||
--env-file $(env) \
|
||||
--loop asyncio \
|
||||
--workers $(workers)
|
||||
else
|
||||
@echo "Running backend respecting the .env file";
|
||||
poetry run uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --env-file .env --loop asyncio --workers $(workers)
|
||||
@echo "Running backend respecting the $(env) file";
|
||||
poetry run uvicorn \
|
||||
--factory langflow.main:create_app \
|
||||
--host 0.0.0.0 \
|
||||
--port $(port) \
|
||||
--reload \
|
||||
--env-file $(env) \
|
||||
--loop asyncio \
|
||||
--workers $(workers)
|
||||
endif
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -93,5 +93,7 @@ ENV PATH="/app/.venv/bin:${PATH}"
|
|||
USER user
|
||||
WORKDIR /app
|
||||
|
||||
ENTRYPOINT ["python", "-m", "langflow", "run"]
|
||||
CMD ["--host", "0.0.0.0", "--port", "7860"]
|
||||
ENV LANGFLOW_HOST=0.0.0.0
|
||||
ENV LANGFLOW_PORT=7860
|
||||
|
||||
ENTRYPOINT ["python", "-m", "langflow", "run"]
|
||||
|
|
@ -5,4 +5,4 @@ ARG LANGFLOW_IMAGE
|
|||
FROM $LANGFLOW_IMAGE
|
||||
|
||||
RUN rm -rf /app/.venv/langflow/frontend
|
||||
CMD ["--host", "0.0.0.0", "--port", "7860", "--backend-only"]
|
||||
CMD ["--backend-only"]
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ USER user
|
|||
# Install the package from the .tar.gz
|
||||
RUN python -m pip install /app/src/backend/base/dist/*.tar.gz --user
|
||||
|
||||
ENV LANGFLOW_HOST=0.0.0.0
|
||||
ENV LANGFLOW_PORT=7860
|
||||
|
||||
ENTRYPOINT ["python", "-m", "langflow", "run"]
|
||||
CMD ["--host", "0.0.0.0", "--port", "7860"]
|
||||
|
|
|
|||
|
|
@ -1,15 +1,3 @@
|
|||
FROM python:3.10-slim
|
||||
FROM langflowai/langflow:latest
|
||||
|
||||
RUN apt-get update && apt-get install gcc g++ git make -y && apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN useradd -m -u 1000 user
|
||||
USER user
|
||||
ENV HOME=/home/user \
|
||||
PATH=/home/user/.local/bin:$PATH
|
||||
|
||||
WORKDIR $HOME/app
|
||||
|
||||
COPY --chown=user . $HOME/app
|
||||
|
||||
RUN pip install langflow>==0.5.0 -U --user
|
||||
CMD ["python", "-m", "langflow", "run", "--host", "0.0.0.0", "--port", "7860"]
|
||||
ENTRYPOINT ["python", "-m", "langflow", "run"]
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ services:
|
|||
environment:
|
||||
- LANGFLOW_DATABASE_URL=postgresql://langflow:langflow@postgres:5432/langflow
|
||||
# This variable defines where the logs, file storage, monitor data and secret keys are stored.
|
||||
- LANGFLOW_CONFIG_DIR=/var/lib/langflow
|
||||
- LANGFLOW_CONFIG_DIR=/app/langflow
|
||||
volumes:
|
||||
- langflow-data:/var/lib/langflow
|
||||
- langflow-data:/app/langflow
|
||||
|
||||
postgres:
|
||||
image: postgres:16
|
||||
|
|
|
|||
281
poetry.lock
generated
281
poetry.lock
generated
|
|
@ -123,13 +123,13 @@ frozenlist = ">=1.1.0"
|
|||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.13.1"
|
||||
version = "1.13.2"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"},
|
||||
{file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"},
|
||||
{file = "alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953"},
|
||||
{file = "alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -167,13 +167,13 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "anthropic"
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
description = "The official Python library for the anthropic API"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "anthropic-0.29.0-py3-none-any.whl", hash = "sha256:d16010715129c8bc3295b74fbf4da73cfb156618bf0abb2d007255983266b76a"},
|
||||
{file = "anthropic-0.29.0.tar.gz", hash = "sha256:3eb558a232d83bdf7cdedb75663bf7ff7a8b50cc10acaa9ce6494ff295b8506a"},
|
||||
{file = "anthropic-0.30.0-py3-none-any.whl", hash = "sha256:061bf58c9c64968361e6c21c76ff5016a6f7fdd9a5f6b7f2280ede2c3b44bfd5"},
|
||||
{file = "anthropic-0.30.0.tar.gz", hash = "sha256:9e9ee2bfce833370eac74d7de433db97a0bf141f9118c40ac0e2f4c39bc2b76f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -461,17 +461,17 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "boto3"
|
||||
version = "1.34.132"
|
||||
version = "1.34.133"
|
||||
description = "The AWS SDK for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "boto3-1.34.132-py3-none-any.whl", hash = "sha256:b5d1681a0d8bf255787c8b37f911d706672d5722c9ace5342cd283a3cdb04820"},
|
||||
{file = "boto3-1.34.132.tar.gz", hash = "sha256:3b2964060620f1bbe9574b5f8d3fb2a4e087faacfc6023c24154b184f1b16443"},
|
||||
{file = "boto3-1.34.133-py3-none-any.whl", hash = "sha256:da7e78c03270be872ad78301892396ffea56647efcb2c3a8621ef46a905541ab"},
|
||||
{file = "boto3-1.34.133.tar.gz", hash = "sha256:7071f8ce1f09113ca5630860fd590464e6325a4df55faae83c956225941016fc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
botocore = ">=1.34.132,<1.35.0"
|
||||
botocore = ">=1.34.133,<1.35.0"
|
||||
jmespath = ">=0.7.1,<2.0.0"
|
||||
s3transfer = ">=0.10.0,<0.11.0"
|
||||
|
||||
|
|
@ -480,13 +480,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
|
|||
|
||||
[[package]]
|
||||
name = "botocore"
|
||||
version = "1.34.132"
|
||||
version = "1.34.133"
|
||||
description = "Low-level, data-driven core of boto 3."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "botocore-1.34.132-py3-none-any.whl", hash = "sha256:06ef8b4bd3b3cb5a9b9a4273a543b257be3304030978ba51516b576a65156c39"},
|
||||
{file = "botocore-1.34.132.tar.gz", hash = "sha256:372a6cfce29e5de9bcf8c95af901d0bc3e27d8aa2295fadee295424f95f43f16"},
|
||||
{file = "botocore-1.34.133-py3-none-any.whl", hash = "sha256:f269dad8e17432d2527b97ed9f1fd30ec8dc705f8b818957170d1af484680ef2"},
|
||||
{file = "botocore-1.34.133.tar.gz", hash = "sha256:5ea609aa4831a6589e32eef052a359ad8d7311733b4d86a9d35dab4bd3ec80ff"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1501,33 +1501,33 @@ vision = ["Pillow (>=6.2.1)"]
|
|||
|
||||
[[package]]
|
||||
name = "debugpy"
|
||||
version = "1.8.1"
|
||||
version = "1.8.2"
|
||||
description = "An implementation of the Debug Adapter Protocol for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"},
|
||||
{file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"},
|
||||
{file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"},
|
||||
{file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"},
|
||||
{file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"},
|
||||
{file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"},
|
||||
{file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"},
|
||||
{file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"},
|
||||
{file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"},
|
||||
{file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"},
|
||||
{file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"},
|
||||
{file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"},
|
||||
{file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"},
|
||||
{file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"},
|
||||
{file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"},
|
||||
{file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"},
|
||||
{file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"},
|
||||
{file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"},
|
||||
{file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"},
|
||||
{file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"},
|
||||
{file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"},
|
||||
{file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"},
|
||||
{file = "debugpy-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7ee2e1afbf44b138c005e4380097d92532e1001580853a7cb40ed84e0ef1c3d2"},
|
||||
{file = "debugpy-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f8c3f7c53130a070f0fc845a0f2cee8ed88d220d6b04595897b66605df1edd6"},
|
||||
{file = "debugpy-1.8.2-cp310-cp310-win32.whl", hash = "sha256:f179af1e1bd4c88b0b9f0fa153569b24f6b6f3de33f94703336363ae62f4bf47"},
|
||||
{file = "debugpy-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:0600faef1d0b8d0e85c816b8bb0cb90ed94fc611f308d5fde28cb8b3d2ff0fe3"},
|
||||
{file = "debugpy-1.8.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8a13417ccd5978a642e91fb79b871baded925d4fadd4dfafec1928196292aa0a"},
|
||||
{file = "debugpy-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdf39855f65c48ac9667b2801234fc64d46778021efac2de7e50907ab90c634"},
|
||||
{file = "debugpy-1.8.2-cp311-cp311-win32.whl", hash = "sha256:2cbd4d9a2fc5e7f583ff9bf11f3b7d78dfda8401e8bb6856ad1ed190be4281ad"},
|
||||
{file = "debugpy-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:d3408fddd76414034c02880e891ea434e9a9cf3a69842098ef92f6e809d09afa"},
|
||||
{file = "debugpy-1.8.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:5d3ccd39e4021f2eb86b8d748a96c766058b39443c1f18b2dc52c10ac2757835"},
|
||||
{file = "debugpy-1.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62658aefe289598680193ff655ff3940e2a601765259b123dc7f89c0239b8cd3"},
|
||||
{file = "debugpy-1.8.2-cp312-cp312-win32.whl", hash = "sha256:bd11fe35d6fd3431f1546d94121322c0ac572e1bfb1f6be0e9b8655fb4ea941e"},
|
||||
{file = "debugpy-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:15bc2f4b0f5e99bf86c162c91a74c0631dbd9cef3c6a1d1329c946586255e859"},
|
||||
{file = "debugpy-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:5a019d4574afedc6ead1daa22736c530712465c0c4cd44f820d803d937531b2d"},
|
||||
{file = "debugpy-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40f062d6877d2e45b112c0bbade9a17aac507445fd638922b1a5434df34aed02"},
|
||||
{file = "debugpy-1.8.2-cp38-cp38-win32.whl", hash = "sha256:c78ba1680f1015c0ca7115671fe347b28b446081dada3fedf54138f44e4ba031"},
|
||||
{file = "debugpy-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf327316ae0c0e7dd81eb92d24ba8b5e88bb4d1b585b5c0d32929274a66a5210"},
|
||||
{file = "debugpy-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1523bc551e28e15147815d1397afc150ac99dbd3a8e64641d53425dba57b0ff9"},
|
||||
{file = "debugpy-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e24ccb0cd6f8bfaec68d577cb49e9c680621c336f347479b3fce060ba7c09ec1"},
|
||||
{file = "debugpy-1.8.2-cp39-cp39-win32.whl", hash = "sha256:7f8d57a98c5a486c5c7824bc0b9f2f11189d08d73635c326abef268f83950326"},
|
||||
{file = "debugpy-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:16c8dcab02617b75697a0a925a62943e26a0330da076e2a10437edd9f0bf3755"},
|
||||
{file = "debugpy-1.8.2-py2.py3-none-any.whl", hash = "sha256:16e16df3a98a35c63c3ab1e4d19be4cbc7fdda92d9ddc059294f18910928e0ca"},
|
||||
{file = "debugpy-1.8.2.zip", hash = "sha256:95378ed08ed2089221896b9b3a8d021e642c24edc8fef20e5d4342ca8be65c00"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4122,19 +4122,19 @@ adal = ["adal (>=1.0.2)"]
|
|||
|
||||
[[package]]
|
||||
name = "langchain"
|
||||
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-0.2.5-py3-none-any.whl", hash = "sha256:9aded9a65348254e1c93dcdaacffe4d1b6a5e7f74ef80c160c88ff78ad299228"},
|
||||
{file = "langchain-0.2.5.tar.gz", hash = "sha256:ffdbf4fcea46a10d461bcbda2402220fcfd72a0c70e9f4161ae0510067b9b3bd"},
|
||||
{file = "langchain-0.2.6-py3-none-any.whl", hash = "sha256:f86e8a7afd3e56f8eb5ba47f01dd00144fb9fc2f1db9873bd197347be2857aa4"},
|
||||
{file = "langchain-0.2.6.tar.gz", hash = "sha256:867f6add370c1e3911b0e87d3dd0e36aec1e8f513bf06131340fe8f151d89dc5"},
|
||||
]
|
||||
|
||||
[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.7,<0.3.0"
|
||||
langchain-core = ">=0.2.10,<0.3.0"
|
||||
langchain-text-splitters = ">=0.2.0,<0.3.0"
|
||||
langsmith = ">=0.1.17,<0.2.0"
|
||||
numpy = [
|
||||
|
|
@ -4145,7 +4145,7 @@ pydantic = ">=1,<3"
|
|||
PyYAML = ">=5.3"
|
||||
requests = ">=2,<3"
|
||||
SQLAlchemy = ">=1.4,<3"
|
||||
tenacity = ">=8.1.0,<9.0.0"
|
||||
tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-anthropic"
|
||||
|
|
@ -4181,18 +4181,18 @@ numpy = ">=1,<2"
|
|||
|
||||
[[package]]
|
||||
name = "langchain-aws"
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
description = "An integration package connecting AWS and LangChain"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_aws-0.1.7-py3-none-any.whl", hash = "sha256:413f88cbb120cc1d6ca0e9f6d72b89c1d930b78ce071fef5b03e1595fc4d6029"},
|
||||
{file = "langchain_aws-0.1.7.tar.gz", hash = "sha256:aa0bbd3e530e21fdc1d0459e97ee14fa387ce9bb2d00d721cf526e9c3ecea78f"},
|
||||
{file = "langchain_aws-0.1.8-py3-none-any.whl", hash = "sha256:d1ade6d01af7d86f42c106bb32c08a99a38c84f54a3d669201362f42fd2684b8"},
|
||||
{file = "langchain_aws-0.1.8.tar.gz", hash = "sha256:d1e5edbda092ddbeda45ef8245a494b5b4f6bef79ed5afd56054c7d348dfed74"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
boto3 = ">=1.34.51,<1.35.0"
|
||||
langchain-core = ">=0.2.2,<0.3"
|
||||
boto3 = ">=1.34.127,<1.35.0"
|
||||
langchain-core = ">=0.2.6,<0.3"
|
||||
numpy = ">=1,<2"
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4229,20 +4229,20 @@ langchain-core = ">=0.2.0,<0.3"
|
|||
|
||||
[[package]]
|
||||
name = "langchain-community"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
description = "Community contributed LangChain integrations."
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_community-0.2.5-py3-none-any.whl", hash = "sha256:bf37a334952e42c7676d083cf2d2c4cbfbb7de1949c4149fe19913e2b06c485f"},
|
||||
{file = "langchain_community-0.2.5.tar.gz", hash = "sha256:476787b8c8c213b67e7b0eceb53346e787f00fbae12d8e680985bd4f93b0bf64"},
|
||||
{file = "langchain_community-0.2.6-py3-none-any.whl", hash = "sha256:758cc800acfe5dd396bf8ba1b57c4792639ead0eab48ed0367f0732ec6ee1f68"},
|
||||
{file = "langchain_community-0.2.6.tar.gz", hash = "sha256:40ce09a50ed798aa651ddb34c8978200fa8589b9813c7a28ce8af027bbf249f0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aiohttp = ">=3.8.3,<4.0.0"
|
||||
dataclasses-json = ">=0.5.7,<0.7"
|
||||
langchain = ">=0.2.5,<0.3.0"
|
||||
langchain-core = ">=0.2.7,<0.3.0"
|
||||
langchain = ">=0.2.6,<0.3.0"
|
||||
langchain-core = ">=0.2.10,<0.3.0"
|
||||
langsmith = ">=0.1.0,<0.2.0"
|
||||
numpy = [
|
||||
{version = ">=1,<2", markers = "python_version < \"3.12\""},
|
||||
|
|
@ -4251,17 +4251,17 @@ numpy = [
|
|||
PyYAML = ">=5.3"
|
||||
requests = ">=2,<3"
|
||||
SQLAlchemy = ">=1.4,<3"
|
||||
tenacity = ">=8.1.0,<9.0.0"
|
||||
tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.2.9"
|
||||
version = "0.2.10"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_core-0.2.9-py3-none-any.whl", hash = "sha256:426a5a4fea95a5db995ba5ab560b76edd4998fb6fe52ccc28ac987092a4cbfcd"},
|
||||
{file = "langchain_core-0.2.9.tar.gz", hash = "sha256:f1c59082642921727844e1cd0eb36d451edd1872c20e193aa3142aac03495986"},
|
||||
{file = "langchain_core-0.2.10-py3-none-any.whl", hash = "sha256:6eb72086b6bc86db9812da98f79e507c2209a15c0112aefd214a04182ada8586"},
|
||||
{file = "langchain_core-0.2.10.tar.gz", hash = "sha256:33d1fc234ab58c80476eb5bbde2107ef522a2ce8f46bdf47d9e1bd21e054208f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -4277,18 +4277,18 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
|
|||
|
||||
[[package]]
|
||||
name = "langchain-experimental"
|
||||
version = "0.0.61"
|
||||
version = "0.0.62"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_experimental-0.0.61-py3-none-any.whl", hash = "sha256:f9c516f528f55919743bd56fe1689a53bf74ae7f8902d64b9d8aebc61249cbe2"},
|
||||
{file = "langchain_experimental-0.0.61.tar.gz", hash = "sha256:e9538efb994be5db3045cc582cddb9787c8299c86ffeee9d3779b7f58eef2226"},
|
||||
{file = "langchain_experimental-0.0.62-py3-none-any.whl", hash = "sha256:9240f9e3490e819976f20a37863970036e7baacb7104b9eb6833d19ab6d518c9"},
|
||||
{file = "langchain_experimental-0.0.62.tar.gz", hash = "sha256:9737fbc8429d24457ea4d368e3c9ba9ed1cace0564fb5f1a96a3027a588bd0ac"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
langchain-community = ">=0.2.5,<0.3.0"
|
||||
langchain-core = ">=0.2.7,<0.3.0"
|
||||
langchain-community = ">=0.2.6,<0.3.0"
|
||||
langchain-core = ">=0.2.10,<0.3.0"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-google-genai"
|
||||
|
|
@ -4377,13 +4377,13 @@ pymongo = ">=4.6.1,<5.0"
|
|||
|
||||
[[package]]
|
||||
name = "langchain-openai"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
description = "An integration package connecting OpenAI and LangChain"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_openai-0.1.9-py3-none-any.whl", hash = "sha256:afae71ef315c967685e53fe061470438000946739a9492d5f2d53bd4ae9d495a"},
|
||||
{file = "langchain_openai-0.1.9.tar.gz", hash = "sha256:730a94d68208678b9b9f64e4959a87057e021d6600754ea8b954e7765c7a62f1"},
|
||||
{file = "langchain_openai-0.1.10-py3-none-any.whl", hash = "sha256:62eb000980eb45e4f16c88acdbaeccf3d59266554b0dd3ce6bebea1bbe8143dd"},
|
||||
{file = "langchain_openai-0.1.10.tar.gz", hash = "sha256:30f881f8ccaec28c054759837c41fd2a2264fcc5564728ce12e1715891a9ce3c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -4409,20 +4409,17 @@ pinecone-client = ">=3.2.2,<4.0.0"
|
|||
|
||||
[[package]]
|
||||
name = "langchain-text-splitters"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
description = "LangChain text splitting utilities"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_text_splitters-0.2.1-py3-none-any.whl", hash = "sha256:c2774a85f17189eaca50339629d2316d13130d4a8d9f1a1a96f3a03670c4a138"},
|
||||
{file = "langchain_text_splitters-0.2.1.tar.gz", hash = "sha256:06853d17d7241ecf5c97c7b6ef01f600f9b0fb953dd997838142a527a4f32ea4"},
|
||||
{file = "langchain_text_splitters-0.2.2-py3-none-any.whl", hash = "sha256:1c80d4b11b55e2995f02d2a326c0323ee1eeff24507329bb22924e420c782dff"},
|
||||
{file = "langchain_text_splitters-0.2.2.tar.gz", hash = "sha256:a1e45de10919fa6fb080ef0525deab56557e9552083600455cb9fa4238076140"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
langchain-core = ">=0.2.0,<0.3.0"
|
||||
|
||||
[package.extras]
|
||||
extended-testing = ["beautifulsoup4 (>=4.12.3,<5.0.0)", "lxml (>=4.9.3,<6.0)"]
|
||||
langchain-core = ">=0.2.10,<0.3.0"
|
||||
|
||||
[[package]]
|
||||
name = "langchainhub"
|
||||
|
|
@ -4456,7 +4453,7 @@ six = "*"
|
|||
|
||||
[[package]]
|
||||
name = "langflow-base"
|
||||
version = "0.0.80"
|
||||
version = "0.0.81"
|
||||
description = "A Python package with a built-in web application"
|
||||
optional = false
|
||||
python-versions = ">=3.10,<3.13"
|
||||
|
|
@ -4474,6 +4471,7 @@ docstring-parser = "^0.15"
|
|||
duckdb = "^1.0.0"
|
||||
emoji = "^2.12.0"
|
||||
fastapi = "^0.111.0"
|
||||
firecrawl-py = "^0.0.16"
|
||||
gunicorn = "^22.0.0"
|
||||
httpx = "*"
|
||||
jq = {version = "^1.7.0", markers = "sys_platform != \"win32\""}
|
||||
|
|
@ -4557,13 +4555,13 @@ requests = ">=2,<3"
|
|||
|
||||
[[package]]
|
||||
name = "litellm"
|
||||
version = "1.40.26"
|
||||
version = "1.40.27"
|
||||
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.26-py3-none-any.whl", hash = "sha256:5daedec00a3a8e32f55b3099190ccd9d550177f6e516823002831e6620ae771c"},
|
||||
{file = "litellm-1.40.26.tar.gz", hash = "sha256:4dfd4ca3eb50a62600e60303c4975ba9fe7c52d07882d0d2b6bad2d474d88758"},
|
||||
{file = "litellm-1.40.27-py3-none-any.whl", hash = "sha256:f6906e5260d784e7e31d579f5b28545e87517268cb96dd0dcaf31e4c5d34073f"},
|
||||
{file = "litellm-1.40.27.tar.gz", hash = "sha256:a13a04168be5a8e52d43c34c2e657ca2521da61039ac39a17abc233a1875923f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -5748,13 +5746,13 @@ sympy = "*"
|
|||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.35.3"
|
||||
version = "1.35.5"
|
||||
description = "The official Python library for the openai API"
|
||||
optional = false
|
||||
python-versions = ">=3.7.1"
|
||||
files = [
|
||||
{file = "openai-1.35.3-py3-none-any.whl", hash = "sha256:7b26544cef80f125431c073ffab3811d2421fbb9e30d3bd5c2436aba00b042d5"},
|
||||
{file = "openai-1.35.3.tar.gz", hash = "sha256:d6177087f150b381d49499be782d764213fdf638d391b29ca692b84dd675a389"},
|
||||
{file = "openai-1.35.5-py3-none-any.whl", hash = "sha256:28d92503c6e4b6a32a89277b36693023ef41f60922a4b5c8c621e8c5697ae3a6"},
|
||||
{file = "openai-1.35.5.tar.gz", hash = "sha256:67ef289ae22d350cbf9381d83ae82c4e3596d71b7ad1cc886143554ee12fe0c9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -7067,71 +7065,61 @@ windows-terminal = ["colorama (>=0.4.6)"]
|
|||
|
||||
[[package]]
|
||||
name = "pymongo"
|
||||
version = "4.7.3"
|
||||
version = "4.8.0"
|
||||
description = "Python driver for MongoDB <http://www.mongodb.org>"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pymongo-4.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e9580b4537b3cc5d412070caabd1dabdf73fdce249793598792bac5782ecf2eb"},
|
||||
{file = "pymongo-4.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:517243b2b189c98004570dd8fc0e89b1a48363d5578b3b99212fa2098b2ea4b8"},
|
||||
{file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23b1e9dabd61da1c7deb54d888f952f030e9e35046cebe89309b28223345b3d9"},
|
||||
{file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03e0f9901ad66c6fb7da0d303461377524d61dab93a4e4e5af44164c5bb4db76"},
|
||||
{file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a870824aa54453aee030bac08c77ebcf2fe8999400f0c2a065bebcbcd46b7f8"},
|
||||
{file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd7b3d3f4261bddbb74a332d87581bc523353e62bb9da4027cc7340f6fcbebc"},
|
||||
{file = "pymongo-4.7.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4d719a643ea6da46d215a3ba51dac805a773b611c641319558d8576cbe31cef8"},
|
||||
{file = "pymongo-4.7.3-cp310-cp310-win32.whl", hash = "sha256:d8b1e06f361f3c66ee694cb44326e1a2e4f93bc9c3a4849ae8547889fca71154"},
|
||||
{file = "pymongo-4.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:c450ab2f9397e2d5caa7fddeb4feb30bf719c47c13ae02c0bbb3b71bf4099c1c"},
|
||||
{file = "pymongo-4.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cc6459209e885ba097779eaa0fe7f2fa049db39ab43b1731cf8d065a4650e8"},
|
||||
{file = "pymongo-4.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6e2287f1e2cc35e73cd74a4867e398a97962c5578a3991c730ef78d276ca8e46"},
|
||||
{file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:413506bd48d8c31ee100645192171e4773550d7cb940b594d5175ac29e329ea1"},
|
||||
{file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cc1febf17646d52b7561caa762f60bdfe2cbdf3f3e70772f62eb624269f9c05"},
|
||||
{file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dfcf18a49955d50a16c92b39230bd0668ffc9c164ccdfe9d28805182b48fa72"},
|
||||
{file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89872041196c008caddf905eb59d3dc2d292ae6b0282f1138418e76f3abd3ad6"},
|
||||
{file = "pymongo-4.7.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3ed97b89de62ea927b672ad524de0d23f3a6b4a01c8d10e3d224abec973fbc3"},
|
||||
{file = "pymongo-4.7.3-cp311-cp311-win32.whl", hash = "sha256:d2f52b38151e946011d888a8441d3d75715c663fc5b41a7ade595e924e12a90a"},
|
||||
{file = "pymongo-4.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:4a4cc91c28e81c0ce03d3c278e399311b0af44665668a91828aec16527082676"},
|
||||
{file = "pymongo-4.7.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cb30c8a78f5ebaca98640943447b6a0afcb146f40b415757c9047bf4a40d07b4"},
|
||||
{file = "pymongo-4.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9cf2069f5d37c398186453589486ea98bb0312214c439f7d320593b61880dc05"},
|
||||
{file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3564f423958fced8a8c90940fd2f543c27adbcd6c7c6ed6715d847053f6200a0"},
|
||||
{file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a8af8a38fa6951fff73e6ff955a6188f829b29fed7c5a1b739a306b4aa56fe8"},
|
||||
{file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a0e81c8dba6d825272867d487f18764cfed3c736d71d7d4ff5b79642acbed42"},
|
||||
{file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88fc1d146feabac4385ea8ddb1323e584922922641303c8bf392fe1c36803463"},
|
||||
{file = "pymongo-4.7.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4225100b2c5d1f7393d7c5d256ceb8b20766830eecf869f8ae232776347625a6"},
|
||||
{file = "pymongo-4.7.3-cp312-cp312-win32.whl", hash = "sha256:5f3569ed119bf99c0f39ac9962fb5591eff02ca210fe80bb5178d7a1171c1b1e"},
|
||||
{file = "pymongo-4.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:eb383c54c0c8ba27e7712b954fcf2a0905fee82a929d277e2e94ad3a5ba3c7db"},
|
||||
{file = "pymongo-4.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a46cffe91912570151617d866a25d07b9539433a32231ca7e7cf809b6ba1745f"},
|
||||
{file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c3cba427dac50944c050c96d958c5e643c33a457acee03bae27c8990c5b9c16"},
|
||||
{file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a5fd893edbeb7fa982f8d44b6dd0186b6cd86c89e23f6ef95049ff72bffe46"},
|
||||
{file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c168a2fadc8b19071d0a9a4f85fe38f3029fe22163db04b4d5c046041c0b14bd"},
|
||||
{file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c59c2c9e70f63a7f18a31e367898248c39c068c639b0579623776f637e8f482"},
|
||||
{file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08165fd82c89d372e82904c3268bd8fe5de44f92a00e97bb1db1785154397d9"},
|
||||
{file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:397fed21afec4fdaecf72f9c4344b692e489756030a9c6d864393e00c7e80491"},
|
||||
{file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f903075f8625e2d228f1b9b9a0cf1385f1c41e93c03fd7536c91780a0fb2e98f"},
|
||||
{file = "pymongo-4.7.3-cp37-cp37m-win32.whl", hash = "sha256:8ed1132f58c38add6b6138b771d0477a3833023c015c455d9a6e26f367f9eb5c"},
|
||||
{file = "pymongo-4.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8d00a5d8fc1043a4f641cbb321da766699393f1b6f87c70fae8089d61c9c9c54"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9377b868c38700c7557aac1bc4baae29f47f1d279cc76b60436e547fd643318c"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da4a6a7b4f45329bb135aa5096823637bd5f760b44d6224f98190ee367b6b5dd"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:487e2f9277f8a63ac89335ec4f1699ae0d96ebd06d239480d69ed25473a71b2c"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db3d608d541a444c84f0bfc7bad80b0b897e0f4afa580a53f9a944065d9b633"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e90af2ad3a8a7c295f4d09a2fbcb9a350c76d6865f787c07fe843b79c6e821d1"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e28feb18dc559d50ededba27f9054c79f80c4edd70a826cecfe68f3266807b3"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f21ecddcba2d9132d5aebd8e959de8d318c29892d0718420447baf2b9bccbb19"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:26140fbb3f6a9a74bd73ed46d0b1f43d5702e87a6e453a31b24fad9c19df9358"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:94baa5fc7f7d22c3ce2ac7bd92f7e03ba7a6875f2480e3b97a400163d6eaafc9"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-win32.whl", hash = "sha256:92dd247727dd83d1903e495acc743ebd757f030177df289e3ba4ef8a8c561fad"},
|
||||
{file = "pymongo-4.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:1c90c848a5e45475731c35097f43026b88ef14a771dfd08f20b67adc160a3f79"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f598be401b416319a535c386ac84f51df38663f7a9d1071922bda4d491564422"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:35ba90477fae61c65def6e7d09e8040edfdd3b7fd47c3c258b4edded60c4d625"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9aa8735955c70892634d7e61b0ede9b1eefffd3cd09ccabee0ffcf1bdfe62254"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:82a97d8f7f138586d9d0a0cff804a045cdbbfcfc1cd6bba542b151e284fbbec5"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de3b9db558930efab5eaef4db46dcad8bf61ac3ddfd5751b3e5ac6084a25e366"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0e149217ef62812d3c2401cf0e2852b0c57fd155297ecc4dcd67172c4eca402"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3a8a1ef4a824f5feb793b3231526d0045eadb5eb01080e38435dfc40a26c3e5"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d14e5e89a4be1f10efc3d9dcb13eb7a3b2334599cb6bb5d06c6a9281b79c8e22"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c6bfa29f032fd4fd7b129520f8cdb51ab71d88c2ba0567cccd05d325f963acb5"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-win32.whl", hash = "sha256:1421d0bd2ce629405f5157bd1aaa9b83f12d53a207cf68a43334f4e4ee312b66"},
|
||||
{file = "pymongo-4.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:f7ee974f8b9370a998919c55b1050889f43815ab588890212023fecbc0402a6d"},
|
||||
{file = "pymongo-4.7.3.tar.gz", hash = "sha256:6354a66b228f2cd399be7429685fb68e07f19110a3679782ecb4fdb68da03831"},
|
||||
{file = "pymongo-4.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2b7bec27e047e84947fbd41c782f07c54c30c76d14f3b8bf0c89f7413fac67a"},
|
||||
{file = "pymongo-4.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c68fe128a171493018ca5c8020fc08675be130d012b7ab3efe9e22698c612a1"},
|
||||
{file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920d4f8f157a71b3cb3f39bc09ce070693d6e9648fb0e30d00e2657d1dca4e49"},
|
||||
{file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52b4108ac9469febba18cea50db972605cc43978bedaa9fea413378877560ef8"},
|
||||
{file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:180d5eb1dc28b62853e2f88017775c4500b07548ed28c0bd9c005c3d7bc52526"},
|
||||
{file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aec2b9088cdbceb87e6ca9c639d0ff9b9d083594dda5ca5d3c4f6774f4c81b33"},
|
||||
{file = "pymongo-4.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0cf61450feadca81deb1a1489cb1a3ae1e4266efd51adafecec0e503a8dcd84"},
|
||||
{file = "pymongo-4.8.0-cp310-cp310-win32.whl", hash = "sha256:8b18c8324809539c79bd6544d00e0607e98ff833ca21953df001510ca25915d1"},
|
||||
{file = "pymongo-4.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e5df28f74002e37bcbdfdc5109799f670e4dfef0fb527c391ff84f078050e7b5"},
|
||||
{file = "pymongo-4.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b50040d9767197b77ed420ada29b3bf18a638f9552d80f2da817b7c4a4c9c68"},
|
||||
{file = "pymongo-4.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:417369ce39af2b7c2a9c7152c1ed2393edfd1cbaf2a356ba31eb8bcbd5c98dd7"},
|
||||
{file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf821bd3befb993a6db17229a2c60c1550e957de02a6ff4dd0af9476637b2e4d"},
|
||||
{file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9365166aa801c63dff1a3cb96e650be270da06e3464ab106727223123405510f"},
|
||||
{file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc8b8582f4209c2459b04b049ac03c72c618e011d3caa5391ff86d1bda0cc486"},
|
||||
{file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e5019f75f6827bb5354b6fef8dfc9d6c7446894a27346e03134d290eb9e758"},
|
||||
{file = "pymongo-4.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b5802151fc2b51cd45492c80ed22b441d20090fb76d1fd53cd7760b340ff554"},
|
||||
{file = "pymongo-4.8.0-cp311-cp311-win32.whl", hash = "sha256:4bf58e6825b93da63e499d1a58de7de563c31e575908d4e24876234ccb910eba"},
|
||||
{file = "pymongo-4.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b747c0e257b9d3e6495a018309b9e0c93b7f0d65271d1d62e572747f4ffafc88"},
|
||||
{file = "pymongo-4.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e6a720a3d22b54183352dc65f08cd1547204d263e0651b213a0a2e577e838526"},
|
||||
{file = "pymongo-4.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31e4d21201bdf15064cf47ce7b74722d3e1aea2597c6785882244a3bb58c7eab"},
|
||||
{file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b804bb4f2d9dc389cc9e827d579fa327272cdb0629a99bfe5b83cb3e269ebf"},
|
||||
{file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2fbdb87fe5075c8beb17a5c16348a1ea3c8b282a5cb72d173330be2fecf22f5"},
|
||||
{file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd39455b7ee70aabee46f7399b32ab38b86b236c069ae559e22be6b46b2bbfc4"},
|
||||
{file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940d456774b17814bac5ea7fc28188c7a1338d4a233efbb6ba01de957bded2e8"},
|
||||
{file = "pymongo-4.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:236bbd7d0aef62e64caf4b24ca200f8c8670d1a6f5ea828c39eccdae423bc2b2"},
|
||||
{file = "pymongo-4.8.0-cp312-cp312-win32.whl", hash = "sha256:47ec8c3f0a7b2212dbc9be08d3bf17bc89abd211901093e3ef3f2adea7de7a69"},
|
||||
{file = "pymongo-4.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e84bc7707492f06fbc37a9f215374d2977d21b72e10a67f1b31893ec5a140ad8"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:519d1bab2b5e5218c64340b57d555d89c3f6c9d717cecbf826fb9d42415e7750"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87075a1feb1e602e539bdb1ef8f4324a3427eb0d64208c3182e677d2c0718b6f"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f53429515d2b3e86dcc83dadecf7ff881e538c168d575f3688698a8707b80a"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdc20cd1e1141b04696ffcdb7c71e8a4a665db31fe72e51ec706b3bdd2d09f36"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:284d0717d1a7707744018b0b6ee7801b1b1ff044c42f7be7a01bb013de639470"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5bf0eb8b6ef40fa22479f09375468c33bebb7fe49d14d9c96c8fd50355188b0"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ecd71b9226bd1d49416dc9f999772038e56f415a713be51bf18d8676a0841c8"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0061af6e8c5e68b13f1ec9ad5251247726653c5af3c0bbdfbca6cf931e99216"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:658d0170f27984e0d89c09fe5c42296613b711a3ffd847eb373b0dbb5b648d5f"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-win32.whl", hash = "sha256:3ed1c316718a2836f7efc3d75b4b0ffdd47894090bc697de8385acd13c513a70"},
|
||||
{file = "pymongo-4.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:7148419eedfea9ecb940961cfe465efaba90595568a1fb97585fb535ea63fe2b"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8400587d594761e5136a3423111f499574be5fd53cf0aefa0d0f05b180710b0"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af3e98dd9702b73e4e6fd780f6925352237f5dce8d99405ff1543f3771201704"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de3a860f037bb51f968de320baef85090ff0bbb42ec4f28ec6a5ddf88be61871"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0fc18b3a093f3db008c5fea0e980dbd3b743449eee29b5718bc2dc15ab5088bb"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18c9d8f975dd7194c37193583fd7d1eb9aea0c21ee58955ecf35362239ff31ac"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:408b2f8fdbeca3c19e4156f28fff1ab11c3efb0407b60687162d49f68075e63c"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6564780cafd6abeea49759fe661792bd5a67e4f51bca62b88faab497ab5fe89"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d18d86bc9e103f4d3d4f18b85a0471c0e13ce5b79194e4a0389a224bb70edd53"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9097c331577cecf8034422956daaba7ec74c26f7b255d718c584faddd7fa2e3c"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-win32.whl", hash = "sha256:d5428dbcd43d02f6306e1c3c95f692f68b284e6ee5390292242f509004c9e3a8"},
|
||||
{file = "pymongo-4.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:ef7225755ed27bfdb18730c68f6cb023d06c28f2b734597480fb4c0e500feb6f"},
|
||||
{file = "pymongo-4.8.0.tar.gz", hash = "sha256:454f2295875744dc70f1881e4b2eb99cdad008a33574bc8aaf120530f66c0cde"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -7139,6 +7127,7 @@ dnspython = ">=1.16.0,<3.0.0"
|
|||
|
||||
[package.extras]
|
||||
aws = ["pymongo-auth-aws (>=1.1.0,<2.0.0)"]
|
||||
docs = ["furo (==2023.9.10)", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<8)", "sphinx-rtd-theme (>=2,<3)", "sphinxcontrib-shellcheck (>=1,<2)"]
|
||||
encryption = ["certifi", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.6.0,<2.0.0)"]
|
||||
gssapi = ["pykerberos", "winkerberos (>=0.5.0)"]
|
||||
ocsp = ["certifi", "cryptography (>=2.5)", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"]
|
||||
|
|
@ -7880,13 +7869,13 @@ websockets = ">=11,<13"
|
|||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "5.0.6"
|
||||
version = "5.0.7"
|
||||
description = "Python client for Redis database and key-value store"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"},
|
||||
{file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"},
|
||||
{file = "redis-5.0.7-py3-none-any.whl", hash = "sha256:0e479e24da960c690be5d9b96d21f7b918a98c0cf49af3b6fafaa0753f93a0db"},
|
||||
{file = "redis-5.0.7.tar.gz", hash = "sha256:8f611490b93c8109b50adc317b31bfd84fff31def3475b92e7e80bf39f48175b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -8361,13 +8350,13 @@ dev = ["pre-commit", "pytest", "ruff (>=0.3.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "sentry_sdk-2.6.0-py2.py3-none-any.whl", hash = "sha256:422b91cb49378b97e7e8d0e8d5a1069df23689d45262b86f54988a7db264e874"},
|
||||
{file = "sentry_sdk-2.6.0.tar.gz", hash = "sha256:65cc07e9c6995c5e316109f138570b32da3bd7ff8d0d0ee4aaf2628c3dd8127d"},
|
||||
{file = "sentry_sdk-2.7.0-py2.py3-none-any.whl", hash = "sha256:db9594c27a4d21c1ebad09908b1f0dc808ef65c2b89c1c8e7e455143262e37c1"},
|
||||
{file = "sentry_sdk-2.7.0.tar.gz", hash = "sha256:d846a211d4a0378b289ced3c434480945f110d0ede00450ba631fc2852e7a0d4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -8399,7 +8388,7 @@ langchain = ["langchain (>=0.0.210)"]
|
|||
loguru = ["loguru (>=0.5)"]
|
||||
openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
|
||||
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
|
||||
opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"]
|
||||
opentelemetry-experimental = ["opentelemetry-instrumentation-aio-pika (==0.46b0)", "opentelemetry-instrumentation-aiohttp-client (==0.46b0)", "opentelemetry-instrumentation-aiopg (==0.46b0)", "opentelemetry-instrumentation-asgi (==0.46b0)", "opentelemetry-instrumentation-asyncio (==0.46b0)", "opentelemetry-instrumentation-asyncpg (==0.46b0)", "opentelemetry-instrumentation-aws-lambda (==0.46b0)", "opentelemetry-instrumentation-boto (==0.46b0)", "opentelemetry-instrumentation-boto3sqs (==0.46b0)", "opentelemetry-instrumentation-botocore (==0.46b0)", "opentelemetry-instrumentation-cassandra (==0.46b0)", "opentelemetry-instrumentation-celery (==0.46b0)", "opentelemetry-instrumentation-confluent-kafka (==0.46b0)", "opentelemetry-instrumentation-dbapi (==0.46b0)", "opentelemetry-instrumentation-django (==0.46b0)", "opentelemetry-instrumentation-elasticsearch (==0.46b0)", "opentelemetry-instrumentation-falcon (==0.46b0)", "opentelemetry-instrumentation-fastapi (==0.46b0)", "opentelemetry-instrumentation-flask (==0.46b0)", "opentelemetry-instrumentation-grpc (==0.46b0)", "opentelemetry-instrumentation-httpx (==0.46b0)", "opentelemetry-instrumentation-jinja2 (==0.46b0)", "opentelemetry-instrumentation-kafka-python (==0.46b0)", "opentelemetry-instrumentation-logging (==0.46b0)", "opentelemetry-instrumentation-mysql (==0.46b0)", "opentelemetry-instrumentation-mysqlclient (==0.46b0)", "opentelemetry-instrumentation-pika (==0.46b0)", "opentelemetry-instrumentation-psycopg (==0.46b0)", "opentelemetry-instrumentation-psycopg2 (==0.46b0)", "opentelemetry-instrumentation-pymemcache (==0.46b0)", "opentelemetry-instrumentation-pymongo (==0.46b0)", "opentelemetry-instrumentation-pymysql (==0.46b0)", "opentelemetry-instrumentation-pyramid (==0.46b0)", "opentelemetry-instrumentation-redis (==0.46b0)", "opentelemetry-instrumentation-remoulade (==0.46b0)", "opentelemetry-instrumentation-requests (==0.46b0)", "opentelemetry-instrumentation-sklearn (==0.46b0)", "opentelemetry-instrumentation-sqlalchemy (==0.46b0)", "opentelemetry-instrumentation-sqlite3 (==0.46b0)", "opentelemetry-instrumentation-starlette (==0.46b0)", "opentelemetry-instrumentation-system-metrics (==0.46b0)", "opentelemetry-instrumentation-threading (==0.46b0)", "opentelemetry-instrumentation-tornado (==0.46b0)", "opentelemetry-instrumentation-tortoiseorm (==0.46b0)", "opentelemetry-instrumentation-urllib (==0.46b0)", "opentelemetry-instrumentation-urllib3 (==0.46b0)", "opentelemetry-instrumentation-wsgi (==0.46b0)"]
|
||||
pure-eval = ["asttokens", "executing", "pure-eval"]
|
||||
pymongo = ["pymongo (>=3.1)"]
|
||||
pyspark = ["pyspark (>=2.4.4)"]
|
||||
|
|
|
|||
|
|
@ -147,11 +147,33 @@ ignore-regex = '.*(Stati Uniti|Tense=Pres).*'
|
|||
minversion = "6.0"
|
||||
testpaths = ["tests", "integration"]
|
||||
console_output_style = "progress"
|
||||
filterwarnings = ["ignore::DeprecationWarning"]
|
||||
filterwarnings = ["ignore::DeprecationWarning", "ignore::ResourceWarning"]
|
||||
log_cli = true
|
||||
markers = ["async_test", "api_key_required"]
|
||||
|
||||
|
||||
[tool.coverage.run]
|
||||
command_line = """
|
||||
-m pytest
|
||||
--cov --cov-report=term --cov-report=html
|
||||
--instafail -ra -n auto -m "not api_key_required"
|
||||
tests/unit
|
||||
"""
|
||||
source = ["src/backend/base/langflow/"]
|
||||
omit = ["*/alembic/*", "tests/*", "*/__init__.py"]
|
||||
|
||||
|
||||
[tool.coverage.report]
|
||||
sort = "Stmts"
|
||||
skip_empty = true
|
||||
show_missing = false
|
||||
ignore_errors = true
|
||||
|
||||
|
||||
[tool.coverage.html]
|
||||
directory = "coverage"
|
||||
|
||||
|
||||
[tool.ruff]
|
||||
exclude = ["src/backend/langflow/alembic/*"]
|
||||
line-length = 120
|
||||
|
|
|
|||
|
|
@ -3,14 +3,16 @@ services:
|
|||
- type: web
|
||||
name: langflow
|
||||
runtime: docker
|
||||
dockerfilePath: ./docker/render.pre-release.Dockerfile
|
||||
dockerfilePath: ./docker/render.Dockerfile
|
||||
repo: https://github.com/langflow-ai/langflow
|
||||
branch: dev
|
||||
branch: main
|
||||
healthCheckPath: /health
|
||||
autoDeploy: false
|
||||
envVars:
|
||||
- key: LANGFLOW_DATABASE_URL
|
||||
value: sqlite:////home/user/.cache/langflow/langflow.db
|
||||
- key: LANGFLOW_HOST
|
||||
value: 0.0.0.0
|
||||
disk:
|
||||
name: langflow-data
|
||||
mountPath: /home/user/.cache/langflow
|
||||
|
|
|
|||
14
scripts/aws/package-lock.json
generated
14
scripts/aws/package-lock.json
generated
|
|
@ -1950,12 +1950,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
|
@ -2763,9 +2763,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
"""Add message table
|
||||
|
||||
Revision ID: 325180f0c4e1
|
||||
Revises: 631faacf5da2
|
||||
Create Date: 2024-06-23 21:29:28.220100
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel
|
||||
from alembic import op
|
||||
|
||||
from langflow.utils import migration
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "325180f0c4e1"
|
||||
down_revision: Union[str, None] = "631faacf5da2"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
if not migration.table_exists("message", conn):
|
||||
op.create_table(
|
||||
"message",
|
||||
sa.Column("timestamp", sa.DateTime(), nullable=False),
|
||||
sa.Column("sender", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||
sa.Column("sender_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||
sa.Column("session_id", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||
sa.Column("text", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||
sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
|
||||
sa.Column("flow_id", sqlmodel.sql.sqltypes.GUID(), nullable=True),
|
||||
sa.Column("files", sa.JSON(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["flow_id"],
|
||||
["flow.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
if migration.table_exists("message", conn):
|
||||
op.drop_table("message")
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -276,15 +276,15 @@ async def webhook_run_flow(
|
|||
# get all webhook components in the flow
|
||||
webhook_components = get_all_webhook_components_in_flow(flow.data)
|
||||
tweaks = {}
|
||||
data_dict = await request.json()
|
||||
|
||||
for component in webhook_components:
|
||||
tweaks[component["id"]] = {"data": data.decode() if isinstance(data, bytes) else data}
|
||||
input_request = SimplifiedAPIRequest(
|
||||
input_value=data_dict.get("input_value", ""),
|
||||
input_type=data_dict.get("input_type", "chat"),
|
||||
output_type=data_dict.get("output_type", "chat"),
|
||||
input_value="",
|
||||
input_type="chat",
|
||||
output_type="chat",
|
||||
tweaks=tweaks,
|
||||
session_id=data_dict.get("session_id"),
|
||||
session_id=None,
|
||||
)
|
||||
logger.debug("Starting background task")
|
||||
background_tasks.add_task( # type: ignore
|
||||
|
|
|
|||
|
|
@ -131,8 +131,8 @@ async def list_profile_pictures(storage_service: StorageService = Depends(get_st
|
|||
people = await storage_service.list_files(flow_id=people_path) # type: ignore
|
||||
space = await storage_service.list_files(flow_id=space_path) # type: ignore
|
||||
|
||||
files = [Path("People") / i for i in people]
|
||||
files += [Path("Space") / i for i in space]
|
||||
files = [f"People/{i}" for i in people]
|
||||
files += [f"Space/{i}" for i in space]
|
||||
|
||||
return {"files": files}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import re
|
||||
from datetime import datetime, timezone
|
||||
from typing import List
|
||||
from uuid import UUID
|
||||
|
|
@ -46,8 +47,14 @@ def create_flow(
|
|||
select(Flow).where(Flow.name.like(f"{flow.name} (%")).where(Flow.user_id == current_user.id) # type: ignore
|
||||
).all()
|
||||
if flows:
|
||||
numbers = [int(flow.name.split("(")[1].split(")")[0]) for flow in flows]
|
||||
flow.name = f"{flow.name} ({max(numbers) + 1})"
|
||||
extract_number = re.compile(r"\((\d+)\)$")
|
||||
numbers = []
|
||||
for _flow in flows:
|
||||
result = extract_number.search(_flow.name)
|
||||
if result:
|
||||
numbers.append(int(result.groups(1)[0]))
|
||||
if numbers:
|
||||
flow.name = f"{flow.name} ({max(numbers) + 1})"
|
||||
else:
|
||||
flow.name = f"{flow.name} (1)"
|
||||
# Now check if the endpoint is unique
|
||||
|
|
@ -204,28 +211,11 @@ def update_flow(
|
|||
if settings_service.settings.remove_api_keys:
|
||||
flow_data = remove_api_keys(flow_data)
|
||||
for key, value in flow_data.items():
|
||||
if value is not None:
|
||||
setattr(db_flow, key, value)
|
||||
setattr(db_flow, key, value)
|
||||
webhook_component = get_webhook_component_in_flow(db_flow.data)
|
||||
db_flow.webhook = webhook_component is not None
|
||||
db_flow.updated_at = datetime.now(timezone.utc)
|
||||
|
||||
# First check if the flow.name is unique
|
||||
# there might be flows with name like: "MyFlow", "MyFlow (1)", "MyFlow (2)"
|
||||
# so we need to check if the name is unique with `like` operator
|
||||
# if we find a flow with the same name, we add a number to the end of the name
|
||||
# based on the highest number found
|
||||
flow_from_db = session.exec(select(Flow).where(Flow.id == flow_id, Flow.user_id == current_user.id)).first()
|
||||
if flow_from_db:
|
||||
flows = session.exec(
|
||||
select(Flow).where(Flow.name.like(f"{flow.name} (%")).where(Flow.user_id == current_user.id) # type: ignore
|
||||
).all()
|
||||
if flows:
|
||||
numbers = [int(flow.name.split("(")[1].split(")")[0]) for flow in flows]
|
||||
flow.name = f"{flow.name} ({max(numbers) + 1})"
|
||||
else:
|
||||
flow.name = f"{flow.name} (1)"
|
||||
|
||||
if db_flow.folder_id is None:
|
||||
default_folder = session.exec(select(Folder).where(Folder.name == DEFAULT_FOLDER_NAME)).first()
|
||||
if default_folder:
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy import delete
|
||||
from sqlmodel import Session, col, select
|
||||
|
||||
from langflow.services.deps import get_monitor_service
|
||||
from langflow.services.monitor.schema import (
|
||||
MessageModelRequest,
|
||||
MessageModelResponse,
|
||||
TransactionModelResponse,
|
||||
VertexBuildMapModel,
|
||||
)
|
||||
from langflow.services.auth.utils import get_current_active_user
|
||||
from langflow.services.database.models.message.model import MessageRead, MessageTable, MessageUpdate
|
||||
from langflow.services.database.models.user.model import User
|
||||
from langflow.services.deps import get_monitor_service, get_session
|
||||
from langflow.services.monitor.schema import MessageModelResponse, TransactionModelResponse, VertexBuildMapModel
|
||||
from langflow.services.monitor.service import MonitorService
|
||||
|
||||
router = APIRouter(prefix="/monitor", tags=["Monitor"])
|
||||
|
|
@ -52,45 +52,59 @@ async def get_messages(
|
|||
sender: Optional[str] = Query(None),
|
||||
sender_name: Optional[str] = Query(None),
|
||||
order_by: Optional[str] = Query("timestamp"),
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
try:
|
||||
df = monitor_service.get_messages(
|
||||
flow_id=flow_id,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
session_id=session_id,
|
||||
order_by=order_by,
|
||||
)
|
||||
dicts = df.to_dict(orient="records")
|
||||
return [MessageModelResponse(**d) for d in dicts]
|
||||
stmt = select(MessageTable)
|
||||
if flow_id:
|
||||
stmt = stmt.where(MessageTable.flow_id == flow_id)
|
||||
if session_id:
|
||||
stmt = stmt.where(MessageTable.session_id == session_id)
|
||||
if sender:
|
||||
stmt = stmt.where(MessageTable.sender == sender)
|
||||
if sender_name:
|
||||
stmt = stmt.where(MessageTable.sender_name == sender_name)
|
||||
if order_by:
|
||||
col = getattr(MessageTable, order_by).asc()
|
||||
stmt = stmt.order_by(col)
|
||||
messages = session.exec(stmt)
|
||||
return [MessageModelResponse.model_validate(d, from_attributes=True) for d in messages]
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/messages", status_code=204)
|
||||
async def delete_messages(
|
||||
message_ids: List[int],
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
message_ids: List[UUID],
|
||||
session: Session = Depends(get_session),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
try:
|
||||
monitor_service.delete_messages(message_ids=message_ids)
|
||||
session.exec(delete(MessageTable).where(MessageTable.id.in_(message_ids))) # type: ignore
|
||||
session.commit()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/messages/{message_id}", response_model=MessageModelResponse)
|
||||
@router.put("/messages/{message_id}", response_model=MessageRead)
|
||||
async def update_message(
|
||||
message_id: int,
|
||||
message: MessageModelRequest,
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
message_id: UUID,
|
||||
message: MessageUpdate,
|
||||
session: Session = Depends(get_session),
|
||||
user: User = Depends(get_current_active_user),
|
||||
):
|
||||
try:
|
||||
message_dict = message.model_dump(exclude_none=True)
|
||||
message_dict.pop("index", None)
|
||||
monitor_service.update_message(message_id=message_id, **message_dict) # type: ignore
|
||||
return MessageModelResponse(index=message_id, **message_dict)
|
||||
|
||||
db_message = session.get(MessageTable, message_id)
|
||||
if not db_message:
|
||||
raise HTTPException(status_code=404, detail="Message not found")
|
||||
message_dict = message.model_dump(exclude_unset=True, exclude_none=True)
|
||||
db_message.sqlmodel_update(message_dict)
|
||||
session.add(db_message)
|
||||
session.commit()
|
||||
session.refresh(db_message)
|
||||
return db_message
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
|
@ -98,10 +112,16 @@ async def update_message(
|
|||
@router.delete("/messages/session/{session_id}", status_code=204)
|
||||
async def delete_messages_session(
|
||||
session_id: str,
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
try:
|
||||
monitor_service.delete_messages_session(session_id=session_id)
|
||||
session.exec( # type: ignore
|
||||
delete(MessageTable)
|
||||
.where(col(MessageTable.session_id) == session_id)
|
||||
.execution_options(synchronize_session="fetch")
|
||||
)
|
||||
session.commit()
|
||||
return {"message": "Messages deleted successfully"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
|
@ -137,4 +157,3 @@ async def get_transactions(
|
|||
return result
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
|
|||
|
|
@ -119,7 +119,11 @@ class LCModelComponent(Component):
|
|||
return status_message
|
||||
|
||||
def get_chat_result(
|
||||
self, runnable: LanguageModel, stream: bool, input_value: str | Message, system_message: Optional[str] = None
|
||||
self,
|
||||
runnable: LanguageModel,
|
||||
stream: bool,
|
||||
input_value: str | Message,
|
||||
system_message: Optional[str] = None,
|
||||
):
|
||||
messages: list[Union[BaseMessage]] = []
|
||||
if not input_value and not system_message:
|
||||
|
|
@ -139,13 +143,13 @@ class LCModelComponent(Component):
|
|||
messages.append(HumanMessage(content=input_value))
|
||||
inputs: Union[list, dict] = messages or {}
|
||||
try:
|
||||
runnable = runnable.with_config(
|
||||
{"run_name": self.display_name, "project_name": self._tracing_service.project_name}
|
||||
runnable = runnable.with_config( # type: ignore
|
||||
{"run_name": self.display_name, "project_name": self._tracing_service.project_name} # type: ignore
|
||||
)
|
||||
if stream:
|
||||
return runnable.stream(inputs)
|
||||
return runnable.stream(inputs) # type: ignore
|
||||
else:
|
||||
message = runnable.invoke(inputs)
|
||||
message = runnable.invoke(inputs) # type: ignore
|
||||
result = message.content if hasattr(message, "content") else message
|
||||
if isinstance(message, AIMessage):
|
||||
status_message = self.build_status_message(message)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
MODEL_NAMES = ["gpt-4o", "gpt-4-turbo", "gpt-4-turbo-preview", "gpt-3.5-turbo", "gpt-3.5-turbo-0125"]
|
||||
MODEL_NAMES = ["gpt-4o", "gpt-4-turbo", "gpt-4-turbo-preview", "gpt-4", "gpt-3.5-turbo", "gpt-3.5-turbo-0125"]
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ class ChatLiteLLMModelComponent(LCModelComponent):
|
|||
Output(display_name="Language Model", name="model_output", method="build_model"),
|
||||
]
|
||||
|
||||
def build_model(self) -> LanguageModel:
|
||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||
try:
|
||||
import litellm # type: ignore
|
||||
|
||||
|
|
@ -176,5 +176,4 @@ class ChatLiteLLMModelComponent(LCModelComponent):
|
|||
openrouter_api_key=api_keys["openrouter_api_key"],
|
||||
)
|
||||
|
||||
return output
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
|
|
|||
|
|
@ -13,20 +13,39 @@ class AstraVectorize(Component):
|
|||
VECTORIZE_PROVIDERS_MAPPING = {
|
||||
"Azure OpenAI": ["azureOpenAI", ["text-embedding-3-small", "text-embedding-3-large", "text-embedding-ada-002"]],
|
||||
"Hugging Face - Dedicated": ["huggingfaceDedicated", ["endpoint-defined-model"]],
|
||||
"Hugging Face - Serverless": ["huggingface",
|
||||
["sentence-transformers/all-MiniLM-L6-v2", "intfloat/multilingual-e5-large",
|
||||
"intfloat/multilingual-e5-large-instruct", "BAAI/bge-small-en-v1.5",
|
||||
"BAAI/bge-base-en-v1.5", "BAAI/bge-large-en-v1.5"]],
|
||||
"Jina AI": ["jinaAI", ["jina-embeddings-v2-base-en", "jina-embeddings-v2-base-de", "jina-embeddings-v2-base-es",
|
||||
"jina-embeddings-v2-base-code", "jina-embeddings-v2-base-zh"]],
|
||||
"Hugging Face - Serverless": [
|
||||
"huggingface",
|
||||
[
|
||||
"sentence-transformers/all-MiniLM-L6-v2",
|
||||
"intfloat/multilingual-e5-large",
|
||||
"intfloat/multilingual-e5-large-instruct",
|
||||
"BAAI/bge-small-en-v1.5",
|
||||
"BAAI/bge-base-en-v1.5",
|
||||
"BAAI/bge-large-en-v1.5",
|
||||
],
|
||||
],
|
||||
"Jina AI": [
|
||||
"jinaAI",
|
||||
[
|
||||
"jina-embeddings-v2-base-en",
|
||||
"jina-embeddings-v2-base-de",
|
||||
"jina-embeddings-v2-base-es",
|
||||
"jina-embeddings-v2-base-code",
|
||||
"jina-embeddings-v2-base-zh",
|
||||
],
|
||||
],
|
||||
"Mistral AI": ["mistral", ["mistral-embed"]],
|
||||
"NVIDIA": ["nvidia", ["NV-Embed-QA"]],
|
||||
"OpenAI": ["openai", ["text-embedding-3-small", "text-embedding-3-large", "text-embedding-ada-002"]],
|
||||
"Upstage": ["upstageAI", ["solar-embedding-1-large"]],
|
||||
"Voyage AI": ["voyageAI",
|
||||
["voyage-large-2-instruct", "voyage-law-2", "voyage-code-2", "voyage-large-2", "voyage-2"]]
|
||||
"Voyage AI": [
|
||||
"voyageAI",
|
||||
["voyage-large-2-instruct", "voyage-law-2", "voyage-code-2", "voyage-large-2", "voyage-2"],
|
||||
],
|
||||
}
|
||||
VECTORIZE_MODELS_STR = "\n\n".join([provider + ": " + (', '.join(models[1])) for provider, models in VECTORIZE_PROVIDERS_MAPPING.items()])
|
||||
VECTORIZE_MODELS_STR = "\n\n".join(
|
||||
[provider + ": " + (", ".join(models[1])) for provider, models in VECTORIZE_PROVIDERS_MAPPING.items()]
|
||||
)
|
||||
|
||||
inputs = [
|
||||
DropdownInput(
|
||||
|
|
@ -39,13 +58,13 @@ class AstraVectorize(Component):
|
|||
name="model_name",
|
||||
display_name="Model name",
|
||||
info=f"The embedding model to use for the selected provider. Each provider has a different set of models "
|
||||
f"available (full list at https://docs.datastax.com/en/astra-db-serverless/databases/embedding-generation.html):\n\n{VECTORIZE_MODELS_STR}",
|
||||
required=True
|
||||
f"available (full list at https://docs.datastax.com/en/astra-db-serverless/databases/embedding-generation.html):\n\n{VECTORIZE_MODELS_STR}",
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="api_key_name",
|
||||
display_name="API Key name",
|
||||
info="The name of the embeddings provider API key stored on Astra. If set, it will override the 'ProviderKey' in the authentication parameters."
|
||||
info="The name of the embeddings provider API key stored on Astra. If set, it will override the 'ProviderKey' in the authentication parameters.",
|
||||
),
|
||||
DictInput(
|
||||
name="authentication",
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class OpenAIEmbeddingsComponent(LCEmbeddingsModel):
|
|||
),
|
||||
DictInput(name="model_kwargs", display_name="Model Kwargs", advanced=True),
|
||||
SecretStrInput(name="openai_api_base", display_name="OpenAI API Base", advanced=True),
|
||||
SecretStrInput(name="openai_api_key", display_name="OpenAI API Key"),
|
||||
SecretStrInput(name="openai_api_key", display_name="OpenAI API Key", value="OPENAI_API_KEY"),
|
||||
SecretStrInput(name="openai_api_type", display_name="OpenAI API Type", advanced=True),
|
||||
MessageTextInput(name="openai_api_version", display_name="OpenAI API Version", advanced=True),
|
||||
MessageTextInput(
|
||||
|
|
|
|||
28
src/backend/base/langflow/components/helpers/CreateList.py
Normal file
28
src/backend/base/langflow/components/helpers/CreateList.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
from langflow.custom import Component
|
||||
from langflow.inputs import StrInput
|
||||
from langflow.schema import Data
|
||||
from langflow.template import Output
|
||||
|
||||
|
||||
class CreateListComponent(Component):
|
||||
display_name = "Create List"
|
||||
description = "Creates a list of texts."
|
||||
icon = "list"
|
||||
|
||||
inputs = [
|
||||
StrInput(
|
||||
name="texts",
|
||||
display_name="Texts",
|
||||
info="Enter one or more texts.",
|
||||
is_list=True,
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data List", name="list", method="create_list"),
|
||||
]
|
||||
|
||||
def create_list(self) -> list[Data]:
|
||||
data = [Data(text=text) for text in self.texts]
|
||||
self.status = data
|
||||
return data
|
||||
|
|
@ -5,7 +5,7 @@ from langflow.custom import CustomComponent
|
|||
from langflow.schema.dotdict import dotdict
|
||||
|
||||
|
||||
class UUIDGeneratorComponent(CustomComponent):
|
||||
class IDGeneratorComponent(CustomComponent):
|
||||
display_name = "ID Generator"
|
||||
description = "Generates a unique ID."
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,47 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.memory import get_messages, store_message
|
||||
from langflow.custom import Component
|
||||
from langflow.inputs import MessageInput, StrInput
|
||||
from langflow.schema.message import Message
|
||||
from langflow.template import Output
|
||||
from langflow.memory import get_messages, store_message
|
||||
|
||||
|
||||
class StoreMessageComponent(CustomComponent):
|
||||
class StoreMessageComponent(Component):
|
||||
display_name = "Store Message"
|
||||
description = "Stores a chat message."
|
||||
description = "Stores a chat message or text."
|
||||
icon = "save"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"message": {"display_name": "Message"},
|
||||
}
|
||||
inputs = [
|
||||
MessageInput(name="message", display_name="Message", info="The chat message to be stored.", required=True),
|
||||
StrInput(
|
||||
name="sender",
|
||||
display_name="Sender",
|
||||
info="The sender of the message.",
|
||||
value="AI",
|
||||
advanced=True,
|
||||
),
|
||||
StrInput(
|
||||
name="sender_name", display_name="Sender Name", info="The name of the sender.", value="AI", advanced=True
|
||||
),
|
||||
StrInput(
|
||||
name="session_id",
|
||||
display_name="Session ID",
|
||||
info="The session ID of the chat.",
|
||||
value="",
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Stored Messages", name="stored_messages", method="store_message"),
|
||||
]
|
||||
|
||||
def store_message(self) -> Message:
|
||||
message = self.message
|
||||
|
||||
message.session_id = self.session_id or message.session_id
|
||||
message.sender = self.sender or message.sender
|
||||
message.sender_name = self.sender_name or message.sender_name
|
||||
|
||||
def build(
|
||||
self,
|
||||
message: Message,
|
||||
) -> Message:
|
||||
store_message(message, flow_id=self.graph.flow_id)
|
||||
self.status = get_messages()
|
||||
|
||||
return message
|
||||
stored = get_messages(session_id=message.session_id, sender_name=message.sender_name, sender=message.sender)
|
||||
self.status = stored
|
||||
return stored
|
||||
|
|
|
|||
|
|
@ -1,22 +1,25 @@
|
|||
from .CombineText import CombineTextComponent
|
||||
from .CustomComponent import CustomComponent
|
||||
from .FilterData import FilterDataComponent
|
||||
from .IDGenerator import UUIDGeneratorComponent
|
||||
from .IDGenerator import IDGeneratorComponent
|
||||
from .Memory import MemoryComponent
|
||||
from .MergeData import MergeDataComponent
|
||||
from .ParseData import ParseDataComponent
|
||||
from .SplitText import SplitTextComponent
|
||||
from .StoreMessage import StoreMessageComponent
|
||||
from .CreateList import CreateListComponent
|
||||
|
||||
|
||||
__all__ = [
|
||||
"CreateListComponent",
|
||||
"CombineTextComponent",
|
||||
"CustomComponent",
|
||||
"FilterDataComponent",
|
||||
"UUIDGeneratorComponent",
|
||||
"IDGeneratorComponent",
|
||||
"MemoryComponent",
|
||||
"MergeDataComponent",
|
||||
"ParseDataComponent",
|
||||
"SplitTextComponent",
|
||||
"StoreMessageComponent",
|
||||
"ListComponent",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import uuid
|
||||
from typing import Optional
|
||||
from firecrawl.firecrawl import FirecrawlApp
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Data
|
||||
import uuid
|
||||
|
||||
|
||||
class FirecrawlCrawlApi(CustomComponent):
|
||||
display_name: str = "FirecrawlCrawlApi"
|
||||
|
|
@ -47,18 +48,25 @@ class FirecrawlCrawlApi(CustomComponent):
|
|||
self,
|
||||
api_key: str,
|
||||
url: str,
|
||||
timeout: Optional[int] = 30000,
|
||||
timeout: int = 30000,
|
||||
crawlerOptions: Optional[Data] = None,
|
||||
pageOptions: Optional[Data] = None,
|
||||
idempotency_key: Optional[str] = None,
|
||||
) -> Data:
|
||||
try:
|
||||
from firecrawl.firecrawl import FirecrawlApp # type: ignore
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import firecrawl integration package. " "Please install it with `pip install firecrawl-py`."
|
||||
)
|
||||
|
||||
if crawlerOptions:
|
||||
crawler_options_dict = crawlerOptions.__dict__['data']['text']
|
||||
crawler_options_dict = crawlerOptions.__dict__["data"]["text"]
|
||||
else:
|
||||
crawler_options_dict = {}
|
||||
|
||||
if pageOptions:
|
||||
page_options_dict = pageOptions.__dict__['data']['text']
|
||||
page_options_dict = pageOptions.__dict__["data"]["text"]
|
||||
else:
|
||||
page_options_dict = {}
|
||||
|
||||
|
|
@ -74,7 +82,7 @@ class FirecrawlCrawlApi(CustomComponent):
|
|||
},
|
||||
True,
|
||||
int(timeout / 1000),
|
||||
idempotency_key
|
||||
idempotency_key,
|
||||
)
|
||||
|
||||
records = Data(data={"results": crawl_result})
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import Optional
|
||||
from firecrawl.firecrawl import FirecrawlApp
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class FirecrawlScrapeApi(CustomComponent):
|
||||
display_name: str = "FirecrawlScrapeApi"
|
||||
description: str = "Firecrawl Scrape API."
|
||||
|
|
@ -46,13 +47,19 @@ class FirecrawlScrapeApi(CustomComponent):
|
|||
pageOptions: Optional[Data] = None,
|
||||
extractorOptions: Optional[Data] = None,
|
||||
) -> Data:
|
||||
try:
|
||||
from firecrawl.firecrawl import FirecrawlApp # type: ignore
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import firecrawl integration package. " "Please install it with `pip install firecrawl-py`."
|
||||
)
|
||||
if extractorOptions:
|
||||
extractor_options_dict = extractorOptions.__dict__['data']['text']
|
||||
extractor_options_dict = extractorOptions.__dict__["data"]["text"]
|
||||
else:
|
||||
extractor_options_dict = {}
|
||||
|
||||
if pageOptions:
|
||||
page_options_dict = pageOptions.__dict__['data']['text']
|
||||
page_options_dict = pageOptions.__dict__["data"]["text"]
|
||||
else:
|
||||
page_options_dict = {}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class AmazonBedrockComponent(LCModelComponent):
|
|||
Output(display_name="Language Model", name="model_output", method="build_model"),
|
||||
]
|
||||
|
||||
def build_model(self) -> LanguageModel:
|
||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||
model_id = self.model_id
|
||||
credentials_profile_name = self.credentials_profile_name
|
||||
region_name = self.region_name
|
||||
|
|
@ -89,4 +89,4 @@ class AmazonBedrockComponent(LCModelComponent):
|
|||
)
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to AmazonBedrock API.") from e
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class AnthropicModelComponent(LCModelComponent):
|
|||
Output(display_name="Language Model", name="model_output", method="build_model"),
|
||||
]
|
||||
|
||||
def build_model(self) -> LanguageModel:
|
||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||
model = self.model
|
||||
anthropic_api_key = self.anthropic_api_key
|
||||
max_tokens = self.max_tokens
|
||||
|
|
@ -83,7 +83,7 @@ class AnthropicModelComponent(LCModelComponent):
|
|||
except Exception as e:
|
||||
raise ValueError("Could not connect to Anthropic API.") from e
|
||||
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
||||
def _get_exception_message(self, exception: Exception) -> str | None:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ class AzureChatOpenAIComponent(LCModelComponent):
|
|||
Output(display_name="Language Model", name="model_output", method="model_response"),
|
||||
]
|
||||
|
||||
def model_response(self) -> LanguageModel:
|
||||
def model_response(self) -> LanguageModel: # type: ignore[type-var]
|
||||
model = self.model
|
||||
azure_endpoint = self.azure_endpoint
|
||||
azure_deployment = self.azure_deployment
|
||||
|
|
@ -107,4 +107,4 @@ class AzureChatOpenAIComponent(LCModelComponent):
|
|||
except Exception as e:
|
||||
raise ValueError("Could not connect to AzureOpenAI API.") from e
|
||||
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
|
|
|||
|
|
@ -51,4 +51,4 @@ class CohereComponent(LCModelComponent):
|
|||
cohere_api_key=api_key,
|
||||
)
|
||||
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
|
|
|||
|
|
@ -3,15 +3,7 @@ from pydantic.v1 import SecretStr
|
|||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import LanguageModel
|
||||
from langflow.inputs import (
|
||||
BoolInput,
|
||||
DropdownInput,
|
||||
FloatInput,
|
||||
IntInput,
|
||||
MessageInput,
|
||||
SecretStrInput,
|
||||
StrInput,
|
||||
)
|
||||
from langflow.inputs import BoolInput, DropdownInput, FloatInput, IntInput, MessageInput, SecretStrInput, StrInput
|
||||
|
||||
|
||||
class GoogleGenerativeAIComponent(LCModelComponent):
|
||||
|
|
@ -66,7 +58,7 @@ class GoogleGenerativeAIComponent(LCModelComponent):
|
|||
),
|
||||
]
|
||||
|
||||
def build_model(self) -> LanguageModel:
|
||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||
try:
|
||||
from langchain_google_genai import ChatGoogleGenerativeAI
|
||||
except ImportError:
|
||||
|
|
@ -90,4 +82,4 @@ class GoogleGenerativeAIComponent(LCModelComponent):
|
|||
google_api_key=SecretStr(google_api_key),
|
||||
)
|
||||
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class GroqModel(LCModelComponent):
|
|||
),
|
||||
]
|
||||
|
||||
def build_model(self) -> LanguageModel:
|
||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||
groq_api_key = self.groq_api_key
|
||||
model_name = self.model_name
|
||||
max_tokens = self.max_tokens
|
||||
|
|
@ -87,4 +87,4 @@ class GroqModel(LCModelComponent):
|
|||
streaming=stream,
|
||||
)
|
||||
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class HuggingFaceEndpointsComponent(LCModelComponent):
|
|||
Output(display_name="Language Model", name="model_output", method="build_model"),
|
||||
]
|
||||
|
||||
def build_model(self) -> LanguageModel:
|
||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||
endpoint_url = self.endpoint_url
|
||||
task = self.task
|
||||
huggingfacehub_api_token = self.huggingfacehub_api_token
|
||||
|
|
@ -53,4 +53,4 @@ class HuggingFaceEndpointsComponent(LCModelComponent):
|
|||
raise ValueError("Could not connect to HuggingFace Endpoints API.") from e
|
||||
|
||||
output = ChatHuggingFace(llm=llm)
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class MistralAIModelComponent(LCModelComponent):
|
|||
Output(display_name="Language Model", name="model_output", method="build_model"),
|
||||
]
|
||||
|
||||
def build_model(self) -> LanguageModel:
|
||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||
mistral_api_key = self.mistral_api_key
|
||||
temperature = self.temperature
|
||||
model_name = self.model_name
|
||||
|
|
@ -102,4 +102,4 @@ class MistralAIModelComponent(LCModelComponent):
|
|||
safe_mode=safe_mode,
|
||||
)
|
||||
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ class ChatOllamaComponent(LCModelComponent):
|
|||
Output(display_name="Language Model", name="model_output", method="build_model"),
|
||||
]
|
||||
|
||||
def build_model(self) -> LanguageModel:
|
||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||
# Mapping mirostat settings to their corresponding values
|
||||
mirostat_options = {"Mirostat": 1, "Mirostat 2.0": 2}
|
||||
|
||||
|
|
@ -272,4 +272,4 @@ class ChatOllamaComponent(LCModelComponent):
|
|||
except Exception as e:
|
||||
raise ValueError("Could not initialize Ollama LLM.") from e
|
||||
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ class OpenAIModelComponent(LCModelComponent):
|
|||
info="The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
|
||||
),
|
||||
DictInput(name="model_kwargs", display_name="Model Kwargs", advanced=True),
|
||||
BoolInput(
|
||||
name="json_mode",
|
||||
display_name="JSON Mode",
|
||||
advanced=True,
|
||||
info="If True, it will output JSON regardless of passing a schema.",
|
||||
),
|
||||
DictInput(
|
||||
name="output_schema",
|
||||
is_list=True,
|
||||
|
|
@ -74,8 +80,8 @@ class OpenAIModelComponent(LCModelComponent):
|
|||
),
|
||||
]
|
||||
|
||||
def build_model(self) -> LanguageModel:
|
||||
# self.output_schea is a list of dictionaries
|
||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||
# self.output_schea is a list of dictionarie s
|
||||
# let's convert it to a dictionary
|
||||
output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})
|
||||
openai_api_key = self.openai_api_key
|
||||
|
|
@ -84,7 +90,7 @@ class OpenAIModelComponent(LCModelComponent):
|
|||
max_tokens = self.max_tokens
|
||||
model_kwargs = self.model_kwargs or {}
|
||||
openai_api_base = self.openai_api_base or "https://api.openai.com/v1"
|
||||
json_mode = bool(output_schema_dict)
|
||||
json_mode = bool(output_schema_dict) or self.json_mode
|
||||
seed = self.seed
|
||||
model_kwargs["seed"] = seed
|
||||
|
||||
|
|
@ -101,9 +107,12 @@ class OpenAIModelComponent(LCModelComponent):
|
|||
temperature=temperature or 0.1,
|
||||
)
|
||||
if json_mode:
|
||||
output = output.with_structured_output(schema=output_schema_dict, method="json_mode") # type: ignore
|
||||
if output_schema_dict:
|
||||
output = output.with_structured_output(schema=output_schema_dict, method="json_mode") # type: ignore
|
||||
else:
|
||||
output = output.bind(response_format={"type": "json_object"}) # type: ignore
|
||||
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
||||
def _get_exception_message(self, e: Exception):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class ChatVertexAIComponent(LCModelComponent):
|
|||
Output(display_name="Language Model", name="model_output", method="build_model"),
|
||||
]
|
||||
|
||||
def build_model(self) -> LanguageModel:
|
||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||
credentials = self.credentials
|
||||
location = self.location
|
||||
max_output_tokens = self.max_output_tokens
|
||||
|
|
@ -75,4 +75,4 @@ class ChatVertexAIComponent(LCModelComponent):
|
|||
verbose=verbose,
|
||||
)
|
||||
|
||||
return output
|
||||
return output # type: ignore
|
||||
|
|
|
|||
|
|
@ -66,21 +66,17 @@ class ConditionalRouterComponent(Component):
|
|||
def true_response(self) -> Message:
|
||||
result = self.evaluate_condition(self.input_text, self.match_text, self.operator, self.case_sensitive)
|
||||
if result:
|
||||
self.stop("false_result")
|
||||
response = self.message if self.message else self.input_text
|
||||
self.status = response
|
||||
return response
|
||||
self.status = self.message
|
||||
return self.message
|
||||
else:
|
||||
self.stop("true_result")
|
||||
return Message()
|
||||
return None # type: ignore
|
||||
|
||||
def false_response(self) -> Message:
|
||||
result = self.evaluate_condition(self.input_text, self.match_text, self.operator, self.case_sensitive)
|
||||
if not result:
|
||||
self.stop("true_result")
|
||||
response = self.message if self.message else self.input_text
|
||||
self.status = response
|
||||
return response
|
||||
self.status = self.message
|
||||
return self.message
|
||||
else:
|
||||
self.stop("false_result")
|
||||
return Message()
|
||||
return None # type: ignore
|
||||
|
|
|
|||
|
|
@ -1,31 +1,32 @@
|
|||
from typing import Union
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Data
|
||||
from langflow.custom import Component
|
||||
from langflow.io import MessageInput
|
||||
from langflow.schema.message import Message
|
||||
from langflow.template import Output
|
||||
|
||||
|
||||
class PassComponent(CustomComponent):
|
||||
class PassMessageComponent(Component):
|
||||
display_name = "Pass"
|
||||
description = "A pass-through component that forwards the second input while ignoring the first, used for controlling workflow direction."
|
||||
field_order = ["ignored_input", "forwarded_input"]
|
||||
beta = True
|
||||
description = "Forwards the input message, unchanged."
|
||||
icon = "arrow-right"
|
||||
|
||||
def build_config(self) -> dict:
|
||||
return {
|
||||
"ignored_input": {
|
||||
"display_name": "Ignored Input",
|
||||
"info": "This input is ignored. It's used to control the flow in the graph.",
|
||||
"input_types": ["Text", "Data"],
|
||||
},
|
||||
"forwarded_input": {
|
||||
"display_name": "Input",
|
||||
"info": "This input is forwarded by the component.",
|
||||
"input_types": ["Text", "Data"],
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
MessageInput(
|
||||
name="input_message",
|
||||
display_name="Input Message",
|
||||
info="The message to be passed forward.",
|
||||
),
|
||||
MessageInput(
|
||||
name="ignored_message",
|
||||
display_name="Ignored Message",
|
||||
info="A second message to be ignored. Used as a workaround for continuity.",
|
||||
advanced=True,
|
||||
),
|
||||
]
|
||||
|
||||
def build(self, ignored_input: Text, forwarded_input: Text) -> Union[Text, Data]:
|
||||
# The ignored_input is not used in the logic, it's just there for graph flow control
|
||||
self.status = forwarded_input
|
||||
return forwarded_input
|
||||
outputs = [
|
||||
Output(display_name="Output Message", name="output_message", method="pass_message"),
|
||||
]
|
||||
|
||||
def pass_message(self) -> Message:
|
||||
self.status = self.input_message
|
||||
return self.input_message
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from .ConditionalRouter import ConditionalRouterComponent
|
|||
from .FlowTool import FlowToolComponent
|
||||
from .Listen import ListenComponent
|
||||
from .Notify import NotifyComponent
|
||||
from .Pass import PassComponent
|
||||
from .Pass import PassMessageComponent
|
||||
from .PythonFunction import PythonFunctionComponent
|
||||
from .RunFlow import RunFlowComponent
|
||||
from .RunnableExecutor import RunnableExecComponent
|
||||
|
|
@ -16,7 +16,7 @@ __all__ = [
|
|||
"FlowToolComponent",
|
||||
"ListenComponent",
|
||||
"NotifyComponent",
|
||||
"PassComponent",
|
||||
"PassMessageComponent",
|
||||
"PythonFunctionComponent",
|
||||
"RunFlowComponent",
|
||||
"RunnableExecComponent",
|
||||
|
|
|
|||
|
|
@ -24,18 +24,20 @@ class CassandraVectorStoreComponent(LCVectorStoreComponent):
|
|||
icon = "Cassandra"
|
||||
|
||||
inputs = [
|
||||
MessageTextInput(name="database_ref",
|
||||
display_name="Contact Points / Astra Database ID",
|
||||
info="Contact points for the database (or AstraDB database ID)",
|
||||
required=True),
|
||||
MessageTextInput(name="username",
|
||||
display_name="Username",
|
||||
info="Username for the database (leave empty for AstraDB)."),
|
||||
MessageTextInput(
|
||||
name="database_ref",
|
||||
display_name="Contact Points / Astra Database ID",
|
||||
info="Contact points for the database (or AstraDB database ID)",
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="username", display_name="Username", info="Username for the database (leave empty for AstraDB)."
|
||||
),
|
||||
SecretStrInput(
|
||||
name="token",
|
||||
display_name="Password / AstraDB Token",
|
||||
info="User password for the database (or AstraDB token).",
|
||||
required=True
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="keyspace",
|
||||
|
|
@ -81,7 +83,7 @@ class CassandraVectorStoreComponent(LCVectorStoreComponent):
|
|||
display_name="Cluster arguments",
|
||||
info="Optional dictionary of additional keyword arguments for the Cassandra cluster.",
|
||||
advanced=True,
|
||||
is_list=True
|
||||
is_list=True,
|
||||
),
|
||||
MultilineInput(name="search_query", display_name="Search Query"),
|
||||
DataInput(
|
||||
|
|
@ -137,7 +139,7 @@ class CassandraVectorStoreComponent(LCVectorStoreComponent):
|
|||
cluster_kwargs=self.cluster_kwargs,
|
||||
)
|
||||
|
||||
if not self.ttl_seconds:
|
||||
if not self.ttl_seconds: # type: ignore
|
||||
self.ttl_seconds = None
|
||||
|
||||
documents = []
|
||||
|
|
|
|||
|
|
@ -329,8 +329,6 @@ class CodeParser:
|
|||
"""
|
||||
Extracts "classes" from the code, including inheritance and init methods.
|
||||
"""
|
||||
if node.name in ["CustomComponent", "Component", "BaseComponent"]:
|
||||
return
|
||||
bases = self.get_base_classes()
|
||||
nodes = []
|
||||
for base in bases:
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ class CustomComponent(BaseComponent):
|
|||
_flows_data: Optional[List[Data]] = None
|
||||
_outputs: List[OutputLog] = []
|
||||
_logs: List[Log] = []
|
||||
_tracing_service: "TracingService"
|
||||
tracing_service: Optional["TracingService"] = None
|
||||
|
||||
def update_state(self, name: str, value: Any):
|
||||
if not self.vertex:
|
||||
|
|
@ -96,7 +96,7 @@ class CustomComponent(BaseComponent):
|
|||
def stop(self, output_name: str | None = None):
|
||||
if not output_name and self.vertex and len(self.vertex.outputs) == 1:
|
||||
output_name = self.vertex.outputs[0]["name"]
|
||||
else:
|
||||
elif not output_name:
|
||||
raise ValueError("You must specify an output name to call stop")
|
||||
if not self.vertex:
|
||||
raise ValueError("Vertex is not set")
|
||||
|
|
@ -488,14 +488,14 @@ class CustomComponent(BaseComponent):
|
|||
Args:
|
||||
message (LoggableType | list[LoggableType]): The message to log.
|
||||
"""
|
||||
if name is None:
|
||||
name = self.display_name if self.display_name else self.__class__.__name__
|
||||
if hasattr(message, "model_dump") and isinstance(message, BaseModel):
|
||||
message = message.model_dump()
|
||||
if name is None and self.display_name:
|
||||
name = self.display_name
|
||||
else:
|
||||
name = self.__class__.__name__
|
||||
log = Log(message=message, type=get_artifact_type(message), name=name)
|
||||
self._logs.append(log)
|
||||
if self.vertex:
|
||||
self._tracing_service.add_log(trace_name=self.vertex.id, log=log)
|
||||
if self.tracing_service and self.vertex:
|
||||
self.tracing_service.add_log(trace_name=self.vertex.id, log=log)
|
||||
|
||||
def post_code_processing(self, new_build_config: dict, current_build_config: dict):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ from .constants import (
|
|||
TextSplitter,
|
||||
Tool,
|
||||
VectorStore,
|
||||
LanguageModel,
|
||||
)
|
||||
from .range_spec import RangeSpec
|
||||
|
||||
|
|
@ -84,4 +85,5 @@ __all__ = [
|
|||
"BaseChatModel",
|
||||
"Retriever",
|
||||
"Text",
|
||||
"LanguageModel",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -343,10 +343,10 @@ class Graph:
|
|||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
tb = traceback.format_exc()
|
||||
await self.end_all_traces(error=f"{exc.__class__.__name__}: {exc}\n\n{tb}")
|
||||
asyncio.create_task(self.end_all_traces(error=f"{exc.__class__.__name__}: {exc}\n\n{tb}"))
|
||||
raise ValueError(f"Error running graph: {exc}") from exc
|
||||
finally:
|
||||
await self.end_all_traces()
|
||||
asyncio.create_task(self.end_all_traces())
|
||||
# Get the outputs
|
||||
vertex_outputs = []
|
||||
for vertex in self.vertices:
|
||||
|
|
@ -1208,6 +1208,7 @@ class Graph:
|
|||
except ValueError:
|
||||
stop_or_start_vertex = self.get_root_of_group_node(vertex_id)
|
||||
stack = [stop_or_start_vertex.id]
|
||||
vertex_id = stop_or_start_vertex.id
|
||||
stop_predecessors = [pre.id for pre in stop_or_start_vertex.predecessors]
|
||||
# DFS to collect all vertices that can reach the specified vertex
|
||||
while stack:
|
||||
|
|
@ -1443,7 +1444,7 @@ class Graph:
|
|||
|
||||
def is_vertex_runnable(self, vertex_id: str) -> bool:
|
||||
"""Returns whether a vertex is runnable."""
|
||||
return self.run_manager.is_vertex_runnable(vertex_id)
|
||||
return self.run_manager.is_vertex_runnable(vertex_id, self.inactivated_vertices)
|
||||
|
||||
def build_run_map(self):
|
||||
"""
|
||||
|
|
@ -1463,7 +1464,7 @@ class Graph:
|
|||
This checks the direct predecessors of each successor to identify any that are
|
||||
immediately runnable, expanding the search to ensure progress can be made.
|
||||
"""
|
||||
return self.run_manager.find_runnable_predecessors_for_successors(vertex_id)
|
||||
return self.run_manager.find_runnable_predecessors_for_successors(vertex_id, self.inactivated_vertices)
|
||||
|
||||
def remove_from_predecessors(self, vertex_id: str):
|
||||
self.run_manager.remove_from_predecessors(vertex_id)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import asyncio
|
||||
from collections import defaultdict
|
||||
from typing import TYPE_CHECKING, Callable, List, Coroutine
|
||||
from typing import TYPE_CHECKING, Callable, Coroutine, List
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.graph.graph.base import Graph
|
||||
|
|
@ -40,19 +40,23 @@ class RunnableVerticesManager:
|
|||
self.run_predecessors = state["run_predecessors"]
|
||||
self.vertices_to_run = state["vertices_to_run"]
|
||||
|
||||
def is_vertex_runnable(self, vertex_id: str) -> bool:
|
||||
def is_vertex_runnable(self, vertex_id: str, inactivated_vertices: set[str]) -> bool:
|
||||
"""Determines if a vertex is runnable."""
|
||||
|
||||
return vertex_id in self.vertices_to_run and not self.run_predecessors.get(vertex_id)
|
||||
return (
|
||||
vertex_id in self.vertices_to_run
|
||||
and not self.run_predecessors.get(vertex_id)
|
||||
and vertex_id not in inactivated_vertices
|
||||
)
|
||||
|
||||
def find_runnable_predecessors_for_successors(self, vertex_id: str) -> List[str]:
|
||||
def find_runnable_predecessors_for_successors(self, vertex_id: str, inactivated_vertices: set[str]) -> List[str]:
|
||||
"""Finds runnable predecessors for the successors of a given vertex."""
|
||||
runnable_vertices = []
|
||||
visited = set()
|
||||
|
||||
for successor_id in self.run_map.get(vertex_id, []):
|
||||
for predecessor_id in self.run_predecessors.get(successor_id, []):
|
||||
if predecessor_id not in visited and self.is_vertex_runnable(predecessor_id):
|
||||
if predecessor_id not in visited and self.is_vertex_runnable(predecessor_id, inactivated_vertices):
|
||||
runnable_vertices.append(predecessor_id)
|
||||
visited.add(predecessor_id)
|
||||
return runnable_vertices
|
||||
|
|
@ -104,10 +108,14 @@ class RunnableVerticesManager:
|
|||
"""
|
||||
async with lock:
|
||||
self.remove_from_predecessors(vertex.id)
|
||||
direct_successors_ready = [v for v in vertex.successors_ids if self.is_vertex_runnable(v)]
|
||||
direct_successors_ready = [
|
||||
v for v in vertex.successors_ids if self.is_vertex_runnable(v, graph.inactivated_vertices)
|
||||
]
|
||||
if not direct_successors_ready:
|
||||
# No direct successors ready, look for runnable predecessors of successors
|
||||
next_runnable_vertices = self.find_runnable_predecessors_for_successors(vertex.id)
|
||||
next_runnable_vertices = self.find_runnable_predecessors_for_successors(
|
||||
vertex.id, graph.inactivated_vertices
|
||||
)
|
||||
else:
|
||||
next_runnable_vertices = direct_successors_ready
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
"data": {
|
||||
"edges": [
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "ChatInput",
|
||||
"id": "ChatInput-pxptT",
|
||||
"id": "ChatInput-Y6mi1",
|
||||
"name": "message",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "user_input",
|
||||
"id": "Prompt-1S5SU",
|
||||
"id": "Prompt-Z4WYI",
|
||||
"inputTypes": [
|
||||
"Message",
|
||||
"Text"
|
||||
|
|
@ -21,17 +22,18 @@
|
|||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-ChatInput-pxptT{œdataTypeœ:œChatInputœ,œidœ:œChatInput-pxptTœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-1S5SU{œfieldNameœ:œuser_inputœ,œidœ:œPrompt-1S5SUœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "ChatInput-pxptT",
|
||||
"sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-pxptTœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-1S5SU",
|
||||
"targetHandle": "{œfieldNameœ: œuser_inputœ, œidœ: œPrompt-1S5SUœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-ChatInput-Y6mi1{œdataTypeœ:œChatInputœ,œidœ:œChatInput-Y6mi1œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-Z4WYI{œfieldNameœ:œuser_inputœ,œidœ:œPrompt-Z4WYIœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "ChatInput-Y6mi1",
|
||||
"sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-Y6mi1œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-Z4WYI",
|
||||
"targetHandle": "{œfieldNameœ: œuser_inputœ, œidœ: œPrompt-Z4WYIœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-1S5SU",
|
||||
"id": "Prompt-Z4WYI",
|
||||
"name": "prompt",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -39,24 +41,24 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-nJXWj",
|
||||
"id": "OpenAIModel-26Eve",
|
||||
"inputTypes": [
|
||||
"Message"
|
||||
],
|
||||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-Prompt-1S5SU{œdataTypeœ:œPromptœ,œidœ:œPrompt-1S5SUœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-nJXWj{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-nJXWjœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "Prompt-1S5SU",
|
||||
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-1S5SUœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "OpenAIModel-nJXWj",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-nJXWjœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-Z4WYI{œdataTypeœ:œPromptœ,œidœ:œPrompt-Z4WYIœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-26Eve{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-26Eveœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "Prompt-Z4WYI",
|
||||
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-Z4WYIœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "OpenAIModel-26Eve",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-26Eveœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-nJXWj",
|
||||
"dataType": "OpenAIModelComponent",
|
||||
"id": "OpenAIModel-26Eve",
|
||||
"name": "text_output",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -64,24 +66,24 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-XP4bj",
|
||||
"id": "ChatOutput-cQnVI",
|
||||
"inputTypes": [
|
||||
"Message"
|
||||
],
|
||||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-OpenAIModel-nJXWj{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-nJXWjœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-XP4bj{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-XP4bjœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "OpenAIModel-nJXWj",
|
||||
"sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-nJXWjœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "ChatOutput-XP4bj",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-XP4bjœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-26Eve{œdataTypeœ:œOpenAIModelComponentœ,œidœ:œOpenAIModel-26Eveœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-cQnVI{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-cQnVIœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "OpenAIModel-26Eve",
|
||||
"sourceHandle": "{œdataTypeœ: œOpenAIModelComponentœ, œidœ: œOpenAIModel-26Eveœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "ChatOutput-cQnVI",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-cQnVIœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"data": {
|
||||
"id": "ChatInput-pxptT",
|
||||
"id": "ChatInput-Y6mi1",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -264,7 +266,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 308,
|
||||
"id": "ChatInput-pxptT",
|
||||
"id": "ChatInput-Y6mi1",
|
||||
"position": {
|
||||
"x": -493.6459512396177,
|
||||
"y": 1083.200545525551
|
||||
|
|
@ -281,7 +283,7 @@
|
|||
"data": {
|
||||
"description": "Create a prompt template with dynamic variables.",
|
||||
"display_name": "Prompt",
|
||||
"id": "Prompt-1S5SU",
|
||||
"id": "Prompt-Z4WYI",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -389,7 +391,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 422,
|
||||
"id": "Prompt-1S5SU",
|
||||
"id": "Prompt-Z4WYI",
|
||||
"position": {
|
||||
"x": 56.354011530798516,
|
||||
"y": 1157.2005405164796
|
||||
|
|
@ -404,7 +406,10 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "OpenAIModel-nJXWj",
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"display_name": "OpenAI",
|
||||
"edited": false,
|
||||
"id": "OpenAIModel-26Eve",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"LanguageModel",
|
||||
|
|
@ -416,11 +421,12 @@
|
|||
"description": "Generates text using OpenAI LLMs.",
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"edited": false,
|
||||
"edited": true,
|
||||
"field_order": [
|
||||
"input_value",
|
||||
"max_tokens",
|
||||
"model_kwargs",
|
||||
"json_mode",
|
||||
"output_schema",
|
||||
"model_name",
|
||||
"openai_api_base",
|
||||
|
|
@ -477,7 +483,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "code",
|
||||
"value": "import operator\nfrom functools import reduce\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 LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n MessageInput(name=\"input_value\", display_name=\"Input\"),\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 DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\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. You 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 IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n # self.output_schea is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict)\n seed = self.seed\n model_kwargs[\"seed\"] = seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\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 or 0.1,\n )\n if json_mode:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n"
|
||||
"value": "import operator\nfrom functools import reduce\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 LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n MessageInput(name=\"input_value\", display_name=\"Input\"),\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 BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\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. You 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 IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n # self.output_schea is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n model_kwargs[\"seed\"] = seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\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 or 0.1,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n else:\n output = output.bind(response_format={\"type\": \"json_object\"}) # type: ignore\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n"
|
||||
},
|
||||
"input_value": {
|
||||
"advanced": false,
|
||||
|
|
@ -499,6 +505,21 @@
|
|||
"type": "str",
|
||||
"value": ""
|
||||
},
|
||||
"json_mode": {
|
||||
"advanced": true,
|
||||
"display_name": "JSON Mode",
|
||||
"dynamic": false,
|
||||
"info": "If True, it will output JSON regardless of passing a schema.",
|
||||
"list": false,
|
||||
"name": "json_mode",
|
||||
"placeholder": "",
|
||||
"required": false,
|
||||
"show": true,
|
||||
"title_case": false,
|
||||
"trace_as_metadata": true,
|
||||
"type": "bool",
|
||||
"value": false
|
||||
},
|
||||
"max_tokens": {
|
||||
"advanced": true,
|
||||
"display_name": "Max Tokens",
|
||||
|
|
@ -572,7 +593,7 @@
|
|||
"dynamic": false,
|
||||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"input_types": [],
|
||||
"load_from_db": false,
|
||||
"load_from_db": true,
|
||||
"name": "openai_api_key",
|
||||
"password": true,
|
||||
"placeholder": "",
|
||||
|
|
@ -580,7 +601,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "str",
|
||||
"value": ""
|
||||
"value": "OPENAI_API_KEY"
|
||||
},
|
||||
"output_schema": {
|
||||
"advanced": true,
|
||||
|
|
@ -660,11 +681,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"type": "OpenAIModel"
|
||||
"type": "OpenAIModelComponent"
|
||||
},
|
||||
"dragging": false,
|
||||
"height": 621,
|
||||
"id": "OpenAIModel-nJXWj",
|
||||
"id": "OpenAIModel-26Eve",
|
||||
"position": {
|
||||
"x": 624.3539730827923,
|
||||
"y": 1053.2005475562555
|
||||
|
|
@ -679,7 +700,7 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "ChatOutput-XP4bj",
|
||||
"id": "ChatOutput-cQnVI",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -839,7 +860,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 308,
|
||||
"id": "ChatOutput-XP4bj",
|
||||
"id": "ChatOutput-cQnVI",
|
||||
"position": {
|
||||
"x": 1219.477374823274,
|
||||
"y": 1200.950216973985
|
||||
|
|
@ -854,15 +875,15 @@
|
|||
}
|
||||
],
|
||||
"viewport": {
|
||||
"x": 392.1085223509972,
|
||||
"y": -327.49805229761307,
|
||||
"zoom": 0.5000000676901589
|
||||
"x": 366.93776265249005,
|
||||
"y": -343.56726676261223,
|
||||
"zoom": 0.5000000676901587
|
||||
}
|
||||
},
|
||||
"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: \" ",
|
||||
"endpoint_name": null,
|
||||
"id": "f652abdc-7ef2-4e52-a00b-847b7aa32cee",
|
||||
"id": "e533253b-818b-4b5a-9793-55ab83fffb07",
|
||||
"is_component": false,
|
||||
"last_tested_version": "1.0.0rc1",
|
||||
"last_tested_version": "1.0.5",
|
||||
"name": "Basic Prompting (Hello, World)"
|
||||
}
|
||||
|
|
@ -2,10 +2,11 @@
|
|||
"data": {
|
||||
"edges": [
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "URL",
|
||||
"id": "URL-k9NkE",
|
||||
"id": "URL-rETJU",
|
||||
"name": "data",
|
||||
"output_types": [
|
||||
"Data"
|
||||
|
|
@ -13,24 +14,25 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "data",
|
||||
"id": "ParseData-EwWXd",
|
||||
"id": "ParseData-AqSfN",
|
||||
"inputTypes": [
|
||||
"Data"
|
||||
],
|
||||
"type": "other"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-URL-k9NkE{œdataTypeœ:œURLœ,œidœ:œURL-k9NkEœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-ParseData-EwWXd{œfieldNameœ:œdataœ,œidœ:œParseData-EwWXdœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
|
||||
"source": "URL-k9NkE",
|
||||
"sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-k9NkEœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}",
|
||||
"target": "ParseData-EwWXd",
|
||||
"targetHandle": "{œfieldNameœ: œdataœ, œidœ: œParseData-EwWXdœ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}"
|
||||
"id": "reactflow__edge-URL-rETJU{œdataTypeœ:œURLœ,œidœ:œURL-rETJUœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-ParseData-AqSfN{œfieldNameœ:œdataœ,œidœ:œParseData-AqSfNœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
|
||||
"source": "URL-rETJU",
|
||||
"sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-rETJUœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}",
|
||||
"target": "ParseData-AqSfN",
|
||||
"targetHandle": "{œfieldNameœ: œdataœ, œidœ: œParseData-AqSfNœ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}"
|
||||
},
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "ParseData",
|
||||
"id": "ParseData-EwWXd",
|
||||
"id": "ParseData-AqSfN",
|
||||
"name": "text",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -38,7 +40,7 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "references",
|
||||
"id": "Prompt-B9Mq6",
|
||||
"id": "Prompt-rizUK",
|
||||
"inputTypes": [
|
||||
"Message",
|
||||
"Text"
|
||||
|
|
@ -46,17 +48,18 @@
|
|||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-ParseData-EwWXd{œdataTypeœ:œParseDataœ,œidœ:œParseData-EwWXdœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-B9Mq6{œfieldNameœ:œreferencesœ,œidœ:œPrompt-B9Mq6œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "ParseData-EwWXd",
|
||||
"sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-EwWXdœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-B9Mq6",
|
||||
"targetHandle": "{œfieldNameœ: œreferencesœ, œidœ: œPrompt-B9Mq6œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-ParseData-AqSfN{œdataTypeœ:œParseDataœ,œidœ:œParseData-AqSfNœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-rizUK{œfieldNameœ:œreferencesœ,œidœ:œPrompt-rizUKœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "ParseData-AqSfN",
|
||||
"sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-AqSfNœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-rizUK",
|
||||
"targetHandle": "{œfieldNameœ: œreferencesœ, œidœ: œPrompt-rizUKœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "TextInput",
|
||||
"id": "TextInput-uf6ij",
|
||||
"id": "TextInput-OffFR",
|
||||
"name": "text",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -64,7 +67,7 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "instructions",
|
||||
"id": "Prompt-B9Mq6",
|
||||
"id": "Prompt-rizUK",
|
||||
"inputTypes": [
|
||||
"Message",
|
||||
"Text"
|
||||
|
|
@ -72,17 +75,18 @@
|
|||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-TextInput-uf6ij{œdataTypeœ:œTextInputœ,œidœ:œTextInput-uf6ijœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-B9Mq6{œfieldNameœ:œinstructionsœ,œidœ:œPrompt-B9Mq6œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "TextInput-uf6ij",
|
||||
"sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-uf6ijœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-B9Mq6",
|
||||
"targetHandle": "{œfieldNameœ: œinstructionsœ, œidœ: œPrompt-B9Mq6œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-TextInput-OffFR{œdataTypeœ:œTextInputœ,œidœ:œTextInput-OffFRœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-rizUK{œfieldNameœ:œinstructionsœ,œidœ:œPrompt-rizUKœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "TextInput-OffFR",
|
||||
"sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-OffFRœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-rizUK",
|
||||
"targetHandle": "{œfieldNameœ: œinstructionsœ, œidœ: œPrompt-rizUKœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-B9Mq6",
|
||||
"id": "Prompt-rizUK",
|
||||
"name": "prompt",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -90,24 +94,24 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-X9ukk",
|
||||
"id": "OpenAIModel-qmhKV",
|
||||
"inputTypes": [
|
||||
"Message"
|
||||
],
|
||||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-Prompt-B9Mq6{œdataTypeœ:œPromptœ,œidœ:œPrompt-B9Mq6œ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-X9ukk{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-X9ukkœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "Prompt-B9Mq6",
|
||||
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-B9Mq6œ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "OpenAIModel-X9ukk",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-X9ukkœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-rizUK{œdataTypeœ:œPromptœ,œidœ:œPrompt-rizUKœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-qmhKV{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-qmhKVœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "Prompt-rizUK",
|
||||
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-rizUKœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "OpenAIModel-qmhKV",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-qmhKVœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-X9ukk",
|
||||
"dataType": "OpenAIModelComponent",
|
||||
"id": "OpenAIModel-qmhKV",
|
||||
"name": "text_output",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -115,24 +119,24 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-5r5Iw",
|
||||
"id": "ChatOutput-W684s",
|
||||
"inputTypes": [
|
||||
"Message"
|
||||
],
|
||||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-OpenAIModel-X9ukk{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-X9ukkœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-5r5Iw{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-5r5Iwœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "OpenAIModel-X9ukk",
|
||||
"sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-X9ukkœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "ChatOutput-5r5Iw",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-5r5Iwœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-qmhKV{œdataTypeœ:œOpenAIModelComponentœ,œidœ:œOpenAIModel-qmhKVœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-W684s{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-W684sœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "OpenAIModel-qmhKV",
|
||||
"sourceHandle": "{œdataTypeœ: œOpenAIModelComponentœ, œidœ: œOpenAIModel-qmhKVœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "ChatOutput-W684s",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-W684sœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"data": {
|
||||
"id": "URL-k9NkE",
|
||||
"id": "URL-rETJU",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Data"
|
||||
|
|
@ -214,7 +218,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 358,
|
||||
"id": "URL-k9NkE",
|
||||
"id": "URL-rETJU",
|
||||
"position": {
|
||||
"x": 220.79156431407534,
|
||||
"y": 498.8186168722667
|
||||
|
|
@ -229,7 +233,7 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "ParseData-EwWXd",
|
||||
"id": "ParseData-AqSfN",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -346,7 +350,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 384,
|
||||
"id": "ParseData-EwWXd",
|
||||
"id": "ParseData-AqSfN",
|
||||
"position": {
|
||||
"x": 754.3607306709101,
|
||||
"y": 736.8516961537598
|
||||
|
|
@ -363,7 +367,7 @@
|
|||
"data": {
|
||||
"description": "Create a prompt template with dynamic variables.",
|
||||
"display_name": "Prompt",
|
||||
"id": "Prompt-B9Mq6",
|
||||
"id": "Prompt-rizUK",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -496,7 +500,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 515,
|
||||
"id": "Prompt-B9Mq6",
|
||||
"id": "Prompt-rizUK",
|
||||
"position": {
|
||||
"x": 1368.0633591447076,
|
||||
"y": 467.19448061224284
|
||||
|
|
@ -511,7 +515,7 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "TextInput-uf6ij",
|
||||
"id": "TextInput-OffFR",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -590,7 +594,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 308,
|
||||
"id": "TextInput-uf6ij",
|
||||
"id": "TextInput-OffFR",
|
||||
"position": {
|
||||
"x": 743.7338453293725,
|
||||
"y": 301.58775454952183
|
||||
|
|
@ -605,7 +609,10 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "OpenAIModel-X9ukk",
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"display_name": "OpenAI",
|
||||
"edited": false,
|
||||
"id": "OpenAIModel-qmhKV",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"LanguageModel",
|
||||
|
|
@ -617,11 +624,12 @@
|
|||
"description": "Generates text using OpenAI LLMs.",
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"edited": false,
|
||||
"edited": true,
|
||||
"field_order": [
|
||||
"input_value",
|
||||
"max_tokens",
|
||||
"model_kwargs",
|
||||
"json_mode",
|
||||
"output_schema",
|
||||
"model_name",
|
||||
"openai_api_base",
|
||||
|
|
@ -678,7 +686,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "code",
|
||||
"value": "import operator\nfrom functools import reduce\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 LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n MessageInput(name=\"input_value\", display_name=\"Input\"),\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 DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\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. You 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 IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n # self.output_schea is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict)\n seed = self.seed\n model_kwargs[\"seed\"] = seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\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 or 0.1,\n )\n if json_mode:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n"
|
||||
"value": "import operator\nfrom functools import reduce\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 LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n MessageInput(name=\"input_value\", display_name=\"Input\"),\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 BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\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. You 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 IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n # self.output_schea is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n model_kwargs[\"seed\"] = seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\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 or 0.1,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n else:\n output = output.bind(response_format={\"type\": \"json_object\"}) # type: ignore\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n"
|
||||
},
|
||||
"input_value": {
|
||||
"advanced": false,
|
||||
|
|
@ -700,6 +708,21 @@
|
|||
"type": "str",
|
||||
"value": ""
|
||||
},
|
||||
"json_mode": {
|
||||
"advanced": true,
|
||||
"display_name": "JSON Mode",
|
||||
"dynamic": false,
|
||||
"info": "If True, it will output JSON regardless of passing a schema.",
|
||||
"list": false,
|
||||
"name": "json_mode",
|
||||
"placeholder": "",
|
||||
"required": false,
|
||||
"show": true,
|
||||
"title_case": false,
|
||||
"trace_as_metadata": true,
|
||||
"type": "bool",
|
||||
"value": false
|
||||
},
|
||||
"max_tokens": {
|
||||
"advanced": true,
|
||||
"display_name": "Max Tokens",
|
||||
|
|
@ -773,7 +796,7 @@
|
|||
"dynamic": false,
|
||||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"input_types": [],
|
||||
"load_from_db": false,
|
||||
"load_from_db": true,
|
||||
"name": "openai_api_key",
|
||||
"password": true,
|
||||
"placeholder": "",
|
||||
|
|
@ -781,7 +804,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "str",
|
||||
"value": ""
|
||||
"value": "OPENAI_API_KEY"
|
||||
},
|
||||
"output_schema": {
|
||||
"advanced": true,
|
||||
|
|
@ -861,11 +884,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"type": "OpenAIModel"
|
||||
"type": "OpenAIModelComponent"
|
||||
},
|
||||
"dragging": false,
|
||||
"height": 621,
|
||||
"id": "OpenAIModel-X9ukk",
|
||||
"id": "OpenAIModel-qmhKV",
|
||||
"position": {
|
||||
"x": 1899.407626221589,
|
||||
"y": 395.9013619556682
|
||||
|
|
@ -880,7 +903,7 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "ChatOutput-5r5Iw",
|
||||
"id": "ChatOutput-W684s",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -1040,7 +1063,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 308,
|
||||
"id": "ChatOutput-5r5Iw",
|
||||
"id": "ChatOutput-W684s",
|
||||
"position": {
|
||||
"x": 2449.3489426461606,
|
||||
"y": 571.2449700910389
|
||||
|
|
@ -1062,8 +1085,8 @@
|
|||
},
|
||||
"description": "This flow can be used to create a blog post following instructions from the user, using two other blogs as reference.",
|
||||
"endpoint_name": null,
|
||||
"id": "13da3150-95b9-4d81-9ad2-f635dcdce7ab",
|
||||
"id": "da9999f8-9013-4bd9-8adb-653c94ebf08c",
|
||||
"is_component": false,
|
||||
"last_tested_version": "1.0.0rc1",
|
||||
"last_tested_version": "1.0.5",
|
||||
"name": "Blog Writer"
|
||||
}
|
||||
|
|
@ -2,10 +2,11 @@
|
|||
"data": {
|
||||
"edges": [
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "File",
|
||||
"id": "File-h46aK",
|
||||
"id": "File-Q3Xrb",
|
||||
"name": "data",
|
||||
"output_types": [
|
||||
"Data"
|
||||
|
|
@ -13,24 +14,25 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "data",
|
||||
"id": "ParseData-sqVr1",
|
||||
"id": "ParseData-1Y5jJ",
|
||||
"inputTypes": [
|
||||
"Data"
|
||||
],
|
||||
"type": "other"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-File-h46aK{œdataTypeœ:œFileœ,œidœ:œFile-h46aKœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-ParseData-sqVr1{œfieldNameœ:œdataœ,œidœ:œParseData-sqVr1œ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
|
||||
"source": "File-h46aK",
|
||||
"sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-h46aKœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}",
|
||||
"target": "ParseData-sqVr1",
|
||||
"targetHandle": "{œfieldNameœ: œdataœ, œidœ: œParseData-sqVr1œ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}"
|
||||
"id": "reactflow__edge-File-Q3Xrb{œdataTypeœ:œFileœ,œidœ:œFile-Q3Xrbœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-ParseData-1Y5jJ{œfieldNameœ:œdataœ,œidœ:œParseData-1Y5jJœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
|
||||
"source": "File-Q3Xrb",
|
||||
"sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-Q3Xrbœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}",
|
||||
"target": "ParseData-1Y5jJ",
|
||||
"targetHandle": "{œfieldNameœ: œdataœ, œidœ: œParseData-1Y5jJœ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}"
|
||||
},
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "ParseData",
|
||||
"id": "ParseData-sqVr1",
|
||||
"id": "ParseData-1Y5jJ",
|
||||
"name": "text",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -38,7 +40,7 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "Document",
|
||||
"id": "Prompt-mQ7w2",
|
||||
"id": "Prompt-CMJEB",
|
||||
"inputTypes": [
|
||||
"Message",
|
||||
"Text"
|
||||
|
|
@ -46,17 +48,18 @@
|
|||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-ParseData-sqVr1{œdataTypeœ:œParseDataœ,œidœ:œParseData-sqVr1œ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-mQ7w2{œfieldNameœ:œDocumentœ,œidœ:œPrompt-mQ7w2œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "ParseData-sqVr1",
|
||||
"sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-sqVr1œ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-mQ7w2",
|
||||
"targetHandle": "{œfieldNameœ: œDocumentœ, œidœ: œPrompt-mQ7w2œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-ParseData-1Y5jJ{œdataTypeœ:œParseDataœ,œidœ:œParseData-1Y5jJœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-CMJEB{œfieldNameœ:œDocumentœ,œidœ:œPrompt-CMJEBœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "ParseData-1Y5jJ",
|
||||
"sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-1Y5jJœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-CMJEB",
|
||||
"targetHandle": "{œfieldNameœ: œDocumentœ, œidœ: œPrompt-CMJEBœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "ChatInput",
|
||||
"id": "ChatInput-cMXe0",
|
||||
"id": "ChatInput-mc7sJ",
|
||||
"name": "message",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -64,7 +67,7 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "Question",
|
||||
"id": "Prompt-mQ7w2",
|
||||
"id": "Prompt-CMJEB",
|
||||
"inputTypes": [
|
||||
"Message",
|
||||
"Text"
|
||||
|
|
@ -72,17 +75,18 @@
|
|||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-ChatInput-cMXe0{œdataTypeœ:œChatInputœ,œidœ:œChatInput-cMXe0œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-mQ7w2{œfieldNameœ:œQuestionœ,œidœ:œPrompt-mQ7w2œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "ChatInput-cMXe0",
|
||||
"sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-cMXe0œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-mQ7w2",
|
||||
"targetHandle": "{œfieldNameœ: œQuestionœ, œidœ: œPrompt-mQ7w2œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-ChatInput-mc7sJ{œdataTypeœ:œChatInputœ,œidœ:œChatInput-mc7sJœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-CMJEB{œfieldNameœ:œQuestionœ,œidœ:œPrompt-CMJEBœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "ChatInput-mc7sJ",
|
||||
"sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-mc7sJœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-CMJEB",
|
||||
"targetHandle": "{œfieldNameœ: œQuestionœ, œidœ: œPrompt-CMJEBœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-mQ7w2",
|
||||
"id": "Prompt-CMJEB",
|
||||
"name": "prompt",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -90,24 +94,24 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-O0AGC",
|
||||
"id": "OpenAIModel-U2g5u",
|
||||
"inputTypes": [
|
||||
"Message"
|
||||
],
|
||||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-Prompt-mQ7w2{œdataTypeœ:œPromptœ,œidœ:œPrompt-mQ7w2œ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-O0AGC{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-O0AGCœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "Prompt-mQ7w2",
|
||||
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-mQ7w2œ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "OpenAIModel-O0AGC",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-O0AGCœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-CMJEB{œdataTypeœ:œPromptœ,œidœ:œPrompt-CMJEBœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-U2g5u{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-U2g5uœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "Prompt-CMJEB",
|
||||
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-CMJEBœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "OpenAIModel-U2g5u",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-U2g5uœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-O0AGC",
|
||||
"dataType": "OpenAIModelComponent",
|
||||
"id": "OpenAIModel-U2g5u",
|
||||
"name": "text_output",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -115,24 +119,24 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-efggd",
|
||||
"id": "ChatOutput-yZjPO",
|
||||
"inputTypes": [
|
||||
"Message"
|
||||
],
|
||||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-OpenAIModel-O0AGC{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-O0AGCœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-efggd{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-efggdœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "OpenAIModel-O0AGC",
|
||||
"sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-O0AGCœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "ChatOutput-efggd",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-efggdœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-U2g5u{œdataTypeœ:œOpenAIModelComponentœ,œidœ:œOpenAIModel-U2g5uœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-yZjPO{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-yZjPOœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "OpenAIModel-U2g5u",
|
||||
"sourceHandle": "{œdataTypeœ: œOpenAIModelComponentœ, œidœ: œOpenAIModel-U2g5uœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "ChatOutput-yZjPO",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-yZjPOœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"data": {
|
||||
"id": "File-h46aK",
|
||||
"id": "File-Q3Xrb",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Data"
|
||||
|
|
@ -243,7 +247,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 300,
|
||||
"id": "File-h46aK",
|
||||
"id": "File-Q3Xrb",
|
||||
"position": {
|
||||
"x": -449.0807503257012,
|
||||
"y": -253.5304920926106
|
||||
|
|
@ -258,7 +262,7 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "ParseData-sqVr1",
|
||||
"id": "ParseData-1Y5jJ",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -375,7 +379,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 384,
|
||||
"id": "ParseData-sqVr1",
|
||||
"id": "ParseData-1Y5jJ",
|
||||
"position": {
|
||||
"x": 73.79471204296345,
|
||||
"y": -186.9430114986888
|
||||
|
|
@ -392,7 +396,7 @@
|
|||
"data": {
|
||||
"description": "Create a prompt template with dynamic variables.",
|
||||
"display_name": "Prompt",
|
||||
"id": "Prompt-mQ7w2",
|
||||
"id": "Prompt-CMJEB",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -525,7 +529,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 515,
|
||||
"id": "Prompt-mQ7w2",
|
||||
"id": "Prompt-CMJEB",
|
||||
"position": {
|
||||
"x": 637.3518652087848,
|
||||
"y": 47.191730368560215
|
||||
|
|
@ -540,7 +544,7 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "ChatInput-cMXe0",
|
||||
"id": "ChatInput-mc7sJ",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -723,7 +727,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 308,
|
||||
"id": "ChatInput-cMXe0",
|
||||
"id": "ChatInput-mc7sJ",
|
||||
"position": {
|
||||
"x": 50.08709924122684,
|
||||
"y": 320.88186720121615
|
||||
|
|
@ -738,7 +742,10 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "OpenAIModel-O0AGC",
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"display_name": "OpenAI",
|
||||
"edited": false,
|
||||
"id": "OpenAIModel-U2g5u",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"LanguageModel",
|
||||
|
|
@ -750,11 +757,12 @@
|
|||
"description": "Generates text using OpenAI LLMs.",
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"edited": false,
|
||||
"edited": true,
|
||||
"field_order": [
|
||||
"input_value",
|
||||
"max_tokens",
|
||||
"model_kwargs",
|
||||
"json_mode",
|
||||
"output_schema",
|
||||
"model_name",
|
||||
"openai_api_base",
|
||||
|
|
@ -811,7 +819,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "code",
|
||||
"value": "import operator\nfrom functools import reduce\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 LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n MessageInput(name=\"input_value\", display_name=\"Input\"),\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 DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\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. You 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 IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n # self.output_schea is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict)\n seed = self.seed\n model_kwargs[\"seed\"] = seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\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 or 0.1,\n )\n if json_mode:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n"
|
||||
"value": "import operator\nfrom functools import reduce\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 LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n MessageInput(name=\"input_value\", display_name=\"Input\"),\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 BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\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. You 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 IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n # self.output_schea is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n model_kwargs[\"seed\"] = seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\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 or 0.1,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n else:\n output = output.bind(response_format={\"type\": \"json_object\"}) # type: ignore\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n"
|
||||
},
|
||||
"input_value": {
|
||||
"advanced": false,
|
||||
|
|
@ -833,6 +841,21 @@
|
|||
"type": "str",
|
||||
"value": ""
|
||||
},
|
||||
"json_mode": {
|
||||
"advanced": true,
|
||||
"display_name": "JSON Mode",
|
||||
"dynamic": false,
|
||||
"info": "If True, it will output JSON regardless of passing a schema.",
|
||||
"list": false,
|
||||
"name": "json_mode",
|
||||
"placeholder": "",
|
||||
"required": false,
|
||||
"show": true,
|
||||
"title_case": false,
|
||||
"trace_as_metadata": true,
|
||||
"type": "bool",
|
||||
"value": false
|
||||
},
|
||||
"max_tokens": {
|
||||
"advanced": true,
|
||||
"display_name": "Max Tokens",
|
||||
|
|
@ -906,7 +929,7 @@
|
|||
"dynamic": false,
|
||||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"input_types": [],
|
||||
"load_from_db": false,
|
||||
"load_from_db": true,
|
||||
"name": "openai_api_key",
|
||||
"password": true,
|
||||
"placeholder": "",
|
||||
|
|
@ -914,7 +937,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "str",
|
||||
"value": ""
|
||||
"value": "OPENAI_API_KEY"
|
||||
},
|
||||
"output_schema": {
|
||||
"advanced": true,
|
||||
|
|
@ -994,26 +1017,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"type": "OpenAIModel"
|
||||
"type": "OpenAIModelComponent"
|
||||
},
|
||||
"dragging": false,
|
||||
"height": 621,
|
||||
"id": "OpenAIModel-O0AGC",
|
||||
"id": "OpenAIModel-U2g5u",
|
||||
"position": {
|
||||
"x": 1227.3672858178775,
|
||||
"y": 11.61201090144857
|
||||
"x": 1249.1992451905348,
|
||||
"y": 2.8792271523856243
|
||||
},
|
||||
"positionAbsolute": {
|
||||
"x": 1227.3672858178775,
|
||||
"y": 11.61201090144857
|
||||
"x": 1249.1992451905348,
|
||||
"y": 2.8792271523856243
|
||||
},
|
||||
"selected": false,
|
||||
"selected": true,
|
||||
"type": "genericNode",
|
||||
"width": 384
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "ChatOutput-efggd",
|
||||
"id": "ChatOutput-yZjPO",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -1173,7 +1196,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 308,
|
||||
"id": "ChatOutput-efggd",
|
||||
"id": "ChatOutput-yZjPO",
|
||||
"position": {
|
||||
"x": 1831.1359796346408,
|
||||
"y": 139.5174517327903
|
||||
|
|
@ -1188,15 +1211,15 @@
|
|||
}
|
||||
],
|
||||
"viewport": {
|
||||
"x": 249.03047748371796,
|
||||
"y": 251.71203687916693,
|
||||
"x": 252.03047748371796,
|
||||
"y": 253.71203687916693,
|
||||
"zoom": 0.4580440916596844
|
||||
}
|
||||
},
|
||||
"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.",
|
||||
"endpoint_name": null,
|
||||
"id": "4b4cbf9e-34fe-4613-a460-3b7af89b7788",
|
||||
"id": "483d5200-b59b-4afa-a71f-52fcfcde8fca",
|
||||
"is_component": false,
|
||||
"last_tested_version": "1.0.0rc1",
|
||||
"last_tested_version": "1.0.5",
|
||||
"name": "Document QA"
|
||||
}
|
||||
|
|
@ -2,10 +2,11 @@
|
|||
"data": {
|
||||
"edges": [
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "Memory",
|
||||
"id": "Memory-uy2TA",
|
||||
"id": "Memory-VIq7F",
|
||||
"name": "messages_text",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "context",
|
||||
"id": "Prompt-m9rUs",
|
||||
"id": "Prompt-gEaWL",
|
||||
"inputTypes": [
|
||||
"Message",
|
||||
"Text"
|
||||
|
|
@ -21,17 +22,18 @@
|
|||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-Memory-uy2TA{œdataTypeœ:œMemoryœ,œidœ:œMemory-uy2TAœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-m9rUs{œfieldNameœ:œcontextœ,œidœ:œPrompt-m9rUsœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "Memory-uy2TA",
|
||||
"sourceHandle": "{œdataTypeœ: œMemoryœ, œidœ: œMemory-uy2TAœ, œnameœ: œmessages_textœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-m9rUs",
|
||||
"targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-m9rUsœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-Memory-VIq7F{œdataTypeœ:œMemoryœ,œidœ:œMemory-VIq7Fœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-gEaWL{œfieldNameœ:œcontextœ,œidœ:œPrompt-gEaWLœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "Memory-VIq7F",
|
||||
"sourceHandle": "{œdataTypeœ: œMemoryœ, œidœ: œMemory-VIq7Fœ, œnameœ: œmessages_textœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-gEaWL",
|
||||
"targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-gEaWLœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "ChatInput",
|
||||
"id": "ChatInput-hSTqh",
|
||||
"id": "ChatInput-gIy9N",
|
||||
"name": "message",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -39,7 +41,7 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "user_message",
|
||||
"id": "Prompt-m9rUs",
|
||||
"id": "Prompt-gEaWL",
|
||||
"inputTypes": [
|
||||
"Message",
|
||||
"Text"
|
||||
|
|
@ -47,17 +49,18 @@
|
|||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-ChatInput-hSTqh{œdataTypeœ:œChatInputœ,œidœ:œChatInput-hSTqhœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-m9rUs{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-m9rUsœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "ChatInput-hSTqh",
|
||||
"sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-hSTqhœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-m9rUs",
|
||||
"targetHandle": "{œfieldNameœ: œuser_messageœ, œidœ: œPrompt-m9rUsœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-ChatInput-gIy9N{œdataTypeœ:œChatInputœ,œidœ:œChatInput-gIy9Nœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-gEaWL{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-gEaWLœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"source": "ChatInput-gIy9N",
|
||||
"sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-gIy9Nœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "Prompt-gEaWL",
|
||||
"targetHandle": "{œfieldNameœ: œuser_messageœ, œidœ: œPrompt-gEaWLœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"className": "",
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-m9rUs",
|
||||
"id": "Prompt-gEaWL",
|
||||
"name": "prompt",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -65,24 +68,24 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-WmUtU",
|
||||
"id": "OpenAIModel-uNcAU",
|
||||
"inputTypes": [
|
||||
"Message"
|
||||
],
|
||||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-Prompt-m9rUs{œdataTypeœ:œPromptœ,œidœ:œPrompt-m9rUsœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-WmUtU{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-WmUtUœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "Prompt-m9rUs",
|
||||
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-m9rUsœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "OpenAIModel-WmUtU",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-WmUtUœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-gEaWL{œdataTypeœ:œPromptœ,œidœ:œPrompt-gEaWLœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-uNcAU{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-uNcAUœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "Prompt-gEaWL",
|
||||
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-gEaWLœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "OpenAIModel-uNcAU",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-uNcAUœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-WmUtU",
|
||||
"dataType": "OpenAIModelComponent",
|
||||
"id": "OpenAIModel-uNcAU",
|
||||
"name": "text_output",
|
||||
"output_types": [
|
||||
"Message"
|
||||
|
|
@ -90,24 +93,24 @@
|
|||
},
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-LIvGN",
|
||||
"id": "ChatOutput-KtSB9",
|
||||
"inputTypes": [
|
||||
"Message"
|
||||
],
|
||||
"type": "str"
|
||||
}
|
||||
},
|
||||
"id": "reactflow__edge-OpenAIModel-WmUtU{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-WmUtUœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-LIvGN{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-LIvGNœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "OpenAIModel-WmUtU",
|
||||
"sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-WmUtUœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "ChatOutput-LIvGN",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-LIvGNœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-uNcAU{œdataTypeœ:œOpenAIModelComponentœ,œidœ:œOpenAIModel-uNcAUœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-KtSB9{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-KtSB9œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
|
||||
"source": "OpenAIModel-uNcAU",
|
||||
"sourceHandle": "{œdataTypeœ: œOpenAIModelComponentœ, œidœ: œOpenAIModel-uNcAUœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
|
||||
"target": "ChatOutput-KtSB9",
|
||||
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-KtSB9œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"data": {
|
||||
"id": "Memory-uy2TA",
|
||||
"id": "Memory-VIq7F",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Data",
|
||||
|
|
@ -296,7 +299,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 266,
|
||||
"id": "Memory-uy2TA",
|
||||
"id": "Memory-VIq7F",
|
||||
"position": {
|
||||
"x": 1264.7588980556088,
|
||||
"y": 506.6868269980502
|
||||
|
|
@ -313,7 +316,7 @@
|
|||
"data": {
|
||||
"description": "Create a prompt template with dynamic variables.",
|
||||
"display_name": "Prompt",
|
||||
"id": "Prompt-m9rUs",
|
||||
"id": "Prompt-gEaWL",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -446,7 +449,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 515,
|
||||
"id": "Prompt-m9rUs",
|
||||
"id": "Prompt-gEaWL",
|
||||
"position": {
|
||||
"x": 1880.8227904110583,
|
||||
"y": 625.8049209882275
|
||||
|
|
@ -461,7 +464,7 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "ChatInput-hSTqh",
|
||||
"id": "ChatInput-gIy9N",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -644,7 +647,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 308,
|
||||
"id": "ChatInput-hSTqh",
|
||||
"id": "ChatInput-gIy9N",
|
||||
"position": {
|
||||
"x": 1275.9262193671882,
|
||||
"y": 836.1228056896347
|
||||
|
|
@ -659,7 +662,10 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "OpenAIModel-WmUtU",
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"display_name": "OpenAI",
|
||||
"edited": false,
|
||||
"id": "OpenAIModel-uNcAU",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"LanguageModel",
|
||||
|
|
@ -671,11 +677,12 @@
|
|||
"description": "Generates text using OpenAI LLMs.",
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"edited": false,
|
||||
"edited": true,
|
||||
"field_order": [
|
||||
"input_value",
|
||||
"max_tokens",
|
||||
"model_kwargs",
|
||||
"json_mode",
|
||||
"output_schema",
|
||||
"model_name",
|
||||
"openai_api_base",
|
||||
|
|
@ -732,7 +739,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "code",
|
||||
"value": "import operator\nfrom functools import reduce\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 LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n MessageInput(name=\"input_value\", display_name=\"Input\"),\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 DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\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. You 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 IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n # self.output_schea is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict)\n seed = self.seed\n model_kwargs[\"seed\"] = seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\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 or 0.1,\n )\n if json_mode:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n"
|
||||
"value": "import operator\nfrom functools import reduce\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 LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n inputs = [\n MessageInput(name=\"input_value\", display_name=\"Input\"),\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 BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\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. You 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 IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n # self.output_schea is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n model_kwargs[\"seed\"] = seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\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 or 0.1,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n else:\n output = output.bind(response_format={\"type\": \"json_object\"}) # type: ignore\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n"
|
||||
},
|
||||
"input_value": {
|
||||
"advanced": false,
|
||||
|
|
@ -754,6 +761,21 @@
|
|||
"type": "str",
|
||||
"value": ""
|
||||
},
|
||||
"json_mode": {
|
||||
"advanced": true,
|
||||
"display_name": "JSON Mode",
|
||||
"dynamic": false,
|
||||
"info": "If True, it will output JSON regardless of passing a schema.",
|
||||
"list": false,
|
||||
"name": "json_mode",
|
||||
"placeholder": "",
|
||||
"required": false,
|
||||
"show": true,
|
||||
"title_case": false,
|
||||
"trace_as_metadata": true,
|
||||
"type": "bool",
|
||||
"value": false
|
||||
},
|
||||
"max_tokens": {
|
||||
"advanced": true,
|
||||
"display_name": "Max Tokens",
|
||||
|
|
@ -827,7 +849,7 @@
|
|||
"dynamic": false,
|
||||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"input_types": [],
|
||||
"load_from_db": false,
|
||||
"load_from_db": true,
|
||||
"name": "openai_api_key",
|
||||
"password": true,
|
||||
"placeholder": "",
|
||||
|
|
@ -835,7 +857,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "str",
|
||||
"value": ""
|
||||
"value": "OPENAI_API_KEY"
|
||||
},
|
||||
"output_schema": {
|
||||
"advanced": true,
|
||||
|
|
@ -915,11 +937,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"type": "OpenAIModel"
|
||||
"type": "OpenAIModelComponent"
|
||||
},
|
||||
"dragging": false,
|
||||
"height": 621,
|
||||
"id": "OpenAIModel-WmUtU",
|
||||
"id": "OpenAIModel-uNcAU",
|
||||
"position": {
|
||||
"x": 2428.0215346784357,
|
||||
"y": 569.9683144303319
|
||||
|
|
@ -934,7 +956,7 @@
|
|||
},
|
||||
{
|
||||
"data": {
|
||||
"id": "ChatOutput-LIvGN",
|
||||
"id": "ChatOutput-KtSB9",
|
||||
"node": {
|
||||
"base_classes": [
|
||||
"Message"
|
||||
|
|
@ -1094,7 +1116,7 @@
|
|||
},
|
||||
"dragging": false,
|
||||
"height": 308,
|
||||
"id": "ChatOutput-LIvGN",
|
||||
"id": "ChatOutput-KtSB9",
|
||||
"position": {
|
||||
"x": 2988.248820475989,
|
||||
"y": 705.837390387878
|
||||
|
|
@ -1116,8 +1138,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.",
|
||||
"endpoint_name": null,
|
||||
"id": "2a47bc35-69ca-4d8b-9895-2a7fab222b9f",
|
||||
"id": "16c029a0-0d89-4c36-8a8c-e5410206df38",
|
||||
"is_component": false,
|
||||
"last_tested_version": "1.0.0rc1",
|
||||
"last_tested_version": "1.0.5",
|
||||
"name": "Memory Chatbot"
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,20 +1,24 @@
|
|||
import warnings
|
||||
from typing import List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from loguru import logger
|
||||
from sqlalchemy import delete
|
||||
from sqlmodel import Session, col, select
|
||||
|
||||
from langflow.schema.message import Message
|
||||
from langflow.services.deps import get_monitor_service
|
||||
from langflow.services.monitor.schema import MessageModel
|
||||
from langflow.services.database.models.message.model import MessageRead, MessageTable
|
||||
from langflow.services.database.utils import migrate_messages_from_monitor_service_to_database
|
||||
from langflow.services.deps import session_scope
|
||||
|
||||
|
||||
def get_messages(
|
||||
sender: Optional[str] = None,
|
||||
sender_name: Optional[str] = None,
|
||||
session_id: Optional[str] = None,
|
||||
order_by: Optional[str] = "timestamp",
|
||||
order: Optional[str] = "DESC",
|
||||
limit: Optional[int] = None,
|
||||
sender: str | None = None,
|
||||
sender_name: str | None = None,
|
||||
session_id: str | None = None,
|
||||
order_by: str | None = "timestamp",
|
||||
order: str | None = "DESC",
|
||||
flow_id: UUID | None = None,
|
||||
limit: int | None = None,
|
||||
):
|
||||
"""
|
||||
Retrieves messages from the monitor service based on the provided filters.
|
||||
|
|
@ -29,42 +33,38 @@ def get_messages(
|
|||
Returns:
|
||||
List[Data]: A list of Data objects representing the retrieved messages.
|
||||
"""
|
||||
monitor_service = get_monitor_service()
|
||||
messages_df = monitor_service.get_messages(
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
session_id=session_id,
|
||||
order_by=order_by,
|
||||
limit=limit,
|
||||
order=order,
|
||||
)
|
||||
with session_scope() as session:
|
||||
migrate_messages_from_monitor_service_to_database(session)
|
||||
messages_read: list[Message] = []
|
||||
with session_scope() as session:
|
||||
stmt = select(MessageTable)
|
||||
if sender:
|
||||
stmt = stmt.where(MessageTable.sender == sender)
|
||||
if sender_name:
|
||||
stmt = stmt.where(MessageTable.sender_name == sender_name)
|
||||
if session_id:
|
||||
stmt = stmt.where(MessageTable.session_id == session_id)
|
||||
if flow_id:
|
||||
stmt = stmt.where(MessageTable.flow_id == flow_id)
|
||||
if order_by:
|
||||
if order == "DESC":
|
||||
col = getattr(MessageTable, order_by).desc()
|
||||
else:
|
||||
col = getattr(MessageTable, order_by).asc()
|
||||
stmt = stmt.order_by(col)
|
||||
if limit:
|
||||
stmt = stmt.limit(limit)
|
||||
messages = session.exec(stmt)
|
||||
messages_read = [Message(**d.model_dump()) for d in messages]
|
||||
|
||||
messages: list[Message] = []
|
||||
# messages_df has a timestamp
|
||||
# it gets the last 5 messages, for example
|
||||
# but now they are ordered from most recent to least recent
|
||||
# so we need to reverse the order
|
||||
messages_df = messages_df[::-1] if order == "DESC" else messages_df
|
||||
for row in messages_df.itertuples():
|
||||
msg = Message(
|
||||
text=row.text,
|
||||
sender=row.sender,
|
||||
session_id=row.session_id,
|
||||
sender_name=row.sender_name,
|
||||
timestamp=row.timestamp,
|
||||
)
|
||||
|
||||
messages.append(msg)
|
||||
|
||||
return messages
|
||||
return messages_read
|
||||
|
||||
|
||||
def add_messages(messages: Message | list[Message], flow_id: Optional[str] = None):
|
||||
def add_messages(messages: Message | list[Message], flow_id: str | None = None):
|
||||
"""
|
||||
Add a message to the monitor service.
|
||||
"""
|
||||
try:
|
||||
monitor_service = get_monitor_service()
|
||||
if not isinstance(messages, list):
|
||||
messages = [messages]
|
||||
|
||||
|
|
@ -72,25 +72,29 @@ def add_messages(messages: Message | list[Message], flow_id: Optional[str] = Non
|
|||
types = ", ".join([str(type(message)) for message in messages])
|
||||
raise ValueError(f"The messages must be instances of Message. Found: {types}")
|
||||
|
||||
messages_models: list[MessageModel] = []
|
||||
messages_models: list[MessageTable] = []
|
||||
for msg in messages:
|
||||
if not msg.timestamp:
|
||||
msg.timestamp = monitor_service.get_timestamp()
|
||||
messages_models.append(MessageModel.from_message(msg, flow_id=flow_id))
|
||||
|
||||
for message_model in messages_models:
|
||||
try:
|
||||
monitor_service.add_message(message_model)
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding message to monitor service: {e}")
|
||||
logger.exception(e)
|
||||
raise e
|
||||
return messages_models
|
||||
messages_models.append(MessageTable.from_message(msg, flow_id=flow_id))
|
||||
with session_scope() as session:
|
||||
messages_models = add_messagetables(messages_models, session)
|
||||
return [Message(**message.model_dump()) for message in messages_models]
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
raise e
|
||||
|
||||
|
||||
def add_messagetables(messages: list[MessageTable], session: Session):
|
||||
for message in messages:
|
||||
try:
|
||||
session.add(message)
|
||||
session.commit()
|
||||
session.refresh(message)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
raise e
|
||||
return [MessageRead.model_validate(message, from_attributes=True) for message in messages]
|
||||
|
||||
|
||||
def delete_messages(session_id: str):
|
||||
"""
|
||||
Delete messages from the monitor service based on the provided session ID.
|
||||
|
|
@ -98,14 +102,19 @@ def delete_messages(session_id: str):
|
|||
Args:
|
||||
session_id (str): The session ID associated with the messages to delete.
|
||||
"""
|
||||
monitor_service = get_monitor_service()
|
||||
monitor_service.delete_messages_session(session_id)
|
||||
with session_scope() as session:
|
||||
session.exec(
|
||||
delete(MessageTable)
|
||||
.where(col(MessageTable.session_id) == session_id)
|
||||
.execution_options(synchronize_session="fetch")
|
||||
)
|
||||
session.commit()
|
||||
|
||||
|
||||
def store_message(
|
||||
message: Message,
|
||||
flow_id: Optional[str] = None,
|
||||
) -> List[Message]:
|
||||
flow_id: str | None = None,
|
||||
) -> list[Message]:
|
||||
"""
|
||||
Stores a message in the memory.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from datetime import datetime, timezone
|
||||
from typing import Annotated, Any, AsyncIterator, Iterator, List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from langchain_core.load import load
|
||||
|
|
@ -31,7 +32,20 @@ class Message(Data):
|
|||
timestamp: Annotated[str, BeforeValidator(_timestamp_to_str)] = Field(
|
||||
default=datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
|
||||
)
|
||||
flow_id: Optional[str] = None
|
||||
flow_id: Optional[str | UUID] = None
|
||||
|
||||
@field_validator("flow_id", mode="before")
|
||||
@classmethod
|
||||
def validate_flow_id(cls, value):
|
||||
if isinstance(value, UUID):
|
||||
value = str(value)
|
||||
return value
|
||||
|
||||
@field_serializer("flow_id")
|
||||
def serialize_flow_id(value):
|
||||
if isinstance(value, str):
|
||||
return UUID(value)
|
||||
return value
|
||||
|
||||
@field_validator("files", mode="before")
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import base64
|
||||
import random
|
||||
import warnings
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Annotated, Coroutine, Optional, Union
|
||||
|
|
@ -330,17 +332,25 @@ def authenticate_user(username: str, password: str, db: Session = Depends(get_se
|
|||
return user if verify_password(password, user.password) else None
|
||||
|
||||
|
||||
def add_padding(s):
|
||||
# Calculate the number of padding characters needed
|
||||
padding_needed = 4 - len(s) % 4
|
||||
return s + "=" * padding_needed
|
||||
def ensure_valid_key(s: str) -> bytes:
|
||||
# If the key is too short, we'll use it as a seed to generate a valid key
|
||||
if len(s) < 32:
|
||||
# Use the input as a seed for the random number generator
|
||||
random.seed(s)
|
||||
# Generate 32 random bytes
|
||||
key = bytes(random.getrandbits(8) for _ in range(32))
|
||||
else:
|
||||
# If the key is long enough, use the first 32 bytes
|
||||
key = s[:32].encode()
|
||||
|
||||
# Ensure the key is URL-safe base64-encoded
|
||||
return base64.urlsafe_b64encode(key)
|
||||
|
||||
|
||||
def get_fernet(settings_service=Depends(get_settings_service)):
|
||||
SECRET_KEY = settings_service.auth_settings.SECRET_KEY.get_secret_value()
|
||||
# It's important that your secret key is 32 url-safe base64-encoded byte
|
||||
padded_secret_key = add_padding(SECRET_KEY)
|
||||
fernet = Fernet(padded_secret_key)
|
||||
valid_key = ensure_valid_key(SECRET_KEY)
|
||||
fernet = Fernet(valid_key)
|
||||
return fernet
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from .api_key import ApiKey
|
||||
from .flow import Flow
|
||||
from .folder import Folder
|
||||
from .message import MessageTable
|
||||
from .user import User
|
||||
from .variable import Variable
|
||||
|
||||
__all__ = ["Flow", "User", "ApiKey", "Variable", "Folder"]
|
||||
__all__ = ["Flow", "User", "ApiKey", "Variable", "Folder", "MessageTable"]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import re
|
||||
import warnings
|
||||
from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, Dict, Optional
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import emoji
|
||||
|
|
@ -17,6 +17,7 @@ from langflow.schema import Data
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.database.models.folder import Folder
|
||||
from langflow.services.database.models.message import MessageTable
|
||||
from langflow.services.database.models.user import User
|
||||
|
||||
|
||||
|
|
@ -141,6 +142,7 @@ class Flow(FlowBase, table=True):
|
|||
user: "User" = Relationship(back_populates="flows")
|
||||
folder_id: Optional[UUID] = Field(default=None, foreign_key="folder.id", nullable=True, index=True)
|
||||
folder: Optional["Folder"] = Relationship(back_populates="flows")
|
||||
messages: List["MessageTable"] = Relationship(back_populates="flow")
|
||||
|
||||
def to_data(self):
|
||||
serialized = self.model_dump()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
from .model import MessageTable, MessageCreate, MessageRead, MessageUpdate
|
||||
|
||||
__all__ = ["MessageTable", "MessageCreate", "MessageRead", "MessageUpdate"]
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from pydantic import field_validator
|
||||
from sqlmodel import JSON, Column, Field, Relationship, SQLModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.schema.message import Message
|
||||
from langflow.services.database.models.flow.model import Flow
|
||||
|
||||
|
||||
class MessageBase(SQLModel):
|
||||
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
sender: str
|
||||
sender_name: str
|
||||
session_id: str
|
||||
text: str
|
||||
files: list[str] = Field(default_factory=list)
|
||||
|
||||
@field_validator("files", mode="before")
|
||||
@classmethod
|
||||
def validate_files(cls, value):
|
||||
if not value:
|
||||
value = []
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def from_message(cls, message: "Message", flow_id: str | UUID | None = None):
|
||||
# first check if the record has all the required fields
|
||||
if message.text is None or not message.sender or not message.sender_name:
|
||||
raise ValueError("The message does not have the required fields (text, sender, sender_name).")
|
||||
if isinstance(message.timestamp, str):
|
||||
timestamp = datetime.fromisoformat(message.timestamp)
|
||||
else:
|
||||
timestamp = message.timestamp
|
||||
if not flow_id and message.flow_id:
|
||||
flow_id = message.flow_id
|
||||
return cls(
|
||||
sender=message.sender,
|
||||
sender_name=message.sender_name,
|
||||
text=message.text,
|
||||
session_id=message.session_id,
|
||||
files=message.files or [],
|
||||
timestamp=timestamp,
|
||||
flow_id=flow_id,
|
||||
)
|
||||
|
||||
|
||||
class MessageTable(MessageBase, table=True):
|
||||
__tablename__ = "message"
|
||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||
flow_id: Optional[UUID] = Field(default=None, foreign_key="flow.id")
|
||||
flow: "Flow" = Relationship(back_populates="messages")
|
||||
files: List[str] = Field(sa_column=Column(JSON))
|
||||
|
||||
@field_validator("flow_id", mode="before")
|
||||
@classmethod
|
||||
def validate_flow_id(cls, value):
|
||||
if value is None:
|
||||
return value
|
||||
if isinstance(value, str):
|
||||
value = UUID(value)
|
||||
return value
|
||||
|
||||
# Needed for Column(JSON)
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
|
||||
class MessageRead(MessageBase):
|
||||
id: UUID
|
||||
flow_id: Optional[UUID] = Field()
|
||||
|
||||
|
||||
class MessageCreate(MessageBase):
|
||||
pass
|
||||
|
||||
|
||||
class MessageUpdate(SQLModel):
|
||||
text: Optional[str] = None
|
||||
sender: Optional[str] = None
|
||||
sender_name: Optional[str] = None
|
||||
session_id: Optional[str] = None
|
||||
files: Optional[list[str]] = None
|
||||
|
|
@ -6,22 +6,24 @@ from typing import TYPE_CHECKING
|
|||
import sqlalchemy as sa
|
||||
from alembic import command, util
|
||||
from alembic.config import Config
|
||||
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
|
||||
|
||||
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, migrate_messages_from_monitor_service_to_database
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.services.utils import teardown_superuser
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.settings.service import SettingsService
|
||||
from sqlalchemy.engine import Engine
|
||||
|
||||
from langflow.services.settings.service import SettingsService
|
||||
|
||||
|
||||
class DatabaseService(Service):
|
||||
name = "database_service"
|
||||
|
|
@ -205,6 +207,10 @@ class DatabaseService(Service):
|
|||
logger.error(f"AutogenerateDiffsDetected: {exc}")
|
||||
if not fix:
|
||||
raise RuntimeError(f"There's a mismatch between the models and the database.\n{exc}")
|
||||
try:
|
||||
migrate_messages_from_monitor_service_to_database(session)
|
||||
except Exception as exc:
|
||||
logger.error(f"Error migrating messages from monitor service to database: {exc}")
|
||||
|
||||
if fix:
|
||||
self.try_downgrade_upgrade_until_success(alembic_cfg)
|
||||
|
|
|
|||
|
|
@ -4,11 +4,82 @@ from typing import TYPE_CHECKING
|
|||
|
||||
from alembic.util.exc import CommandError
|
||||
from loguru import logger
|
||||
from sqlmodel import Session, text
|
||||
from sqlmodel import Session, select, text
|
||||
|
||||
from langflow.services.deps import get_monitor_service
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.database.service import DatabaseService
|
||||
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
def migrate_messages_from_monitor_service_to_database(session: Session) -> bool:
|
||||
from langflow.schema.message import Message
|
||||
from langflow.services.database.models.message import MessageTable
|
||||
|
||||
try:
|
||||
monitor_service = get_monitor_service()
|
||||
messages_df = monitor_service.get_messages()
|
||||
except Exception as e:
|
||||
logger.error(f"Error retrieving messages from monitor service: {e}")
|
||||
return False
|
||||
|
||||
if messages_df.empty:
|
||||
logger.info("No messages to migrate.")
|
||||
return True
|
||||
|
||||
original_messages: List[Dict] = messages_df.to_dict(orient="records")
|
||||
|
||||
db_messages = session.exec(select(MessageTable)).all()
|
||||
db_messages = [msg[0] for msg in db_messages] # type: ignore
|
||||
db_msg_dict = {(msg.text, msg.timestamp.isoformat(), str(msg.flow_id), msg.session_id): msg for msg in db_messages}
|
||||
# Filter out messages that already exist in the database
|
||||
original_messages_filtered = []
|
||||
for message in original_messages:
|
||||
key = (message["text"], message["timestamp"].isoformat(), str(message["flow_id"]), message["session_id"])
|
||||
if key not in db_msg_dict:
|
||||
original_messages_filtered.append(message)
|
||||
if not original_messages_filtered:
|
||||
logger.info("No messages to migrate.")
|
||||
return True
|
||||
try:
|
||||
# Bulk insert messages
|
||||
session.bulk_insert_mappings(
|
||||
MessageTable, # type: ignore
|
||||
[MessageTable.from_message(Message(**msg)).model_dump() for msg in original_messages_filtered],
|
||||
)
|
||||
session.commit()
|
||||
except Exception as e:
|
||||
logger.error(f"Error during message insertion: {str(e)}")
|
||||
session.rollback()
|
||||
return False
|
||||
|
||||
# Create a dictionary for faster lookup
|
||||
|
||||
all_ok = True
|
||||
for orig_msg in original_messages_filtered:
|
||||
key = (orig_msg["text"], orig_msg["timestamp"].isoformat(), str(orig_msg["flow_id"]), orig_msg["session_id"])
|
||||
matching_db_msg = db_msg_dict.get(key)
|
||||
|
||||
if matching_db_msg is None:
|
||||
logger.warning(f"Message not found in database: {orig_msg}")
|
||||
all_ok = False
|
||||
else:
|
||||
# Validate other fields
|
||||
if any(getattr(matching_db_msg, k) != v for k, v in orig_msg.items() if k != "index"):
|
||||
logger.warning(f"Message mismatch in database: {orig_msg}")
|
||||
all_ok = False
|
||||
|
||||
if all_ok:
|
||||
messages_ids = [message["index"] for message in original_messages]
|
||||
monitor_service.delete_messages(messages_ids)
|
||||
logger.info("Migration completed successfully. Original messages deleted.")
|
||||
else:
|
||||
logger.warning("Migration completed with errors. Original messages not deleted.")
|
||||
|
||||
return all_ok
|
||||
|
||||
|
||||
def initialize_database(fix_migration: bool = False):
|
||||
logger.debug("Initializing database")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import json
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, Field, field_serializer, field_validator
|
||||
|
||||
|
|
@ -27,15 +28,15 @@ class DefaultModel(BaseModel):
|
|||
|
||||
|
||||
class TransactionModel(DefaultModel):
|
||||
index: Optional[int] = Field(default=None)
|
||||
timestamp: Optional[datetime] = Field(default_factory=datetime.now, alias="timestamp")
|
||||
index: int | None = Field(default=None)
|
||||
timestamp: datetime | None = Field(default_factory=datetime.now, alias="timestamp")
|
||||
vertex_id: str
|
||||
target_id: str | None = None
|
||||
inputs: dict
|
||||
outputs: Optional[dict] = None
|
||||
outputs: dict | None = None
|
||||
status: str
|
||||
error: Optional[str] = None
|
||||
flow_id: Optional[str] = Field(default=None, alias="flow_id")
|
||||
error: str | None = None
|
||||
flow_id: str | None = Field(default=None, alias="flow_id")
|
||||
|
||||
# validate target_args in case it is a JSON
|
||||
@field_validator("outputs", "inputs", mode="before")
|
||||
|
|
@ -52,16 +53,16 @@ class TransactionModel(DefaultModel):
|
|||
|
||||
|
||||
class TransactionModelResponse(DefaultModel):
|
||||
index: Optional[int] = Field(default=None)
|
||||
timestamp: Optional[datetime] = Field(default_factory=datetime.now, alias="timestamp")
|
||||
index: int | None = Field(default=None)
|
||||
timestamp: datetime | None = Field(default_factory=datetime.now, alias="timestamp")
|
||||
vertex_id: str
|
||||
inputs: dict
|
||||
outputs: Optional[dict] = None
|
||||
outputs: dict | None = None
|
||||
status: str
|
||||
error: Optional[str] = None
|
||||
flow_id: Optional[str] = Field(default=None, alias="flow_id")
|
||||
source: Optional[str] = None
|
||||
target: Optional[str] = None
|
||||
error: str | None = None
|
||||
flow_id: str | None = Field(default=None, alias="flow_id")
|
||||
source: str | None = None
|
||||
target: str | None = None
|
||||
|
||||
# validate target_args in case it is a JSON
|
||||
@field_validator("outputs", "inputs", mode="before")
|
||||
|
|
@ -80,9 +81,9 @@ class TransactionModelResponse(DefaultModel):
|
|||
return v
|
||||
|
||||
|
||||
class MessageModel(DefaultModel):
|
||||
index: Optional[int] = Field(default=None)
|
||||
flow_id: Optional[str] = Field(default=None, alias="flow_id")
|
||||
class DuckDbMessageModel(DefaultModel):
|
||||
index: int | None = Field(default=None, alias="index")
|
||||
flow_id: str | None = Field(default=None, alias="flow_id")
|
||||
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
sender: str
|
||||
sender_name: str
|
||||
|
|
@ -111,7 +112,53 @@ class MessageModel(DefaultModel):
|
|||
return v
|
||||
|
||||
@classmethod
|
||||
def from_message(cls, message: Message, flow_id: Optional[str] = None):
|
||||
def from_message(cls, message: Message, flow_id: str | None = None):
|
||||
# first check if the record has all the required fields
|
||||
if message.text is None or not message.sender or not message.sender_name:
|
||||
raise ValueError("The message does not have the required fields (text, sender, sender_name).")
|
||||
return cls(
|
||||
sender=message.sender,
|
||||
sender_name=message.sender_name,
|
||||
text=message.text,
|
||||
session_id=message.session_id,
|
||||
files=message.files or [],
|
||||
timestamp=message.timestamp,
|
||||
flow_id=flow_id,
|
||||
)
|
||||
|
||||
|
||||
class MessageModel(DefaultModel):
|
||||
id: str | UUID | None = Field(default=None)
|
||||
flow_id: UUID | None = Field(default=None)
|
||||
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
sender: str
|
||||
sender_name: str
|
||||
session_id: str
|
||||
text: str
|
||||
files: list[str] = []
|
||||
|
||||
@field_validator("files", mode="before")
|
||||
@classmethod
|
||||
def validate_files(cls, v):
|
||||
if isinstance(v, str):
|
||||
v = json.loads(v)
|
||||
return v
|
||||
|
||||
@field_serializer("timestamp")
|
||||
@classmethod
|
||||
def serialize_timestamp(cls, v):
|
||||
v = v.replace(microsecond=0)
|
||||
return v.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
@field_serializer("files")
|
||||
@classmethod
|
||||
def serialize_files(cls, v):
|
||||
if isinstance(v, list):
|
||||
return json.dumps(v)
|
||||
return v
|
||||
|
||||
@classmethod
|
||||
def from_message(cls, message: Message, flow_id: str | None = None):
|
||||
# first check if the record has all the required fields
|
||||
if message.text is None or not message.sender or not message.sender_name:
|
||||
raise ValueError("The message does not have the required fields (text, sender, sender_name).")
|
||||
|
|
@ -127,16 +174,7 @@ class MessageModel(DefaultModel):
|
|||
|
||||
|
||||
class MessageModelResponse(MessageModel):
|
||||
index: Optional[int] = Field(default=None)
|
||||
|
||||
@field_validator("index", mode="before")
|
||||
def validate_id(cls, v):
|
||||
if isinstance(v, float):
|
||||
try:
|
||||
return int(v)
|
||||
except ValueError:
|
||||
return None
|
||||
return v
|
||||
pass
|
||||
|
||||
|
||||
class MessageModelRequest(MessageModel):
|
||||
|
|
@ -147,8 +185,8 @@ class MessageModelRequest(MessageModel):
|
|||
|
||||
|
||||
class VertexBuildModel(DefaultModel):
|
||||
index: Optional[int] = Field(default=None, alias="index", exclude=True)
|
||||
id: Optional[str] = Field(default=None, alias="id")
|
||||
index: int | None = Field(default=None, alias="index", exclude=True)
|
||||
id: str | None = Field(default=None, alias="id")
|
||||
flow_id: str
|
||||
valid: bool
|
||||
params: Any
|
||||
|
|
|
|||
|
|
@ -1,30 +1,31 @@
|
|||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, List, Optional, Union
|
||||
from typing import TYPE_CHECKING, Union
|
||||
|
||||
import duckdb
|
||||
from langflow.services.base import Service
|
||||
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
|
||||
|
||||
from langflow.services.base import Service
|
||||
from langflow.services.monitor.utils import add_row_to_table, drop_and_create_table_if_schema_mismatch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.monitor.schema import DuckDbMessageModel, TransactionModel, VertexBuildModel
|
||||
from langflow.services.settings.service import SettingsService
|
||||
from langflow.services.monitor.schema import MessageModel, TransactionModel, VertexBuildModel
|
||||
|
||||
|
||||
class MonitorService(Service):
|
||||
name = "monitor_service"
|
||||
|
||||
def __init__(self, settings_service: "SettingsService"):
|
||||
from langflow.services.monitor.schema import MessageModel, TransactionModel, VertexBuildModel
|
||||
from langflow.services.monitor.schema import DuckDbMessageModel, TransactionModel, VertexBuildModel
|
||||
|
||||
self.settings_service = settings_service
|
||||
self.base_cache_dir = Path(user_cache_dir("langflow"))
|
||||
self.db_path = self.base_cache_dir / "monitor.duckdb"
|
||||
self.table_map: dict[str, type[TransactionModel | MessageModel | VertexBuildModel]] = {
|
||||
self.table_map: dict[str, type[TransactionModel | DuckDbMessageModel | VertexBuildModel]] = {
|
||||
"transactions": TransactionModel,
|
||||
"messages": MessageModel,
|
||||
"messages": DuckDbMessageModel,
|
||||
"vertex_builds": VertexBuildModel,
|
||||
}
|
||||
|
||||
|
|
@ -47,7 +48,7 @@ class MonitorService(Service):
|
|||
def add_row(
|
||||
self,
|
||||
table_name: str,
|
||||
data: Union[dict, "TransactionModel", "MessageModel", "VertexBuildModel"],
|
||||
data: Union[dict, "TransactionModel", "DuckDbMessageModel", "VertexBuildModel"],
|
||||
):
|
||||
# Make sure the model passed matches the table
|
||||
|
||||
|
|
@ -67,80 +68,15 @@ class MonitorService(Service):
|
|||
def get_timestamp():
|
||||
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def get_vertex_builds(
|
||||
self,
|
||||
flow_id: Optional[str] = None,
|
||||
vertex_id: Optional[str] = None,
|
||||
valid: Optional[bool] = None,
|
||||
order_by: Optional[str] = "timestamp",
|
||||
):
|
||||
query = "SELECT id, index,flow_id, valid, params, data, artifacts, timestamp FROM vertex_builds"
|
||||
conditions = []
|
||||
if flow_id:
|
||||
conditions.append(f"flow_id = '{flow_id}'")
|
||||
if vertex_id:
|
||||
conditions.append(f"id = '{vertex_id}'")
|
||||
if valid is not None: # Check for None because valid is a boolean
|
||||
valid_str = "true" if valid else "false"
|
||||
conditions.append(f"valid = {valid_str}")
|
||||
|
||||
if conditions:
|
||||
query += " WHERE " + " AND ".join(conditions)
|
||||
|
||||
if order_by:
|
||||
query += f" ORDER BY {order_by}"
|
||||
|
||||
with duckdb.connect(str(self.db_path), read_only=True) as conn:
|
||||
df = conn.execute(query).df()
|
||||
|
||||
return df.to_dict(orient="records")
|
||||
|
||||
def delete_vertex_builds(self, flow_id: Optional[str] = None):
|
||||
query = "DELETE FROM vertex_builds"
|
||||
if flow_id:
|
||||
query += f" WHERE flow_id = '{flow_id}'"
|
||||
|
||||
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, read_only=False)
|
||||
|
||||
def delete_messages(self, message_ids: Union[List[int], str]):
|
||||
if isinstance(message_ids, list):
|
||||
# If message_ids is a list, join the string representations of the integers
|
||||
ids_str = ",".join(map(str, message_ids))
|
||||
elif isinstance(message_ids, str):
|
||||
# If message_ids is already a string, use it directly
|
||||
ids_str = message_ids
|
||||
else:
|
||||
raise ValueError("message_ids must be a list of integers or a string")
|
||||
|
||||
query = f"DELETE FROM messages WHERE index IN ({ids_str})"
|
||||
|
||||
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, read_only=False)
|
||||
|
||||
def add_message(self, message: "MessageModel"):
|
||||
self.add_row("messages", message)
|
||||
|
||||
def get_messages(
|
||||
self,
|
||||
flow_id: Optional[str] = None,
|
||||
sender: Optional[str] = None,
|
||||
sender_name: Optional[str] = None,
|
||||
session_id: Optional[str] = None,
|
||||
order_by: Optional[str] = "timestamp",
|
||||
order: Optional[str] = "DESC",
|
||||
limit: Optional[int] = None,
|
||||
flow_id: str | None = None,
|
||||
sender: str | None = None,
|
||||
sender_name: str | None = None,
|
||||
session_id: str | None = None,
|
||||
order_by: str | None = "timestamp",
|
||||
order: str | None = "DESC",
|
||||
limit: int | None = None,
|
||||
):
|
||||
query = "SELECT index, flow_id, sender_name, sender, session_id, text, files, timestamp FROM messages"
|
||||
conditions = []
|
||||
|
|
@ -168,13 +104,75 @@ class MonitorService(Service):
|
|||
|
||||
return df
|
||||
|
||||
def get_vertex_builds(
|
||||
self,
|
||||
flow_id: str | None = None,
|
||||
vertex_id: str | None = None,
|
||||
valid: bool | None = None,
|
||||
order_by: str | None = "timestamp",
|
||||
):
|
||||
query = "SELECT id, index,flow_id, valid, params, data, artifacts, timestamp FROM vertex_builds"
|
||||
conditions = []
|
||||
if flow_id:
|
||||
conditions.append(f"flow_id = '{flow_id}'")
|
||||
if vertex_id:
|
||||
conditions.append(f"id = '{vertex_id}'")
|
||||
if valid is not None: # Check for None because valid is a boolean
|
||||
valid_str = "true" if valid else "false"
|
||||
conditions.append(f"valid = {valid_str}")
|
||||
|
||||
if conditions:
|
||||
query += " WHERE " + " AND ".join(conditions)
|
||||
|
||||
if order_by:
|
||||
query += f" ORDER BY {order_by}"
|
||||
|
||||
with duckdb.connect(str(self.db_path), read_only=True) as conn:
|
||||
df = conn.execute(query).df()
|
||||
|
||||
return df.to_dict(orient="records")
|
||||
|
||||
def delete_vertex_builds(self, flow_id: str | None = None):
|
||||
query = "DELETE FROM vertex_builds"
|
||||
if flow_id:
|
||||
query += f" WHERE flow_id = '{flow_id}'"
|
||||
|
||||
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, read_only=False)
|
||||
|
||||
def delete_messages(self, message_ids: list[int] | str):
|
||||
if isinstance(message_ids, list):
|
||||
# If message_ids is a list, join the string representations of the integers
|
||||
ids_str = ",".join(map(str, message_ids))
|
||||
elif isinstance(message_ids, str):
|
||||
# If message_ids is already a string, use it directly
|
||||
ids_str = message_ids
|
||||
else:
|
||||
raise ValueError("message_ids must be a list of integers or a string")
|
||||
|
||||
query = f"DELETE FROM messages WHERE index IN ({ids_str})"
|
||||
|
||||
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, read_only=False)
|
||||
|
||||
def get_transactions(
|
||||
self,
|
||||
source: Optional[str] = None,
|
||||
target: Optional[str] = None,
|
||||
status: Optional[str] = None,
|
||||
order_by: Optional[str] = "timestamp",
|
||||
flow_id: Optional[str] = None,
|
||||
source: str | None = None,
|
||||
target: str | None = None,
|
||||
status: str | None = None,
|
||||
order_by: str | None = "timestamp",
|
||||
flow_id: str | None = None,
|
||||
):
|
||||
query = (
|
||||
"SELECT index,flow_id, status, error, timestamp, vertex_id, inputs, outputs, target_id FROM transactions"
|
||||
|
|
|
|||
33
src/backend/base/langflow/services/tracing/base.py
Normal file
33
src/backend/base/langflow/services/tracing/base.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
class BaseTracer(ABC):
|
||||
@abstractmethod
|
||||
def __init__(self, trace_name: str, trace_type: str, project_name: str, trace_id: UUID):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def ready(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def add_trace(
|
||||
self, trace_name: str, trace_type: str, inputs: Dict[str, Any], metadata: Dict[str, Any] | None = None
|
||||
):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def end_trace(self, trace_name: str, outputs: Dict[str, Any] | None = None, error: str | None = None):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def end(
|
||||
self,
|
||||
inputs: dict[str, Any],
|
||||
outputs: Dict[str, Any],
|
||||
error: str | None = None,
|
||||
metadata: dict[str, Any] | None = None,
|
||||
):
|
||||
raise NotImplementedError
|
||||
|
|
@ -12,6 +12,7 @@ from loguru import logger
|
|||
|
||||
from langflow.schema.data import Data
|
||||
from langflow.services.base import Service
|
||||
from langflow.services.tracing.base import BaseTracer
|
||||
from langflow.services.tracing.schema import Log
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -180,7 +181,7 @@ class TracingService(Service):
|
|||
self.outputs_metadata[trace_name] |= output_metadata or {}
|
||||
|
||||
|
||||
class LangSmithTracer:
|
||||
class LangSmithTracer(BaseTracer):
|
||||
def __init__(self, trace_name: str, trace_type: str, project_name: str, trace_id: UUID):
|
||||
from langsmith.run_trees import RunTree
|
||||
|
||||
|
|
@ -292,7 +293,7 @@ class LangSmithTracer:
|
|||
inputs: dict[str, Any],
|
||||
outputs: Dict[str, Any],
|
||||
error: str | None = None,
|
||||
metadata: Optional[dict[str, Any]] = None,
|
||||
metadata: dict[str, Any] | None = None,
|
||||
):
|
||||
self._run_tree.add_metadata({"inputs": inputs, "metadata": metadata or {}})
|
||||
self._run_tree.end(outputs=outputs, error=error)
|
||||
|
|
|
|||
34
src/backend/base/langflow/services/tracing/utils.py
Normal file
34
src/backend/base/langflow/services/tracing/utils.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from typing import Any, Dict
|
||||
|
||||
from langflow.schema.data import Data
|
||||
|
||||
|
||||
def convert_to_langchain_type(value):
|
||||
from langflow.schema.message import Message
|
||||
|
||||
if isinstance(value, dict):
|
||||
for key, _value in value.copy().items():
|
||||
_value = convert_to_langchain_type(_value)
|
||||
value[key] = _value
|
||||
elif isinstance(value, list):
|
||||
value = [convert_to_langchain_type(v) for v in value]
|
||||
elif isinstance(value, Message):
|
||||
if "prompt" in value:
|
||||
value = value.load_lc_prompt()
|
||||
elif value.sender:
|
||||
value = value.to_lc_message()
|
||||
else:
|
||||
value = value.to_lc_document()
|
||||
elif isinstance(value, Data):
|
||||
if "text" in value.data:
|
||||
value = value.to_lc_document()
|
||||
else:
|
||||
value = value.data
|
||||
return value
|
||||
|
||||
|
||||
def convert_to_langchain_types(io_dict: Dict[str, Any]):
|
||||
converted = {}
|
||||
for key, value in io_dict.items():
|
||||
converted[key] = convert_to_langchain_type(value)
|
||||
return converted
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
|
@ -11,7 +12,7 @@ from rich.logging import RichHandler
|
|||
VALID_LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
|
||||
|
||||
def serialize(record):
|
||||
def serialize_log(record):
|
||||
subset = {
|
||||
"timestamp": record["time"].timestamp(),
|
||||
"message": record["message"],
|
||||
|
|
@ -22,54 +23,67 @@ def serialize(record):
|
|||
|
||||
|
||||
def patching(record):
|
||||
record["extra"]["serialized"] = serialize(record)
|
||||
record["extra"]["serialized"] = serialize_log(record)
|
||||
|
||||
|
||||
def configure(log_level: Optional[str] = None, log_file: Optional[Path] = None, disable: Optional[bool] = False):
|
||||
def configure(
|
||||
log_level: Optional[str] = None,
|
||||
log_file: Optional[Path] = None,
|
||||
disable: Optional[bool] = False,
|
||||
log_env: Optional[str] = None,
|
||||
):
|
||||
if disable and log_level is None and log_file is None:
|
||||
logger.disable("langflow")
|
||||
if os.getenv("LANGFLOW_LOG_LEVEL", "").upper() in VALID_LOG_LEVELS and log_level is None:
|
||||
log_level = os.getenv("LANGFLOW_LOG_LEVEL")
|
||||
if log_level is None:
|
||||
log_level = "ERROR"
|
||||
# Human-readable
|
||||
log_format = (
|
||||
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> - <level>"
|
||||
"{level: <8}</level> - {module} - <level>{message}</level>"
|
||||
)
|
||||
|
||||
# log_format = log_format_dev if log_level.upper() == "DEBUG" else log_format_prod
|
||||
if log_env is None:
|
||||
log_env = os.getenv("LANGFLOW_LOG_ENV", "")
|
||||
|
||||
logger.remove() # Remove default handlers
|
||||
logger.patch(patching)
|
||||
# Configure loguru to use RichHandler
|
||||
logger.configure(
|
||||
handlers=[
|
||||
{
|
||||
"sink": RichHandler(rich_tracebacks=True, markup=True),
|
||||
"format": log_format,
|
||||
"level": log_level.upper(),
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
if not log_file:
|
||||
cache_dir = Path(user_cache_dir("langflow"))
|
||||
logger.debug(f"Cache directory: {cache_dir}")
|
||||
log_file = cache_dir / "langflow.log"
|
||||
logger.debug(f"Log file: {log_file}")
|
||||
try:
|
||||
log_file = Path(log_file)
|
||||
log_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
logger.add(
|
||||
sink=str(log_file),
|
||||
level=log_level.upper(),
|
||||
format=log_format,
|
||||
rotation="10 MB", # Log rotation based on file size
|
||||
serialize=True,
|
||||
if log_env.lower() == "container" or log_env.lower() == "container_json":
|
||||
logger.add(sys.stdout, format="{message}", serialize=True)
|
||||
elif log_env.lower() == "container_csv":
|
||||
logger.add(sys.stdout, format="{time:YYYY-MM-DD HH:mm:ss.SSS} {level} {file} {line} {function} {message}")
|
||||
else:
|
||||
# Human-readable
|
||||
log_format = (
|
||||
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> - <level>"
|
||||
"{level: <8}</level> - {module} - <level>{message}</level>"
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(f"Error setting up log file: {exc}")
|
||||
|
||||
# Configure loguru to use RichHandler
|
||||
logger.configure(
|
||||
handlers=[
|
||||
{
|
||||
"sink": RichHandler(rich_tracebacks=True, markup=True),
|
||||
"format": log_format,
|
||||
"level": log_level.upper(),
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
if not log_file:
|
||||
cache_dir = Path(user_cache_dir("langflow"))
|
||||
logger.debug(f"Cache directory: {cache_dir}")
|
||||
log_file = cache_dir / "langflow.log"
|
||||
logger.debug(f"Log file: {log_file}")
|
||||
try:
|
||||
log_file = Path(log_file)
|
||||
log_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
logger.add(
|
||||
sink=str(log_file),
|
||||
level=log_level.upper(),
|
||||
format=log_format,
|
||||
rotation="10 MB", # Log rotation based on file size
|
||||
serialize=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(f"Error setting up log file: {exc}")
|
||||
|
||||
logger.debug(f"Logger set up with log level: {log_level}")
|
||||
|
||||
|
|
|
|||
63
src/backend/base/poetry.lock
generated
63
src/backend/base/poetry.lock
generated
|
|
@ -112,13 +112,13 @@ frozenlist = ">=1.1.0"
|
|||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.13.1"
|
||||
version = "1.13.2"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"},
|
||||
{file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"},
|
||||
{file = "alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953"},
|
||||
{file = "alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1172,19 +1172,19 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "langchain"
|
||||
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-0.2.5-py3-none-any.whl", hash = "sha256:9aded9a65348254e1c93dcdaacffe4d1b6a5e7f74ef80c160c88ff78ad299228"},
|
||||
{file = "langchain-0.2.5.tar.gz", hash = "sha256:ffdbf4fcea46a10d461bcbda2402220fcfd72a0c70e9f4161ae0510067b9b3bd"},
|
||||
{file = "langchain-0.2.6-py3-none-any.whl", hash = "sha256:f86e8a7afd3e56f8eb5ba47f01dd00144fb9fc2f1db9873bd197347be2857aa4"},
|
||||
{file = "langchain-0.2.6.tar.gz", hash = "sha256:867f6add370c1e3911b0e87d3dd0e36aec1e8f513bf06131340fe8f151d89dc5"},
|
||||
]
|
||||
|
||||
[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.7,<0.3.0"
|
||||
langchain-core = ">=0.2.10,<0.3.0"
|
||||
langchain-text-splitters = ">=0.2.0,<0.3.0"
|
||||
langsmith = ">=0.1.17,<0.2.0"
|
||||
numpy = [
|
||||
|
|
@ -1195,24 +1195,24 @@ pydantic = ">=1,<3"
|
|||
PyYAML = ">=5.3"
|
||||
requests = ">=2,<3"
|
||||
SQLAlchemy = ">=1.4,<3"
|
||||
tenacity = ">=8.1.0,<9.0.0"
|
||||
tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-community"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
description = "Community contributed LangChain integrations."
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_community-0.2.5-py3-none-any.whl", hash = "sha256:bf37a334952e42c7676d083cf2d2c4cbfbb7de1949c4149fe19913e2b06c485f"},
|
||||
{file = "langchain_community-0.2.5.tar.gz", hash = "sha256:476787b8c8c213b67e7b0eceb53346e787f00fbae12d8e680985bd4f93b0bf64"},
|
||||
{file = "langchain_community-0.2.6-py3-none-any.whl", hash = "sha256:758cc800acfe5dd396bf8ba1b57c4792639ead0eab48ed0367f0732ec6ee1f68"},
|
||||
{file = "langchain_community-0.2.6.tar.gz", hash = "sha256:40ce09a50ed798aa651ddb34c8978200fa8589b9813c7a28ce8af027bbf249f0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aiohttp = ">=3.8.3,<4.0.0"
|
||||
dataclasses-json = ">=0.5.7,<0.7"
|
||||
langchain = ">=0.2.5,<0.3.0"
|
||||
langchain-core = ">=0.2.7,<0.3.0"
|
||||
langchain = ">=0.2.6,<0.3.0"
|
||||
langchain-core = ">=0.2.10,<0.3.0"
|
||||
langsmith = ">=0.1.0,<0.2.0"
|
||||
numpy = [
|
||||
{version = ">=1,<2", markers = "python_version < \"3.12\""},
|
||||
|
|
@ -1221,17 +1221,17 @@ numpy = [
|
|||
PyYAML = ">=5.3"
|
||||
requests = ">=2,<3"
|
||||
SQLAlchemy = ">=1.4,<3"
|
||||
tenacity = ">=8.1.0,<9.0.0"
|
||||
tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.2.9"
|
||||
version = "0.2.10"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_core-0.2.9-py3-none-any.whl", hash = "sha256:426a5a4fea95a5db995ba5ab560b76edd4998fb6fe52ccc28ac987092a4cbfcd"},
|
||||
{file = "langchain_core-0.2.9.tar.gz", hash = "sha256:f1c59082642921727844e1cd0eb36d451edd1872c20e193aa3142aac03495986"},
|
||||
{file = "langchain_core-0.2.10-py3-none-any.whl", hash = "sha256:6eb72086b6bc86db9812da98f79e507c2209a15c0112aefd214a04182ada8586"},
|
||||
{file = "langchain_core-0.2.10.tar.gz", hash = "sha256:33d1fc234ab58c80476eb5bbde2107ef522a2ce8f46bdf47d9e1bd21e054208f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1247,35 +1247,32 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
|
|||
|
||||
[[package]]
|
||||
name = "langchain-experimental"
|
||||
version = "0.0.61"
|
||||
version = "0.0.62"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_experimental-0.0.61-py3-none-any.whl", hash = "sha256:f9c516f528f55919743bd56fe1689a53bf74ae7f8902d64b9d8aebc61249cbe2"},
|
||||
{file = "langchain_experimental-0.0.61.tar.gz", hash = "sha256:e9538efb994be5db3045cc582cddb9787c8299c86ffeee9d3779b7f58eef2226"},
|
||||
{file = "langchain_experimental-0.0.62-py3-none-any.whl", hash = "sha256:9240f9e3490e819976f20a37863970036e7baacb7104b9eb6833d19ab6d518c9"},
|
||||
{file = "langchain_experimental-0.0.62.tar.gz", hash = "sha256:9737fbc8429d24457ea4d368e3c9ba9ed1cace0564fb5f1a96a3027a588bd0ac"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
langchain-community = ">=0.2.5,<0.3.0"
|
||||
langchain-core = ">=0.2.7,<0.3.0"
|
||||
langchain-community = ">=0.2.6,<0.3.0"
|
||||
langchain-core = ">=0.2.10,<0.3.0"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-text-splitters"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
description = "LangChain text splitting utilities"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_text_splitters-0.2.1-py3-none-any.whl", hash = "sha256:c2774a85f17189eaca50339629d2316d13130d4a8d9f1a1a96f3a03670c4a138"},
|
||||
{file = "langchain_text_splitters-0.2.1.tar.gz", hash = "sha256:06853d17d7241ecf5c97c7b6ef01f600f9b0fb953dd997838142a527a4f32ea4"},
|
||||
{file = "langchain_text_splitters-0.2.2-py3-none-any.whl", hash = "sha256:1c80d4b11b55e2995f02d2a326c0323ee1eeff24507329bb22924e420c782dff"},
|
||||
{file = "langchain_text_splitters-0.2.2.tar.gz", hash = "sha256:a1e45de10919fa6fb080ef0525deab56557e9552083600455cb9fa4238076140"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
langchain-core = ">=0.2.0,<0.3.0"
|
||||
|
||||
[package.extras]
|
||||
extended-testing = ["beautifulsoup4 (>=4.12.3,<5.0.0)", "lxml (>=4.9.3,<6.0)"]
|
||||
langchain-core = ">=0.2.10,<0.3.0"
|
||||
|
||||
[[package]]
|
||||
name = "langchainhub"
|
||||
|
|
@ -2482,13 +2479,13 @@ pyasn1 = ">=0.1.3"
|
|||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "sentry_sdk-2.6.0-py2.py3-none-any.whl", hash = "sha256:422b91cb49378b97e7e8d0e8d5a1069df23689d45262b86f54988a7db264e874"},
|
||||
{file = "sentry_sdk-2.6.0.tar.gz", hash = "sha256:65cc07e9c6995c5e316109f138570b32da3bd7ff8d0d0ee4aaf2628c3dd8127d"},
|
||||
{file = "sentry_sdk-2.7.0-py2.py3-none-any.whl", hash = "sha256:db9594c27a4d21c1ebad09908b1f0dc808ef65c2b89c1c8e7e455143262e37c1"},
|
||||
{file = "sentry_sdk-2.7.0.tar.gz", hash = "sha256:d846a211d4a0378b289ced3c434480945f110d0ede00450ba631fc2852e7a0d4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -2520,7 +2517,7 @@ langchain = ["langchain (>=0.0.210)"]
|
|||
loguru = ["loguru (>=0.5)"]
|
||||
openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
|
||||
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
|
||||
opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"]
|
||||
opentelemetry-experimental = ["opentelemetry-instrumentation-aio-pika (==0.46b0)", "opentelemetry-instrumentation-aiohttp-client (==0.46b0)", "opentelemetry-instrumentation-aiopg (==0.46b0)", "opentelemetry-instrumentation-asgi (==0.46b0)", "opentelemetry-instrumentation-asyncio (==0.46b0)", "opentelemetry-instrumentation-asyncpg (==0.46b0)", "opentelemetry-instrumentation-aws-lambda (==0.46b0)", "opentelemetry-instrumentation-boto (==0.46b0)", "opentelemetry-instrumentation-boto3sqs (==0.46b0)", "opentelemetry-instrumentation-botocore (==0.46b0)", "opentelemetry-instrumentation-cassandra (==0.46b0)", "opentelemetry-instrumentation-celery (==0.46b0)", "opentelemetry-instrumentation-confluent-kafka (==0.46b0)", "opentelemetry-instrumentation-dbapi (==0.46b0)", "opentelemetry-instrumentation-django (==0.46b0)", "opentelemetry-instrumentation-elasticsearch (==0.46b0)", "opentelemetry-instrumentation-falcon (==0.46b0)", "opentelemetry-instrumentation-fastapi (==0.46b0)", "opentelemetry-instrumentation-flask (==0.46b0)", "opentelemetry-instrumentation-grpc (==0.46b0)", "opentelemetry-instrumentation-httpx (==0.46b0)", "opentelemetry-instrumentation-jinja2 (==0.46b0)", "opentelemetry-instrumentation-kafka-python (==0.46b0)", "opentelemetry-instrumentation-logging (==0.46b0)", "opentelemetry-instrumentation-mysql (==0.46b0)", "opentelemetry-instrumentation-mysqlclient (==0.46b0)", "opentelemetry-instrumentation-pika (==0.46b0)", "opentelemetry-instrumentation-psycopg (==0.46b0)", "opentelemetry-instrumentation-psycopg2 (==0.46b0)", "opentelemetry-instrumentation-pymemcache (==0.46b0)", "opentelemetry-instrumentation-pymongo (==0.46b0)", "opentelemetry-instrumentation-pymysql (==0.46b0)", "opentelemetry-instrumentation-pyramid (==0.46b0)", "opentelemetry-instrumentation-redis (==0.46b0)", "opentelemetry-instrumentation-remoulade (==0.46b0)", "opentelemetry-instrumentation-requests (==0.46b0)", "opentelemetry-instrumentation-sklearn (==0.46b0)", "opentelemetry-instrumentation-sqlalchemy (==0.46b0)", "opentelemetry-instrumentation-sqlite3 (==0.46b0)", "opentelemetry-instrumentation-starlette (==0.46b0)", "opentelemetry-instrumentation-system-metrics (==0.46b0)", "opentelemetry-instrumentation-threading (==0.46b0)", "opentelemetry-instrumentation-tornado (==0.46b0)", "opentelemetry-instrumentation-tortoiseorm (==0.46b0)", "opentelemetry-instrumentation-urllib (==0.46b0)", "opentelemetry-instrumentation-urllib3 (==0.46b0)", "opentelemetry-instrumentation-wsgi (==0.46b0)"]
|
||||
pure-eval = ["asttokens", "executing", "pure-eval"]
|
||||
pymongo = ["pymongo (>=3.1)"]
|
||||
pyspark = ["pyspark (>=2.4.4)"]
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@
|
|||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"format": "npx prettier --write \"{tests,src}/**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore",
|
||||
"check-format": "npx prettier --check \"{tests,src}/**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore",
|
||||
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
|
@ -132,4 +133,4 @@
|
|||
"ua-parser-js": "^1.0.38",
|
||||
"vite": "^5.3.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,13 +15,13 @@ dotenv.config({ path: path.resolve(__dirname, "../../.env") });
|
|||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: false,
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
retries: process.env.CI ? 2 : 3,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: 1,
|
||||
workers: 2,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
timeout: 120 * 1000,
|
||||
// reporter: [
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export default function HandleRenderComponent({
|
|||
colors,
|
||||
setFilterEdge,
|
||||
showNode,
|
||||
testIdComplement,
|
||||
}: {
|
||||
left: boolean;
|
||||
nodes: any;
|
||||
|
|
@ -33,6 +34,7 @@ export default function HandleRenderComponent({
|
|||
colors: string[];
|
||||
setFilterEdge: any;
|
||||
showNode: any;
|
||||
testIdComplement?: string;
|
||||
}) {
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -52,7 +54,7 @@ export default function HandleRenderComponent({
|
|||
side={left ? "left" : "right"}
|
||||
>
|
||||
<Handle
|
||||
data-test-id={`handle-${title.toLowerCase()}-${
|
||||
data-testid={`handle-${testIdComplement}-${title.toLowerCase()}-${
|
||||
!showNode ? (left ? "target" : "source") : left ? "left" : "right"
|
||||
}`}
|
||||
type={left ? "target" : "source"}
|
||||
|
|
|
|||
|
|
@ -174,9 +174,10 @@ export default function ParameterComponent({
|
|||
|
||||
const handleOnNewValue = async (
|
||||
newValue: string | string[] | boolean | Object[],
|
||||
dbValue?: boolean,
|
||||
skipSnapshot: boolean | undefined = false,
|
||||
): Promise<void> => {
|
||||
handleOnNewValueHook(newValue, skipSnapshot);
|
||||
handleOnNewValueHook(newValue, dbValue, skipSnapshot);
|
||||
};
|
||||
|
||||
const handleNodeClass = (newNodeClass: APIClassType, code?: string): void => {
|
||||
|
|
@ -264,6 +265,7 @@ export default function ParameterComponent({
|
|||
colors={colors}
|
||||
setFilterEdge={setFilterEdge}
|
||||
showNode={showNode}
|
||||
testIdComplement={`${data?.type?.toLowerCase()}-noshownode`}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
|
|
@ -390,6 +392,7 @@ export default function ParameterComponent({
|
|||
colors={colors}
|
||||
setFilterEdge={setFilterEdge}
|
||||
showNode={showNode}
|
||||
testIdComplement={`${data?.type?.toLowerCase()}-shownode`}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
@ -468,16 +471,6 @@ export default function ParameterComponent({
|
|||
<InputGlobalComponent
|
||||
disabled={disabled}
|
||||
onChange={handleOnNewValue}
|
||||
setDb={(value) => {
|
||||
setNode(data.id, (oldNode) => {
|
||||
let newNode = cloneDeep(oldNode);
|
||||
newNode.data = {
|
||||
...newNode.data,
|
||||
};
|
||||
newNode.data.node.template[name].load_from_db = value;
|
||||
return newNode;
|
||||
});
|
||||
}}
|
||||
name={name}
|
||||
data={data.node?.template[name]!}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import useFlowStore from "../../stores/flowStore";
|
|||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { useShortcutsStore } from "../../stores/shortcuts";
|
||||
import { useTypesStore } from "../../stores/typesStore";
|
||||
import { VertexBuildTypeAPI } from "../../types/api";
|
||||
import { OutputFieldType, VertexBuildTypeAPI } from "../../types/api";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
|
||||
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
||||
|
|
@ -279,7 +279,7 @@ export default function GenericNode({
|
|||
|
||||
const shortcuts = useShortcutsStore((state) => state.shortcuts);
|
||||
|
||||
const renderOutputParameter = (output, idx) => {
|
||||
const renderOutputParameter = (output: OutputFieldType, idx: number) => {
|
||||
return (
|
||||
<ParameterComponent
|
||||
index={idx}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { nodeColors } from "../../utils/styleUtils";
|
||||
|
||||
export function getNodeInputColors(input_types, type, types) {
|
||||
export function getNodeInputColors(
|
||||
input_types: string[] | undefined,
|
||||
type: string | undefined,
|
||||
types: { [char: string]: string },
|
||||
) {
|
||||
// Helper function to get the color based on type
|
||||
const getColorByType = (type) => nodeColors[type] ?? nodeColors.unknown;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
import { OutputFieldType } from "../../types/api";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import { nodeColors } from "../../utils/styleUtils";
|
||||
|
||||
export function getNodeOutputColors(output, data, types): string[] {
|
||||
export function getNodeOutputColors(
|
||||
output: OutputFieldType,
|
||||
data: NodeDataType,
|
||||
types: { [char: string]: string },
|
||||
): string[] {
|
||||
// Helper function to get the color based on type
|
||||
const getColorByType = (type) => nodeColors[type] ?? nodeColors.unknown;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@ import {
|
|||
} from "../../constants/constants";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { ResponseErrorDetailAPI } from "../../types/api";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
|
||||
const useFetchDataOnMount = (
|
||||
data,
|
||||
name,
|
||||
handleUpdateValues,
|
||||
setNode,
|
||||
setIsLoading,
|
||||
data: NodeDataType,
|
||||
name: string,
|
||||
handleUpdateValues: (name: string, data: NodeDataType) => Promise<any>,
|
||||
setNode: (id: string, callback: (oldNode: any) => any) => void,
|
||||
setIsLoading: (value: boolean) => void,
|
||||
) => {
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,19 +5,20 @@ import {
|
|||
} from "../../constants/constants";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { ResponseErrorTypeAPI } from "../../types/api";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
|
||||
const useHandleOnNewValue = (
|
||||
data,
|
||||
name,
|
||||
takeSnapshot,
|
||||
handleUpdateValues,
|
||||
debouncedHandleUpdateValues,
|
||||
setNode,
|
||||
setIsLoading,
|
||||
data: NodeDataType,
|
||||
name: string,
|
||||
takeSnapshot: () => void,
|
||||
handleUpdateValues: (name: string, data: NodeDataType) => Promise<any>,
|
||||
debouncedHandleUpdateValues: any,
|
||||
setNode: (id: string, callback: (oldNode: any) => any) => void,
|
||||
setIsLoading: (value: boolean) => void,
|
||||
) => {
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
||||
const handleOnNewValue = async (newValue, skipSnapshot = false) => {
|
||||
const handleOnNewValue = async (newValue, dbValue, skipSnapshot = false) => {
|
||||
const nodeTemplate = data.node!.template[name];
|
||||
const currentValue = nodeTemplate.value;
|
||||
|
||||
|
|
@ -62,6 +63,10 @@ const useHandleOnNewValue = (
|
|||
...newNode.data,
|
||||
};
|
||||
|
||||
if (dbValue) {
|
||||
newNode.data.node.template[name].load_from_db = dbValue;
|
||||
}
|
||||
|
||||
if (data.node?.template[name].real_time_refresh && newTemplate) {
|
||||
newNode.data.node.template = newTemplate;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
|
||||
const useHandleNodeClass = (
|
||||
data,
|
||||
name,
|
||||
takeSnapshot,
|
||||
setNode,
|
||||
updateNodeInternals,
|
||||
data: NodeDataType,
|
||||
name: string,
|
||||
takeSnapshot: () => void,
|
||||
setNode: (id: string, callback: (oldNode: any) => any) => void,
|
||||
updateNodeInternals: (id: string) => void,
|
||||
) => {
|
||||
const handleNodeClass = (newNodeClass, code, type?: string) => {
|
||||
if (!data.node) return;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ import useAlertStore from "../../stores/alertStore";
|
|||
import { ResponseErrorDetailAPI } from "../../types/api";
|
||||
import { handleUpdateValues } from "../../utils/parameterUtils";
|
||||
|
||||
const useHandleRefreshButtonPress = (setIsLoading, setNode) => {
|
||||
const useHandleRefreshButtonPress = (
|
||||
setIsLoading: (value: boolean) => void,
|
||||
setNode: (id: string, callback: (oldNode: any) => any) => void,
|
||||
) => {
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
||||
const handleRefreshButtonPress = async (name, data) => {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ const useIconStatus = (
|
|||
buildStatus: BuildStatus | undefined,
|
||||
validationStatus: VertexBuildTypeAPI | null,
|
||||
) => {
|
||||
const conditionSuccess = validationStatus && validationStatus.valid;
|
||||
const conditionSuccess =
|
||||
!(!buildStatus || buildStatus === BuildStatus.TO_BUILD) &&
|
||||
validationStatus &&
|
||||
validationStatus.valid;
|
||||
const conditionError = buildStatus === BuildStatus.ERROR;
|
||||
const conditionInactive = buildStatus === BuildStatus.INACTIVE;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import { useEffect } from "react";
|
||||
import { FlowPoolType } from "../../types/zustand/flow";
|
||||
|
||||
const useUpdateValidationStatus = (dataId, flowPool, setValidationStatus) => {
|
||||
const useUpdateValidationStatus = (
|
||||
dataId: string,
|
||||
flowPool: FlowPoolType,
|
||||
setValidationStatus: (value: any) => void,
|
||||
) => {
|
||||
useEffect(() => {
|
||||
const relevantData =
|
||||
flowPool[dataId] && flowPool[dataId]?.length > 0
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { isErrorLog } from "../../types/utils/typeCheckingUtils";
|
|||
|
||||
const useValidationStatusString = (
|
||||
validationStatus: VertexBuildTypeAPI | null,
|
||||
setValidationString,
|
||||
setValidationString: (value: any) => void,
|
||||
) => {
|
||||
useEffect(() => {
|
||||
if (validationStatus && validationStatus.data?.outputs) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import useAlertStore from "../../stores/alertStore";
|
|||
import ForwardedIconComponent from "../genericIconComponent";
|
||||
import { Separator } from "../ui/separator";
|
||||
|
||||
export default function ImageViewer({ image }) {
|
||||
export default function ImageViewer({ image }: { image: string }) {
|
||||
const viewerRef = useRef(null);
|
||||
const [errorDownloading, setErrordownloading] = useState(false);
|
||||
const setErrorList = useAlertStore((state) => state.setErrorData);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ 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 = () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import { useEffect } from "react";
|
||||
import { storeComponent } from "../../../types/store";
|
||||
|
||||
const useDataEffect = (
|
||||
data: storeComponent,
|
||||
setLikedByUser: (value: any) => void,
|
||||
setLikesCount: (value: any) => void,
|
||||
setDownloadsCount: (value: any) => void,
|
||||
) => {
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setLikedByUser(data?.liked_by_user ?? false);
|
||||
setLikesCount(data?.liked_by_count ?? 0);
|
||||
setDownloadsCount(data?.downloads_count ?? 0);
|
||||
}
|
||||
}, [data, data?.liked_by_count, data?.liked_by_user, data?.downloads_count]);
|
||||
};
|
||||
|
||||
export default useDataEffect;
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import { useState } from "react";
|
||||
import { getComponent } from "../../../controllers/API";
|
||||
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
||||
import { storeComponent } from "../../../types/store";
|
||||
import cloneFlowWithParent from "../../../utils/storeUtils";
|
||||
|
||||
const useInstallComponent = (
|
||||
data: storeComponent,
|
||||
name: string,
|
||||
isStore: boolean,
|
||||
downloadsCount: number,
|
||||
setDownloadsCount: (value: any) => void,
|
||||
setLoading: (value: boolean) => void,
|
||||
setSuccessData: (value: { title: string }) => void,
|
||||
setErrorData: (value: { title: string; list: string[] }) => void,
|
||||
) => {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
|
||||
const handleInstall = () => {
|
||||
const temp = downloadsCount;
|
||||
setDownloadsCount((old) => Number(old) + 1);
|
||||
setLoading(true);
|
||||
|
||||
getComponent(data.id)
|
||||
.then((res) => {
|
||||
const newFlow = cloneFlowWithParent(res, res.id, data.is_component);
|
||||
addFlow(true, newFlow)
|
||||
.then((id) => {
|
||||
setSuccessData({
|
||||
title: `${name} ${isStore ? "Downloaded" : "Installed"} Successfully.`,
|
||||
});
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
setErrorData({
|
||||
title: `Error ${isStore ? "downloading" : "installing"} the ${name}`,
|
||||
list: [error.response.data.detail],
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoading(false);
|
||||
setErrorData({
|
||||
title: `Error ${isStore ? "downloading" : "installing"} the ${name}`,
|
||||
list: [err.response.data.detail],
|
||||
});
|
||||
setDownloadsCount(temp);
|
||||
});
|
||||
};
|
||||
|
||||
return { handleInstall };
|
||||
};
|
||||
|
||||
export default useInstallComponent;
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import { postLikeComponent } from "../../../controllers/API";
|
||||
import { storeComponent } from "../../../types/store";
|
||||
|
||||
const useLikeComponent = (
|
||||
data: storeComponent,
|
||||
name: string,
|
||||
setLoadingLike: (value: boolean) => void,
|
||||
likedByUser: boolean | null | undefined,
|
||||
likesCount: number,
|
||||
setLikedByUser: (value: any) => void,
|
||||
setLikesCount: (value: any) => void,
|
||||
setValidApiKey: (value: boolean) => void,
|
||||
setErrorData: (value: { title: string; list: string[] }) => void,
|
||||
) => {
|
||||
const handleLike = () => {
|
||||
setLoadingLike(true);
|
||||
if (likedByUser !== undefined || likedByUser !== null) {
|
||||
const temp = likedByUser;
|
||||
const tempNum = likesCount;
|
||||
setLikedByUser((prev) => !prev);
|
||||
setLikesCount((prev) => (temp ? prev - 1 : prev + 1));
|
||||
|
||||
postLikeComponent(data.id)
|
||||
.then((response) => {
|
||||
setLoadingLike(false);
|
||||
setLikesCount(response.data.likes_count);
|
||||
setLikedByUser(response.data.liked_by_user);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoadingLike(false);
|
||||
setLikesCount(tempNum);
|
||||
setLikedByUser(temp);
|
||||
if (error.response.status === 403) {
|
||||
setValidApiKey(false);
|
||||
} else {
|
||||
console.error(error);
|
||||
setErrorData({
|
||||
title: `Error liking ${name}.`,
|
||||
list: [error.response.data.detail],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
handleLike,
|
||||
};
|
||||
};
|
||||
|
||||
export default useLikeComponent;
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import { useCallback } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
||||
import { storeComponent } from "../../../types/store";
|
||||
import DragCardComponent from "../components/dragCardComponent";
|
||||
|
||||
const useDragStart = (data: storeComponent) => {
|
||||
const getFlowById = useFlowsManagerStore((state) => state.getFlowById);
|
||||
|
||||
const onDragStart = useCallback(
|
||||
(event) => {
|
||||
let image = <DragCardComponent data={data} />; // Replace with whatever you want here
|
||||
|
||||
const ghost = document.createElement("div");
|
||||
ghost.style.transform = "translate(-10000px, -10000px)";
|
||||
ghost.style.position = "absolute";
|
||||
document.body.appendChild(ghost);
|
||||
event.dataTransfer.setDragImage(ghost, 0, 0);
|
||||
|
||||
const root = createRoot(ghost);
|
||||
root.render(image);
|
||||
|
||||
const flow = getFlowById(data.id);
|
||||
if (flow) {
|
||||
event.dataTransfer.setData("flow", JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
[data],
|
||||
);
|
||||
|
||||
return { onDragStart };
|
||||
};
|
||||
|
||||
export default useDragStart;
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import { useEffect } from "react";
|
||||
import { FlowType } from "../../../types/flow";
|
||||
|
||||
const usePlaygroundEffect = (
|
||||
currentFlowId: string,
|
||||
playground: boolean,
|
||||
openPlayground: boolean,
|
||||
currentFlow: FlowType | undefined,
|
||||
setNodes: (value: any, value2: boolean) => void,
|
||||
setEdges: (value: any, value2: boolean) => void,
|
||||
cleanFlowPool: () => void,
|
||||
) => {
|
||||
useEffect(() => {
|
||||
if (currentFlowId && playground) {
|
||||
if (openPlayground) {
|
||||
setNodes(currentFlow?.data?.nodes ?? [], true);
|
||||
setEdges(currentFlow?.data?.edges ?? [], true);
|
||||
} else {
|
||||
setNodes([], true);
|
||||
setEdges([], true);
|
||||
}
|
||||
cleanFlowPool();
|
||||
}
|
||||
}, [openPlayground]);
|
||||
};
|
||||
|
||||
export default usePlaygroundEffect;
|
||||
|
|
@ -28,6 +28,11 @@ import { Checkbox } from "../ui/checkbox";
|
|||
import { FormControl, FormField } from "../ui/form";
|
||||
import Loading from "../ui/loading";
|
||||
import DragCardComponent from "./components/dragCardComponent";
|
||||
import useDataEffect from "./hooks/use-data-effect";
|
||||
import useInstallComponent from "./hooks/use-handle-install";
|
||||
import useLikeComponent from "./hooks/use-handle-like";
|
||||
import useDragStart from "./hooks/use-on-drag-start";
|
||||
import usePlaygroundEffect from "./hooks/use-playground-effect";
|
||||
import { convertTestName } from "./utils/convert-test-name";
|
||||
|
||||
export default function CollectionCardComponent({
|
||||
|
|
@ -59,11 +64,9 @@ export default function CollectionCardComponent({
|
|||
const isStore = false;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [loadingLike, setLoadingLike] = useState(false);
|
||||
const [liked_by_user, setLiked_by_user] = useState(
|
||||
data?.liked_by_user ?? false,
|
||||
);
|
||||
const [likes_count, setLikes_count] = useState(data?.liked_by_count ?? 0);
|
||||
const [downloads_count, setDownloads_count] = useState(
|
||||
const [likedByUser, setLikedByUser] = useState(data?.liked_by_user ?? false);
|
||||
const [likesCount, setLikesCount] = useState(data?.liked_by_count ?? 0);
|
||||
const [downloadsCount, setDownloadsCount] = useState(
|
||||
data?.downloads_count ?? 0,
|
||||
);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
|
|
@ -99,115 +102,45 @@ export default function CollectionCardComponent({
|
|||
return inputs.length > 0 || outputs.length > 0;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (currentFlowId && playground) {
|
||||
if (openPlayground) {
|
||||
setNodes(currentFlow?.data?.nodes ?? [], true);
|
||||
setEdges(currentFlow?.data?.edges ?? [], true);
|
||||
} else {
|
||||
setNodes([], true);
|
||||
setEdges([], true);
|
||||
}
|
||||
cleanFlowPool();
|
||||
}
|
||||
}, [openPlayground]);
|
||||
usePlaygroundEffect(
|
||||
currentFlowId,
|
||||
playground!,
|
||||
openPlayground,
|
||||
currentFlow,
|
||||
setNodes,
|
||||
setEdges,
|
||||
cleanFlowPool,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setLiked_by_user(data?.liked_by_user ?? false);
|
||||
setLikes_count(data?.liked_by_count ?? 0);
|
||||
setDownloads_count(data?.downloads_count ?? 0);
|
||||
}
|
||||
}, [data, data.liked_by_count, data.liked_by_user, data.downloads_count]);
|
||||
useDataEffect(data, setLikedByUser, setLikesCount, setDownloadsCount);
|
||||
|
||||
function handleInstall() {
|
||||
const temp = downloads_count;
|
||||
setDownloads_count((old) => Number(old) + 1);
|
||||
setLoading(true);
|
||||
getComponent(data.id)
|
||||
.then((res) => {
|
||||
const newFlow = cloneFLowWithParent(res, res.id, data.is_component);
|
||||
addFlow(true, newFlow)
|
||||
.then((id) => {
|
||||
setSuccessData({
|
||||
title: `${name} ${
|
||||
isStore ? "Downloaded" : "Installed"
|
||||
} Successfully.`,
|
||||
});
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
setErrorData({
|
||||
title: `Error ${
|
||||
isStore ? "downloading" : "installing"
|
||||
} the ${name}`,
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoading(false);
|
||||
setErrorData({
|
||||
title: `Error ${isStore ? "downloading" : "installing"} the ${name}`,
|
||||
list: [err["response"]["data"]["detail"]],
|
||||
});
|
||||
setDownloads_count(temp);
|
||||
});
|
||||
}
|
||||
const { handleInstall } = useInstallComponent(
|
||||
data,
|
||||
name,
|
||||
isStore,
|
||||
downloadsCount,
|
||||
setDownloadsCount,
|
||||
setLoading,
|
||||
setSuccessData,
|
||||
setErrorData,
|
||||
);
|
||||
|
||||
function handleLike() {
|
||||
setLoadingLike(true);
|
||||
if (liked_by_user !== undefined || liked_by_user !== null) {
|
||||
const temp = liked_by_user;
|
||||
const tempNum = likes_count;
|
||||
setLiked_by_user((prev) => !prev);
|
||||
if (!temp) {
|
||||
setLikes_count((prev) => Number(prev) + 1);
|
||||
} else {
|
||||
setLikes_count((prev) => Number(prev) - 1);
|
||||
}
|
||||
postLikeComponent(data.id)
|
||||
.then((response) => {
|
||||
setLoadingLike(false);
|
||||
setLikes_count(response.data.likes_count);
|
||||
setLiked_by_user(response.data.liked_by_user);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoadingLike(false);
|
||||
setLikes_count(tempNum);
|
||||
setLiked_by_user(temp);
|
||||
if (error.response.status === 403) {
|
||||
setValidApiKey(false);
|
||||
} else {
|
||||
console.error(error);
|
||||
setErrorData({
|
||||
title: `Error liking ${name}.`,
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
const { handleLike } = useLikeComponent(
|
||||
data,
|
||||
name,
|
||||
setLoadingLike,
|
||||
likedByUser,
|
||||
likesCount,
|
||||
setLikedByUser,
|
||||
setLikesCount,
|
||||
setValidApiKey,
|
||||
setErrorData,
|
||||
);
|
||||
|
||||
const isSelectedCard =
|
||||
selectedFlowsComponentsCards?.includes(data?.id) ?? false;
|
||||
|
||||
function onDragStart(event: React.DragEvent<any>) {
|
||||
let image: JSX.Element = <DragCardComponent data={data} />; // <== whatever you want here
|
||||
|
||||
var ghost = document.createElement("div");
|
||||
ghost.style.transform = "translate(-10000px, -10000px)";
|
||||
ghost.style.position = "absolute";
|
||||
document.body.appendChild(ghost);
|
||||
event.dataTransfer.setDragImage(ghost, 0, 0);
|
||||
const root = createRoot(ghost);
|
||||
root.render(image);
|
||||
const flow = getFlowById(data.id);
|
||||
if (flow) {
|
||||
event.dataTransfer.setData("flow", JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
const { onDragStart } = useDragStart(data);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -264,7 +197,7 @@ export default function CollectionCardComponent({
|
|||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="Heart" className={cn("h-4 w-4")} />
|
||||
<span data-testid={`likes-${data.name}`}>
|
||||
{likes_count ?? 0}
|
||||
{likesCount ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
|
|
@ -275,7 +208,7 @@ export default function CollectionCardComponent({
|
|||
className="h-4 w-4"
|
||||
/>
|
||||
<span data-testid={`downloads-${data.name}`}>
|
||||
{downloads_count ?? 0}
|
||||
{downloadsCount ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
|
|
@ -296,6 +229,7 @@ export default function CollectionCardComponent({
|
|||
render={({ field }) => (
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
data-testid={`checkbox-component`}
|
||||
aria-label="checkbox-component"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
|
|
@ -324,24 +258,19 @@ export default function CollectionCardComponent({
|
|||
)}
|
||||
</span>
|
||||
)}
|
||||
<div className="flex w-full flex-1 flex-wrap gap-2">
|
||||
{/* {data.tags &&
|
||||
data.tags.length > 0 &&
|
||||
data.tags.map((tag, index) => (
|
||||
<Badge
|
||||
key={index}
|
||||
variant="outline"
|
||||
size="xq"
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
{tag.name}
|
||||
</Badge>
|
||||
))} */}
|
||||
</div>
|
||||
<div className="flex w-full flex-1 flex-wrap gap-2"></div>
|
||||
</div>
|
||||
|
||||
<CardDescription className="pb-2 pt-2">
|
||||
<div className="truncate-doubleline">{data.description}</div>
|
||||
<div
|
||||
className={
|
||||
data?.metadata !== undefined
|
||||
? "truncate"
|
||||
: "truncate-doubleline"
|
||||
}
|
||||
>
|
||||
{data.description}
|
||||
</div>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</div>
|
||||
|
|
@ -457,7 +386,7 @@ export default function CollectionCardComponent({
|
|||
name="Heart"
|
||||
className={cn(
|
||||
"h-5 w-5",
|
||||
liked_by_user
|
||||
likedByUser
|
||||
? "fill-destructive stroke-destructive"
|
||||
: "",
|
||||
!authorized ? "text-ring" : "",
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ export default function FlowToolbar(): JSX.Element {
|
|||
? "button-disable text-muted-foreground"
|
||||
: "",
|
||||
)}
|
||||
data-testid="shared-button-flow"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="Share3"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export default function CodeAreaComponent({
|
|||
useEffect(() => {
|
||||
if (disabled && myValue !== "") {
|
||||
setMyValue("");
|
||||
onChange("", true);
|
||||
onChange("", undefined, true);
|
||||
}
|
||||
}, [disabled]);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
TabsTrigger,
|
||||
} from "../../components/ui/tabs";
|
||||
import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants";
|
||||
import getTabsOrder from "../../modals/apiModal/utils/get-tabs-order";
|
||||
import { Case } from "../../shared/components/caseComponent";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
|
|
@ -56,6 +57,8 @@ export default function CodeTabsComponent({
|
|||
setActiveTweaks,
|
||||
activeTweaks,
|
||||
allowExport = false,
|
||||
isThereTweaks = false,
|
||||
isThereWH = false,
|
||||
}: codeTabsPropsType) {
|
||||
const [isCopied, setIsCopied] = useState<Boolean>(false);
|
||||
const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null);
|
||||
|
|
@ -93,6 +96,8 @@ export default function CodeTabsComponent({
|
|||
return node.data.node.template[templateParam].type;
|
||||
};
|
||||
|
||||
const tabsOrder = getTabsOrder(isThereWH, isThereTweaks);
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
|
|
@ -172,7 +177,7 @@ export default function CodeTabsComponent({
|
|||
className="api-modal-tabs-content overflow-hidden"
|
||||
key={idx} // Remember to add a unique key prop
|
||||
>
|
||||
{idx < 5 ? (
|
||||
{tabsOrder[idx].toLowerCase() !== "tweaks" ? (
|
||||
<div className="flex h-full w-full flex-col">
|
||||
{tab.description && (
|
||||
<div
|
||||
|
|
@ -188,7 +193,7 @@ export default function CodeTabsComponent({
|
|||
{tab.code}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
) : idx === 5 ? (
|
||||
) : tabsOrder[idx].toLowerCase() === "tweaks" ? (
|
||||
<>
|
||||
<div className="api-modal-according-display">
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -10,9 +10,14 @@ function DataOutputComponent({
|
|||
columnMode = "union",
|
||||
}: {
|
||||
pagination: boolean;
|
||||
rows: any;
|
||||
rows: any[];
|
||||
columnMode?: "intersection" | "union";
|
||||
}) {
|
||||
// If the rows are not an array of objects, convert them to an array of objects
|
||||
if (rows.some((row) => typeof row !== "object")) {
|
||||
rows = rows.map((row) => ({ data: row }));
|
||||
}
|
||||
|
||||
const columns = extractColumnsFromRows(rows, columnMode);
|
||||
|
||||
const columnDefs = columns.map((col, idx) => ({
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Label } from "../../components/ui/label";
|
|||
import { Textarea } from "../../components/ui/textarea";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { InputProps } from "../../types/components";
|
||||
import { cn } from "../../utils/utils";
|
||||
import { cn, isEndpointNameValid } from "../../utils/utils";
|
||||
|
||||
export const EditFlowSettings: React.FC<InputProps> = ({
|
||||
name,
|
||||
|
|
@ -17,7 +17,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
setEndpointName,
|
||||
}: InputProps): JSX.Element => {
|
||||
const [isMaxLength, setIsMaxLength] = useState(false);
|
||||
const [isEndpointNameValid, setIsEndpointNameValid] = useState(true);
|
||||
const [validEndpointName, setValidEndpointName] = useState(true);
|
||||
const [isInvalidName, setIsInvalidName] = useState(false);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
|
||||
|
|
@ -34,10 +34,6 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
invalid = true;
|
||||
break;
|
||||
}
|
||||
if (value === currentFlow?.name) {
|
||||
invalid = true;
|
||||
break;
|
||||
}
|
||||
invalid = false;
|
||||
}
|
||||
setIsInvalidName(invalid);
|
||||
|
|
@ -51,12 +47,8 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
const handleEndpointNameChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
// Validate the endpoint name
|
||||
// use this regex r'^[a-zA-Z0-9_-]+$'
|
||||
const isValid =
|
||||
(/^[a-zA-Z0-9_-]+$/.test(event.target.value) &&
|
||||
event.target.value.length <= maxLength) ||
|
||||
// empty is also valid
|
||||
event.target.value.length === 0;
|
||||
setIsEndpointNameValid(isValid);
|
||||
const isValid = isEndpointNameValid(event.target.value, maxLength);
|
||||
setValidEndpointName(isValid);
|
||||
setEndpointName!(event.target.value);
|
||||
};
|
||||
|
||||
|
|
@ -115,21 +107,21 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
}}
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
<div
|
||||
className={cn(
|
||||
"font-normal text-muted-foreground word-break-break-word",
|
||||
"max-h-[250px] overflow-auto font-normal text-muted-foreground word-break-break-word",
|
||||
description === "" ? "font-light italic" : "",
|
||||
)}
|
||||
>
|
||||
{description === "" ? "No description" : description}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</Label>
|
||||
{setEndpointName && (
|
||||
<Label>
|
||||
<div className="edit-flow-arrangement mt-3">
|
||||
<span className="font-medium">Endpoint Name</span>
|
||||
{!isEndpointNameValid && (
|
||||
{!validEndpointName && (
|
||||
<span className="edit-flow-span">
|
||||
Invalid endpoint name. Use only letters, numbers, hyphens, and
|
||||
underscores ({maxLength} characters max).
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue