fix: streamline tool failure feedback
This commit is contained in:
@@ -1276,6 +1276,58 @@ describe('App', () => {
|
||||
expect(screen.getByText('项目:默认项目')).toHaveClass('scope-badge')
|
||||
})
|
||||
|
||||
it('keeps a tool failure in details and hides retry after continuing', async () => {
|
||||
render(<App />)
|
||||
|
||||
fireEvent.change(screen.getByLabelText('向 GoodBuddy 提问'), {
|
||||
target: { value: '读取演示文稿' }
|
||||
})
|
||||
fireEvent.click(await screen.findByLabelText('发送'))
|
||||
await waitFor(() => expect(run).toHaveBeenCalledOnce())
|
||||
const request = run.mock.calls[0]?.[0]
|
||||
if (!request) {
|
||||
throw new Error('Missing request')
|
||||
}
|
||||
const toolError =
|
||||
'Cannot read binary file: D:\\workspace\\presentation.pptx'
|
||||
const runtimeError = `OpenCode 工具执行失败(call-1):${toolError}`
|
||||
|
||||
act(() => {
|
||||
agentListener?.({
|
||||
requestId: request.requestId,
|
||||
type: 'tool',
|
||||
callId: 'call-1',
|
||||
name: 'read',
|
||||
state: 'failed',
|
||||
summary: 'OpenCode 工具:read',
|
||||
input: '{"path":"D:\\\\workspace\\\\presentation.pptx"}',
|
||||
error: toolError
|
||||
})
|
||||
agentListener?.({
|
||||
requestId: request.requestId,
|
||||
type: 'error',
|
||||
status: 'failed',
|
||||
message: runtimeError
|
||||
})
|
||||
})
|
||||
|
||||
expect(screen.getByText(toolError)).toBeInTheDocument()
|
||||
expect(screen.queryByText(runtimeError)).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByRole('button', { name: '重新编辑并发送' })
|
||||
).toBeInTheDocument()
|
||||
|
||||
fireEvent.change(screen.getByLabelText('向 GoodBuddy 提问'), {
|
||||
target: { value: '继续处理' }
|
||||
})
|
||||
fireEvent.click(screen.getByLabelText('发送'))
|
||||
await waitFor(() => expect(run).toHaveBeenCalledTimes(2))
|
||||
|
||||
expect(
|
||||
screen.queryByRole('button', { name: '重新编辑并发送' })
|
||||
).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('submits knowledge scope without eager search or prompt injection and merges runtime references', async () => {
|
||||
const libraryId = '11111111-1111-4111-8111-111111111111'
|
||||
vi.mocked(api.knowledge.getSnapshot).mockResolvedValueOnce({
|
||||
|
||||
@@ -454,6 +454,20 @@ function terminalizeMessageToolBlocks(
|
||||
)
|
||||
}
|
||||
|
||||
function isErrorRepresentedByFailedTool(
|
||||
tools: ToolActivity[] | undefined,
|
||||
errorMessage: string
|
||||
): boolean {
|
||||
return Boolean(
|
||||
tools?.some(
|
||||
(tool) =>
|
||||
tool.state === 'failed' &&
|
||||
((tool.error && errorMessage.includes(tool.error)) ||
|
||||
(tool.callId && errorMessage.includes(tool.callId)))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
type MessageBlockRenderItem =
|
||||
| {
|
||||
kind: 'block'
|
||||
@@ -2687,6 +2701,12 @@ function App(): React.JSX.Element {
|
||||
status: terminalStatus
|
||||
})
|
||||
updateMessage(run.conversationId, run.messageId, (message) => {
|
||||
const representedToolError =
|
||||
event.type === 'error' &&
|
||||
isErrorRepresentedByFailedTool(
|
||||
message.tools,
|
||||
event.message
|
||||
)
|
||||
const toolTerminalState =
|
||||
event.type === 'error'
|
||||
? event.status === 'cancelled'
|
||||
@@ -2694,13 +2714,18 @@ function App(): React.JSX.Element {
|
||||
: ('failed' as const)
|
||||
: undefined
|
||||
const fallbackError =
|
||||
event.type === 'error' && !message.content
|
||||
event.type === 'error' &&
|
||||
!representedToolError &&
|
||||
!message.content
|
||||
? event.message.slice(0, maxMessageContentLength)
|
||||
: ''
|
||||
return {
|
||||
...message,
|
||||
state: event.type === 'error' ? 'error' : 'complete',
|
||||
status: event.type === 'error' ? event.message : undefined,
|
||||
status:
|
||||
event.type === 'error' && !representedToolError
|
||||
? event.message
|
||||
: undefined,
|
||||
approval: undefined,
|
||||
question: undefined,
|
||||
tools: toolTerminalState
|
||||
@@ -5594,7 +5619,9 @@ function App(): React.JSX.Element {
|
||||
{message.status}
|
||||
</div>
|
||||
)}
|
||||
{message.state === 'error' && (
|
||||
{message.state === 'error' &&
|
||||
messageIndex ===
|
||||
activeConversation.messages.length - 1 && (
|
||||
<button
|
||||
className="message-retry"
|
||||
onClick={() => {
|
||||
@@ -5609,7 +5636,7 @@ function App(): React.JSX.Element {
|
||||
>
|
||||
{t('chat.retry')}
|
||||
</button>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user