Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
550da0cee8 | ||
|
|
7ff3936efe |
@@ -165,6 +165,54 @@ func TestEnsureCacheControl(t *testing.T) {
|
|||||||
t.Errorf("system should have cache_control even with empty tools array")
|
t.Errorf("system should have cache_control even with empty tools array")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Test case 8: Messages caching for multi-turn (second-to-last user)
|
||||||
|
t.Run("Messages Caching Second-To-Last User", func(t *testing.T) {
|
||||||
|
input := []byte(`{
|
||||||
|
"model": "claude-3-5-sonnet",
|
||||||
|
"messages": [
|
||||||
|
{"role": "user", "content": "First user"},
|
||||||
|
{"role": "assistant", "content": "Assistant reply"},
|
||||||
|
{"role": "user", "content": "Second user"},
|
||||||
|
{"role": "assistant", "content": "Assistant reply 2"},
|
||||||
|
{"role": "user", "content": "Third user"}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
output := ensureCacheControl(input)
|
||||||
|
|
||||||
|
cacheType := gjson.GetBytes(output, "messages.2.content.0.cache_control.type")
|
||||||
|
if cacheType.String() != "ephemeral" {
|
||||||
|
t.Errorf("cache_control not found on second-to-last user turn. Output: %s", string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
lastUserCache := gjson.GetBytes(output, "messages.4.content.0.cache_control")
|
||||||
|
if lastUserCache.Exists() {
|
||||||
|
t.Errorf("last user turn should NOT have cache_control")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test case 9: Existing message cache_control should skip injection
|
||||||
|
t.Run("Messages Skip When Cache Control Exists", func(t *testing.T) {
|
||||||
|
input := []byte(`{
|
||||||
|
"model": "claude-3-5-sonnet",
|
||||||
|
"messages": [
|
||||||
|
{"role": "user", "content": [{"type": "text", "text": "First user"}]},
|
||||||
|
{"role": "assistant", "content": [{"type": "text", "text": "Assistant reply", "cache_control": {"type": "ephemeral"}}]},
|
||||||
|
{"role": "user", "content": [{"type": "text", "text": "Second user"}]}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
output := ensureCacheControl(input)
|
||||||
|
|
||||||
|
userCache := gjson.GetBytes(output, "messages.0.content.0.cache_control")
|
||||||
|
if userCache.Exists() {
|
||||||
|
t.Errorf("cache_control should NOT be injected when a message already has cache_control")
|
||||||
|
}
|
||||||
|
|
||||||
|
existingCache := gjson.GetBytes(output, "messages.1.content.0.cache_control.type")
|
||||||
|
if existingCache.String() != "ephemeral" {
|
||||||
|
t.Errorf("existing cache_control should be preserved. Output: %s", string(output))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestCacheControlOrder verifies the correct order: tools -> system -> messages
|
// TestCacheControlOrder verifies the correct order: tools -> system -> messages
|
||||||
|
|||||||
@@ -642,13 +642,17 @@ func applyClaudeHeaders(r *http.Request, auth *cliproxyauth.Auth, apiKey string,
|
|||||||
ginHeaders = ginCtx.Request.Header
|
ginHeaders = ginCtx.Request.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
baseBetas := "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14,prompt-caching-2024-07-31"
|
promptCachingBeta := "prompt-caching-2024-07-31"
|
||||||
|
baseBetas := "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14," + promptCachingBeta
|
||||||
if val := strings.TrimSpace(ginHeaders.Get("Anthropic-Beta")); val != "" {
|
if val := strings.TrimSpace(ginHeaders.Get("Anthropic-Beta")); val != "" {
|
||||||
baseBetas = val
|
baseBetas = val
|
||||||
if !strings.Contains(val, "oauth") {
|
if !strings.Contains(val, "oauth") {
|
||||||
baseBetas += ",oauth-2025-04-20"
|
baseBetas += ",oauth-2025-04-20"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(baseBetas, promptCachingBeta) {
|
||||||
|
baseBetas += "," + promptCachingBeta
|
||||||
|
}
|
||||||
|
|
||||||
// Merge extra betas from request body
|
// Merge extra betas from request body
|
||||||
if len(extraBetas) > 0 {
|
if len(extraBetas) > 0 {
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ func convertOpenAIDoneToAnthropic(param *ConvertOpenAIResponseToAnthropicParams)
|
|||||||
|
|
||||||
// If we haven't sent message_delta yet (no usage info was received), send it now
|
// If we haven't sent message_delta yet (no usage info was received), send it now
|
||||||
if param.FinishReason != "" && !param.MessageDeltaSent {
|
if param.FinishReason != "" && !param.MessageDeltaSent {
|
||||||
messageDeltaJSON := `{"type":"message_delta","delta":{"stop_reason":"","stop_sequence":null}}`
|
messageDeltaJSON := `{"type":"message_delta","delta":{"stop_reason":"","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`
|
||||||
messageDeltaJSON, _ = sjson.Set(messageDeltaJSON, "delta.stop_reason", mapOpenAIFinishReasonToAnthropic(param.FinishReason))
|
messageDeltaJSON, _ = sjson.Set(messageDeltaJSON, "delta.stop_reason", mapOpenAIFinishReasonToAnthropic(param.FinishReason))
|
||||||
results = append(results, "event: message_delta\ndata: "+messageDeltaJSON+"\n\n")
|
results = append(results, "event: message_delta\ndata: "+messageDeltaJSON+"\n\n")
|
||||||
param.MessageDeltaSent = true
|
param.MessageDeltaSent = true
|
||||||
|
|||||||
Reference in New Issue
Block a user