mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 18:15:34 +00:00
Ollama models like qwq and deepseek-r1 have thinking enabled by default. Disable it to prevent bloated response generation unless explicitly requested. - Add isOllamaEndpoint() detection (providerType, name, apiBase) - Always send think=false in Ollama requests unless OptThinkingLevel is set - SupportsThinking() and Capabilities().Thinking return false for Ollama - Add tests covering default-off, explicit-opt-in, and capability flags Co-authored-by: Bruno Clermont <bruno.clermont@gmail.com>
97 lines
4.0 KiB
Go
97 lines
4.0 KiB
Go
package providers
|
|
|
|
import "strings"
|
|
|
|
// isOpenAINativeEndpoint returns true for endpoints confirmed to be native OpenAI
|
|
// infrastructure that accepts the "developer" message role.
|
|
// Azure OpenAI, proxies, and other OpenAI-compatible backends only support "system".
|
|
// Matching OpenClaw TS: model-compat.ts → isOpenAINativeEndpoint().
|
|
func isOpenAINativeEndpoint(apiBase string) bool {
|
|
// Extract hostname from the API base URL.
|
|
lower := strings.ToLower(apiBase)
|
|
return strings.Contains(lower, "api.openai.com")
|
|
}
|
|
|
|
// isFireworksEndpoint returns true for Fireworks AI endpoints.
|
|
// Fireworks requires stream=true for max_tokens > 4096.
|
|
func (p *OpenAIProvider) isFireworksEndpoint() bool {
|
|
return strings.Contains(strings.ToLower(p.apiBase), "fireworks.ai")
|
|
}
|
|
|
|
// isTogetherEndpoint returns true for Together AI inference hosts.
|
|
// Together rejects some OpenAI extensions (e.g. stream_options, reasoning_effort) with HTTP 400.
|
|
// Uses URL, provider_type, and name so reverse-proxied Together endpoints are also detected.
|
|
func (p *OpenAIProvider) isTogetherEndpoint() bool {
|
|
b := strings.ToLower(p.apiBase)
|
|
if strings.Contains(b, "together.xyz") || strings.Contains(b, "together.ai") {
|
|
return true
|
|
}
|
|
if strings.Contains(strings.ToLower(strings.TrimSpace(p.providerType)), "together") {
|
|
return true
|
|
}
|
|
if strings.Contains(strings.ToLower(p.name), "together") {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isDashScopeAPIBase returns true for Alibaba DashScope OpenAI-compatible endpoints.
|
|
func isDashScopeAPIBase(apiBase string) bool {
|
|
return strings.Contains(strings.ToLower(apiBase), "dashscope")
|
|
}
|
|
|
|
// isOllamaEndpoint returns true for local or self-hosted Ollama instances.
|
|
// Ollama models such as qwq and deepseek-r1 have thinking enabled by default;
|
|
// goclaw must send think=false to suppress it unless the user explicitly opts in.
|
|
// Detection uses providerType (DB), name, and apiBase so both ProviderOllama and
|
|
// ProviderOllamaCloud are covered, as well as self-hosted instances behind a proxy.
|
|
func (p *OpenAIProvider) isOllamaEndpoint() bool {
|
|
pt := strings.ToLower(strings.TrimSpace(p.providerType))
|
|
if pt == "ollama" || pt == "ollama_cloud" {
|
|
return true
|
|
}
|
|
if strings.Contains(strings.ToLower(p.name), "ollama") {
|
|
return true
|
|
}
|
|
b := strings.ToLower(p.apiBase)
|
|
return strings.Contains(b, "11434") || strings.Contains(b, "ollama")
|
|
}
|
|
|
|
// dashScopePassthroughKeys is true when enable_thinking / thinking_budget may be added to the JSON body.
|
|
// Uses the same DashScope/Bailian route detection as prompt-cache wrapping.
|
|
func (p *OpenAIProvider) dashScopePassthroughKeys() bool {
|
|
return p.isDashScope()
|
|
}
|
|
|
|
// ollamaNativeURL returns the full URL for Ollama's native /api/chat endpoint.
|
|
// Ollama's OpenAI-compat shim at /v1/chat/completions silently ignores options.num_ctx,
|
|
// while the native /api/chat endpoint honors it. The apiBase may include a /v1 suffix
|
|
// (e.g. "http://localhost:11434/v1") — it is stripped before appending /api/chat.
|
|
func (p *OpenAIProvider) ollamaNativeURL() string {
|
|
base := strings.TrimRight(strings.TrimSuffix(strings.TrimRight(p.apiBase, "/"), "/v1"), "/")
|
|
return base + "/api/chat"
|
|
}
|
|
|
|
// isDashScope returns true when this provider routes requests to DashScope/Bailian
|
|
// (supports cache_control:ephemeral wire format - verified live 2026-05-08).
|
|
// Uses 3-source detection (URL + providerType + name) to handle reverse-proxied
|
|
// DashScope endpoints. Includes "bailian" because live qwen-richard provider has
|
|
// provider_type=bailian.
|
|
//
|
|
// Used by buildRequestBody to wrap system content with Anthropic-style
|
|
// cache_control blocks for prompt caching (90% discount on cached prefix tokens).
|
|
func (p *OpenAIProvider) isDashScope() bool {
|
|
if isDashScopeAPIBase(p.apiBase) {
|
|
return true
|
|
}
|
|
pt := strings.ToLower(strings.TrimSpace(p.providerType))
|
|
if strings.Contains(pt, "dashscope") || strings.Contains(pt, "bailian") {
|
|
return true
|
|
}
|
|
name := strings.ToLower(p.name)
|
|
if strings.Contains(name, "dashscope") || strings.Contains(name, "bailian") {
|
|
return true
|
|
}
|
|
return false
|
|
}
|