This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/server/capabilities/intrinsic.go
2026-02-23 01:57:28 +00:00

352 lines
10 KiB
Go

package capabilities
import (
"regexp"
"strings"
"git.gobha.me/xcaliber/chat-switchboard/models"
)
// ── Known Model Defaults ────────────────────
// Authoritative capabilities for models where the provider API
// doesn't report them. Keyed by exact model ID or prefix.
//
// Sources:
// Anthropic: https://docs.anthropic.com/en/docs/about-claude/models
// OpenAI: https://platform.openai.com/docs/models
// Meta: Model cards on Hugging Face
// Google: https://ai.google.dev/gemini-api/docs/models
var knownModels = map[string]models.ModelCapabilities{
// ── Anthropic ────────────────────────────
"claude-opus-4": {
Streaming: true, ToolCalling: true, Vision: true, Thinking: true,
MaxContext: 200000, MaxOutputTokens: 32000,
},
"claude-sonnet-4": {
Streaming: true, ToolCalling: true, Vision: true, Thinking: true,
MaxContext: 200000, MaxOutputTokens: 16000,
},
"claude-3-5-sonnet": {
Streaming: true, ToolCalling: true, Vision: true,
MaxContext: 200000, MaxOutputTokens: 8192,
},
"claude-3-5-haiku": {
Streaming: true, ToolCalling: true, Vision: true,
MaxContext: 200000, MaxOutputTokens: 8192,
},
"claude-3-opus": {
Streaming: true, ToolCalling: true, Vision: true,
MaxContext: 200000, MaxOutputTokens: 4096,
},
"claude-3-haiku": {
Streaming: true, ToolCalling: true, Vision: true,
MaxContext: 200000, MaxOutputTokens: 4096,
},
// ── OpenAI ──────────────────────────────
"gpt-4o": {
Streaming: true, ToolCalling: true, Vision: true,
MaxContext: 128000, MaxOutputTokens: 16384,
},
"gpt-4o-mini": {
Streaming: true, ToolCalling: true, Vision: true,
MaxContext: 128000, MaxOutputTokens: 16384,
},
"gpt-4-turbo": {
Streaming: true, ToolCalling: true, Vision: true,
MaxContext: 128000, MaxOutputTokens: 4096,
},
"gpt-4": {
Streaming: true, ToolCalling: true,
MaxContext: 8192, MaxOutputTokens: 4096,
},
"o1": {
Streaming: true, Reasoning: true,
MaxContext: 200000, MaxOutputTokens: 100000,
},
"o1-mini": {
Streaming: true, Reasoning: true,
MaxContext: 128000, MaxOutputTokens: 65536,
},
"o3": {
Streaming: true, ToolCalling: true, Reasoning: true,
MaxContext: 200000, MaxOutputTokens: 100000,
},
"o3-mini": {
Streaming: true, ToolCalling: true, Reasoning: true,
MaxContext: 200000, MaxOutputTokens: 65536,
},
"o4-mini": {
Streaming: true, ToolCalling: true, Reasoning: true,
MaxContext: 200000, MaxOutputTokens: 100000,
},
// ── Meta Llama ──────────────────────────
"llama-3.1-405b": {
Streaming: true, ToolCalling: true,
MaxContext: 131072, MaxOutputTokens: 4096,
},
"llama-3.1-70b": {
Streaming: true, ToolCalling: true,
MaxContext: 131072, MaxOutputTokens: 4096,
},
"llama-3.1-8b": {
Streaming: true, ToolCalling: true,
MaxContext: 131072, MaxOutputTokens: 4096,
},
"llama-3.3-70b": {
Streaming: true, ToolCalling: true,
MaxContext: 131072, MaxOutputTokens: 4096,
},
"llama-4-maverick": {
Streaming: true, ToolCalling: true, Vision: true,
MaxContext: 1048576, MaxOutputTokens: 16384,
},
"llama-4-scout": {
Streaming: true, ToolCalling: true, Vision: true,
MaxContext: 524288, MaxOutputTokens: 16384,
},
// ── Google Gemini ───────────────────────
"gemini-2.5-pro": {
Streaming: true, ToolCalling: true, Vision: true, Thinking: true,
MaxContext: 1048576, MaxOutputTokens: 65536,
},
"gemini-2.5-flash": {
Streaming: true, ToolCalling: true, Vision: true, Thinking: true,
MaxContext: 1048576, MaxOutputTokens: 65536,
},
"gemini-2.0-flash": {
Streaming: true, ToolCalling: true, Vision: true,
MaxContext: 1048576, MaxOutputTokens: 8192,
},
// ── DeepSeek ────────────────────────────
"deepseek-r1": {
Streaming: true, Reasoning: true,
MaxContext: 65536, MaxOutputTokens: 8192,
},
"deepseek-v3": {
Streaming: true, ToolCalling: true,
MaxContext: 65536, MaxOutputTokens: 8192,
},
"deepseek-chat": {
Streaming: true, ToolCalling: true,
MaxContext: 65536, MaxOutputTokens: 8192,
},
// ── Mistral ─────────────────────────────
"mistral-large": {
Streaming: true, ToolCalling: true,
MaxContext: 131072, MaxOutputTokens: 8192,
},
"mistral-small": {
Streaming: true, ToolCalling: true,
MaxContext: 131072, MaxOutputTokens: 8192,
},
"codestral": {
Streaming: true, ToolCalling: true, CodeOptimized: true,
MaxContext: 262144, MaxOutputTokens: 8192,
},
// ── Qwen ────────────────────────────────
"qwen-2.5-72b": {
Streaming: true, ToolCalling: true,
MaxContext: 131072, MaxOutputTokens: 8192,
},
"qwen-2.5-coder-32b": {
Streaming: true, ToolCalling: true, CodeOptimized: true,
MaxContext: 131072, MaxOutputTokens: 8192,
},
"qwq-32b": {
Streaming: true, Reasoning: true,
MaxContext: 131072, MaxOutputTokens: 8192,
},
}
// LookupKnownModel finds capabilities for a model by exact ID or prefix match.
func LookupKnownModel(modelID string) (models.ModelCapabilities, bool) {
id := normalizeModelID(modelID)
// Exact match first
if caps, ok := knownModels[id]; ok {
return caps, true
}
// Prefix match: "claude-sonnet-4-20250514" matches "claude-sonnet-4"
var bestKey string
var bestCaps models.ModelCapabilities
for key, caps := range knownModels {
if strings.HasPrefix(id, key) && len(key) > len(bestKey) {
bestKey = key
bestCaps = caps
}
}
if bestKey != "" {
return bestCaps, true
}
return models.ModelCapabilities{}, false
}
// ── Heuristic Capability Detection ──────────
var (
toolPatterns = []*regexp.Regexp{
regexp.MustCompile(`(?i)llama[_-]?[34]`),
regexp.MustCompile(`(?i)granite[_-]?[34]`),
regexp.MustCompile(`(?i)hermes[_-]?[23]?`),
regexp.MustCompile(`(?i)qwen[_-]?2`),
regexp.MustCompile(`(?i)mistral|mixtral`),
regexp.MustCompile(`(?i)command[_-]?r`),
regexp.MustCompile(`(?i)phi[_-]?[34]`),
regexp.MustCompile(`(?i)deepseek[_-]?v[23]`),
regexp.MustCompile(`(?i)functionary`),
regexp.MustCompile(`(?i)^gpt[_-]?[34]`),
regexp.MustCompile(`(?i)^claude`),
regexp.MustCompile(`(?i)gemma[_-]?2`),
regexp.MustCompile(`(?i)glm[_-]?4`),
regexp.MustCompile(`(?i)gemini`),
}
visionPatterns = []*regexp.Regexp{
regexp.MustCompile(`(?i)llava|bakllava`),
regexp.MustCompile(`(?i)moondream`),
regexp.MustCompile(`(?i)llama.*vision|vision.*llama`),
regexp.MustCompile(`(?i)minicpm[_-]?v`),
regexp.MustCompile(`(?i)^gpt[_-]?4[_-]?o|^gpt[_-]?4[_-]?turbo`),
regexp.MustCompile(`(?i)^claude[_-]?3`),
regexp.MustCompile(`(?i)gemini`),
regexp.MustCompile(`(?i)qwen.*vl|vl.*qwen`),
regexp.MustCompile(`(?i)phi[_-]?[34].*vision`),
regexp.MustCompile(`(?i)internvl`),
regexp.MustCompile(`(?i)glm[_-]?4v`),
}
reasoningPatterns = []*regexp.Regexp{
regexp.MustCompile(`(?i)deepseek[_-]?r1`),
regexp.MustCompile(`(?i)qwq`),
regexp.MustCompile(`(?i)^o[1234][_-]|^o[1234]$`),
}
codePatterns = []*regexp.Regexp{
regexp.MustCompile(`(?i)codellama|code[_-]?llama`),
regexp.MustCompile(`(?i)deepseek[_-]?coder`),
regexp.MustCompile(`(?i)starcoder`),
regexp.MustCompile(`(?i)coder|codestral`),
regexp.MustCompile(`(?i)wizardcoder`),
regexp.MustCompile(`(?i)codegemma`),
}
)
func matchesAny(id string, patterns []*regexp.Regexp) bool {
for _, p := range patterns {
if p.MatchString(id) {
return true
}
}
return false
}
// InferCapabilities guesses model capabilities from the model ID string.
// Fallback when neither the known model table nor the provider API has data.
func InferCapabilities(modelID string) models.ModelCapabilities {
id := normalizeModelID(modelID)
return models.ModelCapabilities{
Streaming: true, // virtually everything streams
ToolCalling: matchesAny(id, toolPatterns),
Vision: matchesAny(id, visionPatterns),
Reasoning: matchesAny(id, reasoningPatterns),
CodeOptimized: matchesAny(id, codePatterns),
}
}
// ResolveIntrinsic determines the intrinsic capabilities of a model.
// Priority:
// 1. catalogCaps (from model_catalog DB — provider API data)
// 2. Known model table (static, compiled-in)
// 3. Heuristic inference (regex patterns)
//
// This is the ONLY function that computes intrinsic capabilities.
func ResolveIntrinsic(modelID string, catalogCaps *models.ModelCapabilities) models.ModelCapabilities {
// Start with catalog data if available
var base models.ModelCapabilities
if catalogCaps != nil && catalogCaps.HasProviderData() {
base = *catalogCaps
}
// Fill gaps from known model table
if known, found := LookupKnownModel(modelID); found {
mergeGaps(&base, &known)
return base
}
// Fill gaps from heuristic inference
inferred := InferCapabilities(modelID)
mergeGaps(&base, &inferred)
return base
}
// ResolveMaxOutput returns the max output tokens for a model.
// Priority: explicit caps → known model table → derive from context → 4096 fallback.
func ResolveMaxOutput(modelID string, caps models.ModelCapabilities) int {
if caps.MaxOutputTokens > 0 {
return caps.MaxOutputTokens
}
if known, ok := LookupKnownModel(modelID); ok && known.MaxOutputTokens > 0 {
return known.MaxOutputTokens
}
if caps.MaxContext > 0 {
derived := caps.MaxContext / 8
if derived < 2048 {
derived = 2048
}
if derived > 16384 {
derived = 16384
}
return derived
}
return 4096
}
// mergeGaps fills zero/false fields in dst from src. Never overrides existing data.
func mergeGaps(dst, src *models.ModelCapabilities) {
if !dst.Streaming && src.Streaming {
dst.Streaming = true
}
if !dst.ToolCalling && src.ToolCalling {
dst.ToolCalling = true
}
if !dst.Vision && src.Vision {
dst.Vision = true
}
if !dst.Thinking && src.Thinking {
dst.Thinking = true
}
if !dst.Reasoning && src.Reasoning {
dst.Reasoning = true
}
if !dst.CodeOptimized && src.CodeOptimized {
dst.CodeOptimized = true
}
if !dst.WebSearch && src.WebSearch {
dst.WebSearch = true
}
if dst.MaxContext == 0 && src.MaxContext > 0 {
dst.MaxContext = src.MaxContext
}
if dst.MaxOutputTokens == 0 && src.MaxOutputTokens > 0 {
dst.MaxOutputTokens = src.MaxOutputTokens
}
}
// normalizeModelID strips provider prefix and lowercases.
func normalizeModelID(modelID string) string {
id := strings.ToLower(modelID)
if idx := strings.Index(id, "/"); idx >= 0 {
id = id[idx+1:]
}
return id
}