fix(provider): anthropic_messages sends system as content blocks with cache_control

- Use SystemParts structured blocks when available, with per-block
  cache_control for Anthropic prompt caching support
- Fall back to plain text block when SystemParts is absent
- Output system as content blocks array instead of flat string
- Update test expectation to match new array format
This commit is contained in:
smallwhite 2026-03-30 20:38:31 +08:00 committed by guangdianclaw
parent 77b0c43392
commit c85f572687
2 changed files with 28 additions and 9 deletions

View file

@ -178,17 +178,34 @@ func buildRequestBody(
} }
// Process messages // Process messages
var systemPrompt string var systemBlocks []map[string]any
var apiMessages []any var apiMessages []any
for _, msg := range messages { for _, msg := range messages {
switch msg.Role { switch msg.Role {
case "system": case "system":
// Accumulate system messages // Prefer structured SystemParts for per-block cache_control.
if systemPrompt != "" { // This enables Anthropic prompt caching: static blocks keep a
systemPrompt += "\n\n" + msg.Content // stable prefix hash while dynamic parts (time, session) change.
if len(msg.SystemParts) > 0 {
for _, part := range msg.SystemParts {
block := map[string]any{
"type": "text",
"text": part.Text,
}
if part.CacheControl != nil && part.CacheControl.Type != "" {
block["cache_control"] = map[string]string{
"type": part.CacheControl.Type,
}
}
systemBlocks = append(systemBlocks, block)
}
} else { } else {
systemPrompt = msg.Content // Fallback: no structured parts, use plain text block.
systemBlocks = append(systemBlocks, map[string]any{
"type": "text",
"text": msg.Content,
})
} }
case "user": case "user":
@ -280,9 +297,9 @@ func buildRequestBody(
result["messages"] = apiMessages result["messages"] = apiMessages
// Set system prompt if present // Set system prompt if present (always as content blocks array for cache_control support)
if systemPrompt != "" { if len(systemBlocks) > 0 {
result["system"] = systemPrompt result["system"] = systemBlocks
} }
// Add tools if present // Add tools if present

View file

@ -86,7 +86,9 @@ func TestBuildRequestBody(t *testing.T) {
want: map[string]any{ want: map[string]any{
"model": "test-model", "model": "test-model",
"max_tokens": int64(8192), "max_tokens": int64(8192),
"system": "You are a helpful assistant.", "system": []map[string]any{
{"type": "text", "text": "You are a helpful assistant."},
},
"messages": []any{ "messages": []any{
map[string]any{ map[string]any{
"role": "user", "role": "user",