mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(chatd): keep provider-executed tool results in assistant content (#22991)
## Problem Anthropic's API returns a 400 error when `web_search` tool results are missing: ``` web_search tool use with id srvtoolu_... was found without a corresponding web_search_tool_result block ``` **Root cause:** `persistStep` in `chatd.go` splits ALL `ToolResultContent` blocks into separate tool-role DB rows. Provider-executed (PE) tool results like `web_search` must stay in the assistant message — Anthropic expects `server_tool_use` and `web_search_tool_result` in the same turn. The previous fix (#22976) added repair passes to drop PE results during reconstruction, which fixed cross-step orphans but broke the normal case (PE result correctly in the same step). ## Fix Three changes that address the root cause: 1. **`persistStep` (chatd.go):** Check `ProviderExecuted` before splitting `ToolResultContent` into tool rows. PE results stay in `assistantBlocks` and are stored in the assistant content column. 2. **`ToMessageParts` (chatprompt.go):** Propagate the `ProviderExecuted` field to `ToolResultPart` so the fantasy Anthropic provider can identify PE results and reconstruct the `web_search_tool_result` block. 3. **Keep existing repair passes** for backward compatibility with legacy DB data where PE results were incorrectly persisted as separate tool messages. ## Tests - `TestProviderExecutedResultInAssistantContent` — PE result stored inline in assistant content round-trips correctly with `ProviderExecuted` preserved. - `TestProviderExecutedResult_LegacyToolRow` — legacy PE results in tool-role rows are still dropped correctly. - All existing tests pass (including the 3 PE tests from #22976).
This commit is contained in:
+12
-5
@@ -2298,17 +2298,24 @@ func (p *Server) runChat(
|
||||
|
||||
// Split the step content into assistant blocks and tool
|
||||
// result blocks so they can be stored as separate messages
|
||||
// with the appropriate roles.
|
||||
// with the appropriate roles. Provider-executed tool results
|
||||
// (e.g. web_search) stay in the assistant content because
|
||||
// the LLM provider expects them inline in the assistant
|
||||
// turn, not as separate tool messages.
|
||||
var assistantBlocks []fantasy.Content
|
||||
var toolResults []fantasy.ToolResultContent
|
||||
for _, block := range step.Content {
|
||||
if tr, ok := fantasy.AsContentType[fantasy.ToolResultContent](block); ok {
|
||||
toolResults = append(toolResults, tr)
|
||||
continue
|
||||
if !tr.ProviderExecuted {
|
||||
toolResults = append(toolResults, tr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if trPtr, ok := fantasy.AsContentType[*fantasy.ToolResultContent](block); ok && trPtr != nil {
|
||||
toolResults = append(toolResults, *trPtr)
|
||||
continue
|
||||
if !trPtr.ProviderExecuted {
|
||||
toolResults = append(toolResults, *trPtr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
assistantBlocks = append(assistantBlocks, block)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user