diff --git a/cmd/zenmux/main.go b/cmd/zenmux/main.go new file mode 100644 index 00000000..f86925ca --- /dev/null +++ b/cmd/zenmux/main.go @@ -0,0 +1,205 @@ +// Package main provides a command-line tool to fetch models from ZenMux +// and generate a configuration file for the provider. +package main + +import ( + "context" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "slices" + "strings" + "time" + + "github.com/charmbracelet/catwalk/pkg/catwalk" +) + +// Pricing represents a single pricing entry +type Pricing struct { + Value float64 `json:"value"` + Unit string `json:"unit"` + Currency string `json:"currency"` +} + +// ModelPricings contains all pricing information for a model +type ModelPricings struct { + Prompt []Pricing `json:"prompt"` + Completion []Pricing `json:"completion"` + InputCacheRead []Pricing `json:"input_cache_read"` + InputCacheWrite5Min []Pricing `json:"input_cache_write_5_min"` + InputCacheWrite1H []Pricing `json:"input_cache_write_1_h"` +} + +// Capabilities represents model capabilities +type Capabilities struct { + Reasoning bool `json:"reasoning"` +} + +// ZenMuxModel represents a model from ZenMux API with full details +type ZenMuxModel struct { + ID string `json:"id"` + DisplayName string `json:"display_name"` + CreatedAt string `json:"created_at"` + Type string `json:"type"` + InputModalities []string `json:"input_modalities"` + OutputModalities []string `json:"output_modalities"` + Capabilities Capabilities `json:"capabilities"` + ContextLength int64 `json:"context_length"` + Pricings ModelPricings `json:"pricings"` +} + +// ZenMuxModelsResponse is the response from ZenMux /api/anthropic/v1/models endpoint +type ZenMuxModelsResponse struct { + Data []ZenMuxModel `json:"data"` + HasMore bool `json:"has_more"` +} + +func fetchZenMuxModels() (*ZenMuxModelsResponse, error) { + client := &http.Client{Timeout: 30 * time.Second} + req, err := http.NewRequestWithContext( + context.Background(), + "GET", + "https://zenmux.ai/api/anthropic/v1/models", + nil, + ) + if err != nil { + return nil, err + } + + req.Header.Set("User-Agent", "Catwalk-Client/1.0") + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("status %d: %s", resp.StatusCode, body) + } + + var modelsResp ZenMuxModelsResponse + if err := json.NewDecoder(resp.Body).Decode(&modelsResp); err != nil { + return nil, err + } + + return &modelsResp, nil +} + +// getPricing extracts a pricing value, returns 0 if not available +func getPricing(pricings []Pricing) float64 { + if len(pricings) == 0 { + return 0 + } + return pricings[0].Value +} + +// supportsImages checks if model supports image input +func supportsImages(modalities []string) bool { + return slices.Contains(modalities, "image") +} + +// estimateMaxTokens provides a reasonable estimate for max output tokens +func estimateMaxTokens(contextLength int64, canReason bool) int64 { + if canReason { + // Reasoning models typically allow larger outputs + return min(contextLength/4, 50000) + } + // Standard models + return min(contextLength/10, 20000) +} + +func main() { + fmt.Println("Fetching models from ZenMux API...") + modelsResp, err := fetchZenMuxModels() + if err != nil { + log.Fatal("Error fetching ZenMux models:", err) + } + + zenmuxProvider := catwalk.Provider{ + Name: "ZenMux", + ID: "zenmux", + APIKey: "$ZENMUX_API_KEY", + APIEndpoint: "https://zenmux.ai/api/anthropic", + Type: catwalk.TypeAnthropic, + DefaultLargeModelID: "anthropic/claude-sonnet-4.5", + DefaultSmallModelID: "anthropic/claude-3.5-haiku", + Models: []catwalk.Model{}, + } + + fmt.Printf("Found %d models from API\n\n", len(modelsResp.Data)) + + addedCount := 0 + for _, model := range modelsResp.Data { + // Filter: require at least 20k context and text I/O + if model.ContextLength < 20000 { + continue + } + if !slices.Contains(model.InputModalities, "text") || + !slices.Contains(model.OutputModalities, "text") { + continue + } + + // Extract pricing information + costIn := getPricing(model.Pricings.Prompt) + costOut := getPricing(model.Pricings.Completion) + costInCached := getPricing(model.Pricings.InputCacheRead) + // Use 5-minute cache write price as it's more commonly used + costOutCached := getPricing(model.Pricings.InputCacheWrite5Min) + + // Build the model configuration + m := catwalk.Model{ + ID: model.ID, + Name: model.DisplayName, + CostPer1MIn: costIn, + CostPer1MOut: costOut, + CostPer1MInCached: costInCached, + CostPer1MOutCached: costOutCached, + ContextWindow: model.ContextLength, + DefaultMaxTokens: estimateMaxTokens(model.ContextLength, model.Capabilities.Reasoning), + CanReason: model.Capabilities.Reasoning, + SupportsImages: supportsImages(model.InputModalities), + } + + zenmuxProvider.Models = append(zenmuxProvider.Models, m) + addedCount++ + + reasoningStr := "" + if model.Capabilities.Reasoning { + reasoningStr = " [Reasoning]" + } + visionStr := "" + if m.SupportsImages { + visionStr = " [Vision]" + } + fmt.Printf("āœ“ %s%s%s\n", model.DisplayName, reasoningStr, visionStr) + } + + // Sort by name for better readability + slices.SortFunc(zenmuxProvider.Models, func(a, b catwalk.Model) int { + return strings.Compare(a.Name, b.Name) + }) + + fmt.Printf("\nšŸ“Š Summary:\n") + fmt.Printf(" Total from API: %d\n", len(modelsResp.Data)) + fmt.Printf(" Added to config: %d\n", addedCount) + fmt.Printf(" Filtered out: %d (context < 20k or no text I/O)\n\n", + len(modelsResp.Data)-addedCount) + + // Save to JSON file + data, err := json.MarshalIndent(zenmuxProvider, "", " ") + if err != nil { + log.Fatal("Error marshaling ZenMux provider:", err) + } + + outputPath := "internal/providers/configs/zenmux.json" + if err := os.WriteFile(outputPath, data, 0o600); err != nil { + log.Fatal("Error writing ZenMux provider config:", err) + } + + fmt.Printf("āœ… Successfully generated: %s\n", outputPath) +} diff --git a/internal/providers/configs/zenmux.json b/internal/providers/configs/zenmux.json new file mode 100644 index 00000000..33bab98c --- /dev/null +++ b/internal/providers/configs/zenmux.json @@ -0,0 +1,674 @@ +{ + "name": "ZenMux", + "id": "zenmux", + "api_key": "$ZENMUX_API_KEY", + "api_endpoint": "https://zenmux.ai/api/anthropic", + "type": "anthropic", + "default_large_model_id": "anthropic/claude-sonnet-4.5", + "default_small_model_id": "anthropic/claude-3.5-haiku", + "models": [ + { + "id": "anthropic/claude-3.5-haiku", + "name": "Anthropic: Claude 3.5 Haiku", + "cost_per_1m_in": 0.8, + "cost_per_1m_out": 4, + "cost_per_1m_in_cached": 0.08, + "cost_per_1m_out_cached": 1, + "context_window": 200000, + "default_max_tokens": 20000, + "can_reason": false, + "supports_attachments": true, + "options": {} + }, + { + "id": "anthropic/claude-3.5-sonnet", + "name": "Anthropic: Claude 3.5 Sonnet (Retiring Soon)", + "cost_per_1m_in": 3, + "cost_per_1m_out": 15, + "cost_per_1m_in_cached": 0.3, + "cost_per_1m_out_cached": 3.75, + "context_window": 200000, + "default_max_tokens": 20000, + "can_reason": false, + "supports_attachments": true, + "options": {} + }, + { + "id": "anthropic/claude-3.7-sonnet", + "name": "Anthropic: Claude 3.7 Sonnet", + "cost_per_1m_in": 3, + "cost_per_1m_out": 15, + "cost_per_1m_in_cached": 0.3, + "cost_per_1m_out_cached": 3.75, + "context_window": 200000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "anthropic/claude-haiku-4.5", + "name": "Anthropic: Claude Haiku 4.5", + "cost_per_1m_in": 1, + "cost_per_1m_out": 5, + "cost_per_1m_in_cached": 0.1, + "cost_per_1m_out_cached": 1.25, + "context_window": 200000, + "default_max_tokens": 20000, + "can_reason": false, + "supports_attachments": true, + "options": {} + }, + { + "id": "anthropic/claude-opus-4", + "name": "Anthropic: Claude Opus 4", + "cost_per_1m_in": 15, + "cost_per_1m_out": 75, + "cost_per_1m_in_cached": 1.5, + "cost_per_1m_out_cached": 18.75, + "context_window": 200000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "anthropic/claude-opus-4.1", + "name": "Anthropic: Claude Opus 4.1", + "cost_per_1m_in": 15, + "cost_per_1m_out": 75, + "cost_per_1m_in_cached": 1.5, + "cost_per_1m_out_cached": 18.75, + "context_window": 200000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "anthropic/claude-opus-4.5", + "name": "Anthropic: Claude Opus 4.5", + "cost_per_1m_in": 5, + "cost_per_1m_out": 25, + "cost_per_1m_in_cached": 0.5, + "cost_per_1m_out_cached": 6.25, + "context_window": 200000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "anthropic/claude-sonnet-4", + "name": "Anthropic: Claude Sonnet 4", + "cost_per_1m_in": 3, + "cost_per_1m_out": 15, + "cost_per_1m_in_cached": 0.3, + "cost_per_1m_out_cached": 3.75, + "context_window": 1000000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "anthropic/claude-sonnet-4.5", + "name": "Anthropic: Claude Sonnet 4.5", + "cost_per_1m_in": 3, + "cost_per_1m_out": 15, + "cost_per_1m_in_cached": 0.3, + "cost_per_1m_out_cached": 3.75, + "context_window": 200000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "baidu/ernie-5.0-thinking-preview", + "name": "Baidu: ERNIE-5.0-Thinking-Preview", + "cost_per_1m_in": 0.84, + "cost_per_1m_out": 3.37, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 128000, + "default_max_tokens": 32000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "deepseek/deepseek-v3.2", + "name": "DeepSeek: DeepSeek V3.2", + "cost_per_1m_in": 0.28, + "cost_per_1m_out": 0.43, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 128000, + "default_max_tokens": 32000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "deepseek/deepseek-chat", + "name": "DeepSeek: DeepSeek-V3.2 (Non-thinking Mode)", + "cost_per_1m_in": 0.28, + "cost_per_1m_out": 0.42, + "cost_per_1m_in_cached": 0.028, + "cost_per_1m_out_cached": 0, + "context_window": 128000, + "default_max_tokens": 12800, + "can_reason": false, + "supports_attachments": false, + "options": {} + }, + { + "id": "deepseek/deepseek-reasoner", + "name": "DeepSeek: DeepSeek-V3.2 (Thinking Mode)", + "cost_per_1m_in": 0.28, + "cost_per_1m_out": 0.42, + "cost_per_1m_in_cached": 0.028, + "cost_per_1m_out_cached": 0, + "context_window": 128000, + "default_max_tokens": 32000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "deepseek/deepseek-v3.2-exp", + "name": "DeepSeek: DeepSeek-V3.2-Exp", + "cost_per_1m_in": 0.216, + "cost_per_1m_out": 0.328, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 163840, + "default_max_tokens": 40960, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "google/gemini-2.5-flash", + "name": "Google: Gemini 2.5 Flash", + "cost_per_1m_in": 0.3, + "cost_per_1m_out": 2.5, + "cost_per_1m_in_cached": 0.075, + "cost_per_1m_out_cached": 0, + "context_window": 1048576, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "google/gemini-2.5-flash-lite", + "name": "Google: Gemini 2.5 Flash Lite", + "cost_per_1m_in": 0.1, + "cost_per_1m_out": 0.4, + "cost_per_1m_in_cached": 0.025, + "cost_per_1m_out_cached": 0, + "context_window": 1048576, + "default_max_tokens": 20000, + "can_reason": false, + "supports_attachments": true, + "options": {} + }, + { + "id": "google/gemini-2.5-pro", + "name": "Google: Gemini 2.5 Pro", + "cost_per_1m_in": 1.25, + "cost_per_1m_out": 10, + "cost_per_1m_in_cached": 0.31, + "cost_per_1m_out_cached": 0, + "context_window": 1048576, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "google/gemini-3-flash-preview", + "name": "Google: Gemini 3 Flash Preview", + "cost_per_1m_in": 0.5, + "cost_per_1m_out": 3, + "cost_per_1m_in_cached": 0.05, + "cost_per_1m_out_cached": 0, + "context_window": 1048576, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "google/gemini-3-flash-preview-free", + "name": "Google: Gemini 3 Flash Preview Free", + "cost_per_1m_in": 0.5, + "cost_per_1m_out": 3, + "cost_per_1m_in_cached": 0.05, + "cost_per_1m_out_cached": 0, + "context_window": 1048576, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "google/gemini-3-pro-preview", + "name": "Google: Gemini 3 Pro Preview", + "cost_per_1m_in": 2, + "cost_per_1m_out": 12, + "cost_per_1m_in_cached": 0.2, + "cost_per_1m_out_cached": 0, + "context_window": 1048576, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "kuaishou/kat-coder-pro-v1", + "name": "KwaiKAT: KAT-Coder-Pro-V1", + "cost_per_1m_in": 0, + "cost_per_1m_out": 0, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 256000, + "default_max_tokens": 20000, + "can_reason": false, + "supports_attachments": false, + "options": {} + }, + { + "id": "minimax/minimax-m2", + "name": "MiniMax: MiniMax M2", + "cost_per_1m_in": 0.3, + "cost_per_1m_out": 1.2, + "cost_per_1m_in_cached": 0.03, + "cost_per_1m_out_cached": 0, + "context_window": 204800, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "minimax/minimax-m2.1", + "name": "MiniMax: MiniMax M2.1", + "cost_per_1m_in": 0.3, + "cost_per_1m_out": 1.2, + "cost_per_1m_in_cached": 0.03, + "cost_per_1m_out_cached": 0, + "context_window": 204800, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "moonshotai/kimi-k2-0905", + "name": "MoonshotAI: Kimi K2 0905", + "cost_per_1m_in": 0.6, + "cost_per_1m_out": 2.5, + "cost_per_1m_in_cached": 0.15, + "cost_per_1m_out_cached": 0, + "context_window": 262100, + "default_max_tokens": 20000, + "can_reason": false, + "supports_attachments": false, + "options": {} + }, + { + "id": "moonshotai/kimi-k2-thinking", + "name": "MoonshotAI: Kimi K2 Thinking", + "cost_per_1m_in": 0.6, + "cost_per_1m_out": 2.5, + "cost_per_1m_in_cached": 0.15, + "cost_per_1m_out_cached": 0, + "context_window": 262144, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "moonshotai/kimi-k2-thinking-turbo", + "name": "MoonshotAI: Kimi K2 Thinking Turbo", + "cost_per_1m_in": 1.15, + "cost_per_1m_out": 8, + "cost_per_1m_in_cached": 0.15, + "cost_per_1m_out_cached": 0, + "context_window": 262144, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "openai/gpt-5", + "name": "OpenAI: GPT-5", + "cost_per_1m_in": 1.25, + "cost_per_1m_out": 10, + "cost_per_1m_in_cached": 0.125, + "cost_per_1m_out_cached": 0, + "context_window": 400000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "openai/gpt-5-codex", + "name": "OpenAI: GPT-5 Codex", + "cost_per_1m_in": 1.25, + "cost_per_1m_out": 10, + "cost_per_1m_in_cached": 0.125, + "cost_per_1m_out_cached": 0, + "context_window": 400000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "openai/gpt-5.1", + "name": "OpenAI: GPT-5.1", + "cost_per_1m_in": 1.25, + "cost_per_1m_out": 10, + "cost_per_1m_in_cached": 0.125, + "cost_per_1m_out_cached": 0, + "context_window": 400000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "openai/gpt-5.1-chat", + "name": "OpenAI: GPT-5.1 Chat", + "cost_per_1m_in": 1.25, + "cost_per_1m_out": 10, + "cost_per_1m_in_cached": 0.125, + "cost_per_1m_out_cached": 0, + "context_window": 128000, + "default_max_tokens": 12800, + "can_reason": false, + "supports_attachments": true, + "options": {} + }, + { + "id": "openai/gpt-5.1-codex", + "name": "OpenAI: GPT-5.1-Codex", + "cost_per_1m_in": 1.25, + "cost_per_1m_out": 10, + "cost_per_1m_in_cached": 0.125, + "cost_per_1m_out_cached": 0, + "context_window": 400000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "openai/gpt-5.1-codex-mini", + "name": "OpenAI: GPT-5.1-Codex-Mini", + "cost_per_1m_in": 0.25, + "cost_per_1m_out": 2, + "cost_per_1m_in_cached": 0.025, + "cost_per_1m_out_cached": 0, + "context_window": 400000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "openai/gpt-5.2", + "name": "OpenAI: GPT-5.2", + "cost_per_1m_in": 1.75, + "cost_per_1m_out": 14, + "cost_per_1m_in_cached": 0.175, + "cost_per_1m_out_cached": 0, + "context_window": 400000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "qwen/qwen3-coder-plus", + "name": "Qwen: Qwen3-Coder-Plus", + "cost_per_1m_in": 1, + "cost_per_1m_out": 5, + "cost_per_1m_in_cached": 0.1, + "cost_per_1m_out_cached": 1.25, + "context_window": 1000000, + "default_max_tokens": 20000, + "can_reason": false, + "supports_attachments": false, + "options": {} + }, + { + "id": "stepfun/step-3", + "name": "StepFun: Step-3", + "cost_per_1m_in": 0.21, + "cost_per_1m_out": 0.57, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 65536, + "default_max_tokens": 16384, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "volcengine/doubao-seed-1.8", + "name": "VolcanoEngine: Doubao-Seed-1.8", + "cost_per_1m_in": 0.11, + "cost_per_1m_out": 0.28, + "cost_per_1m_in_cached": 0.023, + "cost_per_1m_out_cached": 0, + "context_window": 256000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "volcengine/doubao-seed-code", + "name": "VolcanoEngine: Doubao-Seed-Code", + "cost_per_1m_in": 0.17, + "cost_per_1m_out": 1.12, + "cost_per_1m_in_cached": 0.034, + "cost_per_1m_out_cached": 0, + "context_window": 256000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "xiaomi/mimo-v2-flash", + "name": "Xiaomi: MiMo-V2-Flash", + "cost_per_1m_in": 0, + "cost_per_1m_out": 0, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 262144, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "z-ai/glm-4.5", + "name": "Z.AI: GLM 4.5", + "cost_per_1m_in": 0.35, + "cost_per_1m_out": 1.54, + "cost_per_1m_in_cached": 0.07, + "cost_per_1m_out_cached": 0, + "context_window": 128000, + "default_max_tokens": 32000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "z-ai/glm-4.5-air", + "name": "Z.AI: GLM 4.5 Air", + "cost_per_1m_in": 0.11, + "cost_per_1m_out": 0.56, + "cost_per_1m_in_cached": 0.022, + "cost_per_1m_out_cached": 0, + "context_window": 128000, + "default_max_tokens": 32000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "z-ai/glm-4.6", + "name": "Z.AI: GLM 4.6", + "cost_per_1m_in": 0.35, + "cost_per_1m_out": 1.54, + "cost_per_1m_in_cached": 0.07, + "cost_per_1m_out_cached": 0, + "context_window": 200000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "z-ai/glm-4.6v", + "name": "Z.AI: GLM 4.6V", + "cost_per_1m_in": 0.14, + "cost_per_1m_out": 0.42, + "cost_per_1m_in_cached": 0.028, + "cost_per_1m_out_cached": 0, + "context_window": 200000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "z-ai/glm-4.6v-flash", + "name": "Z.AI: GLM 4.6V Flash", + "cost_per_1m_in": 0, + "cost_per_1m_out": 0, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 200000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "z-ai/glm-4.7", + "name": "Z.AI: GLM 4.7", + "cost_per_1m_in": 0.28, + "cost_per_1m_out": 1.14, + "cost_per_1m_in_cached": 0.057, + "cost_per_1m_out_cached": 0, + "context_window": 200000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "inclusionai/ling-1t", + "name": "inclusionAI: Ling-1T", + "cost_per_1m_in": 0.56, + "cost_per_1m_out": 2.24, + "cost_per_1m_in_cached": 0.112, + "cost_per_1m_out_cached": 0, + "context_window": 128000, + "default_max_tokens": 12800, + "can_reason": false, + "supports_attachments": false, + "options": {} + }, + { + "id": "inclusionai/ring-1t", + "name": "inclusionAI: Ring-1T", + "cost_per_1m_in": 0.56, + "cost_per_1m_out": 2.24, + "cost_per_1m_in_cached": 0.112, + "cost_per_1m_out_cached": 0, + "context_window": 128000, + "default_max_tokens": 32000, + "can_reason": true, + "supports_attachments": false, + "options": {} + }, + { + "id": "x-ai/grok-4", + "name": "xAI: Grok 4", + "cost_per_1m_in": 3, + "cost_per_1m_out": 15, + "cost_per_1m_in_cached": 0.75, + "cost_per_1m_out_cached": 0, + "context_window": 256000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "x-ai/grok-4-fast", + "name": "xAI: Grok 4 Fast", + "cost_per_1m_in": 0.2, + "cost_per_1m_out": 0.5, + "cost_per_1m_in_cached": 0.05, + "cost_per_1m_out_cached": 0, + "context_window": 2000000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "x-ai/grok-4.1-fast", + "name": "xAI: Grok 4.1 Fast", + "cost_per_1m_in": 0.2, + "cost_per_1m_out": 0.5, + "cost_per_1m_in_cached": 0.05, + "cost_per_1m_out_cached": 0, + "context_window": 2000000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": true, + "options": {} + }, + { + "id": "x-ai/grok-4.1-fast-non-reasoning", + "name": "xAI: Grok 4.1 Fast Non Reasoning", + "cost_per_1m_in": 0.2, + "cost_per_1m_out": 0.5, + "cost_per_1m_in_cached": 0.05, + "cost_per_1m_out_cached": 0, + "context_window": 2000000, + "default_max_tokens": 20000, + "can_reason": false, + "supports_attachments": true, + "options": {} + }, + { + "id": "x-ai/grok-code-fast-1", + "name": "xAI: Grok Code Fast 1", + "cost_per_1m_in": 0.2, + "cost_per_1m_out": 1.5, + "cost_per_1m_in_cached": 0.02, + "cost_per_1m_out_cached": 0, + "context_window": 256000, + "default_max_tokens": 50000, + "can_reason": true, + "supports_attachments": false, + "options": {} + } + ] +} \ No newline at end of file diff --git a/internal/providers/providers.go b/internal/providers/providers.go index d5299c49..28649775 100644 --- a/internal/providers/providers.go +++ b/internal/providers/providers.go @@ -36,6 +36,9 @@ var xAIConfig []byte //go:embed configs/zai.json var zAIConfig []byte +//go:embed configs/zenmux.json +var zenMuxConfig []byte + //go:embed configs/bedrock.json var bedrockConfig []byte @@ -78,6 +81,7 @@ var providerRegistry = []ProviderFunc{ vertexAIProvider, xAIProvider, zAIProvider, + zenMuxProvider, kimiCodingProvider, groqProvider, openRouterProvider, @@ -145,6 +149,10 @@ func zAIProvider() catwalk.Provider { return loadProviderFromConfig(zAIConfig) } +func zenMuxProvider() catwalk.Provider { + return loadProviderFromConfig(zenMuxConfig) +} + func openRouterProvider() catwalk.Provider { return loadProviderFromConfig(openRouterConfig) }