Merge branch 'main' into chore/Remove-Deprecated-Nodes
|
|
@ -1,2 +0,0 @@
|
|||
node_modules
|
||||
dist
|
||||
|
|
@ -27,6 +27,7 @@ jobs:
|
|||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
check-latest: false
|
||||
cache: 'pnpm'
|
||||
- run: npm i -g pnpm
|
||||
- run: pnpm install
|
||||
- run: ./node_modules/.bin/cypress install
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
**/logs
|
||||
**/*.log
|
||||
|
||||
## pnpm
|
||||
.pnpm-store/
|
||||
|
||||
## build
|
||||
**/dist
|
||||
**/build
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
**/node_modules
|
||||
**/dist
|
||||
**/build
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
module.exports = {
|
||||
printWidth: 140,
|
||||
singleQuote: true,
|
||||
jsxSingleQuote: true,
|
||||
trailingComma: 'none',
|
||||
tabWidth: 4,
|
||||
semi: false,
|
||||
endOfLine: 'auto'
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
English | [中文](<./CODE_OF_CONDUCT-ZH.md>)
|
||||
English | [中文](./i18n/CODE_OF_CONDUCT-ZH.md)
|
||||
|
||||
## Our Pledge
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Contributing to Flowise
|
||||
|
||||
English | [中文](./CONTRIBUTING-ZH.md)
|
||||
English | [中文](./i18n/CONTRIBUTING-ZH.md)
|
||||
|
||||
We appreciate any form of contributions.
|
||||
|
||||
|
|
@ -120,40 +120,41 @@ Flowise has 3 different modules in a single mono repository.
|
|||
|
||||
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://docs.flowiseai.com/environment-variables)
|
||||
|
||||
| Variable | Description | Type | Default |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- |
|
||||
| PORT | The HTTP port Flowise runs on | Number | 3000 |
|
||||
| CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | 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 |
|
||||
| DEBUG | Print logs from components | Boolean | |
|
||||
| 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_JSON_SPACES | Spaces to beautify JSON logs | | 2 |
|
||||
| APIKEY_PATH | Location where api keys are saved | String | `your-path/Flowise/packages/server` |
|
||||
| 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 | |
|
||||
| DATABASE_TYPE | Type of database to store the flowise data | Enum String: `sqlite`, `mysql`, `postgres` | `sqlite` |
|
||||
| DATABASE_PATH | Location where database is saved (When DATABASE_TYPE is sqlite) | String | `your-home-dir/.flowise` |
|
||||
| DATABASE_HOST | Host URL or IP address (When DATABASE_TYPE is not sqlite) | String | |
|
||||
| DATABASE_PORT | Database port (When DATABASE_TYPE is not sqlite) | String | |
|
||||
| DATABASE_USER | Database username (When DATABASE_TYPE is not sqlite) | String | |
|
||||
| DATABASE_PASSWORD | Database password (When DATABASE_TYPE is not sqlite) | String | |
|
||||
| DATABASE_NAME | Database name (When DATABASE_TYPE is not sqlite) | String | |
|
||||
| DATABASE_SSL_KEY_BASE64 | Database SSL client cert in base64 (takes priority over DATABASE_SSL) | Boolean | false |
|
||||
| DATABASE_SSL | Database connection overssl (When DATABASE_TYPE is postgre) | Boolean | false |
|
||||
| SECRETKEY_PATH | Location where encryption key (used to encrypt/decrypt credentials) is saved | String | `your-path/Flowise/packages/server` |
|
||||
| FLOWISE_SECRETKEY_OVERWRITE | Encryption key to be used instead of the key stored in SECRETKEY_PATH | String |
|
||||
| DISABLE_FLOWISE_TELEMETRY | Turn off telemetry | Boolean |
|
||||
| MODEL_LIST_CONFIG_JSON | File path to load list of models from your local config file | String | `/your_model_list_config_file_path` |
|
||||
| STORAGE_TYPE | Type of storage for uploaded files. default is `local` | Enum String: `s3`, `local` | `local` |
|
||||
| BLOB_STORAGE_PATH | Local folder path where uploaded files are stored when `STORAGE_TYPE` is `local` | String | `your-home-dir/.flowise/storage` |
|
||||
| S3_STORAGE_BUCKET_NAME | Bucket name to hold the uploaded files when `STORAGE_TYPE` is `s3` | String | |
|
||||
| S3_STORAGE_ACCESS_KEY_ID | AWS Access Key | String | |
|
||||
| S3_STORAGE_SECRET_ACCESS_KEY | AWS Secret Key | String | |
|
||||
| S3_STORAGE_REGION | Region for S3 bucket | String | |
|
||||
| Variable | Description | Type | Default |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- |
|
||||
| PORT | The HTTP port Flowise runs on | Number | 3000 |
|
||||
| CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | 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 |
|
||||
| DISABLE_CHATFLOW_REUSE | Forces the creation of a new ChatFlow for each call instead of reusing existing ones from cache | Boolean | |
|
||||
| DEBUG | Print logs from components | Boolean | |
|
||||
| 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_JSON_SPACES | Spaces to beautify JSON logs | | 2 |
|
||||
| APIKEY_PATH | Location where api keys are saved | String | `your-path/Flowise/packages/server` |
|
||||
| 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 | |
|
||||
| DATABASE_TYPE | Type of database to store the flowise data | Enum String: `sqlite`, `mysql`, `postgres` | `sqlite` |
|
||||
| DATABASE_PATH | Location where database is saved (When DATABASE_TYPE is sqlite) | String | `your-home-dir/.flowise` |
|
||||
| DATABASE_HOST | Host URL or IP address (When DATABASE_TYPE is not sqlite) | String | |
|
||||
| DATABASE_PORT | Database port (When DATABASE_TYPE is not sqlite) | String | |
|
||||
| DATABASE_USER | Database username (When DATABASE_TYPE is not sqlite) | String | |
|
||||
| DATABASE_PASSWORD | Database password (When DATABASE_TYPE is not sqlite) | String | |
|
||||
| DATABASE_NAME | Database name (When DATABASE_TYPE is not sqlite) | String | |
|
||||
| DATABASE_SSL_KEY_BASE64 | Database SSL client cert in base64 (takes priority over DATABASE_SSL) | Boolean | false |
|
||||
| DATABASE_SSL | Database connection overssl (When DATABASE_TYPE is postgre) | Boolean | false |
|
||||
| SECRETKEY_PATH | Location where encryption key (used to encrypt/decrypt credentials) is saved | String | `your-path/Flowise/packages/server` |
|
||||
| FLOWISE_SECRETKEY_OVERWRITE | Encryption key to be used instead of the key stored in SECRETKEY_PATH | String |
|
||||
| DISABLE_FLOWISE_TELEMETRY | Turn off telemetry | Boolean |
|
||||
| MODEL_LIST_CONFIG_JSON | File path to load list of models from your local config file | String | `/your_model_list_config_file_path` |
|
||||
| STORAGE_TYPE | Type of storage for uploaded files. default is `local` | Enum String: `s3`, `local` | `local` |
|
||||
| BLOB_STORAGE_PATH | Local folder path where uploaded files are stored when `STORAGE_TYPE` is `local` | String | `your-home-dir/.flowise/storage` |
|
||||
| S3_STORAGE_BUCKET_NAME | Bucket name to hold the uploaded files when `STORAGE_TYPE` is `s3` | String | |
|
||||
| S3_STORAGE_ACCESS_KEY_ID | AWS Access Key | String | |
|
||||
| S3_STORAGE_SECRET_ACCESS_KEY | AWS Secret Key | String | |
|
||||
| S3_STORAGE_REGION | Region for S3 bucket | String | |
|
||||
|
||||
You can also specify the env variables when using `npx`. For example:
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
[](https://star-history.com/#FlowiseAI/Flowise)
|
||||
[](https://github.com/FlowiseAI/Flowise/fork)
|
||||
|
||||
English | [中文](./README-ZH.md) | [日本語](./README-JA.md) | [한국어](./README-KR.md)
|
||||
English | [中文](./i18n/README-ZH.md) | [日本語](./i18n/README-JA.md) | [한국어](./i18n/README-KR.md)
|
||||
|
||||
<h3>Drag & drop UI to build your customized LLM flow</h3>
|
||||
<a href="https://github.com/FlowiseAI/Flowise">
|
||||
|
|
@ -44,9 +44,9 @@ Download and Install [NodeJS](https://nodejs.org/en/download) >= 18.15.0
|
|||
|
||||
1. Go to `docker` folder at the root of the project
|
||||
2. Copy `.env.example` file, paste it into the same location, and rename to `.env`
|
||||
3. `docker-compose up -d`
|
||||
3. `docker compose up -d`
|
||||
4. Open [http://localhost:3000](http://localhost:3000)
|
||||
5. You can bring the containers down by `docker-compose stop`
|
||||
5. You can bring the containers down by `docker compose stop`
|
||||
|
||||
### Docker Image
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@babel/preset-typescript',
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
node: 'current'
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
|
@ -23,6 +23,8 @@ BLOB_STORAGE_PATH=/root/.flowise/storage
|
|||
# FLOWISE_SECRETKEY_OVERWRITE=myencryptionkey
|
||||
# FLOWISE_FILE_SIZE_LIMIT=50mb
|
||||
|
||||
# DISABLE_CHATFLOW_REUSE=true
|
||||
|
||||
# DEBUG=true
|
||||
# LOG_LEVEL=info (error | warn | info | verbose | debug)
|
||||
# TOOL_FUNCTION_BUILTIN_DEP=crypto,fs
|
||||
|
|
|
|||
|
|
@ -1,21 +1,25 @@
|
|||
FROM node:20-alpine
|
||||
# Stage 1: Build stage
|
||||
FROM node:20-alpine as build
|
||||
|
||||
USER root
|
||||
|
||||
RUN apk add --no-cache git
|
||||
RUN apk add --no-cache python3 py3-pip make g++
|
||||
# needed for pdfjs-dist
|
||||
RUN apk add --no-cache build-base cairo-dev pango-dev
|
||||
|
||||
# Install Chromium
|
||||
RUN apk add --no-cache chromium
|
||||
|
||||
# Skip downloading Chrome for Puppeteer (saves build time)
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD=true
|
||||
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
||||
|
||||
# You can install a specific version like: flowise@1.0.0
|
||||
# Install latest Flowise globally (specific version can be set: flowise@1.0.0)
|
||||
RUN npm install -g flowise
|
||||
|
||||
WORKDIR /data
|
||||
# Stage 2: Runtime stage
|
||||
FROM node:20-alpine
|
||||
|
||||
CMD "flowise"
|
||||
# Install runtime dependencies
|
||||
RUN apk add --no-cache chromium git python3 py3-pip make g++ build-base cairo-dev pango-dev
|
||||
|
||||
# Set the environment variable for Puppeteer to find Chromium
|
||||
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
||||
|
||||
# Copy Flowise from the build stage
|
||||
COPY --from=build /usr/local/lib/node_modules /usr/local/lib/node_modules
|
||||
COPY --from=build /usr/local/bin /usr/local/bin
|
||||
|
||||
ENTRYPOINT ["flowise", "start"]
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ Starts Flowise from [DockerHub Image](https://hub.docker.com/r/flowiseai/flowise
|
|||
## Usage
|
||||
|
||||
1. Create `.env` file and specify the `PORT` (refer to `.env.example`)
|
||||
2. `docker-compose up -d`
|
||||
2. `docker compose up -d`
|
||||
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
|
||||
|
||||
|
|
@ -19,9 +19,9 @@ Starts Flowise from [DockerHub Image](https://hub.docker.com/r/flowiseai/flowise
|
|||
- FLOWISE_USERNAME=${FLOWISE_USERNAME}
|
||||
- FLOWISE_PASSWORD=${FLOWISE_PASSWORD}
|
||||
```
|
||||
3. `docker-compose up -d`
|
||||
3. `docker compose up -d`
|
||||
4. Open [http://localhost:3000](http://localhost:3000)
|
||||
5. You can bring the containers down by `docker-compose stop`
|
||||
5. You can bring the containers down by `docker compose stop`
|
||||
|
||||
## 🌱 Env Variables
|
||||
|
||||
|
|
|
|||
|
|
@ -33,4 +33,4 @@ services:
|
|||
- '${PORT}:${PORT}'
|
||||
volumes:
|
||||
- ~/.flowise:/root/.flowise
|
||||
command: /bin/sh -c "sleep 3; flowise start"
|
||||
entrypoint: /bin/sh -c "sleep 3; flowise start"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# 贡献者公约行为准则
|
||||
|
||||
[English](<./CODE_OF_CONDUCT.md>) | 中文
|
||||
[English](../CODE_OF_CONDUCT.md) | 中文
|
||||
|
||||
## 我们的承诺
|
||||
|
||||
|
|
@ -44,6 +44,6 @@
|
|||
|
||||
## 归属
|
||||
|
||||
该行为准则的内容来自于[贡献者公约](http://contributor-covenant.org/)1.4版,可在[http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4)上获取。
|
||||
该行为准则的内容来自于[贡献者公约](http://contributor-covenant.org/)1.4 版,可在[http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4)上获取。
|
||||
|
||||
[主页]: http://contributor-covenant.org
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# 贡献给 Flowise
|
||||
|
||||
[English](./CONTRIBUTING.md) | 中文
|
||||
[English](../CONTRIBUTING.md) | 中文
|
||||
|
||||
我们欢迎任何形式的贡献。
|
||||
|
||||
|
|
@ -118,35 +118,36 @@ Flowise 在一个单一的单体存储库中有 3 个不同的模块。
|
|||
|
||||
Flowise 支持不同的环境变量来配置您的实例。您可以在 `packages/server` 文件夹中的 `.env` 文件中指定以下变量。阅读[更多信息](https://docs.flowiseai.com/environment-variables)
|
||||
|
||||
| 变量名 | 描述 | 类型 | 默认值 |
|
||||
| ---------------------------- | ------------------------------------------------------- | ----------------------------------------------- | ----------------------------------- |
|
||||
| PORT | Flowise 运行的 HTTP 端口 | 数字 | 3000 |
|
||||
| FLOWISE_USERNAME | 登录用户名 | 字符串 | |
|
||||
| FLOWISE_PASSWORD | 登录密码 | 字符串 | |
|
||||
| FLOWISE_FILE_SIZE_LIMIT | 上传文件大小限制 | 字符串 | 50mb |
|
||||
| DEBUG | 打印组件的日志 | 布尔值 | |
|
||||
| LOG_PATH | 存储日志文件的位置 | 字符串 | `your-path/Flowise/logs` |
|
||||
| LOG_LEVEL | 日志的不同级别 | 枚举字符串: `error`, `info`, `verbose`, `debug` | `info` |
|
||||
| APIKEY_PATH | 存储 API 密钥的位置 | 字符串 | `your-path/Flowise/packages/server` |
|
||||
| TOOL_FUNCTION_BUILTIN_DEP | 用于工具函数的 NodeJS 内置模块 | 字符串 | |
|
||||
| TOOL_FUNCTION_EXTERNAL_DEP | 用于工具函数的外部模块 | 字符串 | |
|
||||
| DATABASE_TYPE | 存储 flowise 数据的数据库类型 | 枚举字符串: `sqlite`, `mysql`, `postgres` | `sqlite` |
|
||||
| DATABASE_PATH | 数据库保存的位置(当 DATABASE_TYPE 是 sqlite 时) | 字符串 | `your-home-dir/.flowise` |
|
||||
| DATABASE_HOST | 主机 URL 或 IP 地址(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
|
||||
| DATABASE_PORT | 数据库端口(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
|
||||
| DATABASE_USERNAME | 数据库用户名(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
|
||||
| DATABASE_PASSWORD | 数据库密码(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
|
||||
| DATABASE_NAME | 数据库名称(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
|
||||
| SECRETKEY_PATH | 保存加密密钥(用于加密/解密凭据)的位置 | 字符串 | `your-path/Flowise/packages/server` |
|
||||
| FLOWISE_SECRETKEY_OVERWRITE | 加密密钥用于替代存储在 SECRETKEY_PATH 中的密钥 | 字符串 |
|
||||
| DISABLE_FLOWISE_TELEMETRY | 关闭遥测 | 字符串 |
|
||||
| MODEL_LIST_CONFIG_JSON | 加载模型的位置 | 字符 | `/your_model_list_config_file_path` |
|
||||
| STORAGE_TYPE | 上传文件的存储类型 | 枚举字符串: `local`, `s3` | `local` |
|
||||
| BLOB_STORAGE_PATH | 上传文件存储的本地文件夹路径, 当`STORAGE_TYPE`是`local` | 字符串 | `your-home-dir/.flowise/storage` |
|
||||
| S3_STORAGE_BUCKET_NAME | S3 存储文件夹路径, 当`STORAGE_TYPE`是`s3` | 字符串 | |
|
||||
| S3_STORAGE_ACCESS_KEY_ID | AWS 访问密钥 (Access Key) | 字符串 | |
|
||||
| S3_STORAGE_SECRET_ACCESS_KEY | AWS 密钥 (Secret Key) | 字符串 | |
|
||||
| S3_STORAGE_REGION | S3 存储地区 | 字符串 | |
|
||||
| 变量名 | 描述 | 类型 | 默认值 |
|
||||
| ---------------------------- | -------------------------------------------------------------------- | ----------------------------------------------- | ----------------------------------- |
|
||||
| PORT | Flowise 运行的 HTTP 端口 | 数字 | 3000 |
|
||||
| FLOWISE_USERNAME | 登录用户名 | 字符串 | |
|
||||
| FLOWISE_PASSWORD | 登录密码 | 字符串 | |
|
||||
| FLOWISE_FILE_SIZE_LIMIT | 上传文件大小限制 | 字符串 | 50mb |
|
||||
| DISABLE_CHATFLOW_REUSE | 强制为每次调用创建一个新的 ChatFlow,而不是重用缓存中的现有 ChatFlow | 布尔值 | |
|
||||
| DEBUG | 打印组件的日志 | 布尔值 | |
|
||||
| LOG_PATH | 存储日志文件的位置 | 字符串 | `your-path/Flowise/logs` |
|
||||
| LOG_LEVEL | 日志的不同级别 | 枚举字符串: `error`, `info`, `verbose`, `debug` | `info` |
|
||||
| APIKEY_PATH | 存储 API 密钥的位置 | 字符串 | `your-path/Flowise/packages/server` |
|
||||
| TOOL_FUNCTION_BUILTIN_DEP | 用于工具函数的 NodeJS 内置模块 | 字符串 | |
|
||||
| TOOL_FUNCTION_EXTERNAL_DEP | 用于工具函数的外部模块 | 字符串 | |
|
||||
| DATABASE_TYPE | 存储 flowise 数据的数据库类型 | 枚举字符串: `sqlite`, `mysql`, `postgres` | `sqlite` |
|
||||
| DATABASE_PATH | 数据库保存的位置(当 DATABASE_TYPE 是 sqlite 时) | 字符串 | `your-home-dir/.flowise` |
|
||||
| DATABASE_HOST | 主机 URL 或 IP 地址(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
|
||||
| DATABASE_PORT | 数据库端口(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
|
||||
| DATABASE_USERNAME | 数据库用户名(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
|
||||
| DATABASE_PASSWORD | 数据库密码(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
|
||||
| DATABASE_NAME | 数据库名称(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
|
||||
| SECRETKEY_PATH | 保存加密密钥(用于加密/解密凭据)的位置 | 字符串 | `your-path/Flowise/packages/server` |
|
||||
| FLOWISE_SECRETKEY_OVERWRITE | 加密密钥用于替代存储在 SECRETKEY_PATH 中的密钥 | 字符串 |
|
||||
| DISABLE_FLOWISE_TELEMETRY | 关闭遥测 | 字符串 |
|
||||
| MODEL_LIST_CONFIG_JSON | 加载模型的位置 | 字符 | `/your_model_list_config_file_path` |
|
||||
| STORAGE_TYPE | 上传文件的存储类型 | 枚举字符串: `local`, `s3` | `local` |
|
||||
| BLOB_STORAGE_PATH | 上传文件存储的本地文件夹路径, 当`STORAGE_TYPE`是`local` | 字符串 | `your-home-dir/.flowise/storage` |
|
||||
| S3_STORAGE_BUCKET_NAME | S3 存储文件夹路径, 当`STORAGE_TYPE`是`s3` | 字符串 | |
|
||||
| S3_STORAGE_ACCESS_KEY_ID | AWS 访问密钥 (Access Key) | 字符串 | |
|
||||
| S3_STORAGE_SECRET_ACCESS_KEY | AWS 密钥 (Secret Key) | 字符串 | |
|
||||
| S3_STORAGE_REGION | S3 存储地区 | 字符串 | |
|
||||
|
||||
您也可以在使用 `npx` 时指定环境变量。例如:
|
||||
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
[](https://star-history.com/#FlowiseAI/Flowise)
|
||||
[](https://github.com/FlowiseAI/Flowise/fork)
|
||||
|
||||
[English](./README.md) | [中文](./README-ZH.md) | 日本語 | [한국어](./README-KR.md)
|
||||
[English](../README.md) | [中文](./README-ZH.md) | 日本語 | [한국어](./README-KR.md)
|
||||
|
||||
<h3>ドラッグ&ドロップでカスタマイズした LLM フローを構築できる UI</h3>
|
||||
<a href="https://github.com/FlowiseAI/Flowise">
|
||||
|
|
@ -44,9 +44,9 @@
|
|||
|
||||
1. プロジェクトのルートにある `docker` フォルダに移動する
|
||||
2. `.env.example` ファイルをコピーして同じ場所に貼り付け、名前を `.env` に変更する
|
||||
3. `docker-compose up -d`
|
||||
3. `docker compose up -d`
|
||||
4. [http://localhost:3000](http://localhost:3000) を開く
|
||||
5. コンテナを停止するには、`docker-compose stop` を使用します
|
||||
5. コンテナを停止するには、`docker compose stop` を使用します
|
||||
|
||||
### Docker Image
|
||||
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
[](https://star-history.com/#FlowiseAI/Flowise)
|
||||
[](https://github.com/FlowiseAI/Flowise/fork)
|
||||
|
||||
English | [中文](./README-ZH.md) | [日本語](./README-JA.md) | 한국어
|
||||
[English](../README.md) | [中文](./README-ZH.md) | [日本語](./README-JA.md) | 한국어
|
||||
|
||||
<h3>드래그 앤 드롭 UI로 맞춤형 LLM 플로우 구축하기</h3>
|
||||
<a href="https://github.com/FlowiseAI/Flowise">
|
||||
|
|
@ -44,9 +44,9 @@ English | [中文](./README-ZH.md) | [日本語](./README-JA.md) | 한국어
|
|||
|
||||
1. 프로젝트의 최상위(root) 디렉토리에 있는 `docker` 폴더로 이동하세요.
|
||||
2. `.env.example` 파일을 복사한 후, 같은 경로에 붙여넣기 한 다음, `.env`로 이름을 변경합니다.
|
||||
3. `docker-compose up -d` 실행
|
||||
3. `docker compose up -d` 실행
|
||||
4. [http://localhost:3000](http://localhost:3000) URL 열기
|
||||
5. `docker-compose stop` 명령어를 통해 컨테이너를 종료시킬 수 있습니다.
|
||||
5. `docker compose stop` 명령어를 통해 컨테이너를 종료시킬 수 있습니다.
|
||||
|
||||
### 도커 이미지 활용
|
||||
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
[](https://star-history.com/#FlowiseAI/Flowise)
|
||||
[](https://github.com/FlowiseAI/Flowise/fork)
|
||||
|
||||
[English](./README.md) | 中文 | [日本語](./README-JA.md) | [한국어](./README-KR.md)
|
||||
[English](../README.md) | 中文 | [日本語](./README-JA.md) | [한국어](./README-KR.md)
|
||||
|
||||
<h3>拖放界面构建定制化的LLM流程</h3>
|
||||
<a href="https://github.com/FlowiseAI/Flowise">
|
||||
|
|
@ -44,9 +44,9 @@
|
|||
|
||||
1. 进入项目根目录下的 `docker` 文件夹
|
||||
2. 创建 `.env` 文件并指定 `PORT`(参考 `.env.example`)
|
||||
3. 运行 `docker-compose up -d`
|
||||
3. 运行 `docker compose up -d`
|
||||
4. 打开 [http://localhost:3000](http://localhost:3000)
|
||||
5. 可以通过 `docker-compose stop` 停止容器
|
||||
5. 可以通过 `docker compose stop` 停止容器
|
||||
|
||||
### Docker 镜像
|
||||
|
||||
32
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "flowise",
|
||||
"version": "1.8.1",
|
||||
"version": "1.8.3",
|
||||
"private": true,
|
||||
"homepage": "https://flowiseai.com",
|
||||
"workspaces": [
|
||||
|
|
@ -65,6 +65,34 @@
|
|||
"resolutions": {
|
||||
"@qdrant/openapi-typescript-fetch": "1.2.1",
|
||||
"@google/generative-ai": "^0.7.0",
|
||||
"openai": "4.38.3"
|
||||
"openai": "4.51.0"
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"**/dist",
|
||||
"**/node_modules",
|
||||
"**/build",
|
||||
"**/package-lock.json"
|
||||
],
|
||||
"prettier": {
|
||||
"printWidth": 140,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"endOfLine": "auto"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"@babel/preset-typescript",
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
import { INodeParams, INodeCredential } from '../src/Interface'
|
||||
|
||||
class BaiduApi implements INodeCredential {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'Baidu API'
|
||||
this.name = 'baiduApi'
|
||||
this.version = 1.0
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Baidu Api Key',
|
||||
name: 'baiduApiKey',
|
||||
type: 'password'
|
||||
},
|
||||
{
|
||||
label: 'Baidu Secret Key',
|
||||
name: 'baiduSecretKey',
|
||||
type: 'password'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { credClass: BaiduApi }
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { INodeParams, INodeCredential } from '../src/Interface'
|
||||
|
||||
class FireCrawlApiCredential implements INodeCredential {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'FireCrawl API'
|
||||
this.name = 'fireCrawlApi'
|
||||
this.version = 1.0
|
||||
this.description =
|
||||
'You can find the FireCrawl API token on your <a target="_blank" href="https://www.firecrawl.dev/">FireCrawl account</a> page.'
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'FireCrawl API',
|
||||
name: 'firecrawlApiToken',
|
||||
type: 'password'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { credClass: FireCrawlApiCredential }
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { INodeParams, INodeCredential } from '../src/Interface'
|
||||
|
||||
class FireworksApi implements INodeCredential {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'Fireworks API'
|
||||
this.name = 'fireworksApi'
|
||||
this.version = 1.0
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Fireworks Api Key',
|
||||
name: 'fireworksApiKey',
|
||||
type: 'password'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { credClass: FireworksApi }
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import { INodeParams, INodeCredential } from '../src/Interface'
|
||||
|
||||
class LangWatchApi implements INodeCredential {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'LangWatch API'
|
||||
this.name = 'langwatchApi'
|
||||
this.version = 1.0
|
||||
this.description =
|
||||
'Refer to <a target="_blank" href="https://docs.langwatch.ai/integration/python/guide">integration guide</a> on how to get API keys on LangWatch'
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'API Key',
|
||||
name: 'langWatchApiKey',
|
||||
type: 'password',
|
||||
placeholder: '<LANGWATCH_API_KEY>'
|
||||
},
|
||||
{
|
||||
label: 'Endpoint',
|
||||
name: 'langWatchEndpoint',
|
||||
type: 'string',
|
||||
default: 'https://app.langwatch.ai'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { credClass: LangWatchApi }
|
||||
|
|
@ -10,12 +10,26 @@ class OpenSearchUrl implements INodeCredential {
|
|||
constructor() {
|
||||
this.label = 'OpenSearch'
|
||||
this.name = 'openSearchUrl'
|
||||
this.version = 1.0
|
||||
this.version = 2.0
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'OpenSearch Url',
|
||||
name: 'openSearchUrl',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
label: 'User',
|
||||
name: 'user',
|
||||
type: 'string',
|
||||
placeholder: '<OPENSEARCH_USERNAME>',
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Password',
|
||||
name: 'password',
|
||||
type: 'password',
|
||||
placeholder: '<OPENSEARCH_PASSWORD>',
|
||||
optional: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
import { INodeParams, INodeCredential } from '../src/Interface'
|
||||
|
||||
class SpiderApiCredential implements INodeCredential {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'Spider API'
|
||||
this.name = 'spiderApi'
|
||||
this.version = 1.0
|
||||
this.description = 'Get your API key from the <a target="_blank" href="https://spider.cloud">Spider</a> dashboard.'
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Spider API Key',
|
||||
name: 'spiderApiKey',
|
||||
type: 'password'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { credClass: SpiderApiCredential }
|
||||
|
|
@ -8,6 +8,11 @@
|
|||
"name": "anthropic.claude-3-haiku-20240307-v1:0",
|
||||
"description": "Image to text, conversation, chat optimized"
|
||||
},
|
||||
{
|
||||
"label": "anthropic.claude-3.5-sonnet",
|
||||
"name": "anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||
"description": "3.5 version of Claude Sonnet model"
|
||||
},
|
||||
{
|
||||
"label": "anthropic.claude-3-sonnet",
|
||||
"name": "anthropic.claude-3-sonnet-20240229-v1:0",
|
||||
|
|
@ -215,6 +220,10 @@
|
|||
{
|
||||
"name": "azureChatOpenAI",
|
||||
"models": [
|
||||
{
|
||||
"label": "gpt-4o",
|
||||
"name": "gpt-4o"
|
||||
},
|
||||
{
|
||||
"label": "gpt-4",
|
||||
"name": "gpt-4"
|
||||
|
|
@ -240,6 +249,10 @@
|
|||
{
|
||||
"name": "azureChatOpenAI_LlamaIndex",
|
||||
"models": [
|
||||
{
|
||||
"label": "gpt-4o",
|
||||
"name": "gpt-4o"
|
||||
},
|
||||
{
|
||||
"label": "gpt-4",
|
||||
"name": "gpt-4"
|
||||
|
|
@ -283,6 +296,11 @@
|
|||
"name": "claude-3-opus-20240229",
|
||||
"description": "Most powerful model for highly complex tasks"
|
||||
},
|
||||
{
|
||||
"label": "claude-3.5-sonnet",
|
||||
"name": "claude-3-5-sonnet-20240620",
|
||||
"description": "3.5 version of Claude Sonnet model"
|
||||
},
|
||||
{
|
||||
"label": "claude-3-sonnet",
|
||||
"name": "claude-3-sonnet-20240229",
|
||||
|
|
@ -1029,22 +1047,42 @@
|
|||
{
|
||||
"label": "voyage-2",
|
||||
"name": "voyage-2",
|
||||
"description": "Base generalist embedding model optimized for both latency and quality"
|
||||
"description": "General-purpose embedding model optimized for a balance between cost, latency, and retrieval quality."
|
||||
},
|
||||
{
|
||||
"label": "voyage-code-2",
|
||||
"name": "voyage-code-2",
|
||||
"description": "Optimized for code retrieval"
|
||||
"description": "Optimized for code retrieval."
|
||||
},
|
||||
{
|
||||
"label": "voyage-finance-2",
|
||||
"name": "voyage-finance-2",
|
||||
"description": "Optimized for finance retrieval and RAG."
|
||||
},
|
||||
{
|
||||
"label": "voyage-large-2",
|
||||
"name": "voyage-large-2",
|
||||
"description": "Powerful generalist embedding model"
|
||||
"description": "General-purpose embedding model that is optimized for retrieval quality."
|
||||
},
|
||||
{
|
||||
"label": "voyage-large-2-instruct",
|
||||
"name": "voyage-large-2-instruct",
|
||||
"description": "Instruction-tuned general-purpose embedding model optimized for clustering, classification, and retrieval."
|
||||
},
|
||||
{
|
||||
"label": "voyage-law-2",
|
||||
"name": "voyage-law-2",
|
||||
"description": "Optimized for legal and long-context retrieval and RAG. Also improved performance across all domains."
|
||||
},
|
||||
{
|
||||
"label": "voyage-lite-02-instruct",
|
||||
"name": "voyage-lite-02-instruct",
|
||||
"description": "Instruction-tuned for classification, clustering, and sentence textual similarity tasks"
|
||||
},
|
||||
{
|
||||
"label": "voyage-multilingual-2",
|
||||
"name": "voyage-multilingual-2",
|
||||
"description": "Optimized for multilingual retrieval and RAG."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class OpenAIAssistant_Agents implements INode {
|
|||
constructor() {
|
||||
this.label = 'OpenAI Assistant'
|
||||
this.name = 'openAIAssistant'
|
||||
this.version = 3.0
|
||||
this.version = 4.0
|
||||
this.type = 'OpenAIAssistant'
|
||||
this.category = 'Agents'
|
||||
this.icon = 'assistant.svg'
|
||||
|
|
@ -54,6 +54,25 @@ class OpenAIAssistant_Agents implements INode {
|
|||
optional: true,
|
||||
list: true
|
||||
},
|
||||
{
|
||||
label: 'Tool Choice',
|
||||
name: 'toolChoice',
|
||||
type: 'string',
|
||||
description:
|
||||
'Controls which (if any) tool is called by the model. Can be "none", "auto", "required", or the name of a tool. Refer <a href="https://platform.openai.com/docs/api-reference/runs/createRun#runs-createrun-tool_choice" target="_blank">here</a> for more information',
|
||||
placeholder: 'file_search',
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'Parallel Tool Calls',
|
||||
name: 'parallelToolCalls',
|
||||
type: 'boolean',
|
||||
description: 'Whether to enable parallel function calling during tool use. Defaults to true',
|
||||
default: true,
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'Disable File Download',
|
||||
name: 'disableFileDownload',
|
||||
|
|
@ -138,10 +157,14 @@ class OpenAIAssistant_Agents implements INode {
|
|||
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||
options.logger.info(`Clearing OpenAI Thread ${sessionId}`)
|
||||
try {
|
||||
if (sessionId) await openai.beta.threads.del(sessionId)
|
||||
options.logger.info(`Successfully cleared OpenAI Thread ${sessionId}`)
|
||||
if (sessionId && sessionId.startsWith('thread_')) {
|
||||
await openai.beta.threads.del(sessionId)
|
||||
options.logger.info(`Successfully cleared OpenAI Thread ${sessionId}`)
|
||||
} else {
|
||||
options.logger.error(`Error clearing OpenAI Thread ${sessionId}`)
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(e)
|
||||
options.logger.error(`Error clearing OpenAI Thread ${sessionId}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -151,6 +174,8 @@ class OpenAIAssistant_Agents implements INode {
|
|||
const databaseEntities = options.databaseEntities as IDatabaseEntity
|
||||
const disableFileDownload = nodeData.inputs?.disableFileDownload as boolean
|
||||
const moderations = nodeData.inputs?.inputModeration as Moderation[]
|
||||
const _toolChoice = nodeData.inputs?.toolChoice as string
|
||||
const parallelToolCalls = nodeData.inputs?.parallelToolCalls as boolean
|
||||
const isStreaming = options.socketIO && options.socketIOClientId
|
||||
const socketIO = isStreaming ? options.socketIO : undefined
|
||||
const socketIOClientId = isStreaming ? options.socketIOClientId : ''
|
||||
|
|
@ -269,10 +294,25 @@ class OpenAIAssistant_Agents implements INode {
|
|||
let runThreadId = ''
|
||||
let isStreamingStarted = false
|
||||
|
||||
let toolChoice: any
|
||||
if (_toolChoice) {
|
||||
if (_toolChoice === 'file_search') {
|
||||
toolChoice = { type: 'file_search' }
|
||||
} else if (_toolChoice === 'code_interpreter') {
|
||||
toolChoice = { type: 'code_interpreter' }
|
||||
} else if (_toolChoice === 'none' || _toolChoice === 'auto' || _toolChoice === 'required') {
|
||||
toolChoice = _toolChoice
|
||||
} else {
|
||||
toolChoice = { type: 'function', function: { name: _toolChoice } }
|
||||
}
|
||||
}
|
||||
|
||||
if (isStreaming) {
|
||||
const streamThread = await openai.beta.threads.runs.create(threadId, {
|
||||
assistant_id: retrievedAssistant.id,
|
||||
stream: true
|
||||
stream: true,
|
||||
tool_choice: toolChoice,
|
||||
parallel_tool_calls: parallelToolCalls
|
||||
})
|
||||
|
||||
for await (const event of streamThread) {
|
||||
|
|
@ -595,7 +635,9 @@ class OpenAIAssistant_Agents implements INode {
|
|||
|
||||
// Polling run status
|
||||
const runThread = await openai.beta.threads.runs.create(threadId, {
|
||||
assistant_id: retrievedAssistant.id
|
||||
assistant_id: retrievedAssistant.id,
|
||||
tool_choice: toolChoice,
|
||||
parallel_tool_calls: parallelToolCalls
|
||||
})
|
||||
runThreadId = runThread.id
|
||||
let state = await promise(threadId, runThread.id)
|
||||
|
|
@ -608,7 +650,9 @@ class OpenAIAssistant_Agents implements INode {
|
|||
if (retries > 0) {
|
||||
retries -= 1
|
||||
const newRunThread = await openai.beta.threads.runs.create(threadId, {
|
||||
assistant_id: retrievedAssistant.id
|
||||
assistant_id: retrievedAssistant.id,
|
||||
tool_choice: toolChoice,
|
||||
parallel_tool_calls: parallelToolCalls
|
||||
})
|
||||
runThreadId = newRunThread.id
|
||||
state = await promise(threadId, newRunThread.id)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="38" height="52" viewBox="0 0 38 52" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 12.383V41.035C0 41.392 0.190002 41.723 0.500002 41.901L17.095 51.481C17.25 51.571 17.422 51.616 17.595 51.616C17.768 51.616 17.94 51.571 18.095 51.481L37.279 40.409C37.589 40.23 37.779 39.9 37.779 39.543V10.887C37.779 10.53 37.589 10.199 37.279 10.021L31.168 6.49498C31.014 6.40598 30.841 6.36098 30.669 6.36098C30.496 6.36098 30.323 6.40498 30.169 6.49498L27.295 8.15398V4.83698C27.295 4.47998 27.105 4.14898 26.795 3.97098L20.684 0.441982C20.529 0.352982 20.357 0.307983 20.184 0.307983C20.011 0.307983 19.839 0.352982 19.684 0.441982L13.781 3.85098C13.471 4.02998 13.281 4.35998 13.281 4.71698V12.157L12.921 12.365V11.872C12.921 11.515 12.731 11.185 12.421 11.006L7.405 8.10698C7.25 8.01798 7.077 7.97298 6.905 7.97298C6.733 7.97298 6.56 8.01798 6.405 8.10698L0.501001 11.517C0.191001 11.695 0 12.025 0 12.383ZM1.5 13.248L5.519 15.566V23.294C5.519 23.304 5.524 23.313 5.525 23.323C5.526 23.345 5.529 23.366 5.534 23.388C5.538 23.411 5.544 23.433 5.552 23.455C5.559 23.476 5.567 23.496 5.577 23.516C5.582 23.525 5.581 23.535 5.587 23.544C5.591 23.551 5.6 23.554 5.604 23.561C5.617 23.581 5.63 23.6 5.646 23.618C5.669 23.644 5.695 23.665 5.724 23.686C5.741 23.698 5.751 23.716 5.77 23.727L11.236 26.886C11.243 26.89 11.252 26.888 11.26 26.892C11.328 26.927 11.402 26.952 11.484 26.952C11.566 26.952 11.641 26.928 11.709 26.893C11.728 26.883 11.743 26.87 11.761 26.858C11.812 26.823 11.855 26.781 11.89 26.731C11.898 26.719 11.911 26.715 11.919 26.702C11.924 26.693 11.924 26.682 11.929 26.674C11.944 26.644 11.951 26.613 11.96 26.58C11.969 26.547 11.978 26.515 11.98 26.481C11.98 26.471 11.986 26.462 11.986 26.452V20.138V19.302L17.096 22.251V49.749L1.5 40.747V13.248ZM35.778 10.887L30.879 13.718L25.768 10.766L26.544 10.317L30.668 7.93698L35.778 10.887ZM25.293 4.83598L20.391 7.66498L15.281 4.71598L20.183 1.88398L25.293 4.83598ZM10.92 11.872L6.019 14.701L2.001 12.383L6.904 9.55098L10.92 11.872ZM20.956 16.51L24.268 14.601V18.788C24.268 18.809 24.278 18.827 24.28 18.848C24.284 18.883 24.29 18.917 24.301 18.95C24.311 18.98 24.325 19.007 24.342 19.034C24.358 19.061 24.373 19.088 24.395 19.112C24.417 19.138 24.444 19.159 24.471 19.18C24.489 19.193 24.499 19.21 24.518 19.221L29.878 22.314L23.998 25.708V18.557C23.998 18.547 23.993 18.538 23.992 18.528C23.991 18.506 23.988 18.485 23.984 18.463C23.979 18.44 23.973 18.418 23.965 18.396C23.958 18.375 23.95 18.355 23.941 18.336C23.936 18.327 23.937 18.316 23.931 18.308C23.925 18.299 23.917 18.294 23.911 18.286C23.898 18.267 23.886 18.251 23.871 18.234C23.855 18.216 23.84 18.2 23.822 18.185C23.805 18.17 23.788 18.157 23.769 18.144C23.76 18.138 23.756 18.129 23.747 18.124L20.956 16.51ZM25.268 11.633L30.379 14.585V21.448L25.268 18.499V13.736V11.633ZM12.486 18.437L17.389 15.604L22.498 18.556L17.595 21.385L12.486 18.437ZM10.985 25.587L7.019 23.295L10.985 21.005V25.587ZM12.42 14.385L14.28 13.311L16.822 14.777L12.42 17.32V14.385ZM14.78 5.58198L19.891 8.53098V15.394L14.78 12.445V5.58198Z" fill="#213B41"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
|
|
@ -0,0 +1,33 @@
|
|||
import { INode, INodeParams } from '../../../src/Interface'
|
||||
|
||||
class LangWatch_Analytic implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
baseClasses: string[]
|
||||
inputs?: INodeParams[]
|
||||
credential: INodeParams
|
||||
|
||||
constructor() {
|
||||
this.label = 'LangWatch'
|
||||
this.name = 'LangWatch'
|
||||
this.version = 1.0
|
||||
this.type = 'LangWatch'
|
||||
this.icon = 'LangWatch.svg'
|
||||
this.category = 'Analytic'
|
||||
this.baseClasses = [this.type]
|
||||
this.inputs = []
|
||||
this.credential = {
|
||||
label: 'Connect Credential',
|
||||
name: 'credential',
|
||||
type: 'credential',
|
||||
credentialNames: ['langwatchApi']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: LangWatch_Analytic }
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import { BaseCache } from '@langchain/core/caches'
|
||||
import { ChatBaiduWenxin } from '@langchain/community/chat_models/baiduwenxin'
|
||||
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
|
||||
class ChatBaiduWenxin_ChatModels implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
description: string
|
||||
baseClasses: string[]
|
||||
credential: INodeParams
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'ChatBaiduWenxin'
|
||||
this.name = 'chatBaiduWenxin'
|
||||
this.version = 1.0
|
||||
this.type = 'ChatBaiduWenxin'
|
||||
this.icon = 'baiduwenxin.svg'
|
||||
this.category = 'Chat Models'
|
||||
this.description = 'Wrapper around BaiduWenxin Chat Endpoints'
|
||||
this.baseClasses = [this.type, ...getBaseClasses(ChatBaiduWenxin)]
|
||||
this.credential = {
|
||||
label: 'Connect Credential',
|
||||
name: 'credential',
|
||||
type: 'credential',
|
||||
credentialNames: ['baiduApi']
|
||||
}
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Cache',
|
||||
name: 'cache',
|
||||
type: 'BaseCache',
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Model',
|
||||
name: 'modelName',
|
||||
type: 'string',
|
||||
placeholder: 'ERNIE-Bot-turbo'
|
||||
},
|
||||
{
|
||||
label: 'Temperature',
|
||||
name: 'temperature',
|
||||
type: 'number',
|
||||
step: 0.1,
|
||||
default: 0.9,
|
||||
optional: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
|
||||
const cache = nodeData.inputs?.cache as BaseCache
|
||||
const temperature = nodeData.inputs?.temperature as string
|
||||
const modelName = nodeData.inputs?.modelName as string
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const baiduApiKey = getCredentialParam('baiduApiKey', credentialData, nodeData)
|
||||
const baiduSecretKey = getCredentialParam('baiduSecretKey', credentialData, nodeData)
|
||||
|
||||
const obj: Partial<ChatBaiduWenxin> = {
|
||||
streaming: true,
|
||||
baiduApiKey,
|
||||
baiduSecretKey,
|
||||
modelName,
|
||||
temperature: temperature ? parseFloat(temperature) : undefined
|
||||
}
|
||||
if (cache) obj.cache = cache
|
||||
|
||||
const model = new ChatBaiduWenxin(obj)
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: ChatBaiduWenxin_ChatModels }
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="Baidu" role="img"
|
||||
viewBox="0 0 512 512"><rect
|
||||
width="512" height="512"
|
||||
rx="15%"
|
||||
fill="#ffffff"/><path d="m131 251c41-9 35-58 34-68-2-17-21-45-48-43-33 3-37 50-37 50-5 22 10 70 51 61m76-82c22 0 40-26 40-58s-18-58-40-58c-23 0-41 26-41 58s18 58 41 58m96 4c31 4 50-28 54-53 4-24-16-52-37-57s-48 29-50 52c-3 27 3 54 33 58m120 41c0-12-10-47-46-47s-41 33-41 57c0 22 2 53 47 52s40-51 40-62m-46 102s-46-36-74-75c-36-57-89-34-106-5-18 29-45 48-49 53-4 4-56 33-44 84 11 52 52 51 52 51s30 3 65-5 65 2 65 2 81 27 104-25c22-53-13-80-13-80" fill="#2319dc"/><path d="m214 266v34h-28s-29 3-39 35c-3 21 4 34 5 36 1 3 10 19 33 23h53v-128zm-1 107h-21s-15-1-19-18c-3-7 0-16 1-20 1-3 6-11 17-14h22zm38-70v68s1 17 24 23h61v-91h-26v68h-25s-8-1-10-7v-61z" fill="#ffffff"/></svg>
|
||||
|
After Width: | Height: | Size: 924 B |
|
|
@ -0,0 +1,79 @@
|
|||
import { BaseCache } from '@langchain/core/caches'
|
||||
import { ChatFireworks } from '@langchain/community/chat_models/fireworks'
|
||||
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
|
||||
class ChatFireworks_ChatModels implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
description: string
|
||||
baseClasses: string[]
|
||||
credential: INodeParams
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'ChatFireworks'
|
||||
this.name = 'chatFireworks'
|
||||
this.version = 1.0
|
||||
this.type = 'ChatFireworks'
|
||||
this.icon = 'Fireworks.png'
|
||||
this.category = 'Chat Models'
|
||||
this.description = 'Wrapper around Fireworks Chat Endpoints'
|
||||
this.baseClasses = [this.type, ...getBaseClasses(ChatFireworks)]
|
||||
this.credential = {
|
||||
label: 'Connect Credential',
|
||||
name: 'credential',
|
||||
type: 'credential',
|
||||
credentialNames: ['fireworksApi']
|
||||
}
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Cache',
|
||||
name: 'cache',
|
||||
type: 'BaseCache',
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Model',
|
||||
name: 'modelName',
|
||||
type: 'string',
|
||||
default: 'accounts/fireworks/models/llama-v2-13b-chat',
|
||||
placeholder: 'accounts/fireworks/models/llama-v2-13b-chat'
|
||||
},
|
||||
{
|
||||
label: 'Temperature',
|
||||
name: 'temperature',
|
||||
type: 'number',
|
||||
step: 0.1,
|
||||
default: 0.9,
|
||||
optional: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
|
||||
const cache = nodeData.inputs?.cache as BaseCache
|
||||
const temperature = nodeData.inputs?.temperature as string
|
||||
const modelName = nodeData.inputs?.modelName as string
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const fireworksApiKey = getCredentialParam('fireworksApiKey', credentialData, nodeData)
|
||||
|
||||
const obj: Partial<ChatFireworks> = {
|
||||
fireworksApiKey,
|
||||
model: modelName,
|
||||
modelName,
|
||||
temperature: temperature ? parseFloat(temperature) : undefined
|
||||
}
|
||||
if (cache) obj.cache = cache
|
||||
|
||||
const model = new ChatFireworks(obj)
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: ChatFireworks_ChatModels }
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
|
|
@ -552,6 +552,13 @@ function zodToGeminiParameters(zodObj: any) {
|
|||
const jsonSchema: any = zodToJsonSchema(zodObj)
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
const { $schema, additionalProperties, ...rest } = jsonSchema
|
||||
if (rest.properties) {
|
||||
Object.keys(rest.properties).forEach((key) => {
|
||||
if (rest.properties[key].enum?.length) {
|
||||
rest.properties[key] = { type: 'string', format: 'enum', enum: rest.properties[key].enum }
|
||||
}
|
||||
})
|
||||
}
|
||||
return rest
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -131,7 +131,11 @@ class Cheerio_DocumentLoaders implements INode {
|
|||
|
||||
async function cheerioLoader(url: string): Promise<any> {
|
||||
try {
|
||||
let docs = []
|
||||
let docs: IDocument[] = []
|
||||
if (url.endsWith('.pdf')) {
|
||||
if (process.env.DEBUG === 'true') options.logger.info(`CheerioWebBaseLoader does not support PDF files: ${url}`)
|
||||
return docs
|
||||
}
|
||||
const loader = new CheerioWebBaseLoader(url, params)
|
||||
if (textSplitter) {
|
||||
docs = await loader.loadAndSplit(textSplitter)
|
||||
|
|
@ -141,6 +145,7 @@ class Cheerio_DocumentLoaders implements INode {
|
|||
return docs
|
||||
} catch (err) {
|
||||
if (process.env.DEBUG === 'true') options.logger.error(`error in CheerioWebBaseLoader: ${err.message}, on page: ${url}`)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,378 @@
|
|||
import { TextSplitter } from 'langchain/text_splitter'
|
||||
import { Document, DocumentInterface } from '@langchain/core/documents'
|
||||
import { BaseDocumentLoader } from 'langchain/document_loaders/base'
|
||||
import { INode, INodeData, INodeParams, ICommonObject } from '../../../src/Interface'
|
||||
import { getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import axios, { AxiosResponse, AxiosRequestHeaders } from 'axios'
|
||||
import { z } from 'zod'
|
||||
import { zodToJsonSchema } from 'zod-to-json-schema'
|
||||
|
||||
// FirecrawlApp interfaces
|
||||
interface FirecrawlAppConfig {
|
||||
apiKey?: string | null
|
||||
apiUrl?: string | null
|
||||
}
|
||||
|
||||
interface FirecrawlDocumentMetadata {
|
||||
title?: string
|
||||
description?: string
|
||||
language?: string
|
||||
// ... (other metadata fields)
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
interface FirecrawlDocument {
|
||||
id?: string
|
||||
url?: string
|
||||
content: string
|
||||
markdown?: string
|
||||
html?: string
|
||||
llm_extraction?: Record<string, any>
|
||||
createdAt?: Date
|
||||
updatedAt?: Date
|
||||
type?: string
|
||||
metadata: FirecrawlDocumentMetadata
|
||||
childrenLinks?: string[]
|
||||
provider?: string
|
||||
warning?: string
|
||||
index?: number
|
||||
}
|
||||
|
||||
interface ScrapeResponse {
|
||||
success: boolean
|
||||
data?: FirecrawlDocument
|
||||
error?: string
|
||||
}
|
||||
|
||||
interface CrawlResponse {
|
||||
success: boolean
|
||||
jobId?: string
|
||||
data?: FirecrawlDocument[]
|
||||
error?: string
|
||||
}
|
||||
|
||||
interface Params {
|
||||
[key: string]: any
|
||||
extractorOptions?: {
|
||||
extractionSchema: z.ZodSchema | any
|
||||
mode?: 'llm-extraction'
|
||||
extractionPrompt?: string
|
||||
}
|
||||
}
|
||||
|
||||
// FirecrawlApp class (not exported)
|
||||
class FirecrawlApp {
|
||||
private apiKey: string
|
||||
private apiUrl: string
|
||||
|
||||
constructor({ apiKey = null, apiUrl = null }: FirecrawlAppConfig) {
|
||||
this.apiKey = apiKey || ''
|
||||
this.apiUrl = apiUrl || 'https://api.firecrawl.dev'
|
||||
if (!this.apiKey) {
|
||||
throw new Error('No API key provided')
|
||||
}
|
||||
}
|
||||
|
||||
async scrapeUrl(url: string, params: Params | null = null): Promise<ScrapeResponse> {
|
||||
const headers = this.prepareHeaders()
|
||||
let jsonData: Params = { url, ...params }
|
||||
if (params?.extractorOptions?.extractionSchema) {
|
||||
let schema = params.extractorOptions.extractionSchema
|
||||
if (schema instanceof z.ZodSchema) {
|
||||
schema = zodToJsonSchema(schema)
|
||||
}
|
||||
jsonData = {
|
||||
...jsonData,
|
||||
extractorOptions: {
|
||||
...params.extractorOptions,
|
||||
extractionSchema: schema,
|
||||
mode: params.extractorOptions.mode || 'llm-extraction'
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
const response: AxiosResponse = await this.postRequest(this.apiUrl + '/v0/scrape', jsonData, headers)
|
||||
if (response.status === 200) {
|
||||
const responseData = response.data
|
||||
if (responseData.success) {
|
||||
return responseData
|
||||
} else {
|
||||
throw new Error(`Failed to scrape URL. Error: ${responseData.error}`)
|
||||
}
|
||||
} else {
|
||||
this.handleError(response, 'scrape URL')
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message)
|
||||
}
|
||||
return { success: false, error: 'Internal server error.' }
|
||||
}
|
||||
|
||||
async crawlUrl(
|
||||
url: string,
|
||||
params: Params | null = null,
|
||||
waitUntilDone: boolean = true,
|
||||
pollInterval: number = 2,
|
||||
idempotencyKey?: string
|
||||
): Promise<CrawlResponse | any> {
|
||||
const headers = this.prepareHeaders(idempotencyKey)
|
||||
let jsonData: Params = { url, ...params }
|
||||
try {
|
||||
const response: AxiosResponse = await this.postRequest(this.apiUrl + '/v0/crawl', jsonData, headers)
|
||||
if (response.status === 200) {
|
||||
const jobId: string = response.data.jobId
|
||||
if (waitUntilDone) {
|
||||
return this.monitorJobStatus(jobId, headers, pollInterval)
|
||||
} else {
|
||||
return { success: true, jobId }
|
||||
}
|
||||
} else {
|
||||
this.handleError(response, 'start crawl job')
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message)
|
||||
}
|
||||
return { success: false, error: 'Internal server error.' }
|
||||
}
|
||||
|
||||
private prepareHeaders(idempotencyKey?: string): AxiosRequestHeaders {
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${this.apiKey}`,
|
||||
...(idempotencyKey ? { 'x-idempotency-key': idempotencyKey } : {})
|
||||
} as AxiosRequestHeaders & { 'x-idempotency-key'?: string }
|
||||
}
|
||||
|
||||
private postRequest(url: string, data: Params, headers: AxiosRequestHeaders): Promise<AxiosResponse> {
|
||||
return axios.post(url, data, { headers })
|
||||
}
|
||||
|
||||
private getRequest(url: string, headers: AxiosRequestHeaders): Promise<AxiosResponse> {
|
||||
return axios.get(url, { headers })
|
||||
}
|
||||
|
||||
private async monitorJobStatus(jobId: string, headers: AxiosRequestHeaders, checkInterval: number): Promise<any> {
|
||||
let isJobCompleted = false
|
||||
while (!isJobCompleted) {
|
||||
const statusResponse: AxiosResponse = await this.getRequest(this.apiUrl + `/v0/crawl/status/${jobId}`, headers)
|
||||
if (statusResponse.status === 200) {
|
||||
const statusData = statusResponse.data
|
||||
switch (statusData.status) {
|
||||
case 'completed':
|
||||
isJobCompleted = true
|
||||
if ('data' in statusData) {
|
||||
return statusData.data
|
||||
} else {
|
||||
throw new Error('Crawl job completed but no data was returned')
|
||||
}
|
||||
case 'active':
|
||||
case 'paused':
|
||||
case 'pending':
|
||||
case 'queued':
|
||||
await new Promise((resolve) => setTimeout(resolve, Math.max(checkInterval, 2) * 1000))
|
||||
break
|
||||
default:
|
||||
throw new Error(`Crawl job failed or was stopped. Status: ${statusData.status}`)
|
||||
}
|
||||
} else {
|
||||
this.handleError(statusResponse, 'check crawl status')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleError(response: AxiosResponse, action: string): void {
|
||||
if ([402, 408, 409, 500].includes(response.status)) {
|
||||
const errorMessage: string = response.data.error || 'Unknown error occurred'
|
||||
throw new Error(`Failed to ${action}. Status code: ${response.status}. Error: ${errorMessage}`)
|
||||
} else {
|
||||
throw new Error(`Unexpected error occurred while trying to ${action}. Status code: ${response.status}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FireCrawl Loader
|
||||
interface FirecrawlLoaderParameters {
|
||||
url: string
|
||||
apiKey?: string
|
||||
mode?: 'crawl' | 'scrape'
|
||||
params?: Record<string, unknown>
|
||||
}
|
||||
|
||||
class FireCrawlLoader extends BaseDocumentLoader {
|
||||
private apiKey: string
|
||||
private url: string
|
||||
private mode: 'crawl' | 'scrape'
|
||||
private params?: Record<string, unknown>
|
||||
|
||||
constructor(loaderParams: FirecrawlLoaderParameters) {
|
||||
super()
|
||||
const { apiKey, url, mode = 'crawl', params } = loaderParams
|
||||
if (!apiKey) {
|
||||
throw new Error('Firecrawl API key not set. You can set it as FIRECRAWL_API_KEY in your .env file, or pass it to Firecrawl.')
|
||||
}
|
||||
|
||||
this.apiKey = apiKey
|
||||
this.url = url
|
||||
this.mode = mode
|
||||
this.params = params
|
||||
}
|
||||
|
||||
public async load(): Promise<DocumentInterface[]> {
|
||||
const app = new FirecrawlApp({ apiKey: this.apiKey })
|
||||
let firecrawlDocs: FirecrawlDocument[]
|
||||
|
||||
if (this.mode === 'scrape') {
|
||||
const response = await app.scrapeUrl(this.url, this.params)
|
||||
if (!response.success) {
|
||||
throw new Error(`Firecrawl: Failed to scrape URL. Error: ${response.error}`)
|
||||
}
|
||||
firecrawlDocs = [response.data as FirecrawlDocument]
|
||||
} else if (this.mode === 'crawl') {
|
||||
const response = await app.crawlUrl(this.url, this.params, true)
|
||||
firecrawlDocs = response as FirecrawlDocument[]
|
||||
} else {
|
||||
throw new Error(`Unrecognized mode '${this.mode}'. Expected one of 'crawl', 'scrape'.`)
|
||||
}
|
||||
|
||||
return firecrawlDocs.map(
|
||||
(doc) =>
|
||||
new Document({
|
||||
pageContent: doc.markdown || '',
|
||||
metadata: doc.metadata || {}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Flowise Node Class
|
||||
class FireCrawl_DocumentLoaders implements INode {
|
||||
label: string
|
||||
name: string
|
||||
description: string
|
||||
type: string
|
||||
icon: string
|
||||
version: number
|
||||
category: string
|
||||
baseClasses: string[]
|
||||
inputs: INodeParams[]
|
||||
credential: INodeParams
|
||||
|
||||
constructor() {
|
||||
this.label = 'FireCrawl'
|
||||
this.name = 'fireCrawl'
|
||||
this.type = 'Document'
|
||||
this.icon = 'firecrawl.png'
|
||||
this.version = 1.0
|
||||
this.category = 'Document Loaders'
|
||||
this.description = 'Load data from URL using FireCrawl'
|
||||
this.baseClasses = [this.type]
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Text Splitter',
|
||||
name: 'textSplitter',
|
||||
type: 'TextSplitter',
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'URLs',
|
||||
name: 'url',
|
||||
type: 'string',
|
||||
description: 'URL to be crawled/scraped',
|
||||
placeholder: 'https://docs.flowiseai.com'
|
||||
},
|
||||
{
|
||||
label: 'Crawler type',
|
||||
type: 'options',
|
||||
name: 'crawlerType',
|
||||
options: [
|
||||
{
|
||||
label: 'Crawl',
|
||||
name: 'crawl',
|
||||
description: 'Crawl a URL and all accessible subpages'
|
||||
},
|
||||
{
|
||||
label: 'Scrape',
|
||||
name: 'scrape',
|
||||
description: 'Scrape a URL and get its content'
|
||||
}
|
||||
],
|
||||
default: 'crawl'
|
||||
}
|
||||
// ... (other input parameters)
|
||||
]
|
||||
this.credential = {
|
||||
label: 'FireCrawl API',
|
||||
name: 'credential',
|
||||
type: 'credential',
|
||||
credentialNames: ['fireCrawlApi']
|
||||
}
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
|
||||
const textSplitter = nodeData.inputs?.textSplitter as TextSplitter
|
||||
const metadata = nodeData.inputs?.metadata
|
||||
const url = nodeData.inputs?.url as string
|
||||
const crawlerType = nodeData.inputs?.crawlerType as string
|
||||
const maxCrawlPages = nodeData.inputs?.maxCrawlPages as string
|
||||
const generateImgAltText = nodeData.inputs?.generateImgAltText as boolean
|
||||
const returnOnlyUrls = nodeData.inputs?.returnOnlyUrls as boolean
|
||||
const onlyMainContent = nodeData.inputs?.onlyMainContent as boolean
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const firecrawlApiToken = getCredentialParam('firecrawlApiToken', credentialData, nodeData)
|
||||
|
||||
const urlPatternsExcludes = nodeData.inputs?.urlPatternsExcludes
|
||||
? (nodeData.inputs.urlPatternsExcludes.split(',') as string[])
|
||||
: undefined
|
||||
const urlPatternsIncludes = nodeData.inputs?.urlPatternsIncludes
|
||||
? (nodeData.inputs.urlPatternsIncludes.split(',') as string[])
|
||||
: undefined
|
||||
|
||||
const input: FirecrawlLoaderParameters = {
|
||||
url,
|
||||
mode: crawlerType as 'crawl' | 'scrape',
|
||||
apiKey: firecrawlApiToken,
|
||||
params: {
|
||||
crawlerOptions: {
|
||||
includes: urlPatternsIncludes,
|
||||
excludes: urlPatternsExcludes,
|
||||
generateImgAltText,
|
||||
returnOnlyUrls,
|
||||
limit: maxCrawlPages ? parseFloat(maxCrawlPages) : undefined
|
||||
},
|
||||
pageOptions: {
|
||||
onlyMainContent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const loader = new FireCrawlLoader(input)
|
||||
|
||||
let docs = []
|
||||
|
||||
if (textSplitter) {
|
||||
docs = await loader.loadAndSplit(textSplitter)
|
||||
} else {
|
||||
docs = await loader.load()
|
||||
}
|
||||
|
||||
if (metadata) {
|
||||
const parsedMetadata = typeof metadata === 'object' ? metadata : JSON.parse(metadata)
|
||||
let finaldocs = []
|
||||
for (const doc of docs) {
|
||||
const newdoc = {
|
||||
...doc,
|
||||
metadata: {
|
||||
...doc.metadata,
|
||||
...parsedMetadata
|
||||
}
|
||||
}
|
||||
finaldocs.push(newdoc)
|
||||
}
|
||||
return finaldocs
|
||||
}
|
||||
|
||||
return docs
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: FireCrawl_DocumentLoaders }
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -0,0 +1,189 @@
|
|||
import { TextSplitter } from 'langchain/text_splitter'
|
||||
import { Document, DocumentInterface } from '@langchain/core/documents'
|
||||
import { BaseDocumentLoader } from 'langchain/document_loaders/base'
|
||||
import { INode, INodeData, INodeParams, ICommonObject } from '../../../src/Interface'
|
||||
import { getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import SpiderApp from './SpiderApp'
|
||||
|
||||
interface SpiderLoaderParameters {
|
||||
url: string
|
||||
apiKey?: string
|
||||
mode?: 'crawl' | 'scrape'
|
||||
limit?: number
|
||||
params?: Record<string, unknown>
|
||||
}
|
||||
|
||||
class SpiderLoader extends BaseDocumentLoader {
|
||||
private apiKey: string
|
||||
private url: string
|
||||
private mode: 'crawl' | 'scrape'
|
||||
private limit?: number
|
||||
private params?: Record<string, unknown>
|
||||
|
||||
constructor(loaderParams: SpiderLoaderParameters) {
|
||||
super()
|
||||
const { apiKey, url, mode = 'crawl', limit, params } = loaderParams
|
||||
if (!apiKey) {
|
||||
throw new Error('Spider API key not set. You can set it as SPIDER_API_KEY in your .env file, or pass it to Spider.')
|
||||
}
|
||||
|
||||
this.apiKey = apiKey
|
||||
this.url = url
|
||||
this.mode = mode
|
||||
this.limit = Number(limit)
|
||||
this.params = params
|
||||
}
|
||||
|
||||
public async load(): Promise<DocumentInterface[]> {
|
||||
const app = new SpiderApp({ apiKey: this.apiKey })
|
||||
let spiderDocs: any[]
|
||||
|
||||
if (this.mode === 'scrape') {
|
||||
const response = await app.scrapeUrl(this.url, this.params)
|
||||
if (!response.success) {
|
||||
throw new Error(`Spider: Failed to scrape URL. Error: ${response.error}`)
|
||||
}
|
||||
spiderDocs = [response.data]
|
||||
} else if (this.mode === 'crawl') {
|
||||
if (this.params) {
|
||||
this.params.limit = this.limit
|
||||
}
|
||||
const response = await app.crawlUrl(this.url, this.params)
|
||||
if (!response.success) {
|
||||
throw new Error(`Spider: Failed to crawl URL. Error: ${response.error}`)
|
||||
}
|
||||
spiderDocs = response.data
|
||||
} else {
|
||||
throw new Error(`Unrecognized mode '${this.mode}'. Expected one of 'crawl', 'scrape'.`)
|
||||
}
|
||||
|
||||
return spiderDocs.map(
|
||||
(doc) =>
|
||||
new Document({
|
||||
pageContent: doc.content || '',
|
||||
metadata: { source: doc.url }
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Spider_DocumentLoaders implements INode {
|
||||
label: string
|
||||
name: string
|
||||
description: string
|
||||
type: string
|
||||
icon: string
|
||||
version: number
|
||||
category: string
|
||||
baseClasses: string[]
|
||||
inputs: INodeParams[]
|
||||
credential: INodeParams
|
||||
|
||||
constructor() {
|
||||
this.label = 'Spider Document Loaders'
|
||||
this.name = 'spiderDocumentLoaders'
|
||||
this.version = 1.0
|
||||
this.type = 'Document'
|
||||
this.icon = 'spider.svg'
|
||||
this.category = 'Document Loaders'
|
||||
this.description = 'Scrape & Crawl the web with Spider'
|
||||
this.baseClasses = [this.type]
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Text Splitter',
|
||||
name: 'textSplitter',
|
||||
type: 'TextSplitter',
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Mode',
|
||||
name: 'mode',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
label: 'Scrape',
|
||||
name: 'scrape',
|
||||
description: 'Scrape a single page'
|
||||
},
|
||||
{
|
||||
label: 'Crawl',
|
||||
name: 'crawl',
|
||||
description: 'Crawl a website and extract pages within the same domain'
|
||||
}
|
||||
],
|
||||
default: 'scrape'
|
||||
},
|
||||
{
|
||||
label: 'Web Page URL',
|
||||
name: 'url',
|
||||
type: 'string',
|
||||
placeholder: 'https://spider.cloud'
|
||||
},
|
||||
{
|
||||
label: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 25
|
||||
},
|
||||
{
|
||||
label: 'Additional Parameters',
|
||||
name: 'params',
|
||||
description:
|
||||
'Find all the available parameters in the <a _target="blank" href="https://spider.cloud/docs/api">Spider API documentation</a>',
|
||||
additionalParams: true,
|
||||
placeholder: '{ "anti_bot": true }',
|
||||
type: 'json',
|
||||
optional: true
|
||||
}
|
||||
]
|
||||
this.credential = {
|
||||
label: 'Credential',
|
||||
name: 'credential',
|
||||
type: 'credential',
|
||||
credentialNames: ['spiderApi']
|
||||
}
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
|
||||
const textSplitter = nodeData.inputs?.textSplitter as TextSplitter
|
||||
const url = nodeData.inputs?.url as string
|
||||
const mode = nodeData.inputs?.mode as 'crawl' | 'scrape'
|
||||
const limit = nodeData.inputs?.limit as number
|
||||
let params = nodeData.inputs?.params || {}
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const spiderApiKey = getCredentialParam('spiderApiKey', credentialData, nodeData)
|
||||
|
||||
if (typeof params === 'string') {
|
||||
try {
|
||||
params = JSON.parse(params)
|
||||
} catch (e) {
|
||||
throw new Error('Invalid JSON string provided for params')
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure return_format is set to markdown
|
||||
params.return_format = 'markdown'
|
||||
|
||||
const input: SpiderLoaderParameters = {
|
||||
url,
|
||||
mode: mode as 'crawl' | 'scrape',
|
||||
apiKey: spiderApiKey,
|
||||
limit: limit as number,
|
||||
params: params as Record<string, unknown>
|
||||
}
|
||||
|
||||
const loader = new SpiderLoader(input)
|
||||
|
||||
let docs = []
|
||||
|
||||
if (textSplitter) {
|
||||
docs = await loader.loadAndSplit(textSplitter)
|
||||
} else {
|
||||
docs = await loader.load()
|
||||
}
|
||||
|
||||
return docs
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: Spider_DocumentLoaders }
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
import axios, { AxiosResponse, AxiosRequestHeaders } from 'axios'
|
||||
|
||||
interface SpiderAppConfig {
|
||||
apiKey?: string | null
|
||||
apiUrl?: string | null
|
||||
}
|
||||
|
||||
interface SpiderDocumentMetadata {
|
||||
title?: string
|
||||
description?: string
|
||||
language?: string
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
interface SpiderDocument {
|
||||
id?: string
|
||||
url?: string
|
||||
content: string
|
||||
markdown?: string
|
||||
html?: string
|
||||
createdAt?: Date
|
||||
updatedAt?: Date
|
||||
type?: string
|
||||
metadata: SpiderDocumentMetadata
|
||||
}
|
||||
|
||||
interface ScrapeResponse {
|
||||
success: boolean
|
||||
data?: SpiderDocument
|
||||
error?: string
|
||||
}
|
||||
|
||||
interface CrawlResponse {
|
||||
success: boolean
|
||||
data?: SpiderDocument[]
|
||||
error?: string
|
||||
}
|
||||
|
||||
interface Params {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
class SpiderApp {
|
||||
private apiKey: string
|
||||
private apiUrl: string
|
||||
|
||||
constructor({ apiKey = null, apiUrl = null }: SpiderAppConfig) {
|
||||
this.apiKey = apiKey || ''
|
||||
this.apiUrl = apiUrl || 'https://api.spider.cloud/v1'
|
||||
if (!this.apiKey) {
|
||||
throw new Error('No API key provided')
|
||||
}
|
||||
}
|
||||
|
||||
async scrapeUrl(url: string, params: Params | null = null): Promise<ScrapeResponse> {
|
||||
const headers = this.prepareHeaders()
|
||||
const jsonData: Params = { url, limit: 1, ...params }
|
||||
|
||||
try {
|
||||
const response: AxiosResponse = await this.postRequest('crawl', jsonData, headers)
|
||||
if (response.status === 200) {
|
||||
const responseData = response.data
|
||||
if (responseData[0].status) {
|
||||
return { success: true, data: responseData[0] }
|
||||
} else {
|
||||
throw new Error(`Failed to scrape URL. Error: ${responseData.error}`)
|
||||
}
|
||||
} else {
|
||||
this.handleError(response, 'scrape URL')
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message)
|
||||
}
|
||||
return { success: false, error: 'Internal server error.' }
|
||||
}
|
||||
|
||||
async crawlUrl(url: string, params: Params | null = null, idempotencyKey?: string): Promise<CrawlResponse | any> {
|
||||
const headers = this.prepareHeaders(idempotencyKey)
|
||||
const jsonData: Params = { url, ...params }
|
||||
|
||||
try {
|
||||
const response: AxiosResponse = await this.postRequest('crawl', jsonData, headers)
|
||||
if (response.status === 200) {
|
||||
return { success: true, data: response.data }
|
||||
} else {
|
||||
this.handleError(response, 'start crawl job')
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message)
|
||||
}
|
||||
return { success: false, error: 'Internal server error.' }
|
||||
}
|
||||
|
||||
private prepareHeaders(idempotencyKey?: string): AxiosRequestHeaders {
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${this.apiKey}`,
|
||||
...(idempotencyKey ? { 'x-idempotency-key': idempotencyKey } : {})
|
||||
} as AxiosRequestHeaders & { 'x-idempotency-key'?: string }
|
||||
}
|
||||
|
||||
private postRequest(url: string, data: Params, headers: AxiosRequestHeaders): Promise<AxiosResponse> {
|
||||
return axios.post(`${this.apiUrl}/${url}`, data, { headers })
|
||||
}
|
||||
|
||||
private handleError(response: AxiosResponse, action: string): void {
|
||||
if ([402, 408, 409, 500].includes(response.status)) {
|
||||
const errorMessage: string = response.data.error || 'Unknown error occurred'
|
||||
throw new Error(`Failed to ${action}. Status code: ${response.status}. Error: ${errorMessage}`)
|
||||
} else {
|
||||
throw new Error(`Unexpected error occurred while trying to ${action}. Status code: ${response.status}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SpiderApp
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg height="30" width="30" viewBox="0 0 36 34" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" class="fill-accent-foreground transition-all group-hover:scale-110"><title>Spider v1 Logo</title><path fill-rule="evenodd" clip-rule="evenodd" d="M9.13883 7.06589V0.164429L13.0938 0.164429V6.175L14.5178 7.4346C15.577 6.68656 16.7337 6.27495 17.945 6.27495C19.1731 6.27495 20.3451 6.69807 21.4163 7.46593L22.8757 6.175V0.164429L26.8307 0.164429V7.06589V7.95679L26.1634 8.54706L24.0775 10.3922C24.3436 10.8108 24.5958 11.2563 24.8327 11.7262L26.0467 11.4215L28.6971 8.08749L31.793 10.5487L28.7257 14.407L28.3089 14.9313L27.6592 15.0944L26.2418 15.4502C26.3124 15.7082 26.3793 15.9701 26.4422 16.2355L28.653 16.6566L29.092 16.7402L29.4524 17.0045L35.3849 21.355L33.0461 24.5444L27.474 20.4581L27.0719 20.3816C27.1214 21.0613 27.147 21.7543 27.147 22.4577C27.147 22.5398 27.1466 22.6214 27.1459 22.7024L29.5889 23.7911L30.3219 24.1177L30.62 24.8629L33.6873 32.5312L30.0152 34L27.246 27.0769L26.7298 26.8469C25.5612 32.2432 22.0701 33.8808 17.945 33.8808C13.8382 33.8808 10.3598 32.2577 9.17593 26.9185L8.82034 27.0769L6.05109 34L2.37897 32.5312L5.44629 24.8629L5.74435 24.1177L6.47743 23.7911L8.74487 22.7806C8.74366 22.6739 8.74305 22.5663 8.74305 22.4577C8.74305 21.7616 8.76804 21.0758 8.81654 20.4028L8.52606 20.4581L2.95395 24.5444L0.615112 21.355L6.54761 17.0045L6.908 16.7402L7.34701 16.6566L9.44264 16.2575C9.50917 15.9756 9.5801 15.6978 9.65528 15.4242L8.34123 15.0944L7.69155 14.9313L7.27471 14.407L4.20739 10.5487L7.30328 8.08749L9.95376 11.4215L11.0697 11.7016C11.3115 11.2239 11.5692 10.7716 11.8412 10.3473L9.80612 8.54706L9.13883 7.95679V7.06589Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -448,7 +448,16 @@ class UnstructuredFile_DocumentLoaders implements INode {
|
|||
if (_omitMetadataKeys) {
|
||||
omitMetadataKeys = _omitMetadataKeys.split(',').map((key) => key.trim())
|
||||
}
|
||||
const fileBase64 = nodeData.inputs?.fileObject as string
|
||||
// give priority to upload with upsert then to fileObject (upload from UI component)
|
||||
const fileBase64 =
|
||||
nodeData.inputs?.pdfFile ||
|
||||
nodeData.inputs?.txtFile ||
|
||||
nodeData.inputs?.yamlFile ||
|
||||
nodeData.inputs?.docxFile ||
|
||||
nodeData.inputs?.jsonlinesFile ||
|
||||
nodeData.inputs?.csvFile ||
|
||||
nodeData.inputs?.jsonFile ||
|
||||
(nodeData.inputs?.fileObject as string)
|
||||
|
||||
const obj: UnstructuredLoaderOptions = {
|
||||
apiUrl: unstructuredAPIUrl,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
import { getBaseClasses, getCredentialData, getCredentialParam, ICommonObject, INode, INodeData, INodeParams } from '../../../src'
|
||||
import { Fireworks } from '@langchain/community/llms/fireworks'
|
||||
import { BaseCache } from '@langchain/core/caches'
|
||||
|
||||
class Fireworks_LLMs implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
description: string
|
||||
baseClasses: string[]
|
||||
credential: INodeParams
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'Fireworks'
|
||||
this.name = 'fireworks'
|
||||
this.version = 1.0
|
||||
this.type = 'Fireworks'
|
||||
this.icon = 'fireworks.png'
|
||||
this.category = 'LLMs'
|
||||
this.description = 'Wrapper around Fireworks API for large language models'
|
||||
this.baseClasses = [this.type, ...getBaseClasses(Fireworks)]
|
||||
this.credential = {
|
||||
label: 'Connect Credential',
|
||||
name: 'credential',
|
||||
type: 'credential',
|
||||
credentialNames: ['fireworksApi']
|
||||
}
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Cache',
|
||||
name: 'cache',
|
||||
type: 'BaseCache',
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Model Name',
|
||||
name: 'modelName',
|
||||
type: 'string',
|
||||
default: 'accounts/fireworks/models/llama-v3-70b-instruct-hf',
|
||||
description: 'For more details see https://fireworks.ai/models',
|
||||
optional: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
|
||||
const cache = nodeData.inputs?.cache as BaseCache
|
||||
const modelName = nodeData.inputs?.modelName as string
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const fireworksKey = getCredentialParam('fireworksApiKey', credentialData, nodeData)
|
||||
|
||||
const obj: any = {
|
||||
fireworksApiKey: fireworksKey,
|
||||
modelName: modelName
|
||||
}
|
||||
if (cache) obj.cache = cache
|
||||
|
||||
const fireworks = new Fireworks(obj)
|
||||
return fireworks
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: Fireworks_LLMs }
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
|
@ -273,10 +273,8 @@ class Supervisor_MultiAgents implements INode {
|
|||
* So we have to place the system + human prompt at last
|
||||
*/
|
||||
let prompt = ChatPromptTemplate.fromMessages([
|
||||
['human', systemPrompt],
|
||||
['ai', ''],
|
||||
['system', systemPrompt],
|
||||
new MessagesPlaceholder('messages'),
|
||||
['ai', ''],
|
||||
['human', userPrompt]
|
||||
])
|
||||
|
||||
|
|
|
|||
|
|
@ -199,19 +199,33 @@ async function createAgent(
|
|||
}
|
||||
const modelWithTools = llm.bindTools(tools)
|
||||
|
||||
const agent = RunnableSequence.from([
|
||||
RunnablePassthrough.assign({
|
||||
//@ts-ignore
|
||||
agent_scratchpad: (input: { steps: ToolsAgentStep[] }) => formatToOpenAIToolMessages(input.steps)
|
||||
}),
|
||||
RunnablePassthrough.assign(transformObjectPropertyToFunction(workerInputVariablesValues)),
|
||||
prompt,
|
||||
modelWithTools,
|
||||
new ToolCallingAgentOutputParser()
|
||||
])
|
||||
let agent
|
||||
|
||||
if (!workerInputVariablesValues || !Object.keys(workerInputVariablesValues).length) {
|
||||
agent = RunnableSequence.from([
|
||||
RunnablePassthrough.assign({
|
||||
//@ts-ignore
|
||||
agent_scratchpad: (input: { steps: ToolsAgentStep[] }) => formatToOpenAIToolMessages(input.steps)
|
||||
}),
|
||||
prompt,
|
||||
modelWithTools,
|
||||
new ToolCallingAgentOutputParser()
|
||||
])
|
||||
} else {
|
||||
agent = RunnableSequence.from([
|
||||
RunnablePassthrough.assign({
|
||||
//@ts-ignore
|
||||
agent_scratchpad: (input: { steps: ToolsAgentStep[] }) => formatToOpenAIToolMessages(input.steps)
|
||||
}),
|
||||
RunnablePassthrough.assign(transformObjectPropertyToFunction(workerInputVariablesValues)),
|
||||
prompt,
|
||||
modelWithTools,
|
||||
new ToolCallingAgentOutputParser()
|
||||
])
|
||||
}
|
||||
|
||||
const executor = AgentExecutor.fromAgentAndTools({
|
||||
agent: agent,
|
||||
agent,
|
||||
tools,
|
||||
sessionId: flowObj?.sessionId,
|
||||
chatId: flowObj?.chatId,
|
||||
|
|
@ -233,12 +247,19 @@ async function createAgent(
|
|||
const msg = HumanMessagePromptTemplate.fromTemplate([...multiModalMessageContent])
|
||||
prompt.promptMessages.splice(1, 0, msg)
|
||||
}
|
||||
const conversationChain = RunnableSequence.from([
|
||||
RunnablePassthrough.assign(transformObjectPropertyToFunction(workerInputVariablesValues)),
|
||||
prompt,
|
||||
llm,
|
||||
new StringOutputParser()
|
||||
])
|
||||
|
||||
let conversationChain
|
||||
|
||||
if (!workerInputVariablesValues || !Object.keys(workerInputVariablesValues).length) {
|
||||
conversationChain = RunnableSequence.from([prompt, llm, new StringOutputParser()])
|
||||
} else {
|
||||
conversationChain = RunnableSequence.from([
|
||||
RunnablePassthrough.assign(transformObjectPropertyToFunction(workerInputVariablesValues)),
|
||||
prompt,
|
||||
llm,
|
||||
new StringOutputParser()
|
||||
])
|
||||
}
|
||||
return conversationChain
|
||||
}
|
||||
}
|
||||
|
|
@ -256,6 +277,7 @@ async function agentNode(
|
|||
if (abortControllerSignal.signal.aborted) {
|
||||
throw new Error('Aborted!')
|
||||
}
|
||||
|
||||
const result = await agent.invoke({ ...state, signal: abortControllerSignal.signal }, config)
|
||||
const additional_kwargs: ICommonObject = {}
|
||||
if (result.usedTools) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
import { PromptTemplate } from '@langchain/core/prompts'
|
||||
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { MultiQueryRetriever } from 'langchain/retrievers/multi_query'
|
||||
|
||||
const defaultPrompt = `You are an AI language model assistant. Your task is
|
||||
to generate 3 different versions of the given user
|
||||
question to retrieve relevant documents from a vector database.
|
||||
By generating multiple perspectives on the user question,
|
||||
your goal is to help the user overcome some of the limitations
|
||||
of distance-based similarity search.
|
||||
|
||||
Provide these alternative questions separated by newlines between XML tags. For example:
|
||||
|
||||
<questions>
|
||||
Question 1
|
||||
Question 2
|
||||
Question 3
|
||||
</questions>
|
||||
|
||||
Original question: {question}`
|
||||
|
||||
class MultiQueryRetriever_Retrievers implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
baseClasses: string[]
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'Multi Query Retriever'
|
||||
this.name = 'multiQueryRetriever'
|
||||
this.version = 1.0
|
||||
this.type = 'MultiQueryRetriever'
|
||||
this.icon = 'multiQueryRetriever.svg'
|
||||
this.category = 'Retrievers'
|
||||
this.description = 'Generate multiple queries from different perspectives for a given user input query'
|
||||
this.baseClasses = [this.type, 'BaseRetriever']
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Vector Store',
|
||||
name: 'vectorStore',
|
||||
type: 'VectorStore'
|
||||
},
|
||||
{
|
||||
label: 'Language Model',
|
||||
name: 'model',
|
||||
type: 'BaseLanguageModel'
|
||||
},
|
||||
{
|
||||
label: 'Prompt',
|
||||
name: 'modelPrompt',
|
||||
description:
|
||||
'Prompt for the language model to generate alternative questions. Use {question} to refer to the original question',
|
||||
type: 'string',
|
||||
rows: 4,
|
||||
default: defaultPrompt
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, input: string): Promise<any> {
|
||||
const model = nodeData.inputs?.model
|
||||
const vectorStore = nodeData.inputs?.vectorStore
|
||||
|
||||
let prompt = nodeData.inputs?.modelPrompt || (defaultPrompt as string)
|
||||
prompt = prompt.replaceAll('{question}', input)
|
||||
|
||||
const retriever = MultiQueryRetriever.fromLLM({
|
||||
llm: model,
|
||||
retriever: vectorStore.asRetriever(),
|
||||
verbose: process.env.DEBUG === 'true',
|
||||
// @ts-ignore
|
||||
prompt: PromptTemplate.fromTemplate(prompt)
|
||||
})
|
||||
return retriever
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: MultiQueryRetriever_Retrievers }
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-relation-one-to-many"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 5m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" /><path d="M7 10h1v4" /><path d="M14 14v-4l3 4v-4" /><path d="M11 10.5l0 .01" /><path d="M11 13.5l0 .01" /></svg>
|
||||
|
After Width: | Height: | Size: 524 B |
|
|
@ -48,6 +48,10 @@ class VoyageAIRerankRetriever_Retrievers implements INode {
|
|||
{
|
||||
label: 'rerank-lite-1',
|
||||
name: 'rerank-lite-1'
|
||||
},
|
||||
{
|
||||
label: 'rerank-1',
|
||||
name: 'rerank-1'
|
||||
}
|
||||
],
|
||||
default: 'rerank-lite-1',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { CallbackManagerForToolRun, Callbacks, CallbackManager, parseCallbackCon
|
|||
import { StructuredTool } from '@langchain/core/tools'
|
||||
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
class ChatflowTool_Tools implements INode {
|
||||
label: string
|
||||
|
|
@ -22,7 +23,7 @@ class ChatflowTool_Tools implements INode {
|
|||
constructor() {
|
||||
this.label = 'Chatflow Tool'
|
||||
this.name = 'ChatflowTool'
|
||||
this.version = 1.0
|
||||
this.version = 3.0
|
||||
this.type = 'ChatflowTool'
|
||||
this.icon = 'chatflowTool.svg'
|
||||
this.category = 'Tools'
|
||||
|
|
@ -56,6 +57,26 @@ class ChatflowTool_Tools implements INode {
|
|||
placeholder:
|
||||
'State of the Union QA - useful for when you need to ask questions about the most recent state of the union address.'
|
||||
},
|
||||
{
|
||||
label: 'Base URL',
|
||||
name: 'baseURL',
|
||||
type: 'string',
|
||||
description:
|
||||
'Base URL to Flowise. By default, it is the URL of the incoming request. Useful when you need to execute the Chatflow through an alternative route.',
|
||||
placeholder: 'http://localhost:3000',
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'Start new session per message',
|
||||
name: 'startNewSession',
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Whether to continue the session with the Chatflow tool or start a new one with each interaction. Useful for Chatflows with memory if you want to avoid it.',
|
||||
default: false,
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'Use Question from Chat',
|
||||
name: 'useQuestionFromChat',
|
||||
|
|
@ -107,7 +128,9 @@ class ChatflowTool_Tools implements INode {
|
|||
const useQuestionFromChat = nodeData.inputs?.useQuestionFromChat as boolean
|
||||
const customInput = nodeData.inputs?.customInput as string
|
||||
|
||||
const baseURL = options.baseURL as string
|
||||
const startNewSession = nodeData.inputs?.startNewSession as boolean
|
||||
|
||||
const baseURL = (nodeData.inputs?.baseURL as string) || (options.baseURL as string)
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const chatflowApiKey = getCredentialParam('chatflowApiKey', credentialData, nodeData)
|
||||
|
|
@ -126,7 +149,7 @@ class ChatflowTool_Tools implements INode {
|
|||
|
||||
let name = _name || 'chatflow_tool'
|
||||
|
||||
return new ChatflowTool({ name, baseURL, description, chatflowid: selectedChatflowId, headers, input: toolInput })
|
||||
return new ChatflowTool({ name, baseURL, description, chatflowid: selectedChatflowId, startNewSession, headers, input: toolInput })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +166,8 @@ class ChatflowTool extends StructuredTool {
|
|||
|
||||
chatflowid = ''
|
||||
|
||||
startNewSession = false
|
||||
|
||||
baseURL = 'http://localhost:3000'
|
||||
|
||||
headers = {}
|
||||
|
|
@ -156,6 +181,7 @@ class ChatflowTool extends StructuredTool {
|
|||
description,
|
||||
input,
|
||||
chatflowid,
|
||||
startNewSession,
|
||||
baseURL,
|
||||
headers
|
||||
}: {
|
||||
|
|
@ -163,6 +189,7 @@ class ChatflowTool extends StructuredTool {
|
|||
description: string
|
||||
input: string
|
||||
chatflowid: string
|
||||
startNewSession: boolean
|
||||
baseURL: string
|
||||
headers: ICommonObject
|
||||
}) {
|
||||
|
|
@ -171,6 +198,7 @@ class ChatflowTool extends StructuredTool {
|
|||
this.description = description
|
||||
this.input = input
|
||||
this.baseURL = baseURL
|
||||
this.startNewSession = startNewSession
|
||||
this.headers = headers
|
||||
this.chatflowid = chatflowid
|
||||
}
|
||||
|
|
@ -230,9 +258,9 @@ class ChatflowTool extends StructuredTool {
|
|||
|
||||
const body = {
|
||||
question: inputQuestion,
|
||||
chatId: flowConfig?.chatId,
|
||||
chatId: this.startNewSession ? uuidv4() : flowConfig?.chatId,
|
||||
overrideConfig: {
|
||||
sessionId: flowConfig?.sessionId
|
||||
sessionId: this.startNewSession ? uuidv4() : flowConfig?.sessionId
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,9 @@ export class DynamicStructuredTool<
|
|||
await runManager?.handleToolError(e)
|
||||
throw e
|
||||
}
|
||||
if (result && typeof result !== 'string') {
|
||||
result = JSON.stringify(result)
|
||||
}
|
||||
await runManager?.handleToolEnd(result)
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class Retriever_Tools implements INode {
|
|||
}
|
||||
|
||||
const schema = z.object({
|
||||
input: z.string().describe('query to look up in retriever')
|
||||
input: z.string().describe('input to look up in retriever')
|
||||
})
|
||||
|
||||
const tool = new DynamicStructuredTool({ ...input, func, schema })
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" id="svg8" version="1.1" viewBox="0 0 92 92" height="92mm" width="92mm">
|
||||
<defs id="defs2"/>
|
||||
<metadata id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g transform="translate(-40.921303,-17.416526)" id="layer1">
|
||||
<circle r="0" style="fill:none;stroke:#000000;stroke-width:12;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" cy="92" cx="75" id="path3713"/>
|
||||
<circle r="30" cy="53.902557" cx="75.921303" id="path834" style="fill:none;fill-opacity:1;stroke:#3050ff;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/>
|
||||
<path d="m 67.514849,37.91524 a 18,18 0 0 1 21.051475,3.312407 18,18 0 0 1 3.137312,21.078282" id="path852" style="fill:none;fill-opacity:1;stroke:#3050ff;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/>
|
||||
<rect transform="rotate(-46.234709)" ry="1.8669105e-13" y="122.08995" x="3.7063529" height="39.963303" width="18.846331" id="rect912" style="opacity:1;fill:#3050ff;fill-opacity:1;stroke:none;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,119 @@
|
|||
import { SearxngSearch } from '@langchain/community/tools/searxng_search'
|
||||
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { getBaseClasses } from '../../../src/utils'
|
||||
|
||||
class Searxng_Tools implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
baseClasses: string[]
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'SearXNG'
|
||||
this.name = 'searXNG'
|
||||
this.version = 1.0
|
||||
this.type = 'SearXNG'
|
||||
this.icon = 'SearXNG.svg'
|
||||
this.category = 'Tools'
|
||||
this.description = 'Wrapper around SearXNG - a free internet metasearch engine'
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Base URL',
|
||||
name: 'apiBase',
|
||||
type: 'string',
|
||||
default: 'http://searxng:8080'
|
||||
},
|
||||
{
|
||||
label: 'Categories',
|
||||
name: 'categories',
|
||||
description:
|
||||
'Comma separated list, specifies the active search categories. (see <a target="_blank" href="https://docs.searxng.org/user/configured_engines.html#configured-engines">Configured Engines</a>)',
|
||||
optional: true,
|
||||
additionalParams: true,
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
label: 'Engines',
|
||||
name: 'engines',
|
||||
description:
|
||||
'Comma separated list, specifies the active search engines. (see <a target="_blank" href="https://docs.searxng.org/user/configured_engines.html#configured-engines">Configured Engines</a>)',
|
||||
optional: true,
|
||||
additionalParams: true,
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
label: 'Language',
|
||||
name: 'language',
|
||||
description: 'Code of the language.',
|
||||
optional: true,
|
||||
additionalParams: true,
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
label: 'Page No.',
|
||||
name: 'pageno',
|
||||
description: 'Search page number.',
|
||||
optional: true,
|
||||
additionalParams: true,
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
label: 'Time Range',
|
||||
name: 'time_range',
|
||||
description:
|
||||
'Time range of search for engines which support it. See if an engine supports time range search in the preferences page of an instance.',
|
||||
optional: true,
|
||||
additionalParams: true,
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
label: 'Safe Search',
|
||||
name: 'safesearch',
|
||||
description:
|
||||
'Filter search results of engines which support safe search. See if an engine supports safe search in the preferences page of an instance.',
|
||||
optional: true,
|
||||
additionalParams: true,
|
||||
type: 'number'
|
||||
}
|
||||
]
|
||||
this.baseClasses = [this.type, ...getBaseClasses(SearxngSearch)]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, _: string): Promise<any> {
|
||||
const apiBase = nodeData.inputs?.apiBase as string
|
||||
const categories = nodeData.inputs?.categories as string
|
||||
const engines = nodeData.inputs?.engines as string
|
||||
const language = nodeData.inputs?.language as string
|
||||
const pageno = nodeData.inputs?.pageno as number
|
||||
const time_range = nodeData.inputs?.time_range as string
|
||||
const safesearch = nodeData.inputs?.safesearch as 0 | 1 | 2 | undefined
|
||||
const format = 'json' as 'json'
|
||||
|
||||
const params = {
|
||||
format,
|
||||
categories,
|
||||
engines,
|
||||
language,
|
||||
pageno,
|
||||
time_range,
|
||||
safesearch
|
||||
}
|
||||
|
||||
const headers = {}
|
||||
|
||||
const tool = new SearxngSearch({
|
||||
apiBase,
|
||||
params,
|
||||
headers
|
||||
})
|
||||
|
||||
return tool
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: Searxng_Tools }
|
||||
|
|
@ -23,7 +23,7 @@ class OpenSearch_VectorStores implements INode {
|
|||
constructor() {
|
||||
this.label = 'OpenSearch'
|
||||
this.name = 'openSearch'
|
||||
this.version = 2.0
|
||||
this.version = 3.0
|
||||
this.type = 'OpenSearch'
|
||||
this.icon = 'opensearch.svg'
|
||||
this.category = 'Vector Stores'
|
||||
|
|
@ -86,6 +86,10 @@ class OpenSearch_VectorStores implements INode {
|
|||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const opensearchURL = getCredentialParam('openSearchUrl', credentialData, nodeData)
|
||||
const user = getCredentialParam('user', credentialData, nodeData)
|
||||
const password = getCredentialParam('password', credentialData, nodeData)
|
||||
|
||||
const client = getOpenSearchClient(opensearchURL, user, password)
|
||||
|
||||
const flattenDocs = docs && docs.length ? flatten(docs) : []
|
||||
const finalDocs = []
|
||||
|
|
@ -95,10 +99,6 @@ class OpenSearch_VectorStores implements INode {
|
|||
}
|
||||
}
|
||||
|
||||
const client = new Client({
|
||||
nodes: [opensearchURL]
|
||||
})
|
||||
|
||||
try {
|
||||
await OpenSearchVectorStore.fromDocuments(finalDocs, embeddings, {
|
||||
client,
|
||||
|
|
@ -120,10 +120,10 @@ class OpenSearch_VectorStores implements INode {
|
|||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const opensearchURL = getCredentialParam('openSearchUrl', credentialData, nodeData)
|
||||
const user = getCredentialParam('user', credentialData, nodeData)
|
||||
const password = getCredentialParam('password', credentialData, nodeData)
|
||||
|
||||
const client = new Client({
|
||||
nodes: [opensearchURL]
|
||||
})
|
||||
const client = getOpenSearchClient(opensearchURL, user, password)
|
||||
|
||||
const vectorStore = new OpenSearchVectorStore(embeddings, {
|
||||
client,
|
||||
|
|
@ -141,4 +141,17 @@ class OpenSearch_VectorStores implements INode {
|
|||
}
|
||||
}
|
||||
|
||||
const getOpenSearchClient = (url: string, user?: string, password?: string): Client => {
|
||||
if (user && password) {
|
||||
const urlObj = new URL(url)
|
||||
urlObj.username = user
|
||||
urlObj.password = password
|
||||
url = urlObj.toString()
|
||||
}
|
||||
|
||||
return new Client({
|
||||
nodes: [url]
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: OpenSearch_VectorStores }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { flatten } from 'lodash'
|
||||
import { Pinecone } from '@pinecone-database/pinecone'
|
||||
import { flatten, isEqual } from 'lodash'
|
||||
import { Pinecone, PineconeConfiguration } from '@pinecone-database/pinecone'
|
||||
import { PineconeStoreParams, PineconeStore } from '@langchain/pinecone'
|
||||
import { Embeddings } from '@langchain/core/embeddings'
|
||||
import { Document } from '@langchain/core/documents'
|
||||
|
|
@ -8,6 +8,23 @@ import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../
|
|||
import { addMMRInputParams, resolveVectorStoreOrRetriever } from '../VectorStoreUtils'
|
||||
import { index } from '../../../src/indexing'
|
||||
|
||||
let pineconeClientSingleton: Pinecone
|
||||
let pineconeClientOption: PineconeConfiguration
|
||||
|
||||
const getPineconeClient = (option: PineconeConfiguration) => {
|
||||
if (!pineconeClientSingleton) {
|
||||
// if client doesn't exists
|
||||
pineconeClientSingleton = new Pinecone(option)
|
||||
pineconeClientOption = option
|
||||
return pineconeClientSingleton
|
||||
} else if (pineconeClientSingleton && !isEqual(option, pineconeClientOption)) {
|
||||
// if client exists but option changed
|
||||
pineconeClientSingleton = new Pinecone(option)
|
||||
return pineconeClientSingleton
|
||||
}
|
||||
return pineconeClientSingleton
|
||||
}
|
||||
|
||||
class Pinecone_VectorStores implements INode {
|
||||
label: string
|
||||
name: string
|
||||
|
|
@ -25,7 +42,7 @@ class Pinecone_VectorStores implements INode {
|
|||
constructor() {
|
||||
this.label = 'Pinecone'
|
||||
this.name = 'pinecone'
|
||||
this.version = 3.0
|
||||
this.version = 4.0
|
||||
this.type = 'Pinecone'
|
||||
this.icon = 'pinecone.svg'
|
||||
this.category = 'Vector Stores'
|
||||
|
|
@ -70,6 +87,15 @@ class Pinecone_VectorStores implements INode {
|
|||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Pinecone Text Key',
|
||||
name: 'pineconeTextKey',
|
||||
description: 'The key in the metadata for storing text. Default to `text`',
|
||||
type: 'string',
|
||||
placeholder: 'text',
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Pinecone Metadata Filter',
|
||||
name: 'pineconeMetadataFilter',
|
||||
|
|
@ -110,13 +136,12 @@ class Pinecone_VectorStores implements INode {
|
|||
const docs = nodeData.inputs?.document as Document[]
|
||||
const embeddings = nodeData.inputs?.embeddings as Embeddings
|
||||
const recordManager = nodeData.inputs?.recordManager
|
||||
const pineconeTextKey = nodeData.inputs?.pineconeTextKey as string
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const pineconeApiKey = getCredentialParam('pineconeApiKey', credentialData, nodeData)
|
||||
|
||||
const client = new Pinecone({
|
||||
apiKey: pineconeApiKey
|
||||
})
|
||||
const client = getPineconeClient({ apiKey: pineconeApiKey })
|
||||
|
||||
const pineconeIndex = client.Index(_index)
|
||||
|
||||
|
|
@ -129,7 +154,8 @@ class Pinecone_VectorStores implements INode {
|
|||
}
|
||||
|
||||
const obj: PineconeStoreParams = {
|
||||
pineconeIndex
|
||||
pineconeIndex,
|
||||
textKey: pineconeTextKey || 'text'
|
||||
}
|
||||
|
||||
if (pineconeNamespace) obj.namespace = pineconeNamespace
|
||||
|
|
@ -165,20 +191,18 @@ class Pinecone_VectorStores implements INode {
|
|||
const pineconeNamespace = nodeData.inputs?.pineconeNamespace as string
|
||||
const pineconeMetadataFilter = nodeData.inputs?.pineconeMetadataFilter
|
||||
const embeddings = nodeData.inputs?.embeddings as Embeddings
|
||||
const pineconeTextKey = nodeData.inputs?.pineconeTextKey as string
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const pineconeApiKey = getCredentialParam('pineconeApiKey', credentialData, nodeData)
|
||||
|
||||
const client = new Pinecone({
|
||||
apiKey: pineconeApiKey
|
||||
})
|
||||
|
||||
await client.describeIndex(index)
|
||||
const client = getPineconeClient({ apiKey: pineconeApiKey })
|
||||
|
||||
const pineconeIndex = client.Index(index)
|
||||
|
||||
const obj: PineconeStoreParams = {
|
||||
pineconeIndex
|
||||
pineconeIndex,
|
||||
textKey: pineconeTextKey || 'text'
|
||||
}
|
||||
|
||||
if (pineconeNamespace) obj.namespace = pineconeNamespace
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class Postgres_VectorStores implements INode {
|
|||
constructor() {
|
||||
this.label = 'Postgres'
|
||||
this.name = 'postgres'
|
||||
this.version = 4.0
|
||||
this.version = 5.0
|
||||
this.type = 'Postgres'
|
||||
this.icon = 'postgres.svg'
|
||||
this.category = 'Vector Stores'
|
||||
|
|
@ -97,6 +97,13 @@ class Postgres_VectorStores implements INode {
|
|||
type: 'number',
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Postgres Metadata Filter',
|
||||
name: 'pgMetadataFilter',
|
||||
type: 'json',
|
||||
additionalParams: true,
|
||||
optional: true
|
||||
}
|
||||
]
|
||||
this.outputs = [
|
||||
|
|
@ -208,6 +215,12 @@ class Postgres_VectorStores implements INode {
|
|||
const output = nodeData.outputs?.output as string
|
||||
const topK = nodeData.inputs?.topK as string
|
||||
const k = topK ? parseFloat(topK) : 4
|
||||
const _pgMetadataFilter = nodeData.inputs?.pgMetadataFilter
|
||||
|
||||
let pgMetadataFilter: any
|
||||
if (_pgMetadataFilter) {
|
||||
pgMetadataFilter = typeof _pgMetadataFilter === 'object' ? _pgMetadataFilter : JSON.parse(_pgMetadataFilter)
|
||||
}
|
||||
|
||||
let additionalConfiguration = {}
|
||||
if (additionalConfig) {
|
||||
|
|
@ -243,7 +256,7 @@ class Postgres_VectorStores implements INode {
|
|||
[ERROR]: uncaughtException: Illegal invocation TypeError: Illegal invocation at Socket.ref (node:net:1524:18) at Connection.ref (.../node_modules/pg/lib/connection.js:183:17) at Client.ref (.../node_modules/pg/lib/client.js:591:21) at BoundPool._pulseQueue (/node_modules/pg-pool/index.js:148:28) at .../node_modules/pg-pool/index.js:184:37 at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
|
||||
*/
|
||||
vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number, filter?: any) => {
|
||||
return await similaritySearchVectorWithScore(query, k, tableName, postgresConnectionOptions, filter)
|
||||
return await similaritySearchVectorWithScore(query, k, tableName, postgresConnectionOptions, filter ?? pgMetadataFilter)
|
||||
}
|
||||
|
||||
if (output === 'retriever') {
|
||||
|
|
@ -251,6 +264,9 @@ class Postgres_VectorStores implements INode {
|
|||
return retriever
|
||||
} else if (output === 'vectorStore') {
|
||||
;(vectorStore as any).k = k
|
||||
if (pgMetadataFilter) {
|
||||
;(vectorStore as any).filter = pgMetadataFilter
|
||||
}
|
||||
return vectorStore
|
||||
}
|
||||
return vectorStore
|
||||
|
|
@ -265,7 +281,8 @@ const similaritySearchVectorWithScore = async (
|
|||
filter?: any
|
||||
) => {
|
||||
const embeddingString = `[${query.join(',')}]`
|
||||
const _filter = filter ?? '{}'
|
||||
let _filter = '{}'
|
||||
if (filter && typeof filter === 'object') _filter = JSON.stringify(filter)
|
||||
|
||||
const queryString = `
|
||||
SELECT *, embedding <=> $1 as "_distance"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { flatten } from 'lodash'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Document } from '@langchain/core/documents'
|
||||
import { Embeddings } from '@langchain/core/embeddings'
|
||||
|
|
@ -212,7 +213,7 @@ class Supabase_VectorStores implements INode {
|
|||
}
|
||||
|
||||
class SupabaseUpsertVectorStore extends SupabaseVectorStore {
|
||||
async addVectors(vectors: number[][], documents: Document[]): Promise<string[]> {
|
||||
async addVectors(vectors: number[][], documents: Document[], options?: { ids?: string[] | number[] }): Promise<string[]> {
|
||||
if (vectors.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
|
@ -222,23 +223,36 @@ class SupabaseUpsertVectorStore extends SupabaseVectorStore {
|
|||
metadata: documents[idx].metadata
|
||||
}))
|
||||
|
||||
let idx = 0
|
||||
const { count } = await this.client.from(this.tableName).select('*', { count: 'exact', head: true })
|
||||
if (count) {
|
||||
idx = count
|
||||
}
|
||||
|
||||
let returnedIds: string[] = []
|
||||
for (let i = 0; i < rows.length; i += this.upsertBatchSize) {
|
||||
const chunk = rows.slice(i, i + this.upsertBatchSize).map((row) => {
|
||||
idx = idx += 1
|
||||
return { id: idx, ...row }
|
||||
const chunk = rows.slice(i, i + this.upsertBatchSize).map((row, j) => {
|
||||
if (options?.ids) {
|
||||
return { id: options.ids[i + j], ...row }
|
||||
}
|
||||
return row
|
||||
})
|
||||
|
||||
const res = await this.client.from(this.tableName).upsert(chunk).select()
|
||||
let res = await this.client.from(this.tableName).upsert(chunk).select()
|
||||
|
||||
if (res.error) {
|
||||
throw new Error(`Error inserting: ${res.error.message} ${res.status} ${res.statusText}`)
|
||||
// If the error is due to null value in column "id", we will generate a new id for the row
|
||||
if (res.error.message.includes(`null value in column "id"`)) {
|
||||
const chunk = rows.slice(i, i + this.upsertBatchSize).map((row, y) => {
|
||||
if (options?.ids) {
|
||||
return { id: options.ids[i + y], ...row }
|
||||
}
|
||||
return { id: uuidv4(), ...row }
|
||||
})
|
||||
res = await this.client.from(this.tableName).upsert(chunk).select()
|
||||
|
||||
if (res.error) {
|
||||
throw new Error(`Error inserting: ${res.error.message} ${res.status} ${res.statusText}`)
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Error inserting: ${res.error.message} ${res.status} ${res.statusText}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (res.data) {
|
||||
returnedIds = returnedIds.concat(res.data.map((row) => row.id))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { flatten } from 'lodash'
|
||||
import { IDocument, ZepClient } from '@getzep/zep-cloud'
|
||||
import { ZepClient } from '@getzep/zep-cloud'
|
||||
import { IZepConfig, ZepVectorStore } from '@getzep/zep-cloud/langchain'
|
||||
import { Embeddings } from 'langchain/embeddings/base'
|
||||
import { Document } from 'langchain/document'
|
||||
|
|
@ -100,7 +100,9 @@ class Zep_CloudVectorStores implements INode {
|
|||
finalDocs.push(new Document(flattenDocs[i]))
|
||||
}
|
||||
}
|
||||
const client = await ZepClient.init(apiKey)
|
||||
const client = new ZepClient({
|
||||
apiKey: apiKey
|
||||
})
|
||||
const zepConfig = {
|
||||
apiKey: apiKey,
|
||||
collectionName: zepCollection,
|
||||
|
|
@ -128,7 +130,9 @@ class Zep_CloudVectorStores implements INode {
|
|||
if (zepMetadataFilter) {
|
||||
zepConfig.filter = typeof zepMetadataFilter === 'object' ? zepMetadataFilter : JSON.parse(zepMetadataFilter)
|
||||
}
|
||||
zepConfig.client = await ZepClient.init(zepConfig.apiKey)
|
||||
zepConfig.client = new ZepClient({
|
||||
apiKey: apiKey
|
||||
})
|
||||
const vectorStore = await ZepExistingVS.init(zepConfig)
|
||||
return resolveVectorStoreOrRetriever(nodeData, vectorStore, zepConfig.filter)
|
||||
}
|
||||
|
|
@ -138,26 +142,6 @@ interface ZepFilter {
|
|||
filter: Record<string, any>
|
||||
}
|
||||
|
||||
function zepDocsToDocumentsAndScore(results: IDocument[]): [Document, number][] {
|
||||
return results.map((d) => [
|
||||
new Document({
|
||||
pageContent: d.content,
|
||||
metadata: d.metadata
|
||||
}),
|
||||
d.score ? d.score : 0
|
||||
])
|
||||
}
|
||||
|
||||
function assignMetadata(value: string | Record<string, unknown> | object | undefined): Record<string, unknown> | undefined {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
return value as Record<string, unknown>
|
||||
}
|
||||
if (value !== undefined) {
|
||||
console.warn('Metadata filters must be an object, Record, or undefined.')
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
class ZepExistingVS extends ZepVectorStore {
|
||||
filter?: Record<string, any>
|
||||
args?: IZepConfig & Partial<ZepFilter>
|
||||
|
|
@ -168,61 +152,6 @@ class ZepExistingVS extends ZepVectorStore {
|
|||
this.args = args
|
||||
}
|
||||
|
||||
async initializeCollection(args: IZepConfig & Partial<ZepFilter>) {
|
||||
this.client = await ZepClient.init(args.apiKey, args.apiUrl)
|
||||
try {
|
||||
this.collection = await this.client.document.getCollection(args.collectionName)
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
if (err.name === 'NotFoundError') {
|
||||
await this.createNewCollection(args)
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async createNewCollection(args: IZepConfig & Partial<ZepFilter>) {
|
||||
this.collection = await this.client.document.addCollection({
|
||||
name: args.collectionName,
|
||||
description: args.description,
|
||||
metadata: args.metadata
|
||||
})
|
||||
}
|
||||
|
||||
async similaritySearchVectorWithScore(
|
||||
query: number[],
|
||||
k: number,
|
||||
filter?: Record<string, unknown> | undefined
|
||||
): Promise<[Document, number][]> {
|
||||
if (filter && this.filter) {
|
||||
throw new Error('cannot provide both `filter` and `this.filter`')
|
||||
}
|
||||
const _filters = filter ?? this.filter
|
||||
const ANDFilters = []
|
||||
for (const filterKey in _filters) {
|
||||
let filterVal = _filters[filterKey]
|
||||
if (typeof filterVal === 'string') filterVal = `"${filterVal}"`
|
||||
ANDFilters.push({ jsonpath: `$[*] ? (@.${filterKey} == ${filterVal})` })
|
||||
}
|
||||
const newfilter = {
|
||||
where: { and: ANDFilters }
|
||||
}
|
||||
await this.initializeCollection(this.args!).catch((err) => {
|
||||
console.error('Error initializing collection:', err)
|
||||
throw err
|
||||
})
|
||||
const results = await this.collection.search(
|
||||
{
|
||||
embedding: new Float32Array(query),
|
||||
metadata: assignMetadata(newfilter)
|
||||
},
|
||||
k
|
||||
)
|
||||
return zepDocsToDocumentsAndScore(results)
|
||||
}
|
||||
|
||||
static async fromExistingIndex(embeddings: Embeddings, dbConfig: IZepConfig & Partial<ZepFilter>): Promise<ZepVectorStore> {
|
||||
return new this(embeddings, dbConfig)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "flowise-components",
|
||||
"version": "1.8.1",
|
||||
"version": "1.8.5",
|
||||
"description": "Flowiseai Components",
|
||||
"main": "dist/src/index",
|
||||
"types": "dist/src/index.d.ts",
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
"@dqbd/tiktoken": "^1.0.7",
|
||||
"@e2b/code-interpreter": "^0.0.5",
|
||||
"@elastic/elasticsearch": "^8.9.0",
|
||||
"@getzep/zep-cloud": "npm:@getzep/zep-js@next",
|
||||
"@getzep/zep-cloud": "~1.0.7",
|
||||
"@getzep/zep-js": "^0.9.0",
|
||||
"@gomomento/sdk": "^1.51.1",
|
||||
"@gomomento/sdk-core": "^1.51.1",
|
||||
|
|
@ -48,10 +48,11 @@
|
|||
"@langchain/openai": "^0.0.30",
|
||||
"@langchain/pinecone": "^0.0.3",
|
||||
"@langchain/weaviate": "^0.0.1",
|
||||
"@mendable/firecrawl-js": "^0.0.28",
|
||||
"@mistralai/mistralai": "0.1.3",
|
||||
"@notionhq/client": "^2.2.8",
|
||||
"@opensearch-project/opensearch": "^1.2.0",
|
||||
"@pinecone-database/pinecone": "2.2.0",
|
||||
"@pinecone-database/pinecone": "2.2.2",
|
||||
"@qdrant/js-client-rest": "^1.2.2",
|
||||
"@supabase/supabase-js": "^2.29.0",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
|
|
@ -84,6 +85,7 @@
|
|||
"langfuse": "3.3.4",
|
||||
"langfuse-langchain": "^3.3.4",
|
||||
"langsmith": "0.1.6",
|
||||
"langwatch": "^0.1.1",
|
||||
"linkifyjs": "^4.1.1",
|
||||
"llamaindex": "^0.3.13",
|
||||
"lodash": "^4.17.21",
|
||||
|
|
@ -96,7 +98,7 @@
|
|||
"node-html-markdown": "^1.3.0",
|
||||
"notion-to-md": "^3.1.1",
|
||||
"object-hash": "^3.0.0",
|
||||
"openai": "^4.38.3",
|
||||
"openai": "^4.51.0",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"pdfjs-dist": "^3.7.107",
|
||||
"pg": "^8.11.2",
|
||||
|
|
|
|||
|
|
@ -427,9 +427,10 @@ export class AgentExecutor extends BaseChain<ChainValues, AgentExecutorOutput> {
|
|||
usedTools.push({
|
||||
tool: tool.name,
|
||||
toolInput: action.toolInput as any,
|
||||
toolOutput: observation.includes(SOURCE_DOCUMENTS_PREFIX)
|
||||
? observation.split(SOURCE_DOCUMENTS_PREFIX)[0]
|
||||
: observation
|
||||
toolOutput:
|
||||
typeof observation === 'string' && observation.includes(SOURCE_DOCUMENTS_PREFIX)
|
||||
? observation.split(SOURCE_DOCUMENTS_PREFIX)[0]
|
||||
: observation
|
||||
})
|
||||
} else {
|
||||
observation = `${action.tool} is not a valid tool, try another one.`
|
||||
|
|
@ -449,7 +450,7 @@ export class AgentExecutor extends BaseChain<ChainValues, AgentExecutorOutput> {
|
|||
return { action, observation: observation ?? '' }
|
||||
}
|
||||
}
|
||||
if (observation?.includes(SOURCE_DOCUMENTS_PREFIX)) {
|
||||
if (typeof observation === 'string' && observation.includes(SOURCE_DOCUMENTS_PREFIX)) {
|
||||
const observationArray = observation.split(SOURCE_DOCUMENTS_PREFIX)
|
||||
observation = observationArray[0]
|
||||
const docs = observationArray[1]
|
||||
|
|
@ -558,7 +559,7 @@ export class AgentExecutor extends BaseChain<ChainValues, AgentExecutorOutput> {
|
|||
input: this.input
|
||||
}
|
||||
)
|
||||
if (observation?.includes(SOURCE_DOCUMENTS_PREFIX)) {
|
||||
if (typeof observation === 'string' && observation.includes(SOURCE_DOCUMENTS_PREFIX)) {
|
||||
const observationArray = observation.split(SOURCE_DOCUMENTS_PREFIX)
|
||||
observation = observationArray[0]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { LunaryHandler } from '@langchain/community/callbacks/handlers/lunary'
|
|||
|
||||
import { getCredentialData, getCredentialParam, getEnvironmentVariable } from './utils'
|
||||
import { ICommonObject, INodeData } from './Interface'
|
||||
import { LangWatch, LangWatchSpan, LangWatchTrace, autoconvertTypedValues } from 'langwatch'
|
||||
|
||||
interface AgentRun extends Run {
|
||||
actions: AgentAction[]
|
||||
|
|
@ -295,6 +296,17 @@ export const additionalCallbacks = async (nodeData: INodeData, options: ICommonO
|
|||
|
||||
const handler = new LunaryHandler(lunaryFields)
|
||||
callbacks.push(handler)
|
||||
} else if (provider === 'langWatch') {
|
||||
const langWatchApiKey = getCredentialParam('langWatchApiKey', credentialData, nodeData)
|
||||
const langWatchEndpoint = getCredentialParam('langWatchEndpoint', credentialData, nodeData)
|
||||
|
||||
const langwatch = new LangWatch({
|
||||
apiKey: langWatchApiKey,
|
||||
endpoint: langWatchEndpoint
|
||||
})
|
||||
|
||||
const trace = langwatch.getTrace()
|
||||
callbacks.push(trace.getLangChainCallback())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -362,6 +374,16 @@ export class AnalyticHandler {
|
|||
})
|
||||
|
||||
this.handlers['lunary'] = { client: lunary }
|
||||
} else if (provider === 'langWatch') {
|
||||
const langWatchApiKey = getCredentialParam('langWatchApiKey', credentialData, this.nodeData)
|
||||
const langWatchEndpoint = getCredentialParam('langWatchEndpoint', credentialData, this.nodeData)
|
||||
|
||||
const langwatch = new LangWatch({
|
||||
apiKey: langWatchApiKey,
|
||||
endpoint: langWatchEndpoint
|
||||
})
|
||||
|
||||
this.handlers['langWatch'] = { client: langwatch }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -374,7 +396,8 @@ export class AnalyticHandler {
|
|||
const returnIds: ICommonObject = {
|
||||
langSmith: {},
|
||||
langFuse: {},
|
||||
lunary: {}
|
||||
lunary: {},
|
||||
langWatch: {}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.handlers, 'langSmith')) {
|
||||
|
|
@ -462,6 +485,33 @@ export class AnalyticHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.handlers, 'langWatch')) {
|
||||
let langwatchTrace: LangWatchTrace
|
||||
|
||||
if (!parentIds || !Object.keys(parentIds).length) {
|
||||
const langwatch: LangWatch = this.handlers['langWatch'].client
|
||||
langwatchTrace = langwatch.getTrace({
|
||||
name,
|
||||
metadata: { tags: ['openai-assistant'], threadId: this.options.chatId },
|
||||
...this.nodeData?.inputs?.analytics?.langWatch
|
||||
})
|
||||
} else {
|
||||
langwatchTrace = this.handlers['langWatch'].trace[parentIds['langWatch']]
|
||||
}
|
||||
|
||||
if (langwatchTrace) {
|
||||
const span = langwatchTrace.startSpan({
|
||||
name,
|
||||
type: 'chain',
|
||||
input: autoconvertTypedValues(input)
|
||||
})
|
||||
this.handlers['langWatch'].trace = { [langwatchTrace.traceId]: langwatchTrace }
|
||||
this.handlers['langWatch'].span = { [span.spanId]: span }
|
||||
returnIds['langWatch'].trace = langwatchTrace.traceId
|
||||
returnIds['langWatch'].span = span.spanId
|
||||
}
|
||||
}
|
||||
|
||||
return returnIds
|
||||
}
|
||||
|
||||
|
|
@ -510,6 +560,15 @@ export class AnalyticHandler {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.handlers, 'langWatch')) {
|
||||
const span: LangWatchSpan | undefined = this.handlers['langWatch'].span[returnIds['langWatch'].span]
|
||||
if (span) {
|
||||
span.end({
|
||||
output: autoconvertTypedValues(output)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onChainError(returnIds: ICommonObject, error: string | object, shutdown = false) {
|
||||
|
|
@ -559,6 +618,15 @@ export class AnalyticHandler {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.handlers, 'langWatch')) {
|
||||
const span: LangWatchSpan | undefined = this.handlers['langWatch'].span[returnIds['langWatch'].span]
|
||||
if (span) {
|
||||
span.end({
|
||||
error
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onLLMStart(name: string, input: string, parentIds: ICommonObject) {
|
||||
|
|
@ -614,6 +682,18 @@ export class AnalyticHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.handlers, 'langWatch')) {
|
||||
const trace: LangWatchTrace | undefined = this.handlers['langWatch'].trace[parentIds['langWatch'].trace]
|
||||
if (trace) {
|
||||
const span = trace.startLLMSpan({
|
||||
name,
|
||||
input: autoconvertTypedValues(input)
|
||||
})
|
||||
this.handlers['langWatch'].span = { [span.spanId]: span }
|
||||
returnIds['langWatch'].span = span.spanId
|
||||
}
|
||||
}
|
||||
|
||||
return returnIds
|
||||
}
|
||||
|
||||
|
|
@ -650,6 +730,15 @@ export class AnalyticHandler {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.handlers, 'langWatch')) {
|
||||
const span: LangWatchSpan | undefined = this.handlers['langWatch'].span[returnIds['langWatch'].span]
|
||||
if (span) {
|
||||
span.end({
|
||||
output: autoconvertTypedValues(output)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onLLMError(returnIds: ICommonObject, error: string | object) {
|
||||
|
|
@ -685,6 +774,15 @@ export class AnalyticHandler {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.handlers, 'langWatch')) {
|
||||
const span: LangWatchSpan | undefined = this.handlers['langWatch'].span[returnIds['langWatch'].span]
|
||||
if (span) {
|
||||
span.end({
|
||||
error
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onToolStart(name: string, input: string | object, parentIds: ICommonObject) {
|
||||
|
|
@ -740,6 +838,19 @@ export class AnalyticHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.handlers, 'langWatch')) {
|
||||
const trace: LangWatchTrace | undefined = this.handlers['langWatch'].trace[parentIds['langWatch'].trace]
|
||||
if (trace) {
|
||||
const span = trace.startSpan({
|
||||
name,
|
||||
type: 'tool',
|
||||
input: autoconvertTypedValues(input)
|
||||
})
|
||||
this.handlers['langWatch'].span = { [span.spanId]: span }
|
||||
returnIds['langWatch'].span = span.spanId
|
||||
}
|
||||
}
|
||||
|
||||
return returnIds
|
||||
}
|
||||
|
||||
|
|
@ -776,6 +887,15 @@ export class AnalyticHandler {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.handlers, 'langWatch')) {
|
||||
const span: LangWatchSpan | undefined = this.handlers['langWatch'].span[returnIds['langWatch'].span]
|
||||
if (span) {
|
||||
span.end({
|
||||
output: autoconvertTypedValues(output)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onToolError(returnIds: ICommonObject, error: string | object) {
|
||||
|
|
@ -811,5 +931,14 @@ export class AnalyticHandler {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.handlers, 'langWatch')) {
|
||||
const span: LangWatchSpan | undefined = this.handlers['langWatch'].span[returnIds['langWatch'].span]
|
||||
if (span) {
|
||||
span.end({
|
||||
error
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,14 @@ export const addImagesToMessages = async (
|
|||
// as the image is stored in the server, read the file and convert it to base64
|
||||
bf = 'data:' + upload.mime + ';base64,' + contents.toString('base64')
|
||||
|
||||
imageContent.push({
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: bf,
|
||||
detail: multiModalOption.image.imageResolution ?? 'low'
|
||||
}
|
||||
})
|
||||
} else if (upload.type == 'url' && bf) {
|
||||
imageContent.push({
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export const availableDependencies = [
|
|||
'langchain',
|
||||
'langfuse',
|
||||
'langsmith',
|
||||
'langwatch',
|
||||
'linkifyjs',
|
||||
'lunary',
|
||||
'mammoth',
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ PORT=3000
|
|||
# FLOWISE_SECRETKEY_OVERWRITE=myencryptionkey
|
||||
# FLOWISE_FILE_SIZE_LIMIT=50mb
|
||||
|
||||
# DISABLE_CHATFLOW_REUSE=true
|
||||
|
||||
# DEBUG=true
|
||||
# LOG_PATH=/your_log_path/.flowise/logs
|
||||
# LOG_LEVEL=info (error | warn | info | verbose | debug)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,557 @@
|
|||
{
|
||||
"description": "Prompt engineering team working together to craft Worker Prompts for your AgentFlow.",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "supervisor_0",
|
||||
"position": {
|
||||
"x": 485.1357844985962,
|
||||
"y": 324.1719351589139
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "supervisor_0",
|
||||
"label": "Supervisor",
|
||||
"version": 1,
|
||||
"name": "supervisor",
|
||||
"type": "Supervisor",
|
||||
"baseClasses": ["Supervisor"],
|
||||
"category": "Multi Agents",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Supervisor Name",
|
||||
"name": "supervisorName",
|
||||
"type": "string",
|
||||
"placeholder": "Supervisor",
|
||||
"default": "Supervisor",
|
||||
"id": "supervisor_0-input-supervisorName-string"
|
||||
},
|
||||
{
|
||||
"label": "Supervisor Prompt",
|
||||
"name": "supervisorPrompt",
|
||||
"type": "string",
|
||||
"description": "Prompt must contains {team_members}",
|
||||
"rows": 4,
|
||||
"default": "You are a supervisor tasked with managing a conversation between the following workers: {team_members}.\nGiven the following user request, respond with the worker to act next.\nEach worker will perform a task and respond with their results and status.\nWhen finished, respond with FINISH.\nSelect strategically to minimize the number of steps taken.",
|
||||
"additionalParams": true,
|
||||
"id": "supervisor_0-input-supervisorPrompt-string"
|
||||
},
|
||||
{
|
||||
"label": "Recursion Limit",
|
||||
"name": "recursionLimit",
|
||||
"type": "number",
|
||||
"description": "Maximum number of times a call can recurse. If not provided, defaults to 100.",
|
||||
"default": 100,
|
||||
"additionalParams": true,
|
||||
"id": "supervisor_0-input-recursionLimit-number"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
{
|
||||
"label": "Tool Calling Chat Model",
|
||||
"name": "model",
|
||||
"type": "BaseChatModel",
|
||||
"description": "Only compatible with models that are capable of function calling: ChatOpenAI, ChatMistral, ChatAnthropic, ChatGoogleGenerativeAI, GroqChat. Best result with GPT-4 model",
|
||||
"id": "supervisor_0-input-model-BaseChatModel"
|
||||
},
|
||||
{
|
||||
"label": "Input Moderation",
|
||||
"description": "Detect text that could generate harmful output and prevent it from being sent to the language model",
|
||||
"name": "inputModeration",
|
||||
"type": "Moderation",
|
||||
"optional": true,
|
||||
"list": true,
|
||||
"id": "supervisor_0-input-inputModeration-Moderation"
|
||||
}
|
||||
],
|
||||
"inputs": {
|
||||
"supervisorName": "Supervisor",
|
||||
"supervisorPrompt": "You are a supervisor tasked with managing a conversation between the following workers: {team_members}.\nGiven the following user request, respond with the worker to act next.\nEach worker will perform a task and respond with their results and status.\nWhen finished, respond with FINISH.\nSelect strategically to minimize the number of steps taken.",
|
||||
"model": "{{chatOpenAI_0.data.instance}}",
|
||||
"recursionLimit": 100,
|
||||
"inputModeration": ""
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "supervisor_0-output-supervisor-Supervisor",
|
||||
"name": "supervisor",
|
||||
"label": "Supervisor",
|
||||
"description": "",
|
||||
"type": "Supervisor"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"width": 300,
|
||||
"height": 430,
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 485.1357844985962,
|
||||
"y": 324.1719351589139
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"id": "worker_0",
|
||||
"position": {
|
||||
"x": 807.6882204663332,
|
||||
"y": 326.15881845953294
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "worker_0",
|
||||
"label": "Worker",
|
||||
"version": 1,
|
||||
"name": "worker",
|
||||
"type": "Worker",
|
||||
"baseClasses": ["Worker"],
|
||||
"category": "Multi Agents",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Worker Name",
|
||||
"name": "workerName",
|
||||
"type": "string",
|
||||
"placeholder": "Worker",
|
||||
"id": "worker_0-input-workerName-string"
|
||||
},
|
||||
{
|
||||
"label": "Worker Prompt",
|
||||
"name": "workerPrompt",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"default": "You are a research assistant who can search for up-to-date info using search engine.",
|
||||
"id": "worker_0-input-workerPrompt-string"
|
||||
},
|
||||
{
|
||||
"label": "Format Prompt Values",
|
||||
"name": "promptValues",
|
||||
"type": "json",
|
||||
"optional": true,
|
||||
"acceptVariable": true,
|
||||
"list": true,
|
||||
"id": "worker_0-input-promptValues-json"
|
||||
},
|
||||
{
|
||||
"label": "Max Iterations",
|
||||
"name": "maxIterations",
|
||||
"type": "number",
|
||||
"optional": true,
|
||||
"id": "worker_0-input-maxIterations-number"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
{
|
||||
"label": "Tools",
|
||||
"name": "tools",
|
||||
"type": "Tool",
|
||||
"list": true,
|
||||
"optional": true,
|
||||
"id": "worker_0-input-tools-Tool"
|
||||
},
|
||||
{
|
||||
"label": "Supervisor",
|
||||
"name": "supervisor",
|
||||
"type": "Supervisor",
|
||||
"id": "worker_0-input-supervisor-Supervisor"
|
||||
},
|
||||
{
|
||||
"label": "Tool Calling Chat Model",
|
||||
"name": "model",
|
||||
"type": "BaseChatModel",
|
||||
"optional": true,
|
||||
"description": "Only compatible with models that are capable of function calling: ChatOpenAI, ChatMistral, ChatAnthropic, ChatGoogleGenerativeAI, ChatVertexAI, GroqChat. If not specified, supervisor's model will be used",
|
||||
"id": "worker_0-input-model-BaseChatModel"
|
||||
}
|
||||
],
|
||||
"inputs": {
|
||||
"workerName": " Prompt Creator",
|
||||
"workerPrompt": "You are a Prompt Engineer. Your job is to craft system prompts for AI Agents based on user requests.\n\nHere is an example:\n\n1. User asks you to craft two AI Agent prompt messages for \"researching leads and creating personalized email drafts for the sales team\".\n\n2. You generate the following:\n\nAGENT 1\n\nName: \nLead Research\n\nSytyem Prompt: \nAs a member of the sales team at company, your mission is to explore the digital landscape for potential leads. Equipped with advanced tools and a strategic approach, you analyze data, trends, and interactions to discover opportunities that others might miss. Your efforts are vital in creating pathways for meaningful engagements and driving the company's growth.\nYour goal is to identify high-value leads that align with our ideal customer profile.\nPerform a thorough analysis of lead_company, a company that has recently shown interest in our solutions. Use all available data sources to create a detailed profile, concentrating on key decision-makers, recent business developments, and potential needs that match our offerings. This task is essential for effectively customizing our engagement strategy.\nAvoid making assumptions and only use information you are certain about.\nYou should produce a comprehensive report on lead_person, including company background, key personnel, recent milestones, and identified needs. Emphasize potential areas where our solutions can add value and suggest tailored engagement strategies. Pass the info to Lead Sales Representative.\n\nAGENT 2\n\nName: \nLead Sales Representative\n\nSystem Prompt: \nYou play a crucial role within company as the link between potential clients and the solutions they need. By crafting engaging, personalized messages, you not only inform leads about our company offerings but also make them feel valued and understood. Your role is essential in transforming interest into action, guiding leads from initial curiosity to committed engagement.\nYour goal is to nurture leads with tailored, compelling communications.\nLeveraging the insights from the lead profiling report on lead_company, create a personalized outreach campaign targeting lead_person, the position of lead_company. he campaign should highlight their recent lead_activity and demonstrate how our solutions can support their objectives. Your communication should align with lead_company's company culture and values, showcasing a thorough understanding of their business and needs. Avoid making assumptions and use only verified information.\nThe output should be a series of personalized email drafts customized for lead_company, specifically addressing lead_person. Each draft should present a compelling narrative that connects our solutions to their recent accomplishments and future goals. Ensure the tone is engaging, professional, and consistent with lead_company's corporate identity. Keep in natural, don't use strange and fancy words.\n\n3. IMPORTANT: Notice how the prompts in this example work together and are connected by \"Pass the info to Lead Sales Representative.\" The first prompt focuses on researching leads, while the second leverages that information to create personalized email drafts. This creates a cohesive workflow for the AI Agents.\n\n4. If the AI agent needs to use a tool to perform its task, it will indicate this on the system prompt, but you will not write any code for them (they already have the code for the tools they use).",
|
||||
"tools": "",
|
||||
"supervisor": "{{supervisor_0.data.instance}}",
|
||||
"model": "",
|
||||
"promptValues": "",
|
||||
"maxIterations": ""
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "worker_0-output-worker-Worker",
|
||||
"name": "worker",
|
||||
"label": "Worker",
|
||||
"description": "",
|
||||
"type": "Worker"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"width": 300,
|
||||
"height": 807,
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 807.6882204663332,
|
||||
"y": 326.15881845953294
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"id": "worker_1",
|
||||
"position": {
|
||||
"x": 1149.1084792409956,
|
||||
"y": 324.68074278187794
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "worker_1",
|
||||
"label": "Worker",
|
||||
"version": 1,
|
||||
"name": "worker",
|
||||
"type": "Worker",
|
||||
"baseClasses": ["Worker"],
|
||||
"category": "Multi Agents",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Worker Name",
|
||||
"name": "workerName",
|
||||
"type": "string",
|
||||
"placeholder": "Worker",
|
||||
"id": "worker_1-input-workerName-string"
|
||||
},
|
||||
{
|
||||
"label": "Worker Prompt",
|
||||
"name": "workerPrompt",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"default": "You are a research assistant who can search for up-to-date info using search engine.",
|
||||
"id": "worker_1-input-workerPrompt-string"
|
||||
},
|
||||
{
|
||||
"label": "Format Prompt Values",
|
||||
"name": "promptValues",
|
||||
"type": "json",
|
||||
"optional": true,
|
||||
"acceptVariable": true,
|
||||
"list": true,
|
||||
"id": "worker_1-input-promptValues-json"
|
||||
},
|
||||
{
|
||||
"label": "Max Iterations",
|
||||
"name": "maxIterations",
|
||||
"type": "number",
|
||||
"optional": true,
|
||||
"id": "worker_1-input-maxIterations-number"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
{
|
||||
"label": "Tools",
|
||||
"name": "tools",
|
||||
"type": "Tool",
|
||||
"list": true,
|
||||
"optional": true,
|
||||
"id": "worker_1-input-tools-Tool"
|
||||
},
|
||||
{
|
||||
"label": "Supervisor",
|
||||
"name": "supervisor",
|
||||
"type": "Supervisor",
|
||||
"id": "worker_1-input-supervisor-Supervisor"
|
||||
},
|
||||
{
|
||||
"label": "Tool Calling Chat Model",
|
||||
"name": "model",
|
||||
"type": "BaseChatModel",
|
||||
"optional": true,
|
||||
"description": "Only compatible with models that are capable of function calling: ChatOpenAI, ChatMistral, ChatAnthropic, ChatGoogleGenerativeAI, ChatVertexAI, GroqChat. If not specified, supervisor's model will be used",
|
||||
"id": "worker_1-input-model-BaseChatModel"
|
||||
}
|
||||
],
|
||||
"inputs": {
|
||||
"workerName": "Prompt Reviewer",
|
||||
"workerPrompt": "You are a meticulous and insightful AI specializing in reviewing and enhancing custom prompts created for other AI agents. Your role is crucial in ensuring that the prompts are not only accurate and clear but also optimized for the best performance of the AI agents. You pay close attention to detail and strive for perfection in prompt design. Your goal is to review and improve the custom prompts created by the prompt_creator AI. Examine the provided system prompt thoroughly, identifying any areas that can be improved for clarity, specificity, or effectiveness. Suggest modifications that enhance the prompt's structure, language, and overall quality. Ensure that the final prompt is free from ambiguity and provides precise, actionable instructions for the AI agent. The output should be an improved version of the system prompt, with clear annotations or explanations of the changes made to enhance its quality and effectiveness.\n",
|
||||
"tools": "",
|
||||
"supervisor": "{{supervisor_0.data.instance}}",
|
||||
"model": "",
|
||||
"promptValues": "",
|
||||
"maxIterations": ""
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "worker_1-output-worker-Worker",
|
||||
"name": "worker",
|
||||
"label": "Worker",
|
||||
"description": "",
|
||||
"type": "Worker"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"width": 300,
|
||||
"height": 807,
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 1149.1084792409956,
|
||||
"y": 324.68074278187794
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"id": "chatOpenAI_0",
|
||||
"position": {
|
||||
"x": 134.3531319624069,
|
||||
"y": 318.3354688270578
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "chatOpenAI_0",
|
||||
"label": "ChatOpenAI",
|
||||
"version": 6,
|
||||
"name": "chatOpenAI",
|
||||
"type": "ChatOpenAI",
|
||||
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel", "Runnable"],
|
||||
"category": "Chat Models",
|
||||
"description": "Wrapper around OpenAI large language models that use the Chat endpoint",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Connect Credential",
|
||||
"name": "credential",
|
||||
"type": "credential",
|
||||
"credentialNames": ["openAIApi"],
|
||||
"id": "chatOpenAI_0-input-credential-credential"
|
||||
},
|
||||
{
|
||||
"label": "Model Name",
|
||||
"name": "modelName",
|
||||
"type": "asyncOptions",
|
||||
"loadMethod": "listModels",
|
||||
"default": "gpt-3.5-turbo",
|
||||
"id": "chatOpenAI_0-input-modelName-asyncOptions"
|
||||
},
|
||||
{
|
||||
"label": "Temperature",
|
||||
"name": "temperature",
|
||||
"type": "number",
|
||||
"step": 0.1,
|
||||
"default": 0.9,
|
||||
"optional": true,
|
||||
"id": "chatOpenAI_0-input-temperature-number"
|
||||
},
|
||||
{
|
||||
"label": "Max Tokens",
|
||||
"name": "maxTokens",
|
||||
"type": "number",
|
||||
"step": 1,
|
||||
"optional": true,
|
||||
"additionalParams": true,
|
||||
"id": "chatOpenAI_0-input-maxTokens-number"
|
||||
},
|
||||
{
|
||||
"label": "Top Probability",
|
||||
"name": "topP",
|
||||
"type": "number",
|
||||
"step": 0.1,
|
||||
"optional": true,
|
||||
"additionalParams": true,
|
||||
"id": "chatOpenAI_0-input-topP-number"
|
||||
},
|
||||
{
|
||||
"label": "Frequency Penalty",
|
||||
"name": "frequencyPenalty",
|
||||
"type": "number",
|
||||
"step": 0.1,
|
||||
"optional": true,
|
||||
"additionalParams": true,
|
||||
"id": "chatOpenAI_0-input-frequencyPenalty-number"
|
||||
},
|
||||
{
|
||||
"label": "Presence Penalty",
|
||||
"name": "presencePenalty",
|
||||
"type": "number",
|
||||
"step": 0.1,
|
||||
"optional": true,
|
||||
"additionalParams": true,
|
||||
"id": "chatOpenAI_0-input-presencePenalty-number"
|
||||
},
|
||||
{
|
||||
"label": "Timeout",
|
||||
"name": "timeout",
|
||||
"type": "number",
|
||||
"step": 1,
|
||||
"optional": true,
|
||||
"additionalParams": true,
|
||||
"id": "chatOpenAI_0-input-timeout-number"
|
||||
},
|
||||
{
|
||||
"label": "BasePath",
|
||||
"name": "basepath",
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"additionalParams": true,
|
||||
"id": "chatOpenAI_0-input-basepath-string"
|
||||
},
|
||||
{
|
||||
"label": "BaseOptions",
|
||||
"name": "baseOptions",
|
||||
"type": "json",
|
||||
"optional": true,
|
||||
"additionalParams": true,
|
||||
"id": "chatOpenAI_0-input-baseOptions-json"
|
||||
},
|
||||
{
|
||||
"label": "Allow Image Uploads",
|
||||
"name": "allowImageUploads",
|
||||
"type": "boolean",
|
||||
"description": "Automatically uses gpt-4-vision-preview when image is being uploaded from chat. Only works with LLMChain, Conversation Chain, ReAct Agent, and Conversational Agent",
|
||||
"default": false,
|
||||
"optional": true,
|
||||
"id": "chatOpenAI_0-input-allowImageUploads-boolean"
|
||||
},
|
||||
{
|
||||
"label": "Image Resolution",
|
||||
"description": "This parameter controls the resolution in which the model views the image.",
|
||||
"name": "imageResolution",
|
||||
"type": "options",
|
||||
"options": [
|
||||
{
|
||||
"label": "Low",
|
||||
"name": "low"
|
||||
},
|
||||
{
|
||||
"label": "High",
|
||||
"name": "high"
|
||||
},
|
||||
{
|
||||
"label": "Auto",
|
||||
"name": "auto"
|
||||
}
|
||||
],
|
||||
"default": "low",
|
||||
"optional": false,
|
||||
"additionalParams": true,
|
||||
"id": "chatOpenAI_0-input-imageResolution-options"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
{
|
||||
"label": "Cache",
|
||||
"name": "cache",
|
||||
"type": "BaseCache",
|
||||
"optional": true,
|
||||
"id": "chatOpenAI_0-input-cache-BaseCache"
|
||||
}
|
||||
],
|
||||
"inputs": {
|
||||
"cache": "",
|
||||
"modelName": "gpt-4o",
|
||||
"temperature": "0.4",
|
||||
"maxTokens": "",
|
||||
"topP": "",
|
||||
"frequencyPenalty": "",
|
||||
"presencePenalty": "",
|
||||
"timeout": "",
|
||||
"basepath": "",
|
||||
"baseOptions": "",
|
||||
"allowImageUploads": "",
|
||||
"imageResolution": "low"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable",
|
||||
"name": "chatOpenAI",
|
||||
"label": "ChatOpenAI",
|
||||
"description": "Wrapper around OpenAI large language models that use the Chat endpoint",
|
||||
"type": "ChatOpenAI | BaseChatModel | BaseLanguageModel | Runnable"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"width": 300,
|
||||
"height": 668,
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 134.3531319624069,
|
||||
"y": 318.3354688270578
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"id": "stickyNote_0",
|
||||
"position": {
|
||||
"x": -204.73108806492982,
|
||||
"y": 321.243965769327
|
||||
},
|
||||
"type": "stickyNote",
|
||||
"data": {
|
||||
"id": "stickyNote_0",
|
||||
"label": "Sticky Note",
|
||||
"version": 1,
|
||||
"name": "stickyNote",
|
||||
"type": "StickyNote",
|
||||
"baseClasses": ["StickyNote"],
|
||||
"category": "Utilities",
|
||||
"description": "Add a sticky note",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "",
|
||||
"name": "note",
|
||||
"type": "string",
|
||||
"rows": 1,
|
||||
"placeholder": "Type something here",
|
||||
"optional": true,
|
||||
"id": "stickyNote_0-input-note-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"note": "How it works?\nSimply explain the app you want to create, and it will generate the system prompt for each Worker.\n\nExample:\nI want to create an AI app with two AI agents. One agent would perform a Google search using the SerpApi tool on any topic provided by the user. The other agent would then send the information to my email,\ntest@test.test, using a custom tool at its disposal."
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "stickyNote_0-output-stickyNote-StickyNote",
|
||||
"name": "stickyNote",
|
||||
"label": "StickyNote",
|
||||
"description": "Add a sticky note",
|
||||
"type": "StickyNote"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"width": 300,
|
||||
"height": 283,
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": -204.73108806492982,
|
||||
"y": 321.243965769327
|
||||
},
|
||||
"dragging": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "supervisor_0",
|
||||
"sourceHandle": "supervisor_0-output-supervisor-Supervisor",
|
||||
"target": "worker_0",
|
||||
"targetHandle": "worker_0-input-supervisor-Supervisor",
|
||||
"type": "buttonedge",
|
||||
"id": "supervisor_0-supervisor_0-output-supervisor-Supervisor-worker_0-worker_0-input-supervisor-Supervisor"
|
||||
},
|
||||
{
|
||||
"source": "supervisor_0",
|
||||
"sourceHandle": "supervisor_0-output-supervisor-Supervisor",
|
||||
"target": "worker_1",
|
||||
"targetHandle": "worker_1-input-supervisor-Supervisor",
|
||||
"type": "buttonedge",
|
||||
"id": "supervisor_0-supervisor_0-output-supervisor-Supervisor-worker_1-worker_1-input-supervisor-Supervisor"
|
||||
},
|
||||
{
|
||||
"source": "chatOpenAI_0",
|
||||
"sourceHandle": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable",
|
||||
"target": "supervisor_0",
|
||||
"targetHandle": "supervisor_0-input-model-BaseChatModel",
|
||||
"type": "buttonedge",
|
||||
"id": "chatOpenAI_0-chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable-supervisor_0-supervisor_0-input-model-BaseChatModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "perplexity_ai_search",
|
||||
"framework": "Langchain",
|
||||
"description": "Useful when conducting research using Perplexity AI online model.",
|
||||
"color": "linear-gradient(rgb(155,190,84), rgb(176,69,245))",
|
||||
"iconSrc": "https://raw.githubusercontent.com/AsharibAli/project-images/main/perplexity-ai-icon.svg",
|
||||
"schema": "[{\"id\":1,\"property\":\"query\",\"description\":\"Query for research\",\"type\":\"string\",\"required\":true}]",
|
||||
"func": "const fetch = require('node-fetch');\nconst apiKey = 'YOUR_PERPLEXITY_API_KEY'; // Put Your Perplexity AI API key here\n\nconst query = $query;\n\nconst options = {\n\tmethod: 'POST',\n\theaders: {\n\t\t'Content-Type': 'application/json',\n\t\t'Authorization': 'Bearer <YOUR_PERPLEXITY_API_KEY>'\n\t},\n\tbody: JSON.stringify({\n\t\tmodel: 'llama-3-sonar-small-32k-online', // Model\n\t\tmessages: [\n\t\t\t{\n\t\t\t\trole: 'system',\n\t\t\t\tcontent: 'You are a research assistant.'\n\t\t\t},\n\t\t\t{\n\t\t\t\trole: 'user',\n\t\t\t\tcontent: query\n\t\t\t}\n\t\t]\n\t})\n};\n\ntry {\n\tconst response = await fetch('https://api.perplexity.ai/chat/completions', options);\n\tconst data = await response.json();\n\treturn JSON.stringify(data);\n} catch (error) {\n\tconsole.error(error);\n\treturn 'Error occurred while fetching data from Perplexity AI';\n}\n\n// For more details: https://docs.perplexity.ai/docs/getting-started"
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "flowise",
|
||||
"version": "1.8.1",
|
||||
"version": "1.8.3",
|
||||
"description": "Flowiseai Server",
|
||||
"main": "dist/index",
|
||||
"types": "dist/index.d.ts",
|
||||
|
|
@ -75,7 +75,7 @@
|
|||
"moment-timezone": "^0.5.34",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.9.2",
|
||||
"openai": "^4.20.0",
|
||||
"openai": "^4.51.0",
|
||||
"pg": "^8.11.1",
|
||||
"posthog-node": "^3.5.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { getUserHome } from './utils'
|
|||
import { entities } from './database/entities'
|
||||
import { sqliteMigrations } from './database/migrations/sqlite'
|
||||
import { mysqlMigrations } from './database/migrations/mysql'
|
||||
import { mariadbMigrations } from './database/migrations/mariadb'
|
||||
import { postgresMigrations } from './database/migrations/postgres'
|
||||
|
||||
let appDataSource: DataSource
|
||||
|
|
@ -44,6 +45,22 @@ export const init = async (): Promise<void> => {
|
|||
ssl: getDatabaseSSLFromEnv()
|
||||
})
|
||||
break
|
||||
case 'mariadb':
|
||||
appDataSource = new DataSource({
|
||||
type: 'mariadb',
|
||||
host: process.env.DATABASE_HOST,
|
||||
port: parseInt(process.env.DATABASE_PORT || '3306'),
|
||||
username: process.env.DATABASE_USER,
|
||||
password: process.env.DATABASE_PASSWORD,
|
||||
database: process.env.DATABASE_NAME,
|
||||
charset: 'utf8mb4',
|
||||
synchronize: false,
|
||||
migrationsRun: false,
|
||||
entities: Object.values(entities),
|
||||
migrations: mariadbMigrations,
|
||||
ssl: getDatabaseSSLFromEnv()
|
||||
})
|
||||
break
|
||||
case 'postgres':
|
||||
appDataSource = new DataSource({
|
||||
type: 'postgres',
|
||||
|
|
|
|||
|
|
@ -124,6 +124,12 @@ export class DocumentStoreDTO {
|
|||
case 'cheerioWebScraper':
|
||||
loader.source = loader.loaderConfig.url
|
||||
break
|
||||
case 'playwrightWebScraper':
|
||||
loader.source = loader.loaderConfig.url
|
||||
break
|
||||
case 'puppeteerWebScraper':
|
||||
loader.source = loader.loaderConfig.url
|
||||
break
|
||||
case 'jsonFile':
|
||||
loader.source = loader.loaderConfig.jsonFile.replace('FILE-STORAGE::', '')
|
||||
break
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export default class Start extends Command {
|
|||
TOOL_FUNCTION_BUILTIN_DEP: Flags.string(),
|
||||
TOOL_FUNCTION_EXTERNAL_DEP: Flags.string(),
|
||||
NUMBER_OF_PROXIES: Flags.string(),
|
||||
DISABLE_CHATFLOW_REUSE: Flags.string(),
|
||||
DATABASE_TYPE: Flags.string(),
|
||||
DATABASE_PATH: Flags.string(),
|
||||
DATABASE_PORT: Flags.string(),
|
||||
|
|
@ -93,6 +94,7 @@ export default class Start extends Command {
|
|||
if (flags.IFRAME_ORIGINS) process.env.IFRAME_ORIGINS = flags.IFRAME_ORIGINS
|
||||
if (flags.DEBUG) process.env.DEBUG = flags.DEBUG
|
||||
if (flags.NUMBER_OF_PROXIES) process.env.NUMBER_OF_PROXIES = flags.NUMBER_OF_PROXIES
|
||||
if (flags.DISABLE_CHATFLOW_REUSE) process.env.DISABLE_CHATFLOW_REUSE = flags.DISABLE_CHATFLOW_REUSE
|
||||
|
||||
// Authorization
|
||||
if (flags.FLOWISE_USERNAME) process.env.FLOWISE_USERNAME = flags.FLOWISE_USERNAME
|
||||
|
|
|
|||
|
|
@ -57,10 +57,10 @@ const deleteApiKey = async (req: Request, res: Response, next: NextFunction) =>
|
|||
// Verify api key
|
||||
const verifyApiKey = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.apiKey) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.verifyApiKey - apiKey not provided!`)
|
||||
if (typeof req.params === 'undefined' || !req.params.apikey) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.verifyApiKey - apikey not provided!`)
|
||||
}
|
||||
const apiResponse = await apikeyService.verifyApiKey(req.params.apiKey)
|
||||
const apiResponse = await apikeyService.verifyApiKey(req.params.apikey)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ const getChatflowByApiKey = async (req: Request, res: Response, next: NextFuncti
|
|||
if (!apikey) {
|
||||
return res.status(401).send('Unauthorized')
|
||||
}
|
||||
const apiResponse = await chatflowsService.getChatflowByApiKey(apikey.id)
|
||||
const apiResponse = await chatflowsService.getChatflowByApiKey(apikey.id, req.query.keyonly)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { Request, Response, NextFunction } from 'express'
|
||||
|
||||
const getPing = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
return res.status(200).send('pong')
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getPing
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class Init1693840429259 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`chat_flow\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`flowData\` text NOT NULL,
|
||||
\`deployed\` tinyint DEFAULT NULL,
|
||||
\`isPublic\` tinyint DEFAULT NULL,
|
||||
\`apikeyid\` varchar(255) DEFAULT NULL,
|
||||
\`chatbotConfig\` varchar(255) DEFAULT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
\`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`id\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`chat_message\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`role\` varchar(255) NOT NULL,
|
||||
\`chatflowid\` varchar(255) NOT NULL,
|
||||
\`content\` text NOT NULL,
|
||||
\`sourceDocuments\` varchar(255) DEFAULT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`id\`),
|
||||
KEY \`IDX_e574527322272fd838f4f0f3d3\` (\`chatflowid\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`credential\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`credentialName\` varchar(255) NOT NULL,
|
||||
\`encryptedData\` varchar(255) NOT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
\`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`id\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`tool\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`description\` text NOT NULL,
|
||||
\`color\` varchar(255) NOT NULL,
|
||||
\`iconSrc\` varchar(255) DEFAULT NULL,
|
||||
\`schema\` varchar(255) DEFAULT NULL,
|
||||
\`func\` varchar(255) DEFAULT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
\`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`id\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE chat_flow`)
|
||||
await queryRunner.query(`DROP TABLE chat_message`)
|
||||
await queryRunner.query(`DROP TABLE credential`)
|
||||
await queryRunner.query(`DROP TABLE tool`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class ModifyChatFlow1693997791471 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_flow\` MODIFY \`chatbotConfig\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_flow\` MODIFY \`chatbotConfig\` VARCHAR;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class ModifyChatMessage1693999022236 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_message\` MODIFY \`sourceDocuments\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_message\` MODIFY \`sourceDocuments\` VARCHAR;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class ModifyCredential1693999261583 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`credential\` MODIFY \`encryptedData\` TEXT NOT NULL;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`credential\` MODIFY \`encryptedData\` VARCHAR NOT NULL;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class ModifyTool1694001465232 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`tool\` MODIFY \`schema\` TEXT, MODIFY \`func\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`tool\` MODIFY \`schema\` VARCHAR, MODIFY \`func\` VARCHAR;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddApiConfig1694099200729 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_flow', 'apiConfig')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_flow\` ADD COLUMN \`apiConfig\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_flow\` DROP COLUMN \`apiConfig\`;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddAnalytic1694432361423 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_flow', 'analytic')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_flow\` ADD COLUMN \`analytic\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_flow\` DROP COLUMN \`analytic\`;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddChatHistory1694658767766 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const chatTypeColumnExists = await queryRunner.hasColumn('chat_message', 'chatType')
|
||||
if (!chatTypeColumnExists)
|
||||
await queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`chatType\` VARCHAR(255) NOT NULL DEFAULT 'INTERNAL';`)
|
||||
|
||||
const chatIdColumnExists = await queryRunner.hasColumn('chat_message', 'chatId')
|
||||
if (!chatIdColumnExists) await queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`chatId\` VARCHAR(255);`)
|
||||
const results: { id: string; chatflowid: string }[] = await queryRunner.query(`WITH RankedMessages AS (
|
||||
SELECT
|
||||
\`chatflowid\`,
|
||||
\`id\`,
|
||||
\`createdDate\`,
|
||||
ROW_NUMBER() OVER (PARTITION BY \`chatflowid\` ORDER BY \`createdDate\`) AS row_num
|
||||
FROM \`chat_message\`
|
||||
)
|
||||
SELECT \`chatflowid\`, \`id\`
|
||||
FROM RankedMessages
|
||||
WHERE row_num = 1;`)
|
||||
for (const chatMessage of results) {
|
||||
await queryRunner.query(
|
||||
`UPDATE \`chat_message\` SET \`chatId\` = '${chatMessage.id}' WHERE \`chatflowid\` = '${chatMessage.chatflowid}'`
|
||||
)
|
||||
}
|
||||
await queryRunner.query(`ALTER TABLE \`chat_message\` MODIFY \`chatId\` VARCHAR(255) NOT NULL;`)
|
||||
|
||||
const memoryTypeColumnExists = await queryRunner.hasColumn('chat_message', 'memoryType')
|
||||
if (!memoryTypeColumnExists) await queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`memoryType\` VARCHAR(255);`)
|
||||
|
||||
const sessionIdColumnExists = await queryRunner.hasColumn('chat_message', 'sessionId')
|
||||
if (!sessionIdColumnExists) await queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`sessionId\` VARCHAR(255);`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE \`chat_message\` DROP COLUMN \`chatType\`, DROP COLUMN \`chatId\`, DROP COLUMN \`memoryType\`, DROP COLUMN \`sessionId\`;`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddAssistantEntity1699325775451 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`assistant\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`credential\` varchar(255) NOT NULL,
|
||||
\`details\` text NOT NULL,
|
||||
\`iconSrc\` varchar(255) DEFAULT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
\`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`id\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE assistant`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddUsedToolsToChatMessage1699481607341 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_message', 'usedTools')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`usedTools\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_message\` DROP COLUMN \`usedTools\`;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddCategoryToChatFlow1699900910291 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_flow', 'category')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_flow\` ADD COLUMN \`category\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_flow\` DROP COLUMN \`category\`;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddFileAnnotationsToChatMessage1700271021237 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_message', 'fileAnnotations')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`fileAnnotations\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_message\` DROP COLUMN \`fileAnnotations\`;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddFileUploadsToChatMessage1701788586491 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_message', 'fileUploads')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`fileUploads\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_message\` DROP COLUMN \`fileUploads\`;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddVariableEntity1699325775451 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`variable\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`value\` text NOT NULL,
|
||||
\`type\` varchar(255) DEFAULT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
\`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`id\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE variable`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSpeechToText1706364937060 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_flow', 'speechToText')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_flow\` ADD COLUMN \`speechToText\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_flow\` DROP COLUMN \`speechToText\`;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddFeedback1707213626553 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`chat_message_feedback\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`chatflowid\` varchar(255) NOT NULL,
|
||||
\`content\` text,
|
||||
\`chatId\` varchar(255) NOT NULL,
|
||||
\`messageId\` varchar(255) NOT NULL,
|
||||
\`rating\` varchar(255) NOT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`id\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE chat_message_feedback`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddUpsertHistoryEntity1709814301358 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`upsert_history\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`chatflowid\` varchar(255) NOT NULL,
|
||||
\`result\` text NOT NULL,
|
||||
\`flowData\` text NOT NULL,
|
||||
\`date\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`id\`),
|
||||
KEY \`IDX_a0b59fd66f6e48d2b198123cb6\` (\`chatflowid\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE upsert_history`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddLead1710832127079 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`lead\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`chatflowid\` varchar(255) NOT NULL,
|
||||
\`chatId\` varchar(255) NOT NULL,
|
||||
\`name\` text,
|
||||
\`email\` text,
|
||||
\`phone\` text,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`id\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE lead`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddLeadToChatMessage1711538023578 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_message', 'leadEmail')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`leadEmail\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_message\` DROP COLUMN \`leadEmail\`;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddDocumentStore1711637331047 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`document_store\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`description\` varchar(255),
|
||||
\`loaders\` text,
|
||||
\`whereUsed\` text,
|
||||
\`status\` varchar(20) NOT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
\`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`id\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`document_store_file_chunk\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`docId\` varchar(36) NOT NULL,
|
||||
\`storeId\` varchar(36) NOT NULL,
|
||||
\`chunkNo\` INT NOT NULL,
|
||||
\`pageContent\` text,
|
||||
\`metadata\` text,
|
||||
PRIMARY KEY (\`id\`),
|
||||
KEY \`IDX_e76bae1780b77e56aab1h2asd4\` (\`docId\`),
|
||||
KEY \`IDX_e213b811b01405a42309a6a410\` (\`storeId\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE document_store`)
|
||||
await queryRunner.query(`DROP TABLE document_store_file_chunk`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddAgentReasoningToChatMessage1714679514451 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_message', 'agentReasoning')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`agentReasoning\` LONGTEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_message\` DROP COLUMN \`agentReasoning\`;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddTypeToChatFlow1766759476232 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_flow', 'type')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_flow\` ADD COLUMN \`type\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_flow\` DROP COLUMN \`type\`;`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import { Init1693840429259 } from './1693840429259-Init'
|
||||
import { ModifyChatFlow1693997791471 } from './1693997791471-ModifyChatFlow'
|
||||
import { ModifyChatMessage1693999022236 } from './1693999022236-ModifyChatMessage'
|
||||
import { ModifyCredential1693999261583 } from './1693999261583-ModifyCredential'
|
||||
import { ModifyTool1694001465232 } from './1694001465232-ModifyTool'
|
||||
import { AddApiConfig1694099200729 } from './1694099200729-AddApiConfig'
|
||||
import { AddAnalytic1694432361423 } from './1694432361423-AddAnalytic'
|
||||
import { AddChatHistory1694658767766 } from './1694658767766-AddChatHistory'
|
||||
import { AddAssistantEntity1699325775451 } from './1699325775451-AddAssistantEntity'
|
||||
import { AddUsedToolsToChatMessage1699481607341 } from './1699481607341-AddUsedToolsToChatMessage'
|
||||
import { AddCategoryToChatFlow1699900910291 } from './1699900910291-AddCategoryToChatFlow'
|
||||
import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage'
|
||||
import { AddFileUploadsToChatMessage1701788586491 } from './1701788586491-AddFileUploadsToChatMessage'
|
||||
import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity'
|
||||
import { AddSpeechToText1706364937060 } from './1706364937060-AddSpeechToText'
|
||||
import { AddUpsertHistoryEntity1709814301358 } from './1709814301358-AddUpsertHistoryEntity'
|
||||
import { AddFeedback1707213626553 } from './1707213626553-AddFeedback'
|
||||
import { AddDocumentStore1711637331047 } from './1711637331047-AddDocumentStore'
|
||||
import { AddLead1710832127079 } from './1710832127079-AddLead'
|
||||
import { AddLeadToChatMessage1711538023578 } from './1711538023578-AddLeadToChatMessage'
|
||||
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
|
||||
import { AddTypeToChatFlow1766759476232 } from './1766759476232-AddTypeToChatFlow'
|
||||
|
||||
export const mariadbMigrations = [
|
||||
Init1693840429259,
|
||||
ModifyChatFlow1693997791471,
|
||||
ModifyChatMessage1693999022236,
|
||||
ModifyCredential1693999261583,
|
||||
ModifyTool1694001465232,
|
||||
AddApiConfig1694099200729,
|
||||
AddAnalytic1694432361423,
|
||||
AddChatHistory1694658767766,
|
||||
AddAssistantEntity1699325775451,
|
||||
AddUsedToolsToChatMessage1699481607341,
|
||||
AddCategoryToChatFlow1699900910291,
|
||||
AddFileAnnotationsToChatMessage1700271021237,
|
||||
AddVariableEntity1699325775451,
|
||||
AddFileUploadsToChatMessage1701788586491,
|
||||
AddSpeechToText1706364937060,
|
||||
AddUpsertHistoryEntity1709814301358,
|
||||
AddFeedback1707213626553,
|
||||
AddDocumentStore1711637331047,
|
||||
AddLead1710832127079,
|
||||
AddLeadToChatMessage1711538023578,
|
||||
AddAgentReasoningToChatMessage1714679514451,
|
||||
AddTypeToChatFlow1766759476232
|
||||
]
|
||||
|
|
@ -137,7 +137,8 @@ export class App {
|
|||
'/api/v1/feedback',
|
||||
'/api/v1/leads',
|
||||
'/api/v1/get-upload-file',
|
||||
'/api/v1/ip'
|
||||
'/api/v1/ip',
|
||||
'/api/v1/ping'
|
||||
]
|
||||
this.app.use((req, res, next) => {
|
||||
if (/\/api\/v1\//i.test(req.url)) {
|
||||
|
|
@ -197,6 +198,7 @@ export async function getAllChatFlow(): Promise<IChatFlow[]> {
|
|||
export async function start(): Promise<void> {
|
||||
serverApp = new App()
|
||||
|
||||
const host = process.env.HOST
|
||||
const port = parseInt(process.env.PORT || '', 10) || 3000
|
||||
const server = http.createServer(serverApp.app)
|
||||
|
||||
|
|
@ -207,8 +209,8 @@ export async function start(): Promise<void> {
|
|||
await serverApp.initDatabase()
|
||||
await serverApp.config(io)
|
||||
|
||||
server.listen(port, () => {
|
||||
logger.info(`⚡️ [server]: Flowise Server is listening at ${port}`)
|
||||
server.listen(port, host, () => {
|
||||
logger.info(`⚡️ [server]: Flowise Server is listening at ${host ? 'http://' + host : ''}:${port}`)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,9 +38,11 @@ import vectorRouter from './vectors'
|
|||
import verifyRouter from './verify'
|
||||
import versionRouter from './versions'
|
||||
import upsertHistoryRouter from './upsert-history'
|
||||
import pingRouter from './ping'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.use('/ping', pingRouter)
|
||||
router.use('/apikey', apikeyRouter)
|
||||
router.use('/assistants', assistantsRouter)
|
||||
router.use('/chatflows', chatflowsRouter)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import express from 'express'
|
||||
import pingController from '../../controllers/ping'
|
||||
const router = express.Router()
|
||||
|
||||
// GET
|
||||
router.get('/', pingController.getPing)
|
||||
|
||||
export default router
|
||||
|
|
@ -55,7 +55,14 @@ const verifyApiKey = async (paramApiKey: string): Promise<string> => {
|
|||
const dbResponse = 'OK'
|
||||
return dbResponse
|
||||
} catch (error) {
|
||||
throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: apikeyService.verifyApiKey - ${getErrorMessage(error)}`)
|
||||
if (error instanceof InternalFlowiseError && error.statusCode === StatusCodes.UNAUTHORIZED) {
|
||||
throw error
|
||||
} else {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||
`Error: apikeyService.verifyApiKey - ${getErrorMessage(error)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||