mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
4.8.10 test (#2573)
* feat: more debug response * fix: debug edge status * perf: doc * fix: workflow edge check * perf: i18n * package.json * perf: markdown mask
This commit is contained in:
@@ -80,3 +80,4 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \
|
||||
31. 修复 - 全局变量在 API 中无法持久化。
|
||||
32. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)
|
||||
33. 修复 - 知识库标签重复加载。
|
||||
34. 修复 - Debug 模式下,循环调用边问题。
|
||||
|
@@ -96,6 +96,8 @@ export type DispatchNodeResponseType = {
|
||||
error?: Record<string, any>;
|
||||
customInputs?: Record<string, any>;
|
||||
customOutputs?: Record<string, any>;
|
||||
nodeInputs?: Record<string, any>;
|
||||
nodeOutputs?: Record<string, any>;
|
||||
|
||||
// bill
|
||||
tokens?: number;
|
||||
|
@@ -254,13 +254,15 @@ export const runToolWithFunctionCall = async (
|
||||
// console.log(tokens, 'tool');
|
||||
|
||||
// Run tool status
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.flowNodeStatus,
|
||||
data: {
|
||||
status: 'running',
|
||||
name: node.name
|
||||
}
|
||||
});
|
||||
if (node.showStatus) {
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.flowNodeStatus,
|
||||
data: {
|
||||
status: 'running',
|
||||
name: node.name
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// tool assistant
|
||||
const toolAssistants = toolsRunResponse
|
||||
|
@@ -258,13 +258,15 @@ export const runToolWithPromptCall = async (
|
||||
})();
|
||||
|
||||
// Run tool status
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.flowNodeStatus,
|
||||
data: {
|
||||
status: 'running',
|
||||
name: node.name
|
||||
}
|
||||
});
|
||||
if (node.showStatus) {
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.flowNodeStatus,
|
||||
data: {
|
||||
status: 'running',
|
||||
name: node.name
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 合并工具调用的结果,使用 functionCall 格式存储。
|
||||
const assistantToolMsgParams: ChatCompletionAssistantMessageParam = {
|
||||
|
@@ -265,13 +265,15 @@ export const runToolWithToolChoice = async (
|
||||
// console.log(tokens, 'tool');
|
||||
|
||||
// Run tool status
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.flowNodeStatus,
|
||||
data: {
|
||||
status: 'running',
|
||||
name: node.name
|
||||
}
|
||||
});
|
||||
if (node.showStatus) {
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.flowNodeStatus,
|
||||
data: {
|
||||
status: 'running',
|
||||
name: node.name
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// tool assistant
|
||||
const toolAssistants = toolsRunResponse
|
||||
|
@@ -310,9 +310,11 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
|
||||
const flat = result.flat().filter(Boolean) as unknown as {
|
||||
node: RuntimeNodeItemType;
|
||||
runStatus: 'run' | 'skip';
|
||||
result: Record<string, any>;
|
||||
}[];
|
||||
if (flat.length === 0) return;
|
||||
// If there are no running nodes, the workflow is complete
|
||||
if (!flat.some((item) => item.runStatus === 'run')) return;
|
||||
|
||||
// Update the node output at the end of the run and get the next nodes
|
||||
const nextNodes = flat.map((item) => nodeOutput(item.node, item.result)).flat();
|
||||
@@ -454,6 +456,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
|
||||
return {
|
||||
node,
|
||||
runStatus: 'run',
|
||||
result: {
|
||||
...dispatchRes,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: formatResponseData
|
||||
@@ -467,6 +470,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
|
||||
return {
|
||||
node,
|
||||
runStatus: 'skip',
|
||||
result: {
|
||||
[DispatchNodeResponseKeyEnum.skipHandleId]: targetEdges.map((item) => item.sourceHandle)
|
||||
}
|
||||
|
@@ -1,14 +1,18 @@
|
||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import type {
|
||||
DispatchNodeResultType,
|
||||
ModuleDispatchProps
|
||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||
|
||||
export type UserChatInputProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.userChatInput]: string;
|
||||
}>;
|
||||
type Response = {
|
||||
type Response = DispatchNodeResultType<{
|
||||
[NodeOutputKeyEnum.userChatInput]: string;
|
||||
[NodeOutputKeyEnum.userFiles]: string[];
|
||||
};
|
||||
}>;
|
||||
|
||||
export const dispatchWorkflowStart = (props: Record<string, any>): Response => {
|
||||
const {
|
||||
@@ -19,6 +23,7 @@ export const dispatchWorkflowStart = (props: Record<string, any>): Response => {
|
||||
const { text, files } = chatValue2RuntimePrompt(query);
|
||||
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {},
|
||||
[NodeInputKeyEnum.userChatInput]: text || userChatInput,
|
||||
[NodeOutputKeyEnum.userFiles]: files
|
||||
.map((item) => {
|
||||
|
@@ -13,7 +13,6 @@ import {
|
||||
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { getElseIFLabel, getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.condition]: IfElseConditionType;
|
||||
|
@@ -5,14 +5,9 @@ import { Box, Image, ImageProps } from '@chakra-ui/react';
|
||||
import { useSystem } from '../../../hooks/useSystem';
|
||||
import Loading from '../MyLoading';
|
||||
|
||||
const MyPhotoView = ({
|
||||
forbidImgPreview,
|
||||
...props
|
||||
}: ImageProps & { forbidImgPreview?: boolean }) => {
|
||||
const MyPhotoView = ({ ...props }: ImageProps) => {
|
||||
const { isPc } = useSystem();
|
||||
return forbidImgPreview ? (
|
||||
<Image {...props} />
|
||||
) : (
|
||||
return (
|
||||
<PhotoProvider
|
||||
maskOpacity={0.6}
|
||||
bannerVisible={!isPc}
|
||||
|
@@ -30,6 +30,9 @@
|
||||
"plugins_output": "Plugin output",
|
||||
"question_tip": "From left to right, the response order of each module",
|
||||
"rearrangement": "Search results rearranged",
|
||||
"response": {
|
||||
"node_inputs": "Node input"
|
||||
},
|
||||
"select_file": "Select file",
|
||||
"select_img": "Select images",
|
||||
"stream_output": "stream output",
|
||||
|
@@ -1105,7 +1105,7 @@
|
||||
"navbar": {
|
||||
"Account": "Account",
|
||||
"Chat": "Chat",
|
||||
"Datasets": "Knowledge Base",
|
||||
"Datasets": "Knowledge",
|
||||
"Studio": "Studio",
|
||||
"Tools": "Tools"
|
||||
},
|
||||
@@ -1133,7 +1133,7 @@
|
||||
"upgrade": "If the package you purchased is higher than the current package, the package will take effect immediately, and the current package will take effect later. \nYou can check the package usage in Account—Personal Information—Package Details."
|
||||
},
|
||||
"to_recharge": "Insufficient balance, please recharge",
|
||||
"wechat": "Please scan the QR code on WeChat to pay:",
|
||||
"wechat": "WeChat scan the QR code to pay: ¥{{price}}",
|
||||
"yuan": "Yuan"
|
||||
},
|
||||
"permission": {
|
||||
|
@@ -30,6 +30,9 @@
|
||||
"plugins_output": "插件输出",
|
||||
"question_tip": "从上到下,为各个模块的响应顺序",
|
||||
"rearrangement": "检索结果重排",
|
||||
"response": {
|
||||
"node_inputs": "节点输入"
|
||||
},
|
||||
"select_file": "选择文件",
|
||||
"select_img": "选择图片",
|
||||
"stream_output": "流输出",
|
||||
|
@@ -1128,12 +1128,12 @@
|
||||
"old_package_price": "旧套餐余额",
|
||||
"other": "其他金额,请取整数",
|
||||
"package_tip": {
|
||||
"buy": "您购买的套餐等级低于当前套餐,该套餐将在当前套餐过期后生效。您可在账号—个人信息—套餐详情里,查看套餐使用情况。",
|
||||
"buy": "您购买的套餐等级低于当前套餐,该套餐将在当前套餐过期后生效。\n您可在账号—个人信息—套餐详情里,查看套餐使用情况。",
|
||||
"renewal": "您正在续费套餐。您可在账号—个人信息—套餐详情里,查看套餐使用情况。",
|
||||
"upgrade": "您购买的套餐等级高于当前套餐,该套餐将即刻生效,当前套餐将延后生效。您可在账号—个人信息—套餐详情里,查看套餐使用情况。"
|
||||
"upgrade": "您购买的套餐等级高于当前套餐,该套餐将即刻生效,当前套餐将延后生效。\n您可在账号—个人信息—套餐详情里,查看套餐使用情况。"
|
||||
},
|
||||
"to_recharge": "余额不足,去充值",
|
||||
"wechat": "请微信扫码支付: {{price}}元\n请勿关闭页面",
|
||||
"wechat": "微信扫码支付: {{price}}元\n请勿关闭页面",
|
||||
"yuan": "{{amount}}元"
|
||||
},
|
||||
"permission": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "4.8.9",
|
||||
"version": "4.8.10",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useLoading } from '@fastgpt/web/hooks/useLoading';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { throttle } from 'lodash';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { getUnreadCount } from '@/web/support/user/inform/api';
|
||||
|
@@ -3,7 +3,7 @@ import { Box, ImageProps, Skeleton } from '@chakra-ui/react';
|
||||
import MyPhotoView from '@fastgpt/web/components/common/Image/PhotoView';
|
||||
import { useBoolean } from 'ahooks';
|
||||
|
||||
const MdImage = ({ src, ...props }: { src?: string; forbidImgPreview?: boolean } & ImageProps) => {
|
||||
const MdImage = ({ src, ...props }: { src?: string } & ImageProps) => {
|
||||
const [isLoaded, { setTrue }] = useBoolean(false);
|
||||
|
||||
const [renderSrc, setRenderSrc] = useState(src);
|
||||
|
@@ -10,7 +10,7 @@ import RehypeExternalLinks from 'rehype-external-links';
|
||||
import styles from './index.module.scss';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import { Link, Button } from '@chakra-ui/react';
|
||||
import { Link, Button, Box } from '@chakra-ui/react';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
@@ -29,21 +29,21 @@ const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false
|
||||
const Markdown = ({
|
||||
source = '',
|
||||
showAnimation = false,
|
||||
forbidImgPreview = false
|
||||
isDisabled = false
|
||||
}: {
|
||||
source?: string;
|
||||
showAnimation?: boolean;
|
||||
forbidImgPreview?: boolean;
|
||||
isDisabled?: boolean;
|
||||
}) => {
|
||||
const components = useMemo<any>(
|
||||
() => ({
|
||||
img: (props: any) => <Image {...props} forbidImgPreview={forbidImgPreview} />,
|
||||
img: Image,
|
||||
pre: RewritePre,
|
||||
p: (pProps: any) => <p {...pProps} dir="auto" />,
|
||||
code: Code,
|
||||
a: A
|
||||
}),
|
||||
[forbidImgPreview]
|
||||
[]
|
||||
);
|
||||
|
||||
const formatSource = useMemo(() => {
|
||||
@@ -59,17 +59,20 @@ const Markdown = ({
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
className={`markdown ${styles.markdown}
|
||||
<Box position={'relative'}>
|
||||
<ReactMarkdown
|
||||
className={`markdown ${styles.markdown}
|
||||
${showAnimation ? `${formatSource ? styles.waitingAnimation : styles.animation}` : ''}
|
||||
`}
|
||||
remarkPlugins={[RemarkMath, [RemarkGfm, { singleTilde: false }], RemarkBreaks]}
|
||||
rehypePlugins={[RehypeKatex, [RehypeExternalLinks, { target: '_blank' }]]}
|
||||
components={components}
|
||||
urlTransform={urlTransform}
|
||||
>
|
||||
{formatSource}
|
||||
</ReactMarkdown>
|
||||
remarkPlugins={[RemarkMath, [RemarkGfm, { singleTilde: false }], RemarkBreaks]}
|
||||
rehypePlugins={[RehypeKatex, [RehypeExternalLinks, { target: '_blank' }]]}
|
||||
components={components}
|
||||
urlTransform={urlTransform}
|
||||
>
|
||||
{formatSource}
|
||||
</ReactMarkdown>
|
||||
{isDisabled && <Box position={'absolute'} top={0} right={0} left={0} bottom={0} />}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -107,8 +110,8 @@ function Code(e: any) {
|
||||
return Component;
|
||||
}
|
||||
|
||||
function Image({ src, forbidImgPreview }: { forbidImgPreview: boolean; src?: string }) {
|
||||
return <MdImage forbidImgPreview={forbidImgPreview} src={src} />;
|
||||
function Image({ src }: { src?: string }) {
|
||||
return <MdImage src={src} />;
|
||||
}
|
||||
|
||||
function A({ children, ...props }: any) {
|
||||
|
@@ -326,7 +326,8 @@ export const WholeResponseContent = ({
|
||||
label={t('common:core.chat.response.context total length')}
|
||||
value={activeModule?.contextTotalLen}
|
||||
/>
|
||||
<Row label={workflowT('response.Error')} value={activeModule?.error} />
|
||||
<Row label={t('workflow:response.Error')} value={activeModule?.error} />
|
||||
<Row label={t('chat:response.node_inputs')} value={activeModule?.nodeInputs} />
|
||||
</>
|
||||
{/* ai chat */}
|
||||
<>
|
||||
|
@@ -75,7 +75,7 @@ const QRCodePayModal = ({
|
||||
<MyModal isOpen title={t('common:user.Pay')} iconSrc="/imgs/modal/pay.svg">
|
||||
<ModalBody textAlign={'center'} py={6} whiteSpace={'pre'}>
|
||||
{tip && (
|
||||
<Box fontSize={'sm'} whiteSpace={'normal'} mb={3}>
|
||||
<Box fontSize={'sm'} whiteSpace={'pre'} mb={3}>
|
||||
{tip}
|
||||
</Box>
|
||||
)}
|
||||
|
@@ -578,7 +578,7 @@ const NodeDebugResponse = React.memo(function NodeDebugResponse({
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
{/* result */}
|
||||
{/* Result card */}
|
||||
{debugResult.showResult && (
|
||||
<Card
|
||||
className="nowheel"
|
||||
@@ -587,12 +587,11 @@ const NodeDebugResponse = React.memo(function NodeDebugResponse({
|
||||
top={0}
|
||||
zIndex={10}
|
||||
w={'420px'}
|
||||
minH={'300px'}
|
||||
maxH={'100%'}
|
||||
maxH={'max(100%,500px)'}
|
||||
border={'base'}
|
||||
>
|
||||
{/* Status header */}
|
||||
<Flex h={'54x'} px={4} mb={1} py={3} alignItems={'center'} borderBottom={'base'}>
|
||||
<Flex h={'54x'} px={4} 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')}
|
||||
@@ -627,18 +626,20 @@ const NodeDebugResponse = React.memo(function NodeDebugResponse({
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
{/* Show result */}
|
||||
<Box overflowY={'auto'}>
|
||||
{!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} showDetail />}
|
||||
</Box>
|
||||
{/* 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} showDetail />}
|
||||
</Box>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
<ConfirmModal />
|
||||
|
@@ -692,19 +692,7 @@ const WorkflowContextProvider = ({
|
||||
const newStoreDebugData = {
|
||||
runtimeNodes: finishedNodes,
|
||||
// edges need to save status
|
||||
runtimeEdges: finishedEdges.map((edge) => {
|
||||
const oldEdge = debugData.runtimeEdges.find(
|
||||
(item) => item.source === edge.source && item.target === edge.target
|
||||
);
|
||||
const status =
|
||||
oldEdge?.status && oldEdge.status !== RuntimeEdgeStatusEnum.waiting
|
||||
? oldEdge.status
|
||||
: edge.status;
|
||||
return {
|
||||
...edge,
|
||||
status
|
||||
};
|
||||
}),
|
||||
runtimeEdges: finishedEdges,
|
||||
nextRunNodes: nextStepRunNodes,
|
||||
variables: newVariables
|
||||
};
|
||||
|
@@ -227,7 +227,8 @@ const DataCard = () => {
|
||||
'& .forbid-switch': { display: 'flex' },
|
||||
bg: index % 2 === 1 ? 'myGray.200' : 'blue.100'
|
||||
}}
|
||||
onClick={() => {
|
||||
onClickCapture={(e) => {
|
||||
e.stopPropagation();
|
||||
if (!collection) return;
|
||||
setEditDataId(item._id);
|
||||
}}
|
||||
@@ -264,11 +265,11 @@ const DataCard = () => {
|
||||
|
||||
{/* Data content */}
|
||||
<Box wordBreak={'break-all'} fontSize={'sm'}>
|
||||
<Markdown source={item.q} forbidImgPreview />
|
||||
<Markdown source={item.q} isDisabled />
|
||||
{!!item.a && (
|
||||
<>
|
||||
<MyDivider />
|
||||
<Markdown source={item.a} forbidImgPreview />
|
||||
<Markdown source={item.a} isDisabled />
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
Reference in New Issue
Block a user