From bee6f75b2cb6f847ebf6a8e8812ce0358e893cd0 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 26 Feb 2026 03:00:10 +0000 Subject: [PATCH 1/2] fix: only send prompt_cache_key for OpenAI provider The prompt_cache_key field is an OpenAI-specific feature for prefix-based prompt caching. When sent to other providers like Gemini that use the OpenAI-compatible HTTP provider, it causes a 400 error because Gemini's API rejects unknown fields. This adds a supportPromptCache flag to the provider struct, enabled only for the OpenAI protocol, so the field is no longer sent to providers that don't support it. Co-Authored-By: Claude Opus 4.6 --- pkg/providers/factory_provider.go | 4 +++- pkg/providers/http_provider.go | 5 +++++ pkg/providers/openai_compat/provider.go | 22 ++++++++++++++++------ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/pkg/providers/factory_provider.go b/pkg/providers/factory_provider.go index 7d5566eef..dd087fdda 100644 --- a/pkg/providers/factory_provider.go +++ b/pkg/providers/factory_provider.go @@ -84,7 +84,9 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err if apiBase == "" { apiBase = getDefaultAPIBase(protocol) } - return NewHTTPProviderWithMaxTokensField(cfg.APIKey, apiBase, cfg.Proxy, cfg.MaxTokensField), modelID, nil + p := NewHTTPProviderWithMaxTokensField(cfg.APIKey, apiBase, cfg.Proxy, cfg.MaxTokensField) + p.SetSupportPromptCache(true) // OpenAI supports prompt_cache_key + return p, modelID, nil case "openrouter", "groq", "zhipu", "gemini", "nvidia", "ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras", diff --git a/pkg/providers/http_provider.go b/pkg/providers/http_provider.go index d0c4344f3..166a8611e 100644 --- a/pkg/providers/http_provider.go +++ b/pkg/providers/http_provider.go @@ -38,6 +38,11 @@ func (p *HTTPProvider) Chat( return p.delegate.Chat(ctx, messages, tools, model, options) } +// SetSupportPromptCache enables or disables sending the prompt_cache_key field. +func (p *HTTPProvider) SetSupportPromptCache(v bool) { + p.delegate.SetSupportPromptCache(v) +} + func (p *HTTPProvider) GetDefaultModel() string { return "" } diff --git a/pkg/providers/openai_compat/provider.go b/pkg/providers/openai_compat/provider.go index a8d244d4a..bb1fc8181 100644 --- a/pkg/providers/openai_compat/provider.go +++ b/pkg/providers/openai_compat/provider.go @@ -28,10 +28,11 @@ type ( ) type Provider struct { - apiKey string - apiBase string - maxTokensField string // Field name for max tokens (e.g., "max_completion_tokens" for o1/glm models) - httpClient *http.Client + apiKey string + apiBase string + maxTokensField string // Field name for max tokens (e.g., "max_completion_tokens" for o1/glm models) + httpClient *http.Client + supportPromptCache bool // Only send prompt_cache_key when true (OpenAI-specific feature) } func NewProvider(apiKey, apiBase, proxy string) *Provider { @@ -62,6 +63,12 @@ func NewProviderWithMaxTokensField(apiKey, apiBase, proxy, maxTokensField string } } +// SetSupportPromptCache enables or disables sending the prompt_cache_key field. +// Only OpenAI supports this field; other providers (e.g. Gemini) reject unknown fields. +func (p *Provider) SetSupportPromptCache(v bool) { + p.supportPromptCache = v +} + func (p *Provider) Chat( ctx context.Context, messages []Message, @@ -115,8 +122,11 @@ func (p *Provider) Chat( // with the same key and reuse prefix KV cache across calls. // The key is typically the agent ID — stable per agent, shared across requests. // See: https://platform.openai.com/docs/guides/prompt-caching - if cacheKey, ok := options["prompt_cache_key"].(string); ok && cacheKey != "" { - requestBody["prompt_cache_key"] = cacheKey + // Only sent for providers that support it (e.g. OpenAI); others like Gemini reject unknown fields. + if p.supportPromptCache { + if cacheKey, ok := options["prompt_cache_key"].(string); ok && cacheKey != "" { + requestBody["prompt_cache_key"] = cacheKey + } } jsonData, err := json.Marshal(requestBody) From 989594716c2c1737f49100a7f138b90654371bc3 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 26 Feb 2026 03:06:56 +0000 Subject: [PATCH 2/2] fix: skip prompt_cache_key for Gemini provider The prompt_cache_key field is accepted by most OpenAI-compatible providers, but Gemini rejects unknown fields in the request body, causing a 400 error. This adds a supportPromptCache flag to the HTTP provider, enabled for all providers except Gemini. Co-Authored-By: Claude Opus 4.6 --- pkg/providers/factory_provider.go | 9 +++++++-- pkg/providers/openai_compat/provider.go | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/providers/factory_provider.go b/pkg/providers/factory_provider.go index dd087fdda..fba34ed00 100644 --- a/pkg/providers/factory_provider.go +++ b/pkg/providers/factory_provider.go @@ -85,7 +85,7 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err apiBase = getDefaultAPIBase(protocol) } p := NewHTTPProviderWithMaxTokensField(cfg.APIKey, apiBase, cfg.Proxy, cfg.MaxTokensField) - p.SetSupportPromptCache(true) // OpenAI supports prompt_cache_key + p.SetSupportPromptCache(true) return p, modelID, nil case "openrouter", "groq", "zhipu", "gemini", "nvidia", @@ -99,7 +99,12 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err if apiBase == "" { apiBase = getDefaultAPIBase(protocol) } - return NewHTTPProviderWithMaxTokensField(cfg.APIKey, apiBase, cfg.Proxy, cfg.MaxTokensField), modelID, nil + p := NewHTTPProviderWithMaxTokensField(cfg.APIKey, apiBase, cfg.Proxy, cfg.MaxTokensField) + // Gemini rejects unknown fields in the request body; disable prompt_cache_key for it. + if protocol != "gemini" { + p.SetSupportPromptCache(true) + } + return p, modelID, nil case "anthropic": if cfg.AuthMethod == "oauth" || cfg.AuthMethod == "token" { diff --git a/pkg/providers/openai_compat/provider.go b/pkg/providers/openai_compat/provider.go index bb1fc8181..387bb50e8 100644 --- a/pkg/providers/openai_compat/provider.go +++ b/pkg/providers/openai_compat/provider.go @@ -64,7 +64,7 @@ func NewProviderWithMaxTokensField(apiKey, apiBase, proxy, maxTokensField string } // SetSupportPromptCache enables or disables sending the prompt_cache_key field. -// Only OpenAI supports this field; other providers (e.g. Gemini) reject unknown fields. +// Most providers accept or ignore this field, but Gemini rejects unknown fields. func (p *Provider) SetSupportPromptCache(v bool) { p.supportPromptCache = v } @@ -122,7 +122,7 @@ func (p *Provider) Chat( // with the same key and reuse prefix KV cache across calls. // The key is typically the agent ID — stable per agent, shared across requests. // See: https://platform.openai.com/docs/guides/prompt-caching - // Only sent for providers that support it (e.g. OpenAI); others like Gemini reject unknown fields. + // Disabled for providers like Gemini that reject unknown fields in the request body. if p.supportPromptCache { if cacheKey, ok := options["prompt_cache_key"].(string); ok && cacheKey != "" { requestBody["prompt_cache_key"] = cacheKey