Fix merge conflicts

This commit is contained in:
Ilango
2024-03-12 13:10:49 +05:30
159 changed files with 31653 additions and 498 deletions
+2
View File
@@ -0,0 +1,2 @@
node_modules
dist
+2 -1
View File
@@ -27,7 +27,8 @@ If applicable, add screenshots to help explain your problem.
If applicable, add exported flow in order to help replicating the problem. If applicable, add exported flow in order to help replicating the problem.
**Setup** **Setup**
- Installation [e.g. docker, `npx flowise start`, `yarn start`]
- Installation [e.g. docker, `npx flowise start`, `pnpm start`]
- Flowise Version [e.g. 1.2.11] - Flowise Version [e.g. 1.2.11]
- OS: [e.g. macOS, Windows, Linux] - OS: [e.g. macOS, Windows, Linux]
- Browser [e.g. chrome, safari] - Browser [e.g. chrome, safari]
+4 -4
View File
@@ -28,10 +28,10 @@ jobs:
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- run: npm i -g yarn - run: npm i -g pnpm
- run: yarn install --ignore-engines - run: pnpm install
- run: yarn lint - run: pnpm lint
- run: yarn build - run: pnpm build
+2 -2
View File
@@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
. "$(dirname "$0")/_/husky.sh" . "$(dirname "$0")/_/husky.sh"
yarn quick # prettify pnpm quick # prettify
yarn lint-staged # eslint lint(also include prettify but prettify support more file extensions than eslint, so run prettify first) pnpm lint-staged # eslint lint(also include prettify but prettify support more file extensions than eslint, so run prettify first)
+6
View File
@@ -0,0 +1,6 @@
auto-install-peers = true
strict-peer-dependencies = false
prefer-workspace-packages = true
link-workspace-packages = deep
hoist = true
shamefully-hoist = true
+10 -10
View File
@@ -44,9 +44,9 @@ Flowise 在一个单一的单体存储库中有 3 个不同的模块。
#### 先决条件 #### 先决条件
- 安装 [Yarn v1](https://classic.yarnpkg.com/en/docs/install) - 安装 [PNPM](https://pnpm.io/installation)
```bash ```bash
npm i -g yarn npm i -g pnpm
``` ```
#### 逐步指南 #### 逐步指南
@@ -71,45 +71,45 @@ Flowise 在一个单一的单体存储库中有 3 个不同的模块。
6. 安装所有模块的依赖项: 6. 安装所有模块的依赖项:
```bash ```bash
yarn install pnpm install
``` ```
7. 构建所有代码: 7. 构建所有代码:
```bash ```bash
yarn build pnpm build
``` ```
8. 在[http://localhost:3000](http://localhost:3000)上启动应用程序 8. 在[http://localhost:3000](http://localhost:3000)上启动应用程序
```bash ```bash
yarn start pnpm start
``` ```
9. 开发时: 9. 开发时:
- 在`packages/ui`中创建`.env`文件并指定`PORT`(参考`.env.example` - 在`packages/ui`中创建`.env`文件并指定`VITE_PORT`(参考`.env.example`
- 在`packages/server`中创建`.env`文件并指定`PORT`(参考`.env.example` - 在`packages/server`中创建`.env`文件并指定`PORT`(参考`.env.example`
- 运行 - 运行
```bash ```bash
yarn dev pnpm dev
``` ```
对`packages/ui`或`packages/server`进行的任何更改都将反映在[http://localhost:8080](http://localhost:8080)上 对`packages/ui`或`packages/server`进行的任何更改都将反映在[http://localhost:8080](http://localhost:8080)上
对于`packages/components`中进行的更改,再次运行`yarn build`以应用更改。 对于`packages/components`中进行的更改,再次运行`pnpm build`以应用更改。
10. 做完所有的更改后,运行以下命令来确保在生产环境中一切正常: 10. 做完所有的更改后,运行以下命令来确保在生产环境中一切正常:
```bash ```bash
yarn build pnpm build
``` ```
```bash ```bash
yarn start pnpm start
``` ```
11. 提交代码并从指向 [Flowise 主分支](https://github.com/FlowiseAI/Flowise/tree/master) 的分叉分支上提交 Pull Request。 11. 提交代码并从指向 [Flowise 主分支](https://github.com/FlowiseAI/Flowise/tree/master) 的分叉分支上提交 Pull Request。
+10 -10
View File
@@ -44,9 +44,9 @@ Flowise has 3 different modules in a single mono repository.
#### Prerequisite #### Prerequisite
- Install [Yarn v1](https://classic.yarnpkg.com/en/docs/install) - Install [PNPM](https://pnpm.io/installation)
```bash ```bash
npm i -g yarn npm i -g pnpm
``` ```
#### Step by step #### Step by step
@@ -71,45 +71,45 @@ Flowise has 3 different modules in a single mono repository.
6. Install all dependencies of all modules: 6. Install all dependencies of all modules:
```bash ```bash
yarn install pnpm install
``` ```
7. Build all the code: 7. Build all the code:
```bash ```bash
yarn build pnpm build
``` ```
8. Start the app on [http://localhost:3000](http://localhost:3000) 8. Start the app on [http://localhost:3000](http://localhost:3000)
```bash ```bash
yarn start pnpm start
``` ```
9. For development: 9. For development:
- Create `.env` file and specify the `PORT` (refer to `.env.example`) in `packages/ui` - Create `.env` file and specify the `VITE_PORT` (refer to `.env.example`) in `packages/ui`
- Create `.env` file and specify the `PORT` (refer to `.env.example`) in `packages/server` - Create `.env` file and specify the `PORT` (refer to `.env.example`) in `packages/server`
- Run - Run
```bash ```bash
yarn dev pnpm dev
``` ```
Any changes made in `packages/ui` or `packages/server` will be reflected on [http://localhost:8080](http://localhost:8080) Any changes made in `packages/ui` or `packages/server` will be reflected on [http://localhost:8080](http://localhost:8080)
For changes made in `packages/components`, run `yarn build` again to pickup the changes. For changes made in `packages/components`, run `pnpm build` again to pickup the changes.
10. After making all the changes, run 10. After making all the changes, run
```bash ```bash
yarn build pnpm build
``` ```
and and
```bash ```bash
yarn start pnpm start
``` ```
to make sure everything works fine in production. to make sure everything works fine in production.
+8 -17
View File
@@ -12,30 +12,21 @@ RUN apk add --no-cache build-base cairo-dev pango-dev
# Install Chromium # Install Chromium
RUN apk add --no-cache chromium RUN apk add --no-cache chromium
#install PNPM globaly
RUN npm install -g pnpm
ENV PUPPETEER_SKIP_DOWNLOAD=true ENV PUPPETEER_SKIP_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
WORKDIR /usr/src/packages WORKDIR /usr/src
# Copy root package.json and lockfile
COPY package.json yarn.loc[k] ./
# Copy components package.json
COPY packages/components/package.json ./packages/components/package.json
# Copy ui package.json
COPY packages/ui/package.json ./packages/ui/package.json
# Copy server package.json
COPY packages/server/package.json ./packages/server/package.json
RUN yarn install
# Copy app source # Copy app source
COPY . . COPY . .
RUN yarn build RUN pnpm install
RUN pnpm build
EXPOSE 3000 EXPOSE 3000
CMD [ "yarn", "start" ] CMD [ "pnpm", "start" ]
+7 -7
View File
@@ -75,9 +75,9 @@ Flowise 在一个单一的代码库中有 3 个不同的模块。
### 先决条件 ### 先决条件
- 安装 [Yarn v1](https://classic.yarnpkg.com/en/docs/install) - 安装 [PNPM](https://pnpm.io/installation)
```bash ```bash
npm i -g yarn npm i -g pnpm
``` ```
### 设置 ### 设置
@@ -97,31 +97,31 @@ Flowise 在一个单一的代码库中有 3 个不同的模块。
3. 安装所有模块的依赖: 3. 安装所有模块的依赖:
```bash ```bash
yarn install pnpm install
``` ```
4. 构建所有代码: 4. 构建所有代码:
```bash ```bash
yarn build pnpm build
``` ```
5. 启动应用: 5. 启动应用:
```bash ```bash
yarn start pnpm start
``` ```
现在可以在 [http://localhost:3000](http://localhost:3000) 访问应用 现在可以在 [http://localhost:3000](http://localhost:3000) 访问应用
6. 用于开发构建: 6. 用于开发构建:
- 在 `packages/ui` 中创建 `.env` 文件并指定 `PORT`(参考 `.env.example` - 在 `packages/ui` 中创建 `.env` 文件并指定 `VITE_PORT`(参考 `.env.example`
- 在 `packages/server` 中创建 `.env` 文件并指定 `PORT`(参考 `.env.example` - 在 `packages/server` 中创建 `.env` 文件并指定 `PORT`(参考 `.env.example`
- 运行 - 运行
```bash ```bash
yarn dev pnpm dev
``` ```
任何代码更改都会自动重新加载应用程序,访问 [http://localhost:8080](http://localhost:8080) 任何代码更改都会自动重新加载应用程序,访问 [http://localhost:8080](http://localhost:8080)
+7 -7
View File
@@ -75,9 +75,9 @@ Flowise has 3 different modules in a single mono repository.
### Prerequisite ### Prerequisite
- Install [Yarn v1](https://classic.yarnpkg.com/en/docs/install) - Install [Yarn v1](https://pnpm.io/installation)
```bash ```bash
npm i -g yarn npm i -g pnpm
``` ```
### Setup ### Setup
@@ -97,31 +97,31 @@ Flowise has 3 different modules in a single mono repository.
3. Install all dependencies of all modules: 3. Install all dependencies of all modules:
```bash ```bash
yarn install pnpm install
``` ```
4. Build all the code: 4. Build all the code:
```bash ```bash
yarn build pnpm build
``` ```
5. Start the app: 5. Start the app:
```bash ```bash
yarn start pnpm start
``` ```
You can now access the app on [http://localhost:3000](http://localhost:3000) You can now access the app on [http://localhost:3000](http://localhost:3000)
6. For development build: 6. For development build:
- Create `.env` file and specify the `PORT` (refer to `.env.example`) in `packages/ui` - Create `.env` file and specify the `VITE_PORT` (refer to `.env.example`) in `packages/ui`
- Create `.env` file and specify the `PORT` (refer to `.env.example`) in `packages/server` - Create `.env` file and specify the `PORT` (refer to `.env.example`) in `packages/server`
- Run - Run
```bash ```bash
yarn dev pnpm dev
``` ```
Any code changes will reload the app automatically on [http://localhost:8080](http://localhost:8080) Any code changes will reload the app automatically on [http://localhost:8080](http://localhost:8080)
+14 -7
View File
@@ -11,19 +11,19 @@
], ],
"scripts": { "scripts": {
"build": "turbo run build", "build": "turbo run build",
"build-force": "turbo run build --force", "build-force": "pnpm clean && turbo run build --force",
"dev": "turbo run dev --parallel", "dev": "turbo run dev --parallel",
"start": "run-script-os", "start": "run-script-os",
"start:windows": "cd packages/server/bin && run start", "start:windows": "cd packages/server/bin && run start",
"start:default": "cd packages/server/bin && ./run start", "start:default": "cd packages/server/bin && ./run start",
"clean": "npm exec -ws -- rimraf dist build", "clean": "pnpm --filter \"./packages/**\" clean",
"nuke": "pnpm --filter \"./packages/**\" nuke && rimraf node_modules .turbo",
"format": "prettier --write \"**/*.{ts,tsx,md}\"", "format": "prettier --write \"**/*.{ts,tsx,md}\"",
"test": "turbo run test",
"lint": "eslint \"**/*.{js,jsx,ts,tsx,json,md}\"", "lint": "eslint \"**/*.{js,jsx,ts,tsx,json,md}\"",
"lint-fix": "yarn lint --fix", "lint-fix": "pnpm lint --fix",
"quick": "pretty-quick --staged", "quick": "pretty-quick --staged",
"postinstall": "husky install", "postinstall": "husky install",
"migration:create": "yarn typeorm migration:create" "migration:create": "pnpm typeorm migration:create"
}, },
"lint-staged": { "lint-staged": {
"*.{js,jsx,ts,tsx,json,md}": "eslint --fix" "*.{js,jsx,ts,tsx,json,md}": "eslint --fix"
@@ -43,16 +43,23 @@
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-unused-imports": "^2.0.0", "eslint-plugin-unused-imports": "^2.0.0",
"husky": "^8.0.1", "husky": "^8.0.1",
"kill-port": "^2.0.1",
"lint-staged": "^13.0.3", "lint-staged": "^13.0.3",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"pretty-quick": "^3.1.3", "pretty-quick": "^3.1.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"run-script-os": "^1.1.6", "run-script-os": "^1.1.6",
"turbo": "^1.7.4", "turbo": "1.10.16",
"typescript": "^4.8.4" "typescript": "^4.8.4"
}, },
"packageManager": "pnpm@8.14.0",
"pnpm": {
"onlyBuiltDependencies": [
"sqlite3"
]
},
"engines": { "engines": {
"node": ">=18.15.0" "node": ">=18.15.0 <19.0.0 || ^20"
}, },
"resolutions": { "resolutions": {
"@qdrant/openapi-typescript-fetch": "1.2.1" "@qdrant/openapi-typescript-fetch": "1.2.1"
+1 -3
View File
@@ -1,6 +1,4 @@
import gulp from 'gulp' const { src, dest } = require('gulp')
const { src, dest } = gulp
function copyIcons() { function copyIcons() {
return src(['nodes/**/*.{jpg,png,svg}']).pipe(dest('dist/nodes')) return src(['nodes/**/*.{jpg,png,svg}']).pipe(dest('dist/nodes'))
@@ -107,7 +107,7 @@ export class HuggingFaceInference extends LLM implements HFInput {
const { HfInference } = await import('@huggingface/inference') const { HfInference } = await import('@huggingface/inference')
return { HfInference } return { HfInference }
} catch (e) { } catch (e) {
throw new Error('Please install huggingface as a dependency with, e.g. `yarn add @huggingface/inference`') throw new Error('Please install huggingface as a dependency with, e.g. `pnpm install @huggingface/inference`')
} }
} }
} }
@@ -72,7 +72,7 @@ export class Cohere extends LLM implements CohereInput {
const { default: cohere } = await import('cohere-ai') const { default: cohere } = await import('cohere-ai')
return { cohere } return { cohere }
} catch (e) { } catch (e) {
throw new Error('Please install cohere-ai as a dependency with, e.g. `yarn add cohere-ai`') throw new Error('Please install cohere-ai as a dependency with, e.g. `pnpm install cohere-ai`')
} }
} }
} }
@@ -46,9 +46,13 @@ class SetVariable_Utilities implements INode {
} }
async init(nodeData: INodeData): Promise<any> { async init(nodeData: INodeData): Promise<any> {
const inputRaw = nodeData.inputs?.input let inputRaw = nodeData.inputs?.input
const variableName = nodeData.inputs?.variableName as string const variableName = nodeData.inputs?.variableName as string
if (Array.isArray(inputRaw) && inputRaw.length === 1) {
inputRaw = inputRaw[0]
}
return { output: inputRaw, dynamicVariables: { [variableName]: inputRaw } } return { output: inputRaw, dynamicVariables: { [variableName]: inputRaw } }
} }
} }
+20 -2
View File
@@ -6,7 +6,11 @@
"types": "dist/src/index.d.ts", "types": "dist/src/index.d.ts",
"scripts": { "scripts": {
"build": "tsc && gulp", "build": "tsc && gulp",
"dev": "tsc --watch" "dev:gulp": "gulp",
"dev": "tsc-watch --noClear -p ./tsconfig.json --onSuccess \"pnpm dev:gulp\"",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"clean": "rimraf dist",
"nuke": "rimraf dist node_modules .turbo"
}, },
"keywords": [], "keywords": [],
"homepage": "https://flowiseai.com", "homepage": "https://flowiseai.com",
@@ -52,6 +56,8 @@
"cheerio": "^1.0.0-rc.12", "cheerio": "^1.0.0-rc.12",
"chromadb": "^1.5.11", "chromadb": "^1.5.11",
"cohere-ai": "^6.2.0", "cohere-ai": "^6.2.0",
"crypto-js": "^4.1.1",
"css-what": "^6.1.0",
"d3-dsv": "2", "d3-dsv": "2",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"express": "^4.17.3", "express": "^4.17.3",
@@ -61,8 +67,8 @@
"google-auth-library": "^9.4.0", "google-auth-library": "^9.4.0",
"graphql": "^16.6.0", "graphql": "^16.6.0",
"html-to-text": "^9.0.5", "html-to-text": "^9.0.5",
"husky": "^8.0.3",
"ioredis": "^5.3.2", "ioredis": "^5.3.2",
"jsdom": "^22.1.0",
"jsonpointer": "^5.0.1", "jsonpointer": "^5.0.1",
"langchain": "^0.1.20", "langchain": "^0.1.20",
"langfuse": "3.3.1", "langfuse": "3.3.1",
@@ -70,6 +76,7 @@
"langsmith": "0.1.6", "langsmith": "0.1.6",
"linkifyjs": "^4.1.1", "linkifyjs": "^4.1.1",
"llamaindex": "^0.0.48", "llamaindex": "^0.0.48",
"lodash": "^4.17.21",
"lunary": "^0.6.16", "lunary": "^0.6.16",
"mammoth": "^1.5.1", "mammoth": "^1.5.1",
"moment": "^2.29.3", "moment": "^2.29.3",
@@ -88,22 +95,33 @@
"pyodide": ">=0.21.0-alpha.2", "pyodide": ">=0.21.0-alpha.2",
"redis": "^4.6.7", "redis": "^4.6.7",
"replicate": "^0.12.3", "replicate": "^0.12.3",
"socket.io": "^4.6.1",
"srt-parser-2": "^1.2.3", "srt-parser-2": "^1.2.3",
"typeorm": "^0.3.6",
"vm2": "^3.9.19", "vm2": "^3.9.19",
"weaviate-ts-client": "^1.1.0", "weaviate-ts-client": "^1.1.0",
"winston": "^3.9.0",
"ws": "^8.9.0", "ws": "^8.9.0",
"zod": "^3.22.4",
"zod-to-json-schema": "^3.21.4" "zod-to-json-schema": "^3.21.4"
}, },
"devDependencies": { "devDependencies": {
"@swc/core": "^1.3.99",
"@types/crypto-js": "^4.1.1",
"@types/gulp": "4.0.9", "@types/gulp": "4.0.9",
"@types/lodash": "^4.14.202",
"@types/node-fetch": "2.6.2", "@types/node-fetch": "2.6.2",
"@types/object-hash": "^3.0.2", "@types/object-hash": "^3.0.2",
"@types/pg": "^8.10.2", "@types/pg": "^8.10.2",
"@types/ws": "^8.5.3", "@types/ws": "^8.5.3",
"babel-register": "^6.26.0",
"eslint-plugin-markdown": "^3.0.1", "eslint-plugin-markdown": "^3.0.1",
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"rimraf": "^5.0.5",
"tsc-watch": "^6.0.4",
"tslib": "^2.6.2",
"typescript": "^4.8.4" "typescript": "^4.8.4"
} }
} }
+2 -1
View File
@@ -16,5 +16,6 @@
"declaration": true, "declaration": true,
"module": "commonjs" "module": "commonjs"
}, },
"include": ["src", "nodes", "credentials"] "include": ["src", "nodes", "credentials"],
"exclude": ["gulpfile.ts", "node_modules", "dist"]
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"ignore": ["**/*.spec.ts", ".git", "node_modules"], "ignore": ["**/*.spec.ts", ".git", "node_modules"],
"watch": ["commands", "index.ts", "src", "../components/nodes", "../components/src"], "watch": ["commands", "index.ts", "src", "../components/nodes", "../components/src"],
"exec": "yarn oclif-dev", "exec": "pnpm start",
"ext": "ts" "ext": "ts"
} }
+15 -6
View File
@@ -22,14 +22,16 @@
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"start": "run-script-os", "start": "run-script-os",
"clean": "rimraf dist",
"nuke": "rimraf dist node_modules .turbo",
"start:windows": "cd bin && run start", "start:windows": "cd bin && run start",
"start:default": "cd bin && ./run start", "start:default": "cd bin && ./run start",
"dev": "concurrently \"yarn watch\" \"nodemon\"", "dev": "tsc-watch --noClear -p ./tsconfig.json --onSuccess \"pnpm start\"",
"oclif-dev": "run-script-os", "oclif-dev": "run-script-os",
"oclif-dev:windows": "cd bin && dev start", "oclif-dev:windows": "cd bin && dev start",
"oclif-dev:default": "cd bin && ./dev start", "oclif-dev:default": "cd bin && ./dev start",
"postpack": "shx rm -f oclif.manifest.json", "postpack": "shx rm -f oclif.manifest.json",
"prepack": "yarn build && oclif manifest && oclif readme", "prepack": "pnpm build && oclif manifest && oclif readme",
"typeorm": "typeorm-ts-node-commonjs", "typeorm": "typeorm-ts-node-commonjs",
"watch": "tsc --watch", "watch": "tsc --watch",
"version": "oclif readme && git add README.md" "version": "oclif readme && git add README.md"
@@ -41,11 +43,13 @@
"email": "henryheng@flowiseai.com" "email": "henryheng@flowiseai.com"
}, },
"engines": { "engines": {
"node": ">=18.15.0" "node": ">=18.15.0 <19.0.0 || ^20"
}, },
"license": "SEE LICENSE IN LICENSE.md", "license": "SEE LICENSE IN LICENSE.md",
"dependencies": { "dependencies": {
"@oclif/core": "^1.13.10", "@oclif/core": "^1.13.10",
"@types/lodash": "^4.14.202",
"@types/uuid": "^9.0.7",
"async-mutex": "^0.4.0", "async-mutex": "^0.4.0",
"axios": "1.6.2", "axios": "1.6.2",
"content-disposition": "0.5.4", "content-disposition": "0.5.4",
@@ -55,11 +59,14 @@
"express": "^4.17.3", "express": "^4.17.3",
"express-basic-auth": "^1.2.1", "express-basic-auth": "^1.2.1",
"express-rate-limit": "^6.9.0", "express-rate-limit": "^6.9.0",
"flowise-components": "*", "flowise-components": "workspace:^",
"flowise-ui": "*", "flowise-ui": "workspace:^",
"lodash": "^4.17.21",
"moment": "^2.29.3",
"moment-timezone": "^0.5.34", "moment-timezone": "^0.5.34",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"openai": "^4.20.0",
"pg": "^8.11.1", "pg": "^8.11.1",
"posthog-node": "^3.5.0", "posthog-node": "^3.5.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
@@ -77,11 +84,13 @@
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/sanitize-html": "^2.9.5", "@types/sanitize-html": "^2.9.5",
"concurrently": "^7.1.0", "concurrently": "^7.1.0",
"nodemon": "^2.0.15", "nodemon": "^2.0.22",
"oclif": "^3", "oclif": "^3",
"rimraf": "^5.0.5",
"run-script-os": "^1.1.6", "run-script-os": "^1.1.6",
"shx": "^0.3.3", "shx": "^0.3.3",
"ts-node": "^10.7.0", "ts-node": "^10.7.0",
"tsc-watch": "^6.0.4",
"typescript": "^4.8.4" "typescript": "^4.8.4"
} }
} }
+2 -2
View File
@@ -11,7 +11,7 @@ import logger from './utils/logger'
import { expressRequestLogger } from './utils/logger' import { expressRequestLogger } from './utils/logger'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import OpenAI from 'openai' import OpenAI from 'openai'
import { FindOptionsWhere, MoreThanOrEqual, LessThanOrEqual, Between } from 'typeorm' import { DataSource, FindOptionsWhere, MoreThanOrEqual, LessThanOrEqual } from 'typeorm'
import { import {
IChatFlow, IChatFlow,
IncomingInput, IncomingInput,
@@ -92,7 +92,7 @@ export class App {
chatflowPool: ChatflowPool chatflowPool: ChatflowPool
cachePool: CachePool cachePool: CachePool
telemetry: Telemetry telemetry: Telemetry
AppDataSource = getDataSource() AppDataSource: DataSource = getDataSource()
constructor() { constructor() {
this.app = express() this.app = express()
+5
View File
@@ -299,6 +299,8 @@ export const buildFlow = async (
exploredNode[startingNodeIds[i]] = { remainingLoop: maxLoop, lastSeenDepth: 0 } exploredNode[startingNodeIds[i]] = { remainingLoop: maxLoop, lastSeenDepth: 0 }
} }
const initializedNodes: Set<string> = new Set()
const reversedGraph = constructGraphs(reactFlowNodes, reactFlowEdges, { isReversed: true }).graph
while (nodeQueue.length) { while (nodeQueue.length) {
const { nodeId, depth } = nodeQueue.shift() as INodeQueue const { nodeId, depth } = nodeQueue.shift() as INodeQueue
@@ -384,6 +386,7 @@ export const buildFlow = async (
flowNodes[nodeIndex].data.instance = outputResult flowNodes[nodeIndex].data.instance = outputResult
logger.debug(`[server]: Finished initializing ${reactFlowNode.data.label} (${reactFlowNode.data.id})`) logger.debug(`[server]: Finished initializing ${reactFlowNode.data.label} (${reactFlowNode.data.id})`)
initializedNodes.add(reactFlowNode.data.id)
} }
} catch (e: any) { } catch (e: any) {
logger.error(e) logger.error(e)
@@ -406,6 +409,8 @@ export const buildFlow = async (
for (let i = 0; i < neighbourNodeIds.length; i += 1) { for (let i = 0; i < neighbourNodeIds.length; i += 1) {
const neighNodeId = neighbourNodeIds[i] const neighNodeId = neighbourNodeIds[i]
if (ignoreNodeIds.includes(neighNodeId)) continue if (ignoreNodeIds.includes(neighNodeId)) continue
if (initializedNodes.has(neighNodeId)) continue
if (reversedGraph[neighNodeId].some((dependId) => !initializedNodes.has(dependId))) continue
// If nodeId has been seen, cycle detected // If nodeId has been seen, cycle detected
if (Object.prototype.hasOwnProperty.call(exploredNode, neighNodeId)) { if (Object.prototype.hasOwnProperty.call(exploredNode, neighNodeId)) {
const { remainingLoop, lastSeenDepth } = exploredNode[neighNodeId] const { remainingLoop, lastSeenDepth } = exploredNode[neighNodeId]
+1 -1
View File
@@ -1 +1 @@
PORT=8080 VITE_PORT=8080
+46
View File
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Flowise - Low-code LLM apps builder</title>
<link rel="icon" href="favicon.ico" />
<!-- Meta Tags-->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#2296f3" />
<meta name="title" content="Flowise - Low-code LLM apps builder" />
<meta name="description" content="Drag & drop UI to build your customized LLM flow" />
<meta name="keywords" content="react, material-ui, workflow automation, llm, artificial-intelligence" />
<meta name="author" content="FlowiseAI" />
<!-- Open Graph / Facebook -->
<meta property="og:locale" content="en_US" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://flowiseai.com/" />
<meta property="og:site_name" content="flowiseai.com" />
<meta property="og:title" content="Flowise - Low-code LLM apps builder" />
<meta property="og:description" content="Drag & drop UI to build your customized LLM flow" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://twitter.com/FlowiseAI" />
<meta property="twitter:title" content="Flowise - Low-code LLM apps builder" />
<meta property="twitter:description" content="Drag & drop UI to build your customized LLM flow" />
<meta name="twitter:creator" content="@FlowiseAI" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@400;500;600;700&family=Roboto:wght@400;500;700&display=swap"
rel="stylesheet"
/>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="portal"></div>
<script type="module" src="src/index.jsx"></script>
<script>
if (global === undefined) {
var global = window
}
</script>
</body>
</html>
+3 -1
View File
@@ -2,7 +2,9 @@
"compilerOptions": { "compilerOptions": {
"target": "esnext", "target": "esnext",
"module": "commonjs", "module": "commonjs",
"baseUrl": "src" "paths": {
"@/*": ["./src/*"]
}
}, },
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules"] "exclude": ["node_modules"]
+13 -7
View File
@@ -22,7 +22,9 @@
"@uiw/codemirror-theme-sublime": "^4.21.21", "@uiw/codemirror-theme-sublime": "^4.21.21",
"@uiw/codemirror-theme-vscode": "^4.21.21", "@uiw/codemirror-theme-vscode": "^4.21.21",
"@uiw/react-codemirror": "^4.21.21", "@uiw/react-codemirror": "^4.21.21",
"axios": "^0.27.2",
"clsx": "^1.1.1", "clsx": "^1.1.1",
"dotenv": "^16.0.0",
"flowise-embed": "*", "flowise-embed": "*",
"flowise-embed-react": "*", "flowise-embed-react": "*",
"flowise-react-json-view": "*", "flowise-react-json-view": "*",
@@ -53,14 +55,14 @@
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remark-math": "^5.1.1", "remark-math": "^5.1.1",
"socket.io-client": "^4.6.1", "socket.io-client": "^4.6.1",
"uuid": "^9.0.1",
"yup": "^0.32.9" "yup": "^0.32.9"
}, },
"scripts": { "scripts": {
"start": "craco start", "dev": "vite",
"dev": "craco start", "build": "vite build",
"build": "craco build", "clean": "rimraf build",
"test": "craco test", "nuke": "rimraf build node_modules .turbo"
"eject": "craco eject"
}, },
"babel": { "babel": {
"presets": [ "presets": [
@@ -82,13 +84,17 @@
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.15.8", "@babel/eslint-parser": "^7.15.8",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@craco/craco": "^7.1.0",
"@testing-library/jest-dom": "^5.11.10", "@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^14.0.0", "@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^12.8.3", "@testing-library/user-event": "^12.8.3",
"@vitejs/plugin-react": "^4.2.0",
"pretty-quick": "^3.1.3", "pretty-quick": "^3.1.3",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"rimraf": "^5.0.5",
"sass": "^1.42.1", "sass": "^1.42.1",
"typescript": "^4.8.4" "typescript": "^4.8.4",
"vite": "^5.0.2",
"vite-plugin-pwa": "^0.17.0",
"vite-plugin-react-js-support": "^1.0.7"
} }
} }
+2 -2
View File
@@ -61,8 +61,8 @@
You can add webfonts, meta tags, or analytics to this file. You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag. The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`. To begin the development, run `pnpm start`.
To create a production bundle, use `npm run build` or `yarn build`. To create a production bundle, use `pnpm build`.
--> -->
</body> </body>
</html> </html>
@@ -4,13 +4,13 @@ import { ThemeProvider } from '@mui/material/styles'
import { CssBaseline, StyledEngineProvider } from '@mui/material' import { CssBaseline, StyledEngineProvider } from '@mui/material'
// routing // routing
import Routes from 'routes' import Routes from '@/routes'
// defaultTheme // defaultTheme
import themes from 'themes' import themes from '@/themes'
// project imports // project imports
import NavigationScroll from 'layout/NavigationScroll' import NavigationScroll from '@/layout/NavigationScroll'
// ==============================|| APP ||============================== // // ==============================|| APP ||============================== //
+1 -1
View File
@@ -1,5 +1,5 @@
import axios from 'axios' import axios from 'axios'
import { baseURL } from 'store/constant' import { baseURL } from '@/store/constant'
const apiClient = axios.create({ const apiClient = axios.create({
baseURL: `${baseURL}/api/v1`, baseURL: `${baseURL}/api/v1`,
+1 -1
View File
@@ -2,7 +2,7 @@
@import 'themes-vars.module.scss'; @import 'themes-vars.module.scss';
// third-party // third-party
@import '~react-perfect-scrollbar/dist/css/styles.css'; @import 'react-perfect-scrollbar/dist/css/styles.css';
// ==============================|| LIGHT BOX ||============================== // // ==============================|| LIGHT BOX ||============================== //
.fullscreen .react-images__blanket { .fullscreen .react-images__blanket {
@@ -1,6 +1,6 @@
import { useContext } from 'react' import { useContext } from 'react'
import ConfirmContext from 'store/context/ConfirmContext' import ConfirmContext from '@/store/context/ConfirmContext'
import { HIDE_CONFIRM, SHOW_CONFIRM } from 'store/actions' import { HIDE_CONFIRM, SHOW_CONFIRM } from '@/store/actions'
let resolveCallback let resolveCallback
const useConfirm = () => { const useConfirm = () => {
@@ -1,17 +1,17 @@
import React from 'react' import React from 'react'
import App from './App' import App from '@/App'
import { store } from 'store' import { store } from '@/store'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
// style + assets // style + assets
import 'assets/scss/style.scss' import '@/assets/scss/style.scss'
// third party // third party
import { BrowserRouter } from 'react-router-dom' import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { SnackbarProvider } from 'notistack' import { SnackbarProvider } from 'notistack'
import ConfirmContextProvider from 'store/context/ConfirmContextProvider' import ConfirmContextProvider from '@/store/context/ConfirmContextProvider'
import { ReactFlowContext } from 'store/context/ReactFlowContext' import { ReactFlowContext } from '@/store/context/ReactFlowContext'
const container = document.getElementById('root') const container = document.getElementById('root')
const root = createRoot(container) const root = createRoot(container)
@@ -23,9 +23,9 @@ import {
import PerfectScrollbar from 'react-perfect-scrollbar' import PerfectScrollbar from 'react-perfect-scrollbar'
// project imports // project imports
import MainCard from 'ui-component/cards/MainCard' import MainCard from '@/ui-component/cards/MainCard'
import Transitions from 'ui-component/extended/Transitions' import Transitions from '@/ui-component/extended/Transitions'
import AboutDialog from 'ui-component/dialog/AboutDialog' import AboutDialog from '@/ui-component/dialog/AboutDialog'
// assets // assets
import { IconLogout, IconSettings, IconInfoCircle } from '@tabler/icons' import { IconLogout, IconSettings, IconInfoCircle } from '@tabler/icons'
@@ -16,7 +16,7 @@ import ProfileSection from './ProfileSection'
import { IconMenu2 } from '@tabler/icons' import { IconMenu2 } from '@tabler/icons'
// store // store
import { SET_DARKMODE } from 'store/actions' import { SET_DARKMODE } from '@/store/actions'
// ==============================|| MAIN NAVBAR / HEADER ||============================== // // ==============================|| MAIN NAVBAR / HEADER ||============================== //
@@ -4,8 +4,8 @@ import { Link } from 'react-router-dom'
import { ButtonBase } from '@mui/material' import { ButtonBase } from '@mui/material'
// project imports // project imports
import config from 'config' import config from '@/config'
import Logo from 'ui-component/extended/Logo' import Logo from '@/ui-component/extended/Logo'
// ==============================|| MAIN LOGO ||============================== // // ==============================|| MAIN LOGO ||============================== //
@@ -8,8 +8,8 @@ import { useTheme } from '@mui/material/styles'
import { Avatar, Chip, ListItemButton, ListItemIcon, ListItemText, Typography, useMediaQuery } from '@mui/material' import { Avatar, Chip, ListItemButton, ListItemIcon, ListItemText, Typography, useMediaQuery } from '@mui/material'
// project imports // project imports
import { MENU_OPEN, SET_MENU } from 'store/actions' import { MENU_OPEN, SET_MENU } from '@/store/actions'
import config from 'config' import config from '@/config'
// assets // assets
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord' import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'
@@ -3,7 +3,7 @@ import { Typography } from '@mui/material'
// project imports // project imports
import NavGroup from './NavGroup' import NavGroup from './NavGroup'
import menuItem from 'menu-items' import menuItem from '@/menu-items'
// ==============================|| SIDEBAR MENU LIST ||============================== // // ==============================|| SIDEBAR MENU LIST ||============================== //
@@ -11,7 +11,7 @@ import { BrowserView, MobileView } from 'react-device-detect'
// project imports // project imports
import MenuList from './MenuList' import MenuList from './MenuList'
import LogoSection from '../LogoSection' import LogoSection from '../LogoSection'
import { drawerWidth } from 'store/constant' import { drawerWidth } from '@/store/constant'
// ==============================|| SIDEBAR DRAWER ||============================== // // ==============================|| SIDEBAR DRAWER ||============================== //
@@ -9,8 +9,8 @@ import { AppBar, Box, CssBaseline, Toolbar, useMediaQuery } from '@mui/material'
// project imports // project imports
import Header from './Header' import Header from './Header'
import Sidebar from './Sidebar' import Sidebar from './Sidebar'
import { drawerWidth } from 'store/constant' import { drawerWidth } from '@/store/constant'
import { SET_MENU } from 'store/actions' import { SET_MENU } from '@/store/actions'
// styles // styles
const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({ const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
@@ -1,12 +1,12 @@
import { lazy } from 'react' import { lazy } from 'react'
// project imports // project imports
import Loadable from 'ui-component/loading/Loadable' import Loadable from '@/ui-component/loading/Loadable'
import MinimalLayout from 'layout/MinimalLayout' import MinimalLayout from '@/layout/MinimalLayout'
// canvas routing // canvas routing
const Canvas = Loadable(lazy(() => import('views/canvas'))) const Canvas = Loadable(lazy(() => import('@/views/canvas')))
const MarketplaceCanvas = Loadable(lazy(() => import('views/marketplaces/MarketplaceCanvas'))) const MarketplaceCanvas = Loadable(lazy(() => import('@/views/marketplaces/MarketplaceCanvas')))
// ==============================|| CANVAS ROUTING ||============================== // // ==============================|| CANVAS ROUTING ||============================== //
@@ -1,11 +1,11 @@
import { lazy } from 'react' import { lazy } from 'react'
// project imports // project imports
import Loadable from 'ui-component/loading/Loadable' import Loadable from '@/ui-component/loading/Loadable'
import MinimalLayout from 'layout/MinimalLayout' import MinimalLayout from '@/layout/MinimalLayout'
// canvas routing // canvas routing
const ChatbotFull = Loadable(lazy(() => import('views/chatbot'))) const ChatbotFull = Loadable(lazy(() => import('@/views/chatbot')))
// ==============================|| CANVAS ROUTING ||============================== // // ==============================|| CANVAS ROUTING ||============================== //
@@ -1,29 +1,29 @@
import { lazy } from 'react' import { lazy } from 'react'
// project imports // project imports
import MainLayout from 'layout/MainLayout' import MainLayout from '@/layout/MainLayout'
import Loadable from 'ui-component/loading/Loadable' import Loadable from '@/ui-component/loading/Loadable'
// chatflows routing // chatflows routing
const Chatflows = Loadable(lazy(() => import('views/chatflows'))) const Chatflows = Loadable(lazy(() => import('@/views/chatflows')))
// marketplaces routing // marketplaces routing
const Marketplaces = Loadable(lazy(() => import('views/marketplaces'))) const Marketplaces = Loadable(lazy(() => import('@/views/marketplaces')))
// apikey routing // apikey routing
const APIKey = Loadable(lazy(() => import('views/apikey'))) const APIKey = Loadable(lazy(() => import('@/views/apikey')))
// tools routing // tools routing
const Tools = Loadable(lazy(() => import('views/tools'))) const Tools = Loadable(lazy(() => import('@/views/tools')))
// assistants routing // assistants routing
const Assistants = Loadable(lazy(() => import('views/assistants'))) const Assistants = Loadable(lazy(() => import('@/views/assistants')))
// credentials routing // credentials routing
const Credentials = Loadable(lazy(() => import('views/credentials'))) const Credentials = Loadable(lazy(() => import('@/views/credentials')))
// variables routing // variables routing
const Variables = Loadable(lazy(() => import('views/variables'))) const Variables = Loadable(lazy(() => import('@/views/variables')))
// ==============================|| MAIN ROUTING ||============================== // // ==============================|| MAIN ROUTING ||============================== //
@@ -4,7 +4,7 @@ import { useRoutes } from 'react-router-dom'
import MainRoutes from './MainRoutes' import MainRoutes from './MainRoutes'
import CanvasRoutes from './CanvasRoutes' import CanvasRoutes from './CanvasRoutes'
import ChatbotRoutes from './ChatbotRoutes' import ChatbotRoutes from './ChatbotRoutes'
import config from 'config' import config from '@/config'
// ==============================|| ROUTING RENDER ||============================== // // ==============================|| ROUTING RENDER ||============================== //
+4 -1
View File
@@ -3,7 +3,10 @@ export const gridSpacing = 3
export const drawerWidth = 260 export const drawerWidth = 260
export const appDrawerWidth = 320 export const appDrawerWidth = 320
export const maxScroll = 100000 export const maxScroll = 100000
export const baseURL = process.env.NODE_ENV === 'production' ? window.location.origin : window.location.origin.replace(':8080', ':3000') export const baseURL =
import.meta.env.PROD === true
? window.location.origin
: window.location.origin.replace(`:${import.meta.env.VITE_PORT ?? '8080'}`, ':3000')
export const uiBaseURL = window.location.origin export const uiBaseURL = window.location.origin
export const FLOWISE_CREDENTIAL_ID = 'FLOWISE_CREDENTIAL_ID' export const FLOWISE_CREDENTIAL_ID = 'FLOWISE_CREDENTIAL_ID'
export const REDACTED_CREDENTIAL_VALUE = '_FLOWISE_BLANK_07167752-1a71-43b1-bf8f-4f32252165db' export const REDACTED_CREDENTIAL_VALUE = '_FLOWISE_BLANK_07167752-1a71-43b1-bf8f-4f32252165db'
@@ -1,9 +1,9 @@
import { createContext, useState } from 'react' import { createContext, useState } from 'react'
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { getUniqueNodeId } from 'utils/genericHelper' import { getUniqueNodeId } from '@/utils/genericHelper'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
import { SET_DIRTY } from 'store/actions' import { SET_DIRTY } from '@/store/actions'
const initialValue = { const initialValue = {
reactFlowInstance: null, reactFlowInstance: null,
@@ -1,5 +1,5 @@
// project imports // project imports
import config from 'config' import config from '@/config'
// action - state management // action - state management
import * as actionTypes from '../actions' import * as actionTypes from '../actions'
+1 -1
View File
@@ -1,7 +1,7 @@
import { createTheme } from '@mui/material/styles' import { createTheme } from '@mui/material/styles'
// assets // assets
import colors from 'assets/scss/_themes-vars.module.scss' import colors from '@/assets/scss/_themes-vars.module.scss'
// project imports // project imports
import componentStyleOverrides from './compStyleOverride' import componentStyleOverrides from './compStyleOverride'
@@ -16,19 +16,19 @@ import Button from '@mui/material/Button'
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'
import { IconX } from '@tabler/icons' import { IconX } from '@tabler/icons'
import chatflowsApi from 'api/chatflows' import chatflowsApi from '@/api/chatflows'
import useApi from '../../hooks/useApi' import useApi from '@/hooks/useApi'
import useConfirm from 'hooks/useConfirm' import useConfirm from '@/hooks/useConfirm'
import { uiBaseURL } from '../../store/constant' import { uiBaseURL } from '@/store/constant'
import { closeSnackbar as closeSnackbarAction, enqueueSnackbar as enqueueSnackbarAction } from '../../store/actions' import { closeSnackbar as closeSnackbarAction, enqueueSnackbar as enqueueSnackbarAction } from '@/store/actions'
import SaveChatflowDialog from '../dialog/SaveChatflowDialog' import SaveChatflowDialog from '@/ui-component/dialog/SaveChatflowDialog'
import TagDialog from '../dialog/TagDialog' import TagDialog from '@/ui-component/dialog/TagDialog'
import StarterPromptsDialog from '@/ui-component/dialog/StarterPromptsDialog'
import { generateExportFlowData } from '../../utils/genericHelper' import { generateExportFlowData } from '@/utils/genericHelper'
import useNotifier from '../../utils/useNotifier' import useNotifier from '@/utils/useNotifier'
import StarterPromptsDialog from '../dialog/StarterPromptsDialog'
const StyledMenu = styled((props) => ( const StyledMenu = styled((props) => (
<Menu <Menu
@@ -5,8 +5,8 @@ import { styled } from '@mui/material/styles'
import { Box, Grid, Typography } from '@mui/material' import { Box, Grid, Typography } from '@mui/material'
// project imports // project imports
import MainCard from 'ui-component/cards/MainCard' import MainCard from '@/ui-component/cards/MainCard'
import SkeletonChatflowCard from 'ui-component/cards/Skeleton/ChatflowCard' import SkeletonChatflowCard from '@/ui-component/cards/Skeleton/ChatflowCard'
const CardWrapper = styled(MainCard)(({ theme }) => ({ const CardWrapper = styled(MainCard)(({ theme }) => ({
background: theme.palette.card.main, background: theme.palette.card.main,
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types'
import { Dialog, DialogContent, DialogTitle, TableContainer, Table, TableHead, TableRow, TableCell, TableBody, Paper } from '@mui/material' import { Dialog, DialogContent, DialogTitle, TableContainer, Table, TableHead, TableRow, TableCell, TableBody, Paper } from '@mui/material'
import moment from 'moment' import moment from 'moment'
import axios from 'axios' import axios from 'axios'
import { baseURL } from 'store/constant' import { baseURL } from '@/store/constant'
const AboutDialog = ({ show, onCancel }) => { const AboutDialog = ({ show, onCancel }) => {
const portalElement = document.getElementById('portal') const portalElement = document.getElementById('portal')
@@ -4,8 +4,8 @@ import { useState, useEffect } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Dialog, DialogContent } from '@mui/material' import { Dialog, DialogContent } from '@mui/material'
import PerfectScrollbar from 'react-perfect-scrollbar' import PerfectScrollbar from 'react-perfect-scrollbar'
import NodeInputHandler from 'views/canvas/NodeInputHandler' import NodeInputHandler from '@/views/canvas/NodeInputHandler'
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
const AdditionalParamsDialog = ({ show, dialogProps, onCancel }) => { const AdditionalParamsDialog = ({ show, dialogProps, onCancel }) => {
const portalElement = document.getElementById('portal') const portalElement = document.getElementById('portal')
@@ -0,0 +1,220 @@
import { createPortal } from 'react-dom'
import { useDispatch } from 'react-redux'
import { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
// material-ui
import {
Button,
IconButton,
Dialog,
DialogContent,
OutlinedInput,
DialogTitle,
DialogActions,
Box,
List,
InputAdornment
} from '@mui/material'
import { IconX, IconTrash, IconPlus } from '@tabler/icons'
// Project import
import { StyledButton } from '@/ui-component/button/StyledButton'
// store
import {
enqueueSnackbar as enqueueSnackbarAction,
closeSnackbar as closeSnackbarAction,
SET_CHATFLOW,
HIDE_CANVAS_DIALOG,
SHOW_CANVAS_DIALOG
} from '@/store/actions'
import useNotifier from '@/utils/useNotifier'
// API
import chatflowsApi from '@/api/chatflows'
const AllowedDomainsDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
const portalElement = document.getElementById('portal')
const dispatch = useDispatch()
useNotifier()
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
const [inputFields, setInputFields] = useState([''])
const [chatbotConfig, setChatbotConfig] = useState({})
const addInputField = () => {
setInputFields([...inputFields, ''])
}
const removeInputFields = (index) => {
const rows = [...inputFields]
rows.splice(index, 1)
setInputFields(rows)
}
const handleChange = (index, evnt) => {
const { value } = evnt.target
const list = [...inputFields]
list[index] = value
setInputFields(list)
}
const onSave = async () => {
try {
let value = {
allowedOrigins: [...inputFields]
}
chatbotConfig.allowedOrigins = value.allowedOrigins
const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, {
chatbotConfig: JSON.stringify(chatbotConfig)
})
if (saveResp.data) {
enqueueSnackbar({
message: 'Allowed Origins Saved',
options: {
key: new Date().getTime() + Math.random(),
variant: 'success',
action: (key) => (
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
<IconX />
</Button>
)
}
})
dispatch({ type: SET_CHATFLOW, chatflow: saveResp.data })
}
onConfirm()
} catch (error) {
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
enqueueSnackbar({
message: `Failed to save Allowed Origins: ${errorData}`,
options: {
key: new Date().getTime() + Math.random(),
variant: 'error',
persist: true,
action: (key) => (
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
<IconX />
</Button>
)
}
})
}
}
useEffect(() => {
if (dialogProps.chatflow && dialogProps.chatflow.chatbotConfig) {
try {
let chatbotConfig = JSON.parse(dialogProps.chatflow.chatbotConfig)
setChatbotConfig(chatbotConfig || {})
if (chatbotConfig.allowedOrigins) {
let inputFields = [...chatbotConfig.allowedOrigins]
setInputFields(inputFields)
} else {
setInputFields([''])
}
} catch (e) {
setInputFields([''])
}
}
return () => {}
}, [dialogProps])
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
const component = show ? (
<Dialog
onClose={onCancel}
open={show}
fullWidth
maxWidth='sm'
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
>
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
{dialogProps.title || 'Allowed Origins'}
</DialogTitle>
<DialogContent>
<div
style={{
display: 'flex',
flexDirection: 'column'
}}
>
<span>Your chatbot will only work when used from the following domains.</span>
</div>
<Box sx={{ '& > :not(style)': { m: 1 }, pt: 2 }}>
<List>
{inputFields.map((origin, index) => {
return (
<div key={index} style={{ display: 'flex', width: '100%' }}>
<Box sx={{ width: '100%', mb: 1 }}>
<OutlinedInput
sx={{ width: '100%' }}
key={index}
type='text'
onChange={(e) => handleChange(index, e)}
size='small'
value={origin}
name='origin'
placeholder='https://example.com'
endAdornment={
<InputAdornment position='end' sx={{ padding: '2px' }}>
{inputFields.length > 1 && (
<IconButton
sx={{ height: 30, width: 30 }}
size='small'
color='error'
disabled={inputFields.length === 1}
onClick={() => removeInputFields(index)}
edge='end'
>
<IconTrash />
</IconButton>
)}
</InputAdornment>
}
/>
</Box>
<Box sx={{ width: '5%', mb: 1 }}>
{index === inputFields.length - 1 && (
<IconButton color='primary' onClick={addInputField}>
<IconPlus />
</IconButton>
)}
</Box>
</div>
)
})}
</List>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={onCancel}>Cancel</Button>
<StyledButton variant='contained' onClick={onSave}>
Save
</StyledButton>
</DialogActions>
</Dialog>
) : null
return createPortal(component, portalElement)
}
AllowedDomainsDialog.propTypes = {
show: PropTypes.bool,
dialogProps: PropTypes.object,
onCancel: PropTypes.func,
onConfirm: PropTypes.func
}
export default AllowedDomainsDialog
@@ -0,0 +1,358 @@
import { createPortal } from 'react-dom'
import { useDispatch } from 'react-redux'
import { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions'
// material-ui
import {
Typography,
Box,
Button,
Dialog,
DialogContent,
DialogTitle,
DialogActions,
Accordion,
AccordionSummary,
AccordionDetails,
ListItem,
ListItemAvatar,
ListItemText
} from '@mui/material'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import { IconX } from '@tabler/icons'
// Project import
import CredentialInputHandler from '@/views/canvas/CredentialInputHandler'
import { TooltipWithParser } from '@/ui-component/tooltip/TooltipWithParser'
import { SwitchInput } from '@/ui-component/switch/Switch'
import { Input } from '@/ui-component/input/Input'
import { StyledButton } from '@/ui-component/button/StyledButton'
import langsmithPNG from '@/assets/images/langchain.png'
import langfuseSVG from '@/assets/images/langfuse.svg'
import lunarySVG from '@/assets/images/lunary.svg'
// store
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
import useNotifier from '@/utils/useNotifier'
// API
import chatflowsApi from '@/api/chatflows'
const analyticProviders = [
{
label: 'LangSmith',
name: 'langSmith',
icon: langsmithPNG,
url: 'https://smith.langchain.com',
inputs: [
{
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['langsmithApi']
},
{
label: 'Project Name',
name: 'projectName',
type: 'string',
optional: true,
description: 'If not provided, default will be used',
placeholder: 'default'
},
{
label: 'On/Off',
name: 'status',
type: 'boolean',
optional: true
}
]
},
{
label: 'LangFuse',
name: 'langFuse',
icon: langfuseSVG,
url: 'https://langfuse.com',
inputs: [
{
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['langfuseApi']
},
{
label: 'Release',
name: 'release',
type: 'string',
optional: true,
description: 'The release number/hash of the application to provide analytics grouped by release'
},
{
label: 'On/Off',
name: 'status',
type: 'boolean',
optional: true
}
]
},
{
label: 'Lunary',
name: 'lunary',
icon: lunarySVG,
url: 'https://lunary.ai',
inputs: [
{
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['lunaryApi']
},
{
label: 'On/Off',
name: 'status',
type: 'boolean',
optional: true
}
]
}
]
const AnalyseFlowDialog = ({ show, dialogProps, onCancel }) => {
const portalElement = document.getElementById('portal')
const dispatch = useDispatch()
useNotifier()
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
const [analytic, setAnalytic] = useState({})
const [providerExpanded, setProviderExpanded] = useState({})
const onSave = async () => {
try {
const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, {
analytic: JSON.stringify(analytic)
})
if (saveResp.data) {
enqueueSnackbar({
message: 'Analytic Configuration Saved',
options: {
key: new Date().getTime() + Math.random(),
variant: 'success',
action: (key) => (
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
<IconX />
</Button>
)
}
})
dispatch({ type: SET_CHATFLOW, chatflow: saveResp.data })
}
onCancel()
} catch (error) {
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
enqueueSnackbar({
message: `Failed to save Analytic Configuration: ${errorData}`,
options: {
key: new Date().getTime() + Math.random(),
variant: 'error',
persist: true,
action: (key) => (
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
<IconX />
</Button>
)
}
})
}
}
const setValue = (value, providerName, inputParamName) => {
let newVal = {}
if (!Object.prototype.hasOwnProperty.call(analytic, providerName)) {
newVal = { ...analytic, [providerName]: {} }
} else {
newVal = { ...analytic }
}
newVal[providerName][inputParamName] = value
setAnalytic(newVal)
}
const handleAccordionChange = (providerName) => (event, isExpanded) => {
const accordianProviders = { ...providerExpanded }
accordianProviders[providerName] = isExpanded
setProviderExpanded(accordianProviders)
}
useEffect(() => {
if (dialogProps.chatflow && dialogProps.chatflow.analytic) {
try {
setAnalytic(JSON.parse(dialogProps.chatflow.analytic))
} catch (e) {
setAnalytic({})
console.error(e)
}
}
return () => {
setAnalytic({})
setProviderExpanded({})
}
}, [dialogProps])
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
const component = show ? (
<Dialog
onClose={onCancel}
open={show}
fullWidth
maxWidth='sm'
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
>
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
Analyse Chatflow
</DialogTitle>
<DialogContent>
{analyticProviders.map((provider, index) => (
<Accordion
expanded={providerExpanded[provider.name] || false}
onChange={handleAccordionChange(provider.name)}
disableGutters
key={index}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls={provider.name} id={provider.name}>
<ListItem style={{ padding: 0, margin: 0 }} alignItems='center'>
<ListItemAvatar>
<div
style={{
width: 50,
height: 50,
borderRadius: '50%',
backgroundColor: 'white'
}}
>
<img
style={{
width: '100%',
height: '100%',
padding: 10,
objectFit: 'contain'
}}
alt='AI'
src={provider.icon}
/>
</div>
</ListItemAvatar>
<ListItemText
sx={{ ml: 1 }}
primary={provider.label}
secondary={
<a target='_blank' rel='noreferrer' href={provider.url}>
{provider.url}
</a>
}
/>
{analytic[provider.name] && analytic[provider.name].status && (
<div
style={{
display: 'flex',
flexDirection: 'row',
alignContent: 'center',
alignItems: 'center',
background: '#d8f3dc',
borderRadius: 15,
padding: 5,
paddingLeft: 7,
paddingRight: 7,
marginRight: 10
}}
>
<div
style={{
width: 15,
height: 15,
borderRadius: '50%',
backgroundColor: '#70e000'
}}
/>
<span style={{ color: '#006400', marginLeft: 10 }}>ON</span>
</div>
)}
</ListItem>
</AccordionSummary>
<AccordionDetails>
{provider.inputs.map((inputParam, index) => (
<Box key={index} sx={{ p: 2 }}>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Typography>
{inputParam.label}
{!inputParam.optional && <span style={{ color: 'red' }}>&nbsp;*</span>}
{inputParam.description && (
<TooltipWithParser style={{ marginLeft: 10 }} title={inputParam.description} />
)}
</Typography>
</div>
{providerExpanded[provider.name] && inputParam.type === 'credential' && (
<CredentialInputHandler
data={analytic[provider.name] ? { credential: analytic[provider.name].credentialId } : {}}
inputParam={inputParam}
onSelect={(newValue) => setValue(newValue, provider.name, 'credentialId')}
/>
)}
{providerExpanded[provider.name] && inputParam.type === 'boolean' && (
<SwitchInput
onChange={(newValue) => setValue(newValue, provider.name, inputParam.name)}
value={
analytic[provider.name]
? analytic[provider.name][inputParam.name]
: inputParam.default ?? false
}
/>
)}
{providerExpanded[provider.name] &&
(inputParam.type === 'string' ||
inputParam.type === 'password' ||
inputParam.type === 'number') && (
<Input
inputParam={inputParam}
onChange={(newValue) => setValue(newValue, provider.name, inputParam.name)}
value={
analytic[provider.name]
? analytic[provider.name][inputParam.name]
: inputParam.default ?? ''
}
/>
)}
</Box>
))}
</AccordionDetails>
</Accordion>
))}
</DialogContent>
<DialogActions>
<StyledButton variant='contained' onClick={onSave}>
Save
</StyledButton>
</DialogActions>
</Dialog>
) : null
return createPortal(component, portalElement)
}
AnalyseFlowDialog.propTypes = {
show: PropTypes.bool,
dialogProps: PropTypes.object,
onCancel: PropTypes.func
}
export default AnalyseFlowDialog
@@ -1,7 +1,7 @@
import { createPortal } from 'react-dom' import { createPortal } from 'react-dom'
import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material' import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material'
import useConfirm from 'hooks/useConfirm' import useConfirm from '@/hooks/useConfirm'
import { StyledButton } from 'ui-component/button/StyledButton' import { StyledButton } from '@/ui-component/button/StyledButton'
const ConfirmDialog = () => { const ConfirmDialog = () => {
const { onConfirm, onCancel, confirmState } = useConfirm() const { onConfirm, onCancel, confirmState } = useConfirm()
@@ -10,15 +10,15 @@ import { useTheme } from '@mui/material/styles'
import { LoadingButton } from '@mui/lab' import { LoadingButton } from '@mui/lab'
// Project Import // Project Import
import { StyledButton } from 'ui-component/button/StyledButton' import { StyledButton } from '@/ui-component/button/StyledButton'
import { CodeEditor } from 'ui-component/editor/CodeEditor' import { CodeEditor } from '@/ui-component/editor/CodeEditor'
// Store // Store
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
// API // API
import nodesApi from 'api/nodes' import nodesApi from '@/api/nodes'
import useApi from 'hooks/useApi' import useApi from '@/hooks/useApi'
import './ExpandTextDialog.css' import './ExpandTextDialog.css'
@@ -4,8 +4,8 @@ import { useSelector, useDispatch } from 'react-redux'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Dialog, DialogContent, DialogTitle } from '@mui/material' import { Dialog, DialogContent, DialogTitle } from '@mui/material'
import PerfectScrollbar from 'react-perfect-scrollbar' import PerfectScrollbar from 'react-perfect-scrollbar'
import { JsonEditorInput } from 'ui-component/json/JsonEditor' import { JsonEditorInput } from '@/ui-component/json/JsonEditor'
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
const FormatPromptValuesDialog = ({ show, dialogProps, onChange, onCancel }) => { const FormatPromptValuesDialog = ({ show, dialogProps, onChange, onCancel }) => {
const portalElement = document.getElementById('portal') const portalElement = document.getElementById('portal')
@@ -3,8 +3,8 @@ import { useState } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Dialog, DialogActions, DialogContent, Typography, DialogTitle } from '@mui/material' import { Dialog, DialogActions, DialogContent, Typography, DialogTitle } from '@mui/material'
import { StyledButton } from 'ui-component/button/StyledButton' import { StyledButton } from '@/ui-component/button/StyledButton'
import { Input } from 'ui-component/input/Input' import { Input } from '@/ui-component/input/Input'
const LoginDialog = ({ show, dialogProps, onConfirm }) => { const LoginDialog = ({ show, dialogProps, onConfirm }) => {
const portalElement = document.getElementById('portal') const portalElement = document.getElementById('portal')
@@ -19,19 +19,19 @@ import {
import { IconEraser, IconTrash, IconX } from '@tabler/icons' import { IconEraser, IconTrash, IconX } from '@tabler/icons'
import PerfectScrollbar from 'react-perfect-scrollbar' import PerfectScrollbar from 'react-perfect-scrollbar'
import { BackdropLoader } from 'ui-component/loading/BackdropLoader' import { BackdropLoader } from '@/ui-component/loading/BackdropLoader'
import { StyledButton } from 'ui-component/button/StyledButton' import { StyledButton } from '@/ui-component/button/StyledButton'
import scraperApi from 'api/scraper' import scraperApi from '@/api/scraper'
import useNotifier from 'utils/useNotifier' import useNotifier from '@/utils/useNotifier'
import { import {
HIDE_CANVAS_DIALOG, HIDE_CANVAS_DIALOG,
SHOW_CANVAS_DIALOG, SHOW_CANVAS_DIALOG,
enqueueSnackbar as enqueueSnackbarAction, enqueueSnackbar as enqueueSnackbarAction,
closeSnackbar as closeSnackbarAction closeSnackbar as closeSnackbarAction
} from 'store/actions' } from '@/store/actions'
const ManageScrapedLinksDialog = ({ show, dialogProps, onCancel, onSave }) => { const ManageScrapedLinksDialog = ({ show, dialogProps, onCancel, onSave }) => {
const portalElement = document.getElementById('portal') const portalElement = document.getElementById('portal')
@@ -5,15 +5,15 @@ import PropTypes from 'prop-types'
// Material // Material
import { Dialog, DialogContent, DialogTitle } from '@mui/material' import { Dialog, DialogContent, DialogTitle } from '@mui/material'
import { TableViewOnly } from 'ui-component/table/Table' import { TableViewOnly } from '@/ui-component/table/Table'
// Store // Store
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
import { baseURL } from 'store/constant' import { baseURL } from '@/store/constant'
// API // API
import configApi from 'api/config' import configApi from '@/api/config'
import useApi from 'hooks/useApi' import useApi from '@/hooks/useApi'
const NodeInfoDialog = ({ show, dialogProps, onCancel }) => { const NodeInfoDialog = ({ show, dialogProps, onCancel }) => {
const portalElement = document.getElementById('portal') const portalElement = document.getElementById('portal')
@@ -42,14 +42,14 @@ import ClearIcon from '@mui/icons-material/Clear'
import { styled } from '@mui/material/styles' import { styled } from '@mui/material/styles'
//Project Import //Project Import
import { StyledButton } from 'ui-component/button/StyledButton' import { StyledButton } from '@/ui-component/button/StyledButton'
import { MemoizedReactMarkdown } from 'ui-component/markdown/MemoizedReactMarkdown' import { MemoizedReactMarkdown } from '@/ui-component/markdown/MemoizedReactMarkdown'
import { CodeBlock } from 'ui-component/markdown/CodeBlock' import { CodeBlock } from '@/ui-component/markdown/CodeBlock'
import promptEmptySVG from 'assets/images/prompt_empty.svg' import promptEmptySVG from '@/assets/images/prompt_empty.svg'
import useApi from 'hooks/useApi' import useApi from '@/hooks/useApi'
import promptApi from 'api/prompt' import promptApi from '@/api/prompt'
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
const NewLineToBr = ({ children = '' }) => { const NewLineToBr = ({ children = '' }) => {
return children.split('\n').reduce(function (arr, line) { return children.split('\n').reduce(function (arr, line) {
@@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Button, Dialog, DialogActions, DialogContent, OutlinedInput, DialogTitle } from '@mui/material' import { Button, Dialog, DialogActions, DialogContent, OutlinedInput, DialogTitle } from '@mui/material'
import { StyledButton } from 'ui-component/button/StyledButton' import { StyledButton } from '@/ui-component/button/StyledButton'
const SaveChatflowDialog = ({ show, dialogProps, onCancel, onConfirm }) => { const SaveChatflowDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
const portalElement = document.getElementById('portal') const portalElement = document.getElementById('portal')
@@ -0,0 +1,348 @@
import { createPortal } from 'react-dom'
import { useDispatch } from 'react-redux'
import { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions'
// material-ui
import {
Typography,
Box,
Button,
Dialog,
DialogContent,
DialogTitle,
DialogActions,
FormControl,
ListItem,
ListItemAvatar,
ListItemText,
MenuItem,
Select
} from '@mui/material'
import { IconX } from '@tabler/icons'
// Project import
import CredentialInputHandler from '@/views/canvas/CredentialInputHandler'
import { TooltipWithParser } from '@/ui-component/tooltip/TooltipWithParser'
import { SwitchInput } from '@/ui-component/switch/Switch'
import { Input } from '@/ui-component/input/Input'
import { StyledButton } from '@/ui-component/button/StyledButton'
import { Dropdown } from '@/ui-component/dropdown/Dropdown'
import openAISVG from '@/assets/images/openai.svg'
import assemblyAIPng from '@/assets/images/assemblyai.png'
// store
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
import useNotifier from '@/utils/useNotifier'
// API
import chatflowsApi from '@/api/chatflows'
const speechToTextProviders = {
openAIWhisper: {
label: 'OpenAI Whisper',
name: 'openAIWhisper',
icon: openAISVG,
url: 'https://platform.openai.com/docs/guides/speech-to-text',
inputs: [
{
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['openAIApi']
},
{
label: 'Language',
name: 'language',
type: 'string',
description:
'The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency.',
placeholder: 'en',
optional: true
},
{
label: 'Prompt',
name: 'prompt',
type: 'string',
rows: 4,
description: `An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.`,
optional: true
},
{
label: 'Temperature',
name: 'temperature',
type: 'number',
step: 0.1,
description: `The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`,
optional: true
}
]
},
assemblyAiTranscribe: {
label: 'Assembly AI',
name: 'assemblyAiTranscribe',
icon: assemblyAIPng,
url: 'https://www.assemblyai.com/',
inputs: [
{
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['assemblyAIApi']
}
]
}
}
const SpeechToTextDialog = ({ show, dialogProps, onCancel }) => {
const portalElement = document.getElementById('portal')
const dispatch = useDispatch()
useNotifier()
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
const [speechToText, setSpeechToText] = useState({})
const [selectedProvider, setSelectedProvider] = useState('none')
const onSave = async () => {
const speechToText = setValue(true, selectedProvider, 'status')
try {
const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, {
speechToText: JSON.stringify(speechToText)
})
if (saveResp.data) {
enqueueSnackbar({
message: 'Speech To Text Configuration Saved',
options: {
key: new Date().getTime() + Math.random(),
variant: 'success',
action: (key) => (
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
<IconX />
</Button>
)
}
})
dispatch({ type: SET_CHATFLOW, chatflow: saveResp.data })
}
onCancel()
} catch (error) {
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
enqueueSnackbar({
message: `Failed to save Speech To Text Configuration: ${errorData}`,
options: {
key: new Date().getTime() + Math.random(),
variant: 'error',
persist: true,
action: (key) => (
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
<IconX />
</Button>
)
}
})
}
}
const setValue = (value, providerName, inputParamName) => {
let newVal = {}
if (!Object.prototype.hasOwnProperty.call(speechToText, providerName)) {
newVal = { ...speechToText, [providerName]: {} }
} else {
newVal = { ...speechToText }
}
newVal[providerName][inputParamName] = value
if (inputParamName === 'status' && value === true) {
// ensure that the others are turned off
Object.keys(speechToTextProviders).forEach((key) => {
const provider = speechToTextProviders[key]
if (provider.name !== providerName) {
newVal[provider.name] = { ...speechToText[provider.name], status: false }
}
})
}
setSpeechToText(newVal)
return newVal
}
const handleProviderChange = (event) => {
setSelectedProvider(event.target.value)
}
useEffect(() => {
if (dialogProps.chatflow && dialogProps.chatflow.speechToText) {
try {
const speechToText = JSON.parse(dialogProps.chatflow.speechToText)
let selectedProvider = 'none'
Object.keys(speechToTextProviders).forEach((key) => {
const providerConfig = speechToText[key]
if (providerConfig && providerConfig.status) {
selectedProvider = key
}
})
setSelectedProvider(selectedProvider)
setSpeechToText(speechToText)
} catch (e) {
setSpeechToText({})
setSelectedProvider('none')
console.error(e)
}
}
return () => {
setSpeechToText({})
setSelectedProvider('none')
}
}, [dialogProps])
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
const component = (
<Dialog
onClose={onCancel}
open={show}
fullWidth
maxWidth='sm'
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
>
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
Speech To Text Configuration
</DialogTitle>
<DialogContent>
<Box fullWidth sx={{ my: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
<Typography>Speech To Text Providers</Typography>
<FormControl fullWidth>
<Select value={selectedProvider} onChange={handleProviderChange}>
<MenuItem value='none'>None</MenuItem>
<MenuItem value='openAIWhisper'>OpenAI Whisper</MenuItem>
<MenuItem value='assemblyAiTranscribe'>Assembly AI</MenuItem>
</Select>
</FormControl>
</Box>
{selectedProvider !== 'none' && (
<>
<ListItem style={{ padding: 0, margin: 0 }} alignItems='center'>
<ListItemAvatar>
<div
style={{
width: 50,
height: 50,
borderRadius: '50%',
backgroundColor: 'white'
}}
>
<img
style={{
width: '100%',
height: '100%',
padding: 10,
objectFit: 'contain'
}}
alt='AI'
src={speechToTextProviders[selectedProvider].icon}
/>
</div>
</ListItemAvatar>
<ListItemText
sx={{ ml: 1 }}
primary={speechToTextProviders[selectedProvider].label}
secondary={
<a target='_blank' rel='noreferrer' href={speechToTextProviders[selectedProvider].url}>
{speechToTextProviders[selectedProvider].url}
</a>
}
/>
</ListItem>
{speechToTextProviders[selectedProvider].inputs.map((inputParam, index) => (
<Box key={index} sx={{ p: 2 }}>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Typography>
{inputParam.label}
{!inputParam.optional && <span style={{ color: 'red' }}>&nbsp;*</span>}
{inputParam.description && (
<TooltipWithParser style={{ marginLeft: 10 }} title={inputParam.description} />
)}
</Typography>
</div>
{inputParam.type === 'credential' && (
<CredentialInputHandler
key={speechToText[selectedProvider]?.credentialId}
data={
speechToText[selectedProvider]?.credentialId
? { credential: speechToText[selectedProvider].credentialId }
: {}
}
inputParam={inputParam}
onSelect={(newValue) => setValue(newValue, selectedProvider, 'credentialId')}
/>
)}
{inputParam.type === 'boolean' && (
<SwitchInput
onChange={(newValue) => setValue(newValue, selectedProvider, inputParam.name)}
value={
speechToText[selectedProvider]
? speechToText[selectedProvider][inputParam.name]
: inputParam.default ?? false
}
/>
)}
{(inputParam.type === 'string' || inputParam.type === 'password' || inputParam.type === 'number') && (
<Input
inputParam={inputParam}
onChange={(newValue) => setValue(newValue, selectedProvider, inputParam.name)}
value={
speechToText[selectedProvider]
? speechToText[selectedProvider][inputParam.name]
: inputParam.default ?? ''
}
/>
)}
{inputParam.type === 'options' && (
<Dropdown
name={inputParam.name}
options={inputParam.options}
onSelect={(newValue) => setValue(newValue, selectedProvider, inputParam.name)}
value={
speechToText[selectedProvider]
? speechToText[selectedProvider][inputParam.name]
: inputParam.default ?? 'choose an option'
}
/>
)}
</Box>
))}
</>
)}
</DialogContent>
<DialogActions>
<StyledButton
disabled={selectedProvider !== 'none' && !speechToText[selectedProvider]?.credentialId}
variant='contained'
onClick={onSave}
>
Save
</StyledButton>
</DialogActions>
</Dialog>
)
return createPortal(component, portalElement)
}
SpeechToTextDialog.propTypes = {
show: PropTypes.bool,
dialogProps: PropTypes.object,
onCancel: PropTypes.func
}
export default SpeechToTextDialog
@@ -7,8 +7,8 @@ import PropTypes from 'prop-types'
import { Dialog, DialogContent, DialogTitle } from '@mui/material' import { Dialog, DialogContent, DialogTitle } from '@mui/material'
// store // store
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
import useNotifier from 'utils/useNotifier' import useNotifier from '@/utils/useNotifier'
// Project imports // Project imports
import StarterPrompts from './StarterPrompts' import StarterPrompts from './StarterPrompts'
@@ -28,37 +28,37 @@ import {
import { useTheme } from '@mui/material/styles' import { useTheme } from '@mui/material/styles'
import DatePicker from 'react-datepicker' import DatePicker from 'react-datepicker'
import robotPNG from 'assets/images/robot.png' import robotPNG from '@/assets/images/robot.png'
import userPNG from 'assets/images/account.png' import userPNG from '@/assets/images/account.png'
import msgEmptySVG from 'assets/images/message_empty.svg' import msgEmptySVG from '@/assets/images/message_empty.svg'
import { IconFileExport, IconEraser, IconX, IconDownload } from '@tabler/icons' import { IconFileExport, IconEraser, IconX, IconDownload } from '@tabler/icons'
// Project import // Project import
import { MemoizedReactMarkdown } from 'ui-component/markdown/MemoizedReactMarkdown' import { MemoizedReactMarkdown } from '@/ui-component/markdown/MemoizedReactMarkdown'
import { CodeBlock } from 'ui-component/markdown/CodeBlock' import { CodeBlock } from '@/ui-component/markdown/CodeBlock'
import SourceDocDialog from 'ui-component/dialog/SourceDocDialog' import SourceDocDialog from '@/ui-component/dialog/SourceDocDialog'
import { MultiDropdown } from 'ui-component/dropdown/MultiDropdown' import { MultiDropdown } from '@/ui-component/dropdown/MultiDropdown'
import { StyledButton } from 'ui-component/button/StyledButton' import { StyledButton } from '@/ui-component/button/StyledButton'
import StatsCard from 'ui-component/cards/StatsCard' import StatsCard from '@/ui-component/cards/StatsCard'
import Feedback from 'ui-component/extended/Feedback' import Feedback from '@/ui-component/extended/Feedback'
// store // store
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
// API // API
import chatmessageApi from 'api/chatmessage' import chatmessageApi from '@/api/chatmessage'
import feedbackApi from 'api/feedback' import feedbackApi from '@/api/feedback'
import useApi from 'hooks/useApi' import useApi from '@/hooks/useApi'
import useConfirm from 'hooks/useConfirm' import useConfirm from '@/hooks/useConfirm'
// Utils // Utils
import { getOS, isValidURL, removeDuplicateURL } from 'utils/genericHelper' import { getOS, isValidURL, removeDuplicateURL } from '@/utils/genericHelper'
import useNotifier from 'utils/useNotifier' import useNotifier from '@/utils/useNotifier'
import { baseURL } from 'store/constant' import { baseURL } from '@/store/constant'
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from 'store/actions' import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from '@/store/actions'
import 'views/chatmessage/ChatMessage.css' import '@/views/chatmessage/ChatMessage.css'
import 'react-datepicker/dist/react-datepicker.css' import 'react-datepicker/dist/react-datepicker.css'
const DatePickerCustomInput = forwardRef(function DatePickerCustomInput({ value, onClick }, ref) { const DatePickerCustomInput = forwardRef(function DatePickerCustomInput({ value, onClick }, ref) {
@@ -9,10 +9,10 @@ import { Popper, CircularProgress, TextField, Box, Typography } from '@mui/mater
import { styled } from '@mui/material/styles' import { styled } from '@mui/material/styles'
// API // API
import credentialsApi from 'api/credentials' import credentialsApi from '@/api/credentials'
// const // const
import { baseURL } from 'store/constant' import { baseURL } from '@/store/constant'
const StyledPopper = styled(Popper)({ const StyledPopper = styled(Popper)({
boxShadow: '0px 8px 10px -5px rgb(0 0 0 / 20%), 0px 16px 24px 2px rgb(0 0 0 / 14%), 0px 6px 30px 5px rgb(0 0 0 / 12%)', boxShadow: '0px 8px 10px -5px rgb(0 0 0 / 20%), 0px 16px 24px 2px rgb(0 0 0 / 14%), 0px 6px 30px 5px rgb(0 0 0 / 12%)',
@@ -8,8 +8,8 @@ import { Box, Card, Divider, Grid, Typography } from '@mui/material'
import MuiBreadcrumbs from '@mui/material/Breadcrumbs' import MuiBreadcrumbs from '@mui/material/Breadcrumbs'
// project imports // project imports
import config from 'config' import config from '@/config'
import { gridSpacing } from 'store/constant' import { gridSpacing } from '@/store/constant'
// assets // assets
import { IconTallymark1 } from '@tabler/icons' import { IconTallymark1 } from '@tabler/icons'
@@ -1,5 +1,5 @@
import logo from 'assets/images/flowise_logo.png' import logo from '@/assets/images/flowise_logo.png'
import logoDark from 'assets/images/flowise_logo_dark.png' import logoDark from '@/assets/images/flowise_logo_dark.png'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import { useTheme } from '@mui/material/styles' import { useTheme } from '@mui/material/styles'
import { FormControl, Button } from '@mui/material' import { FormControl, Button } from '@mui/material'
import { IconUpload } from '@tabler/icons' import { IconUpload } from '@tabler/icons'
import { getFileName } from 'utils/genericHelper' import { getFileName } from '@/utils/genericHelper'
export const File = ({ value, fileType, onChange, disabled = false }) => { export const File = ({ value, fileType, onChange, disabled = false }) => {
const theme = useTheme() const theme = useTheme()
@@ -5,7 +5,7 @@ import { IconPlus } from '@tabler/icons'
import { Button } from '@mui/material' import { Button } from '@mui/material'
import DeleteIcon from '@mui/icons-material/Delete' import DeleteIcon from '@mui/icons-material/Delete'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
import { formatDataGridRows } from 'utils/genericHelper' import { formatDataGridRows } from '@/utils/genericHelper'
export const DataGrid = ({ columns, rows, style, disabled = false, hideFooter = false, onChange }) => { export const DataGrid = ({ columns, rows, style, disabled = false, hideFooter = false, onChange }) => {
const [rowValues, setRowValues] = useState(formatDataGridRows(rows) ?? []) const [rowValues, setRowValues] = useState(formatDataGridRows(rows) ?? [])

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