Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
赵月辉
/
fastgpt-migrated
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
b70c57fa
authored
Jul 21, 2026
by
Archer
Committed by
GitHub
Jul 21, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chatbox tip (#7341)
parent
6c12f1ab
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
138 additions
and
36 deletions
+138
-36
pro
+1
-1
projects/app/src/components/core/chat/ChatContainer/ChatBox/hooks/useChatGenerate.ts
+3
-6
projects/app/src/components/core/chat/components/WholeResponseModal/ResponseRows.tsx
+5
-1
projects/app/src/components/core/chat/components/WholeResponseModal/Row.tsx
+12
-4
projects/app/src/components/core/chat/components/WholeResponseModal/SideTab.tsx
+16
-12
projects/app/src/global/core/chat/utils.ts
+23
-11
projects/app/test/global/core/chat/utils.test.ts
+78
-1
No files found.
pro
@
d0c7829e
Subproject commit
e7951abea8762970704556d99bbd5697da95f11c
Subproject commit
d0c7829e79ec8040d92537970e22b0d6feb43427
projects/app/src/components/core/chat/ChatContainer/ChatBox/hooks/useChatGenerate.ts
View file @
b70c57fa
...
@@ -47,6 +47,7 @@ import type { StartChatFnProps, generatingMessageProps } from '../../type';
...
@@ -47,6 +47,7 @@ import type { StartChatFnProps, generatingMessageProps } from '../../type';
import
{
cloneDeep
}
from
'lodash-es'
;
import
{
cloneDeep
}
from
'lodash-es'
;
import
type
{
ChatAuthTargetInput
}
from
'@/web/core/chat/utils'
;
import
type
{
ChatAuthTargetInput
}
from
'@/web/core/chat/utils'
;
import
{
useChatAuthApiTarget
}
from
'@/web/core/chat/utils'
;
import
{
useChatAuthApiTarget
}
from
'@/web/core/chat/utils'
;
import
{
getChatItemErrorText
}
from
'@/global/core/chat/utils'
;
type
HumanChatSiteItemType
=
Extract
<
ChatSiteItemType
,
{
obj
:
ChatRoleEnum
.
Human
}
>
;
type
HumanChatSiteItemType
=
Extract
<
ChatSiteItemType
,
{
obj
:
ChatRoleEnum
.
Human
}
>
;
...
@@ -805,12 +806,8 @@ export const useChatGenerate = ({
...
@@ -805,12 +806,8 @@ export const useChatGenerate = ({
const
responseData
=
mergeNodeResponseDataByIdAndParent
(
item
.
responseData
||
[]);
const
responseData
=
mergeNodeResponseDataByIdAndParent
(
item
.
responseData
||
[]);
if
(
!
abortSignal
?.
signal
?.
aborted
)
{
if
(
!
abortSignal
?.
signal
?.
aborted
)
{
const
uncaughtErr
=
responseData
.
find
((
r
)
=>
r
.
error
&&
!
r
.
errorCaptured
)?.
error
;
const
errorText
=
getChatItemErrorText
(
responseData
)?.
errorText
;
const
lastUncapturedErrorText
=
[...
responseData
]
const
errorMsg
=
errorText
?
t
(
errorText
)
:
undefined
;
.
reverse
()
.
find
((
r
)
=>
r
.
errorText
&&
!
r
.
errorCaptured
)?.
errorText
;
const
err
=
uncaughtErr
??
lastUncapturedErrorText
;
const
errorMsg
=
err
?
t
(
getErrText
(
err
))
:
undefined
;
return
{
return
{
...
item
,
...
item
,
...
...
projects/app/src/components/core/chat/components/WholeResponseModal/ResponseRows.tsx
View file @
b70c57fa
...
@@ -427,7 +427,11 @@ export const WorkflowResultRows = ({
...
@@ -427,7 +427,11 @@ export const WorkflowResultRows = ({
/>
/>
)
}
)
}
<
Row
label=
{
t
(
'chat:response.tool_params_result'
)
}
value=
{
activeModule
.
toolParamsResult
}
/>
<
Row
label=
{
t
(
'chat:response.tool_params_result'
)
}
value=
{
activeModule
.
toolParamsResult
}
/>
<
Row
label=
{
t
(
'chat:response.tool_result'
)
}
value=
{
activeModule
.
toolRes
}
/>
<
Row
label=
{
t
(
'chat:response.tool_result'
)
}
value=
{
activeModule
.
toolRes
}
renderStringAsMarkdown=
{
false
}
/>
</>
</>
);
);
};
};
projects/app/src/components/core/chat/components/WholeResponseModal/Row.tsx
View file @
b70c57fa
...
@@ -46,13 +46,15 @@ export const Row = ({
...
@@ -46,13 +46,15 @@ export const Row = ({
value
,
value
,
rawDom
,
rawDom
,
rawDomBoxProps
,
rawDomBoxProps
,
contentBoxProps
contentBoxProps
,
renderStringAsMarkdown
=
true
}:
{
}:
{
label
:
string
;
label
:
string
;
value
?:
string
|
number
|
boolean
|
object
;
value
?:
string
|
number
|
boolean
|
object
;
rawDom
?:
ReactNode
;
rawDom
?:
ReactNode
;
rawDomBoxProps
?:
BoxProps
;
rawDomBoxProps
?:
BoxProps
;
contentBoxProps
?:
BoxProps
;
contentBoxProps
?:
BoxProps
;
renderStringAsMarkdown
?:
boolean
;
})
=>
{
})
=>
{
const
{
t
}
=
useSafeTranslation
();
const
{
t
}
=
useSafeTranslation
();
const
val
=
value
||
rawDom
;
const
val
=
value
||
rawDom
;
...
@@ -63,10 +65,10 @@ export const Row = ({
...
@@ -63,10 +65,10 @@ export const Row = ({
return
`~~~json\n
${
JSON
.
stringify
(
value
,
null
,
2
)}
\n~~~`
;
return
`~~~json\n
${
JSON
.
stringify
(
value
,
null
,
2
)}
\n~~~`
;
}
}
if
(
typeof
value
===
'string'
)
{
if
(
typeof
value
===
'string'
)
{
return
t
(
value
)
;
return
renderStringAsMarkdown
?
t
(
value
)
:
value
;
}
}
return
`
${
value
}
`
;
return
`
${
value
}
`
;
},
[
isObject
,
t
,
value
]);
},
[
isObject
,
renderStringAsMarkdown
,
t
,
value
]);
if
(
rawDom
)
{
if
(
rawDom
)
{
return
(
return
(
...
@@ -99,7 +101,13 @@ export const Row = ({
...
@@ -99,7 +101,13 @@ export const Row = ({
...
contentBoxProps
?.
sx
...
contentBoxProps
?.
sx
}
}
}
}
>
>
<
Markdown
className=
{
markdownStyles
.
markdown
}
source=
{
formatValue
}
/>
{
typeof
value
===
'string'
&&
!
renderStringAsMarkdown
?
(
<
Box
whiteSpace=
{
'pre-wrap'
}
overflowWrap=
{
'anywhere'
}
>
{
formatValue
}
</
Box
>
)
:
(
<
Markdown
className=
{
markdownStyles
.
markdown
}
source=
{
formatValue
}
/>
)
}
</
Box
>
</
Box
>
</
RowRender
>
</
RowRender
>
);
);
...
...
projects/app/src/components/core/chat/components/WholeResponseModal/SideTab.tsx
View file @
b70c57fa
...
@@ -3,6 +3,7 @@ import { Box, Flex, useDisclosure } from '@chakra-ui/react';
...
@@ -3,6 +3,7 @@ import { Box, Flex, useDisclosure } from '@chakra-ui/react';
import
{
moduleTemplatesFlat
}
from
'@fastgpt/global/core/workflow/template/constants'
;
import
{
moduleTemplatesFlat
}
from
'@fastgpt/global/core/workflow/template/constants'
;
import
Avatar
from
'@fastgpt/web/components/common/Avatar'
;
import
Avatar
from
'@fastgpt/web/components/common/Avatar'
;
import
MyIconButton
from
'@fastgpt/web/components/common/Icon/button'
;
import
MyIconButton
from
'@fastgpt/web/components/common/Icon/button'
;
import
MyTooltip
from
'@fastgpt/web/components/common/MyTooltip'
;
import
{
useSafeTranslation
}
from
'@fastgpt/web/hooks/useSafeTranslation'
;
import
{
useSafeTranslation
}
from
'@fastgpt/web/hooks/useSafeTranslation'
;
import
type
{
SideTabItemType
}
from
'./types'
;
import
type
{
SideTabItemType
}
from
'./types'
;
...
@@ -38,6 +39,7 @@ const NormalSideTabItem = ({
...
@@ -38,6 +39,7 @@ const NormalSideTabItem = ({
})
=>
{
})
=>
{
const
{
t
}
=
useSafeTranslation
();
const
{
t
}
=
useSafeTranslation
();
const
leftPad
=
getSideTabLeftPadding
(
index
);
const
leftPad
=
getSideTabLeftPadding
(
index
);
const
nodeName
=
t
(
sideBarItem
.
moduleName
as
any
,
sideBarItem
.
moduleNameArgs
);
return
(
return
(
<
Flex
<
Flex
...
@@ -68,18 +70,20 @@ const NormalSideTabItem = ({
...
@@ -68,18 +70,20 @@ const NormalSideTabItem = ({
borderRadius=
{
'4px'
}
borderRadius=
{
'4px'
}
/>
/>
<
Box
ml=
{
2
}
flex=
{
'1 1 0'
}
minW=
{
0
}
>
<
Box
ml=
{
2
}
flex=
{
'1 1 0'
}
minW=
{
0
}
>
<
Box
<
MyTooltip
label=
{
nodeName
}
showOnlyWhenOverflow
>
fontSize=
{
'12px'
}
<
Box
lineHeight=
{
'16px'
}
fontSize=
{
'12px'
}
fontWeight=
{
500
}
lineHeight=
{
'16px'
}
color=
{
'myGray.900'
}
fontWeight=
{
500
}
letterSpacing=
{
'0.5px'
}
color=
{
'myGray.900'
}
overflow=
{
'hidden'
}
letterSpacing=
{
'0.5px'
}
whiteSpace=
{
'nowrap'
}
overflow=
{
'hidden'
}
textOverflow=
{
'ellipsis'
}
whiteSpace=
{
'nowrap'
}
>
textOverflow=
{
'ellipsis'
}
{
t
(
sideBarItem
.
moduleName
as
any
,
sideBarItem
.
moduleNameArgs
)
}
>
</
Box
>
{
nodeName
}
</
Box
>
</
MyTooltip
>
<
Box
<
Box
fontSize=
{
'11px'
}
fontSize=
{
'11px'
}
lineHeight=
{
'16px'
}
lineHeight=
{
'16px'
}
...
...
projects/app/src/global/core/chat/utils.ts
View file @
b70c57fa
...
@@ -53,6 +53,27 @@ const getNodeErrorText = (item: ChatHistoryItemResType) => {
...
@@ -53,6 +53,27 @@ const getNodeErrorText = (item: ChatHistoryItemResType) => {
};
};
/**
/**
* 按历史记录加载口径提取聊天气泡错误。
*
* 只有根节点失败才代表本轮对话失败;带 parentId 的响应和内嵌 children 都属于
* ToolCall/Agent 的工具执行详情,错误会作为工具结果交回上层,不应提升为聊天错误。
*/
export
const
getChatItemErrorText
=
(
responseData
:
ChatHistoryItemResType
[]
=
[]
):
ErrorTextItemType
|
undefined
=>
responseData
.
reduce
<
ErrorTextItemType
|
undefined
>
((
errorText
,
item
)
=>
{
if
(
item
.
parentId
)
return
errorText
;
const
nodeErrorText
=
getNodeErrorText
(
item
);
if
(
!
nodeErrorText
)
return
errorText
;
return
{
moduleName
:
item
.
moduleName
,
errorText
:
nodeErrorText
};
},
undefined
);
/**
* 聊天列表预览只需要从 nodeResponse rows 中提取标签和错误摘要。
* 聊天列表预览只需要从 nodeResponse rows 中提取标签和错误摘要。
*
*
* 不包含 `historyPreview` 和 `error`:前者只用于详情弹窗,后者可能是大对象;列表错误展示优先
* 不包含 `historyPreview` 和 `error`:前者只用于详情弹窗,后者可能是大对象;列表错误展示优先
...
@@ -129,8 +150,7 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemMiniType) {
...
@@ -129,8 +150,7 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemMiniType) {
llmModuleAccount
,
llmModuleAccount
,
historyPreviewLength
,
historyPreviewLength
,
totalQuoteList
,
totalQuoteList
,
toolCiteLinks
,
toolCiteLinks
errorText
}
=
flatResData
.
reduce
(
}
=
flatResData
.
reduce
(
(
acc
,
item
)
=>
{
(
acc
,
item
)
=>
{
// LLM
// LLM
...
@@ -164,14 +184,6 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemMiniType) {
...
@@ -164,14 +184,6 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemMiniType) {
}
}
}
}
const
nodeErrorText
=
getNodeErrorText
(
item
);
if
(
nodeErrorText
)
{
acc
.
errorText
=
{
moduleName
:
item
.
moduleName
,
errorText
:
nodeErrorText
};
}
return
acc
;
return
acc
;
},
},
{
{
...
@@ -179,11 +191,11 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemMiniType) {
...
@@ -179,11 +191,11 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemMiniType) {
totalQuoteList
:
[]
as
SearchDataResponseQuoteListItemType
[],
totalQuoteList
:
[]
as
SearchDataResponseQuoteListItemType
[],
toolCiteLinks
:
[]
as
ToolCiteLinksType
[],
toolCiteLinks
:
[]
as
ToolCiteLinksType
[],
linkDedupe
:
new
Set
<
string
>
(),
linkDedupe
:
new
Set
<
string
>
(),
errorText
:
undefined
as
ErrorTextItemType
|
undefined
,
llmModuleAccount
:
0
,
llmModuleAccount
:
0
,
historyPreviewLength
:
undefined
as
number
|
undefined
historyPreviewLength
:
undefined
as
number
|
undefined
}
}
);
);
const
errorText
=
getChatItemErrorText
(
historyItem
.
responseData
);
// Filter quote list to only include citations actually referenced in the response text
// Filter quote list to only include citations actually referenced in the response text
const
responseText
=
historyItem
.
value
.
map
((
v
)
=>
v
.
text
?.
content
||
''
).
join
(
''
);
const
responseText
=
historyItem
.
value
.
map
((
v
)
=>
v
.
text
?.
content
||
''
).
join
(
''
);
...
...
projects/app/test/global/core/chat/utils.test.ts
View file @
b70c57fa
...
@@ -6,7 +6,7 @@ import {
...
@@ -6,7 +6,7 @@ import {
SANDBOX_SHELL_TOOL_NAME
SANDBOX_SHELL_TOOL_NAME
}
from
'@fastgpt/global/core/ai/sandbox/tools'
;
}
from
'@fastgpt/global/core/ai/sandbox/tools'
;
import
{
FlowNodeTypeEnum
}
from
'@fastgpt/global/core/workflow/node/constant'
;
import
{
FlowNodeTypeEnum
}
from
'@fastgpt/global/core/workflow/node/constant'
;
import
{
addStatisticalDataToHistoryItem
}
from
'@/global/core/chat/utils'
;
import
{
addStatisticalDataToHistoryItem
,
getChatItemErrorText
}
from
'@/global/core/chat/utils'
;
describe
(
'addStatisticalDataToHistoryItem'
,
()
=>
{
describe
(
'addStatisticalDataToHistoryItem'
,
()
=>
{
it
(
'marks sandbox usage from streaming tool call cards before responseData is loaded'
,
()
=>
{
it
(
'marks sandbox usage from streaming tool call cards before responseData is loaded'
,
()
=>
{
...
@@ -364,6 +364,83 @@ describe('addStatisticalDataToHistoryItem', () => {
...
@@ -364,6 +364,83 @@ describe('addStatisticalDataToHistoryItem', () => {
});
});
});
});
it
(
'ignores a failed tool child when later tool calls complete'
,
()
=>
{
const
responseData
:
NonNullable
<
ChatItemMiniType
[
'responseData'
]
>
=
[
{
id
:
'agent-response'
,
nodeId
:
'agent-node'
,
moduleName
:
'Agent'
,
moduleType
:
FlowNodeTypeEnum
.
agent
},
{
id
:
'tool-response-1'
,
parentId
:
'agent-response'
,
nodeId
:
'tool-node-1'
,
moduleName
:
'Tool 1'
,
moduleType
:
FlowNodeTypeEnum
.
tool
,
toolRes
:
'done'
},
{
id
:
'tool-response-2'
,
parentId
:
'agent-response'
,
nodeId
:
'tool-node-2'
,
moduleName
:
'Tool 2'
,
moduleType
:
FlowNodeTypeEnum
.
tool
,
error
:
'tool failed'
},
{
id
:
'tool-response-3'
,
parentId
:
'agent-response'
,
nodeId
:
'tool-node-3'
,
moduleName
:
'Tool 3'
,
moduleType
:
FlowNodeTypeEnum
.
tool
,
toolRes
:
'done'
},
{
id
:
'tool-response-4'
,
parentId
:
'agent-response'
,
nodeId
:
'tool-node-4'
,
moduleName
:
'Tool 4'
,
moduleType
:
FlowNodeTypeEnum
.
tool
,
toolRes
:
'done'
}
];
const
historyItem
:
ChatItemMiniType
=
{
obj
:
ChatRoleEnum
.
AI
,
value
:
[{
text
:
{
content
:
'done'
}
}],
responseData
};
expect
(
getChatItemErrorText
(
responseData
)).
toBeUndefined
();
expect
(
addStatisticalDataToHistoryItem
(
historyItem
).
errorText
).
toBeUndefined
();
});
it
(
'ignores tool errors nested inside an agent response'
,
()
=>
{
const
historyItem
:
ChatItemMiniType
=
{
obj
:
ChatRoleEnum
.
AI
,
value
:
[{
text
:
{
content
:
'done'
}
}],
responseData
:
[
{
id
:
'agent-response'
,
nodeId
:
'agent-node'
,
moduleName
:
'Agent'
,
moduleType
:
FlowNodeTypeEnum
.
agent
,
childrenResponses
:
[
{
id
:
'tool-response'
,
nodeId
:
'tool-node'
,
moduleName
:
'Tool'
,
moduleType
:
FlowNodeTypeEnum
.
tool
,
errorText
:
'tool failed'
}
]
}
]
};
expect
(
addStatisticalDataToHistoryItem
(
historyItem
).
errorText
).
toBeUndefined
();
});
it
(
'does not use HTTP result error as chat bubble error text when node error is absent'
,
()
=>
{
it
(
'does not use HTTP result error as chat bubble error text when node error is absent'
,
()
=>
{
const
historyItem
:
ChatItemMiniType
=
{
const
historyItem
:
ChatItemMiniType
=
{
obj
:
ChatRoleEnum
.
AI
,
obj
:
ChatRoleEnum
.
AI
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment