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:
Archer
2024-08-29 23:19:39 +08:00
committed by GitHub
parent 813eaacfd0
commit 6d00f73e91
22 changed files with 103 additions and 92 deletions

View File

@@ -80,3 +80,4 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \
31. 修复 - 全局变量在 API 中无法持久化。
32. 修复 - OpenAPIdetail=false模式下不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)
33. 修复 - 知识库标签重复加载。
34. 修复 - Debug 模式下,循环调用边问题。

View File

@@ -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;

View File

@@ -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

View File

@@ -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 = {

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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) => {

View File

@@ -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;

View File

@@ -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}

View File

@@ -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",

View File

@@ -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": {

View File

@@ -30,6 +30,9 @@
"plugins_output": "插件输出",
"question_tip": "从上到下,为各个模块的响应顺序",
"rearrangement": "检索结果重排",
"response": {
"node_inputs": "节点输入"
},
"select_file": "选择文件",
"select_img": "选择图片",
"stream_output": "流输出",

View File

@@ -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": {

View File

@@ -1,6 +1,6 @@
{
"name": "app",
"version": "4.8.9",
"version": "4.8.10",
"private": false,
"scripts": {
"dev": "next dev",

View File

@@ -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';

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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 */}
<>

View File

@@ -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>
)}

View File

@@ -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 />

View File

@@ -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
};

View File

@@ -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>