Compare commits

...

77 Commits

Author SHA1 Message Date
Henry Heng 54ff43e8f1
Bugfix/HF custom endpoint (#2811)
include fix for hf custom endpoint
2024-07-16 21:42:24 +01:00
Ong Chung Yau 95b2cf7b7f
Feature/extract import all (#2796)
* use existing route to get all chatflows

* add export all chatflows functionality

* add read exported all chatflows json file functionality

* add save chatflows functionality in server

* chore rename saveChatflows to importChatflows and others

* chore rewrite snackbar message

* fix import chatflows when no data in chatflows db

* add handle when import file array length is 0

* chore update and add meaning comment in importChatflows

* update method of storing flowdata for importChatflows function

* Refresh/redirect to chatflows when import is successful

* fix lint

---------

Co-authored-by: Ilango <rajagopalilango@gmail.com>
2024-07-16 09:47:41 +08:00
Henry Heng 074bb738a3
Release/1.8.4 (#2805)
* 🥳 flowise release 1.8.4

* 🥳 flowise-components release 1.8.6
2024-07-15 15:34:33 +01:00
Henry Heng 78e60e22d2
Bugfix/Undefined substring error (#2804)
fix undefined substring error
2024-07-15 15:16:56 +01:00
Neal Beeken 9e88c45051
feat: add driverInfo to mongodb component (#2779)
* feat: add driverInfo to mongodb component

NODE-6240

* chore: add a getVersion utility function
2024-07-15 12:24:00 +01:00
Henry Heng 363d1bfc44
Chore/update deprecating nodes (#2540)
* update deprecating nodes

* add filters use cases to marketplace

* update log level
2024-07-12 18:37:57 +01:00
Asharib Ali 9ea439d135
Add Perplexity AI Search Tool to Marketplaces/Tools (#2771)
add the perplexity_ai_search tool
2024-07-12 18:01:36 +01:00
Pavlo Paliychuk 1015e1193f
chore: Bump zep cloud sdk version and clean up zep cloud vector store node (#2767) 2024-07-12 18:01:00 +01:00
William Espegren 7166317482
Fix docker command (#2743)
* fix docker compose

* correct docker compose command
2024-07-12 17:57:58 +01:00
Henry Heng 3cbbd59242
Bugfix/Enum type tools for gemini (#2766)
fix enum type tools for gemini
2024-07-09 00:25:18 +01:00
Ahmed Osman 90558ca688
FIX #2617 Cherio Web Crawler doesn't work with large sites (#2678)
* FIX #2617 Big sites scan error

* FIX #2617 Big sites scan error - review fix

---------

Co-authored-by: Ahmed Osman <ahmed.osman@evolpe.pl>
2024-07-05 11:34:47 +01:00
Arun Lodhi b1e38783e4
Bugfix/observation-includes-not-function (#2744)
* Bugfix: observation?.includes is not a function

* Check type of observation before checking source document prefix

* lint-fix

---------

Co-authored-by: Henry Heng <henryheng@flowiseai.com>
2024-07-05 11:26:05 +01:00
Mubashir Shariq dfdeb02b3a
Feat/added chattBaiduWenxin chat model (#2752)
* added cahtBaiduWenxin model

* fix linting

* fixed linting

* added baidu secret key
2024-07-05 11:25:37 +01:00
William Espegren cacbfa8162
feat: Add limit parameter to Spider tool (#2762)
* feat: Add limit parameter to Spider tool

* fix pnpm lint
2024-07-05 11:23:34 +01:00
William Espegren 656f6cad81
Feature/Spider (open-source web scraper & crawler) (#2738)
* Add Spider Scraper & Crawler

* fix pnpm lint

* chore: Update metadata to be correct format

* fix pnpm lint
2024-07-02 00:00:52 +01:00
Henry Heng efc6e02828
Bugfix/Add showagent message when agentflow (#2749)
add showagent message when agentflow
2024-07-01 18:46:10 +01:00
Mubashir Shariq 512df4197c
Bugfix/broken icon display on market place tab (#2737)
* fixed broken display on marketplace tab

* updated

* updated backgroudImg to background when no Imgsrc
2024-07-01 16:28:19 +01:00
Rogério Chaves 4d174495dc
Fix langwatch link (#2748) 2024-07-01 16:20:42 +01:00
Henry Heng 15a416a58f
Bugfix/Verify apikey params typo (#2742)
fix verify apikey params typo
2024-06-28 22:09:12 +01:00
Aman Soni e69fee1375
Embed chat configuration updated (#2723) 2024-06-27 12:08:29 +01:00
Henry Heng cc24f94358
Release/1.8.3 (#2730)
* release flowise 1.8.3

* update flowise-components to 1.8.5

* Update pnpm-lock.yaml
2024-06-26 14:55:30 +01:00
Henry Heng b55f87cc40
Feature/FireCrawl (#2728)
* add firecrawl

* Update FireCrawl.ts (#2692)

---------

Co-authored-by: Eric Ciarla <43451761+ericciarla@users.noreply.github.com>
2024-06-26 14:40:43 +01:00
Henry Heng 7067f90153
Release/1.8.3 (#2727)
release flowise 1.8.3
2024-06-26 14:01:26 +01:00
Henry Heng d0354bb25c
Chore/update flowise embed version (#2722)
* update flowise-embed version on lock file

* add agent messages to share chatbot
2024-06-26 02:18:08 +01:00
Henry Heng 96dfedde6e
Bugfix/Add check for setError (#2721)
add check for setError
2024-06-25 17:54:38 +01:00
Henry Heng 1367f095d4
Bugfix/Fix export lead email (#2717)
fix export lead email
2024-06-25 00:23:16 +01:00
Henry Heng 109b0367cc
Bugfix/Pinecone Text Key (#2708)
* add source tag to pinecone

* update pinecone

* add text key to pinecone

* update pinecone version, and singleton
2024-06-24 02:08:30 +01:00
Saurav Maheshkar e2ae524edd
chore: move configurations to `package.json` (#2613)
feat: refactor to have minimal config files
2024-06-21 22:13:13 +01:00
rennokki eff1336b82
update: VoyageAI Models (#2688)
* Updated VoyageAI Models

* Update VoyageAI Rerankers

* Update VoyageAIRerankRetriever.ts

---------

Co-authored-by: Henry Heng <henryheng@flowiseai.com>
2024-06-21 16:52:54 +01:00
YISH 18b83333d3
Fix `observation?.includes is not a function` (#2696) 2024-06-21 16:45:20 +01:00
Rogério Chaves 0fc5e3d0c5
Add LangWatch integration (#2677)
Add langwatch integration
2024-06-21 16:26:55 +01:00
Harsha aec9e7a3b7
Bug fix - ChatBot Share - CURL with i/p config (#2672) 2024-06-21 16:11:34 +01:00
YISH 83ecc88b35
feat (server): add support for setting listening host (#2604) 2024-06-21 14:51:03 +01:00
Henry Heng f811fc4e5d
Chore/Sonnet 3.5 (#2698)
* add gemini flash

* add gemin flash to vertex

* add gemin-1.5-flash-preview to vertex

* add azure gpt 4o

* add claude 3.5 sonnet
2024-06-21 14:23:06 +01:00
Daniel D'Abate d4f80394d3
Feature - Add option to start a new session with each interaction with the Chatflow tool (#2633)
* Feature - Add option to start a new session with each interaction with the Chatflow tool

* ChatflowTool - Create random chatId when startNewSession is set
2024-06-20 00:03:45 +01:00
Henry Heng 842bfc66fe
Feature/Add ability to upload image url when calling API (#2683)
add ability to upload image url when calling API
2024-06-19 23:26:56 +01:00
Henry Heng 72e5287343
Feature/Add tool choices to openai assistant (#2682)
add tool choices to openai
2024-06-19 22:41:53 +01:00
Aman Soni 8bb841641e
Embed Chat configuration updated (#2648) 2024-06-19 21:12:41 +01:00
Henry Heng b662dd79c6
Feature/Add Text Key to Pinecone (#2681)
* add source tag to pinecone

* update pinecone

* add text key to pinecone

* update pinecone version, and singleton
2024-06-19 21:10:09 +01:00
Henry Heng 1849637af8
Bugfix/Update retriever tool wordings (#2679)
update retriever tool wordings
2024-06-19 18:49:01 +01:00
Henry Heng 21743656a8
Feature/Add multi query retriever (#2676)
add multi query retriever
2024-06-19 14:52:58 +01:00
Henry Heng b5ead0745b
Feature/Add filter to Postgres VectorStore (#2674)
* add filter to pgvs

* fix types
2024-06-19 13:44:20 +01:00
YISH 371e632a2c
Fix apikey not URL safe (#2602) 2024-06-14 11:35:25 +01:00
Mohamed Akram c34eb8ee15
Unstructured Upsert bug (#2628)
* Unstructured Upsert bug
When upserting with the API, the uploaded files are of type pdfFile, txtFile, etc.
but the code reads only fileObject which is the uploaded file using the button

* Update UnstructuredFile.ts

fixed linting error

---------

Co-authored-by: Mohamed Akram <makram@ntgclarity.com>
2024-06-14 02:39:46 +01:00
Henry Heng f2c6a1988f
Chore/models update (#2634)
* add gemini flash

* add gemin flash to vertex

* add gemin-1.5-flash-preview to vertex

* add azure gpt 4o
2024-06-13 14:35:35 +01:00
Daniel D'Abate 76c5e6a893
[Feature] Add sort capabilities to ChatFlow and AgentFlow tables (#2609)
Add sort capabilities to ChatFlow and AgentFlow tables
2024-06-12 21:26:44 +01:00
Henry Heng 3ab0d99711
Bugfix/Correctly throw 401 error when unauthorized (#2626)
correctly throw 401 error when unauthorized
2024-06-12 19:10:42 +01:00
Henry Heng 5ba468b4cc
🥳 flowise@1.8.2 release (#2625) 2024-06-11 22:39:59 +01:00
Jared McQueen 5e4d640ed7
fix totalChars accumulator for undefined pageContent (#2619) 2024-06-11 22:05:04 +01:00
jiabaow 6fb775fe95
add Fireworks LLM (#2597)
* add Fireworks LLM

* support multiple models

* Update Fireworks.ts

* fix linting

---------

Co-authored-by: Henry Heng <henryheng@flowiseai.com>
2024-06-11 21:56:34 +01:00
Mingxin Hou 88ee9b09a7
Fireworks ai chat model (#2596)
* fireworks chat model

* add chatFireworks to streamAvaliableLLMs

* add model parameter input

* Update ChatFireworks.ts

* fix linting

---------

Co-authored-by: Henry Heng <henryheng@flowiseai.com>
2024-06-11 21:56:22 +01:00
Torsten Raudssus 9f9aff34f8
Adding pnpm-store to gitignore (#2600) 2024-06-11 16:29:28 +01:00
Torsten Raudssus 66e1296a06
Searxng Tool implementation (#2599)
* Searxng Tool implementation, first commit with most functionality

* Fixed complains of pnpm lint

* Picky linter
2024-06-11 16:29:07 +01:00
Saurav Maheshkar f1e78d870e
gh: refactor community files (#2572)
* gh: refactor community files

* fix: links to other files

* docs: move to i18n
2024-06-07 15:49:24 +01:00
Daniel D'Abate be3a887e68
Improvement - Reduce size of final docker image with multistage build (#2598)
* Reduce size of final docker image with multistage build

* Dockerfile - Move PUPPETEER_SKIP_DOWNLOAD flag to build stage
2024-06-07 15:35:57 +01:00
不知火 Shiranui c8939dc2a6
Add data source adapter of MariaDB (#2595)
fix(datasource.ts): add mariadb
2024-06-07 12:14:06 +01:00
Daniel D'Abate 34251fa336
Bugfix - Add x-forwarded-proto as source for http protocol for base url and add base url field to ChatflowTool (#2592) 2024-06-07 12:04:25 +01:00
Daniel D'Abate cb93d9d557
Bugfix - JS function "execute" from node crashing the frontend and response is hidden (#2589)
* Bugfix - Fix crash when executing JS function from node and fix hidden response

* add toString() to code executed result

* Change height of CodeEditor to make it bigger, but still make the result visible below.

---------

Co-authored-by: Henry Heng <henryheng@flowiseai.com>
2024-06-06 14:17:35 +01:00
toi500 5899e50c54
Adding Agentflow Template (#2586)
adding agentflow template

Co-authored-by: toi500 <toi500@gmail.com>
2024-06-05 21:55:45 +01:00
Aman Soni e0a03ad46d
HTML Embed Chat popup configuration updated (#2583)
* HTML Embed Chat popup configuration updated

* HTML Embed Chat popup configuration

* Update EmbedChat.jsx

update spacing to be consistent

---------

Co-authored-by: Henry Heng <henryheng@flowiseai.com>
2024-06-05 21:55:27 +01:00
Henry Heng 582dcc8508
Feature/add status check ping (#2579)
add status check ping
2024-06-04 18:45:45 +01:00
Erasmo Pinheiro 5a73eaa588
fix: Change the CMD flowise command in Dockerfile to be the same as in the document (#2563)
* fix: Change the CMD flowise command to be the same as in the documentation.

* Remove unnecessary npx command from dockerfile

* Command was changed by the entrypoint in Docker Composer

---------

Co-authored-by: Erasmo De Souza Pinheiro <erasmodesouzapinheiro@Erasmos-MacBook-Air.local>
2024-06-04 16:23:30 +01:00
Henry Heng 4ec8376efa
Bugfix/get rid of double quotes when replacing variable value (#2577)
get rid of double quotes when replacing variable value
2024-06-04 15:44:27 +01:00
Daniel D'Abate 5ba9493b30
Feature - Env variable to disable ChatFlow reuse (#2559) 2024-06-04 10:11:57 +01:00
Jared McQueen bdbb6f850a
adding a document loader returns the correct id (#2538) 2024-06-04 02:57:30 +01:00
Saurav Maheshkar 55f52c4d50
feat(ci): enable `pnpm` caching in CI (#2530) 2024-06-04 02:57:07 +01:00
Jared McQueen 7eb9341fdc
adding url to source for all web scrapers (#2537) 2024-06-04 02:50:43 +01:00
Daniel D'Abate a799ac8087
Improve OpenSearchURL credential storing user and password in separate fields from the URL (#2549)
OpenSearch - Improve OpenSearchURL credential storing user and password in separate fields from the URL
2024-06-04 02:31:36 +01:00
Henry 76abd20e85 🥳 flowise-components@1.8.2 minor bugfix release 2024-06-03 13:34:21 +01:00
Henry Heng 272fd914bd
Bugfix/supabase upsert ids (#2561)
* fix upserting same id with supabase

* remove dedicated addvectors logic for ids

* Update pnpm-lock.yaml

* add fix for null id column
2024-06-03 13:09:59 +01:00
Henry Heng f2a0ffe542
Bugfix/Check for proper thread id and avoid throwing error (#2551)
check for proper thread id and avoid throwing error
2024-06-02 02:41:48 +01:00
Henry Heng 8c66d2c735
Bugfix/Avoid passing runnable assign when worker agent has no input variables (#2550)
avoid passing runnable assign when worker agent has no input variables
2024-06-02 02:33:40 +01:00
Henry Heng e15e6fafdc
Bugfix/Disable output prediction from llmchain streaming (#2543)
disable output prediction from llmchain streaming
2024-06-01 12:37:00 +01:00
Daniel D'Abate e5f0ca2dd3
Fix OpenSearch vector store upsert (#2545) 2024-06-01 12:36:39 +01:00
Henry 1d9927027d 🥳 flowise@1.8.1 minor bugfix release 2024-06-01 00:09:38 +01:00
Henry c42ef95a15 🥳 flowise-ui@1.8.1 minor bugfix release 2024-06-01 00:09:10 +01:00
Henry f64931bfcc 🥳 flowise-components@1.8.1 minor bugfix release 2024-06-01 00:08:49 +01:00
270 changed files with 44932 additions and 46338 deletions

View File

@ -1,2 +0,0 @@
node_modules
dist

View File

@ -27,6 +27,7 @@ jobs:
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
check-latest: false check-latest: false
cache: 'pnpm'
- run: npm i -g pnpm - run: npm i -g pnpm
- run: pnpm install - run: pnpm install
- run: ./node_modules/.bin/cypress install - run: ./node_modules/.bin/cypress install

3
.gitignore vendored
View File

@ -11,6 +11,9 @@
**/logs **/logs
**/*.log **/*.log
## pnpm
.pnpm-store/
## build ## build
**/dist **/dist
**/build **/build

View File

@ -1,3 +0,0 @@
**/node_modules
**/dist
**/build

View File

@ -1,9 +0,0 @@
module.exports = {
printWidth: 140,
singleQuote: true,
jsxSingleQuote: true,
trailingComma: 'none',
tabWidth: 4,
semi: false,
endOfLine: 'auto'
}

View File

@ -1,6 +1,6 @@
# Contributor Covenant Code of Conduct # Contributor Covenant Code of Conduct
English | [中文](<./CODE_OF_CONDUCT-ZH.md>) English | [中文](./i18n/CODE_OF_CONDUCT-ZH.md)
## Our Pledge ## Our Pledge

View File

@ -2,7 +2,7 @@
# Contributing to Flowise # Contributing to Flowise
English | [中文](./CONTRIBUTING-ZH.md) English | [中文](./i18n/CONTRIBUTING-ZH.md)
We appreciate any form of contributions. 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) 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 | | Variable | Description | Type | Default |
| ---------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- | | ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- |
| PORT | The HTTP port Flowise runs on | Number | 3000 | | PORT | The HTTP port Flowise runs on | Number | 3000 |
| CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | String | | | CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | String | |
| IFRAME_ORIGINS | The allowed origins for iframe src embedding | String | | | IFRAME_ORIGINS | The allowed origins for iframe src embedding | String | |
| FLOWISE_USERNAME | Username to login | String | | | FLOWISE_USERNAME | Username to login | String | |
| FLOWISE_PASSWORD | Password to login | String | | | FLOWISE_PASSWORD | Password to login | String | |
| FLOWISE_FILE_SIZE_LIMIT | Upload File Size Limit | String | 50mb | | FLOWISE_FILE_SIZE_LIMIT | Upload File Size Limit | String | 50mb |
| DEBUG | Print logs from components | Boolean | | | DISABLE_CHATFLOW_REUSE | Forces the creation of a new ChatFlow for each call instead of reusing existing ones from cache | Boolean | |
| LOG_PATH | Location where log files are stored | String | `your-path/Flowise/logs` | | DEBUG | Print logs from components | Boolean | |
| LOG_LEVEL | Different levels of logs | Enum String: `error`, `info`, `verbose`, `debug` | `info` | | LOG_PATH | Location where log files are stored | String | `your-path/Flowise/logs` |
| LOG_JSON_SPACES | Spaces to beautify JSON logs | | 2 | | LOG_LEVEL | Different levels of logs | Enum String: `error`, `info`, `verbose`, `debug` | `info` |
| APIKEY_PATH | Location where api keys are saved | String | `your-path/Flowise/packages/server` | | LOG_JSON_SPACES | Spaces to beautify JSON logs | | 2 |
| TOOL_FUNCTION_BUILTIN_DEP | NodeJS built-in modules to be used for Tool Function | String | | | APIKEY_PATH | Location where api keys are saved | String | `your-path/Flowise/packages/server` |
| TOOL_FUNCTION_EXTERNAL_DEP | External modules to be used for Tool Function | String | | | TOOL_FUNCTION_BUILTIN_DEP | NodeJS built-in modules to be used for Tool Function | String | |
| DATABASE_TYPE | Type of database to store the flowise data | Enum String: `sqlite`, `mysql`, `postgres` | `sqlite` | | TOOL_FUNCTION_EXTERNAL_DEP | External modules to be used for Tool Function | String | |
| DATABASE_PATH | Location where database is saved (When DATABASE_TYPE is sqlite) | String | `your-home-dir/.flowise` | | DATABASE_TYPE | Type of database to store the flowise data | Enum String: `sqlite`, `mysql`, `postgres` | `sqlite` |
| DATABASE_HOST | Host URL or IP address (When DATABASE_TYPE is not sqlite) | String | | | DATABASE_PATH | Location where database is saved (When DATABASE_TYPE is sqlite) | String | `your-home-dir/.flowise` |
| DATABASE_PORT | Database port (When DATABASE_TYPE is not sqlite) | String | | | DATABASE_HOST | Host URL or IP address (When DATABASE_TYPE is not sqlite) | String | |
| DATABASE_USER | Database username (When DATABASE_TYPE is not sqlite) | String | | | DATABASE_PORT | Database port (When DATABASE_TYPE is not sqlite) | String | |
| DATABASE_PASSWORD | Database password (When DATABASE_TYPE is not sqlite) | String | | | DATABASE_USER | Database username (When DATABASE_TYPE is not sqlite) | String | |
| DATABASE_NAME | Database name (When DATABASE_TYPE is not sqlite) | String | | | DATABASE_PASSWORD | Database password (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_NAME | Database name (When DATABASE_TYPE is not sqlite) | String | |
| DATABASE_SSL | Database connection overssl (When DATABASE_TYPE is postgre) | Boolean | false | | DATABASE_SSL_KEY_BASE64 | Database SSL client cert in base64 (takes priority over DATABASE_SSL) | Boolean | false |
| SECRETKEY_PATH | Location where encryption key (used to encrypt/decrypt credentials) is saved | String | `your-path/Flowise/packages/server` | | DATABASE_SSL | Database connection overssl (When DATABASE_TYPE is postgre) | Boolean | false |
| FLOWISE_SECRETKEY_OVERWRITE | Encryption key to be used instead of the key stored in SECRETKEY_PATH | String | | SECRETKEY_PATH | Location where encryption key (used to encrypt/decrypt credentials) is saved | String | `your-path/Flowise/packages/server` |
| DISABLE_FLOWISE_TELEMETRY | Turn off telemetry | Boolean | | FLOWISE_SECRETKEY_OVERWRITE | Encryption key to be used instead of the key stored in SECRETKEY_PATH | String |
| MODEL_LIST_CONFIG_JSON | File path to load list of models from your local config file | String | `/your_model_list_config_file_path` | | DISABLE_FLOWISE_TELEMETRY | Turn off telemetry | Boolean |
| STORAGE_TYPE | Type of storage for uploaded files. default is `local` | Enum String: `s3`, `local` | `local` | | MODEL_LIST_CONFIG_JSON | File path to load list of models from your local config file | String | `/your_model_list_config_file_path` |
| BLOB_STORAGE_PATH | Local folder path where uploaded files are stored when `STORAGE_TYPE` is `local` | String | `your-home-dir/.flowise/storage` | | STORAGE_TYPE | Type of storage for uploaded files. default is `local` | Enum String: `s3`, `local` | `local` |
| S3_STORAGE_BUCKET_NAME | Bucket name to hold the uploaded files when `STORAGE_TYPE` is `s3` | String | | | BLOB_STORAGE_PATH | Local folder path where uploaded files are stored when `STORAGE_TYPE` is `local` | String | `your-home-dir/.flowise/storage` |
| S3_STORAGE_ACCESS_KEY_ID | AWS Access Key | String | | | S3_STORAGE_BUCKET_NAME | Bucket name to hold the uploaded files when `STORAGE_TYPE` is `s3` | String | |
| S3_STORAGE_SECRET_ACCESS_KEY | AWS Secret Key | String | | | S3_STORAGE_ACCESS_KEY_ID | AWS Access Key | String | |
| S3_STORAGE_REGION | Region for S3 bucket | 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: You can also specify the env variables when using `npx`. For example:

View File

@ -10,7 +10,7 @@
[![GitHub star chart](https://img.shields.io/github/stars/FlowiseAI/Flowise?style=social)](https://star-history.com/#FlowiseAI/Flowise) [![GitHub star chart](https://img.shields.io/github/stars/FlowiseAI/Flowise?style=social)](https://star-history.com/#FlowiseAI/Flowise)
[![GitHub fork](https://img.shields.io/github/forks/FlowiseAI/Flowise?style=social)](https://github.com/FlowiseAI/Flowise/fork) [![GitHub fork](https://img.shields.io/github/forks/FlowiseAI/Flowise?style=social)](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> <h3>Drag & drop UI to build your customized LLM flow</h3>
<a href="https://github.com/FlowiseAI/Flowise"> <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 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` 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) 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 ### Docker Image

View File

@ -1,13 +0,0 @@
module.exports = {
presets: [
'@babel/preset-typescript',
[
'@babel/preset-env',
{
targets: {
node: 'current'
}
}
]
]
}

View File

@ -23,8 +23,10 @@ BLOB_STORAGE_PATH=/root/.flowise/storage
# FLOWISE_SECRETKEY_OVERWRITE=myencryptionkey # FLOWISE_SECRETKEY_OVERWRITE=myencryptionkey
# FLOWISE_FILE_SIZE_LIMIT=50mb # FLOWISE_FILE_SIZE_LIMIT=50mb
# DISABLE_CHATFLOW_REUSE=true
# DEBUG=true # DEBUG=true
# LOG_LEVEL=debug (error | warn | info | verbose | debug) # LOG_LEVEL=info (error | warn | info | verbose | debug)
# TOOL_FUNCTION_BUILTIN_DEP=crypto,fs # TOOL_FUNCTION_BUILTIN_DEP=crypto,fs
# TOOL_FUNCTION_EXTERNAL_DEP=moment,lodash # TOOL_FUNCTION_EXTERNAL_DEP=moment,lodash

View File

@ -1,21 +1,25 @@
FROM node:20-alpine # Stage 1: Build stage
FROM node:20-alpine as build
USER root USER root
RUN apk add --no-cache git # Skip downloading Chrome for Puppeteer (saves build time)
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
ENV PUPPETEER_SKIP_DOWNLOAD=true 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 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"]

View File

@ -5,9 +5,9 @@ Starts Flowise from [DockerHub Image](https://hub.docker.com/r/flowiseai/flowise
## Usage ## Usage
1. Create `.env` file and specify the `PORT` (refer to `.env.example`) 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) 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 ## 🔒 Authentication
@ -19,9 +19,9 @@ Starts Flowise from [DockerHub Image](https://hub.docker.com/r/flowiseai/flowise
- FLOWISE_USERNAME=${FLOWISE_USERNAME} - FLOWISE_USERNAME=${FLOWISE_USERNAME}
- FLOWISE_PASSWORD=${FLOWISE_PASSWORD} - FLOWISE_PASSWORD=${FLOWISE_PASSWORD}
``` ```
3. `docker-compose up -d` 3. `docker compose up -d`
4. Open [http://localhost:3000](http://localhost:3000) 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 ## 🌱 Env Variables

View File

@ -33,4 +33,4 @@ services:
- '${PORT}:${PORT}' - '${PORT}:${PORT}'
volumes: volumes:
- ~/.flowise:/root/.flowise - ~/.flowise:/root/.flowise
command: /bin/sh -c "sleep 3; flowise start" entrypoint: /bin/sh -c "sleep 3; flowise start"

View File

@ -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 [主页]: http://contributor-covenant.org

View File

@ -2,7 +2,7 @@
# 贡献给 Flowise # 贡献给 Flowise
[English](./CONTRIBUTING.md) | 中文 [English](../CONTRIBUTING.md) | 中文
我们欢迎任何形式的贡献。 我们欢迎任何形式的贡献。
@ -118,35 +118,36 @@ Flowise 在一个单一的单体存储库中有 3 个不同的模块。
Flowise 支持不同的环境变量来配置您的实例。您可以在 `packages/server` 文件夹中的 `.env` 文件中指定以下变量。阅读[更多信息](https://docs.flowiseai.com/environment-variables) Flowise 支持不同的环境变量来配置您的实例。您可以在 `packages/server` 文件夹中的 `.env` 文件中指定以下变量。阅读[更多信息](https://docs.flowiseai.com/environment-variables)
| 变量名 | 描述 | 类型 | 默认值 | | 变量名 | 描述 | 类型 | 默认值 |
| ---------------------------- | ------------------------------------------------------- | ----------------------------------------------- | ----------------------------------- | | ---------------------------- | -------------------------------------------------------------------- | ----------------------------------------------- | ----------------------------------- |
| PORT | Flowise 运行的 HTTP 端口 | 数字 | 3000 | | PORT | Flowise 运行的 HTTP 端口 | 数字 | 3000 |
| FLOWISE_USERNAME | 登录用户名 | 字符串 | | | FLOWISE_USERNAME | 登录用户名 | 字符串 | |
| FLOWISE_PASSWORD | 登录密码 | 字符串 | | | FLOWISE_PASSWORD | 登录密码 | 字符串 | |
| FLOWISE_FILE_SIZE_LIMIT | 上传文件大小限制 | 字符串 | 50mb | | FLOWISE_FILE_SIZE_LIMIT | 上传文件大小限制 | 字符串 | 50mb |
| DEBUG | 打印组件的日志 | 布尔值 | | | DISABLE_CHATFLOW_REUSE | 强制为每次调用创建一个新的 ChatFlow而不是重用缓存中的现有 ChatFlow | 布尔值 | |
| LOG_PATH | 存储日志文件的位置 | 字符串 | `your-path/Flowise/logs` | | DEBUG | 打印组件的日志 | 布尔值 | |
| LOG_LEVEL | 日志的不同级别 | 枚举字符串: `error`, `info`, `verbose`, `debug` | `info` | | LOG_PATH | 存储日志文件的位置 | 字符串 | `your-path/Flowise/logs` |
| APIKEY_PATH | 存储 API 密钥的位置 | 字符串 | `your-path/Flowise/packages/server` | | LOG_LEVEL | 日志的不同级别 | 枚举字符串: `error`, `info`, `verbose`, `debug` | `info` |
| TOOL_FUNCTION_BUILTIN_DEP | 用于工具函数的 NodeJS 内置模块 | 字符串 | | | APIKEY_PATH | 存储 API 密钥的位置 | 字符串 | `your-path/Flowise/packages/server` |
| TOOL_FUNCTION_EXTERNAL_DEP | 用于工具函数的外部模块 | 字符串 | | | TOOL_FUNCTION_BUILTIN_DEP | 用于工具函数的 NodeJS 内置模块 | 字符串 | |
| DATABASE_TYPE | 存储 flowise 数据的数据库类型 | 枚举字符串: `sqlite`, `mysql`, `postgres` | `sqlite` | | TOOL_FUNCTION_EXTERNAL_DEP | 用于工具函数的外部模块 | 字符串 | |
| DATABASE_PATH | 数据库保存的位置(当 DATABASE_TYPE 是 sqlite 时) | 字符串 | `your-home-dir/.flowise` | | DATABASE_TYPE | 存储 flowise 数据的数据库类型 | 枚举字符串: `sqlite`, `mysql`, `postgres` | `sqlite` |
| DATABASE_HOST | 主机 URL 或 IP 地址(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | | | DATABASE_PATH | 数据库保存的位置(当 DATABASE_TYPE 是 sqlite 时) | 字符串 | `your-home-dir/.flowise` |
| DATABASE_PORT | 数据库端口(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | | | DATABASE_HOST | 主机 URL 或 IP 地址(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
| DATABASE_USERNAME | 数据库用户名(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | | | DATABASE_PORT | 数据库端口(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
| DATABASE_PASSWORD | 数据库密码(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | | | DATABASE_USERNAME | 数据库用户名(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
| DATABASE_NAME | 数据库名称(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | | | DATABASE_PASSWORD | 数据库密码(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
| SECRETKEY_PATH | 保存加密密钥(用于加密/解密凭据)的位置 | 字符串 | `your-path/Flowise/packages/server` | | DATABASE_NAME | 数据库名称(当 DATABASE_TYPE 不是 sqlite 时) | 字符串 | |
| FLOWISE_SECRETKEY_OVERWRITE | 加密密钥用于替代存储在 SECRETKEY_PATH 中的密钥 | 字符串 | | SECRETKEY_PATH | 保存加密密钥(用于加密/解密凭据)的位置 | 字符串 | `your-path/Flowise/packages/server` |
| DISABLE_FLOWISE_TELEMETRY | 关闭遥测 | 字符串 | | FLOWISE_SECRETKEY_OVERWRITE | 加密密钥用于替代存储在 SECRETKEY_PATH 中的密钥 | 字符串 |
| MODEL_LIST_CONFIG_JSON | 加载模型的位置 | 字符 | `/your_model_list_config_file_path` | | DISABLE_FLOWISE_TELEMETRY | 关闭遥测 | 字符串 |
| STORAGE_TYPE | 上传文件的存储类型 | 枚举字符串: `local`, `s3` | `local` | | MODEL_LIST_CONFIG_JSON | 加载模型的位置 | 字符 | `/your_model_list_config_file_path` |
| BLOB_STORAGE_PATH | 上传文件存储的本地文件夹路径, 当`STORAGE_TYPE`是`local` | 字符串 | `your-home-dir/.flowise/storage` | | STORAGE_TYPE | 上传文件的存储类型 | 枚举字符串: `local`, `s3` | `local` |
| S3_STORAGE_BUCKET_NAME | S3 存储文件夹路径, 当`STORAGE_TYPE`是`s3` | 字符串 | | | BLOB_STORAGE_PATH | 上传文件存储的本地文件夹路径, 当`STORAGE_TYPE`是`local` | 字符串 | `your-home-dir/.flowise/storage` |
| S3_STORAGE_ACCESS_KEY_ID | AWS 访问密钥 (Access Key) | 字符串 | | | S3_STORAGE_BUCKET_NAME | S3 存储文件夹路径, 当`STORAGE_TYPE`是`s3` | 字符串 | |
| S3_STORAGE_SECRET_ACCESS_KEY | AWS 密钥 (Secret Key) | 字符串 | | | S3_STORAGE_ACCESS_KEY_ID | AWS 访问密钥 (Access Key) | 字符串 | |
| S3_STORAGE_REGION | S3 存储地区 | 字符串 | | | S3_STORAGE_SECRET_ACCESS_KEY | AWS 密钥 (Secret Key) | 字符串 | |
| S3_STORAGE_REGION | S3 存储地区 | 字符串 | |
您也可以在使用 `npx` 时指定环境变量。例如: 您也可以在使用 `npx` 时指定环境变量。例如:

View File

@ -10,7 +10,7 @@
[![GitHub star chart](https://img.shields.io/github/stars/FlowiseAI/Flowise?style=social)](https://star-history.com/#FlowiseAI/Flowise) [![GitHub star chart](https://img.shields.io/github/stars/FlowiseAI/Flowise?style=social)](https://star-history.com/#FlowiseAI/Flowise)
[![GitHub fork](https://img.shields.io/github/forks/FlowiseAI/Flowise?style=social)](https://github.com/FlowiseAI/Flowise/fork) [![GitHub fork](https://img.shields.io/github/forks/FlowiseAI/Flowise?style=social)](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> <h3>ドラッグ&ドロップでカスタマイズした LLM フローを構築できる UI</h3>
<a href="https://github.com/FlowiseAI/Flowise"> <a href="https://github.com/FlowiseAI/Flowise">
@ -44,9 +44,9 @@
1. プロジェクトのルートにある `docker` フォルダに移動する 1. プロジェクトのルートにある `docker` フォルダに移動する
2. `.env.example` ファイルをコピーして同じ場所に貼り付け、名前を `.env` に変更する 2. `.env.example` ファイルをコピーして同じ場所に貼り付け、名前を `.env` に変更する
3. `docker-compose up -d` 3. `docker compose up -d`
4. [http://localhost:3000](http://localhost:3000) を開く 4. [http://localhost:3000](http://localhost:3000) を開く
5. コンテナを停止するには、`docker-compose stop` を使用します 5. コンテナを停止するには、`docker compose stop` を使用します
### Docker Image ### Docker Image

View File

@ -10,7 +10,7 @@
[![GitHub star chart](https://img.shields.io/github/stars/FlowiseAI/Flowise?style=social)](https://star-history.com/#FlowiseAI/Flowise) [![GitHub star chart](https://img.shields.io/github/stars/FlowiseAI/Flowise?style=social)](https://star-history.com/#FlowiseAI/Flowise)
[![GitHub fork](https://img.shields.io/github/forks/FlowiseAI/Flowise?style=social)](https://github.com/FlowiseAI/Flowise/fork) [![GitHub fork](https://img.shields.io/github/forks/FlowiseAI/Flowise?style=social)](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> <h3>드래그 앤 드롭 UI로 맞춤형 LLM 플로우 구축하기</h3>
<a href="https://github.com/FlowiseAI/Flowise"> <a href="https://github.com/FlowiseAI/Flowise">
@ -44,9 +44,9 @@ English | [中文](./README-ZH.md) | [日本語](./README-JA.md) | 한국어
1. 프로젝트의 최상위(root) 디렉토리에 있는 `docker` 폴더로 이동하세요. 1. 프로젝트의 최상위(root) 디렉토리에 있는 `docker` 폴더로 이동하세요.
2. `.env.example` 파일을 복사한 후, 같은 경로에 붙여넣기 한 다음, `.env`로 이름을 변경합니다. 2. `.env.example` 파일을 복사한 후, 같은 경로에 붙여넣기 한 다음, `.env`로 이름을 변경합니다.
3. `docker-compose up -d` 실행 3. `docker compose up -d` 실행
4. [http://localhost:3000](http://localhost:3000) URL 열기 4. [http://localhost:3000](http://localhost:3000) URL 열기
5. `docker-compose stop` 명령어를 통해 컨테이너를 종료시킬 수 있습니다. 5. `docker compose stop` 명령어를 통해 컨테이너를 종료시킬 수 있습니다.
### 도커 이미지 활용 ### 도커 이미지 활용

View File

@ -10,7 +10,7 @@
[![GitHub星图](https://img.shields.io/github/stars/FlowiseAI/Flowise?style=social)](https://star-history.com/#FlowiseAI/Flowise) [![GitHub星图](https://img.shields.io/github/stars/FlowiseAI/Flowise?style=social)](https://star-history.com/#FlowiseAI/Flowise)
[![GitHub分支](https://img.shields.io/github/forks/FlowiseAI/Flowise?style=social)](https://github.com/FlowiseAI/Flowise/fork) [![GitHub分支](https://img.shields.io/github/forks/FlowiseAI/Flowise?style=social)](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> <h3>拖放界面构建定制化的LLM流程</h3>
<a href="https://github.com/FlowiseAI/Flowise"> <a href="https://github.com/FlowiseAI/Flowise">
@ -44,9 +44,9 @@
1. 进入项目根目录下的 `docker` 文件夹 1. 进入项目根目录下的 `docker` 文件夹
2. 创建 `.env` 文件并指定 `PORT`(参考 `.env.example` 2. 创建 `.env` 文件并指定 `PORT`(参考 `.env.example`
3. 运行 `docker-compose up -d` 3. 运行 `docker compose up -d`
4. 打开 [http://localhost:3000](http://localhost:3000) 4. 打开 [http://localhost:3000](http://localhost:3000)
5. 可以通过 `docker-compose stop` 停止容器 5. 可以通过 `docker compose stop` 停止容器
### Docker 镜像 ### Docker 镜像

View File

@ -1,6 +1,6 @@
{ {
"name": "flowise", "name": "flowise",
"version": "1.8.0", "version": "1.8.4",
"private": true, "private": true,
"homepage": "https://flowiseai.com", "homepage": "https://flowiseai.com",
"workspaces": [ "workspaces": [
@ -65,6 +65,34 @@
"resolutions": { "resolutions": {
"@qdrant/openapi-typescript-fetch": "1.2.1", "@qdrant/openapi-typescript-fetch": "1.2.1",
"@google/generative-ai": "^0.7.0", "@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"
}
}
]
]
} }
} }

View File

@ -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 }

View File

@ -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 }

View File

@ -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 }

View File

@ -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 }

View File

@ -10,12 +10,26 @@ class OpenSearchUrl implements INodeCredential {
constructor() { constructor() {
this.label = 'OpenSearch' this.label = 'OpenSearch'
this.name = 'openSearchUrl' this.name = 'openSearchUrl'
this.version = 1.0 this.version = 2.0
this.inputs = [ this.inputs = [
{ {
label: 'OpenSearch Url', label: 'OpenSearch Url',
name: 'openSearchUrl', name: 'openSearchUrl',
type: 'string' type: 'string'
},
{
label: 'User',
name: 'user',
type: 'string',
placeholder: '<OPENSEARCH_USERNAME>',
optional: true
},
{
label: 'Password',
name: 'password',
type: 'password',
placeholder: '<OPENSEARCH_PASSWORD>',
optional: true
} }
] ]
} }

View File

@ -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 }

View File

@ -8,6 +8,11 @@
"name": "anthropic.claude-3-haiku-20240307-v1:0", "name": "anthropic.claude-3-haiku-20240307-v1:0",
"description": "Image to text, conversation, chat optimized" "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", "label": "anthropic.claude-3-sonnet",
"name": "anthropic.claude-3-sonnet-20240229-v1:0", "name": "anthropic.claude-3-sonnet-20240229-v1:0",
@ -215,6 +220,10 @@
{ {
"name": "azureChatOpenAI", "name": "azureChatOpenAI",
"models": [ "models": [
{
"label": "gpt-4o",
"name": "gpt-4o"
},
{ {
"label": "gpt-4", "label": "gpt-4",
"name": "gpt-4" "name": "gpt-4"
@ -240,6 +249,10 @@
{ {
"name": "azureChatOpenAI_LlamaIndex", "name": "azureChatOpenAI_LlamaIndex",
"models": [ "models": [
{
"label": "gpt-4o",
"name": "gpt-4o"
},
{ {
"label": "gpt-4", "label": "gpt-4",
"name": "gpt-4" "name": "gpt-4"
@ -283,6 +296,11 @@
"name": "claude-3-opus-20240229", "name": "claude-3-opus-20240229",
"description": "Most powerful model for highly complex tasks" "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", "label": "claude-3-sonnet",
"name": "claude-3-sonnet-20240229", "name": "claude-3-sonnet-20240229",
@ -1029,22 +1047,42 @@
{ {
"label": "voyage-2", "label": "voyage-2",
"name": "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", "label": "voyage-code-2",
"name": "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", "label": "voyage-large-2",
"name": "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", "label": "voyage-lite-02-instruct",
"name": "voyage-lite-02-instruct", "name": "voyage-lite-02-instruct",
"description": "Instruction-tuned for classification, clustering, and sentence textual similarity tasks" "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."
} }
] ]
}, },

View File

@ -1,187 +0,0 @@
import { flatten } from 'lodash'
import { BaseMessage } from '@langchain/core/messages'
import { ChainValues } from '@langchain/core/utils/types'
import { AgentStep } from '@langchain/core/agents'
import { RunnableSequence } from '@langchain/core/runnables'
import { ChatOpenAI, formatToOpenAIFunction } from '@langchain/openai'
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'
import { OpenAIFunctionsAgentOutputParser } from 'langchain/agents/openai/output_parser'
import { FlowiseMemory, ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
import { getBaseClasses } from '../../../src/utils'
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
import { AgentExecutor, formatAgentSteps } from '../../../src/agents'
import { checkInputs, Moderation } from '../../moderation/Moderation'
import { formatResponse } from '../../outputparsers/OutputParserHelpers'
const defaultMessage = `Do your best to answer the questions. Feel free to use any tools available to look up relevant information, only if necessary.`
class ConversationalRetrievalAgent_Agents implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
baseClasses: string[]
inputs: INodeParams[]
badge?: string
sessionId?: string
constructor(fields?: { sessionId?: string }) {
this.label = 'Conversational Retrieval Agent'
this.name = 'conversationalRetrievalAgent'
this.version = 4.0
this.type = 'AgentExecutor'
this.category = 'Agents'
this.badge = 'DEPRECATING'
this.icon = 'agent.svg'
this.description = `An agent optimized for retrieval during conversation, answering questions based on past dialogue, all using OpenAI's Function Calling`
this.baseClasses = [this.type, ...getBaseClasses(AgentExecutor)]
this.inputs = [
{
label: 'Allowed Tools',
name: 'tools',
type: 'Tool',
list: true
},
{
label: 'Memory',
name: 'memory',
type: 'BaseChatMemory'
},
{
label: 'OpenAI/Azure Chat Model',
name: 'model',
type: 'BaseChatModel'
},
{
label: 'System Message',
name: 'systemMessage',
type: 'string',
default: defaultMessage,
rows: 4,
optional: true,
additionalParams: true
},
{
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
},
{
label: 'Max Iterations',
name: 'maxIterations',
type: 'number',
optional: true,
additionalParams: true
}
]
this.sessionId = fields?.sessionId
}
async init(nodeData: INodeData, input: string, options: ICommonObject): Promise<any> {
return prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
}
async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string | object> {
const memory = nodeData.inputs?.memory as FlowiseMemory
const moderations = nodeData.inputs?.inputModeration as Moderation[]
if (moderations && moderations.length > 0) {
try {
// Use the output of the moderation chain as input for the BabyAGI agent
input = await checkInputs(moderations, input)
} catch (e) {
await new Promise((resolve) => setTimeout(resolve, 500))
//streamResponse(options.socketIO && options.socketIOClientId, e.message, options.socketIO, options.socketIOClientId)
return formatResponse(e.message)
}
}
const executor = prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
const loggerHandler = new ConsoleCallbackHandler(options.logger)
const callbacks = await additionalCallbacks(nodeData, options)
let res: ChainValues = {}
if (options.socketIO && options.socketIOClientId) {
const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId)
res = await executor.invoke({ input }, { callbacks: [loggerHandler, handler, ...callbacks] })
} else {
res = await executor.invoke({ input }, { callbacks: [loggerHandler, ...callbacks] })
}
await memory.addChatMessages(
[
{
text: input,
type: 'userMessage'
},
{
text: res?.output,
type: 'apiMessage'
}
],
this.sessionId
)
return res?.output
}
}
const prepareAgent = (nodeData: INodeData, options: ICommonObject, flowObj: { sessionId?: string; chatId?: string; input?: string }) => {
const model = nodeData.inputs?.model as ChatOpenAI
const memory = nodeData.inputs?.memory as FlowiseMemory
const systemMessage = nodeData.inputs?.systemMessage as string
const maxIterations = nodeData.inputs?.maxIterations as string
let tools = nodeData.inputs?.tools
tools = flatten(tools)
const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history'
const inputKey = memory.inputKey ? memory.inputKey : 'input'
const prependMessages = options?.prependMessages
const prompt = ChatPromptTemplate.fromMessages([
['ai', systemMessage ? systemMessage : defaultMessage],
new MessagesPlaceholder(memoryKey),
['human', `{${inputKey}}`],
new MessagesPlaceholder('agent_scratchpad')
])
const modelWithFunctions = model.bind({
functions: [...tools.map((tool: any) => formatToOpenAIFunction(tool))]
})
const runnableAgent = RunnableSequence.from([
{
[inputKey]: (i: { input: string; steps: AgentStep[] }) => i.input,
agent_scratchpad: (i: { input: string; steps: AgentStep[] }) => formatAgentSteps(i.steps),
[memoryKey]: async (_: { input: string; steps: AgentStep[] }) => {
const messages = (await memory.getChatMessages(flowObj?.sessionId, true, prependMessages)) as BaseMessage[]
return messages ?? []
}
},
prompt,
modelWithFunctions,
new OpenAIFunctionsAgentOutputParser()
])
const executor = AgentExecutor.fromAgentAndTools({
agent: runnableAgent,
tools,
sessionId: flowObj?.sessionId,
chatId: flowObj?.chatId,
input: flowObj?.input,
returnIntermediateSteps: true,
verbose: process.env.DEBUG === 'true' ? true : false,
maxIterations: maxIterations ? parseFloat(maxIterations) : undefined
})
return executor
}
module.exports = { nodeClass: ConversationalRetrievalAgent_Agents }

View File

@ -1,7 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 6C10 5.44772 10.4477 5 11 5H21C21.5523 5 22 5.44772 22 6V11C22 13.2091 20.2091 15 18 15H14C11.7909 15 10 13.2091 10 11V6Z" stroke="black" stroke-width="2" stroke-linejoin="round"/>
<path d="M16 5V3" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="14" cy="9" r="1.5" fill="black"/>
<circle cx="18" cy="9" r="1.5" fill="black"/>
<path d="M26 27C26 22.0294 21.5228 18 16 18C10.4772 18 6 22.0294 6 27" stroke="black" stroke-width="2" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 616 B

View File

@ -1 +0,0 @@
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 6H4v19.5h1m8-7.5v3h1m7-11.5V6h1m-5 7.5V10h1" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><mask id="MistralAI__a" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="5" y="6" width="22" height="20"><path d="M5 6v19.5h5v-8h4V21h4v-3.5h4V25h5V6h-4.5v4H18v3.5h-4v-4h-4V6H5Z" fill="#FD7000"/></mask><g mask="url(#MistralAI__a)"><path fill="#FFCD00" d="M4 6h25v4H4z"/></g><mask id="MistralAI__b" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="5" y="6" width="22" height="20"><path d="M5 6v19.5h5v-8h4V21h4v-3.5h4V25h5V6h-4.5v4H18v3.5h-4v-4h-4V6H5Z" fill="#FD7000"/></mask><g mask="url(#MistralAI__b)"><path fill="#FFA200" d="M4 10h25v4H4z"/></g><mask id="MistralAI__c" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="5" y="6" width="22" height="20"><path d="M5 6v19.5h5v-8h4V21h4v-3.5h4V25h5V6h-4.5v4H18v3.5h-4v-4h-4V6H5Z" fill="#FD7000"/></mask><g mask="url(#MistralAI__c)"><path fill="#FF6E00" d="M4 14h25v4H4z"/></g><mask id="MistralAI__d" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="5" y="6" width="22" height="20"><path d="M5 6v19.5h5v-8h4V21h4v-3.5h4V25h5V6h-4.5v4H18v3.5h-4v-4h-4V6H5Z" fill="#FD7000"/></mask><g mask="url(#MistralAI__d)"><path fill="#FF4A09" d="M4 18h25v4H4z"/></g><mask id="MistralAI__e" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="5" y="6" width="22" height="20"><path d="M5 6v19.5h5v-8h4V21h4v-3.5h4V25h5V6h-4.5v4H18v3.5h-4v-4h-4V6H5Z" fill="#FD7000"/></mask><g mask="url(#MistralAI__e)"><path fill="#FE060F" d="M4 22h25v4H4z"/></g><path d="M21 18v7h1" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M5 6v19.5h5v-8h4V21h4v-3.5h4V25h5V6h-4.5v4H18v3.5h-4v-4h-4V6H5Z" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,213 +0,0 @@
import { flatten } from 'lodash'
import { BaseMessage } from '@langchain/core/messages'
import { ChainValues } from '@langchain/core/utils/types'
import { AgentStep } from '@langchain/core/agents'
import { RunnableSequence } from '@langchain/core/runnables'
import { ChatOpenAI } from '@langchain/openai'
import { convertToOpenAITool } from '@langchain/core/utils/function_calling'
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'
import { OpenAIToolsAgentOutputParser } from 'langchain/agents/openai/output_parser'
import { getBaseClasses } from '../../../src/utils'
import { FlowiseMemory, ICommonObject, INode, INodeData, INodeParams, IUsedTool } from '../../../src/Interface'
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
import { AgentExecutor, formatAgentSteps } from '../../../src/agents'
import { Moderation, checkInputs, streamResponse } from '../../moderation/Moderation'
import { formatResponse } from '../../outputparsers/OutputParserHelpers'
class MistralAIToolAgent_Agents implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
baseClasses: string[]
inputs: INodeParams[]
sessionId?: string
badge?: string
constructor(fields?: { sessionId?: string }) {
this.label = 'MistralAI Tool Agent'
this.name = 'mistralAIToolAgent'
this.version = 1.0
this.type = 'AgentExecutor'
this.category = 'Agents'
this.icon = 'MistralAI.svg'
this.badge = 'DEPRECATING'
this.description = `Agent that uses MistralAI Function Calling to pick the tools and args to call`
this.baseClasses = [this.type, ...getBaseClasses(AgentExecutor)]
this.inputs = [
{
label: 'Tools',
name: 'tools',
type: 'Tool',
list: true
},
{
label: 'Memory',
name: 'memory',
type: 'BaseChatMemory'
},
{
label: 'MistralAI Chat Model',
name: 'model',
type: 'BaseChatModel'
},
{
label: 'System Message',
name: 'systemMessage',
type: 'string',
rows: 4,
optional: true,
additionalParams: true
},
{
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
},
{
label: 'Max Iterations',
name: 'maxIterations',
type: 'number',
optional: true,
additionalParams: true
}
]
this.sessionId = fields?.sessionId
}
async init(nodeData: INodeData, input: string, options: ICommonObject): Promise<any> {
return prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
}
async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string | ICommonObject> {
const memory = nodeData.inputs?.memory as FlowiseMemory
const moderations = nodeData.inputs?.inputModeration as Moderation[]
if (moderations && moderations.length > 0) {
try {
// Use the output of the moderation chain as input for the OpenAI Function Agent
input = await checkInputs(moderations, input)
} catch (e) {
await new Promise((resolve) => setTimeout(resolve, 500))
streamResponse(options.socketIO && options.socketIOClientId, e.message, options.socketIO, options.socketIOClientId)
return formatResponse(e.message)
}
}
const executor = prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
const loggerHandler = new ConsoleCallbackHandler(options.logger)
const callbacks = await additionalCallbacks(nodeData, options)
let res: ChainValues = {}
let sourceDocuments: ICommonObject[] = []
let usedTools: IUsedTool[] = []
if (options.socketIO && options.socketIOClientId) {
const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId)
res = await executor.invoke({ input }, { callbacks: [loggerHandler, handler, ...callbacks] })
if (res.sourceDocuments) {
options.socketIO.to(options.socketIOClientId).emit('sourceDocuments', flatten(res.sourceDocuments))
sourceDocuments = res.sourceDocuments
}
if (res.usedTools) {
options.socketIO.to(options.socketIOClientId).emit('usedTools', res.usedTools)
usedTools = res.usedTools
}
} else {
res = await executor.invoke({ input }, { callbacks: [loggerHandler, ...callbacks] })
if (res.sourceDocuments) {
sourceDocuments = res.sourceDocuments
}
if (res.usedTools) {
usedTools = res.usedTools
}
}
await memory.addChatMessages(
[
{
text: input,
type: 'userMessage'
},
{
text: res?.output,
type: 'apiMessage'
}
],
this.sessionId
)
let finalRes = res?.output
if (sourceDocuments.length || usedTools.length) {
finalRes = { text: res?.output }
if (sourceDocuments.length) {
finalRes.sourceDocuments = flatten(sourceDocuments)
}
if (usedTools.length) {
finalRes.usedTools = usedTools
}
return finalRes
}
return finalRes
}
}
const prepareAgent = (nodeData: INodeData, options: ICommonObject, flowObj: { sessionId?: string; chatId?: string; input?: string }) => {
const model = nodeData.inputs?.model as ChatOpenAI
const memory = nodeData.inputs?.memory as FlowiseMemory
const maxIterations = nodeData.inputs?.maxIterations as string
const systemMessage = nodeData.inputs?.systemMessage as string
let tools = nodeData.inputs?.tools
tools = flatten(tools)
const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history'
const inputKey = memory.inputKey ? memory.inputKey : 'input'
const prependMessages = options?.prependMessages
const prompt = ChatPromptTemplate.fromMessages([
['system', systemMessage ? systemMessage : `You are a helpful AI assistant.`],
new MessagesPlaceholder(memoryKey),
['human', `{${inputKey}}`],
new MessagesPlaceholder('agent_scratchpad')
])
const llmWithTools = model.bind({
tools: tools.map(convertToOpenAITool)
})
const runnableAgent = RunnableSequence.from([
{
[inputKey]: (i: { input: string; steps: AgentStep[] }) => i.input,
agent_scratchpad: (i: { input: string; steps: AgentStep[] }) => formatAgentSteps(i.steps),
[memoryKey]: async (_: { input: string; steps: AgentStep[] }) => {
const messages = (await memory.getChatMessages(flowObj?.sessionId, true, prependMessages)) as BaseMessage[]
return messages ?? []
}
},
prompt,
llmWithTools,
new OpenAIToolsAgentOutputParser()
])
const executor = AgentExecutor.fromAgentAndTools({
agent: runnableAgent,
tools,
sessionId: flowObj?.sessionId,
chatId: flowObj?.chatId,
input: flowObj?.input,
verbose: process.env.DEBUG === 'true' ? true : false,
maxIterations: maxIterations ? parseFloat(maxIterations) : undefined
})
return executor
}
module.exports = { nodeClass: MistralAIToolAgent_Agents }

View File

@ -27,7 +27,7 @@ class OpenAIAssistant_Agents implements INode {
constructor() { constructor() {
this.label = 'OpenAI Assistant' this.label = 'OpenAI Assistant'
this.name = 'openAIAssistant' this.name = 'openAIAssistant'
this.version = 3.0 this.version = 4.0
this.type = 'OpenAIAssistant' this.type = 'OpenAIAssistant'
this.category = 'Agents' this.category = 'Agents'
this.icon = 'assistant.svg' this.icon = 'assistant.svg'
@ -54,6 +54,25 @@ class OpenAIAssistant_Agents implements INode {
optional: true, optional: true,
list: 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', label: 'Disable File Download',
name: 'disableFileDownload', name: 'disableFileDownload',
@ -138,10 +157,14 @@ class OpenAIAssistant_Agents implements INode {
const openai = new OpenAI({ apiKey: openAIApiKey }) const openai = new OpenAI({ apiKey: openAIApiKey })
options.logger.info(`Clearing OpenAI Thread ${sessionId}`) options.logger.info(`Clearing OpenAI Thread ${sessionId}`)
try { try {
if (sessionId) await openai.beta.threads.del(sessionId) if (sessionId && sessionId.startsWith('thread_')) {
options.logger.info(`Successfully cleared OpenAI Thread ${sessionId}`) 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) { } 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 databaseEntities = options.databaseEntities as IDatabaseEntity
const disableFileDownload = nodeData.inputs?.disableFileDownload as boolean const disableFileDownload = nodeData.inputs?.disableFileDownload as boolean
const moderations = nodeData.inputs?.inputModeration as Moderation[] 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 isStreaming = options.socketIO && options.socketIOClientId
const socketIO = isStreaming ? options.socketIO : undefined const socketIO = isStreaming ? options.socketIO : undefined
const socketIOClientId = isStreaming ? options.socketIOClientId : '' const socketIOClientId = isStreaming ? options.socketIOClientId : ''
@ -269,10 +294,25 @@ class OpenAIAssistant_Agents implements INode {
let runThreadId = '' let runThreadId = ''
let isStreamingStarted = false 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) { if (isStreaming) {
const streamThread = await openai.beta.threads.runs.create(threadId, { const streamThread = await openai.beta.threads.runs.create(threadId, {
assistant_id: retrievedAssistant.id, assistant_id: retrievedAssistant.id,
stream: true stream: true,
tool_choice: toolChoice,
parallel_tool_calls: parallelToolCalls
}) })
for await (const event of streamThread) { for await (const event of streamThread) {
@ -595,7 +635,9 @@ class OpenAIAssistant_Agents implements INode {
// Polling run status // Polling run status
const runThread = await openai.beta.threads.runs.create(threadId, { 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 runThreadId = runThread.id
let state = await promise(threadId, runThread.id) let state = await promise(threadId, runThread.id)
@ -608,7 +650,9 @@ class OpenAIAssistant_Agents implements INode {
if (retries > 0) { if (retries > 0) {
retries -= 1 retries -= 1
const newRunThread = await openai.beta.threads.runs.create(threadId, { 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 runThreadId = newRunThread.id
state = await promise(threadId, newRunThread.id) state = await promise(threadId, newRunThread.id)

View File

@ -1,212 +0,0 @@
import { flatten } from 'lodash'
import { BaseMessage } from '@langchain/core/messages'
import { ChainValues } from '@langchain/core/utils/types'
import { AgentStep } from '@langchain/core/agents'
import { RunnableSequence } from '@langchain/core/runnables'
import { ChatOpenAI, formatToOpenAIFunction } from '@langchain/openai'
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'
import { OpenAIFunctionsAgentOutputParser } from 'langchain/agents/openai/output_parser'
import { getBaseClasses } from '../../../src/utils'
import { FlowiseMemory, ICommonObject, INode, INodeData, INodeParams, IUsedTool } from '../../../src/Interface'
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
import { AgentExecutor, formatAgentSteps } from '../../../src/agents'
import { Moderation, checkInputs } from '../../moderation/Moderation'
import { formatResponse } from '../../outputparsers/OutputParserHelpers'
class OpenAIFunctionAgent_Agents implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
baseClasses: string[]
inputs: INodeParams[]
badge?: string
sessionId?: string
constructor(fields?: { sessionId?: string }) {
this.label = 'OpenAI Function Agent'
this.name = 'openAIFunctionAgent'
this.version = 4.0
this.type = 'AgentExecutor'
this.category = 'Agents'
this.icon = 'function.svg'
this.description = `An agent that uses OpenAI Function Calling to pick the tool and args to call`
this.baseClasses = [this.type, ...getBaseClasses(AgentExecutor)]
this.badge = 'DEPRECATING'
this.inputs = [
{
label: 'Allowed Tools',
name: 'tools',
type: 'Tool',
list: true
},
{
label: 'Memory',
name: 'memory',
type: 'BaseChatMemory'
},
{
label: 'OpenAI/Azure Chat Model',
name: 'model',
type: 'BaseChatModel'
},
{
label: 'System Message',
name: 'systemMessage',
type: 'string',
rows: 4,
optional: true,
additionalParams: true
},
{
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
},
{
label: 'Max Iterations',
name: 'maxIterations',
type: 'number',
optional: true,
additionalParams: true
}
]
this.sessionId = fields?.sessionId
}
async init(nodeData: INodeData, input: string, options: ICommonObject): Promise<any> {
return prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
}
async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string | ICommonObject> {
const memory = nodeData.inputs?.memory as FlowiseMemory
const moderations = nodeData.inputs?.inputModeration as Moderation[]
if (moderations && moderations.length > 0) {
try {
// Use the output of the moderation chain as input for the OpenAI Function Agent
input = await checkInputs(moderations, input)
} catch (e) {
await new Promise((resolve) => setTimeout(resolve, 500))
//streamResponse(options.socketIO && options.socketIOClientId, e.message, options.socketIO, options.socketIOClientId)
return formatResponse(e.message)
}
}
const executor = prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
const loggerHandler = new ConsoleCallbackHandler(options.logger)
const callbacks = await additionalCallbacks(nodeData, options)
let res: ChainValues = {}
let sourceDocuments: ICommonObject[] = []
let usedTools: IUsedTool[] = []
if (options.socketIO && options.socketIOClientId) {
const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId)
res = await executor.invoke({ input }, { callbacks: [loggerHandler, handler, ...callbacks] })
if (res.sourceDocuments) {
options.socketIO.to(options.socketIOClientId).emit('sourceDocuments', flatten(res.sourceDocuments))
sourceDocuments = res.sourceDocuments
}
if (res.usedTools) {
options.socketIO.to(options.socketIOClientId).emit('usedTools', res.usedTools)
usedTools = res.usedTools
}
} else {
res = await executor.invoke({ input }, { callbacks: [loggerHandler, ...callbacks] })
if (res.sourceDocuments) {
sourceDocuments = res.sourceDocuments
}
if (res.usedTools) {
usedTools = res.usedTools
}
}
await memory.addChatMessages(
[
{
text: input,
type: 'userMessage'
},
{
text: res?.output,
type: 'apiMessage'
}
],
this.sessionId
)
let finalRes = res?.output
if (sourceDocuments.length || usedTools.length) {
finalRes = { text: res?.output }
if (sourceDocuments.length) {
finalRes.sourceDocuments = flatten(sourceDocuments)
}
if (usedTools.length) {
finalRes.usedTools = usedTools
}
return finalRes
}
return finalRes
}
}
const prepareAgent = (nodeData: INodeData, options: ICommonObject, flowObj: { sessionId?: string; chatId?: string; input?: string }) => {
const model = nodeData.inputs?.model as ChatOpenAI
const maxIterations = nodeData.inputs?.maxIterations as string
const memory = nodeData.inputs?.memory as FlowiseMemory
const systemMessage = nodeData.inputs?.systemMessage as string
let tools = nodeData.inputs?.tools
tools = flatten(tools)
const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history'
const inputKey = memory.inputKey ? memory.inputKey : 'input'
const prependMessages = options?.prependMessages
const prompt = ChatPromptTemplate.fromMessages([
['system', systemMessage ? systemMessage : `You are a helpful AI assistant.`],
new MessagesPlaceholder(memoryKey),
['human', `{${inputKey}}`],
new MessagesPlaceholder('agent_scratchpad')
])
const modelWithFunctions = model.bind({
functions: [...tools.map((tool: any) => formatToOpenAIFunction(tool))]
})
const runnableAgent = RunnableSequence.from([
{
[inputKey]: (i: { input: string; steps: AgentStep[] }) => i.input,
agent_scratchpad: (i: { input: string; steps: AgentStep[] }) => formatAgentSteps(i.steps),
[memoryKey]: async (_: { input: string; steps: AgentStep[] }) => {
const messages = (await memory.getChatMessages(flowObj?.sessionId, true, prependMessages)) as BaseMessage[]
return messages ?? []
}
},
prompt,
modelWithFunctions,
new OpenAIFunctionsAgentOutputParser()
])
const executor = AgentExecutor.fromAgentAndTools({
agent: runnableAgent,
tools,
sessionId: flowObj?.sessionId,
chatId: flowObj?.chatId,
input: flowObj?.input,
verbose: process.env.DEBUG === 'true' ? true : false,
maxIterations: maxIterations ? parseFloat(maxIterations) : undefined
})
return executor
}
module.exports = { nodeClass: OpenAIFunctionAgent_Agents }

View File

@ -1,9 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 12.6108L22 15.9608" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.17701 19.5848C6.49568 20.4069 6.12505 21.4424 6.12993 22.5101C6.13481 23.5779 6.51489 24.6099 7.2037 25.4258C7.89252 26.2416 8.84622 26.7893 9.89802 26.9732C10.9498 27.157 12.0328 26.9653 12.9575 26.4314L15.4787 24.9657M18.6002 14.106V19.5848" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.19877 9.98459C6.39026 9.67775 4.57524 10.4982 3.60403 12.1806C3.00524 13.2178 2.84295 14.4504 3.15284 15.6073C3.46273 16.7642 4.21943 17.7507 5.25652 18.3498L10.3049 21.3269C10.6109 21.5074 10.9898 21.5119 11.3001 21.3388L18.6 17.2655" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.0172 6.06585C16.6456 5.06522 15.9342 4.227 15.0072 3.6977C14.0803 3.1684 12.9969 2.98168 11.9462 3.17018C10.8956 3.35869 9.94464 3.91042 9.25954 4.72895C8.57444 5.54747 8.19879 6.58074 8.19824 7.64814V13.6575C8.19824 14.0154 8.38951 14.346 8.69977 14.5244L15.9992 18.7215" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M24.8216 11.7476C25.5029 10.9255 25.8735 9.89004 25.8687 8.8223C25.8638 7.75457 25.4837 6.72253 24.7949 5.90667C24.1061 5.09082 23.1524 4.54308 22.1006 4.35924C21.0488 4.17541 19.9658 4.36718 19.0411 4.90101L13.8942 7.90613C13.5872 8.08539 13.3984 8.41418 13.3984 8.76971V17.2265" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M28.3944 19.0635C28.9932 18.0263 29.1555 16.7937 28.8456 15.6368C28.5357 14.4799 27.779 13.4934 26.7419 12.8943L21.6409 9.91752C21.3316 9.73703 20.9494 9.7357 20.6388 9.91405L13.3984 14.0723" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 28.9997H18.8071C19.6909 28.9997 20.4526 28.3921 20.6297 27.546L21.991 21.4537C22.1681 20.6076 22.9299 20 23.8136 20H24.6207M20.0929 22.7023H23.8136M24 25.0214H24.5014C24.8438 25.0214 25.1586 25.2052 25.3207 25.5L27.3429 28.5213C27.5051 28.8161 27.8198 29 28.1622 29H28.6997M24.049 29C24.6261 29 25.1609 28.7041 25.4578 28.2205L27.2424 25.8009C27.5393 25.3173 28.0741 25.0214 28.6512 25.0214" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,211 +0,0 @@
import { flatten } from 'lodash'
import { BaseMessage } from '@langchain/core/messages'
import { ChainValues } from '@langchain/core/utils/types'
import { RunnableSequence } from '@langchain/core/runnables'
import { ChatOpenAI } from '@langchain/openai'
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'
import { convertToOpenAITool } from '@langchain/core/utils/function_calling'
import { formatToOpenAIToolMessages } from 'langchain/agents/format_scratchpad/openai_tools'
import { OpenAIToolsAgentOutputParser, type ToolsAgentStep } from 'langchain/agents/openai/output_parser'
import { getBaseClasses } from '../../../src/utils'
import { FlowiseMemory, ICommonObject, INode, INodeData, INodeParams, IUsedTool } from '../../../src/Interface'
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
import { AgentExecutor } from '../../../src/agents'
import { Moderation, checkInputs } from '../../moderation/Moderation'
import { formatResponse } from '../../outputparsers/OutputParserHelpers'
class OpenAIToolAgent_Agents implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
baseClasses: string[]
inputs: INodeParams[]
sessionId?: string
badge?: string
constructor(fields?: { sessionId?: string }) {
this.label = 'OpenAI Tool Agent'
this.name = 'openAIToolAgent'
this.version = 1.0
this.type = 'AgentExecutor'
this.category = 'Agents'
this.icon = 'function.svg'
this.description = `Agent that uses OpenAI Function Calling to pick the tools and args to call`
this.baseClasses = [this.type, ...getBaseClasses(AgentExecutor)]
this.badge = 'DEPRECATING'
this.inputs = [
{
label: 'Tools',
name: 'tools',
type: 'Tool',
list: true
},
{
label: 'Memory',
name: 'memory',
type: 'BaseChatMemory'
},
{
label: 'OpenAI/Azure Chat Model',
name: 'model',
type: 'BaseChatModel'
},
{
label: 'System Message',
name: 'systemMessage',
type: 'string',
rows: 4,
optional: true,
additionalParams: true
},
{
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
},
{
label: 'Max Iterations',
name: 'maxIterations',
type: 'number',
optional: true,
additionalParams: true
}
]
this.sessionId = fields?.sessionId
}
async init(nodeData: INodeData, input: string, options: ICommonObject): Promise<any> {
return prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
}
async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string | ICommonObject> {
const memory = nodeData.inputs?.memory as FlowiseMemory
const moderations = nodeData.inputs?.inputModeration as Moderation[]
if (moderations && moderations.length > 0) {
try {
// Use the output of the moderation chain as input for the OpenAI Function Agent
input = await checkInputs(moderations, input)
} catch (e) {
await new Promise((resolve) => setTimeout(resolve, 500))
//streamResponse(options.socketIO && options.socketIOClientId, e.message, options.socketIO, options.socketIOClientId)
return formatResponse(e.message)
}
}
const executor = prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input })
const loggerHandler = new ConsoleCallbackHandler(options.logger)
const callbacks = await additionalCallbacks(nodeData, options)
let res: ChainValues = {}
let sourceDocuments: ICommonObject[] = []
let usedTools: IUsedTool[] = []
if (options.socketIO && options.socketIOClientId) {
const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId)
res = await executor.invoke({ input }, { callbacks: [loggerHandler, handler, ...callbacks] })
if (res.sourceDocuments) {
options.socketIO.to(options.socketIOClientId).emit('sourceDocuments', flatten(res.sourceDocuments))
sourceDocuments = res.sourceDocuments
}
if (res.usedTools) {
options.socketIO.to(options.socketIOClientId).emit('usedTools', res.usedTools)
usedTools = res.usedTools
}
} else {
res = await executor.invoke({ input }, { callbacks: [loggerHandler, ...callbacks] })
if (res.sourceDocuments) {
sourceDocuments = res.sourceDocuments
}
if (res.usedTools) {
usedTools = res.usedTools
}
}
await memory.addChatMessages(
[
{
text: input,
type: 'userMessage'
},
{
text: res?.output,
type: 'apiMessage'
}
],
this.sessionId
)
let finalRes = res?.output
if (sourceDocuments.length || usedTools.length) {
finalRes = { text: res?.output }
if (sourceDocuments.length) {
finalRes.sourceDocuments = flatten(sourceDocuments)
}
if (usedTools.length) {
finalRes.usedTools = usedTools
}
return finalRes
}
return finalRes
}
}
const prepareAgent = (nodeData: INodeData, options: ICommonObject, flowObj: { sessionId?: string; chatId?: string; input?: string }) => {
const model = nodeData.inputs?.model as ChatOpenAI
const maxIterations = nodeData.inputs?.maxIterations as string
const memory = nodeData.inputs?.memory as FlowiseMemory
const systemMessage = nodeData.inputs?.systemMessage as string
let tools = nodeData.inputs?.tools
tools = flatten(tools)
const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history'
const inputKey = memory.inputKey ? memory.inputKey : 'input'
const prependMessages = options?.prependMessages
const prompt = ChatPromptTemplate.fromMessages([
['system', systemMessage ? systemMessage : `You are a helpful AI assistant.`],
new MessagesPlaceholder(memoryKey),
['human', `{${inputKey}}`],
new MessagesPlaceholder('agent_scratchpad')
])
const modelWithTools = model.bind({ tools: tools.map(convertToOpenAITool) })
const runnableAgent = RunnableSequence.from([
{
[inputKey]: (i: { input: string; steps: ToolsAgentStep[] }) => i.input,
agent_scratchpad: (i: { input: string; steps: ToolsAgentStep[] }) => formatToOpenAIToolMessages(i.steps),
[memoryKey]: async (_: { input: string; steps: ToolsAgentStep[] }) => {
const messages = (await memory.getChatMessages(flowObj?.sessionId, true, prependMessages)) as BaseMessage[]
return messages ?? []
}
},
prompt,
modelWithTools,
new OpenAIToolsAgentOutputParser()
])
const executor = AgentExecutor.fromAgentAndTools({
agent: runnableAgent,
tools,
sessionId: flowObj?.sessionId,
chatId: flowObj?.chatId,
input: flowObj?.input,
verbose: process.env.DEBUG === 'true' ? true : false,
maxIterations: maxIterations ? parseFloat(maxIterations) : undefined
})
return executor
}
module.exports = { nodeClass: OpenAIToolAgent_Agents }

View File

@ -1,9 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 12.6108L22 15.9608" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.17701 19.5848C6.49568 20.4069 6.12505 21.4424 6.12993 22.5101C6.13481 23.5779 6.51489 24.6099 7.2037 25.4258C7.89252 26.2416 8.84622 26.7893 9.89802 26.9732C10.9498 27.157 12.0328 26.9653 12.9575 26.4314L15.4787 24.9657M18.6002 14.106V19.5848" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.19877 9.98459C6.39026 9.67775 4.57524 10.4982 3.60403 12.1806C3.00524 13.2178 2.84295 14.4504 3.15284 15.6073C3.46273 16.7642 4.21943 17.7507 5.25652 18.3498L10.3049 21.3269C10.6109 21.5074 10.9898 21.5119 11.3001 21.3388L18.6 17.2655" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.0172 6.06585C16.6456 5.06522 15.9342 4.227 15.0072 3.6977C14.0803 3.1684 12.9969 2.98168 11.9462 3.17018C10.8956 3.35869 9.94464 3.91042 9.25954 4.72895C8.57444 5.54747 8.19879 6.58074 8.19824 7.64814V13.6575C8.19824 14.0154 8.38951 14.346 8.69977 14.5244L15.9992 18.7215" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M24.8216 11.7476C25.5029 10.9255 25.8735 9.89004 25.8687 8.8223C25.8638 7.75457 25.4837 6.72253 24.7949 5.90667C24.1061 5.09082 23.1524 4.54308 22.1006 4.35924C21.0488 4.17541 19.9658 4.36718 19.0411 4.90101L13.8942 7.90613C13.5872 8.08539 13.3984 8.41418 13.3984 8.76971V17.2265" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M28.3944 19.0635C28.9932 18.0263 29.1555 16.7937 28.8456 15.6368C28.5357 14.4799 27.779 13.4934 26.7419 12.8943L21.6409 9.91752C21.3316 9.73703 20.9494 9.7357 20.6388 9.91405L13.3984 14.0723" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 28.9997H18.8071C19.6909 28.9997 20.4526 28.3921 20.6297 27.546L21.991 21.4537C22.1681 20.6076 22.9299 20 23.8136 20H24.6207M20.0929 22.7023H23.8136M24 25.0214H24.5014C24.8438 25.0214 25.1586 25.2052 25.3207 25.5L27.3429 28.5213C27.5051 28.8161 27.8198 29 28.1622 29H28.6997M24.049 29C24.6261 29 25.1609 28.7041 25.4578 28.2205L27.2424 25.8009C27.5393 25.3173 28.0741 25.0214 28.6512 25.0214" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -36,7 +36,6 @@ class ToolAgent_Agents implements INode {
this.icon = 'toolAgent.png' this.icon = 'toolAgent.png'
this.description = `Agent that uses Function Calling to pick the tools and args to call` this.description = `Agent that uses Function Calling to pick the tools and args to call`
this.baseClasses = [this.type, ...getBaseClasses(AgentExecutor)] this.baseClasses = [this.type, ...getBaseClasses(AgentExecutor)]
this.badge = 'NEW'
this.inputs = [ this.inputs = [
{ {
label: 'Tools', label: 'Tools',

View File

@ -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

View File

@ -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 }

View File

@ -110,7 +110,9 @@ class LLMChain_Chains implements INode {
}) })
const inputVariables = chain.prompt.inputVariables as string[] // ["product"] const inputVariables = chain.prompt.inputVariables as string[] // ["product"]
promptValues = injectOutputParser(this.outputParser, chain, promptValues) promptValues = injectOutputParser(this.outputParser, chain, promptValues)
const res = await runPrediction(inputVariables, chain, input, promptValues, options, nodeData) // Disable streaming because its not final chain
const disableStreaming = true
const res = await runPrediction(inputVariables, chain, input, promptValues, options, nodeData, disableStreaming)
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('\x1b[92m\x1b[1m\n*****OUTPUT PREDICTION*****\n\x1b[0m\x1b[0m') console.log('\x1b[92m\x1b[1m\n*****OUTPUT PREDICTION*****\n\x1b[0m\x1b[0m')
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -154,12 +156,13 @@ const runPrediction = async (
input: string, input: string,
promptValuesRaw: ICommonObject | undefined, promptValuesRaw: ICommonObject | undefined,
options: ICommonObject, options: ICommonObject,
nodeData: INodeData nodeData: INodeData,
disableStreaming?: boolean
) => { ) => {
const loggerHandler = new ConsoleCallbackHandler(options.logger) const loggerHandler = new ConsoleCallbackHandler(options.logger)
const callbacks = await additionalCallbacks(nodeData, options) const callbacks = await additionalCallbacks(nodeData, options)
const isStreaming = options.socketIO && options.socketIOClientId const isStreaming = !disableStreaming && options.socketIO && options.socketIOClientId
const socketIO = isStreaming ? options.socketIO : undefined const socketIO = isStreaming ? options.socketIO : undefined
const socketIOClientId = isStreaming ? options.socketIOClientId : '' const socketIOClientId = isStreaming ? options.socketIOClientId : ''
const moderations = nodeData.inputs?.inputModeration as Moderation[] const moderations = nodeData.inputs?.inputModeration as Moderation[]

View File

@ -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 }

View File

@ -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

View File

@ -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 }

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -552,6 +552,13 @@ function zodToGeminiParameters(zodObj: any) {
const jsonSchema: any = zodToJsonSchema(zodObj) const jsonSchema: any = zodToJsonSchema(zodObj)
// eslint-disable-next-line unused-imports/no-unused-vars // eslint-disable-next-line unused-imports/no-unused-vars
const { $schema, additionalProperties, ...rest } = jsonSchema 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 return rest
} }

View File

@ -18,7 +18,7 @@ class ChatHuggingFace_ChatModels implements INode {
constructor() { constructor() {
this.label = 'ChatHuggingFace' this.label = 'ChatHuggingFace'
this.name = 'chatHuggingFace' this.name = 'chatHuggingFace'
this.version = 2.0 this.version = 3.0
this.type = 'ChatHuggingFace' this.type = 'ChatHuggingFace'
this.icon = 'HuggingFace.svg' this.icon = 'HuggingFace.svg'
this.category = 'Chat Models' this.category = 'Chat Models'
@ -96,6 +96,16 @@ class ChatHuggingFace_ChatModels implements INode {
description: 'Frequency Penalty parameter may not apply to certain model. Please check available model parameters', description: 'Frequency Penalty parameter may not apply to certain model. Please check available model parameters',
optional: true, optional: true,
additionalParams: true additionalParams: true
},
{
label: 'Stop Sequence',
name: 'stop',
type: 'string',
rows: 4,
placeholder: 'AI assistant:',
description: 'Sets the stop sequences to use. Use comma to seperate different sequences.',
optional: true,
additionalParams: true
} }
] ]
} }
@ -109,6 +119,7 @@ class ChatHuggingFace_ChatModels implements INode {
const frequencyPenalty = nodeData.inputs?.frequencyPenalty as string const frequencyPenalty = nodeData.inputs?.frequencyPenalty as string
const endpoint = nodeData.inputs?.endpoint as string const endpoint = nodeData.inputs?.endpoint as string
const cache = nodeData.inputs?.cache as BaseCache const cache = nodeData.inputs?.cache as BaseCache
const stop = nodeData.inputs?.stop as string
const credentialData = await getCredentialData(nodeData.credential ?? '', options) const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const huggingFaceApiKey = getCredentialParam('huggingFaceApiKey', credentialData, nodeData) const huggingFaceApiKey = getCredentialParam('huggingFaceApiKey', credentialData, nodeData)
@ -123,7 +134,11 @@ class ChatHuggingFace_ChatModels implements INode {
if (topP) obj.topP = parseFloat(topP) if (topP) obj.topP = parseFloat(topP)
if (hfTopK) obj.topK = parseFloat(hfTopK) if (hfTopK) obj.topK = parseFloat(hfTopK)
if (frequencyPenalty) obj.frequencyPenalty = parseFloat(frequencyPenalty) if (frequencyPenalty) obj.frequencyPenalty = parseFloat(frequencyPenalty)
if (endpoint) obj.endpoint = endpoint if (endpoint) obj.endpointUrl = endpoint
if (stop) {
const stopSequences = stop.split(',')
obj.stopSequences = stopSequences
}
const huggingFace = new HuggingFaceInference(obj) const huggingFace = new HuggingFaceInference(obj)
if (cache) huggingFace.cache = cache if (cache) huggingFace.cache = cache

View File

@ -1,32 +1,19 @@
import { LLM, BaseLLMParams } from '@langchain/core/language_models/llms' import { LLM, BaseLLMParams } from '@langchain/core/language_models/llms'
import { getEnvironmentVariable } from '../../../src/utils' import { getEnvironmentVariable } from '../../../src/utils'
import { GenerationChunk } from '@langchain/core/outputs'
import { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager'
export interface HFInput { export interface HFInput {
/** Model to use */
model: string model: string
/** Sampling temperature to use */
temperature?: number temperature?: number
/**
* Maximum number of tokens to generate in the completion.
*/
maxTokens?: number maxTokens?: number
stopSequences?: string[]
/** Total probability mass of tokens to consider at each step */
topP?: number topP?: number
/** Integer to define the top tokens considered within the sample operation to create new text. */
topK?: number topK?: number
/** Penalizes repeated tokens according to frequency */
frequencyPenalty?: number frequencyPenalty?: number
/** API key to use. */
apiKey?: string apiKey?: string
endpointUrl?: string
/** Private endpoint to use. */ includeCredentials?: string | boolean
endpoint?: string
} }
export class HuggingFaceInference extends LLM implements HFInput { export class HuggingFaceInference extends LLM implements HFInput {
@ -40,6 +27,8 @@ export class HuggingFaceInference extends LLM implements HFInput {
temperature: number | undefined = undefined temperature: number | undefined = undefined
stopSequences: string[] | undefined = undefined
maxTokens: number | undefined = undefined maxTokens: number | undefined = undefined
topP: number | undefined = undefined topP: number | undefined = undefined
@ -50,7 +39,9 @@ export class HuggingFaceInference extends LLM implements HFInput {
apiKey: string | undefined = undefined apiKey: string | undefined = undefined
endpoint: string | undefined = undefined endpointUrl: string | undefined = undefined
includeCredentials: string | boolean | undefined = undefined
constructor(fields?: Partial<HFInput> & BaseLLMParams) { constructor(fields?: Partial<HFInput> & BaseLLMParams) {
super(fields ?? {}) super(fields ?? {})
@ -58,11 +49,13 @@ export class HuggingFaceInference extends LLM implements HFInput {
this.model = fields?.model ?? this.model this.model = fields?.model ?? this.model
this.temperature = fields?.temperature ?? this.temperature this.temperature = fields?.temperature ?? this.temperature
this.maxTokens = fields?.maxTokens ?? this.maxTokens this.maxTokens = fields?.maxTokens ?? this.maxTokens
this.stopSequences = fields?.stopSequences ?? this.stopSequences
this.topP = fields?.topP ?? this.topP this.topP = fields?.topP ?? this.topP
this.topK = fields?.topK ?? this.topK this.topK = fields?.topK ?? this.topK
this.frequencyPenalty = fields?.frequencyPenalty ?? this.frequencyPenalty this.frequencyPenalty = fields?.frequencyPenalty ?? this.frequencyPenalty
this.endpoint = fields?.endpoint ?? ''
this.apiKey = fields?.apiKey ?? getEnvironmentVariable('HUGGINGFACEHUB_API_KEY') this.apiKey = fields?.apiKey ?? getEnvironmentVariable('HUGGINGFACEHUB_API_KEY')
this.endpointUrl = fields?.endpointUrl
this.includeCredentials = fields?.includeCredentials
if (!this.apiKey) { if (!this.apiKey) {
throw new Error( throw new Error(
'Please set an API key for HuggingFace Hub in the environment variable HUGGINGFACEHUB_API_KEY or in the apiKey field of the HuggingFaceInference constructor.' 'Please set an API key for HuggingFace Hub in the environment variable HUGGINGFACEHUB_API_KEY or in the apiKey field of the HuggingFaceInference constructor.'
@ -74,31 +67,65 @@ export class HuggingFaceInference extends LLM implements HFInput {
return 'hf' return 'hf'
} }
/** @ignore */ invocationParams(options?: this['ParsedCallOptions']) {
async _call(prompt: string, options: this['ParsedCallOptions']): Promise<string> { return {
const { HfInference } = await HuggingFaceInference.imports() model: this.model,
const hf = new HfInference(this.apiKey)
const obj: any = {
parameters: { parameters: {
// make it behave similar to openai, returning only the generated text // make it behave similar to openai, returning only the generated text
return_full_text: false, return_full_text: false,
temperature: this.temperature, temperature: this.temperature,
max_new_tokens: this.maxTokens, max_new_tokens: this.maxTokens,
stop: options?.stop ?? this.stopSequences,
top_p: this.topP, top_p: this.topP,
top_k: this.topK, top_k: this.topK,
repetition_penalty: this.frequencyPenalty repetition_penalty: this.frequencyPenalty
}, }
inputs: prompt
} }
if (this.endpoint) { }
hf.endpoint(this.endpoint)
} else { async *_streamResponseChunks(
obj.model = this.model prompt: string,
options: this['ParsedCallOptions'],
runManager?: CallbackManagerForLLMRun
): AsyncGenerator<GenerationChunk> {
const hfi = await this._prepareHFInference()
const stream = await this.caller.call(async () =>
hfi.textGenerationStream({
...this.invocationParams(options),
inputs: prompt
})
)
for await (const chunk of stream) {
const token = chunk.token.text
yield new GenerationChunk({ text: token, generationInfo: chunk })
await runManager?.handleLLMNewToken(token ?? '')
// stream is done
if (chunk.generated_text)
yield new GenerationChunk({
text: '',
generationInfo: { finished: true }
})
} }
const res = await this.caller.callWithOptions({ signal: options.signal }, hf.textGeneration.bind(hf), obj) }
/** @ignore */
async _call(prompt: string, options: this['ParsedCallOptions']): Promise<string> {
const hfi = await this._prepareHFInference()
const args = { ...this.invocationParams(options), inputs: prompt }
const res = await this.caller.callWithOptions({ signal: options.signal }, hfi.textGeneration.bind(hfi), args)
return res.generated_text return res.generated_text
} }
/** @ignore */
private async _prepareHFInference() {
const { HfInference } = await HuggingFaceInference.imports()
const hfi = new HfInference(this.apiKey, {
includeCredentials: this.includeCredentials
})
return this.endpointUrl ? hfi.endpoint(this.endpointUrl) : hfi
}
/** @ignore */ /** @ignore */
static async imports(): Promise<{ static async imports(): Promise<{
HfInference: typeof import('@huggingface/inference').HfInference HfInference: typeof import('@huggingface/inference').HfInference

View File

@ -45,7 +45,6 @@ class ChatOllamaFunction_ChatModels implements INode {
this.category = 'Chat Models' this.category = 'Chat Models'
this.description = 'Run open-source function-calling compatible LLM on Ollama' this.description = 'Run open-source function-calling compatible LLM on Ollama'
this.baseClasses = [this.type, ...getBaseClasses(OllamaFunctions)] this.baseClasses = [this.type, ...getBaseClasses(OllamaFunctions)]
this.badge = 'NEW'
this.inputs = [ this.inputs = [
{ {
label: 'Cache', label: 'Cache',

View File

@ -131,7 +131,11 @@ class Cheerio_DocumentLoaders implements INode {
async function cheerioLoader(url: string): Promise<any> { async function cheerioLoader(url: string): Promise<any> {
try { 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) const loader = new CheerioWebBaseLoader(url, params)
if (textSplitter) { if (textSplitter) {
docs = await loader.loadAndSplit(textSplitter) docs = await loader.loadAndSplit(textSplitter)
@ -141,6 +145,7 @@ class Cheerio_DocumentLoaders implements INode {
return docs return docs
} catch (err) { } catch (err) {
if (process.env.DEBUG === 'true') options.logger.error(`error in CheerioWebBaseLoader: ${err.message}, on page: ${url}`) if (process.env.DEBUG === 'true') options.logger.error(`error in CheerioWebBaseLoader: ${err.message}, on page: ${url}`)
return []
} }
} }

View File

@ -23,7 +23,6 @@ class CustomDocumentLoader_DocumentLoaders implements INode {
this.type = 'Document' this.type = 'Document'
this.icon = 'customDocLoader.svg' this.icon = 'customDocLoader.svg'
this.category = 'Document Loaders' this.category = 'Document Loaders'
this.badge = 'NEW'
this.description = `Custom function for loading documents` this.description = `Custom function for loading documents`
this.baseClasses = [this.type] this.baseClasses = [this.type]
this.inputs = [ this.inputs = [

View File

@ -22,7 +22,6 @@ class DocStore_DocumentLoaders implements INode {
this.version = 1.0 this.version = 1.0
this.type = 'Document' this.type = 'Document'
this.icon = 'dstore.svg' this.icon = 'dstore.svg'
this.badge = 'NEW'
this.category = 'Document Loaders' this.category = 'Document Loaders'
this.description = `Load data from pre-configured document stores` this.description = `Load data from pre-configured document stores`
this.baseClasses = [this.type] this.baseClasses = [this.type]

View File

@ -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 }

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -448,7 +448,16 @@ class UnstructuredFile_DocumentLoaders implements INode {
if (_omitMetadataKeys) { if (_omitMetadataKeys) {
omitMetadataKeys = _omitMetadataKeys.split(',').map((key) => key.trim()) 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 = { const obj: UnstructuredLoaderOptions = {
apiUrl: unstructuredAPIUrl, apiUrl: unstructuredAPIUrl,

View File

@ -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 }

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -273,10 +273,8 @@ class Supervisor_MultiAgents implements INode {
* So we have to place the system + human prompt at last * So we have to place the system + human prompt at last
*/ */
let prompt = ChatPromptTemplate.fromMessages([ let prompt = ChatPromptTemplate.fromMessages([
['human', systemPrompt], ['system', systemPrompt],
['ai', ''],
new MessagesPlaceholder('messages'), new MessagesPlaceholder('messages'),
['ai', ''],
['human', userPrompt] ['human', userPrompt]
]) ])

View File

@ -199,19 +199,33 @@ async function createAgent(
} }
const modelWithTools = llm.bindTools(tools) const modelWithTools = llm.bindTools(tools)
const agent = RunnableSequence.from([ let agent
RunnablePassthrough.assign({
//@ts-ignore if (!workerInputVariablesValues || !Object.keys(workerInputVariablesValues).length) {
agent_scratchpad: (input: { steps: ToolsAgentStep[] }) => formatToOpenAIToolMessages(input.steps) agent = RunnableSequence.from([
}), RunnablePassthrough.assign({
RunnablePassthrough.assign(transformObjectPropertyToFunction(workerInputVariablesValues)), //@ts-ignore
prompt, agent_scratchpad: (input: { steps: ToolsAgentStep[] }) => formatToOpenAIToolMessages(input.steps)
modelWithTools, }),
new ToolCallingAgentOutputParser() 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({ const executor = AgentExecutor.fromAgentAndTools({
agent: agent, agent,
tools, tools,
sessionId: flowObj?.sessionId, sessionId: flowObj?.sessionId,
chatId: flowObj?.chatId, chatId: flowObj?.chatId,
@ -233,12 +247,19 @@ async function createAgent(
const msg = HumanMessagePromptTemplate.fromTemplate([...multiModalMessageContent]) const msg = HumanMessagePromptTemplate.fromTemplate([...multiModalMessageContent])
prompt.promptMessages.splice(1, 0, msg) prompt.promptMessages.splice(1, 0, msg)
} }
const conversationChain = RunnableSequence.from([
RunnablePassthrough.assign(transformObjectPropertyToFunction(workerInputVariablesValues)), let conversationChain
prompt,
llm, if (!workerInputVariablesValues || !Object.keys(workerInputVariablesValues).length) {
new StringOutputParser() conversationChain = RunnableSequence.from([prompt, llm, new StringOutputParser()])
]) } else {
conversationChain = RunnableSequence.from([
RunnablePassthrough.assign(transformObjectPropertyToFunction(workerInputVariablesValues)),
prompt,
llm,
new StringOutputParser()
])
}
return conversationChain return conversationChain
} }
} }
@ -256,6 +277,7 @@ async function agentNode(
if (abortControllerSignal.signal.aborted) { if (abortControllerSignal.signal.aborted) {
throw new Error('Aborted!') throw new Error('Aborted!')
} }
const result = await agent.invoke({ ...state, signal: abortControllerSignal.signal }, config) const result = await agent.invoke({ ...state, signal: abortControllerSignal.signal }, config)
const additional_kwargs: ICommonObject = {} const additional_kwargs: ICommonObject = {}
if (result.usedTools) { if (result.usedTools) {

View File

@ -25,7 +25,6 @@ class MySQLRecordManager_RecordManager implements INode {
this.category = 'Record Manager' this.category = 'Record Manager'
this.description = 'Use MySQL to keep track of document writes into the vector databases' this.description = 'Use MySQL to keep track of document writes into the vector databases'
this.baseClasses = [this.type, 'RecordManager', ...getBaseClasses(MySQLRecordManager)] this.baseClasses = [this.type, 'RecordManager', ...getBaseClasses(MySQLRecordManager)]
this.badge = 'NEW'
this.inputs = [ this.inputs = [
{ {
label: 'Host', label: 'Host',

View File

@ -25,7 +25,6 @@ class PostgresRecordManager_RecordManager implements INode {
this.category = 'Record Manager' this.category = 'Record Manager'
this.description = 'Use Postgres to keep track of document writes into the vector databases' this.description = 'Use Postgres to keep track of document writes into the vector databases'
this.baseClasses = [this.type, 'RecordManager', ...getBaseClasses(PostgresRecordManager)] this.baseClasses = [this.type, 'RecordManager', ...getBaseClasses(PostgresRecordManager)]
this.badge = 'NEW'
this.inputs = [ this.inputs = [
{ {
label: 'Host', label: 'Host',

View File

@ -25,7 +25,6 @@ class SQLiteRecordManager_RecordManager implements INode {
this.category = 'Record Manager' this.category = 'Record Manager'
this.description = 'Use SQLite to keep track of document writes into the vector databases' this.description = 'Use SQLite to keep track of document writes into the vector databases'
this.baseClasses = [this.type, 'RecordManager', ...getBaseClasses(SQLiteRecordManager)] this.baseClasses = [this.type, 'RecordManager', ...getBaseClasses(SQLiteRecordManager)]
this.badge = 'NEW'
this.inputs = [ this.inputs = [
{ {
label: 'Database File Path', label: 'Database File Path',

View File

@ -26,7 +26,6 @@ class CohereRerankRetriever_Retrievers implements INode {
this.type = 'Cohere Rerank Retriever' this.type = 'Cohere Rerank Retriever'
this.icon = 'Cohere.svg' this.icon = 'Cohere.svg'
this.category = 'Retrievers' this.category = 'Retrievers'
this.badge = 'NEW'
this.description = 'Cohere Rerank indexes the documents from most to least semantically relevant to the query.' this.description = 'Cohere Rerank indexes the documents from most to least semantically relevant to the query.'
this.baseClasses = [this.type, 'BaseRetriever'] this.baseClasses = [this.type, 'BaseRetriever']
this.credential = { this.credential = {

View File

@ -25,7 +25,6 @@ class EmbeddingsFilterRetriever_Retrievers implements INode {
this.type = 'EmbeddingsFilterRetriever' this.type = 'EmbeddingsFilterRetriever'
this.icon = 'compressionRetriever.svg' this.icon = 'compressionRetriever.svg'
this.category = 'Retrievers' this.category = 'Retrievers'
this.badge = 'NEW'
this.description = 'A document compressor that uses embeddings to drop documents unrelated to the query' this.description = 'A document compressor that uses embeddings to drop documents unrelated to the query'
this.baseClasses = [this.type, 'BaseRetriever'] this.baseClasses = [this.type, 'BaseRetriever']
this.inputs = [ this.inputs = [

View File

@ -25,7 +25,6 @@ class LLMFilterCompressionRetriever_Retrievers implements INode {
this.type = 'LLMFilterRetriever' this.type = 'LLMFilterRetriever'
this.icon = 'llmFilterRetriever.svg' this.icon = 'llmFilterRetriever.svg'
this.category = 'Retrievers' this.category = 'Retrievers'
this.badge = 'NEW'
this.description = this.description =
'Iterate over the initially returned documents and extract, from each, only the content that is relevant to the query' 'Iterate over the initially returned documents and extract, from each, only the content that is relevant to the query'
this.baseClasses = [this.type, 'BaseRetriever'] this.baseClasses = [this.type, 'BaseRetriever']

View File

@ -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 }

View File

@ -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

View File

@ -24,7 +24,6 @@ class RRFRetriever_Retrievers implements INode {
this.name = 'RRFRetriever' this.name = 'RRFRetriever'
this.version = 1.0 this.version = 1.0
this.type = 'RRFRetriever' this.type = 'RRFRetriever'
this.badge = 'NEW'
this.icon = 'rrfRetriever.svg' this.icon = 'rrfRetriever.svg'
this.category = 'Retrievers' this.category = 'Retrievers'
this.description = 'Reciprocal Rank Fusion to re-rank search results by multiple query generation.' this.description = 'Reciprocal Rank Fusion to re-rank search results by multiple query generation.'

View File

@ -26,7 +26,6 @@ class VoyageAIRerankRetriever_Retrievers implements INode {
this.type = 'VoyageAIRerankRetriever' this.type = 'VoyageAIRerankRetriever'
this.icon = 'voyageai.png' this.icon = 'voyageai.png'
this.category = 'Retrievers' this.category = 'Retrievers'
this.badge = 'NEW'
this.description = 'Voyage AI Rerank indexes the documents from most to least semantically relevant to the query.' this.description = 'Voyage AI Rerank indexes the documents from most to least semantically relevant to the query.'
this.baseClasses = [this.type, 'BaseRetriever'] this.baseClasses = [this.type, 'BaseRetriever']
this.credential = { this.credential = {
@ -49,6 +48,10 @@ class VoyageAIRerankRetriever_Retrievers implements INode {
{ {
label: 'rerank-lite-1', label: 'rerank-lite-1',
name: 'rerank-lite-1' name: 'rerank-lite-1'
},
{
label: 'rerank-1',
name: 'rerank-1'
} }
], ],
default: 'rerank-lite-1', default: 'rerank-lite-1',

View File

@ -6,6 +6,7 @@ import { CallbackManagerForToolRun, Callbacks, CallbackManager, parseCallbackCon
import { StructuredTool } from '@langchain/core/tools' import { StructuredTool } from '@langchain/core/tools'
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface' import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface'
import { availableDependencies, defaultAllowBuiltInDep, getCredentialData, getCredentialParam } from '../../../src/utils' import { availableDependencies, defaultAllowBuiltInDep, getCredentialData, getCredentialParam } from '../../../src/utils'
import { v4 as uuidv4 } from 'uuid'
class ChatflowTool_Tools implements INode { class ChatflowTool_Tools implements INode {
label: string label: string
@ -22,7 +23,7 @@ class ChatflowTool_Tools implements INode {
constructor() { constructor() {
this.label = 'Chatflow Tool' this.label = 'Chatflow Tool'
this.name = 'ChatflowTool' this.name = 'ChatflowTool'
this.version = 1.0 this.version = 3.0
this.type = 'ChatflowTool' this.type = 'ChatflowTool'
this.icon = 'chatflowTool.svg' this.icon = 'chatflowTool.svg'
this.category = 'Tools' this.category = 'Tools'
@ -56,6 +57,26 @@ class ChatflowTool_Tools implements INode {
placeholder: placeholder:
'State of the Union QA - useful for when you need to ask questions about the most recent state of the union address.' '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', label: 'Use Question from Chat',
name: 'useQuestionFromChat', name: 'useQuestionFromChat',
@ -107,7 +128,9 @@ class ChatflowTool_Tools implements INode {
const useQuestionFromChat = nodeData.inputs?.useQuestionFromChat as boolean const useQuestionFromChat = nodeData.inputs?.useQuestionFromChat as boolean
const customInput = nodeData.inputs?.customInput as string 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 credentialData = await getCredentialData(nodeData.credential ?? '', options)
const chatflowApiKey = getCredentialParam('chatflowApiKey', credentialData, nodeData) const chatflowApiKey = getCredentialParam('chatflowApiKey', credentialData, nodeData)
@ -126,7 +149,7 @@ class ChatflowTool_Tools implements INode {
let name = _name || 'chatflow_tool' 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 = '' chatflowid = ''
startNewSession = false
baseURL = 'http://localhost:3000' baseURL = 'http://localhost:3000'
headers = {} headers = {}
@ -156,6 +181,7 @@ class ChatflowTool extends StructuredTool {
description, description,
input, input,
chatflowid, chatflowid,
startNewSession,
baseURL, baseURL,
headers headers
}: { }: {
@ -163,6 +189,7 @@ class ChatflowTool extends StructuredTool {
description: string description: string
input: string input: string
chatflowid: string chatflowid: string
startNewSession: boolean
baseURL: string baseURL: string
headers: ICommonObject headers: ICommonObject
}) { }) {
@ -171,6 +198,7 @@ class ChatflowTool extends StructuredTool {
this.description = description this.description = description
this.input = input this.input = input
this.baseURL = baseURL this.baseURL = baseURL
this.startNewSession = startNewSession
this.headers = headers this.headers = headers
this.chatflowid = chatflowid this.chatflowid = chatflowid
} }
@ -230,9 +258,9 @@ class ChatflowTool extends StructuredTool {
const body = { const body = {
question: inputQuestion, question: inputQuestion,
chatId: flowConfig?.chatId, chatId: this.startNewSession ? uuidv4() : flowConfig?.chatId,
overrideConfig: { overrideConfig: {
sessionId: flowConfig?.sessionId sessionId: this.startNewSession ? uuidv4() : flowConfig?.sessionId
} }
} }

View File

@ -96,6 +96,9 @@ export class DynamicStructuredTool<
await runManager?.handleToolError(e) await runManager?.handleToolError(e)
throw e throw e
} }
if (result && typeof result !== 'string') {
result = JSON.stringify(result)
}
await runManager?.handleToolEnd(result) await runManager?.handleToolEnd(result)
return result return result
} }

View File

@ -36,7 +36,6 @@ class E2B_Tools implements INode {
this.type = 'E2B' this.type = 'E2B'
this.icon = 'e2b.png' this.icon = 'e2b.png'
this.category = 'Tools' this.category = 'Tools'
this.badge = 'NEW'
this.description = 'Execute code in E2B Code Intepreter' this.description = 'Execute code in E2B Code Intepreter'
this.baseClasses = [this.type, 'Tool', ...getBaseClasses(E2BTool)] this.baseClasses = [this.type, 'Tool', ...getBaseClasses(E2BTool)]
this.credential = { this.credential = {

View File

@ -36,7 +36,6 @@ class PythonInterpreter_Tools implements INode {
this.type = 'PythonInterpreter' this.type = 'PythonInterpreter'
this.icon = 'python.svg' this.icon = 'python.svg'
this.category = 'Tools' this.category = 'Tools'
this.badge = 'NEW'
this.description = 'Execute python code in Pyodide sandbox environment' this.description = 'Execute python code in Pyodide sandbox environment'
this.baseClasses = [this.type, 'Tool', ...getBaseClasses(PythonInterpreterTool)] this.baseClasses = [this.type, 'Tool', ...getBaseClasses(PythonInterpreterTool)]
this.inputs = [ this.inputs = [

View File

@ -76,7 +76,7 @@ class Retriever_Tools implements INode {
} }
const schema = z.object({ 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 }) const tool = new DynamicStructuredTool({ ...input, func, schema })

View File

@ -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

View File

@ -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 }

View File

@ -11,6 +11,7 @@ class CustomFunction_Utilities implements INode {
type: string type: string
icon: string icon: string
category: string category: string
tags: string[]
baseClasses: string[] baseClasses: string[]
inputs: INodeParams[] inputs: INodeParams[]
outputs: INodeOutputsValue[] outputs: INodeOutputsValue[]
@ -18,12 +19,13 @@ class CustomFunction_Utilities implements INode {
constructor() { constructor() {
this.label = 'Custom JS Function' this.label = 'Custom JS Function'
this.name = 'customFunction' this.name = 'customFunction'
this.version = 1.0 this.version = 2.0
this.type = 'CustomFunction' this.type = 'CustomFunction'
this.icon = 'customfunction.svg' this.icon = 'customfunction.svg'
this.category = 'Utilities' this.category = 'Utilities'
this.description = `Execute custom javascript function` this.description = `Execute custom javascript function`
this.baseClasses = [this.type, 'Utilities'] this.baseClasses = [this.type, 'Utilities']
this.tags = ['Utilities']
this.inputs = [ this.inputs = [
{ {
label: 'Input Variables', label: 'Input Variables',

View File

@ -8,6 +8,7 @@ class GetVariable_Utilities implements INode {
type: string type: string
icon: string icon: string
category: string category: string
tags: string[]
baseClasses: string[] baseClasses: string[]
inputs: INodeParams[] inputs: INodeParams[]
outputs: INodeOutputsValue[] outputs: INodeOutputsValue[]
@ -15,12 +16,13 @@ class GetVariable_Utilities implements INode {
constructor() { constructor() {
this.label = 'Get Variable' this.label = 'Get Variable'
this.name = 'getVariable' this.name = 'getVariable'
this.version = 1.0 this.version = 2.0
this.type = 'GetVariable' this.type = 'GetVariable'
this.icon = 'getvar.svg' this.icon = 'getvar.svg'
this.category = 'Utilities' this.category = 'Utilities'
this.description = `Get variable that was saved using Set Variable node` this.description = `Get variable that was saved using Set Variable node`
this.baseClasses = [this.type, 'Utilities'] this.baseClasses = [this.type, 'Utilities']
this.tags = ['Utilities']
this.inputs = [ this.inputs = [
{ {
label: 'Variable Name', label: 'Variable Name',

View File

@ -11,6 +11,7 @@ class IfElseFunction_Utilities implements INode {
type: string type: string
icon: string icon: string
category: string category: string
tags: string[]
baseClasses: string[] baseClasses: string[]
inputs: INodeParams[] inputs: INodeParams[]
outputs: INodeOutputsValue[] outputs: INodeOutputsValue[]
@ -18,12 +19,13 @@ class IfElseFunction_Utilities implements INode {
constructor() { constructor() {
this.label = 'IfElse Function' this.label = 'IfElse Function'
this.name = 'ifElseFunction' this.name = 'ifElseFunction'
this.version = 1.0 this.version = 2.0
this.type = 'IfElseFunction' this.type = 'IfElseFunction'
this.icon = 'ifelsefunction.svg' this.icon = 'ifelsefunction.svg'
this.category = 'Utilities' this.category = 'Utilities'
this.description = `Split flows based on If Else javascript functions` this.description = `Split flows based on If Else javascript functions`
this.baseClasses = [this.type, 'Utilities'] this.baseClasses = [this.type, 'Utilities']
this.tags = ['Utilities']
this.inputs = [ this.inputs = [
{ {
label: 'Input Variables', label: 'Input Variables',

View File

@ -8,6 +8,7 @@ class SetVariable_Utilities implements INode {
type: string type: string
icon: string icon: string
category: string category: string
tags: string[]
baseClasses: string[] baseClasses: string[]
inputs: INodeParams[] inputs: INodeParams[]
outputs: INodeOutputsValue[] outputs: INodeOutputsValue[]
@ -15,11 +16,12 @@ class SetVariable_Utilities implements INode {
constructor() { constructor() {
this.label = 'Set Variable' this.label = 'Set Variable'
this.name = 'setVariable' this.name = 'setVariable'
this.version = 1.0 this.version = 2.0
this.type = 'SetVariable' this.type = 'SetVariable'
this.icon = 'setvar.svg' this.icon = 'setvar.svg'
this.category = 'Utilities' this.category = 'Utilities'
this.description = `Set variable which can be retrieved at a later stage. Variable is only available during runtime.` this.description = `Set variable which can be retrieved at a later stage. Variable is only available during runtime.`
this.tags = ['Utilities']
this.baseClasses = [this.type, 'Utilities'] this.baseClasses = [this.type, 'Utilities']
this.inputs = [ this.inputs = [
{ {

View File

@ -8,16 +8,18 @@ class StickyNote implements INode {
type: string type: string
icon: string icon: string
category: string category: string
tags: string[]
baseClasses: string[] baseClasses: string[]
inputs: INodeParams[] inputs: INodeParams[]
constructor() { constructor() {
this.label = 'Sticky Note' this.label = 'Sticky Note'
this.name = 'stickyNote' this.name = 'stickyNote'
this.version = 1.0 this.version = 2.0
this.type = 'StickyNote' this.type = 'StickyNote'
this.icon = 'stickyNote.svg' this.icon = 'stickyNote.svg'
this.category = 'Utilities' this.category = 'Utilities'
this.tags = ['Utilities']
this.description = 'Add a sticky note' this.description = 'Add a sticky note'
this.inputs = [ this.inputs = [
{ {

View File

@ -30,7 +30,6 @@ class Chroma_VectorStores implements INode {
this.category = 'Vector Stores' this.category = 'Vector Stores'
this.description = 'Upsert embedded data and perform similarity search upon query using Chroma, an open-source embedding database' this.description = 'Upsert embedded data and perform similarity search upon query using Chroma, an open-source embedding database'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever'] this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'NEW'
this.credential = { this.credential = {
label: 'Connect Credential', label: 'Connect Credential',
name: 'credential', name: 'credential',

View File

@ -1,129 +0,0 @@
import { Chroma } from '@langchain/community/vectorstores/chroma'
import { Embeddings } from '@langchain/core/embeddings'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
import { ChromaExtended } from './core'
class Chroma_Existing_VectorStores implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
badge: string
baseClasses: string[]
inputs: INodeParams[]
credential: INodeParams
outputs: INodeOutputsValue[]
constructor() {
this.label = 'Chroma Load Existing Index'
this.name = 'chromaExistingIndex'
this.version = 1.0
this.type = 'Chroma'
this.icon = 'chroma.svg'
this.category = 'Vector Stores'
this.description = 'Load existing index from Chroma (i.e: Document has been upserted)'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'DEPRECATING'
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
description: 'Only needed if you have chroma on cloud services with X-Api-key',
optional: true,
credentialNames: ['chromaApi']
}
this.inputs = [
{
label: 'Embeddings',
name: 'embeddings',
type: 'Embeddings'
},
{
label: 'Collection Name',
name: 'collectionName',
type: 'string'
},
{
label: 'Chroma URL',
name: 'chromaURL',
type: 'string',
optional: true
},
{
label: 'Chroma Metadata Filter',
name: 'chromaMetadataFilter',
type: 'json',
optional: true,
additionalParams: true
},
{
label: 'Top K',
name: 'topK',
description: 'Number of top results to fetch. Default to 4',
placeholder: '4',
type: 'number',
additionalParams: true,
optional: true
}
]
this.outputs = [
{
label: 'Chroma Retriever',
name: 'retriever',
baseClasses: this.baseClasses
},
{
label: 'Chroma Vector Store',
name: 'vectorStore',
baseClasses: [this.type, ...getBaseClasses(Chroma)]
}
]
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const collectionName = nodeData.inputs?.collectionName as string
const embeddings = nodeData.inputs?.embeddings as Embeddings
const chromaURL = nodeData.inputs?.chromaURL as string
const output = nodeData.outputs?.output as string
const topK = nodeData.inputs?.topK as string
const k = topK ? parseFloat(topK) : 4
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const chromaApiKey = getCredentialParam('chromaApiKey', credentialData, nodeData)
const chromaMetadataFilter = nodeData.inputs?.chromaMetadataFilter
const obj: {
collectionName: string
url?: string
chromaApiKey?: string
filter?: object | undefined
} = { collectionName }
if (chromaURL) obj.url = chromaURL
if (chromaApiKey) obj.chromaApiKey = chromaApiKey
if (chromaMetadataFilter) {
const metadatafilter = typeof chromaMetadataFilter === 'object' ? chromaMetadataFilter : JSON.parse(chromaMetadataFilter)
obj.filter = metadatafilter
}
const vectorStore = await ChromaExtended.fromExistingCollection(embeddings, obj)
if (output === 'retriever') {
const retriever = vectorStore.asRetriever(k)
return retriever
} else if (output === 'vectorStore') {
;(vectorStore as any).k = k
if (chromaMetadataFilter) {
;(vectorStore as any).filter = obj.filter
}
return vectorStore
}
return vectorStore
}
}
module.exports = { nodeClass: Chroma_Existing_VectorStores }

View File

@ -1,129 +0,0 @@
import { flatten } from 'lodash'
import { Chroma } from '@langchain/community/vectorstores/chroma'
import { Embeddings } from '@langchain/core/embeddings'
import { Document } from '@langchain/core/documents'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
import { ChromaExtended } from './core'
class ChromaUpsert_VectorStores implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
badge: string
baseClasses: string[]
inputs: INodeParams[]
credential: INodeParams
outputs: INodeOutputsValue[]
constructor() {
this.label = 'Chroma Upsert Document'
this.name = 'chromaUpsert'
this.version = 1.0
this.type = 'Chroma'
this.icon = 'chroma.svg'
this.category = 'Vector Stores'
this.description = 'Upsert documents to Chroma'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'DEPRECATING'
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
description: 'Only needed if you have chroma on cloud services with X-Api-key',
optional: true,
credentialNames: ['chromaApi']
}
this.inputs = [
{
label: 'Document',
name: 'document',
type: 'Document',
list: true
},
{
label: 'Embeddings',
name: 'embeddings',
type: 'Embeddings'
},
{
label: 'Collection Name',
name: 'collectionName',
type: 'string'
},
{
label: 'Chroma URL',
name: 'chromaURL',
type: 'string',
optional: true
},
{
label: 'Top K',
name: 'topK',
description: 'Number of top results to fetch. Default to 4',
placeholder: '4',
type: 'number',
additionalParams: true,
optional: true
}
]
this.outputs = [
{
label: 'Chroma Retriever',
name: 'retriever',
baseClasses: this.baseClasses
},
{
label: 'Chroma Vector Store',
name: 'vectorStore',
baseClasses: [this.type, ...getBaseClasses(Chroma)]
}
]
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const collectionName = nodeData.inputs?.collectionName as string
const docs = nodeData.inputs?.document as Document[]
const embeddings = nodeData.inputs?.embeddings as Embeddings
const chromaURL = nodeData.inputs?.chromaURL as string
const output = nodeData.outputs?.output as string
const topK = nodeData.inputs?.topK as string
const k = topK ? parseFloat(topK) : 4
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const chromaApiKey = getCredentialParam('chromaApiKey', credentialData, nodeData)
const flattenDocs = docs && docs.length ? flatten(docs) : []
const finalDocs = []
for (let i = 0; i < flattenDocs.length; i += 1) {
if (flattenDocs[i] && flattenDocs[i].pageContent) {
finalDocs.push(new Document(flattenDocs[i]))
}
}
const obj: {
collectionName: string
url?: string
chromaApiKey?: string
} = { collectionName }
if (chromaURL) obj.url = chromaURL
if (chromaApiKey) obj.chromaApiKey = chromaApiKey
const vectorStore = await ChromaExtended.fromDocuments(finalDocs, embeddings, obj)
if (output === 'retriever') {
const retriever = vectorStore.asRetriever(k)
return retriever
} else if (output === 'vectorStore') {
;(vectorStore as any).k = k
return vectorStore
}
return vectorStore
}
}
module.exports = { nodeClass: ChromaUpsert_VectorStores }

View File

@ -1,208 +0,0 @@
import {
getBaseClasses,
getCredentialData,
getCredentialParam,
ICommonObject,
INodeData,
INodeOutputsValue,
INodeParams
} from '../../../src'
import { Client, ClientOptions } from '@elastic/elasticsearch'
import { ElasticClientArgs, ElasticVectorSearch } from '@langchain/community/vectorstores/elasticsearch'
import { Embeddings } from '@langchain/core/embeddings'
import { VectorStore } from '@langchain/core/vectorstores'
import { Document } from '@langchain/core/documents'
export abstract class ElasticSearchBase {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
badge: string
baseClasses: string[]
inputs: INodeParams[]
credential: INodeParams
outputs: INodeOutputsValue[]
protected constructor() {
this.type = 'Elasticsearch'
this.icon = 'elasticsearch.png'
this.category = 'Vector Stores'
this.badge = 'DEPRECATING'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['elasticsearchApi', 'elasticSearchUserPassword']
}
this.inputs = [
{
label: 'Embeddings',
name: 'embeddings',
type: 'Embeddings'
},
{
label: 'Index Name',
name: 'indexName',
placeholder: '<INDEX_NAME>',
type: 'string'
},
{
label: 'Top K',
name: 'topK',
description: 'Number of top results to fetch. Default to 4',
placeholder: '4',
type: 'number',
additionalParams: true,
optional: true
},
{
label: 'Similarity',
name: 'similarity',
description: 'Similarity measure used in Elasticsearch.',
type: 'options',
default: 'l2_norm',
options: [
{
label: 'l2_norm',
name: 'l2_norm'
},
{
label: 'dot_product',
name: 'dot_product'
},
{
label: 'cosine',
name: 'cosine'
}
],
additionalParams: true,
optional: true
}
]
this.outputs = [
{
label: 'Elasticsearch Retriever',
name: 'retriever',
baseClasses: this.baseClasses
},
{
label: 'Elasticsearch Vector Store',
name: 'vectorStore',
baseClasses: [this.type, ...getBaseClasses(ElasticVectorSearch)]
}
]
}
abstract constructVectorStore(
embeddings: Embeddings,
elasticSearchClientArgs: ElasticClientArgs,
docs: Document<Record<string, any>>[] | undefined
): Promise<VectorStore>
async init(nodeData: INodeData, _: string, options: ICommonObject, docs: Document<Record<string, any>>[] | undefined): Promise<any> {
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const endPoint = getCredentialParam('endpoint', credentialData, nodeData)
const cloudId = getCredentialParam('cloudId', credentialData, nodeData)
const indexName = nodeData.inputs?.indexName as string
const embeddings = nodeData.inputs?.embeddings as Embeddings
const topK = nodeData.inputs?.topK as string
const similarityMeasure = nodeData.inputs?.similarityMeasure as string
const k = topK ? parseFloat(topK) : 4
const output = nodeData.outputs?.output as string
const elasticSearchClientArgs = this.prepareClientArgs(endPoint, cloudId, credentialData, nodeData, similarityMeasure, indexName)
const vectorStore = await this.constructVectorStore(embeddings, elasticSearchClientArgs, docs)
if (output === 'retriever') {
return vectorStore.asRetriever(k)
} else if (output === 'vectorStore') {
;(vectorStore as any).k = k
return vectorStore
}
return vectorStore
}
protected prepareConnectionOptions(
endPoint: string | undefined,
cloudId: string | undefined,
credentialData: ICommonObject,
nodeData: INodeData
) {
let elasticSearchClientOptions: ClientOptions = {}
if (endPoint) {
let apiKey = getCredentialParam('apiKey', credentialData, nodeData)
elasticSearchClientOptions = {
node: endPoint,
auth: {
apiKey: apiKey
}
}
} else if (cloudId) {
let username = getCredentialParam('username', credentialData, nodeData)
let password = getCredentialParam('password', credentialData, nodeData)
if (cloudId.startsWith('http')) {
elasticSearchClientOptions = {
node: cloudId,
auth: {
username: username,
password: password
},
tls: {
rejectUnauthorized: false
}
}
} else {
elasticSearchClientOptions = {
cloud: {
id: cloudId
},
auth: {
username: username,
password: password
}
}
}
}
return elasticSearchClientOptions
}
protected prepareClientArgs(
endPoint: string | undefined,
cloudId: string | undefined,
credentialData: ICommonObject,
nodeData: INodeData,
similarityMeasure: string,
indexName: string
) {
let elasticSearchClientOptions = this.prepareConnectionOptions(endPoint, cloudId, credentialData, nodeData)
let vectorSearchOptions = {}
switch (similarityMeasure) {
case 'dot_product':
vectorSearchOptions = {
similarity: 'dot_product'
}
break
case 'cosine':
vectorSearchOptions = {
similarity: 'cosine'
}
break
default:
vectorSearchOptions = {
similarity: 'l2_norm'
}
}
const elasticSearchClientArgs: ElasticClientArgs = {
client: new Client(elasticSearchClientOptions),
indexName: indexName,
vectorSearchOptions: vectorSearchOptions
}
return elasticSearchClientArgs
}
}

View File

@ -31,7 +31,6 @@ class Elasticsearch_VectorStores implements INode {
this.icon = 'elasticsearch.png' this.icon = 'elasticsearch.png'
this.category = 'Vector Stores' this.category = 'Vector Stores'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever'] this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'NEW'
this.credential = { this.credential = {
label: 'Connect Credential', label: 'Connect Credential',
name: 'credential', name: 'credential',

View File

@ -1,30 +0,0 @@
import { Embeddings } from '@langchain/core/embeddings'
import { ElasticClientArgs, ElasticVectorSearch } from '@langchain/community/vectorstores/elasticsearch'
import { VectorStore } from '@langchain/core/vectorstores'
import { Document } from '@langchain/core/documents'
import { ElasticSearchBase } from './ElasticSearchBase'
import { ICommonObject, INode, INodeData } from '../../../src/Interface'
class ElasicsearchExisting_VectorStores extends ElasticSearchBase implements INode {
constructor() {
super()
this.label = 'Elasticsearch Load Existing Index'
this.name = 'ElasticsearchIndex'
this.version = 1.0
this.description = 'Load existing index from Elasticsearch (i.e: Document has been upserted)'
}
async constructVectorStore(
embeddings: Embeddings,
elasticSearchClientArgs: ElasticClientArgs,
_: Document<Record<string, any>>[] | undefined
): Promise<VectorStore> {
return await ElasticVectorSearch.fromExistingIndex(embeddings, elasticSearchClientArgs)
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
return super.init(nodeData, _, options, undefined)
}
}
module.exports = { nodeClass: ElasicsearchExisting_VectorStores }

View File

@ -1,56 +0,0 @@
import { flatten } from 'lodash'
import { Embeddings } from '@langchain/core/embeddings'
import { Document } from '@langchain/core/documents'
import { VectorStore } from '@langchain/core/vectorstores'
import { ElasticClientArgs, ElasticVectorSearch } from '@langchain/community/vectorstores/elasticsearch'
import { ICommonObject, INode, INodeData } from '../../../src/Interface'
import { ElasticSearchBase } from './ElasticSearchBase'
class ElasicsearchUpsert_VectorStores extends ElasticSearchBase implements INode {
constructor() {
super()
this.label = 'Elasticsearch Upsert Document'
this.name = 'ElasticsearchUpsert'
this.version = 1.0
this.description = 'Upsert documents to Elasticsearch'
this.inputs.unshift({
label: 'Document',
name: 'document',
type: 'Document',
list: true
})
}
async constructVectorStore(
embeddings: Embeddings,
elasticSearchClientArgs: ElasticClientArgs,
docs: Document<Record<string, any>>[]
): Promise<VectorStore> {
const vectorStore = new ElasticVectorSearch(embeddings, elasticSearchClientArgs)
await vectorStore.addDocuments(docs)
return vectorStore
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const docs = nodeData.inputs?.document as Document[]
const flattenDocs = docs && docs.length ? flatten(docs) : []
const finalDocs = []
for (let i = 0; i < flattenDocs.length; i += 1) {
if (flattenDocs[i] && flattenDocs[i].pageContent) {
finalDocs.push(new Document(flattenDocs[i]))
}
}
// The following code is a workaround for a bug (Langchain Issue #1589) in the underlying library.
// Store does not support object in metadata and fail silently
finalDocs.forEach((d) => {
delete d.metadata.pdf
delete d.metadata.loc
})
// end of workaround
return super.init(nodeData, _, options, finalDocs)
}
}
module.exports = { nodeClass: ElasicsearchUpsert_VectorStores }

View File

@ -27,7 +27,6 @@ class Faiss_VectorStores implements INode {
this.category = 'Vector Stores' this.category = 'Vector Stores'
this.description = 'Upsert embedded data and perform similarity search upon query using Faiss library from Meta' this.description = 'Upsert embedded data and perform similarity search upon query using Faiss library from Meta'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever'] this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'NEW'
this.inputs = [ this.inputs = [
{ {
label: 'Document', label: 'Document',

View File

@ -1,104 +0,0 @@
import { FaissStore } from '@langchain/community/vectorstores/faiss'
import { Embeddings } from '@langchain/core/embeddings'
import { Document } from '@langchain/core/documents'
import { INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
import { getBaseClasses } from '../../../src/utils'
class Faiss_Existing_VectorStores implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
badge: string
baseClasses: string[]
inputs: INodeParams[]
outputs: INodeOutputsValue[]
constructor() {
this.label = 'Faiss Load Existing Index'
this.name = 'faissExistingIndex'
this.version = 1.0
this.type = 'Faiss'
this.icon = 'faiss.svg'
this.category = 'Vector Stores'
this.description = 'Load existing index from Faiss (i.e: Document has been upserted)'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'DEPRECATING'
this.inputs = [
{
label: 'Embeddings',
name: 'embeddings',
type: 'Embeddings'
},
{
label: 'Base Path to load',
name: 'basePath',
description: 'Path to load faiss.index file',
placeholder: `C:\\Users\\User\\Desktop`,
type: 'string'
},
{
label: 'Top K',
name: 'topK',
description: 'Number of top results to fetch. Default to 4',
placeholder: '4',
type: 'number',
additionalParams: true,
optional: true
}
]
this.outputs = [
{
label: 'Faiss Retriever',
name: 'retriever',
baseClasses: this.baseClasses
},
{
label: 'Faiss Vector Store',
name: 'vectorStore',
baseClasses: [this.type, ...getBaseClasses(FaissStore)]
}
]
}
async init(nodeData: INodeData): Promise<any> {
const embeddings = nodeData.inputs?.embeddings as Embeddings
const basePath = nodeData.inputs?.basePath as string
const output = nodeData.outputs?.output as string
const topK = nodeData.inputs?.topK as string
const k = topK ? parseFloat(topK) : 4
const vectorStore = await FaissStore.load(basePath, embeddings)
// Avoid illegal invocation error
vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number) => {
const index = vectorStore.index
if (k > index.ntotal()) {
const total = index.ntotal()
console.warn(`k (${k}) is greater than the number of elements in the index (${total}), setting k to ${total}`)
k = total
}
const result = index.search(query, k)
return result.labels.map((id, index) => {
const uuid = vectorStore._mapping[id]
return [vectorStore.docstore.search(uuid), result.distances[index]] as [Document, number]
})
}
if (output === 'retriever') {
const retriever = vectorStore.asRetriever(k)
return retriever
} else if (output === 'vectorStore') {
;(vectorStore as any).k = k
return vectorStore
}
return vectorStore
}
}
module.exports = { nodeClass: Faiss_Existing_VectorStores }

View File

@ -1,121 +0,0 @@
import { flatten } from 'lodash'
import { Embeddings } from '@langchain/core/embeddings'
import { Document } from '@langchain/core/documents'
import { FaissStore } from '@langchain/community/vectorstores/faiss'
import { INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
import { getBaseClasses } from '../../../src/utils'
class FaissUpsert_VectorStores implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
badge: string
baseClasses: string[]
inputs: INodeParams[]
outputs: INodeOutputsValue[]
constructor() {
this.label = 'Faiss Upsert Document'
this.name = 'faissUpsert'
this.version = 1.0
this.type = 'Faiss'
this.icon = 'faiss.svg'
this.category = 'Vector Stores'
this.description = 'Upsert documents to Faiss'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'DEPRECATING'
this.inputs = [
{
label: 'Document',
name: 'document',
type: 'Document',
list: true
},
{
label: 'Embeddings',
name: 'embeddings',
type: 'Embeddings'
},
{
label: 'Base Path to store',
name: 'basePath',
description: 'Path to store faiss.index file',
placeholder: `C:\\Users\\User\\Desktop`,
type: 'string'
},
{
label: 'Top K',
name: 'topK',
description: 'Number of top results to fetch. Default to 4',
placeholder: '4',
type: 'number',
additionalParams: true,
optional: true
}
]
this.outputs = [
{
label: 'Faiss Retriever',
name: 'retriever',
baseClasses: this.baseClasses
},
{
label: 'Faiss Vector Store',
name: 'vectorStore',
baseClasses: [this.type, ...getBaseClasses(FaissStore)]
}
]
}
async init(nodeData: INodeData): Promise<any> {
const docs = nodeData.inputs?.document as Document[]
const embeddings = nodeData.inputs?.embeddings as Embeddings
const output = nodeData.outputs?.output as string
const basePath = nodeData.inputs?.basePath as string
const topK = nodeData.inputs?.topK as string
const k = topK ? parseFloat(topK) : 4
const flattenDocs = docs && docs.length ? flatten(docs) : []
const finalDocs = []
for (let i = 0; i < flattenDocs.length; i += 1) {
if (flattenDocs[i] && flattenDocs[i].pageContent) {
finalDocs.push(new Document(flattenDocs[i]))
}
}
const vectorStore = await FaissStore.fromDocuments(finalDocs, embeddings)
await vectorStore.save(basePath)
// Avoid illegal invocation error
vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number) => {
const index = vectorStore.index
if (k > index.ntotal()) {
const total = index.ntotal()
console.warn(`k (${k}) is greater than the number of elements in the index (${total}), setting k to ${total}`)
k = total
}
const result = index.search(query, k)
return result.labels.map((id, index) => {
const uuid = vectorStore._mapping[id]
return [vectorStore.docstore.search(uuid), result.distances[index]] as [Document, number]
})
}
if (output === 'retriever') {
const retriever = vectorStore.asRetriever(k)
return retriever
} else if (output === 'vectorStore') {
;(vectorStore as any).k = k
return vectorStore
}
return vectorStore
}
}
module.exports = { nodeClass: FaissUpsert_VectorStores }

View File

@ -33,7 +33,6 @@ class Milvus_VectorStores implements INode {
this.category = 'Vector Stores' this.category = 'Vector Stores'
this.description = `Upsert embedded data and perform similarity search upon query using Milvus, world's most advanced open-source vector database` this.description = `Upsert embedded data and perform similarity search upon query using Milvus, world's most advanced open-source vector database`
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever'] this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'NEW'
this.credential = { this.credential = {
label: 'Connect Credential', label: 'Connect Credential',
name: 'credential', name: 'credential',

View File

@ -1,210 +0,0 @@
import { DataType, ErrorCode } from '@zilliz/milvus2-sdk-node'
import { MilvusLibArgs, Milvus } from '@langchain/community/vectorstores/milvus'
import { Embeddings } from '@langchain/core/embeddings'
import { Document } from '@langchain/core/documents'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
class Milvus_Existing_VectorStores implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
badge: string
baseClasses: string[]
inputs: INodeParams[]
credential: INodeParams
outputs: INodeOutputsValue[]
constructor() {
this.label = 'Milvus Load Existing collection'
this.name = 'milvusExistingCollection'
this.version = 2.0
this.type = 'Milvus'
this.icon = 'milvus.svg'
this.category = 'Vector Stores'
this.description = 'Load existing collection from Milvus (i.e: Document has been upserted)'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'DEPRECATING'
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
optional: true,
credentialNames: ['milvusAuth']
}
this.inputs = [
{
label: 'Embeddings',
name: 'embeddings',
type: 'Embeddings'
},
{
label: 'Milvus Server URL',
name: 'milvusServerUrl',
type: 'string',
placeholder: 'http://localhost:19530'
},
{
label: 'Milvus Collection Name',
name: 'milvusCollection',
type: 'string'
},
{
label: 'Milvus Filter',
name: 'milvusFilter',
type: 'string',
optional: true,
description:
'Filter data with a simple string query. Refer Milvus <a target="_blank" href="https://milvus.io/blog/2022-08-08-How-to-use-string-data-to-empower-your-similarity-search-applications.md#Hybrid-search">docs</a> for more details.',
placeholder: 'doc=="a"',
additionalParams: true
},
{
label: 'Top K',
name: 'topK',
description: 'Number of top results to fetch. Default to 4',
placeholder: '4',
type: 'number',
additionalParams: true,
optional: true
}
]
this.outputs = [
{
label: 'Milvus Retriever',
name: 'retriever',
baseClasses: this.baseClasses
},
{
label: 'Milvus Vector Store',
name: 'vectorStore',
baseClasses: [this.type, ...getBaseClasses(Milvus)]
}
]
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
// server setup
const address = nodeData.inputs?.milvusServerUrl as string
const collectionName = nodeData.inputs?.milvusCollection as string
const milvusFilter = nodeData.inputs?.milvusFilter as string
// embeddings
const embeddings = nodeData.inputs?.embeddings as Embeddings
const topK = nodeData.inputs?.topK as string
// output
const output = nodeData.outputs?.output as string
// format data
const k = topK ? parseInt(topK, 10) : 4
// credential
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const milvusUser = getCredentialParam('milvusUser', credentialData, nodeData)
const milvusPassword = getCredentialParam('milvusPassword', credentialData, nodeData)
// init MilvusLibArgs
const milVusArgs: MilvusLibArgs = {
url: address,
collectionName: collectionName
}
if (milvusUser) milVusArgs.username = milvusUser
if (milvusPassword) milVusArgs.password = milvusPassword
const vectorStore = await Milvus.fromExistingCollection(embeddings, milVusArgs)
// Avoid Illegal Invocation
vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number, filter?: string) => {
const hasColResp = await vectorStore.client.hasCollection({
collection_name: vectorStore.collectionName
})
if (hasColResp.status.error_code !== ErrorCode.SUCCESS) {
throw new Error(`Error checking collection: ${hasColResp}`)
}
if (hasColResp.value === false) {
throw new Error(`Collection not found: ${vectorStore.collectionName}, please create collection before search.`)
}
const filterStr = milvusFilter ?? filter ?? ''
await vectorStore.grabCollectionFields()
const loadResp = await vectorStore.client.loadCollectionSync({
collection_name: vectorStore.collectionName
})
if (loadResp.error_code !== ErrorCode.SUCCESS) {
throw new Error(`Error loading collection: ${loadResp}`)
}
const outputFields = vectorStore.fields.filter((field) => field !== vectorStore.vectorField)
const searchResp = await vectorStore.client.search({
collection_name: vectorStore.collectionName,
search_params: {
anns_field: vectorStore.vectorField,
topk: k.toString(),
metric_type: vectorStore.indexCreateParams.metric_type,
params: vectorStore.indexSearchParams
},
output_fields: outputFields,
vector_type: DataType.FloatVector,
vectors: [query],
filter: filterStr
})
if (searchResp.status.error_code !== ErrorCode.SUCCESS) {
throw new Error(`Error searching data: ${JSON.stringify(searchResp)}`)
}
const results: [Document, number][] = []
searchResp.results.forEach((result) => {
const fields = {
pageContent: '',
metadata: {} as Record<string, any>
}
Object.keys(result).forEach((key) => {
if (key === vectorStore.textField) {
fields.pageContent = result[key]
} else if (vectorStore.fields.includes(key) || key === vectorStore.primaryField) {
if (typeof result[key] === 'string') {
const { isJson, obj } = checkJsonString(result[key])
fields.metadata[key] = isJson ? obj : result[key]
} else {
fields.metadata[key] = result[key]
}
}
})
results.push([new Document(fields), result.score])
})
return results
}
if (output === 'retriever') {
const retriever = vectorStore.asRetriever(k)
return retriever
} else if (output === 'vectorStore') {
;(vectorStore as any).k = k
if (milvusFilter) {
;(vectorStore as any).filter = milvusFilter
}
return vectorStore
}
return vectorStore
}
}
function checkJsonString(value: string): { isJson: boolean; obj: any } {
try {
const result = JSON.parse(value)
return { isJson: true, obj: result }
} catch (e) {
return { isJson: false, obj: null }
}
}
module.exports = { nodeClass: Milvus_Existing_VectorStores }

View File

@ -1,285 +0,0 @@
import { flatten } from 'lodash'
import { DataType, ErrorCode, MetricType, IndexType } from '@zilliz/milvus2-sdk-node'
import { MilvusLibArgs, Milvus } from '@langchain/community/vectorstores/milvus'
import { Embeddings } from '@langchain/core/embeddings'
import { Document } from '@langchain/core/documents'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
interface InsertRow {
[x: string]: string | number[]
}
class Milvus_Upsert_VectorStores implements INode {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
badge: string
baseClasses: string[]
inputs: INodeParams[]
credential: INodeParams
outputs: INodeOutputsValue[]
constructor() {
this.label = 'Milvus Upsert Document'
this.name = 'milvusUpsert'
this.version = 1.0
this.type = 'Milvus'
this.icon = 'milvus.svg'
this.category = 'Vector Stores'
this.description = 'Upsert documents to Milvus'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'DEPRECATING'
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
optional: true,
credentialNames: ['milvusAuth']
}
this.inputs = [
{
label: 'Document',
name: 'document',
type: 'Document',
list: true
},
{
label: 'Embeddings',
name: 'embeddings',
type: 'Embeddings'
},
{
label: 'Milvus Server URL',
name: 'milvusServerUrl',
type: 'string',
placeholder: 'http://localhost:19530'
},
{
label: 'Milvus Collection Name',
name: 'milvusCollection',
type: 'string'
}
]
this.outputs = [
{
label: 'Milvus Retriever',
name: 'retriever',
baseClasses: this.baseClasses
},
{
label: 'Milvus Vector Store',
name: 'vectorStore',
baseClasses: [this.type, ...getBaseClasses(Milvus)]
}
]
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
// server setup
const address = nodeData.inputs?.milvusServerUrl as string
const collectionName = nodeData.inputs?.milvusCollection as string
// embeddings
const docs = nodeData.inputs?.document as Document[]
const embeddings = nodeData.inputs?.embeddings as Embeddings
const topK = nodeData.inputs?.topK as string
// output
const output = nodeData.outputs?.output as string
// format data
const k = topK ? parseInt(topK, 10) : 4
// credential
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const milvusUser = getCredentialParam('milvusUser', credentialData, nodeData)
const milvusPassword = getCredentialParam('milvusPassword', credentialData, nodeData)
// init MilvusLibArgs
const milVusArgs: MilvusLibArgs = {
url: address,
collectionName: collectionName
}
if (milvusUser) milVusArgs.username = milvusUser
if (milvusPassword) milVusArgs.password = milvusPassword
const flattenDocs = docs && docs.length ? flatten(docs) : []
const finalDocs = []
for (let i = 0; i < flattenDocs.length; i += 1) {
if (flattenDocs[i] && flattenDocs[i].pageContent) {
finalDocs.push(new Document(flattenDocs[i]))
}
}
const vectorStore = await MilvusUpsert.fromDocuments(finalDocs, embeddings, milVusArgs)
// Avoid Illegal Invocation
vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number, filter?: string) => {
const hasColResp = await vectorStore.client.hasCollection({
collection_name: vectorStore.collectionName
})
if (hasColResp.status.error_code !== ErrorCode.SUCCESS) {
throw new Error(`Error checking collection: ${hasColResp}`)
}
if (hasColResp.value === false) {
throw new Error(`Collection not found: ${vectorStore.collectionName}, please create collection before search.`)
}
const filterStr = filter ?? ''
await vectorStore.grabCollectionFields()
const loadResp = await vectorStore.client.loadCollectionSync({
collection_name: vectorStore.collectionName
})
if (loadResp.error_code !== ErrorCode.SUCCESS) {
throw new Error(`Error loading collection: ${loadResp}`)
}
const outputFields = vectorStore.fields.filter((field) => field !== vectorStore.vectorField)
const searchResp = await vectorStore.client.search({
collection_name: vectorStore.collectionName,
search_params: {
anns_field: vectorStore.vectorField,
topk: k.toString(),
metric_type: vectorStore.indexCreateParams.metric_type,
params: vectorStore.indexSearchParams
},
output_fields: outputFields,
vector_type: DataType.FloatVector,
vectors: [query],
filter: filterStr
})
if (searchResp.status.error_code !== ErrorCode.SUCCESS) {
throw new Error(`Error searching data: ${JSON.stringify(searchResp)}`)
}
const results: [Document, number][] = []
searchResp.results.forEach((result) => {
const fields = {
pageContent: '',
metadata: {} as Record<string, any>
}
Object.keys(result).forEach((key) => {
if (key === vectorStore.textField) {
fields.pageContent = result[key]
} else if (vectorStore.fields.includes(key) || key === vectorStore.primaryField) {
if (typeof result[key] === 'string') {
const { isJson, obj } = checkJsonString(result[key])
fields.metadata[key] = isJson ? obj : result[key]
} else {
fields.metadata[key] = result[key]
}
}
})
results.push([new Document(fields), result.score])
})
return results
}
if (output === 'retriever') {
const retriever = vectorStore.asRetriever(k)
return retriever
} else if (output === 'vectorStore') {
;(vectorStore as any).k = k
return vectorStore
}
return vectorStore
}
}
function checkJsonString(value: string): { isJson: boolean; obj: any } {
try {
const result = JSON.parse(value)
return { isJson: true, obj: result }
} catch (e) {
return { isJson: false, obj: null }
}
}
class MilvusUpsert extends Milvus {
async addVectors(vectors: number[][], documents: Document[]): Promise<void> {
if (vectors.length === 0) {
return
}
await this.ensureCollection(vectors, documents)
const insertDatas: InsertRow[] = []
for (let index = 0; index < vectors.length; index++) {
const vec = vectors[index]
const doc = documents[index]
const data: InsertRow = {
[this.textField]: doc.pageContent,
[this.vectorField]: vec
}
this.fields.forEach((field) => {
switch (field) {
case this.primaryField:
if (!this.autoId) {
if (doc.metadata[this.primaryField] === undefined) {
throw new Error(
`The Collection's primaryField is configured with autoId=false, thus its value must be provided through metadata.`
)
}
data[field] = doc.metadata[this.primaryField]
}
break
case this.textField:
data[field] = doc.pageContent
break
case this.vectorField:
data[field] = vec
break
default: // metadata fields
if (doc.metadata[field] === undefined) {
throw new Error(`The field "${field}" is not provided in documents[${index}].metadata.`)
} else if (typeof doc.metadata[field] === 'object') {
data[field] = JSON.stringify(doc.metadata[field])
} else {
data[field] = doc.metadata[field]
}
break
}
})
insertDatas.push(data)
}
const descIndexResp = await this.client.describeIndex({
collection_name: this.collectionName
})
if (descIndexResp.status.error_code === ErrorCode.IndexNotExist) {
const resp = await this.client.createIndex({
collection_name: this.collectionName,
field_name: this.vectorField,
index_name: `myindex_${Date.now().toString()}`,
index_type: IndexType.AUTOINDEX,
metric_type: MetricType.L2
})
if (resp.error_code !== ErrorCode.SUCCESS) {
throw new Error(`Error creating index`)
}
}
const insertResp = await this.client.insert({
collection_name: this.collectionName,
fields_data: insertDatas
})
if (insertResp.status.error_code !== ErrorCode.SUCCESS) {
throw new Error(`Error inserting data: ${JSON.stringify(insertResp)}`)
}
await this.client.flushSync({ collection_names: [this.collectionName] })
}
}
module.exports = { nodeClass: Milvus_Upsert_VectorStores }

View File

@ -4,7 +4,7 @@ import { MongoDBAtlasVectorSearch } from '@langchain/mongodb'
import { Embeddings } from '@langchain/core/embeddings' import { Embeddings } from '@langchain/core/embeddings'
import { Document } from '@langchain/core/documents' import { Document } from '@langchain/core/documents'
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams, IndexingResult } from '../../../src/Interface' import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams, IndexingResult } from '../../../src/Interface'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' import { getBaseClasses, getCredentialData, getCredentialParam, getVersion } from '../../../src/utils'
import { addMMRInputParams, resolveVectorStoreOrRetriever } from '../VectorStoreUtils' import { addMMRInputParams, resolveVectorStoreOrRetriever } from '../VectorStoreUtils'
class MongoDBAtlas_VectorStores implements INode { class MongoDBAtlas_VectorStores implements INode {
@ -30,7 +30,6 @@ class MongoDBAtlas_VectorStores implements INode {
this.icon = 'mongodb.svg' this.icon = 'mongodb.svg'
this.category = 'Vector Stores' this.category = 'Vector Stores'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever'] this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'NEW'
this.credential = { this.credential = {
label: 'Connect Credential', label: 'Connect Credential',
name: 'credential', name: 'credential',
@ -192,15 +191,17 @@ let mongoClientSingleton: MongoClient
let mongoUrl: string let mongoUrl: string
const getMongoClient = async (newMongoUrl: string) => { const getMongoClient = async (newMongoUrl: string) => {
const driverInfo = { name: 'Flowise', version: (await getVersion()).version }
if (!mongoClientSingleton) { if (!mongoClientSingleton) {
// if client does not exist // if client does not exist
mongoClientSingleton = new MongoClient(newMongoUrl) mongoClientSingleton = new MongoClient(newMongoUrl, { driverInfo })
mongoUrl = newMongoUrl mongoUrl = newMongoUrl
return mongoClientSingleton return mongoClientSingleton
} else if (mongoClientSingleton && newMongoUrl !== mongoUrl) { } else if (mongoClientSingleton && newMongoUrl !== mongoUrl) {
// if client exists but url changed // if client exists but url changed
mongoClientSingleton.close() mongoClientSingleton.close()
mongoClientSingleton = new MongoClient(newMongoUrl) mongoClientSingleton = new MongoClient(newMongoUrl, { driverInfo })
mongoUrl = newMongoUrl mongoUrl = newMongoUrl
return mongoClientSingleton return mongoClientSingleton
} }

View File

@ -1,146 +0,0 @@
import {
getBaseClasses,
getCredentialData,
getCredentialParam,
ICommonObject,
INodeData,
INodeOutputsValue,
INodeParams
} from '../../../src'
import { Embeddings } from '@langchain/core/embeddings'
import { VectorStore } from '@langchain/core/vectorstores'
import { Document } from '@langchain/core/documents'
import { MongoDBAtlasVectorSearch } from '@langchain/community/vectorstores/mongodb_atlas'
import { Collection, MongoClient } from 'mongodb'
export abstract class MongoDBSearchBase {
label: string
name: string
version: number
description: string
type: string
icon: string
category: string
badge: string
baseClasses: string[]
inputs: INodeParams[]
credential: INodeParams
outputs: INodeOutputsValue[]
mongoClient: MongoClient
protected constructor() {
this.type = 'MongoDB Atlas'
this.icon = 'mongodb.svg'
this.category = 'Vector Stores'
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'DEPRECATING'
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['mongoDBUrlApi']
}
this.inputs = [
{
label: 'Embeddings',
name: 'embeddings',
type: 'Embeddings'
},
{
label: 'Database',
name: 'databaseName',
placeholder: '<DB_NAME>',
type: 'string'
},
{
label: 'Collection Name',
name: 'collectionName',
placeholder: '<COLLECTION_NAME>',
type: 'string'
},
{
label: 'Index Name',
name: 'indexName',
placeholder: '<VECTOR_INDEX_NAME>',
type: 'string'
},
{
label: 'Content Field',
name: 'textKey',
description: 'Name of the field (column) that contains the actual content',
type: 'string',
default: 'text',
additionalParams: true,
optional: true
},
{
label: 'Embedded Field',
name: 'embeddingKey',
description: 'Name of the field (column) that contains the Embedding',
type: 'string',
default: 'embedding',
additionalParams: true,
optional: true
},
{
label: 'Top K',
name: 'topK',
description: 'Number of top results to fetch. Default to 4',
placeholder: '4',
type: 'number',
additionalParams: true,
optional: true
}
]
this.outputs = [
{
label: 'MongoDB Retriever',
name: 'retriever',
baseClasses: this.baseClasses
},
{
label: 'MongoDB Vector Store',
name: 'vectorStore',
baseClasses: [this.type, ...getBaseClasses(MongoDBAtlasVectorSearch)]
}
]
}
abstract constructVectorStore(
embeddings: Embeddings,
collection: Collection,
indexName: string,
textKey: string,
embeddingKey: string,
docs: Document<Record<string, any>>[] | undefined
): Promise<VectorStore>
async init(nodeData: INodeData, _: string, options: ICommonObject, docs: Document<Record<string, any>>[] | undefined): Promise<any> {
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const databaseName = nodeData.inputs?.databaseName as string
const collectionName = nodeData.inputs?.collectionName as string
const indexName = nodeData.inputs?.indexName as string
let textKey = nodeData.inputs?.textKey as string
let embeddingKey = nodeData.inputs?.embeddingKey as string
const embeddings = nodeData.inputs?.embeddings as Embeddings
const topK = nodeData.inputs?.topK as string
const k = topK ? parseFloat(topK) : 4
const output = nodeData.outputs?.output as string
let mongoDBConnectUrl = getCredentialParam('mongoDBConnectUrl', credentialData, nodeData)
this.mongoClient = new MongoClient(mongoDBConnectUrl)
const collection = this.mongoClient.db(databaseName).collection(collectionName)
if (!textKey || textKey === '') textKey = 'text'
if (!embeddingKey || embeddingKey === '') embeddingKey = 'embedding'
const vectorStore = await this.constructVectorStore(embeddings, collection, indexName, textKey, embeddingKey, docs)
if (output === 'retriever') {
return vectorStore.asRetriever(k)
} else if (output === 'vectorStore') {
;(vectorStore as any).k = k
return vectorStore
}
return vectorStore
}
}

View File

@ -1,39 +0,0 @@
import { Collection } from 'mongodb'
import { MongoDBAtlasVectorSearch } from '@langchain/community/vectorstores/mongodb_atlas'
import { Embeddings } from '@langchain/core/embeddings'
import { VectorStore } from '@langchain/core/vectorstores'
import { Document } from '@langchain/core/documents'
import { MongoDBSearchBase } from './MongoDBSearchBase'
import { ICommonObject, INode, INodeData } from '../../../src/Interface'
class MongoDBExisting_VectorStores extends MongoDBSearchBase implements INode {
constructor() {
super()
this.label = 'MongoDB Atlas Load Existing Index'
this.name = 'MongoDBIndex'
this.version = 1.0
this.description = 'Load existing data from MongoDB Atlas (i.e: Document has been upserted)'
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
return super.init(nodeData, _, options, undefined)
}
async constructVectorStore(
embeddings: Embeddings,
collection: Collection,
indexName: string,
textKey: string,
embeddingKey: string,
_: Document<Record<string, any>>[] | undefined
): Promise<VectorStore> {
return new MongoDBAtlasVectorSearch(embeddings, {
collection: collection,
indexName: indexName,
textKey: textKey,
embeddingKey: embeddingKey
})
}
}
module.exports = { nodeClass: MongoDBExisting_VectorStores }

View File

@ -1,59 +0,0 @@
import { flatten } from 'lodash'
import { Collection } from 'mongodb'
import { Embeddings } from '@langchain/core/embeddings'
import { Document } from '@langchain/core/documents'
import { VectorStore } from '@langchain/core/vectorstores'
import { MongoDBAtlasVectorSearch } from '@langchain/community/vectorstores/mongodb_atlas'
import { ICommonObject, INode, INodeData } from '../../../src/Interface'
import { MongoDBSearchBase } from './MongoDBSearchBase'
class MongoDBUpsert_VectorStores extends MongoDBSearchBase implements INode {
constructor() {
super()
this.label = 'MongoDB Atlas Upsert Document'
this.name = 'MongoDBUpsert'
this.version = 1.0
this.description = 'Upsert documents to MongoDB Atlas'
this.inputs.unshift({
label: 'Document',
name: 'document',
type: 'Document',
list: true
})
}
async constructVectorStore(
embeddings: Embeddings,
collection: Collection,
indexName: string,
textKey: string,
embeddingKey: string,
docs: Document<Record<string, any>>[]
): Promise<VectorStore> {
const mongoDBAtlasVectorSearch = new MongoDBAtlasVectorSearch(embeddings, {
collection: collection,
indexName: indexName,
textKey: textKey,
embeddingKey: embeddingKey
})
await mongoDBAtlasVectorSearch.addDocuments(docs)
return mongoDBAtlasVectorSearch
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const docs = nodeData.inputs?.document as Document[]
const flattenDocs = docs && docs.length ? flatten(docs) : []
const finalDocs = []
for (let i = 0; i < flattenDocs.length; i += 1) {
if (flattenDocs[i] && flattenDocs[i].pageContent) {
const document = new Document(flattenDocs[i])
finalDocs.push(document)
}
}
return super.init(nodeData, _, options, finalDocs)
}
}
module.exports = { nodeClass: MongoDBUpsert_VectorStores }

View File

@ -23,13 +23,12 @@ class OpenSearch_VectorStores implements INode {
constructor() { constructor() {
this.label = 'OpenSearch' this.label = 'OpenSearch'
this.name = 'openSearch' this.name = 'openSearch'
this.version = 2.0 this.version = 3.0
this.type = 'OpenSearch' this.type = 'OpenSearch'
this.icon = 'opensearch.svg' this.icon = 'opensearch.svg'
this.category = 'Vector Stores' this.category = 'Vector Stores'
this.description = `Upsert embedded data and perform similarity search upon query using OpenSearch, an open-source, all-in-one vector database` this.description = `Upsert embedded data and perform similarity search upon query using OpenSearch, an open-source, all-in-one vector database`
this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever'] this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
this.badge = 'NEW'
this.credential = { this.credential = {
label: 'Connect Credential', label: 'Connect Credential',
name: 'credential', name: 'credential',
@ -80,13 +79,17 @@ class OpenSearch_VectorStores implements INode {
//@ts-ignore //@ts-ignore
vectorStoreMethods = { vectorStoreMethods = {
async upsert(nodeData: INodeData, _: string, options: ICommonObject): Promise<Partial<IndexingResult>> { async upsert(nodeData: INodeData, options: ICommonObject): Promise<Partial<IndexingResult>> {
const docs = nodeData.inputs?.document as Document[] const docs = nodeData.inputs?.document as Document[]
const embeddings = nodeData.inputs?.embeddings as Embeddings const embeddings = nodeData.inputs?.embeddings as Embeddings
const indexName = nodeData.inputs?.indexName as string const indexName = nodeData.inputs?.indexName as string
const credentialData = await getCredentialData(nodeData.credential ?? '', options) const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const opensearchURL = getCredentialParam('openSearchUrl', credentialData, nodeData) 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 flattenDocs = docs && docs.length ? flatten(docs) : []
const finalDocs = [] const finalDocs = []
@ -96,10 +99,6 @@ class OpenSearch_VectorStores implements INode {
} }
} }
const client = new Client({
nodes: [opensearchURL]
})
try { try {
await OpenSearchVectorStore.fromDocuments(finalDocs, embeddings, { await OpenSearchVectorStore.fromDocuments(finalDocs, embeddings, {
client, client,
@ -121,10 +120,10 @@ class OpenSearch_VectorStores implements INode {
const credentialData = await getCredentialData(nodeData.credential ?? '', options) const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const opensearchURL = getCredentialParam('openSearchUrl', credentialData, nodeData) const opensearchURL = getCredentialParam('openSearchUrl', credentialData, nodeData)
const user = getCredentialParam('user', credentialData, nodeData)
const password = getCredentialParam('password', credentialData, nodeData)
const client = new Client({ const client = getOpenSearchClient(opensearchURL, user, password)
nodes: [opensearchURL]
})
const vectorStore = new OpenSearchVectorStore(embeddings, { const vectorStore = new OpenSearchVectorStore(embeddings, {
client, client,
@ -142,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 } module.exports = { nodeClass: OpenSearch_VectorStores }

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