mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-25 20:02:47 +00:00
perf: helperbot ui
This commit is contained in:
parent
1f100276f6
commit
363f9b888a
|
|
@ -10,11 +10,9 @@ import type { HelperBotChatItemType } from './type';
|
||||||
import { simpleUserContentPart } from '../adapt';
|
import { simpleUserContentPart } from '../adapt';
|
||||||
|
|
||||||
export const helperChats2GPTMessages = ({
|
export const helperChats2GPTMessages = ({
|
||||||
messages,
|
messages
|
||||||
reserveTool = false
|
|
||||||
}: {
|
}: {
|
||||||
messages: HelperBotChatItemType[];
|
messages: HelperBotChatItemType[];
|
||||||
reserveTool?: boolean;
|
|
||||||
}): ChatCompletionMessageParam[] => {
|
}): ChatCompletionMessageParam[] => {
|
||||||
let results: ChatCompletionMessageParam[] = [];
|
let results: ChatCompletionMessageParam[] = [];
|
||||||
|
|
||||||
|
|
@ -66,29 +64,25 @@ export const helperChats2GPTMessages = ({
|
||||||
|
|
||||||
//AI: 只需要把根节点转化即可
|
//AI: 只需要把根节点转化即可
|
||||||
item.value.forEach((value, i) => {
|
item.value.forEach((value, i) => {
|
||||||
if ('tool' in value && reserveTool) {
|
if ('collectionForm' in value) {
|
||||||
const tool_calls: ChatCompletionMessageToolCall[] = [
|
const text = JSON.stringify(
|
||||||
{
|
value.collectionForm.params.inputForm.map((item) => ({
|
||||||
id: value.tool.id,
|
label: item.label,
|
||||||
type: 'function',
|
type: item.type
|
||||||
function: {
|
}))
|
||||||
name: value.tool.functionName,
|
);
|
||||||
arguments: value.tool.params
|
|
||||||
}
|
// Concat text
|
||||||
}
|
const lastValue = item.value[i - 1];
|
||||||
];
|
const lastResult = aiResults[aiResults.length - 1];
|
||||||
const toolResponse: ChatCompletionToolMessageParam[] = [
|
if (lastValue && typeof lastResult?.content === 'string') {
|
||||||
{
|
lastResult.content += text;
|
||||||
tool_call_id: value.tool.id,
|
} else {
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
aiResults.push({
|
||||||
content: value.tool.response
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
}
|
content: text
|
||||||
];
|
});
|
||||||
aiResults.push({
|
}
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
|
||||||
tool_calls
|
|
||||||
});
|
|
||||||
aiResults.push(...toolResponse);
|
|
||||||
} else if ('text' in value && typeof value.text?.content === 'string') {
|
} else if ('text' in value && typeof value.text?.content === 'string') {
|
||||||
if (!value.text.content && item.value.length > 1) {
|
if (!value.text.content && item.value.length > 1) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { ObjectIdSchema } from '../../../common/type/mongo';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { ChatRoleEnum } from '../constants';
|
import { ChatRoleEnum } from '../constants';
|
||||||
import { UserChatItemSchema, SystemChatItemSchema, ToolModuleResponseItemSchema } from '../type';
|
import { UserChatItemSchema, SystemChatItemSchema, ToolModuleResponseItemSchema } from '../type';
|
||||||
|
import { UserInputInteractiveSchema } from '../../workflow/template/system/interactive/type';
|
||||||
|
|
||||||
export enum HelperBotTypeEnum {
|
export enum HelperBotTypeEnum {
|
||||||
topAgent = 'topAgent',
|
topAgent = 'topAgent',
|
||||||
|
|
@ -34,7 +35,7 @@ export const AIChatItemValueItemSchema = z.union([
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
tool: ToolModuleResponseItemSchema
|
collectionForm: UserInputInteractiveSchema
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
export type AIChatItemValueItemType = z.infer<typeof AIChatItemValueItemSchema>;
|
export type AIChatItemValueItemType = z.infer<typeof AIChatItemValueItemSchema>;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@ export enum SseResponseEventEnum {
|
||||||
|
|
||||||
agentPlan = 'agentPlan', // agent plan
|
agentPlan = 'agentPlan', // agent plan
|
||||||
|
|
||||||
formData = 'formData', // form data for TopAgent
|
// Helperbot
|
||||||
|
collectionForm = 'collectionForm', // collection form for HelperBot
|
||||||
|
topAgentConfig = 'topAgentConfig', // form data for TopAgent
|
||||||
generatedSkill = 'generatedSkill' // generated skill for SkillAgent
|
generatedSkill = 'generatedSkill' // generated skill for SkillAgent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
import type { HelperBotDispatchParamsType, HelperBotDispatchResponseType } from '../type';
|
import {
|
||||||
|
AICollectionAnswerSchema,
|
||||||
|
type HelperBotDispatchParamsType,
|
||||||
|
type HelperBotDispatchResponseType
|
||||||
|
} from '../type';
|
||||||
import { helperChats2GPTMessages } from '@fastgpt/global/core/chat/helperBot/adaptor';
|
import { helperChats2GPTMessages } from '@fastgpt/global/core/chat/helperBot/adaptor';
|
||||||
import { getPrompt } from './prompt';
|
import { getPrompt } from './prompt';
|
||||||
import { createLLMResponse } from '../../../../ai/llm/request';
|
import { createLLMResponse } from '../../../../ai/llm/request';
|
||||||
|
|
@ -6,10 +10,17 @@ import { getLLMModel } from '../../../../ai/model';
|
||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { generateResourceList } from './utils';
|
import { generateResourceList } from './utils';
|
||||||
import { TopAgentFormDataSchema } from './type';
|
import { TopAgentAnswerSchema, TopAgentFormDataSchema } from './type';
|
||||||
import { addLog } from '../../../../../common/system/log';
|
import { addLog } from '../../../../../common/system/log';
|
||||||
import { formatAIResponse } from '../utils';
|
import { formatAIResponse } from '../utils';
|
||||||
import type { TopAgentParamsType } from '@fastgpt/global/core/chat/helperBot/topAgent/type';
|
import type { TopAgentParamsType } from '@fastgpt/global/core/chat/helperBot/topAgent/type';
|
||||||
|
import type {
|
||||||
|
UserInputFormItemType,
|
||||||
|
UserInputInteractive
|
||||||
|
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
|
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
|
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
|
|
||||||
export const dispatchTopAgent = async (
|
export const dispatchTopAgent = async (
|
||||||
props: HelperBotDispatchParamsType<TopAgentParamsType>
|
props: HelperBotDispatchParamsType<TopAgentParamsType>
|
||||||
|
|
@ -37,27 +48,20 @@ export const dispatchTopAgent = async (
|
||||||
});
|
});
|
||||||
|
|
||||||
const historyMessages = helperChats2GPTMessages({
|
const historyMessages = helperChats2GPTMessages({
|
||||||
messages: histories,
|
messages: histories
|
||||||
reserveTool: false
|
|
||||||
});
|
});
|
||||||
const conversationMessages = [
|
const conversationMessages = [
|
||||||
{ role: 'system' as const, content: systemPrompt },
|
{ role: 'system' as const, content: systemPrompt },
|
||||||
...historyMessages,
|
...historyMessages,
|
||||||
{ role: 'user' as const, content: query }
|
{ role: 'user' as const, content: query }
|
||||||
];
|
];
|
||||||
|
console.log(JSON.stringify(conversationMessages, null, 2));
|
||||||
const llmResponse = await createLLMResponse({
|
const llmResponse = await createLLMResponse({
|
||||||
body: {
|
body: {
|
||||||
messages: conversationMessages,
|
messages: conversationMessages,
|
||||||
model: modelData,
|
model: modelData,
|
||||||
stream: true
|
stream: true
|
||||||
},
|
},
|
||||||
onStreaming: ({ text }) => {
|
|
||||||
workflowResponseWrite?.({
|
|
||||||
event: SseResponseEventEnum.answer,
|
|
||||||
data: textAdaptGptResponse({ text })
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onReasoning: ({ text }) => {
|
onReasoning: ({ text }) => {
|
||||||
workflowResponseWrite?.({
|
workflowResponseWrite?.({
|
||||||
event: SseResponseEventEnum.answer,
|
event: SseResponseEventEnum.answer,
|
||||||
|
|
@ -71,9 +75,9 @@ export const dispatchTopAgent = async (
|
||||||
|
|
||||||
const answerText = llmResponse.answerText;
|
const answerText = llmResponse.answerText;
|
||||||
const reasoningText = llmResponse.reasoningText;
|
const reasoningText = llmResponse.reasoningText;
|
||||||
|
console.log('Top agent response:', answerText);
|
||||||
try {
|
try {
|
||||||
const responseJson = JSON.parse(answerText);
|
const responseJson = TopAgentAnswerSchema.parse(JSON.parse(answerText));
|
||||||
|
|
||||||
if (responseJson.phase === 'generation') {
|
if (responseJson.phase === 'generation') {
|
||||||
addLog.debug('🔄 TopAgent: Configuration generation phase');
|
addLog.debug('🔄 TopAgent: Configuration generation phase');
|
||||||
|
|
@ -82,12 +86,12 @@ export const dispatchTopAgent = async (
|
||||||
role: responseJson.task_analysis?.role,
|
role: responseJson.task_analysis?.role,
|
||||||
taskObject: responseJson.task_analysis?.goal,
|
taskObject: responseJson.task_analysis?.goal,
|
||||||
tools: responseJson.resources?.tools?.map((tool: any) => tool.id),
|
tools: responseJson.resources?.tools?.map((tool: any) => tool.id),
|
||||||
fileUploadEnabled: responseJson.resources?.system_features?.file_upload?.enabled || false
|
fileUploadEnabled: responseJson.resources?.file_upload?.enabled
|
||||||
});
|
});
|
||||||
|
|
||||||
if (formData) {
|
if (formData) {
|
||||||
workflowResponseWrite?.({
|
workflowResponseWrite?.({
|
||||||
event: SseResponseEventEnum.formData,
|
event: SseResponseEventEnum.topAgentConfig,
|
||||||
data: formData
|
data: formData
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -102,16 +106,60 @@ export const dispatchTopAgent = async (
|
||||||
} else if (responseJson.phase === 'collection') {
|
} else if (responseJson.phase === 'collection') {
|
||||||
addLog.debug('📝 TopAgent: Information collection phase');
|
addLog.debug('📝 TopAgent: Information collection phase');
|
||||||
|
|
||||||
const displayText = responseJson.question || answerText;
|
const formDeata = responseJson.form;
|
||||||
|
if (formDeata) {
|
||||||
|
const inputForm: UserInputInteractive = {
|
||||||
|
type: 'userInput',
|
||||||
|
params: {
|
||||||
|
inputForm: formDeata.map((item) => {
|
||||||
|
return {
|
||||||
|
type: item.type as FlowNodeInputTypeEnum,
|
||||||
|
key: getNanoid(6),
|
||||||
|
label: item.label,
|
||||||
|
value: '',
|
||||||
|
required: false,
|
||||||
|
valueType:
|
||||||
|
item.type === FlowNodeInputTypeEnum.numberInput
|
||||||
|
? WorkflowIOValueTypeEnum.number
|
||||||
|
: WorkflowIOValueTypeEnum.string,
|
||||||
|
list:
|
||||||
|
'options' in item
|
||||||
|
? item.options?.map((option) => ({ label: option, value: option }))
|
||||||
|
: undefined
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
description: responseJson.question
|
||||||
|
}
|
||||||
|
};
|
||||||
|
workflowResponseWrite?.({
|
||||||
|
event: SseResponseEventEnum.collectionForm,
|
||||||
|
data: inputForm
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
aiResponse: formatAIResponse({
|
||||||
|
text: responseJson.question,
|
||||||
|
reasoning: reasoningText,
|
||||||
|
collectionForm: inputForm
|
||||||
|
}),
|
||||||
|
usage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
workflowResponseWrite?.({
|
||||||
|
event: SseResponseEventEnum.answer,
|
||||||
|
data: textAdaptGptResponse({ text: responseJson.question })
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
aiResponse: formatAIResponse({
|
aiResponse: formatAIResponse({
|
||||||
text: displayText,
|
text: responseJson.question,
|
||||||
reasoning: responseJson.reasoning || reasoningText
|
reasoning: reasoningText
|
||||||
}),
|
}),
|
||||||
usage
|
usage
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
addLog.warn(`[Top agent] Unknown phase: ${responseJson.phase}`);
|
addLog.warn(`[Top agent] Unknown phase`, responseJson);
|
||||||
return {
|
return {
|
||||||
aiResponse: formatAIResponse({
|
aiResponse: formatAIResponse({
|
||||||
text: answerText,
|
text: answerText,
|
||||||
|
|
|
||||||
|
|
@ -102,20 +102,50 @@ ${buildMetadataInfo(metadata)}
|
||||||
"question": "实际向用户提出的问题内容"
|
"question": "实际向用户提出的问题内容"
|
||||||
}
|
}
|
||||||
|
|
||||||
问题内容可以是开放式问题,也可以包含选项:
|
问题内容可以是开放式问题,也可以包含表单填写:
|
||||||
|
|
||||||
开放式问题示例:
|
开放式问题,无需表单填写:
|
||||||
{
|
{
|
||||||
"phase": "collection",
|
"phase": "collection",
|
||||||
"reasoning": "需要首先了解任务的基本定位和目标场景,这将决定后续需要确认的工具类型和能力边界",
|
"reasoning": "需要首先了解任务的基本定位和目标场景,这将决定后续需要确认的工具类型和能力边界",
|
||||||
"question": "我想了解一下您希望这个流程模板实现什么功能?能否详细描述一下具体要处理什么样的任务或问题?"
|
"question": "我想了解一下您希望这个流程模板实现什么功能?能否详细描述一下具体要处理什么样的任务或问题?"
|
||||||
}
|
}
|
||||||
|
|
||||||
选择题示例:
|
表单示例,一共有 4 类表单类型:
|
||||||
{
|
{
|
||||||
"phase": "collection",
|
"phase": "collection",
|
||||||
"reasoning": "需要确认参数化设计的重点方向,这将影响流程模板的灵活性设计",
|
"reasoning": "需要确认参数化设计的重点方向,这将影响流程模板的灵活性设计",
|
||||||
"question": "关于流程的参数化设计,用户最需要调整的是:\\nA. 输入数据源(不同类型的数据库/文件)\\nB. 处理参数(阈值、过滤条件、算法选择)\\nC. 输出格式(报告类型、文件格式、目标系统)\\nD. 执行环境(触发方式、频率、并发度)\\n\\n请选择最符合的选项,或输入您的详细回答:"
|
"question": "我需要和你确认一些参数,请根据你的需求选择对应的选项:",
|
||||||
|
"form": [
|
||||||
|
{
|
||||||
|
"type": "input",
|
||||||
|
"label": "请输入你想优化的方向"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "numberInput",
|
||||||
|
"label": "你想优化多少次"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "关于流程的参数化设计,用户最需要调整的是",
|
||||||
|
"options": [
|
||||||
|
"输入数据源(不同类型的数据库/文件)",
|
||||||
|
"处理参数(阈值、过滤条件、算法选择)",
|
||||||
|
"输出格式(报告类型、文件格式、目标系统)",
|
||||||
|
"执行环境(触发方式、频率、并发度)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "multipleSelect",
|
||||||
|
"label": "你想了解用户什么信息",
|
||||||
|
"options": [
|
||||||
|
"选项 A",
|
||||||
|
"选项 B",
|
||||||
|
"选项 C",
|
||||||
|
"选项 D"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
选项设计原则:
|
选项设计原则:
|
||||||
|
|
@ -277,24 +307,23 @@ ${resourceList}
|
||||||
直接输出以下格式的JSON(千万不要添加其他字段进来):
|
直接输出以下格式的JSON(千万不要添加其他字段进来):
|
||||||
{
|
{
|
||||||
"phase": "generation",
|
"phase": "generation",
|
||||||
|
"reasoning": "详细说明所有资源的选择理由:工具、知识库和系统功能如何协同工作来完成任务目标",
|
||||||
"task_analysis": {
|
"task_analysis": {
|
||||||
"goal": "任务的核心目标描述",
|
"goal": "任务的核心目标描述",
|
||||||
"role": "该流程的角色信息",
|
"role": "该流程的角色信息",
|
||||||
"key_features": "收集到的信息,对任务的深度理解和定位"
|
"key_features": "收集到的信息,对任务的深度理解和定位"
|
||||||
},
|
},
|
||||||
"reasoning": "详细说明所有资源的选择理由:工具、知识库和系统功能如何协同工作来完成任务目标",
|
|
||||||
"resources": {
|
"resources": {
|
||||||
"tools": [
|
"tools": [
|
||||||
{"id": "工具ID", "type": "tool"}
|
"工具ID"
|
||||||
],
|
],
|
||||||
"knowledges": [
|
"knowledges": [
|
||||||
{"id": "知识库ID", "type": "knowledge"}
|
"知识库ID"
|
||||||
],
|
],
|
||||||
"system_features": {
|
"system_features": {
|
||||||
"file_upload": {
|
"file_upload": {
|
||||||
"enabled": true/false,
|
"enabled": true/false,
|
||||||
"purpose": "说明原因(enabled=true时必填)",
|
"purpose": "说明原因(enabled=true时必填)"
|
||||||
"file_types": ["可选的文件类型"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -309,7 +338,6 @@ ${resourceList}
|
||||||
* system_features: 系统功能配置对象
|
* system_features: 系统功能配置对象
|
||||||
- file_upload.enabled: 是否需要文件上传(必填)
|
- file_upload.enabled: 是否需要文件上传(必填)
|
||||||
- file_upload.purpose: 为什么需要(enabled=true时必填)
|
- file_upload.purpose: 为什么需要(enabled=true时必填)
|
||||||
- file_upload.file_types: 建议的文件类型(可选),如["pdf", "xlsx"]
|
|
||||||
|
|
||||||
**✅ 正确示例1**(需要文件上传):
|
**✅ 正确示例1**(需要文件上传):
|
||||||
{
|
{
|
||||||
|
|
@ -321,14 +349,13 @@ ${resourceList}
|
||||||
"reasoning": "使用数据分析工具处理Excel数据,需要用户上传自己的财务报表文件",
|
"reasoning": "使用数据分析工具处理Excel数据,需要用户上传自己的财务报表文件",
|
||||||
"resources": {
|
"resources": {
|
||||||
"tools": [
|
"tools": [
|
||||||
{"id": "data_analysis/tool", "type": "tool"}
|
"data_analysis/tool"
|
||||||
],
|
],
|
||||||
"knowledges": [],
|
"knowledges": [],
|
||||||
"system_features": {
|
"system_features": {
|
||||||
"file_upload": {
|
"file_upload": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"purpose": "需要您上传财务报表文件(Excel或PDF格式)进行数据提取和分析",
|
"purpose": "需要您上传财务报表文件(Excel或PDF格式)进行数据提取和分析"
|
||||||
"file_types": ["xlsx", "xls", "pdf"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -340,10 +367,10 @@ ${resourceList}
|
||||||
"reasoning": "使用搜索工具获取实时信息,结合知识库的专业知识",
|
"reasoning": "使用搜索工具获取实时信息,结合知识库的专业知识",
|
||||||
"resources": {
|
"resources": {
|
||||||
"tools": [
|
"tools": [
|
||||||
{"id": "metaso/metasoSearch", "type": "tool"}
|
"metaso/metasoSearch"
|
||||||
],
|
],
|
||||||
"knowledges": [
|
"knowledges": [
|
||||||
{"id": "travel_kb", "type": "knowledge"}
|
"travel_kb"
|
||||||
],
|
],
|
||||||
"system_features": {
|
"system_features": {
|
||||||
"file_upload": {
|
"file_upload": {
|
||||||
|
|
@ -374,9 +401,9 @@ ${resourceList}
|
||||||
{
|
{
|
||||||
"resources": {
|
"resources": {
|
||||||
"tools": [
|
"tools": [
|
||||||
{"id": "bing/webSearch", "type": "tool"},
|
"bing/webSearch",
|
||||||
{"id": "google/search", "type": "tool"},
|
"google/search",
|
||||||
{"id": "metaso/metasoSearch", "type": "tool"}
|
"metaso/metasoSearch"
|
||||||
// ❌ 错误:这三个都是网页搜索工具,只应该选择一个最合适的
|
// ❌ 错误:这三个都是网页搜索工具,只应该选择一个最合适的
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -437,7 +464,7 @@ ${resourceList}
|
||||||
<conversation_rules>
|
<conversation_rules>
|
||||||
**回复格式要求**:
|
**回复格式要求**:
|
||||||
- **所有回复必须是 JSON 格式**,包含 \`phase\` 字段
|
- **所有回复必须是 JSON 格式**,包含 \`phase\` 字段
|
||||||
- 信息收集阶段:输出 \`{"phase": "collection", "reasoning": "...", "question": "..."}\`
|
- 信息收集阶段:输出 \`{"phase": "collection", "reasoning": "...", "question": "...","form":[...]}\`
|
||||||
- 配置生成阶段:输出 \`{"phase": "generation", "task_analysis": {...}, "resources": {...}, ...}\`
|
- 配置生成阶段:输出 \`{"phase": "generation", "task_analysis": {...}, "resources": {...}, ...}\`
|
||||||
- ❌ 不要输出任何非 JSON 格式的内容
|
- ❌ 不要输出任何非 JSON 格式的内容
|
||||||
- ❌ 不要添加代码块标记(如 \\\`\\\`\\\`json)
|
- ❌ 不要添加代码块标记(如 \\\`\\\`\\\`json)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
import { AICollectionAnswerSchema } from '../type';
|
||||||
|
|
||||||
export const TopAgentFormDataSchema = z.object({
|
export const TopAgentFormDataSchema = z.object({
|
||||||
role: z.string().optional(),
|
role: z.string().optional(),
|
||||||
|
|
@ -7,3 +8,31 @@ export const TopAgentFormDataSchema = z.object({
|
||||||
fileUploadEnabled: z.boolean().optional().default(false)
|
fileUploadEnabled: z.boolean().optional().default(false)
|
||||||
});
|
});
|
||||||
export type TopAgentFormDataType = z.infer<typeof TopAgentFormDataSchema>;
|
export type TopAgentFormDataType = z.infer<typeof TopAgentFormDataSchema>;
|
||||||
|
|
||||||
|
// 表单收集
|
||||||
|
export const TopAgentCollectionAnswerSchema = AICollectionAnswerSchema.extend({
|
||||||
|
phase: z.literal('collection'),
|
||||||
|
reasoning: z.string().nullish()
|
||||||
|
});
|
||||||
|
export const TopAgentGenerationAnswerSchema = z.object({
|
||||||
|
phase: z.literal('generation'),
|
||||||
|
reasoning: z.string().nullish(),
|
||||||
|
task_analysis: z.object({
|
||||||
|
goal: z.string(),
|
||||||
|
role: z.string(),
|
||||||
|
key_features: z.string()
|
||||||
|
}),
|
||||||
|
resources: z.object({
|
||||||
|
tools: z.array(z.string()),
|
||||||
|
knowledges: z.array(z.string()),
|
||||||
|
file_upload: z.object({
|
||||||
|
enabled: z.boolean(),
|
||||||
|
purpose: z.string()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
export const TopAgentAnswerSchema = z.discriminatedUnion('phase', [
|
||||||
|
TopAgentCollectionAnswerSchema,
|
||||||
|
TopAgentGenerationAnswerSchema
|
||||||
|
]);
|
||||||
|
export type TopAgentAnswerType = z.infer<typeof TopAgentAnswerSchema>;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {
|
||||||
} from '@fastgpt/global/core/chat/helperBot/type';
|
} from '@fastgpt/global/core/chat/helperBot/type';
|
||||||
import { WorkflowResponseFnSchema } from '../../../workflow/dispatch/type';
|
import { WorkflowResponseFnSchema } from '../../../workflow/dispatch/type';
|
||||||
import { LocaleList } from '@fastgpt/global/common/i18n/type';
|
import { LocaleList } from '@fastgpt/global/common/i18n/type';
|
||||||
|
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
|
|
||||||
export const HelperBotDispatchParamsSchema = z.object({
|
export const HelperBotDispatchParamsSchema = z.object({
|
||||||
query: z.string(),
|
query: z.string(),
|
||||||
|
|
@ -40,3 +41,19 @@ export const HelperBotDispatchResponseSchema = z.object({
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
export type HelperBotDispatchResponseType = z.infer<typeof HelperBotDispatchResponseSchema>;
|
export type HelperBotDispatchResponseType = z.infer<typeof HelperBotDispatchResponseSchema>;
|
||||||
|
|
||||||
|
/* AI 表单输出 schema */
|
||||||
|
const InputSchema = z.object({
|
||||||
|
type: z.enum([FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.numberInput]),
|
||||||
|
label: z.string()
|
||||||
|
});
|
||||||
|
const SelectSchema = z.object({
|
||||||
|
type: z.enum([FlowNodeInputTypeEnum.select, FlowNodeInputTypeEnum.multipleSelect]),
|
||||||
|
label: z.string(),
|
||||||
|
options: z.array(z.string())
|
||||||
|
});
|
||||||
|
export const AICollectionAnswerSchema = z.object({
|
||||||
|
question: z.string(), // 可能只有一个问题,可能
|
||||||
|
form: z.array(z.union([InputSchema, SelectSchema])).optional()
|
||||||
|
});
|
||||||
|
export type AICollectionAnswerType = z.infer<typeof AICollectionAnswerSchema>;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
import type { AIChatItemValueItemType } from '@fastgpt/global/core/chat/helperBot/type';
|
import type { AIChatItemValueItemType } from '@fastgpt/global/core/chat/helperBot/type';
|
||||||
|
import type { UserInputInteractive } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
|
|
||||||
export const formatAIResponse = ({
|
export const formatAIResponse = ({
|
||||||
text,
|
text,
|
||||||
reasoning
|
reasoning,
|
||||||
|
collectionForm
|
||||||
}: {
|
}: {
|
||||||
text: string;
|
text: string;
|
||||||
reasoning?: string;
|
reasoning?: string;
|
||||||
|
collectionForm?: UserInputInteractive;
|
||||||
}): AIChatItemValueItemType[] => {
|
}): AIChatItemValueItemType[] => {
|
||||||
const result: AIChatItemValueItemType[] = [];
|
const result: AIChatItemValueItemType[] = [];
|
||||||
|
|
||||||
|
|
@ -23,5 +26,11 @@ export const formatAIResponse = ({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (collectionForm) {
|
||||||
|
result.push({
|
||||||
|
collectionForm
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ import { MaxLengthPlugin } from './plugins/MaxLengthPlugin';
|
||||||
import { VariableLabelNode } from './plugins/VariableLabelPlugin/node';
|
import { VariableLabelNode } from './plugins/VariableLabelPlugin/node';
|
||||||
import VariableLabelPlugin from './plugins/VariableLabelPlugin';
|
import VariableLabelPlugin from './plugins/VariableLabelPlugin';
|
||||||
import { useDeepCompareEffect } from 'ahooks';
|
import { useDeepCompareEffect } from 'ahooks';
|
||||||
import VariablePickerPlugin from './plugins/VariablePickerPlugin';
|
|
||||||
import MarkdownPlugin from './plugins/MarkdownPlugin';
|
import MarkdownPlugin from './plugins/MarkdownPlugin';
|
||||||
import MyIcon from '../../Icon';
|
import MyIcon from '../../Icon';
|
||||||
import ListExitPlugin from './plugins/ListExitPlugin';
|
import ListExitPlugin from './plugins/ListExitPlugin';
|
||||||
|
|
@ -48,7 +47,6 @@ import type { SkillLabelItemType } from './plugins/SkillLabelPlugin';
|
||||||
import SkillLabelPlugin from './plugins/SkillLabelPlugin';
|
import SkillLabelPlugin from './plugins/SkillLabelPlugin';
|
||||||
import { SkillNode } from './plugins/SkillLabelPlugin/node';
|
import { SkillNode } from './plugins/SkillLabelPlugin/node';
|
||||||
import type { SkillOptionItemType } from './plugins/SkillPickerPlugin';
|
import type { SkillOptionItemType } from './plugins/SkillPickerPlugin';
|
||||||
import { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
|
||||||
|
|
||||||
const Placeholder = ({ children, padding }: { children: React.ReactNode; padding: string }) => (
|
const Placeholder = ({ children, padding }: { children: React.ReactNode; padding: string }) => (
|
||||||
<Box
|
<Box
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@ import { mergeRegister } from '@lexical/utils';
|
||||||
import { registerLexicalTextEntity } from '../../utils';
|
import { registerLexicalTextEntity } from '../../utils';
|
||||||
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
|
import type { SelectedToolItemType } from '@fastgpt/global/core/app/formEdit/type';
|
||||||
|
|
||||||
const REGEX = new RegExp(getSkillRegexString(), 'i');
|
const REGEX = new RegExp(getSkillRegexString(), 'i');
|
||||||
|
|
||||||
export type SkillLabelItemType = FlowNodeTemplateType & {
|
export type SkillLabelItemType = SelectedToolItemType & {
|
||||||
configStatus: 'active' | 'invalid' | 'waitingForConfig';
|
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import {
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { useCallback, useEffect, useRef, useMemo } from 'react';
|
import { useCallback, useEffect, useRef, useMemo } from 'react';
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex, HStack } from '@chakra-ui/react';
|
||||||
import { useBasicTypeaheadTriggerMatch } from '../../utils';
|
import { useBasicTypeaheadTriggerMatch } from '../../utils';
|
||||||
import Avatar from '../../../../Avatar';
|
import Avatar from '../../../../Avatar';
|
||||||
import MyIcon from '../../../../Icon';
|
import MyIcon from '../../../../Icon';
|
||||||
|
|
@ -27,6 +27,10 @@ import { useRequest2 } from '../../../../../../hooks/useRequest';
|
||||||
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
|
/*
|
||||||
|
两列渲染器
|
||||||
|
第三列会展示第二列选中的 tool/toolset 的详细信息:头像、名称、描述、子工具(如有)
|
||||||
|
*/
|
||||||
export type SkillOptionItemType = {
|
export type SkillOptionItemType = {
|
||||||
description?: string;
|
description?: string;
|
||||||
list: SkillItemType[];
|
list: SkillItemType[];
|
||||||
|
|
@ -40,16 +44,20 @@ export type SkillItemType = {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
description?: string;
|
||||||
canClick: boolean;
|
canClick: boolean;
|
||||||
|
children?: SkillOptionItemType;
|
||||||
|
|
||||||
// Folder
|
// Folder
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
isFolder?: boolean;
|
isFolder?: boolean;
|
||||||
folderChildren?: SkillItemType[];
|
folderChildren?: SkillItemType[];
|
||||||
|
|
||||||
// System tool/ model
|
// Toolset
|
||||||
showArrow?: boolean;
|
tools?: {
|
||||||
children?: SkillOptionItemType;
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function SkillPickerPlugin({
|
export default function SkillPickerPlugin({
|
||||||
|
|
@ -558,6 +566,12 @@ export default function SkillPickerPlugin({
|
||||||
getFlattenedVisibleItems
|
getFlattenedVisibleItems
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const selectedTool = useMemo(() => {
|
||||||
|
const item = skillOptions[currentColumnIndex]?.list[currentRowIndex];
|
||||||
|
if (!item || !item.canClick) return null;
|
||||||
|
return item;
|
||||||
|
}, [skillOptions, currentColumnIndex, currentRowIndex]);
|
||||||
|
|
||||||
// Recursively render item list
|
// Recursively render item list
|
||||||
const renderItemList = useCallback(
|
const renderItemList = useCallback(
|
||||||
(
|
(
|
||||||
|
|
@ -661,8 +675,8 @@ export default function SkillPickerPlugin({
|
||||||
) : columnData.onFolderLoad ? (
|
) : columnData.onFolderLoad ? (
|
||||||
<Box w={3} flexShrink={0} />
|
<Box w={3} flexShrink={0} />
|
||||||
) : null}
|
) : null}
|
||||||
{item.icon && <Avatar src={item.icon} w={'1.2rem'} borderRadius={'xs'} />}
|
|
||||||
|
|
||||||
|
{item.icon && <Avatar src={item.icon} w={'1.2rem'} borderRadius={'xs'} />}
|
||||||
{/* Folder content */}
|
{/* Folder content */}
|
||||||
<Box fontSize={'sm'} fontWeight={'medium'} flex={'1 0 0'} className="textEllipsis">
|
<Box fontSize={'sm'} fontWeight={'medium'} flex={'1 0 0'} className="textEllipsis">
|
||||||
{item.label}
|
{item.label}
|
||||||
|
|
@ -672,9 +686,6 @@ export default function SkillPickerPlugin({
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
{item.showArrow && (
|
|
||||||
<MyIcon name={'core/chat/chevronRight'} w={'0.8rem'} color={'myGray.400'} />
|
|
||||||
)}
|
|
||||||
</MyBox>
|
</MyBox>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -779,8 +790,12 @@ export default function SkillPickerPlugin({
|
||||||
|
|
||||||
// Insert skill node text at current selection
|
// Insert skill node text at current selection
|
||||||
selection.insertNodes([$createTextNode(`{{@${skillId}@}}`)]);
|
selection.insertNodes([$createTextNode(`{{@${skillId}@}}`)]);
|
||||||
closeMenu();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Close menu after editor update to avoid flushSync warning
|
||||||
|
setTimeout(() => {
|
||||||
|
closeMenu();
|
||||||
|
}, 0);
|
||||||
} else {
|
} else {
|
||||||
// If onClick didn't return a skillId, just close the menu
|
// If onClick didn't return a skillId, just close the menu
|
||||||
closeMenu();
|
closeMenu();
|
||||||
|
|
@ -821,6 +836,63 @@ export default function SkillPickerPlugin({
|
||||||
{skillOptions.map((column, index) => {
|
{skillOptions.map((column, index) => {
|
||||||
return renderColumn(column, index);
|
return renderColumn(column, index);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{selectedTool && (
|
||||||
|
<Box
|
||||||
|
ml={2}
|
||||||
|
p={2.5}
|
||||||
|
borderRadius={'sm'}
|
||||||
|
w={'200px'}
|
||||||
|
boxShadow={
|
||||||
|
'0 4px 10px 0 rgba(19, 51, 107, 0.10), 0 0 1px 0 rgba(19, 51, 107, 0.10)'
|
||||||
|
}
|
||||||
|
bg={'white'}
|
||||||
|
flexShrink={0}
|
||||||
|
maxH={'350px'}
|
||||||
|
overflow={'auto'}
|
||||||
|
>
|
||||||
|
<HStack>
|
||||||
|
{selectedTool.icon && (
|
||||||
|
<Avatar src={selectedTool.icon} w={'1.3rem'} borderRadius={'xs'} />
|
||||||
|
)}
|
||||||
|
{/* Folder content */}
|
||||||
|
<Box fontSize={'sm'} fontWeight={'medium'}>
|
||||||
|
{selectedTool.label}
|
||||||
|
</Box>
|
||||||
|
</HStack>
|
||||||
|
<Box color={'myGray.500'} fontSize={'xs'} className="textEllipsis3">
|
||||||
|
{selectedTool.description || t('app:tool_not_desc')}
|
||||||
|
</Box>
|
||||||
|
{/* Tools */}
|
||||||
|
{selectedTool.tools && selectedTool.tools.length > 0 && (
|
||||||
|
<>
|
||||||
|
<Box mt={2} color={'myGray.900'} fontSize={'sm'}>
|
||||||
|
{t('app:tools')}({selectedTool.tools.length})
|
||||||
|
</Box>
|
||||||
|
{selectedTool.tools.map((tool) => (
|
||||||
|
<Box
|
||||||
|
key={tool.id}
|
||||||
|
mt={1}
|
||||||
|
fontSize={'xs'}
|
||||||
|
color={'myGray.600'}
|
||||||
|
display={'flex'}
|
||||||
|
alignItems={'center'}
|
||||||
|
gap={1.5}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
w={'6px'}
|
||||||
|
h={'6px'}
|
||||||
|
borderRadius={'50%'}
|
||||||
|
bg={'primary.600'}
|
||||||
|
flexShrink={0}
|
||||||
|
/>
|
||||||
|
{tool.name}
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Flex>,
|
</Flex>,
|
||||||
anchorElementRef.current!
|
anchorElementRef.current!
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,12 @@ export default function VariableLabelPickerPlugin({
|
||||||
selection.insertNodes([
|
selection.insertNodes([
|
||||||
$createTextNode(`{{$${selectedOption.parent?.id}.${selectedOption.key}$}}`)
|
$createTextNode(`{{$${selectedOption.parent?.id}.${selectedOption.key}$}}`)
|
||||||
]);
|
]);
|
||||||
closeMenu();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Close menu after editor update to avoid flushSync warning
|
||||||
|
setTimeout(() => {
|
||||||
|
closeMenu();
|
||||||
|
}, 0);
|
||||||
},
|
},
|
||||||
[editor]
|
[editor]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,12 @@ export default function VariablePickerPlugin({
|
||||||
nodeToRemove.remove();
|
nodeToRemove.remove();
|
||||||
}
|
}
|
||||||
selection.insertNodes([$createTextNode(`{{${selectedOption.key}}}`)]);
|
selection.insertNodes([$createTextNode(`{{${selectedOption.key}}}`)]);
|
||||||
closeMenu();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Close menu after editor update to avoid flushSync warning
|
||||||
|
setTimeout(() => {
|
||||||
|
closeMenu();
|
||||||
|
}, 0);
|
||||||
},
|
},
|
||||||
[editor]
|
[editor]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,6 @@
|
||||||
"auto_execute_default_prompt_placeholder": "Default questions sent when executing automatically",
|
"auto_execute_default_prompt_placeholder": "Default questions sent when executing automatically",
|
||||||
"auto_execute_tip": "After turning it on, the workflow will be automatically triggered when the user enters the conversation interface. \nExecution order: 1. Dialogue starter; 2. Global variables; 3. Automatic execution.",
|
"auto_execute_tip": "After turning it on, the workflow will be automatically triggered when the user enters the conversation interface. \nExecution order: 1. Dialogue starter; 2. Global variables; 3. Automatic execution.",
|
||||||
"auto_save": "Auto save",
|
"auto_save": "Auto save",
|
||||||
"can_select_toolset": "Entire toolset available for selection",
|
|
||||||
"change_app_type": "Change App Type",
|
"change_app_type": "Change App Type",
|
||||||
"chat_agent_intro": "AI autonomously plans executable processes",
|
"chat_agent_intro": "AI autonomously plans executable processes",
|
||||||
"chat_debug": "Chat Preview",
|
"chat_debug": "Chat Preview",
|
||||||
|
|
@ -392,6 +391,7 @@
|
||||||
"tool_detail": "Tool details",
|
"tool_detail": "Tool details",
|
||||||
"tool_input_param_tip": "This tool requires configuration of relevant information for normal operation.",
|
"tool_input_param_tip": "This tool requires configuration of relevant information for normal operation.",
|
||||||
"tool_not_active": "This tool has not been activated yet",
|
"tool_not_active": "This tool has not been activated yet",
|
||||||
|
"tool_not_desc": "The tool lacks a description ~",
|
||||||
"tool_offset_tips": "This tool is no longer available and will interrupt application operation. Please replace it immediately.",
|
"tool_offset_tips": "This tool is no longer available and will interrupt application operation. Please replace it immediately.",
|
||||||
"tool_param_config": "Parameter configuration",
|
"tool_param_config": "Parameter configuration",
|
||||||
"tool_params_description_tips": "The description of parameter functions, if used as tool invocation parameters, affects the model tool invocation effect.",
|
"tool_params_description_tips": "The description of parameter functions, if used as tool invocation parameters, affects the model tool invocation effect.",
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@
|
||||||
"auto_execute_default_prompt_placeholder": "自动执行时,发送的默认问题",
|
"auto_execute_default_prompt_placeholder": "自动执行时,发送的默认问题",
|
||||||
"auto_execute_tip": "开启后,用户进入对话界面将自动触发工作流。执行顺序:1、对话开场白;2、全局变量;3、自动执行。",
|
"auto_execute_tip": "开启后,用户进入对话界面将自动触发工作流。执行顺序:1、对话开场白;2、全局变量;3、自动执行。",
|
||||||
"auto_save": "自动保存",
|
"auto_save": "自动保存",
|
||||||
"can_select_toolset": "可选择整个工具集",
|
|
||||||
"change_app_type": "更改应用类型",
|
"change_app_type": "更改应用类型",
|
||||||
"chat_agent_intro": "由 AI 自主规划可执行流程",
|
"chat_agent_intro": "由 AI 自主规划可执行流程",
|
||||||
"chat_debug": "调试预览",
|
"chat_debug": "调试预览",
|
||||||
|
|
@ -411,6 +410,7 @@
|
||||||
"tool_input_param_tip": "该工具正常运行需要配置相关信息",
|
"tool_input_param_tip": "该工具正常运行需要配置相关信息",
|
||||||
"tool_load_failed": "部分工具加载失败",
|
"tool_load_failed": "部分工具加载失败",
|
||||||
"tool_not_active": "该工具尚未激活",
|
"tool_not_active": "该工具尚未激活",
|
||||||
|
"tool_not_desc": "工具缺少描述~",
|
||||||
"tool_offset_tips": "该工具已无法使用,将中断应用运行,请立即替换",
|
"tool_offset_tips": "该工具已无法使用,将中断应用运行,请立即替换",
|
||||||
"tool_param_config": "参数配置",
|
"tool_param_config": "参数配置",
|
||||||
"tool_params_description_tips": "参数功能的描述,若作为工具调用参数,影响模型工具调用效果",
|
"tool_params_description_tips": "参数功能的描述,若作为工具调用参数,影响模型工具调用效果",
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,6 @@
|
||||||
"auto_execute_default_prompt_placeholder": "自動執行時,傳送的預設問題",
|
"auto_execute_default_prompt_placeholder": "自動執行時,傳送的預設問題",
|
||||||
"auto_execute_tip": "開啟後,使用者進入對話式介面將自動觸發工作流程。\n執行順序:1、對話開場白;2、全域變數;3、自動執行。",
|
"auto_execute_tip": "開啟後,使用者進入對話式介面將自動觸發工作流程。\n執行順序:1、對話開場白;2、全域變數;3、自動執行。",
|
||||||
"auto_save": "自動儲存",
|
"auto_save": "自動儲存",
|
||||||
"can_select_toolset": "可選擇整個工具集",
|
|
||||||
"change_app_type": "更改應用程式類型",
|
"change_app_type": "更改應用程式類型",
|
||||||
"chat_agent_intro": "由 AI 自主規劃可執行流程",
|
"chat_agent_intro": "由 AI 自主規劃可執行流程",
|
||||||
"chat_debug": "聊天預覽",
|
"chat_debug": "聊天預覽",
|
||||||
|
|
@ -388,6 +387,7 @@
|
||||||
"tool_detail": "工具詳情",
|
"tool_detail": "工具詳情",
|
||||||
"tool_input_param_tip": "該工具正常運行需要配置相關信息",
|
"tool_input_param_tip": "該工具正常運行需要配置相關信息",
|
||||||
"tool_not_active": "該工具尚未激活",
|
"tool_not_active": "該工具尚未激活",
|
||||||
|
"tool_not_desc": "工具缺少描述~",
|
||||||
"tool_offset_tips": "該工具已無法使用,將中斷應用運行,請立即替換",
|
"tool_offset_tips": "該工具已無法使用,將中斷應用運行,請立即替換",
|
||||||
"tool_param_config": "參數配置",
|
"tool_param_config": "參數配置",
|
||||||
"tool_params_description_tips": "參數功能的描述,若作為工具調用參數,影響模型工具調用效果",
|
"tool_params_description_tips": "參數功能的描述,若作為工具調用參數,影響模型工具調用效果",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,10 @@ import type {
|
||||||
type ToolModuleResponseItemType
|
type ToolModuleResponseItemType
|
||||||
} from '@fastgpt/global/core/chat/type';
|
} from '@fastgpt/global/core/chat/type';
|
||||||
import type { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import type { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import type { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
import type {
|
||||||
|
UserInputInteractive,
|
||||||
|
WorkflowInteractiveResponseType
|
||||||
|
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
import type { TopAgentFormDataType } from '@fastgpt/service/core/chat/HelperBot/dispatch/topAgent/type';
|
import type { TopAgentFormDataType } from '@fastgpt/service/core/chat/HelperBot/dispatch/topAgent/type';
|
||||||
import type { GeneratedSkillDataType } from '@fastgpt/global/core/chat/helperBot/generatedSkill/type';
|
import type { GeneratedSkillDataType } from '@fastgpt/global/core/chat/helperBot/generatedSkill/type';
|
||||||
|
|
||||||
|
|
@ -26,7 +29,8 @@ export type generatingMessageProps = {
|
||||||
nodeResponse?: ChatHistoryItemResType;
|
nodeResponse?: ChatHistoryItemResType;
|
||||||
durationSeconds?: number;
|
durationSeconds?: number;
|
||||||
|
|
||||||
// Agent
|
// HelperBot
|
||||||
|
collectionForm?: UserInputInteractive;
|
||||||
formData?: TopAgentFormDataType;
|
formData?: TopAgentFormDataType;
|
||||||
generatedSkill?: GeneratedSkillDataType;
|
generatedSkill?: GeneratedSkillDataType;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import {
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionPanel,
|
AccordionPanel,
|
||||||
Flex,
|
Flex,
|
||||||
HStack
|
HStack,
|
||||||
|
Button
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
|
|
@ -16,6 +17,11 @@ import Markdown from '@/components/Markdown';
|
||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||||
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
|
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
|
||||||
|
import type { UserInputInteractive } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
|
import { nodeInputTypeToInputType } from '@/components/core/app/formRender/utils';
|
||||||
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
|
import InputRender from '@/components/core/app/formRender';
|
||||||
|
|
||||||
const accordionButtonStyle = {
|
const accordionButtonStyle = {
|
||||||
w: 'auto',
|
w: 'auto',
|
||||||
|
|
@ -69,7 +75,6 @@ const RenderResoningContent = React.memo(function RenderResoningContent({
|
||||||
</Accordion>
|
</Accordion>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const RenderText = React.memo(function RenderText({
|
const RenderText = React.memo(function RenderText({
|
||||||
showAnimation,
|
showAnimation,
|
||||||
text
|
text
|
||||||
|
|
@ -86,19 +91,87 @@ const RenderText = React.memo(function RenderText({
|
||||||
|
|
||||||
return <Markdown source={source} showAnimation={showAnimation} />;
|
return <Markdown source={source} showAnimation={showAnimation} />;
|
||||||
});
|
});
|
||||||
|
const RenderCollectionForm = React.memo(function RenderCollectionForm({
|
||||||
|
collectionForm,
|
||||||
|
onSubmit
|
||||||
|
}: {
|
||||||
|
collectionForm: UserInputInteractive;
|
||||||
|
onSubmit: (formData: string) => void;
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { control, handleSubmit } = useForm();
|
||||||
|
|
||||||
|
const submitted = collectionForm.params.submitted;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={3}>{collectionForm.params.description}</Box>
|
||||||
|
<Flex flexDirection={'column'} gap={3}>
|
||||||
|
{collectionForm.params.inputForm.map((input) => {
|
||||||
|
const inputType = nodeInputTypeToInputType([input.type]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Controller
|
||||||
|
key={input.key}
|
||||||
|
control={control}
|
||||||
|
name={input.key}
|
||||||
|
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<FormLabel whiteSpace={'pre-wrap'} mb={0.5}>
|
||||||
|
{input.label}
|
||||||
|
</FormLabel>
|
||||||
|
<InputRender
|
||||||
|
{...input}
|
||||||
|
inputType={inputType}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
isDisabled={submitted}
|
||||||
|
isInvalid={!!error}
|
||||||
|
isRichText={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{!submitted && (
|
||||||
|
<Flex justifyContent={'flex-end'} mt={4}>
|
||||||
|
<Button
|
||||||
|
size={'sm'}
|
||||||
|
onClick={handleSubmit((data) => {
|
||||||
|
// 需要把 label 作为 key
|
||||||
|
const dataByLabel = Object.fromEntries(
|
||||||
|
collectionForm.params.inputForm.map((input) => [input.label, data[input.key]])
|
||||||
|
);
|
||||||
|
onSubmit(JSON.stringify(dataByLabel));
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{t('common:Submit')}
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const AIItem = ({
|
const AIItem = ({
|
||||||
chat,
|
chat,
|
||||||
isChatting,
|
isChatting,
|
||||||
isLastChild
|
isLastChild,
|
||||||
|
onSubmitCollectionForm
|
||||||
}: {
|
}: {
|
||||||
chat: HelperBotChatItemSiteType;
|
chat: HelperBotChatItemSiteType;
|
||||||
isChatting: boolean;
|
isChatting: boolean;
|
||||||
isLastChild: boolean;
|
isLastChild: boolean;
|
||||||
|
onSubmitCollectionForm: (formData: string) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { copyData } = useCopyData();
|
const { copyData } = useCopyData();
|
||||||
|
console.log(chat, 111122);
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
_hover={{
|
_hover={{
|
||||||
|
|
@ -136,6 +209,15 @@ const AIItem = ({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if ('collectionForm' in value && value.collectionForm) {
|
||||||
|
return (
|
||||||
|
<RenderCollectionForm
|
||||||
|
key={i}
|
||||||
|
collectionForm={value.collectionForm}
|
||||||
|
onSubmit={onSubmitCollectionForm}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
})}
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
{/* Controller */}
|
{/* Controller */}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react';
|
import React, { useCallback, useImperativeHandle, useRef, useState } from 'react';
|
||||||
|
|
||||||
import HelperBotContextProvider, { type HelperBotRefType, type HelperBotProps } from './context';
|
import HelperBotContextProvider, { type HelperBotProps } from './context';
|
||||||
import type { AIChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
import type { AIChatItemValueItemType } from '@fastgpt/global/core/chat/helperBot/type';
|
||||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
|
|
@ -145,7 +145,7 @@ const ChatBox = ({ type, metadata, onApply, ChatBoxRef, ...props }: HelperBotPro
|
||||||
event,
|
event,
|
||||||
text = '',
|
text = '',
|
||||||
reasoningText,
|
reasoningText,
|
||||||
tool,
|
collectionForm,
|
||||||
formData,
|
formData,
|
||||||
generatedSkill
|
generatedSkill
|
||||||
}: generatingMessageProps) => {
|
}: generatingMessageProps) => {
|
||||||
|
|
@ -158,26 +158,34 @@ const ChatBox = ({ type, metadata, onApply, ChatBoxRef, ...props }: HelperBotPro
|
||||||
const updateValue: AIChatItemValueItemType = item.value[updateIndex];
|
const updateValue: AIChatItemValueItemType = item.value[updateIndex];
|
||||||
|
|
||||||
// Special event: form data
|
// Special event: form data
|
||||||
if (event === SseResponseEventEnum.formData && formData) {
|
if (event === SseResponseEventEnum.collectionForm && collectionForm) {
|
||||||
if (type === HelperBotTypeEnum.topAgent) {
|
return {
|
||||||
onApply?.(formData);
|
...item,
|
||||||
}
|
value: item.value.concat({
|
||||||
|
collectionForm
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
event === SseResponseEventEnum.topAgentConfig &&
|
||||||
|
formData &&
|
||||||
|
type === HelperBotTypeEnum.topAgent
|
||||||
|
) {
|
||||||
|
onApply(formData);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
// Special event: generated skill
|
event === SseResponseEventEnum.generatedSkill &&
|
||||||
if (event === SseResponseEventEnum.generatedSkill && generatedSkill) {
|
generatedSkill &&
|
||||||
console.log('📊 HelperBot: Received generatedSkill event', generatedSkill);
|
type === HelperBotTypeEnum.skillAgent
|
||||||
// 直接将生成的 skill 数据传递给 onApply 回调(仅在 skillAgent 类型时)
|
) {
|
||||||
if (type === HelperBotTypeEnum.skillAgent) {
|
onApply(generatedSkill);
|
||||||
onApply?.(generatedSkill);
|
|
||||||
}
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event === SseResponseEventEnum.answer || event === SseResponseEventEnum.fastAnswer) {
|
if (event === SseResponseEventEnum.answer || event === SseResponseEventEnum.fastAnswer) {
|
||||||
if (reasoningText) {
|
if (reasoningText) {
|
||||||
if (updateValue?.reasoning) {
|
if ('reasoning' in updateValue && updateValue.reasoning) {
|
||||||
updateValue.reasoning.content += reasoningText;
|
updateValue.reasoning.content += reasoningText;
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
|
|
@ -200,7 +208,7 @@ const ChatBox = ({ type, metadata, onApply, ChatBoxRef, ...props }: HelperBotPro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (text) {
|
if (text) {
|
||||||
if (updateValue?.text) {
|
if ('text' in updateValue && updateValue.text) {
|
||||||
updateValue.text.content += text;
|
updateValue.text.content += text;
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
|
|
@ -224,50 +232,6 @@ const ChatBox = ({ type, metadata, onApply, ChatBoxRef, ...props }: HelperBotPro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tool call
|
|
||||||
if (event === SseResponseEventEnum.toolCall && tool) {
|
|
||||||
const val: AIChatItemValueItemType = {
|
|
||||||
tool: {
|
|
||||||
...tool,
|
|
||||||
response: ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
value: [...item.value, val]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (event === SseResponseEventEnum.toolParams && tool && updateValue?.tool) {
|
|
||||||
if (tool.params) {
|
|
||||||
updateValue.tool.params += tool.params;
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
value: [
|
|
||||||
...item.value.slice(0, updateIndex),
|
|
||||||
updateValue,
|
|
||||||
...item.value.slice(updateIndex + 1)
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
if (event === SseResponseEventEnum.toolResponse && tool && updateValue?.tool) {
|
|
||||||
if (tool.response) {
|
|
||||||
// replace tool response
|
|
||||||
updateValue.tool.response += tool.response;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
value: [
|
|
||||||
...item.value.slice(0, updateIndex),
|
|
||||||
updateValue,
|
|
||||||
...item.value.slice(updateIndex + 1)
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
@ -275,91 +239,98 @@ const ChatBox = ({ type, metadata, onApply, ChatBoxRef, ...props }: HelperBotPro
|
||||||
generatingScroll(false);
|
generatingScroll(false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const handleSendMessage = useMemoizedFn(async ({ query = '' }: onSendMessageParamsType) => {
|
const handleSendMessage = useMemoizedFn(
|
||||||
// Init check
|
async ({ query = '', collectionFormData }: onSendMessageParamsType) => {
|
||||||
if (isChatting) {
|
// Init check
|
||||||
return toast({
|
if (isChatting) {
|
||||||
title: t('chat:is_chatting'),
|
return toast({
|
||||||
status: 'warning'
|
title: t('chat:is_chatting'),
|
||||||
});
|
status: 'warning'
|
||||||
}
|
});
|
||||||
|
|
||||||
abortRequest();
|
|
||||||
query = query.trim();
|
|
||||||
|
|
||||||
if (!query) {
|
|
||||||
toast({
|
|
||||||
title: t('chat:content_empty'),
|
|
||||||
status: 'warning'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const chatItemDataId = getNanoid(24);
|
|
||||||
const newChatList: HelperBotChatItemSiteType[] = [
|
|
||||||
...chatRecords,
|
|
||||||
// 用户消息
|
|
||||||
{
|
|
||||||
_id: getNanoid(24),
|
|
||||||
createTime: new Date(),
|
|
||||||
dataId: chatItemDataId,
|
|
||||||
obj: ChatRoleEnum.Human,
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
text: {
|
|
||||||
content: query
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// AI 消息 - 空白,用于接收流式输出
|
|
||||||
{
|
|
||||||
_id: getNanoid(24),
|
|
||||||
createTime: new Date(),
|
|
||||||
dataId: chatItemDataId,
|
|
||||||
obj: ChatRoleEnum.AI,
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
text: {
|
|
||||||
content: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
];
|
|
||||||
setChatRecords(newChatList);
|
|
||||||
|
|
||||||
resetInputVal({});
|
abortRequest();
|
||||||
scrollToBottom();
|
query = query.trim();
|
||||||
|
const mergeQuery = query || collectionFormData;
|
||||||
|
|
||||||
setIsChatting(true);
|
if (!mergeQuery) {
|
||||||
try {
|
toast({
|
||||||
const abortSignal = new AbortController();
|
title: t('chat:content_empty'),
|
||||||
chatController.current = abortSignal;
|
status: 'warning'
|
||||||
console.log('metadata-fronted', metadata);
|
});
|
||||||
const { responseText } = await streamFetch({
|
return;
|
||||||
url: '/api/core/chat/helperBot/completions',
|
}
|
||||||
data: {
|
|
||||||
chatId,
|
const chatItemDataId = getNanoid(24);
|
||||||
chatItemId: chatItemDataId,
|
const newChatList: HelperBotChatItemSiteType[] = [
|
||||||
query,
|
...chatRecords,
|
||||||
files: chatForm.getValues('files').map((item) => ({
|
// 用户消息
|
||||||
type: item.type,
|
{
|
||||||
key: item.key,
|
_id: getNanoid(24),
|
||||||
// url: item.url,
|
createTime: new Date(),
|
||||||
name: item.name
|
dataId: chatItemDataId,
|
||||||
})),
|
obj: ChatRoleEnum.Human,
|
||||||
metadata: {
|
value: [
|
||||||
type: type,
|
{
|
||||||
data: metadata
|
text: {
|
||||||
}
|
content: mergeQuery
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
onMessage: generatingMessage,
|
// AI 消息 - 空白,用于接收流式输出
|
||||||
abortCtrl: abortSignal
|
...(query
|
||||||
});
|
? [
|
||||||
} catch (error) {}
|
{
|
||||||
setIsChatting(false);
|
_id: getNanoid(24),
|
||||||
});
|
createTime: new Date(),
|
||||||
|
dataId: chatItemDataId,
|
||||||
|
obj: ChatRoleEnum.AI,
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
text: {
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: [])
|
||||||
|
];
|
||||||
|
setChatRecords(newChatList);
|
||||||
|
|
||||||
|
resetInputVal({});
|
||||||
|
scrollToBottom();
|
||||||
|
|
||||||
|
setIsChatting(true);
|
||||||
|
try {
|
||||||
|
const abortSignal = new AbortController();
|
||||||
|
chatController.current = abortSignal;
|
||||||
|
|
||||||
|
const { responseText } = await streamFetch({
|
||||||
|
url: '/api/core/chat/helperBot/completions',
|
||||||
|
data: {
|
||||||
|
chatId,
|
||||||
|
chatItemId: chatItemDataId,
|
||||||
|
query: mergeQuery,
|
||||||
|
files: chatForm.getValues('files').map((item) => ({
|
||||||
|
type: item.type,
|
||||||
|
key: item.key,
|
||||||
|
// url: item.url,
|
||||||
|
name: item.name
|
||||||
|
})),
|
||||||
|
metadata: {
|
||||||
|
type: type,
|
||||||
|
data: metadata
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMessage: generatingMessage,
|
||||||
|
abortCtrl: abortSignal
|
||||||
|
});
|
||||||
|
} catch (error) {}
|
||||||
|
setIsChatting(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
useImperativeHandle(ChatBoxRef, () => ({
|
useImperativeHandle(ChatBoxRef, () => ({
|
||||||
restartChat() {
|
restartChat() {
|
||||||
|
|
@ -397,6 +368,7 @@ const ChatBox = ({ type, metadata, onApply, ChatBoxRef, ...props }: HelperBotPro
|
||||||
chat={item}
|
chat={item}
|
||||||
isChatting={isChatting}
|
isChatting={isChatting}
|
||||||
isLastChild={index === chatRecords.length - 1}
|
isLastChild={index === chatRecords.length - 1}
|
||||||
|
onSubmitCollectionForm={(data) => handleSendMessage({ query: data })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import type { UserInputFileItemType } from '../ChatContainer/ChatBox/type';
|
||||||
|
|
||||||
export type onSendMessageParamsType = {
|
export type onSendMessageParamsType = {
|
||||||
query?: string;
|
query?: string;
|
||||||
|
collectionFormData?: string;
|
||||||
files?: UserInputFileItemType[];
|
files?: UserInputFileItemType[];
|
||||||
};
|
};
|
||||||
export type onSendMessageFnType = (e: onSendMessageParamsType) => Promise<any>;
|
export type onSendMessageFnType = (e: onSendMessageParamsType) => Promise<any>;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import {
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
Grid,
|
Grid,
|
||||||
useTheme,
|
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
Button,
|
Button,
|
||||||
HStack,
|
HStack,
|
||||||
|
|
@ -16,10 +15,9 @@ import { type AppFileSelectConfigType } from '@fastgpt/global/core/app/type/conf
|
||||||
import type { SelectedToolItemType, SkillEditType } from '@fastgpt/global/core/app/formEdit/type';
|
import type { SelectedToolItemType, SkillEditType } from '@fastgpt/global/core/app/formEdit/type';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useFieldArray, useForm } from 'react-hook-form';
|
||||||
|
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
|
|
@ -32,6 +30,8 @@ import { useContextSelector } from 'use-context-selector';
|
||||||
import { AppContext } from '@/pageComponents/app/detail/context';
|
import { AppContext } from '@/pageComponents/app/detail/context';
|
||||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
import { useSkillManager } from '../hooks/useSkillManager';
|
||||||
|
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||||
|
|
||||||
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
|
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
|
||||||
|
|
||||||
|
|
@ -52,7 +52,6 @@ const EditForm = ({
|
||||||
onClose,
|
onClose,
|
||||||
onSave
|
onSave
|
||||||
}: EditFormProps) => {
|
}: EditFormProps) => {
|
||||||
const theme = useTheme();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const appId = useContextSelector(AppContext, (v) => v.appId);
|
const appId = useContextSelector(AppContext, (v) => v.appId);
|
||||||
|
|
@ -64,7 +63,8 @@ const EditForm = ({
|
||||||
watch,
|
watch,
|
||||||
setValue,
|
setValue,
|
||||||
reset,
|
reset,
|
||||||
formState: { isDirty }
|
formState: { isDirty },
|
||||||
|
control
|
||||||
} = useForm<SkillEditType>({
|
} = useForm<SkillEditType>({
|
||||||
defaultValues: skill
|
defaultValues: skill
|
||||||
});
|
});
|
||||||
|
|
@ -75,10 +75,39 @@ const EditForm = ({
|
||||||
}, [skill, reset]);
|
}, [skill, reset]);
|
||||||
|
|
||||||
const selectedModel = getWebLLMModel(model);
|
const selectedModel = getWebLLMModel(model);
|
||||||
const selectedTools = watch('selectedTools') || [];
|
const stepsText = watch('stepsText') || '';
|
||||||
const selectDatasets = watch('dataset.list') || [];
|
const selectDatasets = watch('dataset.list') || [];
|
||||||
const skillName = watch('name');
|
const skillName = watch('name');
|
||||||
|
|
||||||
|
const {
|
||||||
|
fields: selectedTools,
|
||||||
|
prepend: prependSelectedTools,
|
||||||
|
remove: removeSelectedTools,
|
||||||
|
update: updateSelectedTools
|
||||||
|
} = useFieldArray({
|
||||||
|
control,
|
||||||
|
name: 'selectedTools',
|
||||||
|
keyName: '_id'
|
||||||
|
});
|
||||||
|
|
||||||
|
const { skillOption, selectedSkills, onClickSkill, onRemoveSkill, SkillModal } = useSkillManager({
|
||||||
|
topAgentSelectedTools,
|
||||||
|
selectedTools,
|
||||||
|
onDeleteTool: (id) => {
|
||||||
|
removeSelectedTools(selectedTools.findIndex((item) => item.id === id));
|
||||||
|
},
|
||||||
|
onUpdateOrAddTool: (tool) => {
|
||||||
|
const index = selectedTools.findIndex((item) => item.id === tool.id);
|
||||||
|
if (index === -1) {
|
||||||
|
prependSelectedTools(tool);
|
||||||
|
} else {
|
||||||
|
updateSelectedTools(index, tool);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canSelectFile: fileSelectConfig?.canSelectFile,
|
||||||
|
canSelectImg: fileSelectConfig?.canSelectImg
|
||||||
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen: isOpenDatasetSelect,
|
isOpen: isOpenDatasetSelect,
|
||||||
onOpen: onOpenKbSelect,
|
onOpen: onOpenKbSelect,
|
||||||
|
|
@ -204,7 +233,21 @@ const EditForm = ({
|
||||||
<FormLabel>{t('app:execution_steps')}</FormLabel>
|
<FormLabel>{t('app:execution_steps')}</FormLabel>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Box mt={2}>
|
<Box mt={2}>
|
||||||
<Textarea
|
<PromptEditor
|
||||||
|
minH={160}
|
||||||
|
title={t('app:execution_steps')}
|
||||||
|
placeholder={t('app:no_steps_yet')}
|
||||||
|
isRichText
|
||||||
|
skillOption={skillOption}
|
||||||
|
selectedSkills={selectedSkills}
|
||||||
|
onClickSkill={onClickSkill}
|
||||||
|
onRemoveSkill={onRemoveSkill}
|
||||||
|
value={stepsText}
|
||||||
|
onChange={(e) => {
|
||||||
|
setValue('stepsText', e);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* <Textarea
|
||||||
{...register('stepsText')}
|
{...register('stepsText')}
|
||||||
maxLength={1000000}
|
maxLength={1000000}
|
||||||
bg={'myGray.50'}
|
bg={'myGray.50'}
|
||||||
|
|
@ -213,7 +256,7 @@ const EditForm = ({
|
||||||
placeholder={t('app:no_steps_yet')}
|
placeholder={t('app:no_steps_yet')}
|
||||||
fontSize={'sm'}
|
fontSize={'sm'}
|
||||||
color={'myGray.900'}
|
color={'myGray.900'}
|
||||||
/>
|
/> */}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
@ -225,23 +268,16 @@ const EditForm = ({
|
||||||
selectedTools={selectedTools}
|
selectedTools={selectedTools}
|
||||||
fileSelectConfig={fileSelectConfig}
|
fileSelectConfig={fileSelectConfig}
|
||||||
onAddTool={(e) => {
|
onAddTool={(e) => {
|
||||||
setValue('selectedTools', [e, ...(selectedTools || [])], { shouldDirty: true });
|
prependSelectedTools(e);
|
||||||
}}
|
}}
|
||||||
onUpdateTool={(e) => {
|
onUpdateTool={(e) => {
|
||||||
setValue(
|
updateSelectedTools(
|
||||||
'selectedTools',
|
selectedTools.findIndex((item) => item.id === e.id),
|
||||||
selectedTools?.map((item) => (item.id === e.id ? e : item)) || [],
|
e
|
||||||
{ shouldDirty: true }
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
onRemoveTool={(id) => {
|
onRemoveTool={(id) => {
|
||||||
setValue(
|
removeSelectedTools(selectedTools.findIndex((item) => item.id === id));
|
||||||
'selectedTools',
|
|
||||||
selectedTools?.filter((item) => item.pluginId !== id) || [],
|
|
||||||
{
|
|
||||||
shouldDirty: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -338,6 +374,7 @@ const EditForm = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ConfirmModal />
|
<ConfirmModal />
|
||||||
|
<SkillModal />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
|
||||||
import type {
|
import type {
|
||||||
SkillOptionItemType,
|
SkillOptionItemType,
|
||||||
SkillItemType
|
SkillItemType
|
||||||
|
|
@ -8,20 +7,16 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
|
checkNeedsUserConfiguration,
|
||||||
getToolConfigStatus,
|
getToolConfigStatus,
|
||||||
validateToolConfiguration
|
validateToolConfiguration
|
||||||
} from '@fastgpt/global/core/app/formEdit/utils';
|
} from '@fastgpt/global/core/app/formEdit/utils';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import {
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
FlowNodeInputTypeEnum,
|
|
||||||
FlowNodeTypeEnum
|
|
||||||
} from '@fastgpt/global/core/workflow/node/constant';
|
|
||||||
import { workflowStartNodeId } from '@/web/core/app/constants';
|
|
||||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
import type { SkillLabelItemType } from '@fastgpt/web/components/common/Textarea/PromptEditor/plugins/SkillLabelPlugin';
|
import type { SkillLabelItemType } from '@fastgpt/web/components/common/Textarea/PromptEditor/plugins/SkillLabelPlugin';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import type { AppFormEditFormType } from '@fastgpt/global/core/app/formEdit/type';
|
import type { SelectedToolItemType } from '@fastgpt/global/core/app/formEdit/type';
|
||||||
import {
|
import {
|
||||||
getAppToolTemplates,
|
getAppToolTemplates,
|
||||||
getToolPreviewNode,
|
getToolPreviewNode,
|
||||||
|
|
@ -46,15 +41,15 @@ const isSubApp = (flowNodeType: FlowNodeTypeEnum) => {
|
||||||
return !!subAppTypeMap[flowNodeType];
|
return !!subAppTypeMap[flowNodeType];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SelectedToolItemType = AppFormEditFormType['selectedTools'][number];
|
|
||||||
|
|
||||||
export const useSkillManager = ({
|
export const useSkillManager = ({
|
||||||
|
topAgentSelectedTools,
|
||||||
selectedTools,
|
selectedTools,
|
||||||
onUpdateOrAddTool,
|
onUpdateOrAddTool,
|
||||||
onDeleteTool,
|
onDeleteTool,
|
||||||
canSelectFile,
|
canSelectFile,
|
||||||
canSelectImg
|
canSelectImg
|
||||||
}: {
|
}: {
|
||||||
|
topAgentSelectedTools: SelectedToolItemType[];
|
||||||
selectedTools: SelectedToolItemType[];
|
selectedTools: SelectedToolItemType[];
|
||||||
onDeleteTool: (id: string) => void;
|
onDeleteTool: (id: string) => void;
|
||||||
onUpdateOrAddTool: (tool: SelectedToolItemType) => void;
|
onUpdateOrAddTool: (tool: SelectedToolItemType) => void;
|
||||||
|
|
@ -70,26 +65,33 @@ export const useSkillManager = ({
|
||||||
const data = await getAppToolTemplates({ getAll: true }).catch((err) => {
|
const data = await getAppToolTemplates({ getAll: true }).catch((err) => {
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
return data.map<SkillItemType>((item) => {
|
return data
|
||||||
return {
|
.map<SkillItemType>((item) => {
|
||||||
id: item.id,
|
return {
|
||||||
parentId: item.parentId,
|
id: item.id,
|
||||||
label: item.name,
|
parentId: item.parentId,
|
||||||
icon: item.avatar,
|
label: item.name,
|
||||||
showArrow: item.isFolder,
|
icon: item.avatar,
|
||||||
canClick: true
|
description: item.intro,
|
||||||
};
|
showArrow: item.isFolder,
|
||||||
});
|
canClick: true,
|
||||||
|
tools: data
|
||||||
|
.filter((tool) => tool.parentId === item.id)
|
||||||
|
.map((tool) => ({
|
||||||
|
id: tool.id,
|
||||||
|
name: tool.name
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((item) => !item.parentId);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
manual: false
|
manual: false
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const onLoadSystemTool = useCallback(
|
const onLoadSystemTool = useCallback(
|
||||||
async ({ parentId = null }: { parentId?: ParentIdType; searchKey?: string }) => {
|
async ({}: { searchKey?: string }) => {
|
||||||
return systemTools.filter((tool) => {
|
return systemTools;
|
||||||
return tool.parentId === parentId;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[systemTools]
|
[systemTools]
|
||||||
);
|
);
|
||||||
|
|
@ -145,19 +147,21 @@ export const useSkillManager = ({
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onAddAppOrTool = useCallback(
|
const onAddAppOrTool = useCallback(
|
||||||
async (appId: string) => {
|
async (toolId: string) => {
|
||||||
const toolTemplate = await getToolPreviewNode({ appId });
|
// Check tool exists, if exists, not update/add tool
|
||||||
|
const existsTool = selectedTools.find((tool) => tool.pluginId === toolId);
|
||||||
if (!toolTemplate) {
|
if (existsTool) {
|
||||||
return;
|
return existsTool.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkRes = validateToolConfiguration({
|
const toolTemplate = await getToolPreviewNode({ appId: toolId });
|
||||||
|
|
||||||
|
const toolValid = validateToolConfiguration({
|
||||||
toolTemplate,
|
toolTemplate,
|
||||||
canSelectFile,
|
canSelectFile,
|
||||||
canSelectImg
|
canSelectImg
|
||||||
});
|
});
|
||||||
if (!checkRes) {
|
if (!toolValid) {
|
||||||
toast({
|
toast({
|
||||||
title: t('app:simple_tool_tips'),
|
title: t('app:simple_tool_tips'),
|
||||||
status: 'warning'
|
status: 'warning'
|
||||||
|
|
@ -165,19 +169,20 @@ export const useSkillManager = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加与 top 相同工具的配置
|
||||||
|
const topTool = topAgentSelectedTools.find((tool) => tool.pluginId === toolTemplate.pluginId);
|
||||||
|
if (topTool) {
|
||||||
|
toolTemplate.inputs.forEach((input) => {
|
||||||
|
const topInput = topTool.inputs.find((tInput) => tInput.key === input.key);
|
||||||
|
if (topInput) {
|
||||||
|
input.value = topInput.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const tool: SelectedToolItemType = {
|
const tool: SelectedToolItemType = {
|
||||||
...toolTemplate,
|
...toolTemplate,
|
||||||
id: `tool_${getNanoid(6)}`,
|
id: toolId
|
||||||
inputs: toolTemplate.inputs.map((input) => {
|
|
||||||
// 如果是文件上传类型,设置为从工作流开始节点获取用户文件
|
|
||||||
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)) {
|
|
||||||
return {
|
|
||||||
...input,
|
|
||||||
value: [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onUpdateOrAddTool({
|
onUpdateOrAddTool({
|
||||||
|
|
@ -187,7 +192,7 @@ export const useSkillManager = ({
|
||||||
|
|
||||||
return tool.id;
|
return tool.id;
|
||||||
},
|
},
|
||||||
[canSelectFile, canSelectImg, onUpdateOrAddTool, t, toast]
|
[canSelectFile, canSelectImg, onUpdateOrAddTool, selectedTools, t, toast, topAgentSelectedTools]
|
||||||
);
|
);
|
||||||
|
|
||||||
/* ===== Skill option ===== */
|
/* ===== Skill option ===== */
|
||||||
|
|
@ -195,21 +200,9 @@ export const useSkillManager = ({
|
||||||
return {
|
return {
|
||||||
onSelect: async (id: string) => {
|
onSelect: async (id: string) => {
|
||||||
if (id === 'systemTool') {
|
if (id === 'systemTool') {
|
||||||
const data = await onLoadSystemTool({ parentId: null });
|
const data = await onLoadSystemTool({});
|
||||||
return {
|
return {
|
||||||
description: t('app:can_select_toolset'),
|
|
||||||
list: data,
|
list: data,
|
||||||
onSelect: async (id: string) => {
|
|
||||||
const data = await onLoadSystemTool({ parentId: id });
|
|
||||||
return {
|
|
||||||
onClick: onAddAppOrTool,
|
|
||||||
list: data.map((item) => ({
|
|
||||||
id: item.id,
|
|
||||||
label: item.label,
|
|
||||||
canClick: true
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onClick: onAddAppOrTool
|
onClick: onAddAppOrTool
|
||||||
};
|
};
|
||||||
} else if (id === 'myTools') {
|
} else if (id === 'myTools') {
|
||||||
|
|
@ -277,8 +270,8 @@ export const useSkillManager = ({
|
||||||
if (!tool) return;
|
if (!tool) return;
|
||||||
|
|
||||||
if (isSubApp(tool.flowNodeType)) {
|
if (isSubApp(tool.flowNodeType)) {
|
||||||
const { needConfig } = getToolConfigStatus(tool);
|
const hasFormInput = checkNeedsUserConfiguration(tool);
|
||||||
if (!needConfig) return;
|
if (!hasFormInput) return;
|
||||||
setConfigTool(tool);
|
setConfigTool(tool);
|
||||||
} else {
|
} else {
|
||||||
console.log('onClickSkill', id);
|
console.log('onClickSkill', id);
|
||||||
|
|
|
||||||
|
|
@ -308,14 +308,20 @@ const RenderList = React.memo(function RenderList({
|
||||||
key={template.id}
|
key={template.id}
|
||||||
label={
|
label={
|
||||||
<Box py={2} minW={['auto', '250px']}>
|
<Box py={2} minW={['auto', '250px']}>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'} w={'100%'}>
|
||||||
<MyAvatar
|
<MyAvatar
|
||||||
src={template.avatar}
|
src={template.avatar}
|
||||||
w={'1.75rem'}
|
w={'1.75rem'}
|
||||||
objectFit={'contain'}
|
objectFit={'contain'}
|
||||||
borderRadius={'sm'}
|
borderRadius={'sm'}
|
||||||
/>
|
/>
|
||||||
<Box fontWeight={'bold'} ml={3} color={'myGray.900'} overflow={'hidden'}>
|
<Box
|
||||||
|
fontWeight={'bold'}
|
||||||
|
ml={3}
|
||||||
|
color={'myGray.900'}
|
||||||
|
flex={'1 0 0'}
|
||||||
|
overflow={'hidden'}
|
||||||
|
>
|
||||||
{t(parseI18nString(template.name, i18n.language))}
|
{t(parseI18nString(template.name, i18n.language))}
|
||||||
</Box>
|
</Box>
|
||||||
{isSystemTool && (
|
{isSystemTool && (
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ async function handler(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toolNode,
|
...toolNode,
|
||||||
|
id: toolNode.pluginId!,
|
||||||
inputs: mergedInputs
|
inputs: mergedInputs
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import type { OnOptimizePromptProps } from '@/components/common/PromptEditor/Opt
|
||||||
import type { OnOptimizeCodeProps } from '@/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeCode/Copilot';
|
import type { OnOptimizeCodeProps } from '@/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeCode/Copilot';
|
||||||
import type { AIChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
import type { AIChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||||
import type { TopAgentFormDataType } from '@fastgpt/service/core/chat/HelperBot/dispatch/topAgent/type';
|
import type { TopAgentFormDataType } from '@fastgpt/service/core/chat/HelperBot/dispatch/topAgent/type';
|
||||||
|
import type { UserInputInteractive } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
|
|
||||||
type StreamFetchProps = {
|
type StreamFetchProps = {
|
||||||
url?: string;
|
url?: string;
|
||||||
|
|
@ -52,7 +53,15 @@ type ResponseQueueItemType = CommonResponseType &
|
||||||
tools: any;
|
tools: any;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
event: SseResponseEventEnum.formData;
|
event: SseResponseEventEnum.collectionForm;
|
||||||
|
collectionForm: UserInputInteractive;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
event: SseResponseEventEnum.generatedSkill;
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
event: SseResponseEventEnum.topAgentConfig;
|
||||||
data: TopAgentFormDataType;
|
data: TopAgentFormDataType;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -274,7 +283,12 @@ export const streamFetch = ({
|
||||||
event,
|
event,
|
||||||
agentPlan: rest.agentPlan
|
agentPlan: rest.agentPlan
|
||||||
});
|
});
|
||||||
} else if (event === SseResponseEventEnum.formData) {
|
} else if (event === SseResponseEventEnum.collectionForm) {
|
||||||
|
onMessage({
|
||||||
|
event,
|
||||||
|
collectionForm: rest
|
||||||
|
});
|
||||||
|
} else if (event === SseResponseEventEnum.topAgentConfig) {
|
||||||
// Directly call onMessage for formData, no need to queue
|
// Directly call onMessage for formData, no need to queue
|
||||||
onMessage({
|
onMessage({
|
||||||
event,
|
event,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue