From aa2eac97410f03ea1d42ba6a789ecd3c2cd330f1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 28 Mar 2026 15:16:59 +0000 Subject: [PATCH 1/2] fix(providers/anthropic): drop empty assistant messages When using the Anthropic API, it expects `tool_result` blocks in a `user` message to immediately follow the `tool_use` block from the preceding `assistant` message. However, the agent's logic can sometimes produce empty `assistant` messages (with no text and no tool calls) between consecutive tool calls. This commit updates `buildRequestBody` in the Anthropic Messages provider to skip empty `assistant` messages, ensuring that the merged `user` tool results correctly follow their originating `assistant` tool uses without interruption. Co-authored-by: TanLuong <28281768+TanLuong@users.noreply.github.com> --- pkg/providers/anthropic_messages/provider.go | 8 +++++ .../anthropic_messages/provider_test.go | 29 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/pkg/providers/anthropic_messages/provider.go b/pkg/providers/anthropic_messages/provider.go index 4093f6a74..0debffc1b 100644 --- a/pkg/providers/anthropic_messages/provider.go +++ b/pkg/providers/anthropic_messages/provider.go @@ -245,6 +245,14 @@ func buildRequestBody( content = append(content, toolUse) } + // Skip empty assistant messages that have no content and no valid tool calls. + // This prevents breaking the Anthropic API requirement where a user message + // containing tool results must immediately follow the assistant message + // containing the corresponding tool calls. + if len(content) == 0 { + continue + } + apiMessages = append(apiMessages, map[string]any{ "role": "assistant", "content": content, diff --git a/pkg/providers/anthropic_messages/provider_test.go b/pkg/providers/anthropic_messages/provider_test.go index 39bc48117..09ba1942b 100644 --- a/pkg/providers/anthropic_messages/provider_test.go +++ b/pkg/providers/anthropic_messages/provider_test.go @@ -506,6 +506,25 @@ func TestBuildRequestBodyEdgeCases(t *testing.T) { }, wantErr: false, }, + { + name: "skip empty assistant messages", + messages: []Message{ + {Role: "user", Content: "hello"}, + {Role: "assistant", Content: "", ToolCalls: []ToolCall{}}, // Should be skipped + {Role: "assistant", Content: "valid message", ToolCalls: []ToolCall{}}, + {Role: "assistant", Content: "", ToolCalls: []ToolCall{ + {ID: "tool-valid", Name: "test_tool", Arguments: map[string]any{"arg": "value"}}, + }}, + {Role: "assistant", Content: "", ToolCalls: []ToolCall{ + {ID: "tool-empty", Name: "", Arguments: map[string]any{"ignored": true}}, + }}, // Should be skipped because tool call is empty and content is empty + }, + model: "test-model", + options: map[string]any{ + "max_tokens": 8192, + }, + wantErr: false, + }, } for _, tt := range tests { @@ -558,6 +577,16 @@ func TestBuildRequestBodyEdgeCases(t *testing.T) { t.Fatalf("tool_use id = %v, want %q", gotID, "tool-valid") } } + + if tt.name == "skip empty assistant messages" { + messages, ok := got["messages"].([]any) + if !ok { + t.Fatalf("messages is not []any") + } + if len(messages) != 3 { + t.Fatalf("expected 3 API messages (user, assistant with text, assistant with tool_use), got %d: %#v", len(messages), messages) + } + } }) } } From c80560348e672a4bcf83b3074d1e937be61442a6 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 28 Mar 2026 15:24:31 +0000 Subject: [PATCH 2/2] fix: resolve golines formatting issues in provider test and config Breaks down long lines in pkg/providers/anthropic_messages/provider_test.go and re-aligns struct tags in pkg/config/config.go to satisfy the golines linter which caused a CI failure. Co-authored-by: TanLuong <28281768+TanLuong@users.noreply.github.com> --- pkg/config/config.go | 8 ++++---- pkg/providers/anthropic_messages/provider_test.go | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 3b558d780..c1a48974f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1048,10 +1048,10 @@ type SearXNGConfig struct { } type GLMSearchConfig struct { - Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_GLM_ENABLED"` - apiKey string - secDirty bool - BaseURL string `json:"base_url" env:"PICOCLAW_TOOLS_WEB_GLM_BASE_URL"` + Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_GLM_ENABLED"` + apiKey string + secDirty bool + BaseURL string `json:"base_url" env:"PICOCLAW_TOOLS_WEB_GLM_BASE_URL"` // SearchEngine specifies the search backend: "search_std" (default), // "search_pro", "search_pro_sogou", or "search_pro_quark". SearchEngine string `json:"search_engine" env:"PICOCLAW_TOOLS_WEB_GLM_SEARCH_ENGINE"` diff --git a/pkg/providers/anthropic_messages/provider_test.go b/pkg/providers/anthropic_messages/provider_test.go index 09ba1942b..153843bb1 100644 --- a/pkg/providers/anthropic_messages/provider_test.go +++ b/pkg/providers/anthropic_messages/provider_test.go @@ -584,7 +584,11 @@ func TestBuildRequestBodyEdgeCases(t *testing.T) { t.Fatalf("messages is not []any") } if len(messages) != 3 { - t.Fatalf("expected 3 API messages (user, assistant with text, assistant with tool_use), got %d: %#v", len(messages), messages) + t.Fatalf( + "expected 3 API messages (user, assistant with text, assistant with tool_use), got %d: %#v", + len(messages), + messages, + ) } } })