mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-22 12:20:34 +00:00
Check debug (#4384)
* feat : Added support for interactive nodes in the debugging interface (#4339)
* feat: add VSCode launch configuration and enhance debug API handler
* feat: refactor debug API handler to streamline workflow processing and enhance interactive chat features
* feat: enhance debug API handler with structured input forms and improved query handling
* feat: enhance debug API handler to support optional query and histories parameters
* feat: simplify query and histories initialization in debug API handler
* feat: add realmode parameter to workflow dispatch and update interactive handling
* feat: add optional query parameter to PostWorkflowDebugProps and remove realmode from ModuleDispatchProps
* feat: add history parameter to PostWorkflowDebugProps and update related components
* feat: remove realmode
* feat: simplify handler parameter destructuring in debug.ts
* feat: remove unused interactive prop from WholeResponseContent component
* feat: refactor onNextNodeDebug to use parameter object for better readability
* feat: Merge selections and next actions to remove unused state management
* feat: 添加 NodeDebugResponse 组件以增强调试功能
* feat: Simplify the import statements in InteractiveComponents.tsx
* feat: Update the handler function to use default parameters to simplify the code
* feat: Add optional workflowInteractiveResponse field to PostWorkflowDebugResponse type
* feat: Add the workflowInteractiveResponse field in the debugging handler to enhance response capabilities
* feat: Added workflowInteractiveResponse field in FlowNodeItemType to enhance responsiveness
* feat: Refactor NodeDebugResponse to utilize workflowInteractiveResponse for improved interactivity
* feat: Extend UserSelectInteractive and UserInputInteractive types to inherit from InteractiveBasicType
* feat: Refactor NodeDebugResponse to streamline interactive handling and improve code clarity
* feat: 重构交互式调试逻辑,创建共用 Hook 以简化用户选择和输入处理
* fix: type error
* feat: 重构 AIResponseBox 组件,简化用户交互逻辑并引入共用表单组件
* feat: 清理 AIResponseBox 和表单组件代码,移除冗余注释和未使用的导入
* fix: type error
* feat: 重构 AIResponseBox 组件,简化类型定义并优化代码结构
* refactor: 将 FormItem 接口更改为类型定义,优化代码结构
* refactor: 将 NodeDebugResponseProps 接口更改为类型定义,优化代码结构
* refactor: 移除不必要的入口节点检查,简化调试处理逻辑
* feat: 移动调试交互组件位置
* refactor: 将 InteractiveBasicType 中的属性设为可选,简化数据结构
* refactor: 优化类型定义
* refactor: 移除未使用的 ChatItemType 和 UserChatItemValueItemType 导入
* refactor: 将接口定义更改为类型别名,简化代码结构
* refactor: 更新类型定义,使用类型别名简化代码结构
* refactor: 使用类型导入简化代码结构,重构 AIResponseBox 组件
* refactor: 提取描述框和表单项标签组件,简化代码结构
* refactor: 移除多余的空行
* refactor: 移除多余的空行和注释
* refactor: 移除多余的空行,简化 AIResponseBox 组件代码
* refactor: 重构组件,移动 FormComponents 到 InteractiveComponents,简化代码结构
* refactor: 移除多余的空行,简化 NodeDebugResponse 组件代码
* refactor: 更新导入语句,使用 type 关键字优化类型导入
* refactor: 在 tsconfig.json 中启用 verbatimModuleSyntax 选项
* Revert "refactor: 在 tsconfig.json 中启用 verbatimModuleSyntax 选项"
This reverts commit 2b335a9938
.
* revert: rendertool
* refactor: Remove unused imports and functions to simplify code
* perf: debug interactive
---------
Co-authored-by: Theresa <63280168+sd0ric4@users.noreply.github.com>
This commit is contained in:
39
.vscode/launch.json
vendored
Normal file
39
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug server-side",
|
||||||
|
"type": "node-terminal",
|
||||||
|
"request": "launch",
|
||||||
|
"command": "pnpm run dev",
|
||||||
|
"cwd": "${workspaceFolder}/projects/app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug client-side",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:3000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug client-side (Edge)",
|
||||||
|
"type": "msedge",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:3000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug full stack",
|
||||||
|
"type": "node-terminal",
|
||||||
|
"request": "launch",
|
||||||
|
"command": "pnpm run dev",
|
||||||
|
"cwd": "${workspaceFolder}/projects/app",
|
||||||
|
"skipFiles": ["<node_internals>/**"],
|
||||||
|
"serverReadyAction": {
|
||||||
|
"action": "debugWithEdge",
|
||||||
|
"killOnServerStop": true,
|
||||||
|
"pattern": "- Local:.+(https?://.+)",
|
||||||
|
"uriFormat": "%s",
|
||||||
|
"webRoot": "${workspaceFolder}/projects/app"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -10,7 +10,6 @@ import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
|||||||
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
||||||
import { replaceVariable, valToStr } from '../../../common/string/tools';
|
import { replaceVariable, valToStr } from '../../../common/string/tools';
|
||||||
import { ChatCompletionChunk } from 'openai/resources';
|
|
||||||
|
|
||||||
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
|
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
|
||||||
let limit = 10;
|
let limit = 10;
|
||||||
|
@@ -5,10 +5,36 @@ import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant';
|
|||||||
import { WorkflowIOValueTypeEnum } from 'core/workflow/constants';
|
import { WorkflowIOValueTypeEnum } from 'core/workflow/constants';
|
||||||
import type { ChatCompletionMessageParam } from '../../../../ai/type';
|
import type { ChatCompletionMessageParam } from '../../../../ai/type';
|
||||||
|
|
||||||
|
type InteractiveBasicType = {
|
||||||
|
entryNodeIds: string[];
|
||||||
|
memoryEdges: RuntimeEdgeItemType[];
|
||||||
|
nodeOutputs: NodeOutputItemType[];
|
||||||
|
|
||||||
|
toolParams?: {
|
||||||
|
entryNodeIds: string[]; // 记录工具中,交互节点的 Id,而不是起始工作流的入口
|
||||||
|
memoryMessages: ChatCompletionMessageParam[]; // 这轮工具中,产生的新的 messages
|
||||||
|
toolCallId: string; // 记录对应 tool 的id,用于后续交互节点可以替换掉 tool 的 response
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type InteractiveNodeType = {
|
||||||
|
entryNodeIds?: string[];
|
||||||
|
memoryEdges?: RuntimeEdgeItemType[];
|
||||||
|
nodeOutputs?: NodeOutputItemType[];
|
||||||
|
};
|
||||||
|
|
||||||
export type UserSelectOptionItemType = {
|
export type UserSelectOptionItemType = {
|
||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
|
type UserSelectInteractive = InteractiveNodeType & {
|
||||||
|
type: 'userSelect';
|
||||||
|
params: {
|
||||||
|
description: string;
|
||||||
|
userSelectOptions: UserSelectOptionItemType[];
|
||||||
|
userSelectedVal?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type UserInputFormItemType = {
|
export type UserInputFormItemType = {
|
||||||
type: FlowNodeInputTypeEnum;
|
type: FlowNodeInputTypeEnum;
|
||||||
@@ -28,29 +54,7 @@ export type UserInputFormItemType = {
|
|||||||
// select
|
// select
|
||||||
list?: { label: string; value: string }[];
|
list?: { label: string; value: string }[];
|
||||||
};
|
};
|
||||||
|
type UserInputInteractive = InteractiveNodeType & {
|
||||||
type InteractiveBasicType = {
|
|
||||||
entryNodeIds: string[];
|
|
||||||
memoryEdges: RuntimeEdgeItemType[];
|
|
||||||
nodeOutputs: NodeOutputItemType[];
|
|
||||||
|
|
||||||
toolParams?: {
|
|
||||||
entryNodeIds: string[]; // 记录工具中,交互节点的 Id,而不是起始工作流的入口
|
|
||||||
memoryMessages: ChatCompletionMessageParam[]; // 这轮工具中,产生的新的 messages
|
|
||||||
toolCallId: string; // 记录对应 tool 的id,用于后续交互节点可以替换掉 tool 的 response
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type UserSelectInteractive = {
|
|
||||||
type: 'userSelect';
|
|
||||||
params: {
|
|
||||||
description: string;
|
|
||||||
userSelectOptions: UserSelectOptionItemType[];
|
|
||||||
userSelectedVal?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type UserInputInteractive = {
|
|
||||||
type: 'userInput';
|
type: 'userInput';
|
||||||
params: {
|
params: {
|
||||||
description: string;
|
description: string;
|
||||||
@@ -58,6 +62,5 @@ type UserInputInteractive = {
|
|||||||
submitted?: boolean;
|
submitted?: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InteractiveNodeResponseType = UserSelectInteractive | UserInputInteractive;
|
export type InteractiveNodeResponseType = UserSelectInteractive | UserInputInteractive;
|
||||||
export type WorkflowInteractiveResponseType = InteractiveBasicType & InteractiveNodeResponseType;
|
export type WorkflowInteractiveResponseType = InteractiveBasicType & InteractiveNodeResponseType;
|
||||||
|
2
packages/global/core/workflow/type/node.d.ts
vendored
2
packages/global/core/workflow/type/node.d.ts
vendored
@@ -23,6 +23,7 @@ import { NextApiResponse } from 'next';
|
|||||||
import { AppDetailType, AppSchema } from '../../app/type';
|
import { AppDetailType, AppSchema } from '../../app/type';
|
||||||
import { ParentIdType } from 'common/parentFolder/type';
|
import { ParentIdType } from 'common/parentFolder/type';
|
||||||
import { AppTypeEnum } from 'core/app/constants';
|
import { AppTypeEnum } from 'core/app/constants';
|
||||||
|
import { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
|
||||||
|
|
||||||
export type FlowNodeCommonType = {
|
export type FlowNodeCommonType = {
|
||||||
parentNodeId?: string;
|
parentNodeId?: string;
|
||||||
@@ -120,6 +121,7 @@ export type FlowNodeItemType = FlowNodeTemplateType & {
|
|||||||
showResult?: boolean; // show and hide result modal
|
showResult?: boolean; // show and hide result modal
|
||||||
response?: ChatHistoryItemResType;
|
response?: ChatHistoryItemResType;
|
||||||
isExpired?: boolean;
|
isExpired?: boolean;
|
||||||
|
workflowInteractiveResponse?: WorkflowInteractiveResponseType;
|
||||||
};
|
};
|
||||||
isFolded?: boolean;
|
isFolded?: boolean;
|
||||||
};
|
};
|
||||||
|
@@ -44,14 +44,14 @@ import {
|
|||||||
textAdaptGptResponse,
|
textAdaptGptResponse,
|
||||||
replaceEditorVariable
|
replaceEditorVariable
|
||||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
import type { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||||
import { dispatchRunTools } from './agent/runTool/index';
|
import { dispatchRunTools } from './agent/runTool/index';
|
||||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { DispatchFlowResponse } from './type';
|
import type { DispatchFlowResponse } from './type';
|
||||||
import { dispatchStopToolCall } from './agent/runTool/stopTool';
|
import { dispatchStopToolCall } from './agent/runTool/stopTool';
|
||||||
import { dispatchLafRequest } from './tools/runLaf';
|
import { dispatchLafRequest } from './tools/runLaf';
|
||||||
import { dispatchIfElse } from './tools/runIfElse';
|
import { dispatchIfElse } from './tools/runIfElse';
|
||||||
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
import type { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { dispatchSystemConfig } from './init/systemConfig';
|
import { dispatchSystemConfig } from './init/systemConfig';
|
||||||
import { dispatchUpdateVariable } from './tools/runUpdateVar';
|
import { dispatchUpdateVariable } from './tools/runUpdateVar';
|
||||||
@@ -62,7 +62,7 @@ import { dispatchTextEditor } from './tools/textEditor';
|
|||||||
import { dispatchCustomFeedback } from './tools/customFeedback';
|
import { dispatchCustomFeedback } from './tools/customFeedback';
|
||||||
import { dispatchReadFiles } from './tools/readFiles';
|
import { dispatchReadFiles } from './tools/readFiles';
|
||||||
import { dispatchUserSelect } from './interactive/userSelect';
|
import { dispatchUserSelect } from './interactive/userSelect';
|
||||||
import {
|
import type {
|
||||||
WorkflowInteractiveResponseType,
|
WorkflowInteractiveResponseType,
|
||||||
InteractiveNodeResponseType
|
InteractiveNodeResponseType
|
||||||
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
@@ -451,6 +451,11 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
const interactiveResponse = nodeRunResult.result?.[DispatchNodeResponseKeyEnum.interactive];
|
const interactiveResponse = nodeRunResult.result?.[DispatchNodeResponseKeyEnum.interactive];
|
||||||
if (interactiveResponse) {
|
if (interactiveResponse) {
|
||||||
pushStore(nodeRunResult.node, nodeRunResult.result);
|
pushStore(nodeRunResult.node, nodeRunResult.result);
|
||||||
|
|
||||||
|
if (props.mode === 'debug') {
|
||||||
|
debugNextStepRunNodes = debugNextStepRunNodes.concat([nodeRunResult.node]);
|
||||||
|
}
|
||||||
|
|
||||||
nodeInteractiveResponse = {
|
nodeInteractiveResponse = {
|
||||||
entryNodeIds: [nodeRunResult.node.nodeId],
|
entryNodeIds: [nodeRunResult.node.nodeId],
|
||||||
interactiveResponse
|
interactiveResponse
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import {
|
import type {
|
||||||
DispatchNodeResultType,
|
DispatchNodeResultType,
|
||||||
ModuleDispatchProps
|
ModuleDispatchProps
|
||||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import {
|
import type {
|
||||||
UserInputFormItemType,
|
UserInputFormItemType,
|
||||||
UserInputInteractive
|
UserInputInteractive
|
||||||
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
@@ -32,7 +32,6 @@ export const dispatchFormInput = async (props: Props): Promise<FormInputResponse
|
|||||||
query
|
query
|
||||||
} = props;
|
} = props;
|
||||||
const { isEntry } = node;
|
const { isEntry } = node;
|
||||||
|
|
||||||
const interactive = getLastInteractiveValue(histories);
|
const interactive = getLastInteractiveValue(histories);
|
||||||
|
|
||||||
// Interactive node is not the entry node, return interactive result
|
// Interactive node is not the entry node, return interactive result
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import {
|
import type {
|
||||||
DispatchNodeResultType,
|
DispatchNodeResultType,
|
||||||
ModuleDispatchProps
|
ModuleDispatchProps
|
||||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
@@ -30,7 +30,6 @@ export const dispatchUserSelect = async (props: Props): Promise<UserSelectRespon
|
|||||||
query
|
query
|
||||||
} = props;
|
} = props;
|
||||||
const { nodeId, isEntry } = node;
|
const { nodeId, isEntry } = node;
|
||||||
|
|
||||||
const interactive = getLastInteractiveValue(histories);
|
const interactive = getLastInteractiveValue(histories);
|
||||||
|
|
||||||
// Interactive node is not the entry node, return interactive result
|
// Interactive node is not the entry node, return interactive result
|
||||||
|
@@ -106,7 +106,7 @@ export const getHistories = (history?: ChatItemType[] | number, histories: ChatI
|
|||||||
/* value type format */
|
/* value type format */
|
||||||
export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
|
export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
|
||||||
if (value === undefined) return;
|
if (value === undefined) return;
|
||||||
if (!type) return value;
|
if (!type || type === WorkflowIOValueTypeEnum.any) return value;
|
||||||
|
|
||||||
if (type === 'string') {
|
if (type === 'string') {
|
||||||
if (typeof value !== 'object') return String(value);
|
if (typeof value !== 'object') return String(value);
|
||||||
@@ -118,7 +118,7 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
|
|||||||
return Boolean(value);
|
return Boolean(value);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (WorkflowIOValueTypeEnum.arrayString && typeof value === 'string') {
|
if (type === WorkflowIOValueTypeEnum.arrayString && typeof value === 'string') {
|
||||||
return [value];
|
return [value];
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
@@ -8,43 +8,80 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Flex,
|
Flex,
|
||||||
HStack,
|
HStack
|
||||||
Textarea
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import {
|
import type {
|
||||||
AIChatItemValueItemType,
|
AIChatItemValueItemType,
|
||||||
ToolModuleResponseItemType,
|
ToolModuleResponseItemType,
|
||||||
UserChatItemValueItemType
|
UserChatItemValueItemType
|
||||||
} from '@fastgpt/global/core/chat/type';
|
} from '@fastgpt/global/core/chat/type';
|
||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import {
|
import type {
|
||||||
InteractiveBasicType,
|
InteractiveBasicType,
|
||||||
UserInputInteractive,
|
UserInputInteractive,
|
||||||
UserSelectInteractive
|
UserSelectInteractive
|
||||||
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
|
||||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
|
||||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
|
||||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
|
||||||
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
|
||||||
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
|
|
||||||
import { SendPromptFnType } from '../ChatContainer/ChatBox/type';
|
|
||||||
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
||||||
|
import { SelectOptionsComponent, FormInputComponent } from './Interactive/InteractiveComponents';
|
||||||
|
|
||||||
type props = {
|
const accordionButtonStyle = {
|
||||||
value: UserChatItemValueItemType | AIChatItemValueItemType;
|
w: 'auto',
|
||||||
isLastResponseValue: boolean;
|
bg: 'white',
|
||||||
isChatting: boolean;
|
borderRadius: 'md',
|
||||||
|
borderWidth: '1px',
|
||||||
|
borderColor: 'myGray.200',
|
||||||
|
boxShadow: '1',
|
||||||
|
pl: 3,
|
||||||
|
pr: 2.5,
|
||||||
|
_hover: {
|
||||||
|
bg: 'auto'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSendPrompt: SendPromptFnType = (e) => eventBus.emit(EventNameEnum.sendQuestion, e);
|
const RenderResoningContent = React.memo(function RenderResoningContent({
|
||||||
|
content,
|
||||||
|
isChatting,
|
||||||
|
isLastResponseValue
|
||||||
|
}: {
|
||||||
|
content: string;
|
||||||
|
isChatting: boolean;
|
||||||
|
isLastResponseValue: boolean;
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const showAnimation = isChatting && isLastResponseValue;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Accordion allowToggle defaultIndex={isLastResponseValue ? 0 : undefined}>
|
||||||
|
<AccordionItem borderTop={'none'} borderBottom={'none'}>
|
||||||
|
<AccordionButton {...accordionButtonStyle} py={1}>
|
||||||
|
<HStack mr={2} spacing={1}>
|
||||||
|
<MyIcon name={'core/chat/think'} w={'0.85rem'} />
|
||||||
|
<Box fontSize={'sm'}>{t('chat:ai_reasoning')}</Box>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
{showAnimation && <MyIcon name={'common/loading'} w={'0.85rem'} />}
|
||||||
|
<AccordionIcon color={'myGray.600'} ml={5} />
|
||||||
|
</AccordionButton>
|
||||||
|
<AccordionPanel
|
||||||
|
py={0}
|
||||||
|
pr={0}
|
||||||
|
pl={3}
|
||||||
|
mt={2}
|
||||||
|
borderLeft={'2px solid'}
|
||||||
|
borderColor={'myGray.300'}
|
||||||
|
color={'myGray.500'}
|
||||||
|
>
|
||||||
|
<Markdown source={content} showAnimation={showAnimation} />
|
||||||
|
</AccordionPanel>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
);
|
||||||
|
});
|
||||||
const RenderText = React.memo(function RenderText({
|
const RenderText = React.memo(function RenderText({
|
||||||
showAnimation,
|
showAnimation,
|
||||||
text
|
text
|
||||||
@@ -58,6 +95,7 @@ const RenderText = React.memo(function RenderText({
|
|||||||
|
|
||||||
return <Markdown source={source} showAnimation={showAnimation} />;
|
return <Markdown source={source} showAnimation={showAnimation} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
const RenderTool = React.memo(
|
const RenderTool = React.memo(
|
||||||
function RenderTool({
|
function RenderTool({
|
||||||
showAnimation,
|
showAnimation,
|
||||||
@@ -69,37 +107,20 @@ const RenderTool = React.memo(
|
|||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{tools.map((tool) => {
|
{tools.map((tool) => {
|
||||||
const toolParams = (() => {
|
const formatJson = (string: string) => {
|
||||||
try {
|
try {
|
||||||
return JSON.stringify(JSON.parse(tool.params), null, 2);
|
return JSON.stringify(JSON.parse(string), null, 2);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return tool.params;
|
return string;
|
||||||
}
|
}
|
||||||
})();
|
};
|
||||||
const toolResponse = (() => {
|
const toolParams = formatJson(tool.params);
|
||||||
try {
|
const toolResponse = formatJson(tool.response);
|
||||||
return JSON.stringify(JSON.parse(tool.response), null, 2);
|
|
||||||
} catch (error) {
|
|
||||||
return tool.response;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Accordion key={tool.id} allowToggle _notLast={{ mb: 2 }}>
|
<Accordion key={tool.id} allowToggle _notLast={{ mb: 2 }}>
|
||||||
<AccordionItem borderTop={'none'} borderBottom={'none'}>
|
<AccordionItem borderTop={'none'} borderBottom={'none'}>
|
||||||
<AccordionButton
|
<AccordionButton {...accordionButtonStyle}>
|
||||||
w={'auto'}
|
|
||||||
bg={'white'}
|
|
||||||
borderRadius={'md'}
|
|
||||||
borderWidth={'1px'}
|
|
||||||
borderColor={'myGray.200'}
|
|
||||||
boxShadow={'1'}
|
|
||||||
pl={3}
|
|
||||||
pr={2.5}
|
|
||||||
_hover={{
|
|
||||||
bg: 'auto'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Avatar src={tool.toolAvatar} w={'1.25rem'} h={'1.25rem'} borderRadius={'sm'} />
|
<Avatar src={tool.toolAvatar} w={'1.25rem'} h={'1.25rem'} borderRadius={'sm'} />
|
||||||
<Box mx={2} fontSize={'sm'} color={'myGray.900'}>
|
<Box mx={2} fontSize={'sm'} color={'myGray.900'}>
|
||||||
{tool.toolName}
|
{tool.toolName}
|
||||||
@@ -140,99 +161,24 @@ ${toolResponse}`}
|
|||||||
},
|
},
|
||||||
(prevProps, nextProps) => isEqual(prevProps, nextProps)
|
(prevProps, nextProps) => isEqual(prevProps, nextProps)
|
||||||
);
|
);
|
||||||
const RenderResoningContent = React.memo(function RenderResoningContent({
|
|
||||||
content,
|
|
||||||
isChatting,
|
|
||||||
isLastResponseValue
|
|
||||||
}: {
|
|
||||||
content: string;
|
|
||||||
isChatting: boolean;
|
|
||||||
isLastResponseValue: boolean;
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const showAnimation = isChatting && isLastResponseValue;
|
|
||||||
|
|
||||||
return (
|
const onSendPrompt = (e: { text: string; isInteractivePrompt: boolean }) =>
|
||||||
<Accordion allowToggle defaultIndex={isLastResponseValue ? 0 : undefined}>
|
eventBus.emit(EventNameEnum.sendQuestion, e);
|
||||||
<AccordionItem borderTop={'none'} borderBottom={'none'}>
|
|
||||||
<AccordionButton
|
|
||||||
w={'auto'}
|
|
||||||
bg={'white'}
|
|
||||||
borderRadius={'md'}
|
|
||||||
borderWidth={'1px'}
|
|
||||||
borderColor={'myGray.200'}
|
|
||||||
boxShadow={'1'}
|
|
||||||
pl={3}
|
|
||||||
pr={2.5}
|
|
||||||
py={1}
|
|
||||||
_hover={{
|
|
||||||
bg: 'auto'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<HStack mr={2} spacing={1}>
|
|
||||||
<MyIcon name={'core/chat/think'} w={'0.85rem'} />
|
|
||||||
<Box fontSize={'sm'}>{t('chat:ai_reasoning')}</Box>
|
|
||||||
</HStack>
|
|
||||||
|
|
||||||
{showAnimation && <MyIcon name={'common/loading'} w={'0.85rem'} />}
|
|
||||||
<AccordionIcon color={'myGray.600'} ml={5} />
|
|
||||||
</AccordionButton>
|
|
||||||
<AccordionPanel
|
|
||||||
py={0}
|
|
||||||
pr={0}
|
|
||||||
pl={3}
|
|
||||||
mt={2}
|
|
||||||
borderLeft={'2px solid'}
|
|
||||||
borderColor={'myGray.300'}
|
|
||||||
color={'myGray.500'}
|
|
||||||
>
|
|
||||||
<Markdown source={content} showAnimation={showAnimation} />
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const RenderUserSelectInteractive = React.memo(function RenderInteractive({
|
const RenderUserSelectInteractive = React.memo(function RenderInteractive({
|
||||||
interactive
|
interactive
|
||||||
}: {
|
}: {
|
||||||
interactive: InteractiveBasicType & UserSelectInteractive;
|
interactive: InteractiveBasicType & UserSelectInteractive;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<SelectOptionsComponent
|
||||||
{interactive?.params?.description && <Markdown source={interactive.params.description} />}
|
interactiveParams={interactive.params}
|
||||||
<Flex flexDirection={'column'} gap={2} w={'250px'}>
|
onSelect={(value) => {
|
||||||
{interactive.params.userSelectOptions?.map((option) => {
|
onSendPrompt({
|
||||||
const selected = option.value === interactive?.params?.userSelectedVal;
|
text: value,
|
||||||
|
isInteractivePrompt: true
|
||||||
return (
|
});
|
||||||
<Button
|
}}
|
||||||
key={option.key}
|
/>
|
||||||
variant={'whitePrimary'}
|
|
||||||
whiteSpace={'pre-wrap'}
|
|
||||||
isDisabled={interactive?.params?.userSelectedVal !== undefined}
|
|
||||||
{...(selected
|
|
||||||
? {
|
|
||||||
_disabled: {
|
|
||||||
cursor: 'default',
|
|
||||||
borderColor: 'primary.300',
|
|
||||||
bg: 'primary.50 !important',
|
|
||||||
color: 'primary.600'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: {})}
|
|
||||||
onClick={() => {
|
|
||||||
onSendPrompt({
|
|
||||||
text: option.value,
|
|
||||||
isInteractivePrompt: true
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{option.value}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
||||||
@@ -241,110 +187,52 @@ const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
|||||||
interactive: InteractiveBasicType & UserInputInteractive;
|
interactive: InteractiveBasicType & UserInputInteractive;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { register, setValue, handleSubmit: handleSubmitChat, control, reset } = useForm();
|
|
||||||
|
|
||||||
const onSubmit = useCallback((data: any) => {
|
const defaultValues = useMemo(() => {
|
||||||
|
if (interactive.type === 'userInput') {
|
||||||
|
return interactive.params.inputForm?.reduce((acc: Record<string, any>, item) => {
|
||||||
|
acc[item.label] = !!item.value ? item.value : item.defaultValue;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}, [interactive]);
|
||||||
|
|
||||||
|
const handleFormSubmit = useCallback((data: Record<string, any>) => {
|
||||||
onSendPrompt({
|
onSendPrompt({
|
||||||
text: JSON.stringify(data),
|
text: JSON.stringify(data),
|
||||||
isInteractivePrompt: true
|
isInteractivePrompt: true
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (interactive.type === 'userInput') {
|
|
||||||
const defaultValues = interactive.params.inputForm?.reduce(
|
|
||||||
(acc: Record<string, any>, item) => {
|
|
||||||
acc[item.label] = !!item.value ? item.value : item.defaultValue;
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
reset(defaultValues);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection={'column'} gap={2} w={'250px'}>
|
<Flex flexDirection={'column'} gap={2} w={'250px'}>
|
||||||
{interactive.params.description && <Markdown source={interactive.params.description} />}
|
<FormInputComponent
|
||||||
{interactive.params.inputForm?.map((input) => (
|
interactiveParams={interactive.params}
|
||||||
<Box key={input.label}>
|
defaultValues={defaultValues}
|
||||||
<FormLabel mb={1} required={input.required} whiteSpace={'pre-wrap'}>
|
SubmitButton={({ onSubmit }) => (
|
||||||
{input.label}
|
<Button onClick={() => onSubmit(handleFormSubmit)()}>{t('common:Submit')}</Button>
|
||||||
{input.description && <QuestionTip ml={1} label={input.description} />}
|
)}
|
||||||
</FormLabel>
|
/>
|
||||||
{input.type === FlowNodeInputTypeEnum.input && (
|
|
||||||
<MyTextarea
|
|
||||||
isDisabled={interactive.params.submitted}
|
|
||||||
{...register(input.label, {
|
|
||||||
required: input.required
|
|
||||||
})}
|
|
||||||
bg={'white'}
|
|
||||||
autoHeight
|
|
||||||
minH={40}
|
|
||||||
maxH={100}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{input.type === FlowNodeInputTypeEnum.textarea && (
|
|
||||||
<Textarea
|
|
||||||
isDisabled={interactive.params.submitted}
|
|
||||||
bg={'white'}
|
|
||||||
{...register(input.label, {
|
|
||||||
required: input.required
|
|
||||||
})}
|
|
||||||
rows={5}
|
|
||||||
maxLength={input.maxLength || 4000}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{input.type === FlowNodeInputTypeEnum.numberInput && (
|
|
||||||
<MyNumberInput
|
|
||||||
min={input.min}
|
|
||||||
max={input.max}
|
|
||||||
defaultValue={input.defaultValue}
|
|
||||||
isDisabled={interactive.params.submitted}
|
|
||||||
bg={'white'}
|
|
||||||
register={register}
|
|
||||||
name={input.label}
|
|
||||||
isRequired={input.required}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{input.type === FlowNodeInputTypeEnum.select && (
|
|
||||||
<Controller
|
|
||||||
key={input.label}
|
|
||||||
control={control}
|
|
||||||
name={input.label}
|
|
||||||
rules={{ required: input.required }}
|
|
||||||
render={({ field: { ref, value } }) => {
|
|
||||||
if (!input.list) return <></>;
|
|
||||||
return (
|
|
||||||
<MySelect
|
|
||||||
ref={ref}
|
|
||||||
width={'100%'}
|
|
||||||
list={input.list}
|
|
||||||
value={value}
|
|
||||||
isDisabled={interactive.params.submitted}
|
|
||||||
onChange={(e) => setValue(input.label, e)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
{!interactive.params.submitted && (
|
|
||||||
<Flex w={'full'} justifyContent={'end'}>
|
|
||||||
<Button onClick={handleSubmitChat(onSubmit)}>{t('common:Submit')}</Button>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const AIResponseBox = ({ value, isLastResponseValue, isChatting }: props) => {
|
const AIResponseBox = ({
|
||||||
if (value.type === ChatItemValueTypeEnum.text && value.text)
|
value,
|
||||||
|
isLastResponseValue,
|
||||||
|
isChatting
|
||||||
|
}: {
|
||||||
|
value: UserChatItemValueItemType | AIChatItemValueItemType;
|
||||||
|
isLastResponseValue: boolean;
|
||||||
|
isChatting: boolean;
|
||||||
|
}) => {
|
||||||
|
if (value.type === ChatItemValueTypeEnum.text && value.text) {
|
||||||
return (
|
return (
|
||||||
<RenderText showAnimation={isChatting && isLastResponseValue} text={value.text.content} />
|
<RenderText showAnimation={isChatting && isLastResponseValue} text={value.text.content} />
|
||||||
);
|
);
|
||||||
if (value.type === ChatItemValueTypeEnum.reasoning && value.reasoning)
|
}
|
||||||
|
if (value.type === ChatItemValueTypeEnum.reasoning && value.reasoning) {
|
||||||
return (
|
return (
|
||||||
<RenderResoningContent
|
<RenderResoningContent
|
||||||
isChatting={isChatting}
|
isChatting={isChatting}
|
||||||
@@ -352,14 +240,18 @@ const AIResponseBox = ({ value, isLastResponseValue, isChatting }: props) => {
|
|||||||
content={value.reasoning.content}
|
content={value.reasoning.content}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
if (value.type === ChatItemValueTypeEnum.tool && value.tools)
|
|
||||||
return <RenderTool showAnimation={isChatting} tools={value.tools} />;
|
|
||||||
if (value.type === ChatItemValueTypeEnum.interactive && value.interactive) {
|
|
||||||
if (value.interactive.type === 'userSelect')
|
|
||||||
return <RenderUserSelectInteractive interactive={value.interactive} />;
|
|
||||||
if (value.interactive?.type === 'userInput')
|
|
||||||
return <RenderUserFormInteractive interactive={value.interactive} />;
|
|
||||||
}
|
}
|
||||||
|
if (value.type === ChatItemValueTypeEnum.tool && value.tools) {
|
||||||
|
return <RenderTool showAnimation={isChatting} tools={value.tools} />;
|
||||||
|
}
|
||||||
|
if (value.type === ChatItemValueTypeEnum.interactive && value.interactive) {
|
||||||
|
if (value.interactive.type === 'userSelect') {
|
||||||
|
return <RenderUserSelectInteractive interactive={value.interactive} />;
|
||||||
|
}
|
||||||
|
if (value.interactive?.type === 'userInput') {
|
||||||
|
return <RenderUserFormInteractive interactive={value.interactive} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(AIResponseBox);
|
export default React.memo(AIResponseBox);
|
||||||
|
@@ -0,0 +1,206 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { Box, Button, Flex, Textarea } from '@chakra-ui/react';
|
||||||
|
import { Controller, useForm, UseFormHandleSubmit } from 'react-hook-form';
|
||||||
|
import Markdown from '@/components/Markdown';
|
||||||
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
|
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||||
|
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
||||||
|
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
|
||||||
|
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
|
import {
|
||||||
|
UserInputFormItemType,
|
||||||
|
UserInputInteractive,
|
||||||
|
UserSelectInteractive,
|
||||||
|
UserSelectOptionItemType
|
||||||
|
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
|
|
||||||
|
const DescriptionBox = React.memo(function DescriptionBox({
|
||||||
|
description
|
||||||
|
}: {
|
||||||
|
description?: string;
|
||||||
|
}) {
|
||||||
|
if (!description) return null;
|
||||||
|
return (
|
||||||
|
<Box mb={4}>
|
||||||
|
<Markdown source={description} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SelectOptionsComponent = React.memo(function SelectOptionsComponent({
|
||||||
|
interactiveParams,
|
||||||
|
onSelect
|
||||||
|
}: {
|
||||||
|
interactiveParams: UserSelectInteractive['params'];
|
||||||
|
onSelect: (value: string) => void;
|
||||||
|
}) {
|
||||||
|
const { description, userSelectOptions, userSelectedVal } = interactiveParams;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box maxW={'100%'}>
|
||||||
|
<DescriptionBox description={description} />
|
||||||
|
<Flex flexDirection={'column'} gap={3} w={'250px'}>
|
||||||
|
{userSelectOptions.map((option: UserSelectOptionItemType) => {
|
||||||
|
const selected = option.value === userSelectedVal;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
key={option.key}
|
||||||
|
variant={'whitePrimary'}
|
||||||
|
whiteSpace={'pre-wrap'}
|
||||||
|
isDisabled={!!userSelectedVal}
|
||||||
|
{...(selected
|
||||||
|
? {
|
||||||
|
_disabled: {
|
||||||
|
cursor: 'default',
|
||||||
|
borderColor: 'primary.300',
|
||||||
|
bg: 'primary.50 !important',
|
||||||
|
color: 'primary.600'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {})}
|
||||||
|
onClick={() => onSelect(option.value)}
|
||||||
|
>
|
||||||
|
{option.value}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const FormInputComponent = React.memo(function FormInputComponent({
|
||||||
|
interactiveParams,
|
||||||
|
defaultValues = {},
|
||||||
|
SubmitButton
|
||||||
|
}: {
|
||||||
|
interactiveParams: UserInputInteractive['params'];
|
||||||
|
defaultValues?: Record<string, any>;
|
||||||
|
SubmitButton: (e: { onSubmit: UseFormHandleSubmit<Record<string, any>> }) => React.JSX.Element;
|
||||||
|
}) {
|
||||||
|
const { description, inputForm, submitted } = interactiveParams;
|
||||||
|
|
||||||
|
const { register, setValue, handleSubmit, control } = useForm({
|
||||||
|
defaultValues
|
||||||
|
});
|
||||||
|
|
||||||
|
const FormItemLabel = useCallback(
|
||||||
|
({
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
description
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
required?: boolean;
|
||||||
|
description?: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Flex mb={1} alignItems={'center'}>
|
||||||
|
<FormLabel required={required} mb={0} fontWeight="medium" color="gray.700">
|
||||||
|
{label}
|
||||||
|
</FormLabel>
|
||||||
|
{description && <QuestionTip ml={1} label={description} />}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const RenderFormInput = useCallback(
|
||||||
|
({ input }: { input: UserInputFormItemType }) => {
|
||||||
|
const { type, label, required, maxLength, min, max, defaultValue, list } = input;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case FlowNodeInputTypeEnum.input:
|
||||||
|
return (
|
||||||
|
<MyTextarea
|
||||||
|
isDisabled={submitted}
|
||||||
|
{...register(label, {
|
||||||
|
required: required
|
||||||
|
})}
|
||||||
|
bg={'white'}
|
||||||
|
autoHeight
|
||||||
|
minH={40}
|
||||||
|
maxH={100}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case FlowNodeInputTypeEnum.textarea:
|
||||||
|
return (
|
||||||
|
<Textarea
|
||||||
|
isDisabled={submitted}
|
||||||
|
bg={'white'}
|
||||||
|
{...register(label, {
|
||||||
|
required: required
|
||||||
|
})}
|
||||||
|
rows={5}
|
||||||
|
maxLength={maxLength || 4000}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case FlowNodeInputTypeEnum.numberInput:
|
||||||
|
return (
|
||||||
|
<MyNumberInput
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
isDisabled={submitted}
|
||||||
|
bg={'white'}
|
||||||
|
register={register}
|
||||||
|
name={label}
|
||||||
|
isRequired={required}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case FlowNodeInputTypeEnum.select:
|
||||||
|
return (
|
||||||
|
<Controller
|
||||||
|
key={label}
|
||||||
|
control={control}
|
||||||
|
name={label}
|
||||||
|
rules={{ required: required }}
|
||||||
|
render={({ field: { ref, value } }) => {
|
||||||
|
if (!list) return <></>;
|
||||||
|
return (
|
||||||
|
<MySelect
|
||||||
|
ref={ref}
|
||||||
|
width={'100%'}
|
||||||
|
list={list}
|
||||||
|
value={value}
|
||||||
|
isDisabled={submitted}
|
||||||
|
onChange={(e) => setValue(label, e)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[control, register, setValue, submitted]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<DescriptionBox description={description} />
|
||||||
|
<Flex flexDirection={'column'} gap={3}>
|
||||||
|
{inputForm.map((input) => (
|
||||||
|
<Box key={input.label}>
|
||||||
|
<FormItemLabel
|
||||||
|
label={input.label}
|
||||||
|
required={input.required}
|
||||||
|
description={input.description}
|
||||||
|
/>
|
||||||
|
<RenderFormInput input={input} />
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{!submitted && (
|
||||||
|
<Flex justifyContent={'flex-end'} mt={4}>
|
||||||
|
<SubmitButton onSubmit={handleSubmit} />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
@@ -1,6 +1,7 @@
|
|||||||
import { AppSchema } from '@fastgpt/global/core/app/type';
|
import { AppSchema } from '@fastgpt/global/core/app/type';
|
||||||
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
||||||
import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
|
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
|
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||||
import { RuntimeEdgeItemType, StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
import { RuntimeEdgeItemType, StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||||
|
|
||||||
@@ -9,6 +10,8 @@ export type PostWorkflowDebugProps = {
|
|||||||
edges: RuntimeEdgeItemType[];
|
edges: RuntimeEdgeItemType[];
|
||||||
variables: Record<string, any>;
|
variables: Record<string, any>;
|
||||||
appId: string;
|
appId: string;
|
||||||
|
query?: UserChatItemValueItemType[];
|
||||||
|
history?: ChatItemType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PostWorkflowDebugResponse = {
|
export type PostWorkflowDebugResponse = {
|
||||||
@@ -16,5 +19,6 @@ export type PostWorkflowDebugResponse = {
|
|||||||
finishedEdges: RuntimeEdgeItemType[];
|
finishedEdges: RuntimeEdgeItemType[];
|
||||||
nextStepRunNodes: RuntimeNodeItemType[];
|
nextStepRunNodes: RuntimeNodeItemType[];
|
||||||
flowResponses: ChatHistoryItemResType[];
|
flowResponses: ChatHistoryItemResType[];
|
||||||
|
workflowInteractiveResponse?: WorkflowInteractiveResponseType;
|
||||||
newVariables: Record<string, any>;
|
newVariables: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { Box, Button, Card, Flex, FlexProps } from '@chakra-ui/react';
|
import { Box, Button, Flex, type FlexProps } from '@chakra-ui/react';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import type { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
import type { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||||
@@ -13,7 +13,6 @@ import { ToolSourceHandle, ToolTargetHandle } from './Handle/ToolHandle';
|
|||||||
import { useEditTextarea } from '@fastgpt/web/hooks/useEditTextarea';
|
import { useEditTextarea } from '@fastgpt/web/hooks/useEditTextarea';
|
||||||
import { ConnectionSourceHandle, ConnectionTargetHandle } from './Handle/ConnectionHandle';
|
import { ConnectionSourceHandle, ConnectionTargetHandle } from './Handle/ConnectionHandle';
|
||||||
import { useDebug } from '../../hooks/useDebug';
|
import { useDebug } from '../../hooks/useDebug';
|
||||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
|
||||||
import { getPreviewPluginNode } from '@/web/core/app/api/plugin';
|
import { getPreviewPluginNode } from '@/web/core/app/api/plugin';
|
||||||
import { storeNode2FlowNode } from '@/web/core/workflow/utils';
|
import { storeNode2FlowNode } from '@/web/core/workflow/utils';
|
||||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
@@ -23,12 +22,12 @@ import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/cons
|
|||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { useWorkflowUtils } from '../../hooks/useUtils';
|
import { useWorkflowUtils } from '../../hooks/useUtils';
|
||||||
import { WholeResponseContent } from '@/components/core/chat/components/WholeResponseModal';
|
|
||||||
import { WorkflowNodeEdgeContext } from '../../../context/workflowInitContext';
|
import { WorkflowNodeEdgeContext } from '../../../context/workflowInitContext';
|
||||||
import { WorkflowEventContext } from '../../../context/workflowEventContext';
|
import { WorkflowEventContext } from '../../../context/workflowEventContext';
|
||||||
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
|
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
|
||||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||||
import UseGuideModal from '@/components/common/Modal/UseGuideModal';
|
import UseGuideModal from '@/components/common/Modal/UseGuideModal';
|
||||||
|
import NodeDebugResponse from './RenderDebug/NodeDebugResponse';
|
||||||
|
|
||||||
type Props = FlowNodeItemType & {
|
type Props = FlowNodeItemType & {
|
||||||
children?: React.ReactNode | React.ReactNode[] | string;
|
children?: React.ReactNode | React.ReactNode[] | string;
|
||||||
@@ -62,6 +61,7 @@ const NodeCard = (props: Props) => {
|
|||||||
w = 'full',
|
w = 'full',
|
||||||
h = 'full',
|
h = 'full',
|
||||||
nodeId,
|
nodeId,
|
||||||
|
flowNodeType,
|
||||||
selected,
|
selected,
|
||||||
menuForbid,
|
menuForbid,
|
||||||
isTool = false,
|
isTool = false,
|
||||||
@@ -409,7 +409,7 @@ const NodeCard = (props: Props) => {
|
|||||||
})}
|
})}
|
||||||
{...customStyle}
|
{...customStyle}
|
||||||
>
|
>
|
||||||
<NodeDebugResponse nodeId={nodeId} debugResult={debugResult} />
|
{debugResult && <NodeDebugResponse nodeId={nodeId} debugResult={debugResult} />}
|
||||||
{Header}
|
{Header}
|
||||||
<Flex flexDirection={'column'} flex={1} my={!isFolded ? 3 : 0} gap={2}>
|
<Flex flexDirection={'column'} flex={1} my={!isFolded ? 3 : 0} gap={2}>
|
||||||
{!isFolded ? children : <Box h={4} />}
|
{!isFolded ? children : <Box h={4} />}
|
||||||
@@ -661,168 +661,3 @@ const NodeIntro = React.memo(function NodeIntro({
|
|||||||
|
|
||||||
return Render;
|
return Render;
|
||||||
});
|
});
|
||||||
|
|
||||||
const NodeDebugResponse = React.memo(function NodeDebugResponse({
|
|
||||||
nodeId,
|
|
||||||
debugResult
|
|
||||||
}: {
|
|
||||||
nodeId: string;
|
|
||||||
debugResult: FlowNodeItemType['debugResult'];
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const { onChangeNode, onStopNodeDebug, onNextNodeDebug, workflowDebugData } = useContextSelector(
|
|
||||||
WorkflowContext,
|
|
||||||
(v) => v
|
|
||||||
);
|
|
||||||
|
|
||||||
const { openConfirm, ConfirmModal } = useConfirm({
|
|
||||||
content: t('common:core.workflow.Confirm stop debug')
|
|
||||||
});
|
|
||||||
|
|
||||||
const RenderStatus = useMemo(() => {
|
|
||||||
const map = {
|
|
||||||
running: {
|
|
||||||
bg: 'primary.50',
|
|
||||||
text: t('common:core.workflow.Running'),
|
|
||||||
icon: 'core/workflow/running'
|
|
||||||
},
|
|
||||||
success: {
|
|
||||||
bg: 'green.50',
|
|
||||||
text: t('common:core.workflow.Success'),
|
|
||||||
icon: 'core/workflow/runSuccess'
|
|
||||||
},
|
|
||||||
failed: {
|
|
||||||
bg: 'red.50',
|
|
||||||
text: t('common:core.workflow.Failed'),
|
|
||||||
icon: 'core/workflow/runError'
|
|
||||||
},
|
|
||||||
skipped: {
|
|
||||||
bg: 'myGray.50',
|
|
||||||
text: t('common:core.workflow.Skipped'),
|
|
||||||
icon: 'core/workflow/runSkip'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const statusData = map[debugResult?.status || 'running'];
|
|
||||||
|
|
||||||
const response = debugResult?.response;
|
|
||||||
|
|
||||||
const onStop = () => {
|
|
||||||
openConfirm(onStopNodeDebug)();
|
|
||||||
};
|
|
||||||
|
|
||||||
return !!debugResult && !!statusData ? (
|
|
||||||
<>
|
|
||||||
<Flex px={3} bg={statusData.bg} borderTopRadius={'md'} py={3}>
|
|
||||||
<MyIcon name={statusData.icon as any} w={'16px'} mr={2} />
|
|
||||||
<Box color={'myGray.900'} fontWeight={'bold'} flex={'1 0 0'}>
|
|
||||||
{statusData.text}
|
|
||||||
</Box>
|
|
||||||
{debugResult.status !== 'running' && (
|
|
||||||
<Box
|
|
||||||
color={'primary.700'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
fontSize={'sm'}
|
|
||||||
onClick={() =>
|
|
||||||
onChangeNode({
|
|
||||||
nodeId,
|
|
||||||
type: 'attr',
|
|
||||||
key: 'debugResult',
|
|
||||||
value: {
|
|
||||||
...debugResult,
|
|
||||||
showResult: !debugResult.showResult
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{debugResult.showResult
|
|
||||||
? t('common:core.workflow.debug.Hide result')
|
|
||||||
: t('common:core.workflow.debug.Show result')}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
{/* Result card */}
|
|
||||||
{debugResult.showResult && (
|
|
||||||
<Card
|
|
||||||
className="nowheel"
|
|
||||||
position={'absolute'}
|
|
||||||
right={'-430px'}
|
|
||||||
top={0}
|
|
||||||
zIndex={10}
|
|
||||||
w={'420px'}
|
|
||||||
maxH={'max(100%,500px)'}
|
|
||||||
border={'base'}
|
|
||||||
>
|
|
||||||
{/* Status header */}
|
|
||||||
<Flex h={'54x'} px={3} py={3} alignItems={'center'}>
|
|
||||||
<MyIcon mr={1} name={'core/workflow/debugResult'} w={'20px'} color={'primary.600'} />
|
|
||||||
<Box fontWeight={'bold'} flex={'1'}>
|
|
||||||
{t('common:core.workflow.debug.Run result')}
|
|
||||||
</Box>
|
|
||||||
{workflowDebugData?.nextRunNodes.length !== 0 && (
|
|
||||||
<Button
|
|
||||||
size={'sm'}
|
|
||||||
leftIcon={<MyIcon name={'core/chat/stopSpeech'} w={'16px'} />}
|
|
||||||
variant={'whiteDanger'}
|
|
||||||
onClick={onStop}
|
|
||||||
>
|
|
||||||
{t('common:core.workflow.Stop debug')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{(debugResult.status === 'success' || debugResult.status === 'skipped') &&
|
|
||||||
!debugResult.isExpired &&
|
|
||||||
workflowDebugData?.nextRunNodes &&
|
|
||||||
workflowDebugData.nextRunNodes.length > 0 && (
|
|
||||||
<Button
|
|
||||||
ml={2}
|
|
||||||
size={'sm'}
|
|
||||||
leftIcon={<MyIcon name={'core/workflow/debugNext'} w={'16px'} />}
|
|
||||||
variant={'primary'}
|
|
||||||
onClick={() => onNextNodeDebug()}
|
|
||||||
>
|
|
||||||
{t('common:common.Next Step')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{workflowDebugData?.nextRunNodes && workflowDebugData?.nextRunNodes.length === 0 && (
|
|
||||||
<Button ml={2} size={'sm'} variant={'primary'} onClick={onStopNodeDebug}>
|
|
||||||
{t('common:core.workflow.debug.Done')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
{/* Response list */}
|
|
||||||
{debugResult.status !== 'skipped' && (
|
|
||||||
<Box borderTop={'base'} mt={1} overflowY={'auto'} minH={'250px'}>
|
|
||||||
{!debugResult.message && !response && (
|
|
||||||
<EmptyTip text={t('common:core.workflow.debug.Not result')} pt={2} pb={5} />
|
|
||||||
)}
|
|
||||||
{debugResult.message && (
|
|
||||||
<Box color={'red.600'} px={3} py={4}>
|
|
||||||
{debugResult.message}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{response && <WholeResponseContent activeModule={response} />}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : null;
|
|
||||||
}, [
|
|
||||||
debugResult,
|
|
||||||
nodeId,
|
|
||||||
onChangeNode,
|
|
||||||
onNextNodeDebug,
|
|
||||||
onStopNodeDebug,
|
|
||||||
openConfirm,
|
|
||||||
t,
|
|
||||||
workflowDebugData
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{RenderStatus}
|
|
||||||
<ConfirmModal />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
@@ -0,0 +1,269 @@
|
|||||||
|
import React, { useCallback, useMemo, useRef } from 'react';
|
||||||
|
import { Box, Button, Card, Flex } from '@chakra-ui/react';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
|
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||||
|
import { useContextSelector } from 'use-context-selector';
|
||||||
|
import { WorkflowContext } from '../../../../context';
|
||||||
|
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||||
|
import { WholeResponseContent } from '@/components/core/chat/components/WholeResponseModal';
|
||||||
|
import type { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||||
|
import {
|
||||||
|
FormInputComponent,
|
||||||
|
SelectOptionsComponent
|
||||||
|
} from '@/components/core/chat/components/Interactive/InteractiveComponents';
|
||||||
|
import { UserInputInteractive } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
|
import { initWorkflowEdgeStatus } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
|
import { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||||
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
|
|
||||||
|
type NodeDebugResponseProps = {
|
||||||
|
nodeId: string;
|
||||||
|
debugResult: FlowNodeItemType['debugResult'];
|
||||||
|
};
|
||||||
|
|
||||||
|
const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
||||||
|
interactive,
|
||||||
|
onNext
|
||||||
|
}: {
|
||||||
|
interactive: UserInputInteractive;
|
||||||
|
onNext: (val: string) => void;
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const defaultValues = useMemo(() => {
|
||||||
|
return interactive.params.inputForm?.reduce((acc: Record<string, any>, item) => {
|
||||||
|
acc[item.label] = !!item.value ? item.value : item.defaultValue;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}, [interactive.params.inputForm]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box px={4} py={4} bg="white" borderRadius="md">
|
||||||
|
<FormInputComponent
|
||||||
|
defaultValues={defaultValues}
|
||||||
|
interactiveParams={interactive.params}
|
||||||
|
SubmitButton={({ onSubmit }) => (
|
||||||
|
<Button
|
||||||
|
leftIcon={<MyIcon name="core/workflow/debugNext" />}
|
||||||
|
onClick={() =>
|
||||||
|
onSubmit((data) => {
|
||||||
|
onNext(JSON.stringify(data));
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t('common:common.Next Step')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const NodeDebugResponse = ({ nodeId, debugResult }: NodeDebugResponseProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { onChangeNode, onStopNodeDebug, onNextNodeDebug, workflowDebugData } = useContextSelector(
|
||||||
|
WorkflowContext,
|
||||||
|
(v) => v
|
||||||
|
);
|
||||||
|
|
||||||
|
const statusMap = useRef({
|
||||||
|
running: {
|
||||||
|
bg: 'primary.50',
|
||||||
|
text: t('common:core.workflow.Running'),
|
||||||
|
icon: 'core/workflow/running'
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
bg: 'green.50',
|
||||||
|
text: t('common:core.workflow.Success'),
|
||||||
|
icon: 'core/workflow/runSuccess'
|
||||||
|
},
|
||||||
|
failed: {
|
||||||
|
bg: 'red.50',
|
||||||
|
text: t('common:core.workflow.Failed'),
|
||||||
|
icon: 'core/workflow/runError'
|
||||||
|
},
|
||||||
|
skipped: {
|
||||||
|
bg: 'myGray.50',
|
||||||
|
text: t('common:core.workflow.Skipped'),
|
||||||
|
icon: 'core/workflow/runSkip'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const statusData = statusMap.current[debugResult?.status || 'running'];
|
||||||
|
|
||||||
|
const response = debugResult?.response;
|
||||||
|
|
||||||
|
const { openConfirm, ConfirmModal } = useConfirm({
|
||||||
|
content: t('common:core.workflow.Confirm stop debug')
|
||||||
|
});
|
||||||
|
const onStop = () => {
|
||||||
|
openConfirm(onStopNodeDebug)();
|
||||||
|
};
|
||||||
|
|
||||||
|
const interactive = debugResult?.workflowInteractiveResponse;
|
||||||
|
const onNextInteractive = useCallback(
|
||||||
|
(userContent: string) => {
|
||||||
|
if (!workflowDebugData || !workflowDebugData || !interactive) return;
|
||||||
|
|
||||||
|
const updatedQuery: UserChatItemValueItemType[] = [
|
||||||
|
{
|
||||||
|
type: ChatItemValueTypeEnum.text,
|
||||||
|
text: { content: userContent }
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockHistory: ChatItemType[] = [
|
||||||
|
{
|
||||||
|
obj: ChatRoleEnum.AI,
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: ChatItemValueTypeEnum.interactive,
|
||||||
|
interactive: {
|
||||||
|
...interactive,
|
||||||
|
memoryEdges: interactive.memoryEdges || [],
|
||||||
|
entryNodeIds: interactive.entryNodeIds || [],
|
||||||
|
nodeOutputs: interactive.nodeOutputs || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
onNextNodeDebug({
|
||||||
|
...workflowDebugData,
|
||||||
|
// Rewrite runtimeEdges
|
||||||
|
runtimeEdges: initWorkflowEdgeStatus(workflowDebugData.runtimeEdges, mockHistory),
|
||||||
|
query: updatedQuery,
|
||||||
|
history: mockHistory
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[workflowDebugData, interactive, onNextNodeDebug]
|
||||||
|
);
|
||||||
|
|
||||||
|
return !!debugResult && !!statusData ? (
|
||||||
|
<>
|
||||||
|
{/* Status header */}
|
||||||
|
<Flex px={3} bg={statusData.bg} borderTopRadius={'md'} py={3}>
|
||||||
|
<MyIcon name={statusData.icon as any} w={'16px'} mr={2} />
|
||||||
|
<Box color={'myGray.900'} fontWeight={'bold'} flex={'1 0 0'}>
|
||||||
|
{statusData.text}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{debugResult.status !== 'running' && (
|
||||||
|
<Box
|
||||||
|
color={'primary.700'}
|
||||||
|
cursor={'pointer'}
|
||||||
|
fontSize={'sm'}
|
||||||
|
onClick={() =>
|
||||||
|
onChangeNode({
|
||||||
|
nodeId,
|
||||||
|
type: 'attr',
|
||||||
|
key: 'debugResult',
|
||||||
|
value: {
|
||||||
|
...debugResult,
|
||||||
|
showResult: !debugResult.showResult
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{debugResult.showResult
|
||||||
|
? t('common:core.workflow.debug.Hide result')
|
||||||
|
: t('common:core.workflow.debug.Show result')}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
{/* Result card */}
|
||||||
|
{debugResult.showResult && (
|
||||||
|
<Card
|
||||||
|
className="nowheel"
|
||||||
|
position={'absolute'}
|
||||||
|
right={'-430px'}
|
||||||
|
top={0}
|
||||||
|
zIndex={10}
|
||||||
|
w={'420px'}
|
||||||
|
maxH={'max(100%,500px)'}
|
||||||
|
border={'base'}
|
||||||
|
>
|
||||||
|
{/* Status header */}
|
||||||
|
<Flex h={'54x'} px={3} py={3} alignItems={'center'}>
|
||||||
|
<MyIcon mr={1} name={'core/workflow/debugResult'} w={'20px'} color={'primary.600'} />
|
||||||
|
<Box fontWeight={'bold'} flex={'1'}>
|
||||||
|
{t('common:core.workflow.debug.Run result')}
|
||||||
|
</Box>
|
||||||
|
{workflowDebugData?.nextRunNodes.length !== 0 && (
|
||||||
|
<Button
|
||||||
|
size={'sm'}
|
||||||
|
leftIcon={<MyIcon name={'core/chat/stopSpeech'} w={'16px'} />}
|
||||||
|
variant={'whiteDanger'}
|
||||||
|
onClick={onStop}
|
||||||
|
>
|
||||||
|
{t('common:core.workflow.Stop debug')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!interactive && (
|
||||||
|
<>
|
||||||
|
{(debugResult.status === 'success' || debugResult.status === 'skipped') &&
|
||||||
|
!debugResult.isExpired &&
|
||||||
|
workflowDebugData?.nextRunNodes &&
|
||||||
|
workflowDebugData.nextRunNodes.length > 0 && (
|
||||||
|
<Button
|
||||||
|
ml={2}
|
||||||
|
size={'sm'}
|
||||||
|
leftIcon={<MyIcon name={'core/workflow/debugNext'} w={'16px'} />}
|
||||||
|
variant={'primary'}
|
||||||
|
onClick={() => onNextNodeDebug(workflowDebugData)}
|
||||||
|
>
|
||||||
|
{t('common:common.Next Step')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{workflowDebugData?.nextRunNodes &&
|
||||||
|
workflowDebugData?.nextRunNodes.length === 0 && (
|
||||||
|
<Button ml={2} size={'sm'} variant={'primary'} onClick={onStopNodeDebug}>
|
||||||
|
{t('common:core.workflow.debug.Done')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
{/* Response list */}
|
||||||
|
{debugResult.status !== 'skipped' && (
|
||||||
|
<Box borderTop={'base'} mt={1} overflowY={'auto'} minH={'250px'}>
|
||||||
|
{!debugResult.message && !response && !interactive && (
|
||||||
|
<EmptyTip text={t('common:core.workflow.debug.Not result')} pt={2} pb={5} />
|
||||||
|
)}
|
||||||
|
{debugResult.message && (
|
||||||
|
<Box color={'red.600'} px={3} py={4}>
|
||||||
|
{debugResult.message}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{interactive && onNextInteractive && (
|
||||||
|
<>
|
||||||
|
{interactive.type === 'userSelect' && (
|
||||||
|
<Box px={4} py={3}>
|
||||||
|
<SelectOptionsComponent
|
||||||
|
interactiveParams={interactive.params}
|
||||||
|
onSelect={(val) => {
|
||||||
|
onNextInteractive(val);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{interactive.type === 'userInput' && (
|
||||||
|
<RenderUserFormInteractive
|
||||||
|
interactive={interactive}
|
||||||
|
onNext={onNextInteractive}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{response && <WholeResponseContent activeModule={response} />}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
<ConfirmModal />
|
||||||
|
</>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
export default React.memo(NodeDebugResponse);
|
@@ -35,6 +35,8 @@ import WorkflowInitContextProvider, { WorkflowNodeEdgeContext } from './workflow
|
|||||||
import WorkflowEventContextProvider from './workflowEventContext';
|
import WorkflowEventContextProvider from './workflowEventContext';
|
||||||
import { getAppConfigByDiff } from '@/web/core/app/diff';
|
import { getAppConfigByDiff } from '@/web/core/app/diff';
|
||||||
import WorkflowStatusContextProvider from './workflowStatusContext';
|
import WorkflowStatusContextProvider from './workflowStatusContext';
|
||||||
|
import { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||||
|
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Context
|
Context
|
||||||
@@ -156,24 +158,22 @@ type WorkflowContextType = {
|
|||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
// debug
|
// debug
|
||||||
workflowDebugData:
|
workflowDebugData?: DebugDataType;
|
||||||
| {
|
onNextNodeDebug: (params: DebugDataType) => Promise<void>;
|
||||||
runtimeNodes: RuntimeNodeItemType[];
|
|
||||||
runtimeEdges: RuntimeEdgeItemType[];
|
|
||||||
nextRunNodes: RuntimeNodeItemType[];
|
|
||||||
}
|
|
||||||
| undefined;
|
|
||||||
onNextNodeDebug: () => Promise<void>;
|
|
||||||
onStartNodeDebug: ({
|
onStartNodeDebug: ({
|
||||||
entryNodeId,
|
entryNodeId,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges,
|
runtimeEdges,
|
||||||
variables
|
variables,
|
||||||
|
query,
|
||||||
|
history
|
||||||
}: {
|
}: {
|
||||||
entryNodeId: string;
|
entryNodeId: string;
|
||||||
runtimeNodes: RuntimeNodeItemType[];
|
runtimeNodes: RuntimeNodeItemType[];
|
||||||
runtimeEdges: RuntimeEdgeItemType[];
|
runtimeEdges: RuntimeEdgeItemType[];
|
||||||
variables: Record<string, any>;
|
variables: Record<string, any>;
|
||||||
|
query?: UserChatItemValueItemType[];
|
||||||
|
history?: ChatItemType[];
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
onStopNodeDebug: () => void;
|
onStopNodeDebug: () => void;
|
||||||
|
|
||||||
@@ -189,11 +189,14 @@ type WorkflowContextType = {
|
|||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DebugDataType = {
|
export type DebugDataType = {
|
||||||
runtimeNodes: RuntimeNodeItemType[];
|
runtimeNodes: RuntimeNodeItemType[];
|
||||||
runtimeEdges: RuntimeEdgeItemType[];
|
runtimeEdges: RuntimeEdgeItemType[];
|
||||||
nextRunNodes: RuntimeNodeItemType[];
|
nextRunNodes: RuntimeNodeItemType[];
|
||||||
variables: Record<string, any>;
|
variables: Record<string, any>;
|
||||||
|
history?: ChatItemType[];
|
||||||
|
query?: UserChatItemValueItemType[];
|
||||||
|
workflowInteractiveResponse?: WorkflowInteractiveResponseType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WorkflowContext = createContext<WorkflowContextType>({
|
export const WorkflowContext = createContext<WorkflowContextType>({
|
||||||
@@ -236,17 +239,25 @@ export const WorkflowContext = createContext<WorkflowContextType>({
|
|||||||
throw new Error('Function not implemented.');
|
throw new Error('Function not implemented.');
|
||||||
},
|
},
|
||||||
workflowDebugData: undefined,
|
workflowDebugData: undefined,
|
||||||
onNextNodeDebug: function (): Promise<void> {
|
onNextNodeDebug: function (params?: {
|
||||||
|
history?: ChatItemType[];
|
||||||
|
query?: UserChatItemValueItemType[];
|
||||||
|
debugData?: DebugDataType;
|
||||||
|
}): Promise<void> {
|
||||||
throw new Error('Function not implemented.');
|
throw new Error('Function not implemented.');
|
||||||
},
|
},
|
||||||
onStartNodeDebug: function ({
|
onStartNodeDebug: function ({
|
||||||
entryNodeId,
|
entryNodeId,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges
|
runtimeEdges,
|
||||||
|
query,
|
||||||
|
history
|
||||||
}: {
|
}: {
|
||||||
entryNodeId: string;
|
entryNodeId: string;
|
||||||
runtimeNodes: RuntimeNodeItemType[];
|
runtimeNodes: RuntimeNodeItemType[];
|
||||||
runtimeEdges: RuntimeEdgeItemType[];
|
runtimeEdges: RuntimeEdgeItemType[];
|
||||||
|
query?: UserChatItemValueItemType[];
|
||||||
|
history?: ChatItemType[];
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
throw new Error('Function not implemented.');
|
throw new Error('Function not implemented.');
|
||||||
},
|
},
|
||||||
@@ -551,8 +562,7 @@ const WorkflowContextProvider = ({
|
|||||||
/* debug */
|
/* debug */
|
||||||
const [workflowDebugData, setWorkflowDebugData] = useState<DebugDataType>();
|
const [workflowDebugData, setWorkflowDebugData] = useState<DebugDataType>();
|
||||||
const onNextNodeDebug = useCallback(
|
const onNextNodeDebug = useCallback(
|
||||||
async (debugData = workflowDebugData) => {
|
async (debugData: DebugDataType) => {
|
||||||
if (!debugData) return;
|
|
||||||
// 1. Cancel node selected status and debugResult.showStatus
|
// 1. Cancel node selected status and debugResult.showStatus
|
||||||
setNodes((state) =>
|
setNodes((state) =>
|
||||||
state.map((node) => ({
|
state.map((node) => ({
|
||||||
@@ -612,26 +622,35 @@ const WorkflowContextProvider = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 4. Run one step
|
// 4. Run one step
|
||||||
const { finishedEdges, finishedNodes, nextStepRunNodes, flowResponses, newVariables } =
|
const {
|
||||||
await postWorkflowDebug({
|
finishedEdges,
|
||||||
nodes: runtimeNodes,
|
finishedNodes,
|
||||||
edges: debugData.runtimeEdges,
|
nextStepRunNodes,
|
||||||
variables: {
|
flowResponses,
|
||||||
appId,
|
newVariables,
|
||||||
cTime: formatTime2YMDHMW(),
|
workflowInteractiveResponse
|
||||||
...debugData.variables
|
} = await postWorkflowDebug({
|
||||||
},
|
nodes: runtimeNodes,
|
||||||
appId
|
edges: debugData.runtimeEdges,
|
||||||
});
|
variables: {
|
||||||
|
appId,
|
||||||
|
cTime: formatTime2YMDHMW(),
|
||||||
|
...debugData.variables
|
||||||
|
},
|
||||||
|
query: debugData.query, // 添加 query 参数
|
||||||
|
history: debugData.history,
|
||||||
|
appId
|
||||||
|
});
|
||||||
|
|
||||||
// 5. Store debug result
|
// 5. Store debug result
|
||||||
const newStoreDebugData = {
|
setWorkflowDebugData({
|
||||||
runtimeNodes: finishedNodes,
|
runtimeNodes: finishedNodes,
|
||||||
// edges need to save status
|
// edges need to save status
|
||||||
runtimeEdges: finishedEdges,
|
runtimeEdges: finishedEdges,
|
||||||
nextRunNodes: nextStepRunNodes,
|
nextRunNodes: nextStepRunNodes,
|
||||||
variables: newVariables
|
variables: newVariables,
|
||||||
};
|
workflowInteractiveResponse: workflowInteractiveResponse
|
||||||
setWorkflowDebugData(newStoreDebugData);
|
});
|
||||||
|
|
||||||
// 6. selected entry node and Update entry node debug result
|
// 6. selected entry node and Update entry node debug result
|
||||||
setNodes((state) =>
|
setNodes((state) =>
|
||||||
@@ -665,16 +684,21 @@ const WorkflowContextProvider = ({
|
|||||||
status: 'success',
|
status: 'success',
|
||||||
response: result,
|
response: result,
|
||||||
showResult: true,
|
showResult: true,
|
||||||
isExpired: false
|
isExpired: false,
|
||||||
|
workflowInteractiveResponse: workflowInteractiveResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check for an empty response
|
// Check for an empty response(Skip node)
|
||||||
if (flowResponses.length === 0 && nextStepRunNodes.length > 0) {
|
if (
|
||||||
onNextNodeDebug(newStoreDebugData);
|
!workflowInteractiveResponse &&
|
||||||
|
flowResponses.length === 0 &&
|
||||||
|
nextStepRunNodes.length > 0
|
||||||
|
) {
|
||||||
|
onNextNodeDebug(debugData);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
entryNodes.forEach((node) => {
|
entryNodes.forEach((node) => {
|
||||||
@@ -692,7 +716,7 @@ const WorkflowContextProvider = ({
|
|||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[appId, onChangeNode, setNodes, workflowDebugData]
|
[appId, onChangeNode, setNodes]
|
||||||
);
|
);
|
||||||
const onStopNodeDebug = useMemoizedFn(() => {
|
const onStopNodeDebug = useMemoizedFn(() => {
|
||||||
setWorkflowDebugData(undefined);
|
setWorkflowDebugData(undefined);
|
||||||
@@ -712,18 +736,24 @@ const WorkflowContextProvider = ({
|
|||||||
entryNodeId,
|
entryNodeId,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges,
|
runtimeEdges,
|
||||||
variables
|
variables,
|
||||||
|
query,
|
||||||
|
history
|
||||||
}: {
|
}: {
|
||||||
entryNodeId: string;
|
entryNodeId: string;
|
||||||
runtimeNodes: RuntimeNodeItemType[];
|
runtimeNodes: RuntimeNodeItemType[];
|
||||||
runtimeEdges: RuntimeEdgeItemType[];
|
runtimeEdges: RuntimeEdgeItemType[];
|
||||||
variables: Record<string, any>;
|
variables: Record<string, any>;
|
||||||
|
query?: UserChatItemValueItemType[];
|
||||||
|
history?: ChatItemType[];
|
||||||
}) => {
|
}) => {
|
||||||
const data = {
|
const data: DebugDataType = {
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges,
|
runtimeEdges,
|
||||||
nextRunNodes: runtimeNodes.filter((node) => node.nodeId === entryNodeId),
|
nextRunNodes: runtimeNodes.filter((node) => node.nodeId === entryNodeId),
|
||||||
variables
|
variables,
|
||||||
|
query,
|
||||||
|
history
|
||||||
};
|
};
|
||||||
onStopNodeDebug();
|
onStopNodeDebug();
|
||||||
setWorkflowDebugData(data);
|
setWorkflowDebugData(data);
|
||||||
|
@@ -11,7 +11,7 @@ import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat
|
|||||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
|
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
|
||||||
import { getUserChatInfoAndAuthTeamPoints } from '@fastgpt/service/support/permission/auth/team';
|
import { getUserChatInfoAndAuthTeamPoints } from '@fastgpt/service/support/permission/auth/team';
|
||||||
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
import type { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||||
import {
|
import {
|
||||||
concatHistories,
|
concatHistories,
|
||||||
getChatTitleFromChatMessage,
|
getChatTitleFromChatMessage,
|
||||||
@@ -25,8 +25,8 @@ import {
|
|||||||
} from '@fastgpt/global/core/workflow/utils';
|
} from '@fastgpt/global/core/workflow/utils';
|
||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import { chatValue2RuntimePrompt, GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
import { chatValue2RuntimePrompt, GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
|
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
|
||||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
import type { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import {
|
import {
|
||||||
getLastInteractiveValue,
|
getLastInteractiveValue,
|
||||||
getMaxHistoryLimitFromNodes,
|
getMaxHistoryLimitFromNodes,
|
||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
storeNodes2RuntimeNodes,
|
storeNodes2RuntimeNodes,
|
||||||
textAdaptGptResponse
|
textAdaptGptResponse
|
||||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||||
import { getWorkflowResponseWrite } from '@fastgpt/service/core/workflow/dispatch/utils';
|
import { getWorkflowResponseWrite } from '@fastgpt/service/core/workflow/dispatch/utils';
|
||||||
import { WORKFLOW_MAX_RUN_TIMES } from '@fastgpt/service/core/workflow/constants';
|
import { WORKFLOW_MAX_RUN_TIMES } from '@fastgpt/service/core/workflow/constants';
|
||||||
import { getPluginInputsFromStoreNodes } from '@fastgpt/global/core/app/plugin/utils';
|
import { getPluginInputsFromStoreNodes } from '@fastgpt/global/core/app/plugin/utils';
|
||||||
|
@@ -5,7 +5,7 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
|||||||
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
|
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
|
||||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||||
import { getUserChatInfoAndAuthTeamPoints } from '@fastgpt/service/support/permission/auth/team';
|
import { getUserChatInfoAndAuthTeamPoints } from '@fastgpt/service/support/permission/auth/team';
|
||||||
import { PostWorkflowDebugProps, PostWorkflowDebugResponse } from '@/global/core/workflow/api';
|
import type { PostWorkflowDebugProps, PostWorkflowDebugResponse } from '@/global/core/workflow/api';
|
||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
import { defaultApp } from '@/web/core/app/constants';
|
import { defaultApp } from '@/web/core/app/constants';
|
||||||
@@ -15,16 +15,22 @@ async function handler(
|
|||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
res: NextApiResponse
|
res: NextApiResponse
|
||||||
): Promise<PostWorkflowDebugResponse> {
|
): Promise<PostWorkflowDebugResponse> {
|
||||||
const { nodes = [], edges = [], variables = {}, appId } = req.body as PostWorkflowDebugProps;
|
const {
|
||||||
|
nodes = [],
|
||||||
|
edges = [],
|
||||||
|
variables = {},
|
||||||
|
appId,
|
||||||
|
query = [],
|
||||||
|
history = []
|
||||||
|
} = req.body as PostWorkflowDebugProps;
|
||||||
if (!nodes) {
|
if (!nodes) {
|
||||||
throw new Error('Prams Error');
|
return Promise.reject('Prams Error');
|
||||||
}
|
}
|
||||||
if (!Array.isArray(nodes)) {
|
if (!Array.isArray(nodes)) {
|
||||||
throw new Error('Nodes is not array');
|
return Promise.reject('Nodes is not array');
|
||||||
}
|
}
|
||||||
if (!Array.isArray(edges)) {
|
if (!Array.isArray(edges)) {
|
||||||
throw new Error('Edges is not array');
|
return Promise.reject('Edges is not array');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* user auth */
|
/* user auth */
|
||||||
@@ -40,31 +46,32 @@ async function handler(
|
|||||||
const { timezone, externalProvider } = await getUserChatInfoAndAuthTeamPoints(tmbId);
|
const { timezone, externalProvider } = await getUserChatInfoAndAuthTeamPoints(tmbId);
|
||||||
|
|
||||||
/* start process */
|
/* start process */
|
||||||
const { flowUsages, flowResponses, debugResponse, newVariables } = await dispatchWorkFlow({
|
const { flowUsages, flowResponses, debugResponse, newVariables, workflowInteractiveResponse } =
|
||||||
res,
|
await dispatchWorkFlow({
|
||||||
requestOrigin: req.headers.origin,
|
res,
|
||||||
mode: 'debug',
|
requestOrigin: req.headers.origin,
|
||||||
runningAppInfo: {
|
mode: 'debug',
|
||||||
id: app._id,
|
timezone,
|
||||||
teamId: app.teamId,
|
externalProvider,
|
||||||
tmbId: app.tmbId
|
uid: tmbId,
|
||||||
},
|
runningAppInfo: {
|
||||||
runningUserInfo: {
|
id: app._id,
|
||||||
teamId,
|
teamId: app.teamId,
|
||||||
tmbId
|
tmbId: app.tmbId
|
||||||
},
|
},
|
||||||
uid: tmbId,
|
runningUserInfo: {
|
||||||
timezone,
|
teamId,
|
||||||
externalProvider,
|
tmbId
|
||||||
runtimeNodes: nodes,
|
},
|
||||||
runtimeEdges: edges,
|
runtimeNodes: nodes,
|
||||||
variables,
|
runtimeEdges: edges,
|
||||||
query: [],
|
variables,
|
||||||
chatConfig: defaultApp.chatConfig,
|
query: query,
|
||||||
histories: [],
|
chatConfig: defaultApp.chatConfig,
|
||||||
stream: false,
|
histories: history,
|
||||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES
|
stream: false,
|
||||||
});
|
maxRunTimes: WORKFLOW_MAX_RUN_TIMES
|
||||||
|
});
|
||||||
|
|
||||||
createChatUsage({
|
createChatUsage({
|
||||||
appName: `${app.name}-Debug`,
|
appName: `${app.name}-Debug`,
|
||||||
@@ -78,12 +85,12 @@ async function handler(
|
|||||||
return {
|
return {
|
||||||
...debugResponse,
|
...debugResponse,
|
||||||
newVariables,
|
newVariables,
|
||||||
flowResponses
|
flowResponses,
|
||||||
|
workflowInteractiveResponse
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAPI(handler);
|
export default NextAPI(handler);
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
bodyParser: {
|
bodyParser: {
|
||||||
|
Reference in New Issue
Block a user