mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-28 09:03:53 +00:00
V4.9.6 feature (#4565)
* Dashboard submenu (#4545) * add app submenu (#4452) * add app submenu * fix * width & i18n * optimize submenu code (#4515) * optimize submenu code * fix * fix * fix * fix ts * perf: dashboard sub menu * doc --------- Co-authored-by: heheer <heheer@sealos.io> * feat: value format test * doc * Mcp export (#4555) * feat: mcp server * feat: mcp server * feat: mcp server build * update doc * perf: path selector (#4556) * perf: path selector * fix: docker file path * perf: add image endpoint to dataset search (#4557) * perf: add image endpoint to dataset search * fix: mcp_server url * human in loop (#4558) * Support interactive nodes for loops, and enhance the function of merging nested and loop node history messages. (#4552) * feat: add LoopInteractive definition * feat: Support LoopInteractive type and update related logic * fix: Refactor loop handling logic and improve output value initialization * feat: Add mergeSignId to dispatchLoop and dispatchRunAppNode responses * feat: Enhance mergeChatResponseData to recursively merge plugin details and improve response handling * refactor: Remove redundant comments in mergeChatResponseData for clarity * perf: loop interactive * perf: human in loop --------- Co-authored-by: Theresa <63280168+sd0ric4@users.noreply.github.com> * mcp server ui * integrate mcp (#4549) * integrate mcp * delete unused code * fix ts * bug fix * fix * support whole mcp tools * add try catch * fix * fix * fix ts * fix test * fix ts * fix: interactive in v1 completions * doc * fix: router path * fix mcp integrate (#4563) * fix mcp integrate * fix ui * fix: mcp ux * feat: mcp call title * remove repeat loading * fix mcp tools avatar (#4564) * fix * fix avatar * fix update version * update doc * fix: value format * close server and remove cache * perf: avatar --------- Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: Theresa <63280168+sd0ric4@users.noreply.github.com>
This commit is contained in:
@@ -217,6 +217,8 @@ export type DispatchNodeResponseType = {
|
||||
// tool params
|
||||
toolParamsResult?: Record<string, any>;
|
||||
|
||||
toolRes?: any;
|
||||
|
||||
// abandon
|
||||
extensionModel?: string;
|
||||
extensionResult?: string;
|
||||
|
@@ -10,6 +10,7 @@ import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
||||
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
||||
import { replaceVariable, valToStr } from '../../../common/string/tools';
|
||||
import json5 from 'json5';
|
||||
import {
|
||||
InteractiveNodeResponseType,
|
||||
WorkflowInteractiveResponseType
|
||||
@@ -18,7 +19,10 @@ import {
|
||||
export const extractDeepestInteractive = (
|
||||
interactive: WorkflowInteractiveResponseType
|
||||
): WorkflowInteractiveResponseType => {
|
||||
if (interactive?.type === 'childrenInteractive' && interactive.params?.childrenResponse) {
|
||||
if (
|
||||
(interactive?.type === 'childrenInteractive' || interactive?.type === 'loopInteractive') &&
|
||||
interactive.params?.childrenResponse
|
||||
) {
|
||||
return extractDeepestInteractive(interactive.params.childrenResponse);
|
||||
}
|
||||
return interactive;
|
||||
@@ -40,6 +44,113 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number
|
||||
return limit * 2;
|
||||
};
|
||||
|
||||
/* value type format */
|
||||
export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
|
||||
const isObjectString = (value: any) => {
|
||||
if (typeof value === 'string' && value !== 'false' && value !== 'true') {
|
||||
const trimmedValue = value.trim();
|
||||
const isJsonString =
|
||||
(trimmedValue.startsWith('{') && trimmedValue.endsWith('}')) ||
|
||||
(trimmedValue.startsWith('[') && trimmedValue.endsWith(']'));
|
||||
return isJsonString;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 1. any值,忽略格式化
|
||||
if (value === undefined || value === null) return value;
|
||||
if (!type || type === WorkflowIOValueTypeEnum.any) return value;
|
||||
|
||||
// 2. 如果值已经符合目标类型,直接返回
|
||||
if (
|
||||
(type === WorkflowIOValueTypeEnum.string && typeof value === 'string') ||
|
||||
(type === WorkflowIOValueTypeEnum.number && typeof value === 'number') ||
|
||||
(type === WorkflowIOValueTypeEnum.boolean && typeof value === 'boolean') ||
|
||||
(type.startsWith('array') && Array.isArray(value)) ||
|
||||
(type === WorkflowIOValueTypeEnum.object && typeof value === 'object') ||
|
||||
(type === WorkflowIOValueTypeEnum.chatHistory &&
|
||||
(Array.isArray(value) || typeof value === 'number')) ||
|
||||
(type === WorkflowIOValueTypeEnum.datasetQuote && Array.isArray(value)) ||
|
||||
(type === WorkflowIOValueTypeEnum.selectDataset && Array.isArray(value)) ||
|
||||
(type === WorkflowIOValueTypeEnum.selectApp && typeof value === 'object')
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 4. 按目标类型,进行格式转化
|
||||
// 4.1 基本类型转换
|
||||
if (type === WorkflowIOValueTypeEnum.string) {
|
||||
return typeof value === 'object' ? JSON.stringify(value) : String(value);
|
||||
}
|
||||
if (type === WorkflowIOValueTypeEnum.number) {
|
||||
return Number(value);
|
||||
}
|
||||
if (type === WorkflowIOValueTypeEnum.boolean) {
|
||||
if (typeof value === 'string') {
|
||||
return value.toLowerCase() === 'true';
|
||||
}
|
||||
return Boolean(value);
|
||||
}
|
||||
|
||||
// 4.3 字符串转对象
|
||||
if (
|
||||
(type === WorkflowIOValueTypeEnum.object || type.startsWith('array')) &&
|
||||
typeof value === 'string' &&
|
||||
value.trim()
|
||||
) {
|
||||
const trimmedValue = value.trim();
|
||||
const isJsonString = isObjectString(trimmedValue);
|
||||
|
||||
if (isJsonString) {
|
||||
try {
|
||||
const parsed = json5.parse(trimmedValue);
|
||||
// 检测解析结果与目标类型是否一致
|
||||
if (type.startsWith('array') && Array.isArray(parsed)) return parsed;
|
||||
if (type === WorkflowIOValueTypeEnum.object && typeof parsed === 'object') return parsed;
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
// 4.4 数组类型(这里 value 不是数组类型)(TODO: 嵌套数据类型转化)
|
||||
if (type.startsWith('array')) {
|
||||
return [value];
|
||||
}
|
||||
|
||||
// 4.5 特殊类型处理
|
||||
if (
|
||||
[WorkflowIOValueTypeEnum.datasetQuote, WorkflowIOValueTypeEnum.selectDataset].includes(type)
|
||||
) {
|
||||
if (isObjectString(value)) {
|
||||
try {
|
||||
return json5.parse(value);
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
if (
|
||||
[WorkflowIOValueTypeEnum.selectApp, WorkflowIOValueTypeEnum.object].includes(type) &&
|
||||
typeof value === 'string'
|
||||
) {
|
||||
if (isObjectString(value)) {
|
||||
try {
|
||||
return json5.parse(value);
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
// Invalid history type
|
||||
if (type === WorkflowIOValueTypeEnum.chatHistory) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 5. 默认返回原值
|
||||
return value;
|
||||
};
|
||||
|
||||
/*
|
||||
Get interaction information (if any) from the last AI message.
|
||||
What can be done:
|
||||
@@ -62,7 +173,10 @@ export const getLastInteractiveValue = (
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastValue.interactive.type === 'childrenInteractive') {
|
||||
if (
|
||||
lastValue.interactive.type === 'childrenInteractive' ||
|
||||
lastValue.interactive.type === 'loopInteractive'
|
||||
) {
|
||||
return lastValue.interactive;
|
||||
}
|
||||
|
||||
@@ -83,7 +197,7 @@ export const getLastInteractiveValue = (
|
||||
return;
|
||||
};
|
||||
|
||||
export const initWorkflowEdgeStatus = (
|
||||
export const storeEdges2RuntimeEdges = (
|
||||
edges: StoreEdgeItemType[],
|
||||
lastInteractive?: WorkflowInteractiveResponseType
|
||||
): RuntimeEdgeItemType[] => {
|
||||
@@ -114,7 +228,12 @@ export const getWorkflowEntryNodeIds = (
|
||||
FlowNodeTypeEnum.pluginInput
|
||||
];
|
||||
return nodes
|
||||
.filter((node) => entryList.includes(node.flowNodeType as any))
|
||||
.filter(
|
||||
(node) =>
|
||||
entryList.includes(node.flowNodeType as any) ||
|
||||
(!nodes.some((item) => entryList.includes(item.flowNodeType as any)) &&
|
||||
node.flowNodeType === FlowNodeTypeEnum.tool)
|
||||
)
|
||||
.map((item) => item.nodeId);
|
||||
};
|
||||
|
||||
@@ -312,7 +431,6 @@ export const formatVariableValByType = (val: any, valueType?: WorkflowIOValueTyp
|
||||
if (
|
||||
[
|
||||
WorkflowIOValueTypeEnum.object,
|
||||
WorkflowIOValueTypeEnum.chatHistory,
|
||||
WorkflowIOValueTypeEnum.datasetQuote,
|
||||
WorkflowIOValueTypeEnum.selectApp,
|
||||
WorkflowIOValueTypeEnum.selectDataset
|
||||
|
Reference in New Issue
Block a user