V4.8.14 dev (#3234)
* feat: rewrite chat context (#3176) * feat: add app auto execute (#3115) * feat: add app auto execute * auto exec configtion * chatting animation * change icon * fix * fix * fix link * feat: add chat context to all chatbox * perf: loading ui --------- Co-authored-by: heheer <heheer@sealos.io> * app auto exec (#3179) * add chat records loaded state (#3184) * perf: chat store reset storage (#3186) * perf: chat store reset storage * perf: auto exec code * chore: workflow ui (#3175) * chore: workflow ui * fix * change icon color config * change popover to mymenu * 4.8.14 test (#3189) * update doc * fix: token check * perf: icon button * update doc * feat: share page support configuration Whether to allow the original view (#3194) * update doc * perf: fix index (#3206) * perf: i18n * perf: Add service entry (#3226) * 4.8.14 test (#3228) * fix: ai log * fix: text splitter * fix: reference unselect & user form description & simple to advance (#3229) * fix: reference unselect & user form description & simple to advance * change abort position * perf * perf: code (#3232) * perf: code * update doc * fix: create btn permission (#3233) * update doc * fix: refresh chatbox listener * perf: check invalid reference * perf: check invalid reference * update doc * fix: ui props --------- Co-authored-by: heheer <heheer@sealos.io>
4
.vscode/settings.json
vendored
@@ -16,8 +16,8 @@
|
||||
"i18n-ally.keystyle": "flat",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.keepFulfilled": false,
|
||||
"i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容
|
||||
"i18n-ally.displayLanguage": "zh", // 显示语言
|
||||
"i18n-ally.sourceLanguage": "zh-CN", // 根据此语言文件翻译其他语言文件的变量和内容
|
||||
"i18n-ally.displayLanguage": "zh-CN", // 显示语言
|
||||
"i18n-ally.namespace": true,
|
||||
"i18n-ally.pathMatcher": "{locale}/{namespaces}.json",
|
||||
"i18n-ally.extract.targetPickingStrategy": "most-similar-by-key",
|
||||
|
@@ -7,13 +7,19 @@ toc: true
|
||||
weight: 810
|
||||
---
|
||||
|
||||
## 更新预告
|
||||
## 更新内容
|
||||
|
||||
1.
|
||||
2. 新增 - 工作流支持进入聊天框/点击开始对话后,自动触发一轮对话。
|
||||
3. 新增 - 重写 chatContext,对话测试也会有日志,并且刷新后不会丢失对话。
|
||||
4. 新增 - 分享链接支持配置是否允许查看原文。
|
||||
5. 优化 - 工作流 ui 细节。
|
||||
6. 优化 - 应用编辑记录采用 diff 存储,避免浏览器溢出。
|
||||
7. 修复 - 分块策略,四级标题会被丢失。 同时新增了五级标题的支持。
|
||||
8. 修复 - MongoDB 知识库集合唯一索引。
|
||||
1. 新增 - 工作流支持进入聊天框/点击开始对话后,自动触发一轮对话。
|
||||
2. 新增 - 重写 chatContext,对话测试也会有日志,并且刷新后不会丢失对话。
|
||||
3. 新增 - 分享链接支持配置是否允许查看原文。
|
||||
4. 新增 - 新的 doc2x 插件。
|
||||
5. 新增 - 繁体中文-台湾。
|
||||
6. 优化 - 工作流 ui 细节。
|
||||
7. 优化 - 应用编辑记录采用 diff 存储,避免浏览器溢出。
|
||||
8. 优化 - 代码入口,增加 register 入口,无需等待首次访问才执行。
|
||||
9. 优化 - 工作流检查,增加更多缺失值检查。
|
||||
10. 修复 - 分块策略,四级标题会被丢失。 同时新增了五级标题的支持。
|
||||
11. 修复 - MongoDB 知识库集合唯一索引。
|
||||
12. 修复 - 反选知识库引用后可能会报错。
|
||||
13. 修复 - 简易模式转工作流,不是使用最新编辑记录进行转移。
|
||||
14. 修复 - 表单输入的说明文字不显示。
|
||||
|
@@ -12,5 +12,7 @@ export const fileImgs = [
|
||||
];
|
||||
|
||||
export function getFileIcon(name = '', defaultImg = 'file/fill/file') {
|
||||
return fileImgs.find((item) => new RegExp(item.suffix, 'gi').test(name))?.src || defaultImg;
|
||||
return (
|
||||
fileImgs.find((item) => new RegExp(`\.${item.suffix}`, 'gi').test(name))?.src || defaultImg
|
||||
);
|
||||
}
|
||||
|
3
packages/global/common/file/type.d.ts
vendored
@@ -3,6 +3,7 @@ import { BucketNameEnum } from './constants';
|
||||
export type FileTokenQuery = {
|
||||
bucketName: `${BucketNameEnum}`;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
uid: string; // tmbId/ share uid/ teamChat uid
|
||||
fileId: string;
|
||||
customExpireMinutes?: number;
|
||||
};
|
||||
|
@@ -140,9 +140,10 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
|
||||
const customRegLen = customReg.length;
|
||||
const checkIsCustomStep = (step: number) => step < customRegLen;
|
||||
const checkIsMarkdownSplit = (step: number) => step >= customRegLen && step <= markdownIndex;
|
||||
const checkIsMarkdownSplit = (step: number) =>
|
||||
step >= customRegLen && step <= markdownIndex + customRegLen;
|
||||
+customReg.length;
|
||||
const checkForbidOverlap = (step: number) => step <= forbidOverlapIndex + customReg.length;
|
||||
const checkForbidOverlap = (step: number) => step <= forbidOverlapIndex + customRegLen;
|
||||
|
||||
// if use markdown title split, Separate record title
|
||||
const getSplitTexts = ({ text, step }: { text: string; step: number }) => {
|
||||
|
1
packages/global/core/ai/type.d.ts
vendored
@@ -49,6 +49,7 @@ export type ChatCompletionMessageParam = (
|
||||
| CustomChatCompletionAssistantMessageParam
|
||||
) & {
|
||||
dataId?: string;
|
||||
hideInUI?: boolean;
|
||||
};
|
||||
export type SdkChatCompletionMessageParam = SdkChatCompletionMessageParam;
|
||||
|
||||
|
@@ -1,4 +1,9 @@
|
||||
import { AppTTSConfigType, AppFileSelectConfigType, AppWhisperConfigType } from './type';
|
||||
import {
|
||||
AppTTSConfigType,
|
||||
AppFileSelectConfigType,
|
||||
AppWhisperConfigType,
|
||||
AppAutoExecuteConfigType
|
||||
} from './type';
|
||||
|
||||
export enum AppTypeEnum {
|
||||
folder = 'folder',
|
||||
@@ -12,6 +17,11 @@ export const AppFolderTypeList = [AppTypeEnum.folder, AppTypeEnum.httpPlugin];
|
||||
|
||||
export const defaultTTSConfig: AppTTSConfigType = { type: 'web' };
|
||||
|
||||
export const defaultAutoExecuteConfig: AppAutoExecuteConfigType = {
|
||||
open: false,
|
||||
defaultPrompt: ''
|
||||
};
|
||||
|
||||
export const defaultWhisperConfig: AppWhisperConfigType = {
|
||||
open: false,
|
||||
autoSend: false,
|
||||
|
6
packages/global/core/app/type.d.ts
vendored
@@ -96,6 +96,7 @@ export type AppSimpleEditFormType = {
|
||||
export type AppChatConfigType = {
|
||||
welcomeText?: string;
|
||||
variables?: VariableItemType[];
|
||||
autoExecute?: AppAutoExecuteConfigType;
|
||||
questionGuide?: boolean;
|
||||
ttsConfig?: AppTTSConfigType;
|
||||
whisperConfig?: AppWhisperConfigType;
|
||||
@@ -158,6 +159,11 @@ export type AppScheduledTriggerConfigType = {
|
||||
timezone: string;
|
||||
defaultPrompt: string;
|
||||
};
|
||||
// auto execute
|
||||
export type AppAutoExecuteConfigType = {
|
||||
open: boolean;
|
||||
defaultPrompt: string;
|
||||
};
|
||||
// File
|
||||
export type AppFileSelectConfigType = {
|
||||
canSelectFile: boolean;
|
||||
|
@@ -76,6 +76,7 @@ export const chats2GPTMessages = ({
|
||||
|
||||
results.push({
|
||||
dataId,
|
||||
hideInUI: item.hideInUI,
|
||||
role: ChatCompletionRequestMessageRoleEnum.User,
|
||||
content: simpleUserContentPart(value)
|
||||
});
|
||||
@@ -318,6 +319,7 @@ export const GPTMessages2Chats = (
|
||||
return {
|
||||
dataId: item.dataId,
|
||||
obj,
|
||||
hideInUI: item.hideInUI,
|
||||
value
|
||||
} as ChatItemType;
|
||||
})
|
||||
|
1
packages/global/core/chat/type.d.ts
vendored
@@ -56,6 +56,7 @@ export type UserChatItemValueItemType = {
|
||||
export type UserChatItemType = {
|
||||
obj: ChatRoleEnum.Human;
|
||||
value: UserChatItemValueItemType[];
|
||||
hideInUI?: boolean;
|
||||
};
|
||||
export type SystemChatItemValueItemType = {
|
||||
type: ChatItemValueTypeEnum.text;
|
||||
|
@@ -101,7 +101,7 @@ export const filterPublicNodeResponseData = ({
|
||||
for (let key in item) {
|
||||
if (key === 'toolDetail' || key === 'pluginDetail') {
|
||||
// @ts-ignore
|
||||
obj[key] = filterPublicNodeResponseData({ flowResponses: item[key] });
|
||||
obj[key] = filterPublicNodeResponseData({ flowResponses: item[key], responseDetail });
|
||||
} else if (filedList.includes(key)) {
|
||||
// @ts-ignore
|
||||
obj[key] = item[key];
|
||||
|
3
packages/global/core/dataset/type.d.ts
vendored
@@ -204,7 +204,8 @@ export type DatasetFileSchema = {
|
||||
contentType: string;
|
||||
metadata: {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
tmbId?: string;
|
||||
uid: string;
|
||||
encoding?: string;
|
||||
};
|
||||
};
|
||||
|
@@ -106,6 +106,7 @@ export enum NodeInputKeyEnum {
|
||||
variables = 'variables',
|
||||
scheduleTrigger = 'scheduleTrigger',
|
||||
chatInputGuide = 'chatInputGuide',
|
||||
autoExecute = 'autoExecute',
|
||||
|
||||
// plugin config
|
||||
instruction = 'instruction',
|
||||
|
@@ -25,10 +25,12 @@ import type {
|
||||
AppWhisperConfigType,
|
||||
AppScheduledTriggerConfigType,
|
||||
ChatInputGuideConfigType,
|
||||
AppChatConfigType
|
||||
AppChatConfigType,
|
||||
AppAutoExecuteConfigType
|
||||
} from '../app/type';
|
||||
import { EditorVariablePickerType } from '../../../web/components/common/Textarea/PromptEditor/type';
|
||||
import {
|
||||
defaultAutoExecuteConfig,
|
||||
defaultChatInputGuideConfig,
|
||||
defaultTTSConfig,
|
||||
defaultWhisperConfig
|
||||
@@ -69,34 +71,37 @@ export const getGuideModule = (modules: StoreNodeItemType[]) =>
|
||||
);
|
||||
export const splitGuideModule = (guideModules?: StoreNodeItemType) => {
|
||||
const welcomeText: string =
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.welcomeText)?.value || '';
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.welcomeText)?.value ?? '';
|
||||
|
||||
const variables: VariableItemType[] =
|
||||
guideModules?.inputs.find((item) => item.key === NodeInputKeyEnum.variables)?.value || [];
|
||||
guideModules?.inputs.find((item) => item.key === NodeInputKeyEnum.variables)?.value ?? [];
|
||||
|
||||
const questionGuide: boolean =
|
||||
!!guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.questionGuide)?.value ||
|
||||
!!guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.questionGuide)?.value ??
|
||||
false;
|
||||
|
||||
const ttsConfig: AppTTSConfigType =
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.tts)?.value ||
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.tts)?.value ??
|
||||
defaultTTSConfig;
|
||||
|
||||
const whisperConfig: AppWhisperConfigType =
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.whisper)?.value ||
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.whisper)?.value ??
|
||||
defaultWhisperConfig;
|
||||
|
||||
const scheduledTriggerConfig: AppScheduledTriggerConfigType = guideModules?.inputs?.find(
|
||||
(item) => item.key === NodeInputKeyEnum.scheduleTrigger
|
||||
)?.value;
|
||||
const scheduledTriggerConfig: AppScheduledTriggerConfigType =
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.scheduleTrigger)?.value ??
|
||||
undefined;
|
||||
|
||||
const chatInputGuide: ChatInputGuideConfigType =
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.chatInputGuide)?.value ||
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.chatInputGuide)?.value ??
|
||||
defaultChatInputGuideConfig;
|
||||
|
||||
// plugin
|
||||
const instruction: string =
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.instruction)?.value || '';
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.instruction)?.value ?? '';
|
||||
|
||||
const autoExecute: AppAutoExecuteConfigType =
|
||||
guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.autoExecute)?.value ??
|
||||
defaultAutoExecuteConfig;
|
||||
|
||||
return {
|
||||
welcomeText,
|
||||
@@ -106,7 +111,8 @@ export const splitGuideModule = (guideModules?: StoreNodeItemType) => {
|
||||
whisperConfig,
|
||||
scheduledTriggerConfig,
|
||||
chatInputGuide,
|
||||
instruction
|
||||
instruction,
|
||||
autoExecute
|
||||
};
|
||||
};
|
||||
|
||||
@@ -132,7 +138,8 @@ export const getAppChatConfig = ({
|
||||
whisperConfig,
|
||||
scheduledTriggerConfig,
|
||||
chatInputGuide,
|
||||
instruction
|
||||
instruction,
|
||||
autoExecute
|
||||
} = splitGuideModule(systemConfigNode);
|
||||
|
||||
const config: AppChatConfigType = {
|
||||
@@ -142,6 +149,7 @@ export const getAppChatConfig = ({
|
||||
scheduledTriggerConfig,
|
||||
chatInputGuide,
|
||||
instruction,
|
||||
autoExecute,
|
||||
...chatConfig,
|
||||
variables: storeVariables ?? chatConfig?.variables ?? variables,
|
||||
welcomeText: storeWelcomeText ?? chatConfig?.welcomeText ?? welcomeText
|
||||
|
@@ -32,7 +32,7 @@ export function getGridBucket(bucket: `${BucketNameEnum}`) {
|
||||
export async function uploadFile({
|
||||
bucketName,
|
||||
teamId,
|
||||
tmbId,
|
||||
uid,
|
||||
path,
|
||||
filename,
|
||||
contentType,
|
||||
@@ -41,7 +41,7 @@ export async function uploadFile({
|
||||
}: {
|
||||
bucketName: `${BucketNameEnum}`;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
uid: string; // tmbId / outLinkUId
|
||||
path: string;
|
||||
filename: string;
|
||||
contentType?: string;
|
||||
@@ -58,7 +58,7 @@ export async function uploadFile({
|
||||
|
||||
// Add default metadata
|
||||
metadata.teamId = teamId;
|
||||
metadata.tmbId = tmbId;
|
||||
metadata.uid = uid;
|
||||
metadata.encoding = encoding;
|
||||
|
||||
// create a gridfs bucket
|
||||
|
@@ -4,7 +4,7 @@ import path from 'path';
|
||||
import { BucketNameEnum, bucketNameMap } from '@fastgpt/global/common/file/constants';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
type FileType = {
|
||||
export type FileType = {
|
||||
fieldname: string;
|
||||
originalname: string;
|
||||
encoding: string;
|
||||
@@ -41,7 +41,7 @@ export const getUploadModel = ({ maxSize = 500 }: { maxSize?: number }) => {
|
||||
})
|
||||
}).single('file');
|
||||
|
||||
async doUpload<T = Record<string, any>>(
|
||||
async doUpload<T = any>(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
originBucketName?: `${BucketNameEnum}`
|
||||
|
@@ -6,6 +6,7 @@ import {
|
||||
} from '@fastgpt/global/core/ai/type';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { addLog } from '../../common/system/log';
|
||||
import { i18nT } from '../../../web/i18n/utils';
|
||||
|
||||
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
|
||||
|
||||
@@ -62,6 +63,7 @@ export const createChatCompletion = async <T extends CompletionsBodyType>({
|
||||
}): Promise<{
|
||||
response: InferResponseType<T>;
|
||||
isStreamResponse: boolean;
|
||||
getEmptyResponseTip: () => string;
|
||||
}> => {
|
||||
try {
|
||||
const formatTimeout = timeout ? timeout : body.stream ? 60000 : 600000;
|
||||
@@ -76,9 +78,21 @@ export const createChatCompletion = async <T extends CompletionsBodyType>({
|
||||
response !== null &&
|
||||
('iterator' in response || 'controller' in response);
|
||||
|
||||
const getEmptyResponseTip = () => {
|
||||
addLog.warn(`LLM response empty`, {
|
||||
baseUrl: userKey?.baseUrl,
|
||||
requestBody: body
|
||||
});
|
||||
if (userKey?.baseUrl) {
|
||||
return `您的 OpenAI key 没有响应: ${JSON.stringify(body)}`;
|
||||
}
|
||||
return i18nT('chat:LLM_model_response_empty');
|
||||
};
|
||||
|
||||
return {
|
||||
response: response as InferResponseType<T>,
|
||||
isStreamResponse
|
||||
isStreamResponse,
|
||||
getEmptyResponseTip
|
||||
};
|
||||
} catch (error) {
|
||||
addLog.error(`LLM response error`, error);
|
||||
|
@@ -17,7 +17,8 @@ export const chatConfigType = {
|
||||
scheduledTriggerConfig: Object,
|
||||
chatInputGuide: Object,
|
||||
fileSelectConfig: Object,
|
||||
instruction: String
|
||||
instruction: String,
|
||||
autoExecute: Object
|
||||
};
|
||||
|
||||
// schema
|
||||
|
@@ -46,6 +46,10 @@ const ChatItemSchema = new Schema({
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
hideInUI: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
obj: {
|
||||
// chat role
|
||||
type: String,
|
||||
|
@@ -1,15 +1,6 @@
|
||||
import type {
|
||||
AIChatItemType,
|
||||
ChatItemType,
|
||||
UserChatItemType
|
||||
} from '@fastgpt/global/core/chat/type.d';
|
||||
import axios from 'axios';
|
||||
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { MongoApp } from '../app/schema';
|
||||
import {
|
||||
ChatItemValueTypeEnum,
|
||||
ChatRoleEnum,
|
||||
ChatSourceEnum
|
||||
} from '@fastgpt/global/core/chat/constants';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { MongoChatItem } from './chatItemSchema';
|
||||
import { MongoChat } from './chatSchema';
|
||||
import { addLog } from '../../common/system/log';
|
||||
@@ -133,21 +124,15 @@ export async function saveChat({
|
||||
export const updateInteractiveChat = async ({
|
||||
chatId,
|
||||
appId,
|
||||
teamId,
|
||||
tmbId,
|
||||
userInteractiveVal,
|
||||
aiResponse,
|
||||
newVariables,
|
||||
newTitle
|
||||
newVariables
|
||||
}: {
|
||||
chatId: string;
|
||||
appId: string;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
userInteractiveVal: string;
|
||||
aiResponse: AIChatItemType & { dataId?: string };
|
||||
newVariables?: Record<string, any>;
|
||||
newTitle: string;
|
||||
}) => {
|
||||
if (!chatId) return;
|
||||
|
||||
@@ -232,7 +217,6 @@ export const updateInteractiveChat = async ({
|
||||
{
|
||||
$set: {
|
||||
variables: newVariables,
|
||||
title: newTitle,
|
||||
updateTime: new Date()
|
||||
}
|
||||
},
|
||||
|
@@ -67,7 +67,7 @@ export async function createOneCollection({
|
||||
|
||||
fileId,
|
||||
rawLink,
|
||||
externalFileId,
|
||||
...(externalFileId ? { externalFileId } : {}),
|
||||
externalFileUrl,
|
||||
|
||||
rawTextLength,
|
||||
|
@@ -118,7 +118,7 @@ try {
|
||||
{
|
||||
unique: true,
|
||||
partialFilterExpression: {
|
||||
externalFileId: { $exists: true, $ne: '' }
|
||||
externalFileId: { $exists: true }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@@ -77,7 +77,7 @@ export async function pushDataListToTrainingQueue({
|
||||
|
||||
if (trainingMode === TrainingModeEnum.chunk) {
|
||||
return {
|
||||
maxToken: vectorModelData.maxToken * 1.3,
|
||||
maxToken: vectorModelData.maxToken * 1.5,
|
||||
model: vectorModelData.model,
|
||||
weight: vectorModelData.weight
|
||||
};
|
||||
@@ -125,10 +125,7 @@ export async function pushDataListToTrainingQueue({
|
||||
|
||||
const text = item.q + item.a;
|
||||
|
||||
// count q token
|
||||
const token = item.q.length;
|
||||
|
||||
if (token > maxToken) {
|
||||
if (text.length > maxToken) {
|
||||
filterResult.overToken.push(item);
|
||||
return;
|
||||
}
|
||||
|
@@ -27,7 +27,6 @@ import { computedMaxToken, llmCompletionsBodyFormat } from '../../../../ai/utils
|
||||
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
||||
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { i18nT } from '../../../../../../web/i18n/utils';
|
||||
|
||||
type FunctionRunResponseType = {
|
||||
toolRunResponse: DispatchFlowResponse;
|
||||
@@ -216,7 +215,11 @@ export const runToolWithFunctionCall = async (
|
||||
|
||||
// console.log(JSON.stringify(requestMessages, null, 2));
|
||||
/* Run llm */
|
||||
const { response: aiResponse, isStreamResponse } = await createChatCompletion({
|
||||
const {
|
||||
response: aiResponse,
|
||||
isStreamResponse,
|
||||
getEmptyResponseTip
|
||||
} = await createChatCompletion({
|
||||
body: requestBody,
|
||||
userKey: user.openaiAccount,
|
||||
options: {
|
||||
@@ -256,6 +259,9 @@ export const runToolWithFunctionCall = async (
|
||||
};
|
||||
}
|
||||
})();
|
||||
if (!answer && functionCalls.length === 0) {
|
||||
return Promise.reject(getEmptyResponseTip());
|
||||
}
|
||||
|
||||
// Run the selected tool.
|
||||
const toolsRunResponse = (
|
||||
@@ -549,9 +555,5 @@ async function streamResponse({
|
||||
}
|
||||
}
|
||||
|
||||
if (!textAnswer && functionCalls.length === 0) {
|
||||
return Promise.reject(i18nT('chat:LLM_model_response_empty'));
|
||||
}
|
||||
|
||||
return { answer: textAnswer, functionCalls };
|
||||
}
|
||||
|
@@ -29,7 +29,6 @@ import { WorkflowResponseType } from '../../type';
|
||||
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
||||
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { i18nT } from '../../../../../../web/i18n/utils';
|
||||
|
||||
type FunctionCallCompletion = {
|
||||
id: string;
|
||||
@@ -225,7 +224,11 @@ export const runToolWithPromptCall = async (
|
||||
|
||||
// console.log(JSON.stringify(requestMessages, null, 2));
|
||||
/* Run llm */
|
||||
const { response: aiResponse, isStreamResponse } = await createChatCompletion({
|
||||
const {
|
||||
response: aiResponse,
|
||||
isStreamResponse,
|
||||
getEmptyResponseTip
|
||||
} = await createChatCompletion({
|
||||
body: requestBody,
|
||||
userKey: user.openaiAccount,
|
||||
options: {
|
||||
@@ -251,8 +254,11 @@ export const runToolWithPromptCall = async (
|
||||
return result.choices?.[0]?.message?.content || '';
|
||||
}
|
||||
})();
|
||||
|
||||
const { answer: replaceAnswer, toolJson } = parseAnswer(answer);
|
||||
if (!answer && !toolJson) {
|
||||
return Promise.reject(getEmptyResponseTip());
|
||||
}
|
||||
|
||||
// No tools
|
||||
if (!toolJson) {
|
||||
if (replaceAnswer === ERROR_TEXT) {
|
||||
@@ -534,9 +540,6 @@ async function streamResponse({
|
||||
}
|
||||
}
|
||||
|
||||
if (!textAnswer) {
|
||||
return Promise.reject(i18nT('chat:LLM_model_response_empty'));
|
||||
}
|
||||
return { answer: textAnswer.trim() };
|
||||
}
|
||||
|
||||
|
@@ -272,7 +272,11 @@ export const runToolWithToolChoice = async (
|
||||
);
|
||||
// console.log(JSON.stringify(requestBody, null, 2), '==requestBody');
|
||||
/* Run llm */
|
||||
const { response: aiResponse, isStreamResponse } = await createChatCompletion({
|
||||
const {
|
||||
response: aiResponse,
|
||||
isStreamResponse,
|
||||
getEmptyResponseTip
|
||||
} = await createChatCompletion({
|
||||
body: requestBody,
|
||||
userKey: user.openaiAccount,
|
||||
options: {
|
||||
@@ -336,6 +340,9 @@ export const runToolWithToolChoice = async (
|
||||
};
|
||||
}
|
||||
})();
|
||||
if (!answer && toolCalls.length === 0) {
|
||||
return Promise.reject(getEmptyResponseTip());
|
||||
}
|
||||
|
||||
// Run the selected tool by LLM.
|
||||
const toolsRunResponse = (
|
||||
@@ -645,9 +652,5 @@ async function streamResponse({
|
||||
}
|
||||
}
|
||||
|
||||
if (!textAnswer && toolCalls.length === 0) {
|
||||
return Promise.reject(i18nT('chat:LLM_model_response_empty'));
|
||||
}
|
||||
|
||||
return { answer: textAnswer, toolCalls };
|
||||
}
|
||||
|
@@ -33,15 +33,13 @@ import { getLLMModel, ModelTypeEnum } from '../../../ai/model';
|
||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { getHistories } from '../utils';
|
||||
import { checkQuoteQAValue, getHistories } from '../utils';
|
||||
import { filterSearchResultsByMaxChars } from '../../utils';
|
||||
import { getHistoryPreview } from '@fastgpt/global/core/chat/utils';
|
||||
import { addLog } from '../../../../common/system/log';
|
||||
import { computedMaxToken, llmCompletionsBodyFormat } from '../../../ai/utils';
|
||||
import { WorkflowResponseType } from '../type';
|
||||
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
|
||||
import { AiChatQuoteRoleType } from '@fastgpt/global/core/workflow/template/system/aiChat/type';
|
||||
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';
|
||||
@@ -93,6 +91,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
stream = stream && isResponseAnswerText;
|
||||
|
||||
const chatHistories = getHistories(history, histories);
|
||||
quoteQA = checkQuoteQAValue(quoteQA);
|
||||
|
||||
const modelConstantsData = getLLMModel(model);
|
||||
if (!modelConstantsData) {
|
||||
@@ -169,99 +168,91 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
modelConstantsData
|
||||
);
|
||||
// console.log(JSON.stringify(requestBody, null, 2), '===');
|
||||
try {
|
||||
const { response, isStreamResponse } = await createChatCompletion({
|
||||
body: requestBody,
|
||||
userKey: user.openaiAccount,
|
||||
options: {
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*'
|
||||
}
|
||||
const { response, isStreamResponse, getEmptyResponseTip } = await createChatCompletion({
|
||||
body: requestBody,
|
||||
userKey: user.openaiAccount,
|
||||
options: {
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*'
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const { answerText } = await (async () => {
|
||||
if (res && isStreamResponse) {
|
||||
// sse response
|
||||
const { answer } = await streamResponse({
|
||||
res,
|
||||
stream: response,
|
||||
workflowStreamResponse
|
||||
const { answerText } = await (async () => {
|
||||
if (res && isStreamResponse) {
|
||||
// sse response
|
||||
const { answer } = await streamResponse({
|
||||
res,
|
||||
stream: response,
|
||||
workflowStreamResponse
|
||||
});
|
||||
|
||||
return {
|
||||
answerText: answer
|
||||
};
|
||||
} else {
|
||||
const unStreamResponse = response as ChatCompletion;
|
||||
const answer = unStreamResponse.choices?.[0]?.message?.content || '';
|
||||
|
||||
if (stream) {
|
||||
// Some models do not support streaming
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.fastAnswer,
|
||||
data: textAdaptGptResponse({
|
||||
text: answer
|
||||
})
|
||||
});
|
||||
|
||||
if (!answer) {
|
||||
return Promise.reject(i18nT('chat:LLM_model_response_empty'));
|
||||
}
|
||||
|
||||
return {
|
||||
answerText: answer
|
||||
};
|
||||
} else {
|
||||
const unStreamResponse = response as ChatCompletion;
|
||||
const answer = unStreamResponse.choices?.[0]?.message?.content || '';
|
||||
|
||||
if (stream) {
|
||||
// Some models do not support streaming
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.fastAnswer,
|
||||
data: textAdaptGptResponse({
|
||||
text: answer
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
answerText: answer
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
const completeMessages = requestMessages.concat({
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
content: answerText
|
||||
});
|
||||
const chatCompleteMessages = GPTMessages2Chats(completeMessages);
|
||||
return {
|
||||
answerText: answer
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
const tokens = await countMessagesTokens(chatCompleteMessages);
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model,
|
||||
if (!answerText) {
|
||||
return Promise.reject(getEmptyResponseTip());
|
||||
}
|
||||
|
||||
const completeMessages = requestMessages.concat({
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
content: answerText
|
||||
});
|
||||
const chatCompleteMessages = GPTMessages2Chats(completeMessages);
|
||||
|
||||
const tokens = await countMessagesTokens(chatCompleteMessages);
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model,
|
||||
tokens,
|
||||
modelType: ModelTypeEnum.llm
|
||||
});
|
||||
|
||||
return {
|
||||
answerText,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
||||
model: modelName,
|
||||
tokens,
|
||||
modelType: ModelTypeEnum.llm
|
||||
});
|
||||
|
||||
return {
|
||||
answerText,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
query: `${userChatInput}`,
|
||||
maxToken: max_tokens,
|
||||
historyPreview: getHistoryPreview(
|
||||
chatCompleteMessages,
|
||||
10000,
|
||||
modelConstantsData.vision && aiChatVision
|
||||
),
|
||||
contextTotalLen: completeMessages.length
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
||||
{
|
||||
moduleName: name,
|
||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
||||
model: modelName,
|
||||
tokens,
|
||||
query: `${userChatInput}`,
|
||||
maxToken: max_tokens,
|
||||
historyPreview: getHistoryPreview(
|
||||
chatCompleteMessages,
|
||||
10000,
|
||||
modelConstantsData.vision && aiChatVision
|
||||
),
|
||||
contextTotalLen: completeMessages.length
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
||||
{
|
||||
moduleName: name,
|
||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
||||
model: modelName,
|
||||
tokens
|
||||
}
|
||||
],
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: answerText,
|
||||
history: chatCompleteMessages
|
||||
};
|
||||
} catch (error) {
|
||||
if (user.openaiAccount?.baseUrl) {
|
||||
return Promise.reject(`您的 OpenAI key 出错了: ${getErrText(error)}`);
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
tokens
|
||||
}
|
||||
],
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: answerText,
|
||||
history: chatCompleteMessages
|
||||
};
|
||||
};
|
||||
|
||||
async function filterDatasetQuote({
|
||||
|
@@ -209,7 +209,6 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
try {
|
||||
const { formatResponse, rawResponse } = await (async () => {
|
||||
const systemPluginCb = global.systemPluginCb;
|
||||
console.log(systemPluginCb, '-=', httpReqUrl);
|
||||
if (systemPluginCb[httpReqUrl]) {
|
||||
const pluginResult = await replaceSystemPluginResponse({
|
||||
response: await systemPluginCb[httpReqUrl](requestBody),
|
||||
@@ -395,7 +394,7 @@ async function replaceSystemPluginResponse({
|
||||
response[key] = `${ReadFileBaseUrl}/${filename}?token=${await createFileToken({
|
||||
bucketName: 'chat',
|
||||
teamId,
|
||||
tmbId,
|
||||
uid: tmbId,
|
||||
fileId
|
||||
})}`;
|
||||
} catch (error) {}
|
||||
|
@@ -13,6 +13,7 @@ import { responseWrite } from '../../../common/response';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
|
||||
export const getWorkflowResponseWrite = ({
|
||||
res,
|
||||
@@ -87,27 +88,6 @@ export const filterToolNodeIdByEdges = ({
|
||||
.map((edge) => edge.target);
|
||||
};
|
||||
|
||||
// export const checkTheModuleConnectedByTool = (
|
||||
// modules: StoreNodeItemType[],
|
||||
// node: StoreNodeItemType
|
||||
// ) => {
|
||||
// let sign = false;
|
||||
// const toolModules = modules.filter((item) => item.flowNodeType === FlowNodeTypeEnum.tools);
|
||||
|
||||
// toolModules.forEach((item) => {
|
||||
// const toolOutput = item.outputs.find(
|
||||
// (output) => output.key === NodeOutputKeyEnum.selectedTools
|
||||
// );
|
||||
// toolOutput?.targets.forEach((target) => {
|
||||
// if (target.moduleId === node.moduleId) {
|
||||
// sign = true;
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// return sign;
|
||||
// };
|
||||
|
||||
export const getHistories = (history?: ChatItemType[] | number, histories: ChatItemType[] = []) => {
|
||||
if (!history) return [];
|
||||
|
||||
@@ -149,6 +129,13 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
|
||||
return value;
|
||||
};
|
||||
|
||||
export const checkQuoteQAValue = (quoteQA: SearchDataResponseItemType[] = []) => {
|
||||
if (quoteQA.some((item) => !item.q || !item.datasetId)) {
|
||||
return undefined;
|
||||
}
|
||||
return quoteQA;
|
||||
};
|
||||
|
||||
/* remove system variable */
|
||||
export const removeSystemVariable = (variables: Record<string, any>) => {
|
||||
const copyVariables = { ...variables };
|
||||
|
@@ -48,7 +48,8 @@ const OutLinkSchema = new Schema({
|
||||
default: false
|
||||
},
|
||||
showNodeStatus: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showRawSource: {
|
||||
type: Boolean
|
||||
|
@@ -7,7 +7,7 @@ import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { OwnerPermissionVal, ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { Permission } from '@fastgpt/global/support/permission/controller';
|
||||
|
||||
export async function authFile({
|
||||
export const authCollectionFile = async ({
|
||||
fileId,
|
||||
per = OwnerPermissionVal,
|
||||
...props
|
||||
@@ -17,7 +17,7 @@ export async function authFile({
|
||||
AuthResponseType & {
|
||||
file: DatasetFileSchema;
|
||||
}
|
||||
> {
|
||||
> => {
|
||||
const authRes = await parseHeaderCert(props);
|
||||
const { teamId, tmbId } = authRes;
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function authFile({
|
||||
|
||||
const permission = new Permission({
|
||||
per: ReadPermissionVal,
|
||||
isOwner: file.metadata?.tmbId === tmbId
|
||||
isOwner: file.metadata?.uid === tmbId || file.metadata?.tmbId === tmbId
|
||||
});
|
||||
|
||||
if (!permission.checkPer(per)) {
|
||||
@@ -45,4 +45,4 @@ export async function authFile({
|
||||
permission,
|
||||
file
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@@ -413,7 +413,8 @@ export const createFileToken = (data: FileTokenQuery) => {
|
||||
return Promise.reject('System unset FILE_TOKEN_KEY');
|
||||
}
|
||||
|
||||
const expireMinutes = bucketNameMap[data.bucketName].previewExpireMinutes;
|
||||
const expireMinutes =
|
||||
data.customExpireMinutes ?? bucketNameMap[data.bucketName].previewExpireMinutes;
|
||||
const expiredTime = Math.floor(addMinutes(new Date(), expireMinutes).getTime() / 1000);
|
||||
|
||||
const key = (process.env.FILE_TOKEN_KEY as string) ?? 'filetoken';
|
||||
@@ -435,14 +436,14 @@ export const authFileToken = (token?: string) =>
|
||||
const key = (process.env.FILE_TOKEN_KEY as string) ?? 'filetoken';
|
||||
|
||||
jwt.verify(token, key, function (err, decoded: any) {
|
||||
if (err || !decoded.bucketName || !decoded?.teamId || !decoded?.tmbId || !decoded?.fileId) {
|
||||
if (err || !decoded.bucketName || !decoded?.teamId || !decoded?.fileId) {
|
||||
reject(ERROR_ENUM.unAuthFile);
|
||||
return;
|
||||
}
|
||||
resolve({
|
||||
bucketName: decoded.bucketName,
|
||||
teamId: decoded.teamId,
|
||||
tmbId: decoded.tmbId,
|
||||
uid: decoded.uid,
|
||||
fileId: decoded.fileId
|
||||
});
|
||||
});
|
||||
|
@@ -62,14 +62,14 @@ export async function authOutLinkValid<T extends OutlinkAppType = undefined>({
|
||||
if (!shareId) {
|
||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||
}
|
||||
const shareChat = (await MongoOutLink.findOne({ shareId }).lean()) as OutLinkSchema<T>;
|
||||
const outLinkConfig = (await MongoOutLink.findOne({ shareId }).lean()) as OutLinkSchema<T>;
|
||||
|
||||
if (!shareChat) {
|
||||
if (!outLinkConfig) {
|
||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||
}
|
||||
|
||||
return {
|
||||
appId: shareChat.appId,
|
||||
shareChat
|
||||
appId: outLinkConfig.appId,
|
||||
outLinkConfig
|
||||
};
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ import { Permission } from '@fastgpt/global/support/permission/controller';
|
||||
import { ApiRequestProps } from '../../type/next';
|
||||
import type { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import { RequireAtLeastOne } from '@fastgpt/global/common/type/utils';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
export type ReqHeaderAuthType = {
|
||||
cookie?: string;
|
||||
|
3
packages/service/type.d.ts
vendored
@@ -23,9 +23,6 @@ declare global {
|
||||
var whisperModel: WhisperModelType;
|
||||
var reRankModels: ReRankModelItemType[];
|
||||
|
||||
var systemLoadedGlobalVariables: boolean;
|
||||
var systemLoadedGlobalConfig: boolean;
|
||||
|
||||
var workerPoll: Record<WorkerNameEnum, WorkerPool>;
|
||||
var appMarketTemplates: TemplateMarketItemType[];
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@ export const hasHttps = () => {
|
||||
return window.location.protocol === 'https:';
|
||||
};
|
||||
|
||||
export const subRoute = process.env.NEXT_PUBLIC_BASE_URL;
|
||||
|
||||
export const getWebReqUrl = (url: string = '') => {
|
||||
if (!url) return '/';
|
||||
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL;
|
||||
|
41
packages/web/components/common/Icon/button.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { Flex, FlexProps } from '@chakra-ui/react';
|
||||
import MyIcon from './index';
|
||||
|
||||
type Props = FlexProps & {
|
||||
icon: string;
|
||||
size?: string;
|
||||
onClick?: () => void;
|
||||
hoverColor?: string;
|
||||
};
|
||||
|
||||
const MyIconButton = ({
|
||||
icon,
|
||||
onClick,
|
||||
hoverColor = 'primary.600',
|
||||
size = '1rem',
|
||||
...props
|
||||
}: Props) => {
|
||||
return (
|
||||
<Flex
|
||||
mr={1}
|
||||
p={1}
|
||||
color={'myGray.500'}
|
||||
rounded={'sm'}
|
||||
alignItems={'center'}
|
||||
bg={'transparent'}
|
||||
transition={'background 0.1s'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
bg: 'myGray.05',
|
||||
color: hoverColor
|
||||
}}
|
||||
onClick={onClick}
|
||||
{...props}
|
||||
>
|
||||
<MyIcon name={icon as any} w={size} />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyIconButton;
|
@@ -5,6 +5,7 @@ export const iconPaths = {
|
||||
change: () => import('./icons/change.svg'),
|
||||
chatSend: () => import('./icons/chatSend.svg'),
|
||||
check: () => import('./icons/check.svg'),
|
||||
checkCircle: () => import('./icons/checkCircle.svg'),
|
||||
closeSolid: () => import('./icons/closeSolid.svg'),
|
||||
collectionLight: () => import('./icons/collectionLight.svg'),
|
||||
collectionSolid: () => import('./icons/collectionSolid.svg'),
|
||||
@@ -101,6 +102,7 @@ export const iconPaths = {
|
||||
'core/app/schedulePlan': () => import('./icons/core/app/schedulePlan.svg'),
|
||||
'core/app/simpleBot': () => import('./icons/core/app/simpleBot.svg'),
|
||||
'core/app/simpleMode/ai': () => import('./icons/core/app/simpleMode/ai.svg'),
|
||||
'core/app/simpleMode/autoExec': () => import('./icons/core/app/simpleMode/autoExec.svg'),
|
||||
'core/app/simpleMode/chat': () => import('./icons/core/app/simpleMode/chat.svg'),
|
||||
'core/app/simpleMode/dataset': () => import('./icons/core/app/simpleMode/dataset.svg'),
|
||||
'core/app/simpleMode/file': () => import('./icons/core/app/simpleMode/file.svg'),
|
||||
@@ -183,6 +185,7 @@ export const iconPaths = {
|
||||
'core/workflow/debugNext': () => import('./icons/core/workflow/debugNext.svg'),
|
||||
'core/workflow/debugResult': () => import('./icons/core/workflow/debugResult.svg'),
|
||||
'core/workflow/edgeArrow': () => import('./icons/core/workflow/edgeArrow.svg'),
|
||||
'core/workflow/edgeArrowBold': () => import('./icons/core/workflow/edgeArrowBold.svg'),
|
||||
'core/workflow/grout': () => import('./icons/core/workflow/grout.svg'),
|
||||
'core/workflow/inputType/array': () => import('./icons/core/workflow/inputType/array.svg'),
|
||||
'core/workflow/inputType/customVariable': () =>
|
||||
|
@@ -1,3 +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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.8043 3.52859C14.0646 3.78894 14.0646 4.21105 13.8043 4.4714L6.47092 11.8047C6.21057 12.0651 5.78846 12.0651 5.52811 11.8047L2.19477 8.4714C1.93442 8.21105 1.93442 7.78894 2.19477 7.52859C2.45512 7.26824 2.87723 7.26824 3.13758 7.52859L5.99951 10.3905L12.8614 3.52859C13.1218 3.26824 13.5439 3.26824 13.8043 3.52859Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 452 B After Width: | Height: | Size: 453 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.1848 3.90471C9.12756 3.43362 7.94633 3.31691 6.81731 3.572C5.6883 3.82708 4.67198 4.44029 3.91995 5.32018C3.16791 6.20006 2.72044 7.29947 2.64429 8.45443C2.56813 9.6094 2.86736 10.758 3.49735 11.7291C4.12734 12.7001 5.05433 13.4414 6.14008 13.8426C7.22582 14.2437 8.41215 14.2831 9.52213 13.9549C10.6321 13.6267 11.6063 12.9485 12.2993 12.0215C12.9923 11.0944 13.3671 9.96815 13.3678 8.81067V8.2559C13.3678 7.88771 13.6663 7.58924 14.0345 7.58924C14.4027 7.58924 14.7011 7.88771 14.7011 8.2559V8.81105C14.7003 10.2561 14.2324 11.6625 13.3672 12.8198C12.502 13.9771 11.2859 14.8238 9.90017 15.2335C8.51447 15.6432 7.03346 15.594 5.67801 15.0933C4.32255 14.5925 3.16529 13.667 2.37881 12.4548C1.59232 11.2425 1.21877 9.80857 1.31384 8.3667C1.40892 6.92484 1.96754 5.55233 2.90638 4.45388C3.84523 3.35543 5.114 2.5899 6.52347 2.27144C7.93294 1.95299 9.4076 2.09869 10.7275 2.6868C11.0638 2.83666 11.215 3.23077 11.0651 3.56709C10.9153 3.9034 10.5211 4.05456 10.1848 3.90471ZM14.5056 3.50856C14.7661 3.76878 14.7663 4.19089 14.5061 4.45137L8.47187 10.4917C8.34686 10.6168 8.17726 10.6871 8.00039 10.6871C7.82352 10.6872 7.65389 10.617 7.52882 10.4919L5.71855 8.68161C5.4582 8.42126 5.4582 7.99915 5.71855 7.7388C5.9789 7.47846 6.40101 7.47846 6.66136 7.7388L7.99999 9.07744L13.5628 3.50904C13.823 3.24856 14.2452 3.24835 14.5056 3.50856Z" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
@@ -1 +1,6 @@
|
||||
<?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="1694403033666" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4053" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M1016.32 494.08l-143.872-143.36c-9.728-9.728-26.112-9.728-35.84 0-9.728 9.728-9.728 26.112 0 35.84L936.96 486.4h-399.36V87.04l99.84 100.352c9.728 9.728 26.112 9.728 35.84 0 9.728-9.728 9.728-26.112 0-35.84l-143.36-143.872c-9.728-9.728-26.112-9.728-35.84 0l-143.36 143.872c-9.728 9.728-9.728 26.112 0 35.84 9.728 9.728 26.112 9.728 35.84 0L486.4 87.04v399.36H87.04l100.352-99.84c9.728-9.728 9.728-26.112 0-35.84-9.728-9.728-26.112-9.728-35.84 0l-143.872 143.36c-9.728 9.728-9.728 26.112 0 35.84l143.872 143.36c9.728 9.728 26.112 9.728 35.84 0 9.728-9.728 9.728-26.112 0-35.84L87.04 537.6h399.36v399.36l-99.84-100.352c-9.728-9.728-26.112-9.728-35.84 0-9.728 9.728-9.728 26.112 0 35.84l143.36 143.872c9.728 9.728 26.112 9.728 35.84 0l143.36-143.872c9.728-9.728 9.728-26.112 0-35.84-9.728-9.728-26.112-9.728-35.84 0L537.6 936.96v-399.36h399.36l-100.352 99.84c-9.728 9.728-9.728 26.112 0 35.84 9.728 9.728 26.112 9.728 35.84 0l143.872-143.36c10.24-9.728 10.24-26.112 0-35.84z" p-id="4054"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M8.47188 1.52653C8.34159 1.39624 8.17079 1.33115 8.00003 1.33127C7.82926 1.33115 7.65847 1.39624 7.52818 1.52653C7.52123 1.53348 7.51447 1.54054 7.5079 1.54771L6.11486 2.94074C5.85451 3.20109 5.85451 3.6232 6.11486 3.88355C6.37521 4.1439 6.79732 4.1439 7.05767 3.88355L7.33381 3.60741V5.99835C7.33381 6.36654 7.63229 6.66502 8.00048 6.66502C8.36867 6.66502 8.66714 6.36654 8.66714 5.99835V3.60831L8.94239 3.88355C9.20274 4.1439 9.62485 4.1439 9.8852 3.88355C10.1455 3.6232 10.1455 3.20109 9.8852 2.94074L8.49216 1.54771C8.48559 1.54054 8.47883 1.53348 8.47188 1.52653Z" />
|
||||
<path d="M7.33381 12.3926V10.0039C7.33381 9.6357 7.63229 9.33723 8.00048 9.33723C8.36867 9.33723 8.66714 9.6357 8.66714 10.0039V12.3917L8.94239 12.1164C9.20274 11.8561 9.62485 11.8561 9.8852 12.1164C10.1455 12.3768 10.1455 12.7989 9.8852 13.0593L8.49216 14.4523C8.48559 14.4595 8.47883 14.4665 8.47188 14.4735C8.34159 14.6038 8.17079 14.6688 8.00003 14.6687C7.82926 14.6688 7.65847 14.6038 7.52818 14.4735C7.52123 14.4665 7.51447 14.4595 7.5079 14.4523L6.11486 13.0593C5.85451 12.7989 5.85451 12.3768 6.11486 12.1164C6.37521 11.8561 6.79732 11.8561 7.05767 12.1164L7.33381 12.3926Z" />
|
||||
<path d="M6.0646 7.33378C6.43279 7.33378 6.73126 7.63226 6.73126 8.00045C6.73126 8.36864 6.43279 8.66711 6.0646 8.66711H3.60834L3.88358 8.94236C4.14393 9.20271 4.14393 9.62482 3.88358 9.88517C3.62323 10.1455 3.20112 10.1455 2.94077 9.88517L1.54774 8.49213C1.54057 8.48556 1.53351 8.4788 1.52656 8.47185C1.39627 8.34156 1.33118 8.17075 1.3313 7.99998C1.33119 7.82922 1.39628 7.65843 1.52656 7.52815C1.53351 7.5212 1.54057 7.51444 1.54774 7.50786L2.94077 6.11483C3.20112 5.85448 3.62323 5.85448 3.88358 6.11483C4.14393 6.37518 4.14393 6.79729 3.88358 7.05764L3.60744 7.33378H6.0646Z" />
|
||||
<path d="M13.0593 6.11483L14.4523 7.50786C14.4595 7.51444 14.4666 7.5212 14.4735 7.52815C14.6038 7.65843 14.6689 7.82923 14.6688 8C14.6689 8.17076 14.6038 8.34156 14.4735 8.47185C14.4666 8.4788 14.4595 8.48556 14.4523 8.49213L13.0593 9.88517C12.799 10.1455 12.3768 10.1455 12.1165 9.88517C11.8562 9.62482 11.8562 9.20271 12.1165 8.94236L12.3911 8.66779H9.92959C9.5614 8.66779 9.26292 8.36931 9.26292 8.00112C9.26292 7.63293 9.5614 7.33445 9.92959 7.33445H12.3933L12.1165 7.05764C11.8562 6.79729 11.8562 6.37518 12.1165 6.11483C12.3769 5.85448 12.799 5.85448 13.0593 6.11483Z" />
|
||||
</svg>
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21" fill="none">
|
||||
<path d="M7.56249 11.1348L9.12024 7.33546C9.17155 7.21031 9.2934 7.12858 9.42866 7.12858H11.6153C11.857 7.12858 12.0184 7.37785 11.9194 7.5984L10.9629 9.72956C10.9134 9.83984 10.9941 9.96447 11.1149 9.96447H12.7714C13.0487 9.96447 13.2048 10.2834 13.0346 10.5024L9.93738 14.4874C9.73038 14.7537 9.30408 14.5793 9.34309 14.2443L9.62999 11.7805C9.64152 11.6815 9.56413 11.5946 9.46444 11.5946H7.87091C7.63386 11.5946 7.47257 11.3541 7.56249 11.1348Z" fill="#8774EE"/>
|
||||
<path d="M3.68233 14.3773C3.28604 14.6067 2.77464 14.4728 2.58571 14.0557C1.82751 12.3817 1.64076 10.4957 2.06882 8.69266C2.561 6.61955 3.82859 4.81371 5.61106 3.64631C7.39354 2.47891 9.55548 2.03863 11.6525 2.41596C13.4764 2.74413 15.1306 3.66906 16.3621 5.03305C16.6689 5.37293 16.5873 5.89525 16.2187 6.16683C15.85 6.43841 15.3344 6.3554 15.0192 6.02321C14.0456 4.99697 12.7652 4.301 11.3589 4.04796C9.67911 3.74571 7.94736 4.09838 6.51957 5.03349C5.09178 5.9686 4.07642 7.4151 3.68218 9.07569C3.35212 10.4659 3.47839 11.9178 4.03001 13.2204C4.20857 13.6421 4.07861 14.1479 3.68233 14.3773Z" fill="#8774EE"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.04324 13.2533C4.0589 13.2943 4.07172 13.336 4.08171 13.3781C4.17284 13.7615 4.0302 14.1759 3.68233 14.3773C3.28604 14.6067 2.77464 14.4728 2.58571 14.0557C2.55736 13.9931 2.52981 13.9302 2.50307 13.867C2.48986 13.8358 2.47685 13.8046 2.46403 13.7733C1.80857 12.1713 1.66467 10.395 2.06882 8.69266C2.561 6.61955 3.82859 4.81371 5.61106 3.64631C7.39354 2.47891 9.55548 2.03863 11.6525 2.41596C13.3745 2.7258 14.9453 3.5676 16.1518 4.80865C16.1754 4.83292 16.1989 4.85734 16.2222 4.88192C16.2694 4.93168 16.316 4.98206 16.3621 5.03305C16.6689 5.37293 16.5873 5.89525 16.2187 6.16683C15.895 6.40524 15.4582 6.37039 15.1431 6.1336C15.1085 6.10762 15.0754 6.07921 15.0441 6.04846C15.0357 6.04021 15.0274 6.03179 15.0192 6.02321C14.0456 4.99697 12.7652 4.301 11.3589 4.04796C9.67911 3.74571 7.94736 4.09838 6.51957 5.03349C5.09178 5.9686 4.07642 7.4151 3.68218 9.07569C3.35212 10.4659 3.47839 11.9178 4.03001 13.2204C4.03463 13.2313 4.03904 13.2423 4.04324 13.2533Z" fill="#8774EE"/>
|
||||
<path d="M17.123 7.77804C17.5469 7.60478 18.0353 7.80718 18.1656 8.24615C18.6407 9.84662 18.6258 11.5599 18.1122 13.1618C17.5139 15.028 16.277 16.6236 14.6189 17.6682C12.9608 18.7129 10.9876 19.1397 9.04598 18.8738C7.37931 18.6455 5.82746 17.9193 4.58891 16.7998C4.24921 16.4928 4.27748 15.9649 4.61675 15.6574C4.95601 15.3498 5.47737 15.3802 5.82456 15.6788C6.79537 16.5136 7.9909 17.0556 9.27099 17.2309C10.8262 17.444 12.4068 17.102 13.735 16.2652C15.0631 15.4285 16.0539 14.1504 16.5332 12.6556C16.9276 11.4252 16.9548 10.1128 16.6209 8.87674C16.5015 8.43469 16.6992 7.95131 17.123 7.77804Z" fill="#8774EE"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.6123 8.8424C16.615 8.85387 16.6178 8.86532 16.6209 8.87674C16.9548 10.1128 16.9276 11.4252 16.5332 12.6556C16.0539 14.1504 15.0631 15.4285 13.735 16.2652C12.4068 17.102 10.8262 17.444 9.27099 17.2309C7.9909 17.0556 6.79537 16.5136 5.82456 15.6788C5.81558 15.671 5.8065 15.6635 5.7973 15.6561C5.76301 15.6287 5.72719 15.6038 5.69016 15.5815C5.3527 15.3778 4.91457 15.3874 4.61675 15.6574C4.27748 15.9649 4.24921 16.4928 4.58891 16.7998C4.63988 16.8459 4.69138 16.8913 4.74339 16.936C4.76907 16.9581 4.79489 16.98 4.82082 17.0018C6.01868 18.0067 7.48176 18.6596 9.04598 18.8738C10.9876 19.1397 12.9608 18.7129 14.6189 17.6682C16.277 16.6236 17.5139 15.028 18.1122 13.1618C18.5942 11.6584 18.637 10.0568 18.2476 8.54252C18.2392 8.50973 18.2306 8.47699 18.2217 8.44429C18.2038 8.37806 18.1851 8.31201 18.1656 8.24615C18.0353 7.80718 17.5469 7.60478 17.123 7.77804C16.751 7.93014 16.5532 8.32121 16.5912 8.71352C16.5953 8.75657 16.6024 8.79963 16.6123 8.8424Z" fill="#8774EE"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 16" fill="none">
|
||||
<path d="M3.07373 0.535309C2.48828 -0.0505066 1.53809 -0.0505066 0.952637 0.535309C0.366699 1.12106 0.366699 2.07083 0.952637 2.65665L6.26123 7.96536L0.952637 13.2737C0.367188 13.8595 0.367188 14.8092 0.952637 15.395C1.53857 15.9807 2.48828 15.9807 3.07422 15.395L9.43799 9.03104C9.66016 8.80875 9.7959 8.53372 9.84961 8.24643C9.90283 7.97018 9.87842 7.68179 9.77588 7.41672C9.70264 7.22818 9.58984 7.05142 9.43799 6.89926L3.07373 0.535309Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 525 B |
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import React, { ForwardedRef, forwardRef } from 'react';
|
||||
import { Image, ImageProps } from '@chakra-ui/react';
|
||||
import { getWebReqUrl } from '../../../common/system/utils';
|
||||
const MyImage = (props: ImageProps) => {
|
||||
const MyImage = (props: ImageProps, ref?: ForwardedRef<any>) => {
|
||||
return <Image {...props} src={getWebReqUrl(props.src)} alt={props.alt || ''} />;
|
||||
};
|
||||
export default React.memo(MyImage);
|
||||
export default forwardRef(MyImage);
|
||||
|
@@ -7,22 +7,26 @@ import {
|
||||
useOutsideClick,
|
||||
MenuButton,
|
||||
MenuItemProps,
|
||||
PlacementWithLogical
|
||||
PlacementWithLogical,
|
||||
AvatarProps,
|
||||
BoxProps,
|
||||
DividerProps
|
||||
} from '@chakra-ui/react';
|
||||
import MyDivider from '../MyDivider';
|
||||
import type { IconNameType } from '../Icon/type';
|
||||
import { useSystem } from '../../../hooks/useSystem';
|
||||
import Avatar from '../Avatar';
|
||||
|
||||
export type MenuItemType = 'primary' | 'danger';
|
||||
export type MenuItemType = 'primary' | 'danger' | 'gray' | 'grayBg';
|
||||
|
||||
export type MenuSizeType = 'sm' | 'md' | 'xs' | 'mini';
|
||||
|
||||
export type Props = {
|
||||
width?: number | string;
|
||||
offset?: [number, number];
|
||||
Button: React.ReactNode;
|
||||
trigger?: 'hover' | 'click';
|
||||
iconSize?: string;
|
||||
iconRadius?: string;
|
||||
size?: MenuSizeType;
|
||||
|
||||
placement?: PlacementWithLogical;
|
||||
menuList: {
|
||||
@@ -39,18 +43,9 @@ export type Props = {
|
||||
}[];
|
||||
};
|
||||
|
||||
const MyMenu = ({
|
||||
width = 'auto',
|
||||
trigger = 'hover',
|
||||
offset,
|
||||
iconSize = '1rem',
|
||||
Button,
|
||||
menuList,
|
||||
iconRadius,
|
||||
placement = 'bottom-start'
|
||||
}: Props) => {
|
||||
const typeMapStyle: Record<MenuItemType, MenuItemProps> = {
|
||||
primary: {
|
||||
const typeMapStyle: Record<MenuItemType, { styles: MenuItemProps; iconColor?: string }> = {
|
||||
primary: {
|
||||
styles: {
|
||||
_hover: {
|
||||
backgroundColor: 'primary.50',
|
||||
color: 'primary.600'
|
||||
@@ -64,7 +59,44 @@ const MyMenu = ({
|
||||
color: 'primary.600'
|
||||
}
|
||||
},
|
||||
danger: {
|
||||
iconColor: 'myGray.600'
|
||||
},
|
||||
gray: {
|
||||
styles: {
|
||||
_hover: {
|
||||
backgroundColor: 'myGray.05',
|
||||
color: 'primary.600'
|
||||
},
|
||||
_focus: {
|
||||
backgroundColor: 'myGray.05',
|
||||
color: 'primary.600'
|
||||
},
|
||||
_active: {
|
||||
backgroundColor: 'myGray.05',
|
||||
color: 'primary.600'
|
||||
}
|
||||
},
|
||||
iconColor: 'myGray.400'
|
||||
},
|
||||
grayBg: {
|
||||
styles: {
|
||||
_hover: {
|
||||
backgroundColor: 'myGray.05',
|
||||
color: 'primary.600'
|
||||
},
|
||||
_focus: {
|
||||
backgroundColor: 'myGray.05',
|
||||
color: 'primary.600'
|
||||
},
|
||||
_active: {
|
||||
backgroundColor: 'myGray.05',
|
||||
color: 'primary.600'
|
||||
}
|
||||
},
|
||||
iconColor: 'myGray.600'
|
||||
},
|
||||
danger: {
|
||||
styles: {
|
||||
color: 'red.600',
|
||||
_hover: {
|
||||
background: 'red.1'
|
||||
@@ -75,9 +107,97 @@ const MyMenu = ({
|
||||
_active: {
|
||||
background: 'red.1'
|
||||
}
|
||||
},
|
||||
iconColor: 'red.600'
|
||||
}
|
||||
};
|
||||
const sizeMapStyle: Record<
|
||||
MenuSizeType,
|
||||
{
|
||||
iconStyle: AvatarProps;
|
||||
labelStyle: BoxProps;
|
||||
dividerStyle: DividerProps;
|
||||
menuItemStyle: MenuItemProps;
|
||||
}
|
||||
> = {
|
||||
mini: {
|
||||
iconStyle: {
|
||||
w: '14px'
|
||||
},
|
||||
labelStyle: {
|
||||
fontSize: 'mini'
|
||||
},
|
||||
dividerStyle: {
|
||||
my: 0.5
|
||||
},
|
||||
menuItemStyle: {
|
||||
py: 1.5,
|
||||
px: 2
|
||||
}
|
||||
};
|
||||
},
|
||||
xs: {
|
||||
iconStyle: {
|
||||
w: '14px'
|
||||
},
|
||||
labelStyle: {
|
||||
fontSize: 'sm'
|
||||
},
|
||||
dividerStyle: {
|
||||
my: 0.5
|
||||
},
|
||||
menuItemStyle: {
|
||||
py: 1.5,
|
||||
px: 2
|
||||
}
|
||||
},
|
||||
sm: {
|
||||
iconStyle: {
|
||||
w: '1rem'
|
||||
},
|
||||
labelStyle: {
|
||||
fontSize: 'sm'
|
||||
},
|
||||
dividerStyle: {
|
||||
my: 1
|
||||
},
|
||||
menuItemStyle: {
|
||||
py: 2,
|
||||
px: 3,
|
||||
_notLast: {
|
||||
mb: 0.5
|
||||
}
|
||||
}
|
||||
},
|
||||
md: {
|
||||
iconStyle: {
|
||||
w: '2rem',
|
||||
borderRadius: '6px'
|
||||
},
|
||||
labelStyle: {
|
||||
fontSize: 'sm'
|
||||
},
|
||||
dividerStyle: {
|
||||
my: 1
|
||||
},
|
||||
menuItemStyle: {
|
||||
py: 2,
|
||||
px: 3,
|
||||
_notLast: {
|
||||
mb: 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const MyMenu = ({
|
||||
width = 'auto',
|
||||
trigger = 'hover',
|
||||
size = 'sm',
|
||||
offset,
|
||||
Button,
|
||||
menuList,
|
||||
placement = 'bottom-start'
|
||||
}: Props) => {
|
||||
const { isPc } = useSystem();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const closeTimer = useRef<any>();
|
||||
@@ -165,7 +285,7 @@ const MyMenu = ({
|
||||
return (
|
||||
<Box key={i}>
|
||||
{item.label && <Box fontSize={'sm'}>{item.label}</Box>}
|
||||
{i !== 0 && <MyDivider h={'1.5px'} my={1} />}
|
||||
{i !== 0 && <MyDivider h={'1.5px'} {...sizeMapStyle[size].dividerStyle} />}
|
||||
{item.children.map((child, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
@@ -177,29 +297,36 @@ const MyMenu = ({
|
||||
child.onClick();
|
||||
}
|
||||
}}
|
||||
py={2}
|
||||
px={3}
|
||||
alignItems={'center'}
|
||||
fontSize={'sm'}
|
||||
color={child.isActive ? 'primary.700' : 'myGray.600'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
_notLast={{ mb: 0.5 }}
|
||||
{...typeMapStyle[child.type || 'primary']}
|
||||
{...typeMapStyle[child.type || 'primary'].styles}
|
||||
{...sizeMapStyle[size].menuItemStyle}
|
||||
{...child.menuItemStyles}
|
||||
>
|
||||
{!!child.icon && (
|
||||
<Avatar
|
||||
src={child.icon as any}
|
||||
borderRadius={iconRadius}
|
||||
w={iconSize}
|
||||
mr={3}
|
||||
mr={2}
|
||||
{...sizeMapStyle[size].iconStyle}
|
||||
color={
|
||||
child.isActive
|
||||
? 'inherit'
|
||||
: typeMapStyle[child.type || 'primary'].iconColor
|
||||
}
|
||||
sx={{
|
||||
'[role="menuitem"]:hover &': {
|
||||
color: 'inherit'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Box w={'100%'}>
|
||||
<Box
|
||||
w={'100%'}
|
||||
color={child.description ? 'myGray.900' : 'inherit'}
|
||||
fontSize={'sm'}
|
||||
{...sizeMapStyle[size].labelStyle}
|
||||
>
|
||||
{child.label}
|
||||
</Box>
|
||||
|
@@ -60,7 +60,9 @@ export const MultipleRowSelect = ({
|
||||
const newValue = [...cloneValue];
|
||||
|
||||
if (item.value === selectedValue) {
|
||||
newValue[index] = undefined;
|
||||
for (let i = index; i < newValue.length; i++) {
|
||||
newValue[i] = undefined;
|
||||
}
|
||||
setCloneValue(newValue);
|
||||
onSelect(newValue);
|
||||
} else {
|
||||
|
@@ -18,12 +18,13 @@ const FillRowTabs = ({ list, value, onChange, py = '7px', px = '12px', ...props
|
||||
display={'inline-flex'}
|
||||
px={'3px'}
|
||||
py={'3px'}
|
||||
borderRadius={'md'}
|
||||
borderRadius={'sm'}
|
||||
borderWidth={'1px'}
|
||||
borderColor={'borderColor.base'}
|
||||
borderColor={'myGray.200'}
|
||||
bg={'myGray.50'}
|
||||
gap={'4px'}
|
||||
fontSize={'sm'}
|
||||
fontWeight={'medium'}
|
||||
{...props}
|
||||
>
|
||||
{list.map((item) => (
|
||||
@@ -33,7 +34,7 @@ const FillRowTabs = ({ list, value, onChange, py = '7px', px = '12px', ...props
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'md'}
|
||||
borderRadius={'xs'}
|
||||
px={px}
|
||||
py={py}
|
||||
userSelect={'none'}
|
||||
@@ -45,10 +46,14 @@ const FillRowTabs = ({ list, value, onChange, py = '7px', px = '12px', ...props
|
||||
color: 'primary.600'
|
||||
}
|
||||
: {
|
||||
color: 'myGray.500',
|
||||
_hover: {
|
||||
color: 'primary.600'
|
||||
},
|
||||
onClick: () => onChange(item.value)
|
||||
})}
|
||||
>
|
||||
{item.icon && <MyIcon name={item.icon as any} mr={1} w={'14px'} />}
|
||||
{item.icon && <MyIcon name={item.icon as any} mr={1.5} w={'18px'} />}
|
||||
<Box>{item.label}</Box>
|
||||
</Flex>
|
||||
))}
|
||||
|
@@ -224,8 +224,8 @@ const JSONEditor = ({
|
||||
{resize && (
|
||||
<Box
|
||||
position={'absolute'}
|
||||
right={'0'}
|
||||
bottom={'0'}
|
||||
right={'-2'}
|
||||
bottom={'-3'}
|
||||
zIndex={10}
|
||||
cursor={'ns-resize'}
|
||||
px={'4px'}
|
||||
@@ -269,6 +269,8 @@ const JSONEditor = ({
|
||||
fontSize={'xs'}
|
||||
color={'myGray.500'}
|
||||
display={placeholderDisplay}
|
||||
pointerEvents={'none'}
|
||||
userSelect={'none'}
|
||||
>
|
||||
{placeholder}
|
||||
</Box>
|
||||
|
@@ -105,8 +105,8 @@ export default function Editor({
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
py={2}
|
||||
px={3}
|
||||
py={3}
|
||||
px={3.5}
|
||||
pointerEvents={'none'}
|
||||
overflow={'hidden'}
|
||||
>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { ChevronRightIcon } from '@chakra-ui/icons';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Avatar from '../../../../../../../components/common/Avatar';
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function VariableLabel({
|
||||
color={parentLabel !== 'undefined' ? 'myGray.900' : 'red.600'}
|
||||
>
|
||||
{parentLabel !== 'undefined' ? (
|
||||
<span>
|
||||
<Flex alignItems={'center'} color={'myGray.600'}>
|
||||
<Avatar
|
||||
src={nodeAvatar as any}
|
||||
w={'1rem'}
|
||||
@@ -36,12 +36,11 @@ export default function VariableLabel({
|
||||
borderRadius={'xs'}
|
||||
display={'inline-flex'}
|
||||
verticalAlign={'middle'}
|
||||
mb={'3px'}
|
||||
/>
|
||||
{parentLabel}
|
||||
<ChevronRightIcon />
|
||||
<ChevronRightIcon color={'myGray.500'} />
|
||||
{childLabel}
|
||||
</span>
|
||||
</Flex>
|
||||
) : (
|
||||
<>
|
||||
<Box>{t('common:invalid_variable')}</Box>
|
||||
|
@@ -6,7 +6,7 @@ import { TextNode } from 'lexical';
|
||||
import { getHashtagRegexString } from './utils';
|
||||
import { mergeRegister } from '@lexical/utils';
|
||||
import { registerLexicalTextEntity } from '../../utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const REGEX = new RegExp(getHashtagRegexString(), 'i');
|
||||
|
||||
|
@@ -1,13 +1,12 @@
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import MyMenu from '../../common/MyMenu';
|
||||
import MyMenu, { MenuItemType } from '../../common/MyMenu';
|
||||
import {
|
||||
FlowNodeInputMap,
|
||||
FlowNodeInputTypeEnum
|
||||
} from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { Box, Button, useTheme } from '@chakra-ui/react';
|
||||
import { Button, useTheme } from '@chakra-ui/react';
|
||||
import MyIcon from '../../common/Icon';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfirm } from '../../../hooks/useConfirm';
|
||||
|
||||
const NodeInputSelect = ({
|
||||
renderTypeList,
|
||||
@@ -19,9 +18,6 @@ const NodeInputSelect = ({
|
||||
onChange: (e: string) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
title: t('common:core.workflow.Change input type tip')
|
||||
});
|
||||
const renderType = renderTypeList[renderTypeIndex];
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -136,7 +132,22 @@ const NodeInputSelect = ({
|
||||
);
|
||||
|
||||
const filterMenuList = useMemo(
|
||||
() => renderList.filter((item) => renderTypeList.includes(item.renderType)),
|
||||
() =>
|
||||
renderList
|
||||
.filter((item) => renderTypeList.includes(item.renderType))
|
||||
.map((item) => ({
|
||||
...item,
|
||||
type: 'gray' as MenuItemType,
|
||||
menuItemStyles: {
|
||||
fontWeight: 'medium',
|
||||
minH: 7,
|
||||
h: 7,
|
||||
px: 1,
|
||||
py: 0,
|
||||
mb: 0,
|
||||
borderRadius: 'xs'
|
||||
}
|
||||
})),
|
||||
[renderTypeList, renderList]
|
||||
);
|
||||
const renderTypeData = useMemo(
|
||||
@@ -148,24 +159,37 @@ const NodeInputSelect = ({
|
||||
<MyMenu
|
||||
offset={[-0.5, 0.5]}
|
||||
trigger="click"
|
||||
size="mini"
|
||||
Button={
|
||||
<Button
|
||||
leftIcon={
|
||||
<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'} ml={-1} />}
|
||||
rightIcon={
|
||||
<MyIcon
|
||||
name={'common/select'}
|
||||
w={'0.8rem'}
|
||||
color={'myGray.500'}
|
||||
mx={-1}
|
||||
sx={{
|
||||
'button:hover &': {
|
||||
color: 'primary.600'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}
|
||||
variant={'grayBase'}
|
||||
border={theme.borders.base}
|
||||
borderColor={'myGray.200'}
|
||||
borderRadius={'sm'}
|
||||
px={'10px'}
|
||||
py={'6px'}
|
||||
px={'8px'}
|
||||
fontSize={'mini'}
|
||||
color={'myGray.600'}
|
||||
h={'28px'}
|
||||
bg={'myGray.100'}
|
||||
minH={'28px'}
|
||||
h={'28px'}
|
||||
>
|
||||
<Box fontWeight={'medium'}>{renderTypeData.title}</Box>
|
||||
{renderTypeData.title}
|
||||
</Button>
|
||||
}
|
||||
menuList={[{ children: filterMenuList }]}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import Cookies, { CookieAttributes } from 'js-cookie';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { LangEnum } from '../../../projects/app/src/web/common/utils/i18n';
|
||||
|
||||
const setCookie = (key: string, value: string, options?: CookieAttributes) => {
|
||||
Cookies.set(key, value, options);
|
||||
@@ -13,11 +14,13 @@ const LANG_KEY = 'NEXT_LOCALE';
|
||||
export const useI18nLng = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const languageMap: Record<string, string> = {
|
||||
zh: 'zh',
|
||||
'zh-CN': 'zh',
|
||||
'zh-Hans': 'zh',
|
||||
en: 'en',
|
||||
'en-US': 'en'
|
||||
zh: LangEnum.zh_CN,
|
||||
'zh-CN': LangEnum.zh_CN,
|
||||
'zh-Hans': LangEnum.zh_CN,
|
||||
'zh-HK': LangEnum.zh_CN,
|
||||
'zh-TW': LangEnum.zh_TW,
|
||||
en: LangEnum.en,
|
||||
'en-US': LangEnum.en
|
||||
};
|
||||
|
||||
const onChangeLng = (lng: string) => {
|
||||
|
@@ -188,7 +188,8 @@ export function useScrollPagination<
|
||||
|
||||
pageSize = 10,
|
||||
params = {},
|
||||
EmptyTip
|
||||
EmptyTip,
|
||||
showErrorToast = true
|
||||
}: {
|
||||
refreshDeps?: any[];
|
||||
scrollLoadType?: 'top' | 'bottom';
|
||||
@@ -196,6 +197,7 @@ export function useScrollPagination<
|
||||
pageSize?: number;
|
||||
params?: Record<string, any>;
|
||||
EmptyTip?: React.JSX.Element;
|
||||
showErrorToast?: boolean;
|
||||
}
|
||||
) {
|
||||
const { t } = useTranslation();
|
||||
@@ -249,10 +251,12 @@ export function useScrollPagination<
|
||||
setData((prevData) => (offset === 0 ? res.list : [...prevData, ...res.list]));
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: getErrText(error, t('common:core.chat.error.data_error')),
|
||||
status: 'error'
|
||||
});
|
||||
if (showErrorToast) {
|
||||
toast({
|
||||
title: getErrText(error, t('common:core.chat.error.data_error')),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,10 @@
|
||||
import { useToast as uToast, UseToastOptions } from '@chakra-ui/react';
|
||||
import { CSSProperties, useCallback } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
export const useToast = (props?: UseToastOptions & { containerStyle?: CSSProperties }) => {
|
||||
const { containerStyle, ...toastProps } = props || {};
|
||||
const { t } = useTranslation();
|
||||
|
||||
const toast = uToast({
|
||||
position: 'top',
|
||||
@@ -17,7 +19,11 @@ export const useToast = (props?: UseToastOptions & { containerStyle?: CSSPropert
|
||||
const myToast = useCallback(
|
||||
(options?: UseToastOptions) => {
|
||||
if (options?.title || options?.description) {
|
||||
toast(options);
|
||||
toast({
|
||||
...(options.title && { title: t(options.title as any) }),
|
||||
...(options.description && { description: t(options.description as any) }),
|
||||
...options
|
||||
});
|
||||
}
|
||||
},
|
||||
[props]
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"Role_setting": "Role setting",
|
||||
"Role_setting": "Permission",
|
||||
"Run": "Execute",
|
||||
"Team Tags Set": "Team tags",
|
||||
"Team_Tags": "Team tags",
|
||||
@@ -14,6 +14,8 @@
|
||||
"app.version_past": "Previously Published",
|
||||
"app.version_publish_tips": "This version will be saved to the team cloud, synchronized with the entire team, and update the app version on all release channels.",
|
||||
"app_detail": "Application Details",
|
||||
"auto_execute": "Automatic execution",
|
||||
"auto_execute_default_prompt_placeholder": "Default questions sent when executing automatically",
|
||||
"chat_debug": "Chat Preview",
|
||||
"chat_logs": "Conversation Logs",
|
||||
"chat_logs_tips": "Logs will record the online, shared, and API (requires chatId) conversation records of this app.",
|
||||
@@ -36,10 +38,10 @@
|
||||
"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_upload": "Document Upload",
|
||||
"edit_app": "Edit Application",
|
||||
"edit_info": "Edit Information",
|
||||
"edit_info": "Edit",
|
||||
"execute_time": "Execution Time",
|
||||
"export_config_successful": "Configuration copied, some sensitive information automatically filtered. Please check for any remaining sensitive data.",
|
||||
"export_configs": "Export Configurations",
|
||||
"export_configs": "Export",
|
||||
"feedback_count": "User Feedback",
|
||||
"file_quote_link": "Files",
|
||||
"file_recover": "File will overwrite current content",
|
||||
@@ -50,7 +52,7 @@
|
||||
"go_to_run": "Go to Execution",
|
||||
"image_upload": "Image Upload",
|
||||
"image_upload_tip": "How to activate model image recognition capabilities",
|
||||
"import_configs": "Import Configurations",
|
||||
"import_configs": "Import",
|
||||
"import_configs_failed": "Import configuration failed, please ensure the configuration is correct!",
|
||||
"import_configs_success": "Import Successful",
|
||||
"initial_form": "initial state",
|
||||
@@ -77,6 +79,7 @@
|
||||
"move.hint": "After moving, the selected application/folder will inherit the permission settings of the new folder, and the original permission settings will become invalid.",
|
||||
"move_app": "Move Application",
|
||||
"not_json_file": "Please select a JSON file",
|
||||
"open_auto_execute": "Enable automatic execution",
|
||||
"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",
|
||||
"paste_config": "Paste Configuration",
|
||||
|
@@ -4,6 +4,7 @@
|
||||
"LLM_model_response_empty": "The model flow response is empty, please check whether the model flow output is normal.",
|
||||
"chat_history": "Conversation History",
|
||||
"chat_input_guide_lexicon_is_empty": "Lexicon not configured yet",
|
||||
"chat_test_app": "Debug-{{name}}",
|
||||
"citations": "{{num}} References",
|
||||
"click_contextual_preview": "Click to see contextual preview",
|
||||
"config_input_guide": "Set Up Input Guide",
|
||||
|
@@ -268,9 +268,11 @@
|
||||
"core.app.Api request": "API Request",
|
||||
"core.app.Api request desc": "Integrate into existing systems through API, or WeChat Work, Feishu, etc.",
|
||||
"core.app.App intro": "App Introduction",
|
||||
"core.app.Auto execute": "Auto execute",
|
||||
"core.app.Chat Variable": "Chat Variable",
|
||||
"core.app.Config schedule plan": "Configure Scheduled Execution",
|
||||
"core.app.Config whisper": "Configure Voice Input",
|
||||
"core.app.Config_auto_execute": "Click to configure automatic execution rules",
|
||||
"core.app.Interval timer config": "Scheduled Execution Configuration",
|
||||
"core.app.Interval timer run": "Scheduled Execution",
|
||||
"core.app.Interval timer tip": "Can Execute App on Schedule",
|
||||
@@ -428,7 +430,7 @@
|
||||
"core.chat.logs.online": "Online Use",
|
||||
"core.chat.logs.share": "External Link Call",
|
||||
"core.chat.logs.team": "Team Space Chat",
|
||||
"core.chat.logs.test": "Test",
|
||||
"core.chat.logs.test": "Online debugging",
|
||||
"core.chat.logs.wecom": "WeChat Work",
|
||||
"core.chat.markdown.Edit Question": "Edit Question",
|
||||
"core.chat.markdown.Quick Question": "Click to Ask Immediately",
|
||||
@@ -732,11 +734,11 @@
|
||||
"core.module.template.AI function": "AI Capability",
|
||||
"core.module.template.AI response switch tip": "If you want the current node not to output content, you can turn off this switch. The content output by AI will not be displayed to the user, and you can manually use 'AI Response Content' for special processing.",
|
||||
"core.module.template.AI support tool tip": "Models that support function calls can better use tool calls.",
|
||||
"core.module.template.Basic Node": "Basic Function",
|
||||
"core.module.template.Basic Node": "Basic",
|
||||
"core.module.template.Query extension": "Question Optimization",
|
||||
"core.module.template.System Plugin": "System Plugin",
|
||||
"core.module.template.System Plugin": "System",
|
||||
"core.module.template.System input module": "System Input",
|
||||
"core.module.template.Team app": "Team App",
|
||||
"core.module.template.Team app": "Team",
|
||||
"core.module.template.Tool module": "Tool",
|
||||
"core.module.template.UnKnow Module": "Unknown Module",
|
||||
"core.module.template.ai_chat": "AI conversation",
|
||||
|
@@ -14,6 +14,9 @@
|
||||
"app.version_past": "发布过",
|
||||
"app.version_publish_tips": "该版本将被保存至团队云端,同步给整个团队,同时更新所有发布渠道的应用版本",
|
||||
"app_detail": "应用详情",
|
||||
"auto_execute": "自动执行",
|
||||
"auto_execute_default_prompt_placeholder": "自动执行时,发送的默认问题",
|
||||
"auto_execute_tip": "开启后,用户进入对话界面将自动触发工作流。执行顺序:1、对话开场白;2、全局变量;3、自动执行。",
|
||||
"chat_debug": "调试预览",
|
||||
"chat_logs": "对话日志",
|
||||
"chat_logs_tips": "日志会记录该应用的在线、分享和 API(需填写 chatId)对话记录",
|
||||
@@ -77,6 +80,7 @@
|
||||
"move.hint": "移动后,所选应用/文件夹将继承新文件夹的权限设置,原先的权限设置失效。",
|
||||
"move_app": "移动应用",
|
||||
"not_json_file": "请选择JSON文件",
|
||||
"open_auto_execute": "启用自动执行",
|
||||
"open_vision_function_tip": "有图示开关的模型即拥有图片识别能力。若开启,模型会解析文件链接里的图片,并自动解析用户问题中的图片(用户问题≤500字时生效)。",
|
||||
"or_drag_JSON": "或拖入JSON文件",
|
||||
"paste_config": "粘贴配置",
|
@@ -4,6 +4,7 @@
|
||||
"LLM_model_response_empty": "模型流响应为空,请检查模型流输出是否正常",
|
||||
"chat_history": "聊天记录",
|
||||
"chat_input_guide_lexicon_is_empty": "还没有配置词库",
|
||||
"chat_test_app": "调试-{{name}}",
|
||||
"citations": "{{num}}条引用",
|
||||
"click_contextual_preview": "点击查看上下文预览",
|
||||
"config_input_guide": "配置输入引导",
|
@@ -267,9 +267,11 @@
|
||||
"core.app.Api request": "API 访问",
|
||||
"core.app.Api request desc": "通过 API 接入到已有系统中,或企微、飞书等",
|
||||
"core.app.App intro": "应用介绍",
|
||||
"core.app.Auto execute": "自动执行",
|
||||
"core.app.Chat Variable": "对话框变量",
|
||||
"core.app.Config schedule plan": "配置定时执行",
|
||||
"core.app.Config whisper": "配置语音输入",
|
||||
"core.app.Config_auto_execute": "点击配置自动执行规则",
|
||||
"core.app.Interval timer config": "定时执行配置",
|
||||
"core.app.Interval timer run": "定时执行",
|
||||
"core.app.Interval timer tip": "可定时执行应用",
|
||||
@@ -427,7 +429,7 @@
|
||||
"core.chat.logs.online": "在线使用",
|
||||
"core.chat.logs.share": "外部链接调用",
|
||||
"core.chat.logs.team": "团队空间对话",
|
||||
"core.chat.logs.test": "测试",
|
||||
"core.chat.logs.test": "在线调试",
|
||||
"core.chat.logs.wecom": "企业微信",
|
||||
"core.chat.markdown.Edit Question": "编辑问题",
|
||||
"core.chat.markdown.Quick Question": "点我立即提问",
|
@@ -221,7 +221,6 @@ const Button = defineStyleConfig({
|
||||
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
|
||||
_hover: {
|
||||
color: 'red.600',
|
||||
background: 'red.1',
|
||||
borderColor: 'red.300'
|
||||
},
|
||||
_active: {
|
||||
@@ -251,6 +250,16 @@ const Button = defineStyleConfig({
|
||||
color: 'red.600'
|
||||
}
|
||||
},
|
||||
grayGhost: {
|
||||
color: 'myGray.500',
|
||||
fontWeight: '500',
|
||||
p: 0,
|
||||
bg: 'transparent',
|
||||
transition: 'background 0.1s',
|
||||
_hover: {
|
||||
bg: 'myGray.05'
|
||||
}
|
||||
},
|
||||
transparentBase: {
|
||||
color: 'myGray.800',
|
||||
fontWeight: '500',
|
||||
@@ -570,6 +579,43 @@ const Table = tableMultiStyle({
|
||||
}
|
||||
})
|
||||
},
|
||||
variants: {
|
||||
workflow: {
|
||||
table: {
|
||||
bg: 'white'
|
||||
},
|
||||
thead: {
|
||||
tr: {
|
||||
th: {
|
||||
p: '0',
|
||||
px: 4,
|
||||
bg: 'myGray.50',
|
||||
borderRadius: 'none !important',
|
||||
borderBottom: 'none',
|
||||
height: '32px',
|
||||
fontSize: 'mini',
|
||||
fontWeight: 'medium'
|
||||
}
|
||||
}
|
||||
},
|
||||
tbody: {
|
||||
tr: {
|
||||
td: {
|
||||
p: '0',
|
||||
px: 4,
|
||||
fontSize: 'xs',
|
||||
borderBottom: 'base',
|
||||
height: '40px'
|
||||
},
|
||||
'&:last-child': {
|
||||
td: {
|
||||
borderBottom: 'none'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultProps: {
|
||||
size: 'md'
|
||||
}
|
||||
|
18
packages/web/types/i18next.d.ts
vendored
@@ -1,13 +1,13 @@
|
||||
import 'i18next';
|
||||
import common from '../i18n/zh/common.json';
|
||||
import dataset from '../i18n/zh/dataset.json';
|
||||
import app from '../i18n/zh/app.json';
|
||||
import file from '../i18n/zh/file.json';
|
||||
import publish from '../i18n/zh/publish.json';
|
||||
import workflow from '../i18n/zh/workflow.json';
|
||||
import user from '../i18n/zh/user.json';
|
||||
import chat from '../i18n/zh/chat.json';
|
||||
import login from '../i18n/zh/login.json';
|
||||
import common from '../i18n/zh-CN/common.json';
|
||||
import dataset from '../i18n/zh-CN/dataset.json';
|
||||
import app from '../i18n/zh-CN/app.json';
|
||||
import file from '../i18n/zh-CN/file.json';
|
||||
import publish from '../i18n/zh-CN/publish.json';
|
||||
import workflow from '../i18n/zh-CN/workflow.json';
|
||||
import user from '../i18n/zh-CN/user.json';
|
||||
import chat from '../i18n/zh-CN/chat.json';
|
||||
import login from '../i18n/zh-CN/login.json';
|
||||
|
||||
export interface I18nNamespaces {
|
||||
common: typeof common;
|
||||
|
@@ -5,8 +5,8 @@
|
||||
|
||||
module.exports = {
|
||||
i18n: {
|
||||
defaultLocale: 'zh',
|
||||
locales: ['en', 'zh', 'zh_TW'],
|
||||
defaultLocale: 'zh-CN',
|
||||
locales: ['en', 'zh-CN', 'zh-TW'],
|
||||
localeDetection: false
|
||||
},
|
||||
localePath:
|
||||
|
@@ -85,7 +85,8 @@ const nextConfig = {
|
||||
experimental: {
|
||||
// 优化 Server Components 的构建和运行,避免不必要的客户端打包。
|
||||
serverComponentsExternalPackages: ['mongoose', 'pg', '@node-rs/jieba', 'duck-duck-scrape'],
|
||||
outputFileTracingRoot: path.join(__dirname, '../../')
|
||||
outputFileTracingRoot: path.join(__dirname, '../../'),
|
||||
instrumentationHook: true
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "4.8.13",
|
||||
"version": "4.8.14",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
8
projects/app/public/imgs/app/autoExec-icon.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg width="36" height="37" viewBox="0 0 36 37" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect y="0.617554" width="36" height="36" rx="6" fill="#F0EEFF"/>
|
||||
<path d="M14.8629 19.2382L16.7322 14.679C16.7937 14.5289 16.94 14.4308 17.1023 14.4308H19.7262C20.0163 14.4308 20.2099 14.7299 20.0912 14.9946L18.9433 17.552C18.884 17.6843 18.9808 17.8339 19.1258 17.8339H21.1135C21.4463 17.8339 21.6336 18.2166 21.4294 18.4793L17.7127 23.2613C17.4643 23.5809 16.9528 23.3717 16.9996 22.9696L17.3439 20.0131C17.3577 19.8943 17.2648 19.79 17.1452 19.79H15.233C14.9485 19.79 14.755 19.5014 14.8629 19.2382Z" fill="#8774EE"/>
|
||||
<path d="M10.2067 23.1292C9.73113 23.4045 9.11745 23.2438 8.89074 22.7433C7.9809 20.7346 7.7568 18.4713 8.27046 16.3077C8.86108 13.8199 10.3822 11.6529 12.5212 10.2521C14.6601 8.85118 17.2545 8.32284 19.7709 8.77564C21.9595 9.16945 23.9446 10.2794 25.4224 11.9162C25.7906 12.324 25.6927 12.9508 25.2503 13.2767C24.8079 13.6026 24.1892 13.503 23.811 13.1043C22.6426 11.8729 21.1061 11.0377 19.4185 10.734C17.4028 10.3713 15.3247 10.7945 13.6114 11.9167C11.898 13.0388 10.6796 14.7746 10.2065 16.7673C9.81043 18.4356 9.96196 20.1778 10.6239 21.741C10.8382 22.247 10.6822 22.854 10.2067 23.1292Z" fill="#8774EE"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6398 21.7804C10.6586 21.8296 10.6739 21.8797 10.6859 21.9302C10.7953 22.3903 10.6241 22.8876 10.2067 23.1292C9.73113 23.4045 9.11745 23.2438 8.89074 22.7433C8.85672 22.6682 8.82366 22.5927 8.79156 22.5169C8.77571 22.4795 8.7601 22.442 8.74472 22.4044C7.95817 20.4821 7.78549 18.3505 8.27046 16.3077C8.86108 13.8199 10.3822 11.6529 12.5212 10.2521C14.6601 8.85118 17.2545 8.32284 19.7709 8.77564C21.8373 9.14745 23.7223 10.1576 25.1701 11.6469C25.1984 11.676 25.2265 11.7053 25.2545 11.7348C25.3112 11.7945 25.3671 11.855 25.4224 11.9162C25.7906 12.324 25.6927 12.9508 25.2503 13.2767C24.8619 13.5628 24.3377 13.521 23.9596 13.2368C23.9181 13.2056 23.8784 13.1715 23.8408 13.1346C23.8307 13.1247 23.8207 13.1146 23.811 13.1043C22.6426 11.8729 21.1061 11.0377 19.4185 10.734C17.4028 10.3713 15.3247 10.7945 13.6114 11.9167C11.898 13.0388 10.6796 14.7746 10.2065 16.7673C9.81043 18.4356 9.96196 20.1778 10.6239 21.741C10.6294 21.7541 10.6347 21.7672 10.6398 21.7804Z" fill="#8774EE"/>
|
||||
<path d="M26.3355 15.2101C26.8441 15.0022 27.4302 15.2451 27.5866 15.7719C28.1567 17.6924 28.1388 19.7484 27.5225 21.6707C26.8045 23.9101 25.3203 25.8248 23.3306 27.0784C21.3408 28.3319 18.973 28.8442 16.6431 28.5251C14.6431 28.2511 12.7808 27.3797 11.2946 26.0363C10.8869 25.6678 10.9209 25.0344 11.328 24.6653C11.7351 24.2963 12.3607 24.3327 12.7774 24.691C13.9423 25.6928 15.377 26.3432 16.9131 26.5536C18.7794 26.8092 20.6761 26.3989 22.2699 25.3948C23.8636 24.3907 25.0526 22.8569 25.6277 21.0632C26.1011 19.5867 26.1337 18.0119 25.733 16.5286C25.5897 15.9981 25.8269 15.4181 26.3355 15.2101Z" fill="#8774EE"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.7226 16.4874C25.7258 16.5011 25.7293 16.5149 25.733 16.5286C26.1337 18.0119 26.1011 19.5867 25.6277 21.0632C25.0526 22.8569 23.8636 24.3907 22.2699 25.3948C20.6761 26.3989 18.7794 26.8092 16.9131 26.5536C15.377 26.3432 13.9423 25.6928 12.7774 24.691C12.7666 24.6817 12.7557 24.6727 12.7446 24.6639C12.7035 24.631 12.6605 24.6011 12.6161 24.5743C12.2111 24.3299 11.6854 24.3414 11.328 24.6653C10.9209 25.0344 10.8869 25.6678 11.2946 26.0363C11.3557 26.0916 11.4175 26.1461 11.48 26.1997C11.5108 26.2262 11.5417 26.2525 11.5729 26.2786C13.0103 27.4846 14.766 28.268 16.6431 28.5251C18.973 28.8442 21.3408 28.3319 23.3306 27.0784C25.3203 25.8248 26.8045 23.9101 27.5225 21.6707C28.101 19.8666 28.1523 17.9447 27.685 16.1275C27.6749 16.0882 27.6646 16.0489 27.654 16.0096C27.6325 15.9302 27.61 15.8509 27.5866 15.7719C27.4302 15.2451 26.8441 15.0022 26.3355 15.2101C25.889 15.3927 25.6517 15.8619 25.6973 16.3327C25.7023 16.3844 25.7107 16.436 25.7226 16.4874Z" fill="#8774EE"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
91
projects/app/public/imgs/app/autoExec.svg
Normal file
After Width: | Height: | Size: 15 KiB |
@@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
|
||||
import { Box, BoxProps, Flex, Link, LinkProps } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { HUMAN_ICON } from '@fastgpt/global/common/system/constants';
|
||||
import NextLink from 'next/link';
|
||||
import Badge from '../Badge';
|
||||
@@ -23,14 +23,14 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
const router = useRouter();
|
||||
const { userInfo } = useUserStore();
|
||||
const { gitStar, feConfigs } = useSystemStore();
|
||||
const { lastChatAppId, lastChatId } = useChatStore();
|
||||
const { lastChatAppId } = useChatStore();
|
||||
const navbarList = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: t('common:navbar.Chat'),
|
||||
icon: 'core/chat/chatLight',
|
||||
activeIcon: 'core/chat/chatFill',
|
||||
link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`,
|
||||
link: `/chat?appId=${lastChatAppId}`,
|
||||
activeLink: ['/chat']
|
||||
},
|
||||
{
|
||||
@@ -55,7 +55,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
activeLink: ['/account']
|
||||
}
|
||||
],
|
||||
[lastChatAppId, lastChatId, t]
|
||||
[lastChatAppId, t]
|
||||
);
|
||||
|
||||
const itemStyles: BoxProps & LinkProps = {
|
||||
@@ -84,6 +84,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
h={'100%'}
|
||||
w={'100%'}
|
||||
userSelect={'none'}
|
||||
pb={2}
|
||||
>
|
||||
{/* logo */}
|
||||
<Box
|
||||
@@ -155,6 +156,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
href={`/account?currentTab=inform`}
|
||||
mb={0}
|
||||
color={'myGray.500'}
|
||||
height={'48px'}
|
||||
>
|
||||
<Badge count={unread}>
|
||||
<MyIcon name={'support/user/informLight'} width={'22px'} height={'22px'} />
|
||||
@@ -171,6 +173,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
target="_blank"
|
||||
mb={0}
|
||||
color={'myGray.500'}
|
||||
height={'48px'}
|
||||
>
|
||||
<MyIcon name={'common/courseLight'} width={'24px'} height={'24px'} />
|
||||
</Link>
|
||||
@@ -186,6 +189,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
{...hoverStyle}
|
||||
mt={0}
|
||||
color={'myGray.500'}
|
||||
height={'48px'}
|
||||
>
|
||||
<MyIcon name={'common/gitInlight'} width={'26px'} height={'26px'} />
|
||||
</Link>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Flex, Box } from '@chakra-ui/react';
|
||||
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Badge from '../Badge';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
@@ -9,14 +9,14 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
const NavbarPhone = ({ unread }: { unread: number }) => {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { lastChatAppId, lastChatId } = useChatStore();
|
||||
const { lastChatAppId } = useChatStore();
|
||||
const navbarList = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: t('common:navbar.Chat'),
|
||||
icon: 'core/chat/chatLight',
|
||||
activeIcon: 'core/chat/chatFill',
|
||||
link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`,
|
||||
link: `/chat?appId=${lastChatAppId}`,
|
||||
activeLink: ['/chat'],
|
||||
unread: 0
|
||||
},
|
||||
@@ -45,7 +45,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
|
||||
unread
|
||||
}
|
||||
],
|
||||
[t, lastChatAppId, lastChatId, unread]
|
||||
[t, lastChatAppId, unread]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { langMap } from '@/web/common/utils/i18n';
|
||||
import { Avatar, Box, Flex } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
@@ -14,7 +14,7 @@ const I18nLngSelector = () => {
|
||||
return Object.entries(langMap).map(([key, lang]) => ({
|
||||
label: (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon borderRadius={'0'} mr={2} name={lang.avatar as any} w={'14px'} h={'9px'} />
|
||||
<MyIcon borderRadius={'0'} mr={2} name={lang.avatar as any} w={'1rem'} />
|
||||
<Box>{lang.label}</Box>
|
||||
</Flex>
|
||||
),
|
||||
|
87
projects/app/src/components/core/app/AutoExecConfig.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { Box, Button, Flex, ModalBody, Switch, Textarea, useDisclosure } from '@chakra-ui/react';
|
||||
import { defaultAutoExecuteConfig } from '@fastgpt/global/core/app/constants';
|
||||
import { AppAutoExecuteConfigType } from '@fastgpt/global/core/app/type';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ChatFunctionTip from './Tip';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
|
||||
const AutoExecConfig = ({
|
||||
value = defaultAutoExecuteConfig,
|
||||
onChange
|
||||
}: {
|
||||
value?: AppAutoExecuteConfigType;
|
||||
onChange: (e: AppAutoExecuteConfigType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const isOpenAutoExec = value.open;
|
||||
const defaultPrompt = value.defaultPrompt;
|
||||
|
||||
const formLabel = isOpenAutoExec
|
||||
? t('common:core.app.whisper.Open')
|
||||
: t('common:core.app.whisper.Close');
|
||||
|
||||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/simpleMode/autoExec'} mr={2} w={'20px'} />
|
||||
<FormLabel color={'myGray.600'}>{t('app:auto_execute')}</FormLabel>
|
||||
<ChatFunctionTip type={'autoExec'} />
|
||||
<Box flex={1} />
|
||||
<MyTooltip label={t('common:core.app.Config_auto_execute')}>
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
mr={'-5px'}
|
||||
onClick={onOpen}
|
||||
color={'myGray.600'}
|
||||
>
|
||||
{formLabel}
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
<MyModal
|
||||
title={t('common:core.app.Auto execute')}
|
||||
iconSrc="core/app/simpleMode/autoExec"
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalBody>
|
||||
<Flex justifyContent={'space-between'} alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'}>{t('app:open_auto_execute')}</FormLabel>
|
||||
<Switch
|
||||
isChecked={isOpenAutoExec}
|
||||
onChange={(e) => {
|
||||
onChange({
|
||||
...value,
|
||||
open: e.target.checked
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
{isOpenAutoExec && (
|
||||
<Box mt={4}>
|
||||
<FormLabel mb={1}>{t('common:core.app.schedule.Default prompt')}</FormLabel>
|
||||
<Textarea
|
||||
value={defaultPrompt}
|
||||
rows={8}
|
||||
bg={'myGray.50'}
|
||||
placeholder={t('app:auto_execute_default_prompt_placeholder')}
|
||||
onChange={(e) => {
|
||||
onChange({
|
||||
...value,
|
||||
defaultPrompt: e.target.value
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default AutoExecConfig;
|
@@ -139,7 +139,7 @@ const InputGuideConfig = ({
|
||||
onOpenLexiconConfig();
|
||||
}}
|
||||
>
|
||||
{chatT('config_input_guide_lexicon')}
|
||||
{t('chat:config_input_guide_lexicon')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<>
|
||||
@@ -152,7 +152,7 @@ const InputGuideConfig = ({
|
||||
cursor={'pointer'}
|
||||
>
|
||||
<MyIcon name={'book'} w={'17px'} ml={4} mr={1} color={'myGray.600'} />
|
||||
{commonT('common.Documents')}
|
||||
{t('common:common.Documents')}
|
||||
</Flex>
|
||||
<Box flex={'1 0 0'} />
|
||||
</Flex>
|
||||
|
@@ -23,7 +23,7 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
const MultipleRowSelect = dynamic(
|
||||
() => import('@fastgpt/web/components/common/MySelect/MultipleRowSelect')
|
||||
);
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
|
||||
// options type:
|
||||
enum CronJobTypeEnum {
|
||||
month = 'month',
|
||||
@@ -233,24 +233,24 @@ const ScheduledTriggerConfig = ({
|
||||
}
|
||||
|
||||
if (cronField[0] === 'month') {
|
||||
return t('core.app.schedule.Every month', {
|
||||
return t('common:core.app.schedule.Every month', {
|
||||
day: cronField[1],
|
||||
hour: cronField[2]
|
||||
});
|
||||
}
|
||||
if (cronField[0] === 'week') {
|
||||
return t('core.app.schedule.Every week', {
|
||||
return t('common:core.app.schedule.Every week', {
|
||||
day: cronField[1] === 0 ? t('app:day') : cronField[1],
|
||||
hour: cronField[2]
|
||||
});
|
||||
}
|
||||
if (cronField[0] === 'day') {
|
||||
return t('core.app.schedule.Every day', {
|
||||
return t('common:core.app.schedule.Every day', {
|
||||
hour: cronField[1]
|
||||
});
|
||||
}
|
||||
if (cronField[0] === 'interval') {
|
||||
return t('core.app.schedule.Interval', {
|
||||
return t('common:core.app.schedule.Interval', {
|
||||
interval: cronField[1]
|
||||
});
|
||||
}
|
||||
|
@@ -12,7 +12,8 @@ enum FnTypeEnum {
|
||||
welcome = 'welcome',
|
||||
file = 'file',
|
||||
visionModel = 'visionModel',
|
||||
instruction = 'instruction'
|
||||
instruction = 'instruction',
|
||||
autoExec = 'autoExec'
|
||||
}
|
||||
|
||||
const ChatFunctionTip = ({ type }: { type: `${FnTypeEnum}` }) => {
|
||||
@@ -66,6 +67,12 @@ const ChatFunctionTip = ({ type }: { type: `${FnTypeEnum}` }) => {
|
||||
title: t('workflow:plugin.Instructions'),
|
||||
desc: t('workflow:plugin.Instruction_Tip'),
|
||||
imgUrl: '/imgs/app/instruction.svg'
|
||||
},
|
||||
[FnTypeEnum.autoExec]: {
|
||||
icon: '/imgs/app/autoExec-icon.svg',
|
||||
title: t('common:core.app.Auto execute'),
|
||||
desc: t('app:auto_execute_tip'),
|
||||
imgUrl: '/imgs/app/autoExec.svg'
|
||||
}
|
||||
});
|
||||
const data = map.current[type];
|
||||
|
@@ -31,6 +31,7 @@ import ChatFunctionTip from './Tip';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import InputTypeConfig from '@/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig';
|
||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||
|
||||
export const defaultVariable: VariableItemType = {
|
||||
id: nanoid(),
|
||||
@@ -190,92 +191,59 @@ const VariableEdit = ({
|
||||
</Flex>
|
||||
{/* Form render */}
|
||||
{formatVariables.length > 0 && (
|
||||
<Box mt={2} borderRadius={'md'} overflow={'hidden'} borderWidth={'1px'} borderBottom="none">
|
||||
<TableContainer>
|
||||
<Table bg={'white'}>
|
||||
<Thead h={8}>
|
||||
<Tr>
|
||||
<Th
|
||||
borderRadius={'none !important'}
|
||||
fontSize={'mini'}
|
||||
bg={'myGray.50'}
|
||||
p={0}
|
||||
px={4}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
{t('workflow:Variable_name')}
|
||||
</Th>
|
||||
<Th fontSize={'mini'} bg={'myGray.50'} p={0} px={4} fontWeight={'medium'}>
|
||||
{t('common:common.Require Input')}
|
||||
</Th>
|
||||
<Th
|
||||
fontSize={'mini'}
|
||||
borderRadius={'none !important'}
|
||||
bg={'myGray.50'}
|
||||
p={0}
|
||||
px={4}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
{t('common:common.Operation')}
|
||||
</Th>
|
||||
<TableContainer mt={2} borderRadius={'md'} overflow={'hidden'} borderWidth={'1px'}>
|
||||
<Table variant={'workflow'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{t('workflow:Variable_name')}</Th>
|
||||
<Th>{t('common:common.Require Input')}</Th>
|
||||
<Th>{t('common:common.Operation')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{formatVariables.map((item, index) => (
|
||||
<Tr key={item.id}>
|
||||
<Td fontWeight={'medium'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={item.icon as any} w={'16px'} color={'myGray.400'} mr={2} />
|
||||
{item.key}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>
|
||||
<Flex alignItems={'center'}>
|
||||
{item.required ? (
|
||||
<MyIcon name={'check'} w={'16px'} color={'myGray.900'} mr={2} />
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>
|
||||
<Flex>
|
||||
<MyIconButton
|
||||
icon={'common/settingLight'}
|
||||
onClick={() => {
|
||||
const formattedItem = {
|
||||
...item,
|
||||
list: item.enums || []
|
||||
};
|
||||
reset(formattedItem);
|
||||
}}
|
||||
/>
|
||||
<MyIconButton
|
||||
icon={'delete'}
|
||||
hoverColor={'red.500'}
|
||||
onClick={() =>
|
||||
onChange(variables.filter((variable) => variable.id !== item.id))
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
</Td>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{formatVariables.map((item) => (
|
||||
<Tr key={item.id}>
|
||||
<Td
|
||||
p={0}
|
||||
px={4}
|
||||
h={8}
|
||||
color={'myGray.900'}
|
||||
fontSize={'mini'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={item.icon as any} w={'16px'} color={'myGray.400'} mr={2} />
|
||||
{item.key}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td p={0} px={4} h={8} color={'myGray.900'} fontSize={'mini'}>
|
||||
<Flex alignItems={'center'}>
|
||||
{item.required ? (
|
||||
<MyIcon name={'check'} w={'16px'} color={'myGray.900'} mr={2} />
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td p={0} px={4} h={8} color={'myGray.600'} fontSize={'mini'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon
|
||||
mr={3}
|
||||
name={'common/settingLight'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
const formattedItem = {
|
||||
...item,
|
||||
list: item.enums || []
|
||||
};
|
||||
reset(formattedItem);
|
||||
}}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() =>
|
||||
onChange(variables.filter((variable) => variable.id !== item.id))
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)}
|
||||
|
||||
{/* Edit modal */}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { Box, Button, Flex, ModalBody, useDisclosure, Switch } from '@chakra-ui/react';
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import type { AppWhisperConfigType } from '@fastgpt/global/core/app/type.d';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
@@ -24,12 +24,9 @@ const WhisperConfig = ({
|
||||
const isOpenWhisper = value.open;
|
||||
const isAutoSend = value.autoSend;
|
||||
|
||||
const formLabel = useMemo(() => {
|
||||
if (!isOpenWhisper) {
|
||||
return t('common:core.app.whisper.Close');
|
||||
}
|
||||
return t('common:core.app.whisper.Open');
|
||||
}, [t, isOpenWhisper]);
|
||||
const formLabel = isOpenWhisper
|
||||
? t('common:core.app.whisper.Open')
|
||||
: t('common:core.app.whisper.Close');
|
||||
|
||||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
|
@@ -33,32 +33,29 @@ const ChatInput = ({
|
||||
onStop,
|
||||
TextareaDom,
|
||||
resetInputVal,
|
||||
chatForm,
|
||||
appId
|
||||
chatForm
|
||||
}: {
|
||||
onSendMessage: SendPromptFnType;
|
||||
onStop: () => void;
|
||||
TextareaDom: React.MutableRefObject<HTMLTextAreaElement | null>;
|
||||
resetInputVal: (val: ChatBoxInputType) => void;
|
||||
chatForm: UseFormReturn<ChatBoxInputFormType>;
|
||||
appId: string;
|
||||
}) => {
|
||||
const { isPc } = useSystem();
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { isPc } = useSystem();
|
||||
|
||||
const { setValue, watch, control } = chatForm;
|
||||
const inputValue = watch('input');
|
||||
|
||||
const {
|
||||
chatId,
|
||||
isChatting,
|
||||
whisperConfig,
|
||||
autoTTSResponse,
|
||||
chatInputGuide,
|
||||
outLinkAuthData,
|
||||
fileSelectConfig
|
||||
} = useContextSelector(ChatBoxContext, (v) => v);
|
||||
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
||||
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
||||
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
|
||||
const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting);
|
||||
const whisperConfig = useContextSelector(ChatBoxContext, (v) => v.whisperConfig);
|
||||
const autoTTSResponse = useContextSelector(ChatBoxContext, (v) => v.autoTTSResponse);
|
||||
const chatInputGuide = useContextSelector(ChatBoxContext, (v) => v.chatInputGuide);
|
||||
const fileSelectConfig = useContextSelector(ChatBoxContext, (v) => v.fileSelectConfig);
|
||||
|
||||
const fileCtrl = useFieldArray({
|
||||
control,
|
||||
@@ -78,8 +75,6 @@ const ChatInput = ({
|
||||
replaceFiles,
|
||||
hasFileUploading
|
||||
} = useFileUpload({
|
||||
outLinkAuthData,
|
||||
chatId: chatId || '',
|
||||
fileSelectConfig,
|
||||
fileCtrl
|
||||
});
|
||||
|
@@ -2,76 +2,73 @@ import React, { useState, useMemo, useCallback } from 'react';
|
||||
import { useAudioPlay } from '@/web/common/utils/voice';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import {
|
||||
AppChatConfigType,
|
||||
AppAutoExecuteConfigType,
|
||||
AppFileSelectConfigType,
|
||||
AppTTSConfigType,
|
||||
AppWhisperConfigType,
|
||||
ChatInputGuideConfigType,
|
||||
VariableItemType
|
||||
} from '@fastgpt/global/core/app/type';
|
||||
import { ChatHistoryItemResType, ChatSiteItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
||||
import {
|
||||
defaultAppSelectFileConfig,
|
||||
defaultAutoExecuteConfig,
|
||||
defaultChatInputGuideConfig,
|
||||
defaultTTSConfig,
|
||||
defaultWhisperConfig
|
||||
} from '@fastgpt/global/core/app/constants';
|
||||
import { createContext } from 'use-context-selector';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import { createContext, useContextSelector } from 'use-context-selector';
|
||||
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { getChatResData } from '@/web/core/chat/api';
|
||||
import { ChatBoxInputFormType } from './type';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext';
|
||||
|
||||
export type ChatProviderProps = OutLinkChatAuthProps & {
|
||||
appAvatar?: string;
|
||||
export type ChatProviderProps = {
|
||||
appId: string;
|
||||
chatConfig?: AppChatConfigType;
|
||||
chatId: string;
|
||||
outLinkAuthData?: OutLinkChatAuthProps;
|
||||
|
||||
chatHistories: ChatSiteItemType[];
|
||||
setChatHistories: React.Dispatch<React.SetStateAction<ChatSiteItemType[]>>;
|
||||
|
||||
variablesForm: UseFormReturn<ChatBoxInputFormType, any>;
|
||||
|
||||
// not chat test params
|
||||
chatId?: string;
|
||||
chatType: 'log' | 'chat' | 'share' | 'team';
|
||||
showRawSource: boolean;
|
||||
showNodeStatus: boolean;
|
||||
};
|
||||
|
||||
type useChatStoreType = OutLinkChatAuthProps &
|
||||
ChatProviderProps & {
|
||||
welcomeText: string;
|
||||
variableList: VariableItemType[];
|
||||
allVariableList: VariableItemType[];
|
||||
questionGuide: boolean;
|
||||
ttsConfig: AppTTSConfigType;
|
||||
whisperConfig: AppWhisperConfigType;
|
||||
autoTTSResponse: boolean;
|
||||
startSegmentedAudio: () => Promise<any>;
|
||||
splitText2Audio: (text: string, done?: boolean | undefined) => void;
|
||||
finishSegmentedAudio: () => void;
|
||||
audioLoading: boolean;
|
||||
audioPlaying: boolean;
|
||||
hasAudio: boolean;
|
||||
playAudioByText: ({
|
||||
text,
|
||||
buffer
|
||||
}: {
|
||||
text: string;
|
||||
buffer?: Uint8Array | undefined;
|
||||
}) => Promise<{
|
||||
buffer?: Uint8Array | undefined;
|
||||
}>;
|
||||
cancelAudio: () => void;
|
||||
audioPlayingChatId: string | undefined;
|
||||
setAudioPlayingChatId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
isChatting: boolean;
|
||||
chatInputGuide: ChatInputGuideConfigType;
|
||||
outLinkAuthData: OutLinkChatAuthProps;
|
||||
getHistoryResponseData: ({ dataId }: { dataId: string }) => Promise<ChatHistoryItemResType[]>;
|
||||
fileSelectConfig: AppFileSelectConfigType;
|
||||
};
|
||||
type useChatStoreType = ChatProviderProps & {
|
||||
welcomeText: string;
|
||||
variableList: VariableItemType[];
|
||||
allVariableList: VariableItemType[];
|
||||
questionGuide: boolean;
|
||||
ttsConfig: AppTTSConfigType;
|
||||
whisperConfig: AppWhisperConfigType;
|
||||
autoTTSResponse: boolean;
|
||||
autoExecute: AppAutoExecuteConfigType;
|
||||
startSegmentedAudio: () => Promise<any>;
|
||||
splitText2Audio: (text: string, done?: boolean | undefined) => void;
|
||||
finishSegmentedAudio: () => void;
|
||||
audioLoading: boolean;
|
||||
audioPlaying: boolean;
|
||||
hasAudio: boolean;
|
||||
playAudioByText: ({
|
||||
text,
|
||||
buffer
|
||||
}: {
|
||||
text: string;
|
||||
buffer?: Uint8Array | undefined;
|
||||
}) => Promise<{
|
||||
buffer?: Uint8Array | undefined;
|
||||
}>;
|
||||
cancelAudio: () => void;
|
||||
audioPlayingChatId: string | undefined;
|
||||
setAudioPlayingChatId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
isChatting: boolean;
|
||||
chatInputGuide: ChatInputGuideConfigType;
|
||||
getHistoryResponseData: ({ dataId }: { dataId: string }) => Promise<ChatHistoryItemResType[]>;
|
||||
fileSelectConfig: AppFileSelectConfigType;
|
||||
|
||||
appId: string;
|
||||
chatId: string;
|
||||
outLinkAuthData: OutLinkChatAuthProps;
|
||||
};
|
||||
|
||||
export const ChatBoxContext = createContext<useChatStoreType>({
|
||||
welcomeText: '',
|
||||
@@ -95,10 +92,6 @@ export const ChatBoxContext = createContext<useChatStoreType>({
|
||||
splitText2Audio: function (text: string, done?: boolean | undefined): void {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
chatHistories: [],
|
||||
setChatHistories: function (value: React.SetStateAction<ChatSiteItemType[]>): void {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
isChatting: false,
|
||||
audioLoading: false,
|
||||
audioPlaying: false,
|
||||
@@ -132,23 +125,24 @@ export const ChatBoxContext = createContext<useChatStoreType>({
|
||||
});
|
||||
|
||||
const Provider = ({
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken,
|
||||
|
||||
chatHistories,
|
||||
setChatHistories,
|
||||
variablesForm,
|
||||
appId,
|
||||
chatId,
|
||||
outLinkAuthData = {},
|
||||
chatType = 'chat',
|
||||
showRawSource,
|
||||
showNodeStatus,
|
||||
chatConfig = {},
|
||||
children,
|
||||
...props
|
||||
}: ChatProviderProps & {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const chatConfig = useContextSelector(
|
||||
ChatItemContext,
|
||||
(v) => v.chatBoxData?.app?.chatConfig || {}
|
||||
);
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
const setChatRecords = useContextSelector(ChatRecordContext, (v) => v.setChatRecords);
|
||||
|
||||
const {
|
||||
welcomeText = '',
|
||||
variables = [],
|
||||
@@ -156,19 +150,10 @@ const Provider = ({
|
||||
ttsConfig = defaultTTSConfig,
|
||||
whisperConfig = defaultWhisperConfig,
|
||||
chatInputGuide = defaultChatInputGuideConfig,
|
||||
fileSelectConfig = defaultAppSelectFileConfig
|
||||
fileSelectConfig = defaultAppSelectFileConfig,
|
||||
autoExecute = defaultAutoExecuteConfig
|
||||
} = useMemo(() => chatConfig, [chatConfig]);
|
||||
|
||||
const outLinkAuthData = useMemo(
|
||||
() => ({
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
}),
|
||||
[shareId, outLinkUid, teamId, teamToken]
|
||||
);
|
||||
|
||||
// segment audio
|
||||
const [audioPlayingChatId, setAudioPlayingChatId] = useState<string>();
|
||||
const {
|
||||
@@ -190,37 +175,34 @@ const Provider = ({
|
||||
|
||||
const isChatting = useMemo(
|
||||
() =>
|
||||
chatHistories[chatHistories.length - 1] &&
|
||||
chatHistories[chatHistories.length - 1]?.status !== 'finish',
|
||||
[chatHistories]
|
||||
chatRecords[chatRecords.length - 1] &&
|
||||
chatRecords[chatRecords.length - 1]?.status !== 'finish',
|
||||
[chatRecords]
|
||||
);
|
||||
const getHistoryResponseData = useCallback(
|
||||
async ({ dataId }: { dataId: string }) => {
|
||||
const aimItem = chatHistories.find((item) => item.dataId === dataId)!;
|
||||
if (!!aimItem?.responseData || !props.chatId) {
|
||||
const aimItem = chatRecords.find((item) => item.dataId === dataId)!;
|
||||
if (!!aimItem?.responseData || !chatId) {
|
||||
return aimItem.responseData || [];
|
||||
} else {
|
||||
let resData = await getChatResData({
|
||||
appId: props.appId,
|
||||
chatId: props.chatId,
|
||||
appId: appId,
|
||||
chatId: chatId,
|
||||
dataId,
|
||||
...outLinkAuthData
|
||||
});
|
||||
setChatHistories((state) =>
|
||||
setChatRecords((state) =>
|
||||
state.map((item) => (item.dataId === dataId ? { ...item, responseData: resData } : item))
|
||||
);
|
||||
return resData;
|
||||
}
|
||||
},
|
||||
[chatHistories, outLinkAuthData, props.appId, props.chatId, setChatHistories]
|
||||
[chatRecords, chatId, appId, outLinkAuthData, setChatRecords]
|
||||
);
|
||||
const value: useChatStoreType = {
|
||||
...props,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken,
|
||||
welcomeText,
|
||||
autoExecute,
|
||||
variableList: variables.filter((item) => item.type !== VariableInputEnum.custom),
|
||||
allVariableList: variables,
|
||||
questionGuide,
|
||||
@@ -238,12 +220,11 @@ const Provider = ({
|
||||
cancelAudio,
|
||||
audioPlayingChatId,
|
||||
setAudioPlayingChatId,
|
||||
chatHistories,
|
||||
setChatHistories,
|
||||
isChatting,
|
||||
chatInputGuide,
|
||||
appId,
|
||||
chatId,
|
||||
outLinkAuthData,
|
||||
variablesForm,
|
||||
getHistoryResponseData,
|
||||
chatType,
|
||||
showRawSource,
|
||||
|
@@ -10,6 +10,7 @@ import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { ChatBoxContext } from '../Provider';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
|
||||
import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext';
|
||||
|
||||
export type ChatControllerProps = {
|
||||
isLastChild: boolean;
|
||||
@@ -24,6 +25,19 @@ export type ChatControllerProps = {
|
||||
onAddUserDislike?: () => void;
|
||||
};
|
||||
|
||||
const controlIconStyle = {
|
||||
w: '14px',
|
||||
cursor: 'pointer',
|
||||
p: '5px',
|
||||
bg: 'white',
|
||||
borderRight: 'base'
|
||||
};
|
||||
const controlContainerStyle = {
|
||||
className: 'control',
|
||||
color: 'myGray.400',
|
||||
display: 'flex'
|
||||
};
|
||||
|
||||
const ChatController = ({
|
||||
chat,
|
||||
showVoiceIcon,
|
||||
@@ -35,34 +49,21 @@ const ChatController = ({
|
||||
onAddUserDislike,
|
||||
onAddUserLike
|
||||
}: ChatControllerProps & FlexProps) => {
|
||||
const theme = useTheme();
|
||||
const {
|
||||
isChatting,
|
||||
setChatHistories,
|
||||
audioLoading,
|
||||
audioPlaying,
|
||||
hasAudio,
|
||||
playAudioByText,
|
||||
cancelAudio,
|
||||
audioPlayingChatId,
|
||||
setAudioPlayingChatId
|
||||
} = useContextSelector(ChatBoxContext, (v) => v);
|
||||
const controlIconStyle = {
|
||||
w: '14px',
|
||||
cursor: 'pointer',
|
||||
p: '5px',
|
||||
bg: 'white',
|
||||
borderRight: theme.borders.base
|
||||
};
|
||||
const controlContainerStyle = {
|
||||
className: 'control',
|
||||
color: 'myGray.400',
|
||||
display: 'flex'
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { copyData } = useCopyData();
|
||||
|
||||
const setChatRecords = useContextSelector(ChatRecordContext, (v) => v.setChatRecords);
|
||||
|
||||
const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting);
|
||||
const audioLoading = useContextSelector(ChatBoxContext, (v) => v.audioLoading);
|
||||
const audioPlaying = useContextSelector(ChatBoxContext, (v) => v.audioPlaying);
|
||||
const hasAudio = useContextSelector(ChatBoxContext, (v) => v.hasAudio);
|
||||
const playAudioByText = useContextSelector(ChatBoxContext, (v) => v.playAudioByText);
|
||||
const cancelAudio = useContextSelector(ChatBoxContext, (v) => v.cancelAudio);
|
||||
const audioPlayingChatId = useContextSelector(ChatBoxContext, (v) => v.audioPlayingChatId);
|
||||
const setAudioPlayingChatId = useContextSelector(ChatBoxContext, (v) => v.setAudioPlayingChatId);
|
||||
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
|
||||
|
||||
const chatText = useMemo(() => formatChatValue2InputType(chat.value).text || '', [chat.value]);
|
||||
|
||||
return (
|
||||
@@ -70,7 +71,7 @@ const ChatController = ({
|
||||
{...controlContainerStyle}
|
||||
borderRadius={'sm'}
|
||||
overflow={'hidden'}
|
||||
border={theme.borders.base}
|
||||
border={'base'}
|
||||
// 最后一个子元素,没有border
|
||||
css={css({
|
||||
'& > *:last-child, & > *:last-child svg': {
|
||||
@@ -87,7 +88,7 @@ const ChatController = ({
|
||||
onClick={() => copyData(chatText)}
|
||||
/>
|
||||
</MyTooltip>
|
||||
{!!onDelete && !isChatting && (
|
||||
{!!onDelete && !isChatting && chatType !== 'log' && (
|
||||
<>
|
||||
{onRetry && (
|
||||
<MyTooltip label={t('common:core.chat.retry')}>
|
||||
@@ -125,12 +126,7 @@ const ChatController = ({
|
||||
onClick={cancelAudio}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<MyImage
|
||||
src="/icon/speaking.gif"
|
||||
w={'23px'}
|
||||
alt={''}
|
||||
borderRight={theme.borders.base}
|
||||
/>
|
||||
<MyImage src="/icon/speaking.gif" w={'23px'} alt={''} borderRight={'base'} />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -154,8 +150,8 @@ const ChatController = ({
|
||||
text: chatText
|
||||
});
|
||||
|
||||
if (!setChatHistories || !response.buffer) return;
|
||||
setChatHistories((state) =>
|
||||
if (!setChatRecords || !response.buffer) return;
|
||||
setChatRecords((state) =>
|
||||
state.map((item) =>
|
||||
item.dataId === chat.dataId
|
||||
? {
|
||||
|
@@ -216,7 +216,7 @@ const ChatItem = (props: Props) => {
|
||||
}}
|
||||
>
|
||||
{/* control icon */}
|
||||
<Flex w={'100%'} alignItems={'flex-end'} gap={2} justifyContent={styleMap.justifyContent}>
|
||||
<Flex w={'100%'} alignItems={'center'} gap={2} justifyContent={styleMap.justifyContent}>
|
||||
{isChatting && type === ChatRoleEnum.AI && isLastChild ? null : (
|
||||
<Flex order={styleMap.order} ml={styleMap.ml} align={'center'} gap={'0.62rem'}>
|
||||
{chat.time && (isPc || isChatLog) && (
|
||||
|
@@ -19,10 +19,12 @@ const ContextModal = ({ onClose, dataId }: { onClose: () => void; dataId: string
|
||||
const flatResData: ChatHistoryItemResType[] =
|
||||
res
|
||||
?.map((item) => {
|
||||
if (item.pluginDetail || item.toolDetail) {
|
||||
return [item, ...(item.pluginDetail || []), ...(item.toolDetail || [])];
|
||||
}
|
||||
return item;
|
||||
return [
|
||||
item,
|
||||
...(item.pluginDetail || []),
|
||||
...(item.toolDetail || []),
|
||||
...(item.loopDetail || [])
|
||||
];
|
||||
})
|
||||
.flat() || [];
|
||||
return flatResData.find(isLLMNode)?.historyPreview || [];
|
||||
|
@@ -7,18 +7,22 @@ import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/ty
|
||||
import QuoteItem from '@/components/core/dataset/QuoteItem';
|
||||
import RawSourceBox from '@/components/core/dataset/RawSourceBox';
|
||||
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatBoxContext } from '../Provider';
|
||||
|
||||
const QuoteModal = ({
|
||||
rawSearch = [],
|
||||
onClose,
|
||||
canEditDataset,
|
||||
showRawSource,
|
||||
chatItemId,
|
||||
metadata
|
||||
}: {
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
onClose: () => void;
|
||||
canEditDataset: boolean;
|
||||
showRawSource: boolean;
|
||||
chatItemId: string;
|
||||
metadata?: {
|
||||
collectionId: string;
|
||||
sourceId?: string;
|
||||
@@ -37,6 +41,13 @@ const QuoteModal = ({
|
||||
[metadata, rawSearch]
|
||||
);
|
||||
|
||||
const RawSourceBoxProps = useContextSelector(ChatBoxContext, (v) => ({
|
||||
appId: v.appId,
|
||||
chatId: v.chatId,
|
||||
chatItemId,
|
||||
...(v.outLinkAuthData || {})
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<MyModal
|
||||
@@ -49,7 +60,7 @@ const QuoteModal = ({
|
||||
title={
|
||||
<Box>
|
||||
{metadata ? (
|
||||
<RawSourceBox {...metadata} canView={showRawSource} />
|
||||
<RawSourceBox {...metadata} {...RawSourceBoxProps} canView={showRawSource} />
|
||||
) : (
|
||||
<>{t('common:core.chat.Quote Amount', { amount: rawSearch.length })}</>
|
||||
)}
|
||||
@@ -64,6 +75,7 @@ const QuoteModal = ({
|
||||
rawSearch={filterResults}
|
||||
canEditDataset={canEditDataset}
|
||||
canViewSource={showRawSource}
|
||||
chatItemId={chatItemId}
|
||||
/>
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
@@ -74,16 +86,25 @@ const QuoteModal = ({
|
||||
export default QuoteModal;
|
||||
|
||||
export const QuoteList = React.memo(function QuoteList({
|
||||
chatItemId,
|
||||
rawSearch = [],
|
||||
canEditDataset,
|
||||
canViewSource
|
||||
}: {
|
||||
chatItemId?: string;
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
canEditDataset: boolean;
|
||||
canViewSource: boolean;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
|
||||
const RawSourceBoxProps = useContextSelector(ChatBoxContext, (v) => ({
|
||||
chatItemId,
|
||||
appId: v.appId,
|
||||
chatId: v.chatId,
|
||||
...(v.outLinkAuthData || {})
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
{rawSearch.map((item, i) => (
|
||||
@@ -101,6 +122,7 @@ export const QuoteList = React.memo(function QuoteList({
|
||||
quoteItem={item}
|
||||
canViewSource={canViewSource}
|
||||
canEditDataset={canEditDataset}
|
||||
{...RawSourceBoxProps}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
|
@@ -250,6 +250,7 @@ const ResponseTags = ({
|
||||
{!!quoteModalData && (
|
||||
<QuoteModal
|
||||
{...quoteModalData}
|
||||
chatItemId={historyItem.dataId}
|
||||
canEditDataset={notSharePage}
|
||||
showRawSource={showRawSource}
|
||||
onClose={() => setQuoteModalData(undefined)}
|
||||
|
@@ -0,0 +1,19 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { formatTimeToChatItemTime } from '@fastgpt/global/common/string/time';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const TimeBox = ({ time }: { time: Date }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Box w={'100%'} fontSize={'mini'} textAlign={'center'} color={'myGray.500'} fontWeight={'400'}>
|
||||
{t(formatTimeToChatItemTime(time) as any, {
|
||||
time: dayjs(time).format('HH#mm')
|
||||
}).replace('#', ':')}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default TimeBox;
|