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