diff --git a/cmd/openrouter/main.go b/cmd/openrouter/main.go index f35cf5c57169fe3995b92ae34ed73221edd35e6d..999e15f91c17088fa11d579b9d984066b9e0e3a1 100644 --- a/cmd/openrouter/main.go +++ b/cmd/openrouter/main.go @@ -195,14 +195,8 @@ func selectBestEndpoint(endpoints []Endpoint) *Endpoint { continue } - // Prefer higher context length - if endpoint.ContextLength > best.ContextLength { + if isBetterEndpoint(endpoint, best) { best = endpoint - } else if endpoint.ContextLength == best.ContextLength { - // If context length is the same, prefer better uptime - if endpoint.UptimeLast30m > best.UptimeLast30m { - best = endpoint - } } } @@ -214,6 +208,29 @@ func selectBestEndpoint(endpoints []Endpoint) *Endpoint { return best } +func isBetterEndpoint(candidate, current *Endpoint) bool { + candidateHasTools := slices.Contains(candidate.SupportedParams, "tools") + currentHasTools := slices.Contains(current.SupportedParams, "tools") + + // Prefer endpoints with tool support over those without + if candidateHasTools && !currentHasTools { + return true + } + if !candidateHasTools && currentHasTools { + return false + } + + // Both have same tool support status, compare other factors + if candidate.ContextLength > current.ContextLength { + return true + } + if candidate.ContextLength == current.ContextLength { + return candidate.UptimeLast30m > current.UptimeLast30m + } + + return false +} + // This is used to generate the openrouter.json config file. func main() { modelsResp, err := fetchOpenRouterModels() diff --git a/internal/providers/configs/openrouter.json b/internal/providers/configs/openrouter.json index 1986cd7597bee5402d4ef8a831772cabc425813a..6b20d0ff4323e593cd33ba8c670b2836160af6b0 100644 --- a/internal/providers/configs/openrouter.json +++ b/internal/providers/configs/openrouter.json @@ -49,8 +49,8 @@ { "id": "qwen/qwen3-235b-a22b-07-25", "name": "Qwen: Qwen3 235B A22B 2507", - "cost_per_1m_in": 0.12, - "cost_per_1m_out": 0.59, + "cost_per_1m_in": 0.15, + "cost_per_1m_out": 0.7999999999999999, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, "context_window": 262144, @@ -59,13 +59,26 @@ "has_reasoning_efforts": false, "supports_attachments": false }, + { + "id": "moonshotai/kimi-k2:free", + "name": "MoonshotAI: Kimi K2 (free)", + "cost_per_1m_in": 0, + "cost_per_1m_out": 0, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 65536, + "default_max_tokens": 6553, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, { "id": "moonshotai/kimi-k2", "name": "MoonshotAI: Kimi K2", "cost_per_1m_in": 0.6, "cost_per_1m_out": 2.5, "cost_per_1m_in_cached": 0, - "cost_per_1m_out_cached": 0, + "cost_per_1m_out_cached": 0.15, "context_window": 131072, "default_max_tokens": 13107, "can_reason": false, @@ -85,19 +98,6 @@ "has_reasoning_efforts": false, "supports_attachments": false }, - { - "id": "mistralai/devstral-small", - "name": "Mistral: Devstral Small 1.1", - "cost_per_1m_in": 0.09999999999999999, - "cost_per_1m_out": 0.3, - "cost_per_1m_in_cached": 0, - "cost_per_1m_out_cached": 0, - "context_window": 131072, - "default_max_tokens": 13107, - "can_reason": false, - "has_reasoning_efforts": false, - "supports_attachments": false - }, { "id": "x-ai/grok-4", "name": "xAI: Grok 4", @@ -124,6 +124,19 @@ "has_reasoning_efforts": false, "supports_attachments": true }, + { + "id": "mistralai/mistral-small-3.2-24b-instruct", + "name": "Mistral: Mistral Small 3.2 24B", + "cost_per_1m_in": 0.09999999999999999, + "cost_per_1m_out": 0.3, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 131072, + "default_max_tokens": 13107, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": true + }, { "id": "minimax/minimax-m1", "name": "MiniMax: MiniMax M1", @@ -267,6 +280,19 @@ "has_reasoning_efforts": false, "supports_attachments": true }, + { + "id": "deepseek/deepseek-r1-0528", + "name": "DeepSeek: R1 0528", + "cost_per_1m_in": 0.7, + "cost_per_1m_out": 2.5, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 163840, + "default_max_tokens": 81920, + "can_reason": true, + "has_reasoning_efforts": false, + "supports_attachments": false + }, { "id": "anthropic/claude-opus-4", "name": "Anthropic: Claude Opus 4", @@ -309,12 +335,12 @@ { "id": "mistralai/devstral-small-2505", "name": "Mistral: Devstral Small 2505", - "cost_per_1m_in": 0.09999999999999999, - "cost_per_1m_out": 0.3, + "cost_per_1m_in": 0.03, + "cost_per_1m_out": 0.03, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, - "context_window": 131072, - "default_max_tokens": 13107, + "context_window": 32768, + "default_max_tokens": 3276, "can_reason": false, "has_reasoning_efforts": false, "supports_attachments": false @@ -556,10 +582,10 @@ { "id": "x-ai/grok-3-mini-beta", "name": "xAI: Grok 3 Mini Beta", - "cost_per_1m_in": 0.3, - "cost_per_1m_out": 0.5, + "cost_per_1m_in": 0.6, + "cost_per_1m_out": 4, "cost_per_1m_in_cached": 0, - "cost_per_1m_out_cached": 0.075, + "cost_per_1m_out_cached": 0.15, "context_window": 131072, "default_max_tokens": 13107, "can_reason": true, @@ -631,6 +657,19 @@ "has_reasoning_efforts": false, "supports_attachments": false }, + { + "id": "deepseek/deepseek-chat-v3-0324", + "name": "DeepSeek: DeepSeek V3 0324", + "cost_per_1m_in": 0.77, + "cost_per_1m_out": 0.77, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 163840, + "default_max_tokens": 65536, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, { "id": "mistralai/mistral-small-3.1-24b-instruct:free", "name": "Mistral: Mistral Small 3.1 24B (free)", @@ -644,6 +683,19 @@ "has_reasoning_efforts": false, "supports_attachments": true }, + { + "id": "mistralai/mistral-small-3.1-24b-instruct", + "name": "Mistral: Mistral Small 3.1 24B", + "cost_per_1m_in": 0.09999999999999999, + "cost_per_1m_out": 0.3, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 131072, + "default_max_tokens": 13107, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": true + }, { "id": "ai21/jamba-1.6-large", "name": "AI21: Jamba 1.6 Large", @@ -813,6 +865,32 @@ "has_reasoning_efforts": false, "supports_attachments": false }, + { + "id": "mistralai/mistral-small-24b-instruct-2501", + "name": "Mistral: Mistral Small 3", + "cost_per_1m_in": 0.09999999999999999, + "cost_per_1m_out": 0.3, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 32768, + "default_max_tokens": 3276, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, + { + "id": "deepseek/deepseek-r1", + "name": "DeepSeek: R1", + "cost_per_1m_in": 0.44999999999999996, + "cost_per_1m_out": 2.1500000000000004, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 163840, + "default_max_tokens": 81920, + "can_reason": true, + "has_reasoning_efforts": false, + "supports_attachments": false + }, { "id": "mistralai/codestral-2501", "name": "Mistral: Codestral 2501", @@ -826,6 +904,19 @@ "has_reasoning_efforts": false, "supports_attachments": false }, + { + "id": "deepseek/deepseek-chat", + "name": "DeepSeek: DeepSeek V3", + "cost_per_1m_in": 0.8999999999999999, + "cost_per_1m_out": 0.8999999999999999, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 131072, + "default_max_tokens": 13107, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, { "id": "openai/o1", "name": "OpenAI: o1", @@ -983,8 +1074,21 @@ "supports_attachments": true }, { - "id": "anthropic/claude-3.5-haiku-20241022:beta", - "name": "Anthropic: Claude 3.5 Haiku (2024-10-22) (self-moderated)", + "id": "thedrummer/unslopnemo-12b", + "name": "TheDrummer: UnslopNemo 12B", + "cost_per_1m_in": 0.39999999999999997, + "cost_per_1m_out": 0.39999999999999997, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 32000, + "default_max_tokens": 16000, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, + { + "id": "anthropic/claude-3.5-haiku:beta", + "name": "Anthropic: Claude 3.5 Haiku (self-moderated)", "cost_per_1m_in": 0.7999999999999999, "cost_per_1m_out": 4, "cost_per_1m_in_cached": 1, @@ -996,8 +1100,8 @@ "supports_attachments": true }, { - "id": "anthropic/claude-3.5-haiku-20241022", - "name": "Anthropic: Claude 3.5 Haiku (2024-10-22)", + "id": "anthropic/claude-3.5-haiku", + "name": "Anthropic: Claude 3.5 Haiku", "cost_per_1m_in": 0.7999999999999999, "cost_per_1m_out": 4, "cost_per_1m_in_cached": 1, @@ -1009,8 +1113,8 @@ "supports_attachments": true }, { - "id": "anthropic/claude-3.5-haiku:beta", - "name": "Anthropic: Claude 3.5 Haiku (self-moderated)", + "id": "anthropic/claude-3.5-haiku-20241022:beta", + "name": "Anthropic: Claude 3.5 Haiku (2024-10-22) (self-moderated)", "cost_per_1m_in": 0.7999999999999999, "cost_per_1m_out": 4, "cost_per_1m_in_cached": 1, @@ -1022,8 +1126,8 @@ "supports_attachments": true }, { - "id": "anthropic/claude-3.5-haiku", - "name": "Anthropic: Claude 3.5 Haiku", + "id": "anthropic/claude-3.5-haiku-20241022", + "name": "Anthropic: Claude 3.5 Haiku (2024-10-22)", "cost_per_1m_in": 0.7999999999999999, "cost_per_1m_out": 4, "cost_per_1m_in_cached": 1, @@ -1112,6 +1216,19 @@ "has_reasoning_efforts": false, "supports_attachments": true }, + { + "id": "thedrummer/rocinante-12b", + "name": "TheDrummer: Rocinante 12B", + "cost_per_1m_in": 0.24, + "cost_per_1m_out": 0.44999999999999996, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 32768, + "default_max_tokens": 16384, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, { "id": "meta-llama/llama-3.2-3b-instruct", "name": "Meta: Llama 3.2 3B Instruct", @@ -1126,14 +1243,14 @@ "supports_attachments": false }, { - "id": "cohere/command-r-08-2024", - "name": "Cohere: Command R (08-2024)", - "cost_per_1m_in": 0.15, - "cost_per_1m_out": 0.6, + "id": "qwen/qwen-2.5-72b-instruct", + "name": "Qwen2.5 72B Instruct", + "cost_per_1m_in": 0.12, + "cost_per_1m_out": 0.39, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, - "context_window": 128000, - "default_max_tokens": 2000, + "context_window": 32768, + "default_max_tokens": 8192, "can_reason": false, "has_reasoning_efforts": false, "supports_attachments": false @@ -1151,6 +1268,19 @@ "has_reasoning_efforts": false, "supports_attachments": false }, + { + "id": "cohere/command-r-08-2024", + "name": "Cohere: Command R (08-2024)", + "cost_per_1m_in": 0.15, + "cost_per_1m_out": 0.6, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 128000, + "default_max_tokens": 2000, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, { "id": "microsoft/phi-3.5-mini-128k-instruct", "name": "Microsoft: Phi-3.5 Mini 128K Instruct", @@ -1164,6 +1294,19 @@ "has_reasoning_efforts": false, "supports_attachments": false }, + { + "id": "nousresearch/hermes-3-llama-3.1-70b", + "name": "Nous: Hermes 3 70B Instruct", + "cost_per_1m_in": 0.39999999999999997, + "cost_per_1m_out": 0.39999999999999997, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 12288, + "default_max_tokens": 1228, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, { "id": "openai/gpt-4o-2024-08-06", "name": "OpenAI: GPT-4o (2024-08-06)", @@ -1177,6 +1320,45 @@ "has_reasoning_efforts": false, "supports_attachments": true }, + { + "id": "meta-llama/llama-3.1-8b-instruct", + "name": "Meta: Llama 3.1 8B Instruct", + "cost_per_1m_in": 0.03, + "cost_per_1m_out": 0.049999999999999996, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 131072, + "default_max_tokens": 8192, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, + { + "id": "meta-llama/llama-3.1-405b-instruct", + "name": "Meta: Llama 3.1 405B Instruct", + "cost_per_1m_in": 0.7999999999999999, + "cost_per_1m_out": 0.7999999999999999, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 131072, + "default_max_tokens": 65536, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, + { + "id": "meta-llama/llama-3.1-70b-instruct", + "name": "Meta: Llama 3.1 70B Instruct", + "cost_per_1m_in": 0.09999999999999999, + "cost_per_1m_out": 0.28, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 131072, + "default_max_tokens": 8192, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, { "id": "mistralai/mistral-nemo", "name": "Mistral: Mistral Nemo", @@ -1190,6 +1372,19 @@ "has_reasoning_efforts": false, "supports_attachments": false }, + { + "id": "openai/gpt-4o-mini", + "name": "OpenAI: GPT-4o-mini", + "cost_per_1m_in": 0.15, + "cost_per_1m_out": 0.6, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0.075, + "context_window": 128000, + "default_max_tokens": 8192, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": true + }, { "id": "openai/gpt-4o-mini-2024-07-18", "name": "OpenAI: GPT-4o-mini (2024-07-18)", @@ -1230,8 +1425,21 @@ "supports_attachments": true }, { - "id": "mistralai/mistral-7b-instruct-v0.3", - "name": "Mistral: Mistral 7B Instruct v0.3", + "id": "mistralai/mistral-7b-instruct:free", + "name": "Mistral: Mistral 7B Instruct (free)", + "cost_per_1m_in": 0, + "cost_per_1m_out": 0, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 32768, + "default_max_tokens": 8192, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, + { + "id": "mistralai/mistral-7b-instruct", + "name": "Mistral: Mistral 7B Instruct", "cost_per_1m_in": 0.028, "cost_per_1m_out": 0.054, "cost_per_1m_in_cached": 0, @@ -1243,10 +1451,10 @@ "supports_attachments": false }, { - "id": "mistralai/mistral-7b-instruct:free", - "name": "Mistral: Mistral 7B Instruct (free)", - "cost_per_1m_in": 0, - "cost_per_1m_out": 0, + "id": "mistralai/mistral-7b-instruct-v0.3", + "name": "Mistral: Mistral 7B Instruct v0.3", + "cost_per_1m_in": 0.028, + "cost_per_1m_out": 0.054, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, "context_window": 32768, @@ -1294,19 +1502,6 @@ "has_reasoning_efforts": false, "supports_attachments": true }, - { - "id": "openai/gpt-4o-2024-05-13", - "name": "OpenAI: GPT-4o (2024-05-13)", - "cost_per_1m_in": 5, - "cost_per_1m_out": 15, - "cost_per_1m_in_cached": 0, - "cost_per_1m_out_cached": 0, - "context_window": 128000, - "default_max_tokens": 2048, - "can_reason": false, - "has_reasoning_efforts": false, - "supports_attachments": true - }, { "id": "openai/gpt-4o", "name": "OpenAI: GPT-4o", @@ -1334,17 +1529,17 @@ "supports_attachments": true }, { - "id": "meta-llama/llama-3-70b-instruct", - "name": "Meta: Llama 3 70B Instruct", - "cost_per_1m_in": 0.3, - "cost_per_1m_out": 0.39999999999999997, + "id": "openai/gpt-4o-2024-05-13", + "name": "OpenAI: GPT-4o (2024-05-13)", + "cost_per_1m_in": 5, + "cost_per_1m_out": 15, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, - "context_window": 8192, - "default_max_tokens": 8192, + "context_window": 128000, + "default_max_tokens": 2048, "can_reason": false, "has_reasoning_efforts": false, - "supports_attachments": false + "supports_attachments": true }, { "id": "meta-llama/llama-3-8b-instruct", @@ -1359,6 +1554,19 @@ "has_reasoning_efforts": false, "supports_attachments": false }, + { + "id": "meta-llama/llama-3-70b-instruct", + "name": "Meta: Llama 3 70B Instruct", + "cost_per_1m_in": 0.3, + "cost_per_1m_out": 0.39999999999999997, + "cost_per_1m_in_cached": 0, + "cost_per_1m_out_cached": 0, + "context_window": 8192, + "default_max_tokens": 8192, + "can_reason": false, + "has_reasoning_efforts": false, + "supports_attachments": false + }, { "id": "mistralai/mixtral-8x22b-instruct", "name": "Mistral: Mixtral 8x22B Instruct", @@ -1373,27 +1581,27 @@ "supports_attachments": false }, { - "id": "openai/gpt-4-turbo", - "name": "OpenAI: GPT-4 Turbo", - "cost_per_1m_in": 10, - "cost_per_1m_out": 30, + "id": "google/gemini-pro-1.5", + "name": "Google: Gemini 1.5 Pro", + "cost_per_1m_in": 1.25, + "cost_per_1m_out": 5, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, - "context_window": 128000, - "default_max_tokens": 2048, + "context_window": 2000000, + "default_max_tokens": 4096, "can_reason": false, "has_reasoning_efforts": false, "supports_attachments": true }, { - "id": "google/gemini-pro-1.5", - "name": "Google: Gemini 1.5 Pro", - "cost_per_1m_in": 1.25, - "cost_per_1m_out": 5, + "id": "openai/gpt-4-turbo", + "name": "OpenAI: GPT-4 Turbo", + "cost_per_1m_in": 10, + "cost_per_1m_out": 30, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, - "context_window": 2000000, - "default_max_tokens": 4096, + "context_window": 128000, + "default_max_tokens": 2048, "can_reason": false, "has_reasoning_efforts": false, "supports_attachments": true @@ -1555,10 +1763,10 @@ "supports_attachments": false }, { - "id": "mistralai/mistral-tiny", - "name": "Mistral Tiny", - "cost_per_1m_in": 0.25, - "cost_per_1m_out": 0.25, + "id": "mistralai/mistral-small", + "name": "Mistral Small", + "cost_per_1m_in": 0.19999999999999998, + "cost_per_1m_out": 0.6, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, "context_window": 32768, @@ -1568,10 +1776,10 @@ "supports_attachments": false }, { - "id": "mistralai/mistral-small", - "name": "Mistral Small", - "cost_per_1m_in": 0.19999999999999998, - "cost_per_1m_out": 0.6, + "id": "mistralai/mistral-tiny", + "name": "Mistral Tiny", + "cost_per_1m_in": 0.25, + "cost_per_1m_out": 0.25, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, "context_window": 32768, @@ -1620,13 +1828,13 @@ "supports_attachments": false }, { - "id": "openai/gpt-4-0314", - "name": "OpenAI: GPT-4 (older v0314)", - "cost_per_1m_in": 30, - "cost_per_1m_out": 60, + "id": "openai/gpt-3.5-turbo", + "name": "OpenAI: GPT-3.5 Turbo", + "cost_per_1m_in": 0.5, + "cost_per_1m_out": 1.5, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, - "context_window": 8191, + "context_window": 16385, "default_max_tokens": 2048, "can_reason": false, "has_reasoning_efforts": false, @@ -1646,13 +1854,13 @@ "supports_attachments": false }, { - "id": "openai/gpt-3.5-turbo", - "name": "OpenAI: GPT-3.5 Turbo", - "cost_per_1m_in": 0.5, - "cost_per_1m_out": 1.5, + "id": "openai/gpt-4-0314", + "name": "OpenAI: GPT-4 (older v0314)", + "cost_per_1m_in": 30, + "cost_per_1m_out": 60, "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0, - "context_window": 16385, + "context_window": 8191, "default_max_tokens": 2048, "can_reason": false, "has_reasoning_efforts": false,