Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions pkg/providers/factory_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
return p, modelID, nil

case "openrouter", "groq", "zhipu", "gemini", "nvidia",
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
Expand All @@ -97,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" {
Expand Down
5 changes: 5 additions & 0 deletions pkg/providers/http_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 ""
}
22 changes: 16 additions & 6 deletions pkg/providers/openai_compat/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -62,6 +63,12 @@ func NewProviderWithMaxTokensField(apiKey, apiBase, proxy, maxTokensField string
}
}

// SetSupportPromptCache enables or disables sending the prompt_cache_key field.
// Most providers accept or ignore this field, but Gemini rejects unknown fields.
func (p *Provider) SetSupportPromptCache(v bool) {
p.supportPromptCache = v
}

func (p *Provider) Chat(
ctx context.Context,
messages []Message,
Expand Down Expand Up @@ -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
// 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
}
}

jsonData, err := json.Marshal(requestBody)
Expand Down