Commit fdeb1590 by Archer Committed by GitHub

User select node (#2397)

* feat: add user select node (#2300)

* feat: add user select node

* fix

* type

* fix

* fix

* fix

* perf: user select code

* perf: user select histories

* perf: i18n

---------

Co-authored-by: heheer <heheer@sealos.io>
parent f8b8fcc1
...@@ -9,6 +9,7 @@ import type { ...@@ -9,6 +9,7 @@ import type {
ChatCompletionUserMessageParam as SdkChatCompletionUserMessageParam ChatCompletionUserMessageParam as SdkChatCompletionUserMessageParam
} from 'openai/resources'; } from 'openai/resources';
import { ChatMessageTypeEnum } from './constants'; import { ChatMessageTypeEnum } from './constants';
import { InteractiveNodeResponseItemType } from '../workflow/template/system/userSelect/type';
export * from 'openai/resources'; export * from 'openai/resources';
...@@ -33,6 +34,7 @@ export type ChatCompletionMessageParam = ( ...@@ -33,6 +34,7 @@ export type ChatCompletionMessageParam = (
| CustomChatCompletionUserMessageParam | CustomChatCompletionUserMessageParam
) & { ) & {
dataId?: string; dataId?: string;
interactive?: InteractiveNodeResponseItemType;
}; };
export type SdkChatCompletionMessageParam = SdkChatCompletionMessageParam; export type SdkChatCompletionMessageParam = SdkChatCompletionMessageParam;
......
...@@ -124,6 +124,13 @@ export const chats2GPTMessages = ({ ...@@ -124,6 +124,13 @@ export const chats2GPTMessages = ({
role: ChatCompletionRequestMessageRoleEnum.Assistant, role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: value.text.content content: value.text.content
}); });
} else if (value.type === ChatItemValueTypeEnum.interactive) {
results = results.concat({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
interactive: value.interactive,
content: ''
});
} }
}); });
} }
...@@ -254,6 +261,12 @@ export const GPTMessages2Chats = ( ...@@ -254,6 +261,12 @@ export const GPTMessages2Chats = (
] ]
}); });
} }
} else if (item.interactive) {
value.push({
//@ts-ignore
type: ChatItemValueTypeEnum.interactive,
interactive: item.interactive
});
} }
} }
......
...@@ -24,7 +24,8 @@ export enum ChatFileTypeEnum { ...@@ -24,7 +24,8 @@ export enum ChatFileTypeEnum {
export enum ChatItemValueTypeEnum { export enum ChatItemValueTypeEnum {
text = 'text', text = 'text',
file = 'file', file = 'file',
tool = 'tool' tool = 'tool',
interactive = 'interactive'
} }
export enum ChatSourceEnum { export enum ChatSourceEnum {
......
...@@ -15,6 +15,7 @@ import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d'; ...@@ -15,6 +15,7 @@ import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
import { DatasetSearchModeEnum } from '../dataset/constants'; import { DatasetSearchModeEnum } from '../dataset/constants';
import { DispatchNodeResponseType } from '../workflow/runtime/type.d'; import { DispatchNodeResponseType } from '../workflow/runtime/type.d';
import { ChatBoxInputType } from '../../../../projects/app/src/components/core/chat/ChatContainer/ChatBox/type'; import { ChatBoxInputType } from '../../../../projects/app/src/components/core/chat/ChatContainer/ChatBox/type';
import { InteractiveNodeResponseItemType } from '../workflow/template/system/userSelect/type';
export type ChatSchema = { export type ChatSchema = {
_id: string; _id: string;
...@@ -67,11 +68,12 @@ export type SystemChatItemType = { ...@@ -67,11 +68,12 @@ export type SystemChatItemType = {
value: SystemChatItemValueItemType[]; value: SystemChatItemValueItemType[];
}; };
export type AIChatItemValueItemType = { export type AIChatItemValueItemType = {
type: ChatItemValueTypeEnum.text | ChatItemValueTypeEnum.tool; type: ChatItemValueTypeEnum.text | ChatItemValueTypeEnum.tool | ChatItemValueTypeEnum.interactive;
text?: { text?: {
content: string; content: string;
}; };
tools?: ToolModuleResponseItemType[]; tools?: ToolModuleResponseItemType[];
interactive?: InteractiveNodeResponseItemType;
}; };
export type AIChatItemType = { export type AIChatItemType = {
obj: ChatRoleEnum.AI; obj: ChatRoleEnum.AI;
...@@ -153,6 +155,13 @@ export type ChatHistoryItemResType = DispatchNodeResponseType & { ...@@ -153,6 +155,13 @@ export type ChatHistoryItemResType = DispatchNodeResponseType & {
moduleName: string; moduleName: string;
}; };
/* ---------- node outputs ------------ */
export type NodeOutputItemType = {
nodeId: string;
key: NodeOutputKeyEnum;
value: any;
};
/* One tool run response */ /* One tool run response */
export type ToolRunResponseItemType = any; export type ToolRunResponseItemType = any;
/* tool module response */ /* tool module response */
......
...@@ -3,6 +3,7 @@ export enum FlowNodeTemplateTypeEnum { ...@@ -3,6 +3,7 @@ export enum FlowNodeTemplateTypeEnum {
ai = 'ai', ai = 'ai',
function = 'function', function = 'function',
tools = 'tools', tools = 'tools',
interactive = 'interactive',
search = 'search', search = 'search',
multimodal = 'multimodal', multimodal = 'multimodal',
...@@ -123,7 +124,9 @@ export enum NodeInputKeyEnum { ...@@ -123,7 +124,9 @@ export enum NodeInputKeyEnum {
codeType = 'codeType', // js|py codeType = 'codeType', // js|py
// read files // read files
fileUrlList = 'fileUrlList' fileUrlList = 'fileUrlList',
// user select
userSelectOptions = 'userSelectOptions'
} }
export enum NodeOutputKeyEnum { export enum NodeOutputKeyEnum {
...@@ -162,7 +165,11 @@ export enum NodeOutputKeyEnum { ...@@ -162,7 +165,11 @@ export enum NodeOutputKeyEnum {
// plugin // plugin
pluginStart = 'pluginStart', pluginStart = 'pluginStart',
ifElseResult = 'ifElseResult' // if else
ifElseResult = 'ifElseResult',
//user select
selectResult = 'selectResult'
} }
export enum VariableInputEnum { export enum VariableInputEnum {
......
...@@ -118,7 +118,8 @@ export enum FlowNodeTypeEnum { ...@@ -118,7 +118,8 @@ export enum FlowNodeTypeEnum {
code = 'code', code = 'code',
textEditor = 'textEditor', textEditor = 'textEditor',
customFeedback = 'customFeedback', customFeedback = 'customFeedback',
readFiles = 'readFiles' readFiles = 'readFiles',
userSelect = 'userSelect'
} }
// node IO value type // node IO value type
......
...@@ -10,7 +10,9 @@ export enum SseResponseEventEnum { ...@@ -10,7 +10,9 @@ export enum SseResponseEventEnum {
toolParams = 'toolParams', // tool params return toolParams = 'toolParams', // tool params return
toolResponse = 'toolResponse', // tool response return toolResponse = 'toolResponse', // tool response return
flowResponses = 'flowResponses', // sse response request flowResponses = 'flowResponses', // sse response request
updateVariables = 'updateVariables' updateVariables = 'updateVariables',
interactive = 'interactive' // user select
} }
export enum DispatchNodeResponseKeyEnum { export enum DispatchNodeResponseKeyEnum {
...@@ -19,7 +21,9 @@ export enum DispatchNodeResponseKeyEnum { ...@@ -19,7 +21,9 @@ export enum DispatchNodeResponseKeyEnum {
nodeDispatchUsages = 'nodeDispatchUsages', // the node bill. nodeDispatchUsages = 'nodeDispatchUsages', // the node bill.
childrenResponses = 'childrenResponses', // Some nodes make recursive calls that need to be returned childrenResponses = 'childrenResponses', // Some nodes make recursive calls that need to be returned
toolResponses = 'toolResponses', // The result is passed back to the tool node for use toolResponses = 'toolResponses', // The result is passed back to the tool node for use
assistantResponses = 'assistantResponses' // assistant response assistantResponses = 'assistantResponses', // assistant response
interactive = 'INTERACTIVE' // is interactive
} }
export const needReplaceReferenceInputTypeList = [ export const needReplaceReferenceInputTypeList = [
......
...@@ -3,7 +3,8 @@ import { ...@@ -3,7 +3,8 @@ import {
ChatItemType, ChatItemType,
UserChatItemValueItemType, UserChatItemValueItemType,
ChatItemValueItemType, ChatItemValueItemType,
ToolRunResponseItemType ToolRunResponseItemType,
NodeOutputItemType
} from '../../chat/type'; } from '../../chat/type';
import { FlowNodeInputItemType, FlowNodeOutputItemType } from '../type/io.d'; import { FlowNodeInputItemType, FlowNodeOutputItemType } from '../type/io.d';
import { StoreNodeItemType } from '../type/node'; import { StoreNodeItemType } from '../type/node';
...@@ -17,6 +18,7 @@ import { AppDetailType, AppSchema } from '../../app/type'; ...@@ -17,6 +18,7 @@ import { AppDetailType, AppSchema } from '../../app/type';
import { RuntimeNodeItemType } from '../runtime/type'; import { RuntimeNodeItemType } from '../runtime/type';
import { RuntimeEdgeItemType } from './edge'; import { RuntimeEdgeItemType } from './edge';
import { ReadFileNodeResponse } from '../template/system/readFiles/type'; import { ReadFileNodeResponse } from '../template/system/readFiles/type';
import { UserSelectOptionType } from '../template/system/userSelect/type';
/* workflow props */ /* workflow props */
export type ChatDispatchProps = { export type ChatDispatchProps = {
...@@ -153,6 +155,9 @@ export type DispatchNodeResponseType = { ...@@ -153,6 +155,9 @@ export type DispatchNodeResponseType = {
// read files // read files
readFilesResult?: string; readFilesResult?: string;
readFiles?: ReadFileNodeResponse; readFiles?: ReadFileNodeResponse;
// user select
userSelectResult?: string;
}; };
export type DispatchNodeResultType<T> = { export type DispatchNodeResultType<T> = {
......
...@@ -6,7 +6,9 @@ import { StoreEdgeItemType } from '../type/edge'; ...@@ -6,7 +6,9 @@ import { StoreEdgeItemType } from '../type/edge';
import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type'; import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type';
import { VARIABLE_NODE_ID } from '../constants'; import { VARIABLE_NODE_ID } from '../constants';
import { isReferenceValue } from '../utils'; import { isReferenceValue } from '../utils';
import { ReferenceValueProps } from '../type/io'; import { FlowNodeOutputItemType, ReferenceValueProps } from '../type/io';
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => { export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
let limit = 10; let limit = 10;
...@@ -25,7 +27,35 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number ...@@ -25,7 +27,35 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number
return limit * 2; return limit * 2;
}; };
export const initWorkflowEdgeStatus = (edges: StoreEdgeItemType[]): RuntimeEdgeItemType[] => { export const getLastInteractiveValue = (histories: ChatItemType[]) => {
const lastAIMessage = histories.findLast((item) => item.obj === ChatRoleEnum.AI);
if (lastAIMessage) {
const interactiveValue = lastAIMessage.value.find(
(v) => v.type === ChatItemValueTypeEnum.interactive
);
if (interactiveValue && 'interactive' in interactiveValue) {
return interactiveValue.interactive;
}
}
return null;
};
export const initWorkflowEdgeStatus = (
edges: StoreEdgeItemType[],
histories?: ChatItemType[]
): RuntimeEdgeItemType[] => {
// If there is a history, use the last interactive value
if (!!histories) {
const memoryEdges = getLastInteractiveValue(histories)?.memoryEdges;
if (memoryEdges && memoryEdges.length > 0) {
return memoryEdges;
}
}
return ( return (
edges?.map((edge) => ({ edges?.map((edge) => ({
...edge, ...edge,
...@@ -34,7 +64,19 @@ export const initWorkflowEdgeStatus = (edges: StoreEdgeItemType[]): RuntimeEdgeI ...@@ -34,7 +64,19 @@ export const initWorkflowEdgeStatus = (edges: StoreEdgeItemType[]): RuntimeEdgeI
); );
}; };
export const getDefaultEntryNodeIds = (nodes: (StoreNodeItemType | RuntimeNodeItemType)[]) => { export const getWorkflowEntryNodeIds = (
nodes: (StoreNodeItemType | RuntimeNodeItemType)[],
histories?: ChatItemType[]
) => {
// If there is a history, use the last interactive entry node
if (!!histories) {
const entryNodeIds = getLastInteractiveValue(histories)?.entryNodeIds;
if (Array.isArray(entryNodeIds) && entryNodeIds.length > 0) {
return entryNodeIds;
}
}
const entryList = [ const entryList = [
FlowNodeTypeEnum.systemConfig, FlowNodeTypeEnum.systemConfig,
FlowNodeTypeEnum.workflowStart, FlowNodeTypeEnum.workflowStart,
...@@ -212,3 +254,29 @@ export const textAdaptGptResponse = ({ ...@@ -212,3 +254,29 @@ export const textAdaptGptResponse = ({
] ]
}); });
}; };
/* Update runtimeNode's outputs with interactive data from history */
export function rewriteNodeOutputByHistories(
histories: ChatItemType[],
runtimeNodes: RuntimeNodeItemType[]
) {
const interactive = getLastInteractiveValue(histories);
if (!interactive?.nodeOutputs) {
return runtimeNodes;
}
return runtimeNodes.map((node) => {
return {
...node,
outputs: node.outputs.map((output: FlowNodeOutputItemType) => {
return {
...output,
value:
interactive?.nodeOutputs?.find(
(item: NodeOutputItemType) => item.nodeId === node.nodeId && item.key === output.key
)?.value || output?.value
};
})
};
});
}
...@@ -26,6 +26,7 @@ import { CodeNode } from './system/sandbox'; ...@@ -26,6 +26,7 @@ import { CodeNode } from './system/sandbox';
import { TextEditorNode } from './system/textEditor'; import { TextEditorNode } from './system/textEditor';
import { CustomFeedbackNode } from './system/customFeedback'; import { CustomFeedbackNode } from './system/customFeedback';
import { ReadFilesNodes } from './system/readFiles'; import { ReadFilesNodes } from './system/readFiles';
import { UserSelectNode } from './system/userSelect/index';
const systemNodes: FlowNodeTemplateType[] = [ const systemNodes: FlowNodeTemplateType[] = [
AiChatModule, AiChatModule,
...@@ -51,7 +52,8 @@ export const appSystemModuleTemplates: FlowNodeTemplateType[] = [ ...@@ -51,7 +52,8 @@ export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
SystemConfigNode, SystemConfigNode,
WorkflowStart, WorkflowStart,
...systemNodes, ...systemNodes,
CustomFeedbackNode CustomFeedbackNode,
UserSelectNode
]; ];
/* plugin flow module templates */ /* plugin flow module templates */
export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [ export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [
......
import { i18nT } from '../../../../../../web/i18n/utils';
import {
FlowNodeTemplateTypeEnum,
NodeInputKeyEnum,
NodeOutputKeyEnum,
WorkflowIOValueTypeEnum
} from '../../../constants';
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../../node/constant';
import { FlowNodeTemplateType } from '../../../type/node.d';
import { getHandleConfig } from '../../utils';
export const UserSelectNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.userSelect,
templateType: FlowNodeTemplateTypeEnum.interactive,
flowNodeType: FlowNodeTypeEnum.userSelect,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(true, false, true, true),
avatar: 'core/workflow/template/userSelect',
diagram: '/imgs/app/userSelect.svg',
name: i18nT('app:workflow.user_select'),
intro: i18nT(`app:workflow.user_select_tip`),
showStatus: true,
version: '489',
inputs: [
{
key: NodeInputKeyEnum.description,
renderTypeList: [FlowNodeInputTypeEnum.textarea],
valueType: WorkflowIOValueTypeEnum.string,
label: i18nT('app:workflow.select_description')
},
{
key: NodeInputKeyEnum.userSelectOptions,
renderTypeList: [FlowNodeInputTypeEnum.custom],
valueType: WorkflowIOValueTypeEnum.any,
label: '',
value: [
{
value: 'Confirm',
key: 'option1'
},
{
value: 'Cancel',
key: 'option2'
}
]
}
],
outputs: [
{
id: NodeOutputKeyEnum.selectResult,
key: NodeOutputKeyEnum.selectResult,
required: true,
label: i18nT('app:workflow.select_result'),
valueType: WorkflowIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.static
}
]
};
import { NodeOutputItemType } from '../../../../chat/type';
import { FlowNodeOutputItemType } from '../../../type/io';
import { RuntimeEdgeItemType } from '../../../runtime/type';
export type UserSelectOptionItemType = {
key: string;
value: string;
};
type InteractiveBasicType = {
entryNodeIds: string[];
memoryEdges: RuntimeEdgeItemType[];
nodeOutputs: NodeOutputItemType[];
};
type UserSelectInteractive = {
type: 'userSelect';
params: {
// description: string;
userSelectOptions: UserSelectOptionItemType[];
userSelectedVal?: string;
};
};
export type InteractiveNodeResponseItemType = InteractiveBasicType & UserSelectInteractive;
export type UserInteractiveType = UserSelectInteractive;
...@@ -66,6 +66,8 @@ export type FlowNodeTemplateType = FlowNodeCommonType & { ...@@ -66,6 +66,8 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
// action // action
forbidDelete?: boolean; // forbid delete forbidDelete?: boolean; // forbid delete
unique?: boolean; unique?: boolean;
diagram?: string; // diagram url
}; };
export type NodeTemplateListItemType = { export type NodeTemplateListItemType = {
......
import type { ChatItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type'; import type { ChatItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type';
import { MongoChatItem } from './chatItemSchema'; import { MongoChatItem } from './chatItemSchema';
import { addLog } from '../../common/system/log'; import { addLog } from '../../common/system/log';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { delFileByFileIdList, getGFSCollection } from '../../common/file/gridfs/controller'; import { delFileByFileIdList, getGFSCollection } from '../../common/file/gridfs/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants'; import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { MongoChat } from './chatSchema'; import { MongoChat } from './chatSchema';
...@@ -79,6 +79,52 @@ export const addCustomFeedbacks = async ({ ...@@ -79,6 +79,52 @@ export const addCustomFeedbacks = async ({
} }
}; };
/*
Update the user selected index of the interactive module
*/
export const updateUserSelectedResult = async ({
appId,
chatId,
userSelectedVal
}: {
appId: string;
chatId?: string;
userSelectedVal: string;
}) => {
if (!chatId) return;
try {
const chatItem = await MongoChatItem.findOne(
{ appId, chatId, obj: ChatRoleEnum.AI },
'value'
).sort({ _id: -1 });
if (!chatItem) return;
const interactiveValue = chatItem.value.find(
(v) => v.type === ChatItemValueTypeEnum.interactive
);
if (
!interactiveValue ||
interactiveValue.type !== ChatItemValueTypeEnum.interactive ||
!interactiveValue.interactive?.params
)
return;
interactiveValue.interactive = {
...interactiveValue.interactive,
params: {
...interactiveValue.interactive.params,
userSelectedVal
}
};
await chatItem.save();
} catch (error) {
addLog.error('updateUserSelectedResult error', error);
}
};
/* /*
Delete chat files Delete chat files
1. ChatId: Delete one chat files 1. ChatId: Delete one chat files
......
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import {
DispatchNodeResponseKeyEnum,
SseResponseEventEnum
} from '@fastgpt/global/core/workflow/runtime/constants';
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import type { import type {
ChatDispatchProps, ChatDispatchProps,
...@@ -10,6 +13,7 @@ import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/ ...@@ -10,6 +13,7 @@ import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/
import type { import type {
AIChatItemValueItemType, AIChatItemValueItemType,
ChatHistoryItemResType, ChatHistoryItemResType,
NodeOutputItemType,
ToolRunResponseItemType ToolRunResponseItemType
} from '@fastgpt/global/core/chat/type.d'; } from '@fastgpt/global/core/chat/type.d';
import { import {
...@@ -17,7 +21,7 @@ import { ...@@ -17,7 +21,7 @@ import {
FlowNodeTypeEnum FlowNodeTypeEnum
} from '@fastgpt/global/core/workflow/node/constant'; } from '@fastgpt/global/core/workflow/node/constant';
import { replaceVariable } from '@fastgpt/global/common/string/tools'; import { replaceVariable } from '@fastgpt/global/common/string/tools';
import { responseWriteNodeStatus } from '../../../common/response'; import { responseWrite, responseWriteNodeStatus } from '../../../common/response';
import { getSystemTime } from '@fastgpt/global/common/time/timezone'; import { getSystemTime } from '@fastgpt/global/common/time/timezone';
import { replaceVariableLabel } from '@fastgpt/global/core/workflow/utils'; import { replaceVariableLabel } from '@fastgpt/global/core/workflow/utils';
...@@ -37,7 +41,8 @@ import { dispatchPluginOutput } from './plugin/runOutput'; ...@@ -37,7 +41,8 @@ import { dispatchPluginOutput } from './plugin/runOutput';
import { removeSystemVariable, valueTypeFormat } from './utils'; import { removeSystemVariable, valueTypeFormat } from './utils';
import { import {
filterWorkflowEdges, filterWorkflowEdges,
checkNodeRunStatus checkNodeRunStatus,
getLastInteractiveValue
} from '@fastgpt/global/core/workflow/runtime/utils'; } from '@fastgpt/global/core/workflow/runtime/utils';
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type'; import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
import { dispatchRunTools } from './agent/runTool/index'; import { dispatchRunTools } from './agent/runTool/index';
...@@ -56,6 +61,13 @@ import { dispatchRunCode } from './code/run'; ...@@ -56,6 +61,13 @@ import { dispatchRunCode } from './code/run';
import { dispatchTextEditor } from './tools/textEditor'; import { dispatchTextEditor } from './tools/textEditor';
import { dispatchCustomFeedback } from './tools/customFeedback'; import { dispatchCustomFeedback } from './tools/customFeedback';
import { dispatchReadFiles } from './tools/readFiles'; import { dispatchReadFiles } from './tools/readFiles';
import { dispatchUserSelect } from './interactive/userSelect';
import { FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/io';
import {
InteractiveNodeResponseItemType,
UserInteractiveType,
UserSelectInteractive
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
const callbackMap: Record<FlowNodeTypeEnum, Function> = { const callbackMap: Record<FlowNodeTypeEnum, Function> = {
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart, [FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
...@@ -80,6 +92,7 @@ const callbackMap: Record<FlowNodeTypeEnum, Function> = { ...@@ -80,6 +92,7 @@ const callbackMap: Record<FlowNodeTypeEnum, Function> = {
[FlowNodeTypeEnum.textEditor]: dispatchTextEditor, [FlowNodeTypeEnum.textEditor]: dispatchTextEditor,
[FlowNodeTypeEnum.customFeedback]: dispatchCustomFeedback, [FlowNodeTypeEnum.customFeedback]: dispatchCustomFeedback,
[FlowNodeTypeEnum.readFiles]: dispatchReadFiles, [FlowNodeTypeEnum.readFiles]: dispatchReadFiles,
[FlowNodeTypeEnum.userSelect]: dispatchUserSelect,
// none // none
[FlowNodeTypeEnum.systemConfig]: dispatchSystemConfig, [FlowNodeTypeEnum.systemConfig]: dispatchSystemConfig,
...@@ -171,7 +184,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons ...@@ -171,7 +184,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
} }
} }
} }
/* Pass the output of the module to the next stage */ /* Pass the output of the node, to get next nodes and update edge status */
function nodeOutput( function nodeOutput(
node: RuntimeNodeItemType, node: RuntimeNodeItemType,
result: Record<string, any> = {} result: Record<string, any> = {}
...@@ -211,54 +224,117 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons ...@@ -211,54 +224,117 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
return nextStepNodes; return nextStepNodes;
} }
function checkNodeCanRun(nodes: RuntimeNodeItemType[] = []): Promise<any> {
return Promise.all(
nodes.map(async (node) => {
const status = checkNodeRunStatus({
node,
runtimeEdges
});
if (res?.closed || props.maxRunTimes <= 0) return; /* Have interactive result, computed edges and node outputs */
props.maxRunTimes--; function handleInteractiveResult({
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, uid: user._id }); entryNodeIds,
interactiveResponse
}: {
entryNodeIds: string[];
interactiveResponse: UserSelectInteractive;
}): AIChatItemValueItemType {
// Get node outputs
const nodeOutputs: NodeOutputItemType[] = [];
runtimeNodes.forEach((node) => {
node.outputs.forEach((output) => {
if (output.value) {
nodeOutputs.push({
nodeId: node.nodeId,
key: output.key as NodeOutputKeyEnum,
value: output.value
});
}
});
});
await surrenderProcess(); const interactiveResult: InteractiveNodeResponseItemType = {
...interactiveResponse,
entryNodeIds,
memoryEdges: runtimeEdges.map((edge) => ({
...edge,
status: entryNodeIds.includes(edge.target)
? 'active'
: entryNodeIds.includes(edge.source)
? 'waiting'
: edge.status
})),
nodeOutputs
};
if (status === 'run') { if (stream && res) {
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`); responseWrite({
return nodeRunWithActive(node); res,
} event: SseResponseEventEnum.interactive,
if (status === 'skip') { data: JSON.stringify({ interactive: interactiveResult })
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`); });
return nodeRunWithSkip(node); }
return {
type: ChatItemValueTypeEnum.interactive,
interactive: interactiveResult
};
}
async function checkNodeCanRun(node: RuntimeNodeItemType): Promise<any> {
const status = checkNodeRunStatus({
node,
runtimeEdges
});
if (res?.closed || props.maxRunTimes <= 0) return;
props.maxRunTimes--;
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, uid: user._id });
await surrenderProcess();
const response:
| {
node: RuntimeNodeItemType;
result: Record<string, any>;
} }
| undefined = await (() => {
if (status === 'run') {
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
return nodeRunWithActive(node);
}
if (status === 'skip') {
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
return nodeRunWithSkip(node);
}
})();
return; if (!response) return;
})
).then((result) => { // Update the node output at the end of the run and get the next nodes
const flat = result.flat().filter(Boolean) as unknown as { const nextNodes = nodeOutput(response.node, response.result);
node: RuntimeNodeItemType; // Remove repeat nodes(Make sure that the node is only executed once)
result: Record<string, any>; const filterNextNodes = nextNodes.filter(
}[]; (node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
if (flat.length === 0) return; );
// Update the node output at the end of the run and get the next nodes // In the current version, only one interactive node is allowed at the same time
const nextNodes = flat.map((item) => nodeOutput(item.node, item.result)).flat(); const interactiveResponse: UserInteractiveType | undefined =
response.result?.[DispatchNodeResponseKeyEnum.interactive];
// Remove repeat nodes(Make sure that the node is only executed once) if (interactiveResponse) {
const filterNextNodes = nextNodes.filter( chatAssistantResponse.push(
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index handleInteractiveResult({
entryNodeIds: [response.node.nodeId],
interactiveResponse
})
); );
return;
}
return checkNodeCanRun(filterNextNodes); return Promise.all(filterNextNodes.map(checkNodeCanRun));
});
} }
// 运行完一轮后,清除连线的状态,避免污染进程 // 运行完一轮后,清除连线的状态,避免污染进程
function nodeRunFinish(node: RuntimeNodeItemType) { function nodeRunFinish(node: RuntimeNodeItemType) {
const edges = runtimeEdges.filter((item) => item.target === node.nodeId); node.isEntry = false;
edges.forEach((item) => {
item.status = 'waiting'; runtimeEdges.forEach((item) => {
if (item.target === node.nodeId) {
item.status = 'waiting';
}
}); });
} }
/* Inject data into module input */ /* Inject data into module input */
...@@ -393,12 +469,12 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons ...@@ -393,12 +469,12 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
// start process width initInput // start process width initInput
const entryNodes = runtimeNodes.filter((item) => item.isEntry); const entryNodes = runtimeNodes.filter((item) => item.isEntry);
console.log(runtimeEdges);
// reset entry // reset entry
runtimeNodes.forEach((item) => { // runtimeNodes.forEach((item) => {
item.isEntry = false; // item.isEntry = false;
}); // });
await checkNodeCanRun(entryNodes); await Promise.all(entryNodes.map(checkNodeCanRun));
// focus try to run pluginOutput // focus try to run pluginOutput
const pluginOutputModule = runtimeNodes.find( const pluginOutputModule = runtimeNodes.find(
......
import {
DispatchNodeResponseKeyEnum,
SseResponseEventEnum
} from '@fastgpt/global/core/workflow/runtime/constants';
import {
DispatchNodeResultType,
ModuleDispatchProps
} from '@fastgpt/global/core/workflow/runtime/type';
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
import type {
UserSelectInteractive,
UserSelectOptionItemType
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
import { updateUserSelectedResult } from '../../../chat/controller';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
import { responseWrite } from '../../../../common/response';
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.description]: string;
[NodeInputKeyEnum.userSelectOptions]: UserSelectOptionItemType[];
}>;
type UserSelectResponse = DispatchNodeResultType<{
[NodeOutputKeyEnum.answerText]?: string;
[DispatchNodeResponseKeyEnum.interactive]?: UserSelectInteractive;
[NodeOutputKeyEnum.selectResult]?: string;
}>;
export const dispatchUserSelect = async (props: Props): Promise<UserSelectResponse> => {
const {
res,
detail,
histories,
stream,
app: { _id: appId },
chatId,
node: { nodeId, isEntry },
params: { description, userSelectOptions },
query
} = props;
// Interactive node is not the entry node, return interactive result
if (!isEntry) {
const answerText = description ? `\n${description}` : undefined;
if (res && stream && answerText) {
responseWrite({
res,
event: detail ? SseResponseEventEnum.fastAnswer : undefined,
data: textAdaptGptResponse({
text: answerText
})
});
}
return {
[NodeOutputKeyEnum.answerText]: answerText,
[DispatchNodeResponseKeyEnum.interactive]: {
type: 'userSelect',
params: {
userSelectOptions
}
}
};
}
const { text: userSelectedVal } = chatValue2RuntimePrompt(query);
// Error status
if (userSelectedVal === undefined) {
return {
[DispatchNodeResponseKeyEnum.skipHandleId]: userSelectOptions.map((item) =>
getHandleId(nodeId, 'source', item.value)
)
};
}
// Update db
updateUserSelectedResult({
appId,
chatId,
userSelectedVal
});
return {
[DispatchNodeResponseKeyEnum.skipHandleId]: userSelectOptions
.filter((item) => item.value !== userSelectedVal)
.map((item: any) => getHandleId(nodeId, 'source', item.key)),
[DispatchNodeResponseKeyEnum.nodeResponse]: {
userSelectResult: userSelectedVal
},
[NodeOutputKeyEnum.selectResult]: userSelectedVal
};
};
...@@ -4,7 +4,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; ...@@ -4,7 +4,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { getPluginRuntimeById } from '../../../app/plugin/controller'; import { getPluginRuntimeById } from '../../../app/plugin/controller';
import { import {
getDefaultEntryNodeIds, getWorkflowEntryNodeIds,
initWorkflowEdgeStatus, initWorkflowEdgeStatus,
storeNodes2RuntimeNodes storeNodes2RuntimeNodes
} from '@fastgpt/global/core/workflow/runtime/utils'; } from '@fastgpt/global/core/workflow/runtime/utils';
...@@ -49,7 +49,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi ...@@ -49,7 +49,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({ const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
...props, ...props,
runtimeNodes: storeNodes2RuntimeNodes(plugin.nodes, getDefaultEntryNodeIds(plugin.nodes)).map( runtimeNodes: storeNodes2RuntimeNodes(plugin.nodes, getWorkflowEntryNodeIds(plugin.nodes)).map(
(node) => { (node) => {
if (node.flowNodeType === FlowNodeTypeEnum.pluginInput) { if (node.flowNodeType === FlowNodeTypeEnum.pluginInput) {
return { return {
......
...@@ -6,7 +6,7 @@ import { responseWrite } from '../../../../common/response'; ...@@ -6,7 +6,7 @@ import { responseWrite } from '../../../../common/response';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { import {
getDefaultEntryNodeIds, getWorkflowEntryNodeIds,
initWorkflowEdgeStatus, initWorkflowEdgeStatus,
storeNodes2RuntimeNodes, storeNodes2RuntimeNodes,
textAdaptGptResponse textAdaptGptResponse
...@@ -67,7 +67,10 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => { ...@@ -67,7 +67,10 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({ const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
...props, ...props,
app: appData, app: appData,
runtimeNodes: storeNodes2RuntimeNodes(appData.modules, getDefaultEntryNodeIds(appData.modules)), runtimeNodes: storeNodes2RuntimeNodes(
appData.modules,
getWorkflowEntryNodeIds(appData.modules)
),
runtimeEdges: initWorkflowEdgeStatus(appData.edges), runtimeEdges: initWorkflowEdgeStatus(appData.edges),
histories: chatHistories, histories: chatHistories,
query: runtimePrompt2ChatsValue({ query: runtimePrompt2ChatsValue({
......
...@@ -212,6 +212,8 @@ export const iconPaths = { ...@@ -212,6 +212,8 @@ export const iconPaths = {
'core/workflow/template/textConcat': () => 'core/workflow/template/textConcat': () =>
import('./icons/core/workflow/template/textConcat.svg'), import('./icons/core/workflow/template/textConcat.svg'),
'core/workflow/template/toolCall': () => import('./icons/core/workflow/template/toolCall.svg'), 'core/workflow/template/toolCall': () => import('./icons/core/workflow/template/toolCall.svg'),
'core/workflow/template/userSelect': () =>
import('./icons/core/workflow/template/userSelect.svg'),
'core/workflow/template/variable': () => import('./icons/core/workflow/template/variable.svg'), 'core/workflow/template/variable': () => import('./icons/core/workflow/template/variable.svg'),
'core/workflow/template/variableUpdate': () => 'core/workflow/template/variableUpdate': () =>
import('./icons/core/workflow/template/variableUpdate.svg'), import('./icons/core/workflow/template/variableUpdate.svg'),
......
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" rx="6" fill="url(#paint0_linear_8765_6055)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6666 7.11108C8.70296 7.11108 7.11108 8.70296 7.11108 10.6666V21.3333C7.11108 23.297 8.70296 24.8889 10.6666 24.8889H21.3333C23.297 24.8889 24.8889 23.297 24.8889 21.3333V10.6666C24.8889 8.70296 23.297 7.11108 21.3333 7.11108H10.6666ZM14.4283 12.586C14.7407 12.2736 14.7407 11.7671 14.4283 11.4547C14.1159 11.1422 13.6094 11.1422 13.2969 11.4547L11.5654 13.1862L10.9314 12.5522C10.619 12.2398 10.1124 12.2398 9.80002 12.5522C9.4876 12.8646 9.4876 13.3712 9.80001 13.6836L10.9973 14.8809C11.1537 15.0372 11.3586 15.1153 11.5636 15.1152C11.7697 15.1163 11.9762 15.0382 12.1335 14.8809L14.4283 12.586ZM22.4342 13.1678C22.4342 13.6096 22.0761 13.9678 21.6342 13.9678H16.8278C16.386 13.9678 16.0278 13.6096 16.0278 13.1678C16.0278 12.7259 16.386 12.3678 16.8278 12.3678L21.6342 12.3678C22.0761 12.3678 22.4342 12.7259 22.4342 13.1678ZM11.8673 21.111C12.7509 21.111 13.4673 20.3946 13.4673 19.511C13.4673 18.6273 12.7509 17.911 11.8673 17.911C10.9836 17.911 10.2673 18.6273 10.2673 19.511C10.2673 20.3946 10.9836 21.111 11.8673 21.111ZM22.4342 19.511C22.4342 19.9528 22.0761 20.311 21.6342 20.311H16.8278C16.386 20.311 16.0278 19.9528 16.0278 19.511C16.0278 19.0691 16.386 18.711 16.8278 18.711H21.6342C22.0761 18.711 22.4342 19.0691 22.4342 19.511Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_8765_6055" x1="16" y1="0" x2="4.88889" y2="29.3333" gradientUnits="userSpaceOnUse">
<stop stop-color="#3ED9AA"/>
<stop offset="1" stop-color="#13C786"/>
</linearGradient>
</defs>
</svg>
<?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="1688632968712" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3386" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M507.904 52.224q95.232 0 179.2 36.352t145.92 98.304 98.304 145.408 36.352 178.688-36.352 179.2-98.304 145.92-145.92 98.304-179.2 36.352-178.688-36.352-145.408-98.304-98.304-145.92-36.352-179.2 36.352-178.688 98.304-145.408 145.408-98.304 178.688-36.352zM736.256 573.44q30.72 0 55.296-15.872t24.576-47.616q0-30.72-24.576-45.568t-55.296-14.848l-452.608 0q-30.72 0-56.32 14.848t-25.6 45.568q0 31.744 25.6 47.616t56.32 15.872l452.608 0z" p-id="3387"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17" >
\ No newline at end of file <path d="M5.33342 7.80417C4.99499 7.80417 4.72063 8.07852 4.72063 8.41695C4.72063 8.75538 4.99499 9.02974 5.33342 9.02974H10.6667C11.0052 9.02974 11.2795 8.75538 11.2795 8.41695C11.2795 8.07852 11.0052 7.80417 10.6667 7.80417H5.33342Z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.7901 8.41695C14.7901 12.167 11.7501 15.207 8.00008 15.207C4.25007 15.207 1.21008 12.167 1.21008 8.41695C1.21008 4.66694 4.25007 1.62695 8.00008 1.62695C11.7501 1.62695 14.7901 4.66694 14.7901 8.41695ZM13.4567 8.41695C13.4567 11.4306 11.0137 13.8736 8.00008 13.8736C4.98645 13.8736 2.54342 11.4306 2.54342 8.41695C2.54342 5.40332 4.98645 2.96029 8.00008 2.96029C11.0137 2.96029 13.4567 5.40332 13.4567 8.41695Z"/>
</svg>
\ No newline at end of file
...@@ -18,6 +18,11 @@ export const workflowNodeTemplateList = [ ...@@ -18,6 +18,11 @@ export const workflowNodeTemplateList = [
list: [] list: []
}, },
{ {
type: FlowNodeTemplateTypeEnum.interactive,
label: i18nT('common:core.workflow.template.Interactive'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.multimodal, type: FlowNodeTemplateTypeEnum.multimodal,
label: i18nT('common:core.workflow.template.Multimodal'), label: i18nT('common:core.workflow.template.Multimodal'),
list: [] list: []
......
...@@ -141,14 +141,20 @@ ...@@ -141,14 +141,20 @@
"workflow": { "workflow": {
"Input guide": "Input guide", "Input guide": "Input guide",
"file_url": "Url", "file_url": "Url",
"option1": "Option 1",
"option2": "Option 2",
"read_files": "Documents parse", "read_files": "Documents parse",
"read_files_result": "Document parsing results", "read_files_result": "Document parsing results",
"read_files_result_desc": "The original text of the document consists of the file name and the document content. Multiple files are separated by horizontal lines.", "read_files_result_desc": "The original text of the document consists of the file name and the document content. Multiple files are separated by horizontal lines.",
"read_files_tip": "Parse all uploaded documents in the conversation and return the corresponding document content", "read_files_tip": "Parse all uploaded documents in the conversation and return the corresponding document content",
"select_description": "Select description",
"select_result": "Select result",
"template": { "template": {
"communication": "Communication" "communication": "Communication"
}, },
"user_file_input": "Files url", "user_file_input": "Files url",
"user_file_input_desc": "Links to documents and images uploaded by users" "user_file_input_desc": "Links to documents and images uploaded by users",
"user_select": "User select",
"user_select_tip": "The module can have multiple options that lead to different workflow branches"
} }
} }
{ {
"Delete_all": "Delete all", "Delete_all": "Delete all",
"delete_all_input_guide_confirm": "Confirm to delete all input guide lexicons",
"chat_history": "chat record", "chat_history": "chat record",
"chat_input_guide_lexicon_is_empty": "No vocabulary has been configured yet", "chat_input_guide_lexicon_is_empty": "No vocabulary has been configured yet",
"citations": "{{num}} citations", "citations": "{{num}} citations",
...@@ -13,25 +12,27 @@ ...@@ -13,25 +12,27 @@
"contextual_preview": "Contextual preview", "contextual_preview": "Contextual preview",
"csv_input_lexicon_tip": "Only supports CSV batch import, click to download the template", "csv_input_lexicon_tip": "Only supports CSV batch import, click to download the template",
"custom_input_guide_url": "Custom thesaurus address", "custom_input_guide_url": "Custom thesaurus address",
"delete_all_input_guide_confirm": "Confirm to delete all input guide lexicons",
"empty_directory": "There is nothing left to choose from in this directory~", "empty_directory": "There is nothing left to choose from in this directory~",
"file_amount_over": "Exceed maximum number of files {{max}}",
"in_progress": "in progress", "in_progress": "in progress",
"input_guide": "Input guide",
"input_guide_lexicon": "Lexicon",
"input_guide_tip": "You can configure some preset questions. When the user enters a question, the relevant question is retrieved from these preset questions for prompt.",
"insert_input_guide,_some_data_already_exists": "Duplicate data, automatically filtered, insert: {{len}} data",
"is_chatting": "Chatting...please wait for the end", "is_chatting": "Chatting...please wait for the end",
"items": "strip", "items": "strip",
"module_runtime_and": "module run time and", "module_runtime_and": "module run time and",
"multiple_AI_conversations": "Multiple AI conversations", "multiple_AI_conversations": "Multiple AI conversations",
"new_chat": "new conversation", "new_chat": "new conversation",
"new_input_guide_lexicon": "New lexicon",
"no_workflow_response": "No running data",
"plugins_output": "Plugin output", "plugins_output": "Plugin output",
"question_tip": "From left to right, the response order of each module", "question_tip": "From left to right, the response order of each module",
"rearrangement": "Search results rearranged", "rearrangement": "Search results rearranged",
"select_file": "Select file",
"select_img": "Select images",
"stream_output": "stream output", "stream_output": "stream output",
"view_citations": "View citations", "view_citations": "View citations",
"web_site_sync": "Web site synchronization", "web_site_sync": "Web site synchronization"
"file_amount_over": "Exceed maximum number of files {{max}}",
"input_guide": "Input guide",
"input_guide_lexicon": "Lexicon",
"input_guide_tip": "You can configure some preset questions. When the user enters a question, the relevant question is retrieved from these preset questions for prompt.",
"insert_input_guide,_some_data_already_exists": "Duplicate data, automatically filtered, insert: {{len}} data",
"new_input_guide_lexicon": "New lexicon",
"select_file": "Select file",
"select_img": "Select images"
} }
...@@ -527,7 +527,8 @@ ...@@ -527,7 +527,8 @@
"module tokens": "total tokens", "module tokens": "total tokens",
"plugin output": "Plugin output value", "plugin output": "Plugin output value",
"search using reRank": "Result rearrangement", "search using reRank": "Result rearrangement",
"text output": "text output" "text output": "text output",
"user_select_result": "User select result"
}, },
"retry": "Regenerate", "retry": "Regenerate",
"tts": { "tts": {
...@@ -761,6 +762,7 @@ ...@@ -761,6 +762,7 @@
}, },
"module": { "module": {
"Add question type": "Add question type", "Add question type": "Add question type",
"Add_option": "Add option",
"Can not connect self": "Cannot connect to self", "Can not connect self": "Cannot connect to self",
"Confirm Delete Node": "Confirm delete node?", "Confirm Delete Node": "Confirm delete node?",
"Data Type": "Data type", "Data Type": "Data type",
...@@ -771,6 +773,7 @@ ...@@ -771,6 +773,7 @@
"Default Value": "Default value", "Default Value": "Default value",
"Default value": "Default value", "Default value": "Default value",
"Default value placeholder": "If not filled, the default return is an empty string", "Default value placeholder": "If not filled, the default return is an empty string",
"Diagram": "Diagram",
"Edit intro": "Edit description", "Edit intro": "Edit description",
"Field Description": "Field description", "Field Description": "Field description",
"Field Name": "Field name", "Field Name": "Field name",
...@@ -948,7 +951,9 @@ ...@@ -948,7 +951,9 @@
"OnRevert version confirm": "Confirm to revert to this version? It will save the configuration of the version being edited and create a new published version for the reverted version.", "OnRevert version confirm": "Confirm to revert to this version? It will save the configuration of the version being edited and create a new published version for the reverted version.",
"histories": "Publishing records" "histories": "Publishing records"
}, },
"run_test": "Test",
"template": { "template": {
"Interactive": "Interactive",
"Multimodal": "Multimodal", "Multimodal": "Multimodal",
"Search": "Search" "Search": "Search"
}, },
...@@ -1060,6 +1065,7 @@ ...@@ -1060,6 +1065,7 @@
"no_data": "No data", "no_data": "No data",
"no_laf_env": "The system is not configured with Laf environment", "no_laf_env": "The system is not configured with Laf environment",
"not_yet_introduced": "No introduction yet", "not_yet_introduced": "No introduction yet",
"option": "Option",
"pay": { "pay": {
"amount": "Amount", "amount": "Amount",
"balance": "Account balance", "balance": "Account balance",
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
"Custom outputs": "Custom outputs", "Custom outputs": "Custom outputs",
"Error": "Error", "Error": "Error",
"Read file result": "Document parsing result preview", "Read file result": "Document parsing result preview",
"User_select_description": "User select description",
"User_select_result": "User select result",
"read files": "parsed document" "read files": "parsed document"
}, },
"template": { "template": {
......
...@@ -143,14 +143,20 @@ ...@@ -143,14 +143,20 @@
"workflow": { "workflow": {
"Input guide": "填写说明", "Input guide": "填写说明",
"file_url": "文档链接", "file_url": "文档链接",
"option1": "选项 1",
"option2": "选项 2",
"read_files": "文档解析", "read_files": "文档解析",
"read_files_result": "文档解析结果", "read_files_result": "文档解析结果",
"read_files_result_desc": "文档原文,由文件名和文档内容组成,多个文件之间通过横线隔开。", "read_files_result_desc": "文档原文,由文件名和文档内容组成,多个文件之间通过横线隔开。",
"read_files_tip": "解析对话中所有上传的文档,并返回对应文档内容", "read_files_tip": "解析对话中所有上传的文档,并返回对应文档内容",
"select_description": "说明文字",
"select_result": "选择的结果",
"template": { "template": {
"communication": "通信" "communication": "通信"
}, },
"user_file_input": "文件链接", "user_file_input": "文件链接",
"user_file_input_desc": "用户上传的文档和图片链接" "user_file_input_desc": "用户上传的文档和图片链接",
"user_select": "用户选择",
"user_select_tip": "该模块可配置多个选项,以供对话时选择。不同选项可导向不同工作流支线"
} }
} }
{ {
"Delete_all": "清空词库", "Delete_all": "清空词库",
"chat_history": "聊天记录",
"chat_input_guide_lexicon_is_empty": "还没有配置词库", "chat_input_guide_lexicon_is_empty": "还没有配置词库",
"citations": "{{num}}条引用",
"click_contextual_preview": "点击查看上下文预览",
"config_input_guide": "配置输入引导", "config_input_guide": "配置输入引导",
"config_input_guide_lexicon": "配置词库", "config_input_guide_lexicon": "配置词库",
"config_input_guide_lexicon_title": "配置词库", "config_input_guide_lexicon_title": "配置词库",
"content_empty": "内容为空",
"contextual": "{{num}}条上下文",
"contextual_preview": "上下文预览 {{num}} 条",
"csv_input_lexicon_tip": "仅支持 CSV 批量导入,点击下载模板", "csv_input_lexicon_tip": "仅支持 CSV 批量导入,点击下载模板",
"custom_input_guide_url": "自定义词库地址", "custom_input_guide_url": "自定义词库地址",
"delete_all_input_guide_confirm": "确定要清空输入引导词库吗?", "delete_all_input_guide_confirm": "确定要清空输入引导词库吗?",
"empty_directory": "这个目录已经没东西可选了~",
"file_amount_over": "超出最大文件数量 {{max}}", "file_amount_over": "超出最大文件数量 {{max}}",
"in_progress": "进行中",
"input_guide": "输入引导", "input_guide": "输入引导",
"input_guide_lexicon": "词库", "input_guide_lexicon": "词库",
"input_guide_tip": "可以配置一些预设的问题。在用户输入问题时,会从这些预设问题中获取相关问题进行提示。", "input_guide_tip": "可以配置一些预设的问题。在用户输入问题时,会从这些预设问题中获取相关问题进行提示。",
"insert_input_guide,_some_data_already_exists": "有重复数据,已自动过滤,共插入 {{len}} 条数据", "insert_input_guide,_some_data_already_exists": "有重复数据,已自动过滤,共插入 {{len}} 条数据",
"new_input_guide_lexicon": "新词库",
"is_chatting": "正在聊天中...请等待结束", "is_chatting": "正在聊天中...请等待结束",
"content_empty": "内容为空",
"contextual": "{{num}}条上下文",
"contextual_preview": "上下文预览 {{num}} 条",
"items": "条", "items": "条",
"view_citations": "查看引用",
"citations": "{{num}}条引用",
"click_contextual_preview": "点击查看上下文预览",
"multiple_AI_conversations": "多组 AI 对话",
"module_runtime_and": "模块运行时间和", "module_runtime_and": "模块运行时间和",
"empty_directory": "这个目录已经没东西可选了~", "multiple_AI_conversations": "多组 AI 对话",
"chat_history": "聊天记录", "new_chat": "新对话",
"stream_output": "流输出", "new_input_guide_lexicon": "新词库",
"no_workflow_response": "没有运行数据",
"plugins_output": "插件输出", "plugins_output": "插件输出",
"in_progress": "进行中",
"question_tip": "从上到下,为各个模块的响应顺序", "question_tip": "从上到下,为各个模块的响应顺序",
"rearrangement": "检索结果重排", "rearrangement": "检索结果重排",
"web_site_sync": "Web站点同步",
"new_chat": "新对话",
"select_file": "选择文件", "select_file": "选择文件",
"select_img": "选择图片" "select_img": "选择图片",
"stream_output": "流输出",
"view_citations": "查看引用",
"web_site_sync": "Web站点同步"
} }
...@@ -537,7 +537,8 @@ ...@@ -537,7 +537,8 @@
"module tokens": "总 tokens", "module tokens": "总 tokens",
"plugin output": "插件输出值", "plugin output": "插件输出值",
"search using reRank": "结果重排", "search using reRank": "结果重排",
"text output": "文本输出" "text output": "文本输出",
"user_select_result": "用户选择结果"
}, },
"retry": "重新生成", "retry": "重新生成",
"tts": { "tts": {
...@@ -771,6 +772,7 @@ ...@@ -771,6 +772,7 @@
}, },
"module": { "module": {
"Add question type": "添加问题类型", "Add question type": "添加问题类型",
"Add_option": "添加选项",
"Can not connect self": "不能连接自身", "Can not connect self": "不能连接自身",
"Confirm Delete Node": "确认删除该节点?", "Confirm Delete Node": "确认删除该节点?",
"Data Type": "数据类型", "Data Type": "数据类型",
...@@ -781,6 +783,7 @@ ...@@ -781,6 +783,7 @@
"Default Value": "默认值", "Default Value": "默认值",
"Default value": "默认值", "Default value": "默认值",
"Default value placeholder": "不填则默认返回空字符", "Default value placeholder": "不填则默认返回空字符",
"Diagram": "示意图",
"Edit intro": "编辑描述", "Edit intro": "编辑描述",
"Field Description": "字段描述", "Field Description": "字段描述",
"Field Name": "字段名", "Field Name": "字段名",
...@@ -958,7 +961,9 @@ ...@@ -958,7 +961,9 @@
"OnRevert version confirm": "确认回退至该版本?会为您保存编辑中版本的配置,并为回退版本创建一个新的发布版本。", "OnRevert version confirm": "确认回退至该版本?会为您保存编辑中版本的配置,并为回退版本创建一个新的发布版本。",
"histories": "发布记录" "histories": "发布记录"
}, },
"run_test": "运行",
"template": { "template": {
"Interactive": "交互",
"Multimodal": "多模态", "Multimodal": "多模态",
"Search": "搜索" "Search": "搜索"
}, },
...@@ -1070,6 +1075,7 @@ ...@@ -1070,6 +1075,7 @@
"no_data": "暂无数据", "no_data": "暂无数据",
"no_laf_env": "系统未配置Laf环境", "no_laf_env": "系统未配置Laf环境",
"not_yet_introduced": "暂无介绍", "not_yet_introduced": "暂无介绍",
"option": "选项",
"pay": { "pay": {
"amount": "金额", "amount": "金额",
"balance": "账号余额", "balance": "账号余额",
......
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
"Reset template confirm": "确认还原代码模板?将会重置所有输入和输出至模板值,请注意保存当前代码。" "Reset template confirm": "确认还原代码模板?将会重置所有输入和输出至模板值,请注意保存当前代码。"
}, },
"confirm_delete_field_tip": "确认删除该字段?", "confirm_delete_field_tip": "确认删除该字段?",
"create_link_error": "创建链接异常",
"custom_input": "自定义输入", "custom_input": "自定义输入",
"delete_api": "确认删除该API密钥?删除后该密钥立即失效,对应的对话日志不会删除,请确认!",
"edit_input": "编辑输入", "edit_input": "编辑输入",
"field_description": "字段描述", "field_description": "字段描述",
"field_description_placeholder": "描述该输入字段的功能,如果为工具调用参数,则该描述会影响模型生成的质量", "field_description_placeholder": "描述该输入字段的功能,如果为工具调用参数,则该描述会影响模型生成的质量",
...@@ -27,6 +29,8 @@ ...@@ -27,6 +29,8 @@
"Custom outputs": "自定义输出", "Custom outputs": "自定义输出",
"Error": "错误信息", "Error": "错误信息",
"Read file result": "文档解析结果预览", "Read file result": "文档解析结果预览",
"User_select_description": "说明文字",
"User_select_result": "选择的结果",
"read files": "解析的文档" "read files": "解析的文档"
}, },
"template": { "template": {
...@@ -40,8 +44,6 @@ ...@@ -40,8 +44,6 @@
"workflow_start": "流程开始" "workflow_start": "流程开始"
}, },
"tool_input": "工具参数", "tool_input": "工具参数",
"variable_picker_tips": "可输入节点名或变量名搜索", "update_link_error": "更新链接异常",
"delete_api": "确认删除该API密钥?删除后该密钥立即失效,对应的对话日志不会删除,请确认!", "variable_picker_tips": "可输入节点名或变量名搜索"
"create_link_error": "创建链接异常",
"update_link_error": "更新链接异常"
} }
...@@ -34,6 +34,7 @@ const Button = defineStyleConfig({ ...@@ -34,6 +34,7 @@ const Button = defineStyleConfig({
transform: 'scale(0.98)' transform: 'scale(0.98)'
}, },
_disabled: { _disabled: {
transform: 'none !important',
_hover: { _hover: {
filter: 'none' filter: 'none'
} }
......
<svg width="422" height="301" viewBox="0 0 422 301" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_8765_5909)">
<rect x="1" y="1.5" width="420" height="298" rx="7.875" fill="#F0F1F6"/>
<path d="M14 39.5C14 35.634 17.134 32.5 21 32.5H400C403.866 32.5 407 35.634 407 39.5V280.5C407 284.366 403.866 287.5 400 287.5H21C17.134 287.5 14 284.366 14 280.5V39.5Z" fill="white"/>
<path d="M24 80.5H224.617C228.142 80.5 231 83.3579 231 86.8834V185.117C231 188.642 228.142 191.5 224.617 191.5H30.3834C26.8579 191.5 24 188.642 24 185.117V80.5Z" fill="#F7F8FA"/>
<path d="M37.0504 95.2877C37.7295 96.1153 38.2494 96.8474 38.6101 97.4734L38.0053 97.8978C37.6127 97.24 37.0822 96.4973 36.4032 95.6909L37.0504 95.2877ZM38.0372 99.6379L38.7268 99.9032C38.2494 101.452 37.7188 102.916 37.1353 104.275L36.4244 103.956C37.0398 102.556 37.5809 101.113 38.0372 99.6379ZM41.878 104.519H40.7321L40.573 103.765C40.9337 103.797 41.2839 103.818 41.6128 103.818C41.9099 103.818 42.0584 103.68 42.0584 103.415V94.8527H42.8329V97.0172C42.9921 97.6007 43.1937 98.1631 43.4271 98.7148C44.138 98.1737 44.764 97.4204 45.3051 96.4548L45.8887 96.9323C45.2521 98.0039 44.5306 98.8103 43.7242 99.362C44.3396 100.656 45.1566 101.834 46.1751 102.916L45.6765 103.553C44.4457 102.11 43.4908 100.54 42.8329 98.8315V103.606C42.8329 104.211 42.5146 104.519 41.878 104.519ZM40.8488 98.0676H38.9921V97.3355H41.5491V98.0252C41.252 100.529 40.3714 102.365 38.8966 103.532L38.3767 102.938C39.7348 101.866 40.5624 100.243 40.8488 98.0676ZM47.553 98.8952H49.2612V97.7705H50.0357V98.8952H51.4787V99.6379H50.0357V99.988C50.5556 100.487 51.0861 101.038 51.6166 101.643L51.1922 102.28C50.7466 101.622 50.354 101.091 50.0357 100.688V104.572H49.2612V100.625C48.8262 101.601 48.232 102.481 47.4893 103.267L47.1604 102.418C48.0835 101.601 48.7519 100.678 49.1657 99.6379H47.553V98.8952ZM56.2215 98.1313V104.572H55.4894V104.094H52.7944V104.572H52.0623V98.1313H56.2215ZM52.7944 103.415H55.4894V102.237H52.7944V103.415ZM52.7944 101.58H55.4894V100.529H52.7944V101.58ZM52.7944 99.8713H55.4894V98.8103H52.7944V99.8713ZM48.9004 96.3169C48.6458 96.7519 48.3593 97.1551 48.0304 97.5159L47.362 97.0915C48.0198 96.3912 48.5079 95.6166 48.805 94.7572L49.5371 94.9164C49.4522 95.171 49.3567 95.4044 49.2506 95.6379H52.0517V96.3169H50.3434C50.6405 96.7095 50.8739 97.0702 51.0543 97.4098L50.3434 97.675C50.11 97.2082 49.8448 96.7625 49.5264 96.3169H48.9004ZM53.4416 96.3169C53.2082 96.7838 52.9535 97.2188 52.6671 97.622L52.0092 97.2082C52.5822 96.4442 53.0066 95.6166 53.2612 94.7042L53.9933 94.8633C53.9084 95.1286 53.8236 95.3832 53.7281 95.6379H56.8899V96.3169H54.8846C55.1817 96.6989 55.4151 97.049 55.5955 97.3779L54.927 97.622C54.683 97.1763 54.4071 96.7413 54.0888 96.3169H53.4416ZM59.9229 98.057V99.4681H62.5118V98.057H59.9229ZM62.5118 100.168H59.1802V95.2241H66.6073V100.168H63.2969V101.41H67.1272V102.142H63.2969V103.458H67.5728V104.19H58.2253V103.458H62.5118V102.142H58.6815V101.41H62.5118V100.168ZM63.2969 99.4681H65.854V98.057H63.2969V99.4681ZM65.854 97.3567V95.9243H63.2969V97.3567H65.854ZM62.5118 95.9243H59.9229V97.3567H62.5118V95.9243ZM70.5209 95.1922H76.9719V98.874H70.5209V95.1922ZM76.2186 98.2267V97.3567H71.2743V98.2267H76.2186ZM71.2743 96.7307H76.2186V95.8501H71.2743V96.7307ZM73.4387 100.359H69.0143V99.6591H78.4043V100.359H74.2133V101.558H77.269V102.237H74.2133V103.649C74.7226 103.691 75.3379 103.723 76.0382 103.723C76.9507 103.723 77.7252 103.712 78.3512 103.691L78.139 104.412C77.3751 104.412 76.696 104.402 76.0913 104.391C74.3406 104.391 73.1523 104.253 72.5263 103.988C71.9851 103.744 71.4971 103.277 71.0727 102.577C70.6589 103.362 70.1284 104.009 69.4917 104.529L68.9931 103.903C69.9692 103.118 70.6376 102.057 70.9878 100.731L71.7093 100.858C71.6032 101.251 71.4865 101.622 71.3485 101.972C71.7942 102.715 72.3034 103.182 72.8552 103.383C73.0249 103.436 73.2159 103.489 73.4387 103.532V100.359ZM80.2277 95.3302H88.822V96.0729H85.4373C85.2675 96.3063 85.0766 96.5397 84.8856 96.7519V100.264H84.1216V97.484C83.1243 98.3116 81.8192 99.0437 80.2065 99.691L79.729 99.065C81.9996 98.2267 83.6124 97.2294 84.5673 96.0729H80.2277V95.3302ZM88.0262 100.635V104.519H87.2516V104.02H81.7768V104.519H81.0023V100.635H88.0262ZM81.7768 103.277H87.2516V101.378H81.7768V103.277ZM85.9996 97.0172C87.2729 97.7387 88.3339 98.4708 89.1933 99.2029L88.6416 99.7652C87.8776 99.065 86.8272 98.3116 85.4904 97.5053L85.9996 97.0172ZM93.7117 98.6512V99.8077H97.924V98.6512H93.7117ZM97.924 100.476H93.7117V101.601H97.924V100.476ZM97.924 102.269H93.7117V104.54H92.9902V98.9907C92.3961 99.7334 91.7064 100.423 90.9425 101.038L90.4438 100.391C91.7807 99.3302 92.8099 98.1206 93.542 96.7519H90.7303V96.0092H93.8921C94.0725 95.5954 94.2316 95.1816 94.3589 94.7678L95.1335 94.8633C95.0062 95.2559 94.8788 95.6379 94.7303 96.0092H100.004V96.7519H94.412C94.1998 97.1763 93.977 97.5901 93.7223 97.9827H98.6455V103.712C98.6455 104.232 98.3484 104.497 97.7542 104.519H96.5659L96.3749 103.818C96.7781 103.84 97.16 103.861 97.5102 103.861C97.786 103.861 97.924 103.733 97.924 103.479V102.269ZM101.371 95.2877H110.835V96.0304H107.631V97.6538H110.22V104.497H109.456V104.02H102.771V104.497H102.007V97.6538H104.533V96.0304H101.371V95.2877ZM102.771 103.298H109.456V101.611H107.917C107.217 101.611 106.877 101.261 106.877 100.572V98.3753H105.286V98.5875C105.265 100.264 104.681 101.484 103.535 102.259L102.941 101.739C103.981 101.081 104.511 100.03 104.533 98.5875V98.3753H102.771V103.298ZM105.286 96.0304V97.6538H106.877V96.0304H105.286ZM107.631 98.3753V100.412C107.631 100.741 107.79 100.911 108.119 100.911H109.456V98.3753H107.631ZM115.767 102.513V103.245C114.717 103.649 113.528 103.967 112.202 104.211L112.107 103.458C113.497 103.224 114.717 102.906 115.767 102.513ZM114.123 94.8845L114.855 95.1604C114.261 96.5291 113.666 97.622 113.062 98.4283C113.592 98.3753 114.123 98.301 114.653 98.2161C114.887 97.7917 115.141 97.3355 115.406 96.8474L116.086 97.1021C115.025 99.0437 114.144 100.412 113.444 101.219C114.271 101.038 115.099 100.794 115.926 100.487V101.187C114.727 101.622 113.582 101.919 112.478 102.068L112.266 101.378C112.404 101.314 112.521 101.229 112.616 101.145C113.062 100.71 113.603 99.9668 114.239 98.9164C113.603 99.0119 112.956 99.0968 112.308 99.1817L112.107 98.4814C112.234 98.4177 112.34 98.3222 112.446 98.1843C113.083 97.2294 113.645 96.1259 114.123 94.8845ZM118.515 96.2957H116.319V95.5636H121.444V96.2957H119.279V103.362H121.72V104.105H116.022V103.362H118.515V96.2957ZM126.652 96.0517H129.092C128.944 95.6273 128.774 95.2559 128.583 94.927L129.4 94.7891C129.548 95.1604 129.686 95.5848 129.835 96.0517H132.445V96.7732H129.909V98.1737H132.148V102.078C132.148 102.725 131.84 103.054 131.246 103.054H130.62L130.429 102.365L131.013 102.386C131.267 102.386 131.405 102.227 131.405 101.919V98.9058H129.909V104.572H129.166V98.9058H127.713V103.129H126.97V98.1737H129.166V96.7732H126.652V96.0517ZM125.219 99.1923V104.519H124.487V99.2878C124.148 100.306 123.723 101.24 123.203 102.078L122.875 101.261C123.617 100.19 124.158 98.9695 124.487 97.6007H123.012V96.8686H124.487V94.8421H125.219V96.8686H126.418V97.6007H125.219V98.3541C125.697 98.927 126.185 99.5848 126.684 100.306L126.238 100.975C125.856 100.243 125.516 99.6485 125.219 99.1923ZM135.945 95.7758C136.624 95.7758 137.165 95.9562 137.579 96.3381C137.971 96.6989 138.173 97.1976 138.173 97.8342C138.173 98.3116 138.035 98.7254 137.77 99.0756C137.664 99.2029 137.377 99.4787 136.91 99.8925C136.677 100.094 136.518 100.285 136.412 100.476C136.274 100.71 136.21 100.964 136.21 101.251V101.495H135.361V101.251C135.361 100.901 135.425 100.593 135.552 100.328C135.701 100.009 136.051 99.6061 136.624 99.0968C136.794 98.927 136.921 98.7997 136.985 98.7148C137.197 98.4496 137.303 98.1737 137.303 97.8766C137.303 97.4522 137.176 97.1233 136.942 96.8899C136.698 96.6352 136.348 96.5185 135.902 96.5185C135.372 96.5185 134.979 96.6883 134.725 97.0384C134.491 97.3355 134.374 97.7387 134.374 98.2586H133.536C133.536 97.5159 133.738 96.9217 134.162 96.4761C134.587 96.0092 135.181 95.7758 135.945 95.7758ZM135.786 102.237C135.966 102.237 136.125 102.29 136.252 102.418C136.369 102.534 136.433 102.683 136.433 102.863C136.433 103.054 136.369 103.203 136.242 103.32C136.114 103.436 135.966 103.5 135.786 103.5C135.605 103.5 135.457 103.436 135.329 103.32C135.202 103.192 135.149 103.044 135.149 102.863C135.149 102.683 135.202 102.534 135.329 102.418C135.457 102.29 135.605 102.237 135.786 102.237Z" fill="#111824"/>
<g filter="url(#filter0_dd_8765_5909)">
<rect x="36" y="117.594" width="181.889" height="28.1259" rx="4.54721" fill="#F0F4FF" shape-rendering="crispEdges"/>
<rect x="36.3789" y="117.973" width="181.131" height="27.368" rx="4.16828" stroke="#94B5FF" stroke-width="0.757869" shape-rendering="crispEdges"/>
<path d="M123.481 126.275H130.07V130.106H123.481V126.275ZM129.073 129.246V128.588H124.479V129.246H129.073ZM124.479 127.793H129.073V127.135H124.479V127.793ZM126.367 131.634H122.028V130.721H131.481V131.634H127.375V132.62H130.346V133.512H127.375V134.647C127.863 134.689 128.436 134.711 129.073 134.711C130.007 134.711 130.802 134.689 131.439 134.668L131.163 135.612C130.42 135.612 129.741 135.602 129.147 135.591C127.386 135.581 126.187 135.443 125.54 135.188C125.031 134.965 124.564 134.541 124.15 133.915C123.747 134.636 123.237 135.252 122.622 135.74L121.964 134.912C122.919 134.159 123.577 133.151 123.916 131.899L124.871 132.058C124.765 132.44 124.649 132.79 124.521 133.13C124.946 133.787 125.412 134.201 125.943 134.382C126.07 134.424 126.208 134.456 126.367 134.498V131.634Z" fill="#2B5FD9"/>
</g>
<g filter="url(#filter1_dd_8765_5909)">
<rect x="36" y="151.783" width="181.889" height="28.1259" rx="4.54721" fill="white" shape-rendering="crispEdges"/>
<rect x="36.3789" y="152.162" width="181.131" height="27.368" rx="4.16828" stroke="#DFE2EA" stroke-width="0.757869" shape-rendering="crispEdges"/>
<path d="M122.442 160.56H131.1V161.536H127.736C127.588 161.748 127.418 161.95 127.248 162.141V165.579H126.251V163.064C125.275 163.828 124.044 164.507 122.558 165.112L121.922 164.284C124.065 163.51 125.614 162.597 126.569 161.536H122.442V160.56ZM130.357 165.865V169.897H129.338V169.419H124.182V169.897H123.163V165.865H130.357ZM124.182 168.454H129.338V166.841H124.182V168.454ZM128.383 162.311C129.614 163.011 130.654 163.722 131.503 164.433L130.771 165.165C130.039 164.486 129.02 163.743 127.704 162.958L128.383 162.311Z" fill="#485264"/>
</g>
<rect x="24.4093" y="44.9057" width="28.6544" height="28.6544" rx="6.14024" stroke="#E8EBF0" stroke-width="0.818698"/>
<rect x="25.6373" y="46.1338" width="26.1983" height="26.1983" rx="4.91219" fill="url(#paint0_linear_8765_5909)"/>
<path d="M35.2734 60.6279C35.2734 61.0314 34.9464 61.3584 34.543 61.3584C34.1396 61.3584 33.8125 61.0314 33.8125 60.6279L33.8125 57.8381C33.8125 57.4346 34.1396 57.1076 34.543 57.1076C34.9464 57.1076 35.2734 57.4346 35.2734 57.8381L35.2734 60.6279Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M34.543 56.6897C35.4543 56.6897 36.1931 55.9509 36.1931 55.0395C36.1931 54.1282 35.4543 53.3894 34.543 53.3894C33.6316 53.3894 32.8928 54.1282 32.8928 55.0395C32.8928 55.9509 33.6316 56.6897 34.543 56.6897ZM34.543 58.1506C36.2612 58.1506 37.654 56.7577 37.654 55.0395C37.654 53.3213 36.2612 51.9285 34.543 51.9285C32.8248 51.9285 31.4319 53.3213 31.4319 55.0395C31.4319 56.7577 32.8248 58.1506 34.543 58.1506Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.4623 54.3388C38.4623 53.9353 38.7894 53.6083 39.1928 53.6083C39.9639 53.6083 40.7283 53.7516 41.4428 54.031C42.1573 54.3104 42.8092 54.7209 43.3602 55.2411C43.9113 55.7613 44.3508 56.3812 44.6515 57.0666C44.9523 57.752 45.1077 58.4883 45.1077 59.233C45.1077 59.6364 44.7807 59.9634 44.3772 59.9634C43.9738 59.9634 43.6468 59.6364 43.6468 59.233C43.6468 58.6922 43.534 58.1557 43.3137 57.6535C43.0933 57.1513 42.7691 56.6921 42.3573 56.3034C41.9455 55.9146 41.4543 55.6041 40.9108 55.3916C40.3672 55.1791 39.7833 55.0692 39.1928 55.0692C38.7894 55.0692 38.4623 54.7422 38.4623 54.3388Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M34.543 65.0766C35.4543 65.0766 36.1931 64.3378 36.1931 63.4265C36.1931 62.5151 35.4543 61.7763 34.543 61.7763C33.6316 61.7763 32.8928 62.5151 32.8928 63.4265C32.8928 64.3378 33.6316 65.0766 34.543 65.0766ZM34.543 66.5375C36.2612 66.5375 37.654 65.1447 37.654 63.4265C37.654 61.7083 36.2612 60.3154 34.543 60.3154C32.8248 60.3154 31.4319 61.7083 31.4319 63.4265C31.4319 65.1447 32.8248 66.5375 34.543 66.5375Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M43.6889 62.0267H42.4213C41.9808 62.0267 41.7398 62.0279 41.5667 62.0423C41.5599 62.0428 41.5535 62.0434 41.5474 62.044C41.5469 62.05 41.5463 62.0565 41.5457 62.0632C41.5313 62.2363 41.5302 62.4774 41.5302 62.9179V64.1855C41.5302 64.626 41.5313 64.867 41.5457 65.0401C41.5463 65.0469 41.5469 65.0533 41.5474 65.0594C41.5535 65.0599 41.5599 65.0605 41.5667 65.0611C41.7398 65.0754 41.9808 65.0766 42.4213 65.0766H43.6889C44.1294 65.0766 44.3705 65.0754 44.5436 65.0611C44.5503 65.0605 44.5567 65.0599 44.5628 65.0594C44.5634 65.0533 44.564 65.0469 44.5645 65.0401C44.5789 64.867 44.5801 64.626 44.5801 64.1855V62.9179C44.5801 62.4774 44.5789 62.2363 44.5645 62.0632C44.564 62.0564 44.5634 62.05 44.5628 62.044C44.5567 62.0434 44.5503 62.0428 44.5436 62.0423C44.3705 62.0279 44.1294 62.0267 43.6889 62.0267ZM40.2337 61.3533C40.0693 61.6699 40.0693 62.0859 40.0693 62.9179V64.1855C40.0693 65.0174 40.0693 65.4334 40.2337 65.75C40.3724 66.0169 40.5899 66.2344 40.8567 66.373C41.1734 66.5375 41.5894 66.5375 42.4213 66.5375H43.6889C44.5209 66.5375 44.9369 66.5375 45.2535 66.373C45.5203 66.2344 45.7379 66.0169 45.8765 65.75C46.041 65.4334 46.041 65.0174 46.041 64.1855V62.9179C46.041 62.0859 46.041 61.6699 45.8765 61.3533C45.7379 61.0865 45.5203 60.8689 45.2535 60.7303C44.9369 60.5658 44.5209 60.5658 43.6889 60.5658H42.4213C41.5894 60.5658 41.1734 60.5658 40.8567 60.7303C40.5899 60.8689 40.3724 61.0865 40.2337 61.3533Z" fill="white"/>
<path d="M36.1931 55.0395C36.1931 55.9509 35.4543 56.6897 34.543 56.6897C33.6316 56.6897 32.8928 55.9509 32.8928 55.0395C32.8928 54.1282 33.6316 53.3894 34.543 53.3894C35.4543 53.3894 36.1931 54.1282 36.1931 55.0395Z" fill="white"/>
<path d="M36.1931 63.4265C36.1931 64.3378 35.4543 65.0766 34.543 65.0766C33.6316 65.0766 32.8928 64.3378 32.8928 63.4265C32.8928 62.5151 33.6316 61.7763 34.543 61.7763C35.4543 61.7763 36.1931 62.5151 36.1931 63.4265Z" fill="white"/>
<path d="M42.4213 62.0267H43.6889C44.1294 62.0267 44.3705 62.0279 44.5436 62.0423L44.5628 62.044L44.5645 62.0632C44.5789 62.2363 44.5801 62.4774 44.5801 62.9179V64.1855C44.5801 64.626 44.5789 64.867 44.5645 65.0401L44.5628 65.0594L44.5436 65.0611C44.3705 65.0754 44.1294 65.0766 43.6889 65.0766H42.4213C41.9808 65.0766 41.7398 65.0754 41.5667 65.0611L41.5474 65.0594L41.5457 65.0401C41.5313 64.867 41.5302 64.626 41.5302 64.1855V62.9179C41.5302 62.4774 41.5313 62.2363 41.5457 62.0632L41.5474 62.044L41.5667 62.0423C41.7398 62.0279 41.9808 62.0267 42.4213 62.0267Z" fill="white"/>
<rect x="359.939" y="197.909" width="28.6514" height="28.6514" rx="6.13958" fill="#487FFF"/>
<rect x="359.939" y="197.909" width="28.6514" height="28.6514" rx="6.13958" stroke="#E8EBF0" stroke-width="0.818611"/>
<path d="M374.265 214.282C372.102 214.282 370.179 215.326 368.955 216.946C368.691 217.295 368.56 217.469 368.564 217.705C368.567 217.887 368.682 218.117 368.825 218.229C369.01 218.375 369.267 218.375 369.781 218.375H378.749C379.263 218.375 379.52 218.375 379.705 218.229C379.848 218.117 379.963 217.887 379.966 217.705C379.97 217.469 379.838 217.295 379.575 216.946C378.351 215.326 376.427 214.282 374.265 214.282Z" fill="white"/>
<path d="M374.265 212.235C375.96 212.235 377.335 210.861 377.335 209.165C377.335 207.47 375.96 206.096 374.265 206.096C372.57 206.096 371.195 207.47 371.195 209.165C371.195 210.861 372.57 212.235 374.265 212.235Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M374.265 206.778C372.946 206.778 371.877 207.847 371.877 209.165C371.877 210.484 372.946 211.553 374.265 211.553C375.584 211.553 376.653 210.484 376.653 209.165C376.653 207.847 375.584 206.778 374.265 206.778ZM370.513 209.165C370.513 207.093 372.193 205.413 374.265 205.413C376.337 205.413 378.017 207.093 378.017 209.165C378.017 211.237 376.337 212.917 374.265 212.917C372.193 212.917 370.513 211.237 370.513 209.165ZM374.265 214.964C372.328 214.964 370.602 215.898 369.499 217.357C369.432 217.447 369.38 217.515 369.337 217.575C369.304 217.621 369.282 217.654 369.267 217.68C369.368 217.691 369.513 217.692 369.781 217.692H378.749C379.017 217.692 379.162 217.691 379.263 217.68C379.248 217.654 379.225 217.621 379.193 217.575C379.15 217.515 379.098 217.447 379.031 217.357C377.928 215.898 376.202 214.964 374.265 214.964ZM368.411 216.535C369.756 214.754 371.877 213.599 374.265 213.599C376.653 213.599 378.773 214.754 380.119 216.535C380.125 216.543 380.131 216.551 380.138 216.559C380.253 216.712 380.377 216.876 380.466 217.035C380.573 217.226 380.653 217.448 380.648 217.717C380.644 217.934 380.578 218.137 380.499 218.297C380.419 218.457 380.297 218.632 380.126 218.766C379.899 218.944 379.651 219.007 379.431 219.034C379.238 219.057 379.01 219.057 378.78 219.057C378.769 219.057 378.759 219.057 378.749 219.057H369.781C369.771 219.057 369.76 219.057 369.75 219.057C369.52 219.057 369.291 219.057 369.099 219.034C368.879 219.007 368.631 218.944 368.404 218.766C368.233 218.632 368.111 218.457 368.031 218.297C367.951 218.137 367.886 217.934 367.882 217.717C367.877 217.448 367.956 217.226 368.064 217.035C368.152 216.876 368.277 216.712 368.392 216.559C368.398 216.551 368.404 216.543 368.411 216.535Z" fill="white"/>
<path d="M355.527 242.017C355.527 238.125 358.682 234.97 362.574 234.97H389V266.681C389 270.573 385.845 273.728 381.953 273.728H362.574C358.682 273.728 355.527 270.573 355.527 266.681V242.017Z" fill="#F0F4FF"/>
<path d="M368.382 248.884H375.88V253.164H368.382V248.884ZM375.004 252.411V251.4H369.257V252.411H375.004ZM369.257 250.672H375.004V249.649H369.257V250.672ZM371.773 254.89H366.631V254.076H377.544V254.89H372.673V256.284H376.225V257.073H372.673V258.713C373.265 258.762 373.98 258.799 374.794 258.799C375.855 258.799 376.755 258.787 377.483 258.762L377.236 259.601C376.348 259.601 375.559 259.589 374.856 259.576C372.821 259.576 371.44 259.416 370.713 259.108C370.084 258.824 369.516 258.281 369.023 257.467C368.542 258.38 367.925 259.132 367.186 259.736L366.606 259.009C367.741 258.096 368.517 256.863 368.924 255.322L369.763 255.47C369.64 255.926 369.504 256.358 369.344 256.764C369.862 257.628 370.454 258.17 371.095 258.405C371.292 258.466 371.514 258.528 371.773 258.577V254.89Z" fill="#24282C"/>
<mask id="path-19-inside-1_8765_5909" fill="white">
<path d="M0.290039 11.8545C0.290039 6.0555 4.99105 1.35449 10.79 1.35449H412.133C417.932 1.35449 422.633 6.0555 422.633 11.8545V21.2646H0.290039V11.8545Z"/>
</mask>
<path d="M0.290039 11.8545C0.290039 6.0555 4.99105 1.35449 10.79 1.35449H412.133C417.932 1.35449 422.633 6.0555 422.633 11.8545V21.2646H0.290039V11.8545Z" fill="#F0F1F6"/>
<path d="M0.290039 1.35449H422.633H0.290039ZM422.633 21.9208H0.290039V20.6083H422.633V21.9208ZM0.290039 21.2646V1.35449V21.2646ZM422.633 1.35449V21.2646V1.35449Z" fill="#DFE2EA" mask="url(#path-19-inside-1_8765_5909)"/>
<circle cx="17.4035" cy="12.1514" r="3.64527" fill="#C4CBD7"/>
<circle cx="31.9845" cy="12.1514" r="3.64527" fill="#C4CBD7"/>
<circle cx="46.5656" cy="12.1514" r="3.64527" fill="#C4CBD7"/>
</g>
<rect x="0.5" y="1" width="421" height="299" rx="8.375" stroke="#E8EBF0"/>
<defs>
<filter id="filter0_dd_8765_5909" x="34" y="116.594" width="185.889" height="32.126" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0745098 0 0 0 0 0.2 0 0 0 0 0.419608 0 0 0 0.08 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_8765_5909"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0745098 0 0 0 0 0.2 0 0 0 0 0.419608 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_8765_5909" result="effect2_dropShadow_8765_5909"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_8765_5909" result="shape"/>
</filter>
<filter id="filter1_dd_8765_5909" x="34" y="150.783" width="185.889" height="32.126" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0745098 0 0 0 0 0.2 0 0 0 0 0.419608 0 0 0 0.08 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_8765_5909"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0745098 0 0 0 0 0.2 0 0 0 0 0.419608 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_8765_5909" result="effect2_dropShadow_8765_5909"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_8765_5909" result="shape"/>
</filter>
<linearGradient id="paint0_linear_8765_5909" x1="38.7365" y1="46.1338" x2="29.6399" y2="70.1489" gradientUnits="userSpaceOnUse">
<stop stop-color="#7895FE"/>
<stop offset="1" stop-color="#7177FF"/>
</linearGradient>
<clipPath id="clip0_8765_5909">
<rect x="1" y="1.5" width="420" height="298" rx="7.875" fill="white"/>
</clipPath>
</defs>
</svg>
...@@ -18,7 +18,12 @@ import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; ...@@ -18,7 +18,12 @@ import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { uploadFile2DB } from '@/web/common/file/controller'; import { uploadFile2DB } from '@/web/common/file/controller';
import { ChatFileTypeEnum } from '@fastgpt/global/core/chat/constants'; import { ChatFileTypeEnum } from '@fastgpt/global/core/chat/constants';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { ChatBoxInputFormType, ChatBoxInputType, UserInputFileItemType } from '../type'; import {
ChatBoxInputFormType,
ChatBoxInputType,
SendPromptFnType,
UserInputFileItemType
} from '../type';
import { textareaMinH } from '../constants'; import { textareaMinH } from '../constants';
import { UseFormReturn, useFieldArray } from 'react-hook-form'; import { UseFormReturn, useFieldArray } from 'react-hook-form';
import { ChatBoxContext } from '../Provider'; import { ChatBoxContext } from '../Provider';
...@@ -51,7 +56,7 @@ const ChatInput = ({ ...@@ -51,7 +56,7 @@ const ChatInput = ({
chatForm, chatForm,
appId appId
}: { }: {
onSendMessage: (val: ChatBoxInputType & { autoTTSResponse?: boolean }) => void; onSendMessage: SendPromptFnType;
onStop: () => void; onStop: () => void;
TextareaDom: React.MutableRefObject<HTMLTextAreaElement | null>; TextareaDom: React.MutableRefObject<HTMLTextAreaElement | null>;
resetInputVal: (val: ChatBoxInputType) => void; resetInputVal: (val: ChatBoxInputType) => void;
......
...@@ -15,6 +15,8 @@ import { useCopyData } from '@/web/common/hooks/useCopyData'; ...@@ -15,6 +15,8 @@ import { useCopyData } from '@/web/common/hooks/useCopyData';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { SendPromptFnType } from '../type';
const colorMap = { const colorMap = {
[ChatStatusEnum.loading]: { [ChatStatusEnum.loading]: {
bg: 'myGray.100', bg: 'myGray.100',
...@@ -30,6 +32,26 @@ const colorMap = { ...@@ -30,6 +32,26 @@ const colorMap = {
} }
}; };
type BasicProps = {
avatar?: string;
statusBoxData?: {
status: `${ChatStatusEnum}`;
name: string;
};
questionGuides?: string[];
children?: React.ReactNode;
} & ChatControllerProps;
type UserItemType = BasicProps & {
type: ChatRoleEnum.Human;
onSendMessage: undefined;
};
type AiItemType = BasicProps & {
type: ChatRoleEnum.AI;
onSendMessage: SendPromptFnType;
};
type Props = UserItemType | AiItemType;
const ChatItem = ({ const ChatItem = ({
type, type,
avatar, avatar,
...@@ -37,17 +59,9 @@ const ChatItem = ({ ...@@ -37,17 +59,9 @@ const ChatItem = ({
children, children,
isLastChild, isLastChild,
questionGuides = [], questionGuides = [],
onSendMessage,
...chatControllerProps ...chatControllerProps
}: { }: Props) => {
type: ChatRoleEnum.Human | ChatRoleEnum.AI;
avatar?: string;
statusBoxData?: {
status: `${ChatStatusEnum}`;
name: string;
};
questionGuides?: string[];
children?: React.ReactNode;
} & ChatControllerProps) => {
const styleMap: BoxProps = const styleMap: BoxProps =
type === ChatRoleEnum.Human type === ChatRoleEnum.Human
? { ? {
...@@ -96,12 +110,13 @@ const ChatItem = ({ ...@@ -96,12 +110,13 @@ const ChatItem = ({
isLastChild={isLastChild} isLastChild={isLastChild}
isChatting={isChatting} isChatting={isChatting}
questionGuides={questionGuides} questionGuides={questionGuides}
onSendMessage={onSendMessage}
/> />
); );
})} })}
</Flex> </Flex>
); );
}, [chat, isChatting, isLastChild, questionGuides, type]); }, [chat, isChatting, isLastChild, onSendMessage, questionGuides, type]);
const chatStatusMap = useMemo(() => { const chatStatusMap = useMemo(() => {
if (!statusBoxData?.status) return; if (!statusBoxData?.status) return;
......
...@@ -11,7 +11,6 @@ import React, { ...@@ -11,7 +11,6 @@ import React, {
import Script from 'next/script'; import Script from 'next/script';
import type { import type {
AIChatItemValueItemType, AIChatItemValueItemType,
ChatHistoryItemResType,
ChatSiteItemType, ChatSiteItemType,
UserChatItemValueItemType UserChatItemValueItemType
} from '@fastgpt/global/core/chat/type.d'; } from '@fastgpt/global/core/chat/type.d';
...@@ -34,7 +33,12 @@ import type { AdminMarkType } from './components/SelectMarkCollection'; ...@@ -34,7 +33,12 @@ import type { AdminMarkType } from './components/SelectMarkCollection';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { postQuestionGuide } from '@/web/core/ai/api'; import { postQuestionGuide } from '@/web/core/ai/api';
import type { ComponentRef, ChatBoxInputType, ChatBoxInputFormType } from './type.d'; import type {
ComponentRef,
ChatBoxInputType,
ChatBoxInputFormType,
SendPromptFnType
} from './type.d';
import type { StartChatFnProps, generatingMessageProps } from '../type'; import type { StartChatFnProps, generatingMessageProps } from '../type';
import ChatInput from './Input/ChatInput'; import ChatInput from './Input/ChatInput';
import ChatBoxDivider from '../../Divider'; import ChatBoxDivider from '../../Divider';
...@@ -151,6 +155,16 @@ const ChatBox = ( ...@@ -151,6 +155,16 @@ const ChatBox = (
isChatting isChatting
} = useContextSelector(ChatBoxContext, (v) => v); } = useContextSelector(ChatBoxContext, (v) => v);
const isInteractive = useMemo(() => {
const lastAIHistory = chatHistories[chatHistories.length - 1];
if (!lastAIHistory) return false;
const lastAIMessage = lastAIHistory.value as AIChatItemValueItemType[];
const interactiveContent = lastAIMessage?.find(
(item) => item.type === ChatItemValueTypeEnum.interactive
)?.interactive?.params;
return !!interactiveContent;
}, [chatHistories]);
// compute variable input is finish. // compute variable input is finish.
const chatForm = useForm<ChatBoxInputFormType>({ const chatForm = useForm<ChatBoxInputFormType>({
defaultValues: { defaultValues: {
...@@ -201,6 +215,7 @@ const ChatBox = ( ...@@ -201,6 +215,7 @@ const ChatBox = (
status, status,
name, name,
tool, tool,
interactive,
autoTTSResponse, autoTTSResponse,
variables variables
}: generatingMessageProps & { autoTTSResponse?: boolean }) => { }: generatingMessageProps & { autoTTSResponse?: boolean }) => {
...@@ -287,6 +302,16 @@ const ChatBox = ( ...@@ -287,6 +302,16 @@ const ChatBox = (
}; };
} else if (event === SseResponseEventEnum.updateVariables && variables) { } else if (event === SseResponseEventEnum.updateVariables && variables) {
variablesForm.reset(variables); variablesForm.reset(variables);
} else if (event === SseResponseEventEnum.interactive) {
const val: AIChatItemValueItemType = {
type: ChatItemValueTypeEnum.interactive,
interactive
};
return {
...item,
value: item.value.concat(val)
};
} }
return item; return item;
...@@ -355,16 +380,8 @@ const ChatBox = ( ...@@ -355,16 +380,8 @@ const ChatBox = (
/** /**
* user confirm send prompt * user confirm send prompt
*/ */
const sendPrompt = useCallback( const sendPrompt: SendPromptFnType = useCallback(
({ ({ text = '', files = [], history = chatHistories, autoTTSResponse = false }) => {
text = '',
files = [],
history = chatHistories,
autoTTSResponse = false
}: ChatBoxInputType & {
autoTTSResponse?: boolean;
history?: ChatSiteItemType[];
}) => {
variablesForm.handleSubmit( variablesForm.handleSubmit(
async (variables) => { async (variables) => {
if (!onStartChat) return; if (!onStartChat) return;
...@@ -898,6 +915,7 @@ const ChatBox = ( ...@@ -898,6 +915,7 @@ const ChatBox = (
onRetry={retryInput(item.dataId)} onRetry={retryInput(item.dataId)}
onDelete={delOneMessage(item.dataId)} onDelete={delOneMessage(item.dataId)}
isLastChild={index === chatHistories.length - 1} isLastChild={index === chatHistories.length - 1}
onSendMessage={undefined}
/> />
)} )}
{item.obj === ChatRoleEnum.AI && ( {item.obj === ChatRoleEnum.AI && (
...@@ -907,7 +925,8 @@ const ChatBox = ( ...@@ -907,7 +925,8 @@ const ChatBox = (
avatar={appAvatar} avatar={appAvatar}
chat={item} chat={item}
isLastChild={index === chatHistories.length - 1} isLastChild={index === chatHistories.length - 1}
{...(item.obj === ChatRoleEnum.AI && { onSendMessage={sendPrompt}
{...{
showVoiceIcon, showVoiceIcon,
shareId, shareId,
outLinkUid, outLinkUid,
...@@ -923,7 +942,7 @@ const ChatBox = ( ...@@ -923,7 +942,7 @@ const ChatBox = (
onCloseUserLike: onCloseUserLike(item), onCloseUserLike: onCloseUserLike(item),
onAddUserDislike: onAddUserDislike(item), onAddUserDislike: onAddUserDislike(item),
onReadUserDislike: onReadUserDislike(item) onReadUserDislike: onReadUserDislike(item)
})} }}
> >
<ResponseTags <ResponseTags
showTags={index !== chatHistories.length - 1 || !isChatting} showTags={index !== chatHistories.length - 1 || !isChatting}
...@@ -973,7 +992,7 @@ const ChatBox = ( ...@@ -973,7 +992,7 @@ const ChatBox = (
</Box> </Box>
</Box> </Box>
{/* message input */} {/* message input */}
{onStartChat && chatStarted && active && appId && ( {onStartChat && chatStarted && active && appId && !isInteractive && (
<ChatInput <ChatInput
onSendMessage={sendPrompt} onSendMessage={sendPrompt}
onStop={() => chatController.current?.abort('stop')} onStop={() => chatController.current?.abort('stop')}
......
...@@ -29,6 +29,16 @@ export type ChatBoxInputType = { ...@@ -29,6 +29,16 @@ export type ChatBoxInputType = {
files?: UserInputFileItemType[]; files?: UserInputFileItemType[];
}; };
export type SendPromptFnType = ({
text,
files,
history,
autoTTSResponse
}: ChatBoxInputType & {
autoTTSResponse?: boolean;
history?: ChatSiteItemType[];
}) => void;
export type ComponentRef = { export type ComponentRef = {
restartChat: () => void; restartChat: () => void;
scrollToBottom: (behavior?: 'smooth' | 'auto') => void; scrollToBottom: (behavior?: 'smooth' | 'auto') => void;
......
import { ChatItemValueItemType } from '@fastgpt/global/core/chat/type'; import { ChatItemValueItemType, ChatSiteItemType } from '@fastgpt/global/core/chat/type';
import { ChatBoxInputType, UserInputFileItemType } from './type'; import { ChatBoxInputType, UserInputFileItemType } from './type';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { getFileIcon } from '@fastgpt/global/common/file/icon'; import { getFileIcon } from '@fastgpt/global/common/file/icon';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
export const formatChatValue2InputType = (value?: ChatItemValueItemType[]): ChatBoxInputType => { export const formatChatValue2InputType = (value?: ChatItemValueItemType[]): ChatBoxInputType => {
if (!value) { if (!value) {
...@@ -37,3 +37,37 @@ export const formatChatValue2InputType = (value?: ChatItemValueItemType[]): Chat ...@@ -37,3 +37,37 @@ export const formatChatValue2InputType = (value?: ChatItemValueItemType[]): Chat
files files
}; };
}; };
export const setUserSelectResultToHistories = (
histories: ChatSiteItemType[],
selectVal: string
): ChatSiteItemType[] => {
if (histories.length === 0) return histories;
// @ts-ignore
return histories.map((item, i) => {
if (i !== histories.length - 1) return item;
item.value;
const value = item.value.map((val) => {
if (val.type !== ChatItemValueTypeEnum.interactive || !val.interactive) return val;
return {
...val,
interactive: {
...val.interactive,
params: {
...val.interactive.params,
userSelectedVal: val.interactive.params.userSelectOptions.find(
(item) => item.value === selectVal
)?.value
}
}
};
});
return {
...item,
value
};
});
};
import { StreamResponseType } from '@/web/common/api/fetch'; import { StreamResponseType } from '@/web/common/api/fetch';
import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type'; import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
import { ChatSiteItemType, ToolModuleResponseItemType } from '@fastgpt/global/core/chat/type'; import { ChatSiteItemType, ToolModuleResponseItemType } from '@fastgpt/global/core/chat/type';
import { InteractiveNodeResponseItemType } from '@fastgpt/global/core/workflow/template/system/userSelect/type';
export type generatingMessageProps = { export type generatingMessageProps = {
event: SseResponseEventEnum; event: SseResponseEventEnum;
...@@ -8,6 +9,7 @@ export type generatingMessageProps = { ...@@ -8,6 +9,7 @@ export type generatingMessageProps = {
name?: string; name?: string;
status?: 'running' | 'finish'; status?: 'running' | 'finish';
tool?: ToolModuleResponseItemType; tool?: ToolModuleResponseItemType;
interactive?: InteractiveNodeResponseItemType;
variables?: Record<string, any>; variables?: Record<string, any>;
}; };
......
...@@ -6,7 +6,9 @@ import { ...@@ -6,7 +6,9 @@ import {
AccordionIcon, AccordionIcon,
AccordionItem, AccordionItem,
AccordionPanel, AccordionPanel,
Box Box,
Button,
Flex
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { import {
...@@ -17,6 +19,10 @@ import { ...@@ -17,6 +19,10 @@ import {
import React from 'react'; import React from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
import { SendPromptFnType } from '../ChatContainer/ChatBox/type';
import { useContextSelector } from 'use-context-selector';
import { ChatBoxContext } from '../ChatContainer/ChatBox/Provider';
import { setUserSelectResultToHistories } from '../ChatContainer/ChatBox/utils';
type props = { type props = {
value: UserChatItemValueItemType | AIChatItemValueItemType; value: UserChatItemValueItemType | AIChatItemValueItemType;
...@@ -25,10 +31,21 @@ type props = { ...@@ -25,10 +31,21 @@ type props = {
isLastChild: boolean; isLastChild: boolean;
isChatting: boolean; isChatting: boolean;
questionGuides: string[]; questionGuides: string[];
onSendMessage?: SendPromptFnType;
}; };
const AIResponseBox = ({ value, index, chat, isLastChild, isChatting, questionGuides }: props) => { const AIResponseBox = ({
if (value.text) { value,
index,
chat,
isLastChild,
isChatting,
questionGuides,
onSendMessage
}: props) => {
const chatHistories = useContextSelector(ChatBoxContext, (v) => v.chatHistories);
if (value.type === ChatItemValueTypeEnum.text && value.text) {
let source = (value.text?.content || '').trim(); let source = (value.text?.content || '').trim();
// First empty line // First empty line
...@@ -126,6 +143,45 @@ ${toolResponse}`} ...@@ -126,6 +143,45 @@ ${toolResponse}`}
</Box> </Box>
); );
} }
if (
value.type === ChatItemValueTypeEnum.interactive &&
value.interactive &&
value.interactive.type === 'userSelect'
) {
return (
<Flex flexDirection={'column'} gap={2} minW={'200px'} maxW={'250px'}>
{value.interactive.params.userSelectOptions?.map((option) => {
const selected = option.value === value.interactive?.params?.userSelectedVal;
return (
<Button
key={option.key}
variant={'whitePrimary'}
isDisabled={!isLastChild && value.interactive?.params?.userSelectedVal !== undefined}
{...(selected
? {
_disabled: {
cursor: 'default',
borderColor: 'primary.300',
bg: 'primary.50 !important',
color: 'primary.600'
}
}
: {})}
onClick={() => {
onSendMessage?.({
text: option.value,
history: setUserSelectResultToHistories(chatHistories, option.value)
});
}}
>
{option.value}
</Button>
);
})}
</Flex>
);
}
return null; return null;
}; };
......
...@@ -17,6 +17,7 @@ import { useContextSelector } from 'use-context-selector'; ...@@ -17,6 +17,7 @@ import { useContextSelector } from 'use-context-selector';
import { ChatBoxContext } from '../ChatContainer/ChatBox/Provider'; import { ChatBoxContext } from '../ChatContainer/ChatBox/Provider';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { getFileIcon } from '@fastgpt/global/common/file/icon'; import { getFileIcon } from '@fastgpt/global/common/file/icon';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
type sideTabItemType = { type sideTabItemType = {
moduleLogo?: string; moduleLogo?: string;
...@@ -124,7 +125,11 @@ const WholeResponseModal = ({ ...@@ -124,7 +125,11 @@ const WholeResponseModal = ({
</Flex> </Flex>
} }
> >
{response?.length && <ResponseBox response={response} showDetail={showDetail} />} {!!response?.length ? (
<ResponseBox response={response} showDetail={showDetail} />
) : (
<EmptyTip text={t('chat:no_workflow_response')} />
)}
</MyModal> </MyModal>
); );
}; };
...@@ -480,6 +485,12 @@ export const WholeResponseContent = ({ ...@@ -480,6 +485,12 @@ export const WholeResponseContent = ({
value={activeModule?.readFilesResult} value={activeModule?.readFilesResult}
/> />
</> </>
{/* user select */}
<Row
label={t('common:core.chat.response.user_select_result')}
value={activeModule?.userSelectResult}
/>
</Box> </Box>
)} )}
</> </>
......
...@@ -9,8 +9,7 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth'; ...@@ -9,8 +9,7 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch'; import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team'; import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'; import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
import { removeEmptyUserInput } from '@fastgpt/global/core/chat/utils'; import { removeEmptyUserInput } from '@fastgpt/global/core/chat/utils';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
...@@ -22,11 +21,18 @@ import { NextAPI } from '@/service/middleware/entry'; ...@@ -22,11 +21,18 @@ import { NextAPI } from '@/service/middleware/entry';
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt'; import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type'; import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
import { AppChatConfigType } from '@fastgpt/global/core/app/type'; import { AppChatConfigType } from '@fastgpt/global/core/app/type';
import {
getWorkflowEntryNodeIds,
initWorkflowEdgeStatus,
rewriteNodeOutputByHistories,
storeNodes2RuntimeNodes
} from '@fastgpt/global/core/workflow/runtime/utils';
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
export type Props = { export type Props = {
messages: ChatCompletionMessageParam[]; messages: ChatCompletionMessageParam[];
nodes: RuntimeNodeItemType[]; nodes: StoreNodeItemType[];
edges: RuntimeEdgeItemType[]; edges: StoreEdgeItemType[];
variables: Record<string, any>; variables: Record<string, any>;
appId: string; appId: string;
appName: string; appName: string;
...@@ -52,8 +58,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ...@@ -52,8 +58,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
chatConfig chatConfig
} = req.body as Props; } = req.body as Props;
try { try {
// [histories, user]
const chatMessages = GPTMessages2Chats(messages); const chatMessages = GPTMessages2Chats(messages);
const userInput = chatMessages.pop()?.value as UserChatItemValueItemType[] | undefined; const userInput = chatMessages.pop()?.value as UserChatItemValueItemType[] | undefined;
/* user auth */ /* user auth */
...@@ -64,6 +70,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ...@@ -64,6 +70,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
authToken: true authToken: true
}) })
]); ]);
// auth balance
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
const isPlugin = app.type === AppTypeEnum.plugin; const isPlugin = app.type === AppTypeEnum.plugin;
if (!Array.isArray(nodes)) { if (!Array.isArray(nodes)) {
...@@ -73,18 +82,19 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ...@@ -73,18 +82,19 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
throw new Error('Edges is not array'); throw new Error('Edges is not array');
} }
let runtimeNodes = storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes, chatMessages));
// Plugin need to replace inputs // Plugin need to replace inputs
if (isPlugin) { if (isPlugin) {
nodes = updatePluginInputByVariables(nodes, variables); runtimeNodes = updatePluginInputByVariables(runtimeNodes, variables);
variables = removePluginInputVariables(variables, nodes); variables = removePluginInputVariables(variables, runtimeNodes);
} else { } else {
if (!userInput) { if (!userInput) {
throw new Error('Params Error'); throw new Error('Params Error');
} }
} }
// auth balance runtimeNodes = rewriteNodeOutputByHistories(chatMessages, runtimeNodes);
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
/* start process */ /* start process */
const { flowResponses, flowUsages } = await dispatchWorkFlow({ const { flowResponses, flowUsages } = await dispatchWorkFlow({
...@@ -95,8 +105,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ...@@ -95,8 +105,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
tmbId, tmbId,
user, user,
app, app,
runtimeNodes: nodes, runtimeNodes,
runtimeEdges: edges, runtimeEdges: initWorkflowEdgeStatus(edges, chatMessages),
variables, variables,
query: removeEmptyUserInput(userInput), query: removeEmptyUserInput(userInput),
chatConfig, chatConfig,
......
...@@ -13,7 +13,7 @@ import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch'; ...@@ -13,7 +13,7 @@ import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
import type { ChatCompletionCreateParams } from '@fastgpt/global/core/ai/type.d'; import type { ChatCompletionCreateParams } from '@fastgpt/global/core/ai/type.d';
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d'; import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
import { import {
getDefaultEntryNodeIds, getWorkflowEntryNodeIds,
getMaxHistoryLimitFromNodes, getMaxHistoryLimitFromNodes,
initWorkflowEdgeStatus, initWorkflowEdgeStatus,
storeNodes2RuntimeNodes, storeNodes2RuntimeNodes,
...@@ -64,6 +64,7 @@ import { ...@@ -64,6 +64,7 @@ import {
getPluginRunContent getPluginRunContent
} from '@fastgpt/global/core/app/plugin/utils'; } from '@fastgpt/global/core/app/plugin/utils';
import { getSystemTime } from '@fastgpt/global/common/time/timezone'; import { getSystemTime } from '@fastgpt/global/common/time/timezone';
import { rewriteNodeOutputByHistories } from '@fastgpt/global/core/workflow/runtime/utils';
type FastGptWebChatProps = { type FastGptWebChatProps = {
chatId?: string; // undefined: get histories from messages, '': new chat, 'xxxxx': get histories from db chatId?: string; // undefined: get histories from messages, '': new chat, 'xxxxx': get histories from db
...@@ -225,24 +226,22 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ...@@ -225,24 +226,22 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
appId: app._id, appId: app._id,
chatId, chatId,
limit, limit,
field: `dataId obj value` field: `dataId obj value nodeOutputs`
}), }),
getAppLatestVersion(app._id, app) getAppLatestVersion(app._id, app)
]); ]);
const newHistories = concatHistories(histories, chatMessages); const newHistories = concatHistories(histories, chatMessages);
// Get runtimeNodes // Get runtimeNodes
const runtimeNodes = isPlugin let runtimeNodes = storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes, newHistories));
? updatePluginInputByVariables(
storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes)), if (isPlugin) {
variables // Rewrite plugin run params variables
) variables = removePluginInputVariables(variables, runtimeNodes);
: storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes)); runtimeNodes = updatePluginInputByVariables(runtimeNodes, variables);
}
const runtimeVariables = removePluginInputVariables(
variables, runtimeNodes = rewriteNodeOutputByHistories(newHistories, runtimeNodes);
storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes))
);
/* start flow controller */ /* start flow controller */
const { flowResponses, flowUsages, assistantResponses, newVariables } = await (async () => { const { flowResponses, flowUsages, assistantResponses, newVariables } = await (async () => {
...@@ -258,8 +257,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ...@@ -258,8 +257,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
chatId, chatId,
responseChatItemId, responseChatItemId,
runtimeNodes, runtimeNodes,
runtimeEdges: initWorkflowEdgeStatus(edges), runtimeEdges: initWorkflowEdgeStatus(edges, newHistories),
variables: runtimeVariables, variables,
query: removeEmptyUserInput(userQuestion.value), query: removeEmptyUserInput(userQuestion.value),
chatConfig, chatConfig,
histories: newHistories, histories: newHistories,
......
...@@ -145,7 +145,7 @@ const Header = () => { ...@@ -145,7 +145,7 @@ const Header = () => {
} }
}} }}
> >
{t('common:core.workflow.Debug')} {t('common:core.workflow.run_test')}
</Button> </Button>
{!historiesDefaultData && ( {!historiesDefaultData && (
......
...@@ -146,7 +146,7 @@ const Header = () => { ...@@ -146,7 +146,7 @@ const Header = () => {
} }
}} }}
> >
{t('common:core.workflow.Debug')} {t('common:core.workflow.run_test')}
</Button> </Button>
{!historiesDefaultData && ( {!historiesDefaultData && (
......
...@@ -56,7 +56,8 @@ const nodeTypes: Record<FlowNodeTypeEnum, any> = { ...@@ -56,7 +56,8 @@ const nodeTypes: Record<FlowNodeTypeEnum, any> = {
[FlowNodeTypeEnum.lafModule]: dynamic(() => import('./nodes/NodeLaf')), [FlowNodeTypeEnum.lafModule]: dynamic(() => import('./nodes/NodeLaf')),
[FlowNodeTypeEnum.ifElseNode]: dynamic(() => import('./nodes/NodeIfElse')), [FlowNodeTypeEnum.ifElseNode]: dynamic(() => import('./nodes/NodeIfElse')),
[FlowNodeTypeEnum.variableUpdate]: dynamic(() => import('./nodes/NodeVariableUpdate')), [FlowNodeTypeEnum.variableUpdate]: dynamic(() => import('./nodes/NodeVariableUpdate')),
[FlowNodeTypeEnum.code]: dynamic(() => import('./nodes/NodeCode')) [FlowNodeTypeEnum.code]: dynamic(() => import('./nodes/NodeCode')),
[FlowNodeTypeEnum.userSelect]: dynamic(() => import('./nodes/NodeUserSelect'))
}; };
const edgeTypes = { const edgeTypes = {
[EDGE_TYPE]: ButtonEdge [EDGE_TYPE]: ButtonEdge
......
import React, { useMemo } from 'react';
import { NodeProps, Position } from 'reactflow';
import { Box, Button, HStack, Input } from '@chakra-ui/react';
import NodeCard from './render/NodeCard';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
import Container from '../components/Container';
import RenderInput from './render/RenderInput';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { useTranslation } from 'next-i18next';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io.d';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { SourceHandle } from './render/Handle';
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../context';
import { UserSelectOptionItemType } from '@fastgpt/global/core/workflow/template/system/userSelect/type';
import IOTitle from '../components/IOTitle';
import RenderOutput from './render/RenderOutput';
const NodeUserSelect = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation();
const { nodeId, inputs, outputs } = data;
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const CustomComponent = useMemo(
() => ({
[NodeInputKeyEnum.userSelectOptions]: ({
key: optionKey,
value = [],
...props
}: FlowNodeInputItemType) => {
const options = value as UserSelectOptionItemType[];
return (
<Box>
{options.map((item, i) => (
<Box key={item.key} mb={4}>
<HStack spacing={1}>
<MyTooltip label={t('common:common.Delete')}>
<MyIcon
mt={0.5}
name={'minus'}
w={'0.8rem'}
cursor={'pointer'}
color={'myGray.600'}
_hover={{ color: 'red.600' }}
onClick={() => {
onChangeNode({
nodeId,
type: 'updateInput',
key: optionKey,
value: {
...props,
key: optionKey,
value: options.filter((input) => input.key !== item.key)
}
});
onChangeNode({
nodeId,
type: 'delOutput',
key: item.key
});
}}
/>
</MyTooltip>
<Box color={'myGray.600'} fontWeight={'medium'} fontSize={'sm'}>
{t('common:option') + (i + 1)}
</Box>
</HStack>
<Box position={'relative'}>
<Input
mt={1}
defaultValue={item.value}
bg={'white'}
fontSize={'sm'}
onChange={(e) => {
const newVal = options.map((val) =>
val.key === item.key
? {
...val,
value: e.target.value
}
: val
);
onChangeNode({
nodeId,
type: 'updateInput',
key: optionKey,
value: {
...props,
key: optionKey,
value: newVal
}
});
}}
/>
<SourceHandle
nodeId={nodeId}
handleId={getHandleId(nodeId, 'source', item.key)}
position={Position.Right}
translate={[26, 0]}
/>
</Box>
</Box>
))}
<Button
fontSize={'sm'}
leftIcon={<MyIcon name={'common/addLight'} w={4} />}
onClick={() => {
onChangeNode({
nodeId,
type: 'updateInput',
key: optionKey,
value: {
...props,
key: optionKey,
value: options.concat({ value: '', key: getNanoid() })
}
});
}}
>
{t('common:core.module.Add_option')}
</Button>
</Box>
);
}
}),
[nodeId, onChangeNode, t]
);
return (
<NodeCard minW={'400px'} selected={selected} {...data}>
<Container>
<RenderInput nodeId={nodeId} flowInputList={inputs} CustomComponent={CustomComponent} />
</Container>
<Container>
<IOTitle text={t('common:common.Output')} />
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
</Container>
</NodeCard>
);
};
export default React.memo(NodeUserSelect);
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Box, Button, Card, Flex } from '@chakra-ui/react'; import { Box, Button, Card, Flex, Image } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
import type { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d'; import type { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
...@@ -42,7 +42,6 @@ type Props = FlowNodeItemType & { ...@@ -42,7 +42,6 @@ type Props = FlowNodeItemType & {
const NodeCard = (props: Props) => { const NodeCard = (props: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { appT } = useI18n();
const { toast } = useToast(); const { toast } = useToast();
...@@ -70,7 +69,7 @@ const NodeCard = (props: Props) => { ...@@ -70,7 +69,7 @@ const NodeCard = (props: Props) => {
// custom title edit // custom title edit
const { onOpenModal: onOpenCustomTitleModal, EditModal: EditTitleModal } = useEditTitle({ const { onOpenModal: onOpenCustomTitleModal, EditModal: EditTitleModal } = useEditTitle({
title: t('common:common.Custom Title'), title: t('common:common.Custom Title'),
placeholder: appT('module.Custom Title Tip') || '' placeholder: t('app:module.Custom Title Tip') || ''
}); });
const showToolHandle = useMemo( const showToolHandle = useMemo(
...@@ -166,7 +165,7 @@ const NodeCard = (props: Props) => { ...@@ -166,7 +165,7 @@ const NodeCard = (props: Props) => {
onSuccess: (e) => { onSuccess: (e) => {
if (!e) { if (!e) {
return toast({ return toast({
title: appT('modules.Title is required'), title: t('app:modules.Title is required'),
status: 'warning' status: 'warning'
}); });
} }
...@@ -183,7 +182,7 @@ const NodeCard = (props: Props) => { ...@@ -183,7 +182,7 @@ const NodeCard = (props: Props) => {
)} )}
<Box flex={1} /> <Box flex={1} />
{hasNewVersion && ( {hasNewVersion && (
<MyTooltip label={appT('app.modules.click to update')}> <MyTooltip label={t('app:app.modules.click to update')}>
<Button <Button
bg={'yellow.50'} bg={'yellow.50'}
color={'yellow.600'} color={'yellow.600'}
...@@ -197,11 +196,29 @@ const NodeCard = (props: Props) => { ...@@ -197,11 +196,29 @@ const NodeCard = (props: Props) => {
_hover={{ bg: 'yellow.100' }} _hover={{ bg: 'yellow.100' }}
onClick={onOpenConfirmSync(onClickSyncVersion)} onClick={onOpenConfirmSync(onClickSyncVersion)}
> >
<Box>{appT('app.modules.has new version')}</Box> <Box>{t('app:app.modules.has new version')}</Box>
<QuestionOutlineIcon ml={1} /> <QuestionOutlineIcon ml={1} />
</Button> </Button>
</MyTooltip> </MyTooltip>
)} )}
{!!nodeTemplate?.diagram && (
<MyTooltip
label={
<Image src={nodeTemplate?.diagram} w={'100%'} minH={['auto', '200px']} alt={''} />
}
>
<Box
fontSize={'sm'}
color={'primary.700'}
p={1}
rounded={'sm'}
cursor={'default'}
_hover={{ bg: 'rgba(17, 24, 36, 0.05)' }}
>
{t('common:core.module.Diagram')}
</Box>
</MyTooltip>
)}
</Flex> </Flex>
<MenuRender nodeId={nodeId} menuForbid={menuForbid} /> <MenuRender nodeId={nodeId} menuForbid={menuForbid} />
<NodeIntro nodeId={nodeId} intro={intro} /> <NodeIntro nodeId={nodeId} intro={intro} />
...@@ -217,9 +234,9 @@ const NodeCard = (props: Props) => { ...@@ -217,9 +234,9 @@ const NodeCard = (props: Props) => {
name, name,
menuForbid, menuForbid,
hasNewVersion, hasNewVersion,
appT,
onOpenConfirmSync, onOpenConfirmSync,
onClickSyncVersion, onClickSyncVersion,
nodeTemplate?.diagram,
intro, intro,
ConfirmSyncModal, ConfirmSyncModal,
onOpenCustomTitleModal, onOpenCustomTitleModal,
......
...@@ -621,7 +621,6 @@ const WorkflowContextProvider = ({ ...@@ -621,7 +621,6 @@ const WorkflowContextProvider = ({
}, },
appId appId
}); });
// console.log({ finishedEdges, finishedNodes, nextStepRunNodes, flowResponses });
// 5. Store debug result // 5. Store debug result
const newStoreDebugData = { const newStoreDebugData = {
runtimeNodes: finishedNodes, runtimeNodes: finishedNodes,
......
...@@ -2,13 +2,7 @@ import { useUserStore } from '@/web/support/user/useUserStore'; ...@@ -2,13 +2,7 @@ import { useUserStore } from '@/web/support/user/useUserStore';
import React from 'react'; import React from 'react';
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type'; import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
import { streamFetch } from '@/web/common/api/fetch'; import { streamFetch } from '@/web/common/api/fetch';
import { checkChatSupportSelectFileByModules } from '@/web/core/chat/utils'; import { getMaxHistoryLimitFromNodes } from '@fastgpt/global/core/workflow/runtime/utils';
import {
getDefaultEntryNodeIds,
getMaxHistoryLimitFromNodes,
initWorkflowEdgeStatus,
storeNodes2RuntimeNodes
} from '@fastgpt/global/core/workflow/runtime/utils';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { AppContext } from './context'; import { AppContext } from './context';
...@@ -47,8 +41,8 @@ export const useChatTest = ({ ...@@ -47,8 +41,8 @@ export const useChatTest = ({
data: { data: {
// Send histories and user messages // Send histories and user messages
messages: messages.slice(-historyMaxLen - 2), messages: messages.slice(-historyMaxLen - 2),
nodes: storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes)), nodes,
edges: initWorkflowEdgeStatus(edges), edges,
variables, variables,
appId: appDetail._id, appId: appDetail._id,
appName: `调试-${appDetail.name}`, appName: `调试-${appDetail.name}`,
......
...@@ -6,7 +6,7 @@ import { getNanoid } from '@fastgpt/global/common/string/tools'; ...@@ -6,7 +6,7 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
import { delay } from '@fastgpt/global/common/system/utils'; import { delay } from '@fastgpt/global/common/system/utils';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { import {
getDefaultEntryNodeIds, getWorkflowEntryNodeIds,
initWorkflowEdgeStatus, initWorkflowEdgeStatus,
storeNodes2RuntimeNodes storeNodes2RuntimeNodes
} from '@fastgpt/global/core/workflow/runtime/utils'; } from '@fastgpt/global/core/workflow/runtime/utils';
...@@ -38,7 +38,7 @@ export const getScheduleTriggerApp = async () => { ...@@ -38,7 +38,7 @@ export const getScheduleTriggerApp = async () => {
teamId: String(app.teamId), teamId: String(app.teamId),
tmbId: String(app.tmbId), tmbId: String(app.tmbId),
app, app,
runtimeNodes: storeNodes2RuntimeNodes(app.modules, getDefaultEntryNodeIds(app.modules)), runtimeNodes: storeNodes2RuntimeNodes(app.modules, getWorkflowEntryNodeIds(app.modules)),
runtimeEdges: initWorkflowEdgeStatus(app.edges), runtimeEdges: initWorkflowEdgeStatus(app.edges),
variables: {}, variables: {},
query: [ query: [
......
...@@ -201,6 +201,11 @@ export const streamFetch = ({ ...@@ -201,6 +201,11 @@ export const streamFetch = ({
event, event,
variables: parseJson variables: parseJson
}); });
} else if (event === SseResponseEventEnum.interactive) {
responseQueue.push({
event,
...parseJson
});
} else if (event === SseResponseEventEnum.error) { } else if (event === SseResponseEventEnum.error) {
if (parseJson.statusText === TeamErrEnum.aiPointsNotEnough) { if (parseJson.statusText === TeamErrEnum.aiPointsNotEnough) {
useSystemStore.getState().setIsNotSufficientModal(true); useSystemStore.getState().setIsNotSufficientModal(true);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment