diff --git a/internal/models/generate_models.go b/internal/models/generate_models.go new file mode 100644 index 00000000..f9390d37 --- /dev/null +++ b/internal/models/generate_models.go @@ -0,0 +1,196 @@ +//go:build ignore + +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "text/template" + "time" +) + +// ModelInfo represents information about a specific model +type ModelInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Attachment bool `json:"attachment"` + Reasoning bool `json:"reasoning"` + Temperature bool `json:"temperature"` + Cost Cost `json:"cost"` + Limit Limit `json:"limit"` +} + +// Cost represents the pricing information for a model +type Cost struct { + Input float64 `json:"input"` + Output float64 `json:"output"` + CacheRead *float64 `json:"cache_read,omitempty"` + CacheWrite *float64 `json:"cache_write,omitempty"` +} + +// Limit represents the context and output limits for a model +type Limit struct { + Context int `json:"context"` + Output int `json:"output"` +} + +// ProviderInfo represents information about a model provider +type ProviderInfo struct { + ID string `json:"id"` + Env []string `json:"env"` + NPM string `json:"npm"` + Name string `json:"name"` + Models map[string]ModelInfo `json:"models"` +} + +const codeTemplate = `// Code generated by go generate; DO NOT EDIT. +// Generated at: {{.Timestamp}} + +package models + +// ModelInfo represents information about a specific model +type ModelInfo struct { + ID string + Name string + Attachment bool + Reasoning bool + Temperature bool + Cost Cost + Limit Limit +} + +// Cost represents the pricing information for a model +type Cost struct { + Input float64 + Output float64 + CacheRead *float64 + CacheWrite *float64 +} + +// Limit represents the context and output limits for a model +type Limit struct { + Context int + Output int +} + +// ProviderInfo represents information about a model provider +type ProviderInfo struct { + ID string + Env []string + NPM string + Name string + Models map[string]ModelInfo +} + +// GetModelsData returns the static models data from models.dev +func GetModelsData() map[string]ProviderInfo { + return map[string]ProviderInfo{ +{{- range $providerID, $provider := .Providers}} + "{{$providerID}}": { + ID: "{{$provider.ID}}", + Env: []string{ {{- range $i, $env := $provider.Env}}{{if $i}}, {{end}}"{{$env}}"{{end}} }, + NPM: "{{$provider.NPM}}", + Name: "{{$provider.Name}}", + Models: map[string]ModelInfo{ +{{- range $modelID, $model := $provider.Models}} + "{{$modelID}}": { + ID: "{{$model.ID}}", + Name: "{{$model.Name}}", + Attachment: {{$model.Attachment}}, + Reasoning: {{$model.Reasoning}}, + Temperature: {{$model.Temperature}}, + Cost: Cost{ + Input: {{$model.Cost.Input}}, + Output: {{$model.Cost.Output}}, + {{- if $model.Cost.CacheRead}} + CacheRead: &[]float64{{"{"}}{{$model.Cost.CacheRead}}{{"}"}}[0], + {{- else}} + CacheRead: nil, + {{- end}} + {{- if $model.Cost.CacheWrite}} + CacheWrite: &[]float64{{"{"}}{{$model.Cost.CacheWrite}}{{"}"}}[0], + {{- else}} + CacheWrite: nil, + {{- end}} + }, + Limit: Limit{ + Context: {{$model.Limit.Context}}, + Output: {{$model.Limit.Output}}, + }, + }, +{{- end}} + }, + }, +{{- end}} + } +} +` + +func main() { + fmt.Println("Fetching models data from models.dev...") + + // Fetch data from API + resp, err := http.Get("https://models.dev/api.json") + if err != nil { + fmt.Fprintf(os.Stderr, "Error fetching data: %v\n", err) + os.Exit(1) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + fmt.Fprintf(os.Stderr, "API returned status %d\n", resp.StatusCode) + os.Exit(1) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading response: %v\n", err) + os.Exit(1) + } + + // Parse JSON + var providers map[string]ProviderInfo + if err := json.Unmarshal(body, &providers); err != nil { + fmt.Fprintf(os.Stderr, "Error parsing JSON: %v\n", err) + os.Exit(1) + } + + // Generate Go code + tmpl, err := template.New("models").Parse(codeTemplate) + if err != nil { + fmt.Fprintf(os.Stderr, "Error parsing template: %v\n", err) + os.Exit(1) + } + + // Create output file + file, err := os.Create("models_data.go") + if err != nil { + fmt.Fprintf(os.Stderr, "Error creating output file: %v\n", err) + os.Exit(1) + } + defer file.Close() + + // Execute template + data := struct { + Providers map[string]ProviderInfo + Timestamp string + }{ + Providers: providers, + Timestamp: time.Now().Format(time.RFC3339), + } + + if err := tmpl.Execute(file, data); err != nil { + fmt.Fprintf(os.Stderr, "Error executing template: %v\n", err) + os.Exit(1) + } + + fmt.Printf("Generated models_data.go with %d providers\n", len(providers)) + + // Print summary + for providerID, provider := range providers { + fmt.Printf(" %s: %d models\n", providerID, len(provider.Models)) + } +} diff --git a/internal/models/models_data.go b/internal/models/models_data.go new file mode 100644 index 00000000..d1d78829 --- /dev/null +++ b/internal/models/models_data.go @@ -0,0 +1,2529 @@ +// Code generated by go generate; DO NOT EDIT. +// Generated at: 2025-06-18T13:43:26+03:00 + +package models + +// ModelInfo represents information about a specific model +type ModelInfo struct { + ID string + Name string + Attachment bool + Reasoning bool + Temperature bool + Cost Cost + Limit Limit +} + +// Cost represents the pricing information for a model +type Cost struct { + Input float64 + Output float64 + CacheRead *float64 + CacheWrite *float64 +} + +// Limit represents the context and output limits for a model +type Limit struct { + Context int + Output int +} + +// ProviderInfo represents information about a model provider +type ProviderInfo struct { + ID string + Env []string + NPM string + Name string + Models map[string]ModelInfo +} + +// GetModelsData returns the static models data from models.dev +func GetModelsData() map[string]ProviderInfo { + return map[string]ProviderInfo{ + "amazon-bedrock": { + ID: "amazon-bedrock", + Env: []string{"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION"}, + NPM: "@ai-sdk/amazon-bedrock", + Name: "Amazon Bedrock", + Models: map[string]ModelInfo{ + "ai21.jamba-1-5-large-v1:0": { + ID: "ai21.jamba-1-5-large-v1:0", + Name: "Jamba 1.5 Large", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 8, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 256000, + Output: 4096, + }, + }, + "ai21.jamba-1-5-mini-v1:0": { + ID: "ai21.jamba-1-5-mini-v1:0", + Name: "Jamba 1.5 Mini", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.2, + Output: 0.4, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 256000, + Output: 4096, + }, + }, + "amazon.nova-lite-v1:0": { + ID: "amazon.nova-lite-v1:0", + Name: "Nova Lite", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.06, + Output: 0.24, + CacheRead: &[]float64{0.015}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 300000, + Output: 8192, + }, + }, + "amazon.nova-micro-v1:0": { + ID: "amazon.nova-micro-v1:0", + Name: "Nova Micro", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.035, + Output: 0.14, + CacheRead: &[]float64{0.00875}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 8192, + }, + }, + "amazon.nova-premier-v1:0": { + ID: "amazon.nova-premier-v1:0", + Name: "Nova Premier", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 2.5, + Output: 12.5, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1000000, + Output: 16384, + }, + }, + "amazon.nova-pro-v1:0": { + ID: "amazon.nova-pro-v1:0", + Name: "Nova Pro", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.8, + Output: 3.2, + CacheRead: &[]float64{0.2}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 300000, + Output: 8192, + }, + }, + "anthropic.claude-3-5-haiku-20241022-v1:0": { + ID: "anthropic.claude-3-5-haiku-20241022-v1:0", + Name: "Claude 3.5 Haiku", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.8, + Output: 4, + CacheRead: &[]float64{0.08}[0], + CacheWrite: &[]float64{1}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 8192, + }, + }, + "anthropic.claude-3-5-sonnet-20240620-v1:0": { + ID: "anthropic.claude-3-5-sonnet-20240620-v1:0", + Name: "Claude 3.5 Sonnet", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{0.3}[0], + CacheWrite: &[]float64{3.75}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 8192, + }, + }, + "anthropic.claude-3-5-sonnet-20241022-v2:0": { + ID: "anthropic.claude-3-5-sonnet-20241022-v2:0", + Name: "Claude 3.5 Sonnet v2", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{0.3}[0], + CacheWrite: &[]float64{3.75}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 8192, + }, + }, + "anthropic.claude-3-7-sonnet-20250219-v1:0": { + ID: "anthropic.claude-3-7-sonnet-20250219-v1:0", + Name: "Claude 3.7 Sonnet", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{0.3}[0], + CacheWrite: &[]float64{3.75}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 8192, + }, + }, + "anthropic.claude-3-haiku-20240307-v1:0": { + ID: "anthropic.claude-3-haiku-20240307-v1:0", + Name: "Claude 3 Haiku", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.25, + Output: 1.25, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 4096, + }, + }, + "anthropic.claude-3-opus-20240229-v1:0": { + ID: "anthropic.claude-3-opus-20240229-v1:0", + Name: "Claude 3 Opus", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 15, + Output: 75, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 4096, + }, + }, + "anthropic.claude-3-sonnet-20240229-v1:0": { + ID: "anthropic.claude-3-sonnet-20240229-v1:0", + Name: "Claude 3 Sonnet", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 4096, + }, + }, + "anthropic.claude-instant-v1": { + ID: "anthropic.claude-instant-v1", + Name: "Claude Instant", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.8, + Output: 2.4, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 100000, + Output: 4096, + }, + }, + "anthropic.claude-opus-4-20250514-v1:0": { + ID: "anthropic.claude-opus-4-20250514-v1:0", + Name: "Claude Opus 4", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 15, + Output: 75, + CacheRead: &[]float64{1.5}[0], + CacheWrite: &[]float64{18.75}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 32000, + }, + }, + "anthropic.claude-sonnet-4-20250514-v1:0": { + ID: "anthropic.claude-sonnet-4-20250514-v1:0", + Name: "Claude Sonnet 4", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{0.3}[0], + CacheWrite: &[]float64{3.75}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 64000, + }, + }, + "anthropic.claude-v2": { + ID: "anthropic.claude-v2", + Name: "Claude 2", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 8, + Output: 24, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 100000, + Output: 4096, + }, + }, + "anthropic.claude-v2:1": { + ID: "anthropic.claude-v2:1", + Name: "Claude 2.1", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 8, + Output: 24, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 4096, + }, + }, + "cohere.command-light-text-v14": { + ID: "cohere.command-light-text-v14", + Name: "Command Light", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.3, + Output: 0.6, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 4096, + Output: 4096, + }, + }, + "cohere.command-r-plus-v1:0": { + ID: "cohere.command-r-plus-v1:0", + Name: "Command R+", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 4096, + }, + }, + "cohere.command-r-v1:0": { + ID: "cohere.command-r-v1:0", + Name: "Command R", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.5, + Output: 1.5, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 4096, + }, + }, + "cohere.command-text-v14": { + ID: "cohere.command-text-v14", + Name: "Command", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 1.5, + Output: 2, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 4096, + Output: 4096, + }, + }, + "deepseek.r1-v1:0": { + ID: "deepseek.r1-v1:0", + Name: "DeepSeek-R1", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 1.35, + Output: 5.4, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 8192, + }, + }, + "meta.llama3-1-70b-instruct-v1:0": { + ID: "meta.llama3-1-70b-instruct-v1:0", + Name: "Llama 3.1 70B Instruct", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.72, + Output: 0.72, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 4096, + }, + }, + "meta.llama3-1-8b-instruct-v1:0": { + ID: "meta.llama3-1-8b-instruct-v1:0", + Name: "Llama 3.1 8B Instruct", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.22, + Output: 0.22, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 4096, + }, + }, + "meta.llama3-2-11b-instruct-v1:0": { + ID: "meta.llama3-2-11b-instruct-v1:0", + Name: "Llama 3.2 11B Instruct", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.16, + Output: 0.16, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 4096, + }, + }, + "meta.llama3-2-1b-instruct-v1:0": { + ID: "meta.llama3-2-1b-instruct-v1:0", + Name: "Llama 3.2 1B Instruct", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.1, + Output: 0.1, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131000, + Output: 4096, + }, + }, + "meta.llama3-2-3b-instruct-v1:0": { + ID: "meta.llama3-2-3b-instruct-v1:0", + Name: "Llama 3.2 3B Instruct", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.15, + Output: 0.15, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131000, + Output: 4096, + }, + }, + "meta.llama3-2-90b-instruct-v1:0": { + ID: "meta.llama3-2-90b-instruct-v1:0", + Name: "Llama 3.2 90B Instruct", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.72, + Output: 0.72, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 4096, + }, + }, + "meta.llama3-3-70b-instruct-v1:0": { + ID: "meta.llama3-3-70b-instruct-v1:0", + Name: "Llama 3.3 70B Instruct", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.72, + Output: 0.72, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 4096, + }, + }, + "meta.llama3-70b-instruct-v1:0": { + ID: "meta.llama3-70b-instruct-v1:0", + Name: "Llama 3 70B Instruct", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2.65, + Output: 3.5, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 8192, + Output: 2048, + }, + }, + "meta.llama3-8b-instruct-v1:0": { + ID: "meta.llama3-8b-instruct-v1:0", + Name: "Llama 3 8B Instruct", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.3, + Output: 0.6, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 8192, + Output: 2048, + }, + }, + "meta.llama4-maverick-17b-instruct-v1:0": { + ID: "meta.llama4-maverick-17b-instruct-v1:0", + Name: "Llama 4 Maverick 17B Instruct", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.24, + Output: 0.97, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1000000, + Output: 16384, + }, + }, + "meta.llama4-scout-17b-instruct-v1:0": { + ID: "meta.llama4-scout-17b-instruct-v1:0", + Name: "Llama 4 Scout 17B Instruct", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.17, + Output: 0.66, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 3500000, + Output: 16384, + }, + }, + }, + }, + "anthropic": { + ID: "anthropic", + Env: []string{"ANTHROPIC_API_KEY"}, + NPM: "@ai-sdk/anthropic", + Name: "Anthropic", + Models: map[string]ModelInfo{ + "claude-3-5-haiku-20241022": { + ID: "claude-3-5-haiku-20241022", + Name: "Claude 3.5 Haiku", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.8, + Output: 4, + CacheRead: &[]float64{0.08}[0], + CacheWrite: &[]float64{0.08}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 8192, + }, + }, + "claude-3-5-sonnet-20240620": { + ID: "claude-3-5-sonnet-20240620", + Name: "Claude 3.5 Sonnet", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{0.3}[0], + CacheWrite: &[]float64{0.3}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 8192, + }, + }, + "claude-3-5-sonnet-20241022": { + ID: "claude-3-5-sonnet-20241022", + Name: "Claude 3.5 Sonnet v2", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{0.3}[0], + CacheWrite: &[]float64{0.3}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 8192, + }, + }, + "claude-3-7-sonnet-20250219": { + ID: "claude-3-7-sonnet-20250219", + Name: "Claude 3.7 Sonnet", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{0.3}[0], + CacheWrite: &[]float64{0.3}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 64000, + }, + }, + "claude-3-haiku-20240307": { + ID: "claude-3-haiku-20240307", + Name: "Claude 3 Haiku", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.25, + Output: 1.25, + CacheRead: &[]float64{0.03}[0], + CacheWrite: &[]float64{0.03}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 4096, + }, + }, + "claude-3-opus-20240229": { + ID: "claude-3-opus-20240229", + Name: "Claude 3 Opus", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 15, + Output: 75, + CacheRead: &[]float64{1.5}[0], + CacheWrite: &[]float64{1.5}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 4096, + }, + }, + "claude-3-sonnet-20240229": { + ID: "claude-3-sonnet-20240229", + Name: "Claude 3 Sonnet", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{0.3}[0], + CacheWrite: &[]float64{0.3}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 4096, + }, + }, + "claude-opus-4-20250514": { + ID: "claude-opus-4-20250514", + Name: "Claude 4 Opus", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 15, + Output: 75, + CacheRead: &[]float64{1.5}[0], + CacheWrite: &[]float64{1.5}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 32000, + }, + }, + "claude-sonnet-4-20250514": { + ID: "claude-sonnet-4-20250514", + Name: "Claude 4 Sonnet", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{3.75}[0], + CacheWrite: &[]float64{0.3}[0], + }, + Limit: Limit{ + Context: 200000, + Output: 50000, + }, + }, + }, + }, + "azure": { + ID: "azure", + Env: []string{"AZURE_RESOURCE_NAME", "AZURE_API_KEY"}, + NPM: "@ai-sdk/azure", + Name: "Azure", + Models: map[string]ModelInfo{ + "gpt-3.5-turbo-0125": { + ID: "gpt-3.5-turbo-0125", + Name: "GPT-3.5 Turbo 0125", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.5, + Output: 1.5, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 16384, + Output: 16384, + }, + }, + "gpt-3.5-turbo-0301": { + ID: "gpt-3.5-turbo-0301", + Name: "GPT-3.5 Turbo 0301", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 1.5, + Output: 2, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 4096, + Output: 4096, + }, + }, + "gpt-3.5-turbo-0613": { + ID: "gpt-3.5-turbo-0613", + Name: "GPT-3.5 Turbo 0613", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 4, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 16384, + Output: 16384, + }, + }, + "gpt-3.5-turbo-1106": { + ID: "gpt-3.5-turbo-1106", + Name: "GPT-3.5 Turbo 1106", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 1, + Output: 2, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 16384, + Output: 16384, + }, + }, + "gpt-3.5-turbo-instruct": { + ID: "gpt-3.5-turbo-instruct", + Name: "GPT-3.5 Turbo Instruct", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 1.5, + Output: 2, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 4096, + Output: 4096, + }, + }, + "gpt-4": { + ID: "gpt-4", + Name: "GPT-4", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 60, + Output: 120, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 8192, + Output: 8192, + }, + }, + "gpt-4-32k": { + ID: "gpt-4-32k", + Name: "GPT-4 32K", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 60, + Output: 120, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 32768, + Output: 32768, + }, + }, + "gpt-4-turbo": { + ID: "gpt-4-turbo", + Name: "GPT-4 Turbo", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 10, + Output: 30, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 4096, + }, + }, + "gpt-4-turbo-vision": { + ID: "gpt-4-turbo-vision", + Name: "GPT-4 Turbo Vision", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 10, + Output: 30, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 4096, + }, + }, + "gpt-4.1": { + ID: "gpt-4.1", + Name: "GPT-4.1", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 8, + CacheRead: &[]float64{0.5}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1047576, + Output: 32768, + }, + }, + "gpt-4.1-mini": { + ID: "gpt-4.1-mini", + Name: "GPT-4.1 Mini", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.4, + Output: 1.6, + CacheRead: &[]float64{0.1}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1047576, + Output: 32768, + }, + }, + "gpt-4.1-nano": { + ID: "gpt-4.1-nano", + Name: "GPT-4.1 Nano", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.1, + Output: 0.4, + CacheRead: &[]float64{0.03}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1047576, + Output: 32768, + }, + }, + "gpt-4o": { + ID: "gpt-4o", + Name: "GPT-4o", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2.5, + Output: 10, + CacheRead: &[]float64{1.25}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 16384, + }, + }, + "gpt-4o-mini": { + ID: "gpt-4o-mini", + Name: "GPT-4o Mini", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.15, + Output: 0.6, + CacheRead: &[]float64{0.08}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 16384, + }, + }, + "o1": { + ID: "o1", + Name: "o1", + Attachment: false, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 15, + Output: 60, + CacheRead: &[]float64{7.5}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + "o1-mini": { + ID: "o1-mini", + Name: "o1-mini", + Attachment: false, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 1.1, + Output: 4.4, + CacheRead: &[]float64{0.55}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 65536, + }, + }, + "o1-preview": { + ID: "o1-preview", + Name: "o1-preview", + Attachment: false, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 16.5, + Output: 66, + CacheRead: &[]float64{8.25}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 32768, + }, + }, + "o3": { + ID: "o3", + Name: "o3", + Attachment: true, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 2, + Output: 8, + CacheRead: &[]float64{0.5}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + "o3-mini": { + ID: "o3-mini", + Name: "o3-mini", + Attachment: false, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 1.1, + Output: 4.4, + CacheRead: &[]float64{0.55}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + "o4-mini": { + ID: "o4-mini", + Name: "o4-mini", + Attachment: true, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 1.1, + Output: 4.4, + CacheRead: &[]float64{0.28}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + }, + }, + "deepseek": { + ID: "deepseek", + Env: []string{"DEEPSEEK_API_KEY"}, + NPM: "@ai-sdk/openai", + Name: "DeepSeek", + Models: map[string]ModelInfo{ + "deepseek-chat": { + ID: "deepseek-chat", + Name: "DeepSeek Chat", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.27, + Output: 1.1, + CacheRead: &[]float64{0.07}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 65536, + Output: 8192, + }, + }, + "deepseek-reasoner": { + ID: "deepseek-reasoner", + Name: "DeepSeek Reasoner", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.55, + Output: 2.19, + CacheRead: &[]float64{0.14}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 65536, + Output: 8192, + }, + }, + }, + }, + "google": { + ID: "google", + Env: []string{"GOOGLE_GENERATIVE_AI_API_KEY"}, + NPM: "@ai-sdk/google", + Name: "Google", + Models: map[string]ModelInfo{ + "gemini-1.5-flash": { + ID: "gemini-1.5-flash", + Name: "Gemini 1.5 Flash", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.075, + Output: 0.3, + CacheRead: &[]float64{0.01875}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1000000, + Output: 8192, + }, + }, + "gemini-1.5-flash-8b": { + ID: "gemini-1.5-flash-8b", + Name: "Gemini 1.5 Flash-8B", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.0375, + Output: 0.15, + CacheRead: &[]float64{0.01}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1000000, + Output: 8192, + }, + }, + "gemini-1.5-pro": { + ID: "gemini-1.5-pro", + Name: "Gemini 1.5 Pro", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 1.25, + Output: 5, + CacheRead: &[]float64{0.3125}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 2000000, + Output: 8192, + }, + }, + "gemini-2.0-flash": { + ID: "gemini-2.0-flash", + Name: "Gemini 2.0 Flash", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.1, + Output: 0.4, + CacheRead: &[]float64{0.025}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1048576, + Output: 8192, + }, + }, + "gemini-2.0-flash-lite": { + ID: "gemini-2.0-flash-lite", + Name: "Gemini 2.0 Flash Lite", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.075, + Output: 0.3, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1048576, + Output: 8192, + }, + }, + "gemini-2.5-flash-preview-04-17": { + ID: "gemini-2.5-flash-preview-04-17", + Name: "Gemini 2.5 Flash Preview 04-17", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.15, + Output: 0.6, + CacheRead: &[]float64{0.0375}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1048576, + Output: 65536, + }, + }, + "gemini-2.5-flash-preview-05-20": { + ID: "gemini-2.5-flash-preview-05-20", + Name: "Gemini 2.5 Flash Preview 05-20", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.15, + Output: 0.6, + CacheRead: &[]float64{0.0375}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1048576, + Output: 65536, + }, + }, + "gemini-2.5-pro-preview-05-06": { + ID: "gemini-2.5-pro-preview-05-06", + Name: "Gemini 2.5 Pro Preview 05-06", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 1.25, + Output: 10, + CacheRead: &[]float64{0.31}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1048576, + Output: 65536, + }, + }, + "gemini-2.5-pro-preview-06-05": { + ID: "gemini-2.5-pro-preview-06-05", + Name: "Gemini 2.5 Pro Preview 06-05", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 1.25, + Output: 10, + CacheRead: &[]float64{0.31}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1048576, + Output: 65536, + }, + }, + }, + }, + "groq": { + ID: "groq", + Env: []string{"GROQ_API_KEY"}, + NPM: "@ai-sdk/groq", + Name: "Groq", + Models: map[string]ModelInfo{ + "deepseek-r1-distill-llama-70b": { + ID: "deepseek-r1-distill-llama-70b", + Name: "DeepSeek R1 Distill Llama 70B", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.75, + Output: 0.99, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "gemma2-9b-it": { + ID: "gemma2-9b-it", + Name: "Gemma 2 9B IT", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.2, + Output: 0.2, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 8192, + Output: 8192, + }, + }, + "llama-3.1-8b-instant": { + ID: "llama-3.1-8b-instant", + Name: "Llama 3.1 8B Instant", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.05, + Output: 0.08, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "llama-3.3-70b-versatile": { + ID: "llama-3.3-70b-versatile", + Name: "Llama 3.3 70B Versatile", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.59, + Output: 0.79, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131072, + Output: 32768, + }, + }, + "llama-guard-3-8b": { + ID: "llama-guard-3-8b", + Name: "Llama Guard 3 8B", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.2, + Output: 0.2, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 8192, + Output: 8192, + }, + }, + "llama3-70b-8192": { + ID: "llama3-70b-8192", + Name: "Llama 3 70B", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.59, + Output: 0.79, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 8192, + Output: 8192, + }, + }, + "llama3-8b-8192": { + ID: "llama3-8b-8192", + Name: "Llama 3 8B", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.05, + Output: 0.08, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 8192, + Output: 8192, + }, + }, + "meta-llama%2Fllama-4-maverick-17b-128e-instruct": { + ID: "meta-llama%2Fllama-4-maverick-17b-128e-instruct", + Name: "Llama 4 Maverick 17B", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.2, + Output: 0.6, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "meta-llama%2Fllama-4-scout-17b-16e-instruct": { + ID: "meta-llama%2Fllama-4-scout-17b-16e-instruct", + Name: "Llama 4 Scout 17B", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.11, + Output: 0.34, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "meta-llama%2Fllama-guard-4-12b": { + ID: "meta-llama%2Fllama-guard-4-12b", + Name: "Llama Guard 4 12B", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.2, + Output: 0.2, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131072, + Output: 128, + }, + }, + "mistral-saba-24b": { + ID: "mistral-saba-24b", + Name: "Mistral Saba 24B", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.79, + Output: 0.79, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 32768, + Output: 32768, + }, + }, + "qwen%2Fqwen3-32b": { + ID: "qwen%2Fqwen3-32b", + Name: "Qwen3 32B", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.29, + Output: 0.59, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131072, + Output: 16384, + }, + }, + "qwen-qwq-32b": { + ID: "qwen-qwq-32b", + Name: "Qwen QwQ 32B", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.29, + Output: 0.39, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131072, + Output: 16384, + }, + }, + }, + }, + "mistral": { + ID: "mistral", + Env: []string{"MISTRAL_API_KEY"}, + NPM: "@ai-sdk/mistral", + Name: "Mistral", + Models: map[string]ModelInfo{ + "codestral-latest": { + ID: "codestral-latest", + Name: "Codestral", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.3, + Output: 0.9, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 256000, + Output: 4096, + }, + }, + "devstral-small-2505": { + ID: "devstral-small-2505", + Name: "Devstral", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.1, + Output: 0.3, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 128000, + }, + }, + "magistral-medium-latest": { + ID: "magistral-medium-latest", + Name: "Magistral Medium", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 5, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 16384, + }, + }, + "magistral-small": { + ID: "magistral-small", + Name: "Magistral Small", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.5, + Output: 1.5, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 128000, + }, + }, + "ministral-3b-latest": { + ID: "ministral-3b-latest", + Name: "Ministral 3B", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.04, + Output: 0.04, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 128000, + }, + }, + "ministral-8b-latest": { + ID: "ministral-8b-latest", + Name: "Ministral 8B", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.1, + Output: 0.1, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 128000, + }, + }, + "mistral-large-latest": { + ID: "mistral-large-latest", + Name: "Mistral Large", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 6, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 131072, + Output: 16384, + }, + }, + "mistral-medium-latest": { + ID: "mistral-medium-latest", + Name: "Mistral Medium", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.4, + Output: 2, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 16384, + }, + }, + "mistral-nemo": { + ID: "mistral-nemo", + Name: "Mistral Nemo", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.15, + Output: 0.15, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 128000, + }, + }, + "mistral-small-latest": { + ID: "mistral-small-latest", + Name: "Mistral Small", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.1, + Output: 0.3, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 16384, + }, + }, + "open-mistral-7b": { + ID: "open-mistral-7b", + Name: "Mistral 7B", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.25, + Output: 0.25, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 8000, + Output: 8000, + }, + }, + "open-mixtral-8x22b": { + ID: "open-mixtral-8x22b", + Name: "Mixtral 8x22B", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 6, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 64000, + Output: 64000, + }, + }, + "open-mixtral-8x7b": { + ID: "open-mixtral-8x7b", + Name: "Mixtral 8x7B", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.7, + Output: 0.7, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 32000, + Output: 32000, + }, + }, + "pixtral-12b": { + ID: "pixtral-12b", + Name: "Pixtral 12B", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.15, + Output: 0.15, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 128000, + }, + }, + "pixtral-large-latest": { + ID: "pixtral-large-latest", + Name: "Pixtral Large", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 6, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 128000, + }, + }, + }, + }, + "morph": { + ID: "morph", + Env: []string{"MORPH_API_KEY"}, + NPM: "", + Name: "Morph", + Models: map[string]ModelInfo{ + "auto": { + ID: "auto", + Name: "Auto", + Attachment: false, + Reasoning: false, + Temperature: false, + Cost: Cost{ + Input: 0.85, + Output: 1.55, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 32000, + Output: 32000, + }, + }, + "morph-v3-fast": { + ID: "morph-v3-fast", + Name: "Morph v3 Fast", + Attachment: false, + Reasoning: false, + Temperature: false, + Cost: Cost{ + Input: 0.8, + Output: 1.2, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 16000, + Output: 16000, + }, + }, + "morph-v3-large": { + ID: "morph-v3-large", + Name: "Morph v3 Large", + Attachment: false, + Reasoning: false, + Temperature: false, + Cost: Cost{ + Input: 0.9, + Output: 1.9, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 32000, + Output: 32000, + }, + }, + }, + }, + "openai": { + ID: "openai", + Env: []string{"OPENAI_API_KEY"}, + NPM: "@ai-sdk/openai", + Name: "OpenAI", + Models: map[string]ModelInfo{ + "codex-mini-latest": { + ID: "codex-mini-latest", + Name: "Codex Mini", + Attachment: true, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 1.5, + Output: 6, + CacheRead: &[]float64{0.375}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + "gpt-4": { + ID: "gpt-4", + Name: "GPT-4 Turbo", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 10, + Output: 30, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 8192, + Output: 8192, + }, + }, + "gpt-4-turbo": { + ID: "gpt-4-turbo", + Name: "GPT-4 Turbo", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 10, + Output: 30, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 4096, + }, + }, + "gpt-4.1": { + ID: "gpt-4.1", + Name: "GPT-4.1", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 8, + CacheRead: &[]float64{0.5}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1047576, + Output: 32768, + }, + }, + "gpt-4.1-mini": { + ID: "gpt-4.1-mini", + Name: "GPT-4.1 Mini", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.4, + Output: 1.6, + CacheRead: &[]float64{0.1}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1047576, + Output: 32768, + }, + }, + "gpt-4.1-nano": { + ID: "gpt-4.1-nano", + Name: "GPT-4.1 Nano", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.1, + Output: 0.4, + CacheRead: &[]float64{0.03}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 1047576, + Output: 32768, + }, + }, + "gpt-4.5-preview": { + ID: "gpt-4.5-preview", + Name: "GPT-4.5 Preview", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 75, + Output: 150, + CacheRead: &[]float64{37.5}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 16384, + }, + }, + "gpt-4o": { + ID: "gpt-4o", + Name: "GPT-4o", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2.5, + Output: 10, + CacheRead: &[]float64{1.25}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 16384, + }, + }, + "gpt-4o-mini": { + ID: "gpt-4o-mini", + Name: "GPT-4o Mini", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 0.15, + Output: 0.6, + CacheRead: &[]float64{0.08}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 16384, + }, + }, + "o1": { + ID: "o1", + Name: "o1", + Attachment: true, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 15, + Output: 60, + CacheRead: &[]float64{7.5}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + "o1-mini": { + ID: "o1-mini", + Name: "o1-mini", + Attachment: false, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 1.1, + Output: 4.4, + CacheRead: &[]float64{0.55}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 65536, + }, + }, + "o1-preview": { + ID: "o1-preview", + Name: "o1-preview", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 15, + Output: 60, + CacheRead: &[]float64{7.5}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 32768, + }, + }, + "o1-pro": { + ID: "o1-pro", + Name: "o1-pro", + Attachment: true, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 150, + Output: 600, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + "o3": { + ID: "o3", + Name: "o3", + Attachment: true, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 2, + Output: 8, + CacheRead: &[]float64{0.5}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + "o3-mini": { + ID: "o3-mini", + Name: "o3-mini", + Attachment: false, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 1.1, + Output: 4.4, + CacheRead: &[]float64{0.55}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + "o3-pro": { + ID: "o3-pro", + Name: "o3-pro", + Attachment: true, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 20, + Output: 80, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + "o4-mini": { + ID: "o4-mini", + Name: "o4-mini", + Attachment: true, + Reasoning: true, + Temperature: false, + Cost: Cost{ + Input: 1.1, + Output: 4.4, + CacheRead: &[]float64{0.28}[0], + CacheWrite: nil, + }, + Limit: Limit{ + Context: 200000, + Output: 100000, + }, + }, + }, + }, + "vercel": { + ID: "vercel", + Env: []string{"V0_API_KEY"}, + NPM: "@ai-sdk/vercel", + Name: "Vercel", + Models: map[string]ModelInfo{ + "v0-1.0-md": { + ID: "v0-1.0-md", + Name: "v0-1.0-md", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 32000, + }, + }, + "v0-1.5-lg": { + ID: "v0-1.5-lg", + Name: "v0-1.5-lg", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 15, + Output: 75, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 512000, + Output: 32000, + }, + }, + "v0-1.5-md": { + ID: "v0-1.5-md", + Name: "v0-1.5-md", + Attachment: true, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: nil, + CacheWrite: nil, + }, + Limit: Limit{ + Context: 128000, + Output: 32000, + }, + }, + }, + }, + "xai": { + ID: "xai", + Env: []string{"XAI_API_KEY"}, + NPM: "@ai-sdk/xai", + Name: "xAI", + Models: map[string]ModelInfo{ + "grok-2": { + ID: "grok-2", + Name: "Grok 2", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 10, + CacheRead: &[]float64{2}[0], + CacheWrite: &[]float64{10}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-2-1212": { + ID: "grok-2-1212", + Name: "Grok 2 (1212)", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 10, + CacheRead: &[]float64{2}[0], + CacheWrite: &[]float64{10}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-2-latest": { + ID: "grok-2-latest", + Name: "Grok 2 Latest", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 10, + CacheRead: &[]float64{2}[0], + CacheWrite: &[]float64{10}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-2-vision": { + ID: "grok-2-vision", + Name: "Grok 2 Vision", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 10, + CacheRead: &[]float64{2}[0], + CacheWrite: &[]float64{10}[0], + }, + Limit: Limit{ + Context: 8192, + Output: 4096, + }, + }, + "grok-2-vision-1212": { + ID: "grok-2-vision-1212", + Name: "Grok 2 Vision (1212)", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 10, + CacheRead: &[]float64{2}[0], + CacheWrite: &[]float64{10}[0], + }, + Limit: Limit{ + Context: 8192, + Output: 4096, + }, + }, + "grok-2-vision-latest": { + ID: "grok-2-vision-latest", + Name: "Grok 2 Vision Latest", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 2, + Output: 10, + CacheRead: &[]float64{2}[0], + CacheWrite: &[]float64{10}[0], + }, + Limit: Limit{ + Context: 8192, + Output: 4096, + }, + }, + "grok-3": { + ID: "grok-3", + Name: "Grok 3", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{0.75}[0], + CacheWrite: &[]float64{15}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-3-fast": { + ID: "grok-3-fast", + Name: "Grok 3 Fast", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 5, + Output: 25, + CacheRead: &[]float64{1.25}[0], + CacheWrite: &[]float64{25}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-3-fast-latest": { + ID: "grok-3-fast-latest", + Name: "Grok 3 Fast Latest", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 5, + Output: 25, + CacheRead: &[]float64{1.25}[0], + CacheWrite: &[]float64{25}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-3-latest": { + ID: "grok-3-latest", + Name: "Grok 3 Latest", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 3, + Output: 15, + CacheRead: &[]float64{0.75}[0], + CacheWrite: &[]float64{15}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-3-mini": { + ID: "grok-3-mini", + Name: "Grok 3 Mini", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.3, + Output: 0.5, + CacheRead: &[]float64{0.075}[0], + CacheWrite: &[]float64{0.5}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-3-mini-fast": { + ID: "grok-3-mini-fast", + Name: "Grok 3 Mini Fast", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.6, + Output: 4, + CacheRead: &[]float64{0.15}[0], + CacheWrite: &[]float64{4}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-3-mini-fast-latest": { + ID: "grok-3-mini-fast-latest", + Name: "Grok 3 Mini Fast Latest", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.6, + Output: 4, + CacheRead: &[]float64{0.15}[0], + CacheWrite: &[]float64{4}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-3-mini-latest": { + ID: "grok-3-mini-latest", + Name: "Grok 3 Mini Latest", + Attachment: false, + Reasoning: true, + Temperature: true, + Cost: Cost{ + Input: 0.3, + Output: 0.5, + CacheRead: &[]float64{0.075}[0], + CacheWrite: &[]float64{0.5}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 8192, + }, + }, + "grok-beta": { + ID: "grok-beta", + Name: "Grok Beta", + Attachment: false, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 5, + Output: 15, + CacheRead: &[]float64{5}[0], + CacheWrite: &[]float64{15}[0], + }, + Limit: Limit{ + Context: 131072, + Output: 4096, + }, + }, + "grok-vision-beta": { + ID: "grok-vision-beta", + Name: "Grok Vision Beta", + Attachment: true, + Reasoning: false, + Temperature: true, + Cost: Cost{ + Input: 5, + Output: 15, + CacheRead: &[]float64{5}[0], + CacheWrite: &[]float64{15}[0], + }, + Limit: Limit{ + Context: 8192, + Output: 4096, + }, + }, + }, + }, + } +} diff --git a/internal/models/providers.go b/internal/models/providers.go index e2241473..bd63db24 100644 --- a/internal/models/providers.go +++ b/internal/models/providers.go @@ -45,6 +45,32 @@ func CreateProvider(ctx context.Context, config *ProviderConfig) (model.ToolCall provider := parts[0] modelName := parts[1] + // Get the global registry for validation + registry := GetGlobalRegistry() + + // Validate the model exists (skip for ollama as it's not in models.dev) + if provider != "ollama" { + modelInfo, err := registry.ValidateModel(provider, modelName) + if err != nil { + // Provide helpful suggestions + suggestions := registry.SuggestModels(provider, modelName) + if len(suggestions) > 0 { + return nil, fmt.Errorf("%v. Did you mean one of: %s", err, strings.Join(suggestions, ", ")) + } + return nil, err + } + + // Validate environment variables + if err := registry.ValidateEnvironment(provider, config.ProviderAPIKey); err != nil { + return nil, err + } + + // Validate configuration parameters against model capabilities + if err := validateModelConfig(config, modelInfo); err != nil { + return nil, err + } + } + switch provider { case "anthropic": return createAnthropicProvider(ctx, config, modelName) @@ -59,6 +85,22 @@ func CreateProvider(ctx context.Context, config *ProviderConfig) (model.ToolCall } } +// validateModelConfig validates configuration parameters against model capabilities +func validateModelConfig(config *ProviderConfig, modelInfo *ModelInfo) error { + // Check if temperature is supported + if config.Temperature != nil && !modelInfo.Temperature { + return fmt.Errorf("model %s does not support temperature parameter", modelInfo.ID) + } + + // Warn about context limits if MaxTokens is set too high + if config.MaxTokens > modelInfo.Limit.Output { + return fmt.Errorf("max_tokens (%d) exceeds model's output limit (%d) for %s", + config.MaxTokens, modelInfo.Limit.Output, modelInfo.ID) + } + + return nil +} + func createAnthropicProvider(ctx context.Context, config *ProviderConfig, modelName string) (model.ToolCallingChatModel, error) { apiKey := config.ProviderAPIKey if apiKey == "" { diff --git a/internal/models/registry.go b/internal/models/registry.go new file mode 100644 index 00000000..6b025390 --- /dev/null +++ b/internal/models/registry.go @@ -0,0 +1,131 @@ +//go:generate go run generate_models.go + +package models + +import ( + "fmt" + "os" + "strings" +) + +// ModelsRegistry provides validation and information about models +type ModelsRegistry struct { + providers map[string]ProviderInfo +} + +// NewModelsRegistry creates a new models registry with static data +func NewModelsRegistry() *ModelsRegistry { + return &ModelsRegistry{ + providers: GetModelsData(), + } +} + +// ValidateModel validates if a model exists and returns detailed information +func (r *ModelsRegistry) ValidateModel(provider, modelID string) (*ModelInfo, error) { + providerInfo, exists := r.providers[provider] + if !exists { + return nil, fmt.Errorf("unsupported provider: %s", provider) + } + + modelInfo, exists := providerInfo.Models[modelID] + if !exists { + return nil, fmt.Errorf("model %s not found for provider %s", modelID, provider) + } + + return &modelInfo, nil +} + +// GetRequiredEnvVars returns the required environment variables for a provider +func (r *ModelsRegistry) GetRequiredEnvVars(provider string) ([]string, error) { + providerInfo, exists := r.providers[provider] + if !exists { + return nil, fmt.Errorf("unsupported provider: %s", provider) + } + + return providerInfo.Env, nil +} + +// ValidateEnvironment checks if required environment variables are set +func (r *ModelsRegistry) ValidateEnvironment(provider string, apiKey string) error { + envVars, err := r.GetRequiredEnvVars(provider) + if err != nil { + return err + } + + // If API key is provided via config, we don't need to check env vars + if apiKey != "" { + return nil + } + + var missingVars []string + for _, envVar := range envVars { + if os.Getenv(envVar) == "" { + missingVars = append(missingVars, envVar) + } + } + + if len(missingVars) > 0 { + return fmt.Errorf("missing required environment variables for %s: %s", + provider, strings.Join(missingVars, ", ")) + } + + return nil +} + +// SuggestModels returns similar model names when an invalid model is provided +func (r *ModelsRegistry) SuggestModels(provider, invalidModel string) []string { + providerInfo, exists := r.providers[provider] + if !exists { + return nil + } + + var suggestions []string + invalidLower := strings.ToLower(invalidModel) + + // Look for models that contain parts of the invalid model name + for modelID, modelInfo := range providerInfo.Models { + modelIDLower := strings.ToLower(modelID) + modelNameLower := strings.ToLower(modelInfo.Name) + + // Check if the invalid model is a substring of existing models + if strings.Contains(modelIDLower, invalidLower) || + strings.Contains(modelNameLower, invalidLower) || + strings.Contains(invalidLower, strings.ToLower(strings.Split(modelID, "-")[0])) { + suggestions = append(suggestions, modelID) + } + } + + // Limit suggestions to avoid overwhelming output + if len(suggestions) > 5 { + suggestions = suggestions[:5] + } + + return suggestions +} + +// GetSupportedProviders returns a list of all supported providers +func (r *ModelsRegistry) GetSupportedProviders() []string { + var providers []string + for providerID := range r.providers { + providers = append(providers, providerID) + } + return providers +} + +// GetModelsForProvider returns all models for a specific provider +func (r *ModelsRegistry) GetModelsForProvider(provider string) (map[string]ModelInfo, error) { + providerInfo, exists := r.providers[provider] + if !exists { + return nil, fmt.Errorf("unsupported provider: %s", provider) + } + + return providerInfo.Models, nil +} + +// Global registry instance +var globalRegistry = NewModelsRegistry() + +// GetGlobalRegistry returns the global models registry instance +func GetGlobalRegistry() *ModelsRegistry { + return globalRegistry +}