Compare commits

...

29 Commits

Author SHA1 Message Date
Archer
608e58ba41 4.8.13 test (#3107)
* perf: select file

* perf: drop files

* fix: imple mode adapt files
2024-11-09 15:07:24 +08:00
Archer
044b0c57f7 4.8.13 test (#3106)
* perf: select file

* perf: drop files

* perf: env template
2024-11-09 14:46:14 +08:00
a.e.
7d7454ef3b feat: source id prefix env (#3103) 2024-11-09 14:44:10 +08:00
Archer
0d658c0114 fix: plugin select files and ai response check (#3104)
* fix: plugin select files and ai response check

* perf: text editor selector;tool call tip;remove invalid image url;

* perf: select file

* perf: drop files
2024-11-09 14:43:15 +08:00
Archer
d58cf44778 4.8.13 test (#3102)
* fix: loop index;edge parent check

* perf: reference invalid check

* fix: ts
2024-11-08 20:53:58 +08:00
Archer
7537330a3b feat: loop start add index (#3101)
* feat: loop start add index

* update doc
2024-11-08 17:21:19 +08:00
heheer
a7f881fc5e array reference check & node ui (#3100) 2024-11-08 17:19:05 +08:00
Archer
fc7304d3cd 4.8.13 test (#3098)
* perf: loop node refresh

* rename context

* comment

* fix: ts

* perf: push chat log
2024-11-08 16:02:33 +08:00
a.e.
aa50174066 feat: support push chat log (#3093)
* feat: custom uid/metadata

* to: custom info

* fix: chat push latest

* feat: add chat log envs

* refactor: move timer to pushChatLog

* fix: using precise log

---------

Co-authored-by: Finley Ge <m13203533462@163.com>
2024-11-08 15:35:27 +08:00
heheer
5b2cc097b0 loop node dynamic height (#3092)
* loop node dynamic height

* fix

* fix
2024-11-08 12:10:15 +08:00
Archer
7a933f73b6 fix: http tool response (#3097) 2024-11-08 11:56:18 +08:00
Archer
3e5d7d0d7a fix: workflow file upload refresh (#3088) 2024-11-07 15:04:46 +08:00
Archer
d15ec1ae69 4.8.13 test (#3087)
* fix: image expired

* fix: datacard navbar ui

* perf: build action
2024-11-07 14:01:00 +08:00
heheer
3b82ed0aa1 feat: support sub route config (#3071)
* feat: support sub route config

* dockerfile

* fix upload

* delete unused code
2024-11-07 13:53:23 +08:00
Archer
dc95ab1dc1 4.8.13 test (#3085)
* perf: workflow node ui

* chat iframe url
2024-11-07 12:03:21 +08:00
Archer
fa2fbc1ddd perf: workflow context split (#3083)
* perf: workflow context split

* perf: context
2024-11-07 10:05:03 +08:00
heheer
10421d73f4 add dispatch try catch (#3075) 2024-11-07 10:05:03 +08:00
Archer
a9ee6e6a5e feat: View will move when workflow check error;fix: ui refresh error when continuous file upload (#3077)
* fix: plugin output check

* fix: ui refresh error when continuous file upload

* feat: View will move when workflow check error
2024-11-07 10:05:03 +08:00
heheer
0f1932aadc node pluginoutput check (#3074) 2024-11-07 10:05:02 +08:00
Archer
65a39e80b8 feat: iframe code block;perf: workflow selector type (#3076)
* feat: iframe code block

* perf: workflow selector type
2024-11-07 10:05:02 +08:00
heheer
0db0cbf376 feat: support array reference multi-select (#3041)
* feat: support array reference multi-select

* fix build

* fix

* fix loop multi-select

* adjust condition

* fix get value

* array and non-array conversion

* fix plugin input

* merge func
2024-11-07 10:05:02 +08:00
heheer
f4dbe7c021 fix ui (#3065)
* fix ui

* fix
2024-11-07 10:05:02 +08:00
Archer
07b3a0a35d perf: dockerfile proxy (#3067) 2024-11-07 10:05:01 +08:00
Archer
fd49ad1342 Adapt findLast api;perf: markdown zh format. (#3066)
* perf: context code

* fix: adapt findLast api

* perf: commercial plugin run error

* perf: markdown zh format
2024-11-07 10:05:01 +08:00
Finley Ge
f90803c558 pref: slow query of full text search (#3044) 2024-11-07 10:05:01 +08:00
papapatrick
49cd2d7a3c add chatType (#3060) 2024-11-07 10:05:01 +08:00
papapatrick
727bd7144c feat: add chat history time label (#3024)
* feat:add chat and logs time

* feat: add chat history time label

* code perf

* code perf

---------

Co-authored-by: 勤劳上班的卑微小张 <jiazhan.zhang@ggimage.com>
2024-11-07 10:05:01 +08:00
Archer
469858877e New file upload (#3058)
* feat: toolNode aiNode readFileNode adapt new version

* update docker-compose

* update tip

* feat: adapt new file version

* perf: file input

* fix: ts
2024-11-07 10:05:01 +08:00
heheer
7a929db0a5 chore(ui): login page & workflow page (#3046)
* login page & number input & multirow select & llm select

* workflow

* adjust nodes
2024-11-07 10:04:58 +08:00
233 changed files with 5364 additions and 3149 deletions

View File

@@ -90,3 +90,45 @@ jobs:
-t ${Docker_Hub_Tag} \ -t ${Docker_Hub_Tag} \
-t ${Docker_Hub_Latest} \ -t ${Docker_Hub_Latest} \
. .
build-fastgpt-images-child-route:
runs-on: ubuntu-20.04
steps:
# Set tag
- name: Set image name and tag
run: |
if [[ "${{ github.ref_name }}" == "main" ]]; then
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-child-route:latest" >> $GITHUB_ENV
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-child-route:latest" >> $GITHUB_ENV
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-child-route:latest" >> $GITHUB_ENV
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-child-route:latest" >> $GITHUB_ENV
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-child-route:latest" >> $GITHUB_ENV
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-child-route:latest" >> $GITHUB_ENV
else
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-child-route:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-child-route:latest" >> $GITHUB_ENV
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-child-route:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-child-route:latest" >> $GITHUB_ENV
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-child-route:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-child-route:latest" >> $GITHUB_ENV
fi
- name: Build and publish image for main branch or tag push event
env:
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
run: |
docker buildx build \
-f projects/app/Dockerfile \
--platform linux/amd64,linux/arm64 \
--build-arg base_url=fastai \
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
--label "org.opencontainers.image.description=fastgpt image" \
--push \
--cache-from=type=local,src=/tmp/.buildx-cache \
--cache-to=type=local,dest=/tmp/.buildx-cache \
-t ${Git_Tag} \
-t ${Git_Latest} \
-t ${Ali_Tag} \
-t ${Ali_Latest} \
-t ${Docker_Hub_Tag} \
-t ${Docker_Hub_Latest} \
.

View File

@@ -9,6 +9,17 @@ weight: 811
## 更新说明 ## 更新说明
1. 1. 新增 - 数组变量选择支持多选,可以选多个数组或对应的单一数据类型,会自动按选择顺序进行合并。
2. 优化 - 知识库上传文件,优化报错提示 2. 新增 - 文件上传方案调整,节点直接支持接收文件链接,插件自定义变量支持文件上传。
3. 修复 - BI 图表生成无法写入文件 3. 新增 - 对话记录增加时间显示。
4. 新增 - 工作流校验错误时,跳转至错误节点。
5. 新增 - 循环节点增加下标值。
6. 新增 - 部分对话错误提醒增加翻译。
7. 优化 - 合并多个 system 提示词成 1 个,避免部分模型不支持多个 system 提示词。
8. 优化 - 知识库上传文件,优化报错提示。
9. 优化 - 全文检索语句,减少一轮查询。
10. 优化 - 修改 findLast 为 [...array].reverse().find适配旧版浏览器。
11. 优化 - Markdown 组件自动空格,避免分割 url 中的中文。
12. 优化 - 工作流上下文拆分,性能优化。
13. 修复 - Dockerfile pnpm install 支持代理。
14. 修复 - BI 图表生成无法写入文件。

View File

@@ -139,6 +139,8 @@ services:
- OPENAI_BASE_URL=http://oneapi:3000/v1 - OPENAI_BASE_URL=http://oneapi:3000/v1
# AI模型的API Key。这里默认填写了OneAPI的快速默认key测试通后务必及时修改 # AI模型的API Key。这里默认填写了OneAPI的快速默认key测试通后务必及时修改
- CHAT_API_KEY=sk-fastgpt - CHAT_API_KEY=sk-fastgpt
# 是否将图片转成 base64 传递给模型,本地开发和内网环境使用共有模型时候需要设置为 true
- MULTIPLE_DATA_TO_BASE64=false
# 数据库最大连接数 # 数据库最大连接数
- DB_MAX_LINK=30 - DB_MAX_LINK=30
# 登录凭证密钥 # 登录凭证密钥

View File

@@ -97,6 +97,8 @@ services:
- OPENAI_BASE_URL=http://oneapi:3000/v1 - OPENAI_BASE_URL=http://oneapi:3000/v1
# AI模型的API Key。这里默认填写了OneAPI的快速默认key测试通后务必及时修改 # AI模型的API Key。这里默认填写了OneAPI的快速默认key测试通后务必及时修改
- CHAT_API_KEY=sk-fastgpt - CHAT_API_KEY=sk-fastgpt
# 是否将图片转成 base64 传递给模型,本地开发和内网环境使用共有模型时候需要设置为 true
- MULTIPLE_DATA_TO_BASE64=false
# 数据库最大连接数 # 数据库最大连接数
- DB_MAX_LINK=30 - DB_MAX_LINK=30
# 登录凭证密钥 # 登录凭证密钥

View File

@@ -77,6 +77,8 @@ services:
- OPENAI_BASE_URL=http://oneapi:3000/v1 - OPENAI_BASE_URL=http://oneapi:3000/v1
# AI模型的API Key。这里默认填写了OneAPI的快速默认key测试通后务必及时修改 # AI模型的API Key。这里默认填写了OneAPI的快速默认key测试通后务必及时修改
- CHAT_API_KEY=sk-fastgpt - CHAT_API_KEY=sk-fastgpt
# 是否将图片转成 base64 传递给模型,本地开发和内网环境使用共有模型时候需要设置为 true
- MULTIPLE_DATA_TO_BASE64=false
# 数据库最大连接数 # 数据库最大连接数
- DB_MAX_LINK=30 - DB_MAX_LINK=30
# 登录凭证密钥 # 登录凭证密钥

View File

@@ -16,6 +16,8 @@ export const bucketNameMap = {
} }
}; };
export const ReadFileBaseUrl = `${process.env.FE_DOMAIN || ''}/api/common/file/read`; export const ReadFileBaseUrl = `${process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL}/api/common/file/read`;
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx'; export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
export const imageFileType =
'.jpg, .jpeg, .png, .gif, .bmp, .webp, .svg, .tiff, .tif, .ico, .heic, .heif, .avif';

View File

@@ -1,4 +1,7 @@
import { detect } from 'jschardet'; import { detect } from 'jschardet';
import { documentFileType, imageFileType } from './constants';
import { ChatFileTypeEnum } from '../../core/chat/constants';
import { UserChatItemValueItemType } from '../../core/chat/type';
export const formatFileSize = (bytes: number): string => { export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 B'; if (bytes === 0) return '0 B';
@@ -13,3 +16,40 @@ export const formatFileSize = (bytes: number): string => {
export const detectFileEncoding = (buffer: Buffer) => { export const detectFileEncoding = (buffer: Buffer) => {
return detect(buffer.slice(0, 200))?.encoding?.toLocaleLowerCase(); return detect(buffer.slice(0, 200))?.encoding?.toLocaleLowerCase();
}; };
// Url => user upload file type
export const parseUrlToFileType = (url: string): UserChatItemValueItemType['file'] | undefined => {
if (typeof url !== 'string') return;
const parseUrl = new URL(url, 'https://locaohost:3000');
const filename = (() => {
// Old version file url: https://xxx.com/file/read?filename=xxx.pdf
const filenameQuery = parseUrl.searchParams.get('filename');
if (filenameQuery) return filenameQuery;
// Common file https://xxx.com/xxx.pdf?xxxx=xxx
const pathname = parseUrl.pathname;
if (pathname) return pathname.split('/').pop();
})();
if (!filename) return;
const extension = filename.split('.').pop()?.toLowerCase() || '';
if (!extension) return;
if (documentFileType.includes(extension)) {
return {
type: ChatFileTypeEnum.file,
name: filename,
url
};
}
if (imageFileType.includes(extension)) {
return {
type: ChatFileTypeEnum.image,
name: filename,
url
};
}
};

View File

@@ -2,6 +2,7 @@ import dayjs from 'dayjs';
import cronParser from 'cron-parser'; import cronParser from 'cron-parser';
import utc from 'dayjs/plugin/utc'; import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone'; import timezone from 'dayjs/plugin/timezone';
import { i18nT } from '../../../web/i18n/utils';
dayjs.extend(utc); dayjs.extend(utc);
dayjs.extend(timezone); dayjs.extend(timezone);
@@ -23,31 +24,51 @@ export const formatTimeToChatTime = (time: Date) => {
// 如果传入时间小于60秒返回刚刚 // 如果传入时间小于60秒返回刚刚
if (now.diff(target, 'second') < 60) { if (now.diff(target, 'second') < 60) {
return '刚刚'; return i18nT('common:just_now');
} }
// 如果时间是今天,展示几时:几分 // 如果时间是今天,展示几时:几分
//用#占位i18n生效后replace成:
if (now.isSame(target, 'day')) { if (now.isSame(target, 'day')) {
return target.format('HH : mm'); return target.format('HH#mm');
} }
// 如果是昨天,展示昨天 // 如果是昨天,展示昨天
if (now.subtract(1, 'day').isSame(target, 'day')) { if (now.subtract(1, 'day').isSame(target, 'day')) {
return '昨天'; return i18nT('common:yesterday');
}
// 如果是前天,展示前天
if (now.subtract(2, 'day').isSame(target, 'day')) {
return '前天';
} }
// 如果是今年,展示某月某日 // 如果是今年,展示某月某日
if (now.isSame(target, 'year')) { if (now.isSame(target, 'year')) {
return target.format('MM/DD'); return target.format('MM-DD');
} }
// 如果是更久之前,展示某年某月某日 // 如果是更久之前,展示某年某月某日
return target.format('YYYY/M/D'); return target.format('YYYY-M-D');
};
export const formatTimeToChatItemTime = (time: Date) => {
const now = dayjs();
const target = dayjs(time);
const detailTime = target.format('HH#mm');
// 如果时间是今天,展示几时:几分
if (now.isSame(target, 'day')) {
return detailTime;
}
// 如果是昨天,展示昨天+几时:几分
if (now.subtract(1, 'day').isSame(target, 'day')) {
return i18nT('common:yesterday_detail_time');
}
// 如果是今年,展示某月某日+几时:几分
if (now.isSame(target, 'year')) {
return target.format('MM-DD') + ' ' + detailTime;
}
// 如果是更久之前,展示某年某月某日+几时:几分
return target.format('YYYY-M-D') + ' ' + detailTime;
}; };
/* cron time parse */ /* cron time parse */

View File

@@ -207,8 +207,8 @@ export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
]; ];
// Document quote prompt // Document quote prompt
export const Prompt_DocumentQuote = `将 <Reference></Reference> 中的内容作为本次对话的参考: export const Prompt_DocumentQuote = `将 <FilesContent></FilesContent> 中的内容作为本次对话的参考:
<Reference> <FilesContent>
{{quote}} {{quote}}
</Reference> </FilesContent>
`; `;

View File

@@ -14,7 +14,6 @@ import type {
ChatCompletionToolMessageParam ChatCompletionToolMessageParam
} from '../../core/ai/type.d'; } from '../../core/ai/type.d';
import { ChatCompletionRequestMessageRoleEnum } from '../../core/ai/constants'; import { ChatCompletionRequestMessageRoleEnum } from '../../core/ai/constants';
const GPT2Chat = { const GPT2Chat = {
[ChatCompletionRequestMessageRoleEnum.System]: ChatRoleEnum.System, [ChatCompletionRequestMessageRoleEnum.System]: ChatRoleEnum.System,
[ChatCompletionRequestMessageRoleEnum.User]: ChatRoleEnum.Human, [ChatCompletionRequestMessageRoleEnum.User]: ChatRoleEnum.Human,
@@ -61,14 +60,14 @@ export const chats2GPTMessages = ({
return { return {
type: 'image_url', type: 'image_url',
image_url: { image_url: {
url: item.file?.url || '' url: item.file.url
} }
}; };
} else if (item.file?.type === ChatFileTypeEnum.file) { } else if (item.file?.type === ChatFileTypeEnum.file) {
return { return {
type: 'file_url', type: 'file_url',
name: item.file?.name || '', name: item.file?.name || '',
url: item.file?.url || '' url: item.file.url
}; };
} }
} }

View File

@@ -126,6 +126,7 @@ export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatIt
moduleName?: string; moduleName?: string;
ttsBuffer?: Uint8Array; ttsBuffer?: Uint8Array;
responseData?: ChatHistoryItemResType[]; responseData?: ChatHistoryItemResType[];
time?: Date;
} & ChatBoxInputType & } & ChatBoxInputType &
ResponseTagItemType; ResponseTagItemType;

View File

@@ -30,7 +30,8 @@ export const getChatTitleFromChatMessage = (message?: ChatItemType, defaultValue
// Keep the first n and last n characters // Keep the first n and last n characters
export const getHistoryPreview = ( export const getHistoryPreview = (
completeMessages: ChatItemType[], completeMessages: ChatItemType[],
size = 100 size = 100,
useVision = false
): { ): {
obj: `${ChatRoleEnum}`; obj: `${ChatRoleEnum}`;
value: string; value: string;
@@ -48,7 +49,8 @@ export const getHistoryPreview = (
item.value item.value
?.map((item) => { ?.map((item) => {
if (item?.text?.content) return item?.text?.content; if (item?.text?.content) return item?.text?.content;
if (item.file?.type === 'image') return 'Input an image'; if (item.file?.type === 'image' && useVision)
return `![Input an image](${item.file.url.slice(0, 100)}...)`;
return ''; return '';
}) })
.filter(Boolean) .filter(Boolean)

View File

@@ -201,6 +201,7 @@ export enum NodeInputKeyEnum {
nodeHeight = 'nodeHeight', nodeHeight = 'nodeHeight',
// loop start // loop start
loopStartInput = 'loopStartInput', loopStartInput = 'loopStartInput',
loopStartIndex = 'loopStartIndex',
// loop end // loop end
loopEndInput = 'loopEndInput', loopEndInput = 'loopEndInput',
@@ -256,9 +257,9 @@ export enum NodeOutputKeyEnum {
// loop // loop
loopArray = 'loopArray', loopArray = 'loopArray',
// loop start // loop start
loopStartInput = 'loopStartInput', loopStartInput = 'loopStartInput',
loopStartIndex = 'loopStartIndex',
// form input // form input
formInputResult = 'formInputResult' formInputResult = 'formInputResult'
@@ -334,3 +335,21 @@ export enum ContentTypes {
xml = 'xml', xml = 'xml',
raw = 'raw-text' raw = 'raw-text'
} }
export const ArrayTypeMap: Record<WorkflowIOValueTypeEnum, WorkflowIOValueTypeEnum> = {
[WorkflowIOValueTypeEnum.string]: WorkflowIOValueTypeEnum.arrayString,
[WorkflowIOValueTypeEnum.number]: WorkflowIOValueTypeEnum.arrayNumber,
[WorkflowIOValueTypeEnum.boolean]: WorkflowIOValueTypeEnum.arrayBoolean,
[WorkflowIOValueTypeEnum.object]: WorkflowIOValueTypeEnum.arrayObject,
[WorkflowIOValueTypeEnum.arrayString]: WorkflowIOValueTypeEnum.arrayString,
[WorkflowIOValueTypeEnum.arrayNumber]: WorkflowIOValueTypeEnum.arrayNumber,
[WorkflowIOValueTypeEnum.arrayBoolean]: WorkflowIOValueTypeEnum.arrayBoolean,
[WorkflowIOValueTypeEnum.arrayObject]: WorkflowIOValueTypeEnum.arrayObject,
[WorkflowIOValueTypeEnum.chatHistory]: WorkflowIOValueTypeEnum.arrayObject,
[WorkflowIOValueTypeEnum.datasetQuote]: WorkflowIOValueTypeEnum.arrayObject,
[WorkflowIOValueTypeEnum.dynamic]: WorkflowIOValueTypeEnum.arrayObject,
[WorkflowIOValueTypeEnum.selectDataset]: WorkflowIOValueTypeEnum.arrayObject,
[WorkflowIOValueTypeEnum.selectApp]: WorkflowIOValueTypeEnum.arrayObject,
[WorkflowIOValueTypeEnum.arrayAny]: WorkflowIOValueTypeEnum.arrayAny,
[WorkflowIOValueTypeEnum.any]: WorkflowIOValueTypeEnum.arrayAny
};

View File

@@ -27,7 +27,9 @@ export enum FlowNodeInputTypeEnum { // render ui
settingDatasetQuotePrompt = 'settingDatasetQuotePrompt', settingDatasetQuotePrompt = 'settingDatasetQuotePrompt',
hidden = 'hidden', hidden = 'hidden',
custom = 'custom' custom = 'custom',
fileSelect = 'fileSelect'
} }
export const FlowNodeInputMap: Record< export const FlowNodeInputMap: Record<
FlowNodeInputTypeEnum, FlowNodeInputTypeEnum,
@@ -85,6 +87,9 @@ export const FlowNodeInputMap: Record<
}, },
[FlowNodeInputTypeEnum.textarea]: { [FlowNodeInputTypeEnum.textarea]: {
icon: 'core/workflow/inputType/textarea' icon: 'core/workflow/inputType/textarea'
},
[FlowNodeInputTypeEnum.fileSelect]: {
icon: 'core/workflow/inputType/file'
} }
}; };
@@ -137,43 +142,43 @@ export enum FlowNodeTypeEnum {
// node IO value type // node IO value type
export const FlowValueTypeMap = { export const FlowValueTypeMap = {
[WorkflowIOValueTypeEnum.string]: { [WorkflowIOValueTypeEnum.string]: {
label: 'string', label: 'String',
value: WorkflowIOValueTypeEnum.string value: WorkflowIOValueTypeEnum.string
}, },
[WorkflowIOValueTypeEnum.number]: { [WorkflowIOValueTypeEnum.number]: {
label: 'number', label: 'Number',
value: WorkflowIOValueTypeEnum.number value: WorkflowIOValueTypeEnum.number
}, },
[WorkflowIOValueTypeEnum.boolean]: { [WorkflowIOValueTypeEnum.boolean]: {
label: 'boolean', label: 'Boolean',
value: WorkflowIOValueTypeEnum.boolean value: WorkflowIOValueTypeEnum.boolean
}, },
[WorkflowIOValueTypeEnum.object]: { [WorkflowIOValueTypeEnum.object]: {
label: 'object', label: 'Object',
value: WorkflowIOValueTypeEnum.object value: WorkflowIOValueTypeEnum.object
}, },
[WorkflowIOValueTypeEnum.arrayString]: { [WorkflowIOValueTypeEnum.arrayString]: {
label: 'array<string>', label: 'Array<string>',
value: WorkflowIOValueTypeEnum.arrayString value: WorkflowIOValueTypeEnum.arrayString
}, },
[WorkflowIOValueTypeEnum.arrayNumber]: { [WorkflowIOValueTypeEnum.arrayNumber]: {
label: 'array<number>', label: 'Array<number>',
value: WorkflowIOValueTypeEnum.arrayNumber value: WorkflowIOValueTypeEnum.arrayNumber
}, },
[WorkflowIOValueTypeEnum.arrayBoolean]: { [WorkflowIOValueTypeEnum.arrayBoolean]: {
label: 'array<boolean>', label: 'Array<boolean>',
value: WorkflowIOValueTypeEnum.arrayBoolean value: WorkflowIOValueTypeEnum.arrayBoolean
}, },
[WorkflowIOValueTypeEnum.arrayObject]: { [WorkflowIOValueTypeEnum.arrayObject]: {
label: 'array<object>', label: 'Array<object>',
value: WorkflowIOValueTypeEnum.arrayObject value: WorkflowIOValueTypeEnum.arrayObject
}, },
[WorkflowIOValueTypeEnum.arrayAny]: { [WorkflowIOValueTypeEnum.arrayAny]: {
label: 'array', label: 'Array',
value: WorkflowIOValueTypeEnum.arrayAny value: WorkflowIOValueTypeEnum.arrayAny
}, },
[WorkflowIOValueTypeEnum.any]: { [WorkflowIOValueTypeEnum.any]: {
label: 'any', label: 'Any',
value: WorkflowIOValueTypeEnum.any value: WorkflowIOValueTypeEnum.any
}, },
[WorkflowIOValueTypeEnum.chatHistory]: { [WorkflowIOValueTypeEnum.chatHistory]: {

View File

@@ -135,6 +135,9 @@ export type DispatchNodeResponseType = {
extensionResult?: string; extensionResult?: string;
extensionTokens?: number; extensionTokens?: number;
// dataset concat
concatLength?: number;
// cq // cq
cqList?: ClassifyQuestionAgentItemType[]; cqList?: ClassifyQuestionAgentItemType[];
cqResult?: string; cqResult?: string;
@@ -216,5 +219,7 @@ export type AIChatNodeProps = {
[NodeInputKeyEnum.aiChatQuoteTemplate]?: string; [NodeInputKeyEnum.aiChatQuoteTemplate]?: string;
[NodeInputKeyEnum.aiChatQuotePrompt]?: string; [NodeInputKeyEnum.aiChatQuotePrompt]?: string;
[NodeInputKeyEnum.aiChatVision]?: boolean; [NodeInputKeyEnum.aiChatVision]?: boolean;
[NodeInputKeyEnum.stringQuoteText]?: string; [NodeInputKeyEnum.stringQuoteText]?: string;
[NodeInputKeyEnum.fileUrlList]?: string[];
}; };

View File

@@ -5,8 +5,8 @@ import { StoreNodeItemType } from '../type/node';
import { StoreEdgeItemType } from '../type/edge'; import { StoreEdgeItemType } from '../type/edge';
import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type'; import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type';
import { VARIABLE_NODE_ID } from '../constants'; import { VARIABLE_NODE_ID } from '../constants';
import { isReferenceValue } from '../utils'; import { isValidReferenceValueFormat } from '../utils';
import { FlowNodeOutputItemType, ReferenceValueProps } from '../type/io'; import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type'; import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants'; import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
@@ -34,7 +34,7 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number
2. Check that the workflow starts at the interaction node 2. Check that the workflow starts at the interaction node
*/ */
export const getLastInteractiveValue = (histories: ChatItemType[]) => { export const getLastInteractiveValue = (histories: ChatItemType[]) => {
const lastAIMessage = histories.findLast((item) => item.obj === ChatRoleEnum.AI); const lastAIMessage = [...histories].reverse().find((item) => item.obj === ChatRoleEnum.AI);
if (lastAIMessage) { if (lastAIMessage) {
const lastValue = lastAIMessage.value[lastAIMessage.value.length - 1]; const lastValue = lastAIMessage.value[lastAIMessage.value.length - 1];
@@ -225,37 +225,129 @@ export const checkNodeRunStatus = ({
return 'wait'; return 'wait';
}; };
/*
Get the value of the reference variable/node output
1. [string,string]
2. [string,string][]
*/
export const getReferenceVariableValue = ({ export const getReferenceVariableValue = ({
value, value,
nodes, nodes,
variables variables
}: { }: {
value: ReferenceValueProps; value?: ReferenceValueType;
nodes: RuntimeNodeItemType[]; nodes: RuntimeNodeItemType[];
variables: Record<string, any>; variables: Record<string, any>;
}) => { }) => {
const nodeIds = nodes.map((node) => node.nodeId); if (!value) return value;
if (!isReferenceValue(value, nodeIds)) {
return value;
}
const sourceNodeId = value[0];
const outputId = value[1];
if (sourceNodeId === VARIABLE_NODE_ID && outputId) { // handle single reference value
return variables[outputId]; if (isValidReferenceValueFormat(value)) {
const sourceNodeId = value[0];
const outputId = value[1];
if (sourceNodeId === VARIABLE_NODE_ID) {
if (!outputId) return undefined;
return variables[outputId];
}
const node = nodes.find((node) => node.nodeId === sourceNodeId);
if (!node) {
return value;
}
return node.outputs.find((output) => output.id === outputId)?.value;
} }
const node = nodes.find((node) => node.nodeId === sourceNodeId); // handle reference array
if (
Array.isArray(value) &&
value.length > 0 &&
value.every((item) => isValidReferenceValueFormat(item))
) {
const result = value.map<any>((val) => {
return getReferenceVariableValue({
value: val,
nodes,
variables
});
});
if (!node) { return result.flat().filter((item) => item !== undefined);
return undefined;
} }
const outputValue = node.outputs.find((output) => output.id === outputId)?.value; return value;
return outputValue;
}; };
// replace {{$xx.xx$}} variables for text
export function replaceEditorVariable({
text,
nodes,
variables,
runningNode
}: {
text: any;
nodes: RuntimeNodeItemType[];
variables: Record<string, any>; // global variables
runningNode: RuntimeNodeItemType;
}) {
if (typeof text !== 'string') return text;
const globalVariables = Object.keys(variables).map((key) => {
return {
nodeId: VARIABLE_NODE_ID,
id: key,
value: variables[key]
};
});
// Upstream node outputs
const nodeVariables = nodes
.map((node) => {
return node.outputs.map((output) => {
return {
nodeId: node.nodeId,
id: output.id,
value: output.value
};
});
})
.flat();
// Get runningNode inputs(Will be replaced with reference)
const customInputs = runningNode.inputs.flatMap((item) => {
return [
{
id: item.key,
value: getReferenceVariableValue({
value: item.value,
nodes,
variables
}),
nodeId: runningNode.nodeId
}
];
});
const allVariables = [...globalVariables, ...nodeVariables, ...customInputs];
// Replace {{$xxx.xxx$}} to value
for (const key in allVariables) {
const variable = allVariables[key];
const val = variable.value;
const formatVal = (() => {
if (val === undefined) return '';
if (val === null) return 'null';
return typeof val === 'object' ? JSON.stringify(val) : String(val);
})();
const regex = new RegExp(`\\{\\{\\$(${variable.nodeId}\\.${variable.id})\\$\\}\\}`, 'g');
text = text.replace(regex, formatVal);
}
return text || '';
}
export const textAdaptGptResponse = ({ export const textAdaptGptResponse = ({
text, text,
model = '', model = '',

View File

@@ -75,10 +75,17 @@ export const Input_Template_Text_Quote: FlowNodeInputItemType = {
description: i18nT('app:document_quote_tip'), description: i18nT('app:document_quote_tip'),
valueType: WorkflowIOValueTypeEnum.string valueType: WorkflowIOValueTypeEnum.string
}; };
export const Input_Template_File_Link_Prompt: FlowNodeInputItemType = {
key: NodeInputKeyEnum.fileUrlList,
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.input],
label: i18nT('app:file_quote_link'),
debugLabel: i18nT('app:file_quote_link'),
valueType: WorkflowIOValueTypeEnum.arrayString
};
export const Input_Template_File_Link: FlowNodeInputItemType = { export const Input_Template_File_Link: FlowNodeInputItemType = {
key: NodeInputKeyEnum.fileUrlList, key: NodeInputKeyEnum.fileUrlList,
renderTypeList: [FlowNodeInputTypeEnum.reference], renderTypeList: [FlowNodeInputTypeEnum.reference],
required: true,
label: i18nT('app:workflow.user_file_input'), label: i18nT('app:workflow.user_file_input'),
debugLabel: i18nT('app:workflow.user_file_input'), debugLabel: i18nT('app:workflow.user_file_input'),
description: i18nT('app:workflow.user_file_input_desc'), description: i18nT('app:workflow.user_file_input_desc'),
@@ -104,7 +111,7 @@ export const Input_Template_Node_Height: FlowNodeInputItemType = {
renderTypeList: [FlowNodeInputTypeEnum.hidden], renderTypeList: [FlowNodeInputTypeEnum.hidden],
valueType: WorkflowIOValueTypeEnum.number, valueType: WorkflowIOValueTypeEnum.number,
label: '', label: '',
value: 900 value: 600
}; };
export const Input_Template_Stream_MODE: FlowNodeInputItemType = { export const Input_Template_Stream_MODE: FlowNodeInputItemType = {

View File

@@ -17,7 +17,8 @@ import {
Input_Template_History, Input_Template_History,
Input_Template_System_Prompt, Input_Template_System_Prompt,
Input_Template_UserChatInput, Input_Template_UserChatInput,
Input_Template_Text_Quote Input_Template_Text_Quote,
Input_Template_File_Link_Prompt
} from '../../input'; } from '../../input';
import { chatNodeSystemPromptTip, systemPromptTip } from '../../tip'; import { chatNodeSystemPromptTip, systemPromptTip } from '../../tip';
import { getHandleConfig } from '../../utils'; import { getHandleConfig } from '../../utils';
@@ -55,7 +56,7 @@ export const AiChatModule: FlowNodeTemplateType = {
showStatus: true, showStatus: true,
isTool: true, isTool: true,
courseUrl: '/docs/workflow/modules/ai_chat/', courseUrl: '/docs/workflow/modules/ai_chat/',
version: '481', version: '4813',
inputs: [ inputs: [
Input_Template_SettingAiModel, Input_Template_SettingAiModel,
// --- settings modal // --- settings modal
@@ -89,7 +90,7 @@ export const AiChatModule: FlowNodeTemplateType = {
renderTypeList: [FlowNodeInputTypeEnum.hidden], renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '', label: '',
valueType: WorkflowIOValueTypeEnum.boolean, valueType: WorkflowIOValueTypeEnum.boolean,
value: false value: true
}, },
// settings modal --- // settings modal ---
{ {
@@ -100,7 +101,7 @@ export const AiChatModule: FlowNodeTemplateType = {
}, },
Input_Template_History, Input_Template_History,
Input_Template_Dataset_Quote, Input_Template_Dataset_Quote,
Input_Template_Text_Quote, Input_Template_File_Link_Prompt,
{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') } { ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }
], ],

View File

@@ -25,7 +25,7 @@ export const getOneQuoteInputTemplate = ({
}): FlowNodeInputItemType => ({ }): FlowNodeInputItemType => ({
key, key,
renderTypeList: [FlowNodeInputTypeEnum.reference], renderTypeList: [FlowNodeInputTypeEnum.reference],
label: `${i18nT('workflow:quote_num')},{ num: ${index} }`, label: `${i18nT('workflow:quote_num')}-${index}`,
debugLabel: i18nT('workflow:knowledge_base_reference'), debugLabel: i18nT('workflow:knowledge_base_reference'),
canEdit: true, canEdit: true,
valueType: WorkflowIOValueTypeEnum.datasetQuote valueType: WorkflowIOValueTypeEnum.datasetQuote

View File

@@ -1,9 +1,9 @@
import { ReferenceValueProps } from 'core/workflow/type/io'; import { ReferenceItemValueType } from '../../../type/io';
import { VariableConditionEnum } from './constant'; import { VariableConditionEnum } from './constant';
export type IfElseConditionType = 'AND' | 'OR'; export type IfElseConditionType = 'AND' | 'OR';
export type ConditionListItemType = { export type ConditionListItemType = {
variable?: ReferenceValueProps; variable?: ReferenceItemValueType;
condition?: VariableConditionEnum; condition?: VariableConditionEnum;
value?: string; value?: string;
}; };

View File

@@ -1,8 +1,13 @@
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../../node/constant'; import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../../node/constant';
import { FlowNodeTemplateType } from '../../../type/node.d'; import { FlowNodeTemplateType } from '../../../type/node.d';
import { import {
FlowNodeTemplateTypeEnum, FlowNodeTemplateTypeEnum,
NodeInputKeyEnum, NodeInputKeyEnum,
NodeOutputKeyEnum,
WorkflowIOValueTypeEnum WorkflowIOValueTypeEnum
} from '../../../constants'; } from '../../../constants';
import { getHandleConfig } from '../../utils'; import { getHandleConfig } from '../../utils';
@@ -28,7 +33,21 @@ export const LoopStartNode: FlowNodeTemplateType = {
label: '', label: '',
required: true, required: true,
value: '' value: ''
},
{
key: NodeInputKeyEnum.loopStartIndex,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
valueType: WorkflowIOValueTypeEnum.number,
label: i18nT('workflow:Array_element_index')
} }
], ],
outputs: [] outputs: [
{
id: NodeOutputKeyEnum.loopStartIndex,
key: NodeOutputKeyEnum.loopStartIndex,
label: i18nT('workflow:Array_element_index'),
type: FlowNodeOutputTypeEnum.static,
valueType: WorkflowIOValueTypeEnum.number
}
]
}; };

View File

@@ -23,7 +23,7 @@ export const ReadFilesNode: FlowNodeTemplateType = {
name: i18nT('app:workflow.read_files'), name: i18nT('app:workflow.read_files'),
intro: i18nT('app:workflow.read_files_tip'), intro: i18nT('app:workflow.read_files_tip'),
showStatus: true, showStatus: true,
version: '489', version: '4812',
isTool: true, isTool: true,
inputs: [ inputs: [
{ {

View File

@@ -24,17 +24,8 @@ export const TextEditorNode: FlowNodeTemplateType = {
name: i18nT('workflow:text_concatenation'), name: i18nT('workflow:text_concatenation'),
intro: i18nT('workflow:intro_text_concatenation'), intro: i18nT('workflow:intro_text_concatenation'),
courseUrl: '/docs/workflow/modules/text_editor/', courseUrl: '/docs/workflow/modules/text_editor/',
version: '486', version: '4813',
inputs: [ inputs: [
{
...Input_Template_DynamicInput,
description: i18nT('workflow:dynamic_input_description_concat'),
customInputConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: false
}
},
{ {
key: NodeInputKeyEnum.textareaInput, key: NodeInputKeyEnum.textareaInput,
renderTypeList: [FlowNodeInputTypeEnum.textarea], renderTypeList: [FlowNodeInputTypeEnum.textarea],

View File

@@ -20,6 +20,7 @@ import { chatNodeSystemPromptTip, systemPromptTip } from '../tip';
import { LLMModelTypeEnum } from '../../../ai/constants'; import { LLMModelTypeEnum } from '../../../ai/constants';
import { getHandleConfig } from '../utils'; import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils'; import { i18nT } from '../../../../../web/i18n/utils';
import { Input_Template_File_Link_Prompt } from '../input';
export const ToolModule: FlowNodeTemplateType = { export const ToolModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.tools, id: FlowNodeTypeEnum.tools,
@@ -32,7 +33,7 @@ export const ToolModule: FlowNodeTemplateType = {
intro: i18nT('workflow:template.tool_call_intro'), intro: i18nT('workflow:template.tool_call_intro'),
showStatus: true, showStatus: true,
courseUrl: '/docs/workflow/modules/tool/', courseUrl: '/docs/workflow/modules/tool/',
version: '481', version: '4813',
inputs: [ inputs: [
{ {
...Input_Template_SettingAiModel, ...Input_Template_SettingAiModel,
@@ -67,6 +68,7 @@ export const ToolModule: FlowNodeTemplateType = {
placeholder: chatNodeSystemPromptTip placeholder: chatNodeSystemPromptTip
}, },
Input_Template_History, Input_Template_History,
Input_Template_File_Link_Prompt,
Input_Template_UserChatInput Input_Template_UserChatInput
], ],
outputs: [ outputs: [

View File

@@ -1,10 +1,10 @@
import { FlowNodeInputTypeEnum } from '../../../node/constant'; import { FlowNodeInputTypeEnum } from '../../../node/constant';
import { ReferenceValueProps } from '../../..//type/io'; import { ReferenceItemValueType, ReferenceValueType } from '../../..//type/io';
import { WorkflowIOValueTypeEnum } from '../../../constants'; import { WorkflowIOValueTypeEnum } from '../../../constants';
export type TUpdateListItem = { export type TUpdateListItem = {
variable?: ReferenceValueProps; variable?: ReferenceItemValueType;
value: ReferenceValueProps; value?: ReferenceValueType; // input: ['',value], reference: [nodeId,outputId]
valueType?: WorkflowIOValueTypeEnum; valueType?: WorkflowIOValueTypeEnum;
renderType: FlowNodeInputTypeEnum.input | FlowNodeInputTypeEnum.reference; renderType: FlowNodeInputTypeEnum.input | FlowNodeInputTypeEnum.reference;
}; };

View File

@@ -43,6 +43,3 @@ export const WorkflowStart: FlowNodeTemplateType = {
} }
] ]
}; };
export const isWorkflowStartOutput = (key?: string) =>
!!WorkflowStart.outputs.find((output) => output.key === key);

View File

@@ -56,6 +56,11 @@ export type FlowNodeInputItemType = InputComponentPropsType & {
canEdit?: boolean; // dynamic inputs canEdit?: boolean; // dynamic inputs
isPro?: boolean; // Pro version field isPro?: boolean; // Pro version field
isToolOutput?: boolean; isToolOutput?: boolean;
// file
canSelectFile?: boolean;
canSelectImg?: boolean;
maxFiles?: number;
}; };
export type FlowNodeOutputItemType = { export type FlowNodeOutputItemType = {
@@ -75,4 +80,6 @@ export type FlowNodeOutputItemType = {
customFieldConfig?: CustomFieldConfigType; customFieldConfig?: CustomFieldConfigType;
}; };
export type ReferenceValueProps = [string, string | undefined]; export type ReferenceItemValueType = [string, string | undefined];
export type ReferenceArrayValueType = ReferenceItemValueType[];
export type ReferenceValueType = ReferenceItemValueType | ReferenceArrayValueType;

View File

@@ -12,7 +12,12 @@ import {
VARIABLE_NODE_ID, VARIABLE_NODE_ID,
NodeOutputKeyEnum NodeOutputKeyEnum
} from './constants'; } from './constants';
import { FlowNodeInputItemType, FlowNodeOutputItemType, ReferenceValueProps } from './type/io.d'; import {
FlowNodeInputItemType,
FlowNodeOutputItemType,
ReferenceArrayValueType,
ReferenceItemValueType
} from './type/io.d';
import { StoreNodeItemType } from './type/node'; import { StoreNodeItemType } from './type/node';
import type { import type {
VariableItemType, VariableItemType,
@@ -30,8 +35,8 @@ import {
} from '../app/constants'; } from '../app/constants';
import { IfElseResultEnum } from './template/system/ifElse/constant'; import { IfElseResultEnum } from './template/system/ifElse/constant';
import { RuntimeNodeItemType } from './runtime/type'; import { RuntimeNodeItemType } from './runtime/type';
import { getReferenceVariableValue } from './runtime/utils';
import { import {
Input_Template_File_Link,
Input_Template_History, Input_Template_History,
Input_Template_Stream_MODE, Input_Template_Stream_MODE,
Input_Template_UserChatInput Input_Template_UserChatInput
@@ -261,8 +266,10 @@ export const appData2FlowNodeIO = ({
inputs: [ inputs: [
Input_Template_Stream_MODE, Input_Template_Stream_MODE,
Input_Template_History, Input_Template_History,
...(chatConfig?.fileSelectConfig?.canSelectFile || chatConfig?.fileSelectConfig?.canSelectImg
? [Input_Template_File_Link]
: []),
Input_Template_UserChatInput, Input_Template_UserChatInput,
// ...(showFileLink ? [Input_Template_File_Link] : []),
...variableInput ...variableInput
], ],
outputs: [ outputs: [
@@ -298,9 +305,37 @@ export const formatEditorVariablePickerIcon = (
})); }));
}; };
export const isReferenceValue = (value: any, nodeIds: string[]): boolean => { // Check the value is a valid reference value format: [variableId, outputId]
const validIdList = [VARIABLE_NODE_ID, ...nodeIds]; export const isValidReferenceValueFormat = (value: any): value is ReferenceItemValueType => {
return Array.isArray(value) && value.length === 2 && validIdList.includes(value[0]); return Array.isArray(value) && value.length === 2 && typeof value[0] === 'string';
};
/*
Check whether the value([variableId, outputId]) value is a valid reference value:
1. The value must be an array of length 2
2. The first item of the array must be one of VARIABLE_NODE_ID or nodeIds
*/
export const isValidReferenceValue = (
value: any,
nodeIds: string[]
): value is ReferenceItemValueType => {
if (!isValidReferenceValueFormat(value)) return false;
const validIdSet = new Set([VARIABLE_NODE_ID, ...nodeIds]);
return validIdSet.has(value[0]);
};
/*
Check whether the value([variableId, outputId][]) value is a valid reference value array:
1. The value must be an array
2. The array must contain at least one element
3. Each element in the array must be a valid reference value
*/
export const isValidArrayReferenceValue = (
value: any,
nodeIds: string[]
): value is ReferenceArrayValueType => {
if (!Array.isArray(value)) return false;
return value.every((item) => isValidReferenceValue(item, nodeIds));
}; };
export const getElseIFLabel = (i: number) => { export const getElseIFLabel = (i: number) => {
@@ -342,79 +377,6 @@ export const updatePluginInputByVariables = (
); );
}; };
// replace {{$xx.xx$}} variables for text
export function replaceEditorVariable({
text,
nodes,
variables,
runningNode
}: {
text: any;
nodes: RuntimeNodeItemType[];
variables: Record<string, any>; // global variables
runningNode: RuntimeNodeItemType;
}) {
if (typeof text !== 'string') return text;
const globalVariables = Object.keys(variables).map((key) => {
return {
nodeId: VARIABLE_NODE_ID,
id: key,
value: variables[key]
};
});
// Upstream node outputs
const nodeVariables = nodes
.map((node) => {
return node.outputs.map((output) => {
return {
nodeId: node.nodeId,
id: output.id,
value: output.value
};
});
})
.flat();
// Get runningNode inputs(Will be replaced with reference)
const customInputs = runningNode.inputs.flatMap((item) => {
if (Array.isArray(item.value)) {
return [
{
id: item.key,
value: getReferenceVariableValue({
value: item.value as ReferenceValueProps,
nodes,
variables
}),
nodeId: runningNode.nodeId
}
];
}
return [
{
id: item.key,
value: item.value,
nodeId: runningNode.nodeId
}
];
});
const allVariables = [...globalVariables, ...nodeVariables, ...customInputs];
// Replace {{$xxx.xxx$}} to value
for (const key in allVariables) {
const variable = allVariables[key];
const val = variable.value;
const formatVal = typeof val === 'object' ? JSON.stringify(val) : String(val);
const regex = new RegExp(`\\{\\{\\$(${variable.nodeId}\\.${variable.id})\\$\\}\\}`, 'g');
text = text.replace(regex, formatVal);
}
return text || '';
}
/* Get plugin runtime input user query */ /* Get plugin runtime input user query */
export const getPluginRunUserQuery = ({ export const getPluginRunUserQuery = ({
pluginInputs, pluginInputs,

View File

@@ -9,6 +9,7 @@ import type { ReadFileResponse } from '../../../worker/readFile/type';
import axios from 'axios'; import axios from 'axios';
import { addLog } from '../../system/log'; import { addLog } from '../../system/log';
import { batchRun } from '@fastgpt/global/common/fn/utils'; import { batchRun } from '@fastgpt/global/common/fn/utils';
import { addHours } from 'date-fns';
export type readRawTextByLocalFileParams = { export type readRawTextByLocalFileParams = {
teamId: string; teamId: string;
@@ -111,6 +112,7 @@ export const readRawContentByFileBuffer = async ({
type: MongoImageTypeEnum.collectionImage, type: MongoImageTypeEnum.collectionImage,
base64Img: `data:${item.mime};base64,${item.base64}`, base64Img: `data:${item.mime};base64,${item.base64}`,
teamId, teamId,
expiredTime: addHours(new Date(), 1),
metadata: { metadata: {
...metadata, ...metadata,
mime: item.mime mime: item.mime

View File

@@ -7,14 +7,20 @@ import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
1. Commercial plugin: n points per times 1. Commercial plugin: n points per times
2. Other plugin: sum of children points 2. Other plugin: sum of children points
*/ */
export const computedPluginUsage = async ( export const computedPluginUsage = async ({
plugin: PluginRuntimeType, plugin,
childrenUsage: ChatNodeUsageType[] childrenUsage,
) => { error
}: {
plugin: PluginRuntimeType;
childrenUsage: ChatNodeUsageType[];
error?: boolean;
}) => {
const { source } = await splitCombinePluginId(plugin.id); const { source } = await splitCombinePluginId(plugin.id);
// Commercial plugin: n points per times // Commercial plugin: n points per times
if (source === PluginSourceEnum.commercial) { if (source === PluginSourceEnum.commercial) {
if (error) return 0;
return plugin.currentCost ?? 0; return plugin.currentCost ?? 0;
} }

View File

@@ -0,0 +1,155 @@
import { addLog } from '../../common/system/log';
import { MongoChatItem } from './chatItemSchema';
import { MongoChat } from './chatSchema';
import axios from 'axios';
import { AIChatItemType, ChatItemType } from '@fastgpt/global/core/chat/type';
export type Metadata = {
[key: string]: {
label: string;
value: string;
};
};
export const pushChatLog = ({
chatId,
chatItemIdHuman,
chatItemIdAi,
appId,
metadata
}: {
chatId: string;
chatItemIdHuman: string;
chatItemIdAi: string;
appId: string;
metadata?: Metadata;
}) => {
const interval = Number(process.env.CHAT_LOG_INTERVAL);
const url = process.env.CHAT_LOG_URL;
if (interval > 0 && url) {
addLog.info(`[ChatLogPush] push chat log after ${interval}ms`, {
appId,
chatItemIdHuman,
chatItemIdAi
});
setTimeout(() => {
pushChatLogInternal({ chatId, chatItemIdHuman, chatItemIdAi, appId, url, metadata });
}, interval);
}
};
type ChatItem = ChatItemType & {
userGoodFeedback?: string;
userBadFeedback?: string;
chatId: string;
responseData: {
moduleType: string;
runningTime: number; //s
historyPreview: { obj: string; value: string }[];
}[];
time: Date;
};
type ChatLog = {
title: string;
feedback: 'like' | 'dislike' | null;
chatItemId: string;
uid: string;
question: string;
answer: string;
chatId: string;
responseTime: number;
metadata: string;
sourceName: string;
createdAt: number;
sourceId: string;
};
const pushChatLogInternal = async ({
chatId,
chatItemIdHuman,
chatItemIdAi,
appId,
url,
metadata
}: {
chatId: string;
chatItemIdHuman: string;
chatItemIdAi: string;
appId: string;
url: string;
metadata?: Metadata;
}) => {
try {
const [chatItemHuman, chatItemAi] = await Promise.all([
MongoChatItem.findById(chatItemIdHuman).lean(),
MongoChatItem.findById(chatItemIdAi).lean() as Promise<AIChatItemType>
]);
if (!chatItemHuman || !chatItemAi) {
return;
}
const chat = await MongoChat.findOne({ chatId }).lean();
// addLog.warn('ChatLogDebug', chat);
// addLog.warn('ChatLogDebug', { chatItemHuman, chatItemAi });
if (!chat) {
return;
}
const metadataString = JSON.stringify(metadata ?? {});
const uid = chat.outLinkUid || chat.tmbId;
// Pop last two items
const question = chatItemHuman.value[chatItemHuman.value.length - 1]?.text?.content;
const answer = chatItemAi.value[chatItemAi.value.length - 1]?.text?.content;
if (!question || !answer) {
addLog.error('[ChatLogPush] question or answer is empty', {
question: chatItemHuman.value,
answer: chatItemAi.value
});
return;
}
const responseData = chatItemAi.responseData;
const responseTime =
responseData?.reduce((acc, item) => acc + (item?.runningTime ?? 0), 0) || 0;
const sourceIdPrefix = process.env.SOURCE_ID_PREFIX ?? '';
const chatLog: ChatLog = {
title: chat.title,
feedback: (() => {
if (chatItemAi.userGoodFeedback) {
return 'like';
} else if (chatItemAi.userBadFeedback) {
return 'dislike';
} else {
return null;
}
})(),
chatItemId: `${chatItemIdHuman},${chatItemIdAi}`,
uid,
question,
answer,
chatId,
responseTime: responseTime * 1000,
metadata: metadataString,
sourceName: chat.source ?? '-',
// @ts-ignore
createdAt: new Date(chatItemAi.time).getTime(),
sourceId: `${sourceIdPrefix}${appId}`
};
await axios
.post(`${url}/api/chat/push`, chatLog)
.then((res) => {
addLog.info('[ChatLogPush] push success', res.data);
})
.catch((e) => {
addLog.error('[ChatLogPush] push failed', { e, resData: e.response?.data });
});
} catch (e) {
addLog.error('[ChatLogPush] error', e);
}
};

View File

@@ -1,4 +1,9 @@
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d'; import type {
AIChatItemType,
ChatItemType,
UserChatItemType
} from '@fastgpt/global/core/chat/type.d';
import axios from 'axios';
import { MongoApp } from '../app/schema'; import { MongoApp } from '../app/schema';
import { import {
ChatItemValueTypeEnum, ChatItemValueTypeEnum,
@@ -13,6 +18,7 @@ import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { getAppChatConfig, getGuideModule } from '@fastgpt/global/core/workflow/utils'; import { getAppChatConfig, getGuideModule } from '@fastgpt/global/core/workflow/utils';
import { AppChatConfigType } from '@fastgpt/global/core/app/type'; import { AppChatConfigType } from '@fastgpt/global/core/app/type';
import { mergeChatResponseData } from '@fastgpt/global/core/chat/utils'; import { mergeChatResponseData } from '@fastgpt/global/core/chat/utils';
import { pushChatLog } from './pushChatLog';
type Props = { type Props = {
chatId: string; chatId: string;
@@ -67,7 +73,7 @@ export async function saveChat({
}); });
await mongoSessionRun(async (session) => { await mongoSessionRun(async (session) => {
await MongoChatItem.insertMany( const [{ _id: chatItemIdHuman }, { _id: chatItemIdAi }] = await MongoChatItem.insertMany(
content.map((item) => ({ content.map((item) => ({
chatId, chatId,
teamId, teamId,
@@ -105,6 +111,13 @@ export async function saveChat({
upsert: true upsert: true
} }
); );
pushChatLog({
chatId,
chatItemIdHuman: String(chatItemIdHuman),
chatItemIdAi: String(chatItemIdAi),
appId
});
}); });
if (isUpdateUseTime) { if (isUpdateUseTime) {

View File

@@ -109,7 +109,7 @@ export const loadRequestMessages = async ({
} }
return Promise.all( return Promise.all(
messages.map(async (item) => { messages.map(async (item) => {
if (item.type === 'image_url') { if (item.type === 'image_url' && process.env.MULTIPLE_DATA_TO_BASE64 === 'true') {
// Remove url origin // Remove url origin
const imgUrl = (() => { const imgUrl = (() => {
if (origin && item.image_url.url.startsWith(origin)) { if (origin && item.image_url.url.startsWith(origin)) {
@@ -118,38 +118,51 @@ export const loadRequestMessages = async ({
return item.image_url.url; return item.image_url.url;
})(); })();
// If imgUrl is a local path, load image from local, and set url to base64 try {
if (imgUrl.startsWith('/')) { // If imgUrl is a local path, load image from local, and set url to base64
addLog.debug('Load image from local server', { if (imgUrl.startsWith('/')) {
baseUrl: serverRequestBaseUrl, addLog.debug('Load image from local server', {
requestUrl: imgUrl baseUrl: serverRequestBaseUrl,
}); requestUrl: imgUrl
const response = await axios.get(imgUrl, { });
baseURL: serverRequestBaseUrl, const response = await axios.get(imgUrl, {
responseType: 'arraybuffer', baseURL: serverRequestBaseUrl,
proxy: false responseType: 'arraybuffer',
}); proxy: false
const base64 = Buffer.from(response.data, 'binary').toString('base64'); });
const imageType = const base64 = Buffer.from(response.data, 'binary').toString('base64');
getFileContentTypeFromHeader(response.headers['content-type']) || const imageType =
guessBase64ImageType(base64); getFileContentTypeFromHeader(response.headers['content-type']) ||
guessBase64ImageType(base64);
return { return {
...item, ...item,
image_url: { image_url: {
...item.image_url, ...item.image_url,
url: `data:${imageType};base64,${base64}` url: `data:${imageType};base64,${base64}`
} }
}; };
}
// 检查下这个图片是否可以被访问,如果不行的话,则过滤掉
const response = await axios.head(imgUrl, {
timeout: 10000
});
if (response.status < 200 || response.status >= 400) {
addLog.info(`Filter invalid image: ${imgUrl}`);
return;
}
} catch (error) {
return;
} }
} }
return item; return item;
}) })
); ).then((res) => res.filter(Boolean) as ChatCompletionContentPart[]);
}; };
// Split question text and image // Split question text and image
const parseStringWithImages = (input: string): ChatCompletionContentPart[] => { const parseStringWithImages = (input: string): ChatCompletionContentPart[] => {
if (!useVision) { if (!useVision || input.length > 500) {
return [{ type: 'text', text: input || '' }]; return [{ type: 'text', text: input || '' }];
} }
@@ -170,8 +183,8 @@ export const loadRequestMessages = async ({
}); });
}); });
// Too many images or too long text, return text // Too many images return text
if (httpsImages.length > 4 || input.length > 1000) { if (httpsImages.length > 4) {
return [{ type: 'text', text: input || '' }]; return [{ type: 'text', text: input || '' }];
} }
@@ -179,7 +192,7 @@ export const loadRequestMessages = async ({
result.push({ type: 'text', text: input }); result.push({ type: 'text', text: input });
return result; return result;
}; };
// Parse user content(text and img) // Parse user content(text and img) Store history => api messages
const parseUserContent = async (content: string | ChatCompletionContentPart[]) => { const parseUserContent = async (content: string | ChatCompletionContentPart[]) => {
if (typeof content === 'string') { if (typeof content === 'string') {
return loadImageToBase64(parseStringWithImages(content)); return loadImageToBase64(parseStringWithImages(content));

View File

@@ -12,7 +12,7 @@ import {
DatasetDataWithCollectionType, DatasetDataWithCollectionType,
SearchDataResponseItemType SearchDataResponseItemType
} from '@fastgpt/global/core/dataset/type'; } from '@fastgpt/global/core/dataset/type';
import { DatasetColCollectionName, MongoDatasetCollection } from '../collection/schema'; import { MongoDatasetCollection } from '../collection/schema';
import { reRankRecall } from '../../../core/ai/rerank'; import { reRankRecall } from '../../../core/ai/rerank';
import { countPromptTokens } from '../../../common/string/tiktoken/index'; import { countPromptTokens } from '../../../common/string/tiktoken/index';
import { datasetSearchResultConcat } from '@fastgpt/global/core/dataset/search/utils'; import { datasetSearchResultConcat } from '@fastgpt/global/core/dataset/search/utils';
@@ -320,11 +320,13 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
const fullTextRecall = async ({ const fullTextRecall = async ({
query, query,
limit, limit,
filterCollectionIdList filterCollectionIdList,
forbidCollectionIdList
}: { }: {
query: string; query: string;
limit: number; limit: number;
filterCollectionIdList?: string[]; filterCollectionIdList?: string[];
forbidCollectionIdList: string[];
}): Promise<{ }): Promise<{
fullTextRecallResults: SearchDataResponseItemType[]; fullTextRecallResults: SearchDataResponseItemType[];
tokenLen: number; tokenLen: number;
@@ -351,6 +353,13 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
$in: filterCollectionIdList.map((id) => new Types.ObjectId(id)) $in: filterCollectionIdList.map((id) => new Types.ObjectId(id))
} }
} }
: {}),
...(forbidCollectionIdList && forbidCollectionIdList.length > 0
? {
collectionId: {
$nin: forbidCollectionIdList.map((id) => new Types.ObjectId(id))
}
}
: {}) : {})
} }
}, },
@@ -367,31 +376,6 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
{ {
$limit: limit $limit: limit
}, },
{
$lookup: {
from: DatasetColCollectionName,
let: { collectionId: '$collectionId' },
pipeline: [
{
$match: {
$expr: { $eq: ['$_id', '$$collectionId'] },
forbid: { $eq: true } // 匹配被禁用的数据
}
},
{
$project: {
_id: 1 // 只需要_id字段来确认匹配
}
}
],
as: 'collection'
}
},
{
$match: {
collection: { $eq: [] } // 没有 forbid=true 的数据
}
},
{ {
$project: { $project: {
_id: 1, _id: 1,
@@ -509,7 +493,8 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
fullTextRecall({ fullTextRecall({
query, query,
limit: fullTextLimit, limit: fullTextLimit,
filterCollectionIdList filterCollectionIdList,
forbidCollectionIdList
}) })
]); ]);
totalTokens += tokens; totalTokens += tokens;

View File

@@ -28,6 +28,7 @@ import { computedMaxToken, llmCompletionsBodyFormat } from '../../../../ai/utils
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants'; import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type'; import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { i18nT } from '../../../../../../web/i18n/utils';
type FunctionRunResponseType = { type FunctionRunResponseType = {
toolRunResponse: DispatchFlowResponse; toolRunResponse: DispatchFlowResponse;
@@ -549,7 +550,7 @@ async function streamResponse({
} }
if (!textAnswer && functionCalls.length === 0) { if (!textAnswer && functionCalls.length === 0) {
return Promise.reject('LLM api response empty'); return Promise.reject(i18nT('chat:LLM_model_response_empty'));
} }
return { answer: textAnswer, functionCalls }; return { answer: textAnswer, functionCalls };

View File

@@ -25,45 +25,16 @@ import { replaceVariable } from '@fastgpt/global/common/string/tools';
import { getMultiplePrompt, Prompt_Tool_Call } from './constants'; import { getMultiplePrompt, Prompt_Tool_Call } from './constants';
import { filterToolResponseToPreview } from './utils'; import { filterToolResponseToPreview } from './utils';
import { InteractiveNodeResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type'; import { InteractiveNodeResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
import { getFileContentFromLinks, getHistoryFileLinks } from '../../tools/readFiles';
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
import { Prompt_DocumentQuote } from '@fastgpt/global/core/ai/prompt/AIChat';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
type Response = DispatchNodeResultType<{ type Response = DispatchNodeResultType<{
[NodeOutputKeyEnum.answerText]: string; [NodeOutputKeyEnum.answerText]: string;
[DispatchNodeResponseKeyEnum.interactive]?: InteractiveNodeResponseType; [DispatchNodeResponseKeyEnum.interactive]?: InteractiveNodeResponseType;
}>; }>;
/*
Tool call auth add file prompt to question。
Guide the LLM to call tool.
*/
export const toolCallMessagesAdapt = ({
userInput
}: {
userInput: UserChatItemValueItemType[];
}) => {
const files = userInput.filter((item) => item.type === 'file');
if (files.length > 0) {
return userInput.map((item) => {
if (item.type === 'text') {
const filesCount = files.filter((file) => file.file?.type === 'file').length;
const imgCount = files.filter((file) => file.file?.type === 'image').length;
const text = item.text?.content || '';
return {
...item,
text: {
content: getMultiplePrompt({ fileCount: filesCount, imgCount, question: text })
}
};
}
return item;
});
}
return userInput;
};
export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<Response> => { export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<Response> => {
const { const {
node: { nodeId, name, isEntry }, node: { nodeId, name, isEntry },
@@ -71,11 +42,21 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
runtimeEdges, runtimeEdges,
histories, histories,
query, query,
requestOrigin,
params: { model, systemPrompt, userChatInput, history = 6 } chatConfig,
runningAppInfo: { teamId },
params: {
model,
systemPrompt,
userChatInput,
history = 6,
fileUrlList: fileLinks,
aiChatVision
}
} = props; } = props;
const toolModel = getLLMModel(model); const toolModel = getLLMModel(model);
const useVision = aiChatVision && toolModel.vision;
const chatHistories = getHistories(history, histories); const chatHistories = getHistories(history, histories);
const toolNodeIds = filterToolNodeIdByEdges({ nodeId, edges: runtimeEdges }); const toolNodeIds = filterToolNodeIdByEdges({ nodeId, edges: runtimeEdges });
@@ -109,18 +90,43 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
} }
})(); })();
props.node.isEntry = false; props.node.isEntry = false;
const hasReadFilesTool = toolNodes.some(
(item) => item.flowNodeType === FlowNodeTypeEnum.readFiles
);
const globalFiles = chatValue2RuntimePrompt(query).files;
const { documentQuoteText, userFiles } = await getMultiInput({
histories: chatHistories,
requestOrigin,
maxFiles: chatConfig?.fileSelectConfig?.maxFiles || 20,
teamId,
fileLinks,
inputFiles: globalFiles
});
const concatenateSystemPrompt = [
toolModel.defaultSystemChatPrompt,
systemPrompt,
documentQuoteText
? replaceVariable(Prompt_DocumentQuote, {
quote: documentQuoteText
})
: ''
]
.filter(Boolean)
.join('\n\n===---===---===\n\n');
const messages: ChatItemType[] = (() => { const messages: ChatItemType[] = (() => {
const value: ChatItemType[] = [ const value: ChatItemType[] = [
...getSystemPrompt_ChatItemType(toolModel.defaultSystemChatPrompt), ...getSystemPrompt_ChatItemType(concatenateSystemPrompt),
...getSystemPrompt_ChatItemType(systemPrompt),
// Add file input prompt to histories // Add file input prompt to histories
...chatHistories.map((item) => { ...chatHistories.map((item) => {
if (item.obj === ChatRoleEnum.Human) { if (item.obj === ChatRoleEnum.Human) {
return { return {
...item, ...item,
value: toolCallMessagesAdapt({ value: toolCallMessagesAdapt({
userInput: item.value userInput: item.value,
skip: !hasReadFilesTool
}) })
}; };
} }
@@ -129,9 +135,10 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
{ {
obj: ChatRoleEnum.Human, obj: ChatRoleEnum.Human,
value: toolCallMessagesAdapt({ value: toolCallMessagesAdapt({
skip: !hasReadFilesTool,
userInput: runtimePrompt2ChatsValue({ userInput: runtimePrompt2ChatsValue({
text: userChatInput, text: userChatInput,
files: chatValue2RuntimePrompt(query).files files: userFiles
}) })
}) })
} }
@@ -237,7 +244,11 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
childTotalPoints: flatUsages.reduce((sum, item) => sum + item.totalPoints, 0), childTotalPoints: flatUsages.reduce((sum, item) => sum + item.totalPoints, 0),
model: modelName, model: modelName,
query: userChatInput, query: userChatInput,
historyPreview: getHistoryPreview(GPTMessages2Chats(completeMessages, false), 10000), historyPreview: getHistoryPreview(
GPTMessages2Chats(completeMessages, false),
10000,
useVision
),
toolDetail: childToolResponse, toolDetail: childToolResponse,
mergeSignId: nodeId mergeSignId: nodeId
}, },
@@ -253,3 +264,88 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
[DispatchNodeResponseKeyEnum.interactive]: toolWorkflowInteractiveResponse [DispatchNodeResponseKeyEnum.interactive]: toolWorkflowInteractiveResponse
}; };
}; };
const getMultiInput = async ({
histories,
fileLinks,
requestOrigin,
maxFiles,
teamId,
inputFiles
}: {
histories: ChatItemType[];
fileLinks?: string[];
requestOrigin?: string;
maxFiles: number;
teamId: string;
inputFiles: UserChatItemValueItemType['file'][];
}) => {
// Not file quote
if (!fileLinks) {
return {
documentQuoteText: '',
userFiles: inputFiles
};
}
const filesFromHistories = getHistoryFileLinks(histories);
const urls = [...fileLinks, ...filesFromHistories];
if (urls.length === 0) {
return {
documentQuoteText: '',
userFiles: []
};
}
// Get files from histories
const { text } = await getFileContentFromLinks({
// Concat fileUrlList and filesFromHistories; remove not supported files
urls,
requestOrigin,
maxFiles,
teamId
});
return {
documentQuoteText: text,
userFiles: fileLinks.map((url) => parseUrlToFileType(url))
};
};
/*
Tool call auth add file prompt to question。
Guide the LLM to call tool.
*/
const toolCallMessagesAdapt = ({
userInput,
skip
}: {
userInput: UserChatItemValueItemType[];
skip?: boolean;
}) => {
if (skip) return userInput;
const files = userInput.filter((item) => item.type === 'file');
if (files.length > 0) {
return userInput.map((item) => {
if (item.type === 'text') {
const filesCount = files.filter((file) => file.file?.type === 'file').length;
const imgCount = files.filter((file) => file.file?.type === 'image').length;
const text = item.text?.content || '';
return {
...item,
text: {
content: getMultiplePrompt({ fileCount: filesCount, imgCount, question: text })
}
};
}
return item;
});
}
return userInput;
};

View File

@@ -29,6 +29,7 @@ import { WorkflowResponseType } from '../../type';
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants'; import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type'; import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { i18nT } from '../../../../../../web/i18n/utils';
type FunctionCallCompletion = { type FunctionCallCompletion = {
id: string; id: string;
@@ -537,7 +538,7 @@ async function streamResponse({
} }
if (!textAnswer) { if (!textAnswer) {
return Promise.reject('LLM api response empty'); return Promise.reject(i18nT('chat:LLM_model_response_empty'));
} }
return { answer: textAnswer.trim() }; return { answer: textAnswer.trim() };
} }

View File

@@ -28,6 +28,7 @@ import { addLog } from '../../../../../common/system/log';
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants'; import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type'; import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { i18nT } from '../../../../../../web/i18n/utils';
type ToolRunResponseType = { type ToolRunResponseType = {
toolRunResponse: DispatchFlowResponse; toolRunResponse: DispatchFlowResponse;
@@ -268,7 +269,7 @@ export const runToolWithToolChoice = async (
}, },
toolModel toolModel
); );
// console.log(JSON.stringify(requestMessages, null, 2), '==requestBody'); // console.log(JSON.stringify(requestBody, null, 2), '==requestBody');
/* Run llm */ /* Run llm */
const ai = getAIApi({ const ai = getAIApi({
timeout: 480000 timeout: 480000
@@ -656,7 +657,7 @@ async function streamResponse({
} }
if (!textAnswer && toolCalls.length === 0) { if (!textAnswer && toolCalls.length === 0) {
return Promise.reject('LLM api response empty'); return Promise.reject(i18nT('chat:LLM_model_response_empty'));
} }
return { answer: textAnswer, toolCalls }; return { answer: textAnswer, toolCalls };

View File

@@ -21,6 +21,7 @@ export type DispatchToolModuleProps = ModuleDispatchProps<{
[NodeInputKeyEnum.aiChatTemperature]: number; [NodeInputKeyEnum.aiChatTemperature]: number;
[NodeInputKeyEnum.aiChatMaxToken]: number; [NodeInputKeyEnum.aiChatMaxToken]: number;
[NodeInputKeyEnum.aiChatVision]?: boolean; [NodeInputKeyEnum.aiChatVision]?: boolean;
[NodeInputKeyEnum.fileUrlList]?: string[];
}> & { }> & {
messages: ChatCompletionMessageParam[]; messages: ChatCompletionMessageParam[];
toolNodes: ToolNodeItemType[]; toolNodes: ToolNodeItemType[];

View File

@@ -5,11 +5,7 @@ import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils'; import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
import { getAIApi } from '../../../ai/config'; import { getAIApi } from '../../../ai/config';
import type { import type { ChatCompletion, StreamChatType } from '@fastgpt/global/core/ai/type.d';
ChatCompletion,
ChatCompletionMessageParam,
StreamChatType
} from '@fastgpt/global/core/ai/type.d';
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils'; import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { postTextCensor } from '../../../../common/api/requestPlusApi'; import { postTextCensor } from '../../../../common/api/requestPlusApi';
@@ -46,6 +42,9 @@ import { WorkflowResponseType } from '../type';
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time'; import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
import { AiChatQuoteRoleType } from '@fastgpt/global/core/workflow/template/system/aiChat/type'; import { AiChatQuoteRoleType } from '@fastgpt/global/core/workflow/template/system/aiChat/type';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { getFileContentFromLinks, getHistoryFileLinks } from '../tools/readFiles';
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
import { i18nT } from '../../../../../web/i18n/utils';
export type ChatProps = ModuleDispatchProps< export type ChatProps = ModuleDispatchProps<
AIChatNodeProps & { AIChatNodeProps & {
@@ -69,7 +68,9 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
histories, histories,
node: { name }, node: { name },
query, query,
runningAppInfo: { teamId },
workflowStreamResponse, workflowStreamResponse,
chatConfig,
params: { params: {
model, model,
temperature = 0, temperature = 0,
@@ -83,14 +84,12 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
quoteTemplate, quoteTemplate,
quotePrompt, quotePrompt,
aiChatVision, aiChatVision,
stringQuoteText fileUrlList: fileLinks, // node quote file links
stringQuoteText //abandon
} }
} = props; } = props;
const { files: inputFiles } = chatValue2RuntimePrompt(query); const { files: inputFiles } = chatValue2RuntimePrompt(query); // Chat box input files
if (!userChatInput && inputFiles.length === 0) {
return Promise.reject('Question is empty');
}
stream = stream && isResponseAnswerText; stream = stream && isResponseAnswerText;
const chatHistories = getHistories(history, histories); const chatHistories = getHistories(history, histories);
@@ -100,11 +99,26 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
return Promise.reject('The chat model is undefined, you need to select a chat model.'); return Promise.reject('The chat model is undefined, you need to select a chat model.');
} }
const { datasetQuoteText } = await filterDatasetQuote({ const [{ datasetQuoteText }, { documentQuoteText, userFiles }] = await Promise.all([
quoteQA, filterDatasetQuote({
model: modelConstantsData, quoteQA,
quoteTemplate model: modelConstantsData,
}); quoteTemplate
}),
getMultiInput({
histories: chatHistories,
inputFiles,
fileLinks,
stringQuoteText,
requestOrigin,
maxFiles: chatConfig?.fileSelectConfig?.maxFiles || 20,
teamId
})
]);
if (!userChatInput && !documentQuoteText && userFiles.length === 0) {
return Promise.reject(i18nT('chat:AI_input_is_empty'));
}
const [{ filterMessages }] = await Promise.all([ const [{ filterMessages }] = await Promise.all([
getChatMessages({ getChatMessages({
@@ -115,9 +129,9 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
aiChatQuoteRole, aiChatQuoteRole,
datasetQuotePrompt: quotePrompt, datasetQuotePrompt: quotePrompt,
userChatInput, userChatInput,
inputFiles,
systemPrompt, systemPrompt,
stringQuoteText userFiles,
documentQuoteText
}), }),
(() => { (() => {
// censor model and system key // censor model and system key
@@ -132,22 +146,9 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
})() })()
]); ]);
// Get the request messages
const concatMessages = [
...(modelConstantsData.defaultSystemChatPrompt
? [
{
role: ChatCompletionRequestMessageRoleEnum.System,
content: modelConstantsData.defaultSystemChatPrompt
}
]
: []),
...filterMessages
] as ChatCompletionMessageParam[];
const [requestMessages, max_tokens] = await Promise.all([ const [requestMessages, max_tokens] = await Promise.all([
loadRequestMessages({ loadRequestMessages({
messages: concatMessages, messages: filterMessages,
useVision: modelConstantsData.vision && aiChatVision, useVision: modelConstantsData.vision && aiChatVision,
origin: requestOrigin origin: requestOrigin
}), }),
@@ -195,7 +196,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
}); });
if (!answer) { if (!answer) {
throw new Error('LLM model response empty'); return Promise.reject(i18nT('chat:LLM_model_response_empty'));
} }
return { return {
@@ -242,7 +243,11 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
tokens, tokens,
query: `${userChatInput}`, query: `${userChatInput}`,
maxToken: max_tokens, maxToken: max_tokens,
historyPreview: getHistoryPreview(chatCompleteMessages, 10000), historyPreview: getHistoryPreview(
chatCompleteMessages,
10000,
modelConstantsData.vision && aiChatVision
),
contextTotalLen: completeMessages.length contextTotalLen: completeMessages.length
}, },
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [ [DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
@@ -302,7 +307,70 @@ async function filterDatasetQuote({
datasetQuoteText datasetQuoteText
}; };
} }
async function getMultiInput({
histories,
inputFiles,
fileLinks,
stringQuoteText,
requestOrigin,
maxFiles,
teamId
}: {
histories: ChatItemType[];
inputFiles: UserChatItemValueItemType['file'][];
fileLinks?: string[];
stringQuoteText?: string; // file quote
requestOrigin?: string;
maxFiles: number;
teamId: string;
}) {
// 旧版本适配====>
if (stringQuoteText) {
return {
documentQuoteText: stringQuoteText,
userFiles: inputFiles
};
}
// 没有引用文件参考,但是可能用了图片识别
if (!fileLinks) {
return {
documentQuoteText: '',
userFiles: inputFiles
};
}
// 旧版本适配<====
// If fileLinks params is not empty, it means it is a new version, not get the global file.
// Get files from histories
const filesFromHistories = getHistoryFileLinks(histories);
const urls = [...fileLinks, ...filesFromHistories];
if (urls.length === 0) {
return {
documentQuoteText: '',
userFiles: []
};
}
const { text } = await getFileContentFromLinks({
// Concat fileUrlList and filesFromHistories; remove not supported files
urls,
requestOrigin,
maxFiles,
teamId
});
return {
documentQuoteText: text,
userFiles: fileLinks.map((url) => parseUrlToFileType(url))
};
}
async function getChatMessages({ async function getChatMessages({
model,
aiChatQuoteRole, aiChatQuoteRole,
datasetQuotePrompt = '', datasetQuotePrompt = '',
datasetQuoteText, datasetQuoteText,
@@ -310,10 +378,10 @@ async function getChatMessages({
histories = [], histories = [],
systemPrompt, systemPrompt,
userChatInput, userChatInput,
inputFiles, userFiles,
model, documentQuoteText
stringQuoteText
}: { }: {
model: LLMModelItemType;
// dataset quote // dataset quote
aiChatQuoteRole: AiChatQuoteRoleType; // user: replace user prompt; system: replace system prompt aiChatQuoteRole: AiChatQuoteRoleType; // user: replace user prompt; system: replace system prompt
datasetQuotePrompt?: string; datasetQuotePrompt?: string;
@@ -323,10 +391,11 @@ async function getChatMessages({
histories: ChatItemType[]; histories: ChatItemType[];
systemPrompt: string; systemPrompt: string;
userChatInput: string; userChatInput: string;
inputFiles: UserChatItemValueItemType['file'][];
model: LLMModelItemType; userFiles: UserChatItemValueItemType['file'][];
stringQuoteText?: string; // file quote documentQuoteText?: string; // document quote
}) { }) {
// Dataset prompt ====>
// User role or prompt include question // User role or prompt include question
const quoteRole = const quoteRole =
aiChatQuoteRole === 'user' || datasetQuotePrompt.includes('{{question}}') ? 'user' : 'system'; aiChatQuoteRole === 'user' || datasetQuotePrompt.includes('{{question}}') ? 'user' : 'system';
@@ -337,6 +406,7 @@ async function getChatMessages({
? Prompt_userQuotePromptList[0].value ? Prompt_userQuotePromptList[0].value
: Prompt_systemQuotePromptList[0].value; : Prompt_systemQuotePromptList[0].value;
// Reset user input, add dataset quote to user input
const replaceInputValue = const replaceInputValue =
useDatasetQuote && quoteRole === 'user' useDatasetQuote && quoteRole === 'user'
? replaceVariable(datasetQuotePromptTemplate, { ? replaceVariable(datasetQuotePromptTemplate, {
@@ -344,31 +414,33 @@ async function getChatMessages({
question: userChatInput question: userChatInput
}) })
: userChatInput; : userChatInput;
// Dataset prompt <====
const replaceSystemPrompt = // Concat system prompt
const concatenateSystemPrompt = [
model.defaultSystemChatPrompt,
systemPrompt,
useDatasetQuote && quoteRole === 'system' useDatasetQuote && quoteRole === 'system'
? `${systemPrompt ? systemPrompt + '\n\n------\n\n' : ''}${replaceVariable( ? replaceVariable(datasetQuotePromptTemplate, {
datasetQuotePromptTemplate, quote: datasetQuoteText
{ })
quote: datasetQuoteText : '',
} documentQuoteText
)}` ? replaceVariable(Prompt_DocumentQuote, {
: systemPrompt; quote: documentQuoteText
})
: ''
]
.filter(Boolean)
.join('\n\n===---===---===\n\n');
const messages: ChatItemType[] = [ const messages: ChatItemType[] = [
...getSystemPrompt_ChatItemType(replaceSystemPrompt), ...getSystemPrompt_ChatItemType(concatenateSystemPrompt),
...(stringQuoteText // file quote
? getSystemPrompt_ChatItemType(
replaceVariable(Prompt_DocumentQuote, {
quote: stringQuoteText
})
)
: []),
...histories, ...histories,
{ {
obj: ChatRoleEnum.Human, obj: ChatRoleEnum.Human,
value: runtimePrompt2ChatsValue({ value: runtimePrompt2ChatsValue({
files: inputFiles, files: userFiles,
text: replaceInputValue text: replaceInputValue
}) })
} }

View File

@@ -1,17 +1,21 @@
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type'; import type {
DispatchNodeResultType,
ModuleDispatchProps
} from '@fastgpt/global/core/workflow/runtime/type';
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { datasetSearchResultConcat } from '@fastgpt/global/core/dataset/search/utils'; import { datasetSearchResultConcat } from '@fastgpt/global/core/dataset/search/utils';
import { filterSearchResultsByMaxChars } from '../../utils'; import { filterSearchResultsByMaxChars } from '../../utils';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
type DatasetConcatProps = ModuleDispatchProps< type DatasetConcatProps = ModuleDispatchProps<
{ {
[NodeInputKeyEnum.datasetMaxTokens]: number; [NodeInputKeyEnum.datasetMaxTokens]: number;
} & { [key: string]: SearchDataResponseItemType[] } } & { [key: string]: SearchDataResponseItemType[] }
>; >;
type DatasetConcatResponse = { type DatasetConcatResponse = DispatchNodeResultType<{
[NodeOutputKeyEnum.datasetQuoteQA]: SearchDataResponseItemType[]; [NodeOutputKeyEnum.datasetQuoteQA]: SearchDataResponseItemType[];
}; }>;
export async function dispatchDatasetConcat( export async function dispatchDatasetConcat(
props: DatasetConcatProps props: DatasetConcatProps
@@ -30,6 +34,12 @@ export async function dispatchDatasetConcat(
); );
return { return {
[NodeOutputKeyEnum.datasetQuoteQA]: await filterSearchResultsByMaxChars(rrfConcatResults, limit) [NodeOutputKeyEnum.datasetQuoteQA]: await filterSearchResultsByMaxChars(
rrfConcatResults,
limit
),
[DispatchNodeResponseKeyEnum.nodeResponse]: {
concatLength: rrfConcatResults.length
}
}; };
} }

View File

@@ -16,6 +16,7 @@ import { datasetSearchQueryExtension } from '../../../dataset/search/utils';
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type'; import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
import { checkTeamReRankPermission } from '../../../../support/permission/teamLimit'; import { checkTeamReRankPermission } from '../../../../support/permission/teamLimit';
import { MongoDataset } from '../../../dataset/schema'; import { MongoDataset } from '../../../dataset/schema';
import { i18nT } from '../../../../../web/i18n/utils';
type DatasetSearchProps = ModuleDispatchProps<{ type DatasetSearchProps = ModuleDispatchProps<{
[NodeInputKeyEnum.datasetSelectList]: SelectedDatasetType; [NodeInputKeyEnum.datasetSelectList]: SelectedDatasetType;
@@ -56,15 +57,15 @@ export async function dispatchDatasetSearch(
} = props as DatasetSearchProps; } = props as DatasetSearchProps;
if (!Array.isArray(datasets)) { if (!Array.isArray(datasets)) {
return Promise.reject('Quote type error'); return Promise.reject(i18nT('chat:dataset_quote_type error'));
} }
if (datasets.length === 0) { if (datasets.length === 0) {
return Promise.reject('core.chat.error.Select dataset empty'); return Promise.reject(i18nT('common:core.chat.error.Select dataset empty'));
} }
if (!userChatInput) { if (!userChatInput) {
return Promise.reject('core.chat.error.User input empty'); return Promise.reject(i18nT('common:core.chat.error.User input empty'));
} }
// query extension // query extension

View File

@@ -23,7 +23,6 @@ import {
} from '@fastgpt/global/core/workflow/node/constant'; } from '@fastgpt/global/core/workflow/node/constant';
import { getNanoid, replaceVariable } from '@fastgpt/global/common/string/tools'; import { getNanoid, replaceVariable } from '@fastgpt/global/common/string/tools';
import { getSystemTime } from '@fastgpt/global/common/time/timezone'; import { getSystemTime } from '@fastgpt/global/common/time/timezone';
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils';
import { dispatchWorkflowStart } from './init/workflowStart'; import { dispatchWorkflowStart } from './init/workflowStart';
import { dispatchChatCompletion } from './chat/oneapi'; import { dispatchChatCompletion } from './chat/oneapi';
@@ -38,11 +37,12 @@ import { dispatchQueryExtension } from './tools/queryExternsion';
import { dispatchRunPlugin } from './plugin/run'; import { dispatchRunPlugin } from './plugin/run';
import { dispatchPluginInput } from './plugin/runInput'; import { dispatchPluginInput } from './plugin/runInput';
import { dispatchPluginOutput } from './plugin/runOutput'; import { dispatchPluginOutput } from './plugin/runOutput';
import { removeSystemVariable, valueTypeFormat } from './utils'; import { formatHttpError, removeSystemVariable, valueTypeFormat } from './utils';
import { import {
filterWorkflowEdges, filterWorkflowEdges,
checkNodeRunStatus, checkNodeRunStatus,
textAdaptGptResponse textAdaptGptResponse,
replaceEditorVariable
} from '@fastgpt/global/core/workflow/runtime/utils'; } from '@fastgpt/global/core/workflow/runtime/utils';
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type'; import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
import { dispatchRunTools } from './agent/runTool/index'; import { dispatchRunTools } from './agent/runTool/index';
@@ -72,6 +72,7 @@ import { dispatchLoopEnd } from './loop/runLoopEnd';
import { dispatchLoopStart } from './loop/runLoopStart'; import { dispatchLoopStart } from './loop/runLoopStart';
import { dispatchFormInput } from './interactive/formInput'; import { dispatchFormInput } from './interactive/formInput';
import { dispatchToolParams } from './agent/runTool/toolParams'; import { dispatchToolParams } from './agent/runTool/toolParams';
import { responseWrite } from '../../../common/response';
const callbackMap: Record<FlowNodeTypeEnum, Function> = { const callbackMap: Record<FlowNodeTypeEnum, Function> = {
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart, [FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
@@ -386,6 +387,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
node, node,
runtimeEdges runtimeEdges
}); });
const nodeRunResult = await (() => { const nodeRunResult = await (() => {
if (status === 'run') { if (status === 'run') {
nodeRunBeforeHook(node); nodeRunBeforeHook(node);
@@ -481,8 +483,16 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
: {}; : {};
node.inputs.forEach((input) => { node.inputs.forEach((input) => {
// Special input, not format
if (input.key === dynamicInput?.key) return; if (input.key === dynamicInput?.key) return;
// Skip some special key
if (input.key === NodeInputKeyEnum.childrenNodeIdList) {
params[input.key] = input.value;
return;
}
// replace {{xx}} variables // replace {{xx}} variables
let value = replaceVariable(input.value, variables); let value = replaceVariable(input.value, variables);
@@ -505,7 +515,6 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
if (input.canEdit && dynamicInput && params[dynamicInput.key]) { if (input.canEdit && dynamicInput && params[dynamicInput.key]) {
params[dynamicInput.key][input.key] = valueTypeFormat(value, input.valueType); params[dynamicInput.key][input.key] = valueTypeFormat(value, input.valueType);
} }
params[input.key] = valueTypeFormat(value, input.valueType); params[input.key] = valueTypeFormat(value, input.valueType);
}); });
@@ -548,7 +557,21 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
// run module // run module
const dispatchRes: Record<string, any> = await (async () => { const dispatchRes: Record<string, any> = await (async () => {
if (callbackMap[node.flowNodeType]) { if (callbackMap[node.flowNodeType]) {
return callbackMap[node.flowNodeType](dispatchData); try {
return await callbackMap[node.flowNodeType](dispatchData);
} catch (error) {
// Get source handles of outgoing edges
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
const skipHandleIds = targetEdges.map((item) => item.sourceHandle);
// Skip all edges and return error
return {
[DispatchNodeResponseKeyEnum.nodeResponse]: {
error: formatHttpError(error)
},
[DispatchNodeResponseKeyEnum.skipHandleId]: skipHandleIds
};
}
} }
return {}; return {};
})(); })();

View File

@@ -25,7 +25,7 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
user, user,
node: { name } node: { name }
} = props; } = props;
const { loopInputArray = [], childrenNodeIdList } = params; const { loopInputArray = [], childrenNodeIdList = [] } = params;
if (!Array.isArray(loopInputArray)) { if (!Array.isArray(loopInputArray)) {
return Promise.reject('Input value is not an array'); return Promise.reject('Input value is not an array');
@@ -43,23 +43,24 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
let totalPoints = 0; let totalPoints = 0;
let newVariables: Record<string, any> = props.variables; let newVariables: Record<string, any> = props.variables;
for await (const item of loopInputArray) { let index = 0;
for await (const item of loopInputArray.filter(Boolean)) {
runtimeNodes.forEach((node) => { runtimeNodes.forEach((node) => {
if ( if (
childrenNodeIdList.includes(node.nodeId) && childrenNodeIdList.includes(node.nodeId) &&
node.flowNodeType === FlowNodeTypeEnum.loopStart node.flowNodeType === FlowNodeTypeEnum.loopStart
) { ) {
node.isEntry = true; node.isEntry = true;
node.inputs = node.inputs.map((input) => node.inputs.forEach((input) => {
input.key === NodeInputKeyEnum.loopStartInput if (input.key === NodeInputKeyEnum.loopStartInput) {
? { input.value = item;
...input, } else if (input.key === NodeInputKeyEnum.loopStartIndex) {
value: item input.value = index++;
} }
: input });
);
} }
}); });
const response = await dispatchWorkFlow({ const response = await dispatchWorkFlow({
...props, ...props,
runtimeEdges: cloneDeep(runtimeEdges) runtimeEdges: cloneDeep(runtimeEdges)
@@ -69,11 +70,13 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
(res) => res.moduleType === FlowNodeTypeEnum.loopEnd (res) => res.moduleType === FlowNodeTypeEnum.loopEnd
)?.loopOutputValue; )?.loopOutputValue;
// Concat runtime response
outputValueArr.push(loopOutputValue); outputValueArr.push(loopOutputValue);
loopDetail.push(...response.flowResponses); loopDetail.push(...response.flowResponses);
assistantResponses.push(...response.assistantResponses); assistantResponses.push(...response.assistantResponses);
totalPoints += response.flowUsages.reduce((acc, usage) => acc + usage.totalPoints, 0);
totalPoints = response.flowUsages.reduce((acc, usage) => acc + usage.totalPoints, 0); // Concat new variables
newVariables = { newVariables = {
...newVariables, ...newVariables,
...response.newVariables ...response.newVariables

View File

@@ -7,9 +7,11 @@ import {
type Props = ModuleDispatchProps<{ type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.loopStartInput]: any; [NodeInputKeyEnum.loopStartInput]: any;
[NodeInputKeyEnum.loopStartIndex]: number;
}>; }>;
type Response = DispatchNodeResultType<{ type Response = DispatchNodeResultType<{
[NodeOutputKeyEnum.loopStartInput]: any; [NodeOutputKeyEnum.loopStartInput]: any;
[NodeOutputKeyEnum.loopStartIndex]: number;
}>; }>;
export const dispatchLoopStart = async (props: Props): Promise<Response> => { export const dispatchLoopStart = async (props: Props): Promise<Response> => {
@@ -18,6 +20,7 @@ export const dispatchLoopStart = async (props: Props): Promise<Response> => {
[DispatchNodeResponseKeyEnum.nodeResponse]: { [DispatchNodeResponseKeyEnum.nodeResponse]: {
loopInputValue: params.loopStartInput loopInputValue: params.loopStartInput
}, },
[NodeOutputKeyEnum.loopStartInput]: params.loopStartInput [NodeOutputKeyEnum.loopStartInput]: params.loopStartInput,
[NodeOutputKeyEnum.loopStartIndex]: params.loopStartIndex
}; };
}; };

View File

@@ -112,7 +112,11 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
output.moduleLogo = plugin.avatar; output.moduleLogo = plugin.avatar;
} }
const usagePoints = await computedPluginUsage(plugin, flowUsages); const usagePoints = await computedPluginUsage({
plugin,
childrenUsage: flowUsages,
error: !!output?.pluginOutput?.error
});
return { return {
// 嵌套运行时,如果 childApp stream=false实际上不会有任何内容输出给用户所以不需要存储 // 嵌套运行时,如果 childApp stream=false实际上不会有任何内容输出给用户所以不需要存储

View File

@@ -17,12 +17,14 @@ import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/ty
import { authAppByTmbId } from '../../../../support/permission/app/auth'; import { authAppByTmbId } from '../../../../support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { getAppVersionById } from '../../../app/version/controller'; import { getAppVersionById } from '../../../app/version/controller';
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
type Props = ModuleDispatchProps<{ type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.userChatInput]: string; [NodeInputKeyEnum.userChatInput]: string;
[NodeInputKeyEnum.history]?: ChatItemType[] | number; [NodeInputKeyEnum.history]?: ChatItemType[] | number;
[NodeInputKeyEnum.fileUrlList]?: string[]; [NodeInputKeyEnum.fileUrlList]?: string[];
[NodeInputKeyEnum.forbidStream]?: boolean; [NodeInputKeyEnum.forbidStream]?: boolean;
[NodeInputKeyEnum.fileUrlList]?: string[];
}>; }>;
type Response = DispatchNodeResultType<{ type Response = DispatchNodeResultType<{
[NodeOutputKeyEnum.answerText]: string; [NodeOutputKeyEnum.answerText]: string;
@@ -40,8 +42,24 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
variables variables
} = props; } = props;
const { system_forbid_stream = false, userChatInput, history, ...childrenAppVariables } = params; const {
if (!userChatInput) { system_forbid_stream = false,
userChatInput,
history,
fileUrlList,
...childrenAppVariables
} = params;
const { files } = chatValue2RuntimePrompt(query);
const userInputFiles = (() => {
if (fileUrlList) {
return fileUrlList.map((url) => parseUrlToFileType(url));
}
// Adapt version 4.8.13 upgrade
return files;
})();
if (!userChatInput && !userInputFiles) {
return Promise.reject('Input is empty'); return Promise.reject('Input is empty');
} }
if (!appId) { if (!appId) {
@@ -72,7 +90,6 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
} }
const chatHistories = getHistories(history, histories); const chatHistories = getHistories(history, histories);
const { files } = chatValue2RuntimePrompt(query);
// Rewrite children app variables // Rewrite children app variables
const systemVariables = filterSystemVariables(variables); const systemVariables = filterSystemVariables(variables);
@@ -102,7 +119,7 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
histories: chatHistories, histories: chatHistories,
variables: childrenRunVariables, variables: childrenRunVariables,
query: runtimePrompt2ChatsValue({ query: runtimePrompt2ChatsValue({
files, files: userInputFiles,
text: userChatInput text: userChatInput
}), }),
chatConfig chatConfig

View File

@@ -1,4 +1,5 @@
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt'; import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
import { ChatFileTypeEnum } from '@fastgpt/global/core/chat/constants';
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type'; import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
@@ -11,6 +12,26 @@ export const dispatchPluginInput = (props: PluginInputProps) => {
const { params, query } = props; const { params, query } = props;
const { files } = chatValue2RuntimePrompt(query); const { files } = chatValue2RuntimePrompt(query);
/*
对 params 中文件类型数据进行处理
* 插件单独运行时,这里会是一个特殊的数组
* 插件调用的话,这个参数是一个 string[] 不会进行处理
* 硬性要求API 单独调用插件时,要避免这种特殊类型冲突
TODO: 需要 filter max files
*/
for (const key in params) {
const val = params[key];
if (
Array.isArray(val) &&
val.every(
(item) => item.type === ChatFileTypeEnum.file || item.type === ChatFileTypeEnum.image
)
) {
params[key] = val.map((item) => item.url);
}
}
return { return {
...params, ...params,
[DispatchNodeResponseKeyEnum.nodeResponse]: {}, [DispatchNodeResponseKeyEnum.nodeResponse]: {},

View File

@@ -14,10 +14,12 @@ import { SERVICE_LOCAL_HOST } from '../../../../common/system/tools';
import { addLog } from '../../../../common/system/log'; import { addLog } from '../../../../common/system/log';
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type'; import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils'; import {
textAdaptGptResponse,
replaceEditorVariable
} from '@fastgpt/global/core/workflow/runtime/utils';
import { getSystemPluginCb } from '../../../../../plugins/register'; import { getSystemPluginCb } from '../../../../../plugins/register';
import { ContentTypes } from '@fastgpt/global/core/workflow/constants'; import { ContentTypes } from '@fastgpt/global/core/workflow/constants';
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils';
import { uploadFileFromBase64Img } from '../../../../common/file/gridfs/controller'; import { uploadFileFromBase64Img } from '../../../../common/file/gridfs/controller';
import { ReadFileBaseUrl } from '@fastgpt/global/common/file/constants'; import { ReadFileBaseUrl } from '@fastgpt/global/common/file/constants';
import { createFileToken } from '../../../../support/permission/controller'; import { createFileToken } from '../../../../support/permission/controller';
@@ -235,7 +237,9 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
node.outputs node.outputs
.filter( .filter(
(item) => (item) =>
item.key !== NodeOutputKeyEnum.error && item.key !== NodeOutputKeyEnum.httpRawResponse item.id !== NodeOutputKeyEnum.error &&
item.id !== NodeOutputKeyEnum.httpRawResponse &&
item.id !== NodeOutputKeyEnum.addOutputParam
) )
.forEach((item) => { .forEach((item) => {
const key = item.key.startsWith('$') ? item.key : `$.${item.key}`; const key = item.key.startsWith('$') ? item.key : `$.${item.key}`;

View File

@@ -2,16 +2,15 @@ import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runti
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type'; import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type'; import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import { documentFileType } from '@fastgpt/global/common/file/constants';
import axios from 'axios'; import axios from 'axios';
import { serverRequestBaseUrl } from '../../../../common/api/serverRequest'; import { serverRequestBaseUrl } from '../../../../common/api/serverRequest';
import { MongoRawTextBuffer } from '../../../../common/buffer/rawText/schema'; import { MongoRawTextBuffer } from '../../../../common/buffer/rawText/schema';
import { readFromSecondary } from '../../../../common/mongo/utils'; import { readFromSecondary } from '../../../../common/mongo/utils';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { detectFileEncoding } from '@fastgpt/global/common/file/tools'; import { detectFileEncoding, parseUrlToFileType } from '@fastgpt/global/common/file/tools';
import { readRawContentByFileBuffer } from '../../../../common/file/read/utils'; import { readRawContentByFileBuffer } from '../../../../common/file/read/utils';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { UserChatItemValueItemType } from '@fastgpt/global/core/chat/type'; import { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
import { parseFileExtensionFromUrl } from '@fastgpt/global/common/string/tools'; import { parseFileExtensionFromUrl } from '@fastgpt/global/common/string/tools';
type Props = ModuleDispatchProps<{ type Props = ModuleDispatchProps<{
@@ -48,12 +47,41 @@ export const dispatchReadFiles = async (props: Props): Promise<Response> => {
runningAppInfo: { teamId }, runningAppInfo: { teamId },
histories, histories,
chatConfig, chatConfig,
node: { version },
params: { fileUrlList = [] } params: { fileUrlList = [] }
} = props; } = props;
const maxFiles = chatConfig?.fileSelectConfig?.maxFiles || 20; const maxFiles = chatConfig?.fileSelectConfig?.maxFiles || 20;
// Get files from histories // Get files from histories
const filesFromHistories = histories const filesFromHistories = version !== '489' ? [] : getHistoryFileLinks(histories);
const { text, readFilesResult } = await getFileContentFromLinks({
// Concat fileUrlList and filesFromHistories; remove not supported files
urls: [...fileUrlList, ...filesFromHistories],
requestOrigin,
maxFiles,
teamId
});
return {
[NodeOutputKeyEnum.text]: text,
[DispatchNodeResponseKeyEnum.nodeResponse]: {
readFiles: readFilesResult.map((item) => ({
name: item?.filename || '',
url: item?.url || ''
})),
readFilesResult: readFilesResult
.map((item) => item?.nodeResponsePreviewText ?? '')
.join('\n******\n')
},
[DispatchNodeResponseKeyEnum.toolResponses]: {
fileContent: text
}
};
};
export const getHistoryFileLinks = (histories: ChatItemType[]) => {
return histories
.filter((item) => { .filter((item) => {
if (item.obj === ChatRoleEnum.Human) { if (item.obj === ChatRoleEnum.Human) {
return item.value.filter((value) => value.type === 'file'); return item.value.filter((value) => value.type === 'file');
@@ -70,28 +98,38 @@ export const dispatchReadFiles = async (props: Props): Promise<Response> => {
return files; return files;
}) })
.flat(); .flat();
};
// Concat fileUrlList and filesFromHistories; remove not supported files export const getFileContentFromLinks = async ({
const parseUrlList = [...fileUrlList, ...filesFromHistories] urls,
requestOrigin,
maxFiles,
teamId
}: {
urls: string[];
requestOrigin?: string;
maxFiles: number;
teamId: string;
}) => {
const parseUrlList = urls
// Remove invalid urls
.filter((url) => {
if (typeof url !== 'string') return false;
// 检查相对路径
const validPrefixList = ['/', 'http', 'ws'];
if (validPrefixList.some((prefix) => url.startsWith(prefix))) {
return true;
}
return false;
})
// Just get the document type file
.filter((url) => parseUrlToFileType(url)?.type === 'file')
.map((url) => { .map((url) => {
try { try {
// Avoid "/api/xxx" file error.
const origin = requestOrigin ?? 'http://localhost:3000';
// Check is system upload file // Check is system upload file
if (url.startsWith('/') || (requestOrigin && url.startsWith(requestOrigin))) { if (url.startsWith('/') || (requestOrigin && url.startsWith(requestOrigin))) {
// Parse url, get filename query. Keep only documents that can be parsed
const parseUrl = new URL(url, origin);
const filenameQuery = parseUrl.searchParams.get('filename');
// Not document
if (filenameQuery) {
const extensionQuery = filenameQuery.split('.').pop()?.toLowerCase() || '';
if (!documentFileType.includes(extensionQuery)) {
return '';
}
}
// Remove the origin(Make intranet requests directly) // Remove the origin(Make intranet requests directly)
if (requestOrigin && url.startsWith(requestOrigin)) { if (requestOrigin && url.startsWith(requestOrigin)) {
url = url.replace(requestOrigin, ''); url = url.replace(requestOrigin, '');
@@ -123,7 +161,7 @@ export const dispatchReadFiles = async (props: Props): Promise<Response> => {
} }
try { try {
// Get file buffer // Get file buffer data
const response = await axios.get(url, { const response = await axios.get(url, {
baseURL: serverRequestBaseUrl, baseURL: serverRequestBaseUrl,
responseType: 'arraybuffer' responseType: 'arraybuffer'
@@ -197,18 +235,7 @@ export const dispatchReadFiles = async (props: Props): Promise<Response> => {
const text = readFilesResult.map((item) => item?.text ?? '').join('\n******\n'); const text = readFilesResult.map((item) => item?.text ?? '').join('\n******\n');
return { return {
[NodeOutputKeyEnum.text]: text, text,
[DispatchNodeResponseKeyEnum.nodeResponse]: { readFilesResult
readFiles: readFilesResult.map((item) => ({
name: item?.filename || '',
url: item?.url || ''
})),
readFilesResult: readFilesResult
.map((item) => item?.nodeResponsePreviewText ?? '')
.join('\n******\n')
},
[DispatchNodeResponseKeyEnum.toolResponses]: {
fileContent: text
}
}; };
}; };

View File

@@ -4,11 +4,14 @@ import {
SseResponseEventEnum SseResponseEventEnum
} from '@fastgpt/global/core/workflow/runtime/constants'; } from '@fastgpt/global/core/workflow/runtime/constants';
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type'; import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils'; import {
getReferenceVariableValue,
replaceEditorVariable
} from '@fastgpt/global/core/workflow/runtime/utils';
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type'; import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type'; import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
import { removeSystemVariable, valueTypeFormat } from '../utils'; import { removeSystemVariable, valueTypeFormat } from '../utils';
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils'; import { isValidReferenceValue } from '@fastgpt/global/core/workflow/utils';
type Props = ModuleDispatchProps<{ type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.updateList]: TUpdateListItem[]; [NodeInputKeyEnum.updateList]: TUpdateListItem[];
@@ -19,15 +22,24 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
const { params, variables, runtimeNodes, workflowStreamResponse, node } = props; const { params, variables, runtimeNodes, workflowStreamResponse, node } = props;
const { updateList } = params; const { updateList } = params;
const result = updateList.map((item) => { const nodeIds = runtimeNodes.map((node) => node.nodeId);
const varNodeId = item.variable?.[0];
const varKey = item.variable?.[1];
if (!varNodeId || !varKey) { const result = updateList.map((item) => {
const variable = item.variable;
if (!isValidReferenceValue(variable, nodeIds)) {
return null;
}
const varNodeId = variable[0];
const varKey = variable[1];
if (!varKey) {
return null; return null;
} }
const value = (() => { const value = (() => {
// If first item is empty, it means it is a input value
if (!item.value?.[0]) { if (!item.value?.[0]) {
const formatValue = valueTypeFormat(item.value?.[1], item.valueType); const formatValue = valueTypeFormat(item.value?.[1], item.valueType);
@@ -48,6 +60,7 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
} }
})(); })();
// Update node output
// Global variable // Global variable
if (varNodeId === VARIABLE_NODE_ID) { if (varNodeId === VARIABLE_NODE_ID) {
variables[varKey] = value; variables[varKey] = value;
@@ -72,6 +85,7 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
}); });
return { return {
[DispatchNodeResponseKeyEnum.newVariables]: variables,
[DispatchNodeResponseKeyEnum.nodeResponse]: { [DispatchNodeResponseKeyEnum.nodeResponse]: {
updateVarResult: result updateVarResult: result
} }

View File

@@ -1,14 +1,5 @@
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { countPromptTokens } from '../../common/string/tiktoken/index'; import { countPromptTokens } from '../../common/string/tiktoken/index';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import {
getPluginInputsFromStoreNodes,
getPluginRunContent
} from '@fastgpt/global/core/app/plugin/utils';
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { RuntimeUserPromptType, UserChatItemType } from '@fastgpt/global/core/chat/type';
import { runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt';
/* filter search result */ /* filter search result */
export const filterSearchResultsByMaxChars = async ( export const filterSearchResultsByMaxChars = async (

View File

@@ -9,3 +9,12 @@ export const getUserFingerprint = async () => {
export const hasHttps = () => { export const hasHttps = () => {
return window.location.protocol === 'https:'; return window.location.protocol === 'https:';
}; };
export const getWebReqUrl = (url: string = '') => {
if (!url) return '/';
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL;
if (!baseUrl) return url;
if (!url.startsWith('/') || url.startsWith(baseUrl)) return url;
return `${baseUrl}${url}`;
};

View File

@@ -1,9 +1,10 @@
import React from 'react'; import React from 'react';
import { Box, Flex, Image } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import type { ImageProps } from '@chakra-ui/react'; import type { ImageProps } from '@chakra-ui/react';
import { LOGO_ICON } from '@fastgpt/global/common/system/constants'; import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
import MyIcon from '../Icon'; import MyIcon from '../Icon';
import { iconPaths } from '../Icon/constants'; import { iconPaths } from '../Icon/constants';
import MyImage from '../Image/MyImage';
const Avatar = ({ w = '30px', src, ...props }: ImageProps) => { const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
// @ts-ignore // @ts-ignore
@@ -14,7 +15,7 @@ const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
<MyIcon name={src as any} w={w} borderRadius={props.borderRadius} /> <MyIcon name={src as any} w={w} borderRadius={props.borderRadius} />
</Box> </Box>
) : ( ) : (
<Image <MyImage
fallbackSrc={LOGO_ICON} fallbackSrc={LOGO_ICON}
fallbackStrategy={'onError'} fallbackStrategy={'onError'}
objectFit={'contain'} objectFit={'contain'}

View File

@@ -4,6 +4,7 @@ export const iconPaths = {
book: () => import('./icons/book.svg'), book: () => import('./icons/book.svg'),
change: () => import('./icons/change.svg'), change: () => import('./icons/change.svg'),
chatSend: () => import('./icons/chatSend.svg'), chatSend: () => import('./icons/chatSend.svg'),
check: () => import('./icons/check.svg'),
closeSolid: () => import('./icons/closeSolid.svg'), closeSolid: () => import('./icons/closeSolid.svg'),
collectionLight: () => import('./icons/collectionLight.svg'), collectionLight: () => import('./icons/collectionLight.svg'),
collectionSolid: () => import('./icons/collectionSolid.svg'), collectionSolid: () => import('./icons/collectionSolid.svg'),
@@ -59,7 +60,6 @@ export const iconPaths = {
'common/playFill': () => import('./icons/common/playFill.svg'), 'common/playFill': () => import('./icons/common/playFill.svg'),
'common/playLight': () => import('./icons/common/playLight.svg'), 'common/playLight': () => import('./icons/common/playLight.svg'),
'common/publishFill': () => import('./icons/common/publishFill.svg'), 'common/publishFill': () => import('./icons/common/publishFill.svg'),
'common/questionLight': () => import('./icons/common/questionLight.svg'),
'common/refreshLight': () => import('./icons/common/refreshLight.svg'), 'common/refreshLight': () => import('./icons/common/refreshLight.svg'),
'common/resultLight': () => import('./icons/common/resultLight.svg'), 'common/resultLight': () => import('./icons/common/resultLight.svg'),
'common/retryLight': () => import('./icons/common/retryLight.svg'), 'common/retryLight': () => import('./icons/common/retryLight.svg'),
@@ -217,6 +217,7 @@ export const iconPaths = {
'core/workflow/template/FileRead': () => import('./icons/core/workflow/template/FileRead.svg'), 'core/workflow/template/FileRead': () => import('./icons/core/workflow/template/FileRead.svg'),
'core/workflow/template/aiChat': () => import('./icons/core/workflow/template/aiChat.svg'), 'core/workflow/template/aiChat': () => import('./icons/core/workflow/template/aiChat.svg'),
'core/workflow/template/baseChart': () => import('./icons/core/workflow/template/baseChart.svg'), 'core/workflow/template/baseChart': () => import('./icons/core/workflow/template/baseChart.svg'),
'core/workflow/template/bing': () => import('./icons/core/workflow/template/bing.svg'),
'core/workflow/template/codeRun': () => import('./icons/core/workflow/template/codeRun.svg'), 'core/workflow/template/codeRun': () => import('./icons/core/workflow/template/codeRun.svg'),
'core/workflow/template/customFeedback': () => 'core/workflow/template/customFeedback': () =>
import('./icons/core/workflow/template/customFeedback.svg'), import('./icons/core/workflow/template/customFeedback.svg'),
@@ -224,18 +225,16 @@ export const iconPaths = {
import('./icons/core/workflow/template/datasetConcat.svg'), import('./icons/core/workflow/template/datasetConcat.svg'),
'core/workflow/template/datasetSearch': () => 'core/workflow/template/datasetSearch': () =>
import('./icons/core/workflow/template/datasetSearch.svg'), import('./icons/core/workflow/template/datasetSearch.svg'),
'core/workflow/template/datasource': () =>
import('./icons/core/workflow/template/datasource.svg'),
'core/workflow/template/duckduckgo': () => 'core/workflow/template/duckduckgo': () =>
import('./icons/core/workflow/template/duckduckgo.svg'), import('./icons/core/workflow/template/duckduckgo.svg'),
'core/workflow/template/extractJson': () => 'core/workflow/template/extractJson': () =>
import('./icons/core/workflow/template/extractJson.svg'), import('./icons/core/workflow/template/extractJson.svg'),
'core/workflow/template/wiki': () => import('./icons/core/workflow/template/wiki.svg'),
'core/workflow/template/datasource': () =>
import('./icons/core/workflow/template/datasource.svg'),
'core/workflow/template/google': () => import('./icons/core/workflow/template/google.svg'),
'core/workflow/template/bing': () => import('./icons/core/workflow/template/bing.svg'),
'core/workflow/template/fetchUrl': () => import('./icons/core/workflow/template/fetchUrl.svg'), 'core/workflow/template/fetchUrl': () => import('./icons/core/workflow/template/fetchUrl.svg'),
'core/workflow/template/formInput': () => import('./icons/core/workflow/template/formInput.svg'), 'core/workflow/template/formInput': () => import('./icons/core/workflow/template/formInput.svg'),
'core/workflow/template/getTime': () => import('./icons/core/workflow/template/getTime.svg'), 'core/workflow/template/getTime': () => import('./icons/core/workflow/template/getTime.svg'),
'core/workflow/template/google': () => import('./icons/core/workflow/template/google.svg'),
'core/workflow/template/httpRequest': () => 'core/workflow/template/httpRequest': () =>
import('./icons/core/workflow/template/httpRequest.svg'), import('./icons/core/workflow/template/httpRequest.svg'),
'core/workflow/template/ifelse': () => import('./icons/core/workflow/template/ifelse.svg'), 'core/workflow/template/ifelse': () => import('./icons/core/workflow/template/ifelse.svg'),
@@ -271,6 +270,7 @@ export const iconPaths = {
'core/workflow/template/variable': () => import('./icons/core/workflow/template/variable.svg'), 'core/workflow/template/variable': () => import('./icons/core/workflow/template/variable.svg'),
'core/workflow/template/variableUpdate': () => 'core/workflow/template/variableUpdate': () =>
import('./icons/core/workflow/template/variableUpdate.svg'), import('./icons/core/workflow/template/variableUpdate.svg'),
'core/workflow/template/wiki': () => import('./icons/core/workflow/template/wiki.svg'),
'core/workflow/template/workflowStart': () => 'core/workflow/template/workflowStart': () =>
import('./icons/core/workflow/template/workflowStart.svg'), import('./icons/core/workflow/template/workflowStart.svg'),
'core/workflow/touchTable': () => import('./icons/core/workflow/touchTable.svg'), 'core/workflow/touchTable': () => import('./icons/core/workflow/touchTable.svg'),
@@ -280,6 +280,7 @@ export const iconPaths = {
date: () => import('./icons/date.svg'), date: () => import('./icons/date.svg'),
delete: () => import('./icons/delete.svg'), delete: () => import('./icons/delete.svg'),
drag: () => import('./icons/drag.svg'), drag: () => import('./icons/drag.svg'),
edgeAdd: () => import('./icons/edgeAdd.svg'),
edit: () => import('./icons/edit.svg'), edit: () => import('./icons/edit.svg'),
empty: () => import('./icons/empty.svg'), empty: () => import('./icons/empty.svg'),
export: () => import('./icons/export.svg'), export: () => import('./icons/export.svg'),
@@ -302,6 +303,7 @@ export const iconPaths = {
'file/pdf': () => import('./icons/file/pdf.svg'), 'file/pdf': () => import('./icons/file/pdf.svg'),
'file/qaImport': () => import('./icons/file/qaImport.svg'), 'file/qaImport': () => import('./icons/file/qaImport.svg'),
'file/uploadFile': () => import('./icons/file/uploadFile.svg'), 'file/uploadFile': () => import('./icons/file/uploadFile.svg'),
help: () => import('./icons/help.svg'),
history: () => import('./icons/history.svg'), history: () => import('./icons/history.svg'),
infoRounded: () => import('./icons/infoRounded.svg'), infoRounded: () => import('./icons/infoRounded.svg'),
kbTest: () => import('./icons/kbTest.svg'), kbTest: () => import('./icons/kbTest.svg'),
@@ -331,7 +333,6 @@ export const iconPaths = {
save: () => import('./icons/save.svg'), save: () => import('./icons/save.svg'),
stop: () => import('./icons/stop.svg'), stop: () => import('./icons/stop.svg'),
'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'), 'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'),
'support/account/passwordLogin': () => import('./icons/support/account/passwordLogin.svg'),
'support/account/plans': () => import('./icons/support/account/plans.svg'), 'support/account/plans': () => import('./icons/support/account/plans.svg'),
'support/account/promotionLight': () => import('./icons/support/account/promotionLight.svg'), 'support/account/promotionLight': () => import('./icons/support/account/promotionLight.svg'),
'support/bill/extraDatasetsize': () => import('./icons/support/bill/extraDatasetsize.svg'), 'support/bill/extraDatasetsize': () => import('./icons/support/bill/extraDatasetsize.svg'),

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 17" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5587 3.69438C13.9492 3.30386 14.5824 3.30386 14.9729 3.69438C15.3634 4.08491 15.3634 4.71807 14.9729 5.1086L7.63956 12.4419C7.24904 12.8325 6.61587 12.8325 6.22535 12.4419L2.89201 9.1086C2.50149 8.71807 2.50149 8.08491 2.89201 7.69438C3.28254 7.30386 3.9157 7.30386 4.30623 7.69438L6.93245 10.3206L13.5587 3.69438Z"/>
</svg>

After

Width:  |  Height:  |  Size: 452 B

View File

@@ -1 +1,4 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1704259732773" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4183" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M319.20128 974.56128L348.16 1003.52l655.36-655.36-28.95872-28.95872-655.36 655.36zM675.84 1003.52l327.68-327.68-28.95872-28.95872-327.68 327.68L675.84 1003.52z" fill="#000000" p-id="4184"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="9" height="9" viewBox="0 0 9 9" fill="none">
<path d="M0.950928 8.44922L8.32385 1.07629" stroke="#8A95A7" stroke-linecap="round"/>
<path d="M4.3418 8.46167L8.32373 4.47974" stroke="#8A95A7" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 534 B

After

Width:  |  Height:  |  Size: 272 B

View File

@@ -1,6 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" > <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path <path d="M4.67391 1.54593C4.67391 1.4286 4.5788 1.3335 4.46148 1.3335C2.77062 1.3335 1.3999 2.70421 1.3999 4.39507V11.6053C1.3999 13.2961 2.77062 14.6668 4.46148 14.6668H11.6717C13.3625 14.6668 14.7332 13.2961 14.7332 11.6053V4.39507C14.7332 2.70421 13.3625 1.3335 11.6717 1.3335H10.1345C9.76634 1.3335 9.46786 1.63197 9.46786 2.00016C9.46786 2.36835 9.76634 2.66683 10.1345 2.66683H11.6717C12.6261 2.66683 13.3999 3.44059 13.3999 4.39507V11.6053C13.3999 12.5597 12.6261 13.3335 11.6717 13.3335H4.46148C3.507 13.3335 2.73324 12.5597 2.73324 11.6053V4.39507C2.73324 3.44059 3.507 2.66683 4.46148 2.66683C4.5788 2.66683 4.67391 2.57172 4.67391 2.4544V1.54593Z"/>
d="M4.25845 0.738983C4.25845 0.606996 4.15146 0.5 4.01947 0.5C2.11725 0.5 0.575195 2.04205 0.575195 3.94428V12.0557C0.575195 13.9579 2.11725 15.5 4.01947 15.5H12.1309C14.0331 15.5 15.5752 13.9579 15.5752 12.0557V3.94428C15.5752 2.04205 14.0331 0.5 12.1309 0.5H10.4017C9.98744 0.5 9.65166 0.835786 9.65166 1.25C9.65166 1.66421 9.98744 2 10.4017 2H12.1309C13.2047 2 14.0752 2.87048 14.0752 3.94428V12.0557C14.0752 13.1295 13.2047 14 12.1309 14H4.01947C2.94568 14 2.0752 13.1295 2.0752 12.0557V3.94428C2.0752 2.87048 2.94568 2 4.01947 2C4.15146 2 4.25845 1.893 4.25845 1.76102V0.738983Z" /> <path d="M7.44944 4.75954C7.34354 4.01799 7.12171 3.55235 6.74882 3.28913C5.8613 2.66266 5.20252 2.66685 5.10562 2.66747L5.0992 2.6675H4.43253V1.33416H5.0992C5.33393 1.33416 6.30657 1.34492 7.51773 2.19984C8.33645 2.77775 8.6439 3.69241 8.76938 4.57104C8.87172 5.28764 8.86601 6.09307 8.86075 6.83513C8.85961 6.99546 8.8585 7.15284 8.8585 7.30571C8.8585 7.73778 8.84925 8.14363 8.83559 8.50744L9.39664 7.94639C9.65699 7.68604 10.0791 7.68604 10.3395 7.94639C10.5998 8.20674 10.5998 8.62885 10.3395 8.8892L8.58233 10.6463C8.44087 10.7878 8.25166 10.8524 8.06659 10.8401C7.88151 10.8524 7.6923 10.7878 7.55084 10.6463L5.79372 8.8892C5.53337 8.62885 5.53337 8.20674 5.79372 7.94639C6.05407 7.68604 6.47618 7.68604 6.73653 7.94639L7.41122 8.62109L7.49304 8.7029C7.5115 8.29493 7.52517 7.81934 7.52517 7.30571C7.52517 7.1294 7.52626 6.956 7.52734 6.78569C7.53201 6.04784 7.53631 5.36788 7.44944 4.75954Z"/>
<path
d="M7.38092 4.3543C7.26179 3.52006 7.01223 2.99621 6.59273 2.70009C5.59427 1.99531 4.85314 2.00002 4.74413 2.00072L4.7369 2.00075H3.9869V0.500749H4.7369C5.00097 0.500749 6.0952 0.512848 7.45775 1.47464C8.37881 2.12479 8.7247 3.15378 8.86586 4.14224C8.98099 4.94841 8.97457 5.85452 8.96865 6.68934C8.96737 6.86971 8.96612 7.04676 8.96612 7.21874C8.96612 7.70483 8.95571 8.16141 8.94035 8.57069L9.57153 7.93951C9.86442 7.64661 10.3393 7.64661 10.6322 7.93951C10.9251 8.2324 10.9251 8.70727 10.6322 9.00017L8.65543 10.9769C8.49629 11.1361 8.28342 11.2087 8.07521 11.1949C7.86701 11.2087 7.65414 11.1361 7.495 10.9769L5.51824 9.00017C5.22535 8.70727 5.22535 8.2324 5.51824 7.93951C5.81113 7.64661 6.28601 7.64661 6.5789 7.93951L7.33793 8.69854L7.42997 8.79058C7.45074 8.33162 7.46612 7.79657 7.46612 7.21874C7.46612 7.02039 7.46735 6.82532 7.46856 6.63372C7.47382 5.80363 7.47866 5.03868 7.38092 4.3543Z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M8.99997 2.196C5.24222 2.196 2.19596 5.24225 2.19596 9C2.19596 12.7577 5.24222 15.804 8.99997 15.804C12.7577 15.804 15.804 12.7577 15.804 9C15.804 5.24225 12.7577 2.196 8.99997 2.196ZM0.529297 9C0.529297 4.32178 4.32174 0.529331 8.99997 0.529331C13.6782 0.529331 17.4706 4.32178 17.4706 9C17.4706 13.6782 13.6782 17.4707 8.99997 17.4707C4.32174 17.4707 0.529297 13.6782 0.529297 9ZM9.18533 6.03224C8.846 5.97403 8.49702 6.0378 8.20019 6.21224C7.90337 6.38669 7.67786 6.66056 7.56361 6.98534C7.41089 7.41949 6.93512 7.64764 6.50097 7.49491C6.06681 7.34218 5.83866 6.86642 5.99139 6.43226C6.23625 5.73619 6.71956 5.14923 7.35572 4.77536C7.99188 4.40148 8.73983 4.26481 9.4671 4.38956C10.1944 4.5143 10.854 4.89241 11.3292 5.45692C11.8043 6.0213 12.0644 6.73558 12.0634 7.4733C12.063 8.67899 11.1697 9.46897 10.5467 9.88431C10.2098 10.1089 9.8788 10.2738 9.63531 10.382C9.51239 10.4367 9.4088 10.4782 9.33398 10.5067C9.2965 10.5209 9.26605 10.532 9.24377 10.54L9.21658 10.5495L9.20781 10.5525L9.20467 10.5535L9.20342 10.554C9.20317 10.554 9.20239 10.5543 8.93887 9.76373L9.20239 10.5543C8.76577 10.6998 8.29384 10.4639 8.1483 10.0273C8.00281 9.59078 8.23857 9.11902 8.67491 8.97331L8.68542 8.9696C8.69671 8.96559 8.71548 8.95878 8.74065 8.94919C8.79114 8.92996 8.86654 8.89986 8.95842 8.85902C9.14454 8.7763 9.38634 8.65481 9.62222 8.49756C10.1447 8.14925 10.3967 7.79388 10.3967 7.47253L10.3967 7.47129C10.3972 7.127 10.2759 6.79364 10.0542 6.53025C9.83245 6.26686 9.52467 6.09044 9.18533 6.03224ZM8.16663 12.8187C8.16663 12.3584 8.53973 11.9853 8.99997 11.9853H9.0076C9.46784 11.9853 9.84094 12.3584 9.84094 12.8187C9.84094 13.2789 9.46784 13.652 9.0076 13.652H8.99997C8.53973 13.652 8.16663 13.2789 8.16663 12.8187Z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,4 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17" fill="none"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" >
<path fill-rule="evenodd" clip-rule="evenodd" <path fill-rule="evenodd" clip-rule="evenodd" d="M5.42475 4.58336C5.1644 4.84371 5.1644 5.26582 5.42475 5.52617C5.6851 5.78652 6.10721 5.78652 6.36756 5.52617L8.00008 3.89366L9.63259 5.52617C9.89294 5.78652 10.315 5.78652 10.5754 5.52617C10.8357 5.26582 10.8357 4.84371 10.5754 4.58336L8.47148 2.47944C8.21113 2.21909 7.78902 2.21909 7.52867 2.47944L5.42475 4.58336ZM5.42475 10.4722C5.1644 10.7325 5.1644 11.1546 5.42475 11.415L7.52867 13.5189C7.56122 13.5514 7.59629 13.5799 7.63325 13.6043C7.89202 13.7752 8.24367 13.7467 8.47148 13.5189L10.5754 11.415C10.8357 11.1546 10.8357 10.7325 10.5754 10.4722C10.315 10.2118 9.89294 10.2118 9.63259 10.4722L8.00008 12.1047L6.36756 10.4722C6.10721 10.2118 5.6851 10.2118 5.42475 10.4722Z" />
d="M5.42469 4.94872C5.16434 5.20906 5.16434 5.63117 5.42469 5.89152C5.68504 6.15187 6.10715 6.15187 6.3675 5.89152L8.00002 4.25901L9.63253 5.89152C9.89288 6.15187 10.315 6.15187 10.5753 5.89152C10.8357 5.63117 10.8357 5.20906 10.5753 4.94872L8.47142 2.8448C8.21107 2.58445 7.78896 2.58445 7.52861 2.8448L5.42469 4.94872ZM5.42469 10.8375C5.16434 11.0979 5.16434 11.52 5.42469 11.7803L7.52861 13.8843C7.56115 13.9168 7.59623 13.9453 7.63319 13.9697C7.89196 14.1405 8.24361 14.1121 8.47142 13.8843L10.5753 11.7803C10.8357 11.52 10.8357 11.0979 10.5753 10.8375C10.315 10.5772 9.89288 10.5772 9.63253 10.8375L8.00002 12.47L6.3675 10.8375C6.10715 10.5772 5.68504 10.5772 5.42469 10.8375Z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 823 B

After

Width:  |  Height:  |  Size: 804 B

View File

@@ -1,11 +1,10 @@
<svg viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="36" height="36" fill="url(#paint0_linear_10832_16007)"/> <rect width="36" height="36" fill="url(#paint0_linear_11_1507)"/>
<path d="M13.9673 10.3122C13.1868 10.8479 13.1935 11.9491 13.863 12.6185C14.3986 13.1541 15.2613 13.1384 15.9008 12.7325C16.5477 12.3219 17.268 12.0318 18.0272 11.8808C19.2374 11.6401 20.4919 11.7636 21.6319 12.2358C22.7719 12.708 23.7463 13.5077 24.4318 14.5337C25.1174 15.5597 25.4833 16.7659 25.4833 17.9999C25.4833 19.2338 25.1174 20.4401 24.4318 21.4661C23.7463 22.4921 22.7719 23.2917 21.6319 23.7639C20.4919 24.2361 19.2374 24.3597 18.0272 24.119C17.268 23.968 16.5477 23.6779 15.9008 23.2673C15.2613 22.8614 14.3986 22.8457 13.863 23.3812C13.1935 24.0507 13.1868 25.1518 13.9673 25.6876C15.0044 26.3995 16.1799 26.8976 17.4252 27.1453C19.234 27.5051 21.1088 27.3204 22.8127 26.6147C24.5165 25.9089 25.9728 24.7138 26.9974 23.1803C28.022 21.6469 28.5689 19.8441 28.5689 17.9999C28.5689 16.1557 28.022 14.3528 26.9974 12.8194C25.9728 11.286 24.5165 10.0908 22.8127 9.38509C21.1088 8.67933 19.234 8.49468 17.4252 8.85447C16.1799 9.10217 15.0044 9.60029 13.9673 10.3122Z" fill="white"/> <path d="M9.22505 16.905C8.38096 17.102 7.87904 17.9714 8.13047 18.8009L9.96306 24.8467C10.3439 26.1032 12.0411 26.2986 12.6976 25.1616L13.5842 23.6259C15.0589 24.1044 16.6267 24.2674 18.1843 24.0937C20.4842 23.8372 22.6439 22.8596 24.3544 21.301C26.0649 19.7424 27.2384 17.6826 27.7071 15.4164C28.074 13.6422 27.9941 11.8127 27.4853 10.0898C27.2506 9.29527 26.3514 8.96094 25.5963 9.30173L23.6663 10.1728C22.9113 10.5136 22.597 11.404 22.7351 12.2208C22.8554 12.9325 22.8437 13.6647 22.6957 14.38C22.4458 15.5886 21.8199 16.6872 20.9077 17.5184C19.9954 18.3497 18.8436 18.871 17.617 19.0078C17.1616 19.0586 16.7045 19.0555 16.2549 19.0002L17.0171 17.6799C17.6736 16.5428 16.6558 15.1707 15.3772 15.4692L9.22505 16.905Z" fill="white"/>
<path d="M19.2443 22.5497C17.3579 22.5497 15.7397 21.4017 15.0501 19.7663H9.19726C8.22171 19.7663 7.43087 18.9754 7.43087 17.9999C7.43087 17.0243 8.22171 16.2335 9.19726 16.2335H15.0501C15.7397 14.598 17.3579 13.45 19.2443 13.45C21.7571 13.45 23.7942 15.4871 23.7942 17.9999C23.7942 20.5127 21.7571 22.5497 19.2443 22.5497Z" fill="white"/>
<defs> <defs>
<linearGradient id="paint0_linear_10832_16007" x1="18" y1="0" x2="5.5" y2="33" gradientUnits="userSpaceOnUse"> <linearGradient id="paint0_linear_11_1507" x1="18" y1="0" x2="5.5" y2="33" gradientUnits="userSpaceOnUse">
<stop stop-color="#68C0FF"/> <stop stop-color="#FF8BFD"/>
<stop offset="1" stop-color="#52A2FF"/> <stop offset="1" stop-color="#7394FF"/>
</linearGradient> </linearGradient>
</defs> </defs>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,11 +1,10 @@
<svg viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="36" height="36" fill="url(#paint0_linear_10829_15860)"/> <rect width="36" height="36" fill="url(#paint0_linear_11_1494)"/>
<path d="M22.0325 10.3122C22.813 10.8479 22.8062 11.9491 22.1368 12.6185C21.6012 13.1541 20.7384 13.1384 20.099 12.7325C19.4521 12.3219 18.7317 12.0318 17.9726 11.8808C16.7623 11.6401 15.5079 11.7636 14.3679 12.2358C13.2279 12.708 12.2535 13.5077 11.5679 14.5337C10.8824 15.5597 10.5165 16.7659 10.5165 17.9999C10.5165 19.2338 10.8824 20.4401 11.5679 21.4661C12.2535 22.4921 13.2279 23.2917 14.3679 23.7639C15.5079 24.2361 16.7624 24.3597 17.9726 24.119C18.7317 23.968 19.4521 23.6779 20.099 23.2673C20.7384 22.8614 21.6012 22.8457 22.1368 23.3812C22.8062 24.0507 22.813 25.1518 22.0325 25.6876C20.9954 26.3995 19.8199 26.8976 18.5746 27.1453C16.7658 27.5051 14.8909 27.3204 13.1871 26.6147C11.4832 25.9089 10.0269 24.7138 9.00232 23.1803C7.97772 21.6469 7.43085 19.8441 7.43085 17.9999C7.43085 16.1557 7.97772 14.3528 9.00232 12.8194C10.0269 11.286 11.4832 10.0908 13.1871 9.38509C14.8909 8.67933 16.7658 8.49468 18.5746 8.85447C19.8199 9.10217 20.9954 9.60029 22.0325 10.3122Z" fill="white"/> <path d="M26.7748 18.1758C27.6189 17.9788 28.1208 17.1095 27.8693 16.2799L26.0368 10.2341C25.6559 8.97762 23.9587 8.7822 23.3022 9.91926L22.4156 11.4549C20.9409 10.9764 19.3731 10.8134 17.8155 10.9871C15.5157 11.2437 13.3559 12.2212 11.6454 13.7798C9.93493 15.3384 8.76137 17.3983 8.29272 19.6644C7.92581 21.4386 8.00571 23.2681 8.51454 24.991C8.74918 25.7855 9.64845 26.1199 10.4035 25.7791L12.3335 24.908C13.0886 24.5672 13.4028 23.6768 13.2647 22.86C13.1444 22.1483 13.1562 21.4161 13.3041 20.7008C13.554 19.4922 14.1799 18.3936 15.0922 17.5624C16.0044 16.7311 17.1562 16.2098 18.3828 16.073C18.8382 16.0222 19.2953 16.0254 19.7449 16.0807L18.9827 17.4009C18.3262 18.538 19.344 19.9101 20.6226 19.6117L26.7748 18.1758Z" fill="white"/>
<path d="M16.7554 22.5497C18.6418 22.5497 20.2601 21.4017 20.9497 19.7663H26.8025C27.778 19.7663 28.5689 18.9754 28.5689 17.9999C28.5689 17.0243 27.778 16.2335 26.8025 16.2335H20.9497C20.2601 14.598 18.6418 13.45 16.7554 13.45C14.2426 13.45 12.2056 15.4871 12.2056 17.9999C12.2056 20.5127 14.2426 22.5497 16.7554 22.5497Z" fill="white"/>
<defs> <defs>
<linearGradient id="paint0_linear_10829_15860" x1="18" y1="0" x2="5.5" y2="33" gradientUnits="userSpaceOnUse"> <linearGradient id="paint0_linear_11_1494" x1="18" y1="0" x2="5.5" y2="33" gradientUnits="userSpaceOnUse">
<stop stop-color="#68C0FF"/> <stop stop-color="#FF8BFD"/>
<stop offset="1" stop-color="#52A2FF"/> <stop offset="1" stop-color="#7394FF"/>
</linearGradient> </linearGradient>
</defs> </defs>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 10 9" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.09993 1.21182C6.09993 0.604303 5.60744 0.111816 4.99993 0.111816C4.39242 0.111816 3.89993 0.604303 3.89993 1.21182L3.89993 3.11182H1.9999C1.39239 3.11182 0.899902 3.6043 0.899902 4.21182C0.899902 4.81933 1.39239 5.31182 1.9999 5.31182H3.89993L3.89993 7.21182C3.89993 7.81933 4.39242 8.31182 4.99993 8.31182C5.60744 8.31182 6.09993 7.81933 6.09993 7.21182V5.31182H7.9999C8.60742 5.31182 9.0999 4.81933 9.0999 4.21182C9.0999 3.6043 8.60742 3.11182 7.9999 3.11182H6.09993V1.21182Z" />
</svg>

After

Width:  |  Height:  |  Size: 603 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.99968 2.55678C4.99348 2.55678 2.55648 4.99379 2.55648 7.99998C2.55648 11.0062 4.99348 13.4432 7.99968 13.4432C11.0059 13.4432 13.4429 11.0062 13.4429 7.99998C13.4429 4.99379 11.0059 2.55678 7.99968 2.55678ZM1.22314 7.99998C1.22314 4.25741 4.2571 1.22345 7.99968 1.22345C11.7423 1.22345 14.7762 4.25741 14.7762 7.99998C14.7762 11.7426 11.7423 14.7765 7.99968 14.7765C4.2571 14.7765 1.22314 11.7426 1.22314 7.99998ZM8.14797 5.62577C7.87651 5.57921 7.59732 5.63022 7.35986 5.76978C7.1224 5.90934 6.942 6.12843 6.8506 6.38825C6.72842 6.73558 6.34781 6.9181 6.00048 6.79591C5.65315 6.67373 5.47064 6.29312 5.59282 5.9458C5.78871 5.38894 6.17536 4.91937 6.68428 4.62027C7.19321 4.32117 7.79157 4.21184 8.37338 4.31163C8.9552 4.41143 9.48292 4.71392 9.86308 5.16552C10.2432 5.61702 10.4512 6.18845 10.4504 6.77862C10.4501 7.74318 9.73548 8.37516 9.23708 8.70743C8.96754 8.88713 8.70274 9.01905 8.50796 9.10562C8.40962 9.14933 8.32674 9.18252 8.26689 9.20532C8.23691 9.21674 8.21254 9.22562 8.19473 9.23195L8.17297 9.23957L8.16595 9.24197L8.16345 9.24282L8.16245 9.24315C8.16225 9.24322 8.16162 9.24343 7.9508 8.61097L8.16162 9.24343C7.81232 9.35986 7.43478 9.17109 7.31835 8.82179C7.20195 8.47261 7.39056 8.0952 7.73963 7.97863L7.74805 7.97567C7.75708 7.97246 7.77209 7.96701 7.79223 7.95934C7.83262 7.94395 7.89294 7.91987 7.96644 7.8872C8.11534 7.82103 8.30878 7.72383 8.49748 7.59803C8.91545 7.31938 9.11709 7.03509 9.11709 6.77801L9.1171 6.77702C9.11751 6.50159 9.02042 6.2349 8.84305 6.02419C8.66567 5.81347 8.41944 5.67234 8.14797 5.62577ZM7.33301 11.0549C7.33301 10.6867 7.63149 10.3883 7.99968 10.3883H8.00579C8.37398 10.3883 8.67246 10.6867 8.67246 11.0549C8.67246 11.4231 8.37398 11.7216 8.00579 11.7216H7.99968C7.63149 11.7216 7.33301 11.4231 7.33301 11.0549Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,6 +0,0 @@
<svg t="1709471698048" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4242"
width="128" height="128">
<path
d="M855.158154 945.664H168.999385c-28.081231 0-50.845538-22.843077-50.845539-51.003077V486.833231C118.153846 458.673231 129.457231 433.230769 157.538462 433.230769h708.923076c28.081231 0 39.502769 25.442462 39.50277 53.602462v407.827692c0 28.16-22.764308 51.003077-50.806154 51.003077z m-340.913231-376.595692a99.761231 99.761231 0 0 0-99.603692 99.958154c0 40.251077 23.827692 74.712615 57.974154 90.54523V827.076923a39.384615 39.384615 0 0 0 78.76923 0v-65.417846a99.879385 99.879385 0 0 0 62.424616-92.632615 99.761231 99.761231 0 0 0-99.564308-99.958154z m0.551385-396.524308c-104.841846 0-189.794462 81.329231-197.159385 184.123077H217.718154C229.060923 201.334154 358.321231 78.769231 516.489846 78.769231s287.428923 122.564923 298.732308 277.897846h-103.266462c-7.364923-102.793846-92.317538-184.123077-197.159384-184.123077z"
fill="#3B9BF8" p-id="4243"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,7 @@
import React from 'react';
import { Image, ImageProps } from '@chakra-ui/react';
import { getWebReqUrl } from '../../../common/system/utils';
const MyImage = (props: ImageProps) => {
return <Image {...props} src={getWebReqUrl(props.src)} alt={props.alt || ''} />;
};
export default React.memo(MyImage);

View File

@@ -1,9 +1,10 @@
import React from 'react'; import React from 'react';
import { PhotoProvider, PhotoView } from 'react-photo-view'; import { PhotoProvider, PhotoView } from 'react-photo-view';
import 'react-photo-view/dist/react-photo-view.css'; import 'react-photo-view/dist/react-photo-view.css';
import { Box, Image, ImageProps } from '@chakra-ui/react'; import { ImageProps } from '@chakra-ui/react';
import { useSystem } from '../../../hooks/useSystem'; import { useSystem } from '../../../hooks/useSystem';
import Loading from '../MyLoading'; import Loading from '../MyLoading';
import MyImage from './MyImage';
const MyPhotoView = ({ ...props }: ImageProps) => { const MyPhotoView = ({ ...props }: ImageProps) => {
const { isPc } = useSystem(); const { isPc } = useSystem();
@@ -15,7 +16,7 @@ const MyPhotoView = ({ ...props }: ImageProps) => {
loadingElement={<Loading fixed={false} />} loadingElement={<Loading fixed={false} />}
> >
<PhotoView src={props.src}> <PhotoView src={props.src}>
<Image cursor={'pointer'} {...props} /> <MyImage cursor={'pointer'} {...props} />
</PhotoView> </PhotoView>
</PhotoProvider> </PhotoProvider>
); );

View File

@@ -7,28 +7,50 @@ import {
NumberInputProps NumberInputProps
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import MyIcon from '../../Icon';
import { UseFormRegister } from 'react-hook-form';
type Props = Omit<NumberInputProps, 'onChange'> & { type Props = Omit<NumberInputProps, 'onChange'> & {
onChange: (e?: number) => any; onChange?: (e?: number) => any;
placeholder?: string; placeholder?: string;
register?: UseFormRegister<any>;
name?: string;
bg?: string;
}; };
const MyNumberInput = (props: Props) => { const MyNumberInput = (props: Props) => {
const { register, name, onChange, placeholder, bg, ...restProps } = props;
return ( return (
<NumberInput <NumberInput
{...props} {...restProps}
onChange={(e) => { onChange={(e) => {
if (!onChange) return;
if (isNaN(Number(e))) { if (isNaN(Number(e))) {
props?.onChange(); onChange();
} else { } else {
props?.onChange(Number(e)); onChange(Number(e));
} }
}} }}
> >
<NumberInputField placeholder={props?.placeholder} /> <NumberInputField
bg={bg}
placeholder={placeholder}
{...(register && name
? register(name, {
required: props.isRequired,
min: props.min,
max: props.max
})
: {})}
/>
<NumberInputStepper> <NumberInputStepper>
<NumberIncrementStepper /> <NumberIncrementStepper>
<NumberDecrementStepper /> <MyIcon name={'core/chat/chevronUp'} width={'12px'} />
</NumberIncrementStepper>
<NumberDecrementStepper>
<MyIcon name={'core/chat/chevronDown'} width={'12px'} />
</NumberDecrementStepper>
</NumberInputStepper> </NumberInputStepper>
</NumberInput> </NumberInput>
); );

View File

@@ -10,7 +10,14 @@ const FormLabel = ({
children: React.ReactNode; children: React.ReactNode;
}) => { }) => {
return ( return (
<Box color={'myGray.900'} fontSize={'sm'} position={'relative'} flexShrink={0} {...props}> <Box
color={'myGray.900'}
fontWeight={'medium'}
fontSize={'sm'}
position={'relative'}
flexShrink={0}
{...props}
>
{required && ( {required && (
<Box color={'red.600'} position={'absolute'} top={'-4px'} left={'-6px'}> <Box color={'red.600'} position={'absolute'} top={'-4px'} left={'-6px'}>
* *

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import MyIcon from '../Icon'; import { Flex, Box, CloseButton, FlexProps } from '@chakra-ui/react';
import { Flex, Image, Box, CloseButton, FlexProps } from '@chakra-ui/react';
import { useLoading } from '../../../hooks/useLoading'; import { useLoading } from '../../../hooks/useLoading';
import Avatar from '../Avatar';
type Props = FlexProps & { type Props = FlexProps & {
onClose: () => void; onClose: () => void;
@@ -50,15 +50,7 @@ const CustomRightDrawer = ({
py={'10px'} py={'10px'}
px={5} px={5}
> >
{iconSrc && ( {iconSrc && <Avatar mr={3} w={'20px'} src={iconSrc} />}
<>
{iconSrc.startsWith('/') ? (
<Image mr={3} objectFit={'contain'} alt="" src={iconSrc} w={'20px'} />
) : (
<MyIcon mr={3} name={iconSrc as any} w={'20px'} />
)}
</>
)}
<Box flex={'1'} fontSize={'md'}> <Box flex={'1'} fontSize={'md'}>
{title} {title}
</Box> </Box>

View File

@@ -13,6 +13,7 @@ import {
Box Box
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useLoading } from '../../../hooks/useLoading'; import { useLoading } from '../../../hooks/useLoading';
import Avatar from '../Avatar';
type Props = DrawerContentProps & { type Props = DrawerContentProps & {
onClose: () => void; onClose: () => void;
@@ -52,15 +53,7 @@ const MyRightDrawer = ({
py={'10px'} py={'10px'}
px={5} px={5}
> >
{iconSrc && ( {iconSrc && <Avatar mr={3} w={'20px'} src={iconSrc} />}
<>
{iconSrc.startsWith('/') ? (
<Image mr={3} objectFit={'contain'} alt="" src={iconSrc} w={'20px'} />
) : (
<MyIcon mr={3} name={iconSrc as any} w={'20px'} />
)}
</>
)}
<Box flex={'1'} fontSize={'md'}> <Box flex={'1'} fontSize={'md'}>
{title} {title}
</Box> </Box>

View File

@@ -148,7 +148,6 @@ const MyMenu = ({
color={isOpen ? 'primary.600' : ''} color={isOpen ? 'primary.600' : ''}
w="fit-content" w="fit-content"
p="1" p="1"
bgColor={isOpen ? 'myGray.50' : ''}
h="fit-content" h="fit-content"
borderRadius="sm" borderRadius="sm"
> >

View File

@@ -1,11 +1,11 @@
import React, { useRef, useCallback, useState } from 'react'; import React, { useRef, useCallback, useState, useMemo } from 'react';
import { Button, useDisclosure, Box, Flex, useOutsideClick } from '@chakra-ui/react'; import { Button, useDisclosure, Box, Flex, useOutsideClick, Checkbox } from '@chakra-ui/react';
import { ChevronDownIcon } from '@chakra-ui/icons'; import { ListItemType, MultipleArraySelectProps, MultipleSelectProps } from './type';
import { MultipleSelectProps } from './type';
import EmptyTip from '../EmptyTip'; import EmptyTip from '../EmptyTip';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import MyIcon from '../../common/Icon';
const MultipleRowSelect = ({ export const MultipleRowSelect = ({
placeholder, placeholder,
label, label,
value = [], value = [],
@@ -106,17 +106,25 @@ const MultipleRowSelect = ({
<Button <Button
justifyContent={'space-between'} justifyContent={'space-between'}
width={'100%'} width={'100%'}
rightIcon={<ChevronDownIcon />} variant={'whitePrimaryOutline'}
variant={'whiteBase'} size={'lg'}
fontSize={'sm'}
px={3}
outline={'none'}
rightIcon={<MyIcon name={'core/chat/chevronDown'} w="1rem" color={'myGray.500'} />}
_active={{ _active={{
transform: 'none' transform: 'none'
}} }}
{...(isOpen {...(isOpen
? { ? {
boxShadow: '0px 0px 4px #A8DBFF', borderColor: 'primary.600',
borderColor: 'primary.500' color: 'primary.700',
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)'
} }
: {})} : {
borderColor: 'myGray.200',
boxShadow: 'none'
})}
{...styles} {...styles}
onClick={() => (isOpen ? onClose() : onOpenSelect())} onClick={() => (isOpen ? onClose() : onOpenSelect())}
> >
@@ -127,10 +135,194 @@ const MultipleRowSelect = ({
position={'absolute'} position={'absolute'}
{...(popDirection === 'top' {...(popDirection === 'top'
? { ? {
bottom: '45px' transform: 'translateY(-105%)',
top: '0'
} }
: { : {
top: '45px' transform: 'translateY(105%)',
bottom: '0'
})}
py={2}
bg={'white'}
border={'1px solid #fff'}
boxShadow={'5'}
borderRadius={'md'}
zIndex={1}
minW={'100%'}
w={'max-content'}
>
<Flex>
<RenderList list={list} index={0} />
</Flex>
</Box>
)}
</Box>
);
};
export const MultipleRowArraySelect = ({
placeholder,
label,
value = [],
list,
emptyTip,
maxH = 300,
onSelect,
popDirection = 'bottom',
styles
}: MultipleArraySelectProps) => {
const { t } = useTranslation();
const ref = useRef<HTMLDivElement>(null);
const { isOpen, onOpen, onClose } = useDisclosure();
const [navigationPath, setNavigationPath] = useState<string[]>([]);
const formatValue = useMemo(() => {
return Array.isArray(value) ? value : [];
}, [value]);
// Close when clicking outside
useOutsideClick({
ref: ref,
handler: onClose
});
const RenderList = useCallback(
({ index, list }: { index: number; list: MultipleSelectProps['list'] }) => {
const currentNavValue = navigationPath[index];
const selectedIndex = list.findIndex((item) => item.value === currentNavValue);
const children = list[selectedIndex]?.children || [];
const hasChildren = list.some((item) => item.children && item.children?.length > 0);
const handleSelect = (item: ListItemType) => {
// Has children, set parent value
if (hasChildren) {
const newPath = [...navigationPath];
newPath[index] = item.value;
setNavigationPath(newPath);
} else {
const parentValue = navigationPath[0];
const newValues = [...formatValue];
const newValue = [parentValue, item.value];
if (newValues.some((v) => v[0] === parentValue && v[1] === item.value)) {
onSelect(newValues.filter((v) => !(v[0] === parentValue && v[1] === item.value)));
} else {
onSelect([...newValues, newValue]);
}
}
};
return (
<>
<Box
className="nowheel"
flex={'1 0 auto'}
px={2}
borderLeft={index !== 0 ? 'base' : 'none'}
maxH={`${maxH}px`}
overflowY={'auto'}
whiteSpace={'nowrap'}
>
{list.map((item) => {
const isSelected = item.value === currentNavValue;
const showCheckbox = !hasChildren;
const isChecked =
showCheckbox &&
formatValue.some((v) => v[1] === item.value && v[0] === navigationPath[0]);
return (
<Flex
key={item.value}
py={2}
cursor={'pointer'}
px={2}
borderRadius={'md'}
_hover={{
bg: 'primary.50',
color: 'primary.600'
}}
onClick={() => handleSelect(item)}
{...(isSelected ? { color: 'primary.600' } : {})}
>
{showCheckbox && (
<Checkbox
isChecked={isChecked}
icon={<MyIcon name={'common/check'} w={'12px'} />}
mr={1}
/>
)}
<Box>{item.label}</Box>
</Flex>
);
})}
{list.length === 0 && (
<EmptyTip
text={emptyTip ?? t('common:common.MultipleRowSelect.No data')}
pt={1}
pb={3}
/>
)}
</Box>
{children.length > 0 && <RenderList list={children} index={index + 1} />}
</>
);
},
[navigationPath, formatValue, onSelect]
);
const onOpenSelect = useCallback(() => {
setNavigationPath([]);
onOpen();
}, []);
return (
<Box ref={ref} position={'relative'}>
<Button
width={'100%'}
variant={'whitePrimaryOutline'}
size={'lg'}
fontSize={'sm'}
px={3}
outline={'none'}
rightIcon={<MyIcon name={'core/chat/chevronDown'} w="1rem" color={'myGray.500'} />}
iconSpacing={2}
h={'auto'}
_active={{
transform: 'none'
}}
_hover={{
borderColor: 'primary.500'
}}
{...(isOpen
? {
borderColor: 'primary.600',
color: 'primary.700',
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)'
}
: {
borderColor: 'myGray.200',
boxShadow: 'none'
})}
{...styles}
onClick={() => (isOpen ? onClose() : onOpenSelect())}
className="nowheel"
>
<Box w={'100%'} textAlign={'left'}>
{label ?? placeholder}
</Box>
</Button>
{isOpen && (
<Box
position={'absolute'}
{...(popDirection === 'top'
? {
transform: 'translateY(-105%)',
top: '0'
}
: {
transform: 'translateY(105%)',
bottom: '0'
})} })}
py={2} py={2}
bg={'white'} bg={'white'}

View File

@@ -59,10 +59,11 @@ const MySelect = <T = any,>(
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
_hover: { _hover: {
backgroundColor: 'myWhite.600' backgroundColor: 'myGray.100',
color: 'primary.700'
}, },
_notLast: { _notLast: {
mb: 2 mb: 1
} }
}; };
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
@@ -107,16 +108,19 @@ const MySelect = <T = any,>(
ref={ButtonRef} ref={ButtonRef}
width={width} width={width}
px={3} px={3}
rightIcon={<ChevronDownIcon />} rightIcon={<MyIcon name={'core/chat/chevronDown'} w={4} color={'myGray.500'} />}
variant={'whitePrimary'} variant={'whitePrimaryOutline'}
size={'lg'}
fontSize={'sm'}
textAlign={'left'} textAlign={'left'}
_active={{ _active={{
transform: 'none' transform: 'none'
}} }}
{...(isOpen {...(isOpen
? { ? {
boxShadow: '0px 0px 4px #A8DBFF', boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)',
borderColor: 'primary.500' borderColor: 'primary.600',
color: 'primary.700'
} }
: {})} : {})}
{...props} {...props}
@@ -157,7 +161,7 @@ const MySelect = <T = any,>(
{...(value === item.value {...(value === item.value
? { ? {
ref: SelectedItemRef, ref: SelectedItemRef,
color: 'primary.600', color: 'primary.700',
bg: 'myGray.100' bg: 'myGray.100'
} }
: { : {

View File

@@ -4,9 +4,9 @@ type ListItemType = {
value: any; value: any;
children?: ListItemType[]; children?: ListItemType[];
}; };
export type MultipleSelectProps<T = any> = { export type MultipleSelectProps = {
label?: string | React.ReactNode; label?: string | React.ReactNode;
value: any[]; value?: any[];
placeholder?: string; placeholder?: string;
list: ListItemType[]; list: ListItemType[];
emptyTip?: string; emptyTip?: string;
@@ -15,3 +15,7 @@ export type MultipleSelectProps<T = any> = {
styles?: ButtonProps; styles?: ButtonProps;
popDirection?: 'top' | 'bottom'; popDirection?: 'top' | 'bottom';
}; };
export type MultipleArraySelectProps = Omit<MultipleSelectProps, 'value'> & {
value?: any[][];
onSelect: (val: any[][]) => void;
};

View File

@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import MyTooltip from '.'; import MyTooltip from '.';
import { IconProps, QuestionOutlineIcon } from '@chakra-ui/icons'; import { IconProps } from '@chakra-ui/icons';
import MyIcon from '../Icon';
type Props = IconProps & { type Props = IconProps & {
label?: string | React.ReactNode; label?: string | React.ReactNode;
@@ -9,7 +10,7 @@ type Props = IconProps & {
const QuestionTip = ({ label, maxW, ...props }: Props) => { const QuestionTip = ({ label, maxW, ...props }: Props) => {
return ( return (
<MyTooltip label={label} maxW={maxW}> <MyTooltip label={label} maxW={maxW}>
<QuestionOutlineIcon w={'0.9rem'} {...props} /> <MyIcon name={'help' as any} w={'16px'} color={'myGray.500'} {...props} />
</MyTooltip> </MyTooltip>
); );
}; };

View File

@@ -72,11 +72,11 @@ const LightRowTabs = <ValueType = string,>({
_hover={{ _hover={{
color: activeColor color: activeColor
}} }}
fontWeight={'medium'}
{...(value === item.value {...(value === item.value
? { ? {
color: activeColor, color: activeColor,
cursor: 'default', cursor: 'default',
fontWeight: 'bold',
borderBottomColor: activeColor borderBottomColor: activeColor
} }
: { : {

View File

@@ -2,9 +2,10 @@ import React, { useCallback, useRef, useState } from 'react';
import Editor, { Monaco, loader } from '@monaco-editor/react'; import Editor, { Monaco, loader } from '@monaco-editor/react';
import { Box, BoxProps } from '@chakra-ui/react'; import { Box, BoxProps } from '@chakra-ui/react';
import MyIcon from '../../Icon'; import MyIcon from '../../Icon';
import { getWebReqUrl } from '../../../../common/system/utils';
loader.config({ loader.config({
paths: { vs: '/js/monaco-editor.0.45.0/vs' } paths: { vs: getWebReqUrl('/js/monaco-editor.0.45.0/vs') }
}); });
type EditorVariablePickerType = { type EditorVariablePickerType = {

View File

@@ -4,9 +4,10 @@ import { Box, BoxProps } from '@chakra-ui/react';
import MyIcon from '../../Icon'; import MyIcon from '../../Icon';
import { useToast } from '../../../../hooks/useToast'; import { useToast } from '../../../../hooks/useToast';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { getWebReqUrl } from '../../../../common/system/utils';
loader.config({ loader.config({
paths: { vs: '/js/monaco-editor.0.45.0/vs' } paths: { vs: getWebReqUrl('/js/monaco-editor.0.45.0/vs') }
}); });
type EditorVariablePickerType = { type EditorVariablePickerType = {

View File

@@ -105,10 +105,10 @@ export default function Editor({
left={0} left={0}
right={0} right={0}
bottom={0} bottom={0}
py={3} py={2}
px={4} px={3}
pointerEvents={'none'} pointerEvents={'none'}
overflow={'overlay'} overflow={'hidden'}
> >
<Box <Box
color={'myGray.400'} color={'myGray.400'}
@@ -146,12 +146,12 @@ export default function Editor({
<Box <Box
zIndex={10} zIndex={10}
position={'absolute'} position={'absolute'}
bottom={0} bottom={-1}
right={2} right={2}
cursor={'pointer'} cursor={'pointer'}
onClick={onOpenModal} onClick={onOpenModal}
> >
<MyIcon name={'common/fullScreenLight'} w={'14px'} color={'myGray.600'} /> <MyIcon name={'common/fullScreenLight'} w={'14px'} color={'myGray.500'} />
</Box> </Box>
)} )}
</Box> </Box>

View File

@@ -9,6 +9,45 @@
font-size: var(--chakra-fontSizes-sm); font-size: var(--chakra-fontSizes-sm);
overflow-y: auto; overflow-y: auto;
&:hover {
border-color: var(--chakra-colors-primary-300);
}
&::-webkit-scrollbar {
color: var(--chakra-colors-myGray-100);
}
&::-webkit-scrollbar-thumb {
background-color: var(--chakra-colors-myGray-200) !important;
cursor: pointer;
}
&::-webkit-scrollbar-thumb:hover {
background-color: var(--chakra-colors-myGray-250) !important;
}
}
.contentEditable_isFlow {
position: relative;
height: 100%;
width: 100%;
border: 1px solid var(--chakra-colors-myGray-200);
border-radius: var(--chakra-radii-sm);
padding: 6px 8px;
font-size: var(--chakra-fontSizes-sm);
overflow-y: auto;
&:hover {
border-color: var(--chakra-colors-primary-300);
}
&::-webkit-scrollbar {
color: var(--chakra-colors-myGray-100);
}
&::-webkit-scrollbar-thumb {
background-color: var(--chakra-colors-myGray-200) !important;
cursor: pointer;
}
&::-webkit-scrollbar-thumb:hover {
background-color: var(--chakra-colors-myGray-250) !important;
}
} }
.contentEditable:focus { .contentEditable:focus {

View File

@@ -1,4 +1,4 @@
import { Box, Button, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react'; import { Button, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { editorStateToText } from './utils'; import { editorStateToText } from './utils';
import Editor from './Editor'; import Editor from './Editor';

View File

@@ -1,97 +0,0 @@
import { Box, Button, Image } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
export default function ComfirmVar({
newVariables,
onCancel,
onConfirm
}: {
newVariables: string[];
onCancel: () => void;
onConfirm: () => void;
}) {
const { t } = useTranslation();
return (
<>
<Box
background={'rgba(35, 56, 118, 0.2)'}
rounded={'sm'}
position={'absolute'}
top={0}
left={0}
right={0}
bottom={0}
/>
<Box
position={'absolute'}
top={'50%'}
left={'50%'}
transform={'translate(-50%, -50%)'}
w={'70%'}
h={'70%'}
bg={'white'}
rounded={'lg'}
boxShadow={'0 2px 4px rgba(0, 0, 0, 0.1)'}
display={'flex'}
flexDirection={'column'}
justifyContent={'space-between'}
pb={4}
>
<Box display={'flex'} mt={4} mr={4}>
<Box
w={'36px'}
h={'36px'}
minW={'36px'}
boxShadow={'0 4px 8px rgba(0, 0, 0, 0.1)'}
display={'flex'}
alignItems={'center'}
justifyContent={'center'}
rounded={'md'}
border={'1px solid rgba(0, 0, 0, 0.1)'}
mx={4}
>
<Image alt={''} src={'/imgs/workflow/variable.png'} objectFit={'contain'} w={'20px'} />
</Box>
<Box>{t('common:undefined_var')}</Box>
</Box>
<Box
ml={16}
mt={4}
fontSize={'sm'}
color={'rgb(28,100,242)'}
display={'flex'}
whiteSpace={'wrap'}
>
{newVariables.map((item, index) => (
<Box
key={index}
display={'flex'}
alignItems={'center'}
justifyContent={'center'}
bg={'rgb(237,242,250)'}
px={1}
h={6}
rounded={'md'}
mr={2}
>
<span>
<span style={{ opacity: '60%' }}>{`{{`}</span>
<span>{item}</span>
<span style={{ opacity: '60%' }}>{`}}`}</span>
</span>
</Box>
))}
</Box>
<Box>
<Box display={'flex'} justifyContent={'flex-end'} mt={4} mr={4}>
<Button size={'sm'} variant={'ghost'} onClick={onCancel}>
{t('common:common.Cancel')}
</Button>
<Button size={'sm'} variant={'primary'} ml={4} onClick={onConfirm}>
{t('common:common.Confirm')}
</Button>
</Box>
</Box>
</Box>
</>
);
}

View File

@@ -150,12 +150,20 @@ const NodeInputSelect = ({
trigger="click" trigger="click"
Button={ Button={
<Button <Button
size={'xs'} leftIcon={
leftIcon={<MyIcon name={renderTypeData.icon as any} w={'0.8rem'} color={'primary.600'} />} <MyIcon name={renderTypeData.icon as any} w={'14px'} color={'primary.600'} mr={-0.5} />
rightIcon={<MyIcon name={'common/select'} w={'0.8rem'} color={'myGray.500'} />} }
rightIcon={<MyIcon name={'common/select'} w={'0.8rem'} color={'myGray.500'} ml={-1} />}
variant={'grayBase'} variant={'grayBase'}
border={theme.borders.base} border={theme.borders.base}
borderRadius={'xs'} borderColor={'myGray.200'}
borderRadius={'sm'}
px={'10px'}
py={'6px'}
fontSize={'mini'}
color={'myGray.600'}
h={'28px'}
bg={'myGray.100'}
> >
<Box fontWeight={'medium'}>{renderTypeData.title}</Box> <Box fontWeight={'medium'}>{renderTypeData.title}</Box>
</Button> </Button>

View File

@@ -1,5 +1,8 @@
{ {
"Role_setting": "Role setting",
"Run": "Execute", "Run": "Execute",
"Team Tags Set": "Team tags",
"Team_Tags": "Team tags",
"ai_settings": "AI Configuration", "ai_settings": "AI Configuration",
"all_apps": "All Applications", "all_apps": "All Applications",
"app.Version name": "Version Name", "app.Version name": "Version Name",
@@ -27,6 +30,7 @@
"cron.every_month": "Run Monthly", "cron.every_month": "Run Monthly",
"cron.every_week": "Run Weekly", "cron.every_week": "Run Weekly",
"cron.interval": "Run at Intervals", "cron.interval": "Run at Intervals",
"dataset_search_tool_description": "Call the \"Semantic Search\" and \"Full-text Search\" capabilities to find reference content that may be related to the problem from the \"Knowledge Base\". \nPrioritize calling this tool to assist in answering user questions.",
"day": "Day", "day": "Day",
"document_quote": "Document Reference", "document_quote": "Document Reference",
"document_quote_tip": "Usually used to accept user-uploaded document content (requires document parsing), and can also be used to reference other string data.", "document_quote_tip": "Usually used to accept user-uploaded document content (requires document parsing), and can also be used to reference other string data.",
@@ -37,6 +41,7 @@
"export_config_successful": "Configuration copied, some sensitive information automatically filtered. Please check for any remaining sensitive data.", "export_config_successful": "Configuration copied, some sensitive information automatically filtered. Please check for any remaining sensitive data.",
"export_configs": "Export Configurations", "export_configs": "Export Configurations",
"feedback_count": "User Feedback", "feedback_count": "User Feedback",
"file_quote_link": "Files",
"file_recover": "File will overwrite current content", "file_recover": "File will overwrite current content",
"file_upload": "File Upload", "file_upload": "File Upload",
"file_upload_tip": "Once enabled, documents/images can be uploaded. Documents are retained for 7 days, images for 15 days. Using this feature may incur additional costs. To ensure a good experience, please choose an AI model with a larger context length when using this feature.", "file_upload_tip": "Once enabled, documents/images can be uploaded. Documents are retained for 7 days, images for 15 days. Using this feature may incur additional costs. To ensure a good experience, please choose an AI model with a larger context length when using this feature.",
@@ -44,7 +49,7 @@
"go_to_chat": "Go to Conversation", "go_to_chat": "Go to Conversation",
"go_to_run": "Go to Execution", "go_to_run": "Go to Execution",
"image_upload": "Image Upload", "image_upload": "Image Upload",
"image_upload_tip": "Please ensure to select a vision model that can process images.", "image_upload_tip": "How to activate model image recognition capabilities",
"import_configs": "Import Configurations", "import_configs": "Import Configurations",
"import_configs_failed": "Import configuration failed, please ensure the configuration is correct!", "import_configs_failed": "Import configuration failed, please ensure the configuration is correct!",
"import_configs_success": "Import Successful", "import_configs_success": "Import Successful",
@@ -58,7 +63,7 @@
"intro": "A comprehensive model application orchestration system that offers out-of-the-box data processing and model invocation capabilities. It allows for rapid Dataset construction and workflow orchestration through Flow visualization, enabling complex Dataset scenarios!", "intro": "A comprehensive model application orchestration system that offers out-of-the-box data processing and model invocation capabilities. It allows for rapid Dataset construction and workflow orchestration through Flow visualization, enabling complex Dataset scenarios!",
"llm_not_support_vision": "This model does not support image recognition", "llm_not_support_vision": "This model does not support image recognition",
"llm_use_vision": "Enable Image Recognition", "llm_use_vision": "Enable Image Recognition",
"llm_use_vision_tip": "Once image recognition is enabled, this model will automatically receive images uploaded from the 'dialog box' and image links in 'user questions'.", "llm_use_vision_tip": "After clicking on the model selection, you can see whether the model supports image recognition and the ability to control whether to start image recognition. \nAfter starting image recognition, the model will read the image content in the file link, and if the user question is less than 500 words, it will automatically parse the image in the user question.",
"logs_chat_user": "user", "logs_chat_user": "user",
"logs_empty": "No logs yet~", "logs_empty": "No logs yet~",
"logs_message_total": "Total Messages", "logs_message_total": "Total Messages",
@@ -71,6 +76,7 @@
"month.unit": "Day", "month.unit": "Day",
"move_app": "Move Application", "move_app": "Move Application",
"not_json_file": "Please select a JSON file", "not_json_file": "Please select a JSON file",
"open_vision_function_tip": "Models with icon switches have image recognition capabilities. \nAfter being turned on, the model will parse the pictures in the file link and automatically parse the pictures in the user's question (user question ≤ 500 words).",
"or_drag_JSON": "or drag in JSON file", "or_drag_JSON": "or drag in JSON file",
"paste_config": "Paste Configuration", "paste_config": "Paste Configuration",
"permission.des.manage": "Based on write permissions, you can configure publishing channels, view conversation logs, and assign permissions to the application.", "permission.des.manage": "Based on write permissions, you can configure publishing channels, view conversation logs, and assign permissions to the application.",
@@ -125,14 +131,14 @@
"type.Simple bot": "Simple App", "type.Simple bot": "Simple App",
"type.Workflow bot": "Workflow", "type.Workflow bot": "Workflow",
"upload_file_max_amount": "Maximum File Quantity", "upload_file_max_amount": "Maximum File Quantity",
"upload_file_max_amount_tip": "1. The maximum number of files that can be uploaded at one time.\n2. The maximum number of files remembered by the chat window: each round of dialogue will automatically retrieve files from history, files beyond the range will be forgotten.", "upload_file_max_amount_tip": "Maximum number of files uploaded in a single round of conversation",
"variable.select type_desc": "You can define a global variable that does not need to be filled in by the user.\n\nThe value of this variable can come from the API interface, the Query of the shared link, or assigned through the [Variable Update] module.", "variable.select type_desc": "You can define a global variable that does not need to be filled in by the user.\n\nThe value of this variable can come from the API interface, the Query of the shared link, or assigned through the [Variable Update] module.",
"variable.textarea_type_desc": "Allows users to input up to 4000 characters in the dialogue box.", "variable.textarea_type_desc": "Allows users to input up to 4000 characters in the dialogue box.",
"version.Revert success": "Revert Successful", "version.Revert success": "Revert Successful",
"version_back": "Revert to Original State", "version_back": "Revert to Original State",
"version_copy": "Duplicate", "version_copy": "Duplicate",
"version_initial_copy": "Duplicate - Original State", "version_initial_copy": "Duplicate - Original State",
"vision_model_title": "Enable Image Recognition", "vision_model_title": "Image recognition ability",
"week.Friday": "Friday", "week.Friday": "Friday",
"week.Monday": "Monday", "week.Monday": "Monday",
"week.Saturday": "Saturday", "week.Saturday": "Saturday",
@@ -149,7 +155,7 @@
"workflow.read_files": "Document Parsing", "workflow.read_files": "Document Parsing",
"workflow.read_files_result": "Document Parsing Result", "workflow.read_files_result": "Document Parsing Result",
"workflow.read_files_result_desc": "Original document text, consisting of file names and document content, separated by hyphens between multiple files.", "workflow.read_files_result_desc": "Original document text, consisting of file names and document content, separated by hyphens between multiple files.",
"workflow.read_files_tip": "Parse all uploaded documents in the dialogue and return the corresponding document content.", "workflow.read_files_tip": "Parse the documents uploaded in this round of dialogue and return the corresponding document content",
"workflow.select_description": "Description Text", "workflow.select_description": "Description Text",
"workflow.select_description_placeholder": "For example: \nAre there tomatoes in the fridge?", "workflow.select_description_placeholder": "For example: \nAre there tomatoes in the fridge?",
"workflow.select_description_tip": "You can add a description text to explain the meaning of each option to the user.", "workflow.select_description_tip": "You can add a description text to explain the meaning of each option to the user.",
@@ -159,4 +165,4 @@
"workflow.user_file_input_desc": "Links to documents and images uploaded by users.", "workflow.user_file_input_desc": "Links to documents and images uploaded by users.",
"workflow.user_select": "User Selection", "workflow.user_select": "User Selection",
"workflow.user_select_tip": "This module can configure multiple options for selection during the dialogue. Different options can lead to different workflow branches." "workflow.user_select_tip": "This module can configure multiple options for selection during the dialogue. Different options can lead to different workflow branches."
} }

View File

@@ -1,5 +1,7 @@
{ {
"AI_input_is_empty": "The content passed to the AI node is empty",
"Delete_all": "Clear All Lexicon", "Delete_all": "Clear All Lexicon",
"LLM_model_response_empty": "The model flow response is empty, please check whether the model flow output is normal.",
"chat_history": "Conversation History", "chat_history": "Conversation History",
"chat_input_guide_lexicon_is_empty": "Lexicon not configured yet", "chat_input_guide_lexicon_is_empty": "Lexicon not configured yet",
"citations": "{{num}} References", "citations": "{{num}} References",
@@ -12,6 +14,7 @@
"contextual_preview": "Contextual Preview {{num}} Items", "contextual_preview": "Contextual Preview {{num}} Items",
"csv_input_lexicon_tip": "Only CSV batch import is supported, click to download the template", "csv_input_lexicon_tip": "Only CSV batch import is supported, click to download the template",
"custom_input_guide_url": "Custom Lexicon URL", "custom_input_guide_url": "Custom Lexicon URL",
"dataset_quote_type error": "Knowledge base reference type is wrong, correct type: { datasetId: string }[]",
"delete_all_input_guide_confirm": "Are you sure you want to clear the input guide lexicon?", "delete_all_input_guide_confirm": "Are you sure you want to clear the input guide lexicon?",
"empty_directory": "This directory is empty~", "empty_directory": "This directory is empty~",
"file_amount_over": "Exceeded maximum file quantity {{max}}", "file_amount_over": "Exceeded maximum file quantity {{max}}",
@@ -29,15 +32,18 @@
"multiple_AI_conversations": "Multiple AI Conversations", "multiple_AI_conversations": "Multiple AI Conversations",
"new_input_guide_lexicon": "New Lexicon", "new_input_guide_lexicon": "New Lexicon",
"no_workflow_response": "No workflow data", "no_workflow_response": "No workflow data",
"not_select_file": "No file selected",
"plugins_output": "Plugin Output", "plugins_output": "Plugin Output",
"question_tip": "From top to bottom, the response order of each module", "question_tip": "From top to bottom, the response order of each module",
"response.dataset_concat_length": "Combined total",
"response.node_inputs": "Node Inputs", "response.node_inputs": "Node Inputs",
"select": "Select", "select": "Select",
"select_file": "Upload File", "select_file": "Upload File",
"select_file_img": "Upload file / image", "select_file_img": "Upload file / image",
"select_img": "Upload Image", "select_img": "Upload Image",
"stream_output": "Stream Output", "stream_output": "Stream Output",
"unsupported_file_type": "Unsupported file types",
"upload": "Upload", "upload": "Upload",
"view_citations": "View References", "view_citations": "View References",
"web_site_sync": "Web Site Sync" "web_site_sync": "Web Site Sync"
} }

View File

@@ -69,8 +69,12 @@
"code_error.system_error.community_version_num_limit": "Exceeded Open Source Version Limit, Please Upgrade to Commercial Version: https://tryfastgpt.ai", "code_error.system_error.community_version_num_limit": "Exceeded Open Source Version Limit, Please Upgrade to Commercial Version: https://tryfastgpt.ai",
"code_error.team_error.ai_points_not_enough": "Insufficient AI Points", "code_error.team_error.ai_points_not_enough": "Insufficient AI Points",
"code_error.team_error.app_amount_not_enough": "Application Limit Reached", "code_error.team_error.app_amount_not_enough": "Application Limit Reached",
"code_error.team_error.cannot_delete_default_group": "Cannot delete default group",
"code_error.team_error.dataset_amount_not_enough": "Dataset Limit Reached", "code_error.team_error.dataset_amount_not_enough": "Dataset Limit Reached",
"code_error.team_error.dataset_size_not_enough": "Insufficient Dataset Capacity, Please Expand", "code_error.team_error.dataset_size_not_enough": "Insufficient Dataset Capacity, Please Expand",
"code_error.team_error.group_name_duplicate": "Duplicate group name",
"code_error.team_error.group_name_empty": "Group name cannot be empty",
"code_error.team_error.group_not_exist": "Group does not exist",
"code_error.team_error.over_size": "error.team.overSize", "code_error.team_error.over_size": "error.team.overSize",
"code_error.team_error.plugin_amount_not_enough": "Plugin Limit Reached", "code_error.team_error.plugin_amount_not_enough": "Plugin Limit Reached",
"code_error.team_error.re_rank_not_enough": "Unauthorized to Use Re-Rank", "code_error.team_error.re_rank_not_enough": "Unauthorized to Use Re-Rank",
@@ -1115,7 +1119,6 @@
"tag_list": "Tag List", "tag_list": "Tag List",
"team_tag": "Team Tag", "team_tag": "Team Tag",
"textarea_variable_picker_tip": "Enter \"/\" to select a variable", "textarea_variable_picker_tip": "Enter \"/\" to select a variable",
"undefined_var": "Referenced an undefined variable, add it automatically?",
"unit.character": "Character", "unit.character": "Character",
"unit.minute": "Minute", "unit.minute": "Minute",
"unusable_variable": "No Usable Variables", "unusable_variable": "No Usable Variables",
@@ -1200,5 +1203,7 @@
"user.type": "Type", "user.type": "Type",
"verification": "Verification", "verification": "Verification",
"xx_search_result": "{{key}} Search Results", "xx_search_result": "{{key}} Search Results",
"yes": "Yes" "yes": "Yes",
"yesterday": "yesterday",
"yesterday_detail_time": "Yesterday {{time}}"
} }

View File

@@ -1,5 +1,6 @@
{ {
"Array_element": "Array element", "Array_element": "Array element",
"Array_element_index": "Index",
"Code": "Code", "Code": "Code",
"Confirm_sync_node": "It will be updated to the latest node configuration and fields that do not exist in the template will be deleted (including all custom fields).\n\nIf the fields are complex, it is recommended that you copy a node first and then update the original node to facilitate parameter copying.", "Confirm_sync_node": "It will be updated to the latest node configuration and fields that do not exist in the template will be deleted (including all custom fields).\n\nIf the fields are complex, it is recommended that you copy a node first and then update the original node to facilitate parameter copying.",
"Node.Open_Node_Course": "Open node course", "Node.Open_Node_Course": "Open node course",
@@ -122,13 +123,15 @@
"pass_returned_object_as_output_to_next_nodes": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", "pass_returned_object_as_output_to_next_nodes": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.",
"plugin.Instruction_Tip": "You can configure an instruction to explain the purpose of the plugin. This instruction will be displayed each time the plugin is used. Supports standard Markdown syntax.", "plugin.Instruction_Tip": "You can configure an instruction to explain the purpose of the plugin. This instruction will be displayed each time the plugin is used. Supports standard Markdown syntax.",
"plugin.Instructions": "Instructions", "plugin.Instructions": "Instructions",
"plugin.global_file_input": "File links (deprecated)",
"plugin_file_abandon_tip": "Plugin global file upload has been deprecated, please adjust it as soon as possible. \nRelated functions can be achieved through plug-in input and adding image type input.",
"plugin_input": "Plugin Input", "plugin_input": "Plugin Input",
"plugin_output_tool": "When the plug-in is executed as a tool, whether this field responds as a result of the tool", "plugin_output_tool": "When the plug-in is executed as a tool, whether this field responds as a result of the tool",
"question_classification": "Question Classification", "question_classification": "Question Classification",
"question_optimization": "Question Optimization", "question_optimization": "Question Optimization",
"quote_content_placeholder": "The structure of the reference content can be customized to better suit different scenarios. \nSome variables can be used for template configuration\n\n{{q}} - main content\n\n{{a}} - auxiliary data\n\n{{source}} - source name\n\n{{sourceId}} - source ID\n\n{{index}} - nth reference", "quote_content_placeholder": "The structure of the reference content can be customized to better suit different scenarios. \nSome variables can be used for template configuration\n\n{{q}} - main content\n\n{{a}} - auxiliary data\n\n{{source}} - source name\n\n{{sourceId}} - source ID\n\n{{index}} - nth reference",
"quote_content_tip": "The structure of the reference content can be customized to better suit different scenarios. Some variables can be used for template configuration:\n\n{{q}} - main content\n{{a}} - auxiliary data\n{{source}} - source name\n{{sourceId}} - source ID\n{{index}} - nth reference\nThey are all optional and the following are the default values:\n\n{{default}}", "quote_content_tip": "The structure of the reference content can be customized to better suit different scenarios. Some variables can be used for template configuration:\n\n{{q}} - main content\n{{a}} - auxiliary data\n{{source}} - source name\n{{sourceId}} - source ID\n{{index}} - nth reference\nThey are all optional and the following are the default values:\n\n{{default}}",
"quote_num": "Quote {{num}}", "quote_num": "Dataset",
"quote_prompt_tip": "You can use {{quote}} to insert a quote content template and {{question}} to insert a question (Role=user).\n\nThe following are the default values:\n\n{{default}}", "quote_prompt_tip": "You can use {{quote}} to insert a quote content template and {{question}} to insert a question (Role=user).\n\nThe following are the default values:\n\n{{default}}",
"quote_role_system_tip": "Please note that the {{question}} variable is removed from the \"Quote Template Prompt Words\"", "quote_role_system_tip": "Please note that the {{question}} variable is removed from the \"Quote Template Prompt Words\"",
"quote_role_user_tip": "Please pay attention to adding the {{question}} variable in the \"Quote Template Prompt Word\"", "quote_role_user_tip": "Please pay attention to adding the {{question}} variable in the \"Quote Template Prompt Word\"",
@@ -190,4 +193,4 @@
"workflow.Switch_success": "Switch Successful", "workflow.Switch_success": "Switch Successful",
"workflow.Team cloud": "Team Cloud", "workflow.Team cloud": "Team Cloud",
"workflow.exit_tips": "Your changes have not been saved. 'Exit directly' will not save your edits." "workflow.exit_tips": "Your changes have not been saved. 'Exit directly' will not save your edits."
} }

View File

@@ -1,5 +1,8 @@
{ {
"Role_setting": "权限设置",
"Run": "运行", "Run": "运行",
"Team Tags Set": "团队标签",
"Team_Tags": "团队标签",
"ai_settings": "AI 配置", "ai_settings": "AI 配置",
"all_apps": "全部应用", "all_apps": "全部应用",
"app.Version name": "版本名称", "app.Version name": "版本名称",
@@ -27,6 +30,7 @@
"cron.every_month": "每月执行", "cron.every_month": "每月执行",
"cron.every_week": "每周执行", "cron.every_week": "每周执行",
"cron.interval": "间隔执行", "cron.interval": "间隔执行",
"dataset_search_tool_description": "调用“语义检索”和“全文检索”能力,从“知识库”中查找可能与问题相关的参考内容。优先调用该工具来辅助回答用户的问题。",
"day": "日", "day": "日",
"document_quote": "文档引用", "document_quote": "文档引用",
"document_quote_tip": "通常用于接受用户上传的文档内容(这需要文档解析),也可以用于引用其他字符串数据。", "document_quote_tip": "通常用于接受用户上传的文档内容(这需要文档解析),也可以用于引用其他字符串数据。",
@@ -37,6 +41,7 @@
"export_config_successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据", "export_config_successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据",
"export_configs": "导出配置", "export_configs": "导出配置",
"feedback_count": "用户反馈", "feedback_count": "用户反馈",
"file_quote_link": "文件链接",
"file_recover": "文件将覆盖当前内容", "file_recover": "文件将覆盖当前内容",
"file_upload": "文件上传", "file_upload": "文件上传",
"file_upload_tip": "开启后,可以上传文档/图片。文档保留7天图片保留15天。使用该功能可能产生较多额外费用。为保证使用体验使用该功能时请选择上下文长度较大的AI模型。", "file_upload_tip": "开启后,可以上传文档/图片。文档保留7天图片保留15天。使用该功能可能产生较多额外费用。为保证使用体验使用该功能时请选择上下文长度较大的AI模型。",
@@ -44,7 +49,7 @@
"go_to_chat": "去对话", "go_to_chat": "去对话",
"go_to_run": "去运行", "go_to_run": "去运行",
"image_upload": "图片上传", "image_upload": "图片上传",
"image_upload_tip": "请确保选择可处理图片的视觉模型", "image_upload_tip": "如何启动模型图片识别能力",
"import_configs": "导入配置", "import_configs": "导入配置",
"import_configs_failed": "导入配置失败,请确保配置正常!", "import_configs_failed": "导入配置失败,请确保配置正常!",
"import_configs_success": "导入成功", "import_configs_success": "导入成功",
@@ -57,8 +62,8 @@
"interval.per_hour": "每小时", "interval.per_hour": "每小时",
"intro": "是一个大模型应用编排系统,提供开箱即用的数据处理、模型调用等能力,可以快速的构建知识库并通过 Flow 可视化进行工作流编排,实现复杂的知识库场景!", "intro": "是一个大模型应用编排系统,提供开箱即用的数据处理、模型调用等能力,可以快速的构建知识库并通过 Flow 可视化进行工作流编排,实现复杂的知识库场景!",
"llm_not_support_vision": "该模型不支持图片识别", "llm_not_support_vision": "该模型不支持图片识别",
"llm_use_vision": "启用图片识别", "llm_use_vision": "图片识别",
"llm_use_vision_tip": "启用图片识别后,模型会自动接收来自“对话框上传”的图片,以及“用户问题中的图片链接。", "llm_use_vision_tip": "点击模型选择后,可以看到模型是否支持图片识别以及控制是否启动图片识别的能力。启动图片识别后,模型会读取文件链接里图片内容,并且如果用户问题少于 500 字,会自动解析用户问题中的图片。",
"logs_chat_user": "使用者", "logs_chat_user": "使用者",
"logs_empty": "还没有日志噢~", "logs_empty": "还没有日志噢~",
"logs_message_total": "消息总数", "logs_message_total": "消息总数",
@@ -69,9 +74,10 @@
"module.type": "\"{{type}}\"类型\n{{description}}", "module.type": "\"{{type}}\"类型\n{{description}}",
"modules.Title is required": "模块名不能为空", "modules.Title is required": "模块名不能为空",
"month.unit": "号", "month.unit": "号",
"move_app": "移动应用",
"move.hint": "移动后,所选应用/文件夹将继承新文件夹的权限设置,原先的权限设置失效。", "move.hint": "移动后,所选应用/文件夹将继承新文件夹的权限设置,原先的权限设置失效。",
"move_app": "移动应用",
"not_json_file": "请选择JSON文件", "not_json_file": "请选择JSON文件",
"open_vision_function_tip": "有图示开关的模型即拥有图片识别能力。若开启模型会解析文件链接里的图片并自动解析用户问题中的图片用户问题≤500字时生效。",
"or_drag_JSON": "或拖入JSON文件", "or_drag_JSON": "或拖入JSON文件",
"paste_config": "粘贴配置", "paste_config": "粘贴配置",
"permission.des.manage": "写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限", "permission.des.manage": "写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限",
@@ -126,14 +132,14 @@
"type.Simple bot": "简易应用", "type.Simple bot": "简易应用",
"type.Workflow bot": "工作流", "type.Workflow bot": "工作流",
"upload_file_max_amount": "最大文件数量", "upload_file_max_amount": "最大文件数量",
"upload_file_max_amount_tip": "1.单次上传文件的最大数量。\n2.对话窗口记忆的最大文件数量:每轮对话会自动获取历史中的文件,超出范围的文件会被遗忘。", "upload_file_max_amount_tip": "单轮对话中最大上传文件数量",
"variable.select type_desc": "可以为工作流定义全局变量,常用临时缓存。赋值的方式包括:\n1. 从对话页面的 query 参数获取。\n2. 通过 API 的 variables 对象传递。\n3. 通过【变量更新】节点进行赋值。", "variable.select type_desc": "可以为工作流定义全局变量,常用临时缓存。赋值的方式包括:\n1. 从对话页面的 query 参数获取。\n2. 通过 API 的 variables 对象传递。\n3. 通过【变量更新】节点进行赋值。",
"variable.textarea_type_desc": "允许用户最多输入4000字的对话框。", "variable.textarea_type_desc": "允许用户最多输入4000字的对话框。",
"version.Revert success": "回滚成功", "version.Revert success": "回滚成功",
"version_back": "回到初始状态", "version_back": "回到初始状态",
"version_copy": "副本", "version_copy": "副本",
"version_initial_copy": "副本-初始状态", "version_initial_copy": "副本-初始状态",
"vision_model_title": "启用图片识别", "vision_model_title": "图片识别能力",
"week.Friday": "星期五", "week.Friday": "星期五",
"week.Monday": "星期一", "week.Monday": "星期一",
"week.Saturday": "星期六", "week.Saturday": "星期六",
@@ -150,7 +156,7 @@
"workflow.read_files": "文档解析", "workflow.read_files": "文档解析",
"workflow.read_files_result": "文档解析结果", "workflow.read_files_result": "文档解析结果",
"workflow.read_files_result_desc": "文档原文,由文件名和文档内容组成,多个文件之间通过横线隔开。", "workflow.read_files_result_desc": "文档原文,由文件名和文档内容组成,多个文件之间通过横线隔开。",
"workflow.read_files_tip": "解析对话中所有上传的文档,并返回对应文档内容", "workflow.read_files_tip": "解析本轮对话上传的文档,并返回对应文档内容",
"workflow.select_description": "说明文字", "workflow.select_description": "说明文字",
"workflow.select_description_placeholder": "例如: \n冰箱里是否有西红柿", "workflow.select_description_placeholder": "例如: \n冰箱里是否有西红柿",
"workflow.select_description_tip": "你可以添加一段说明文字,用以向用户说明每个选项代表的含义。", "workflow.select_description_tip": "你可以添加一段说明文字,用以向用户说明每个选项代表的含义。",
@@ -160,4 +166,4 @@
"workflow.user_file_input_desc": "用户上传的文档和图片链接", "workflow.user_file_input_desc": "用户上传的文档和图片链接",
"workflow.user_select": "用户选择", "workflow.user_select": "用户选择",
"workflow.user_select_tip": "该模块可配置多个选项,以供对话时选择。不同选项可导向不同工作流支线" "workflow.user_select_tip": "该模块可配置多个选项,以供对话时选择。不同选项可导向不同工作流支线"
} }

View File

@@ -1,5 +1,7 @@
{ {
"AI_input_is_empty": "传入AI 节点的内容为空",
"Delete_all": "清空词库", "Delete_all": "清空词库",
"LLM_model_response_empty": "模型流响应为空,请检查模型流输出是否正常",
"chat_history": "聊天记录", "chat_history": "聊天记录",
"chat_input_guide_lexicon_is_empty": "还没有配置词库", "chat_input_guide_lexicon_is_empty": "还没有配置词库",
"citations": "{{num}}条引用", "citations": "{{num}}条引用",
@@ -12,6 +14,7 @@
"contextual_preview": "上下文预览 {{num}} 条", "contextual_preview": "上下文预览 {{num}} 条",
"csv_input_lexicon_tip": "仅支持 CSV 批量导入,点击下载模板", "csv_input_lexicon_tip": "仅支持 CSV 批量导入,点击下载模板",
"custom_input_guide_url": "自定义词库地址", "custom_input_guide_url": "自定义词库地址",
"dataset_quote_type error": "知识库引用类型错误,正确类型:{ datasetId: string }[]",
"delete_all_input_guide_confirm": "确定要清空输入引导词库吗?", "delete_all_input_guide_confirm": "确定要清空输入引导词库吗?",
"empty_directory": "这个目录已经没东西可选了~", "empty_directory": "这个目录已经没东西可选了~",
"file_amount_over": "超出最大文件数量 {{max}}", "file_amount_over": "超出最大文件数量 {{max}}",
@@ -29,16 +32,19 @@
"multiple_AI_conversations": "多组 AI 对话", "multiple_AI_conversations": "多组 AI 对话",
"new_input_guide_lexicon": "新词库", "new_input_guide_lexicon": "新词库",
"no_workflow_response": "没有运行数据", "no_workflow_response": "没有运行数据",
"not_select_file": "未选择文件",
"plugins_output": "插件输出", "plugins_output": "插件输出",
"question_tip": "从上到下,为各个模块的响应顺序", "question_tip": "从上到下,为各个模块的响应顺序",
"response.child total points": "子工作流积分消耗", "response.child total points": "子工作流积分消耗",
"response.dataset_concat_length": "合并后总数",
"response.node_inputs": "节点输入", "response.node_inputs": "节点输入",
"select": "选择", "select": "选择",
"select_file": "上传文件", "select_file": "上传文件",
"select_file_img": "上传文件/图片", "select_file_img": "上传文件/图片",
"select_img": "上传图片", "select_img": "上传图片",
"stream_output": "流输出", "stream_output": "流输出",
"unsupported_file_type": "不支持的文件类型",
"upload": "上传", "upload": "上传",
"view_citations": "查看引用", "view_citations": "查看引用",
"web_site_sync": "Web站点同步" "web_site_sync": "Web站点同步"
} }

View File

@@ -18,6 +18,9 @@
"FAQ.switch_package_a": "套餐使用规则为优先使用更高级的套餐,因此,购买的新套餐若比当前套餐更高级,则新套餐立即生效:否则将继续使用当前套餐。", "FAQ.switch_package_a": "套餐使用规则为优先使用更高级的套餐,因此,购买的新套餐若比当前套餐更高级,则新套餐立即生效:否则将继续使用当前套餐。",
"FAQ.switch_package_q": "是否切换订阅套餐?", "FAQ.switch_package_q": "是否切换订阅套餐?",
"Folder": "文件夹", "Folder": "文件夹",
"just_now": "刚刚",
"yesterday": "昨天",
"yesterday_detail_time": "昨天 {{time}}",
"Login": "登录", "Login": "登录",
"Move": "移动", "Move": "移动",
"Name": "名称", "Name": "名称",
@@ -1122,7 +1125,6 @@
"tag_list": "标签列表", "tag_list": "标签列表",
"team_tag": "团队标签", "team_tag": "团队标签",
"textarea_variable_picker_tip": "输入\"/\"可选择变量", "textarea_variable_picker_tip": "输入\"/\"可选择变量",
"undefined_var": "引用了未定义的变量,是否自动添加?",
"unit.character": "字符", "unit.character": "字符",
"unit.minute": "分钟", "unit.minute": "分钟",
"unusable_variable": "无可用变量", "unusable_variable": "无可用变量",

View File

@@ -1,5 +1,6 @@
{ {
"Array_element": "数组元素", "Array_element": "数组元素",
"Array_element_index": "下标",
"Code": "代码", "Code": "代码",
"Confirm_sync_node": "将会更新至最新的节点配置,不存在模板中的字段将会被删除(包括所有自定义字段)。\n如果字段较为复杂建议您先复制一份节点再更新原来的节点便于参数复制。", "Confirm_sync_node": "将会更新至最新的节点配置,不存在模板中的字段将会被删除(包括所有自定义字段)。\n如果字段较为复杂建议您先复制一份节点再更新原来的节点便于参数复制。",
"Node.Open_Node_Course": "查看节点教程", "Node.Open_Node_Course": "查看节点教程",
@@ -123,13 +124,15 @@
"pass_returned_object_as_output_to_next_nodes": "将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key", "pass_returned_object_as_output_to_next_nodes": "将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key",
"plugin.Instruction_Tip": "可以配置一段说明,以解释该插件的用途。每次使用插件前,会显示该段说明。支持标准 Markdown 语法。", "plugin.Instruction_Tip": "可以配置一段说明,以解释该插件的用途。每次使用插件前,会显示该段说明。支持标准 Markdown 语法。",
"plugin.Instructions": "使用说明", "plugin.Instructions": "使用说明",
"plugin.global_file_input": "文件链接(弃用)",
"plugin_file_abandon_tip": "插件全局文件上传已弃用,请尽快调整。可以通过插件输入,添加图片类型输入来实现相关功能。",
"plugin_input": "插件输入", "plugin_input": "插件输入",
"plugin_output_tool": "插件作为工具执行时,该字段是否作为工具响应结果", "plugin_output_tool": "插件作为工具执行时,该字段是否作为工具响应结果",
"question_classification": "问题分类", "question_classification": "问题分类",
"question_optimization": "问题优化", "question_optimization": "问题优化",
"quote_content_placeholder": "可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置\n{{q}} - 主要内容\n{{a}} - 辅助数据\n{{source}} - 来源名\n{{sourceId}} - 来源ID\n{{index}} - 第 n 个引用", "quote_content_placeholder": "可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置\n{{q}} - 主要内容\n{{a}} - 辅助数据\n{{source}} - 来源名\n{{sourceId}} - 来源ID\n{{index}} - 第 n 个引用",
"quote_content_tip": "可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置\n{{q}} - 主要内容\n{{a}} - 辅助数据\n{{source}} - 来源名\n{{sourceId}} - 来源ID\n{{index}} - 第 n 个引用\n他们都是可选的下面是默认值\n{{default}}", "quote_content_tip": "可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置\n{{q}} - 主要内容\n{{a}} - 辅助数据\n{{source}} - 来源名\n{{sourceId}} - 来源ID\n{{index}} - 第 n 个引用\n他们都是可选的下面是默认值\n{{default}}",
"quote_num": "引用{{num}}", "quote_num": "引用",
"quote_prompt_tip": "可以用 {{quote}} 来插入引用内容模板,使用 {{question}} 来插入问题(Role=user)。\n下面是默认值\n{{default}}", "quote_prompt_tip": "可以用 {{quote}} 来插入引用内容模板,使用 {{question}} 来插入问题(Role=user)。\n下面是默认值\n{{default}}",
"quote_role_system_tip": "请注意从“引用模板提示词”中移除 {{question}} 变量", "quote_role_system_tip": "请注意从“引用模板提示词”中移除 {{question}} 变量",
"quote_role_user_tip": "请注意在“引用模板提示词”中添加 {{question}} 变量", "quote_role_user_tip": "请注意在“引用模板提示词”中添加 {{question}} 变量",
@@ -192,4 +195,4 @@
"workflow.Switch_success": "切换成功", "workflow.Switch_success": "切换成功",
"workflow.Team cloud": "团队云端", "workflow.Team cloud": "团队云端",
"workflow.exit_tips": "您的更改尚未保存,「直接退出」将不会保存您的编辑记录。" "workflow.exit_tips": "您的更改尚未保存,「直接退出」将不会保存您的编辑记录。"
} }

View File

@@ -46,7 +46,8 @@ const Button = defineStyleConfig({
px: '2', px: '2',
py: '0', py: '0',
h: '24px', h: '24px',
fontWeight: 'normal', minH: '24px',
fontWeight: 'medium',
borderRadius: 'sm' borderRadius: 'sm'
}, },
xsSquare: { xsSquare: {
@@ -54,24 +55,27 @@ const Button = defineStyleConfig({
px: '0', px: '0',
py: '0', py: '0',
h: '24px', h: '24px',
minH: '24px',
w: '24px', w: '24px',
fontWeight: 'normal', fontWeight: 'medium',
borderRadius: 'sm' borderRadius: 'sm'
}, },
sm: { sm: {
fontSize: 'sm', fontSize: 'sm',
px: '3', px: '3',
py: 0, py: 0,
fontWeight: 'normal', fontWeight: 'medium',
h: '30px', h: '30px',
minH: '30px',
borderRadius: 'sm' borderRadius: 'sm'
}, },
smSquare: { smSquare: {
fontSize: 'sm', fontSize: 'sm',
px: '0', px: '0',
py: 0, py: 0,
fontWeight: 'normal', fontWeight: 'medium',
h: '30px', h: '30px',
minH: '30px',
w: '30px', w: '30px',
borderRadius: 'sm' borderRadius: 'sm'
}, },
@@ -80,34 +84,38 @@ const Button = defineStyleConfig({
px: '4', px: '4',
py: 0, py: 0,
h: '34px', h: '34px',
fontWeight: 'normal', minH: '34px',
borderRadius: 'md' fontWeight: 'medium',
borderRadius: 'sm'
}, },
mdSquare: { mdSquare: {
fontSize: 'sm', fontSize: 'sm',
px: '0', px: '0',
py: 0, py: 0,
h: '34px', h: '34px',
minH: '34px',
w: '34px', w: '34px',
fontWeight: 'normal', fontWeight: 'medium',
borderRadius: 'md' borderRadius: 'sm'
}, },
lg: { lg: {
fontSize: 'md', fontSize: 'md',
px: '4', px: '4',
py: 0, py: 0,
h: '40px', h: '40px',
fontWeight: 'normal', minH: '40px',
borderRadius: 'lg' fontWeight: 'medium',
borderRadius: 'md'
}, },
lgSquare: { lgSquare: {
fontSize: 'md', fontSize: 'md',
px: '0', px: '0',
py: 0, py: 0,
h: '40px', h: '40px',
minH: '40px',
w: '40px', w: '40px',
fontWeight: 'normal', fontWeight: 'medium',
borderRadius: 'lg' borderRadius: 'md'
} }
}, },
variants: { variants: {
@@ -175,6 +183,16 @@ const Button = defineStyleConfig({
color: 'myGray.600 !important' color: 'myGray.600 !important'
} }
}, },
whitePrimaryOutline: {
border: '1px solid',
borderColor: 'myGray.250',
bg: 'white',
transition: 'background 0.1s',
_hover: {
color: 'primary.600',
borderColor: 'primary.300'
}
},
whitePrimary: { whitePrimary: {
color: 'myGray.600', color: 'myGray.600',
border: '1px solid', border: '1px solid',
@@ -288,12 +306,18 @@ const Input: ComponentStyleConfig = {
sm: defineStyle({ sm: defineStyle({
field: { field: {
h: '32px', h: '32px',
borderRadius: 'md' borderRadius: 'sm'
} }
}), }),
md: defineStyle({ md: defineStyle({
field: { field: {
h: '34px', h: '36px',
borderRadius: 'sm'
}
}),
lg: defineStyle({
field: {
h: '40px',
borderRadius: 'md' borderRadius: 'md'
} }
}) })
@@ -303,11 +327,15 @@ const Input: ComponentStyleConfig = {
field: { field: {
border: '1px solid', border: '1px solid',
borderColor: 'borderColor.low', borderColor: 'borderColor.low',
px: 3,
_focus: { _focus: {
borderColor: 'primary.500', borderColor: 'primary.500',
boxShadow: shadowLight, boxShadow: shadowLight,
bg: 'white' bg: 'white'
}, },
_hover: {
borderColor: 'primary.300'
},
_disabled: { _disabled: {
color: 'myGray.400', color: 'myGray.400',
bg: 'myWhite.300' bg: 'myWhite.300'
@@ -326,14 +354,14 @@ const NumberInput = numInputMultiStyle({
sm: defineStyle({ sm: defineStyle({
field: { field: {
h: '32px', h: '32px',
borderRadius: 'md', borderRadius: 'sm',
fontsize: 'sm' fontsize: 'sm'
} }
}), }),
md: defineStyle({ lg: defineStyle({
field: { field: {
h: '40px', h: '40px',
borderRadius: 'md', borderRadius: 'sm',
fontsize: 'sm' fontsize: 'sm'
} }
}) })
@@ -347,7 +375,7 @@ const NumberInput = numInputMultiStyle({
_focus: { _focus: {
borderColor: 'primary.500 !important', borderColor: 'primary.500 !important',
boxShadow: `${shadowLight} !important`, boxShadow: `${shadowLight} !important`,
bg: 'transparent' bg: 'white'
}, },
_disabled: { _disabled: {
color: 'myGray.400 !important', color: 'myGray.400 !important',
@@ -356,10 +384,12 @@ const NumberInput = numInputMultiStyle({
}, },
stepper: { stepper: {
bg: 'transparent', bg: 'transparent',
border: 'none',
color: 'myGray.600', color: 'myGray.600',
_active: { _active: {
color: 'primary.500' color: 'primary.500'
},
_hover: {
bg: 'myGray.100'
} }
} }
}) })
@@ -373,16 +403,24 @@ const Textarea: ComponentStyleConfig = {
variants: { variants: {
outline: { outline: {
border: '1px solid', border: '1px solid',
px: 3,
borderRadius: 'md', borderRadius: 'md',
borderColor: 'myGray.200', borderColor: 'myGray.200',
fontSize: 'sm', fontSize: 'sm',
_hover: { _hover: {
borderColor: '' borderColor: 'primary.300'
}, },
_focus: { _focus: {
borderColor: 'primary.500', borderColor: 'primary.500',
boxShadow: shadowLight, boxShadow: shadowLight,
bg: 'white' bg: 'white'
},
'&::-webkit-resizer': {
background: "url('/icon/resizer.svg') no-repeat",
backgroundSize: '11px',
backgroundPosition: 'right bottom',
backgroundPositionX: 'right 12px',
backgroundPositionY: 'bottom 12px'
} }
} }
}, },

View File

@@ -16,6 +16,9 @@ OPENAI_BASE_URL=https://api.openai.com/v1
# 通用key。可以是 openai 的也可以是 oneapi 的。 # 通用key。可以是 openai 的也可以是 oneapi 的。
# 此处逻辑:优先走 ONEAPI_URL如果填写了 ONEAPI_URLkey 也需要是 ONEAPI 的 key # 此处逻辑:优先走 ONEAPI_URL如果填写了 ONEAPI_URLkey 也需要是 ONEAPI 的 key
CHAT_API_KEY=sk-xxxx CHAT_API_KEY=sk-xxxx
# 是否将图片转成 base64 传递给模型,本地开发和内网环境使用共有模型时候需要设置为 true
MULTIPLE_DATA_TO_BASE64=true
# mongo 数据库连接参数,本地开发连接远程数据库时,可能需要增加 directConnection=true 参数,才能连接上。 # mongo 数据库连接参数,本地开发连接远程数据库时,可能需要增加 directConnection=true 参数,才能连接上。
MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin
@@ -32,6 +35,8 @@ SANDBOX_URL=http://localhost:3001
PRO_URL= PRO_URL=
# 页面的地址,用于自动补全相对路径资源的 domain # 页面的地址,用于自动补全相对路径资源的 domain
FE_DOMAIN=http://localhost:3000 FE_DOMAIN=http://localhost:3000
# 二级路由,需要打包时候就确定
# NEXT_PUBLIC_BASE_URL=/fastai
# 日志等级: debug, info, warn, error # 日志等级: debug, info, warn, error
LOG_LEVEL=debug LOG_LEVEL=debug
@@ -41,4 +46,12 @@ STORE_LOG_LEVEL=warn
# 工作流最大运行次数,避免极端的死循环情况 # 工作流最大运行次数,避免极端的死循环情况
WORKFLOW_MAX_RUN_TIMES=500 WORKFLOW_MAX_RUN_TIMES=500
# 循环最大运行次数,避免极端的死循环情况 # 循环最大运行次数,避免极端的死循环情况
WORKFLOW_MAX_LOOP_TIMES=50 WORKFLOW_MAX_LOOP_TIMES=50
# 对话日志推送服务
# # 日志服务地址
# CHAT_LOG_URL=http://localhost:8080
# # 日志推送间隔
# CHAT_LOG_INTERVAL=10000
# # 日志来源前缀
# SOURCE_ID_PREFIX=fastgpt-

View File

@@ -6,8 +6,6 @@ ARG proxy
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache libc6-compat && npm install -g pnpm@9.4.0 RUN apk add --no-cache libc6-compat && npm install -g pnpm@9.4.0
# if proxy exists, set proxy
RUN [ -z "$proxy" ] || pnpm config set registry https://registry.npmmirror.com
# copy packages and one project # copy packages and one project
COPY pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ COPY pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
@@ -16,13 +14,19 @@ COPY ./projects/app/package.json ./projects/app/package.json
RUN [ -f pnpm-lock.yaml ] || (echo "Lockfile not found." && exit 1) RUN [ -f pnpm-lock.yaml ] || (echo "Lockfile not found." && exit 1)
RUN pnpm i # if proxy exists, set proxy
RUN if [ -z "$proxy" ]; then \
pnpm i; \
else \
pnpm i --registry=https://registry.npmmirror.com; \
fi
# --------- builder ----------- # --------- builder -----------
FROM node:20.14.0-alpine AS builder FROM node:20.14.0-alpine AS builder
WORKDIR /app WORKDIR /app
ARG proxy ARG proxy
ARG base_url
# copy common node_modules and one project node_modules # copy common node_modules and one project node_modules
COPY package.json pnpm-workspace.yaml .npmrc tsconfig.json ./ COPY package.json pnpm-workspace.yaml .npmrc tsconfig.json ./
@@ -36,6 +40,7 @@ RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /
RUN apk add --no-cache libc6-compat && npm install -g pnpm@9.4.0 RUN apk add --no-cache libc6-compat && npm install -g pnpm@9.4.0
ENV NODE_OPTIONS="--max-old-space-size=4096" ENV NODE_OPTIONS="--max-old-space-size=4096"
ENV NEXT_PUBLIC_BASE_URL=$base_url
RUN pnpm --filter=app build RUN pnpm --filter=app build
# --------- runner ----------- # --------- runner -----------
@@ -43,6 +48,7 @@ FROM node:20.14.0-alpine AS runner
WORKDIR /app WORKDIR /app
ARG proxy ARG proxy
ARG base_url
# create user and use it # create user and use it
RUN addgroup --system --gid 1001 nodejs RUN addgroup --system --gid 1001 nodejs
@@ -78,6 +84,7 @@ RUN chown -R nextjs:nodejs /app/data
ENV NODE_ENV=production ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000 ENV PORT=3000
ENV NEXT_PUBLIC_BASE_URL=$base_url
EXPOSE 3000 EXPOSE 3000

View File

@@ -6,6 +6,7 @@ const isDev = process.env.NODE_ENV === 'development';
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
basePath: process.env.NEXT_PUBLIC_BASE_URL,
i18n, i18n,
output: 'standalone', output: 'standalone',
reactStrictMode: isDev ? false : true, reactStrictMode: isDev ? false : true,

View File

@@ -1,12 +1,37 @@
<svg width="1041" height="1348" viewBox="0 0 1041 1348" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M340.837 0.33933L681.068 0.338989V0.455643C684.032 0.378397 686.999 0.339702 689.967 0.339702C735.961 0.3397 781.504 9.62899 823.997 27.6772C866.49 45.7254 905.099 72.1791 937.622 105.528C970.144 138.877 995.942 178.467 1013.54 222.04C1031.14 265.612 1040.2 312.312 1040.2 359.474L340.836 359.474L340.836 1347.84C296.157 1347.84 251.914 1338.55 210.636 1320.49C169.357 1302.43 131.85 1275.95 100.257 1242.58C68.6636 1209.21 43.6023 1169.59 26.5041 1125.99C11.3834 1087.43 2.75216 1046.42 0.957956 1004.81H0.605869L0.605897 368.098H0.70363C0.105752 341.831 2.23741 315.443 7.14306 289.411C20.2709 219.745 52.6748 155.754 100.257 105.528C147.839 55.3017 208.462 21.0975 274.461 7.24017C296.426 2.62833 318.657 0.339101 340.837 0.33933Z" fill="url(#paint0_linear_1172_228)"/> <path d="M21.5938 10.9C21.5938 9.45229 21.1475 8.03706 20.3113 6.8333C19.4751 5.62954 18.2866 4.69132 16.8961 4.13729C15.5056 3.58326 13.9755 3.4383 12.4993 3.72075C11.0231 4.00319 9.66715 4.70034 8.60288 5.72406C7.53861 6.74777 6.81384 8.05206 6.52021 9.47199C6.22658 10.8919 6.37728 12.3637 6.95326 13.7013C7.52923 15.0388 8.50462 16.182 9.75606 16.9864C11.0075 17.7907 12.4788 18.22 13.9839 18.22V10.9H21.5938Z" fill="url(#paint0_linear_0_5622)"/>
<path d="M633.639 904.645H513.029V576.37H635.422V576.377C678.161 576.607 720.454 585.093 759.951 601.37C799.997 617.874 836.384 642.064 867.033 672.559C897.683 703.054 921.996 739.257 938.583 779.101C955.171 818.944 963.709 861.648 963.709 904.775H633.639V904.645Z" fill="url(#paint1_linear_1172_228)"/> <path d="M29.6263 10.9C29.6263 9.93877 29.4498 8.98691 29.1069 8.09882C28.764 7.21072 28.2613 6.40377 27.6277 5.72405C26.9941 5.04433 26.2418 4.50515 25.414 4.13729C24.5861 3.76943 23.6987 3.58009 22.8027 3.58009C21.9066 3.58009 21.0192 3.76943 20.1913 4.13729C19.3635 4.50515 18.6112 5.04434 17.9776 5.72406C17.344 6.40378 16.8413 7.21072 16.4984 8.09882C16.1555 8.98692 15.979 9.93877 15.979 10.9L29.6263 10.9Z" fill="url(#paint1_linear_0_5622)"/>
<path d="M28.3745 22.634C28.3745 21.8174 28.2342 21.0087 27.9617 20.2543C27.6892 19.4998 27.2898 18.8143 26.7863 18.2368C26.2828 17.6594 25.685 17.2013 25.0272 16.8888C24.3693 16.5763 23.6642 16.4155 22.9521 16.4155V22.634H28.3745Z" fill="url(#paint2_linear_0_5622)"/>
<path d="M13.9836 20.3912C12.9843 20.3912 11.9947 20.5635 11.0715 20.8985C10.1482 21.2334 9.30926 21.7242 8.60262 22.3431C7.89597 22.9619 7.33543 23.6966 6.95299 24.5051C6.57056 25.3137 6.37372 26.1803 6.37372 27.0554C6.37372 27.9306 6.57056 28.7972 6.95299 29.6057C7.33543 30.4143 7.89597 31.1489 8.60262 31.7678C9.30926 32.3866 10.1482 32.8775 11.0715 33.2124C11.9947 33.5473 12.9843 33.7197 13.9836 33.7197L13.9836 20.3912Z" fill="url(#paint3_linear_0_5622)"/>
<path d="M13.9837 10.6101L13.9837 26.9823L6.3736 26.9823L6.3736 10.6101H13.9837Z" fill="url(#paint4_linear_0_5622)"/>
<path d="M23.0327 10.8988L13.8973 10.8988L13.8973 3.58008L23.0327 3.58008V10.8988Z" fill="url(#paint5_linear_0_5622)"/>
<path d="M23.0327 22.6316H17.9771V16.4155L23.0327 16.4155V22.6316Z" fill="url(#paint6_linear_0_5622)"/>
<defs> <defs>
<linearGradient id="paint0_linear_1172_228" x1="520.404" y1="0.338989" x2="520.404" y2="1347.84" gradientUnits="userSpaceOnUse"> <linearGradient id="paint0_linear_0_5622" x1="18" y1="3.58008" x2="18" y2="33.7197" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/> <stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/> <stop offset="1" stop-color="#8EAEFF"/>
</linearGradient> </linearGradient>
<linearGradient id="paint1_linear_1172_228" x1="738.369" y1="576.37" x2="738.369" y2="904.775" gradientUnits="userSpaceOnUse"> <linearGradient id="paint1_linear_0_5622" x1="18" y1="3.58008" x2="18" y2="33.7197" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
<linearGradient id="paint2_linear_0_5622" x1="18" y1="3.58008" x2="18" y2="33.7197" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
<linearGradient id="paint3_linear_0_5622" x1="18" y1="3.58008" x2="18" y2="33.7197" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
<linearGradient id="paint4_linear_0_5622" x1="18" y1="3.58008" x2="18" y2="33.7197" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
<linearGradient id="paint5_linear_0_5622" x1="18" y1="3.58008" x2="18" y2="33.7197" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/>
</linearGradient>
<linearGradient id="paint6_linear_0_5622" x1="18" y1="3.58008" x2="18" y2="33.7197" gradientUnits="userSpaceOnUse">
<stop stop-color="#326DFF"/> <stop stop-color="#326DFF"/>
<stop offset="1" stop-color="#8EAEFF"/> <stop offset="1" stop-color="#8EAEFF"/>
</linearGradient> </linearGradient>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,4 @@
<svg width="12" height="11" viewBox="0 0 12 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.45093 7.94055L8.82385 0.567627" stroke="#8A95A7" stroke-linecap="round"/>
<path d="M4.8418 7.953L8.82373 3.97107" stroke="#8A95A7" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 48 KiB

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