anthropic: fix KV cache reuse degraded by tool call argument reordering
Use typed structs for tool call arguments instead of map[string]any to preserve JSON key order, which Go maps do not guarantee.
This commit is contained in:
@@ -283,7 +283,7 @@ func (w *WebSearchAnthropicWriter) runWebSearchLoop(ctx context.Context, initial
|
||||
Type: "server_tool_use",
|
||||
ID: toolUseID,
|
||||
Name: "web_search",
|
||||
Input: map[string]any{"query": query},
|
||||
Input: queryArgs(query),
|
||||
},
|
||||
anthropic.ContentBlock{
|
||||
Type: "web_search_tool_result",
|
||||
@@ -348,7 +348,7 @@ func (w *WebSearchAnthropicWriter) runWebSearchLoop(ctx context.Context, initial
|
||||
Type: "server_tool_use",
|
||||
ID: maxLoopToolUseID,
|
||||
Name: "web_search",
|
||||
Input: map[string]any{"query": maxLoopQuery},
|
||||
Input: queryArgs(maxLoopQuery),
|
||||
},
|
||||
anthropic.ContentBlock{
|
||||
Type: "web_search_tool_result",
|
||||
@@ -786,7 +786,7 @@ func (w *WebSearchAnthropicWriter) webSearchErrorResponse(errorCode, query strin
|
||||
Type: "server_tool_use",
|
||||
ID: toolUseID,
|
||||
Name: "web_search",
|
||||
Input: map[string]any{"query": query},
|
||||
Input: queryArgs(query),
|
||||
},
|
||||
{
|
||||
Type: "web_search_tool_result",
|
||||
@@ -942,6 +942,13 @@ func writeSSE(w http.ResponseWriter, eventType string, data any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// queryArgs creates a ToolCallFunctionArguments with a single "query" key.
|
||||
func queryArgs(query string) api.ToolCallFunctionArguments {
|
||||
args := api.NewToolCallFunctionArguments()
|
||||
args.Set("query", query)
|
||||
return args
|
||||
}
|
||||
|
||||
// serverToolUseID derives a server tool use ID from a message ID
|
||||
func serverToolUseID(messageID string) string {
|
||||
return "srvtoolu_" + strings.TrimPrefix(messageID, "msg_")
|
||||
|
||||
@@ -1208,7 +1208,7 @@ func TestWebSearchStreamResponse(t *testing.T) {
|
||||
Type: "server_tool_use",
|
||||
ID: "srvtoolu_test123",
|
||||
Name: "web_search",
|
||||
Input: map[string]any{"query": "test query"},
|
||||
Input: queryArgs("test query"),
|
||||
},
|
||||
{
|
||||
Type: "web_search_tool_result",
|
||||
@@ -1413,12 +1413,8 @@ func TestWebSearchSendError_NonStreaming(t *testing.T) {
|
||||
t.Errorf("expected name 'web_search', got %q", result.Content[0].Name)
|
||||
}
|
||||
// Verify input contains the query
|
||||
inputMap, ok := result.Content[0].Input.(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected Input to be map, got %T", result.Content[0].Input)
|
||||
}
|
||||
if inputMap["query"] != "test query" {
|
||||
t.Errorf("expected query 'test query', got %v", inputMap["query"])
|
||||
if q, ok := result.Content[0].Input.Get("query"); !ok || q != "test query" {
|
||||
t.Errorf("expected query 'test query', got %v", q)
|
||||
}
|
||||
|
||||
// Block 1: web_search_tool_result with error
|
||||
@@ -1561,12 +1557,8 @@ func TestWebSearchSendError_EmptyQuery(t *testing.T) {
|
||||
}
|
||||
|
||||
// Verify the input has empty query
|
||||
inputMap, ok := result.Content[0].Input.(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected Input to be map, got %T", result.Content[0].Input)
|
||||
}
|
||||
if inputMap["query"] != "" {
|
||||
t.Errorf("expected empty query, got %v", inputMap["query"])
|
||||
if q, ok := result.Content[0].Input.Get("query"); !ok || q != "" {
|
||||
t.Errorf("expected empty query, got %v", q)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user