Chore/refractor (#4454)

* markdown files and env examples cleanup

* components update

* update jsonlines description

* server refractor

* update telemetry

* add execute custom node

* add ui refractor

* add username and password authenticate

* correctly retrieve past images in agentflowv2

* disable e2e temporarily

* add existing username and password authenticate

* update migration to default workspace

* update todo

* blob storage migrating

* throw error on agent tool call error

* add missing execution import

* add referral

* chore: add error message when importData is undefined

* migrate api keys to db

* fix: data too long for column executionData

* migrate api keys from json to db at init

* add info on account setup

* update docstore missing fields

---------

Co-authored-by: chungyau97 <chungyau97@gmail.com>
This commit is contained in:
Henry Heng
2025-05-27 14:29:42 +08:00
committed by GitHub
parent e35a126b46
commit 5a37227d14
560 changed files with 62127 additions and 4100 deletions
@@ -1,33 +0,0 @@
name: autoSyncMergedPullRequest
on:
pull_request_target:
types:
- closed
branches: ['main']
jobs:
autoSyncMergedPullRequest:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Show PR info
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo The PR #${{ github.event.pull_request.number }} was merged on main branch!
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.AUTOSYNC_TOKEN }}
repository: ${{ secrets.AUTOSYNC_CH_URL }}
event-type: ${{ secrets.AUTOSYNC_PR_EVENT_TYPE }}
client-payload: >-
{
"ref": "${{ github.ref }}",
"prNumber": "${{ github.event.pull_request.number }}",
"prTitle": "${{ github.event.pull_request.title }}",
"prDescription": "",
"sha": "${{ github.sha }}"
}
@@ -1,36 +0,0 @@
name: autoSyncSingleCommit
on:
push:
branches:
- main
jobs:
doNotAutoSyncSingleCommit:
if: github.event.commits[1] != null
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: IGNORE autoSyncSingleCommit
run: |
echo This single commit has came from a merged commit. We will ignore it. This case is handled in autoSyncMergedPullRequest workflow for merge commits comming from merged pull requests only! Beware, the regular merge commits are not handled by any workflow for the moment.
autoSyncSingleCommit:
if: github.event.commits[1] == null
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: autoSyncSingleCommit
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
run: |
echo Autosync a single commit with id: ${{ github.sha }} from openSource main branch towards cloud hosted version.
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.AUTOSYNC_TOKEN }}
repository: ${{ secrets.AUTOSYNC_CH_URL }}
event-type: ${{ secrets.AUTOSYNC_SC_EVENT_TYPE }}
client-payload: >-
{
"ref": "${{ github.ref }}",
"sha": "${{ github.sha }}",
"commitMessage": "${{ github.event.commits[0].id }}"
}
+51 -3
View File
@@ -3,6 +3,22 @@ name: Docker Image CI
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
registry:
description: 'Container Registry to push the image to.'
type: choice
required: true
default: 'aws_ecr'
options:
- 'docker_hub'
- 'aws_ecr'
environment:
description: 'Environment to push the image to.'
required: true
default: 'dev'
type: choice
options:
- dev
- prod
node_version: node_version:
description: 'Node.js version to build this image with.' description: 'Node.js version to build this image with.'
type: choice type: choice
@@ -19,25 +35,57 @@ on:
jobs: jobs:
docker: docker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment }}
steps: steps:
- name: Set default values
id: defaults
run: |
echo "registry=${{ github.event.inputs.registry || 'aws_ecr' }}" >> $GITHUB_OUTPUT
echo "node_version=${{ github.event.inputs.node_version || '20' }}" >> $GITHUB_OUTPUT
echo "tag_version=${{ github.event.inputs.tag_version || 'latest' }}" >> $GITHUB_OUTPUT
- name: Checkout - name: Checkout
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.1
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0 uses: docker/setup-qemu-action@v3.0.0
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0 uses: docker/setup-buildx-action@v3.0.0
# ------------------------
# Login Steps (conditional)
# ------------------------
- name: Login to Docker Hub - name: Login to Docker Hub
if: steps.defaults.outputs.registry == 'docker_hub'
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Configure AWS Credentials
if: steps.defaults.outputs.registry == 'aws_ecr'
uses: aws-actions/configure-aws-credentials@v3
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to Amazon ECR
if: steps.defaults.outputs.registry == 'aws_ecr'
uses: aws-actions/amazon-ecr-login@v1
# -------------------------
# Build and push (conditional tags)
# -------------------------
- name: Build and push - name: Build and push
uses: docker/build-push-action@v5.3.0 uses: docker/build-push-action@v5.3.0
with: with:
context: . context: .
file: ./docker/Dockerfile file: Dockerfile
build-args: | build-args: |
NODE_VERSION=${{github.event.inputs.node_version}} NODE_VERSION=${{ steps.defaults.outputs.node_version }}
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: flowiseai/flowise:${{github.event.inputs.tag_version}} tags: |
${{ steps.defaults.outputs.registry == 'docker_hub' && format('flowiseai/flowise:{0}', steps.defaults.outputs.tag_version) || format('{0}.dkr.ecr.{1}.amazonaws.com/flowise:{2}', secrets.AWS_ACCOUNT_ID, secrets.AWS_REGION, steps.defaults.outputs.tag_version) }}
+3
View File
@@ -6,6 +6,7 @@ on:
pull_request: pull_request:
branches: branches:
- '*' - '*'
workflow_dispatch:
permissions: permissions:
contents: read contents: read
jobs: jobs:
@@ -31,6 +32,8 @@ jobs:
- run: pnpm install - run: pnpm install
- run: pnpm lint - run: pnpm lint
- run: pnpm build - run: pnpm build
env:
NODE_OPTIONS: '--max_old_space_size=4096'
- name: Cypress install - name: Cypress install
run: pnpm cypress install run: pnpm cypress install
- name: Install dependencies (Cypress Action) - name: Install dependencies (Cypress Action)
+2 -3
View File
@@ -8,13 +8,12 @@ on:
pull_request: pull_request:
branches: branches:
- '*' - '*'
workflow_dispatch:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
PUPPETEER_SKIP_DOWNLOAD: true PUPPETEER_SKIP_DOWNLOAD: true
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- run: docker build --no-cache -t flowise . - run: docker build --no-cache -t flowise .
-4
View File
@@ -125,15 +125,11 @@ Flowise support different environment variables to configure your instance. You
| PORT | The HTTP port Flowise runs on | Number | 3000 | | PORT | The HTTP port Flowise runs on | Number | 3000 |
| CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | String | | | CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | String | |
| IFRAME_ORIGINS | The allowed origins for iframe src embedding | String | | | IFRAME_ORIGINS | The allowed origins for iframe src embedding | String | |
| FLOWISE_USERNAME | Username to login | String | |
| FLOWISE_PASSWORD | Password to login | String | |
| FLOWISE_FILE_SIZE_LIMIT | Upload File Size Limit | String | 50mb | | FLOWISE_FILE_SIZE_LIMIT | Upload File Size Limit | String | 50mb |
| DEBUG | Print logs from components | Boolean | | | DEBUG | Print logs from components | Boolean | |
| LOG_PATH | Location where log files are stored | String | `your-path/Flowise/logs` | | LOG_PATH | Location where log files are stored | String | `your-path/Flowise/logs` |
| LOG_LEVEL | Different levels of logs | Enum String: `error`, `info`, `verbose`, `debug` | `info` | | LOG_LEVEL | Different levels of logs | Enum String: `error`, `info`, `verbose`, `debug` | `info` |
| LOG_JSON_SPACES | Spaces to beautify JSON logs | | 2 | | LOG_JSON_SPACES | Spaces to beautify JSON logs | | 2 |
| APIKEY_STORAGE_TYPE | To store api keys on a JSON file or database. Default is `json` | Enum String: `json`, `db` | `json` |
| APIKEY_PATH | Location where api keys are saved when `APIKEY_STORAGE_TYPE` is `json` | String | `your-path/Flowise/packages/server` |
| TOOL_FUNCTION_BUILTIN_DEP | NodeJS built-in modules to be used for Tool Function | String | | | TOOL_FUNCTION_BUILTIN_DEP | NodeJS built-in modules to be used for Tool Function | String | |
| TOOL_FUNCTION_EXTERNAL_DEP | External modules to be used for Tool Function | String | | | TOOL_FUNCTION_EXTERNAL_DEP | External modules to be used for Tool Function | String | |
| DATABASE_TYPE | Type of database to store the flowise data | Enum String: `sqlite`, `mysql`, `postgres` | `sqlite` | | DATABASE_TYPE | Type of database to store the flowise data | Enum String: `sqlite`, `mysql`, `postgres` | `sqlite` |
+8
View File
@@ -1,3 +1,11 @@
Copyright (c) 2023-present FlowiseAI, Inc.
Portions of this software are licensed as follows:
- All content that resides under https://github.com/FlowiseAI/Flowise/tree/main/packages/server/src/enterprise directory and files with explicit copyright notice such as [IdentityManager.ts](https://github.com/FlowiseAI/Flowise/tree/main/packages/server/src/IdentityManager.ts) are licensed under [Commercial License](https://github.com/FlowiseAI/Flowise/tree/main/packages/server/src/enterprise/LICENSE.md).
- All third party components incorporated into the FlowiseAI Software are licensed under the original license provided by the owner of the applicable component.
- Content outside of the above mentioned directories or restrictions above is available under the "Apache 2.0" license as defined below.
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004
http://www.apache.org/licenses/ http://www.apache.org/licenses/
-15
View File
@@ -31,12 +31,6 @@ Download and Install [NodeJS](https://nodejs.org/en/download) >= 18.15.0
npx flowise start npx flowise start
``` ```
With username & password
```bash
npx flowise start --FLOWISE_USERNAME=user --FLOWISE_PASSWORD=1234
```
3. Open [http://localhost:3000](http://localhost:3000) 3. Open [http://localhost:3000](http://localhost:3000)
## 🐳 Docker ## 🐳 Docker
@@ -138,15 +132,6 @@ Flowise has 3 different modules in a single mono repository.
Any code changes will reload the app automatically on [http://localhost:8080](http://localhost:8080) Any code changes will reload the app automatically on [http://localhost:8080](http://localhost:8080)
## 🔒 Authentication
To enable app level authentication, add `FLOWISE_USERNAME` and `FLOWISE_PASSWORD` to the `.env` file in `packages/server`:
```
FLOWISE_USERNAME=user
FLOWISE_PASSWORD=1234
```
## 🌱 Env Variables ## 🌱 Env Variables
Flowise support different environment variables to configure your instance. You can specify the following variables in the `.env` file inside `packages/server` folder. Read [more](https://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables) Flowise support different environment variables to configure your instance. You can specify the following variables in the `.env` file inside `packages/server` folder. Read [more](https://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables)
+98 -36
View File
@@ -1,16 +1,12 @@
PORT=3000 PORT=3000
# APIKEY_PATH=/your_apikey_path/.flowise # (will be deprecated by end of 2025)
############################################################################################################
############################################## DATABASE ####################################################
############################################################################################################
DATABASE_PATH=/root/.flowise DATABASE_PATH=/root/.flowise
APIKEY_PATH=/root/.flowise
SECRETKEY_PATH=/root/.flowise
LOG_PATH=/root/.flowise/logs
BLOB_STORAGE_PATH=/root/.flowise/storage
# APIKEY_STORAGE_TYPE=json (json | db)
# NUMBER_OF_PROXIES= 1
# CORS_ORIGINS=*
# IFRAME_ORIGINS=*
# DATABASE_TYPE=postgres # DATABASE_TYPE=postgres
# DATABASE_PORT=5432 # DATABASE_PORT=5432
# DATABASE_HOST="" # DATABASE_HOST=""
@@ -20,34 +16,37 @@ BLOB_STORAGE_PATH=/root/.flowise/storage
# DATABASE_SSL=true # DATABASE_SSL=true
# DATABASE_SSL_KEY_BASE64=<Self signed certificate in BASE64> # DATABASE_SSL_KEY_BASE64=<Self signed certificate in BASE64>
############################################################################################################
############################################## SECRET KEYS #################################################
############################################################################################################
# SECRETKEY_STORAGE_TYPE=local #(local | aws) # SECRETKEY_STORAGE_TYPE=local #(local | aws)
# SECRETKEY_PATH=/your_api_key_path/.flowise SECRETKEY_PATH=/root/.flowise
# FLOWISE_SECRETKEY_OVERWRITE=myencryptionkey # FLOWISE_SECRETKEY_OVERWRITE=myencryptionkey # (if you want to overwrite the secret key)
# SECRETKEY_AWS_ACCESS_KEY=<your-access-key> # SECRETKEY_AWS_ACCESS_KEY=<your-access-key>
# SECRETKEY_AWS_SECRET_KEY=<your-secret-key> # SECRETKEY_AWS_SECRET_KEY=<your-secret-key>
# SECRETKEY_AWS_REGION=us-west-2 # SECRETKEY_AWS_REGION=us-west-2
# SECRETKEY_AWS_NAME=FlowiseEncryptionKey
# FLOWISE_USERNAME=user
# FLOWISE_PASSWORD=1234 ############################################################################################################
# FLOWISE_SECRETKEY_OVERWRITE=myencryptionkey ############################################## LOGGING #####################################################
# FLOWISE_FILE_SIZE_LIMIT=50mb ############################################################################################################
# DEBUG=true # DEBUG=true
# LOG_LEVEL=info (error | warn | info | verbose | debug) LOG_PATH=/root/.flowise/logs
# LOG_LEVEL=info #(error | warn | info | verbose | debug)
# TOOL_FUNCTION_BUILTIN_DEP=crypto,fs # TOOL_FUNCTION_BUILTIN_DEP=crypto,fs
# TOOL_FUNCTION_EXTERNAL_DEP=moment,lodash # TOOL_FUNCTION_EXTERNAL_DEP=moment,lodash
# LANGCHAIN_TRACING_V2=true
# LANGCHAIN_ENDPOINT=https://api.smith.langchain.com
# LANGCHAIN_API_KEY=your_api_key
# LANGCHAIN_PROJECT=your_project
# Uncomment the following line to enable model list config, load the list of models from your local config file ############################################################################################################
# see https://raw.githubusercontent.com/FlowiseAI/Flowise/main/packages/components/models.json for the format ############################################## STORAGE #####################################################
# MODEL_LIST_CONFIG_JSON=/your_model_list_config_file_path ############################################################################################################
# STORAGE_TYPE=local (local | s3 | gcs) # STORAGE_TYPE=local (local | s3 | gcs)
# BLOB_STORAGE_PATH=/your_storage_path/.flowise/storage BLOB_STORAGE_PATH=/root/.flowise/storage
# S3_STORAGE_BUCKET_NAME=flowise # S3_STORAGE_BUCKET_NAME=flowise
# S3_STORAGE_ACCESS_KEY_ID=<your-access-key> # S3_STORAGE_ACCESS_KEY_ID=<your-access-key>
# S3_STORAGE_SECRET_ACCESS_KEY=<your-secret-key> # S3_STORAGE_SECRET_ACCESS_KEY=<your-secret-key>
@@ -59,12 +58,69 @@ BLOB_STORAGE_PATH=/root/.flowise/storage
# GOOGLE_CLOUD_STORAGE_BUCKET_NAME=<the-bucket-name> # GOOGLE_CLOUD_STORAGE_BUCKET_NAME=<the-bucket-name>
# GOOGLE_CLOUD_UNIFORM_BUCKET_ACCESS=true # GOOGLE_CLOUD_UNIFORM_BUCKET_ACCESS=true
# SHOW_COMMUNITY_NODES=true
# DISABLED_NODES=bufferMemory,chatOpenAI (comma separated list of node names to disable)
###################### ############################################################################################################
# METRICS COLLECTION ############################################## SETTINGS ####################################################
####################### ############################################################################################################
# NUMBER_OF_PROXIES= 1
# CORS_ORIGINS=*
# IFRAME_ORIGINS=*
# FLOWISE_FILE_SIZE_LIMIT=50mb
# SHOW_COMMUNITY_NODES=true
# DISABLE_FLOWISE_TELEMETRY=true
# DISABLED_NODES=bufferMemory,chatOpenAI (comma separated list of node names to disable)
# Uncomment the following line to enable model list config, load the list of models from your local config file
# see https://raw.githubusercontent.com/FlowiseAI/Flowise/main/packages/components/models.json for the format
# MODEL_LIST_CONFIG_JSON=/your_model_list_config_file_path
############################################################################################################
############################################ AUTH PARAMETERS ###############################################
############################################################################################################
# APP_URL=http://localhost:3000
# SMTP_HOST=smtp.host.com
# SMTP_PORT=465
# SMTP_USER=smtp_user
# SMTP_PASSWORD=smtp_password
# SMTP_SECURE=true
# ALLOW_UNAUTHORIZED_CERTS=false
# SENDER_EMAIL=team@example.com
# JWT_AUTH_TOKEN_SECRET='AABBCCDDAABBCCDDAABBCCDDAABBCCDDAABBCCDD'
# JWT_REFRESH_TOKEN_SECRET='AABBCCDDAABBCCDDAABBCCDDAABBCCDDAABBCCDD'
# JWT_ISSUER='ISSUER'
# JWT_AUDIENCE='AUDIENCE'
# JWT_TOKEN_EXPIRY_IN_MINUTES=360
# JWT_REFRESH_TOKEN_EXPIRY_IN_MINUTES=43200
# EXPIRE_AUTH_TOKENS_ON_RESTART=true # (if you need to expire all tokens on app restart)
# EXPRESS_SESSION_SECRET=flowise
# INVITE_TOKEN_EXPIRY_IN_HOURS=24
# PASSWORD_RESET_TOKEN_EXPIRY_IN_MINS=15
# PASSWORD_SALT_HASH_ROUNDS=10
# TOKEN_HASH_SECRET='popcorn'
# WORKSPACE_INVITE_TEMPLATE_PATH=/path/to/custom/workspace_invite.hbs
############################################################################################################
############################################# ENTERPRISE ###################################################
############################################################################################################
# LICENSE_URL=
# FLOWISE_EE_LICENSE_KEY=
# OFFLINE=
############################################################################################################
########################################### METRICS COLLECTION #############################################
############################################################################################################
# POSTHOG_PUBLIC_API_KEY=your_posthog_public_api_key
# ENABLE_METRICS=false # ENABLE_METRICS=false
# METRICS_PROVIDER=prometheus # prometheus | open_telemetry # METRICS_PROVIDER=prometheus # prometheus | open_telemetry
# METRICS_INCLUDE_NODE_METRICS=true # default is true # METRICS_INCLUDE_NODE_METRICS=true # default is true
@@ -75,15 +131,21 @@ BLOB_STORAGE_PATH=/root/.flowise/storage
# METRICS_OPEN_TELEMETRY_PROTOCOL=http # http | grpc | proto (default is http) # METRICS_OPEN_TELEMETRY_PROTOCOL=http # http | grpc | proto (default is http)
# METRICS_OPEN_TELEMETRY_DEBUG=true # default is false # METRICS_OPEN_TELEMETRY_DEBUG=true # default is false
# Uncomment the following lines to enable global agent proxy
# see https://www.npmjs.com/package/global-agent for more details ############################################################################################################
############################################### PROXY ######################################################
############################################################################################################
# Uncomment the following lines to enable global agent proxy, see https://www.npmjs.com/package/global-agent for more details
# GLOBAL_AGENT_HTTP_PROXY=CorporateHttpProxyUrl # GLOBAL_AGENT_HTTP_PROXY=CorporateHttpProxyUrl
# GLOBAL_AGENT_HTTPS_PROXY=CorporateHttpsProxyUrl # GLOBAL_AGENT_HTTPS_PROXY=CorporateHttpsProxyUrl
# GLOBAL_AGENT_NO_PROXY=ExceptionHostsToBypassProxyIfNeeded # GLOBAL_AGENT_NO_PROXY=ExceptionHostsToBypassProxyIfNeeded
######################
# QUEUE CONFIGURATION ############################################################################################################
####################### ########################################### QUEUE CONFIGURATION ############################################
############################################################################################################
# MODE=queue #(queue | main) # MODE=queue #(queue | main)
# QUEUE_NAME=flowise-queue # QUEUE_NAME=flowise-queue
# QUEUE_REDIS_EVENT_STREAM_MAX_LEN=100000 # QUEUE_REDIS_EVENT_STREAM_MAX_LEN=100000
-15
View File
@@ -9,26 +9,11 @@ Starts Flowise from [DockerHub Image](https://hub.docker.com/r/flowiseai/flowise
3. Open [http://localhost:3000](http://localhost:3000) 3. Open [http://localhost:3000](http://localhost:3000)
4. You can bring the containers down by `docker compose stop` 4. You can bring the containers down by `docker compose stop`
## 🔒 Authentication
1. Create `.env` file and specify the `PORT`, `FLOWISE_USERNAME`, and `FLOWISE_PASSWORD` (refer to `.env.example`)
2. Pass `FLOWISE_USERNAME` and `FLOWISE_PASSWORD` to the `docker-compose.yml` file:
```
environment:
- PORT=${PORT}
- FLOWISE_USERNAME=${FLOWISE_USERNAME}
- FLOWISE_PASSWORD=${FLOWISE_PASSWORD}
```
3. `docker compose up -d`
4. Open [http://localhost:3000](http://localhost:3000)
5. You can bring the containers down by `docker compose stop`
## 🌱 Env Variables ## 🌱 Env Variables
If you like to persist your data (flows, logs, apikeys, credentials), set these variables in the `.env` file inside `docker` folder: If you like to persist your data (flows, logs, apikeys, credentials), set these variables in the `.env` file inside `docker` folder:
- DATABASE_PATH=/root/.flowise - DATABASE_PATH=/root/.flowise
- APIKEY_PATH=/root/.flowise
- LOG_PATH=/root/.flowise/logs - LOG_PATH=/root/.flowise/logs
- SECRETKEY_PATH=/root/.flowise - SECRETKEY_PATH=/root/.flowise
- BLOB_STORAGE_PATH=/root/.flowise/storage - BLOB_STORAGE_PATH=/root/.flowise/storage
+5 -4
View File
@@ -8,8 +8,6 @@ services:
- PORT=${PORT} - PORT=${PORT}
- CORS_ORIGINS=${CORS_ORIGINS} - CORS_ORIGINS=${CORS_ORIGINS}
- IFRAME_ORIGINS=${IFRAME_ORIGINS} - IFRAME_ORIGINS=${IFRAME_ORIGINS}
- FLOWISE_USERNAME=${FLOWISE_USERNAME}
- FLOWISE_PASSWORD=${FLOWISE_PASSWORD}
- FLOWISE_FILE_SIZE_LIMIT=${FLOWISE_FILE_SIZE_LIMIT} - FLOWISE_FILE_SIZE_LIMIT=${FLOWISE_FILE_SIZE_LIMIT}
- DEBUG=${DEBUG} - DEBUG=${DEBUG}
- DATABASE_PATH=${DATABASE_PATH} - DATABASE_PATH=${DATABASE_PATH}
@@ -21,10 +19,13 @@ services:
- DATABASE_PASSWORD=${DATABASE_PASSWORD} - DATABASE_PASSWORD=${DATABASE_PASSWORD}
- DATABASE_SSL=${DATABASE_SSL} - DATABASE_SSL=${DATABASE_SSL}
- DATABASE_SSL_KEY_BASE64=${DATABASE_SSL_KEY_BASE64} - DATABASE_SSL_KEY_BASE64=${DATABASE_SSL_KEY_BASE64}
- APIKEY_STORAGE_TYPE=${APIKEY_STORAGE_TYPE}
- APIKEY_PATH=${APIKEY_PATH}
- SECRETKEY_PATH=${SECRETKEY_PATH} - SECRETKEY_PATH=${SECRETKEY_PATH}
- FLOWISE_SECRETKEY_OVERWRITE=${FLOWISE_SECRETKEY_OVERWRITE} - FLOWISE_SECRETKEY_OVERWRITE=${FLOWISE_SECRETKEY_OVERWRITE}
- SECRETKEY_STORAGE_TYPE=${SECRETKEY_STORAGE_TYPE}
- SECRETKEY_AWS_ACCESS_KEY=${SECRETKEY_AWS_ACCESS_KEY}
- SECRETKEY_AWS_SECRET_KEY=${SECRETKEY_AWS_SECRET_KEY}
- SECRETKEY_AWS_REGION=${SECRETKEY_AWS_REGION}
- SECRETKEY_AWS_NAME=${SECRETKEY_AWS_NAME}
- LOG_LEVEL=${LOG_LEVEL} - LOG_LEVEL=${LOG_LEVEL}
- LOG_PATH=${LOG_PATH} - LOG_PATH=${LOG_PATH}
- BLOB_STORAGE_PATH=${BLOB_STORAGE_PATH} - BLOB_STORAGE_PATH=${BLOB_STORAGE_PATH}
-4
View File
@@ -8,8 +8,6 @@ services:
- PORT=${PORT} - PORT=${PORT}
- CORS_ORIGINS=${CORS_ORIGINS} - CORS_ORIGINS=${CORS_ORIGINS}
- IFRAME_ORIGINS=${IFRAME_ORIGINS} - IFRAME_ORIGINS=${IFRAME_ORIGINS}
- FLOWISE_USERNAME=${FLOWISE_USERNAME}
- FLOWISE_PASSWORD=${FLOWISE_PASSWORD}
- FLOWISE_FILE_SIZE_LIMIT=${FLOWISE_FILE_SIZE_LIMIT} - FLOWISE_FILE_SIZE_LIMIT=${FLOWISE_FILE_SIZE_LIMIT}
- DEBUG=${DEBUG} - DEBUG=${DEBUG}
- DATABASE_PATH=${DATABASE_PATH} - DATABASE_PATH=${DATABASE_PATH}
@@ -21,8 +19,6 @@ services:
- DATABASE_PASSWORD=${DATABASE_PASSWORD} - DATABASE_PASSWORD=${DATABASE_PASSWORD}
- DATABASE_SSL=${DATABASE_SSL} - DATABASE_SSL=${DATABASE_SSL}
- DATABASE_SSL_KEY_BASE64=${DATABASE_SSL_KEY_BASE64} - DATABASE_SSL_KEY_BASE64=${DATABASE_SSL_KEY_BASE64}
- APIKEY_STORAGE_TYPE=${APIKEY_STORAGE_TYPE}
- APIKEY_PATH=${APIKEY_PATH}
- SECRETKEY_PATH=${SECRETKEY_PATH} - SECRETKEY_PATH=${SECRETKEY_PATH}
- FLOWISE_SECRETKEY_OVERWRITE=${FLOWISE_SECRETKEY_OVERWRITE} - FLOWISE_SECRETKEY_OVERWRITE=${FLOWISE_SECRETKEY_OVERWRITE}
- LOG_LEVEL=${LOG_LEVEL} - LOG_LEVEL=${LOG_LEVEL}
+2 -6
View File
@@ -119,16 +119,12 @@ Flowise 在一个单一的单体存储库中有 3 个不同的模块。
Flowise 支持不同的环境变量来配置您的实例。您可以在 `packages/server` 文件夹中的 `.env` 文件中指定以下变量。阅读[更多信息](https://docs.flowiseai.com/environment-variables) Flowise 支持不同的环境变量来配置您的实例。您可以在 `packages/server` 文件夹中的 `.env` 文件中指定以下变量。阅读[更多信息](https://docs.flowiseai.com/environment-variables)
| 变量名 | 描述 | 类型 | 默认值 | | 变量名 | 描述 | 类型 | 默认值 |
| ---------------------------- | ------------------------------------------------------- | ----------------------------------------------- | ----------------------------------- | --- | | ---------------------------- | ------------------------------------------------------- | ----------------------------------------------- | ----------------------------------- | --- | --- |
| PORT | Flowise 运行的 HTTP 端口 | 数字 | 3000 | | PORT | Flowise 运行的 HTTP 端口 | 数字 | 3000 | | |
| FLOWISE_USERNAME | 登录用户名 | 字符串 | |
| FLOWISE_PASSWORD | 登录密码 | 字符串 | |
| FLOWISE_FILE_SIZE_LIMIT | 上传文件大小限制 | 字符串 | 50mb | | | FLOWISE_FILE_SIZE_LIMIT | 上传文件大小限制 | 字符串 | 50mb | |
| DEBUG | 打印组件的日志 | 布尔值 | | | DEBUG | 打印组件的日志 | 布尔值 | |
| LOG_PATH | 存储日志文件的位置 | 字符串 | `your-path/Flowise/logs` | | LOG_PATH | 存储日志文件的位置 | 字符串 | `your-path/Flowise/logs` |
| LOG_LEVEL | 日志的不同级别 | 枚举字符串: `error`, `info`, `verbose`, `debug` | `info` | | LOG_LEVEL | 日志的不同级别 | 枚举字符串: `error`, `info`, `verbose`, `debug` | `info` |
| APIKEY_STORAGE_TYPE | 存储 API 密钥的存储类型 | 枚举字符串: `json`, `db` | `json` |
| APIKEY_PATH | 存储 API 密钥的位置, 当`APIKEY_STORAGE_TYPE`是`json` | 字符串 | `your-path/Flowise/packages/server` |
| TOOL_FUNCTION_BUILTIN_DEP | 用于工具函数的 NodeJS 内置模块 | 字符串 | | | TOOL_FUNCTION_BUILTIN_DEP | 用于工具函数的 NodeJS 内置模块 | 字符串 | |
| TOOL_FUNCTION_EXTERNAL_DEP | 用于工具函数的外部模块 | 字符串 | | | TOOL_FUNCTION_EXTERNAL_DEP | 用于工具函数的外部模块 | 字符串 | |
| DATABASE_TYPE | 存储 flowise 数据的数据库类型 | 枚举字符串: `sqlite`, `mysql`, `postgres` | `sqlite` | | DATABASE_TYPE | 存储 flowise 数据的数据库类型 | 枚举字符串: `sqlite`, `mysql`, `postgres` | `sqlite` |
-15
View File
@@ -31,12 +31,6 @@
npx flowise start npx flowise start
``` ```
ユーザー名とパスワードを入力
```bash
npx flowise start --FLOWISE_USERNAME=user --FLOWISE_PASSWORD=1234
```
3. [http://localhost:3000](http://localhost:3000) を開く 3. [http://localhost:3000](http://localhost:3000) を開く
## 🐳 Docker ## 🐳 Docker
@@ -127,15 +121,6 @@ Flowise には、3 つの異なるモジュールが 1 つの mono リポジト
コードの変更は [http://localhost:8080](http://localhost:8080) に自動的にアプリをリロードします コードの変更は [http://localhost:8080](http://localhost:8080) に自動的にアプリをリロードします
## 🔒 認証
アプリレベルの認証を有効にするには、 `FLOWISE_USERNAME` と `FLOWISE_PASSWORD` を `packages/server` の `.env` ファイルに追加します:
```
FLOWISE_USERNAME=user
FLOWISE_PASSWORD=1234
```
## 🌱 環境変数 ## 🌱 環境変数
Flowise は、インスタンスを設定するためのさまざまな環境変数をサポートしています。`packages/server` フォルダ内の `.env` ファイルで以下の変数を指定することができる。[続き](https://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables)を読む Flowise は、インスタンスを設定するためのさまざまな環境変数をサポートしています。`packages/server` フォルダ内の `.env` ファイルで以下の変数を指定することができる。[続き](https://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables)を読む
-15
View File
@@ -31,12 +31,6 @@
npx flowise start npx flowise start
``` ```
사용자 이름과 비밀번호로 시작하기
```bash
npx flowise start --FLOWISE_USERNAME=user --FLOWISE_PASSWORD=1234
```
3. [http://localhost:3000](http://localhost:3000) URL 열기 3. [http://localhost:3000](http://localhost:3000) URL 열기
## 🐳 도커(Docker)를 활용하여 시작하기 ## 🐳 도커(Docker)를 활용하여 시작하기
@@ -127,15 +121,6 @@ Flowise는 단일 리포지토리에 3개의 서로 다른 모듈이 있습니
코드가 변경되면 [http://localhost:8080](http://localhost:8080)에서 자동으로 애플리케이션을 새로고침 합니다. 코드가 변경되면 [http://localhost:8080](http://localhost:8080)에서 자동으로 애플리케이션을 새로고침 합니다.
## 🔒 인증
애플리케이션 수준의 인증을 사용하려면 `packages/server`의 `.env` 파일에 `FLOWISE_USERNAME` 및 `FLOWISE_PASSWORD`를 추가합니다:
```
FLOWISE_USERNAME=user
FLOWISE_PASSWORD=1234
```
## 🌱 환경 변수 ## 🌱 환경 변수
Flowise는 인스턴스 구성을 위한 다양한 환경 변수를 지원합니다. `packages/server` 폴더 내 `.env` 파일에 다양한 환경 변수를 지정할 수 있습니다. [자세히 보기](https://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables) Flowise는 인스턴스 구성을 위한 다양한 환경 변수를 지원합니다. `packages/server` 폴더 내 `.env` 파일에 다양한 환경 변수를 지정할 수 있습니다. [자세히 보기](https://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables)
-15
View File
@@ -31,12 +31,6 @@
npx flowise start npx flowise start
``` ```
使用用戶名和密碼
```bash
npx flowise start --FLOWISE_USERNAME=user --FLOWISE_PASSWORD=1234
```
3. 打開 [http://localhost:3000](http://localhost:3000) 3. 打開 [http://localhost:3000](http://localhost:3000)
## 🐳 Docker ## 🐳 Docker
@@ -138,15 +132,6 @@ Flowise 在單個 mono 存儲庫中有 3 個不同的模塊。
任何代碼更改都會自動重新加載應用程序 [http://localhost:8080](http://localhost:8080) 任何代碼更改都會自動重新加載應用程序 [http://localhost:8080](http://localhost:8080)
## 🔒 認證
要啟用應用級別的身份驗證,請在 `packages/server` 中的 `.env` 文件中添加 `FLOWISE_USERNAME` 和 `FLOWISE_PASSWORD`
```
FLOWISE_USERNAME=user
FLOWISE_PASSWORD=1234
```
## 🌱 環境變量 ## 🌱 環境變量
Flowise 支持不同的環境變量來配置您的實例。您可以在 `packages/server` 文件夾中的 `.env` 文件中指定以下變量。閱讀 [更多](https://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables) Flowise 支持不同的環境變量來配置您的實例。您可以在 `packages/server` 文件夾中的 `.env` 文件中指定以下變量。閱讀 [更多](https://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables)
-15
View File
@@ -31,12 +31,6 @@
npx flowise start npx flowise start
``` ```
使用用户名和密码
```bash
npx flowise start --FLOWISE_USERNAME=user --FLOWISE_PASSWORD=1234
```
3. 打开 [http://localhost:3000](http://localhost:3000) 3. 打开 [http://localhost:3000](http://localhost:3000)
## 🐳 Docker ## 🐳 Docker
@@ -127,15 +121,6 @@ Flowise 在一个单一的代码库中有 3 个不同的模块。
任何代码更改都会自动重新加载应用程序,访问 [http://localhost:8080](http://localhost:8080) 任何代码更改都会自动重新加载应用程序,访问 [http://localhost:8080](http://localhost:8080)
## 🔒 认证
要启用应用程序级身份验证,在 `packages/server` 的 `.env` 文件中添加 `FLOWISE_USERNAME` 和 `FLOWISE_PASSWORD`
```
FLOWISE_USERNAME=user
FLOWISE_PASSWORD=1234
```
## 🌱 环境变量 ## 🌱 环境变量
Flowise 支持不同的环境变量来配置您的实例。您可以在 `packages/server` 文件夹中的 `.env` 文件中指定以下变量。了解更多信息,请阅读[文档](https://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables) Flowise 支持不同的环境变量来配置您的实例。您可以在 `packages/server` 文件夹中的 `.env` 文件中指定以下变量。了解更多信息,请阅读[文档](https://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables)
+1
View File
@@ -20,6 +20,7 @@
"start-worker": "run-script-os", "start-worker": "run-script-os",
"start-worker:windows": "cd packages/server/bin && run worker", "start-worker:windows": "cd packages/server/bin && run worker",
"start-worker:default": "cd packages/server/bin && ./run worker", "start-worker:default": "cd packages/server/bin && ./run worker",
"test": "turbo run test",
"clean": "pnpm --filter \"./packages/**\" clean", "clean": "pnpm --filter \"./packages/**\" clean",
"nuke": "pnpm --filter \"./packages/**\" nuke && rimraf node_modules .turbo", "nuke": "pnpm --filter \"./packages/**\" nuke && rimraf node_modules .turbo",
"format": "prettier --write \"**/*.{ts,tsx,md}\"", "format": "prettier --write \"**/*.{ts,tsx,md}\"",
@@ -0,0 +1,165 @@
import { RunCollectorCallbackHandler } from '@langchain/core/tracers/run_collector'
import { Run } from '@langchain/core/tracers/base'
import { EvaluationRunner } from './EvaluationRunner'
import { encoding_for_model, get_encoding } from '@dqbd/tiktoken'
export class EvaluationRunTracer extends RunCollectorCallbackHandler {
evaluationRunId: string
model: string
constructor(id: string) {
super()
this.evaluationRunId = id
}
async persistRun(run: Run): Promise<void> {
return super.persistRun(run)
}
countPromptTokens = (encoding: any, run: Run): number => {
let promptTokenCount = 0
if (encoding) {
if (run.inputs?.messages?.length > 0 && run.inputs?.messages[0]?.length > 0) {
run.inputs.messages[0].map((message: any) => {
let content = message.content
? message.content
: message.SystemMessage?.content
? message.SystemMessage.content
: message.HumanMessage?.content
? message.HumanMessage.content
: message.AIMessage?.content
? message.AIMessage.content
: undefined
promptTokenCount += content ? encoding.encode(content).length : 0
})
}
if (run.inputs?.prompts?.length > 0) {
const content = run.inputs.prompts[0]
promptTokenCount += content ? encoding.encode(content).length : 0
}
}
return promptTokenCount
}
countCompletionTokens = (encoding: any, run: Run): number => {
let completionTokenCount = 0
if (encoding) {
if (run.outputs?.generations?.length > 0 && run.outputs?.generations[0]?.length > 0) {
run.outputs?.generations[0].map((chunk: any) => {
let content = chunk.text ? chunk.text : chunk.message?.content ? chunk.message?.content : undefined
completionTokenCount += content ? encoding.encode(content).length : 0
})
}
}
return completionTokenCount
}
extractModelName = (run: Run): string => {
return (
(run?.serialized as any)?.kwargs?.model ||
(run?.serialized as any)?.kwargs?.model_name ||
(run?.extra as any)?.metadata?.ls_model_name ||
(run?.extra as any)?.metadata?.fw_model_name
)
}
onLLMEnd?(run: Run): void | Promise<void> {
if (run.name) {
let provider = run.name
if (provider === 'BedrockChat') {
provider = 'awsChatBedrock'
}
EvaluationRunner.addMetrics(
this.evaluationRunId,
JSON.stringify({
provider: provider
})
)
}
let model = this.extractModelName(run)
if (run.outputs?.llmOutput?.tokenUsage) {
const tokenUsage = run.outputs?.llmOutput?.tokenUsage
if (tokenUsage) {
const metric = {
completionTokens: tokenUsage.completionTokens,
promptTokens: tokenUsage.promptTokens,
model: model,
totalTokens: tokenUsage.totalTokens
}
EvaluationRunner.addMetrics(this.evaluationRunId, JSON.stringify(metric))
}
} else if (
run.outputs?.generations?.length > 0 &&
run.outputs?.generations[0].length > 0 &&
run.outputs?.generations[0][0]?.message?.usage_metadata?.total_tokens
) {
const usage_metadata = run.outputs?.generations[0][0]?.message?.usage_metadata
if (usage_metadata) {
const metric = {
completionTokens: usage_metadata.output_tokens,
promptTokens: usage_metadata.input_tokens,
model: model || this.model,
totalTokens: usage_metadata.total_tokens
}
EvaluationRunner.addMetrics(this.evaluationRunId, JSON.stringify(metric))
}
} else {
let encoding: any = undefined
let promptInputTokens = 0
let completionTokenCount = 0
try {
encoding = encoding_for_model(model as any)
promptInputTokens = this.countPromptTokens(encoding, run)
completionTokenCount = this.countCompletionTokens(encoding, run)
} catch (e) {
try {
// as tiktoken will fail for non openai models, assume that is 'cl100k_base'
encoding = get_encoding('cl100k_base')
promptInputTokens = this.countPromptTokens(encoding, run)
completionTokenCount = this.countCompletionTokens(encoding, run)
} catch (e) {
// stay silent
}
}
const metric = {
completionTokens: completionTokenCount,
promptTokens: promptInputTokens,
model: model,
totalTokens: promptInputTokens + completionTokenCount
}
EvaluationRunner.addMetrics(this.evaluationRunId, JSON.stringify(metric))
//cleanup
this.model = ''
}
}
async onRunUpdate(run: Run): Promise<void> {
const json = {
[run.run_type]: elapsed(run)
}
let metric = JSON.stringify(json)
if (metric) {
EvaluationRunner.addMetrics(this.evaluationRunId, metric)
}
if (run.run_type === 'llm') {
let model = this.extractModelName(run)
if (model) {
EvaluationRunner.addMetrics(this.evaluationRunId, JSON.stringify({ model: model }))
this.model = model
}
// OpenAI non streaming models
const estimatedTokenUsage = run.outputs?.llmOutput?.estimatedTokenUsage
if (estimatedTokenUsage && typeof estimatedTokenUsage === 'object' && Object.keys(estimatedTokenUsage).length > 0) {
EvaluationRunner.addMetrics(this.evaluationRunId, estimatedTokenUsage)
}
}
}
}
function elapsed(run: Run) {
if (!run.end_time) return ''
const elapsed = run.end_time - run.start_time
return `${elapsed.toFixed(2)}`
}
@@ -0,0 +1,186 @@
import { ChatMessage, LLMEndEvent, LLMStartEvent, LLMStreamEvent, MessageContentTextDetail, RetrievalEndEvent, Settings } from 'llamaindex'
import { EvaluationRunner } from './EvaluationRunner'
import { additionalCallbacks, ICommonObject, INodeData } from '../src'
import { RetrievalStartEvent } from 'llamaindex/dist/type/llm/types'
import { AgentEndEvent, AgentStartEvent } from 'llamaindex/dist/type/agent/types'
import { encoding_for_model } from '@dqbd/tiktoken'
import { MessageContent } from '@langchain/core/messages'
export class EvaluationRunTracerLlama {
evaluationRunId: string
static cbInit = false
static startTimes = new Map<string, number>()
static models = new Map<string, string>()
static tokenCounts = new Map<string, number>()
constructor(id: string) {
this.evaluationRunId = id
EvaluationRunTracerLlama.constructCallBacks()
}
static constructCallBacks = () => {
if (!EvaluationRunTracerLlama.cbInit) {
Settings.callbackManager.on('llm-start', (event: LLMStartEvent) => {
const evalID = (event as any).reason.parent?.caller?.evaluationRunId || (event as any).reason.caller?.evaluationRunId
if (!evalID) return
const model = (event as any).reason?.caller?.model
if (model) {
EvaluationRunTracerLlama.models.set(evalID, model)
try {
const encoding = encoding_for_model(model)
if (encoding) {
const { messages } = event.detail.payload
let tokenCount = messages.reduce((count: number, message: ChatMessage) => {
return count + encoding.encode(extractText(message.content)).length
}, 0)
EvaluationRunTracerLlama.tokenCounts.set(evalID + '_promptTokens', tokenCount)
EvaluationRunTracerLlama.tokenCounts.set(evalID + '_outputTokens', 0)
}
} catch (e) {
// catch the error and continue to work.
}
}
EvaluationRunTracerLlama.startTimes.set(evalID + '_llm', event.timeStamp)
})
Settings.callbackManager.on('llm-end', (event: LLMEndEvent) => {
this.calculateAndSetMetrics(event, 'llm')
})
Settings.callbackManager.on('llm-stream', (event: LLMStreamEvent) => {
const evalID = (event as any).reason.parent?.caller?.evaluationRunId || (event as any).reason.caller?.evaluationRunId
if (!evalID) return
const { chunk } = event.detail.payload
const { delta } = chunk
const model = (event as any).reason?.caller?.model
try {
const encoding = encoding_for_model(model)
if (encoding) {
let tokenCount = EvaluationRunTracerLlama.tokenCounts.get(evalID + '_outputTokens') || 0
tokenCount += encoding.encode(extractText(delta)).length
EvaluationRunTracerLlama.tokenCounts.set(evalID + '_outputTokens', tokenCount)
}
} catch (e) {
// catch the error and continue to work.
}
})
Settings.callbackManager.on('retrieve-start', (event: RetrievalStartEvent) => {
const evalID = (event as any).reason.parent?.caller?.evaluationRunId || (event as any).reason.caller?.evaluationRunId
if (evalID) {
EvaluationRunTracerLlama.startTimes.set(evalID + '_retriever', event.timeStamp)
}
})
Settings.callbackManager.on('retrieve-end', (event: RetrievalEndEvent) => {
this.calculateAndSetMetrics(event, 'retriever')
})
Settings.callbackManager.on('agent-start', (event: AgentStartEvent) => {
const evalID = (event as any).reason.parent?.caller?.evaluationRunId || (event as any).reason.caller?.evaluationRunId
if (evalID) {
EvaluationRunTracerLlama.startTimes.set(evalID + '_agent', event.timeStamp)
}
})
Settings.callbackManager.on('agent-end', (event: AgentEndEvent) => {
this.calculateAndSetMetrics(event, 'agent')
})
EvaluationRunTracerLlama.cbInit = true
}
}
private static calculateAndSetMetrics(event: any, label: string) {
const evalID = event.reason.parent?.caller?.evaluationRunId || event.reason.caller?.evaluationRunId
if (!evalID) return
const startTime = EvaluationRunTracerLlama.startTimes.get(evalID + '_' + label) as number
let model =
(event as any).reason?.caller?.model || (event as any).reason?.caller?.llm?.model || EvaluationRunTracerLlama.models.get(evalID)
if (event.detail.payload?.response?.message && model) {
try {
const encoding = encoding_for_model(model)
if (encoding) {
let tokenCount = EvaluationRunTracerLlama.tokenCounts.get(evalID + '_outputTokens') || 0
tokenCount += encoding.encode(event.detail.payload.response?.message?.content || '').length
EvaluationRunTracerLlama.tokenCounts.set(evalID + '_outputTokens', tokenCount)
}
} catch (e) {
// catch the error and continue to work.
}
}
// Anthropic
if (event.detail?.payload?.response?.raw?.usage) {
const usage = event.detail.payload.response.raw.usage
if (usage.output_tokens) {
const metric = {
completionTokens: usage.output_tokens,
promptTokens: usage.input_tokens,
model: model,
totalTokens: usage.input_tokens + usage.output_tokens
}
EvaluationRunner.addMetrics(evalID, JSON.stringify(metric))
} else if (usage.completion_tokens) {
const metric = {
completionTokens: usage.completion_tokens,
promptTokens: usage.prompt_tokens,
model: model,
totalTokens: usage.total_tokens
}
EvaluationRunner.addMetrics(evalID, JSON.stringify(metric))
}
} else if (event.detail?.payload?.response?.raw['amazon-bedrock-invocationMetrics']) {
const usage = event.detail?.payload?.response?.raw['amazon-bedrock-invocationMetrics']
const metric = {
completionTokens: usage.outputTokenCount,
promptTokens: usage.inputTokenCount,
model: event.detail?.payload?.response?.raw.model,
totalTokens: usage.inputTokenCount + usage.outputTokenCount
}
EvaluationRunner.addMetrics(evalID, JSON.stringify(metric))
} else {
const metric = {
[label]: (event.timeStamp - startTime).toFixed(2),
completionTokens: EvaluationRunTracerLlama.tokenCounts.get(evalID + '_outputTokens'),
promptTokens: EvaluationRunTracerLlama.tokenCounts.get(evalID + '_promptTokens'),
model: model || EvaluationRunTracerLlama.models.get(evalID) || '',
totalTokens:
(EvaluationRunTracerLlama.tokenCounts.get(evalID + '_outputTokens') || 0) +
(EvaluationRunTracerLlama.tokenCounts.get(evalID + '_promptTokens') || 0)
}
EvaluationRunner.addMetrics(evalID, JSON.stringify(metric))
}
//cleanup
EvaluationRunTracerLlama.startTimes.delete(evalID + '_' + label)
EvaluationRunTracerLlama.startTimes.delete(evalID + '_outputTokens')
EvaluationRunTracerLlama.startTimes.delete(evalID + '_promptTokens')
EvaluationRunTracerLlama.models.delete(evalID)
}
static async injectEvaluationMetadata(nodeData: INodeData, options: ICommonObject, callerObj: any) {
if (options.evaluationRunId && callerObj) {
// these are needed for evaluation runs
options.llamaIndex = true
await additionalCallbacks(nodeData, options)
Object.defineProperty(callerObj, 'evaluationRunId', {
enumerable: true,
configurable: true,
writable: true,
value: options.evaluationRunId
})
}
}
}
// from https://github.com/run-llama/LlamaIndexTS/blob/main/packages/core/src/llm/utils.ts
export function extractText(message: MessageContent): string {
if (typeof message !== 'string' && !Array.isArray(message)) {
console.warn('extractText called with non-MessageContent message, this is likely a bug.')
return `${message}`
} else if (typeof message !== 'string' && Array.isArray(message)) {
// message is of type MessageContentDetail[] - retrieve just the text parts and concatenate them
// so we can pass them to the context generator
return message
.filter((c): c is MessageContentTextDetail => c.type === 'text')
.map((c) => c.text)
.join('\n\n')
} else {
return message
}
}
@@ -0,0 +1,172 @@
import axios from 'axios'
import { v4 as uuidv4 } from 'uuid'
import { ICommonObject } from '../src'
import { getModelConfigByModelName, MODEL_TYPE } from '../src/modelLoader'
export class EvaluationRunner {
static metrics = new Map<string, string[]>()
static async getAndDeleteMetrics(id: string) {
const val = EvaluationRunner.metrics.get(id)
if (val) {
try {
//first lets get the provider and model
let selectedModel = undefined
let selectedProvider = undefined
if (val && val.length > 0) {
let modelName = ''
let providerName = ''
for (let i = 0; i < val.length; i++) {
const metric = val[i]
if (typeof metric === 'object') {
modelName = metric['model']
providerName = metric['provider']
} else {
modelName = JSON.parse(metric)['model']
providerName = JSON.parse(metric)['provider']
}
if (modelName) {
selectedModel = modelName
}
if (providerName) {
selectedProvider = providerName
}
}
}
let modelConfig = await getModelConfigByModelName(MODEL_TYPE.CHAT, selectedProvider, selectedModel)
if (modelConfig) {
val.push(JSON.stringify({ cost_values: modelConfig }))
} else {
modelConfig = await getModelConfigByModelName(MODEL_TYPE.LLM, selectedProvider, selectedModel)
if (modelConfig) {
val.push(JSON.stringify({ cost_values: modelConfig }))
}
}
} catch (error) {
//stay silent
}
}
EvaluationRunner.metrics.delete(id)
return val
}
static addMetrics(id: string, metric: string) {
if (EvaluationRunner.metrics.has(id)) {
EvaluationRunner.metrics.get(id)?.push(metric)
} else {
EvaluationRunner.metrics.set(id, [metric])
}
}
baseURL = ''
constructor(baseURL: string) {
this.baseURL = baseURL
}
getChatflowApiKey(chatflowId: string, apiKeys: { chatflowId: string; apiKey: string }[] = []) {
return apiKeys.find((item) => item.chatflowId === chatflowId)?.apiKey || ''
}
public async runEvaluations(data: ICommonObject) {
const chatflowIds = JSON.parse(data.chatflowId)
const returnData: ICommonObject = {}
returnData.evaluationId = data.evaluationId
returnData.runDate = new Date()
returnData.rows = []
for (let i = 0; i < data.dataset.rows.length; i++) {
returnData.rows.push({
input: data.dataset.rows[i].input,
expectedOutput: data.dataset.rows[i].output,
itemNo: data.dataset.rows[i].sequenceNo,
evaluations: [],
status: 'pending'
})
}
for (let i = 0; i < chatflowIds.length; i++) {
const chatflowId = chatflowIds[i]
await this.evaluateChatflow(chatflowId, this.getChatflowApiKey(chatflowId, data.apiKeys), data, returnData)
}
return returnData
}
async evaluateChatflow(chatflowId: string, apiKey: string, data: any, returnData: any) {
for (let i = 0; i < data.dataset.rows.length; i++) {
const item = data.dataset.rows[i]
const uuid = uuidv4()
const headers: any = {
'X-Request-ID': uuid,
'X-Flowise-Evaluation': 'true'
}
if (apiKey) {
headers['Authorization'] = `Bearer ${apiKey}`
}
let axiosConfig = {
headers: headers
}
let startTime = performance.now()
const runData: any = {}
runData.chatflowId = chatflowId
runData.startTime = startTime
const postData: any = { question: item.input, evaluationRunId: uuid, evaluation: true }
if (data.sessionId) {
postData.overrideConfig = { sessionId: data.sessionId }
}
try {
let response = await axios.post(`${this.baseURL}/api/v1/prediction/${chatflowId}`, postData, axiosConfig)
const endTime = performance.now()
const timeTaken = (endTime - startTime).toFixed(2)
if (response?.data?.metrics) {
runData.metrics = response.data.metrics
runData.metrics.push({
apiLatency: timeTaken
})
} else {
runData.metrics = [
{
apiLatency: timeTaken
}
]
}
runData.status = 'complete'
let resultText = ''
if (response.data.text) resultText = response.data.text
else if (response.data.json) resultText = '```json\n' + JSON.stringify(response.data.json, null, 2)
else resultText = JSON.stringify(response.data, null, 2)
runData.actualOutput = resultText
runData.latency = timeTaken
runData.error = ''
} catch (error: any) {
runData.status = 'error'
runData.actualOutput = ''
runData.error = error?.response?.data?.message
? error.response.data.message
: error?.message
? error.message
: 'Unknown error'
try {
if (runData.error.indexOf('-') > -1) {
// if there is a dash, remove all content before
runData.error = 'Error: ' + runData.error.substr(runData.error.indexOf('-') + 1).trim()
}
} catch (error) {
//stay silent
}
const endTime = performance.now()
const timeTaken = (endTime - startTime).toFixed(2)
runData.metrics = [
{
apiLatency: timeTaken
}
]
runData.latency = timeTaken
}
runData.uuid = uuid
returnData.rows[i].evaluations.push(runData)
}
return returnData
}
}
@@ -427,7 +427,8 @@ class Agent_Agentflow implements INode {
return returnData return returnData
} }
const stores = await appDataSource.getRepository(databaseEntities['DocumentStore']).find() const searchOptions = options.searchOptions || {}
const stores = await appDataSource.getRepository(databaseEntities['DocumentStore']).findBy(searchOptions)
for (const store of stores) { for (const store of stores) {
if (store.status === 'UPSERTED') { if (store.status === 'UPSERTED') {
const obj = { const obj = {
@@ -152,7 +152,7 @@ class CustomFunction_Agentflow implements INode {
newState = updateFlowState(state, _customFunctionUpdateState) newState = updateFlowState(state, _customFunctionUpdateState)
} }
const variables = await getVars(appDataSource, databaseEntities, nodeData) const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
const flow = { const flow = {
chatflowId: options.chatflowid, chatflowId: options.chatflowid,
sessionId: options.sessionId, sessionId: options.sessionId,
@@ -127,7 +127,8 @@ class ExecuteFlow_Agentflow implements INode {
return returnData return returnData
} }
const chatflows = await appDataSource.getRepository(databaseEntities['ChatFlow']).find() const searchOptions = options.searchOptions || {}
const chatflows = await appDataSource.getRepository(databaseEntities['ChatFlow']).findBy(searchOptions)
for (let i = 0; i < chatflows.length; i += 1) { for (let i = 0; i < chatflows.length; i += 1) {
let cfType = 'Chatflow' let cfType = 'Chatflow'
@@ -119,7 +119,8 @@ class Retriever_Agentflow implements INode {
return returnData return returnData
} }
const stores = await appDataSource.getRepository(databaseEntities['DocumentStore']).find() const searchOptions = options.searchOptions || {}
const stores = await appDataSource.getRepository(databaseEntities['DocumentStore']).findBy(searchOptions)
for (const store of stores) { for (const store of stores) {
if (store.status === 'UPSERTED') { if (store.status === 'UPSERTED') {
const obj = { const obj = {
+3 -3
View File
@@ -18,7 +18,7 @@ export const addImagesToMessages = async (
for (const upload of imageUploads) { for (const upload of imageUploads) {
let bf = upload.data let bf = upload.data
if (upload.type == 'stored-file') { if (upload.type == 'stored-file') {
const contents = await getFileFromStorage(upload.name, options.chatflowid, options.chatId) const contents = await getFileFromStorage(upload.name, options.orgId, options.chatflowid, options.chatId)
// as the image is stored in the server, read the file and convert it to base64 // as the image is stored in the server, read the file and convert it to base64
bf = 'data:' + upload.mime + ';base64,' + contents.toString('base64') bf = 'data:' + upload.mime + ';base64,' + contents.toString('base64')
@@ -90,7 +90,7 @@ export const processMessagesWithImages = async (
hasImageReferences = true hasImageReferences = true
try { try {
// Get file contents from storage // Get file contents from storage
const contents = await getFileFromStorage(item.name, options.chatflowid, options.chatId) const contents = await getFileFromStorage(item.name, options.orgId, options.chatflowid, options.chatId)
// Create base64 data URL // Create base64 data URL
const base64Data = 'data:' + item.mime + ';base64,' + contents.toString('base64') const base64Data = 'data:' + item.mime + ';base64,' + contents.toString('base64')
@@ -319,7 +319,7 @@ export const getPastChatHistoryImageMessages = async (
const imageContents: MessageContentImageUrl[] = [] const imageContents: MessageContentImageUrl[] = []
for (const upload of uploads) { for (const upload of uploads) {
if (upload.type === 'stored-file' && upload.mime.startsWith('image/')) { if (upload.type === 'stored-file' && upload.mime.startsWith('image/')) {
const fileData = await getFileFromStorage(upload.name, options.chatflowid, options.chatId) const fileData = await getFileFromStorage(upload.name, options.orgId, options.chatflowid, options.chatId)
// as the image is stored in the server, read the file and convert it to base64 // as the image is stored in the server, read the file and convert it to base64
const bf = 'data:' + upload.mime + ';base64,' + fileData.toString('base64') const bf = 'data:' + upload.mime + ';base64,' + fileData.toString('base64')
@@ -128,7 +128,7 @@ class Airtable_Agents implements INode {
let base64String = Buffer.from(JSON.stringify(airtableData)).toString('base64') let base64String = Buffer.from(JSON.stringify(airtableData)).toString('base64')
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
const pyodide = await LoadPyodide() const pyodide = await LoadPyodide()
@@ -163,7 +163,7 @@ json.dumps(my_dict)`
const chain = new LLMChain({ const chain = new LLMChain({
llm: model, llm: model,
prompt: PromptTemplate.fromTemplate(systemPrompt), prompt: PromptTemplate.fromTemplate(systemPrompt),
verbose: process.env.DEBUG === 'true' verbose: process.env.DEBUG === 'true' ? true : false
}) })
const inputs = { const inputs = {
dict: dataframeColDict, dict: dataframeColDict,
@@ -192,7 +192,7 @@ json.dumps(my_dict)`
const chain = new LLMChain({ const chain = new LLMChain({
llm: model, llm: model,
prompt: PromptTemplate.fromTemplate(finalSystemPrompt), prompt: PromptTemplate.fromTemplate(finalSystemPrompt),
verbose: process.env.DEBUG === 'true' verbose: process.env.DEBUG === 'true' ? true : false
}) })
const inputs = { const inputs = {
question: input, question: input,
@@ -97,7 +97,7 @@ class CSV_Agents implements INode {
} }
} }
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const shouldStreamResponse = options.shouldStreamResponse const shouldStreamResponse = options.shouldStreamResponse
const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer
const chatId = options.chatId const chatId = options.chatId
@@ -114,11 +114,12 @@ class CSV_Agents implements INode {
} else { } else {
files = [fileName] files = [fileName]
} }
const orgId = options.orgId
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await getFileFromStorage(file, chatflowid) const fileData = await getFileFromStorage(file, orgId, chatflowid)
base64String += fileData.toString('base64') base64String += fileData.toString('base64')
} }
} else { } else {
@@ -170,7 +171,7 @@ json.dumps(my_dict)`
const chain = new LLMChain({ const chain = new LLMChain({
llm: model, llm: model,
prompt: PromptTemplate.fromTemplate(systemPrompt), prompt: PromptTemplate.fromTemplate(systemPrompt),
verbose: process.env.DEBUG === 'true' verbose: process.env.DEBUG === 'true' ? true : false
}) })
const inputs = { const inputs = {
dict: dataframeColDict, dict: dataframeColDict,
@@ -201,7 +202,7 @@ json.dumps(my_dict)`
prompt: PromptTemplate.fromTemplate( prompt: PromptTemplate.fromTemplate(
systemMessagePrompt ? `${systemMessagePrompt}\n${finalSystemPrompt}` : finalSystemPrompt systemMessagePrompt ? `${systemMessagePrompt}\n${finalSystemPrompt}` : finalSystemPrompt
), ),
verbose: process.env.DEBUG === 'true' verbose: process.env.DEBUG === 'true' ? true : false
}) })
const inputs = { const inputs = {
question: input, question: input,
@@ -132,7 +132,7 @@ class ConversationalAgent_Agents implements INode {
} }
const executor = await prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) const executor = await prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
let res: ChainValues = {} let res: ChainValues = {}
@@ -130,7 +130,7 @@ class ConversationalRetrievalToolAgent_Agents implements INode {
const executor = await prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) const executor = await prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
let res: ChainValues = {} let res: ChainValues = {}
@@ -288,7 +288,7 @@ const prepareAgent = async (
sessionId: flowObj?.sessionId, sessionId: flowObj?.sessionId,
chatId: flowObj?.chatId, chatId: flowObj?.chatId,
input: flowObj?.input, input: flowObj?.input,
verbose: process.env.DEBUG === 'true', verbose: process.env.DEBUG === 'true' ? true : false,
maxIterations: maxIterations ? parseFloat(maxIterations) : undefined maxIterations: maxIterations ? parseFloat(maxIterations) : undefined
}) })
@@ -2,6 +2,7 @@ import { flatten } from 'lodash'
import { MessageContentTextDetail, ChatMessage, AnthropicAgent, Anthropic } from 'llamaindex' import { MessageContentTextDetail, ChatMessage, AnthropicAgent, Anthropic } from 'llamaindex'
import { getBaseClasses } from '../../../../src/utils' import { getBaseClasses } from '../../../../src/utils'
import { FlowiseMemory, ICommonObject, IMessage, INode, INodeData, INodeParams, IUsedTool } from '../../../../src/Interface' import { FlowiseMemory, ICommonObject, IMessage, INode, INodeData, INodeParams, IUsedTool } from '../../../../src/Interface'
import { EvaluationRunTracerLlama } from '../../../../evaluation/EvaluationRunTracerLlama'
class AnthropicAgent_LlamaIndex_Agents implements INode { class AnthropicAgent_LlamaIndex_Agents implements INode {
label: string label: string
@@ -96,13 +97,16 @@ class AnthropicAgent_LlamaIndex_Agents implements INode {
tools, tools,
llm: model, llm: model,
chatHistory: chatHistory, chatHistory: chatHistory,
verbose: process.env.DEBUG === 'true' verbose: process.env.DEBUG === 'true' ? true : false
}) })
// these are needed for evaluation runs
await EvaluationRunTracerLlama.injectEvaluationMetadata(nodeData, options, agent)
let text = '' let text = ''
const usedTools: IUsedTool[] = [] const usedTools: IUsedTool[] = []
const response = await agent.chat({ message: input, chatHistory, verbose: process.env.DEBUG === 'true' }) const response = await agent.chat({ message: input, chatHistory, verbose: process.env.DEBUG === 'true' ? true : false })
if (response.sources.length) { if (response.sources.length) {
for (const sourceTool of response.sources) { for (const sourceTool of response.sources) {
@@ -1,6 +1,7 @@
import { flatten } from 'lodash' import { flatten } from 'lodash'
import { ChatMessage, OpenAI, OpenAIAgent } from 'llamaindex' import { ChatMessage, OpenAI, OpenAIAgent } from 'llamaindex'
import { getBaseClasses } from '../../../../src/utils' import { getBaseClasses } from '../../../../src/utils'
import { EvaluationRunTracerLlama } from '../../../../evaluation/EvaluationRunTracerLlama'
import { import {
FlowiseMemory, FlowiseMemory,
ICommonObject, ICommonObject,
@@ -107,9 +108,12 @@ class OpenAIFunctionAgent_LlamaIndex_Agents implements INode {
tools, tools,
llm: model, llm: model,
chatHistory: chatHistory, chatHistory: chatHistory,
verbose: process.env.DEBUG === 'true' verbose: process.env.DEBUG === 'true' ? true : false
}) })
// these are needed for evaluation runs
await EvaluationRunTracerLlama.injectEvaluationMetadata(nodeData, options, agent)
let text = '' let text = ''
let isStreamingStarted = false let isStreamingStarted = false
const usedTools: IUsedTool[] = [] const usedTools: IUsedTool[] = []
@@ -119,10 +123,9 @@ class OpenAIFunctionAgent_LlamaIndex_Agents implements INode {
message: input, message: input,
chatHistory, chatHistory,
stream: true, stream: true,
verbose: process.env.DEBUG === 'true' verbose: process.env.DEBUG === 'true' ? true : false
}) })
for await (const chunk of stream) { for await (const chunk of stream) {
//console.log('chunk', chunk)
text += chunk.response.delta text += chunk.response.delta
if (!isStreamingStarted) { if (!isStreamingStarted) {
isStreamingStarted = true isStreamingStarted = true
@@ -147,7 +150,7 @@ class OpenAIFunctionAgent_LlamaIndex_Agents implements INode {
} }
} }
} else { } else {
const response = await agent.chat({ message: input, chatHistory, verbose: process.env.DEBUG === 'true' }) const response = await agent.chat({ message: input, chatHistory, verbose: process.env.DEBUG === 'true' ? true : false })
if (response.sources.length) { if (response.sources.length) {
for (const sourceTool of response.sources) { for (const sourceTool of response.sources) {
usedTools.push({ usedTools.push({
@@ -107,7 +107,11 @@ class OpenAIAssistant_Agents implements INode {
return returnData return returnData
} }
const assistants = await appDataSource.getRepository(databaseEntities['Assistant']).find() const searchOptions = options.searchOptions || {}
const assistants = await appDataSource.getRepository(databaseEntities['Assistant']).findBy({
...searchOptions,
type: 'OPENAI'
})
for (let i = 0; i < assistants.length; i += 1) { for (let i = 0; i < assistants.length; i += 1) {
const assistantDetails = JSON.parse(assistants[i].details) const assistantDetails = JSON.parse(assistants[i].details)
@@ -130,13 +134,14 @@ class OpenAIAssistant_Agents implements INode {
const selectedAssistantId = nodeData.inputs?.selectedAssistant as string const selectedAssistantId = nodeData.inputs?.selectedAssistant as string
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const orgId = options.orgId
const assistant = await appDataSource.getRepository(databaseEntities['Assistant']).findOneBy({ const assistant = await appDataSource.getRepository(databaseEntities['Assistant']).findOneBy({
id: selectedAssistantId id: selectedAssistantId
}) })
if (!assistant) { if (!assistant) {
options.logger.error(`Assistant ${selectedAssistantId} not found`) options.logger.error(`[${orgId}]: Assistant ${selectedAssistantId} not found`)
return return
} }
@@ -149,7 +154,7 @@ class OpenAIAssistant_Agents implements INode {
chatId chatId
}) })
if (!chatmsg) { if (!chatmsg) {
options.logger.error(`Chat Message with Chat Id: ${chatId} not found`) options.logger.error(`[${orgId}]: Chat Message with Chat Id: ${chatId} not found`)
return return
} }
sessionId = chatmsg.sessionId sessionId = chatmsg.sessionId
@@ -160,21 +165,21 @@ class OpenAIAssistant_Agents implements INode {
const credentialData = await getCredentialData(assistant.credential ?? '', options) const credentialData = await getCredentialData(assistant.credential ?? '', options)
const openAIApiKey = getCredentialParam('openAIApiKey', credentialData, nodeData) const openAIApiKey = getCredentialParam('openAIApiKey', credentialData, nodeData)
if (!openAIApiKey) { if (!openAIApiKey) {
options.logger.error(`OpenAI ApiKey not found`) options.logger.error(`[${orgId}]: OpenAI ApiKey not found`)
return return
} }
const openai = new OpenAI({ apiKey: openAIApiKey }) const openai = new OpenAI({ apiKey: openAIApiKey })
options.logger.info(`Clearing OpenAI Thread ${sessionId}`) options.logger.info(`[${orgId}]: Clearing OpenAI Thread ${sessionId}`)
try { try {
if (sessionId && sessionId.startsWith('thread_')) { if (sessionId && sessionId.startsWith('thread_')) {
await openai.beta.threads.del(sessionId) await openai.beta.threads.del(sessionId)
options.logger.info(`Successfully cleared OpenAI Thread ${sessionId}`) options.logger.info(`[${orgId}]: Successfully cleared OpenAI Thread ${sessionId}`)
} else { } else {
options.logger.error(`Error clearing OpenAI Thread ${sessionId}`) options.logger.error(`[${orgId}]: Error clearing OpenAI Thread ${sessionId}`)
} }
} catch (e) { } catch (e) {
options.logger.error(`Error clearing OpenAI Thread ${sessionId}`) options.logger.error(`[${orgId}]: Error clearing OpenAI Thread ${sessionId}`)
} }
} }
@@ -190,6 +195,17 @@ class OpenAIAssistant_Agents implements INode {
const shouldStreamResponse = options.shouldStreamResponse const shouldStreamResponse = options.shouldStreamResponse
const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer
const chatId = options.chatId const chatId = options.chatId
const checkStorage = options.checkStorage
? (options.checkStorage as (orgId: string, subscriptionId: string, usageCacheManager: any) => Promise<void>)
: undefined
const updateStorageUsage = options.updateStorageUsage
? (options.updateStorageUsage as (
orgId: string,
workspaceId: string,
totalSize: number,
usageCacheManager: any
) => Promise<void>)
: undefined
if (moderations && moderations.length > 0) { if (moderations && moderations.length > 0) {
try { try {
@@ -380,17 +396,30 @@ class OpenAIAssistant_Agents implements INode {
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
const fileName = cited_file.filename.split(/[\/\\]/).pop() ?? cited_file.filename const fileName = cited_file.filename.split(/[\/\\]/).pop() ?? cited_file.filename
if (!disableFileDownload) { if (!disableFileDownload) {
filePath = await downloadFile( if (checkStorage)
await checkStorage(options.orgId, options.subscriptionId, options.usageCacheManager)
const { path, totalSize } = await downloadFile(
openAIApiKey, openAIApiKey,
cited_file, cited_file,
fileName, fileName,
options.orgId,
options.chatflowid, options.chatflowid,
options.chatId options.chatId
) )
filePath = path
fileAnnotations.push({ fileAnnotations.push({
filePath, filePath,
fileName fileName
}) })
if (updateStorageUsage)
await updateStorageUsage(
options.orgId,
options.workspaceId,
totalSize,
options.usageCacheManager
)
} }
} else { } else {
const file_path = (annotation as OpenAI.Beta.Threads.Messages.FilePathAnnotation).file_path const file_path = (annotation as OpenAI.Beta.Threads.Messages.FilePathAnnotation).file_path
@@ -399,17 +428,30 @@ class OpenAIAssistant_Agents implements INode {
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
const fileName = cited_file.filename.split(/[\/\\]/).pop() ?? cited_file.filename const fileName = cited_file.filename.split(/[\/\\]/).pop() ?? cited_file.filename
if (!disableFileDownload) { if (!disableFileDownload) {
filePath = await downloadFile( if (checkStorage)
await checkStorage(options.orgId, options.subscriptionId, options.usageCacheManager)
const { path, totalSize } = await downloadFile(
openAIApiKey, openAIApiKey,
cited_file, cited_file,
fileName, fileName,
options.orgId,
options.chatflowid, options.chatflowid,
options.chatId options.chatId
) )
filePath = path
fileAnnotations.push({ fileAnnotations.push({
filePath, filePath,
fileName fileName
}) })
if (updateStorageUsage)
await updateStorageUsage(
options.orgId,
options.workspaceId,
totalSize,
options.usageCacheManager
)
} }
} }
} }
@@ -467,15 +509,21 @@ class OpenAIAssistant_Agents implements INode {
const fileId = chunk.image_file.file_id const fileId = chunk.image_file.file_id
const fileObj = await openai.files.retrieve(fileId) const fileObj = await openai.files.retrieve(fileId)
const filePath = await downloadImg( if (checkStorage) await checkStorage(options.orgId, options.subscriptionId, options.usageCacheManager)
const { filePath, totalSize } = await downloadImg(
openai, openai,
fileId, fileId,
`${fileObj.filename}.png`, `${fileObj.filename}.png`,
options.orgId,
options.chatflowid, options.chatflowid,
options.chatId options.chatId
) )
artifacts.push({ type: 'png', data: filePath }) artifacts.push({ type: 'png', data: filePath })
if (updateStorageUsage)
await updateStorageUsage(options.orgId, options.workspaceId, totalSize, options.usageCacheManager)
if (!isStreamingStarted) { if (!isStreamingStarted) {
isStreamingStarted = true isStreamingStarted = true
if (sseStreamer) { if (sseStreamer) {
@@ -776,7 +824,21 @@ class OpenAIAssistant_Agents implements INode {
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
const fileName = cited_file.filename.split(/[\/\\]/).pop() ?? cited_file.filename const fileName = cited_file.filename.split(/[\/\\]/).pop() ?? cited_file.filename
if (!disableFileDownload) { if (!disableFileDownload) {
filePath = await downloadFile(openAIApiKey, cited_file, fileName, options.chatflowid, options.chatId) if (checkStorage) await checkStorage(options.orgId, options.subscriptionId, options.usageCacheManager)
const { path, totalSize } = await downloadFile(
openAIApiKey,
cited_file,
fileName,
options.orgId,
options.chatflowid,
options.chatId
)
filePath = path
if (updateStorageUsage)
await updateStorageUsage(options.orgId, options.workspaceId, totalSize, options.usageCacheManager)
fileAnnotations.push({ fileAnnotations.push({
filePath, filePath,
fileName fileName
@@ -789,13 +851,27 @@ class OpenAIAssistant_Agents implements INode {
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
const fileName = cited_file.filename.split(/[\/\\]/).pop() ?? cited_file.filename const fileName = cited_file.filename.split(/[\/\\]/).pop() ?? cited_file.filename
if (!disableFileDownload) { if (!disableFileDownload) {
filePath = await downloadFile( if (checkStorage)
await checkStorage(options.orgId, options.subscriptionId, options.usageCacheManager)
const { path, totalSize } = await downloadFile(
openAIApiKey, openAIApiKey,
cited_file, cited_file,
fileName, fileName,
options.orgId,
options.chatflowid, options.chatflowid,
options.chatId options.chatId
) )
filePath = path
if (updateStorageUsage)
await updateStorageUsage(
options.orgId,
options.workspaceId,
totalSize,
options.usageCacheManager
)
fileAnnotations.push({ fileAnnotations.push({
filePath, filePath,
fileName fileName
@@ -822,7 +898,20 @@ class OpenAIAssistant_Agents implements INode {
const fileId = content.image_file.file_id const fileId = content.image_file.file_id
const fileObj = await openai.files.retrieve(fileId) const fileObj = await openai.files.retrieve(fileId)
const filePath = await downloadImg(openai, fileId, `${fileObj.filename}.png`, options.chatflowid, options.chatId) if (checkStorage) await checkStorage(options.orgId, options.subscriptionId, options.usageCacheManager)
const { filePath, totalSize } = await downloadImg(
openai,
fileId,
`${fileObj.filename}.png`,
options.orgId,
options.chatflowid,
options.chatId
)
if (updateStorageUsage)
await updateStorageUsage(options.orgId, options.workspaceId, totalSize, options.usageCacheManager)
artifacts.push({ type: 'png', data: filePath }) artifacts.push({ type: 'png', data: filePath })
} }
} }
@@ -847,7 +936,13 @@ class OpenAIAssistant_Agents implements INode {
} }
} }
const downloadImg = async (openai: OpenAI, fileId: string, fileName: string, ...paths: string[]) => { const downloadImg = async (
openai: OpenAI,
fileId: string,
fileName: string,
orgId: string,
...paths: string[]
): Promise<{ filePath: string; totalSize: number }> => {
const response = await openai.files.content(fileId) const response = await openai.files.content(fileId)
// Extract the binary data from the Response object // Extract the binary data from the Response object
@@ -857,12 +952,18 @@ const downloadImg = async (openai: OpenAI, fileId: string, fileName: string, ...
const image_data_buffer = Buffer.from(image_data) const image_data_buffer = Buffer.from(image_data)
const mime = 'image/png' const mime = 'image/png'
const res = await addSingleFileToStorage(mime, image_data_buffer, fileName, ...paths) const { path, totalSize } = await addSingleFileToStorage(mime, image_data_buffer, fileName, orgId, ...paths)
return res return { filePath: path, totalSize }
} }
const downloadFile = async (openAIApiKey: string, fileObj: any, fileName: string, ...paths: string[]) => { const downloadFile = async (
openAIApiKey: string,
fileObj: any,
fileName: string,
orgId: string,
...paths: string[]
): Promise<{ path: string; totalSize: number }> => {
try { try {
const response = await fetch(`https://api.openai.com/v1/files/${fileObj.id}/content`, { const response = await fetch(`https://api.openai.com/v1/files/${fileObj.id}/content`, {
method: 'GET', method: 'GET',
@@ -880,10 +981,12 @@ const downloadFile = async (openAIApiKey: string, fileObj: any, fileName: string
const data_buffer = Buffer.from(data) const data_buffer = Buffer.from(data)
const mime = 'application/octet-stream' const mime = 'application/octet-stream'
return await addSingleFileToStorage(mime, data_buffer, fileName, ...paths) const { path, totalSize } = await addSingleFileToStorage(mime, data_buffer, fileName, orgId, ...paths)
return { path, totalSize }
} catch (error) { } catch (error) {
console.error('Error downloading or writing the file:', error) console.error('Error downloading or writing the file:', error)
return '' return { path: '', totalSize: 0 }
} }
} }
@@ -97,7 +97,7 @@ class ReActAgentLLM_Agents implements INode {
const executor = new AgentExecutor({ const executor = new AgentExecutor({
agent, agent,
tools, tools,
verbose: process.env.DEBUG === 'true', verbose: process.env.DEBUG === 'true' ? true : false,
maxIterations: maxIterations ? parseFloat(maxIterations) : undefined maxIterations: maxIterations ? parseFloat(maxIterations) : undefined
}) })
@@ -143,7 +143,7 @@ class ToolAgent_Agents implements INode {
const executor = await prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) const executor = await prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
// Add custom streaming handler if detailed streaming is enabled // Add custom streaming handler if detailed streaming is enabled
@@ -370,7 +370,7 @@ const prepareAgent = async (
sessionId: flowObj?.sessionId, sessionId: flowObj?.sessionId,
chatId: flowObj?.chatId, chatId: flowObj?.chatId,
input: flowObj?.input, input: flowObj?.input,
verbose: process.env.DEBUG === 'true', verbose: process.env.DEBUG === 'true' ? true : false,
maxIterations: maxIterations ? parseFloat(maxIterations) : undefined maxIterations: maxIterations ? parseFloat(maxIterations) : undefined
}) })
@@ -138,7 +138,7 @@ class XMLAgent_Agents implements INode {
} }
const executor = await prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) const executor = await prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
let res: ChainValues = {} let res: ChainValues = {}
@@ -278,7 +278,7 @@ const prepareAgent = async (
chatId: flowObj?.chatId, chatId: flowObj?.chatId,
input: flowObj?.input, input: flowObj?.input,
isXML: true, isXML: true,
verbose: process.env.DEBUG === 'true', verbose: process.env.DEBUG === 'true' ? true : false,
maxIterations: maxIterations ? parseFloat(maxIterations) : undefined maxIterations: maxIterations ? parseFloat(maxIterations) : undefined
}) })
@@ -98,7 +98,7 @@ class GETApiChain_Chains implements INode {
const ansPrompt = nodeData.inputs?.ansPrompt as string const ansPrompt = nodeData.inputs?.ansPrompt as string
const chain = await getAPIChain(apiDocs, model, headers, urlPrompt, ansPrompt) const chain = await getAPIChain(apiDocs, model, headers, urlPrompt, ansPrompt)
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
const shouldStreamResponse = options.shouldStreamResponse const shouldStreamResponse = options.shouldStreamResponse
const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer
@@ -129,7 +129,7 @@ const getAPIChain = async (documents: string, llm: BaseLanguageModel, headers: s
const chain = APIChain.fromLLMAndAPIDocs(llm, documents, { const chain = APIChain.fromLLMAndAPIDocs(llm, documents, {
apiUrlPrompt, apiUrlPrompt,
apiResponsePrompt, apiResponsePrompt,
verbose: process.env.DEBUG === 'true', verbose: process.env.DEBUG === 'true' ? true : false,
headers: typeof headers === 'object' ? headers : headers ? JSON.parse(headers) : {} headers: typeof headers === 'object' ? headers : headers ? JSON.parse(headers) : {}
}) })
return chain return chain
@@ -71,7 +71,7 @@ class OpenApiChain_Chains implements INode {
async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string | object> { async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string | object> {
const chain = await initChain(nodeData, options) const chain = await initChain(nodeData, options)
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
const moderations = nodeData.inputs?.inputModeration as Moderation[] const moderations = nodeData.inputs?.inputModeration as Moderation[]
const shouldStreamResponse = options.shouldStreamResponse const shouldStreamResponse = options.shouldStreamResponse
@@ -114,8 +114,9 @@ const initChain = async (nodeData: INodeData, options: ICommonObject) => {
} else { } else {
if (yamlFileBase64.startsWith('FILE-STORAGE::')) { if (yamlFileBase64.startsWith('FILE-STORAGE::')) {
const file = yamlFileBase64.replace('FILE-STORAGE::', '') const file = yamlFileBase64.replace('FILE-STORAGE::', '')
const orgId = options.orgId
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
const fileData = await getFileFromStorage(file, chatflowid) const fileData = await getFileFromStorage(file, orgId, chatflowid)
yamlString = fileData.toString() yamlString = fileData.toString()
} else { } else {
const splitDataURI = yamlFileBase64.split(',') const splitDataURI = yamlFileBase64.split(',')
@@ -128,7 +129,7 @@ const initChain = async (nodeData: INodeData, options: ICommonObject) => {
return await createOpenAPIChain(yamlString, { return await createOpenAPIChain(yamlString, {
llm: model, llm: model,
headers: typeof headers === 'object' ? headers : headers ? JSON.parse(headers) : {}, headers: typeof headers === 'object' ? headers : headers ? JSON.parse(headers) : {},
verbose: process.env.DEBUG === 'true' verbose: process.env.DEBUG === 'true' ? true : false
}) })
} }
@@ -87,7 +87,7 @@ class POSTApiChain_Chains implements INode {
const ansPrompt = nodeData.inputs?.ansPrompt as string const ansPrompt = nodeData.inputs?.ansPrompt as string
const chain = await getAPIChain(apiDocs, model, headers, urlPrompt, ansPrompt) const chain = await getAPIChain(apiDocs, model, headers, urlPrompt, ansPrompt)
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
const shouldStreamResponse = options.shouldStreamResponse const shouldStreamResponse = options.shouldStreamResponse
@@ -119,7 +119,7 @@ const getAPIChain = async (documents: string, llm: BaseLanguageModel, headers: s
const chain = APIChain.fromLLMAndAPIDocs(llm, documents, { const chain = APIChain.fromLLMAndAPIDocs(llm, documents, {
apiUrlPrompt, apiUrlPrompt,
apiResponsePrompt, apiResponsePrompt,
verbose: process.env.DEBUG === 'true', verbose: process.env.DEBUG === 'true' ? true : false,
headers: typeof headers === 'object' ? headers : headers ? JSON.parse(headers) : {} headers: typeof headers === 'object' ? headers : headers ? JSON.parse(headers) : {}
}) })
return chain return chain
@@ -132,7 +132,7 @@ class ConversationChain_Chains implements INode {
} }
} }
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const additionalCallback = await additionalCallbacks(nodeData, options) const additionalCallback = await additionalCallbacks(nodeData, options)
let res = '' let res = ''
@@ -185,6 +185,7 @@ class ConversationalRetrievalQAChain_Chains implements INode {
const shouldStreamResponse = options.shouldStreamResponse const shouldStreamResponse = options.shouldStreamResponse
const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer
const chatId = options.chatId const chatId = options.chatId
const orgId = options.orgId
let customResponsePrompt = responsePrompt let customResponsePrompt = responsePrompt
// If the deprecated systemMessagePrompt is still exists // If the deprecated systemMessagePrompt is still exists
@@ -200,7 +201,8 @@ class ConversationalRetrievalQAChain_Chains implements INode {
memoryKey: 'chat_history', memoryKey: 'chat_history',
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
}) })
} }
@@ -220,7 +222,7 @@ class ConversationalRetrievalQAChain_Chains implements INode {
const history = ((await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[]) ?? [] const history = ((await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[]) ?? []
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const additionalCallback = await additionalCallbacks(nodeData, options) const additionalCallback = await additionalCallbacks(nodeData, options)
let callbacks = [loggerHandler, ...additionalCallback] let callbacks = [loggerHandler, ...additionalCallback]
@@ -407,18 +409,21 @@ interface BufferMemoryExtendedInput {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
} }
class BufferMemory extends FlowiseMemory implements MemoryMethods { class BufferMemory extends FlowiseMemory implements MemoryMethods {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
constructor(fields: BufferMemoryInput & BufferMemoryExtendedInput) { constructor(fields: BufferMemoryInput & BufferMemoryExtendedInput) {
super(fields) super(fields)
this.appDataSource = fields.appDataSource this.appDataSource = fields.appDataSource
this.databaseEntities = fields.databaseEntities this.databaseEntities = fields.databaseEntities
this.chatflowid = fields.chatflowid this.chatflowid = fields.chatflowid
this.orgId = fields.orgId
} }
async getChatMessages( async getChatMessages(
@@ -443,7 +448,7 @@ class BufferMemory extends FlowiseMemory implements MemoryMethods {
} }
if (returnBaseMessages) { if (returnBaseMessages) {
return await mapChatMessageToBaseMessage(chatMessage) return await mapChatMessageToBaseMessage(chatMessage, this.orgId)
} }
let returnIMessages: IMessage[] = [] let returnIMessages: IMessage[] = []
@@ -215,7 +215,7 @@ class GraphCypherQA_Chain implements INode {
query: input query: input
} }
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbackHandlers = await additionalCallbacks(nodeData, options) const callbackHandlers = await additionalCallbacks(nodeData, options)
let callbacks = [loggerHandler, ...callbackHandlers] let callbacks = [loggerHandler, ...callbackHandlers]
@@ -167,7 +167,7 @@ const runPrediction = async (
nodeData: INodeData, nodeData: INodeData,
disableStreaming?: boolean disableStreaming?: boolean
) => { ) => {
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
const moderations = nodeData.inputs?.inputModeration as Moderation[] const moderations = nodeData.inputs?.inputModeration as Moderation[]
@@ -66,7 +66,7 @@ class MultiPromptChain_Chains implements INode {
promptNames, promptNames,
promptDescriptions, promptDescriptions,
promptTemplates, promptTemplates,
llmChainOpts: { verbose: process.env.DEBUG === 'true' } llmChainOpts: { verbose: process.env.DEBUG === 'true' ? true : false }
}) })
return chain return chain
@@ -95,7 +95,7 @@ class MultiPromptChain_Chains implements INode {
} }
const obj = { input } const obj = { input }
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
if (shouldStreamResponse) { if (shouldStreamResponse) {
@@ -74,7 +74,7 @@ class MultiRetrievalQAChain_Chains implements INode {
retrieverNames, retrieverNames,
retrieverDescriptions, retrieverDescriptions,
retrievers, retrievers,
retrievalQAChainOpts: { verbose: process.env.DEBUG === 'true', returnSourceDocuments } retrievalQAChainOpts: { verbose: process.env.DEBUG === 'true' ? true : false, returnSourceDocuments }
}) })
return chain return chain
} }
@@ -101,7 +101,7 @@ class MultiRetrievalQAChain_Chains implements INode {
} }
} }
const obj = { input } const obj = { input }
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
if (shouldStreamResponse) { if (shouldStreamResponse) {
@@ -53,7 +53,7 @@ class RetrievalQAChain_Chains implements INode {
const model = nodeData.inputs?.model as BaseLanguageModel const model = nodeData.inputs?.model as BaseLanguageModel
const vectorStoreRetriever = nodeData.inputs?.vectorStoreRetriever as BaseRetriever const vectorStoreRetriever = nodeData.inputs?.vectorStoreRetriever as BaseRetriever
const chain = RetrievalQAChain.fromLLM(model, vectorStoreRetriever, { verbose: process.env.DEBUG === 'true' }) const chain = RetrievalQAChain.fromLLM(model, vectorStoreRetriever, { verbose: process.env.DEBUG === 'true' ? true : false })
return chain return chain
} }
@@ -80,7 +80,7 @@ class RetrievalQAChain_Chains implements INode {
const obj = { const obj = {
query: input query: input
} }
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
if (shouldStreamResponse) { if (shouldStreamResponse) {
@@ -194,7 +194,7 @@ class SqlDatabaseChain_Chains implements INode {
topK, topK,
customPrompt customPrompt
) )
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
if (shouldStreamResponse) { if (shouldStreamResponse) {
@@ -241,7 +241,7 @@ const getSQLDBChain = async (
const obj: SqlDatabaseChainInput = { const obj: SqlDatabaseChainInput = {
llm, llm,
database: db, database: db,
verbose: process.env.DEBUG === 'true', verbose: process.env.DEBUG === 'true' ? true : false,
topK: topK topK: topK
} }
@@ -55,7 +55,7 @@ class VectorDBQAChain_Chains implements INode {
const chain = VectorDBQAChain.fromLLM(model, vectorStore, { const chain = VectorDBQAChain.fromLLM(model, vectorStore, {
k: (vectorStore as any)?.k ?? 4, k: (vectorStore as any)?.k ?? 4,
verbose: process.env.DEBUG === 'true' verbose: process.env.DEBUG === 'true' ? true : false
}) })
return chain return chain
} }
@@ -84,7 +84,7 @@ class VectorDBQAChain_Chains implements INode {
query: input query: input
} }
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
if (shouldStreamResponse) { if (shouldStreamResponse) {
@@ -5,7 +5,7 @@ Azure OpenAI Chat Model integration for Flowise
## 🌱 Env Variables ## 🌱 Env Variables
| Variable | Description | Type | Default | | Variable | Description | Type | Default |
| ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- | | -------------------------------- | ------------------------------------------------------------------------ | ------ | ------- |
| AZURE_OPENAI_API_KEY | Default `credential.azureOpenAIApiKey` for Azure OpenAI Model | String | | | AZURE_OPENAI_API_KEY | Default `credential.azureOpenAIApiKey` for Azure OpenAI Model | String | |
| AZURE_OPENAI_API_INSTANCE_NAME | Default `credential.azureOpenAIApiInstanceName` for Azure OpenAI Model | String | | | AZURE_OPENAI_API_INSTANCE_NAME | Default `credential.azureOpenAIApiInstanceName` for Azure OpenAI Model | String | |
| AZURE_OPENAI_API_DEPLOYMENT_NAME | Default `credential.azureOpenAIApiDeploymentName` for Azure OpenAI Model | String | | | AZURE_OPENAI_API_DEPLOYMENT_NAME | Default `credential.azureOpenAIApiDeploymentName` for Azure OpenAI Model | String | |
@@ -216,6 +216,10 @@ class GoogleGenerativeAI_ChatModels implements INode {
streaming: streaming ?? true streaming: streaming ?? true
} }
// this extra metadata is needed, as langchain does not show the model name in the callbacks.
obj.metadata = {
fw_model_name: customModelName || modelName
}
if (maxOutputTokens) obj.maxOutputTokens = parseInt(maxOutputTokens, 10) if (maxOutputTokens) obj.maxOutputTokens = parseInt(maxOutputTokens, 10)
if (topP) obj.topP = parseFloat(topP) if (topP) obj.topP = parseFloat(topP)
if (topK) obj.topK = parseFloat(topK) if (topK) obj.topK = parseFloat(topK)
@@ -161,12 +161,13 @@ class ChatIBMWatsonx_ChatModels implements INode {
watsonxAIBearerToken watsonxAIBearerToken
} }
const obj: ChatWatsonxInput & WatsonxAuth = { const obj = {
...auth, ...auth,
streaming: streaming ?? true, streaming: streaming ?? true,
model: modelName, model: modelName,
temperature: temperature ? parseFloat(temperature) : undefined temperature: temperature ? parseFloat(temperature) : undefined
} } as ChatWatsonxInput & WatsonxAuth
if (cache) obj.cache = cache if (cache) obj.cache = cache
if (maxTokens) obj.maxTokens = parseInt(maxTokens, 10) if (maxTokens) obj.maxTokens = parseInt(maxTokens, 10)
if (frequencyPenalty) obj.frequencyPenalty = parseInt(frequencyPenalty, 10) if (frequencyPenalty) obj.frequencyPenalty = parseInt(frequencyPenalty, 10)
@@ -123,6 +123,7 @@ class Cheerio_DocumentLoaders implements INode {
const selectedLinks = nodeData.inputs?.selectedLinks as string[] const selectedLinks = nodeData.inputs?.selectedLinks as string[]
let limit = parseInt(nodeData.inputs?.limit as string) let limit = parseInt(nodeData.inputs?.limit as string)
const output = nodeData.outputs?.output as string const output = nodeData.outputs?.output as string
const orgId = options.orgId
const _omitMetadataKeys = nodeData.inputs?.omitMetadataKeys as string const _omitMetadataKeys = nodeData.inputs?.omitMetadataKeys as string
@@ -149,7 +150,8 @@ class Cheerio_DocumentLoaders implements INode {
try { try {
let docs: IDocument[] = [] let docs: IDocument[] = []
if (url.endsWith('.pdf')) { if (url.endsWith('.pdf')) {
if (process.env.DEBUG === 'true') options.logger.info(`CheerioWebBaseLoader does not support PDF files: ${url}`) if (process.env.DEBUG === 'true')
options.logger.info(`[${orgId}]: CheerioWebBaseLoader does not support PDF files: ${url}`)
return docs return docs
} }
const loader = new CheerioWebBaseLoader(url, params) const loader = new CheerioWebBaseLoader(url, params)
@@ -161,7 +163,8 @@ class Cheerio_DocumentLoaders implements INode {
} }
return docs return docs
} catch (err) { } catch (err) {
if (process.env.DEBUG === 'true') options.logger.error(`error in CheerioWebBaseLoader: ${err.message}, on page: ${url}`) if (process.env.DEBUG === 'true')
options.logger.error(`[${orgId}]: Error in CheerioWebBaseLoader: ${err.message}, on page: ${url}`)
return [] return []
} }
} }
@@ -169,7 +172,7 @@ class Cheerio_DocumentLoaders implements INode {
let docs: IDocument[] = [] let docs: IDocument[] = []
if (relativeLinksMethod) { if (relativeLinksMethod) {
if (process.env.DEBUG === 'true') options.logger.info(`Start ${relativeLinksMethod}`) if (process.env.DEBUG === 'true') options.logger.info(`[${orgId}]: Start CheerioWebBaseLoader ${relativeLinksMethod}`)
// if limit is 0 we don't want it to default to 10 so we check explicitly for null or undefined // if limit is 0 we don't want it to default to 10 so we check explicitly for null or undefined
// so when limit is 0 we can fetch all the links // so when limit is 0 we can fetch all the links
if (limit === null || limit === undefined) limit = 10 if (limit === null || limit === undefined) limit = 10
@@ -180,15 +183,18 @@ class Cheerio_DocumentLoaders implements INode {
: relativeLinksMethod === 'webCrawl' : relativeLinksMethod === 'webCrawl'
? await webCrawl(url, limit) ? await webCrawl(url, limit)
: await xmlScrape(url, limit) : await xmlScrape(url, limit)
if (process.env.DEBUG === 'true') options.logger.info(`pages: ${JSON.stringify(pages)}, length: ${pages.length}`) if (process.env.DEBUG === 'true')
options.logger.info(`[${orgId}]: CheerioWebBaseLoader pages: ${JSON.stringify(pages)}, length: ${pages.length}`)
if (!pages || pages.length === 0) throw new Error('No relative links found') if (!pages || pages.length === 0) throw new Error('No relative links found')
for (const page of pages) { for (const page of pages) {
docs.push(...(await cheerioLoader(page))) docs.push(...(await cheerioLoader(page)))
} }
if (process.env.DEBUG === 'true') options.logger.info(`Finish ${relativeLinksMethod}`) if (process.env.DEBUG === 'true') options.logger.info(`[${orgId}]: Finish CheerioWebBaseLoader ${relativeLinksMethod}`)
} else if (selectedLinks && selectedLinks.length > 0) { } else if (selectedLinks && selectedLinks.length > 0) {
if (process.env.DEBUG === 'true') if (process.env.DEBUG === 'true')
options.logger.info(`pages: ${JSON.stringify(selectedLinks)}, length: ${selectedLinks.length}`) options.logger.info(
`[${orgId}]: CheerioWebBaseLoader pages: ${JSON.stringify(selectedLinks)}, length: ${selectedLinks.length}`
)
for (const page of selectedLinks.slice(0, limit)) { for (const page of selectedLinks.slice(0, limit)) {
docs.push(...(await cheerioLoader(page))) docs.push(...(await cheerioLoader(page)))
} }
@@ -107,9 +107,9 @@ class Csv_DocumentLoaders implements INode {
return { files, fromStorage } return { files, fromStorage }
} }
async getFileData(file: string, { chatflowid }: { chatflowid: string }, fromStorage?: boolean) { async getFileData(file: string, { orgId, chatflowid }: { orgId: string; chatflowid: string }, fromStorage?: boolean) {
if (fromStorage) { if (fromStorage) {
return getFileFromStorage(file, chatflowid) return getFileFromStorage(file, orgId, chatflowid)
} else { } else {
const splitDataURI = file.split(',') const splitDataURI = file.split(',')
splitDataURI.pop() splitDataURI.pop()
@@ -126,6 +126,7 @@ class Csv_DocumentLoaders implements INode {
let docs: IDocument[] = [] let docs: IDocument[] = []
const orgId = options.orgId
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
const { files, fromStorage } = this.getFiles(nodeData) const { files, fromStorage } = this.getFiles(nodeData)
@@ -133,7 +134,7 @@ class Csv_DocumentLoaders implements INode {
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await this.getFileData(file, { chatflowid }, fromStorage) const fileData = await this.getFileData(file, { orgId, chatflowid }, fromStorage)
const blob = new Blob([fileData]) const blob = new Blob([fileData])
const loader = new CSVLoader(blob, columnName.trim().length === 0 ? undefined : columnName.trim()) const loader = new CSVLoader(blob, columnName.trim().length === 0 ? undefined : columnName.trim())
@@ -72,7 +72,7 @@ class CustomDocumentLoader_DocumentLoaders implements INode {
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const variables = await getVars(appDataSource, databaseEntities, nodeData) const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
const flow = { const flow = {
chatflowId: options.chatflowid, chatflowId: options.chatflowid,
sessionId: options.sessionId, sessionId: options.sessionId,
@@ -60,7 +60,8 @@ class DocStore_DocumentLoaders implements INode {
return returnData return returnData
} }
const stores = await appDataSource.getRepository(databaseEntities['DocumentStore']).find() const searchOptions = options.searchOptions || {}
const stores = await appDataSource.getRepository(databaseEntities['DocumentStore']).findBy(searchOptions)
for (const store of stores) { for (const store of stores) {
if (store.status === 'SYNC') { if (store.status === 'SYNC') {
const obj = { const obj = {
@@ -96,11 +96,12 @@ class Docx_DocumentLoaders implements INode {
} else { } else {
files = [fileName] files = [fileName]
} }
const orgId = options.orgId
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await getFileFromStorage(file, chatflowid) const fileData = await getFileFromStorage(file, orgId, chatflowid)
const blob = new Blob([fileData]) const blob = new Blob([fileData])
const loader = new DocxLoader(blob) const loader = new DocxLoader(blob)
@@ -118,10 +118,11 @@ class Epub_DocumentLoaders implements INode {
files = fileName.startsWith('[') && fileName.endsWith(']') ? JSON.parse(fileName) : [fileName] files = fileName.startsWith('[') && fileName.endsWith(']') ? JSON.parse(fileName) : [fileName]
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
const orgId = options.orgId
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await getFileFromStorage(file, chatflowid) const fileData = await getFileFromStorage(file, orgId, chatflowid)
const tempFilePath = path.join(tempDir, `${Date.now()}_${file}`) const tempFilePath = path.join(tempDir, `${Date.now()}_${file}`)
fs.writeFileSync(tempFilePath, fileData) fs.writeFileSync(tempFilePath, fileData)
await this.extractDocs(usage, tempFilePath, textSplitter, docs) await this.extractDocs(usage, tempFilePath, textSplitter, docs)
@@ -144,6 +144,7 @@ class File_DocumentLoaders implements INode {
} else { } else {
files = [fileName] files = [fileName]
} }
const orgId = options.orgId
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
// specific to createAttachment to get files from chatId // specific to createAttachment to get files from chatId
@@ -151,14 +152,14 @@ class File_DocumentLoaders implements INode {
if (retrieveAttachmentChatId) { if (retrieveAttachmentChatId) {
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await getFileFromStorage(file, chatflowid, options.chatId) const fileData = await getFileFromStorage(file, orgId, chatflowid, options.chatId)
const blob = new Blob([fileData]) const blob = new Blob([fileData])
fileBlobs.push({ blob, ext: file.split('.').pop() || '' }) fileBlobs.push({ blob, ext: file.split('.').pop() || '' })
} }
} else { } else {
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await getFileFromStorage(file, chatflowid) const fileData = await getFileFromStorage(file, orgId, chatflowid)
const blob = new Blob([fileData]) const blob = new Blob([fileData])
fileBlobs.push({ blob, ext: file.split('.').pop() || '' }) fileBlobs.push({ blob, ext: file.split('.').pop() || '' })
} }
@@ -146,11 +146,12 @@ class Json_DocumentLoaders implements INode {
} else { } else {
files = [fileName] files = [fileName]
} }
const orgId = options.orgId
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await getFileFromStorage(file, chatflowid) const fileData = await getFileFromStorage(file, orgId, chatflowid)
const blob = new Blob([fileData]) const blob = new Blob([fileData])
const loader = new JSONLoader(blob, pointers.length != 0 ? pointers : undefined, metadata) const loader = new JSONLoader(blob, pointers.length != 0 ? pointers : undefined, metadata)
@@ -135,11 +135,12 @@ class Jsonlines_DocumentLoaders implements INode {
} else { } else {
files = [fileName] files = [fileName]
} }
const orgId = options.orgId
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await getFileFromStorage(file, chatflowid) const fileData = await getFileFromStorage(file, orgId, chatflowid)
const blob = new Blob([fileData]) const blob = new Blob([fileData])
const loader = new JSONLinesLoader(blob, pointer, metadata) const loader = new JSONLinesLoader(blob, pointer, metadata)
@@ -122,11 +122,12 @@ class Pdf_DocumentLoaders implements INode {
} else { } else {
files = [fileName] files = [fileName]
} }
const orgId = options.orgId
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await getFileFromStorage(file, chatflowid) const fileData = await getFileFromStorage(file, orgId, chatflowid)
const bf = Buffer.from(fileData) const bf = Buffer.from(fileData)
await this.extractDocs(usage, bf, legacyBuild, textSplitter, docs) await this.extractDocs(usage, bf, legacyBuild, textSplitter, docs)
} }
@@ -159,6 +159,7 @@ class Playwright_DocumentLoaders implements INode {
let waitForSelector = nodeData.inputs?.waitForSelector as string let waitForSelector = nodeData.inputs?.waitForSelector as string
const _omitMetadataKeys = nodeData.inputs?.omitMetadataKeys as string const _omitMetadataKeys = nodeData.inputs?.omitMetadataKeys as string
const output = nodeData.outputs?.output as string const output = nodeData.outputs?.output as string
const orgId = options.orgId
let omitMetadataKeys: string[] = [] let omitMetadataKeys: string[] = []
if (_omitMetadataKeys) { if (_omitMetadataKeys) {
@@ -202,13 +203,14 @@ class Playwright_DocumentLoaders implements INode {
} }
return docs return docs
} catch (err) { } catch (err) {
if (process.env.DEBUG === 'true') options.logger.error(`error in PlaywrightWebBaseLoader: ${err.message}, on page: ${url}`) if (process.env.DEBUG === 'true')
options.logger.error(`[${orgId}]: Error in PlaywrightWebBaseLoader: ${err.message}, on page: ${url}`)
} }
} }
let docs: IDocument[] = [] let docs: IDocument[] = []
if (relativeLinksMethod) { if (relativeLinksMethod) {
if (process.env.DEBUG === 'true') options.logger.info(`Start ${relativeLinksMethod}`) if (process.env.DEBUG === 'true') options.logger.info(`[${orgId}]: Start PlaywrightWebBaseLoader ${relativeLinksMethod}`)
// if limit is 0 we don't want it to default to 10 so we check explicitly for null or undefined // if limit is 0 we don't want it to default to 10 so we check explicitly for null or undefined
// so when limit is 0 we can fetch all the links // so when limit is 0 we can fetch all the links
if (limit === null || limit === undefined) limit = 10 if (limit === null || limit === undefined) limit = 10
@@ -219,15 +221,18 @@ class Playwright_DocumentLoaders implements INode {
: relativeLinksMethod === 'webCrawl' : relativeLinksMethod === 'webCrawl'
? await webCrawl(url, limit) ? await webCrawl(url, limit)
: await xmlScrape(url, limit) : await xmlScrape(url, limit)
if (process.env.DEBUG === 'true') options.logger.info(`pages: ${JSON.stringify(pages)}, length: ${pages.length}`) if (process.env.DEBUG === 'true')
options.logger.info(`[${orgId}]: PlaywrightWebBaseLoader pages: ${JSON.stringify(pages)}, length: ${pages.length}`)
if (!pages || pages.length === 0) throw new Error('No relative links found') if (!pages || pages.length === 0) throw new Error('No relative links found')
for (const page of pages) { for (const page of pages) {
docs.push(...(await playwrightLoader(page))) docs.push(...(await playwrightLoader(page)))
} }
if (process.env.DEBUG === 'true') options.logger.info(`Finish ${relativeLinksMethod}`) if (process.env.DEBUG === 'true') options.logger.info(`[${orgId}]: Finish PlaywrightWebBaseLoader ${relativeLinksMethod}`)
} else if (selectedLinks && selectedLinks.length > 0) { } else if (selectedLinks && selectedLinks.length > 0) {
if (process.env.DEBUG === 'true') if (process.env.DEBUG === 'true')
options.logger.info(`pages: ${JSON.stringify(selectedLinks)}, length: ${selectedLinks.length}`) options.logger.info(
`[${orgId}]: PlaywrightWebBaseLoader pages: ${JSON.stringify(selectedLinks)}, length: ${selectedLinks.length}`
)
for (const page of selectedLinks.slice(0, limit)) { for (const page of selectedLinks.slice(0, limit)) {
docs.push(...(await playwrightLoader(page))) docs.push(...(await playwrightLoader(page)))
} }
@@ -155,6 +155,7 @@ class Puppeteer_DocumentLoaders implements INode {
let waitForSelector = nodeData.inputs?.waitForSelector as string let waitForSelector = nodeData.inputs?.waitForSelector as string
const _omitMetadataKeys = nodeData.inputs?.omitMetadataKeys as string const _omitMetadataKeys = nodeData.inputs?.omitMetadataKeys as string
const output = nodeData.outputs?.output as string const output = nodeData.outputs?.output as string
const orgId = options.orgId
let omitMetadataKeys: string[] = [] let omitMetadataKeys: string[] = []
if (_omitMetadataKeys) { if (_omitMetadataKeys) {
@@ -198,13 +199,14 @@ class Puppeteer_DocumentLoaders implements INode {
} }
return docs return docs
} catch (err) { } catch (err) {
if (process.env.DEBUG === 'true') options.logger.error(`error in PuppeteerWebBaseLoader: ${err.message}, on page: ${url}`) if (process.env.DEBUG === 'true')
options.logger.error(`[${orgId}]: Error in PuppeteerWebBaseLoader: ${err.message}, on page: ${url}`)
} }
} }
let docs: IDocument[] = [] let docs: IDocument[] = []
if (relativeLinksMethod) { if (relativeLinksMethod) {
if (process.env.DEBUG === 'true') options.logger.info(`Start ${relativeLinksMethod}`) if (process.env.DEBUG === 'true') options.logger.info(`[${orgId}]: Start PuppeteerWebBaseLoader ${relativeLinksMethod}`)
// if limit is 0 we don't want it to default to 10 so we check explicitly for null or undefined // if limit is 0 we don't want it to default to 10 so we check explicitly for null or undefined
// so when limit is 0 we can fetch all the links // so when limit is 0 we can fetch all the links
if (limit === null || limit === undefined) limit = 10 if (limit === null || limit === undefined) limit = 10
@@ -215,15 +217,18 @@ class Puppeteer_DocumentLoaders implements INode {
: relativeLinksMethod === 'webCrawl' : relativeLinksMethod === 'webCrawl'
? await webCrawl(url, limit) ? await webCrawl(url, limit)
: await xmlScrape(url, limit) : await xmlScrape(url, limit)
if (process.env.DEBUG === 'true') options.logger.info(`pages: ${JSON.stringify(pages)}, length: ${pages.length}`) if (process.env.DEBUG === 'true')
options.logger.info(`[${orgId}]: PuppeteerWebBaseLoader pages: ${JSON.stringify(pages)}, length: ${pages.length}`)
if (!pages || pages.length === 0) throw new Error('No relative links found') if (!pages || pages.length === 0) throw new Error('No relative links found')
for (const page of pages) { for (const page of pages) {
docs.push(...(await puppeteerLoader(page))) docs.push(...(await puppeteerLoader(page)))
} }
if (process.env.DEBUG === 'true') options.logger.info(`Finish ${relativeLinksMethod}`) if (process.env.DEBUG === 'true') options.logger.info(`[${orgId}]: Finish PuppeteerWebBaseLoader ${relativeLinksMethod}`)
} else if (selectedLinks && selectedLinks.length > 0) { } else if (selectedLinks && selectedLinks.length > 0) {
if (process.env.DEBUG === 'true') if (process.env.DEBUG === 'true')
options.logger.info(`pages: ${JSON.stringify(selectedLinks)}, length: ${selectedLinks.length}`) options.logger.info(
`[${orgId}]: PuppeteerWebBaseLoader pages: ${JSON.stringify(selectedLinks)}, length: ${selectedLinks.length}`
)
for (const page of selectedLinks.slice(0, limit)) { for (const page of selectedLinks.slice(0, limit)) {
docs.push(...(await puppeteerLoader(page))) docs.push(...(await puppeteerLoader(page)))
} }
@@ -5,7 +5,7 @@ DS File Loarder integration for Flowise
## 🌱 Env Variables ## 🌱 Env Variables
| Variable | Description | Type | Default | | Variable | Description | Type | Default |
| ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- | | -------------------- | ----------------------------------------------- | ------ | ---------------------------------------- |
| UNSTRUCTURED_API_URL | Default `unstructuredApiUrl` for S3 File Loader | String | http://localhost:8000/general/v0/general | | UNSTRUCTURED_API_URL | Default `unstructuredApiUrl` for S3 File Loader | String | http://localhost:8000/general/v0/general |
## License ## License
@@ -98,11 +98,12 @@ class Text_DocumentLoaders implements INode {
} else { } else {
files = [fileName] files = [fileName]
} }
const orgId = options.orgId
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await getFileFromStorage(file, chatflowid) const fileData = await getFileFromStorage(file, orgId, chatflowid)
const blob = new Blob([fileData]) const blob = new Blob([fileData])
const loader = new TextLoader(blob) const loader = new TextLoader(blob)
@@ -5,7 +5,7 @@ Unstructured File Loader integration for Flowise
## 🌱 Env Variables ## 🌱 Env Variables
| Variable | Description | Type | Default | | Variable | Description | Type | Default |
| ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- | | -------------------- | ---------------------------------------------------- | ------ | ---------------------------------------- |
| UNSTRUCTURED_API_URL | Default `apiUrl` for Unstructured File/Floder Loader | String | http://localhost:8000/general/v0/general | | UNSTRUCTURED_API_URL | Default `apiUrl` for Unstructured File/Floder Loader | String | http://localhost:8000/general/v0/general |
## License ## License
@@ -532,11 +532,12 @@ class UnstructuredFile_DocumentLoaders implements INode {
} else { } else {
files = [fileName] files = [fileName]
} }
const orgId = options.orgId
const chatflowid = options.chatflowid const chatflowid = options.chatflowid
for (const file of files) { for (const file of files) {
if (!file) continue if (!file) continue
const fileData = await getFileFromStorage(file, chatflowid) const fileData = await getFileFromStorage(file, orgId, chatflowid)
const loaderDocs = await loader.loadAndSplitBuffer(fileData, file) const loaderDocs = await loader.loadAndSplitBuffer(fileData, file)
docs.push(...loaderDocs) docs.push(...loaderDocs)
} }
@@ -5,7 +5,7 @@ Azure OpenAI Embedding Model integration for Flowise
## 🌱 Env Variables ## 🌱 Env Variables
| Variable | Description | Type | Default | | Variable | Description | Type | Default |
| ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- | | ------------------------------------------- | ------------------------------------------------------------------------ | ------ | ------- |
| AZURE_OPENAI_API_KEY | Default `credential.azureOpenAIApiKey` for Azure OpenAI Model | String | | | AZURE_OPENAI_API_KEY | Default `credential.azureOpenAIApiKey` for Azure OpenAI Model | String | |
| AZURE_OPENAI_API_INSTANCE_NAME | Default `credential.azureOpenAIApiInstanceName` for Azure OpenAI Model | String | | | AZURE_OPENAI_API_INSTANCE_NAME | Default `credential.azureOpenAIApiInstanceName` for Azure OpenAI Model | String | |
| AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME | Default `credential.azureOpenAIApiDeploymentName` for Azure OpenAI Model | String | | | AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME | Default `credential.azureOpenAIApiDeploymentName` for Azure OpenAI Model | String | |
@@ -10,6 +10,7 @@ import {
} from '../../../src/Interface' } from '../../../src/Interface'
import { Metadata, BaseRetriever, LLM, ContextChatEngine, ChatMessage, NodeWithScore } from 'llamaindex' import { Metadata, BaseRetriever, LLM, ContextChatEngine, ChatMessage, NodeWithScore } from 'llamaindex'
import { reformatSourceDocuments } from '../EngineUtils' import { reformatSourceDocuments } from '../EngineUtils'
import { EvaluationRunTracerLlama } from '../../../evaluation/EvaluationRunTracerLlama'
class ContextChatEngine_LlamaIndex implements INode { class ContextChatEngine_LlamaIndex implements INode {
label: string label: string
@@ -93,6 +94,9 @@ class ContextChatEngine_LlamaIndex implements INode {
const chatEngine = new ContextChatEngine({ chatModel: model, retriever: vectorStoreRetriever }) const chatEngine = new ContextChatEngine({ chatModel: model, retriever: vectorStoreRetriever })
// these are needed for evaluation runs
await EvaluationRunTracerLlama.injectEvaluationMetadata(nodeData, options, chatEngine)
const msgs = (await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[] const msgs = (await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[]
for (const message of msgs) { for (const message of msgs) {
if (message.type === 'apiMessage') { if (message.type === 'apiMessage') {
@@ -9,6 +9,7 @@ import {
IServerSideEventStreamer IServerSideEventStreamer
} from '../../../src/Interface' } from '../../../src/Interface'
import { LLM, ChatMessage, SimpleChatEngine } from 'llamaindex' import { LLM, ChatMessage, SimpleChatEngine } from 'llamaindex'
import { EvaluationRunTracerLlama } from '../../../evaluation/EvaluationRunTracerLlama'
class SimpleChatEngine_LlamaIndex implements INode { class SimpleChatEngine_LlamaIndex implements INode {
label: string label: string
@@ -78,6 +79,9 @@ class SimpleChatEngine_LlamaIndex implements INode {
const chatEngine = new SimpleChatEngine({ llm: model }) const chatEngine = new SimpleChatEngine({ llm: model })
// these are needed for evaluation runs
await EvaluationRunTracerLlama.injectEvaluationMetadata(nodeData, options, chatEngine)
const msgs = (await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[] const msgs = (await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[]
for (const message of msgs) { for (const message of msgs) {
if (message.type === 'apiMessage') { if (message.type === 'apiMessage') {
@@ -10,6 +10,7 @@ import {
NodeWithScore NodeWithScore
} from 'llamaindex' } from 'llamaindex'
import { reformatSourceDocuments } from '../EngineUtils' import { reformatSourceDocuments } from '../EngineUtils'
import { EvaluationRunTracerLlama } from '../../../evaluation/EvaluationRunTracerLlama'
class QueryEngine_LlamaIndex implements INode { class QueryEngine_LlamaIndex implements INode {
label: string label: string
@@ -72,6 +73,8 @@ class QueryEngine_LlamaIndex implements INode {
let sourceNodes: NodeWithScore<Metadata>[] = [] let sourceNodes: NodeWithScore<Metadata>[] = []
let isStreamingStarted = false let isStreamingStarted = false
await EvaluationRunTracerLlama.injectEvaluationMetadata(nodeData, options, queryEngine)
const shouldStreamResponse = options.shouldStreamResponse const shouldStreamResponse = options.shouldStreamResponse
const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer
const chatId = options.chatId const chatId = options.chatId
@@ -15,6 +15,7 @@ import {
NodeWithScore NodeWithScore
} from 'llamaindex' } from 'llamaindex'
import { reformatSourceDocuments } from '../EngineUtils' import { reformatSourceDocuments } from '../EngineUtils'
import { EvaluationRunTracerLlama } from '../../../evaluation/EvaluationRunTracerLlama'
class SubQuestionQueryEngine_LlamaIndex implements INode { class SubQuestionQueryEngine_LlamaIndex implements INode {
label: string label: string
@@ -89,6 +90,8 @@ class SubQuestionQueryEngine_LlamaIndex implements INode {
let sourceNodes: NodeWithScore<Metadata>[] = [] let sourceNodes: NodeWithScore<Metadata>[] = []
let isStreamingStarted = false let isStreamingStarted = false
await EvaluationRunTracerLlama.injectEvaluationMetadata(nodeData, options, queryEngine)
const shouldStreamResponse = options.shouldStreamResponse const shouldStreamResponse = options.shouldStreamResponse
const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer
const chatId = options.chatId const chatId = options.chatId
@@ -5,7 +5,7 @@ Azure OpenAI LLM integration for Flowise
## 🌱 Env Variables ## 🌱 Env Variables
| Variable | Description | Type | Default | | Variable | Description | Type | Default |
| ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- | | -------------------------------- | ---------------------------------------------------------------------- | ------ | ------- |
| AZURE_OPENAI_API_KEY | Default `credential.azureOpenAIApiKey` for Azure OpenAI LLM | String | | | AZURE_OPENAI_API_KEY | Default `credential.azureOpenAIApiKey` for Azure OpenAI LLM | String | |
| AZURE_OPENAI_API_INSTANCE_NAME | Default `credential.azureOpenAIApiInstanceName` for Azure OpenAI LLM | String | | | AZURE_OPENAI_API_INSTANCE_NAME | Default `credential.azureOpenAIApiInstanceName` for Azure OpenAI LLM | String | |
| AZURE_OPENAI_API_DEPLOYMENT_NAME | Default `credential.azureOpenAIApiDeploymentName` for Azure OpenAI LLM | String | | | AZURE_OPENAI_API_DEPLOYMENT_NAME | Default `credential.azureOpenAIApiDeploymentName` for Azure OpenAI LLM | String | |
@@ -108,6 +108,7 @@ class AgentMemory_Memory implements INode {
const databaseType = nodeData.inputs?.databaseType as string const databaseType = nodeData.inputs?.databaseType as string
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const chatflowid = options.chatflowid as string const chatflowid = options.chatflowid as string
const orgId = options.orgId as string
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
let additionalConfiguration = {} let additionalConfiguration = {}
@@ -135,7 +136,8 @@ class AgentMemory_Memory implements INode {
threadId, threadId,
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
} }
const recordManager = new SqliteSaver(args) const recordManager = new SqliteSaver(args)
return recordManager return recordManager
@@ -159,7 +161,8 @@ class AgentMemory_Memory implements INode {
threadId, threadId,
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
} }
const recordManager = new PostgresSaver(args) const recordManager = new PostgresSaver(args)
return recordManager return recordManager
@@ -184,7 +187,8 @@ class AgentMemory_Memory implements INode {
threadId, threadId,
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
} }
const recordManager = new MySQLSaver(args) const recordManager = new MySQLSaver(args)
return recordManager return recordManager
@@ -65,6 +65,7 @@ class MySQLAgentMemory_Memory implements INode {
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const chatflowid = options.chatflowid as string const chatflowid = options.chatflowid as string
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
const orgId = options.orgId as string
let additionalConfiguration = {} let additionalConfiguration = {}
if (additionalConfig) { if (additionalConfig) {
@@ -102,7 +103,8 @@ class MySQLAgentMemory_Memory implements INode {
threadId, threadId,
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
} }
const recordManager = new MySQLSaver(args) const recordManager = new MySQLSaver(args)
return recordManager return recordManager
@@ -242,7 +242,7 @@ export class MySQLSaver extends BaseCheckpointSaver implements MemoryMethods {
} }
if (returnBaseMessages) { if (returnBaseMessages) {
return await mapChatMessageToBaseMessage(chatMessage) return await mapChatMessageToBaseMessage(chatMessage, this.config.orgId)
} }
let returnIMessages: IMessage[] = [] let returnIMessages: IMessage[] = []
@@ -65,6 +65,7 @@ class PostgresAgentMemory_Memory implements INode {
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const chatflowid = options.chatflowid as string const chatflowid = options.chatflowid as string
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
const orgId = options.orgId as string
let additionalConfiguration = {} let additionalConfiguration = {}
if (additionalConfig) { if (additionalConfig) {
@@ -101,7 +102,8 @@ class PostgresAgentMemory_Memory implements INode {
threadId, threadId,
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
} }
const recordManager = new PostgresSaver(args) const recordManager = new PostgresSaver(args)
return recordManager return recordManager
@@ -283,7 +283,7 @@ CREATE TABLE IF NOT EXISTS ${tableName} (
} }
if (returnBaseMessages) { if (returnBaseMessages) {
return await mapChatMessageToBaseMessage(chatMessage) return await mapChatMessageToBaseMessage(chatMessage, this.config.orgId)
} }
let returnIMessages: IMessage[] = [] let returnIMessages: IMessage[] = []
@@ -51,6 +51,7 @@ class SQLiteAgentMemory_Memory implements INode {
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const chatflowid = options.chatflowid as string const chatflowid = options.chatflowid as string
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
const orgId = options.orgId as string
let additionalConfiguration = {} let additionalConfiguration = {}
if (additionalConfig) { if (additionalConfig) {
@@ -76,7 +77,8 @@ class SQLiteAgentMemory_Memory implements INode {
threadId, threadId,
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
} }
const recordManager = new SqliteSaver(args) const recordManager = new SqliteSaver(args)
@@ -266,7 +266,7 @@ CREATE TABLE IF NOT EXISTS ${tableName} (
} }
if (returnBaseMessages) { if (returnBaseMessages) {
return await mapChatMessageToBaseMessage(chatMessage) return await mapChatMessageToBaseMessage(chatMessage, this.config.orgId)
} }
let returnIMessages: IMessage[] = [] let returnIMessages: IMessage[] = []
@@ -9,6 +9,7 @@ export type SaverOptions = {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
} }
export interface CheckpointTuple { export interface CheckpointTuple {
@@ -61,6 +61,7 @@ class BufferMemory_Memory implements INode {
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const chatflowid = options.chatflowid as string const chatflowid = options.chatflowid as string
const orgId = options.orgId as string
return new BufferMemoryExtended({ return new BufferMemoryExtended({
returnMessages: true, returnMessages: true,
@@ -68,7 +69,8 @@ class BufferMemory_Memory implements INode {
sessionId, sessionId,
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
}) })
} }
} }
@@ -78,12 +80,14 @@ interface BufferMemoryExtendedInput {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
} }
class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
sessionId = '' sessionId = ''
constructor(fields: BufferMemoryInput & BufferMemoryExtendedInput) { constructor(fields: BufferMemoryInput & BufferMemoryExtendedInput) {
@@ -92,6 +96,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
this.appDataSource = fields.appDataSource this.appDataSource = fields.appDataSource
this.databaseEntities = fields.databaseEntities this.databaseEntities = fields.databaseEntities
this.chatflowid = fields.chatflowid this.chatflowid = fields.chatflowid
this.orgId = fields.orgId
} }
async getChatMessages( async getChatMessages(
@@ -117,7 +122,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
} }
if (returnBaseMessages) { if (returnBaseMessages) {
return await mapChatMessageToBaseMessage(chatMessage) return await mapChatMessageToBaseMessage(chatMessage, this.orgId)
} }
let returnIMessages: IMessage[] = [] let returnIMessages: IMessage[] = []
@@ -69,6 +69,7 @@ class BufferWindowMemory_Memory implements INode {
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const chatflowid = options.chatflowid as string const chatflowid = options.chatflowid as string
const orgId = options.orgId as string
const obj: Partial<BufferWindowMemoryInput> & BufferMemoryExtendedInput = { const obj: Partial<BufferWindowMemoryInput> & BufferMemoryExtendedInput = {
returnMessages: true, returnMessages: true,
@@ -77,7 +78,8 @@ class BufferWindowMemory_Memory implements INode {
k: parseInt(k, 10), k: parseInt(k, 10),
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
} }
return new BufferWindowMemoryExtended(obj) return new BufferWindowMemoryExtended(obj)
@@ -89,12 +91,14 @@ interface BufferMemoryExtendedInput {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
} }
class BufferWindowMemoryExtended extends FlowiseWindowMemory implements MemoryMethods { class BufferWindowMemoryExtended extends FlowiseWindowMemory implements MemoryMethods {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
sessionId = '' sessionId = ''
constructor(fields: BufferWindowMemoryInput & BufferMemoryExtendedInput) { constructor(fields: BufferWindowMemoryInput & BufferMemoryExtendedInput) {
@@ -103,6 +107,7 @@ class BufferWindowMemoryExtended extends FlowiseWindowMemory implements MemoryMe
this.appDataSource = fields.appDataSource this.appDataSource = fields.appDataSource
this.databaseEntities = fields.databaseEntities this.databaseEntities = fields.databaseEntities
this.chatflowid = fields.chatflowid this.chatflowid = fields.chatflowid
this.orgId = fields.orgId
} }
async getChatMessages( async getChatMessages(
@@ -134,7 +139,7 @@ class BufferWindowMemoryExtended extends FlowiseWindowMemory implements MemoryMe
} }
if (returnBaseMessages) { if (returnBaseMessages) {
return await mapChatMessageToBaseMessage(chatMessage) return await mapChatMessageToBaseMessage(chatMessage, this.orgId)
} }
let returnIMessages: IMessage[] = [] let returnIMessages: IMessage[] = []
@@ -78,6 +78,7 @@ class ConversationSummaryBufferMemory_Memory implements INode {
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const chatflowid = options.chatflowid as string const chatflowid = options.chatflowid as string
const orgId = options.orgId as string
const obj: ConversationSummaryBufferMemoryInput & BufferMemoryExtendedInput = { const obj: ConversationSummaryBufferMemoryInput & BufferMemoryExtendedInput = {
llm: model, llm: model,
@@ -87,7 +88,8 @@ class ConversationSummaryBufferMemory_Memory implements INode {
returnMessages: true, returnMessages: true,
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
} }
return new ConversationSummaryBufferMemoryExtended(obj) return new ConversationSummaryBufferMemoryExtended(obj)
@@ -99,12 +101,14 @@ interface BufferMemoryExtendedInput {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
} }
class ConversationSummaryBufferMemoryExtended extends FlowiseSummaryBufferMemory implements MemoryMethods { class ConversationSummaryBufferMemoryExtended extends FlowiseSummaryBufferMemory implements MemoryMethods {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
sessionId = '' sessionId = ''
constructor(fields: ConversationSummaryBufferMemoryInput & BufferMemoryExtendedInput) { constructor(fields: ConversationSummaryBufferMemoryInput & BufferMemoryExtendedInput) {
@@ -113,6 +117,7 @@ class ConversationSummaryBufferMemoryExtended extends FlowiseSummaryBufferMemory
this.appDataSource = fields.appDataSource this.appDataSource = fields.appDataSource
this.databaseEntities = fields.databaseEntities this.databaseEntities = fields.databaseEntities
this.chatflowid = fields.chatflowid this.chatflowid = fields.chatflowid
this.orgId = fields.orgId
} }
async getChatMessages( async getChatMessages(
@@ -137,7 +142,7 @@ class ConversationSummaryBufferMemoryExtended extends FlowiseSummaryBufferMemory
chatMessage.unshift(...prependMessages) chatMessage.unshift(...prependMessages)
} }
let baseMessages = await mapChatMessageToBaseMessage(chatMessage) let baseMessages = await mapChatMessageToBaseMessage(chatMessage, this.orgId)
// Prune baseMessages if it exceeds max token limit // Prune baseMessages if it exceeds max token limit
if (this.movingSummaryBuffer) { if (this.movingSummaryBuffer) {
@@ -69,6 +69,7 @@ class ConversationSummaryMemory_Memory implements INode {
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const chatflowid = options.chatflowid as string const chatflowid = options.chatflowid as string
const orgId = options.orgId as string
const obj: ConversationSummaryMemoryInput & BufferMemoryExtendedInput = { const obj: ConversationSummaryMemoryInput & BufferMemoryExtendedInput = {
llm: model, llm: model,
@@ -77,7 +78,8 @@ class ConversationSummaryMemory_Memory implements INode {
sessionId, sessionId,
appDataSource, appDataSource,
databaseEntities, databaseEntities,
chatflowid chatflowid,
orgId
} }
return new ConversationSummaryMemoryExtended(obj) return new ConversationSummaryMemoryExtended(obj)
@@ -89,12 +91,14 @@ interface BufferMemoryExtendedInput {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
} }
class ConversationSummaryMemoryExtended extends FlowiseSummaryMemory implements MemoryMethods { class ConversationSummaryMemoryExtended extends FlowiseSummaryMemory implements MemoryMethods {
appDataSource: DataSource appDataSource: DataSource
databaseEntities: IDatabaseEntity databaseEntities: IDatabaseEntity
chatflowid: string chatflowid: string
orgId: string
sessionId = '' sessionId = ''
constructor(fields: ConversationSummaryMemoryInput & BufferMemoryExtendedInput) { constructor(fields: ConversationSummaryMemoryInput & BufferMemoryExtendedInput) {
@@ -103,6 +107,7 @@ class ConversationSummaryMemoryExtended extends FlowiseSummaryMemory implements
this.appDataSource = fields.appDataSource this.appDataSource = fields.appDataSource
this.databaseEntities = fields.databaseEntities this.databaseEntities = fields.databaseEntities
this.chatflowid = fields.chatflowid this.chatflowid = fields.chatflowid
this.orgId = fields.orgId
} }
async getChatMessages( async getChatMessages(
@@ -128,7 +133,7 @@ class ConversationSummaryMemoryExtended extends FlowiseSummaryMemory implements
chatMessage.unshift(...prependMessages) chatMessage.unshift(...prependMessages)
} }
const baseMessages = await mapChatMessageToBaseMessage(chatMessage) const baseMessages = await mapChatMessageToBaseMessage(chatMessage, this.orgId)
// Get summary // Get summary
if (this.llm && typeof this.llm !== 'string') { if (this.llm && typeof this.llm !== 'string') {
@@ -125,6 +125,8 @@ const initializeDynamoDB = async (nodeData: INodeData, options: ICommonObject):
config config
}) })
const orgId = options.orgId as string
const memory = new BufferMemoryExtended({ const memory = new BufferMemoryExtended({
memoryKey: memoryKey ?? 'chat_history', memoryKey: memoryKey ?? 'chat_history',
chatHistory: dynamoDb, chatHistory: dynamoDb,
@@ -132,7 +134,8 @@ const initializeDynamoDB = async (nodeData: INodeData, options: ICommonObject):
dynamodbClient: client, dynamodbClient: client,
tableName, tableName,
partitionKey, partitionKey,
dynamoKey: { [partitionKey]: { S: sessionId } } dynamoKey: { [partitionKey]: { S: sessionId } },
orgId
}) })
return memory return memory
} }
@@ -143,6 +146,7 @@ interface BufferMemoryExtendedInput {
tableName: string tableName: string
partitionKey: string partitionKey: string
dynamoKey: Record<string, AttributeValue> dynamoKey: Record<string, AttributeValue>
orgId: string
} }
interface DynamoDBSerializedChatMessage { interface DynamoDBSerializedChatMessage {
@@ -165,6 +169,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
private dynamoKey: Record<string, AttributeValue> private dynamoKey: Record<string, AttributeValue>
private messageAttributeName: string private messageAttributeName: string
sessionId = '' sessionId = ''
orgId = ''
dynamodbClient: DynamoDBClient dynamodbClient: DynamoDBClient
constructor(fields: BufferMemoryInput & BufferMemoryExtendedInput) { constructor(fields: BufferMemoryInput & BufferMemoryExtendedInput) {
@@ -174,6 +179,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
this.tableName = fields.tableName this.tableName = fields.tableName
this.partitionKey = fields.partitionKey this.partitionKey = fields.partitionKey
this.dynamoKey = fields.dynamoKey this.dynamoKey = fields.dynamoKey
this.orgId = fields.orgId
} }
overrideDynamoKey(overrideSessionId = '') { overrideDynamoKey(overrideSessionId = '') {
@@ -260,7 +266,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
.filter((x): x is StoredMessage => x.type !== undefined && x.data.content !== undefined) .filter((x): x is StoredMessage => x.type !== undefined && x.data.content !== undefined)
const baseMessages = messages.map(mapStoredMessageToChatMessage) const baseMessages = messages.map(mapStoredMessageToChatMessage)
if (prependMessages?.length) { if (prependMessages?.length) {
baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages))) baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages, this.orgId)))
} }
return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages)
} }
@@ -151,6 +151,7 @@ class Mem0_Memory implements INode {
const initializeMem0 = async (nodeData: INodeData, options: ICommonObject): Promise<BaseMem0Memory> => { const initializeMem0 = async (nodeData: INodeData, options: ICommonObject): Promise<BaseMem0Memory> => {
const initialUserId = nodeData.inputs?.user_id as string const initialUserId = nodeData.inputs?.user_id as string
const useFlowiseChatId = nodeData.inputs?.useFlowiseChatId as boolean const useFlowiseChatId = nodeData.inputs?.useFlowiseChatId as boolean
const orgId = options.orgId as string
if (!useFlowiseChatId && !initialUserId) { if (!useFlowiseChatId && !initialUserId) {
throw new Error('User ID field cannot be empty when "Use Flowise Chat ID" is OFF.') throw new Error('User ID field cannot be empty when "Use Flowise Chat ID" is OFF.')
@@ -198,7 +199,8 @@ const initializeMem0 = async (nodeData: INodeData, options: ICommonObject): Prom
databaseEntities: options.databaseEntities as IDatabaseEntity, databaseEntities: options.databaseEntities as IDatabaseEntity,
chatflowid: options.chatflowid as string, chatflowid: options.chatflowid as string,
searchOnly: (nodeData.inputs?.searchOnly as boolean) || false, searchOnly: (nodeData.inputs?.searchOnly as boolean) || false,
useFlowiseChatId: useFlowiseChatId useFlowiseChatId: useFlowiseChatId,
orgId: orgId
} }
return new Mem0MemoryExtended(obj) return new Mem0MemoryExtended(obj)
@@ -207,11 +209,13 @@ const initializeMem0 = async (nodeData: INodeData, options: ICommonObject): Prom
interface Mem0MemoryExtendedInput extends Mem0MemoryInput { interface Mem0MemoryExtendedInput extends Mem0MemoryInput {
memoryOptions?: MemoryOptions | SearchOptions memoryOptions?: MemoryOptions | SearchOptions
useFlowiseChatId: boolean useFlowiseChatId: boolean
orgId: string
} }
class Mem0MemoryExtended extends BaseMem0Memory implements MemoryMethods { class Mem0MemoryExtended extends BaseMem0Memory implements MemoryMethods {
initialUserId: string initialUserId: string
userId: string userId: string
orgId: string
memoryKey: string memoryKey: string
inputKey: string inputKey: string
appDataSource: DataSource appDataSource: DataSource
@@ -233,6 +237,7 @@ class Mem0MemoryExtended extends BaseMem0Memory implements MemoryMethods {
this.chatflowid = fields.chatflowid this.chatflowid = fields.chatflowid
this.searchOnly = fields.searchOnly this.searchOnly = fields.searchOnly
this.useFlowiseChatId = fields.useFlowiseChatId this.useFlowiseChatId = fields.useFlowiseChatId
this.orgId = fields.orgId
} }
// Selects Mem0 user_id based on toggle state (Flowise chat ID or input field) // Selects Mem0 user_id based on toggle state (Flowise chat ID or input field)
@@ -337,7 +342,7 @@ class Mem0MemoryExtended extends BaseMem0Memory implements MemoryMethods {
console.warn('Mem0 history is not a string, cannot prepend directly.') console.warn('Mem0 history is not a string, cannot prepend directly.')
} }
return await mapChatMessageToBaseMessage(chatMessage) return await mapChatMessageToBaseMessage(chatMessage, this.orgId)
} }
return returnIMessages return returnIMessages
@@ -88,9 +88,12 @@ const initializeMongoDB = async (nodeData: INodeData, options: ICommonObject): P
const mongoDBConnectUrl = getCredentialParam('mongoDBConnectUrl', credentialData, nodeData) const mongoDBConnectUrl = getCredentialParam('mongoDBConnectUrl', credentialData, nodeData)
const driverInfo = { name: 'Flowise', version: (await getVersion()).version } const driverInfo = { name: 'Flowise', version: (await getVersion()).version }
const orgId = options.orgId as string
return new BufferMemoryExtended({ return new BufferMemoryExtended({
memoryKey: memoryKey ?? 'chat_history', memoryKey: memoryKey ?? 'chat_history',
sessionId, sessionId,
orgId,
mongoConnection: { mongoConnection: {
databaseName, databaseName,
collectionName, collectionName,
@@ -102,6 +105,7 @@ const initializeMongoDB = async (nodeData: INodeData, options: ICommonObject): P
interface BufferMemoryExtendedInput { interface BufferMemoryExtendedInput {
sessionId: string sessionId: string
orgId: string
mongoConnection: { mongoConnection: {
databaseName: string databaseName: string
collectionName: string collectionName: string
@@ -112,6 +116,7 @@ interface BufferMemoryExtendedInput {
class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
sessionId = '' sessionId = ''
orgId = ''
mongoConnection: { mongoConnection: {
databaseName: string databaseName: string
collectionName: string collectionName: string
@@ -122,6 +127,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
constructor(fields: BufferMemoryInput & BufferMemoryExtendedInput) { constructor(fields: BufferMemoryInput & BufferMemoryExtendedInput) {
super(fields) super(fields)
this.sessionId = fields.sessionId this.sessionId = fields.sessionId
this.orgId = fields.orgId
this.mongoConnection = fields.mongoConnection this.mongoConnection = fields.mongoConnection
} }
@@ -138,7 +144,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
const messages = document?.messages || [] const messages = document?.messages || []
const baseMessages = messages.map(mapStoredMessageToChatMessage) const baseMessages = messages.map(mapStoredMessageToChatMessage)
if (prependMessages?.length) { if (prependMessages?.length) {
baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages))) baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages, this.orgId)))
} }
await client.close() await client.close()
@@ -88,6 +88,7 @@ const initializeRedis = async (nodeData: INodeData, options: ICommonObject): Pro
const credentialData = await getCredentialData(nodeData.credential ?? '', options) const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const redisUrl = getCredentialParam('redisUrl', credentialData, nodeData) const redisUrl = getCredentialParam('redisUrl', credentialData, nodeData)
const orgId = options.orgId as string
const redisOptions = redisUrl const redisOptions = redisUrl
? redisUrl ? redisUrl
@@ -104,7 +105,8 @@ const initializeRedis = async (nodeData: INodeData, options: ICommonObject): Pro
sessionId, sessionId,
windowSize, windowSize,
sessionTTL, sessionTTL,
redisOptions redisOptions,
orgId
}) })
return memory return memory
@@ -114,11 +116,13 @@ interface BufferMemoryExtendedInput {
sessionId: string sessionId: string
windowSize?: number windowSize?: number
sessionTTL?: number sessionTTL?: number
orgId: string
redisOptions: RedisOptions | string redisOptions: RedisOptions | string
} }
class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
sessionId = '' sessionId = ''
orgId = ''
windowSize?: number windowSize?: number
sessionTTL?: number sessionTTL?: number
redisOptions: RedisOptions | string redisOptions: RedisOptions | string
@@ -128,6 +132,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
this.sessionId = fields.sessionId this.sessionId = fields.sessionId
this.windowSize = fields.windowSize this.windowSize = fields.windowSize
this.sessionTTL = fields.sessionTTL this.sessionTTL = fields.sessionTTL
this.orgId = fields.orgId
this.redisOptions = fields.redisOptions this.redisOptions = fields.redisOptions
} }
@@ -165,7 +170,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
const orderedMessages = rawStoredMessages.reverse().map((message) => JSON.parse(message)) const orderedMessages = rawStoredMessages.reverse().map((message) => JSON.parse(message))
const baseMessages = orderedMessages.map(mapStoredMessageToChatMessage) const baseMessages = orderedMessages.map(mapStoredMessageToChatMessage)
if (prependMessages?.length) { if (prependMessages?.length) {
baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages))) baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages, this.orgId)))
} }
return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages)
}) })
@@ -100,13 +100,14 @@ const initalizeUpstashRedis = async (nodeData: INodeData, options: ICommonObject
sessionTTL, sessionTTL,
client client
}) })
const orgId = options.orgId as string
const memory = new BufferMemoryExtended({ const memory = new BufferMemoryExtended({
memoryKey: memoryKey ?? 'chat_history', memoryKey: memoryKey ?? 'chat_history',
chatHistory: redisChatMessageHistory, chatHistory: redisChatMessageHistory,
sessionId, sessionId,
sessionTTL, sessionTTL,
redisClient: client redisClient: client,
orgId
}) })
return memory return memory
@@ -115,11 +116,13 @@ const initalizeUpstashRedis = async (nodeData: INodeData, options: ICommonObject
interface BufferMemoryExtendedInput { interface BufferMemoryExtendedInput {
redisClient: Redis redisClient: Redis
sessionId: string sessionId: string
orgId: string
sessionTTL?: number sessionTTL?: number
} }
class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
sessionId = '' sessionId = ''
orgId = ''
redisClient: Redis redisClient: Redis
sessionTTL?: number sessionTTL?: number
@@ -128,6 +131,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
this.sessionId = fields.sessionId this.sessionId = fields.sessionId
this.redisClient = fields.redisClient this.redisClient = fields.redisClient
this.sessionTTL = fields.sessionTTL this.sessionTTL = fields.sessionTTL
this.orgId = fields.orgId
} }
async getChatMessages( async getChatMessages(
@@ -143,7 +147,7 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods {
const previousMessages = orderedMessages.filter((x): x is StoredMessage => x.type !== undefined && x.data.content !== undefined) const previousMessages = orderedMessages.filter((x): x is StoredMessage => x.type !== undefined && x.data.content !== undefined)
const baseMessages = previousMessages.map(mapStoredMessageToChatMessage) const baseMessages = previousMessages.map(mapStoredMessageToChatMessage)
if (prependMessages?.length) { if (prependMessages?.length) {
baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages))) baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages, this.orgId)))
} }
return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages)
} }
@@ -119,6 +119,7 @@ const initializeZep = async (nodeData: INodeData, options: ICommonObject): Promi
const credentialData = await getCredentialData(nodeData.credential ?? '', options) const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const apiKey = getCredentialParam('apiKey', credentialData, nodeData) const apiKey = getCredentialParam('apiKey', credentialData, nodeData)
const orgId = options.orgId as string
const obj: ZepMemoryInput & ZepMemoryExtendedInput = { const obj: ZepMemoryInput & ZepMemoryExtendedInput = {
baseURL, baseURL,
aiPrefix, aiPrefix,
@@ -127,6 +128,7 @@ const initializeZep = async (nodeData: INodeData, options: ICommonObject): Promi
memoryKey, memoryKey,
inputKey, inputKey,
sessionId, sessionId,
orgId,
k: k ? parseInt(k, 10) : undefined k: k ? parseInt(k, 10) : undefined
} }
if (apiKey) obj.apiKey = apiKey if (apiKey) obj.apiKey = apiKey
@@ -136,14 +138,17 @@ const initializeZep = async (nodeData: INodeData, options: ICommonObject): Promi
interface ZepMemoryExtendedInput { interface ZepMemoryExtendedInput {
k?: number k?: number
orgId: string
} }
class ZepMemoryExtended extends ZepMemory implements MemoryMethods { class ZepMemoryExtended extends ZepMemory implements MemoryMethods {
lastN?: number lastN?: number
orgId = ''
constructor(fields: ZepMemoryInput & ZepMemoryExtendedInput) { constructor(fields: ZepMemoryInput & ZepMemoryExtendedInput) {
super(fields) super(fields)
this.lastN = fields.k this.lastN = fields.k
this.orgId = fields.orgId
} }
async loadMemoryVariables(values: InputValues, overrideSessionId = ''): Promise<MemoryVariables> { async loadMemoryVariables(values: InputValues, overrideSessionId = ''): Promise<MemoryVariables> {
@@ -176,7 +181,7 @@ class ZepMemoryExtended extends ZepMemory implements MemoryMethods {
const memoryVariables = await this.loadMemoryVariables({}, id) const memoryVariables = await this.loadMemoryVariables({}, id)
const baseMessages = memoryVariables[this.memoryKey] const baseMessages = memoryVariables[this.memoryKey]
if (prependMessages?.length) { if (prependMessages?.length) {
baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages))) baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages, this.orgId)))
} }
return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages)
} }
@@ -113,6 +113,7 @@ const initializeZep = async (nodeData: INodeData, options: ICommonObject): Promi
const credentialData = await getCredentialData(nodeData.credential ?? '', options) const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const apiKey = getCredentialParam('apiKey', credentialData, nodeData) const apiKey = getCredentialParam('apiKey', credentialData, nodeData)
const orgId = options.orgId as string
const obj: ZepMemoryInput & ZepMemoryExtendedInput = { const obj: ZepMemoryInput & ZepMemoryExtendedInput = {
apiKey, apiKey,
aiPrefix, aiPrefix,
@@ -121,7 +122,8 @@ const initializeZep = async (nodeData: INodeData, options: ICommonObject): Promi
sessionId, sessionId,
inputKey, inputKey,
memoryType: memoryType, memoryType: memoryType,
returnMessages: true returnMessages: true,
orgId
} }
return new ZepMemoryExtended(obj) return new ZepMemoryExtended(obj)
@@ -129,14 +131,17 @@ const initializeZep = async (nodeData: INodeData, options: ICommonObject): Promi
interface ZepMemoryExtendedInput { interface ZepMemoryExtendedInput {
memoryType?: 'perpetual' | 'message_window' memoryType?: 'perpetual' | 'message_window'
orgId: string
} }
class ZepMemoryExtended extends ZepMemory implements MemoryMethods { class ZepMemoryExtended extends ZepMemory implements MemoryMethods {
memoryType: 'perpetual' | 'message_window' memoryType: 'perpetual' | 'message_window'
orgId: string
constructor(fields: ZepMemoryInput & ZepMemoryExtendedInput) { constructor(fields: ZepMemoryInput & ZepMemoryExtendedInput) {
super(fields) super(fields)
this.memoryType = fields.memoryType ?? 'perpetual' this.memoryType = fields.memoryType ?? 'perpetual'
this.orgId = fields.orgId
} }
async loadMemoryVariables(values: InputValues, overrideSessionId = ''): Promise<MemoryVariables> { async loadMemoryVariables(values: InputValues, overrideSessionId = ''): Promise<MemoryVariables> {
@@ -169,7 +174,7 @@ class ZepMemoryExtended extends ZepMemory implements MemoryMethods {
const memoryVariables = await this.loadMemoryVariables({}, id) const memoryVariables = await this.loadMemoryVariables({}, id)
const baseMessages = memoryVariables[this.memoryKey] const baseMessages = memoryVariables[this.memoryKey]
if (prependMessages?.length) { if (prependMessages?.length) {
baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages))) baseMessages.unshift(...(await mapChatMessageToBaseMessage(prependMessages, this.orgId)))
} }
return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages)
} }
@@ -233,7 +233,7 @@ async function createAgent(
sessionId: flowObj?.sessionId, sessionId: flowObj?.sessionId,
chatId: flowObj?.chatId, chatId: flowObj?.chatId,
input: flowObj?.input, input: flowObj?.input,
verbose: process.env.DEBUG === 'true', verbose: process.env.DEBUG === 'true' ? true : false,
maxIterations: maxIterations ? parseFloat(maxIterations) : undefined maxIterations: maxIterations ? parseFloat(maxIterations) : undefined
}) })
return executor return executor
@@ -120,7 +120,7 @@ class ChatPromptTemplate_Prompts implements INode {
) { ) {
const appDataSource = options.appDataSource as DataSource const appDataSource = options.appDataSource as DataSource
const databaseEntities = options.databaseEntities as IDatabaseEntity const databaseEntities = options.databaseEntities as IDatabaseEntity
const vm = await getVM(appDataSource, databaseEntities, nodeData, {}) const vm = await getVM(appDataSource, databaseEntities, nodeData, options, {})
try { try {
const response = await vm.run(`module.exports = async function() {${messageHistoryCode}}()`, __dirname) const response = await vm.run(`module.exports = async function() {${messageHistoryCode}}()`, __dirname)
if (!Array.isArray(response)) throw new Error('Returned message history must be an array') if (!Array.isArray(response)) throw new Error('Returned message history must be an array')
@@ -680,7 +680,7 @@ async function createAgent(
sessionId: flowObj?.sessionId, sessionId: flowObj?.sessionId,
chatId: flowObj?.chatId, chatId: flowObj?.chatId,
input: flowObj?.input, input: flowObj?.input,
verbose: process.env.DEBUG === 'true', verbose: process.env.DEBUG === 'true' ? true : false,
maxIterations: maxIterations ? parseFloat(maxIterations) : undefined maxIterations: maxIterations ? parseFloat(maxIterations) : undefined
}) })
return executor return executor
@@ -877,7 +877,7 @@ const getReturnOutput = async (nodeData: INodeData, input: string, options: ICom
const updateStateMemory = nodeData.inputs?.updateStateMemory as string const updateStateMemory = nodeData.inputs?.updateStateMemory as string
const selectedTab = tabIdentifier ? tabIdentifier.split(`_${nodeData.id}`)[0] : 'updateStateMemoryUI' const selectedTab = tabIdentifier ? tabIdentifier.split(`_${nodeData.id}`)[0] : 'updateStateMemoryUI'
const variables = await getVars(appDataSource, databaseEntities, nodeData) const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
const flow = { const flow = {
chatflowId: options.chatflowid, chatflowId: options.chatflowid,
@@ -930,7 +930,7 @@ const getReturnOutput = async (nodeData: INodeData, input: string, options: ICom
throw new Error(e) throw new Error(e)
} }
} else if (selectedTab === 'updateStateMemoryCode' && updateStateMemoryCode) { } else if (selectedTab === 'updateStateMemoryCode' && updateStateMemoryCode) {
const vm = await getVM(appDataSource, databaseEntities, nodeData, flow) const vm = await getVM(appDataSource, databaseEntities, nodeData, options, flow)
try { try {
const response = await vm.run(`module.exports = async function() {${updateStateMemoryCode}}()`, __dirname) const response = await vm.run(`module.exports = async function() {${updateStateMemoryCode}}()`, __dirname)
if (typeof response !== 'object') throw new Error('Return output must be an object') if (typeof response !== 'object') throw new Error('Return output must be an object')
@@ -267,7 +267,7 @@ const runCondition = async (nodeData: INodeData, input: string, options: ICommon
const tabIdentifier = nodeData.inputs?.[`${TAB_IDENTIFIER}_${nodeData.id}`] as string const tabIdentifier = nodeData.inputs?.[`${TAB_IDENTIFIER}_${nodeData.id}`] as string
const selectedTab = tabIdentifier ? tabIdentifier.split(`_${nodeData.id}`)[0] : 'conditionUI' const selectedTab = tabIdentifier ? tabIdentifier.split(`_${nodeData.id}`)[0] : 'conditionUI'
const variables = await getVars(appDataSource, databaseEntities, nodeData) const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
const flow = { const flow = {
chatflowId: options.chatflowid, chatflowId: options.chatflowid,
@@ -279,7 +279,7 @@ const runCondition = async (nodeData: INodeData, input: string, options: ICommon
} }
if (selectedTab === 'conditionFunction' && conditionFunction) { if (selectedTab === 'conditionFunction' && conditionFunction) {
const vm = await getVM(appDataSource, databaseEntities, nodeData, flow) const vm = await getVM(appDataSource, databaseEntities, nodeData, options, flow)
try { try {
const response = await vm.run(`module.exports = async function() {${conditionFunction}}()`, __dirname) const response = await vm.run(`module.exports = async function() {${conditionFunction}}()`, __dirname)
if (typeof response !== 'string') throw new Error('Condition function must return a string') if (typeof response !== 'string') throw new Error('Condition function must return a string')
@@ -540,7 +540,7 @@ const runCondition = async (
result = { ...jsonResult, additional_kwargs: { nodeId: nodeData.id } } result = { ...jsonResult, additional_kwargs: { nodeId: nodeData.id } }
} }
const variables = await getVars(appDataSource, databaseEntities, nodeData) const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
const flow = { const flow = {
chatflowId: options.chatflowid, chatflowId: options.chatflowid,
@@ -553,7 +553,7 @@ const runCondition = async (
} }
if (selectedTab === 'conditionFunction' && conditionFunction) { if (selectedTab === 'conditionFunction' && conditionFunction) {
const vm = await getVM(appDataSource, databaseEntities, nodeData, flow) const vm = await getVM(appDataSource, databaseEntities, nodeData, options, flow)
try { try {
const response = await vm.run(`module.exports = async function() {${conditionFunction}}()`, __dirname) const response = await vm.run(`module.exports = async function() {${conditionFunction}}()`, __dirname)
if (typeof response !== 'string') throw new Error('Condition function must return a string') if (typeof response !== 'string') throw new Error('Condition function must return a string')
@@ -102,7 +102,7 @@ class CustomFunction_SeqAgents implements INode {
if (!sequentialNodes || !sequentialNodes.length) throw new Error('Custom function must have a predecessor!') if (!sequentialNodes || !sequentialNodes.length) throw new Error('Custom function must have a predecessor!')
const executeFunc = async (state: ISeqAgentsState) => { const executeFunc = async (state: ISeqAgentsState) => {
const variables = await getVars(appDataSource, databaseEntities, nodeData) const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
const flow = { const flow = {
chatflowId: options.chatflowid, chatflowId: options.chatflowid,
sessionId: options.sessionId, sessionId: options.sessionId,
@@ -141,7 +141,8 @@ class ExecuteFlow_SeqAgents implements INode {
return returnData return returnData
} }
const chatflows = await appDataSource.getRepository(databaseEntities['ChatFlow']).find() const searchOptions = options.searchOptions || {}
const chatflows = await appDataSource.getRepository(databaseEntities['ChatFlow']).findBy(searchOptions)
for (let i = 0; i < chatflows.length; i += 1) { for (let i = 0; i < chatflows.length; i += 1) {
const data = { const data = {
@@ -189,7 +190,7 @@ class ExecuteFlow_SeqAgents implements INode {
const chatId = options.chatId const chatId = options.chatId
const executeFunc = async (state: ISeqAgentsState) => { const executeFunc = async (state: ISeqAgentsState) => {
const variables = await getVars(appDataSource, databaseEntities, nodeData) const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
let flowInput = '' let flowInput = ''
if (seqExecuteFlowInput === 'userQuestion') { if (seqExecuteFlowInput === 'userQuestion') {
@@ -223,7 +224,7 @@ class ExecuteFlow_SeqAgents implements INode {
} }
} }
const options = { const callOptions = {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -234,7 +235,7 @@ class ExecuteFlow_SeqAgents implements INode {
let sandbox: ICommonObject = { let sandbox: ICommonObject = {
$input: flowInput, $input: flowInput,
$callOptions: options, $callOptions: callOptions,
$callBody: body, $callBody: body,
util: undefined, util: undefined,
Symbol: undefined, Symbol: undefined,
@@ -668,7 +668,7 @@ const getReturnOutput = async (nodeData: INodeData, input: string, options: ICom
const updateStateMemory = nodeData.inputs?.updateStateMemory as string const updateStateMemory = nodeData.inputs?.updateStateMemory as string
const selectedTab = tabIdentifier ? tabIdentifier.split(`_${nodeData.id}`)[0] : 'updateStateMemoryUI' const selectedTab = tabIdentifier ? tabIdentifier.split(`_${nodeData.id}`)[0] : 'updateStateMemoryUI'
const variables = await getVars(appDataSource, databaseEntities, nodeData) const variables = await getVars(appDataSource, databaseEntities, nodeData, options)
const flow = { const flow = {
chatflowId: options.chatflowid, chatflowId: options.chatflowid,
@@ -721,7 +721,7 @@ const getReturnOutput = async (nodeData: INodeData, input: string, options: ICom
throw new Error(e) throw new Error(e)
} }
} else if (selectedTab === 'updateStateMemoryCode' && updateStateMemoryCode) { } else if (selectedTab === 'updateStateMemoryCode' && updateStateMemoryCode) {
const vm = await getVM(appDataSource, databaseEntities, nodeData, flow) const vm = await getVM(appDataSource, databaseEntities, nodeData, options, flow)
try { try {
const response = await vm.run(`module.exports = async function() {${updateStateMemoryCode}}()`, __dirname) const response = await vm.run(`module.exports = async function() {${updateStateMemoryCode}}()`, __dirname)
if (typeof response !== 'object') throw new Error('Return output must be an object') if (typeof response !== 'object') throw new Error('Return output must be an object')

Some files were not shown because too many files have changed in this diff Show More