// Package registry löst Modell-Namen auf Caps-Defaults auf (E-10): // "OpenAI-kompatibel" ist ein Sammelbegriff, Groq/Cerebras/vLLM/Ollama/ // LM Studio/OpenRouter verhalten sich unterschiedlich. Die Tabelle hier ist // bewusst klein — unbekannte Modelle fallen auf einen konservativen Default, // Config-Overrides (CapsOverride) korrigieren im Einzelfall. package registry import "nub/internal/llm" var known = map[string]llm.Caps{ "gpt-4o": { ParallelToolCalls: true, UsageInStream: true, SystemRole: "system", MaxContext: 128000, }, "gpt-4o-mini": { ParallelToolCalls: true, UsageInStream: true, SystemRole: "system", MaxContext: 128000, }, "gpt-4.1": { ParallelToolCalls: true, UsageInStream: true, SystemRole: "system", MaxContext: 1000000, }, "o1": { ParallelToolCalls: false, UsageInStream: true, SystemRole: "developer", Reasoning: true, MaxContext: 200000, }, } // DefaultCaps liefert die bekannten Caps für ein Modell, oder einen // Fallback für unbekannte Modelle (z.B. neuere OpenAI-Modelle, die noch // nicht in der Tabelle stehen, oder Drittanbieter/lokale Endpoints). // // UsageInStream im Fallback ist bewusst true: stream_options.include_usage // ist ein Request-Flag, kein modellspezifisches Feature — praktisch jedes // OpenAI-Modell (auch zukünftige) honoriert es. Ein Endpoint, der es nicht // unterstützt, ist die Ausnahme, nicht die Regel, und lässt sich gezielt // über [endpoint.caps] usage_in_stream = false abschalten. func DefaultCaps(model string) llm.Caps { if caps, ok := known[model]; ok { return caps } return llm.Caps{ ParallelToolCalls: true, UsageInStream: true, SystemRole: "system", MaxContext: 32000, } } // CapsOverride überschreibt einzelne Caps-Felder aus der Endpoint-Config. // Pointer-Felder unterscheiden "nicht gesetzt" von "explizit false". type CapsOverride struct { ParallelToolCalls *bool `toml:"parallel_tool_calls"` UsageInStream *bool `toml:"usage_in_stream"` SystemRole *string `toml:"system_role"` ExplicitCache *bool `toml:"explicit_cache"` Reasoning *bool `toml:"reasoning"` StrictSchemas *bool `toml:"strict_schemas"` MaxContext *int `toml:"max_context"` SupportsImages *bool `toml:"supports_images"` } // Apply merged eine CapsOverride über eine Basis-Caps-Struktur. func Apply(base llm.Caps, o CapsOverride) llm.Caps { if o.ParallelToolCalls != nil { base.ParallelToolCalls = *o.ParallelToolCalls } if o.UsageInStream != nil { base.UsageInStream = *o.UsageInStream } if o.SystemRole != nil { base.SystemRole = *o.SystemRole } if o.ExplicitCache != nil { base.ExplicitCache = *o.ExplicitCache } if o.Reasoning != nil { base.Reasoning = *o.Reasoning } if o.StrictSchemas != nil { base.StrictSchemas = *o.StrictSchemas } if o.MaxContext != nil { base.MaxContext = *o.MaxContext } if o.SupportsImages != nil { base.SupportsImages = *o.SupportsImages } return base }