mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-25 20:02:47 +00:00
skill agent (#6089)
* cp skill chat
* rebase fdf933d
and add skill chat
* 1. skill 的 CRUD
2. skill 的信息渲染到前端界面
* solve comment
* remove chatid and chatItemId
* skill match
* perf: skill manage
* fix: ts
---------
Co-authored-by: xxyyh <2289112474@qq>
Co-authored-by: archer <545436317@qq.com>
This commit is contained in:
parent
2c0de7d80e
commit
501f263e1b
|
|
@ -0,0 +1,23 @@
|
|||
import { ObjectIdSchema } from '../../../common/type/mongo';
|
||||
import z from 'zod';
|
||||
|
||||
export const SkillToolSchema = z.object({
|
||||
id: z.string(),
|
||||
config: z.record(z.string(), z.any())
|
||||
});
|
||||
export type SkillToolType = z.infer<typeof SkillToolSchema>;
|
||||
|
||||
export const AiSkillSchema = z.object({
|
||||
_id: ObjectIdSchema,
|
||||
teamId: ObjectIdSchema,
|
||||
tmbId: ObjectIdSchema,
|
||||
appId: ObjectIdSchema,
|
||||
createTime: z.date(),
|
||||
updateTime: z.date(),
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
steps: z.string().default(''),
|
||||
tools: z.array(SkillToolSchema),
|
||||
datasets: z.array(z.any())
|
||||
});
|
||||
export type AiSkillSchemaType = z.infer<typeof AiSkillSchema>;
|
||||
|
|
@ -23,12 +23,11 @@ export const SkillEditTypeSchema = z.object({
|
|||
id: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
prompt: z.string(),
|
||||
stepsText: z.string().optional(), // 执行步骤的文本描述
|
||||
dataset: z.object({
|
||||
list: z.array(SelectedDatasetSchema)
|
||||
}),
|
||||
selectedTools: z.array(SelectedToolItemTypeSchema),
|
||||
fileSelectConfig: AppFileSelectConfigTypeSchema
|
||||
selectedTools: z.array(SelectedToolItemTypeSchema)
|
||||
});
|
||||
export type SkillEditType = z.infer<typeof SkillEditTypeSchema>;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
import { z } from 'zod';
|
||||
import { topAgentParamsSchema } from '../topAgent/type';
|
||||
|
||||
// SkillAgent 参数配置 (区分 skill 特有配置和 topAgent 通用配置)
|
||||
export const skillAgentParamsSchema = z.object({
|
||||
// Skill 特有配置
|
||||
skillAgent: z
|
||||
.object({
|
||||
name: z.string().nullish(),
|
||||
description: z.string().nullish(),
|
||||
stepsText: z.string().nullish()
|
||||
})
|
||||
.nullish(),
|
||||
// TopAgent 通用配置
|
||||
topAgent: topAgentParamsSchema.nullish()
|
||||
});
|
||||
export type SkillAgentParamsType = z.infer<typeof skillAgentParamsSchema>;
|
||||
|
||||
/* 模型生成结构 */
|
||||
// 工具定义
|
||||
export const GeneratedSkillToolSchema = z.object({
|
||||
id: z.string(),
|
||||
type: z.enum(['tool', 'knowledge'])
|
||||
});
|
||||
|
||||
// 步骤定义
|
||||
export const GeneratedSkillStepSchema = z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
expectedTools: z.array(GeneratedSkillToolSchema)
|
||||
});
|
||||
|
||||
// 任务分析
|
||||
const TaskAnalysisSchema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
goal: z.string(),
|
||||
type: z.string()
|
||||
});
|
||||
|
||||
// LLM 返回的完整数据
|
||||
export const GeneratedSkillDataCollectionSchema = z.object({
|
||||
phase: z.literal('collection'),
|
||||
reasoning: z.string(),
|
||||
question: z.string()
|
||||
});
|
||||
export type GeneratedSkillDataCollectionType = z.infer<typeof GeneratedSkillDataCollectionSchema>;
|
||||
export const GeneratedSkillResultSchema = z.object({
|
||||
phase: z.literal('generation'),
|
||||
plan_analysis: TaskAnalysisSchema,
|
||||
execution_plan: z.object({
|
||||
total_steps: z.number(),
|
||||
steps: z.array(GeneratedSkillStepSchema)
|
||||
})
|
||||
});
|
||||
export type GeneratedSkillResultType = z.infer<typeof GeneratedSkillResultSchema>;
|
||||
export const GeneratedSkillSchema = z.union([
|
||||
GeneratedSkillDataCollectionSchema,
|
||||
GeneratedSkillResultSchema
|
||||
]);
|
||||
export type GeneratedSkillType = z.infer<typeof GeneratedSkillSchema>;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
// TopAgent 参数配置
|
||||
export const topAgentParamsSchema = z.object({
|
||||
role: z.string().nullish(),
|
||||
taskObject: z.string().nullish(),
|
||||
selectedTools: z.array(z.string()).nullish(),
|
||||
selectedDatasets: z.array(z.string()).nullish(),
|
||||
fileUpload: z.boolean().nullish()
|
||||
});
|
||||
export type TopAgentParamsType = z.infer<typeof topAgentParamsSchema>;
|
||||
|
|
@ -5,7 +5,7 @@ import { UserChatItemSchema, SystemChatItemSchema, ToolModuleResponseItemSchema
|
|||
|
||||
export enum HelperBotTypeEnum {
|
||||
topAgent = 'topAgent',
|
||||
skillEditor = 'skillEditor'
|
||||
skillAgent = 'skillAgent'
|
||||
}
|
||||
export const HelperBotTypeEnumSchema = z.enum(Object.values(HelperBotTypeEnum));
|
||||
export type HelperBotTypeEnumType = z.infer<typeof HelperBotTypeEnumSchema>;
|
||||
|
|
@ -70,19 +70,3 @@ export const HelperBotChatItemSiteSchema = z
|
|||
})
|
||||
.and(HelperBotChatRoleSchema);
|
||||
export type HelperBotChatItemSiteType = z.infer<typeof HelperBotChatItemSiteSchema>;
|
||||
|
||||
/* 具体的 bot 的特有参数 */
|
||||
|
||||
// Top agent
|
||||
export const topAgentParamsSchema = z.object({
|
||||
role: z.string().nullish(),
|
||||
taskObject: z.string().nullish(),
|
||||
selectedTools: z.array(z.string()).nullish(),
|
||||
selectedDatasets: z.array(z.string()).nullish(),
|
||||
fileUpload: z.boolean().nullish()
|
||||
});
|
||||
export type TopAgentParamsType = z.infer<typeof topAgentParamsSchema>;
|
||||
|
||||
// Skill editor
|
||||
export const skillEditorParamsSchema = z.object({});
|
||||
export type SkillEditorParamsType = z.infer<typeof skillEditorParamsSchema>;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ export enum SseResponseEventEnum {
|
|||
|
||||
agentPlan = 'agentPlan', // agent plan
|
||||
|
||||
formData = 'formData' // form data for TopAgent
|
||||
formData = 'formData', // form data for TopAgent
|
||||
generatedSkill = 'generatedSkill' // generated skill for SkillAgent
|
||||
}
|
||||
|
||||
export enum DispatchNodeResponseKeyEnum {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
import type { OpenAPIPath } from '../../type';
|
||||
import { AISkillPath } from './skill';
|
||||
|
||||
export const AIPath: OpenAPIPath = {
|
||||
...AISkillPath
|
||||
};
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import { z } from 'zod';
|
||||
import { AiSkillSchema, SkillToolSchema } from '../../../../core/ai/skill/type';
|
||||
import { SelectedToolItemTypeSchema } from '../../../../core/app/formEdit/type';
|
||||
import { ObjectIdSchema } from '../../../../common/type/mongo';
|
||||
|
||||
export const ListAiSkillBody = z.object({
|
||||
appId: z.string(),
|
||||
searchText: z.string().optional()
|
||||
});
|
||||
export type ListAiSkillBodyType = z.infer<typeof ListAiSkillBody>;
|
||||
// Simplified list item schema - only id and name
|
||||
export const ListAiSkillItemSchema = z.object({
|
||||
_id: ObjectIdSchema,
|
||||
name: z.string()
|
||||
});
|
||||
export const ListAiSkillResponseSchema = z.array(ListAiSkillItemSchema);
|
||||
export type ListAiSkillResponse = z.infer<typeof ListAiSkillResponseSchema>;
|
||||
|
||||
export const GetAiSkillDetailQuery = z.object({
|
||||
id: z.string()
|
||||
});
|
||||
export type GetAiSkillDetailQueryType = z.infer<typeof GetAiSkillDetailQuery>;
|
||||
// Detail response with expanded tools
|
||||
export const GetAiSkillDetailResponseSchema = AiSkillSchema.omit({
|
||||
tools: true,
|
||||
teamId: true,
|
||||
tmbId: true,
|
||||
appId: true,
|
||||
createTime: true,
|
||||
updateTime: true
|
||||
}).extend({
|
||||
tools: z.array(SelectedToolItemTypeSchema)
|
||||
});
|
||||
export type GetAiSkillDetailResponse = z.infer<typeof GetAiSkillDetailResponseSchema>;
|
||||
|
||||
export const UpdateAiSkillBody = z.object({
|
||||
id: z.string().optional(),
|
||||
appId: z.string(), // Required for creating new skill, optional for updating existing skill
|
||||
name: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
steps: z.string().optional(),
|
||||
tools: z.array(SkillToolSchema).optional(),
|
||||
datasets: z.array(z.any()).optional()
|
||||
});
|
||||
export type UpdateAiSkillBodyType = z.infer<typeof UpdateAiSkillBody>;
|
||||
export const UpdateAiSkillResponseSchema = z.string();
|
||||
export type UpdateAiSkillResponse = z.infer<typeof UpdateAiSkillResponseSchema>;
|
||||
|
||||
export const DeleteAiSkillQuery = z.object({
|
||||
id: z.string()
|
||||
});
|
||||
export type DeleteAiSkillQueryType = z.infer<typeof DeleteAiSkillQuery>;
|
||||
export const DeleteAiSkillResponseSchema = z.object({});
|
||||
export type DeleteAiSkillResponse = z.infer<typeof DeleteAiSkillResponseSchema>;
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
import type { OpenAPIPath } from '../../../type';
|
||||
import {
|
||||
ListAiSkillBody,
|
||||
GetAiSkillDetailQuery,
|
||||
UpdateAiSkillBody,
|
||||
DeleteAiSkillQuery
|
||||
} from './api';
|
||||
import { TagsMap } from '../../../tag';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const AISkillPath: OpenAPIPath = {
|
||||
'/core/ai/skill/list': {
|
||||
post: {
|
||||
summary: '获取AI技能列表',
|
||||
description: '获取指定应用的AI技能列表,支持分页和搜索',
|
||||
tags: [TagsMap.aiSkill],
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: ListAiSkillBody
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取技能列表',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.object({
|
||||
list: z.array(z.any()),
|
||||
total: z.number()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/core/ai/skill/detail': {
|
||||
get: {
|
||||
summary: '获取AI技能详情',
|
||||
description: '根据技能ID获取详细信息,会自动获取对应的应用权限进行鉴权',
|
||||
tags: [TagsMap.aiSkill],
|
||||
requestParams: {
|
||||
query: GetAiSkillDetailQuery
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取技能详情',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.any()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/core/ai/skill/update': {
|
||||
put: {
|
||||
summary: '更新或创建AI技能',
|
||||
description:
|
||||
'使用 upsert 方式更新或创建AI技能。如果提供 id 则更新现有技能(会自动获取 appId 进行鉴权),如果不提供 id 则创建新技能(需提供 appId)',
|
||||
tags: [TagsMap.aiSkill],
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: UpdateAiSkillBody
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功更新或创建技能',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.object({
|
||||
success: z.boolean(),
|
||||
_id: z.string()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/core/ai/skill/delete': {
|
||||
delete: {
|
||||
summary: '删除AI技能',
|
||||
description: '根据技能ID删除AI技能,会自动获取对应的应用权限进行鉴权',
|
||||
tags: [TagsMap.aiSkill],
|
||||
requestParams: {
|
||||
query: DeleteAiSkillQuery
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功删除技能',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.object({
|
||||
success: z.boolean()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import { PaginationPropsSchema, PaginationResponseSchema } from '../../../type';
|
||||
import { PaginationPropsSchema } from '../../../type';
|
||||
import {
|
||||
type HelperBotChatItemSiteType,
|
||||
HelperBotTypeEnum,
|
||||
HelperBotTypeEnumSchema,
|
||||
skillEditorParamsSchema,
|
||||
topAgentParamsSchema
|
||||
HelperBotTypeEnumSchema
|
||||
} from '../../../../core/chat/helperBot/type';
|
||||
import { topAgentParamsSchema } from '../../../../core/chat/helperBot/topAgent/type';
|
||||
import { skillAgentParamsSchema } from '../../../../core/chat/helperBot/skillAgent/type';
|
||||
import { z } from 'zod';
|
||||
import type { PaginationResponse } from '../../../../../web/common/fetch/type';
|
||||
import { ChatFileTypeEnum } from '../../../../core/chat/constants';
|
||||
|
|
@ -61,8 +61,8 @@ export const HelperBotCompletionsParamsSchema = z.object({
|
|||
data: topAgentParamsSchema
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(HelperBotTypeEnum.skillEditor),
|
||||
data: skillEditorParamsSchema
|
||||
type: z.literal(HelperBotTypeEnum.skillAgent),
|
||||
data: skillAgentParamsSchema
|
||||
})
|
||||
])
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
import { z } from 'zod';
|
||||
import { PaginationPropsSchema } from '../../../../type';
|
||||
import type { PaginationResponse } from '../../../../../../web/common/fetch/type';
|
||||
import { type GeneratedSkillSiteType } from '../../../../../core/chat/helperBot/skillAgent/type';
|
||||
|
||||
// Save Generated Skill
|
||||
export const SaveGeneratedSkillParamsSchema = z.object({
|
||||
appId: z.string(),
|
||||
chatId: z.string(),
|
||||
chatItemId: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
steps: z.string().default(''),
|
||||
status: z.enum(['draft', 'active', 'archived']).optional()
|
||||
});
|
||||
export type SaveGeneratedSkillParamsType = z.infer<typeof SaveGeneratedSkillParamsSchema>;
|
||||
export const SaveGeneratedSkillResponseSchema = z.object({
|
||||
_id: z.string()
|
||||
});
|
||||
export type SaveGeneratedSkillResponseType = z.infer<typeof SaveGeneratedSkillResponseSchema>;
|
||||
|
||||
// Get Generated Skills List
|
||||
export const GetGeneratedSkillsParamsSchema = z
|
||||
.object({
|
||||
appId: z.string(),
|
||||
searchText: z.string().optional(),
|
||||
status: z.enum(['draft', 'active', 'archived']).optional()
|
||||
})
|
||||
.and(PaginationPropsSchema);
|
||||
export type GetGeneratedSkillsParamsType = z.infer<typeof GetGeneratedSkillsParamsSchema>;
|
||||
export type GetGeneratedSkillsResponseType = PaginationResponse<GeneratedSkillSiteType>;
|
||||
|
||||
// Get Generated Skill Detail
|
||||
export const GetGeneratedSkillDetailParamsSchema = z.object({
|
||||
id: z.string()
|
||||
});
|
||||
export type GetGeneratedSkillDetailParamsType = z.infer<typeof GetGeneratedSkillDetailParamsSchema>;
|
||||
|
||||
// Update Generated Skill
|
||||
export const UpdateGeneratedSkillParamsSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
steps: z.string().optional(),
|
||||
status: z.enum(['draft', 'active', 'archived']).optional()
|
||||
});
|
||||
export type UpdateGeneratedSkillParamsType = z.infer<typeof UpdateGeneratedSkillParamsSchema>;
|
||||
|
||||
// Delete Generated Skill
|
||||
export const DeleteGeneratedSkillParamsSchema = z.object({
|
||||
id: z.string()
|
||||
});
|
||||
export type DeleteGeneratedSkillParamsType = z.infer<typeof DeleteGeneratedSkillParamsSchema>;
|
||||
|
|
@ -6,6 +6,7 @@ import { PluginPath } from './core/plugin';
|
|||
import { WalletPath } from './support/wallet';
|
||||
import { CustomDomainPath } from './support/customDomain';
|
||||
import { AppPath } from './core/app';
|
||||
import { AIPath } from './core/ai';
|
||||
|
||||
export const openAPIDocument = createDocument({
|
||||
openapi: '3.1.0',
|
||||
|
|
@ -20,7 +21,8 @@ export const openAPIDocument = createDocument({
|
|||
...ApiKeyPath,
|
||||
...PluginPath,
|
||||
...WalletPath,
|
||||
...CustomDomainPath
|
||||
...CustomDomainPath,
|
||||
...AIPath
|
||||
},
|
||||
servers: [{ url: '/api' }],
|
||||
'x-tagGroups': [
|
||||
|
|
@ -28,6 +30,14 @@ export const openAPIDocument = createDocument({
|
|||
name: 'Agent 应用',
|
||||
tags: [TagsMap.appLog]
|
||||
},
|
||||
{
|
||||
name: 'AI 相关',
|
||||
tags: [TagsMap.aiSkill]
|
||||
},
|
||||
{
|
||||
name: '对话',
|
||||
tags: [TagsMap.chatSetting, TagsMap.chatPage]
|
||||
},
|
||||
{
|
||||
name: '对话管理',
|
||||
tags: [TagsMap.chatHistory, TagsMap.chatPage, TagsMap.chatFeedback, TagsMap.chatSetting]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
export const TagsMap = {
|
||||
/* Core */
|
||||
// Helper
|
||||
helperBot: '辅助助手',
|
||||
// Agent - log
|
||||
appLog: 'Agent 日志',
|
||||
// Ai -skill
|
||||
aiSkill: 'AI技能管理',
|
||||
|
||||
// Chat - home
|
||||
chatPage: '对话页操作',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||
const { Schema } = connectionMongo;
|
||||
import { AppCollectionName } from '../../app/schema';
|
||||
import type { AiSkillSchemaType } from '@fastgpt/global/core/ai/skill/type';
|
||||
|
||||
const AppAISkillSchema = new Schema({
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
required: true,
|
||||
ref: TeamCollectionName
|
||||
},
|
||||
tmbId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
required: true,
|
||||
ref: TeamMemberCollectionName
|
||||
},
|
||||
appId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
required: true,
|
||||
ref: AppCollectionName
|
||||
},
|
||||
createTime: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
updateTime: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
description: String,
|
||||
steps: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
tools: {
|
||||
type: [Object],
|
||||
default: []
|
||||
},
|
||||
datasets: {
|
||||
type: [Object],
|
||||
default: []
|
||||
}
|
||||
});
|
||||
|
||||
// 复合索引
|
||||
AppAISkillSchema.index({ teamId: 1, appId: 1, updateTime: -1 });
|
||||
|
||||
export const MongoAiSkill = getMongoModel<AiSkillSchemaType>('app_ai_skills', AppAISkillSchema);
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { HelperBotTypeEnum } from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import { dispatchTopAgent } from './topAgent';
|
||||
import { dispatchSkillEditor } from './skillEditor';
|
||||
import { dispatchSkillAgent } from './skillAgent';
|
||||
|
||||
export const dispatchMap = {
|
||||
[HelperBotTypeEnum.topAgent]: dispatchTopAgent,
|
||||
[HelperBotTypeEnum.skillEditor]: dispatchSkillEditor
|
||||
[HelperBotTypeEnum.skillAgent]: dispatchSkillAgent
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,149 @@
|
|||
import type { HelperBotDispatchParamsType, HelperBotDispatchResponseType } from '../type';
|
||||
import { helperChats2GPTMessages } from '@fastgpt/global/core/chat/helperBot/adaptor';
|
||||
import { getPrompt } from './prompt';
|
||||
import { createLLMResponse } from '../../../../ai/llm/request';
|
||||
import { getLLMModel } from '../../../../ai/model';
|
||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { generateResourceList } from '../topAgent/utils';
|
||||
import { addLog } from '../../../../../common/system/log';
|
||||
import { formatAIResponse } from '../utils';
|
||||
import {
|
||||
type SkillAgentParamsType,
|
||||
type GeneratedSkillType,
|
||||
GeneratedSkillResultSchema
|
||||
} from '@fastgpt/global/core/chat/helperBot/skillAgent/type';
|
||||
import { parseJsonArgs } from '../../../../ai/utils';
|
||||
|
||||
export const dispatchSkillAgent = async (
|
||||
props: HelperBotDispatchParamsType<SkillAgentParamsType>
|
||||
): Promise<HelperBotDispatchResponseType> => {
|
||||
const { query, files, data, histories, workflowResponseWrite, user } = props;
|
||||
|
||||
const modelData = getLLMModel();
|
||||
if (!modelData) {
|
||||
return Promise.reject('Can not get model data');
|
||||
}
|
||||
|
||||
const usage = {
|
||||
model: modelData.model,
|
||||
inputTokens: 0,
|
||||
outputTokens: 0
|
||||
};
|
||||
|
||||
const resourceList = await generateResourceList({
|
||||
teamId: user.teamId,
|
||||
isRoot: user.isRoot
|
||||
});
|
||||
|
||||
const systemPrompt = getPrompt({
|
||||
resourceList,
|
||||
metadata: data
|
||||
});
|
||||
|
||||
const historyMessages = helperChats2GPTMessages({
|
||||
messages: histories,
|
||||
reserveTool: false
|
||||
});
|
||||
const conversationMessages = [
|
||||
{ role: 'system' as const, content: systemPrompt },
|
||||
...historyMessages,
|
||||
{ role: 'user' as const, content: query }
|
||||
];
|
||||
|
||||
console.dir(conversationMessages, { depth: null });
|
||||
// Single LLM call - LLM self-determines phase and outputs corresponding format
|
||||
const llmResponse = await createLLMResponse({
|
||||
body: {
|
||||
messages: conversationMessages,
|
||||
model: modelData,
|
||||
stream: true
|
||||
},
|
||||
onStreaming: ({ text }) => {
|
||||
workflowResponseWrite?.({
|
||||
event: SseResponseEventEnum.answer,
|
||||
data: textAdaptGptResponse({ text })
|
||||
});
|
||||
},
|
||||
onReasoning: ({ text }) => {
|
||||
workflowResponseWrite?.({
|
||||
event: SseResponseEventEnum.answer,
|
||||
data: textAdaptGptResponse({ reasoning_content: text })
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
usage.inputTokens = llmResponse.usage.inputTokens;
|
||||
usage.outputTokens = llmResponse.usage.outputTokens;
|
||||
|
||||
const answerText = llmResponse.answerText;
|
||||
const reasoningText = llmResponse.reasoningText;
|
||||
|
||||
// Parse JSON response
|
||||
try {
|
||||
const responseJson = parseJsonArgs<GeneratedSkillType>(answerText);
|
||||
|
||||
if (!responseJson) {
|
||||
addLog.warn(`[Skill agent] Failed to parse JSON response`, { text: answerText });
|
||||
throw new Error('Failed to parse JSON response');
|
||||
}
|
||||
console.log(responseJson, 22323);
|
||||
// Handle based on phase field
|
||||
if (responseJson.phase === 'generation') {
|
||||
addLog.debug('🔄 SkillAgent: Generated skill generation phase');
|
||||
|
||||
const parseResult = GeneratedSkillResultSchema.safeParse(responseJson).data;
|
||||
|
||||
if (!parseResult) {
|
||||
addLog.warn(`[Skill agent] Failed to parse JSON response`, { responseJson });
|
||||
throw new Error('Failed to parse JSON response');
|
||||
}
|
||||
|
||||
// Send generatedSkill event
|
||||
workflowResponseWrite?.({
|
||||
event: SseResponseEventEnum.generatedSkill,
|
||||
data: parseResult
|
||||
});
|
||||
|
||||
// Return original format (backward compatible)
|
||||
return {
|
||||
aiResponse: formatAIResponse({
|
||||
text: answerText,
|
||||
reasoning: reasoningText
|
||||
}),
|
||||
usage
|
||||
};
|
||||
} else if (responseJson.phase === 'collection') {
|
||||
addLog.debug('📝 SkillAgent: Information collection phase');
|
||||
|
||||
const displayText = responseJson.question || answerText;
|
||||
return {
|
||||
aiResponse: formatAIResponse({
|
||||
text: displayText,
|
||||
reasoning: responseJson.reasoning || reasoningText
|
||||
}),
|
||||
usage
|
||||
};
|
||||
} else {
|
||||
// Unknown phase
|
||||
addLog.warn(`[Skill agent] Unknown phase`, responseJson);
|
||||
return {
|
||||
aiResponse: formatAIResponse({
|
||||
text: answerText,
|
||||
reasoning: reasoningText
|
||||
}),
|
||||
usage
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
// JSON parse failed - return original text
|
||||
addLog.warn(`[Skill agent] Failed to parse JSON response`, { text: answerText });
|
||||
return {
|
||||
aiResponse: formatAIResponse({
|
||||
text: answerText,
|
||||
reasoning: reasoningText
|
||||
}),
|
||||
usage
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,425 @@
|
|||
import type { SkillAgentParamsType } from '@fastgpt/global/core/chat/helperBot/skillAgent/type';
|
||||
import { buildSkillAgentMetadataInfo } from './utils';
|
||||
|
||||
export const getPrompt = ({
|
||||
resourceList,
|
||||
metadata
|
||||
}: {
|
||||
resourceList: string;
|
||||
metadata?: SkillAgentParamsType;
|
||||
}) => {
|
||||
const currentConfigContext = buildSkillAgentMetadataInfo(metadata);
|
||||
// console.log('------currentConfigContext------', currentConfigContext);
|
||||
return `<!-- 任务执行流程设计系统 -->
|
||||
|
||||
<role>
|
||||
你是一个**任务执行流程设计专家**,专门将任务目标转化为清晰的顺序执行步骤。
|
||||
|
||||
**核心价值**:提供简洁、清晰、可执行的步骤列表,帮助用户完成任务。
|
||||
|
||||
**核心能力**:
|
||||
- 任务分解:将任务拆解为清晰的执行步骤
|
||||
- 工具匹配:为每个步骤选择合适的工具
|
||||
- 流程组织:按照逻辑顺序组织步骤
|
||||
</role>
|
||||
|
||||
<mission>
|
||||
**核心目标**:设计一个顺序执行的流程来完成任务,包含:
|
||||
1. **清晰的步骤**:每个步骤说明要做什么
|
||||
2. **工具指定**:每个步骤使用哪个工具
|
||||
3. **顺序执行**:从第一步到最后一步,依次执行
|
||||
|
||||
**输出价值**:
|
||||
- 用户可以按照步骤顺序完成任务
|
||||
- 每个步骤明确使用的工具
|
||||
- 流程简单直接,易于理解
|
||||
</mission>
|
||||
|
||||
${currentConfigContext}
|
||||
|
||||
<context_awareness>
|
||||
**如果有前置信息**(任务目标、工具列表等):
|
||||
- 这些是已经确定的,不需要重复询问
|
||||
- 你的任务是将它们组织成顺序执行的步骤
|
||||
- 重点是"按什么顺序做什么"
|
||||
</context_awareness>
|
||||
|
||||
<system_features_handling>
|
||||
**系统功能处理**(如果预设计划中包含系统功能配置):
|
||||
|
||||
如果预设计划中已包含系统功能配置(如文件上传),你需要:
|
||||
1. **识别已启用的系统功能**
|
||||
- 检查 resources.system_features 中哪些功能已启用
|
||||
- 理解每个系统功能的目的和使用场景
|
||||
|
||||
2. **在步骤设计中考虑系统功能**
|
||||
- 如果文件上传已启用:在第一步或相关步骤中说明需要用户上传文件
|
||||
- 在步骤描述中明确说明文件的作用和处理方式
|
||||
- 考虑文件处理相关的步骤(如数据提取、格式转换等)
|
||||
|
||||
3. **系统功能不在 expected_tools 中**
|
||||
- 系统功能是平台级配置,不是工具调用
|
||||
- expected_tools 中只包含实际执行操作的工具和知识库
|
||||
- 系统功能已在前期确定,步骤设计时只需要考虑其影响即可
|
||||
</system_features_handling>
|
||||
|
||||
<info_collection_phase>
|
||||
当处于信息收集阶段时:
|
||||
|
||||
**信息收集目标**:收集设计执行步骤所需的信息:
|
||||
|
||||
1. **步骤分解**
|
||||
- 完成任务需要哪些步骤?
|
||||
- 每个步骤的目的是什么?
|
||||
- 步骤的执行顺序是什么?
|
||||
|
||||
2. **工具使用**
|
||||
- 每个步骤应该使用哪个工具?
|
||||
- 是否有步骤需要使用多个工具?
|
||||
|
||||
3. **流程细节**
|
||||
- 任务的起点是什么?
|
||||
- 任务的终点是什么?
|
||||
- 是否需要中间验证步骤?
|
||||
|
||||
**关键原则**:
|
||||
- **简单直接**:不考虑复杂的分支和条件,就是顺序执行
|
||||
- **基于已知**:使用已确定的工具列表
|
||||
- **用户友好**:步骤描述清晰,容易理解
|
||||
|
||||
**输出格式**:
|
||||
|
||||
**重要:信息收集阶段的所有回复必须使用JSON格式,包含 phase 字段**
|
||||
|
||||
直接输出以下格式的JSON(不要添加代码块标记):
|
||||
{
|
||||
"phase": "collection",
|
||||
"reasoning": "为什么问这个问题的推理过程:基于什么考虑、希望收集什么信息、对后续有什么帮助",
|
||||
"question": "实际向用户提出的问题内容"
|
||||
}
|
||||
|
||||
问题内容可以是开放式问题,也可以包含选项:
|
||||
|
||||
开放式问题示例:
|
||||
{
|
||||
"phase": "collection",
|
||||
"reasoning": "需要首先了解任务的基本定位和目标场景,这将决定后续需要确认的工具类型和步骤设计",
|
||||
"question": "我想了解一下您希望这个执行流程实现什么功能?能否详细描述一下具体要处理什么样的任务?"
|
||||
}
|
||||
|
||||
选择题示例:
|
||||
{
|
||||
"phase": "collection",
|
||||
"reasoning": "需要确认步骤设计的重点方向,这将影响流程的详细程度",
|
||||
"question": "关于流程的设计,您更关注:\\nA. 步骤的详细程度(每个步骤都很详细)\\nB. 步骤的灵活性(可以根据情况调整)\\nC. 步骤的简洁性(尽可能少的步骤)\\nD. 其他\\n\\n请选择最符合的选项,或输入您的详细回答:"
|
||||
}
|
||||
|
||||
适合选择题的场景:
|
||||
- 经验水平判断(初学者/有经验/熟练/专家)
|
||||
- 优先级排序(时间/质量/成本/创新)
|
||||
- 任务类型分类(分析/设计/开发/测试)
|
||||
- 复杂度判断(简单/中等/复杂/极复杂)
|
||||
|
||||
避免的行为:
|
||||
- 不要为所有问题都强制提供选项
|
||||
- 选项之间要有明显的区分度
|
||||
- 不要使用过于技术化的术语
|
||||
</info_collection_phase>
|
||||
|
||||
<capability_boundary_enforcement>
|
||||
**系统能力边界确认**:
|
||||
|
||||
**动态约束原则**:
|
||||
1. **只规划现有能力**:只能使用系统当前提供的工具和功能
|
||||
2. **基于实际能力判断**:如果系统有编程工具,就可以规划编程任务
|
||||
3. **能力适配规划**:根据可用工具库的能力边界来设计流程
|
||||
4. **避免能力假设**:不能假设系统有未明确提供的能力
|
||||
|
||||
**规划前自我检查**:
|
||||
- 这个步骤需要什么具体能力?
|
||||
- 当前系统中是否有对应的工具提供这种能力?
|
||||
- 用户是否具备使用该工具的条件?
|
||||
- 如果没有合适的工具,能否用现有能力组合实现?
|
||||
|
||||
**能力发现机制**:
|
||||
- 优先使用系统中明确提供的工具
|
||||
- 探索现有工具的组合能力
|
||||
- 基于实际可用能力设计解决方案
|
||||
- 避免依赖系统中不存在的能力
|
||||
|
||||
**重要提醒**:请基于下面提供的可用工具列表,仔细分析系统能力边界,确保规划的每个步骤都有对应的工具支持。
|
||||
</capability_boundary_enforcement>
|
||||
|
||||
<resource_definitions>
|
||||
**系统资源定义**(重要:理解三类资源的本质区别)
|
||||
|
||||
**工具 (Tool)**:
|
||||
- 定义:可以执行特定功能的能力模块
|
||||
- 功能:执行操作、调用API、处理数据、生成内容等
|
||||
- 特点:主动执行,产生结果或副作用
|
||||
- 示例:搜索引擎、数据库操作、邮件发送、内容生成
|
||||
|
||||
**知识库 (Knowledge)**:
|
||||
- 定义:系统上已经搭建好的文件存储系统,包含特定领域的结构化信息
|
||||
- 功能:存储和检索信息,提供领域知识查询
|
||||
- 特点:被动查询,返回已存储的信息
|
||||
- 示例:产品文档库、技术手册、行业知识库
|
||||
|
||||
**系统功能 (System Features)**:
|
||||
- 定义:平台级的功能开关,控制执行流程的特殊能力
|
||||
- 功能:影响任务执行方式的系统级配置
|
||||
- 特点:开关控制,改变交互模式
|
||||
- 示例:文件上传、用户交互、实时数据流
|
||||
|
||||
**关键区别**:
|
||||
- 工具 = "做事情"(执行动作、调用服务、处理数据)
|
||||
- 知识库 = "查信息"(检索已有知识、获取领域信息)
|
||||
- 系统功能 = "改变模式"(启用特殊交互方式、系统级能力)
|
||||
|
||||
**选择建议**:
|
||||
- 需要执行操作(搜索、发送、计算、转换)→ 选择工具
|
||||
- 需要查询特定领域的信息(产品资料、技术文档、行业知识)→ 选择知识库
|
||||
- 需要用户提供文件/特殊交互方式 → 启用系统功能
|
||||
- 三者可以配合使用:例如用搜索工具获取实时信息,用知识库补充领域知识,启用文件上传让用户提供私有数据
|
||||
</resource_definitions>
|
||||
|
||||
<plan_generation_phase>
|
||||
当处于计划生成阶段时:
|
||||
|
||||
**可用资源列表**:
|
||||
"""
|
||||
${resourceList}
|
||||
"""
|
||||
|
||||
**计划生成要求**:
|
||||
1. 严格按照JSON格式输出
|
||||
2. **严格确保所有引用的工具都在可用工具列表中** - 这是硬性要求
|
||||
3. 考虑搭建者的实际约束条件(时间、资源、技能等)
|
||||
4. 计划要具体、可执行、步骤清晰,适合作为流程模板
|
||||
5. **绝不要使用任何不在可用工具列表中的工具** - 违背此项将导致计划被拒绝
|
||||
|
||||
**🚨 资源使用严格限制(极其重要)**:
|
||||
|
||||
**资源识别规则**:
|
||||
1. 在上面的"## 可用资源列表"中查找所有可用资源
|
||||
2. 每个资源ID后面都有标签:[工具] 或 [知识库]
|
||||
3. 输出时必须根据标签确定 type 值:
|
||||
- 标签是 [工具] → "type": "tool"
|
||||
- 标签是 [知识库] → "type": "knowledge"
|
||||
4. **系统功能不在 expected_tools 中**:
|
||||
- 系统功能(如file_upload)已在前期确定,不需要在步骤的 expected_tools 中列出
|
||||
- expected_tools 只包含实际执行操作的工具和知识库
|
||||
- 如果预设计划中启用了文件上传,只需在步骤描述中说明,不要在 expected_tools 中添加
|
||||
|
||||
**输出格式要求**:
|
||||
- ✅ expected_tools 必须使用对象数组格式:[{"id": "...", "type": "..."}]
|
||||
- ✅ 资源ID必须完全匹配列表中的ID(包括大小写、特殊字符)
|
||||
- ❌ 不要使用字符串数组格式:["...", "..."]
|
||||
- ❌ 不要猜测 type 值,必须根据列表中的标签确定
|
||||
|
||||
**输出前的自我检查步骤**:
|
||||
1. 查看你选择的每个资源ID,它在列表中的标签是什么?
|
||||
2. 如果标签是 [工具] → 设置 "type": "tool"
|
||||
3. 如果标签是 [知识库] → 设置 "type": "knowledge"
|
||||
4. 确保每个资源都有 id 和 type 两个字段
|
||||
|
||||
**常见错误避免**:
|
||||
- ❌ 不要凭空想象资源名称
|
||||
- ❌ 不要使用通用描述如"数据库工具"而不指定具体资源ID
|
||||
- ❌ 不要引用"可能"存在但未在列表中明确的资源
|
||||
- ❌ 不要输出字符串数组,必须是对象数组
|
||||
- ❌ 不要把 [知识库] 标签的资源设置为 type: "tool"
|
||||
- ✅ 必须根据列表中的标签准确设置 type 值
|
||||
- ✅ 基于实际可用的资源进行规划
|
||||
|
||||
**深度分析框架**(内部思考过程,不输出):
|
||||
🔍 第一层:任务本质分析
|
||||
- 识别用户的核心目标和真实意图
|
||||
- 分析任务的复杂度、范围和关键约束
|
||||
- 确定主要的功能需求和预期成果
|
||||
|
||||
📋 第二层:阶段划分
|
||||
- 将任务分解为逻辑清晰的执行阶段
|
||||
- 识别每个阶段的具体子任务和依赖关系
|
||||
- 确保阶段间的逻辑连贯性和合理性
|
||||
|
||||
🛠️ 第三层:工具类别匹配
|
||||
根据每个阶段的功能需求,建议最合适的工具类别:
|
||||
- 信息获取 → 搜索工具类(搜索引擎、网页抓取、API查询)
|
||||
- 数据处理 → 数据处理类(数据库操作、数据分析、格式转换)
|
||||
- 内容创建 → 内容生成类(文本生成、图像创作、PPT制作)
|
||||
- 服务集成 → API集成类(第三方服务、邮件发送、消息通知)
|
||||
- 实用操作 → 实用工具类(编码解码、文件处理、实用操作)
|
||||
|
||||
🎯 第四层:精确工具选择
|
||||
在确定的类别中,建议最合适的1-2个具体工具:
|
||||
- 基于任务细节选择功能最匹配的工具
|
||||
- 考虑工具组合的协同效应
|
||||
- 确保工具调用格式的准确性
|
||||
|
||||
**输出格式要求**:
|
||||
**重要**:只输出JSON,不要有任何其他文字说明、注释或markdown标记。
|
||||
直接输出以下格式的JSON:
|
||||
{
|
||||
"phase": "generation",
|
||||
"plan_analysis": {
|
||||
"name": "简洁的计划名称(英文,适合作为函数名)",
|
||||
"description": "详细的计划描述,说明这个计划的功能和适用场景,用于后续plan匹配",
|
||||
"goal": "任务的核心目标描述",
|
||||
"type": "任务类型分类"
|
||||
},
|
||||
"execution_plan": {
|
||||
"total_steps": 数字,
|
||||
"steps": [
|
||||
{
|
||||
"id": "step1",
|
||||
"title": "简洁明确的步骤标题",
|
||||
"description": "使用@[资源名称]格式的简洁任务描述,明确指出要做什么",
|
||||
"expectedTools": [
|
||||
{"id": "资源ID1", "type": "tool或knowledge"},
|
||||
{"id": "资源ID2", "type": "tool或knowledge"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
**字段说明**:
|
||||
- name: 简洁的英文计划名称,使用驼峰命名或下划线,例如:"createMarketingReport", "analyzeCustomerData"
|
||||
- description: 详细描述计划的功能、使用场景和输出结果,用于智能匹配用户需求
|
||||
- description应该包含:功能说明、适用场景、预期输出、关键特性等
|
||||
|
||||
资源使用格式:
|
||||
- 在description中使用@[资源名称]格式引用资源
|
||||
- 在expected_tools中使用对象数组格式列出资源
|
||||
- 每个对象必须包含 id 和 type 两个字段
|
||||
- type 值根据资源列表中的标签确定([工具]→"tool",[知识库]→"knowledge")
|
||||
- 确保资源引用的准确性
|
||||
- 优先选择功能最匹配的资源
|
||||
|
||||
工具选择原则:
|
||||
1. 功能匹配优先:选择最能满足步骤需求的工具
|
||||
2. 组合优化:多个工具可以组合使用以获得更好效果
|
||||
3. 逻辑连贯:确保工具选择的逻辑性
|
||||
4. 简洁高效:避免不必要的工具冗余
|
||||
|
||||
**✅ 正确示例**:
|
||||
{
|
||||
"phase": "generation",
|
||||
"plan_analysis": {
|
||||
"name": "createTravelItinerary",
|
||||
"description": "创建旅游行程计划,包括景点查询、天气预报、行程文档生成。适用于需要规划旅行的场景,输出完整的markdown格式行程文档。",
|
||||
"goal": "为用户规划详细的旅游行程",
|
||||
"type": "内容生成"
|
||||
},
|
||||
"execution_plan": {
|
||||
"total_steps": 3,
|
||||
"steps": [
|
||||
{
|
||||
"id": "step1",
|
||||
"title": "查询旅游目的地信息",
|
||||
"description": "使用@[travel_destinations]知识库查询目的地的景点、美食、住宿等详细信息",
|
||||
"expectedTools": [
|
||||
{"id": "travel_destinations", "type": "knowledge"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "step2",
|
||||
"title": "查询实时天气信息",
|
||||
"description": "使用@[mojiWeather/tool]工具获取目的地未来7天的天气预报",
|
||||
"expectedTools": [
|
||||
{"id": "mojiWeather/tool", "type": "tool"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "step3",
|
||||
"title": "生成行程计划文档",
|
||||
"description": "使用@[markdownTransform]工具将行程信息格式化为markdown文档",
|
||||
"expectedTools": [
|
||||
{"id": "markdownTransform", "type": "tool"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
**❌ 错误示例1**(使用字符串数组而非对象数组):
|
||||
{
|
||||
"expectedTools": ["travel_destinations", "mojiWeather/tool"] // ❌ 应该是对象数组
|
||||
}
|
||||
|
||||
**❌ 错误示例2**(type 值错误):
|
||||
{
|
||||
"expectedTools": [
|
||||
{"id": "travel_destinations", "type": "tool"} // ❌ 这是知识库,应该是 "knowledge"
|
||||
]
|
||||
}
|
||||
|
||||
**❌ 错误示例3**(缺少必需字段):
|
||||
{
|
||||
"expectedTools": [
|
||||
{"id": "mojiWeather/tool"} // ❌ 缺少 type 字段
|
||||
]
|
||||
}
|
||||
|
||||
质量要求:
|
||||
1. 任务理解深度:确保每个计划都基于对用户需求的深度理解
|
||||
2. 逻辑严谨性:步骤间要有清晰的逻辑关系和依赖关系
|
||||
3. 工具匹配精度:每个工具的选择都要有明确的理由
|
||||
4. 输出格式规范:严格遵循JSON格式要求,expected_tools必须是对象数组
|
||||
5. 描述简洁性:步骤描述要简洁明了,只说明做什么
|
||||
6. 资源类型准确:type值必须根据资源列表中的标签准确设置
|
||||
</plan_generation_phase>
|
||||
|
||||
<phase_decision_guidelines>
|
||||
**🎯 关键:如何判断当前应该处于哪个阶段**
|
||||
|
||||
**每次回复前,你必须自主评估以下问题**:
|
||||
|
||||
1. **信息充分性评估**:
|
||||
- 我是否已经明确了解要完成的任务目标?
|
||||
- 我是否知道需要使用哪些工具和资源?
|
||||
- 我是否了解任务的关键约束条件?
|
||||
- 如果上述问题有任何不确定,应该输出 \`"phase": "collection"\` 继续提问
|
||||
|
||||
2. **配置生成时机判断**:
|
||||
- 满足以下**所有条件**时,才能输出 \`"phase": "generation"\`:
|
||||
* 已经明确任务的核心目标和场景
|
||||
* 已经确认可用的工具和资源
|
||||
* 已经收集到足够信息来设计执行步骤
|
||||
* 对话轮次达到 2-4 轮(避免过早生成)
|
||||
|
||||
3. **阶段回退机制**:
|
||||
- 如果用户在配置生成后继续发送消息
|
||||
- 评估新信息:
|
||||
* 如果是小调整(修改步骤、工具选择等)→ 输出 \`"phase": "generation"\` 生成新计划
|
||||
* 如果发现核心需求变化或信息不足 → 输出 \`"phase": "collection"\` 回退继续提问
|
||||
|
||||
**重要原则**:
|
||||
- ❌ 不要在第一轮对话就生成计划(除非用户提供了极其详细的需求)
|
||||
- ❌ 不要在信息不足时强行生成计划
|
||||
- ✅ 宁可多问一两个问题,也不要生成不准确的计划
|
||||
- ✅ 当确信信息充分时,果断切换到计划生成阶段
|
||||
- ✅ 支持灵活的阶段切换,包括从计划生成回退到信息收集
|
||||
</phase_decision_guidelines>
|
||||
|
||||
<conversation_rules>
|
||||
**回复格式要求**:
|
||||
- **所有回复必须是 JSON 格式**,包含 \`phase\` 字段
|
||||
- 信息收集阶段:输出 \`{"phase": "collection", "reasoning": "...", "question": "..."}\`
|
||||
- 计划生成阶段:输出 \`{"phase": "generation", "plan_analysis": {...}, "execution_plan": {...}}\`
|
||||
- ❌ 不要输出任何非 JSON 格式的内容
|
||||
- ❌ 不要添加代码块标记(如 \\\`\\\`\\\`json)
|
||||
|
||||
**特殊场景处理**:
|
||||
- 如果用户明确要求"直接生成计划",即使信息不足也应输出 \`"phase": "generation"\`
|
||||
- 如果用户说"重新开始"或"从头来过",回到 \`"phase": "collection"\` 重新收集
|
||||
- 避免过度询问,通常 2-4 轮即可完成信息收集
|
||||
|
||||
**质量保证**:
|
||||
- 收集的信息要具体、准确、可验证
|
||||
- 生成的计划要基于收集到的信息
|
||||
- 确保计划中的每个步骤都是可执行的
|
||||
- 严格基于系统能力边界进行规划
|
||||
</conversation_rules>`;
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import type { SkillAgentParamsType } from '@fastgpt/global/core/chat/helperBot/skillAgent/type';
|
||||
|
||||
export const buildSkillAgentMetadataInfo = (metadata?: SkillAgentParamsType): string => {
|
||||
if (!metadata) return '';
|
||||
|
||||
const sections: string[] = [];
|
||||
|
||||
if (metadata.skillAgent) {
|
||||
const { name, description, stepsText } = metadata.skillAgent;
|
||||
if (name || description || stepsText) {
|
||||
sections.push('**Skill 配置** (当前 Skill 的专属配置):');
|
||||
if (name) sections.push(`- skill名称: ${name}`);
|
||||
if (description) sections.push(`- skill描述: ${description}`);
|
||||
if (stepsText) sections.push(`- 详细的步骤信息: ${stepsText}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata.topAgent) {
|
||||
const topAgentSections: string[] = [];
|
||||
const { role, taskObject, selectedTools, selectedDatasets, fileUpload } = metadata.topAgent;
|
||||
|
||||
if (role) topAgentSections.push(`- 预设角色: ${role}`);
|
||||
if (taskObject) topAgentSections.push(`- 预设任务目标: ${taskObject}`);
|
||||
if (selectedTools && selectedTools.length > 0) {
|
||||
topAgentSections.push(`- 预设工具: ${selectedTools.join(', ')}`);
|
||||
}
|
||||
if (selectedDatasets && selectedDatasets.length > 0) {
|
||||
topAgentSections.push(`- 预设知识库: ${selectedDatasets.join(', ')}`);
|
||||
}
|
||||
if (fileUpload !== undefined && fileUpload !== null) {
|
||||
topAgentSections.push(`- 文件上传: ${fileUpload ? '已启用' : '已禁用'}`);
|
||||
}
|
||||
|
||||
if (topAgentSections.length > 0) {
|
||||
sections.push('\n**Agent 的整体背景信息配置** :');
|
||||
sections.push(...topAgentSections);
|
||||
}
|
||||
}
|
||||
|
||||
if (sections.length === 0) return '';
|
||||
|
||||
return `
|
||||
<preset_info>
|
||||
搭建者已提供以下预设信息,这些信息具有**高优先级**,请在后续的信息收集和规划中优先参考:
|
||||
|
||||
${sections.join('\n')}
|
||||
|
||||
**重要提示**:
|
||||
- **Skill 配置**优先级最高,如果已有 name、description、stepsText,说明这是已配置的 skill,应基于这些信息执行任务
|
||||
- **TopAgent 通用配置**提供了全局的角色、工具、知识库等配置,可以在 skill 执行中使用
|
||||
- 在信息收集阶段,如果预设信息已经提供了某个维度的答案,可以跳过相关问题
|
||||
- 在规划阶段,**优先使用**预设的工具和知识库
|
||||
</preset_info>
|
||||
`;
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
import type { HelperBotDispatchParamsType, HelperBotDispatchResponseType } from '../type';
|
||||
|
||||
export const dispatchSkillEditor = async (
|
||||
props: HelperBotDispatchParamsType
|
||||
): Promise<HelperBotDispatchResponseType> => {
|
||||
console.log(props, 22222);
|
||||
return {
|
||||
aiResponse: [],
|
||||
usage: {
|
||||
model: '',
|
||||
inputTokens: 0,
|
||||
outputTokens: 0
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -9,7 +9,7 @@ import { generateResourceList } from './utils';
|
|||
import { TopAgentFormDataSchema } from './type';
|
||||
import { addLog } from '../../../../../common/system/log';
|
||||
import { formatAIResponse } from '../utils';
|
||||
import type { TopAgentParamsType } from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import type { TopAgentParamsType } from '@fastgpt/global/core/chat/helperBot/topAgent/type';
|
||||
|
||||
export const dispatchTopAgent = async (
|
||||
props: HelperBotDispatchParamsType<TopAgentParamsType>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { TopAgentParamsType } from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import type { TopAgentParamsType } from '@fastgpt/global/core/chat/helperBot/topAgent/type';
|
||||
import { buildMetadataInfo } from './utils';
|
||||
|
||||
export const getPrompt = ({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { localeType } from '@fastgpt/global/common/i18n/type';
|
||||
import { getSystemToolsWithInstalled } from '../../../../app/tool/controller';
|
||||
import type { TopAgentParamsType } from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import type { TopAgentParamsType } from '@fastgpt/global/core/chat/helperBot/topAgent/type';
|
||||
export const generateResourceList = async ({
|
||||
teamId,
|
||||
isRoot,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { HelperBotTypeEnum } from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import type { HelperBotCompletionsParamsType } from '@fastgpt/global/openapi/core/chat/helperBot/api';
|
||||
import { MongoHelperBotChat } from './chatSchema';
|
||||
import type {
|
||||
AIChatItemValueItemType,
|
||||
UserChatItemValueItemType
|
||||
} from '@fastgpt/global/core/chat/type';
|
||||
HelperBotTypeEnum
|
||||
} from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import type { HelperBotCompletionsParamsType } from '@fastgpt/global/openapi/core/chat/helperBot/api';
|
||||
import { MongoHelperBotChat } from './chatSchema';
|
||||
import type { UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { MongoHelperBotChatItem } from './chatItemSchema';
|
||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import type { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type
|
|||
import { addLog } from '../../../../../common/system/log';
|
||||
import { checkTaskComplexity } from './master/taskComplexity';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { matchSkillForPlan } from './skillMatcher';
|
||||
|
||||
export type DispatchAgentModuleProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.history]?: ChatItemType[];
|
||||
|
|
@ -163,7 +164,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
|||
|
||||
if (taskIsComplexity) {
|
||||
/* ===== Plan Agent ===== */
|
||||
const planCallFn = async () => {
|
||||
const planCallFn = async (referencePlanSystemPrompt?: string) => {
|
||||
// 点了确认。此时肯定有 agentPlans
|
||||
if (
|
||||
lastInteractive?.type === 'agentPlanCheck' &&
|
||||
|
|
@ -179,7 +180,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
|||
interactive: lastInteractive,
|
||||
subAppList,
|
||||
getSubAppInfo,
|
||||
systemPrompt,
|
||||
systemPrompt: referencePlanSystemPrompt || systemPrompt,
|
||||
model,
|
||||
temperature,
|
||||
top_p: aiChatTopP,
|
||||
|
|
@ -349,9 +350,36 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
|||
// Replan step: 已有 plan,且有 replan 历史消息
|
||||
const isReplanStep = isPlanAgent && agentPlan && replanMessages;
|
||||
|
||||
// 🆕 执行 Skill 匹配(仅在 isPlanStep 且没有 planHistoryMessages 时)
|
||||
let matchedSkillSystemPrompt: string | undefined;
|
||||
|
||||
console.log('planHistoryMessages', planHistoryMessages);
|
||||
// 执行 Plan/replan
|
||||
if (isPlanStep) {
|
||||
const result = await planCallFn();
|
||||
// match skill
|
||||
addLog.debug('尝试匹配用户的历史 skills');
|
||||
const matchResult = await matchSkillForPlan({
|
||||
teamId: runningUserInfo.teamId,
|
||||
appId: runningAppInfo.id,
|
||||
userInput: lastInteractive ? interactiveInput : userChatInput,
|
||||
model
|
||||
});
|
||||
if (matchResult.matched && matchResult.systemPrompt) {
|
||||
addLog.debug(`匹配到 skill: ${matchResult.skill?.name}`);
|
||||
matchedSkillSystemPrompt = matchResult.systemPrompt;
|
||||
|
||||
// 可选: 推送匹配信息给前端
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.answer,
|
||||
data: textAdaptGptResponse({
|
||||
text: `📋 找到参考技能: ${matchResult.systemPrompt}`
|
||||
})
|
||||
});
|
||||
} else {
|
||||
addLog.debug(`未匹配到 skill,原因: ${matchResult.reason}`);
|
||||
}
|
||||
|
||||
const result = await planCallFn(matchedSkillSystemPrompt);
|
||||
// 有 result 代表 plan 有交互响应(check/ask)
|
||||
if (result) return result;
|
||||
} else if (isReplanStep) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,205 @@
|
|||
import { MongoAiSkill } from '../../../../ai/skill/schema';
|
||||
import type { AiSkillSchemaType } from '@fastgpt/global/core/ai/skill/type';
|
||||
import { createLLMResponse } from '../../../../ai/llm/request';
|
||||
import type { ChatCompletionMessageParam, ChatCompletionTool } from '@fastgpt/global/core/ai/type';
|
||||
import { getLLMModel } from '../../../../ai/model';
|
||||
|
||||
/**
|
||||
* 生成唯一函数名
|
||||
* 参考 MatcherService.ts 的 _generateUniqueFunctionName
|
||||
*/
|
||||
const generateUniqueFunctionName = (skill: HelperBotGeneratedSkillType): string => {
|
||||
let baseName = skill.name || skill._id.toString();
|
||||
|
||||
// 清理名称
|
||||
let cleanName = baseName.replace(/[^a-zA-Z0-9_]/g, '_');
|
||||
|
||||
if (cleanName && !/^[a-zA-Z_]/.test(cleanName)) {
|
||||
cleanName = 'skill_' + cleanName;
|
||||
} else if (!cleanName) {
|
||||
cleanName = 'skill_unknown';
|
||||
}
|
||||
|
||||
const timestampSuffix = Date.now().toString().slice(-6);
|
||||
// return `${cleanName}_${timestampSuffix}`;
|
||||
return `${cleanName}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* 构建 Skill Tools 数组
|
||||
* 参考 MatcherService.ts 的 match 函数
|
||||
*/
|
||||
export const buildSkillTools = (
|
||||
skills: HelperBotGeneratedSkillType[]
|
||||
): {
|
||||
tools: ChatCompletionTool[];
|
||||
skillsMap: Record<string, HelperBotGeneratedSkillType>;
|
||||
} => {
|
||||
const tools: ChatCompletionTool[] = [];
|
||||
const skillsMap: Record<string, HelperBotGeneratedSkillType> = {};
|
||||
|
||||
for (const skill of skills) {
|
||||
// 生成唯一函数名
|
||||
const functionName = generateUniqueFunctionName(skill);
|
||||
skillsMap[functionName] = skill;
|
||||
|
||||
// 构建 description
|
||||
let description = skill.description || 'No description available';
|
||||
|
||||
tools.push({
|
||||
type: 'function',
|
||||
function: {
|
||||
name: functionName,
|
||||
description: description,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
required: []
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return { tools, skillsMap };
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化 Skill 为 SystemPrompt
|
||||
* 将匹配到的 skill 格式化为 XML 提示词
|
||||
*/
|
||||
export const formatSkillAsSystemPrompt = (skill: HelperBotGeneratedSkillType): string => {
|
||||
let prompt = '<reference_skill>\n';
|
||||
prompt += `**参考技能**: ${skill.name}\n\n`;
|
||||
|
||||
if (skill.description) {
|
||||
prompt += `**描述**: ${skill.description}\n\n`;
|
||||
}
|
||||
|
||||
if (skill.steps && skill.steps.trim()) {
|
||||
prompt += `**步骤信息**:\n${skill.steps}\n\n`;
|
||||
}
|
||||
|
||||
prompt += '**说明**:\n';
|
||||
prompt += '1. 以上是用户之前保存的类似任务的执行计划\n';
|
||||
prompt += '2. 请参考该技能的步骤流程和工具选择\n';
|
||||
prompt += '3. 根据当前用户的具体需求,调整和优化计划\n';
|
||||
prompt += '4. 保持步骤的逻辑性和完整性\n';
|
||||
prompt += '</reference_skill>\n';
|
||||
|
||||
return prompt;
|
||||
};
|
||||
|
||||
/**
|
||||
* 主匹配函数
|
||||
* 参考 MatcherService.ts 的 match 方法
|
||||
*/
|
||||
export const matchSkillForPlan = async ({
|
||||
teamId,
|
||||
appId,
|
||||
userInput,
|
||||
model
|
||||
}: {
|
||||
teamId: string;
|
||||
appId: string;
|
||||
userInput: string;
|
||||
model: string;
|
||||
}): Promise<{
|
||||
matched: boolean;
|
||||
skill?: HelperBotGeneratedSkillType;
|
||||
systemPrompt?: string;
|
||||
reason?: string;
|
||||
}> => {
|
||||
try {
|
||||
// 1. 查询用户的 skills (使用 teamId 和 appId)
|
||||
const skills = await MongoAiSkill.find({
|
||||
teamId,
|
||||
appId,
|
||||
status: { $in: ['active', 'draft'] }
|
||||
})
|
||||
.sort({ createTime: -1 })
|
||||
.limit(50) // 限制数量,避免 tools 过多
|
||||
.lean();
|
||||
console.log('skill list length', skills.length);
|
||||
console.log('skill', skills);
|
||||
if (!skills || skills.length === 0) {
|
||||
return { matched: false, reason: 'No skills available' };
|
||||
}
|
||||
|
||||
// 2. 构建 tools 数组
|
||||
const { tools, skillsMap } = buildSkillTools(skills);
|
||||
|
||||
console.log('tools', tools);
|
||||
|
||||
// 3. 获取模型配置
|
||||
const modelData = getLLMModel(model);
|
||||
|
||||
// 4. 调用 LLM Tool Calling 进行匹配
|
||||
// 构建系统提示词,指导 LLM 选择相似的任务
|
||||
const systemPrompt = `你是一个智能任务匹配助手。请根据用户的当前需求,从提供的技能工具集中选择最相似的任务。
|
||||
|
||||
**匹配原则**:
|
||||
1. **任务目标相似性**:选择与用户目标最匹配的技能
|
||||
2. **执行步骤相似性**:考虑任务执行的流程和步骤
|
||||
3. **工具使用相似性**:优先选择使用类似工具组合的技能
|
||||
4. **场景适用性**:考虑应用场景和上下文的相似性
|
||||
|
||||
**选择建议**:
|
||||
- 如果用户的需求与某个技能高度匹配,直接选择对应的工具
|
||||
- 如果有多个相似技能,选择最符合主要目标的那个
|
||||
- 如果没有找到完全匹配的技能,选择功能最相近的
|
||||
|
||||
请从以下工具中选择一个最匹配的:`;
|
||||
|
||||
// 构建简化的消息,只包含系统提示词和用户输入
|
||||
const allMessages = [
|
||||
{
|
||||
role: 'system' as const,
|
||||
content: systemPrompt
|
||||
},
|
||||
{
|
||||
role: 'user' as const,
|
||||
content: userInput
|
||||
}
|
||||
];
|
||||
console.log('match request', { userInput, skillCount: skills.length });
|
||||
|
||||
const { toolCalls } = await createLLMResponse({
|
||||
body: {
|
||||
model: modelData.model,
|
||||
messages: allMessages,
|
||||
tools,
|
||||
tool_choice: 'auto',
|
||||
toolCallMode: modelData.toolChoice ? 'toolChoice' : 'prompt',
|
||||
stream: false
|
||||
}
|
||||
});
|
||||
|
||||
// 5. 解析匹配结果
|
||||
if (toolCalls && toolCalls.length > 0) {
|
||||
const toolCall = toolCalls[0];
|
||||
const functionName = toolCall.function.name;
|
||||
|
||||
if (skillsMap[functionName]) {
|
||||
const matchedSkill = skillsMap[functionName];
|
||||
const systemPrompt = formatSkillAsSystemPrompt(matchedSkill);
|
||||
|
||||
return {
|
||||
matched: true,
|
||||
skill: matchedSkill,
|
||||
systemPrompt
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
matched: false,
|
||||
reason: 'No matching skill found'
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error('Error during skill matching:', error);
|
||||
return {
|
||||
matched: false,
|
||||
reason: error.message || 'Unknown error'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -103,11 +103,11 @@ export const dispatchPlanAgent = async ({
|
|||
});
|
||||
}
|
||||
|
||||
console.log('Plan request messages');
|
||||
console.dir(
|
||||
{ requestMessages, tools: isTopPlanAgent ? [PlanAgentAskTool] : [] },
|
||||
{ depth: null }
|
||||
);
|
||||
// console.log('Plan request messages');
|
||||
// console.dir(
|
||||
// { requestMessages, tools: isTopPlanAgent ? [PlanAgentAskTool] : [] },
|
||||
// { depth: null }
|
||||
// );
|
||||
let {
|
||||
answerText,
|
||||
toolCalls = [],
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ export const getUserContent = ({
|
|||
systemPrompt?: string;
|
||||
getSubAppInfo: GetSubAppInfoFnType;
|
||||
}) => {
|
||||
let userContent = `任务描述:${userInput}`;
|
||||
let userContent = `用户输入:${userInput}`;
|
||||
if (systemPrompt) {
|
||||
userContent += `\n\n背景信息:${parseSystemPrompt({ systemPrompt, getSubAppInfo })}\n请按照用户提供的背景信息来重新生成计划,优先遵循用户的步骤安排和偏好。`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@
|
|||
"confirm_delete_chat_content": "Are you sure you want to delete this chat? This action cannot be undone!",
|
||||
"confirm_delete_chats": "Are you sure you want to delete {{n}} conversation records? \nThe records will be permanently deleted!",
|
||||
"confirm_delete_folder_tip": "When you delete this folder, all applications and corresponding chat records under it will be deleted.",
|
||||
"confirm_delete_skill": "Confirm to delete this skill?",
|
||||
"confirm_delete_tool": "Confirm to delete this tool?",
|
||||
"copilot_config_message": "Current Node Configuration Information: \n Code Type: {{codeType}} \n Current Code: \\\\`\\\\`\\\\`{{codeType}} \n{{code}} \\\\`\\\\`\\\\` \n Input Parameters: {{inputs}} \n Output Parameters: {{outputs}}",
|
||||
"copilot_confirm_message": "The original configuration has been received to understand the current code structure and input and output parameters. \nPlease explain your optimization requirements.",
|
||||
|
|
@ -139,6 +140,7 @@
|
|||
"dataset_search_tool_description": "Call the \"Semantic Search\" and \"Full-text Search\" capabilities to find reference content that may be related to the problem from the \"Knowledge Base\". \nPrioritize calling this tool to assist in answering user questions.",
|
||||
"dataset_select": "Optional knowledge base",
|
||||
"day": "Day",
|
||||
"delete_failed": "Delete failed",
|
||||
"deleted": "App deleted",
|
||||
"document": "document",
|
||||
"document_quote": "Document Reference",
|
||||
|
|
@ -149,6 +151,7 @@
|
|||
"empty_folder": "(empty folder)",
|
||||
"empty_tool_tips": "Please add tools on the left side",
|
||||
"execute_time": "Execution Time",
|
||||
"execution_steps": "Execution Steps",
|
||||
"expand_tool_create": "Expand MCP/Http create",
|
||||
"export_config_successful": "Configuration copied, some sensitive information automatically filtered. Please check for any remaining sensitive data.",
|
||||
"export_configs": "Export",
|
||||
|
|
@ -273,6 +276,7 @@
|
|||
"move_app": "Move Application",
|
||||
"my_agents": "my agents",
|
||||
"no_mcp_tools_list": "No data yet, the MCP address needs to be parsed first",
|
||||
"no_steps_yet": "No steps yet, generate via chat",
|
||||
"node_not_intro": "This node is not introduced",
|
||||
"not_json_file": "Please select a JSON file",
|
||||
"not_the_newest": "Not the latest",
|
||||
|
|
@ -327,9 +331,11 @@
|
|||
"show_templates": "Expand",
|
||||
"show_top_p_tip": "An alternative method of temperature sampling, called Nucleus sampling, the model considers the results of tokens with TOP_P probability mass quality. \nTherefore, 0.1 means that only tokens containing the highest probability quality are considered. \nThe default is 1.",
|
||||
"simple_tool_tips": "This tool contains special inputs and does not support being called by simple applications.",
|
||||
"skill_delete_success": "Skill deleted successfully",
|
||||
"skill_description_placeholder": "Used to guide the Agent to select the skill for execution",
|
||||
"skill_editor": "Skill-assisted generation",
|
||||
"skill_empty_name": "Unnamed skill",
|
||||
"skill_local_removed": "Local skill removed",
|
||||
"skill_name_placeholder": "Please enter the skill description, for display only",
|
||||
"skills": "Skills",
|
||||
"skills_tip": "Model behavioral knowledge",
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@
|
|||
"confirm_choice": "Confirm Choice",
|
||||
"confirm_input_delete_placeholder": "Please enter: {{confirmText}}",
|
||||
"confirm_input_delete_tip": "Please type <bold>{{confirmText}}</bold> to confirm",
|
||||
"confirm_exit_without_saving": "Confirm to leave? \nEditing results will not be retained.",
|
||||
"confirm_logout": "Confirm to log out?",
|
||||
"confirm_move": "Move Here",
|
||||
"confirm_update": "confirm_update",
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@
|
|||
"confirm_delete_chat_content": "确定要删除这个对话吗?此操作不可恢复!",
|
||||
"confirm_delete_chats": "确认删除 {{n}} 组对话记录?记录将会永久删除!",
|
||||
"confirm_delete_folder_tip": "删除该文件夹时,将会删除它下面所有应用及对应的聊天记录。",
|
||||
"confirm_delete_skill": "确认删除该技能?",
|
||||
"confirm_delete_tool": "确认删除该工具?",
|
||||
"copilot_config_message": "`当前节点配置信息: \n代码类型:{{codeType}} \n当前代码: \\`\\`\\`{{codeType}} \n{{code}} \\`\\`\\` \n输入参数: {{inputs}} \n输出参数: {{outputs}}`",
|
||||
"copilot_confirm_message": "已接收到原始配置,了解当前代码结构和输入输出参数。请说明您的优化需求。",
|
||||
|
|
@ -142,6 +143,7 @@
|
|||
"dataset_search_tool_description": "调用“语义检索”和“全文检索”能力,从“知识库”中查找可能与问题相关的参考内容。优先调用该工具来辅助回答用户的问题。",
|
||||
"dataset_select": "可选知识库",
|
||||
"day": "日",
|
||||
"delete_failed": "删除失败",
|
||||
"deleted": "应用已删除",
|
||||
"document": "文档",
|
||||
"document_quote": "文档引用",
|
||||
|
|
@ -153,6 +155,7 @@
|
|||
"empty_folder": "(空文件夹)",
|
||||
"empty_tool_tips": "请在左侧添加工具",
|
||||
"execute_time": "执行时间",
|
||||
"execution_steps": "执行步骤",
|
||||
"expand_tool_create": "展开MCP、Http创建",
|
||||
"export_config_successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据",
|
||||
"export_configs": "导出配置",
|
||||
|
|
@ -286,6 +289,7 @@
|
|||
"move_app": "移动应用",
|
||||
"my_agents": "我的 agents",
|
||||
"no_mcp_tools_list": "暂无数据,需先解析 MCP 地址",
|
||||
"no_steps_yet": "暂无步骤,请通过对话生成",
|
||||
"node_not_intro": "这个节点没有介绍",
|
||||
"not_json_file": "请选择JSON文件",
|
||||
"not_the_newest": "非最新版",
|
||||
|
|
@ -341,9 +345,11 @@
|
|||
"show_templates": "显示模板",
|
||||
"show_top_p_tip": "用温度采样的替代方法,称为Nucleus采样,该模型考虑了具有TOP_P概率质量质量的令牌的结果。因此,0.1表示仅考虑包含最高概率质量的令牌。默认为 1。",
|
||||
"simple_tool_tips": "该工具含有特殊输入,暂不支持被简易应用调用",
|
||||
"skill_delete_success": "技能删除成功",
|
||||
"skill_description_placeholder": "用于引导 Agent 选中该技能进行执行",
|
||||
"skill_editor": "技能辅助生成",
|
||||
"skill_empty_name": "未命名的技能",
|
||||
"skill_local_removed": "本地技能已移除",
|
||||
"skill_name_placeholder": "请输入技能明,仅用于展示",
|
||||
"skills": "技能",
|
||||
"skills_tip": "模型的行为知识",
|
||||
|
|
|
|||
|
|
@ -220,6 +220,7 @@
|
|||
"confirm_choice": "确认选择",
|
||||
"confirm_input_delete_placeholder": "请输入: {{confirmText}}",
|
||||
"confirm_input_delete_tip": "请输入 <bold>{{confirmText}}</bold> 确认",
|
||||
"confirm_exit_without_saving": "确认离开?编辑结果不会被保留。",
|
||||
"confirm_logout": "确认退出登录?",
|
||||
"confirm_move": "移动到这",
|
||||
"confirm_update": "确认更新",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@
|
|||
"confirm_delete_chat_content": "確定要刪除這個對話嗎?\n此操作不可恢復!",
|
||||
"confirm_delete_chats": "確認刪除 {{n}} 組對話記錄?\n記錄將會永久刪除!",
|
||||
"confirm_delete_folder_tip": "刪除該文件夾時,將會刪除它下面所有應用及對應的聊天記錄。",
|
||||
"confirm_delete_skill": "確認刪除該技能?",
|
||||
"confirm_delete_tool": "確認刪除該工具?",
|
||||
"copilot_config_message": "當前節點配置信息: \n代碼類型:{{codeType}} \n當前代碼: \\\\`\\\\`\\\\`{{codeType}} \n{{code}} \\\\`\\\\`\\\\` \n輸入參數: {{inputs}} \n輸出參數: {{outputs}}",
|
||||
"copilot_confirm_message": "已接收到原始配置,了解當前代碼結構和輸入輸出參數。\n請說明您的優化需求。",
|
||||
|
|
@ -138,6 +139,7 @@
|
|||
"dataset_search_tool_description": "呼叫「語意搜尋」和「全文搜尋」功能,從「知識庫」中尋找可能與問題相關的參考內容。優先呼叫這個工具來協助回答使用者的問題。",
|
||||
"dataset_select": "可選知識庫",
|
||||
"day": "日",
|
||||
"delete_failed": "刪除失敗",
|
||||
"deleted": "應用已刪除",
|
||||
"document": "文件",
|
||||
"document_quote": "文件引用",
|
||||
|
|
@ -325,9 +327,11 @@
|
|||
"show_templates": "顯示模板",
|
||||
"show_top_p_tip": "用溫度取樣的替代方法,稱為 Nucleus 取樣,該模型考慮了具有 TOP_P 機率質量質量的令牌的結果。\n因此,0.1 表示僅考慮包含最高機率質量的令牌。\n預設為 1。",
|
||||
"simple_tool_tips": "該工具含有特殊輸入,暫不支持被簡易應用調用",
|
||||
"skill_delete_success": "技能刪除成功",
|
||||
"skill_description_placeholder": "用於引導 Agent 選中該技能進行執行",
|
||||
"skill_editor": "技能輔助生成",
|
||||
"skill_empty_name": "未命名的技能",
|
||||
"skill_local_removed": "本地技能已移除",
|
||||
"skill_name_placeholder": "請輸入技能明,僅用於展示",
|
||||
"skills": "技能",
|
||||
"skills_tip": "模型的行為知識",
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@
|
|||
"confirm_choice": "確認選擇",
|
||||
"confirm_input_delete_placeholder": "請輸入: {{confirmText}}",
|
||||
"confirm_input_delete_tip": "請輸入 <bold>{{confirmText}}</bold> 確認",
|
||||
"confirm_exit_without_saving": "確認離開?\n編輯結果不會被保留。",
|
||||
"confirm_logout": "確認退出登錄?",
|
||||
"confirm_move": "移動至此",
|
||||
"confirm_update": "確認更新",
|
||||
|
|
|
|||
296
pnpm-lock.yaml
296
pnpm-lock.yaml
|
|
@ -140,7 +140,7 @@ importers:
|
|||
version: 6.3.4
|
||||
'@modelcontextprotocol/sdk':
|
||||
specifier: ^1.24.0
|
||||
version: 1.24.0(zod@4.1.12)
|
||||
version: 1.24.3(zod@4.1.12)
|
||||
'@node-rs/jieba':
|
||||
specifier: 2.0.1
|
||||
version: 2.0.1
|
||||
|
|
@ -529,7 +529,7 @@ importers:
|
|||
version: 3.0.6
|
||||
'@modelcontextprotocol/sdk':
|
||||
specifier: ^1.24.0
|
||||
version: 1.24.0(zod@4.1.12)
|
||||
version: 1.24.3(zod@4.1.12)
|
||||
'@node-rs/jieba':
|
||||
specifier: 2.0.1
|
||||
version: 2.0.1
|
||||
|
|
@ -822,7 +822,7 @@ importers:
|
|||
version: link:../../packages/global
|
||||
'@modelcontextprotocol/sdk':
|
||||
specifier: ^1.24.0
|
||||
version: 1.24.0(zod@4.1.12)
|
||||
version: 1.24.3(zod@4.1.12)
|
||||
chalk:
|
||||
specifier: ^5.3.0
|
||||
version: 5.4.1
|
||||
|
|
@ -834,7 +834,7 @@ importers:
|
|||
version: 16.5.0
|
||||
express:
|
||||
specifier: ^4.22.0
|
||||
version: 4.22.0
|
||||
version: 4.22.1
|
||||
devDependencies:
|
||||
'@types/express':
|
||||
specifier: ^5.0.1
|
||||
|
|
@ -935,7 +935,7 @@ importers:
|
|||
dependencies:
|
||||
express:
|
||||
specifier: ^4.22.0
|
||||
version: 4.22.0
|
||||
version: 4.22.1
|
||||
|
||||
packages:
|
||||
|
||||
|
|
@ -2886,8 +2886,8 @@ packages:
|
|||
'@mixmark-io/domino@2.2.0':
|
||||
resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
|
||||
|
||||
'@modelcontextprotocol/sdk@1.24.0':
|
||||
resolution: {integrity: sha512-D8h5KXY2vHFW8zTuxn2vuZGN0HGrQ5No6LkHwlEA9trVgNdPL3TF1dSqKA7Dny6BbBYKSW/rOBDXdC8KJAjUCg==}
|
||||
'@modelcontextprotocol/sdk@1.24.3':
|
||||
resolution: {integrity: sha512-YgSHW29fuzKKAHTGe9zjNoo+yF8KaQPzDC2W9Pv41E7/57IfY+AMGJ/aDFlgTLcVVELoggKE4syABCE75u3NCw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@cfworker/json-schema': ^4.1.1
|
||||
|
|
@ -5104,12 +5104,12 @@ packages:
|
|||
bluebird@3.4.7:
|
||||
resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==}
|
||||
|
||||
body-parser@1.20.4:
|
||||
resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==}
|
||||
body-parser@1.20.3:
|
||||
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
|
||||
body-parser@2.2.1:
|
||||
resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==}
|
||||
body-parser@2.2.0:
|
||||
resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
boolbase@1.0.0:
|
||||
|
|
@ -5523,9 +5523,9 @@ packages:
|
|||
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
content-disposition@1.0.1:
|
||||
resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==}
|
||||
engines: {node: '>=18'}
|
||||
content-disposition@1.0.0:
|
||||
resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
content-type@1.0.5:
|
||||
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
|
||||
|
|
@ -5537,8 +5537,8 @@ packages:
|
|||
convert-source-map@2.0.0:
|
||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||
|
||||
cookie-signature@1.0.7:
|
||||
resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==}
|
||||
cookie-signature@1.0.6:
|
||||
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
|
||||
|
||||
cookie-signature@1.2.2:
|
||||
resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
|
||||
|
|
@ -5548,8 +5548,8 @@ packages:
|
|||
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
cookie@1.1.1:
|
||||
resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
|
||||
cookie@1.0.2:
|
||||
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
cookiejar@2.1.4:
|
||||
|
|
@ -6416,12 +6416,12 @@ packages:
|
|||
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||
engines: {node: '>=0.8.x'}
|
||||
|
||||
eventsource-parser@3.0.6:
|
||||
resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
|
||||
eventsource-parser@3.0.1:
|
||||
resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
eventsource@3.0.7:
|
||||
resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==}
|
||||
eventsource@3.0.6:
|
||||
resolution: {integrity: sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
execa@5.1.1:
|
||||
|
|
@ -6455,18 +6455,18 @@ packages:
|
|||
exponential-backoff@3.1.2:
|
||||
resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==}
|
||||
|
||||
express-rate-limit@7.5.1:
|
||||
resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==}
|
||||
express-rate-limit@7.5.0:
|
||||
resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==}
|
||||
engines: {node: '>= 16'}
|
||||
peerDependencies:
|
||||
express: '>= 4.11'
|
||||
express: ^4.11 || 5 || ^5.0.0-beta.1
|
||||
|
||||
express@4.22.0:
|
||||
resolution: {integrity: sha512-c2iPh3xp5vvCLgaHK03+mWLFPhox7j1LwyxcZwFVApEv5i0X+IjPpbT50SJJwwLpdBVfp45AkK/v+AFgv/XlfQ==}
|
||||
express@4.22.1:
|
||||
resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
|
||||
express@5.2.1:
|
||||
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
|
||||
express@5.1.0:
|
||||
resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
extend@3.0.2:
|
||||
|
|
@ -6592,13 +6592,13 @@ packages:
|
|||
resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
finalhandler@1.3.2:
|
||||
resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==}
|
||||
finalhandler@1.3.1:
|
||||
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
finalhandler@2.1.1:
|
||||
resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
|
||||
engines: {node: '>= 18.0.0'}
|
||||
finalhandler@2.1.0:
|
||||
resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
find-cache-dir@3.3.2:
|
||||
resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
|
||||
|
|
@ -7029,10 +7029,6 @@ packages:
|
|||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
http-errors@2.0.1:
|
||||
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
http-proxy-agent@7.0.2:
|
||||
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
|
||||
engines: {node: '>= 14'}
|
||||
|
|
@ -7082,10 +7078,6 @@ packages:
|
|||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
iconv-lite@0.7.0:
|
||||
resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
|
|
@ -8404,9 +8396,9 @@ packages:
|
|||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-types@3.0.2:
|
||||
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
|
||||
engines: {node: '>=18'}
|
||||
mime-types@3.0.1:
|
||||
resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime@1.6.0:
|
||||
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
|
||||
|
|
@ -9054,8 +9046,9 @@ packages:
|
|||
path-to-regexp@6.3.0:
|
||||
resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==}
|
||||
|
||||
path-to-regexp@8.3.0:
|
||||
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
|
||||
path-to-regexp@8.2.0:
|
||||
resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
path-type@4.0.0:
|
||||
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||
|
|
@ -9182,8 +9175,8 @@ packages:
|
|||
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
pkce-challenge@5.0.1:
|
||||
resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==}
|
||||
pkce-challenge@5.0.0:
|
||||
resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==}
|
||||
engines: {node: '>=16.20.0'}
|
||||
|
||||
pkg-dir@4.2.0:
|
||||
|
|
@ -9394,6 +9387,10 @@ packages:
|
|||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
|
||||
qs@6.13.0:
|
||||
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
qs@6.14.0:
|
||||
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
|
@ -9427,13 +9424,13 @@ packages:
|
|||
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
raw-body@2.5.3:
|
||||
resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==}
|
||||
raw-body@2.5.2:
|
||||
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
raw-body@3.0.2:
|
||||
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
|
||||
engines: {node: '>= 0.10'}
|
||||
raw-body@3.0.0:
|
||||
resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
rc@1.2.8:
|
||||
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
||||
|
|
@ -9955,10 +9952,6 @@ packages:
|
|||
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
send@0.19.1:
|
||||
resolution: {integrity: sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
send@1.2.0:
|
||||
resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
|
@ -10171,10 +10164,6 @@ packages:
|
|||
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
statuses@2.0.2:
|
||||
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
std-env@3.8.1:
|
||||
resolution: {integrity: sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==}
|
||||
|
||||
|
|
@ -11640,7 +11629,7 @@ snapshots:
|
|||
'@babel/core': 7.26.10
|
||||
'@babel/helper-compilation-targets': 7.26.5
|
||||
'@babel/helper-plugin-utils': 7.26.5
|
||||
debug: 4.4.3
|
||||
debug: 4.4.1
|
||||
lodash.debounce: 4.0.8
|
||||
resolve: 1.22.10
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -12352,7 +12341,7 @@ snapshots:
|
|||
'@babel/parser': 7.26.10
|
||||
'@babel/template': 7.26.9
|
||||
'@babel/types': 7.26.10
|
||||
debug: 4.4.3
|
||||
debug: 4.4.1
|
||||
globals: 11.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -13164,7 +13153,7 @@ snapshots:
|
|||
'@humanwhocodes/config-array@0.13.0':
|
||||
dependencies:
|
||||
'@humanwhocodes/object-schema': 2.0.3
|
||||
debug: 4.4.3
|
||||
debug: 4.4.0
|
||||
minimatch: 3.1.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -13726,20 +13715,20 @@ snapshots:
|
|||
|
||||
'@mixmark-io/domino@2.2.0': {}
|
||||
|
||||
'@modelcontextprotocol/sdk@1.24.0(zod@4.1.12)':
|
||||
'@modelcontextprotocol/sdk@1.24.3(zod@4.1.12)':
|
||||
dependencies:
|
||||
ajv: 8.17.1
|
||||
ajv-formats: 3.0.1(ajv@8.17.1)
|
||||
content-type: 1.0.5
|
||||
cors: 2.8.5
|
||||
cross-spawn: 7.0.6
|
||||
eventsource: 3.0.7
|
||||
eventsource-parser: 3.0.6
|
||||
express: 5.2.1
|
||||
express-rate-limit: 7.5.1(express@5.2.1)
|
||||
eventsource: 3.0.6
|
||||
eventsource-parser: 3.0.1
|
||||
express: 5.1.0
|
||||
express-rate-limit: 7.5.0(express@5.1.0)
|
||||
jose: 6.1.3
|
||||
pkce-challenge: 5.0.1
|
||||
raw-body: 3.0.2
|
||||
pkce-challenge: 5.0.0
|
||||
raw-body: 3.0.0
|
||||
zod: 4.1.12
|
||||
zod-to-json-schema: 3.25.0(zod@4.1.12)
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -16014,7 +16003,7 @@ snapshots:
|
|||
|
||||
accepts@2.0.0:
|
||||
dependencies:
|
||||
mime-types: 3.0.2
|
||||
mime-types: 3.0.1
|
||||
negotiator: 1.0.0
|
||||
|
||||
acorn-import-attributes@1.9.5(acorn@8.15.0):
|
||||
|
|
@ -16266,7 +16255,7 @@ snapshots:
|
|||
|
||||
axios@1.12.1:
|
||||
dependencies:
|
||||
follow-redirects: 1.15.9(debug@4.4.3)
|
||||
follow-redirects: 1.15.9(debug@4.4.0)
|
||||
form-data: 4.0.4
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -16397,33 +16386,33 @@ snapshots:
|
|||
|
||||
bluebird@3.4.7: {}
|
||||
|
||||
body-parser@1.20.4:
|
||||
body-parser@1.20.3:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
content-type: 1.0.5
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
destroy: 1.2.0
|
||||
http-errors: 2.0.1
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.4.24
|
||||
on-finished: 2.4.1
|
||||
qs: 6.14.0
|
||||
raw-body: 2.5.3
|
||||
qs: 6.13.0
|
||||
raw-body: 2.5.2
|
||||
type-is: 1.6.18
|
||||
unpipe: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
body-parser@2.2.1:
|
||||
body-parser@2.2.0:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
content-type: 1.0.5
|
||||
debug: 4.4.3
|
||||
http-errors: 2.0.1
|
||||
iconv-lite: 0.7.0
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.6.3
|
||||
on-finished: 2.4.1
|
||||
qs: 6.14.0
|
||||
raw-body: 3.0.2
|
||||
raw-body: 3.0.0
|
||||
type-is: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -16870,7 +16859,9 @@ snapshots:
|
|||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
content-disposition@1.0.1: {}
|
||||
content-disposition@1.0.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
content-type@1.0.5: {}
|
||||
|
||||
|
|
@ -16878,13 +16869,13 @@ snapshots:
|
|||
|
||||
convert-source-map@2.0.0: {}
|
||||
|
||||
cookie-signature@1.0.7: {}
|
||||
cookie-signature@1.0.6: {}
|
||||
|
||||
cookie-signature@1.2.2: {}
|
||||
|
||||
cookie@0.7.2: {}
|
||||
|
||||
cookie@1.1.1: {}
|
||||
cookie@1.0.2: {}
|
||||
|
||||
cookiejar@2.1.4: {}
|
||||
|
||||
|
|
@ -18167,11 +18158,11 @@ snapshots:
|
|||
|
||||
events@3.3.0: {}
|
||||
|
||||
eventsource-parser@3.0.6: {}
|
||||
eventsource-parser@3.0.1: {}
|
||||
|
||||
eventsource@3.0.7:
|
||||
eventsource@3.0.6:
|
||||
dependencies:
|
||||
eventsource-parser: 3.0.6
|
||||
eventsource-parser: 3.0.1
|
||||
|
||||
execa@5.1.1:
|
||||
dependencies:
|
||||
|
|
@ -18225,27 +18216,27 @@ snapshots:
|
|||
|
||||
exponential-backoff@3.1.2: {}
|
||||
|
||||
express-rate-limit@7.5.1(express@5.2.1):
|
||||
express-rate-limit@7.5.0(express@5.1.0):
|
||||
dependencies:
|
||||
express: 5.2.1
|
||||
express: 5.1.0
|
||||
|
||||
express@4.22.0:
|
||||
express@4.22.1:
|
||||
dependencies:
|
||||
accepts: 1.3.8
|
||||
array-flatten: 1.1.1
|
||||
body-parser: 1.20.4
|
||||
body-parser: 1.20.3
|
||||
content-disposition: 0.5.4
|
||||
content-type: 1.0.5
|
||||
cookie: 0.7.2
|
||||
cookie-signature: 1.0.7
|
||||
cookie-signature: 1.0.6
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
finalhandler: 1.3.2
|
||||
finalhandler: 1.3.1
|
||||
fresh: 0.5.2
|
||||
http-errors: 2.0.1
|
||||
http-errors: 2.0.0
|
||||
merge-descriptors: 1.0.3
|
||||
methods: 1.1.2
|
||||
on-finished: 2.4.1
|
||||
|
|
@ -18255,34 +18246,33 @@ snapshots:
|
|||
qs: 6.14.0
|
||||
range-parser: 1.2.1
|
||||
safe-buffer: 5.2.1
|
||||
send: 0.19.1
|
||||
send: 0.19.0
|
||||
serve-static: 1.16.2
|
||||
setprototypeof: 1.2.0
|
||||
statuses: 2.0.2
|
||||
statuses: 2.0.1
|
||||
type-is: 1.6.18
|
||||
utils-merge: 1.0.1
|
||||
vary: 1.1.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
express@5.2.1:
|
||||
express@5.1.0:
|
||||
dependencies:
|
||||
accepts: 2.0.0
|
||||
body-parser: 2.2.1
|
||||
content-disposition: 1.0.1
|
||||
body-parser: 2.2.0
|
||||
content-disposition: 1.0.0
|
||||
content-type: 1.0.5
|
||||
cookie: 0.7.2
|
||||
cookie-signature: 1.2.2
|
||||
debug: 4.4.3
|
||||
depd: 2.0.0
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
finalhandler: 2.1.1
|
||||
finalhandler: 2.1.0
|
||||
fresh: 2.0.0
|
||||
http-errors: 2.0.1
|
||||
http-errors: 2.0.0
|
||||
merge-descriptors: 2.0.0
|
||||
mime-types: 3.0.2
|
||||
mime-types: 3.0.1
|
||||
on-finished: 2.4.1
|
||||
once: 1.4.0
|
||||
parseurl: 1.3.3
|
||||
|
|
@ -18292,7 +18282,7 @@ snapshots:
|
|||
router: 2.2.0
|
||||
send: 1.2.0
|
||||
serve-static: 2.2.0
|
||||
statuses: 2.0.2
|
||||
statuses: 2.0.1
|
||||
type-is: 2.0.1
|
||||
vary: 1.1.2
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -18447,26 +18437,26 @@ snapshots:
|
|||
|
||||
filter-obj@1.1.0: {}
|
||||
|
||||
finalhandler@1.3.2:
|
||||
finalhandler@1.3.1:
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
on-finished: 2.4.1
|
||||
parseurl: 1.3.3
|
||||
statuses: 2.0.2
|
||||
statuses: 2.0.1
|
||||
unpipe: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
finalhandler@2.1.1:
|
||||
finalhandler@2.1.0:
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
on-finished: 2.4.1
|
||||
parseurl: 1.3.3
|
||||
statuses: 2.0.2
|
||||
statuses: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -18512,9 +18502,9 @@ snapshots:
|
|||
dependencies:
|
||||
tabbable: 6.2.0
|
||||
|
||||
follow-redirects@1.15.9(debug@4.4.3):
|
||||
follow-redirects@1.15.9(debug@4.4.0):
|
||||
optionalDependencies:
|
||||
debug: 4.4.3
|
||||
debug: 4.4.0
|
||||
|
||||
for-each@0.3.5:
|
||||
dependencies:
|
||||
|
|
@ -19022,14 +19012,6 @@ snapshots:
|
|||
statuses: 2.0.1
|
||||
toidentifier: 1.0.1
|
||||
|
||||
http-errors@2.0.1:
|
||||
dependencies:
|
||||
depd: 2.0.0
|
||||
inherits: 2.0.4
|
||||
setprototypeof: 1.2.0
|
||||
statuses: 2.0.2
|
||||
toidentifier: 1.0.1
|
||||
|
||||
http-proxy-agent@7.0.2:
|
||||
dependencies:
|
||||
agent-base: 7.1.3
|
||||
|
|
@ -19077,10 +19059,6 @@ snapshots:
|
|||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
iconv-lite@0.7.0:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
ignore@5.3.2: {}
|
||||
|
|
@ -19455,7 +19433,7 @@ snapshots:
|
|||
|
||||
istanbul-lib-source-maps@4.0.1:
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
debug: 4.4.1
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
source-map: 0.6.1
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -20018,7 +19996,7 @@ snapshots:
|
|||
|
||||
light-my-request@6.3.0:
|
||||
dependencies:
|
||||
cookie: 1.1.1
|
||||
cookie: 1.0.2
|
||||
process-warning: 4.0.1
|
||||
set-cookie-parser: 2.7.1
|
||||
|
||||
|
|
@ -20832,7 +20810,7 @@ snapshots:
|
|||
micromark@3.2.0:
|
||||
dependencies:
|
||||
'@types/debug': 4.1.12
|
||||
debug: 4.4.3
|
||||
debug: 4.4.1
|
||||
decode-named-character-reference: 1.2.0
|
||||
micromark-core-commonmark: 1.1.0
|
||||
micromark-factory-space: 1.1.0
|
||||
|
|
@ -20854,7 +20832,7 @@ snapshots:
|
|||
micromark@4.0.2:
|
||||
dependencies:
|
||||
'@types/debug': 4.1.12
|
||||
debug: 4.4.3
|
||||
debug: 4.4.1
|
||||
decode-named-character-reference: 1.2.0
|
||||
devlop: 1.1.0
|
||||
micromark-core-commonmark: 2.0.3
|
||||
|
|
@ -20891,7 +20869,7 @@ snapshots:
|
|||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
mime-types@3.0.2:
|
||||
mime-types@3.0.1:
|
||||
dependencies:
|
||||
mime-db: 1.54.0
|
||||
|
||||
|
|
@ -21015,9 +20993,9 @@ snapshots:
|
|||
dependencies:
|
||||
async-mutex: 0.5.0
|
||||
camelcase: 6.3.0
|
||||
debug: 4.4.3
|
||||
debug: 4.4.0
|
||||
find-cache-dir: 3.3.2
|
||||
follow-redirects: 1.15.9(debug@4.4.3)
|
||||
follow-redirects: 1.15.9(debug@4.4.0)
|
||||
https-proxy-agent: 7.0.6
|
||||
mongodb: 6.14.2(socks@2.8.4)
|
||||
new-find-package-json: 2.0.0
|
||||
|
|
@ -21166,7 +21144,7 @@ snapshots:
|
|||
|
||||
new-find-package-json@2.0.0:
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
debug: 4.4.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -21731,7 +21709,7 @@ snapshots:
|
|||
|
||||
path-to-regexp@6.3.0: {}
|
||||
|
||||
path-to-regexp@8.3.0: {}
|
||||
path-to-regexp@8.2.0: {}
|
||||
|
||||
path-type@4.0.0: {}
|
||||
|
||||
|
|
@ -21848,7 +21826,7 @@ snapshots:
|
|||
|
||||
pirates@4.0.6: {}
|
||||
|
||||
pkce-challenge@5.0.1: {}
|
||||
pkce-challenge@5.0.0: {}
|
||||
|
||||
pkg-dir@4.2.0:
|
||||
dependencies:
|
||||
|
|
@ -22045,6 +22023,10 @@ snapshots:
|
|||
pngjs: 5.0.0
|
||||
yargs: 15.4.1
|
||||
|
||||
qs@6.13.0:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
|
||||
qs@6.14.0:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
|
|
@ -22087,18 +22069,18 @@ snapshots:
|
|||
|
||||
range-parser@1.2.1: {}
|
||||
|
||||
raw-body@2.5.3:
|
||||
raw-body@2.5.2:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
http-errors: 2.0.1
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.4.24
|
||||
unpipe: 1.0.0
|
||||
|
||||
raw-body@3.0.2:
|
||||
raw-body@3.0.0:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
http-errors: 2.0.1
|
||||
iconv-lite: 0.7.0
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.6.3
|
||||
unpipe: 1.0.0
|
||||
|
||||
rc@1.2.8:
|
||||
|
|
@ -22656,7 +22638,7 @@ snapshots:
|
|||
depd: 2.0.0
|
||||
is-promise: 4.0.0
|
||||
parseurl: 1.3.3
|
||||
path-to-regexp: 8.3.0
|
||||
path-to-regexp: 8.2.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -22780,24 +22762,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
send@0.19.1:
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
destroy: 1.2.0
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
fresh: 0.5.2
|
||||
http-errors: 2.0.0
|
||||
mime: 1.6.0
|
||||
ms: 2.1.3
|
||||
on-finished: 2.4.1
|
||||
range-parser: 1.2.1
|
||||
statuses: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
send@1.2.0:
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
|
|
@ -22805,12 +22769,12 @@ snapshots:
|
|||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
fresh: 2.0.0
|
||||
http-errors: 2.0.1
|
||||
mime-types: 3.0.2
|
||||
http-errors: 2.0.0
|
||||
mime-types: 3.0.1
|
||||
ms: 2.1.3
|
||||
on-finished: 2.4.1
|
||||
range-parser: 1.2.1
|
||||
statuses: 2.0.2
|
||||
statuses: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -22978,7 +22942,7 @@ snapshots:
|
|||
socks-proxy-agent@8.0.5:
|
||||
dependencies:
|
||||
agent-base: 7.1.3
|
||||
debug: 4.4.3
|
||||
debug: 4.4.1
|
||||
socks: 2.8.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -23052,8 +23016,6 @@ snapshots:
|
|||
|
||||
statuses@2.0.1: {}
|
||||
|
||||
statuses@2.0.2: {}
|
||||
|
||||
std-env@3.8.1: {}
|
||||
|
||||
stdin-discarder@0.1.0:
|
||||
|
|
@ -23251,7 +23213,7 @@ snapshots:
|
|||
dependencies:
|
||||
component-emitter: 1.3.1
|
||||
cookiejar: 2.1.4
|
||||
debug: 4.4.3
|
||||
debug: 4.4.1
|
||||
fast-safe-stringify: 2.1.1
|
||||
form-data: 4.0.4
|
||||
formidable: 2.1.2
|
||||
|
|
@ -23632,7 +23594,7 @@ snapshots:
|
|||
dependencies:
|
||||
content-type: 1.0.5
|
||||
media-typer: 1.1.0
|
||||
mime-types: 3.0.2
|
||||
mime-types: 3.0.1
|
||||
|
||||
typed-array-buffer@1.0.3:
|
||||
dependencies:
|
||||
|
|
@ -23979,7 +23941,7 @@ snapshots:
|
|||
vite-node@1.6.1(@types/node@24.0.13)(lightningcss@1.30.1)(sass@1.85.1)(terser@5.39.0):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.3
|
||||
debug: 4.4.1
|
||||
pathe: 1.1.2
|
||||
picocolors: 1.1.1
|
||||
vite: 5.4.14(@types/node@24.0.13)(lightningcss@1.30.1)(sass@1.85.1)(terser@5.39.0)
|
||||
|
|
@ -24087,7 +24049,7 @@ snapshots:
|
|||
'@vitest/utils': 1.6.1
|
||||
acorn-walk: 8.3.4
|
||||
chai: 4.5.0
|
||||
debug: 4.4.3
|
||||
debug: 4.4.0
|
||||
execa: 8.0.1
|
||||
local-pkg: 0.5.1
|
||||
magic-string: 0.30.17
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import type {
|
|||
import type { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import type { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import type { TopAgentFormDataType } from '@fastgpt/service/core/chat/HelperBot/dispatch/topAgent/type';
|
||||
import type { GeneratedSkillDataType } from '@fastgpt/global/core/chat/helperBot/generatedSkill/type';
|
||||
|
||||
export type generatingMessageProps = {
|
||||
event: SseResponseEventEnum;
|
||||
|
|
@ -27,6 +28,7 @@ export type generatingMessageProps = {
|
|||
|
||||
// Agent
|
||||
formData?: TopAgentFormDataType;
|
||||
generatedSkill?: GeneratedSkillDataType;
|
||||
};
|
||||
|
||||
export type StartChatFnProps = {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,9 @@ import {
|
|||
AccordionIcon,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Button,
|
||||
Flex,
|
||||
HStack
|
||||
} from '@chakra-ui/react';
|
||||
import AIResponseBox from '../../components/AIResponseBox';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import Markdown from '@/components/Markdown';
|
||||
|
|
@ -71,6 +69,7 @@ const RenderResoningContent = React.memo(function RenderResoningContent({
|
|||
</Accordion>
|
||||
);
|
||||
});
|
||||
|
||||
const RenderText = React.memo(function RenderText({
|
||||
showAnimation,
|
||||
text
|
||||
|
|
|
|||
|
|
@ -1,22 +1,30 @@
|
|||
import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance';
|
||||
import React, { useState, type ReactNode } from 'react';
|
||||
import React, { type ReactNode } from 'react';
|
||||
import { createContext } from 'use-context-selector';
|
||||
import {
|
||||
HelperBotTypeEnum,
|
||||
type HelperBotTypeEnumType,
|
||||
type TopAgentParamsType
|
||||
} from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import { HelperBotTypeEnum } from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import type { TopAgentParamsType } from '@fastgpt/global/core/chat/helperBot/topAgent/type';
|
||||
import type { AppFileSelectConfigType } from '@fastgpt/global/core/app/type';
|
||||
import type { TopAgentFormDataType } from '@fastgpt/service/core/chat/HelperBot/dispatch/topAgent/type';
|
||||
import type {
|
||||
GeneratedSkillResultType,
|
||||
SkillAgentParamsType
|
||||
} from '@fastgpt/global/core/chat/helperBot/skillAgent/type';
|
||||
|
||||
export type HelperBotProps = {
|
||||
emptyDom?: ReactNode;
|
||||
fileSelectConfig?: AppFileSelectConfigType;
|
||||
} & {
|
||||
type: HelperBotTypeEnumType;
|
||||
metadata: TopAgentParamsType;
|
||||
onApply: (e: TopAgentFormDataType) => void;
|
||||
};
|
||||
} & (
|
||||
| {
|
||||
type: typeof HelperBotTypeEnum.topAgent;
|
||||
metadata: TopAgentParamsType;
|
||||
onApply: (e: TopAgentFormDataType) => void;
|
||||
}
|
||||
| {
|
||||
type: typeof HelperBotTypeEnum.skillAgent;
|
||||
metadata: SkillAgentParamsType;
|
||||
onApply: (e: GeneratedSkillResultType) => void;
|
||||
}
|
||||
);
|
||||
type HelperBotContextType = HelperBotProps & {};
|
||||
|
||||
export const HelperBotContext = createContext<HelperBotContextType>({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { POST, GET, PUT, DELETE } from '@/web/common/api/request';
|
||||
import type {
|
||||
ListAiSkillBodyType,
|
||||
ListAiSkillResponse,
|
||||
GetAiSkillDetailQueryType,
|
||||
UpdateAiSkillBodyType,
|
||||
DeleteAiSkillQueryType
|
||||
} from '@fastgpt/global/openapi/core/ai/skill/api';
|
||||
import type { AiSkillSchemaType } from '@fastgpt/global/core/ai/skill/type';
|
||||
|
||||
export const updateAiSkill = (data: UpdateAiSkillBodyType) =>
|
||||
PUT<{ success: boolean; _id: string }>('/core/ai/skill/update', data);
|
||||
|
||||
export const getAiSkillList = (data: ListAiSkillBodyType) =>
|
||||
POST<ListAiSkillResponse>('/core/ai/skill/list', data);
|
||||
|
||||
export const getAiSkillDetail = (data: GetAiSkillDetailQueryType) =>
|
||||
GET<AiSkillSchemaType>('/core/ai/skill/detail', data);
|
||||
|
||||
export const deleteAiSkill = (data: DeleteAiSkillQueryType) =>
|
||||
DELETE<{ success: boolean }>('/core/ai/skill/delete', data);
|
||||
|
|
@ -27,7 +27,10 @@ import { useForm } from 'react-hook-form';
|
|||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useMemoizedFn, useThrottleFn } from 'ahooks';
|
||||
import type { HelperBotChatItemSiteType } from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import {
|
||||
HelperBotTypeEnum,
|
||||
type HelperBotChatItemSiteType
|
||||
} from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import type { onSendMessageParamsType } from './type';
|
||||
import { textareaMinH } from '../ChatContainer/ChatBox/constants';
|
||||
import { streamFetch } from '@/web/common/api/fetch';
|
||||
|
|
@ -42,6 +45,7 @@ const ChatBox = ({ type, metadata, onApply, ...props }: HelperBotProps) => {
|
|||
// Messages 管理
|
||||
const [chatId, setChatId] = useState<string>(getNanoid(12));
|
||||
const [isChatting, setIsChatting] = useState(false);
|
||||
|
||||
const chatForm = useForm<ChatBoxInputFormType>({
|
||||
defaultValues: {
|
||||
input: '',
|
||||
|
|
@ -144,7 +148,14 @@ const ChatBox = ({ type, metadata, onApply, ...props }: HelperBotProps) => {
|
|||
}
|
||||
);
|
||||
const generatingMessage = useMemoizedFn(
|
||||
({ event, text = '', reasoningText, tool, formData }: generatingMessageProps) => {
|
||||
({
|
||||
event,
|
||||
text = '',
|
||||
reasoningText,
|
||||
tool,
|
||||
formData,
|
||||
generatedSkill
|
||||
}: generatingMessageProps) => {
|
||||
setChatRecords((state) =>
|
||||
state.map((item, index) => {
|
||||
if (index !== state.length - 1) return item;
|
||||
|
|
@ -155,7 +166,19 @@ const ChatBox = ({ type, metadata, onApply, ...props }: HelperBotProps) => {
|
|||
|
||||
// Special event: form data
|
||||
if (event === SseResponseEventEnum.formData && formData) {
|
||||
onApply?.(formData);
|
||||
if (type === HelperBotTypeEnum.topAgent) {
|
||||
onApply?.(formData);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
// Special event: generated skill
|
||||
if (event === SseResponseEventEnum.generatedSkill && generatedSkill) {
|
||||
console.log('📊 HelperBot: Received generatedSkill event', generatedSkill);
|
||||
// 直接将生成的 skill 数据传递给 onApply 回调(仅在 skillAgent 类型时)
|
||||
if (type === HelperBotTypeEnum.skillAgent) {
|
||||
onApply?.(generatedSkill);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +343,7 @@ const ChatBox = ({ type, metadata, onApply, ...props }: HelperBotProps) => {
|
|||
try {
|
||||
const abortSignal = new AbortController();
|
||||
chatController.current = abortSignal;
|
||||
|
||||
console.log('metadata-fronted', metadata);
|
||||
const { responseText } = await streamFetch({
|
||||
url: '/api/core/chat/helperBot/completions',
|
||||
data: {
|
||||
|
|
@ -334,7 +357,7 @@ const ChatBox = ({ type, metadata, onApply, ...props }: HelperBotProps) => {
|
|||
name: item.name
|
||||
})),
|
||||
metadata: {
|
||||
type,
|
||||
type: type,
|
||||
data: metadata
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
|
||||
import ChatTest from './ChatTest';
|
||||
|
|
@ -12,6 +12,8 @@ import { type SimpleAppSnapshotType } from '../FormComponent/useSnapshots';
|
|||
import { agentForm2AppWorkflow } from './utils';
|
||||
import styles from '../FormComponent/styles.module.scss';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { getAiSkillDetail } from '@/web/core/ai/skill/api';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
|
||||
const SkillEditForm = dynamic(() => import('./SkillEdit/EditForm'), { ssr: false });
|
||||
const SKillChatTest = dynamic(() => import('./SkillEdit/ChatTest'), { ssr: false });
|
||||
|
|
@ -27,7 +29,31 @@ const Edit = ({
|
|||
}) => {
|
||||
const { isPc } = useSystem();
|
||||
const [renderEdit, setRenderEdit] = useState(true);
|
||||
const [editSkill, setEditSkill] = useState<SkillEditType>();
|
||||
// 状态:当前正在编辑的 skill(完整对象)
|
||||
const [editingSkill, setEditingSkill] = useState<SkillEditType>();
|
||||
|
||||
// 处理保存
|
||||
const handleSaveSkill = useCallback(
|
||||
(savedSkill: SkillEditType) => {
|
||||
setAppForm((state) => {
|
||||
const skillExists = state.skills.some((s) => s.id === savedSkill.id);
|
||||
return {
|
||||
...state,
|
||||
skills: skillExists
|
||||
? state.skills.map((s) => (s.id === savedSkill.id ? savedSkill : s))
|
||||
: [savedSkill, ...state.skills]
|
||||
};
|
||||
});
|
||||
setEditingSkill(undefined);
|
||||
},
|
||||
[setAppForm]
|
||||
);
|
||||
const handleAIGenerate = useCallback(
|
||||
(updates: Partial<SkillEditType>) => {
|
||||
setEditingSkill((prev) => (prev ? { ...prev, ...updates } : prev));
|
||||
},
|
||||
[setEditingSkill]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
|
|
@ -55,11 +81,7 @@ const Edit = ({
|
|||
</Box>
|
||||
|
||||
<Box pb={4}>
|
||||
<EditForm
|
||||
appForm={appForm}
|
||||
setAppForm={setAppForm}
|
||||
onEditSkill={(e) => setEditSkill(e)}
|
||||
/>
|
||||
<EditForm appForm={appForm} setAppForm={setAppForm} onEditSkill={setEditingSkill} />
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
|
@ -75,7 +97,7 @@ const Edit = ({
|
|||
)}
|
||||
|
||||
{/* Mask */}
|
||||
{editSkill && (
|
||||
{editingSkill && (
|
||||
<Box
|
||||
position={'absolute'}
|
||||
top={0}
|
||||
|
|
@ -98,23 +120,27 @@ const Edit = ({
|
|||
bg={'white'}
|
||||
borderRadius={'md'}
|
||||
zIndex={10}
|
||||
transform={editSkill ? 'translateX(0)' : 'translateX(100%)'}
|
||||
transform={editingSkill ? 'translateX(0)' : 'translateX(100%)'}
|
||||
transition={'transform 0.3s ease-in-out'}
|
||||
pointerEvents={editSkill ? 'auto' : 'none'}
|
||||
pointerEvents={editingSkill ? 'auto' : 'none'}
|
||||
>
|
||||
{editSkill && (
|
||||
{editingSkill && (
|
||||
<>
|
||||
<Box overflowY={'auto'} minW={['auto', '580px']} flex={'1'} borderRight={'base'}>
|
||||
<SkillEditForm
|
||||
model={appForm.aiSettings.model}
|
||||
fileSelectConfig={appForm.chatConfig.fileSelectConfig}
|
||||
defaultSkill={editSkill}
|
||||
onClose={() => setEditSkill(undefined)}
|
||||
setAppForm={setAppForm}
|
||||
skill={editingSkill}
|
||||
onClose={() => setEditingSkill(undefined)}
|
||||
onSave={handleSaveSkill}
|
||||
/>
|
||||
</Box>
|
||||
<Box flex={'2 0 0'} w={0} mb={3}>
|
||||
<SKillChatTest skill={editSkill} setAppForm={setAppForm} />
|
||||
<SKillChatTest
|
||||
skill={editingSkill}
|
||||
appForm={appForm}
|
||||
onAIGenerate={handleAIGenerate}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState, useTransition } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useTransition } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
|
|
@ -7,8 +7,7 @@ import {
|
|||
useTheme,
|
||||
useDisclosure,
|
||||
Button,
|
||||
HStack,
|
||||
Input
|
||||
HStack
|
||||
} from '@chakra-ui/react';
|
||||
import type { SkillEditType } from '@fastgpt/global/core/app/formEdit/type';
|
||||
import type { AppFormEditFormType } from '@fastgpt/global/core/app/formEdit/type';
|
||||
|
|
@ -32,6 +31,8 @@ import ToolSelect from '../FormComponent/ToolSelector/ToolSelect';
|
|||
import SkillRow from './SkillEdit/Row';
|
||||
import { cardStyles } from '../../constants';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { getAiSkillDetail } from '@/web/core/ai/skill/api';
|
||||
|
||||
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
|
||||
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'));
|
||||
|
|
@ -102,6 +103,29 @@ const EditForm = ({
|
|||
}
|
||||
}, [selectedModel, setAppForm]);
|
||||
|
||||
// 打开编辑器
|
||||
const handleEditSkill = useCallback(
|
||||
async (skill: SkillEditType) => {
|
||||
// If skill has dbId, load full details from server
|
||||
if (skill.id) {
|
||||
const detail = await getAiSkillDetail({ id: skill.id });
|
||||
// Merge server data with local data
|
||||
onEditSkill({
|
||||
id: detail._id,
|
||||
name: detail.name,
|
||||
description: detail.description || '',
|
||||
stepsText: detail.steps,
|
||||
selectedTools: detail.tools || [],
|
||||
dataset: { list: detail.datasets || [] }
|
||||
});
|
||||
} else {
|
||||
// New skill without dbId
|
||||
onEditSkill(skill);
|
||||
}
|
||||
},
|
||||
[onEditSkill]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box mt={4} {...cardStyles} boxShadow={'3.5'}>
|
||||
|
|
@ -168,7 +192,6 @@ const EditForm = ({
|
|||
}));
|
||||
});
|
||||
}}
|
||||
// variableLabels={formatVariables}
|
||||
title={t('app:ai_role')}
|
||||
isRichText={false}
|
||||
/>
|
||||
|
|
@ -204,16 +227,7 @@ const EditForm = ({
|
|||
</Box>
|
||||
|
||||
<Box {...BoxStyles}>
|
||||
<SkillRow
|
||||
skills={appForm.skills}
|
||||
onEditSkill={onEditSkill}
|
||||
onDeleteSkill={(id) => {
|
||||
setAppForm((state) => ({
|
||||
...state,
|
||||
skills: state.skills.filter((item) => item.id !== id)
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<SkillRow skills={appForm.skills} onEditSkill={handleEditSkill} setAppForm={setAppForm} />
|
||||
</Box>
|
||||
{/* tool choice */}
|
||||
<Box {...BoxStyles}>
|
||||
|
|
|
|||
|
|
@ -5,24 +5,35 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import type { SkillEditType } from '@fastgpt/global/core/app/formEdit/type';
|
||||
import type { AppFormEditFormType } from '@fastgpt/global/core/app/formEdit/type';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '../../../context';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { cardStyles } from '../../../constants';
|
||||
import HelperBot from '@/components/core/chat/HelperBot';
|
||||
import { HelperBotTypeEnum } from '@fastgpt/global/core/chat/helperBot/type';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
|
||||
type Props = {
|
||||
skill: SkillEditType;
|
||||
setAppForm: React.Dispatch<React.SetStateAction<AppFormEditFormType>>;
|
||||
appForm: AppFormEditFormType;
|
||||
onAIGenerate: (updates: Partial<SkillEditType>) => void;
|
||||
};
|
||||
const ChatTest = ({ skill, setAppForm }: Props) => {
|
||||
const ChatTest = ({ skill, appForm, onAIGenerate }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
|
||||
// 构建 SkillAgent metadata,从 appForm 中提取配置
|
||||
const skillAgentMetadata = useMemo(() => ({}), []);
|
||||
const skillAgentMetadata = useMemo(() => {
|
||||
return {
|
||||
skillAgent: {
|
||||
name: skill.name,
|
||||
description: skill.description,
|
||||
stepsText: skill.stepsText
|
||||
},
|
||||
topAgent: {
|
||||
role: appForm.aiSettings.aiRole,
|
||||
taskObject: appForm.aiSettings.aiTaskObject,
|
||||
fileUpload: appForm.chatConfig.fileSelectConfig?.canSelectFile || false,
|
||||
selectedTools: skill.selectedTools?.map((tool) => tool.id) || [],
|
||||
selectedDatasets: skill.dataset?.list?.map((ds) => ds.datasetId) || []
|
||||
}
|
||||
};
|
||||
}, [appForm.aiSettings, appForm.chatConfig.fileSelectConfig, skill]);
|
||||
|
||||
return (
|
||||
<MyBox display={'flex'} position={'relative'} flexDirection={'column'} h={'full'} py={4}>
|
||||
|
|
@ -46,10 +57,28 @@ const ChatTest = ({ skill, setAppForm }: Props) => {
|
|||
</Flex>
|
||||
<Box flex={1}>
|
||||
<HelperBot
|
||||
type={HelperBotTypeEnum.skillEditor}
|
||||
type={HelperBotTypeEnum.skillAgent}
|
||||
metadata={skillAgentMetadata}
|
||||
onApply={(e) => {
|
||||
console.log(e);
|
||||
onApply={(generatedSkillData) => {
|
||||
console.log(generatedSkillData, 222);
|
||||
// const stepsText = generatedSkillData.execution_plan.steps
|
||||
// .map((step, index) => {
|
||||
// let stepText = `步骤 ${index + 1}: ${step.title}\n${step.description}`;
|
||||
// if (step.expectedTools && step.expectedTools.length > 0) {
|
||||
// const tools = step.expectedTools
|
||||
// .map((tool) => `${tool.type === 'tool' ? '🔧' : '📚'} ${tool.id}`)
|
||||
// .join(', ');
|
||||
// stepText += `\n使用工具: ${tools}`;
|
||||
// }
|
||||
// return stepText;
|
||||
// })
|
||||
// .join('\n\n');
|
||||
|
||||
// onAIGenerate({
|
||||
// name: generatedSkillData.plan_analysis.name || skill.name,
|
||||
// description: generatedSkillData.plan_analysis.description || skill.description,
|
||||
// stepsText: stepsText
|
||||
// });
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import React, { useEffect, useMemo, useTransition } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Grid,
|
||||
type BoxProps,
|
||||
useTheme,
|
||||
useDisclosure,
|
||||
Button,
|
||||
|
|
@ -14,79 +13,62 @@ import {
|
|||
} from '@chakra-ui/react';
|
||||
import type { AppFileSelectConfigType } from '@fastgpt/global/core/app/type';
|
||||
import type { SkillEditType } from '@fastgpt/global/core/app/formEdit/type';
|
||||
import type { AppFormEditFormType } from '@fastgpt/global/core/app/formEdit/type';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import VariableEdit from '@/components/core/app/VariableEdit';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/workflow/utils';
|
||||
import SearchParamsTip from '@/components/core/dataset/SearchParamsTip';
|
||||
import SettingLLMModel from '@/components/core/ai/SettingLLMModel';
|
||||
import { TTSTypeEnum } from '@/web/core/app/constants';
|
||||
import { workflowSystemVariables } from '@/web/core/app/utils';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '@/pageComponents/app/detail/context';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip';
|
||||
import { getWebLLMModel } from '@/web/common/system/utils';
|
||||
import ToolSelect from '../../FormComponent/ToolSelector/ToolSelect';
|
||||
import OptimizerPopover from '@/components/common/PromptEditor/OptimizerPopover';
|
||||
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { useSkillManager } from '../hooks/useSkillManager';
|
||||
import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance';
|
||||
import { cardStyles } from '../../../constants';
|
||||
import { defaultSkill as defaultEditSkill } from './Row';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { updateAiSkill } from '@/web/core/ai/skill/api';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '@/pageComponents/app/detail/context';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
|
||||
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
|
||||
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'));
|
||||
const TTSSelect = dynamic(() => import('@/components/core/app/TTSSelect'));
|
||||
const QGConfig = dynamic(() => import('@/components/core/app/QGConfig'));
|
||||
const WhisperConfig = dynamic(() => import('@/components/core/app/WhisperConfig'));
|
||||
const InputGuideConfig = dynamic(() => import('@/components/core/app/InputGuideConfig'));
|
||||
const WelcomeTextConfig = dynamic(() => import('@/components/core/app/WelcomeTextConfig'));
|
||||
const FileSelectConfig = dynamic(() => import('@/components/core/app/FileSelect'));
|
||||
|
||||
const EditForm = ({
|
||||
model,
|
||||
fileSelectConfig,
|
||||
defaultSkill = defaultEditSkill,
|
||||
onClose,
|
||||
setAppForm
|
||||
}: {
|
||||
type EditFormProps = {
|
||||
model: string;
|
||||
fileSelectConfig?: AppFileSelectConfigType;
|
||||
defaultSkill?: SkillEditType;
|
||||
skill: SkillEditType;
|
||||
onClose: () => void;
|
||||
setAppForm: React.Dispatch<React.SetStateAction<AppFormEditFormType>>;
|
||||
}) => {
|
||||
onSave: (skill: SkillEditType) => void;
|
||||
};
|
||||
|
||||
const EditForm = ({ model, fileSelectConfig, skill, onClose, onSave }: EditFormProps) => {
|
||||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const [, startTst] = useTransition();
|
||||
const appId = useContextSelector(AppContext, (v) => v.appId);
|
||||
|
||||
// Form state management with validation
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
reset,
|
||||
formState: { isDirty }
|
||||
} = useForm<SkillEditType>({
|
||||
defaultValues: skill
|
||||
});
|
||||
|
||||
// Reset form when skill prop changes
|
||||
useEffect(() => {
|
||||
reset(skill);
|
||||
}, [skill, reset]);
|
||||
|
||||
const selectedModel = getWebLLMModel(model);
|
||||
|
||||
const { register, setValue, handleSubmit, reset, watch } = useForm<SkillEditType>({
|
||||
defaultValues: defaultSkill
|
||||
});
|
||||
useEffect(() => {
|
||||
reset(defaultSkill);
|
||||
}, [defaultSkill, reset]);
|
||||
|
||||
const name = watch('name');
|
||||
const prompt = watch('prompt');
|
||||
const selectedTools = watch('selectedTools');
|
||||
const selectDatasets = watch('dataset.list');
|
||||
const selectedTools = watch('selectedTools') || [];
|
||||
const selectDatasets = watch('dataset.list') || [];
|
||||
const skillName = watch('name');
|
||||
|
||||
const {
|
||||
isOpen: isOpenDatasetSelect,
|
||||
|
|
@ -94,15 +76,46 @@ const EditForm = ({
|
|||
onClose: onCloseKbSelect
|
||||
} = useDisclosure();
|
||||
|
||||
const onSave = (e: SkillEditType) => {
|
||||
setAppForm((state) => ({
|
||||
...state,
|
||||
skills: e.id
|
||||
? state.skills.map((item) => (item.id === e.id ? e : item))
|
||||
: [{ ...e, id: getNanoid(6) }, ...state.skills]
|
||||
}));
|
||||
onClose();
|
||||
};
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
content: t('common:confirm_exit_without_saving')
|
||||
});
|
||||
|
||||
const { runAsync: onSubmit, loading: isSaving } = useRequest2(
|
||||
async (formData: SkillEditType) => {
|
||||
const result = await updateAiSkill({
|
||||
id: formData.id,
|
||||
appId,
|
||||
name: formData.name,
|
||||
description: formData.description || '',
|
||||
steps: formData.stepsText || '',
|
||||
tools: (formData.selectedTools || []).map((item) => ({
|
||||
id: item.pluginId!,
|
||||
// 遍历 tool 的 inputs,转成 object
|
||||
config: item.inputs?.reduce(
|
||||
(acc, input) => {
|
||||
acc[input.key] = input.value;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>
|
||||
)
|
||||
})),
|
||||
datasets: (formData.dataset?.list || []).map((item) => ({
|
||||
datasetId: item.datasetId,
|
||||
name: item.name,
|
||||
avatar: item.avatar,
|
||||
vectorModel: item.vectorModel
|
||||
}))
|
||||
});
|
||||
|
||||
onSave({ ...formData, id: result });
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
successToast: t('common:save_success')
|
||||
}
|
||||
);
|
||||
|
||||
const handleFormSubmit = handleSubmit(onSubmit);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -116,17 +129,26 @@ const EditForm = ({
|
|||
aria-label={''}
|
||||
w={'28px'}
|
||||
h={'28px'}
|
||||
onClick={onClose}
|
||||
onClick={() => {
|
||||
if (isDirty) {
|
||||
openConfirm(() => {
|
||||
onClose();
|
||||
})();
|
||||
} else {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Box color={'myGray.900'} flex={'1 0 0'} w={'0'} className={'textEllipsis'}>
|
||||
{name || t('app:skill_empty_name')}
|
||||
{skillName || t('app:skill_empty_name')}
|
||||
</Box>
|
||||
|
||||
<Button
|
||||
variant={'primaryOutline'}
|
||||
size={'sm'}
|
||||
leftIcon={<MyIcon name="save" w={'1rem'} />}
|
||||
onClick={handleSubmit(onSave)}
|
||||
onClick={handleFormSubmit}
|
||||
isLoading={isSaving}
|
||||
>
|
||||
{t('common:Save')}
|
||||
</Button>
|
||||
|
|
@ -136,12 +158,16 @@ const EditForm = ({
|
|||
<FormLabel mr={3} required>
|
||||
{t('common:Name')}
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...register('name', { required: true })}
|
||||
bg={'myGray.50'}
|
||||
maxLength={30}
|
||||
placeholder={t('app:skill_name_placeholder')}
|
||||
/>
|
||||
<Box flex={1}>
|
||||
<Input
|
||||
{...register('name', {
|
||||
required: true
|
||||
})}
|
||||
bg={'myGray.50'}
|
||||
maxLength={50}
|
||||
placeholder={t('app:skill_name_placeholder')}
|
||||
/>
|
||||
</Box>
|
||||
</HStack>
|
||||
{/* Desc */}
|
||||
<Box mt={4}>
|
||||
|
|
@ -152,30 +178,32 @@ const EditForm = ({
|
|||
<QuestionTip label={t('app:skill_description_placeholder')} />
|
||||
</HStack>
|
||||
<Textarea
|
||||
{...register('description', {
|
||||
required: true
|
||||
})}
|
||||
bg={'myGray.50'}
|
||||
maxLength={10000}
|
||||
rows={3}
|
||||
mt={1}
|
||||
resize={'vertical'}
|
||||
{...register('description', { required: true })}
|
||||
placeholder={t('app:skill_description_placeholder')}
|
||||
/>
|
||||
</Box>
|
||||
{/* Prompt */}
|
||||
{/* Steps */}
|
||||
<Box mt={4}>
|
||||
<HStack w={'100%'}>
|
||||
<FormLabel>Prompt</FormLabel>
|
||||
<FormLabel>{t('app:execution_steps')}</FormLabel>
|
||||
</HStack>
|
||||
<Box mt={1}>
|
||||
<PromptEditor
|
||||
minH={100}
|
||||
maxH={300}
|
||||
value={prompt}
|
||||
onChange={(text) => {
|
||||
startTst(() => {
|
||||
setValue('prompt', text);
|
||||
});
|
||||
}}
|
||||
isRichText={false}
|
||||
<Box mt={2}>
|
||||
<Textarea
|
||||
{...register('stepsText')}
|
||||
maxLength={1000000}
|
||||
bg={'myGray.50'}
|
||||
rows={10}
|
||||
resize={'vertical'}
|
||||
placeholder={t('app:no_steps_yet')}
|
||||
fontSize={'sm'}
|
||||
color={'myGray.900'}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
|
@ -187,16 +215,19 @@ const EditForm = ({
|
|||
selectedTools={selectedTools}
|
||||
fileSelectConfig={fileSelectConfig}
|
||||
onAddTool={(e) => {
|
||||
setValue('selectedTools', [e, ...(selectedTools || [])]);
|
||||
setValue('selectedTools', [e, ...(selectedTools || [])], { shouldDirty: true });
|
||||
}}
|
||||
onUpdateTool={(e) => {
|
||||
setValue(
|
||||
'selectedTools',
|
||||
selectedTools?.map((item) => (item.id === e.id ? e : item)) || []
|
||||
selectedTools?.map((item) => (item.id === e.id ? e : item)) || [],
|
||||
{ shouldDirty: true }
|
||||
);
|
||||
}}
|
||||
onRemoveTool={(id) => {
|
||||
setValue('selectedTools', selectedTools?.filter((item) => item.id !== id) || []);
|
||||
setValue('selectedTools', selectedTools?.filter((item) => item.id !== id) || [], {
|
||||
shouldDirty: true
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
|
@ -268,10 +299,12 @@ const EditForm = ({
|
|||
}))}
|
||||
onClose={onCloseKbSelect}
|
||||
onChange={(e) => {
|
||||
setValue('dataset.list', e);
|
||||
setValue('dataset.list', e, { shouldDirty: true });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ConfirmModal />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Box, Button, Flex, Grid, HStack, useDisclosure } from '@chakra-ui/react';
|
||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
|
|
@ -7,37 +7,55 @@ import { SmallAddIcon } from '@chakra-ui/icons';
|
|||
import { useTranslation } from 'next-i18next';
|
||||
import type { SkillEditType } from '@fastgpt/global/core/app/formEdit/type';
|
||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { deleteAiSkill } from '@/web/core/ai/skill/api';
|
||||
import type { AppFormEditFormType } from '@fastgpt/global/core/app/formEdit/type';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
|
||||
export const defaultSkill: SkillEditType = {
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
prompt: '',
|
||||
stepsText: '',
|
||||
dataset: {
|
||||
list: []
|
||||
},
|
||||
selectedTools: [],
|
||||
fileSelectConfig: {
|
||||
canSelectFile: false,
|
||||
canSelectImg: false,
|
||||
canSelectVideo: false,
|
||||
canSelectAudio: false,
|
||||
canSelectCustomFileExtension: false,
|
||||
customFileExtensionList: []
|
||||
}
|
||||
selectedTools: []
|
||||
};
|
||||
|
||||
const Row = ({
|
||||
skills,
|
||||
onEditSkill,
|
||||
onDeleteSkill
|
||||
setAppForm
|
||||
}: {
|
||||
skills: SkillEditType[];
|
||||
onEditSkill: (e: SkillEditType) => void;
|
||||
onDeleteSkill: (id: string) => void;
|
||||
onEditSkill: (e: SkillEditType) => Promise<void>;
|
||||
setAppForm: React.Dispatch<React.SetStateAction<AppFormEditFormType>>;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { runAsync: handleEditSkill, loading: isEditingSkill } = useRequest2(onEditSkill, {
|
||||
manual: true
|
||||
});
|
||||
const { runAsync: handleDeleteSkill, loading: isDeletingSkill } = useRequest2(
|
||||
async (skill: SkillEditType) => {
|
||||
await deleteAiSkill({ id: skill.id });
|
||||
// Remove from local state
|
||||
setAppForm((state) => ({
|
||||
...state,
|
||||
skills: state.skills.filter((s) => s.id !== skill.id)
|
||||
}));
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
successToast: t('app:skill_delete_success'),
|
||||
errorToast: t('app:delete_failed')
|
||||
}
|
||||
);
|
||||
|
||||
const isLoading = isEditingSkill;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Flex alignItems={'center'}>
|
||||
|
|
@ -53,34 +71,43 @@ const Row = ({
|
|||
mr={'-5px'}
|
||||
size={'sm'}
|
||||
fontSize={'sm'}
|
||||
onClick={() => onEditSkill({ ...defaultSkill })}
|
||||
onClick={() => handleEditSkill({ ...defaultSkill })}
|
||||
>
|
||||
{t('common:Add')}
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<Box mt={3}>
|
||||
<MyBox isLoading={isLoading} maxH={'200px'} overflowY={'auto'}>
|
||||
{skills.map((skill) => (
|
||||
<HStack
|
||||
<Flex
|
||||
key={skill.id}
|
||||
alignItems={'center'}
|
||||
justifyContent={'space-between'}
|
||||
gap={2}
|
||||
py={2}
|
||||
px={4}
|
||||
borderRadius={'md'}
|
||||
border={'base'}
|
||||
_notLast={{
|
||||
mb: 2
|
||||
}}
|
||||
mt={3}
|
||||
_hover={{
|
||||
bg: 'myGray.25'
|
||||
}}
|
||||
>
|
||||
<Box flex={'1 0 0'}>{skill.name}</Box>
|
||||
<MyIconButton icon={'edit'} onClick={() => onEditSkill(skill)} />
|
||||
<MyIconButton icon={'delete'} onClick={() => onDeleteSkill(skill.id)} />
|
||||
</HStack>
|
||||
<MyIconButton icon={'edit'} onClick={() => handleEditSkill(skill)} />
|
||||
<PopoverConfirm
|
||||
type="delete"
|
||||
content={t('app:confirm_delete_skill')}
|
||||
onConfirm={() => handleDeleteSkill(skill)}
|
||||
Trigger={
|
||||
<Box>
|
||||
<MyIconButton icon={'delete'} />
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
</MyBox>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ import { useTranslation } from 'next-i18next';
|
|||
import { useSimpleAppSnapshots } from '../FormComponent/useSnapshots';
|
||||
import { useDebounceEffect, useMount } from 'ahooks';
|
||||
import { defaultAppSelectFileConfig } from '@fastgpt/global/core/app/constants';
|
||||
import { getAiSkillList } from '@/web/core/ai/skill/api';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { defaultSkill } from './SkillEdit/Row';
|
||||
import type { SkillEditType } from '@fastgpt/global/core/app/formEdit/type';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
|
||||
const Edit = dynamic(() => import('./Edit'));
|
||||
const Logs = dynamic(() => import('../../Logs/index'));
|
||||
|
|
@ -27,9 +32,11 @@ const AgentEdit = () => {
|
|||
const [appForm, setAppForm] = useState(getDefaultAppForm());
|
||||
|
||||
// Init app form
|
||||
useMount(() => {
|
||||
useMount(async () => {
|
||||
let initialAppForm;
|
||||
|
||||
if (past.length === 0) {
|
||||
const appForm = appWorkflow2AgentForm({
|
||||
initialAppForm = appWorkflow2AgentForm({
|
||||
nodes: appDetail.modules,
|
||||
chatConfig: {
|
||||
...appDetail.chatConfig,
|
||||
|
|
@ -40,16 +47,48 @@ const AgentEdit = () => {
|
|||
}
|
||||
});
|
||||
saveSnapshot({
|
||||
appForm,
|
||||
appForm: initialAppForm,
|
||||
title: t('app:initial_form'),
|
||||
isSaved: true
|
||||
});
|
||||
setAppForm(appForm);
|
||||
} else {
|
||||
setAppForm(past[0].appForm);
|
||||
initialAppForm = past[0].appForm;
|
||||
}
|
||||
|
||||
// Set initial app form
|
||||
setAppForm(initialAppForm);
|
||||
});
|
||||
|
||||
// Load skills list using useRequest2
|
||||
useRequest2(
|
||||
async () => {
|
||||
const result = await getAiSkillList({
|
||||
appId: appDetail._id
|
||||
});
|
||||
|
||||
// Map database data to SkillEditType format
|
||||
const skills: SkillEditType[] = result.map((skill) => ({
|
||||
id: skill._id,
|
||||
name: skill.name,
|
||||
description: '',
|
||||
stepsText: '',
|
||||
dataset: { list: [] },
|
||||
selectedTools: []
|
||||
}));
|
||||
|
||||
// Update appForm with skills
|
||||
setAppForm((state) => ({
|
||||
...state,
|
||||
skills
|
||||
}));
|
||||
|
||||
return skills;
|
||||
},
|
||||
{
|
||||
manual: false
|
||||
}
|
||||
);
|
||||
|
||||
// Save snapshot to local
|
||||
useDebounceEffect(
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ const ToolSelect = ({
|
|||
borderColor={toolError ? 'red.600' : ''}
|
||||
userSelect={'none'}
|
||||
_hover={{
|
||||
...hoverDeleteStyles,
|
||||
borderColor: toolError ? 'red.600' : 'primary.300',
|
||||
'.delete': {
|
||||
display: 'block'
|
||||
|
|
@ -158,18 +157,17 @@ const ToolSelect = ({
|
|||
)}
|
||||
|
||||
{/* Delete icon */}
|
||||
<MyIconButton
|
||||
className="hoverStyle"
|
||||
display={['block', 'none']}
|
||||
ml={0.5}
|
||||
icon="delete"
|
||||
hoverBg="red.50"
|
||||
hoverColor="red.600"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRemoveTool(item.id);
|
||||
}}
|
||||
/>
|
||||
<Box className="hoverStyle" display={['block', 'none']} ml={0.5}>
|
||||
<MyIconButton
|
||||
icon="delete"
|
||||
hoverBg="red.50"
|
||||
hoverColor="red.600"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRemoveTool(item.id);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import {
|
||||
DeleteAiSkillQuery,
|
||||
DeleteAiSkillResponseSchema,
|
||||
type DeleteAiSkillResponse
|
||||
} from '@fastgpt/global/openapi/core/ai/skill/api';
|
||||
import { MongoAiSkill } from '@fastgpt/service/core/ai/skill/schema';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { UserError } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<DeleteAiSkillResponse> {
|
||||
const { id } = DeleteAiSkillQuery.parse(req.query);
|
||||
|
||||
// First, find the skill to get appId
|
||||
const skill = await MongoAiSkill.findById(id, 'appId').lean();
|
||||
if (!skill) {
|
||||
return Promise.reject(new UserError('AI skill not found'));
|
||||
}
|
||||
|
||||
// Auth app with write permission
|
||||
const { teamId } = await authApp({
|
||||
req,
|
||||
appId: String(skill.appId),
|
||||
per: WritePermissionVal,
|
||||
authToken: true
|
||||
});
|
||||
|
||||
// Delete the document
|
||||
const result = await MongoAiSkill.deleteOne({
|
||||
_id: id,
|
||||
appId: skill.appId,
|
||||
teamId
|
||||
});
|
||||
|
||||
if (result.deletedCount === 0) {
|
||||
return Promise.reject(new UserError('AI skill not found or access denied'));
|
||||
}
|
||||
|
||||
return DeleteAiSkillResponseSchema.parse({});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import {
|
||||
GetAiSkillDetailQuery,
|
||||
GetAiSkillDetailResponseSchema,
|
||||
type GetAiSkillDetailQueryType,
|
||||
type GetAiSkillDetailResponse
|
||||
} from '@fastgpt/global/openapi/core/ai/skill/api';
|
||||
import { MongoAiSkill } from '@fastgpt/service/core/ai/skill/schema';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { getChildAppPreviewNode } from '@fastgpt/service/core/app/tool/controller';
|
||||
import { getLocale } from '@fastgpt/service/common/middle/i18n';
|
||||
import type { SelectedToolItemType } from '@fastgpt/global/core/app/formEdit/type';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { UserError } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<{}, GetAiSkillDetailQueryType>,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<GetAiSkillDetailResponse> {
|
||||
const { id } = GetAiSkillDetailQuery.parse(req.query);
|
||||
|
||||
// First, find the skill to get appId
|
||||
const skill = await MongoAiSkill.findById(id).lean();
|
||||
if (!skill) {
|
||||
return Promise.reject(new UserError('AI skill not found'));
|
||||
}
|
||||
|
||||
// Auth app with read permission
|
||||
const { teamId } = await authApp({
|
||||
req,
|
||||
appId: String(skill.appId),
|
||||
per: ReadPermissionVal,
|
||||
authToken: true
|
||||
});
|
||||
|
||||
// Verify team ownership
|
||||
if (String(skill.teamId) !== teamId) {
|
||||
return Promise.reject(new UserError('AI skill not found or access denied'));
|
||||
}
|
||||
|
||||
// Get full tool data using getChildAppPreviewNode
|
||||
const expandedTools: SelectedToolItemType[] = await Promise.all(
|
||||
(skill.tools || []).map(async (tool) => {
|
||||
try {
|
||||
const toolNode = await getChildAppPreviewNode({
|
||||
appId: tool.id,
|
||||
lang: getLocale(req)
|
||||
});
|
||||
return {
|
||||
...toolNode,
|
||||
configStatus: 'active' as const
|
||||
};
|
||||
} catch (error) {
|
||||
// If tool not found or error, mark as invalid
|
||||
return {
|
||||
id: tool.id,
|
||||
templateType: 'personalTool' as const,
|
||||
flowNodeType: FlowNodeTypeEnum.tool,
|
||||
name: 'Invalid Tool',
|
||||
avatar: '',
|
||||
intro: '',
|
||||
showStatus: false,
|
||||
weight: 0,
|
||||
isTool: true,
|
||||
version: 'v1',
|
||||
inputs: [],
|
||||
outputs: [],
|
||||
configStatus: 'invalid' as const
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return GetAiSkillDetailResponseSchema.parse({
|
||||
...skill,
|
||||
tools: expandedTools
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import {
|
||||
ListAiSkillBody,
|
||||
ListAiSkillResponseSchema,
|
||||
type ListAiSkillBodyType,
|
||||
type ListAiSkillResponse
|
||||
} from '@fastgpt/global/openapi/core/ai/skill/api';
|
||||
import { MongoAiSkill } from '@fastgpt/service/core/ai/skill/schema';
|
||||
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<ListAiSkillBodyType>,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<ListAiSkillResponse> {
|
||||
const { appId, searchText } = ListAiSkillBody.parse(req.body);
|
||||
|
||||
// Auth app with read permission
|
||||
const { teamId } = await authApp({ req, appId, per: WritePermissionVal, authToken: true });
|
||||
|
||||
const { offset, pageSize } = parsePaginationRequest(req);
|
||||
|
||||
// Build query
|
||||
const query = {
|
||||
teamId,
|
||||
appId,
|
||||
...(searchText && {
|
||||
$or: [
|
||||
{ name: { $regex: searchText, $options: 'i' } },
|
||||
{ description: { $regex: searchText, $options: 'i' } }
|
||||
]
|
||||
})
|
||||
};
|
||||
|
||||
// Execute query with pagination - only fetch _id and name
|
||||
const list = await MongoAiSkill.find(query, '_id name')
|
||||
.sort({ updateTime: -1 })
|
||||
.skip(offset)
|
||||
.limit(pageSize)
|
||||
.lean();
|
||||
return ListAiSkillResponseSchema.parse(list);
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import {
|
||||
UpdateAiSkillBody,
|
||||
type UpdateAiSkillBodyType,
|
||||
type UpdateAiSkillResponse
|
||||
} from '@fastgpt/global/openapi/core/ai/skill/api';
|
||||
import { MongoAiSkill } from '@fastgpt/service/core/ai/skill/schema';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { UserError } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<UpdateAiSkillBodyType>,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<UpdateAiSkillResponse> {
|
||||
const { id, appId, name, description, steps, tools, datasets } = UpdateAiSkillBody.parse(
|
||||
req.body
|
||||
);
|
||||
|
||||
// Auth app with write permission
|
||||
const { teamId, tmbId } = await authApp({
|
||||
req,
|
||||
appId,
|
||||
per: WritePermissionVal,
|
||||
authToken: true
|
||||
});
|
||||
|
||||
if (id) {
|
||||
const skill = await MongoAiSkill.findOne({ _id: id, teamId, appId });
|
||||
if (!skill) {
|
||||
return Promise.reject(new UserError('AI skill not found'));
|
||||
}
|
||||
|
||||
if (name !== undefined) {
|
||||
skill.name = name;
|
||||
}
|
||||
if (description !== undefined) skill.description = description;
|
||||
if (steps !== undefined) skill.steps = steps;
|
||||
if (tools !== undefined) skill.tools = tools;
|
||||
if (datasets !== undefined) skill.datasets = datasets;
|
||||
skill.updateTime = new Date();
|
||||
|
||||
await skill.save();
|
||||
|
||||
return skill._id;
|
||||
}
|
||||
|
||||
// Create
|
||||
const newSkill = await MongoAiSkill.create({
|
||||
teamId,
|
||||
tmbId,
|
||||
appId,
|
||||
name,
|
||||
description,
|
||||
steps,
|
||||
tools,
|
||||
datasets
|
||||
});
|
||||
|
||||
return newSkill._id;
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
@ -37,6 +37,12 @@ async function handler(req: ApiRequestProps<completionsBody>, res: ApiResponseTy
|
|||
id: chatId,
|
||||
showNodeStatus: true
|
||||
});
|
||||
// console.log('=== HelperBot Completions ===');
|
||||
// console.log('chatId:', chatId);
|
||||
// console.log('chatItemId:', chatItemId);
|
||||
// console.log('query:', query);
|
||||
// console.log('files:', files);
|
||||
// console.log('metadata:', JSON.stringify(metadata, null, 2));
|
||||
|
||||
// 执行不同逻辑
|
||||
const fn = dispatchMap[metadata.type];
|
||||
|
|
|
|||
|
|
@ -276,9 +276,9 @@ export const authHelperBotChatCrud = async ({
|
|||
type: `${HelperBotTypeEnum}`;
|
||||
chatId: string;
|
||||
}) => {
|
||||
const { userId } = await authCert(props);
|
||||
const { userId, teamId, tmbId } = await authCert(props);
|
||||
|
||||
const chat = await MongoHelperBotChat.findOne({ type, userId, chatId }).lean();
|
||||
|
||||
return { chat, userId };
|
||||
return { chat, userId, teamId, tmbId };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -280,6 +280,12 @@ export const streamFetch = ({
|
|||
event,
|
||||
formData: rest
|
||||
});
|
||||
} else if (event === SseResponseEventEnum.generatedSkill) {
|
||||
// Directly call onMessage for generatedSkill, no need to queue
|
||||
onMessage({
|
||||
event,
|
||||
generatedSkill: rest
|
||||
});
|
||||
} else if (event === SseResponseEventEnum.error) {
|
||||
if (rest.statusText === TeamErrEnum.aiPointsNotEnough) {
|
||||
useSystemStore.getState().setNotSufficientModalType(TeamErrEnum.aiPointsNotEnough);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
import { POST, GET, PUT, DELETE } from '@/web/common/api/request';
|
||||
import type {
|
||||
ListAiSkillBodyType,
|
||||
ListAiSkillResponse,
|
||||
GetAiSkillDetailQueryType,
|
||||
UpdateAiSkillBodyType,
|
||||
UpdateAiSkillResponse,
|
||||
DeleteAiSkillQueryType,
|
||||
GetAiSkillDetailResponse
|
||||
} from '@fastgpt/global/openapi/core/ai/skill/api';
|
||||
|
||||
export const getAiSkillList = (data: ListAiSkillBodyType) =>
|
||||
POST<ListAiSkillResponse>('/core/ai/skill/list', data);
|
||||
|
||||
export const getAiSkillDetail = (data: GetAiSkillDetailQueryType) =>
|
||||
GET<GetAiSkillDetailResponse>('/core/ai/skill/detail', data);
|
||||
|
||||
export const updateAiSkill = (data: UpdateAiSkillBodyType) =>
|
||||
PUT<UpdateAiSkillResponse>('/core/ai/skill/update', data);
|
||||
|
||||
export const deleteAiSkill = (data: DeleteAiSkillQueryType) =>
|
||||
DELETE('/core/ai/skill/delete', data);
|
||||
Loading…
Reference in New Issue