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';
import
{
cloneDeep
}
from
'lodash-es'
;
import
type
{
ChatAuthTargetInput
}
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
}
>
;
...
...
@@ -805,12 +806,8 @@ export const useChatGenerate = ({
const
responseData
=
mergeNodeResponseDataByIdAndParent
(
item
.
responseData
||
[]);
if
(
!
abortSignal
?.
signal
?.
aborted
)
{
const
uncaughtErr
=
responseData
.
find
((
r
)
=>
r
.
error
&&
!
r
.
errorCaptured
)?.
error
;
const
lastUncapturedErrorText
=
[...
responseData
]
.
reverse
()
.
find
((
r
)
=>
r
.
errorText
&&
!
r
.
errorCaptured
)?.
errorText
;
const
err
=
uncaughtErr
??
lastUncapturedErrorText
;
const
errorMsg
=
err
?
t
(
getErrText
(
err
))
:
undefined
;
const
errorText
=
getChatItemErrorText
(
responseData
)?.
errorText
;
const
errorMsg
=
errorText
?
t
(
errorText
)
:
undefined
;
return
{
...
item
,
...
...
projects/app/src/components/core/chat/components/WholeResponseModal/ResponseRows.tsx
View file @
b70c57fa
...
...
@@ -427,7 +427,11 @@ export const WorkflowResultRows = ({
/>
)
}
<
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 = ({
value
,
rawDom
,
rawDomBoxProps
,
contentBoxProps
contentBoxProps
,
renderStringAsMarkdown
=
true
}:
{
label
:
string
;
value
?:
string
|
number
|
boolean
|
object
;
rawDom
?:
ReactNode
;
rawDomBoxProps
?:
BoxProps
;
contentBoxProps
?:
BoxProps
;
renderStringAsMarkdown
?:
boolean
;
})
=>
{
const
{
t
}
=
useSafeTranslation
();
const
val
=
value
||
rawDom
;
...
...
@@ -63,10 +65,10 @@ export const Row = ({
return
`~~~json\n
${
JSON
.
stringify
(
value
,
null
,
2
)}
\n~~~`
;
}
if
(
typeof
value
===
'string'
)
{
return
t
(
value
)
;
return
renderStringAsMarkdown
?
t
(
value
)
:
value
;
}
return
`
${
value
}
`
;
},
[
isObject
,
t
,
value
]);
},
[
isObject
,
renderStringAsMarkdown
,
t
,
value
]);
if
(
rawDom
)
{
return
(
...
...
@@ -99,7 +101,13 @@ export const Row = ({
...
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
>
</
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';
import
{
moduleTemplatesFlat
}
from
'@fastgpt/global/core/workflow/template/constants'
;
import
Avatar
from
'@fastgpt/web/components/common/Avatar'
;
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
type
{
SideTabItemType
}
from
'./types'
;
...
...
@@ -38,6 +39,7 @@ const NormalSideTabItem = ({
})
=>
{
const
{
t
}
=
useSafeTranslation
();
const
leftPad
=
getSideTabLeftPadding
(
index
);
const
nodeName
=
t
(
sideBarItem
.
moduleName
as
any
,
sideBarItem
.
moduleNameArgs
);
return
(
<
Flex
...
...
@@ -68,18 +70,20 @@ const NormalSideTabItem = ({
borderRadius=
{
'4px'
}
/>
<
Box
ml=
{
2
}
flex=
{
'1 1 0'
}
minW=
{
0
}
>
<
Box
fontSize=
{
'12px'
}
lineHeight=
{
'16px'
}
fontWeight=
{
500
}
color=
{
'myGray.900'
}
letterSpacing=
{
'0.5px'
}
overflow=
{
'hidden'
}
whiteSpace=
{
'nowrap'
}
textOverflow=
{
'ellipsis'
}
>
{
t
(
sideBarItem
.
moduleName
as
any
,
sideBarItem
.
moduleNameArgs
)
}
</
Box
>
<
MyTooltip
label=
{
nodeName
}
showOnlyWhenOverflow
>
<
Box
fontSize=
{
'12px'
}
lineHeight=
{
'16px'
}
fontWeight=
{
500
}
color=
{
'myGray.900'
}
letterSpacing=
{
'0.5px'
}
overflow=
{
'hidden'
}
whiteSpace=
{
'nowrap'
}
textOverflow=
{
'ellipsis'
}
>
{
nodeName
}
</
Box
>
</
MyTooltip
>
<
Box
fontSize=
{
'11px'
}
lineHeight=
{
'16px'
}
...
...
projects/app/src/global/core/chat/utils.ts
View file @
b70c57fa
...
...
@@ -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 中提取标签和错误摘要。
*
* 不包含 `historyPreview` 和 `error`:前者只用于详情弹窗,后者可能是大对象;列表错误展示优先
...
...
@@ -129,8 +150,7 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemMiniType) {
llmModuleAccount
,
historyPreviewLength
,
totalQuoteList
,
toolCiteLinks
,
errorText
toolCiteLinks
}
=
flatResData
.
reduce
(
(
acc
,
item
)
=>
{
// LLM
...
...
@@ -164,14 +184,6 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemMiniType) {
}
}
const
nodeErrorText
=
getNodeErrorText
(
item
);
if
(
nodeErrorText
)
{
acc
.
errorText
=
{
moduleName
:
item
.
moduleName
,
errorText
:
nodeErrorText
};
}
return
acc
;
},
{
...
...
@@ -179,11 +191,11 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemMiniType) {
totalQuoteList
:
[]
as
SearchDataResponseQuoteListItemType
[],
toolCiteLinks
:
[]
as
ToolCiteLinksType
[],
linkDedupe
:
new
Set
<
string
>
(),
errorText
:
undefined
as
ErrorTextItemType
|
undefined
,
llmModuleAccount
:
0
,
historyPreviewLength
:
undefined
as
number
|
undefined
}
);
const
errorText
=
getChatItemErrorText
(
historyItem
.
responseData
);
// Filter quote list to only include citations actually referenced in the response text
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 {
SANDBOX_SHELL_TOOL_NAME
}
from
'@fastgpt/global/core/ai/sandbox/tools'
;
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'
,
()
=>
{
it
(
'marks sandbox usage from streaming tool call cards before responseData is loaded'
,
()
=>
{
...
...
@@ -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'
,
()
=>
{
const
historyItem
:
ChatItemMiniType
=
{
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