cbaf41f50c
Introduces an opt-in level-based reasoning toggle (low/medium/high) that each provider translates to its native parameter: - Anthropic: thinking.budget_tokens (1024/8000/24000), with temperature forced to default and MaxTokens auto-grown above the budget. - OpenAI/xAI/Groq via openaicompat: reasoning_effort string, gated by a new Rules.SupportsReasoning predicate so non-reasoning models don't receive the parameter. xAI uses Rules.MapReasoningEffort to remap "medium" to "high" since its API only accepts low|high. - Google: thinking_config.thinking_budget + include_thoughts:true. - DeepSeek: SupportsReasoning=false (reasoner is always-on; the reasoning_content trace was already extracted via openaicompat). Reasoning content is surfaced as Response.Thinking on Complete and as StreamEventThinking deltas during streaming. Provider-side: extracted from Anthropic thinking content blocks, Google's part.Thought=true parts, and the non-standard reasoning_content field that DeepSeek and Groq emit (parsed out of raw JSON since openai-go doesn't type it). Public API: - llm.ReasoningLevel + ReasoningLow/Medium/High constants - llm.WithReasoning(level) request option - Model.WithReasoning(level) for baked-in defaults - provider.Request.Reasoning, provider.Response.Thinking - provider.StreamEventThinking Tests cover Rules-based gating, MapReasoningEffort, reasoning_content extraction (Complete + Stream), Anthropic budget mapping, and temperature suppression when thinking is enabled. Existing behavior is unchanged when Reasoning is the empty string. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
42 lines
1.4 KiB
Go
42 lines
1.4 KiB
Go
// Package deepseek implements the go-llm v2 provider interface for DeepSeek
|
|
// (https://platform.deepseek.com). DeepSeek speaks the OpenAI Chat Completions
|
|
// protocol, so this package is a thin wrapper around openaicompat with its own
|
|
// defaults and per-model Rules.
|
|
package deepseek
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"gitea.stevedudenhoeffer.com/steve/go-llm/v2/openaicompat"
|
|
)
|
|
|
|
// DefaultBaseURL is the public DeepSeek API endpoint.
|
|
const DefaultBaseURL = "https://api.deepseek.com/v1"
|
|
|
|
// Provider is a type alias over openaicompat.Provider.
|
|
type Provider = openaicompat.Provider
|
|
|
|
// New creates a new DeepSeek provider. An empty baseURL uses DefaultBaseURL.
|
|
func New(apiKey, baseURL string) *Provider {
|
|
if baseURL == "" {
|
|
baseURL = DefaultBaseURL
|
|
}
|
|
return openaicompat.New(apiKey, baseURL, openaicompat.Rules{
|
|
// DeepSeek's chat and reasoner models are text-only.
|
|
SupportsVision: func(string) bool { return false },
|
|
// Reasoner doesn't accept tool calls.
|
|
SupportsTools: func(m string) bool {
|
|
return !strings.Contains(m, "reasoner")
|
|
},
|
|
// Reasoner rejects user-supplied temperature.
|
|
RestrictTemperature: func(m string) bool {
|
|
return strings.Contains(m, "reasoner")
|
|
},
|
|
// DeepSeek's reasoner thinks unconditionally; the API rejects an
|
|
// explicit reasoning_effort parameter. The thinking trace is
|
|
// surfaced via openaicompat's reasoning_content extraction without
|
|
// any opt-in.
|
|
SupportsReasoning: func(string) bool { return false },
|
|
})
|
|
}
|