From 8095852c7dca90c6b18fcd149d054344732b23dc Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 12:49:56 -0600 Subject: [PATCH 01/30] feat: add hand-owned model quirks overlay --- quirks.yaml | 880 ++++++++++++++++++++++++++++++++++++++++++ src/client/catalog.rs | 327 ++++++++++++++++ src/client/mod.rs | 1 + src/client/model.rs | 6 +- 4 files changed, 1211 insertions(+), 3 deletions(-) create mode 100644 quirks.yaml create mode 100644 src/client/catalog.rs diff --git a/quirks.yaml b/quirks.yaml new file mode 100644 index 0000000..65b2653 --- /dev/null +++ b/quirks.yaml @@ -0,0 +1,880 @@ +# Hand-owned model behavior overlay. +# +# Everything in this file is curated by hand and layered on top of API-sourced +# model data: oauth blocks, request patches, wire-name aliases, behavioral +# flags, and models that no public catalog provides. +# +# `models:` entries are self-complete — `rules` apply ONLY to API-sourced +# models, never to hand-owned `models:` entries. Legacy verbatim-preserved +# entries are grouped under a `# legacy — pruning candidates` comment. + +- provider: openai + rules: + - match: "gpt-5.6-*" + default_reasoning_effort: medium + - match: "gpt-5.5" + default_reasoning_effort: medium + - match: "gpt-5.5-pro" + default_reasoning_effort: high + - match: "gpt-5.4" + default_reasoning_effort: none + - match: "gpt-5.4-pro" + default_reasoning_effort: medium + - match: "gpt-5.4-mini" + default_reasoning_effort: none + - match: "gpt-5.4-nano" + default_reasoning_effort: none + - match: "gpt-5.3-codex" + default_reasoning_effort: medium + - match: "gpt-5.2" + default_reasoning_effort: none + - match: "gpt-5.2-pro" + default_reasoning_effort: medium + - match: "gpt-5.1" + default_reasoning_effort: none + - match: "gpt-5.1-chat-latest" + default_reasoning_effort: none + - match: "gpt-5" + default_reasoning_effort: medium + - match: "gpt-5-chat-latest" + default_reasoning_effort: medium + - match: "o4-mini" + system_prompt_prefix: Formatting re-enabled + default_reasoning_effort: medium + patch: + body: + max_tokens: null + temperature: null + top_p: null + - match: "o3" + system_prompt_prefix: Formatting re-enabled + patch: + body: + max_tokens: null + temperature: null + top_p: null + - match: "o3-mini" + system_prompt_prefix: Formatting re-enabled + patch: + body: + max_tokens: null + temperature: null + top_p: null + models: + - name: text-embedding-3-large + type: embedding + input_price: 0.13 + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: text-embedding-3-small + type: embedding + input_price: 0.02 + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + # legacy — pruning candidates + - name: o4-mini-high + max_output_tokens: 100000 + real_name: o4-mini + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + - name: o3-high + real_name: o3 + max_input_tokens: 200000 + input_price: 2 + output_price: 8 + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + - name: o3-mini-high + max_output_tokens: 100000 + real_name: o3-mini + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + +- provider: gemini + rules: + - match: "gemini-3.6-flash" + default_reasoning_effort: medium + - match: "gemini-3.5-flash" + default_reasoning_effort: medium + - match: "gemini-3-flash-preview" + default_reasoning_effort: high + - match: "gemini-3.1-flash-lite" + default_reasoning_effort: minimal + - match: "gemini-3.1-pro-preview" + default_reasoning_effort: high + - match: "gemini-3-pro-preview" + default_reasoning_effort: high + - match: "gemini-2.5-pro" + default_reasoning_effort: high + models: + - name: text-embedding-004 + type: embedding + input_price: 0 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 100 + +- provider: claude + rules: + - match: "claude-*" + require_max_tokens: true + - match: "claude-opus-5" + default_reasoning_effort: high + - match: "claude-fable-*" + default_reasoning_effort: high + - match: "claude-opus-4-8" + default_reasoning_effort: high + - match: "claude-opus-4-7" + default_reasoning_effort: high + - match: "claude-opus-4-6" + default_reasoning_effort: high + - match: "claude-sonnet-5" + default_reasoning_effort: high + - match: "claude-sonnet-4-6" + default_reasoning_effort: high + variants: + - match: "claude-opus-4-6" + suffix: ":thinking" + real_name: claude-opus-4-6 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-sonnet-4-6" + suffix: ":thinking" + real_name: claude-sonnet-4-6 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-sonnet-4-5-20250929" + suffix: ":thinking" + real_name: claude-sonnet-4-5-20250929 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-haiku-4-5-20251001" + suffix: ":thinking" + real_name: claude-haiku-4-5-20251001 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-opus-4-5-20251101" + suffix: ":thinking" + real_name: claude-opus-4-5-20251101 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-opus-4-1-20250805" + suffix: ":thinking" + real_name: claude-opus-4-1-20250805 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-opus-4-20250514" + suffix: ":thinking" + real_name: claude-opus-4-20250514 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + +- provider: mistral + models: + - name: mistral-embed + type: embedding + max_input_tokens: 8092 + input_price: 0.1 + max_tokens_per_chunk: 8092 + default_chunk_size: 2000 + +- provider: ai21 + models: + - name: jamba-large + max_input_tokens: 256000 + input_price: 2 + output_price: 8 + supports_function_calling: true + - name: jamba-mini + max_input_tokens: 256000 + input_price: 0.2 + output_price: 0.4 + supports_function_calling: true + +- provider: cohere + models: + - name: embed-v4.0 + type: embedding + input_price: 0.12 + max_tokens_per_chunk: 2048 + default_chunk_size: 2000 + max_batch_size: 96 + - name: embed-english-v3.0 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: embed-multilingual-v3.0 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: rerank-v3.5 + type: reranker + max_input_tokens: 4096 + - name: rerank-english-v3.0 + type: reranker + max_input_tokens: 4096 + - name: rerank-multilingual-v3.0 + type: reranker + max_input_tokens: 4096 + +- provider: xai + oauth: + client_id: b1a00492-073a-47ea-816f-4c329264a828 + authorize_url: https://auth.x.ai/oauth2/authorize + token_url: https://auth.x.ai/oauth2/token + scopes: + - openid + - profile + - email + - offline_access + - grok-cli:access + - api:access + redirect_port: 56121 + flow: pkce + token_request_format: form_url_encoded + extra_authorize_params: + plan: generic + referrer: coyote + echo_pkce_in_token_exchange: true + models: + - name: grok-4.20 + real_name: grok-4.20-multi-agent-0309 + input_price: 1.25 + output_price: 2.5 + max_input_tokens: 1000000 + supports_function_calling: true + - name: grok-4.20-reasoning + real_name: grok-4.20-0309-reasoning + input_price: 1.25 + output_price: 2.5 + max_input_tokens: 1000000 + supports_function_calling: true + - name: grok-4.20-non-reasoning + real_name: grok-4.20-0309-non-reasoning + input_price: 1.25 + output_price: 2.5 + max_input_tokens: 1000000 + supports_function_calling: true + +- provider: vertexai + rules: + - match: "claude-*" + require_max_tokens: true + - match: "gemini-3.5-flash" + default_reasoning_effort: medium + - match: "gemini-3-flash-preview" + default_reasoning_effort: high + - match: "gemini-3.1-flash-lite" + default_reasoning_effort: minimal + - match: "gemini-3.1-pro-preview" + default_reasoning_effort: high + - match: "gemini-3-pro-preview" + default_reasoning_effort: high + - match: "gemini-2.5-flash" + default_reasoning_effort: medium + - match: "gemini-2.5-pro" + default_reasoning_effort: high + - match: "claude-opus-5" + default_reasoning_effort: high + - match: "claude-fable-*" + default_reasoning_effort: high + - match: "claude-opus-4-8" + default_reasoning_effort: high + - match: "claude-opus-4-7" + default_reasoning_effort: high + - match: "claude-sonnet-5" + default_reasoning_effort: high + - match: "claude-sonnet-4-6" + default_reasoning_effort: high + variants: + - match: "claude-opus-4-6" + suffix: ":thinking" + real_name: claude-opus-4-6 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-sonnet-4-6" + suffix: ":thinking" + real_name: claude-sonnet-4-6 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-opus-4-5@20251101" + suffix: ":thinking" + real_name: claude-opus-4-5@20251101 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-sonnet-4-5@20250929" + suffix: ":thinking" + real_name: claude-sonnet-4-5@20250929 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-haiku-4-5@20251001" + suffix: ":thinking" + real_name: claude-haiku-4-5@20251001 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + models: + - name: text-embedding-005 + type: embedding + max_input_tokens: 20000 + input_price: 0.025 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 5 + - name: text-multilingual-embedding-002 + type: embedding + max_input_tokens: 20000 + input_price: 0.2 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 5 + +- provider: bedrock + rules: + - match: "us.anthropic.claude-*" + require_max_tokens: true + - match: "us.meta.llama*" + require_max_tokens: true + - match: "us.anthropic.claude-opus-5" + default_reasoning_effort: high + - match: "us.anthropic.claude-fable-*" + default_reasoning_effort: high + - match: "us.anthropic.claude-opus-4-8" + default_reasoning_effort: high + - match: "us.anthropic.claude-opus-4-7" + default_reasoning_effort: high + - match: "us.anthropic.claude-opus-4-6-v1" + default_reasoning_effort: high + - match: "us.anthropic.claude-sonnet-5" + default_reasoning_effort: high + - match: "us.anthropic.claude-sonnet-4-6" + default_reasoning_effort: high + variants: + - match: "us.anthropic.claude-opus-4-6-v1" + suffix: ":thinking" + real_name: us.anthropic.claude-opus-4-6-v1 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + - match: "us.anthropic.claude-sonnet-4-6" + suffix: ":thinking" + real_name: us.anthropic.claude-sonnet-4-6 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + - match: "us.anthropic.claude-opus-4-5-20251101-v1:0" + suffix: ":thinking" + real_name: us.anthropic.claude-opus-4-5-20251101-v1:0 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + - match: "us.anthropic.claude-sonnet-4-5-20250929-v1:0" + suffix: ":thinking" + real_name: us.anthropic.claude-sonnet-4-5-20250929-v1:0 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + - match: "us.anthropic.claude-haiku-4-5-20251001-v1:0" + suffix: ":thinking" + real_name: us.anthropic.claude-haiku-4-5-20251001-v1:0 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + models: + - name: cohere.embed-english-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: cohere.embed-multilingual-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + +- provider: cloudflare + rules: + - match: "@cf/*" + require_max_tokens: true + models: + - name: '@cf/baai/bge-large-en-v1.5' + type: embedding + input_price: 0 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + +- provider: ernie + models: + - name: ernie-4.5-turbo-128k + max_input_tokens: 131072 + input_price: 0.112 + output_price: 0.448 + - name: ernie-4.5-turbo-vl-32k + max_input_tokens: 32768 + input_price: 0.42 + output_price: 1.26 + supports_vision: true + - name: ernie-5.0-thinking-preview + max_input_tokens: 131072 + input_price: 1.4 + output_price: 5.6 + - name: ernie-x1.1-preview + max_input_tokens: 65536 + input_price: 0.14 + output_price: 0.56 + - name: bge-large-zh + type: embedding + input_price: 0.07 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 16 + - name: bge-large-en + type: embedding + input_price: 0.07 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 16 + - name: bce-reranker-base + type: reranker + max_input_tokens: 1024 + input_price: 0.07 + +- provider: qianwen + rules: + - match: "qwen3.5-plus" + patch: + body: + enable_thinking: false + - match: "qwen3.5-397b-a17b" + patch: + body: + enable_thinking: false + variants: + - match: "qwen3-max" + suffix: ":thinking" + real_name: qwen3-max + patch: + body: + enable_thinking: true + models: + - name: qwen3.5-plus:thinking + real_name: qwen3.5-plus + max_input_tokens: 262144 + supports_function_calling: true + - name: qwen3.5-397b-a17b:thinking + real_name: qwen3.5-397b-a17b + max_input_tokens: 262144 + supports_function_calling: true + - name: text-embedding-v4 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 10 + - name: text-embedding-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 10 + +- provider: hunyuan + models: + - name: hunyuan-2.0-instruct-20251111 + max_input_tokens: 131072 + input_price: 0.112 + output_price: 0.28 + supports_function_calling: true + - name: hunyuan-2.0-thinking-20251109 + max_input_tokens: 131072 + input_price: 0.14 + output_price: 0.56 + supports_function_calling: true + - name: hunyuan-vision-1.5-instruct + max_input_tokens: 24576 + input_price: 0.42 + output_price: 1.26 + supports_vision: true + - name: hunyuan-embedding + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 1024 + default_chunk_size: 1000 + max_batch_size: 100 + +- provider: zhipuai + variants: + - match: "glm-5" + suffix: ":instruct" + real_name: glm-5 + patch: + body: + thinking: + type: disabled + - match: "glm-4.7" + suffix: ":instruct" + real_name: glm-4.7 + patch: + body: + thinking: + type: disabled + models: + - name: embedding-3 + type: embedding + max_input_tokens: 8192 + input_price: 0.07 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + - name: rerank + type: reranker + max_input_tokens: 4096 + input_price: 0.112 + +- provider: openrouter + rules: + - match: "anthropic/claude-*" + require_max_tokens: true + - match: "openai/gpt-5.6-*" + default_reasoning_effort: medium + - match: "openai/gpt-5.5" + default_reasoning_effort: medium + - match: "openai/gpt-5.5-pro" + default_reasoning_effort: high + - match: "openai/gpt-5.4" + default_reasoning_effort: none + - match: "openai/gpt-5.4-pro" + default_reasoning_effort: medium + - match: "openai/gpt-5.4-mini" + default_reasoning_effort: none + - match: "openai/gpt-5.4-nano" + default_reasoning_effort: none + - match: "openai/gpt-5.3-codex" + default_reasoning_effort: medium + - match: "openai/gpt-5.2" + default_reasoning_effort: none + - match: "openai/gpt-5.2-pro" + default_reasoning_effort: medium + - match: "openai/gpt-5" + default_reasoning_effort: medium + - match: "google/gemini-3.5-flash" + default_reasoning_effort: medium + - match: "google/gemini-3-flash-preview" + default_reasoning_effort: high + - match: "google/gemini-3.1-flash-lite" + default_reasoning_effort: minimal + - match: "google/gemini-3.1-pro-preview" + default_reasoning_effort: high + - match: "google/gemini-3-pro-preview" + default_reasoning_effort: high + - match: "google/gemini-2.5-flash" + default_reasoning_effort: low + - match: "google/gemini-2.5-pro" + default_reasoning_effort: high + - match: "anthropic/claude-opus-5" + default_reasoning_effort: high + - match: "anthropic/claude-fable-*" + default_reasoning_effort: high + - match: "anthropic/claude-opus-4-8" + default_reasoning_effort: high + - match: "anthropic/claude-opus-4-7" + default_reasoning_effort: high + - match: "anthropic/claude-opus-4.6" + default_reasoning_effort: high + - match: "anthropic/claude-sonnet-5" + default_reasoning_effort: high + - match: "anthropic/claude-sonnet-4.6" + default_reasoning_effort: high + - match: "perplexity/sonar-reasoning-pro" + patch: + body: + include_reasoning: true + - match: "perplexity/sonar-deep-research" + patch: + body: + include_reasoning: true + +- provider: github + models: + - name: text-embedding-3-large + type: embedding + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: text-embedding-3-small + type: embedding + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: cohere-embed-v3-english + type: embedding + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: cohere-embed-v3-multilingual + type: embedding + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + +- provider: deepinfra + models: + - name: BAAI/bge-large-en-v1.5 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: BAAI/bge-m3 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 100 + - name: intfloat/e5-large-v2 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: intfloat/multilingual-e5-large + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: thenlper/gte-large + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + +- provider: jina + models: + - name: jina-embeddings-v3 + type: embedding + input_price: 0 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 100 + - name: jina-clip-v2 + type: embedding + input_price: 0 + max_tokens_per_chunk: 8192 + default_chunk_size: 1500 + max_batch_size: 100 + - name: jina-colbert-v2 + type: embedding + input_price: 0 + max_tokens_per_chunk: 8192 + default_chunk_size: 1500 + max_batch_size: 100 + - name: jina-reranker-v2-base-multilingual + type: reranker + max_input_tokens: 8192 + input_price: 0 + - name: jina-colbert-v2 + type: reranker + max_input_tokens: 8192 + input_price: 0 + +- provider: voyageai + models: + - name: voyage-3-large + type: embedding + max_input_tokens: 120000 + input_price: 0.18 + max_tokens_per_chunk: 32000 + default_chunk_size: 2000 + max_batch_size: 128 + - name: voyage-3 + type: embedding + max_input_tokens: 320000 + input_price: 0.06 + max_tokens_per_chunk: 32000 + default_chunk_size: 2000 + max_batch_size: 128 + - name: voyage-3-lite + type: embedding + max_input_tokens: 1000000 + input_price: 0.02 + max_tokens_per_chunk: 32000 + default_chunk_size: 1000 + max_batch_size: 128 + - name: rerank-2 + type: reranker + max_input_tokens: 16000 + input_price: 0.05 + - name: rerank-2-lite + type: reranker + max_input_tokens: 8000 + input_price: 0.02 diff --git a/src/client/catalog.rs b/src/client/catalog.rs new file mode 100644 index 0000000..27e0cda --- /dev/null +++ b/src/client/catalog.rs @@ -0,0 +1,327 @@ +use super::model::ModelData; +use super::oauth::OAuthConfig; + +use fancy_regex::Regex; +use log::warn; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::sync::LazyLock; + +pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); + +pub static ALL_QUIRKS: LazyLock> = + LazyLock::new(|| serde_yaml::from_str(QUIRKS_YAML).expect("quirks.yaml must be valid")); + +/// Hand-owned catalog overrides for a single provider: oauth configuration, +/// curation excludes, field overlays for API-sourced models, suffixed model +/// variants, and fully hand-owned model entries. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ProviderQuirks { + pub provider: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub oauth: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub exclude: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub rules: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub variants: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub models: Vec, +} + +/// A field overlay applied to every API-sourced model whose name matches the +/// glob in `match` (`*` = any run of characters, `?` = exactly one). Only +/// explicitly present fields are written; absent fields leave the model +/// untouched. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct QuirkRule { + pub r#match: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub real_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_input_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_output_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub input_price: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub output_price: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub patch: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub require_max_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub supports_vision: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub supports_function_calling: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub reasoning_levels: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub default_reasoning_effort: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub no_stream: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub no_system_message: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub system_prompt_prefix: Option, +} + +/// Clones every model whose name matches the glob in `match`, appends `suffix` +/// to the clone's name, and overlays any explicitly present fields onto the +/// clone. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct QuirkVariant { + pub r#match: String, + pub suffix: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub real_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_input_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_output_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub input_price: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub output_price: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub patch: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub require_max_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub supports_vision: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub supports_function_calling: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub reasoning_levels: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub default_reasoning_effort: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub no_stream: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub no_system_message: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub system_prompt_prefix: Option, +} + +/// Applies every rule whose glob matches `data.name`, in file order; later +/// rules overwrite earlier ones on the same field. +#[allow(dead_code)] +pub fn apply_rules(provider_quirks: &ProviderQuirks, data: &mut ModelData) { + for rule in &provider_quirks.rules { + if !glob_matches(&rule.r#match, &data.name) { + continue; + } + if let Some(real_name) = &rule.real_name { + data.real_name = Some(real_name.clone()); + } + if let Some(max_input_tokens) = rule.max_input_tokens { + data.max_input_tokens = Some(max_input_tokens); + } + if let Some(max_output_tokens) = rule.max_output_tokens { + data.max_output_tokens = Some(max_output_tokens); + } + if let Some(input_price) = rule.input_price { + data.input_price = Some(input_price); + } + if let Some(output_price) = rule.output_price { + data.output_price = Some(output_price); + } + if let Some(patch) = &rule.patch { + data.patch = Some(patch.clone()); + } + if let Some(require_max_tokens) = rule.require_max_tokens { + data.require_max_tokens = require_max_tokens; + } + if let Some(supports_vision) = rule.supports_vision { + data.supports_vision = supports_vision; + } + if let Some(supports_function_calling) = rule.supports_function_calling { + data.supports_function_calling = supports_function_calling; + } + if let Some(reasoning_levels) = &rule.reasoning_levels { + data.reasoning_levels = reasoning_levels.clone(); + } + if let Some(default_reasoning_effort) = &rule.default_reasoning_effort { + data.default_reasoning_effort = Some(default_reasoning_effort.clone()); + } + if let Some(no_stream) = rule.no_stream { + data.no_stream = no_stream; + } + if let Some(no_system_message) = rule.no_system_message { + data.no_system_message = no_system_message; + } + if let Some(system_prompt_prefix) = &rule.system_prompt_prefix { + data.system_prompt_prefix = Some(system_prompt_prefix.clone()); + } + } +} + +/// Finds the quirks for a client: an exact provider match wins, otherwise a +/// client whose name starts with the provider name (openai-compatible clients +/// are conventionally named after the provider they wrap). +#[allow(dead_code)] +pub fn quirks_for(client_name: &str) -> Option<&'static ProviderQuirks> { + ALL_QUIRKS + .iter() + .find(|quirks| quirks.provider == client_name) + .or_else(|| { + ALL_QUIRKS + .iter() + .find(|quirks| client_name.starts_with(&quirks.provider)) + }) +} + +fn glob_matches(pattern: &str, name: &str) -> bool { + compile_glob(pattern).is_match(name).unwrap_or(false) +} + +/// Translates a glob pattern (`*` = any run of characters, `?` = exactly one) +/// into an anchored regex. Patterns that fail to compile match nothing. +fn compile_glob(pattern: &str) -> Regex { + let translated = format!( + "^{}$", + fancy_regex::escape(pattern) + .replace("\\*", ".*") + .replace("\\?", ".") + ); + Regex::new(&translated).unwrap_or_else(|error| { + warn!("Invalid model quirk pattern '{pattern}': {error}. It will match nothing."); + Regex::new("(?!)").expect("'(?!)' is a valid never-matching regex") + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + const KNOWN_PROVIDERS: [&str; 24] = [ + "openai", + "gemini", + "claude", + "mistral", + "ai21", + "cohere", + "xai", + "perplexity", + "groq", + "vertexai", + "bedrock", + "cloudflare", + "ernie", + "qianwen", + "hunyuan", + "moonshot", + "deepseek", + "zhipuai", + "minimax", + "openrouter", + "github", + "deepinfra", + "jina", + "voyageai", + ]; + + #[test] + fn quirks_yaml_parses() { + assert!(!ALL_QUIRKS.is_empty()); + for quirks in ALL_QUIRKS.iter() { + assert!( + KNOWN_PROVIDERS.contains(&quirks.provider.as_str()), + "unknown provider '{}' in quirks.yaml", + quirks.provider + ); + } + } + + #[test] + fn quirks_models_no_dropped_keys() { + let skipped_when_default = [ + "type", + "require_max_tokens", + "supports_vision", + "supports_function_calling", + "reasoning_levels", + "no_stream", + "no_system_message", + ]; + let raw: serde_yaml::Value = serde_yaml::from_str(QUIRKS_YAML).unwrap(); + for provider in raw.as_sequence().unwrap() { + let Some(models) = provider + .get("models") + .and_then(|models| models.as_sequence()) + else { + continue; + }; + for entry in models { + let parsed: ModelData = serde_yaml::from_value(entry.clone()).unwrap(); + let reserialized = serde_yaml::to_value(&parsed).unwrap(); + let reserialized_keys: Vec<&str> = reserialized + .as_mapping() + .unwrap() + .keys() + .map(|key| key.as_str().unwrap()) + .collect(); + for key in entry.as_mapping().unwrap().keys() { + let key = key.as_str().unwrap(); + assert!( + reserialized_keys.contains(&key) || skipped_when_default.contains(&key), + "key '{key}' on model '{}' was dropped by ModelData", + parsed.name + ); + } + } + } + } + + #[test] + fn rules_apply_in_order() { + let quirks: ProviderQuirks = serde_yaml::from_str( + r#" +provider: openai +rules: + - match: "model-*" + default_reasoning_effort: low + require_max_tokens: true + - match: "model-a" + default_reasoning_effort: high +"#, + ) + .unwrap(); + + let mut matched_by_both = ModelData::new("model-a"); + apply_rules(&quirks, &mut matched_by_both); + assert_eq!( + matched_by_both.default_reasoning_effort.as_deref(), + Some("high") + ); + assert!(matched_by_both.require_max_tokens); + + let mut matched_by_glob = ModelData::new("model-b"); + apply_rules(&quirks, &mut matched_by_glob); + assert_eq!( + matched_by_glob.default_reasoning_effort.as_deref(), + Some("low") + ); + + let mut unmatched = ModelData::new("other"); + apply_rules(&quirks, &mut unmatched); + assert_eq!(unmatched.default_reasoning_effort, None); + assert!(!unmatched.require_max_tokens); + } + + #[test] + fn glob_semantics() { + assert!(glob_matches("claude-*", "claude-opus-4-8")); + assert!(!glob_matches("claude-*", "gpt-4")); + assert!(glob_matches("claude-*", "claude-")); + assert!(glob_matches("gpt-?", "gpt-4")); + assert!(!glob_matches("gpt-?", "gpt-45")); + assert!(!glob_matches("gpt-?", "gpt-")); + assert!(glob_matches("o3", "o3")); + assert!(!glob_matches("o3", "o3-mini")); + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs index c82c561..54cd832 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,4 +1,5 @@ mod access_token; +pub mod catalog; mod claude_oauth; mod common; mod gemini_oauth; diff --git a/src/client/model.rs b/src/client/model.rs index 0137be9..2c84ec8 100644 --- a/src/client/model.rs +++ b/src/client/model.rs @@ -330,11 +330,11 @@ pub struct ModelData { #[serde(skip_serializing_if = "Option::is_none")] pub default_reasoning_effort: Option, #[serde(default, skip_serializing_if = "std::ops::Not::not")] - no_stream: bool, + pub(crate) no_stream: bool, #[serde(default, skip_serializing_if = "std::ops::Not::not")] - no_system_message: bool, + pub(crate) no_system_message: bool, #[serde(skip_serializing_if = "Option::is_none")] - system_prompt_prefix: Option, + pub(crate) system_prompt_prefix: Option, // embedding-only properties #[serde(skip_serializing_if = "Option::is_none")] From 61c403703309e3f5c360bbdeb305847814742904 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 13:17:48 -0600 Subject: [PATCH 02/30] feat: add model catalog source adapters and merge engine --- src/client/catalog.rs | 1111 ++++++++++++++++++- src/client/testdata/modelsdev_fixture.json | 457 ++++++++ src/client/testdata/openrouter_fixture.json | 538 +++++++++ 3 files changed, 2100 insertions(+), 6 deletions(-) create mode 100644 src/client/testdata/modelsdev_fixture.json create mode 100644 src/client/testdata/openrouter_fixture.json diff --git a/src/client/catalog.rs b/src/client/catalog.rs index 27e0cda..4d18162 100644 --- a/src/client/catalog.rs +++ b/src/client/catalog.rs @@ -1,17 +1,549 @@ -use super::model::ModelData; +use super::model::{ModelData, ProviderModels}; use super::oauth::OAuthConfig; +use crate::utils::fetch; + +use anyhow::{Result, bail}; use fancy_regex::Regex; use log::warn; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::collections::HashMap; use std::sync::LazyLock; pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); +#[allow(dead_code)] pub static ALL_QUIRKS: LazyLock> = LazyLock::new(|| serde_yaml::from_str(QUIRKS_YAML).expect("quirks.yaml must be valid")); +const MODELSDEV_API_URL: &str = "https://models.dev/api.json"; +const OPENROUTER_API_URL: &str = "https://openrouter.ai/api/v1/models"; + +/// Upstream ids for one coyote provider. The table order is the fixed output +/// order of the built catalog. Providers with neither upstream id are fully +/// quirks-owned; `openrouter` is special-cased in the merge: its models come +/// from the OpenRouter list natively instead of via a prefix join. +struct SourceIds { + provider: &'static str, + modelsdev: Option<&'static str>, + openrouter: Option<&'static str>, +} + +const PROVIDER_SOURCES: [SourceIds; 24] = [ + SourceIds { + provider: "openai", + modelsdev: Some("openai"), + openrouter: Some("openai"), + }, + SourceIds { + provider: "gemini", + modelsdev: Some("google"), + openrouter: Some("google"), + }, + SourceIds { + provider: "claude", + modelsdev: Some("anthropic"), + openrouter: Some("anthropic"), + }, + SourceIds { + provider: "mistral", + modelsdev: Some("mistral"), + openrouter: Some("mistralai"), + }, + SourceIds { + provider: "ai21", + modelsdev: None, + openrouter: None, + }, + SourceIds { + provider: "cohere", + modelsdev: Some("cohere"), + openrouter: Some("cohere"), + }, + SourceIds { + provider: "xai", + modelsdev: Some("xai"), + openrouter: Some("x-ai"), + }, + SourceIds { + provider: "perplexity", + modelsdev: Some("perplexity"), + openrouter: Some("perplexity"), + }, + SourceIds { + provider: "groq", + modelsdev: Some("groq"), + openrouter: Some("groq"), + }, + SourceIds { + provider: "vertexai", + modelsdev: Some("google-vertex"), + openrouter: Some("google"), + }, + SourceIds { + provider: "bedrock", + modelsdev: Some("amazon-bedrock"), + openrouter: Some("amazon"), + }, + SourceIds { + provider: "cloudflare", + modelsdev: Some("cloudflare-workers-ai"), + openrouter: None, + }, + SourceIds { + provider: "ernie", + modelsdev: None, + openrouter: None, + }, + SourceIds { + provider: "qianwen", + modelsdev: Some("alibaba"), + openrouter: Some("qwen"), + }, + SourceIds { + provider: "hunyuan", + modelsdev: None, + openrouter: None, + }, + SourceIds { + provider: "moonshot", + modelsdev: Some("moonshotai"), + openrouter: Some("moonshotai"), + }, + SourceIds { + provider: "deepseek", + modelsdev: Some("deepseek"), + openrouter: Some("deepseek"), + }, + SourceIds { + provider: "zhipuai", + modelsdev: Some("zhipuai"), + openrouter: Some("zhipuai"), + }, + SourceIds { + provider: "minimax", + modelsdev: Some("minimax"), + openrouter: Some("minimax"), + }, + SourceIds { + provider: "openrouter", + modelsdev: None, + openrouter: None, + }, + SourceIds { + provider: "github", + modelsdev: Some("github-copilot"), + openrouter: Some("github"), + }, + SourceIds { + provider: "deepinfra", + modelsdev: Some("deepinfra"), + openrouter: None, + }, + SourceIds { + provider: "jina", + modelsdev: None, + openrouter: None, + }, + SourceIds { + provider: "voyageai", + modelsdev: None, + openrouter: None, + }, +]; + +/// Raw payloads from the two upstream catalogs. Each source is independently +/// fallible; `None` means that fetch failed and the merge degrades gracefully. +pub struct FetchedSources { + pub modelsdev: Option, + pub openrouter: Option, +} + +/// Fetches both upstream catalogs concurrently. A failed fetch is logged and +/// surfaces as `None` rather than an error. +#[allow(dead_code)] +pub async fn fetch_sources() -> FetchedSources { + let (modelsdev, openrouter) = tokio::join!(fetch(MODELSDEV_API_URL), fetch(OPENROUTER_API_URL)); + FetchedSources { + modelsdev: modelsdev + .inspect_err(|err| warn!("Failed to fetch {MODELSDEV_API_URL}: {err}")) + .ok(), + openrouter: openrouter + .inspect_err(|err| warn!("Failed to fetch {OPENROUTER_API_URL}: {err}")) + .ok(), + } +} + +/// An API-sourced model plus the release date used for ordering. The date is +/// deliberately kept out of [`ModelData`] and never serialized. +struct SourcedModel { + data: ModelData, + release_date: Option, +} + +fn clamp_input_tokens(value: u64) -> Option { + (1..=50_000_000).contains(&value).then_some(value as usize) +} + +fn clamp_output_tokens(value: i64) -> Option { + (1..=10_000_000).contains(&value).then_some(value as isize) +} + +fn clamp_price(value: f64) -> Option { + (0.0..=10_000.0).contains(&value).then_some(value) +} + +/// Extracts the models of one models.dev provider. Tolerant by design: +/// missing or malformed fields are skipped while the model is kept, and +/// out-of-range numbers are dropped field-wise. +fn modelsdev_provider_models(api: &Value, provider_id: &str) -> Vec { + let Some(models) = api[provider_id]["models"].as_object() else { + return Vec::new(); + }; + models + .iter() + .map(|(name, entry)| { + let mut data = ModelData::new(name); + data.max_input_tokens = entry["limit"]["context"] + .as_u64() + .and_then(clamp_input_tokens); + data.max_output_tokens = entry["limit"]["output"] + .as_i64() + .and_then(clamp_output_tokens); + data.input_price = entry["cost"]["input"].as_f64().and_then(clamp_price); + data.output_price = entry["cost"]["output"].as_f64().and_then(clamp_price); + data.supports_vision = entry["modalities"]["input"] + .as_array() + .is_some_and(|modes| modes.iter().any(|mode| mode.as_str() == Some("image"))); + data.supports_function_calling = entry["tool_call"].as_bool().unwrap_or(false); + data.reasoning_levels = entry["reasoning_options"] + .as_array() + .and_then(|options| { + options + .iter() + .find(|option| option["type"].as_str() == Some("effort")) + }) + .and_then(|option| option["values"].as_array()) + .map(|values| { + values + .iter() + .filter_map(|value| value.as_str().map(str::to_string)) + .collect() + }) + .unwrap_or_default(); + let release_date = entry["release_date"].as_str().map(str::to_string); + SourcedModel { data, release_date } + }) + .collect() +} + +/// OpenRouter prices are USD per token; the catalog stores USD per million +/// tokens, rounded to 4 decimals. +fn openrouter_price(value: &Value) -> Option { + let per_token: f64 = value.as_str()?.parse().ok()?; + clamp_price((per_token * 1_000_000.0 * 10_000.0).round() / 10_000.0) +} + +/// Maps one OpenRouter list entry onto [`ModelData`], with the same field-wise +/// tolerance and clamping as the models.dev adapter. +fn openrouter_model_data(name: &str, entry: &Value) -> ModelData { + let mut data = ModelData::new(name); + data.max_input_tokens = entry["context_length"] + .as_u64() + .and_then(clamp_input_tokens); + data.max_output_tokens = entry["top_provider"]["max_completion_tokens"] + .as_i64() + .and_then(clamp_output_tokens); + data.input_price = openrouter_price(&entry["pricing"]["prompt"]); + data.output_price = openrouter_price(&entry["pricing"]["completion"]); + data.supports_vision = entry["architecture"]["modality"] + .as_str() + .is_some_and(|modality| modality.contains("image")); + data.supports_function_calling = entry["supported_parameters"] + .as_array() + .is_some_and(|params| params.iter().any(|param| param.as_str() == Some("tools"))); + data +} + +fn openrouter_index(payload: &Value) -> HashMap<&str, &Value> { + payload["data"] + .as_array() + .map(|entries| { + entries + .iter() + .filter_map(|entry| Some((entry["id"].as_str()?, entry))) + .collect() + }) + .unwrap_or_default() +} + +/// Every OpenRouter list entry as a model of the `openrouter` provider, named +/// by its full id. OpenRouter publishes no release date, so ordering falls +/// back to name-ascending. +fn openrouter_native_models(payload: &Value) -> Vec { + payload["data"] + .as_array() + .map(|entries| { + entries + .iter() + .filter_map(|entry| { + let id = entry["id"].as_str()?; + Some(SourcedModel { + data: openrouter_model_data(id, entry), + release_date: None, + }) + }) + .collect() + }) + .unwrap_or_default() +} + +/// Fills fields that models.dev left unset on the same model: OpenRouter data +/// never overrides a present models.dev value. +fn fill_from_openrouter(data: &mut ModelData, entry: &Value) { + let filler = openrouter_model_data(&data.name, entry); + if data.max_input_tokens.is_none() { + data.max_input_tokens = filler.max_input_tokens; + } + if data.max_output_tokens.is_none() { + data.max_output_tokens = filler.max_output_tokens; + } + if data.input_price.is_none() { + data.input_price = filler.input_price; + } + if data.output_price.is_none() { + data.output_price = filler.output_price; + } + if !data.supports_vision { + data.supports_vision = filler.supports_vision; + } + if !data.supports_function_calling { + data.supports_function_calling = filler.supports_function_calling; + } +} + +/// Outcome of a catalog merge. `degraded` lists API-mapped providers that fell +/// back to their hand-owned quirks models because API data was unavailable or +/// empty; `failed` lists providers that emitted nothing at all. +pub struct CatalogBuild { + pub providers: Vec, + pub degraded: Vec, + pub failed: Vec, +} + +/// Merges API-sourced model data with hand-owned quirks into the full +/// provider catalog, in the fixed provider order of [`PROVIDER_SOURCES`]. +/// +/// Per provider: API models (models.dev primary, OpenRouter gap-fill) are +/// curated by `exclude` globs and `rules`, hand-owned `models:` entries are +/// merged in field-wise (hand-owned wins) and placed first, API models are +/// sorted newest-first by release date (name-ascending tiebreak, undated +/// last), and `variants` clones are inserted immediately after their base +/// model. Providers without usable data are routed to `degraded` or `failed` +/// instead of erroring; a provider never emits zero models. +pub fn build_catalog(sources: &FetchedSources, quirks: &[ProviderQuirks]) -> CatalogBuild { + let modelsdev: Option = sources + .modelsdev + .as_deref() + .and_then(|payload| serde_json::from_str(payload).ok()); + let openrouter: Option = sources + .openrouter + .as_deref() + .and_then(|payload| serde_json::from_str(payload).ok()); + let or_index = openrouter + .as_ref() + .map(openrouter_index) + .unwrap_or_default(); + + let mut providers = Vec::new(); + let mut degraded = Vec::new(); + let mut failed = Vec::new(); + + for source in &PROVIDER_SOURCES { + let provider_quirks = quirks_for(quirks, source.provider); + let api_mapped = source.provider == "openrouter" || source.modelsdev.is_some(); + + let mut api_models: Vec = if source.provider == "openrouter" { + openrouter + .as_ref() + .map(openrouter_native_models) + .unwrap_or_default() + } else if let (Some(id), Some(api)) = (source.modelsdev, modelsdev.as_ref()) { + let mut models = modelsdev_provider_models(api, id); + if let Some(prefix) = source.openrouter { + for model in &mut models { + if let Some(entry) = + or_index.get(format!("{prefix}/{}", model.data.name).as_str()) + { + fill_from_openrouter(&mut model.data, entry); + } + } + } + models + } else { + Vec::new() + }; + + if let Some(provider_quirks) = provider_quirks { + api_models.retain(|model| { + !provider_quirks + .exclude + .iter() + .any(|pattern| glob_matches(pattern, &model.data.name)) + }); + for model in &mut api_models { + apply_rules(provider_quirks, &mut model.data); + } + } + + let hand_owned = provider_quirks + .map(|provider_quirks| provider_quirks.models.clone()) + .unwrap_or_default(); + + if api_models.is_empty() { + if hand_owned.is_empty() { + failed.push(source.provider.to_string()); + continue; + } + if api_mapped { + degraded.push(source.provider.to_string()); + } + } + + let mut merged_hand = Vec::with_capacity(hand_owned.len()); + for hand in hand_owned { + match api_models + .iter() + .position(|model| model.data.name == hand.name) + { + Some(position) => { + let api = api_models.remove(position); + merged_hand.push(overlay_hand_owned(hand, api.data)); + } + None => merged_hand.push(hand), + } + } + + api_models.sort_by(|a, b| match (&a.release_date, &b.release_date) { + (Some(a_date), Some(b_date)) => b_date + .cmp(a_date) + .then_with(|| a.data.name.cmp(&b.data.name)), + (Some(_), None) => std::cmp::Ordering::Less, + (None, Some(_)) => std::cmp::Ordering::Greater, + (None, None) => a.data.name.cmp(&b.data.name), + }); + + let bases = merged_hand + .into_iter() + .chain(api_models.into_iter().map(|model| model.data)); + + let variants = provider_quirks + .map(|provider_quirks| provider_quirks.variants.as_slice()) + .unwrap_or_default(); + let mut models = Vec::new(); + for base in bases { + let mut clones = Vec::new(); + for variant in variants { + if !glob_matches(&variant.r#match, &base.name) { + continue; + } + let mut clone = base.clone(); + clone.name = format!("{}{}", base.name, variant.suffix); + // Without an explicit real_name the clone would send its + // suffixed name on the wire; default to the base's wire name. + if clone.real_name.is_none() { + clone.real_name = Some(base.name.clone()); + } + apply_variant(variant, &mut clone); + clones.push(clone); + } + models.push(base); + models.append(&mut clones); + } + + providers.push(ProviderModels { + provider: source.provider.to_string(), + oauth: provider_quirks.and_then(|provider_quirks| provider_quirks.oauth.clone()), + models, + }); + } + + CatalogBuild { + providers, + degraded, + failed, + } +} + +/// Like [`build_catalog`], but errors unless every provider was assembled +/// from live API data (or is quirks-owned by design). +#[allow(dead_code)] +pub fn build_catalog_strict( + sources: &FetchedSources, + quirks: &[ProviderQuirks], +) -> Result> { + let build = build_catalog(sources, quirks); + if !build.degraded.is_empty() || !build.failed.is_empty() { + bail!( + "model catalog build incomplete: degraded providers [{}], failed providers [{}]", + build.degraded.join(", "), + build.failed.join(", ") + ); + } + Ok(build.providers) +} + +/// Fraction of `old` names that also appear in `new`; empty `old` yields 1.0. +/// Used to guard against upstream id-shape drift: a low overlap means the +/// upstream catalog renamed its models out from under us. +#[allow(dead_code)] +pub(crate) fn name_overlap(old: &[String], new: &[String]) -> f64 { + if old.is_empty() { + return 1.0; + } + let new_names: std::collections::HashSet<&str> = new.iter().map(String::as_str).collect(); + let kept = old + .iter() + .filter(|name| new_names.contains(name.as_str())) + .count(); + kept as f64 / old.len() as f64 +} + +/// Overlays a hand-owned quirks entry onto the API-sourced model of the same +/// name. Every field the hand-owned entry carries (Some, true, or non-empty) +/// wins; absent fields keep the API value. `model_type` is authoritative on +/// the hand-owned entry. +fn overlay_hand_owned(hand: ModelData, api: ModelData) -> ModelData { + ModelData { + name: hand.name, + model_type: hand.model_type, + real_name: hand.real_name.or(api.real_name), + max_input_tokens: hand.max_input_tokens.or(api.max_input_tokens), + input_price: hand.input_price.or(api.input_price), + output_price: hand.output_price.or(api.output_price), + patch: hand.patch.or(api.patch), + max_output_tokens: hand.max_output_tokens.or(api.max_output_tokens), + require_max_tokens: hand.require_max_tokens || api.require_max_tokens, + supports_vision: hand.supports_vision || api.supports_vision, + supports_function_calling: hand.supports_function_calling || api.supports_function_calling, + reasoning_levels: if hand.reasoning_levels.is_empty() { + api.reasoning_levels + } else { + hand.reasoning_levels + }, + default_reasoning_effort: hand + .default_reasoning_effort + .or(api.default_reasoning_effort), + no_stream: hand.no_stream || api.no_stream, + no_system_message: hand.no_system_message || api.no_system_message, + system_prompt_prefix: hand.system_prompt_prefix.or(api.system_prompt_prefix), + max_tokens_per_chunk: hand.max_tokens_per_chunk.or(api.max_tokens_per_chunk), + default_chunk_size: hand.default_chunk_size.or(api.default_chunk_size), + max_batch_size: hand.max_batch_size.or(api.max_batch_size), + } +} + /// Hand-owned catalog overrides for a single provider: oauth configuration, /// curation excludes, field overlays for API-sourced models, suffixed model /// variants, and fully hand-owned model entries. @@ -109,7 +641,6 @@ pub struct QuirkVariant { /// Applies every rule whose glob matches `data.name`, in file order; later /// rules overwrite earlier ones on the same field. -#[allow(dead_code)] pub fn apply_rules(provider_quirks: &ProviderQuirks, data: &mut ModelData) { for rule in &provider_quirks.rules { if !glob_matches(&rule.r#match, &data.name) { @@ -160,16 +691,65 @@ pub fn apply_rules(provider_quirks: &ProviderQuirks, data: &mut ModelData) { } } +/// Overlays a variant's explicitly present fields onto a cloned model; same +/// field semantics as [`apply_rules`]. +fn apply_variant(variant: &QuirkVariant, data: &mut ModelData) { + if let Some(real_name) = &variant.real_name { + data.real_name = Some(real_name.clone()); + } + if let Some(max_input_tokens) = variant.max_input_tokens { + data.max_input_tokens = Some(max_input_tokens); + } + if let Some(max_output_tokens) = variant.max_output_tokens { + data.max_output_tokens = Some(max_output_tokens); + } + if let Some(input_price) = variant.input_price { + data.input_price = Some(input_price); + } + if let Some(output_price) = variant.output_price { + data.output_price = Some(output_price); + } + if let Some(patch) = &variant.patch { + data.patch = Some(patch.clone()); + } + if let Some(require_max_tokens) = variant.require_max_tokens { + data.require_max_tokens = require_max_tokens; + } + if let Some(supports_vision) = variant.supports_vision { + data.supports_vision = supports_vision; + } + if let Some(supports_function_calling) = variant.supports_function_calling { + data.supports_function_calling = supports_function_calling; + } + if let Some(reasoning_levels) = &variant.reasoning_levels { + data.reasoning_levels = reasoning_levels.clone(); + } + if let Some(default_reasoning_effort) = &variant.default_reasoning_effort { + data.default_reasoning_effort = Some(default_reasoning_effort.clone()); + } + if let Some(no_stream) = variant.no_stream { + data.no_stream = no_stream; + } + if let Some(no_system_message) = variant.no_system_message { + data.no_system_message = no_system_message; + } + if let Some(system_prompt_prefix) = &variant.system_prompt_prefix { + data.system_prompt_prefix = Some(system_prompt_prefix.clone()); + } +} + /// Finds the quirks for a client: an exact provider match wins, otherwise a /// client whose name starts with the provider name (openai-compatible clients /// are conventionally named after the provider they wrap). -#[allow(dead_code)] -pub fn quirks_for(client_name: &str) -> Option<&'static ProviderQuirks> { - ALL_QUIRKS +pub fn quirks_for<'a>( + quirks: &'a [ProviderQuirks], + client_name: &str, +) -> Option<&'a ProviderQuirks> { + quirks .iter() .find(|quirks| quirks.provider == client_name) .or_else(|| { - ALL_QUIRKS + quirks .iter() .find(|quirks| client_name.starts_with(&quirks.provider)) }) @@ -198,6 +778,8 @@ fn compile_glob(pattern: &str) -> Regex { mod tests { use super::*; + use serde_json::json; + const KNOWN_PROVIDERS: [&str; 24] = [ "openai", "gemini", @@ -225,6 +807,40 @@ mod tests { "voyageai", ]; + const MODELSDEV_FIXTURE: &str = include_str!("testdata/modelsdev_fixture.json"); + const OPENROUTER_FIXTURE: &str = include_str!("testdata/openrouter_fixture.json"); + + fn make_sources(modelsdev: Option<&str>, openrouter: Option<&str>) -> FetchedSources { + FetchedSources { + modelsdev: modelsdev.map(str::to_string), + openrouter: openrouter.map(str::to_string), + } + } + + fn fixture_sources() -> FetchedSources { + make_sources(Some(MODELSDEV_FIXTURE), Some(OPENROUTER_FIXTURE)) + } + + fn parse_quirks(yaml: &str) -> Vec { + serde_yaml::from_str(yaml).unwrap() + } + + fn find_provider<'a>(build: &'a CatalogBuild, provider: &str) -> &'a ProviderModels { + build + .providers + .iter() + .find(|candidate| candidate.provider == provider) + .unwrap_or_else(|| panic!("provider '{provider}' missing")) + } + + fn find_model<'a>(provider: &'a ProviderModels, name: &str) -> &'a ModelData { + provider + .models + .iter() + .find(|model| model.name == name) + .unwrap_or_else(|| panic!("model '{name}' missing")) + } + #[test] fn quirks_yaml_parses() { assert!(!ALL_QUIRKS.is_empty()); @@ -237,6 +853,15 @@ mod tests { } } + #[test] + fn provider_sources_match_known_providers() { + let order: Vec<&str> = PROVIDER_SOURCES + .iter() + .map(|source| source.provider) + .collect(); + assert_eq!(order, KNOWN_PROVIDERS); + } + #[test] fn quirks_models_no_dropped_keys() { let skipped_when_default = [ @@ -324,4 +949,478 @@ rules: assert!(glob_matches("o3", "o3")); assert!(!glob_matches("o3", "o3-mini")); } + + #[test] + fn modelsdev_adapter_maps_fields() { + let api: Value = serde_json::from_str(MODELSDEV_FIXTURE).unwrap(); + + let openai = modelsdev_provider_models(&api, "openai"); + let gpt56 = openai + .iter() + .find(|model| model.data.name == "gpt-5.6") + .unwrap(); + assert_eq!(gpt56.data.max_input_tokens, Some(1_050_000)); + assert_eq!(gpt56.data.max_output_tokens, Some(128_000)); + assert_eq!(gpt56.data.input_price, Some(4.0)); + assert_eq!(gpt56.data.output_price, Some(20.0)); + assert!(gpt56.data.supports_vision); + assert!(gpt56.data.supports_function_calling); + assert_eq!( + gpt56.data.reasoning_levels, + ["none", "low", "medium", "high", "xhigh", "max"] + ); + assert_eq!(gpt56.release_date.as_deref(), Some("2026-07-09")); + + let alibaba = modelsdev_provider_models(&api, "alibaba"); + assert_eq!(alibaba.len(), 1); + let qwen = &alibaba[0]; + assert_eq!(qwen.data.name, "qwen-max"); + assert_eq!(qwen.data.max_input_tokens, Some(32_768)); + assert_eq!(qwen.data.max_output_tokens, Some(8_192)); + assert_eq!(qwen.data.input_price, Some(1.6)); + assert_eq!(qwen.data.output_price, Some(6.4)); + assert!(!qwen.data.supports_vision); + assert!(qwen.data.supports_function_calling); + assert!(qwen.data.reasoning_levels.is_empty()); + assert_eq!(qwen.release_date.as_deref(), Some("2024-04-03")); + + assert!(modelsdev_provider_models(&api, "no-such-provider").is_empty()); + } + + #[test] + fn openrouter_adapter_maps_fields() { + let payload: Value = serde_json::from_str(OPENROUTER_FIXTURE).unwrap(); + let index = openrouter_index(&payload); + + let data = openrouter_model_data("gpt-4.1", index["openai/gpt-4.1"]); + assert_eq!(data.max_input_tokens, Some(1_047_576)); + assert_eq!(data.max_output_tokens, Some(32_768)); + assert_eq!(data.input_price, Some(2.0)); + assert_eq!(data.output_price, Some(8.0)); + assert!(data.supports_vision); + assert!(data.supports_function_calling); + + let rounded = openrouter_model_data( + "m", + &json!({"pricing": {"prompt": "0.00000123456789", "completion": "0.0000025"}}), + ); + assert_eq!(rounded.input_price, Some(1.2346)); + assert_eq!(rounded.output_price, Some(2.5)); + } + + #[test] + fn clamps_drop_out_of_range_fields() { + let api = json!({ + "p": {"models": {"m": { + "limit": {"context": 100_000_000, "output": 0}, + "cost": {"input": -1.0, "output": 20_000.0}, + }}} + }); + let models = modelsdev_provider_models(&api, "p"); + assert_eq!(models.len(), 1); + let data = &models[0].data; + assert_eq!(data.name, "m"); + assert_eq!(data.max_input_tokens, None); + assert_eq!(data.max_output_tokens, None); + assert_eq!(data.input_price, None); + assert_eq!(data.output_price, None); + + let data = openrouter_model_data( + "m", + &json!({ + "context_length": 60_000_000, + "top_provider": {"max_completion_tokens": 20_000_000}, + "pricing": {"prompt": "-0.000001", "completion": "999"}, + }), + ); + assert_eq!(data.max_input_tokens, None); + assert_eq!(data.max_output_tokens, None); + assert_eq!(data.input_price, None); + assert_eq!(data.output_price, None); + } + + #[test] + fn adapters_never_set_quirks_owned_fields() { + let api: Value = serde_json::from_str(MODELSDEV_FIXTURE).unwrap(); + let payload: Value = serde_json::from_str(OPENROUTER_FIXTURE).unwrap(); + let mut all: Vec = ["openai", "anthropic", "xai", "alibaba"] + .iter() + .flat_map(|id| modelsdev_provider_models(&api, id)) + .collect(); + all.extend(openrouter_native_models(&payload)); + assert!(!all.is_empty()); + for model in &all { + let data = &model.data; + assert_eq!(data.model_type, "chat", "{}", data.name); + assert!(data.patch.is_none(), "{}", data.name); + assert!(data.real_name.is_none(), "{}", data.name); + assert!(data.system_prompt_prefix.is_none(), "{}", data.name); + assert!(data.max_tokens_per_chunk.is_none(), "{}", data.name); + assert!(data.default_chunk_size.is_none(), "{}", data.name); + assert!(data.max_batch_size.is_none(), "{}", data.name); + } + } + + #[test] + fn oauth_comes_only_from_quirks() { + let without_quirks = build_catalog(&fixture_sources(), &[]); + assert!( + without_quirks + .providers + .iter() + .all(|provider| provider.oauth.is_none()) + ); + + let quirks = parse_quirks( + r#" +- provider: xai + oauth: + client_id: id + token_url: https://example.com/token +"#, + ); + let build = build_catalog(&fixture_sources(), &quirks); + assert!(find_provider(&build, "xai").oauth.is_some()); + assert!(find_provider(&build, "openai").oauth.is_none()); + } + + #[test] + fn merge_precedence_hand_owned_beats_rule_beats_modelsdev_beats_openrouter() { + let modelsdev = json!({ + "xai": {"models": { + "m-1": {"limit": {"context": 1000}, "cost": {"input": 1.0}}, + "m-2": {"limit": {"context": 1000}}, + }} + }) + .to_string(); + let openrouter = json!({"data": [ + { + "id": "x-ai/m-1", + "context_length": 2000, + "pricing": {"prompt": "0.000003", "completion": "0.000004"}, + "top_provider": {"max_completion_tokens": 512}, + }, + {"id": "x-ai/m-2", "context_length": 2000}, + ]}) + .to_string(); + let quirks = parse_quirks( + r#" +- provider: xai + rules: + - match: "m-1" + input_price: 5 + max_input_tokens: 3000 + models: + - name: m-1 + input_price: 7 +"#, + ); + let build = build_catalog(&make_sources(Some(&modelsdev), Some(&openrouter)), &quirks); + let xai = find_provider(&build, "xai"); + + let merged = find_model(xai, "m-1"); + // hand-owned beats the rule, which beat models.dev, which beat openrouter + assert_eq!(merged.input_price, Some(7.0)); + // rule beats models.dev + assert_eq!(merged.max_input_tokens, Some(3000)); + // openrouter fills fields models.dev left unset + assert_eq!(merged.output_price, Some(4.0)); + assert_eq!(merged.max_output_tokens, Some(512)); + + // models.dev beats openrouter on the same field + assert_eq!(find_model(xai, "m-2").max_input_tokens, Some(1000)); + } + + #[test] + fn excludes_drop_api_models_but_never_hand_owned() { + let quirks = parse_quirks( + r#" +- provider: openai + exclude: + - "gpt-4*" + models: + - name: gpt-4.1 + input_price: 9 +"#, + ); + let build = build_catalog(&fixture_sources(), &quirks); + let openai = find_provider(&build, "openai"); + assert!(openai.models.iter().all(|model| model.name != "gpt-4o")); + find_model(openai, "gpt-5.6"); + let hand = find_model(openai, "gpt-4.1"); + assert_eq!(hand.input_price, Some(9.0)); + // the API copy was excluded before the merge, so nothing leaked in + assert_eq!(hand.max_input_tokens, None); + } + + #[test] + fn variant_expansion() { + let quirks = parse_quirks( + r#" +- provider: xai + variants: + - match: "grok-4.6" + suffix: ":thinking" + max_output_tokens: 24000 + patch: + body: + thinking: true + - match: "grok-4.5" + suffix: ":fast" + real_name: grok-4.5-fast +"#, + ); + let build = build_catalog(&fixture_sources(), &quirks); + let xai = find_provider(&build, "xai"); + let names: Vec<&str> = xai.models.iter().map(|model| model.name.as_str()).collect(); + assert_eq!( + names, + ["grok-4.6", "grok-4.6:thinking", "grok-4.5", "grok-4.5:fast"] + ); + + let thinking = find_model(xai, "grok-4.6:thinking"); + assert_eq!(thinking.real_name.as_deref(), Some("grok-4.6")); + assert_eq!(thinking.max_output_tokens, Some(24_000)); + assert_eq!(thinking.patch, Some(json!({"body": {"thinking": true}}))); + + assert_eq!( + find_model(xai, "grok-4.5:fast").real_name.as_deref(), + Some("grok-4.5-fast") + ); + assert_eq!(find_model(xai, "grok-4.6").real_name, None); + } + + #[test] + fn ordering_hand_owned_first_then_release_date_desc_name_asc() { + let shuffled_payloads = [ + r#"{"openai":{"models":{ + "b":{"release_date":"2026-05-01"}, + "a":{"release_date":"2026-05-01"}, + "c":{"release_date":"2026-06-01"}, + "z":{}, + "y":{} + }}}"#, + r#"{"openai":{"models":{ + "y":{}, + "c":{"release_date":"2026-06-01"}, + "z":{}, + "a":{"release_date":"2026-05-01"}, + "b":{"release_date":"2026-05-01"} + }}}"#, + ]; + let quirks = parse_quirks( + r#" +- provider: openai + models: + - name: n2 + - name: n1 +"#, + ); + for payload in shuffled_payloads { + let build = build_catalog(&make_sources(Some(payload), None), &quirks); + let names: Vec<&str> = find_provider(&build, "openai") + .models + .iter() + .map(|model| model.name.as_str()) + .collect(); + assert_eq!(names, ["n2", "n1", "c", "a", "b", "y", "z"]); + } + } + + #[test] + fn catalog_round_trips_through_yaml() { + let build = build_catalog(&fixture_sources(), &ALL_QUIRKS); + assert!(!build.providers.is_empty()); + let yaml = serde_yaml::to_string(&build.providers).unwrap(); + let parsed: Vec = serde_yaml::from_str(&yaml).unwrap(); + assert_eq!(serde_yaml::to_string(&parsed).unwrap(), yaml); + } + + #[test] + fn openrouter_native_models_use_full_ids() { + let build = build_catalog(&fixture_sources(), &[]); + let openrouter = find_provider(&build, "openrouter"); + assert_eq!(openrouter.models.len(), 8); + + let auto = find_model(openrouter, "openrouter/auto"); + assert_eq!(auto.max_input_tokens, Some(2_000_000)); + // null max_completion_tokens and the "-1" sentinel prices are dropped + assert_eq!(auto.max_output_tokens, None); + assert_eq!(auto.input_price, None); + assert_eq!(auto.output_price, None); + assert!(auto.supports_vision); + assert!(auto.supports_function_calling); + + let free = find_model(openrouter, "openrouter/free"); + assert_eq!(free.max_input_tokens, Some(200_000)); + assert_eq!(free.input_price, Some(0.0)); + assert_eq!(free.output_price, Some(0.0)); + + // no release dates upstream, so full ids sort ascending + let names: Vec<&str> = openrouter + .models + .iter() + .map(|model| model.name.as_str()) + .collect(); + let mut sorted = names.clone(); + sorted.sort_unstable(); + assert_eq!(names, sorted); + } + + #[test] + fn failure_routing_degraded_and_failed() { + let quirks = parse_quirks( + r#" +- provider: gemini + models: [{name: g-1}] +- provider: ai21 + models: [{name: jamba}] +"#, + ); + let build = build_catalog(&make_sources(None, None), &quirks); + assert_eq!(build.degraded, ["gemini"]); + assert!(build.failed.contains(&"openai".to_string())); + assert!(build.failed.contains(&"openrouter".to_string())); + assert!(build.failed.contains(&"jina".to_string())); + assert!(!build.failed.contains(&"gemini".to_string())); + assert!(!build.failed.contains(&"ai21".to_string())); + assert!(!build.degraded.contains(&"ai21".to_string())); + + let providers: Vec<&str> = build + .providers + .iter() + .map(|provider| provider.provider.as_str()) + .collect(); + assert_eq!(providers, ["gemini", "ai21"]); + assert!( + build + .providers + .iter() + .all(|provider| !provider.models.is_empty()) + ); + } + + #[test] + fn strict_errors_on_degraded_or_failed() { + let quirks = parse_quirks( + r#" +- provider: gemini + models: [{name: g-1}] +"#, + ); + let err = build_catalog_strict(&make_sources(None, None), &quirks) + .unwrap_err() + .to_string(); + assert!(err.contains("gemini"), "{err}"); + assert!(err.contains("openai"), "{err}"); + } + + #[test] + fn strict_succeeds_when_every_provider_resolves() { + let mut api = serde_json::Map::new(); + for source in &PROVIDER_SOURCES { + if let Some(id) = source.modelsdev { + api.insert(id.to_string(), json!({"models": {"m": {}}})); + } + } + let modelsdev = Value::Object(api).to_string(); + let openrouter = json!({"data": [{"id": "openrouter/auto"}]}).to_string(); + let quirks = parse_quirks( + r#" +- provider: ai21 + models: [{name: a}] +- provider: ernie + models: [{name: e}] +- provider: hunyuan + models: [{name: h}] +- provider: jina + models: [{name: j}] +- provider: voyageai + models: [{name: v}] +"#, + ); + let providers = + build_catalog_strict(&make_sources(Some(&modelsdev), Some(&openrouter)), &quirks) + .unwrap(); + let order: Vec<&str> = providers + .iter() + .map(|provider| provider.provider.as_str()) + .collect(); + assert_eq!(order, KNOWN_PROVIDERS); + } + + #[test] + fn name_overlap_fraction_of_old_names_still_present() { + let old: Vec = ["a", "b", "c", "d"].map(str::to_string).into(); + let new: Vec = ["a", "c", "x"].map(str::to_string).into(); + assert_eq!(name_overlap(&old, &new), 0.5); + assert_eq!(name_overlap(&[], &new), 1.0); + assert_eq!(name_overlap(&old, &[]), 0.0); + } + + /// Guards against upstream id-shape drift by comparing live API model + /// names against the embedded models.yaml. Run manually: + /// `cargo test idshape_guard_live -- --ignored --nocapture` + #[tokio::test] + #[ignore] + async fn idshape_guard_live() { + let sources = fetch_sources().await; + let modelsdev: Value = serde_json::from_str( + sources + .modelsdev + .as_deref() + .expect("models.dev fetch failed"), + ) + .unwrap(); + let openrouter: Value = serde_json::from_str( + sources + .openrouter + .as_deref() + .expect("openrouter fetch failed"), + ) + .unwrap(); + let embedded: Vec = + serde_yaml::from_str(crate::client::MODELS_YAML).unwrap(); + + println!( + "{:<12} {:>7} {:>5} {:>5}", + "provider", "overlap", "old", "new" + ); + for source in &PROVIDER_SOURCES { + let new: Vec = if source.provider == "openrouter" { + openrouter_native_models(&openrouter) + .into_iter() + .map(|model| model.data.name) + .collect() + } else if let Some(id) = source.modelsdev { + modelsdev_provider_models(&modelsdev, id) + .into_iter() + .map(|model| model.data.name) + .collect() + } else { + continue; + }; + let old: Vec = embedded + .iter() + .find(|provider| provider.provider == source.provider) + .map(|provider| { + provider + .models + .iter() + .filter(|model| { + model.model_type == "chat" && !model.name.ends_with(":thinking") + }) + .map(|model| model.name.clone()) + .collect() + }) + .unwrap_or_default(); + println!( + "{:<12} {:>7.2} {:>5} {:>5}", + source.provider, + name_overlap(&old, &new), + old.len(), + new.len() + ); + } + } } diff --git a/src/client/testdata/modelsdev_fixture.json b/src/client/testdata/modelsdev_fixture.json new file mode 100644 index 0000000..d91242a --- /dev/null +++ b/src/client/testdata/modelsdev_fixture.json @@ -0,0 +1,457 @@ +{ + "openai": { + "id": "openai", + "env": [ + "OPENAI_API_KEY" + ], + "npm": "@ai-sdk/openai", + "name": "OpenAI", + "doc": "https://platform.openai.com/docs/models", + "models": { + "gpt-5.6": { + "id": "gpt-5.6", + "name": "GPT-5.6", + "description": "Frontier GPT-5.6 model for complex professional work, coding, and agentic workflows", + "family": "gpt-sol", + "attachment": true, + "reasoning": true, + "reasoning_options": [ + { + "type": "effort", + "values": [ + "none", + "low", + "medium", + "high", + "xhigh", + "max" + ] + } + ], + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2026-02-16", + "release_date": "2026-07-09", + "last_updated": "2026-07-09", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 8, + "output": 40, + "cache_read": 0.8, + "cache_write": 10 + }, + "provider": { + "body": { + "service_tier": "priority" + } + } + }, + "pro": { + "provider": { + "body": { + "reasoning": { + "mode": "pro" + } + } + } + } + } + }, + "cost": { + "input": 4, + "output": 20, + "cache_read": 0.4, + "cache_write": 5, + "tiers": [ + { + "input": 8, + "output": 30, + "cache_read": 0.8, + "cache_write": 10, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 8, + "output": 30, + "cache_read": 0.8, + "cache_write": 10 + } + } + }, + "gpt-4.1": { + "id": "gpt-4.1", + "name": "GPT-4.1", + "description": "Long-lived GPT workhorse for coding, instruction following, and production apps", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } + }, + "gpt-4o": { + "id": "gpt-4o", + "name": "GPT-4o", + "description": "Omni-era GPT for multimodal chat, practical coding, and general assistants", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-08-06", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } + } + } + }, + "anthropic": { + "id": "anthropic", + "env": [ + "ANTHROPIC_API_KEY" + ], + "npm": "@ai-sdk/anthropic", + "name": "Anthropic", + "doc": "https://docs.anthropic.com/en/docs/about-claude/models", + "models": { + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "description": "Claude workhorse for coding agents, careful analysis, and production cost control", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "reasoning_options": [ + { + "type": "effort", + "values": [ + "low", + "medium", + "high", + "max" + ] + }, + { + "type": "budget_tokens", + "min": 1024 + } + ], + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "description": "High-end Claude for difficult coding, planning, and slower expert reasoning", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "reasoning_options": [ + { + "type": "effort", + "values": [ + "low", + "medium", + "high", + "max" + ] + }, + { + "type": "budget_tokens", + "min": 1024 + } + ], + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-04", + "last_updated": "2026-03-13", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + } + } + }, + "xai": { + "id": "xai", + "env": [ + "XAI_API_KEY" + ], + "npm": "@ai-sdk/xai", + "name": "xAI", + "doc": "https://docs.x.ai/docs/models", + "models": { + "grok-4.6": { + "id": "grok-4.6", + "name": "Grok 4.6", + "description": "xAI's frontier model for long-running agents, coding, knowledge work, and visual projects", + "family": "grok", + "attachment": true, + "reasoning": true, + "reasoning_options": [ + { + "type": "effort", + "values": [ + "low", + "medium", + "high", + "xhigh" + ] + } + ], + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2026-02-01", + "release_date": "2026-08-12", + "last_updated": "2026-08-12", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 500000, + "output": 500000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.5, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 1, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 1 + } + } + }, + "grok-4.5": { + "id": "grok-4.5", + "name": "Grok 4.5", + "description": "xAI's Grok model for chat, coding, agentic tools, and lower hallucination risk", + "family": "grok", + "attachment": true, + "reasoning": true, + "reasoning_options": [ + { + "type": "effort", + "values": [ + "low", + "medium", + "high" + ] + } + ], + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2026-07-08", + "last_updated": "2026-07-08", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 500000, + "output": 500000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.3, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 0.6, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 0.6 + } + } + } + } + }, + "alibaba": { + "id": "alibaba", + "env": [ + "DASHSCOPE_API_KEY" + ], + "npm": "@ai-sdk/openai-compatible", + "name": "Alibaba", + "doc": "https://www.alibabacloud.com/help/en/model-studio/models", + "models": { + "qwen-max": { + "id": "qwen-max", + "name": "Qwen Max", + "description": "Flagship Qwen model for complex reasoning, coding, and agentic workflows", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-04-03", + "last_updated": "2025-01-25", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 1.6, + "output": 6.4 + } + } + } + } +} diff --git a/src/client/testdata/openrouter_fixture.json b/src/client/testdata/openrouter_fixture.json new file mode 100644 index 0000000..956cbdd --- /dev/null +++ b/src/client/testdata/openrouter_fixture.json @@ -0,0 +1,538 @@ +{ + "data": [ + { + "id": "x-ai/grok-4.6", + "canonical_slug": "x-ai/grok-4.6-20260810", + "hugging_face_id": null, + "name": "SpaceXAI: Grok 4.6", + "created": 1786548957, + "description": "Grok 4.6 is SpaceXAI's smartest model with frontier performance on coding, knowledge work, and STEM.", + "context_length": 500000, + "architecture": { + "modality": "text+image+file->text", + "input_modalities": [ + "text", + "image", + "file" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "Grok", + "instruct_type": null + }, + "pricing": { + "prompt": "0.000002", + "completion": "0.000006", + "web_search": "0.005", + "input_cache_read": "0.0000005", + "overrides": [ + { + "min_prompt_tokens": 200000, + "prompt": "0.000004", + "completion": "0.000012", + "input_cache_read": "0.000001" + } + ] + }, + "top_provider": { + "context_length": 500000, + "max_completion_tokens": 450000, + "is_moderated": false + }, + "per_request_limits": null, + "supported_parameters": [ + "include_reasoning", + "logprobs", + "max_tokens", + "reasoning", + "reasoning_effort", + "response_format", + "seed", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/x-ai/grok-4.6-20260810/endpoints" + }, + "reasoning": { + "mandatory": true, + "default_enabled": true, + "supported_efforts": [ + "xhigh", + "high", + "medium", + "low" + ], + "default_effort": "high" + } + }, + { + "id": "qwen/qwen3.8-max", + "canonical_slug": "qwen/qwen3.8-max-20260803", + "hugging_face_id": null, + "name": "Qwen: Qwen3.8 Max", + "created": 1785731612, + "description": "Qwen3.8 Max is the flagship model in Alibaba's Qwen3.8 series, the general-availability successor to the Qwen3.8 Max Preview. It is a multimodal reasoning model intended for complex reasoning, visual understanding,...", + "context_length": 1000000, + "architecture": { + "modality": "text+image+video->text", + "input_modalities": [ + "text", + "image", + "video" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "Qwen", + "instruct_type": null + }, + "pricing": { + "prompt": "0.000002", + "completion": "0.000006", + "input_cache_read": "0.00000025", + "input_cache_write": "0.0000025" + }, + "top_provider": { + "context_length": 1000000, + "max_completion_tokens": 131072, + "is_moderated": false + }, + "per_request_limits": null, + "supported_parameters": [ + "frequency_penalty", + "include_reasoning", + "logprobs", + "max_tokens", + "presence_penalty", + "reasoning", + "reasoning_effort", + "response_format", + "seed", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/qwen/qwen3.8-max-20260803/endpoints" + }, + "reasoning": { + "mandatory": true, + "default_enabled": true, + "supported_efforts": [ + "xhigh", + "high", + "medium", + "low", + "minimal" + ], + "default_effort": "xhigh" + } + }, + { + "id": "x-ai/grok-4.5", + "canonical_slug": "x-ai/grok-4.5-20260708", + "hugging_face_id": null, + "name": "SpaceXAI: Grok 4.5", + "created": 1783523154, + "description": "Grok 4.5 is a model from SpaceXAI with frontier performance on coding, knowledge work, and STEM.", + "context_length": 500000, + "architecture": { + "modality": "text+image+file->text", + "input_modalities": [ + "text", + "image", + "file" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "Grok", + "instruct_type": null + }, + "pricing": { + "prompt": "0.000002", + "completion": "0.000006", + "web_search": "0.005", + "input_cache_read": "0.0000003", + "overrides": [ + { + "min_prompt_tokens": 200000, + "prompt": "0.000004", + "completion": "0.000012", + "input_cache_read": "0.0000006" + } + ] + }, + "top_provider": { + "context_length": 500000, + "max_completion_tokens": 450000, + "is_moderated": false + }, + "per_request_limits": null, + "supported_parameters": [ + "include_reasoning", + "logprobs", + "max_tokens", + "reasoning", + "reasoning_effort", + "response_format", + "seed", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_logprobs", + "top_p" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/x-ai/grok-4.5-20260708/endpoints" + }, + "reasoning": { + "mandatory": true, + "default_enabled": true, + "supported_efforts": [ + "high", + "medium", + "low" + ], + "default_effort": "high" + } + }, + { + "id": "anthropic/claude-sonnet-4.6", + "canonical_slug": "anthropic/claude-4.6-sonnet-20260217", + "hugging_face_id": "", + "name": "Anthropic: Claude Sonnet 4.6", + "created": 1771342990, + "description": "Sonnet 4.6 is Anthropic's most capable Sonnet-class model yet, with frontier performance across coding, agents, and professional work. It excels at iterative development, complex codebase navigation, end-to-end project management with...", + "context_length": 1000000, + "architecture": { + "modality": "text+image+file->text", + "input_modalities": [ + "text", + "image", + "file" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "Claude", + "instruct_type": null + }, + "pricing": { + "prompt": "0.000003", + "completion": "0.000015", + "web_search": "0.01", + "input_cache_read": "0.0000003", + "input_cache_write": "0.00000375", + "input_cache_write_1h": "0.000006" + }, + "top_provider": { + "context_length": 1000000, + "max_completion_tokens": 128000, + "is_moderated": true + }, + "per_request_limits": null, + "supported_parameters": [ + "include_reasoning", + "max_completion_tokens", + "max_tokens", + "reasoning", + "reasoning_effort", + "response_format", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_k", + "top_p", + "verbosity" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/anthropic/claude-4.6-sonnet-20260217/endpoints" + }, + "reasoning": { + "mandatory": false, + "supported_efforts": [ + "max", + "high", + "medium", + "low" + ], + "default_effort": "medium" + } + }, + { + "id": "openrouter/free", + "canonical_slug": "openrouter/free", + "hugging_face_id": "", + "name": "Free Models Router", + "created": 1769917427, + "description": "The simplest way to get free inference. openrouter/free is a router that selects free models at random from the models available on OpenRouter. The router smartly filters for models that...", + "context_length": 200000, + "architecture": { + "modality": "text+image->text", + "input_modalities": [ + "text", + "image" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "Router", + "instruct_type": null + }, + "pricing": { + "prompt": "0", + "completion": "0" + }, + "top_provider": { + "context_length": null, + "max_completion_tokens": null, + "is_moderated": false + }, + "per_request_limits": null, + "supported_parameters": [ + "frequency_penalty", + "include_reasoning", + "logprobs", + "max_completion_tokens", + "max_tokens", + "min_p", + "presence_penalty", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "seed", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p" + ], + "default_parameters": { + "temperature": null, + "top_p": null, + "frequency_penalty": null + }, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/openrouter/free/endpoints" + } + }, + { + "id": "openai/gpt-4.1", + "canonical_slug": "openai/gpt-4.1-2025-04-14", + "hugging_face_id": "", + "name": "OpenAI: GPT-4.1", + "created": 1744651385, + "description": "GPT-4.1 is a flagship large language model optimized for advanced instruction following, real-world software engineering, and long-context reasoning. It supports a 1 million token context window and outperforms GPT-4o and...", + "context_length": 1047576, + "architecture": { + "modality": "text+image+file->text", + "input_modalities": [ + "image", + "text", + "file" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "GPT", + "instruct_type": null + }, + "pricing": { + "prompt": "0.000002", + "completion": "0.000008", + "web_search": "0.01", + "input_cache_read": "0.0000005" + }, + "top_provider": { + "context_length": 1047576, + "max_completion_tokens": 32768, + "is_moderated": true + }, + "per_request_limits": null, + "supported_parameters": [ + "max_completion_tokens", + "max_tokens", + "response_format", + "seed", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_p" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": "2024-06-30", + "expiration_date": null, + "links": { + "details": "/api/v1/models/openai/gpt-4.1-2025-04-14/endpoints" + } + }, + { + "id": "openai/gpt-4o", + "canonical_slug": "openai/gpt-4o", + "hugging_face_id": null, + "name": "OpenAI: GPT-4o", + "created": 1715558400, + "description": "GPT-4o (\"o\" for \"omni\") is OpenAI's latest AI model, supporting both text and image inputs with text outputs. It maintains the intelligence level of [GPT-4 Turbo](/models/openai/gpt-4-turbo) while being twice as...", + "context_length": 128000, + "architecture": { + "modality": "text+image+file->text", + "input_modalities": [ + "text", + "image", + "file" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "GPT", + "instruct_type": null + }, + "pricing": { + "prompt": "0.0000025", + "completion": "0.00001", + "input_cache_read": "0.00000125" + }, + "top_provider": { + "context_length": 128000, + "max_completion_tokens": 16384, + "is_moderated": true + }, + "per_request_limits": null, + "supported_parameters": [ + "frequency_penalty", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_tokens", + "prediction", + "presence_penalty", + "response_format", + "seed", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_logprobs", + "top_p", + "web_search_options" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": "2023-10-31", + "expiration_date": null, + "links": { + "details": "/api/v1/models/openai/gpt-4o/endpoints" + } + }, + { + "id": "openrouter/auto", + "canonical_slug": "openrouter/auto", + "hugging_face_id": null, + "name": "Auto Router", + "created": 1699401600, + "description": "The Auto Router automatically selects the best model for your prompt, powered by the wisdom of the market. It routes you based on what the OpenRouter community collectively spends on...", + "context_length": 2000000, + "architecture": { + "modality": "text+image+file+audio+video->text+image", + "input_modalities": [ + "text", + "image", + "audio", + "file", + "video" + ], + "output_modalities": [ + "text", + "image" + ], + "tokenizer": "Router", + "instruct_type": null + }, + "pricing": { + "prompt": "-1", + "completion": "-1" + }, + "top_provider": { + "context_length": null, + "max_completion_tokens": null, + "is_moderated": false + }, + "per_request_limits": null, + "supported_parameters": [ + "frequency_penalty", + "include_reasoning", + "logit_bias", + "logprobs", + "max_tokens", + "min_p", + "prediction", + "presence_penalty", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "seed", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_a", + "top_k", + "top_logprobs", + "top_p", + "web_search_options" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/openrouter/auto/endpoints" + } + } + ] +} From 46c8561173fc6296993cc8d9ca1661742b4572f2 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 13:38:56 -0600 Subject: [PATCH 03/30] feat: generate the model catalog from live API data Add a hidden --generate-models flag that rebuilds models.yaml from models.dev and OpenRouter merged with the hand-owned quirks overlay, regenerate the committed catalog with it (393 -> 1090 models), and retire the Python updater. GitHub becomes fully quirks-owned since upstream only tracks Copilot ids, legacy ids upstream dropped are preserved as hand-owned quirks entries, and non-chat model families (image/tts/audio/realtime) are excluded from generation. --- models.yaml | 12326 ++++++++++++++++++++++++++++--------- quirks.yaml | 592 ++ scripts/requirements.txt | 2 - scripts/update_models.py | 255 - src/cli/mod.rs | 25 +- src/client/catalog.rs | 125 +- src/client/model.rs | 2 +- src/main.rs | 7 +- 8 files changed, 10276 insertions(+), 3058 deletions(-) delete mode 100644 scripts/requirements.txt delete mode 100644 scripts/update_models.py diff --git a/models.yaml b/models.yaml index 2502e8c..a368855 100644 --- a/models.yaml +++ b/models.yaml @@ -1,2850 +1,9590 @@ -# Links: -# - https://platform.openai.com/docs/models -# - https://platform.openai.com/docs/api-reference/chat +# GENERATED by 'coyote --generate-models' — DO NOT hand-edit model data. +# Hand-owned behavior lives in quirks.yaml; this file is the embedded offline fallback. - provider: openai models: - - name: gpt-5.6-sol - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: gpt-5.6-terra - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: gpt-5.6-luna - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: gpt-5.5 - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: medium - - name: gpt-5.5-pro - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 30 - output_price: 180 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: high - - name: gpt-5.4 - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 2.5 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: gpt-5.4-pro - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 30 - output_price: 180 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: medium - - name: gpt-5.4-mini - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.75 - output_price: 4.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: gpt-5.4-nano - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.2 - output_price: 1.25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: gpt-5.3-codex - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.75 - output_price: 14 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh] - default_reasoning_effort: medium - - name: chat-latest - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - - name: gpt-5.2 - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.75 - output_price: 14 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: gpt-5.2-pro - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 21 - output_price: 168 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: medium - - name: gpt-5.1 - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high] - default_reasoning_effort: none - - name: gpt-5.1-chat-latest - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high] - default_reasoning_effort: none - - name: gpt-5 - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: gpt-5-chat-latest - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: gpt-5-mini - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.25 - output_price: 2 - supports_vision: true - supports_function_calling: true - - name: gpt-5-nano - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.05 - output_price: 0.4 - supports_vision: true - supports_function_calling: true - - name: gpt-4.1 - max_input_tokens: 1047576 - max_output_tokens: 32768 - input_price: 2 - output_price: 8 - supports_vision: true - supports_function_calling: true - - name: gpt-4.1-mini - max_input_tokens: 1047576 - max_output_tokens: 32768 - input_price: 0.4 - output_price: 1.6 - supports_vision: true - supports_function_calling: true - - name: gpt-4.1-nano - max_input_tokens: 1047576 - max_output_tokens: 32768 - input_price: 0.1 - output_price: 0.4 - supports_vision: true - supports_function_calling: true - - name: gpt-4o - max_input_tokens: 128000 - max_output_tokens: 16384 - input_price: 2.5 - output_price: 10 - supports_vision: true - supports_function_calling: true - - name: gpt-4o-mini - max_input_tokens: 128000 - max_output_tokens: 16384 - input_price: 0.15 - output_price: 0.6 - supports_vision: true - supports_function_calling: true - - name: o4-mini - max_output_tokens: 100000 - max_input_tokens: 200000 - input_price: 1.1 - output_price: 4.4 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - reasoning_levels: [low, medium, high] - default_reasoning_effort: medium - patch: - body: - max_tokens: null - temperature: null - top_p: null - - name: o4-mini-high - max_output_tokens: 100000 - real_name: o4-mini - max_input_tokens: 200000 - input_price: 1.1 - output_price: 4.4 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - patch: - body: - reasoning_effort: high - max_tokens: null - temperature: null - top_p: null - - name: o3 - max_output_tokens: 100000 - max_input_tokens: 200000 - input_price: 2 - output_price: 8 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - patch: - body: - max_tokens: null - temperature: null - top_p: null - - name: o3-high - real_name: o3 - max_input_tokens: 200000 - input_price: 2 - output_price: 8 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - patch: - body: - reasoning_effort: high - max_tokens: null - temperature: null - top_p: null - - name: o3-mini - max_output_tokens: 100000 - max_input_tokens: 200000 - input_price: 1.1 - output_price: 4.4 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - patch: - body: - max_tokens: null - temperature: null - top_p: null - - name: o3-mini-high - max_output_tokens: 100000 - real_name: o3-mini - max_input_tokens: 200000 - input_price: 1.1 - output_price: 4.4 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - patch: - body: - reasoning_effort: high - max_tokens: null - temperature: null - top_p: null - - name: gpt-4-turbo - max_input_tokens: 128000 - max_output_tokens: 4096 - input_price: 10 - output_price: 30 - supports_vision: true - supports_function_calling: true - - name: gpt-3.5-turbo - max_input_tokens: 16385 - max_output_tokens: 4096 - input_price: 0.5 - output_price: 1.5 - supports_function_calling: true - - name: text-embedding-3-large - type: embedding - input_price: 0.13 - max_tokens_per_chunk: 8191 - default_chunk_size: 2000 - max_batch_size: 100 - - name: text-embedding-3-small - type: embedding - input_price: 0.02 - max_tokens_per_chunk: 8191 - default_chunk_size: 2000 - max_batch_size: 100 - -# Links: -# - https://ai.google.dev/models/gemini -# - https://ai.google.dev/pricing -# - https://ai.google.dev/api/rest/v1beta/models/streamGenerateContent + - name: text-embedding-3-large + type: embedding + input_price: 0.13 + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: text-embedding-3-small + type: embedding + input_price: 0.02 + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: o4-mini-high + type: chat + real_name: o4-mini + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + - name: o3-high + type: chat + real_name: o3 + max_input_tokens: 200000 + input_price: 2.0 + output_price: 8.0 + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + - name: o3-mini-high + type: chat + real_name: o3-mini + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + - name: chat-latest + type: chat + max_input_tokens: 400000 + input_price: 5.0 + output_price: 30.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-5.1-chat-latest + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + default_reasoning_effort: none + - name: gpt-5-chat-latest + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: gpt-5.6 + type: chat + max_input_tokens: 1050000 + input_price: 4.0 + output_price: 20.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: gpt-5.6-luna + type: chat + max_input_tokens: 1050000 + input_price: 0.2 + output_price: 1.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: gpt-5.6-sol + type: chat + max_input_tokens: 1050000 + input_price: 4.0 + output_price: 20.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: gpt-5.6-terra + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: gpt-5.5 + type: chat + max_input_tokens: 1050000 + input_price: 5.0 + output_price: 30.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: gpt-5.5-pro + type: chat + max_input_tokens: 1050000 + input_price: 30.0 + output_price: 180.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: high + - name: gpt-5.4-mini + type: chat + max_input_tokens: 400000 + input_price: 0.75 + output_price: 4.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: gpt-5.4-nano + type: chat + max_input_tokens: 400000 + input_price: 0.2 + output_price: 1.25 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: gpt-5.4 + type: chat + max_input_tokens: 1050000 + input_price: 2.5 + output_price: 15.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: gpt-5.4-pro + type: chat + max_input_tokens: 1050000 + input_price: 30.0 + output_price: 180.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: gpt-5.3-chat-latest + type: chat + max_input_tokens: 128000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: gpt-5.3-codex + type: chat + max_input_tokens: 400000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: gpt-5.3-codex-spark + type: chat + max_input_tokens: 128000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 32000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - name: gpt-5.2 + type: chat + max_input_tokens: 400000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: gpt-5.2-chat-latest + type: chat + max_input_tokens: 128000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - name: gpt-5.2-pro + type: chat + max_input_tokens: 400000 + input_price: 21.0 + output_price: 168.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: gpt-5.1 + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + default_reasoning_effort: none + - name: gpt-5-pro + type: chat + max_input_tokens: 400000 + input_price: 15.0 + output_price: 120.0 + max_output_tokens: 272000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - high + - name: gpt-5 + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: gpt-5-mini + type: chat + max_input_tokens: 400000 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gpt-5-nano + type: chat + max_input_tokens: 400000 + input_price: 0.05 + output_price: 0.4 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: o3-pro + type: chat + max_input_tokens: 200000 + input_price: 20.0 + output_price: 80.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: o3 + type: chat + max_input_tokens: 200000 + input_price: 2.0 + output_price: 8.0 + patch: + body: + max_tokens: null + temperature: null + top_p: null + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + system_prompt_prefix: Formatting re-enabled + - name: o4-mini + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + patch: + body: + max_tokens: null + temperature: null + top_p: null + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: medium + system_prompt_prefix: Formatting re-enabled + - name: gpt-4.1 + type: chat + max_input_tokens: 1047576 + input_price: 2.0 + output_price: 8.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gpt-4.1-mini + type: chat + max_input_tokens: 1047576 + input_price: 0.4 + output_price: 1.6 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gpt-4.1-nano + type: chat + max_input_tokens: 1047576 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: o1-pro + type: chat + max_input_tokens: 200000 + input_price: 150.0 + output_price: 600.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: o3-mini + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + patch: + body: + max_tokens: null + temperature: null + top_p: null + max_output_tokens: 100000 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + system_prompt_prefix: Formatting re-enabled + - name: o1 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 60.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gpt-4o-2024-11-20 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: gpt-4o-2024-08-06 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: gpt-4o-mini + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: gpt-4o + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: gpt-4o-2024-05-13 + type: chat + max_input_tokens: 128000 + input_price: 5.0 + output_price: 15.0 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: gpt-4 + type: chat + max_input_tokens: 8192 + input_price: 30.0 + output_price: 60.0 + max_output_tokens: 8192 + supports_function_calling: true + - name: gpt-4-turbo + type: chat + max_input_tokens: 128000 + input_price: 10.0 + output_price: 30.0 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: gpt-3.5-turbo + type: chat + max_input_tokens: 16385 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 4096 + supports_function_calling: true - provider: gemini models: - - name: gemini-3.6-flash - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 1.5 - output_price: 7.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_level: medium - - name: gemini-3.5-flash - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: gemini-3-flash-preview - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: high - - name: gemini-3.1-flash-lite - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: minimal - - name: gemini-3.1-pro-preview - max_input_tokens: 1048576 - max_output_tokens: 65535 - input_price: 0.3 - output_price: 2.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: gemini-2.5-flash - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0 - output_price: 0 - supports_vision: true - supports_function_calling: true - - name: gemini-2.5-pro - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0 - output_price: 0 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: gemini-2.5-flash-lite - max_input_tokens: 1000000 - max_output_tokens: 64000 - input_price: 0 - output_price: 0 - supports_vision: true - supports_function_calling: true - - name: gemini-3-pro-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, high] - default_reasoning_level: high - - name: gemini-3-flash-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_level: high - - name: gemma-3-27b-it - max_input_tokens: 131072 - max_output_tokens: 8192 - input_price: 0 - output_price: 0 - - name: text-embedding-004 - type: embedding - input_price: 0 - max_tokens_per_chunk: 2048 - default_chunk_size: 1500 - max_batch_size: 100 - -# Links: -# - https://docs.anthropic.com/en/docs/about-claude/models/all-models -# - https://docs.anthropic.com/en/api/messages + - name: text-embedding-004 + type: embedding + input_price: 0.0 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 100 + - name: gemini-3-pro-preview + type: chat + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + default_reasoning_effort: high + - name: gemma-3-27b-it + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 8192 + - name: gemini-3.8-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-3.7-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-flash-latest + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-3.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-3.6-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: gemini-flash-lite-latest + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-3.1-flash-lite-image + type: chat + max_input_tokens: 65536 + input_price: 0.25 + output_price: 30.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - high + - name: gemini-omni-flash-preview + type: chat + max_input_tokens: 131072 + input_price: 1.5 + output_price: 17.5 + max_output_tokens: 65536 + supports_vision: true + - name: gemini-3.5-live-translate-preview + type: chat + max_input_tokens: 16384 + input_price: 3.5 + output_price: 21.0 + max_output_tokens: 32768 + - name: gemini-3-pro-image + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 120.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - name: gemini-3.1-flash-image + type: chat + max_input_tokens: 65536 + input_price: 0.5 + output_price: 60.0 + max_output_tokens: 65536 + supports_vision: true + reasoning_levels: + - minimal + - high + - name: gemini-3.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 1.5 + output_price: 9.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: gemini-3.1-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: minimal + - name: deep-research-max-preview-04-2026 + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: deep-research-preview-04-2026 + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: gemma-4-26b-a4b-it + type: chat + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.34 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gemma-4-31b-it + type: chat + max_input_tokens: 262144 + input_price: 0.09 + output_price: 0.34 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gemini-3.1-flash-live-preview + type: chat + max_input_tokens: 131072 + input_price: 0.75 + output_price: 4.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: lyria-3-clip-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_vision: true + - name: lyria-3-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_vision: true + - name: gemini-3.1-flash-lite-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-3.1-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high + - name: gemini-3.1-pro-preview-customtools + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-3-flash-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: high + - name: gemini-2.5-computer-use-preview-10-2025 + type: chat + max_input_tokens: 131072 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: gemini-2.5-flash-image + type: chat + max_input_tokens: 32768 + input_price: 0.3 + output_price: 30.0 + max_output_tokens: 32768 + supports_vision: true + - name: gemini-2.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: gemini-2.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: gemini-2.5-pro + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high - provider: claude models: - - name: claude-opus-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-fable-5-1 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-fable-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-8 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-7 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: claude-opus-4-6:thinking - real_name: claude-opus-4-6 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-sonnet-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: claude-sonnet-4-6:thinking - real_name: claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-sonnet-4-5-20250929 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: claude-sonnet-4-5-20250929:thinking - real_name: claude-sonnet-4-5-20250929 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-haiku-4-5-20251001 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - - name: claude-haiku-4-5-20251001:thinking - real_name: claude-haiku-4-5-20251001 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-opus-4-5-20251101 - type: chat - max_input_tokens: 200000 - input_price: 15.0 - output_price: 75.0 - max_output_tokens: 8192 - require_max_tokens: true - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-5-20251101:thinking - type: chat - real_name: claude-opus-4-5-20251101 - max_input_tokens: 200000 - input_price: 15.0 - output_price: 75.0 - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - max_output_tokens: 24000 - require_max_tokens: true - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-1-20250805 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 15 - output_price: 75 - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-1-20250805:thinking - real_name: claude-opus-4-1-20250805 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 15 - output_price: 75 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-opus-4-20250514 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 15 - output_price: 75 - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-20250514:thinking - real_name: claude-opus-4-20250514 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 15 - output_price: 75 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-sonnet-4-5-20250929 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: claude-sonnet-4-5-20250929:thinking - real_name: claude-sonnet-4-5-20250929 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-haiku-4-5-20251001 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - - name: claude-haiku-4-5-20251001:thinking - real_name: claude-haiku-4-5-20251001 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - -# Links: -# - https://docs.mistral.ai/getting-started/models/models_overview/ -# - https://mistral.ai/pricing#api-pricing -# - https://docs.mistral.ai/api/ + - name: claude-opus-4-1-20250805 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 8192 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-opus-4-1-20250805:thinking + type: chat + real_name: claude-opus-4-1-20250805 + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-opus-4-20250514 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 8192 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-opus-4-20250514:thinking + type: chat + real_name: claude-opus-4-20250514 + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: claude-sonnet-4-6:thinking + type: chat + real_name: claude-sonnet-4-6 + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + default_reasoning_effort: high + - name: claude-opus-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: claude-opus-4-6:thinking + type: chat + real_name: claude-opus-4-6 + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + default_reasoning_effort: high + - name: claude-opus-4-5 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: claude-opus-4-5-20251101 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: claude-opus-4-5-20251101:thinking + type: chat + real_name: claude-opus-4-5-20251101 + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-haiku-4-5 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-haiku-4-5-20251001 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-haiku-4-5-20251001:thinking + type: chat + real_name: claude-haiku-4-5-20251001 + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-4-5 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-4-5-20250929 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-4-5-20250929:thinking + type: chat + real_name: claude-sonnet-4-5-20250929 + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true - provider: mistral models: - - name: mistral-large-latest - max_output_tokens: 262144 - input_price: 0.5 - output_price: 1.5 - supports_function_calling: true - supports_vision: true - - name: mistral-medium-latest - max_input_tokens: 131072 - input_price: 0.4 - output_price: 2 - supports_function_calling: true - supports_vision: true - - name: mistral-small-latest - max_input_tokens: 32768 - input_price: 0.1 - output_price: 0.3 - supports_function_calling: true - supports_vision: true - - name: magistral-medium-latest - max_input_tokens: 131072 - input_price: 2 - output_price: 5 - - name: magistral-small-latest - max_input_tokens: 131072 - input_price: 0.5 - output_price: 1.5 - - name: devstral-medium-latest - max_input_tokens: 262144 - input_price: 0.4 - output_price: 2 - supports_function_calling: true - - name: devstral-small-latest - max_input_tokens: 262144 - input_price: 0.1 - output_price: 0.3 - supports_function_calling: true - - name: codestral-latest - max_input_tokens: 262144 - input_price: 0.3 - output_price: 0.9 - supports_function_calling: true - - name: ministral-14b-latest - max_input_tokens: 262144 - input_price: 0.2 - output_price: 0.2 - supports_function_calling: true - - name: mistral-embed - type: embedding - max_input_tokens: 8092 - input_price: 0.1 - max_tokens_per_chunk: 8092 - default_chunk_size: 2000 - -# Links: -# - https://docs.ai21.com/docs/jamba-foundation-models -# - https://www.ai21.com/pricing -# - https://docs.ai21.com/reference/jamba-1-6-api-ref + - name: mistral-embed + type: embedding + max_input_tokens: 8092 + input_price: 0.1 + output_price: 0.0 + max_output_tokens: 3072 + max_tokens_per_chunk: 8092 + default_chunk_size: 2000 + - name: magistral-small-latest + type: chat + max_input_tokens: 131072 + input_price: 0.5 + output_price: 1.5 + - name: devstral-small-latest + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.3 + supports_function_calling: true + - name: ministral-14b-latest + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 0.2 + supports_function_calling: true + - name: zai-glm-5-2 + type: chat + max_input_tokens: 1000000 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - high + - max + - name: mistral-medium-2604 + type: chat + max_input_tokens: 262144 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - high + - name: mistral-medium-latest + type: chat + max_input_tokens: 262144 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - high + - name: mistral-small-2603 + type: chat + max_input_tokens: 256000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 256000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - high + - name: mistral-small-latest + type: chat + max_input_tokens: 256000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 256000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - high + - name: devstral-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 262144 + supports_function_calling: true + - name: devstral-latest + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 262144 + supports_function_calling: true + - name: labs-devstral-small-2512 + type: chat + max_input_tokens: 256000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 256000 + supports_vision: true + supports_function_calling: true + - name: devstral-medium-latest + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 262144 + supports_function_calling: true + - name: mistral-medium-2508 + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: devstral-medium-2507 + type: chat + max_input_tokens: 128000 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 128000 + supports_function_calling: true + - name: devstral-small-2507 + type: chat + max_input_tokens: 128000 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 128000 + supports_function_calling: true + - name: mistral-small-2506 + type: chat + max_input_tokens: 128000 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: devstral-small-2505 + type: chat + max_input_tokens: 128000 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 128000 + supports_function_calling: true + - name: mistral-medium-2505 + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: magistral-medium-latest + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 5.0 + max_output_tokens: 16384 + supports_function_calling: true + - name: magistral-small + type: chat + max_input_tokens: 128000 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 128000 + supports_function_calling: true + - name: mistral-large-2411 + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 16384 + supports_function_calling: true + - name: mistral-large-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: mistral-large-latest + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: pixtral-large-latest + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ministral-3b-latest + type: chat + max_input_tokens: 128000 + input_price: 0.04 + output_price: 0.04 + max_output_tokens: 128000 + supports_function_calling: true + - name: ministral-8b-latest + type: chat + max_input_tokens: 128000 + input_price: 0.1 + output_price: 0.1 + max_output_tokens: 128000 + supports_function_calling: true + - name: pixtral-12b + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.15 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: mistral-nemo + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.15 + max_output_tokens: 128000 + supports_function_calling: true + - name: open-mistral-nemo + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.15 + max_output_tokens: 128000 + supports_function_calling: true + - name: codestral-latest + type: chat + max_input_tokens: 256000 + input_price: 0.3 + output_price: 0.9 + max_output_tokens: 4096 + supports_function_calling: true + - name: open-mixtral-8x22b + type: chat + max_input_tokens: 64000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 64000 + supports_function_calling: true + - name: open-mixtral-8x7b + type: chat + max_input_tokens: 32000 + input_price: 0.7 + output_price: 0.7 + max_output_tokens: 32000 + supports_function_calling: true + - name: open-mistral-7b + type: chat + max_input_tokens: 8000 + input_price: 0.25 + output_price: 0.25 + max_output_tokens: 8000 + supports_function_calling: true - provider: ai21 models: - - name: jamba-large - max_input_tokens: 256000 - input_price: 2 - output_price: 8 - supports_function_calling: true - - name: jamba-mini - max_input_tokens: 256000 - input_price: 0.2 - output_price: 0.4 - supports_function_calling: true - -# Links: -# - https://docs.cohere.com/docs/models -# - https://cohere.com/pricing -# - https://docs.cohere.com/reference/chat + - name: jamba-large + type: chat + max_input_tokens: 256000 + input_price: 2.0 + output_price: 8.0 + supports_function_calling: true + - name: jamba-mini + type: chat + max_input_tokens: 256000 + input_price: 0.2 + output_price: 0.4 + supports_function_calling: true - provider: cohere models: - - name: command-a-03-2025 - max_input_tokens: 262144 - max_output_tokens: 8192 - input_price: 2.5 - output_price: 10 - supports_function_calling: true - - name: command-a-reasoning-08-2025 - max_input_tokens: 262144 - max_output_tokens: 32768 - input_price: 2.5 - output_price: 10 - - name: command-a-vision-07-2025 - max_input_tokens: 131072 - max_output_tokens: 8192 - input_price: 2.5 - output_price: 10 - supports_vision: true - - name: command-r7b-12-2024 - max_input_tokens: 131072 - max_output_tokens: 4096 - input_price: 0.0375 - output_price: 0.15 - - name: embed-v4.0 - type: embedding - input_price: 0.12 - max_tokens_per_chunk: 2048 - default_chunk_size: 2000 - max_batch_size: 96 - - name: embed-english-v3.0 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: embed-multilingual-v3.0 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: rerank-v3.5 - type: reranker - max_input_tokens: 4096 - - name: rerank-english-v3.0 - type: reranker - max_input_tokens: 4096 - - name: rerank-multilingual-v3.0 - type: reranker - max_input_tokens: 4096 - -# Links: -# - https://docs.x.ai/docs/models -# - https://docs.x.ai/docs/api-reference#chat-completions + - name: embed-v4.0 + type: embedding + input_price: 0.12 + max_tokens_per_chunk: 2048 + default_chunk_size: 2000 + max_batch_size: 96 + - name: embed-english-v3.0 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: embed-multilingual-v3.0 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: rerank-v3.5 + type: reranker + max_input_tokens: 4096 + - name: rerank-english-v3.0 + type: reranker + max_input_tokens: 4096 + - name: rerank-multilingual-v3.0 + type: reranker + max_input_tokens: 4096 + - name: north-mini-code-1-0 + type: chat + max_input_tokens: 256000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 64000 + supports_function_calling: true + reasoning_levels: + - none + - high + - name: command-a-plus-05-2026 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: command-a-translate-08-2025 + type: chat + max_input_tokens: 8000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 8000 + supports_function_calling: true + - name: command-a-reasoning-08-2025 + type: chat + max_input_tokens: 256000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 32000 + supports_function_calling: true + - name: command-a-vision-07-2025 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 8000 + supports_vision: true + - name: command-a-03-2025 + type: chat + max_input_tokens: 256000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 8000 + supports_function_calling: true + - name: c4ai-aya-vision-32b + type: chat + max_input_tokens: 16000 + max_output_tokens: 4000 + supports_vision: true + - name: c4ai-aya-vision-8b + type: chat + max_input_tokens: 16000 + max_output_tokens: 4000 + supports_vision: true + - name: command-r7b-arabic-02-2025 + type: chat + max_input_tokens: 128000 + input_price: 0.0375 + output_price: 0.15 + max_output_tokens: 4000 + supports_function_calling: true + - name: command-r7b-12-2024 + type: chat + max_input_tokens: 128000 + input_price: 0.0375 + output_price: 0.15 + max_output_tokens: 4000 + supports_function_calling: true + - name: c4ai-aya-expanse-32b + type: chat + max_input_tokens: 128000 + max_output_tokens: 4000 + - name: c4ai-aya-expanse-8b + type: chat + max_input_tokens: 8000 + max_output_tokens: 4000 + - name: command-r-08-2024 + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 4000 + supports_function_calling: true + - name: command-r-plus-08-2024 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 4000 + supports_function_calling: true - provider: xai oauth: client_id: b1a00492-073a-47ea-816f-4c329264a828 - authorize_url: https://auth.x.ai/oauth2/authorize token_url: https://auth.x.ai/oauth2/token - scopes: - - openid - - profile - - email - - offline_access - - grok-cli:access - - api:access - redirect_port: 56121 flow: pkce + client_secret: null + authorize_url: https://auth.x.ai/oauth2/authorize + redirect_uri: null + redirect_port: 56121 + device_authorization_url: null + scopes: + - openid + - profile + - email + - offline_access + - grok-cli:access + - api:access token_request_format: form_url_encoded extra_authorize_params: plan: generic referrer: coyote + extra_token_headers: {} + extra_request_headers: {} echo_pkce_in_token_exchange: true + use_pkce_in_device_flow: false + include_state_in_token_exchange: true models: - - name: grok-4.6 - input_price: 2 - output_price: 6 - max_input_tokens: 500000 - supports_function_calling: true - - name: grok-4.5 - input_price: 2 - output_price: 6 - max_input_tokens: 256000 - supports_function_calling: true - - name: grok-build-0.1 - input_price: 1 - output_price: 2 - max_input_tokens: 256000 - supports_function_calling: true - - name: grok-4.3 - input_price: 1.25 - output_price: 2.5 - max_input_tokens: 1000000 - supports_function_calling: true - - name: grok-4.20 - real_name: grok-4.20-multi-agent-0309 - input_price: 1.25 - output_price: 2.5 - max_input_tokens: 1000000 - supports_function_calling: true - - name: grok-4.20-reasoning - real_name: grok-4.20-0309-reasoning - input_price: 1.25 - output_price: 2.5 - max_input_tokens: 1000000 - supports_function_calling: true - - name: grok-4.20-non-reasoning - real_name: grok-4.20-0309-non-reasoning - input_price: 1.25 - output_price: 2.5 - max_input_tokens: 1000000 - supports_function_calling: true - - name: grok-4-1-fast-non-reasoning - max_input_tokens: 1000000 - input_price: 0.2 - output_price: 0.5 - supports_function_calling: true - - name: grok-4-1-fast-reasoning - max_input_tokens: 1000000 - input_price: 0.2 - output_price: 0.5 - supports_function_calling: true - - name: grok-code-fast-1 - max_input_tokens: 256000 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - - name: grok-3 - max_input_tokens: 131072 - input_price: 3 - output_price: 15 - supports_function_calling: true - - name: grok-3-mini - max_input_tokens: 131072 - input_price: 0.3 - output_price: 0.5 - supports_function_calling: true - -# Links: -# - https://docs.perplexity.ai/getting-started/models -# - https://docs.perplexity.ai/api-reference/chat-completions + - name: grok-4.20 + type: chat + real_name: grok-4.20-multi-agent-0309 + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + supports_function_calling: true + - name: grok-4.20-reasoning + type: chat + real_name: grok-4.20-0309-reasoning + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + supports_function_calling: true + - name: grok-4.20-non-reasoning + type: chat + real_name: grok-4.20-0309-non-reasoning + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + supports_function_calling: true + - name: grok-4-1-fast-non-reasoning + type: chat + max_input_tokens: 1000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: grok-4-1-fast-reasoning + type: chat + max_input_tokens: 1000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: grok-code-fast-1 + type: chat + max_input_tokens: 256000 + input_price: 0.2 + output_price: 1.5 + supports_function_calling: true + - name: grok-3 + type: chat + max_input_tokens: 131072 + input_price: 3.0 + output_price: 15.0 + supports_function_calling: true + - name: grok-3-mini + type: chat + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.5 + supports_function_calling: true + - name: grok-4.6 + type: chat + max_input_tokens: 500000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 500000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - name: grok-4.5 + type: chat + max_input_tokens: 500000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 500000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: grok-4.3 + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 30000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - name: grok-build-0.1 + type: chat + max_input_tokens: 256000 + input_price: 1.0 + output_price: 2.0 + max_output_tokens: 256000 + supports_vision: true + supports_function_calling: true + - name: grok-4.20-0309-non-reasoning + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 30000 + supports_vision: true + supports_function_calling: true + - name: grok-4.20-0309-reasoning + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 30000 + supports_vision: true + supports_function_calling: true + - name: grok-4.20-multi-agent-0309 + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 30000 + supports_vision: true + reasoning_levels: + - low + - medium + - high + - xhigh - provider: perplexity models: - - name: sonar-pro - max_output_tokens: 8000 - supports_vision: true - max_input_tokens: 200000 - input_price: 3 - output_price: 15 - - name: sonar - supports_vision: true - max_input_tokens: 127072 - input_price: 1 - output_price: 1 - - name: sonar-reasoning-pro - supports_vision: true - max_input_tokens: 128000 - input_price: 2 - output_price: 8 - - name: sonar-reasoning - max_input_tokens: 128000 - input_price: 1 - output_price: 5 - - name: sonar-deep-research - max_input_tokens: 128000 - input_price: 2 - output_price: 8 - -# Links: -# - https://console.groq.com/docs/models -# - https://console.groq.com/docs/api-reference#chat + - name: sonar-reasoning + type: chat + max_input_tokens: 128000 + input_price: 1.0 + output_price: 5.0 + - name: sonar-deep-research + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 8.0 + max_output_tokens: 32768 + reasoning_levels: + - minimal + - low + - medium + - high + - name: sonar + type: chat + max_input_tokens: 128000 + input_price: 1.0 + output_price: 1.0 + max_output_tokens: 4096 + supports_vision: true + - name: sonar-pro + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 8192 + supports_vision: true + - name: sonar-reasoning-pro + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 8.0 + max_output_tokens: 4096 + supports_vision: true + reasoning_levels: + - minimal + - low + - medium + - high - provider: groq models: - - name: openai/gpt-oss-120b - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - supports_function_calling: true - - name: openai/gpt-oss-20b - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - supports_function_calling: true - - name: meta-llama/llama-4-maverick-17b-128e-instruct - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - supports_vision: true - supports_function_calling: true - - name: meta-llama/llama-4-scout-17b-16e-instruct - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - supports_vision: true - supports_function_calling: true - - name: llama-3.3-70b-versatile - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - supports_function_calling: true - - name: moonshotai/kimi-k2-instruct-0905 - max_input_tokens: 262144 - input_price: 0 - output_price: 0 - supports_function_calling: true - - name: qwen/qwen3-32b - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - - name: groq/compound - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - - name: groq/compound-mini - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - -# Links: -# - https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models -# - https://cloud.google.com/vertex-ai/generative-ai/pricing -# - https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/gemini + - name: meta-llama/llama-4-maverick-17b-128e-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + supports_vision: true + supports_function_calling: true + - name: meta-llama/llama-4-scout-17b-16e-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k2-instruct-0905 + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + supports_function_calling: true + - name: qwen/qwen3-32b + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + - name: qwen/qwen3.8-27b + type: chat + max_input_tokens: 131042 + input_price: 0.8 + output_price: 4.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - default + - low + - medium + - high + - name: qwen/qwen3.6-27b + type: chat + max_input_tokens: 131072 + input_price: 0.6 + output_price: 3.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - default + - name: canopylabs/orpheus-v1-english + type: chat + max_input_tokens: 4000 + max_output_tokens: 50000 + - name: canopylabs/orpheus-arabic-saudi + type: chat + max_input_tokens: 4000 + max_output_tokens: 50000 + - name: openai/gpt-oss-safeguard-20b + type: chat + max_input_tokens: 131072 + input_price: 0.075 + output_price: 0.3 + max_output_tokens: 65536 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: groq/compound + type: chat + max_input_tokens: 131072 + max_output_tokens: 8192 + - name: groq/compound-mini + type: chat + max_input_tokens: 131072 + max_output_tokens: 8192 + - name: openai/gpt-oss-120b + type: chat + max_input_tokens: 131072 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 65536 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai/gpt-oss-20b + type: chat + max_input_tokens: 131072 + input_price: 0.075 + output_price: 0.3 + max_output_tokens: 65536 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: meta-llama/llama-prompt-guard-2-22m + type: chat + max_input_tokens: 512 + input_price: 0.03 + output_price: 0.03 + max_output_tokens: 512 + - name: meta-llama/llama-prompt-guard-2-86m + type: chat + max_input_tokens: 512 + input_price: 0.04 + output_price: 0.04 + max_output_tokens: 512 + - name: allam-2-7b + type: chat + max_input_tokens: 4096 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 4096 + - name: llama-3.3-70b-versatile + type: chat + max_input_tokens: 131072 + input_price: 0.59 + output_price: 0.79 + max_output_tokens: 32768 + supports_function_calling: true + - name: llama-3.1-8b-instant + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.08 + max_output_tokens: 131072 + supports_function_calling: true - provider: vertexai models: - - name: gemini-3.5-flash - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: gemini-3-flash-preview - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: high - - name: gemini-3.1-flash-lite - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, high] - default_reasoning_effort: minimal - - name: gemini-3.1-pro-preview - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 2 - output_price: 12 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: gemini-2.5-flash - max_input_tokens: 1048576 - max_output_tokens: 65535 - input_price: 0.3 - output_price: 2.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: medium - - name: gemini-2.5-pro - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: gemini-2.5-flash-lite - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.3 - output_price: 0.4 - supports_vision: true - supports_function_calling: true - - name: gemini-3-pro-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, high] - default_reasoning_effort: high - - name: gemini-3-flash-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: high - - name: claude-opus-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-fable-5-1 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-fable-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-8 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-7 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-6:thinking - real_name: claude-opus-4-6 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-sonnet-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: claude-sonnet-4-6:thinking - real_name: claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-opus-4-5@20251101 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-5@20251101:thinking - real_name: claude-opus-4-5@20251101 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-sonnet-4-5@20250929 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: claude-sonnet-4-5@20250929:thinking - real_name: claude-sonnet-4-5@20250929 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-haiku-4-5@20251001 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - - name: claude-haiku-4-5@20251001:thinking - real_name: claude-haiku-4-5@20251001 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-3-5-haiku@20241022 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 0.8 - output_price: 4 - supports_vision: true - supports_function_calling: true - - name: mistral-small-2503 - max_input_tokens: 32000 - input_price: 0.1 - output_price: 0.3 - supports_function_calling: true - - name: codestral-2501 - max_input_tokens: 256000 - input_price: 0.3 - output_price: 0.9 - supports_function_calling: true - - name: text-embedding-005 - type: embedding - max_input_tokens: 20000 - input_price: 0.025 - max_tokens_per_chunk: 2048 - default_chunk_size: 1500 - max_batch_size: 5 - - name: text-multilingual-embedding-002 - type: embedding - max_input_tokens: 20000 - input_price: 0.2 - max_tokens_per_chunk: 2048 - default_chunk_size: 1500 - max_batch_size: 5 - -# Links: -# - https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html#model-ids-arns -# - https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html -# - https://aws.amazon.com/bedrock/pricing/ -# - https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-call.html + - name: text-embedding-005 + type: embedding + max_input_tokens: 20000 + input_price: 0.025 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 5 + - name: text-multilingual-embedding-002 + type: embedding + max_input_tokens: 20000 + input_price: 0.2 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 5 + - name: gemini-3-pro-preview + type: chat + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + default_reasoning_effort: high + - name: claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-6 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 8192 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-opus-4-6:thinking + type: chat + real_name: claude-opus-4-6 + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-sonnet-4-6 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 8192 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: claude-sonnet-4-6:thinking + type: chat + real_name: claude-sonnet-4-6 + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + default_reasoning_effort: high + - name: claude-3-5-haiku@20241022 + type: chat + max_input_tokens: 200000 + input_price: 0.8 + output_price: 4.0 + max_output_tokens: 8192 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: mistral-small-2503 + type: chat + max_input_tokens: 32000 + input_price: 0.1 + output_price: 0.3 + supports_function_calling: true + - name: codestral-2501 + type: chat + max_input_tokens: 256000 + input_price: 0.3 + output_price: 0.9 + supports_function_calling: true + - name: gemini-3.8-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: claude-fable-5-1@default + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: gemini-3.7-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-flash-latest + type: chat + max_input_tokens: 1048576 + input_price: 1.5 + output_price: 9.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: claude-opus-5@default + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: gemini-3.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-3.6-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-flash-lite-latest + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: claude-sonnet-5@default + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: claude-fable-5@default + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-8@default + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: gemini-3-pro-image + type: chat + max_input_tokens: 65536 + input_price: 2.0 + output_price: 120.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gemini-3.1-flash-image + type: chat + max_input_tokens: 131072 + input_price: 0.5 + output_price: 60.0 + max_output_tokens: 32768 + supports_vision: true + reasoning_levels: + - minimal + - high + - name: gemini-3.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 1.5 + output_price: 9.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: gemini-3.1-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: minimal + - name: claude-opus-4-7@default + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: gemini-3.1-flash-lite-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-3.1-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high + - name: gemini-3.1-pro-preview-customtools + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: claude-sonnet-4-6@default + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: zai-org/glm-5-maas + type: chat + max_input_tokens: 202752 + input_price: 1.0 + output_price: 3.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: claude-opus-4-6@default + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: zai-org/glm-4.7-maas + type: chat + max_input_tokens: 200000 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 128000 + supports_function_calling: true + - name: deepseek-ai/deepseek-v3.2-maas + type: chat + max_input_tokens: 163840 + input_price: 0.56 + output_price: 1.68 + max_output_tokens: 65536 + supports_function_calling: true + - name: gemini-3-flash-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: high + - name: moonshotai/kimi-k2-thinking-maas + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 262144 + supports_function_calling: true + - name: claude-opus-4-5@20251101 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: claude-opus-4-5@20251101:thinking + type: chat + real_name: claude-opus-4-5@20251101 + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: claude-haiku-4-5@20251001 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-haiku-4-5@20251001:thinking + type: chat + real_name: claude-haiku-4-5@20251001 + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: claude-sonnet-4-5@20250929 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-4-5@20250929:thinking + type: chat + real_name: claude-sonnet-4-5@20250929 + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: deepseek-ai/deepseek-v3.1-maas + type: chat + max_input_tokens: 163840 + input_price: 0.6 + output_price: 1.7 + max_output_tokens: 32768 + supports_function_calling: true + - name: gemini-2.5-flash-image + type: chat + max_input_tokens: 32768 + input_price: 0.3 + output_price: 30.0 + max_output_tokens: 32768 + supports_vision: true + - name: qwen/qwen3-235b-a22b-instruct-2507-maas + type: chat + max_input_tokens: 262144 + input_price: 0.22 + output_price: 0.88 + max_output_tokens: 16384 + supports_function_calling: true + - name: claude-opus-4-1@20250805 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: openai/gpt-oss-120b-maas + type: chat + max_input_tokens: 131072 + input_price: 0.09 + output_price: 0.36 + max_output_tokens: 32768 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai/gpt-oss-20b-maas + type: chat + max_input_tokens: 131072 + input_price: 0.07 + output_price: 0.25 + max_output_tokens: 32768 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-2.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: medium + - name: gemini-2.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: gemini-2.5-pro + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high + - name: claude-opus-4@20250514 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-4@20250514 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: meta/llama-3.3-70b-instruct-maas + type: chat + max_input_tokens: 128000 + input_price: 0.72 + output_price: 0.72 + max_output_tokens: 8192 + supports_function_calling: true + - name: meta/llama-4-maverick-17b-128e-instruct-maas + type: chat + max_input_tokens: 524288 + input_price: 0.35 + output_price: 1.15 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true - provider: bedrock models: - - name: us.anthropic.claude-opus-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-fable-5-1 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-fable-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-opus-4-8 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-opus-4-7 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-opus-4-6-v1 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: us.anthropic.claude-opus-4-6-v1:thinking - real_name: us.anthropic.claude-opus-4-6-v1 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - patch: - body: - inferenceConfig: - temperature: null - topP: null - additionalModelRequestFields: - thinking: - type: enabled - budget_tokens: 16000 - - name: us.anthropic.claude-sonnet-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: us.anthropic.claude-sonnet-4-6:thinking - real_name: us.anthropic.claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - patch: - body: - inferenceConfig: - temperature: null - topP: null - additionalModelRequestFields: - thinking: - type: enabled - budget_tokens: 16000 - - name: us.anthropic.claude-opus-4-5-20251101-v1:0 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - - name: us.anthropic.claude-opus-4-5-20251101-v1:0:thinking - real_name: us.anthropic.claude-opus-4-5-20251101-v1:0 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - patch: - body: - inferenceConfig: - temperature: null - topP: null - additionalModelRequestFields: - thinking: - type: enabled - budget_tokens: 16000 - - name: us.anthropic.claude-sonnet-4-5-20250929-v1:0 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: us.anthropic.claude-sonnet-4-5-20250929-v1:0:thinking - real_name: us.anthropic.claude-sonnet-4-5-20250929-v1:0 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - patch: - body: - inferenceConfig: - temperature: null - topP: null - additionalModelRequestFields: - thinking: - type: enabled - budget_tokens: 16000 - - name: us.anthropic.claude-haiku-4-5-20251001-v1:0 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - - name: us.anthropic.claude-haiku-4-5-20251001-v1:0:thinking - real_name: us.anthropic.claude-haiku-4-5-20251001-v1:0 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - patch: - body: - inferenceConfig: - temperature: null - topP: null - additionalModelRequestFields: - thinking: - type: enabled - budget_tokens: 16000 - - name: us.meta.llama4-maverick-17b-instruct-v1:0 - max_input_tokens: 131072 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 0.24 - output_price: 0.97 - supports_function_calling: true - supports_vision: true - - name: us.meta.llama4-scout-17b-instruct-v1:0 - max_input_tokens: 131072 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 0.17 - output_price: 0.66 - supports_function_calling: true - supports_vision: true - - name: us.meta.llama3-3-70b-instruct-v1:0 - max_input_tokens: 131072 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 0.72 - output_price: 0.72 - supports_function_calling: true - - name: us.amazon.nova-premier-v1:0 - max_input_tokens: 300000 - max_output_tokens: 5120 - input_price: 2.5 - output_price: 12.5 - - name: us.amazon.nova-pro-v1:0 - max_input_tokens: 300000 - max_output_tokens: 5120 - input_price: 0.8 - output_price: 3.2 - supports_vision: true - - name: us.amazon.nova-lite-v1:0 - max_input_tokens: 300000 - max_output_tokens: 5120 - input_price: 0.06 - output_price: 0.24 - supports_vision: true - - name: us.amazon.nova-micro-v1:0 - max_input_tokens: 128000 - max_output_tokens: 5120 - input_price: 0.035 - output_price: 0.14 - - name: cohere.embed-english-v3 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: cohere.embed-multilingual-v3 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: us.deepseek.r1-v1:0 - max_input_tokens: 128000 - input_price: 1.35 - output_price: 5.4 - -# Links: -# - https://developers.cloudflare.com/workers-ai/models/ -# - https://developers.cloudflare.com/workers-ai/configuration/open-ai-compatibility/ + - name: cohere.embed-english-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: cohere.embed-multilingual-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: us.meta.llama3-3-70b-instruct-v1:0 + type: chat + max_input_tokens: 131072 + input_price: 0.72 + output_price: 0.72 + max_output_tokens: 8192 + require_max_tokens: true + supports_function_calling: true + - name: us.amazon.nova-premier-v1:0 + type: chat + max_input_tokens: 300000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 5120 + - name: us.amazon.nova-pro-v1:0 + type: chat + max_input_tokens: 300000 + input_price: 0.8 + output_price: 3.2 + max_output_tokens: 5120 + supports_vision: true + - name: us.amazon.nova-lite-v1:0 + type: chat + max_input_tokens: 300000 + input_price: 0.06 + output_price: 0.24 + max_output_tokens: 5120 + supports_vision: true + - name: us.amazon.nova-micro-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.035 + output_price: 0.14 + max_output_tokens: 5120 + - name: anthropic.claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 11.0 + output_price: 55.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: xai.grok-4.6 + type: chat + max_input_tokens: 500000 + input_price: 2.2 + output_price: 6.6 + max_output_tokens: 500000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - name: anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: au.anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: eu.anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.5 + output_price: 27.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: jp.anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: global.openai.gpt-5.6-luna + type: chat + max_input_tokens: 1050000 + input_price: 0.2 + output_price: 1.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: global.openai.gpt-5.6-sol + type: chat + max_input_tokens: 1050000 + input_price: 4.0 + output_price: 20.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: global.openai.gpt-5.6-terra + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: openai.gpt-5.6-luna + type: chat + max_input_tokens: 1050000 + input_price: 0.22 + output_price: 1.32 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: openai.gpt-5.6-sol + type: chat + max_input_tokens: 1050000 + input_price: 4.4 + output_price: 22.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: openai.gpt-5.6-terra + type: chat + max_input_tokens: 1050000 + input_price: 2.2 + output_price: 13.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: au.anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: eu.anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.2 + output_price: 11.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: jp.anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic.claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: eu.anthropic.claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 11.0 + output_price: 55.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: au.anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: eu.anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.5 + output_price: 27.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: jp.anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: openai.gpt-5.5 + type: chat + max_input_tokens: 272000 + input_price: 5.5 + output_price: 33.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - name: xai.grok-4.3 + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - name: anthropic.claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: eu.anthropic.claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.5 + output_price: 27.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: jp.anthropic.claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: minimax.minimax-m2.5 + type: chat + max_input_tokens: 196608 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 98304 + supports_function_calling: true + - name: zai.glm-5 + type: chat + max_input_tokens: 202752 + input_price: 1.0 + output_price: 3.2 + max_output_tokens: 101376 + supports_function_calling: true + - name: nvidia.nemotron-super-3-120b + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 0.65 + max_output_tokens: 131072 + supports_function_calling: true + - name: openai.gpt-5.4 + type: chat + max_input_tokens: 272000 + input_price: 2.75 + output_price: 16.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - name: anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: au.anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.3 + output_price: 16.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: eu.anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.3 + output_price: 16.5 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: global.anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: jp.anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: mistral.devstral-2-123b + type: chat + max_input_tokens: 256000 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 8192 + supports_function_calling: true + - name: us.anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: us.anthropic.claude-sonnet-4-6:thinking + type: chat + real_name: us.anthropic.claude-sonnet-4-6 + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + default_reasoning_effort: high + - name: deepseek.v3.2 + type: chat + max_input_tokens: 163840 + input_price: 0.62 + output_price: 1.85 + max_output_tokens: 81920 + supports_function_calling: true + - name: moonshotai.kimi-k2.5 + type: chat + max_input_tokens: 262143 + input_price: 0.6 + output_price: 3.0 + max_output_tokens: 16000 + supports_vision: true + supports_function_calling: true + - name: qwen.qwen3-coder-next + type: chat + max_input_tokens: 131072 + input_price: 0.22 + output_price: 1.8 + max_output_tokens: 65536 + supports_function_calling: true + - name: anthropic.claude-opus-4-6-v1 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: au.anthropic.claude-opus-4-6-v1 + type: chat + max_input_tokens: 1000000 + input_price: 16.5 + output_price: 82.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: eu.anthropic.claude-opus-4-6-v1 + type: chat + max_input_tokens: 1000000 + input_price: 5.5 + output_price: 27.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: global.anthropic.claude-opus-4-6-v1 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: us.anthropic.claude-opus-4-6-v1 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: us.anthropic.claude-opus-4-6-v1:thinking + type: chat + real_name: us.anthropic.claude-opus-4-6-v1 + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + default_reasoning_effort: high + - name: zai.glm-4.7-flash + type: chat + max_input_tokens: 200000 + input_price: 0.07 + output_price: 0.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: minimax.minimax-m2.1 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: nvidia.nemotron-nano-3-30b + type: chat + max_input_tokens: 128000 + input_price: 0.06 + output_price: 0.24 + max_output_tokens: 4096 + supports_function_calling: true + - name: zai.glm-4.7 + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: mistral.magistral-small-2509 + type: chat + max_input_tokens: 128000 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 40000 + supports_vision: true + supports_function_calling: true + - name: mistral.ministral-3-3b-instruct + type: chat + max_input_tokens: 256000 + input_price: 0.1 + output_price: 0.1 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: mistral.mistral-large-3-675b-instruct + type: chat + max_input_tokens: 256000 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: moonshot.kimi-k2-thinking + type: chat + max_input_tokens: 262143 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 16000 + supports_function_calling: true + - name: anthropic.claude-opus-4-5-20251101-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: eu.anthropic.claude-opus-4-5-20251101-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 5.5 + output_price: 27.5 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: global.anthropic.claude-opus-4-5-20251101-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: us.anthropic.claude-opus-4-5-20251101-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: us.anthropic.claude-opus-4-5-20251101-v1:0:thinking + type: chat + real_name: us.anthropic.claude-opus-4-5-20251101-v1:0 + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: openai.gpt-oss-safeguard-120b + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_function_calling: true + - name: openai.gpt-oss-safeguard-20b + type: chat + max_input_tokens: 128000 + input_price: 0.07 + output_price: 0.2 + max_output_tokens: 16384 + supports_function_calling: true + - name: minimax.minimax-m2 + type: chat + max_input_tokens: 204608 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 128000 + supports_function_calling: true + - name: anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: au.anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: eu.anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 5.5 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: global.anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: jp.anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: us.anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: us.anthropic.claude-haiku-4-5-20251001-v1:0:thinking + type: chat + real_name: us.anthropic.claude-haiku-4-5-20251001-v1:0 + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: qwen.qwen3-vl-235b-a22b + type: chat + max_input_tokens: 262000 + input_price: 0.3 + output_price: 1.5 + max_output_tokens: 262000 + supports_vision: true + supports_function_calling: true + - name: anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: au.anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: eu.anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.3 + output_price: 16.5 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: global.anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: jp.anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: us.anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: us.anthropic.claude-sonnet-4-5-20250929-v1:0:thinking + type: chat + real_name: us.anthropic.claude-sonnet-4-5-20250929-v1:0 + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: deepseek.v3-v1:0 + type: chat + max_input_tokens: 163840 + input_price: 0.58 + output_price: 1.68 + max_output_tokens: 81920 + supports_function_calling: true + - name: qwen.qwen3-235b-a22b-2507-v1:0 + type: chat + max_input_tokens: 262144 + input_price: 0.22 + output_price: 0.88 + max_output_tokens: 131072 + supports_function_calling: true + - name: qwen.qwen3-32b-v1:0 + type: chat + max_input_tokens: 16384 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen.qwen3-coder-30b-a3b-v1:0 + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 131072 + supports_function_calling: true + - name: qwen.qwen3-coder-480b-a35b-v1:0 + type: chat + max_input_tokens: 131072 + input_price: 0.22 + output_price: 1.8 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen.qwen3-next-80b-a3b + type: chat + max_input_tokens: 262000 + input_price: 0.14 + output_price: 1.4 + max_output_tokens: 262000 + supports_function_calling: true + - name: anthropic.claude-opus-4-1-20250805-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + supports_vision: true + supports_function_calling: true + - name: openai.gpt-oss-120b + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai.gpt-oss-120b-1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai.gpt-oss-20b + type: chat + max_input_tokens: 128000 + input_price: 0.07 + output_price: 0.3 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai.gpt-oss-20b-1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.07 + output_price: 0.3 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: us.anthropic.claude-opus-4-1-20250805-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: google.gemma-3-27b-it + type: chat + max_input_tokens: 202752 + input_price: 0.12 + output_price: 0.2 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: mistral.voxtral-small-24b-2507 + type: chat + max_input_tokens: 32000 + input_price: 0.15 + output_price: 0.35 + max_output_tokens: 8192 + supports_function_calling: true + - name: writer.palmyra-x4-v1:0 + type: chat + max_input_tokens: 122880 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 8192 + supports_function_calling: true + - name: writer.palmyra-x5-v1:0 + type: chat + max_input_tokens: 1040000 + input_price: 0.6 + output_price: 6.0 + max_output_tokens: 8192 + supports_function_calling: true + - name: mistral.pixtral-large-2502-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: meta.llama4-maverick-17b-instruct-v1:0 + type: chat + max_input_tokens: 1000000 + input_price: 0.24 + output_price: 0.97 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: meta.llama4-scout-17b-instruct-v1:0 + type: chat + max_input_tokens: 3500000 + input_price: 0.17 + output_price: 0.66 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: us.meta.llama4-maverick-17b-instruct-v1:0 + type: chat + max_input_tokens: 1000000 + input_price: 0.24 + output_price: 0.97 + max_output_tokens: 16384 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: us.meta.llama4-scout-17b-instruct-v1:0 + type: chat + max_input_tokens: 3500000 + input_price: 0.17 + output_price: 0.66 + max_output_tokens: 16384 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: deepseek.r1-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 1.35 + output_price: 5.4 + max_output_tokens: 32768 + supports_function_calling: true + - name: us.deepseek.r1-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 1.35 + output_price: 5.4 + max_output_tokens: 32768 + supports_function_calling: true + - name: meta.llama3-3-70b-instruct-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.72 + output_price: 0.72 + max_output_tokens: 4096 + supports_function_calling: true + - name: amazon.nova-lite-v1:0 + type: chat + max_input_tokens: 300000 + input_price: 0.06 + output_price: 0.24 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: amazon.nova-micro-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.035 + output_price: 0.14 + max_output_tokens: 8192 + supports_function_calling: true + - name: amazon.nova-pro-v1:0 + type: chat + max_input_tokens: 300000 + input_price: 0.8 + output_price: 3.2 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: amazon.nova-2-lite-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.33 + output_price: 2.75 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: google.gemma-3-12b-it + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.1 + max_output_tokens: 8192 + supports_vision: true + - name: google.gemma-3-4b-it + type: chat + max_input_tokens: 128000 + input_price: 0.04 + output_price: 0.08 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: mistral.ministral-3-14b-instruct + type: chat + max_input_tokens: 128000 + input_price: 0.2 + output_price: 0.2 + max_output_tokens: 4096 + supports_function_calling: true + - name: mistral.ministral-3-8b-instruct + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.15 + max_output_tokens: 4096 + supports_function_calling: true + - name: mistral.voxtral-mini-3b-2507 + type: chat + max_input_tokens: 128000 + input_price: 0.04 + output_price: 0.04 + max_output_tokens: 4096 + supports_function_calling: true + - name: nvidia.nemotron-nano-12b-v2 + type: chat + max_input_tokens: 128000 + input_price: 0.2 + output_price: 0.6 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: nvidia.nemotron-nano-9b-v2 + type: chat + max_input_tokens: 128000 + input_price: 0.06 + output_price: 0.23 + max_output_tokens: 4096 + supports_function_calling: true + - name: meta.llama3-1-70b-instruct-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.72 + output_price: 0.72 + max_output_tokens: 4096 + supports_function_calling: true + - name: meta.llama3-1-8b-instruct-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.22 + output_price: 0.22 + max_output_tokens: 4096 + supports_function_calling: true - provider: cloudflare models: - - name: '@cf/meta/llama-4-scout-17b-16e-instruct' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/meta/llama-3.3-70b-instruct-fp8-fast' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/qwen/qwen3-30b-a3b-fp8' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/qwen/qwen2.5-coder-32b-instruct' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/zai-org/glm-4.7-flash' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/google/gemma-3-12b-it' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/mistralai/mistral-small-3.1-24b-instruct' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/baai/bge-large-en-v1.5' - type: embedding - input_price: 0 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 100 - -# Links: -# - https://cloud.baidu.com/doc/qianfan/s/rmh4stp0j -# - https://cloud.baidu.com/doc/qianfan/s/wmh4sv6ya + - name: '@cf/baai/bge-large-en-v1.5' + type: embedding + input_price: 0.0 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: '@cf/google/gemma-3-12b-it' + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 2048 + require_max_tokens: true + - name: '@cf/zai-org/glm-5.3-flash' + type: chat + max_input_tokens: 1310720 + input_price: 0.15 + output_price: 0.5 + max_output_tokens: 1048576 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: '@cf/qwen/qwen3.8-27b' + type: chat + max_input_tokens: 262144 + input_price: 0.45 + output_price: 3.2 + max_output_tokens: 262144 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - xhigh + - name: '@cf/zai-org/glm-5.3' + type: chat + max_input_tokens: 1310720 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 1310720 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/deepseek-ai/deepseek-v4-pro-0813' + type: chat + max_input_tokens: 1048576 + input_price: 1.32 + output_price: 3.96 + max_output_tokens: 1048576 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - high + - max + - name: '@cf/deepseek-ai/deepseek-v4-flash-0731' + type: chat + max_input_tokens: 1310720 + input_price: 0.44 + output_price: 1.32 + max_output_tokens: 1048576 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - high + - max + - name: '@cf/zai-org/glm-5.2' + type: chat + max_input_tokens: 262144 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 256000 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/moonshotai/kimi-k2.7-code' + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 262144 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/moonshotai/kimi-k2.6' + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 256000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/google/gemma-4-26b-a4b-it' + type: chat + max_input_tokens: 256000 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 16384 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/nvidia/nemotron-3-120b-a12b' + type: chat + max_input_tokens: 256000 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 256000 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/zai-org/glm-4.7-flash' + type: chat + max_input_tokens: 131072 + input_price: 0.0605 + output_price: 0.4 + max_output_tokens: 131072 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/ibm-granite/granite-4.0-h-micro' + type: chat + max_input_tokens: 131000 + input_price: 0.017 + output_price: 0.112 + max_output_tokens: 131000 + require_max_tokens: true + supports_function_calling: true + - name: '@cf/aisingapore/gemma-sea-lion-v4-27b-it' + type: chat + max_input_tokens: 128000 + input_price: 0.351 + output_price: 0.555 + max_output_tokens: 128000 + require_max_tokens: true + - name: '@cf/openai/gpt-oss-120b' + type: chat + max_input_tokens: 128000 + input_price: 0.35 + output_price: 0.75 + max_output_tokens: 16384 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/openai/gpt-oss-20b' + type: chat + max_input_tokens: 128000 + input_price: 0.2 + output_price: 0.3 + max_output_tokens: 16384 + require_max_tokens: true + supports_function_calling: true + - name: '@cf/qwen/qwen3-30b-a3b-fp8' + type: chat + max_input_tokens: 32768 + input_price: 0.0509 + output_price: 0.335 + max_output_tokens: 32768 + require_max_tokens: true + supports_function_calling: true + - name: '@cf/meta/llama-4-scout-17b-16e-instruct' + type: chat + max_input_tokens: 131000 + input_price: 0.27 + output_price: 0.85 + max_output_tokens: 16384 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: '@cf/mistralai/mistral-small-3.1-24b-instruct' + type: chat + max_input_tokens: 128000 + input_price: 0.351 + output_price: 0.555 + max_output_tokens: 128000 + require_max_tokens: true + supports_function_calling: true + - name: '@cf/qwen/qwq-32b' + type: chat + max_input_tokens: 24000 + input_price: 0.66 + output_price: 1.0 + max_output_tokens: 24000 + require_max_tokens: true + - name: '@cf/deepseek-ai/deepseek-r1-distill-qwen-32b' + type: chat + max_input_tokens: 80000 + input_price: 0.497 + output_price: 4.881 + max_output_tokens: 80000 + require_max_tokens: true + - name: '@cf/meta/llama-3.3-70b-instruct-fp8-fast' + type: chat + max_input_tokens: 24000 + input_price: 0.293 + output_price: 2.253 + max_output_tokens: 24000 + require_max_tokens: true + supports_function_calling: true + - name: '@cf/qwen/qwen2.5-coder-32b-instruct' + type: chat + max_input_tokens: 32768 + input_price: 0.66 + output_price: 1.0 + max_output_tokens: 32768 + require_max_tokens: true + - name: '@cf/meta/llama-3.2-11b-vision-instruct' + type: chat + max_input_tokens: 128000 + input_price: 0.0485 + output_price: 0.676 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + - name: '@cf/meta/llama-3.2-1b-instruct' + type: chat + max_input_tokens: 60000 + input_price: 0.027 + output_price: 0.201 + max_output_tokens: 60000 + require_max_tokens: true + - name: '@cf/meta/llama-3.2-3b-instruct' + type: chat + max_input_tokens: 80000 + input_price: 0.0509 + output_price: 0.335 + max_output_tokens: 80000 + require_max_tokens: true + - name: '@cf/meta/llama-3.1-8b-instruct-fp8' + type: chat + max_input_tokens: 32000 + input_price: 0.152 + output_price: 0.287 + max_output_tokens: 32000 + require_max_tokens: true + - name: '@cf/meta/llama-guard-3-8b' + type: chat + max_input_tokens: 131072 + input_price: 0.484 + output_price: 0.03 + max_output_tokens: 131072 + require_max_tokens: true - provider: ernie models: - - name: ernie-4.5-turbo-128k - max_input_tokens: 131072 - input_price: 0.112 - output_price: 0.448 - - name: ernie-4.5-turbo-vl-32k - max_input_tokens: 32768 - input_price: 0.42 - output_price: 1.26 - supports_vision: true - - name: ernie-5.0-thinking-preview - max_input_tokens: 131072 - input_price: 1.4 - output_price: 5.6 - - name: ernie-x1.1-preview - max_input_tokens: 65536 - input_price: 0.14 - output_price: 0.56 - - name: bge-large-zh - type: embedding - input_price: 0.07 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 16 - - name: bge-large-en - type: embedding - input_price: 0.07 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 16 - - name: bce-reranker-base - type: reranker - max_input_tokens: 1024 - input_price: 0.07 - -# Links: -# - https://help.aliyun.com/zh/model-studio/getting-started/models -# - https://help.aliyun.com/zh/model-studio/developer-reference/use-qwen-by-calling-api + - name: ernie-4.5-turbo-128k + type: chat + max_input_tokens: 131072 + input_price: 0.112 + output_price: 0.448 + - name: ernie-4.5-turbo-vl-32k + type: chat + max_input_tokens: 32768 + input_price: 0.42 + output_price: 1.26 + supports_vision: true + - name: ernie-5.0-thinking-preview + type: chat + max_input_tokens: 131072 + input_price: 1.4 + output_price: 5.6 + - name: ernie-x1.1-preview + type: chat + max_input_tokens: 65536 + input_price: 0.14 + output_price: 0.56 + - name: bge-large-zh + type: embedding + input_price: 0.07 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 16 + - name: bge-large-en + type: embedding + input_price: 0.07 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 16 + - name: bce-reranker-base + type: reranker + max_input_tokens: 1024 + input_price: 0.07 - provider: qianwen models: - - name: qwen3.5-plus - max_input_tokens: 262144 - supports_function_calling: true - patch: - body: - enable_thinking: false - - name: qwen3.5-plus:thinking - real_name: qwen3.5-plus - max_input_tokens: 262144 - supports_function_calling: true - - name: qwen3-max - max_input_tokens: 262144 - supports_function_calling: true - - name: qwen3-max:thinking - real_name: qwen3-max - max_input_tokens: 262144 - supports_function_calling: true - patch: - body: - enable_thinking: true - - name: qwen3-vl-plus - max_input_tokens: 262144 - supports_vision: true - - name: qwen3-vl-flash - max_input_tokens: 262144 - supports_vision: true - - name: qwen3-coder-plus - max_input_tokens: 1000000 - - name: qwen3-coder-flash - max_input_tokens: 1000000 - - name: qwen3.5-397b-a17b - max_input_tokens: 262144 - supports_function_calling: true - patch: - body: - enable_thinking: false - - name: qwen3.5-397b-a17b:thinking - real_name: qwen3.5-397b-a17b - max_input_tokens: 262144 - supports_function_calling: true - - name: qwen3-next-80b-a3b-instruct - max_input_tokens: 131072 - input_price: 0.14 - output_price: 0.56 - supports_function_calling: true - - name: qwen3-next-80b-a3b-thinking - max_input_tokens: 131072 - input_price: 0.14 - output_price: 1.4 - - name: qwen3-235b-a22b-instruct-2507 - max_input_tokens: 131072 - input_price: 0.28 - output_price: 1.12 - supports_function_calling: true - - name: qwen3-235b-a22b-thinking-2507 - max_input_tokens: 131072 - input_price: 0.28 - output_price: 2.8 - - name: qwen3-30b-a3b-instruct-2507 - max_input_tokens: 131072 - input_price: 0.105 - output_price: 0.42 - supports_function_calling: true - - name: qwen3-30b-a3b-thinking-2507 - max_input_tokens: 131072 - input_price: 0.105 - output_price: 1.05 - - name: qwen3-vl-32b-instruct - max_input_tokens: 131072 - input_price: 0.28 - output_price: 1.12 - supports_vision: true - - name: qwen3-vl-8b-instruct - max_input_tokens: 131072 - input_price: 0.07 - output_price: 0.28 - supports_vision: true - - name: qwen3-coder-next - max_input_tokens: 262144 - - name: qwen3-coder-480b-a35b-instruct - max_input_tokens: 262144 - - name: qwen3-coder-30b-a3b-instruct - max_input_tokens: 262144 - - name: text-embedding-v4 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 8192 - default_chunk_size: 2000 - max_batch_size: 10 - - name: text-embedding-v3 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 8192 - default_chunk_size: 2000 - max_batch_size: 10 - -# links: -# - https://cloud.tencent.com/document/product/1729/104753 -# - https://cloud.tencent.com/document/product/1729/97731 -# - https://cloud.tencent.com/document/product/1729/111007 + - name: qwen3.5-plus:thinking + type: chat + real_name: qwen3.5-plus + max_input_tokens: 262144 + supports_function_calling: true + - name: qwen3.5-397b-a17b:thinking + type: chat + real_name: qwen3.5-397b-a17b + max_input_tokens: 262144 + supports_function_calling: true + - name: text-embedding-v4 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 10 + - name: text-embedding-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 10 + - name: qwen3-vl-flash + type: chat + max_input_tokens: 262144 + supports_vision: true + - name: qwen3-235b-a22b-instruct-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.28 + output_price: 1.12 + supports_function_calling: true + - name: qwen3-235b-a22b-thinking-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.28 + output_price: 2.8 + - name: qwen3-30b-a3b-instruct-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.105 + output_price: 0.42 + supports_function_calling: true + - name: qwen3-30b-a3b-thinking-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.105 + output_price: 1.05 + - name: qwen3-vl-32b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.28 + output_price: 1.12 + supports_vision: true + - name: qwen3-vl-8b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.07 + output_price: 0.28 + supports_vision: true + - name: qwen3-coder-next + type: chat + max_input_tokens: 262144 + - name: qwen3.8-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.15 + output_price: 0.47 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - xhigh + - name: qwen3.8-max + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - xhigh + - name: deepseek-v4-flash-0731 + type: chat + max_input_tokens: 1000000 + input_price: 0.2 + output_price: 0.4 + max_output_tokens: 384000 + supports_function_calling: true + reasoning_levels: + - high + - max + - name: glm-5.2 + type: chat + max_input_tokens: 1000000 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - none + - minimal + - low + - medium + - high + - xhigh + - max + - name: qwen3.7-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.7-max + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 7.5 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3.6-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.1875 + output_price: 1.125 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.6-27b + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 3.6 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.6-max-preview + type: chat + max_input_tokens: 262144 + input_price: 1.3 + output_price: 7.8 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3.6-35b-a3b + type: chat + max_input_tokens: 262144 + input_price: 0.248 + output_price: 1.485 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.6-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.5-122b-a10b + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 3.2 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.5-27b + type: chat + max_input_tokens: 262144 + input_price: 0.3 + output_price: 2.4 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.5-35b-a3b + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.5-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.4 + output_price: 2.4 + patch: + body: + enable_thinking: false + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.5-397b-a17b + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 3.6 + patch: + body: + enable_thinking: false + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3-max + type: chat + max_input_tokens: 262144 + input_price: 1.2 + output_price: 6.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-max:thinking + type: chat + real_name: qwen3-max + max_input_tokens: 262144 + input_price: 1.2 + output_price: 6.0 + patch: + body: + enable_thinking: true + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-vl-plus + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 1.6 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen3-omni-flash + type: chat + max_input_tokens: 65536 + input_price: 0.43 + output_price: 1.66 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: qwen3-asr-flash + type: chat + max_input_tokens: 53248 + input_price: 0.035 + output_price: 0.035 + max_output_tokens: 4096 + - name: qwen3-next-80b-a3b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.5 + output_price: 2.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen3-next-80b-a3b-thinking + type: chat + max_input_tokens: 131072 + input_price: 0.5 + output_price: 6.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.05 + output_price: 0.4 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen3-coder-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.3 + output_price: 1.5 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-coder-plus + type: chat + max_input_tokens: 1048576 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-14b + type: chat + max_input_tokens: 131072 + input_price: 0.35 + output_price: 1.4 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen3-235b-a22b + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 2.8 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen3-32b + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 2.8 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen3-8b + type: chat + max_input_tokens: 131072 + input_price: 0.18 + output_price: 0.7 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen3-coder-30b-a3b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.45 + output_price: 2.25 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-coder-480b-a35b-instruct + type: chat + max_input_tokens: 262144 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-vl-235b-a22b + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 2.8 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen3-vl-30b-a3b + type: chat + max_input_tokens: 131072 + input_price: 0.2 + output_price: 0.8 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qvq-max + type: chat + max_input_tokens: 131072 + input_price: 1.2 + output_price: 4.8 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: qwq-plus + type: chat + max_input_tokens: 131072 + input_price: 0.8 + output_price: 2.4 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen-omni-turbo + type: chat + max_input_tokens: 32768 + input_price: 0.07 + output_price: 0.27 + max_output_tokens: 2048 + supports_vision: true + supports_function_calling: true + - name: qwen-mt-plus + type: chat + max_input_tokens: 16384 + input_price: 2.46 + output_price: 7.37 + max_output_tokens: 8192 + - name: qwen-mt-turbo + type: chat + max_input_tokens: 16384 + input_price: 0.16 + output_price: 0.49 + max_output_tokens: 8192 + - name: qwen2-5-omni-7b + type: chat + max_input_tokens: 32768 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 2048 + supports_vision: true + supports_function_calling: true + - name: qwen-turbo + type: chat + max_input_tokens: 1000000 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen-vl-ocr + type: chat + max_input_tokens: 34096 + input_price: 0.72 + output_price: 0.72 + max_output_tokens: 4096 + supports_vision: true + - name: qwen2-5-14b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.35 + output_price: 1.4 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen2-5-32b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 2.8 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen2-5-72b-instruct + type: chat + max_input_tokens: 131072 + input_price: 1.4 + output_price: 5.6 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen2-5-7b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.175 + output_price: 0.7 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen2-5-vl-72b-instruct + type: chat + max_input_tokens: 131072 + input_price: 2.8 + output_price: 8.4 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: qwen2-5-vl-7b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.35 + output_price: 1.05 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: qwen-vl-max + type: chat + max_input_tokens: 131072 + input_price: 0.8 + output_price: 3.2 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: qwen-max + type: chat + max_input_tokens: 32768 + input_price: 1.6 + output_price: 6.4 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.4 + output_price: 1.2 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen-vl-plus + type: chat + max_input_tokens: 131072 + input_price: 0.21 + output_price: 0.63 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: qwen-plus-character-ja + type: chat + max_input_tokens: 8192 + input_price: 0.5 + output_price: 1.4 + max_output_tokens: 512 + supports_function_calling: true - provider: hunyuan models: - - name: hunyuan-2.0-instruct-20251111 - max_input_tokens: 131072 - input_price: 0.112 - output_price: 0.28 - supports_function_calling: true - - name: hunyuan-2.0-thinking-20251109 - max_input_tokens: 131072 - input_price: 0.14 - output_price: 0.56 - supports_function_calling: true - - name: hunyuan-vision-1.5-instruct - max_input_tokens: 24576 - input_price: 0.42 - output_price: 1.26 - supports_vision: true - - name: hunyuan-embedding - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 1024 - default_chunk_size: 1000 - max_batch_size: 100 - -# Links: -# - https://platform.moonshot.cn/docs/pricing/chat#%E8%AE%A1%E8%B4%B9%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5 -# - https://platform.moonshot.cn/docs/api/chat#%E5%85%AC%E5%BC%80%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%9C%B0%E5%9D%80 + - name: hunyuan-2.0-instruct-20251111 + type: chat + max_input_tokens: 131072 + input_price: 0.112 + output_price: 0.28 + supports_function_calling: true + - name: hunyuan-2.0-thinking-20251109 + type: chat + max_input_tokens: 131072 + input_price: 0.14 + output_price: 0.56 + supports_function_calling: true + - name: hunyuan-vision-1.5-instruct + type: chat + max_input_tokens: 24576 + input_price: 0.42 + output_price: 1.26 + supports_vision: true + - name: hunyuan-embedding + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 1024 + default_chunk_size: 1000 + max_batch_size: 100 - provider: moonshot models: - - name: kimi-k3 - max_input_tokens: 1048576 - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: kimi-k2.7-code - max_input_tokens: 262144 - input_price: 0.95 - output_price: 4 - supports_vision: true - supports_function_calling: true - - name: kimi-k2.7-code-highspeed - max_input_tokens: 262144 - input_price: 1.9 - output_price: 8 - supports_vision: true - supports_function_calling: true - - name: kimi-k2.6 - max_input_tokens: 262144 - input_price: 0.95 - output_price: 4 - supports_vision: true - supports_function_calling: true - - name: kimi-k2.5 - max_input_tokens: 262144 - input_price: 0.56 - output_price: 2.94 - supports_vision: true - supports_function_calling: true - - name: kimi-k2-turbo-preview - max_input_tokens: 262144 - input_price: 1.12 - output_price: 8.12 - supports_vision: true - supports_function_calling: true - - name: kimi-k2-0905-preview - max_input_tokens: 262144 - input_price: 0.56 - output_price: 2.24 - supports_vision: true - supports_function_calling: true - - name: kimi-k2-thinking-turbo - max_input_tokens: 262144 - input_price: 1.12 - output_price: 8.12 - supports_vision: true - - name: kimi-k2-thinking - max_input_tokens: 262144 - input_price: 0.56 - output_price: 2.24 - supports_vision: true - -# Links: -# - https://api-docs.deepseek.com/quick_start/pricing -# - https://platform.deepseek.com/api-docs/api/create-chat-completion + - name: kimi-k3 + type: chat + max_input_tokens: 1048576 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: kimi-k2.7-code + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: kimi-k2.7-code-highspeed + type: chat + max_input_tokens: 262144 + input_price: 1.9 + output_price: 8.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: kimi-k2.6 + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: kimi-k2.5 + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 3.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: kimi-k2-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 262144 + supports_function_calling: true + - name: kimi-k2-thinking-turbo + type: chat + max_input_tokens: 262144 + input_price: 1.15 + output_price: 8.0 + max_output_tokens: 262144 + supports_function_calling: true + - name: kimi-k2-0905-preview + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 262144 + supports_function_calling: true + - name: kimi-k2-turbo-preview + type: chat + max_input_tokens: 262144 + input_price: 2.4 + output_price: 10.0 + max_output_tokens: 262144 + supports_function_calling: true + - name: kimi-k2-0711-preview + type: chat + max_input_tokens: 131072 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 16384 + supports_function_calling: true - provider: deepseek models: - - name: deepseek-v4-pro - max_input_tokens: 1000000 - max_output_tokens: 384000 - input_price: 0.435 - output_price: 0.87 - supports_function_calling: true - - name: deepseek-v4-flash - max_input_tokens: 1000000 - max_output_tokens: 384000 - input_price: 0.14 - output_price: 0.28 - supports_function_calling: true - - name: deepseek-chat - max_input_tokens: 64000 - max_output_tokens: 8192 - input_price: 0.56 - output_price: 1.68 - supports_function_calling: true - - name: deepseek-reasoner - max_input_tokens: 64000 - max_output_tokens: 32768 - input_price: 0.56 - output_price: 1.68 - -# Links: -# - https://open.bigmodel.cn/pricing -# - https://open.bigmodel.cn/dev/api#glm-4 + - name: deepseek-chat + type: chat + max_input_tokens: 64000 + input_price: 0.56 + output_price: 1.68 + max_output_tokens: 8192 + supports_function_calling: true + - name: deepseek-reasoner + type: chat + max_input_tokens: 64000 + input_price: 0.56 + output_price: 1.68 + max_output_tokens: 32768 + - name: deepseek-v4-flash-vision-exp + type: chat + max_input_tokens: 1000000 + input_price: 0.14 + output_price: 0.28 + max_output_tokens: 384000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: deepseek-v4-pro + type: chat + max_input_tokens: 1000000 + input_price: 0.435 + output_price: 0.87 + max_output_tokens: 384000 + supports_function_calling: true + reasoning_levels: + - high + - max + - name: deepseek-v4-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.14 + output_price: 0.28 + max_output_tokens: 384000 + supports_function_calling: true + reasoning_levels: + - low + - high + - max - provider: zhipuai models: - - name: glm-5 - max_input_tokens: 202752 - supports_function_calling: true - - name: glm-5:instruct - real_name: glm-5 - max_input_tokens: 202752 - supports_function_calling: true - patch: - body: - thinking: - type: disabled - - name: glm-4.7 - max_input_tokens: 202752 - supports_function_calling: true - - name: glm-4.7:instruct - real_name: glm-4.7 - max_input_tokens: 202752 - supports_function_calling: true - patch: - body: - thinking: - type: disabled - - name: glm-4.7-flash - max_input_tokens: 202752 - input_price: 0 - output_price: 0 - supports_function_calling: true - - name: glm-4.6v - max_input_tokens: 65536 - supports_vision: true - - name: glm-4.6v-flash - max_input_tokens: 65536 - input_price: 0 - output_price: 0 - supports_vision: true - - name: embedding-3 - type: embedding - max_input_tokens: 8192 - input_price: 0.07 - max_tokens_per_chunk: 8192 - default_chunk_size: 2000 - - name: rerank - type: reranker - max_input_tokens: 4096 - input_price: 0.112 - -# Links: -# - https://platform.minimaxi.com/docs/guides/pricing-paygo -# - https://platform.minimaxi.com/document/ChatCompletion%20v2 + - name: embedding-3 + type: embedding + max_input_tokens: 8192 + input_price: 0.07 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + - name: rerank + type: reranker + max_input_tokens: 4096 + input_price: 0.112 + - name: glm-4.6v-flash + type: chat + max_input_tokens: 65536 + input_price: 0.0 + output_price: 0.0 + supports_vision: true + - name: glm-5.3-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.075 + output_price: 0.25 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: glm-5.3 + type: chat + max_input_tokens: 1000000 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: glm-5.2 + type: chat + max_input_tokens: 1000000 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - high + - max + - name: glm-5v-turbo + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 22.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: glm-5.1 + type: chat + max_input_tokens: 200000 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-5 + type: chat + max_input_tokens: 204800 + input_price: 1.0 + output_price: 3.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-5:instruct + type: chat + real_name: glm-5 + max_input_tokens: 204800 + input_price: 1.0 + output_price: 3.2 + patch: + body: + thinking: + type: disabled + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.7-flash + type: chat + max_input_tokens: 200000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.7-flashx + type: chat + max_input_tokens: 200000 + input_price: 0.07 + output_price: 0.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.7 + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.7:instruct + type: chat + real_name: glm-4.7 + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.2 + patch: + body: + thinking: + type: disabled + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.6v + type: chat + max_input_tokens: 128000 + input_price: 0.3 + output_price: 0.9 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: glm-4.6 + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.5v + type: chat + max_input_tokens: 64000 + input_price: 0.6 + output_price: 1.8 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: glm-4.5 + type: chat + max_input_tokens: 131072 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 98304 + supports_function_calling: true + - name: glm-4.5-air + type: chat + max_input_tokens: 131072 + input_price: 0.2 + output_price: 1.1 + max_output_tokens: 98304 + supports_function_calling: true + - name: glm-4.5-flash + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 98304 + supports_function_calling: true - provider: minimax models: - - name: minimax-m3 - max_input_tokens: 1000000 - input_price: 4.2 - output_price: 16.8 - supports_function_calling: true - - name: minimax-m2.7 - max_input_tokens: 204800 - input_price: 0.294 - output_price: 1.176 - supports_function_calling: true - - name: minimax-m2.5 - max_input_tokens: 204800 - input_price: 0.294 - output_price: 1.176 - supports_function_calling: true - - name: minimax-m2.5-highspeed - max_input_tokens: 204800 - input_price: 0.588 - output_price: 2.352 - supports_function_calling: true - - name: minimax-m2.1 - max_input_tokens: 204800 - input_price: 0.294 - output_price: 1.176 - supports_function_calling: true - - name: minimax-m2.1-highspeed - max_input_tokens: 204800 - input_price: 0.588 - output_price: 2.352 - supports_function_calling: true - -# Links: -# - https://openrouter.ai/models -# - https://openrouter.ai/docs/api-reference/chat-completion + - name: minimax-m3 + type: chat + max_input_tokens: 1000000 + input_price: 4.2 + output_price: 16.8 + supports_function_calling: true + - name: minimax-m2.7 + type: chat + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.5 + type: chat + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.5-highspeed + type: chat + max_input_tokens: 204800 + input_price: 0.588 + output_price: 2.352 + supports_function_calling: true + - name: minimax-m2.1 + type: chat + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.1-highspeed + type: chat + max_input_tokens: 204800 + input_price: 0.588 + output_price: 2.352 + supports_function_calling: true + - name: MiniMax-M3 + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 512000 + supports_vision: true + supports_function_calling: true + - name: MiniMax-M2.7 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: MiniMax-M2.7-highspeed + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: MiniMax-M2.5-highspeed + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: MiniMax-M2.5 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: MiniMax-M2.1 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: MiniMax-M2 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true - provider: openrouter models: - - name: openai/gpt-5.6-sol - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: openai/gpt-5.6-terra - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: openai/gpt-5.6-luna - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: openai/gpt-5.5 - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: medium - - name: openai/gpt-5.5-pro - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 30 - output_price: 180 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: high - - name: openai/gpt-5.4 - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 2.5 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: openai/gpt-5.4-pro - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 30 - output_price: 180 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: medium - - name: openai/gpt-5.4-mini - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.75 - output_price: 4.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: openai/gpt-5.4-nano - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.2 - output_price: 1.25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: openai/gpt-5.3-codex - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.75 - output_price: 14 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh] - default_reasoning_effort: medium - - name: openai/gpt-5.2 - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.75 - output_price: 14 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: openai/gpt-5.2-pro - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 21 - output_price: 168 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: medium - - name: openai/gpt-5 - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: openai/gpt-5-mini - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.25 - output_price: 2 - supports_vision: true - supports_function_calling: true - - name: openai/gpt-5-nano - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.05 - output_price: 0.4 - supports_vision: true - supports_function_calling: true - - name: openai/gpt-4.1 - max_input_tokens: 1047576 - max_output_tokens: 32768 - input_price: 2 - output_price: 8 - supports_vision: true - supports_function_calling: true - - name: openai/gpt-4o - max_input_tokens: 128000 - input_price: 2.5 - output_price: 10 - supports_vision: true - supports_function_calling: true - - name: openai/gpt-oss-120b - max_input_tokens: 131072 - input_price: 0.09 - output_price: 0.45 - supports_function_calling: true - - name: openai/gpt-oss-20b - max_input_tokens: 131072 - input_price: 0.04 - output_price: 0.16 - supports_function_calling: true - - name: google/gemini-3.5-flash - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: google/gemini-3-flash-preview - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: high - - name: google/gemini-3.1-flash-lite - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: minimal - - name: google/gemini-3.1-pro-preview - max_input_tokens: 1048576 - max_output_tokens: 65535 - input_price: 0.3 - output_price: 2.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: google/gemini-3-pro-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, high] - default_reasoning_level: high - - name: google/gemini-3-flash-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_level: high - - name: google/gemini-2.5-flash - max_input_tokens: 1048576 - input_price: 0.3 - output_price: 2.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: low - - name: google/gemini-2.5-pro - max_input_tokens: 1048576 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: google/gemini-2.5-flash-lite - max_input_tokens: 1048576 - input_price: 0.3 - output_price: 0.4 - supports_vision: true - - name: google/gemini-2.0-flash-001 - max_input_tokens: 1000000 - input_price: 0.15 - output_price: 0.6 - supports_vision: true - supports_function_calling: true - - name: google/gemini-2.0-flash-lite-001 - max_input_tokens: 1048576 - input_price: 0.075 - output_price: 0.3 - supports_vision: true - supports_function_calling: true - - name: google/gemma-3-27b-it - max_input_tokens: 131072 - input_price: 0.1 - output_price: 0.2 - - name: anthropic/claude-opus-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-fable-5-1 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-fable-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-opus-4-8 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-opus-4-7 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-opus-4.6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: anthropic/claude-sonnet-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-sonnet-4.6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: anthropic/claude-opus-4.5 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - - name: anthropic/claude-sonnet-4.5 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: anthropic/claude-haiku-4.5 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - - name: meta-llama/llama-4-maverick - max_input_tokens: 1048576 - input_price: 0.18 - output_price: 0.6 - supports_vision: true - supports_function_calling: true - - name: meta-llama/llama-4-scout - max_input_tokens: 327680 - input_price: 0.08 - output_price: 0.3 - supports_vision: true - supports_function_calling: true - - name: meta-llama/llama-3.3-70b-instruct - max_input_tokens: 131072 - input_price: 0.12 - output_price: 0.3 - - name: mistralai/mistral-large-2512 - max_input_tokens: 262144 - input_price: 0.5 - output_price: 1.5 - supports_function_calling: true - - name: mistralai/mistral-medium-3.1 - max_input_tokens: 131072 - input_price: 0.4 - output_price: 2 - supports_function_calling: true - supports_vision: true - - name: mistralai/mistral-small-3.2-24b-instruct - max_input_tokens: 131072 - input_price: 0.1 - output_price: 0.3 - supports_vision: true - - name: mistralai/devstral-2512 - max_input_tokens: 262144 - input_price: 0.5 - output_price: 0.22 - supports_function_calling: true - - name: mistralai/devstral-small - max_input_tokens: 131072 - input_price: 0.07 - output_price: 0.28 - supports_function_calling: true - - name: mistralai/codestral-2508 - max_input_tokens: 256000 - input_price: 0.3 - output_price: 0.9 - supports_function_calling: true - - name: mistralai/ministral-14b-2512 - max_input_tokens: 262144 - input_price: 0.2 - output_price: 0.2 - supports_function_calling: true - - name: ai21/jamba-large-1.7 - max_input_tokens: 256000 - input_price: 2 - output_price: 8 - supports_function_calling: true - - name: cohere/command-a - max_input_tokens: 256000 - input_price: 2.5 - output_price: 10 - supports_function_calling: true - - name: cohere/command-r7b-12-2024 - max_input_tokens: 128000 - max_output_tokens: 4096 - input_price: 0.0375 - output_price: 0.15 - - name: deepseek/deepseek-v3.2 - max_input_tokens: 163840 - input_price: 0.25 - output_price: 0.38 - - name: qwen/qwen3-max - max_input_tokens: 262144 - input_price: 1.2 - output_price: 6 - supports_function_calling: true - - name: qwen/qwen3-max-thinking - max_input_tokens: 262144 - input_price: 1.2 - output_price: 6 - supports_function_calling: true - - name: qwen/qwen3.5-plus-02-15 - max_input_tokens: 1000000 - max_output_tokens: 8192 - input_price: 0.4 - output_price: 2.4 - supports_function_calling: true - - name: qwen/qwen3.5-397b-a17b - max_input_tokens: 262144 - max_output_tokens: 8192 - input_price: 0.15 - output_price: 1 - supports_function_calling: true - - name: qwen/qwen3-next-80b-a3b-instruct - max_input_tokens: 262144 - input_price: 0.1 - output_price: 0.8 - supports_function_calling: true - - name: qwen/qwen3-next-80b-a3b-thinking - max_input_tokens: 262144 - input_price: 0.1 - output_price: 0.8 - - name: qwen/qwen3-235b-a22b-2507 # Qwen3 235B A22B Instruct 2507 - max_input_tokens: 262144 - input_price: 0.12 - output_price: 0.59 - supports_function_calling: true - - name: qwen/qwen3-235b-a22b-thinking-2507 - max_input_tokens: 262144 - input_price: 0.118 - output_price: 0.118 - - name: qwen/qwen3-30b-a3b-instruct-2507 - max_input_tokens: 131072 - input_price: 0.2 - output_price: 0.8 - - name: qwen/qwen3-30b-a3b-thinking-2507 - max_input_tokens: 262144 - input_price: 0.071 - output_price: 0.285 - - name: qwen/qwen3-vl-32b-instruct - max_input_tokens: 262144 - input_price: 0.35 - output_price: 1.1 - supports_vision: true - - name: qwen/qwen3-vl-8b-instruct - max_input_tokens: 262144 - input_price: 0.08 - output_price: 0.50 - supports_vision: true - - name: qwen/qwen3-coder-next - max_input_tokens: 262144 - input_price: 0.12 - output_price: 0.75 - supports_function_calling: true - - name: qwen/qwen3-coder-plus - max_input_tokens: 128000 - input_price: 1 - output_price: 5 - supports_function_calling: true - - name: qwen/qwen3-coder-flash - max_input_tokens: 128000 - input_price: 0.3 - output_price: 1.5 - supports_function_calling: true - - name: qwen/qwen3-coder # Qwen3 Coder 480B A35B - max_input_tokens: 262144 - input_price: 0.22 - output_price: 0.95 - supports_function_calling: true - - name: qwen/qwen3-coder-30b-a3b-instruct - max_input_tokens: 262144 - input_price: 0.052 - output_price: 0.207 - supports_function_calling: true - - name: moonshotai/kimi-k2.5 - max_input_tokens: 262144 - input_price: 0.57 - output_price: 2.85 - supports_vision: true - supports_function_calling: true - - name: moonshotai/kimi-k2-0905 - max_input_tokens: 262144 - input_price: 0.296 - output_price: 1.185 - supports_vision: true - supports_function_calling: true - - name: moonshotai/kimi-k2-thinking - max_input_tokens: 262144 - input_price: 0.45 - output_price: 2.35 - supports_function_calling: true - - name: x-ai/grok-4.1-fast - max_input_tokens: 2000000 - input_price: 0.2 - output_price: 0.5 - supports_function_calling: true - - name: x-ai/grok-code-fast-1 - max_input_tokens: 256000 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - - name: amazon/nova-premier-v1 - max_input_tokens: 1000000 - input_price: 2.5 - output_price: 12.5 - supports_vision: true - - name: amazon/nova-pro-v1 - max_input_tokens: 300000 - max_output_tokens: 5120 - input_price: 0.8 - output_price: 3.2 - supports_vision: true - - name: amazon/nova-lite-v1 - max_input_tokens: 300000 - max_output_tokens: 5120 - input_price: 0.06 - output_price: 0.24 - supports_vision: true - - name: amazon/nova-micro-v1 - max_input_tokens: 128000 - max_output_tokens: 5120 - input_price: 0.035 - output_price: 0.14 - - name: perplexity/sonar-pro - max_input_tokens: 200000 - input_price: 3 - output_price: 15 - - name: perplexity/sonar - max_input_tokens: 127072 - input_price: 1 - output_price: 1 - - name: perplexity/sonar-reasoning-pro - max_input_tokens: 128000 - input_price: 2 - output_price: 8 - patch: - body: - include_reasoning: true - - name: perplexity/sonar-deep-research - max_input_tokens: 200000 - input_price: 2 - output_price: 8 - patch: - body: - include_reasoning: true - - name: minimax/minimax-m2.5 - max_input_tokens: 196608 - input_price: 0.3 - output_price: 1.1 - supports_function_calling: true - - name: minimax/minimax-m2.1 - max_input_tokens: 196608 - input_price: 0.12 - output_price: 0.48 - supports_function_calling: true - - name: z-ai/glm-5 - max_input_tokens: 204800 - input_price: 0.95 - output_price: 2.55 - supports_function_calling: true - - name: z-ai/glm-4.7 - max_input_tokens: 202752 - input_price: 0.16 - output_price: 0.80 - supports_function_calling: true - - name: z-ai/glm-4.7-flash - max_input_tokens: 202752 - input_price: 0.07 - output_price: 0.40 - supports_function_calling: true - - name: z-ai/glm-4.6v - max_input_tokens: 131072 - input_price: 0.3 - output_price: 0.9 - supports_vision: true - -# Links: -# - https://github.com/marketplace?type=models + - name: google/gemini-3-pro-preview + type: chat + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + default_reasoning_effort: high + - name: google/gemini-2.0-flash-001 + type: chat + max_input_tokens: 1000000 + input_price: 0.15 + output_price: 0.6 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.0-flash-lite-001 + type: chat + max_input_tokens: 1048576 + input_price: 0.075 + output_price: 0.3 + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: mistralai/devstral-small + type: chat + max_input_tokens: 131072 + input_price: 0.07 + output_price: 0.28 + supports_function_calling: true + - name: ai21/jamba-large-1.7 + type: chat + max_input_tokens: 256000 + input_price: 2.0 + output_price: 8.0 + supports_function_calling: true + - name: x-ai/grok-4.1-fast + type: chat + max_input_tokens: 2000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: x-ai/grok-code-fast-1 + type: chat + max_input_tokens: 256000 + input_price: 0.2 + output_price: 1.5 + supports_function_calling: true + - name: aion-labs/aion-2.0 + type: chat + max_input_tokens: 131072 + input_price: 0.8 + output_price: 1.6 + max_output_tokens: 32768 + supports_function_calling: true + - name: aion-labs/aion-3.0 + type: chat + max_input_tokens: 131072 + input_price: 3.0 + output_price: 6.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: aion-labs/aion-3.0-mini + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 1.4 + max_output_tokens: 32768 + supports_function_calling: true + - name: aion-labs/aion-rp-llama-3.1-8b + type: chat + max_input_tokens: 32768 + input_price: 0.8 + output_price: 1.6 + max_output_tokens: 29491 + - name: amazon/nova-2-lite-v1 + type: chat + max_input_tokens: 1000000 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + - name: amazon/nova-lite-v1 + type: chat + max_input_tokens: 300000 + input_price: 0.06 + output_price: 0.24 + max_output_tokens: 5120 + supports_vision: true + supports_function_calling: true + - name: amazon/nova-micro-v1 + type: chat + max_input_tokens: 128000 + input_price: 0.035 + output_price: 0.14 + max_output_tokens: 5120 + supports_function_calling: true + - name: amazon/nova-premier-v1 + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 32000 + supports_vision: true + supports_function_calling: true + - name: amazon/nova-pro-v1 + type: chat + max_input_tokens: 300000 + input_price: 0.8 + output_price: 3.2 + max_output_tokens: 5120 + supports_vision: true + supports_function_calling: true + - name: anthracite-org/magnum-v4-72b + type: chat + max_input_tokens: 32768 + input_price: 2.5 + output_price: 5.0 + max_output_tokens: 4096 + - name: anthropic/claude-3-haiku + type: chat + max_input_tokens: 200000 + input_price: 0.25 + output_price: 1.25 + max_output_tokens: 4096 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-fable-5.1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-fable-5.1:batch + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-fable-5:batch + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-haiku-4.5 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-haiku-4.5:batch + type: chat + max_input_tokens: 200000 + input_price: 0.5 + output_price: 2.5 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.1 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.1:batch + type: chat + max_input_tokens: 200000 + input_price: 7.5 + output_price: 37.5 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.5 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.5:batch + type: chat + max_input_tokens: 200000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.6 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: anthropic/claude-opus-4.6:batch + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.7:batch + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.8:batch + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-opus-5:batch + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-sonnet-4 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-sonnet-4.5 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-sonnet-4.5:batch + type: chat + max_input_tokens: 1000000 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-sonnet-4.6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: anthropic/claude-sonnet-4.6:batch + type: chat + max_input_tokens: 1000000 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-sonnet-5:batch + type: chat + max_input_tokens: 1000000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: arcee-ai/trinity-large-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 0.8 + max_output_tokens: 80000 + supports_function_calling: true + - name: baidu/ernie-4.5-vl-424b-a47b + type: chat + max_input_tokens: 123000 + input_price: 0.42 + output_price: 1.25 + max_output_tokens: 16000 + supports_vision: true + - name: bytedance-seed/seed-1.6 + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: bytedance-seed/seed-1.6-flash + type: chat + max_input_tokens: 262144 + input_price: 0.075 + output_price: 0.3 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: bytedance-seed/seed-2-1-turbo + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 2.5 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: bytedance-seed/seed-2.0-code + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: bytedance-seed/seed-2.0-lite + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: bytedance-seed/seed-2.0-mini + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: bytedance/ui-tars-1.5-7b + type: chat + max_input_tokens: 128000 + input_price: 0.1 + output_price: 0.2 + max_output_tokens: 2048 + supports_vision: true + - name: cognitivecomputations/dolphin-mistral-24b-venice-edition + type: chat + max_input_tokens: 128000 + input_price: 0.2 + output_price: 0.9 + max_output_tokens: 8192 + - name: cohere/command-a + type: chat + max_input_tokens: 256000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 8192 + - name: cohere/command-r-08-2024 + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 4000 + supports_function_calling: true + - name: cohere/command-r-plus-08-2024 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 4000 + supports_function_calling: true + - name: cohere/command-r7b-12-2024 + type: chat + max_input_tokens: 128000 + input_price: 0.0375 + output_price: 0.15 + max_output_tokens: 4000 + - name: cohere/north-mini-code:free + type: chat + max_input_tokens: 256000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 64000 + supports_function_calling: true + - name: deepseek/deepseek-chat + type: chat + max_input_tokens: 163840 + input_price: 0.2574 + output_price: 1.0287 + max_output_tokens: 16000 + supports_function_calling: true + - name: deepseek/deepseek-chat-v3-0324 + type: chat + max_input_tokens: 163840 + input_price: 0.25 + output_price: 1.0 + max_output_tokens: 147456 + supports_function_calling: true + - name: deepseek/deepseek-chat-v3.1 + type: chat + max_input_tokens: 163840 + input_price: 0.25 + output_price: 0.95 + max_output_tokens: 32768 + supports_function_calling: true + - name: deepseek/deepseek-r1 + type: chat + max_input_tokens: 64000 + input_price: 0.7 + output_price: 2.5 + max_output_tokens: 16000 + supports_function_calling: true + - name: deepseek/deepseek-r1-0528 + type: chat + max_input_tokens: 163840 + input_price: 0.5 + output_price: 2.15 + max_output_tokens: 32768 + supports_function_calling: true + - name: deepseek/deepseek-r1-distill-llama-70b + type: chat + max_input_tokens: 8192 + input_price: 0.8 + output_price: 0.8 + max_output_tokens: 7372 + - name: deepseek/deepseek-v3.1-terminus + type: chat + max_input_tokens: 163840 + input_price: 0.27 + output_price: 1.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: deepseek/deepseek-v3.2 + type: chat + max_input_tokens: 163840 + input_price: 0.269 + output_price: 0.4 + max_output_tokens: 65536 + supports_function_calling: true + - name: deepseek/deepseek-v3.2-exp + type: chat + max_input_tokens: 163840 + input_price: 0.27 + output_price: 0.41 + max_output_tokens: 65536 + supports_function_calling: true + - name: deepseek/deepseek-v4-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.0825 + output_price: 0.1649 + max_output_tokens: 384000 + supports_function_calling: true + - name: deepseek/deepseek-v4-flash-0731 + type: chat + max_input_tokens: 1310720 + input_price: 0.065 + output_price: 0.18 + max_output_tokens: 943718 + supports_function_calling: true + - name: deepseek/deepseek-v4-flash-0731:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.14 + output_price: 0.28 + max_output_tokens: 943718 + supports_function_calling: true + - name: deepseek/deepseek-v4-flash-vision-exp + type: chat + max_input_tokens: 1048576 + input_price: 0.22 + output_price: 0.66 + max_output_tokens: 384000 + supports_vision: true + supports_function_calling: true + - name: deepseek/deepseek-v4-pro + type: chat + max_input_tokens: 1048576 + input_price: 1.0308 + output_price: 2.0616 + max_output_tokens: 384000 + supports_function_calling: true + - name: deepseek/deepseek-v4-pro-0813 + type: chat + max_input_tokens: 1048576 + input_price: 0.66 + output_price: 1.98 + max_output_tokens: 384000 + supports_function_calling: true + - name: deepseek/deepseek-v4-pro-0813:batch + type: chat + max_input_tokens: 1048576 + input_price: 1.32 + output_price: 3.96 + max_output_tokens: 943718 + supports_function_calling: true + - name: dots-studio/dots-3-note-preview:free + type: chat + max_input_tokens: 512000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 460800 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: low + - name: google/gemini-2.5-flash-image + type: chat + max_input_tokens: 32768 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 8192 + supports_vision: true + - name: google/gemini-2.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-flash-lite:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-flash:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.15 + output_price: 1.25 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-pro + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high + - name: google/gemini-2.5-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-pro-preview-05-06 + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-pro:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.625 + output_price: 5.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3-flash-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: high + - name: google/gemini-3-flash-preview:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3-pro-image + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3-pro-image-preview + type: chat + max_input_tokens: 65536 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 32768 + supports_vision: true + - name: google/gemini-3.1-flash-image + type: chat + max_input_tokens: 131072 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 32768 + supports_vision: true + - name: google/gemini-3.1-flash-image-preview + type: chat + max_input_tokens: 65536 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 58982 + supports_vision: true + - name: google/gemini-3.1-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: minimal + - name: google/gemini-3.1-flash-lite-image + type: chat + max_input_tokens: 65536 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 58982 + supports_vision: true + - name: google/gemini-3.1-flash-lite-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.1-flash-lite:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.125 + output_price: 0.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.1-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high + - name: google/gemini-3.1-pro-preview-customtools + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.1-pro-preview:batch + type: chat + max_input_tokens: 1048576 + input_price: 1.0 + output_price: 6.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 1.5 + output_price: 9.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: google/gemini-3.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.5-flash-lite:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.15 + output_price: 1.25 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.5-flash:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 4.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.6-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.6-flash:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.375 + output_price: 1.875 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.7-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.7-flash:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.375 + output_price: 1.875 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.8-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.8-flash:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.375 + output_price: 1.875 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemma-2-27b-it + type: chat + max_input_tokens: 8192 + input_price: 0.65 + output_price: 0.65 + max_output_tokens: 2048 + - name: google/gemma-3-12b-it + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.15 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: google/gemma-3-27b-it + type: chat + max_input_tokens: 131072 + input_price: 0.08 + output_price: 0.45 + max_output_tokens: 117964 + supports_vision: true + supports_function_calling: true + - name: google/gemma-3-4b-it + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.1 + max_output_tokens: 16384 + supports_vision: true + - name: google/gemma-4-26b-a4b-it + type: chat + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.34 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-26b-a4b-it:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-31b-it + type: chat + max_input_tokens: 262144 + input_price: 0.09 + output_price: 0.34 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-31b-it:batch + type: chat + max_input_tokens: 262144 + input_price: 0.39 + output_price: 0.97 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-31b-it:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: google/lyria-3-clip-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_vision: true + - name: google/lyria-3-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_vision: true + - name: gryphe/mythomax-l2-13b + type: chat + max_input_tokens: 8192 + input_price: 0.06 + output_price: 0.06 + max_output_tokens: 3686 + - name: ibm-granite/granite-4.0-h-micro + type: chat + max_input_tokens: 131000 + input_price: 0.017 + output_price: 0.112 + max_output_tokens: 117900 + - name: ibm-granite/granite-4.1-8b + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.1 + max_output_tokens: 117964 + supports_function_calling: true + - name: ibm-granite/granite-4.2-8b + type: chat + max_input_tokens: 131072 + input_price: 0.1 + output_price: 0.15 + max_output_tokens: 117964 + supports_function_calling: true + - name: inception/mercury-2 + type: chat + max_input_tokens: 128000 + input_price: 0.25 + output_price: 0.75 + max_output_tokens: 50000 + supports_function_calling: true + - name: inception/mercury-2.5-preview + type: chat + max_input_tokens: 260000 + input_price: 0.04 + output_price: 0.15 + max_output_tokens: 65536 + supports_function_calling: true + - name: inclusionai/ling-3.0-flash + type: chat + max_input_tokens: 262144 + input_price: 0.021 + output_price: 0.063 + max_output_tokens: 32768 + supports_function_calling: true + - name: inclusionai/ling-3.0-flash-fin:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: kwaipilot/kat-coder-pro-v2 + type: chat + max_input_tokens: 262144 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 144000 + supports_function_calling: true + - name: kwaipilot/kat-coder-pro-v2.5 + type: chat + max_input_tokens: 262144 + input_price: 0.74 + output_price: 2.96 + max_output_tokens: 235929 + supports_function_calling: true + - name: liquid/lfm-2.5-2.6b:free + type: chat + max_input_tokens: 65536 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 8192 + supports_function_calling: true + - name: mancer/weaver + type: chat + max_input_tokens: 8000 + input_price: 0.4 + output_price: 0.75 + max_output_tokens: 6000 + - name: meituan/longcat-2.0 + type: chat + max_input_tokens: 1048756 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 262144 + supports_function_calling: true + - name: meta-llama/llama-3.1-70b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 0.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: meta-llama/llama-3.1-8b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.08 + max_output_tokens: 117964 + supports_function_calling: true + - name: meta-llama/llama-3.2-1b-instruct + type: chat + max_input_tokens: 60000 + input_price: 0.027 + output_price: 0.201 + max_output_tokens: 54000 + - name: meta-llama/llama-3.2-3b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.33 + max_output_tokens: 117964 + - name: meta-llama/llama-3.3-70b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.71 + output_price: 0.71 + max_output_tokens: 115200 + supports_function_calling: true + - name: meta-llama/llama-4-maverick + type: chat + max_input_tokens: 1048576 + input_price: 0.2 + output_price: 0.696 + max_output_tokens: 115200 + supports_vision: true + supports_function_calling: true + - name: meta-llama/llama-4-scout + type: chat + max_input_tokens: 1310720 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: meta-llama/llama-guard-4-12b + type: chat + max_input_tokens: 163840 + input_price: 0.18 + output_price: 0.18 + max_output_tokens: 16384 + supports_vision: true + - name: meta/muse-glimmer-30b + type: chat + max_input_tokens: 131072 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: meta/muse-glimmer-30b:batch + type: chat + max_input_tokens: 131072 + input_price: 0.35 + output_price: 1.5 + max_output_tokens: 117964 + supports_vision: true + supports_function_calling: true + - name: meta/muse-spark-1.1 + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 4.25 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: meta/muse-spark-1.2 + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 4.25 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: meta/muse-spark-1.2-contributor + type: chat + max_input_tokens: 1048576 + input_price: 0.1 + output_price: 0.2 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: microsoft/phi-4 + type: chat + max_input_tokens: 16384 + input_price: 0.07 + output_price: 0.14 + max_output_tokens: 14745 + - name: microsoft/wizardlm-2-8x22b + type: chat + max_input_tokens: 65535 + input_price: 0.62 + output_price: 0.62 + max_output_tokens: 8000 + - name: minimax/minimax-01 + type: chat + max_input_tokens: 1000192 + input_price: 0.2 + output_price: 1.1 + max_output_tokens: 900172 + supports_vision: true + - name: minimax/minimax-m1 + type: chat + max_input_tokens: 1000000 + input_price: 0.55 + output_price: 2.2 + max_output_tokens: 40000 + supports_function_calling: true + - name: minimax/minimax-m2 + type: chat + max_input_tokens: 204800 + input_price: 0.255 + output_price: 1.02 + max_output_tokens: 131072 + supports_function_calling: true + - name: minimax/minimax-m2-her + type: chat + max_input_tokens: 65536 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 2048 + - name: minimax/minimax-m2.1 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: minimax/minimax-m2.5 + type: chat + max_input_tokens: 204800 + input_price: 0.27 + output_price: 1.08 + max_output_tokens: 128000 + supports_function_calling: true + - name: minimax/minimax-m2.7 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: minimax/minimax-m2.7:free + type: chat + max_input_tokens: 196608 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 176947 + supports_function_calling: true + - name: minimax/minimax-m3 + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 512000 + supports_vision: true + supports_function_calling: true + - name: minimax/minimax-m3:batch + type: chat + max_input_tokens: 524288 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 471859 + supports_vision: true + supports_function_calling: true + - name: minimax/minimax-m3:free + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: mistralai/codestral-2508 + type: chat + max_input_tokens: 256000 + input_price: 0.3 + output_price: 0.9 + max_output_tokens: 204800 + supports_function_calling: true + - name: mistralai/devstral-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 209715 + supports_function_calling: true + - name: mistralai/ministral-14b-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 0.2 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/ministral-3b-2512 + type: chat + max_input_tokens: 131072 + input_price: 0.1 + output_price: 0.1 + max_output_tokens: 104857 + supports_vision: true + supports_function_calling: true + - name: mistralai/ministral-8b-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 0.15 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-large + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 102400 + supports_function_calling: true + - name: mistralai/mistral-large-2407 + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 104857 + supports_function_calling: true + - name: mistralai/mistral-large-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-medium-3 + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 104857 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-medium-3-5 + type: chat + max_input_tokens: 262144 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-medium-3-5:batch + type: chat + max_input_tokens: 262144 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-medium-3.1 + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 104857 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-nemo + type: chat + max_input_tokens: 131072 + input_price: 0.019 + output_price: 0.03 + max_output_tokens: 16384 + supports_function_calling: true + - name: mistralai/mistral-saba + type: chat + max_input_tokens: 32768 + input_price: 0.2 + output_price: 0.6 + max_output_tokens: 26214 + supports_function_calling: true + - name: mistralai/mistral-small-24b-instruct-2501 + type: chat + max_input_tokens: 32768 + input_price: 0.05 + output_price: 0.08 + max_output_tokens: 16384 + - name: mistralai/mistral-small-2603 + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-small-3.1-24b-instruct + type: chat + max_input_tokens: 128000 + input_price: 0.351 + output_price: 0.555 + max_output_tokens: 102400 + supports_vision: true + - name: mistralai/mistral-small-3.2-24b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.075 + output_price: 0.2 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: mistralai/mixtral-8x22b-instruct + type: chat + max_input_tokens: 65536 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 52428 + supports_function_calling: true + - name: mistralai/voxtral-small-24b-2507 + type: chat + max_input_tokens: 32768 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 26214 + supports_function_calling: true + - name: moonshotai/kimi-k2 + type: chat + max_input_tokens: 131072 + input_price: 0.57 + output_price: 2.3 + max_output_tokens: 100352 + supports_function_calling: true + - name: moonshotai/kimi-k2-0905 + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 100352 + supports_function_calling: true + - name: moonshotai/kimi-k2-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 100352 + supports_function_calling: true + - name: moonshotai/kimi-k2.5 + type: chat + max_input_tokens: 262144 + input_price: 0.45 + output_price: 2.25 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k2.6 + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k2.7-code + type: chat + max_input_tokens: 262144 + input_price: 0.66 + output_price: 3.4 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k3 + type: chat + max_input_tokens: 1048576 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k3:batch + type: chat + max_input_tokens: 1048576 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: morph/morph-v3-fast + type: chat + max_input_tokens: 81920 + input_price: 0.8 + output_price: 1.2 + max_output_tokens: 38000 + - name: morph/morph-v3-large + type: chat + max_input_tokens: 262144 + input_price: 0.9 + output_price: 1.9 + max_output_tokens: 131072 + - name: nex-agi/nex-n2-mini + type: chat + max_input_tokens: 262144 + input_price: 0.025 + output_price: 0.1 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: nex-agi/nex-n2-pro + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 1.0 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: nousresearch/hermes-3-llama-3.1-405b + type: chat + max_input_tokens: 131072 + input_price: 1.0 + output_price: 1.0 + max_output_tokens: 16384 + - name: nousresearch/hermes-3-llama-3.1-70b + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 0.7 + max_output_tokens: 16384 + - name: nousresearch/hermes-4-405b + type: chat + max_input_tokens: 131072 + input_price: 1.0 + output_price: 3.0 + max_output_tokens: 117964 + - name: nousresearch/hermes-4-70b + type: chat + max_input_tokens: 131072 + input_price: 0.13 + output_price: 0.4 + max_output_tokens: 117964 + - name: nvidia/nemotron-3-nano-30b-a3b + type: chat + max_input_tokens: 262144 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 228000 + supports_function_calling: true + - name: nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free + type: chat + max_input_tokens: 256000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: nvidia/nemotron-3-super-120b-a12b + type: chat + max_input_tokens: 1000000 + input_price: 0.085 + output_price: 0.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: nvidia/nemotron-3-super-120b-a12b:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 235929 + supports_function_calling: true + - name: nvidia/nemotron-3-ultra-550b-a55b + type: chat + max_input_tokens: 262144 + input_price: 0.625 + output_price: 3.125 + max_output_tokens: 32768 + supports_function_calling: true + - name: nvidia/nemotron-3-ultra-550b-a55b:batch + type: chat + max_input_tokens: 512288 + input_price: 0.6 + output_price: 3.6 + max_output_tokens: 461059 + supports_function_calling: true + - name: nvidia/nemotron-3-ultra-550b-a55b:free + type: chat + max_input_tokens: 1000000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: nvidia/nemotron-3.5-content-safety:free + type: chat + max_input_tokens: 128000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 8192 + supports_vision: true + - name: nvidia/nemotron-3.5-lightning + type: chat + max_input_tokens: 262144 + input_price: 0.08 + output_price: 0.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: nvidia/nemotron-3.5-lightning:free + type: chat + max_input_tokens: 1000000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: openai/gpt-3.5-turbo + type: chat + max_input_tokens: 16385 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 4096 + supports_function_calling: true + - name: openai/gpt-3.5-turbo-0613 + type: chat + max_input_tokens: 4095 + input_price: 1.0 + output_price: 2.0 + max_output_tokens: 3685 + supports_function_calling: true + - name: openai/gpt-3.5-turbo-16k + type: chat + max_input_tokens: 16385 + input_price: 3.0 + output_price: 4.0 + max_output_tokens: 4096 + supports_function_calling: true + - name: openai/gpt-3.5-turbo-instruct + type: chat + max_input_tokens: 4095 + input_price: 1.5 + output_price: 2.0 + max_output_tokens: 3685 + - name: openai/gpt-3.5-turbo:batch + type: chat + max_input_tokens: 16385 + input_price: 0.25 + output_price: 0.75 + max_output_tokens: 4096 + supports_function_calling: true + - name: openai/gpt-4 + type: chat + max_input_tokens: 8191 + input_price: 30.0 + output_price: 60.0 + max_output_tokens: 4096 + supports_function_calling: true + - name: openai/gpt-4-turbo + type: chat + max_input_tokens: 128000 + input_price: 10.0 + output_price: 30.0 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4-turbo-preview + type: chat + max_input_tokens: 128000 + input_price: 10.0 + output_price: 30.0 + max_output_tokens: 4096 + supports_function_calling: true + - name: openai/gpt-4-turbo:batch + type: chat + max_input_tokens: 128000 + input_price: 5.0 + output_price: 15.0 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1 + type: chat + max_input_tokens: 1047576 + input_price: 2.0 + output_price: 8.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1-mini + type: chat + max_input_tokens: 1047576 + input_price: 0.4 + output_price: 1.6 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1-mini:batch + type: chat + max_input_tokens: 1047576 + input_price: 0.2 + output_price: 0.8 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1-nano + type: chat + max_input_tokens: 1047576 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1-nano:batch + type: chat + max_input_tokens: 1047576 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1:batch + type: chat + max_input_tokens: 1047576 + input_price: 1.0 + output_price: 4.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-2024-05-13 + type: chat + max_input_tokens: 128000 + input_price: 5.0 + output_price: 15.0 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-2024-08-06 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-2024-11-20 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-mini + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-mini-2024-07-18 + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-mini:batch + type: chat + max_input_tokens: 128000 + input_price: 0.075 + output_price: 0.3 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o:batch + type: chat + max_input_tokens: 128000 + input_price: 1.25 + output_price: 5.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5 + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: openai/gpt-5-image + type: chat + max_input_tokens: 400000 + input_price: 10.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + - name: openai/gpt-5-image-mini + type: chat + max_input_tokens: 400000 + input_price: 2.5 + output_price: 2.0 + max_output_tokens: 128000 + supports_vision: true + - name: openai/gpt-5-mini + type: chat + max_input_tokens: 400000 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5-mini:batch + type: chat + max_input_tokens: 400000 + input_price: 0.125 + output_price: 1.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5-nano + type: chat + max_input_tokens: 400000 + input_price: 0.05 + output_price: 0.4 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5-nano:batch + type: chat + max_input_tokens: 400000 + input_price: 0.025 + output_price: 0.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5-pro + type: chat + max_input_tokens: 400000 + input_price: 15.0 + output_price: 120.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5-pro:batch + type: chat + max_input_tokens: 400000 + input_price: 7.5 + output_price: 60.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.1 + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.1-codex + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.1-codex-max + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.1-codex-mini + type: chat + max_input_tokens: 400000 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.1:batch + type: chat + max_input_tokens: 400000 + input_price: 0.625 + output_price: 5.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.2 + type: chat + max_input_tokens: 400000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: openai/gpt-5.2-chat + type: chat + max_input_tokens: 128000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 32000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.2-codex + type: chat + max_input_tokens: 400000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.2-pro + type: chat + max_input_tokens: 400000 + input_price: 21.0 + output_price: 168.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: openai/gpt-5.2-pro:batch + type: chat + max_input_tokens: 400000 + input_price: 10.5 + output_price: 84.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.2:batch + type: chat + max_input_tokens: 400000 + input_price: 0.875 + output_price: 7.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.3-codex + type: chat + max_input_tokens: 400000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: openai/gpt-5.4 + type: chat + max_input_tokens: 1050000 + input_price: 2.5 + output_price: 15.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: openai/gpt-5.4-image-2 + type: chat + max_input_tokens: 272000 + input_price: 8.0 + output_price: 15.0 + max_output_tokens: 128000 + supports_vision: true + - name: openai/gpt-5.4-mini + type: chat + max_input_tokens: 400000 + input_price: 0.75 + output_price: 4.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: openai/gpt-5.4-mini:batch + type: chat + max_input_tokens: 400000 + input_price: 0.375 + output_price: 2.25 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.4-nano + type: chat + max_input_tokens: 400000 + input_price: 0.2 + output_price: 1.25 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: openai/gpt-5.4-nano:batch + type: chat + max_input_tokens: 400000 + input_price: 0.1 + output_price: 0.625 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.4-pro + type: chat + max_input_tokens: 1050000 + input_price: 30.0 + output_price: 180.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: openai/gpt-5.4-pro:batch + type: chat + max_input_tokens: 1050000 + input_price: 15.0 + output_price: 90.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.4:batch + type: chat + max_input_tokens: 1050000 + input_price: 1.25 + output_price: 7.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.5 + type: chat + max_input_tokens: 1050000 + input_price: 5.0 + output_price: 30.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: openai/gpt-5.5-pro + type: chat + max_input_tokens: 1050000 + input_price: 30.0 + output_price: 180.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: high + - name: openai/gpt-5.5-pro:batch + type: chat + max_input_tokens: 1050000 + input_price: 15.0 + output_price: 90.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.5:batch + type: chat + max_input_tokens: 1050000 + input_price: 2.5 + output_price: 15.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.6-luna + type: chat + max_input_tokens: 1050000 + input_price: 0.2 + output_price: 1.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-luna-pro + type: chat + max_input_tokens: 1050000 + input_price: 0.2 + output_price: 1.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-luna-pro:batch + type: chat + max_input_tokens: 1050000 + input_price: 0.1 + output_price: 0.6 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-luna:batch + type: chat + max_input_tokens: 1050000 + input_price: 0.1 + output_price: 0.6 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-sol + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-sol-pro + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-sol-pro:batch + type: chat + max_input_tokens: 1050000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-sol:batch + type: chat + max_input_tokens: 1050000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-terra + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-terra-pro + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-terra-pro:batch + type: chat + max_input_tokens: 1050000 + input_price: 1.0 + output_price: 6.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-terra:batch + type: chat + max_input_tokens: 1050000 + input_price: 1.0 + output_price: 6.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5:batch + type: chat + max_input_tokens: 400000 + input_price: 0.625 + output_price: 5.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-audio + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_function_calling: true + - name: openai/gpt-audio-mini + type: chat + max_input_tokens: 128000 + input_price: 0.6 + output_price: 2.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: openai/gpt-chat-latest + type: chat + max_input_tokens: 400000 + input_price: 5.0 + output_price: 30.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-oss-120b + type: chat + max_input_tokens: 131072 + input_price: 0.037 + output_price: 0.17 + max_output_tokens: 117964 + supports_function_calling: true + - name: openai/gpt-oss-120b:batch + type: chat + max_input_tokens: 131072 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 117964 + supports_function_calling: true + - name: openai/gpt-oss-20b + type: chat + max_input_tokens: 131072 + input_price: 0.03 + output_price: 0.13 + max_output_tokens: 117964 + supports_function_calling: true + - name: openai/gpt-oss-20b:batch + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 117964 + - name: openai/gpt-oss-safeguard-20b + type: chat + max_input_tokens: 131072 + input_price: 0.075 + output_price: 0.3 + max_output_tokens: 65536 + supports_function_calling: true + - name: openai/o1 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 60.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o1-pro + type: chat + max_input_tokens: 200000 + input_price: 150.0 + output_price: 600.0 + max_output_tokens: 100000 + supports_vision: true + - name: openai/o3 + type: chat + max_input_tokens: 200000 + input_price: 2.0 + output_price: 8.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o3-mini + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + max_output_tokens: 100000 + supports_function_calling: true + - name: openai/o3-mini-high + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + max_output_tokens: 100000 + supports_function_calling: true + - name: openai/o3-mini:batch + type: chat + max_input_tokens: 200000 + input_price: 0.55 + output_price: 2.2 + max_output_tokens: 100000 + supports_function_calling: true + - name: openai/o3-pro + type: chat + max_input_tokens: 200000 + input_price: 20.0 + output_price: 80.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o3:batch + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 4.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o4-mini + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o4-mini-high + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o4-mini:batch + type: chat + max_input_tokens: 200000 + input_price: 0.55 + output_price: 2.2 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openrouter/auto + type: chat + max_input_tokens: 2000000 + supports_vision: true + supports_function_calling: true + - name: openrouter/auto-beta + type: chat + max_input_tokens: 2000000 + supports_vision: true + supports_function_calling: true + - name: openrouter/bodybuilder + type: chat + max_input_tokens: 128000 + - name: openrouter/free + type: chat + max_input_tokens: 200000 + input_price: 0.0 + output_price: 0.0 + supports_vision: true + supports_function_calling: true + - name: openrouter/fusion + type: chat + max_input_tokens: 1000000 + - name: openrouter/pareto-code + type: chat + max_input_tokens: 2000000 + - name: perceptron/perceptron-mk1 + type: chat + max_input_tokens: 32768 + input_price: 0.15 + output_price: 1.5 + max_output_tokens: 8192 + supports_vision: true + - name: perplexity/sonar + type: chat + max_input_tokens: 127072 + input_price: 1.0 + output_price: 1.0 + max_output_tokens: 114364 + supports_vision: true + - name: perplexity/sonar-deep-research + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 8.0 + patch: + body: + include_reasoning: true + max_output_tokens: 115200 + - name: perplexity/sonar-pro + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 8000 + supports_vision: true + - name: perplexity/sonar-pro-search + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 8000 + supports_vision: true + - name: perplexity/sonar-reasoning-pro + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 8.0 + patch: + body: + include_reasoning: true + max_output_tokens: 115200 + supports_vision: true + - name: poolside/laguna-s-2.1 + type: chat + max_input_tokens: 1048576 + input_price: 0.09 + output_price: 0.18 + max_output_tokens: 131072 + supports_function_calling: true + - name: poolside/laguna-s-2.1:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: poolside/laguna-xs-2.1 + type: chat + max_input_tokens: 262144 + input_price: 0.06 + output_price: 0.12 + max_output_tokens: 32768 + supports_function_calling: true + - name: poolside/laguna-xs-2.1:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen/qwen-2.5-72b-instruct + type: chat + max_input_tokens: 32768 + input_price: 0.36 + output_price: 0.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen/qwen-2.5-7b-instruct + type: chat + max_input_tokens: 32768 + input_price: 0.1 + output_price: 0.2 + max_output_tokens: 29491 + supports_function_calling: true + - name: qwen/qwen-2.5-coder-32b-instruct + type: chat + max_input_tokens: 32768 + input_price: 0.66 + output_price: 1.0 + max_output_tokens: 29491 + - name: qwen/qwen-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.26 + output_price: 0.78 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen/qwen-plus-2025-07-28 + type: chat + max_input_tokens: 1000000 + input_price: 0.26 + output_price: 0.78 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen/qwen2.5-vl-72b-instruct + type: chat + max_input_tokens: 128000 + input_price: 0.25 + output_price: 0.75 + max_output_tokens: 28800 + supports_vision: true + - name: qwen/qwen3-14b + type: chat + max_input_tokens: 131072 + input_price: 0.12 + output_price: 0.24 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen/qwen3-235b-a22b + type: chat + max_input_tokens: 131072 + input_price: 0.455 + output_price: 1.82 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen/qwen3-235b-a22b-2507 + type: chat + max_input_tokens: 262144 + input_price: 0.0875 + output_price: 0.35 + max_output_tokens: 235929 + supports_function_calling: true + - name: qwen/qwen3-235b-a22b-thinking-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.23 + output_price: 2.3 + max_output_tokens: 117964 + supports_function_calling: true + - name: qwen/qwen3-30b-a3b + type: chat + max_input_tokens: 131072 + input_price: 0.12 + output_price: 0.5 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen/qwen3-30b-a3b-instruct-2507 + type: chat + max_input_tokens: 262144 + input_price: 0.0482 + output_price: 0.1931 + max_output_tokens: 32000 + supports_function_calling: true + - name: qwen/qwen3-30b-a3b-thinking-2507 + type: chat + max_input_tokens: 81920 + input_price: 0.2 + output_price: 2.4 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen/qwen3-32b + type: chat + max_input_tokens: 131072 + input_price: 0.08 + output_price: 0.28 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen/qwen3-8b + type: chat + max_input_tokens: 131072 + input_price: 0.117 + output_price: 0.455 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen/qwen3-coder + type: chat + max_input_tokens: 262144 + input_price: 0.3 + output_price: 1.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3-coder-30b-a3b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.28 + max_output_tokens: 235929 + supports_function_calling: true + - name: qwen/qwen3-coder-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.195 + output_price: 0.975 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3-coder-next + type: chat + max_input_tokens: 262144 + input_price: 0.12 + output_price: 0.8 + max_output_tokens: 235929 + supports_function_calling: true + - name: qwen/qwen3-coder-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.65 + output_price: 3.25 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3-max + type: chat + max_input_tokens: 262144 + input_price: 0.78 + output_price: 3.9 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3-max-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.78 + output_price: 3.9 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3-next-80b-a3b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 1.1 + max_output_tokens: 235929 + supports_function_calling: true + - name: qwen/qwen3-next-80b-a3b-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 1.2 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen/qwen3-vl-235b-a22b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.21 + output_price: 1.9 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-235b-a22b-thinking + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 4.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-30b-a3b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-30b-a3b-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 2.4 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-32b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.104 + output_price: 0.416 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-8b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.117 + output_price: 0.455 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-8b-thinking + type: chat + max_input_tokens: 131072 + input_price: 0.18 + output_price: 2.1 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-122b-a10b + type: chat + max_input_tokens: 262144 + input_price: 0.29 + output_price: 2.4 + max_output_tokens: 81920 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-27b + type: chat + max_input_tokens: 262144 + input_price: 0.195 + output_price: 1.56 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-35b-a3b + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 1.25 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-397b-a17b + type: chat + max_input_tokens: 262144 + input_price: 0.39 + output_price: 2.34 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-9b + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.15 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-9b:batch + type: chat + max_input_tokens: 262144 + input_price: 0.17 + output_price: 0.25 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-flash-02-23 + type: chat + max_input_tokens: 1000000 + input_price: 0.065 + output_price: 0.26 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-plus-02-15 + type: chat + max_input_tokens: 1000000 + input_price: 0.26 + output_price: 1.56 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-plus-20260420 + type: chat + max_input_tokens: 1000000 + input_price: 0.3 + output_price: 1.8 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.6-27b + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 3.6 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.6-35b-a3b + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.9 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.6-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.1875 + output_price: 1.125 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.6-max-preview + type: chat + max_input_tokens: 262144 + input_price: 1.027 + output_price: 6.162 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3.6-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.325 + output_price: 1.95 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.7-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.03 + output_price: 0.13 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.7-max + type: chat + max_input_tokens: 1000000 + input_price: 1.475 + output_price: 4.425 + max_output_tokens: 131072 + supports_function_calling: true + - name: qwen/qwen3.7-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.32 + output_price: 1.28 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.8-2.4t-a95b + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 131072 + supports_function_calling: true + - name: qwen/qwen3.8-2.4t-a95b:batch + type: chat + max_input_tokens: 1010000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 909000 + supports_function_calling: true + - name: qwen/qwen3.8-27b + type: chat + max_input_tokens: 1000000 + input_price: 0.425 + output_price: 2.55 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.8-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.15 + output_price: 0.47 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.8-max + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: rekaai/reka-edge + type: chat + max_input_tokens: 16384 + input_price: 0.1 + output_price: 0.1 + max_output_tokens: 14745 + supports_vision: true + supports_function_calling: true + - name: rekaai/reka-flash-3 + type: chat + max_input_tokens: 65536 + input_price: 0.1 + output_price: 0.2 + max_output_tokens: 58982 + - name: relace/relace-apply-3 + type: chat + max_input_tokens: 256000 + input_price: 0.85 + output_price: 1.25 + max_output_tokens: 128000 + - name: relace/relace-search + type: chat + max_input_tokens: 256000 + input_price: 1.0 + output_price: 3.0 + max_output_tokens: 128000 + supports_function_calling: true + - name: sakana/fugu-ultra + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 30.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: sakana/sakana-namazu + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: sao10k/l3-lunaris-8b + type: chat + max_input_tokens: 8192 + input_price: 0.04 + output_price: 0.05 + max_output_tokens: 7372 + - name: sao10k/l3.1-euryale-70b + type: chat + max_input_tokens: 131072 + input_price: 0.85 + output_price: 0.85 + max_output_tokens: 16384 + supports_function_calling: true + - name: sao10k/l3.3-euryale-70b + type: chat + max_input_tokens: 131072 + input_price: 0.65 + output_price: 0.75 + max_output_tokens: 16384 + - name: stepfun/step-3.5-flash + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 65536 + supports_function_calling: true + - name: stepfun/step-3.7-flash + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 1.15 + max_output_tokens: 230400 + supports_vision: true + supports_function_calling: true + - name: tencent/hunyuan-a13b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.14 + output_price: 0.57 + max_output_tokens: 117964 + - name: tencent/hy-mt2-1.8b + type: chat + max_input_tokens: 8192 + input_price: 0.044 + output_price: 0.177 + max_output_tokens: 4096 + - name: tencent/hy-mt2-30b-a3b + type: chat + max_input_tokens: 8192 + input_price: 0.074 + output_price: 0.295 + max_output_tokens: 4096 + - name: tencent/hy-mt2-7b + type: chat + max_input_tokens: 8192 + input_price: 0.074 + output_price: 0.295 + max_output_tokens: 4096 + - name: tencent/hy3 + type: chat + max_input_tokens: 262144 + input_price: 0.0825 + output_price: 0.33 + max_output_tokens: 128000 + supports_function_calling: true + - name: tencent/hy3-preview + type: chat + max_input_tokens: 262144 + input_price: 0.18 + output_price: 0.6 + max_output_tokens: 235929 + supports_function_calling: true + - name: tencent/hy4-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.834 + output_price: 2.501 + max_output_tokens: 64000 + supports_function_calling: true + - name: thedrummer/cydonia-24b-v4.1 + type: chat + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.5 + max_output_tokens: 117964 + - name: thedrummer/skyfall-36b-v2 + type: chat + max_input_tokens: 32768 + input_price: 0.55 + output_price: 0.8 + max_output_tokens: 29491 + - name: thedrummer/unslopnemo-12b + type: chat + max_input_tokens: 1024000 + input_price: 0.4 + output_price: 0.4 + max_output_tokens: 26214 + supports_function_calling: true + - name: thinkingmachines/inkling + type: chat + max_input_tokens: 1048576 + input_price: 1.0 + output_price: 4.05 + max_output_tokens: 471859 + supports_vision: true + supports_function_calling: true + - name: thinkingmachines/inkling-small + type: chat + max_input_tokens: 1048576 + input_price: 0.45 + output_price: 1.2 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: thinkingmachines/inkling-small:batch + type: chat + max_input_tokens: 524288 + input_price: 0.5 + output_price: 1.2 + max_output_tokens: 471859 + supports_vision: true + supports_function_calling: true + - name: thinkingmachines/inkling-small:free + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: thinkingmachines/inkling:batch + type: chat + max_input_tokens: 524288 + input_price: 1.0 + output_price: 4.05 + max_output_tokens: 471859 + supports_vision: true + supports_function_calling: true + - name: thinkingmachines/inkling:free + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: undi95/remm-slerp-l2-13b + type: chat + max_input_tokens: 6144 + input_price: 0.45 + output_price: 0.65 + max_output_tokens: 4096 + - name: upstage/solar-pro-3 + type: chat + max_input_tokens: 131072 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 117964 + supports_function_calling: true + - name: upstage/solar-pro4 + type: chat + max_input_tokens: 524288 + input_price: 0.03 + output_price: 0.12 + max_output_tokens: 131072 + supports_function_calling: true + - name: writer/palmyra-x5 + type: chat + max_input_tokens: 1040000 + input_price: 0.6 + output_price: 6.0 + max_output_tokens: 8192 + - name: x-ai/grok-4.20 + type: chat + max_input_tokens: 2000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 1800000 + supports_vision: true + supports_function_calling: true + - name: x-ai/grok-4.20-multi-agent + type: chat + max_input_tokens: 2000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 1800000 + supports_vision: true + - name: x-ai/grok-4.3 + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 900000 + supports_vision: true + supports_function_calling: true + - name: x-ai/grok-4.5 + type: chat + max_input_tokens: 500000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 450000 + supports_vision: true + supports_function_calling: true + - name: x-ai/grok-4.6 + type: chat + max_input_tokens: 500000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 450000 + supports_vision: true + supports_function_calling: true + - name: x-ai/grok-build-0.1 + type: chat + max_input_tokens: 256000 + input_price: 1.0 + output_price: 2.0 + max_output_tokens: 230400 + supports_vision: true + supports_function_calling: true + - name: xiaomi/mimo-v2.5 + type: chat + max_input_tokens: 1050000 + input_price: 0.14 + output_price: 0.28 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: xiaomi/mimo-v2.5-pro + type: chat + max_input_tokens: 1050000 + input_price: 0.435 + output_price: 0.87 + max_output_tokens: 131072 + supports_function_calling: true + - name: z-ai/glm-4.5 + type: chat + max_input_tokens: 131072 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 98304 + supports_function_calling: true + - name: z-ai/glm-4.5-air + type: chat + max_input_tokens: 131072 + input_price: 0.13 + output_price: 0.85 + max_output_tokens: 98304 + supports_function_calling: true + - name: z-ai/glm-4.5v + type: chat + max_input_tokens: 65536 + input_price: 0.6 + output_price: 1.8 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: z-ai/glm-4.6 + type: chat + max_input_tokens: 204800 + input_price: 0.43 + output_price: 1.75 + max_output_tokens: 16384 + supports_function_calling: true + - name: z-ai/glm-4.6v + type: chat + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.9 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: z-ai/glm-4.7 + type: chat + max_input_tokens: 204800 + input_price: 0.4 + output_price: 1.75 + max_output_tokens: 131072 + supports_function_calling: true + - name: z-ai/glm-4.7-flash + type: chat + max_input_tokens: 202752 + input_price: 0.06 + output_price: 0.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: z-ai/glm-5 + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 1.92 + max_output_tokens: 128000 + supports_function_calling: true + - name: z-ai/glm-5-turbo + type: chat + max_input_tokens: 202752 + input_price: 1.2 + output_price: 4.0 + max_output_tokens: 131072 + supports_function_calling: true + - name: z-ai/glm-5.1 + type: chat + max_input_tokens: 204800 + input_price: 0.966 + output_price: 3.036 + max_output_tokens: 128000 + supports_function_calling: true + - name: z-ai/glm-5.2 + type: chat + max_input_tokens: 1048576 + input_price: 0.966 + output_price: 3.036 + max_output_tokens: 131072 + supports_function_calling: true + - name: z-ai/glm-5.2:free + type: chat + max_input_tokens: 256000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 230400 + supports_function_calling: true + - name: z-ai/glm-5.3 + type: chat + max_input_tokens: 1310720 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: z-ai/glm-5.3-flash + type: chat + max_input_tokens: 1310720 + input_price: 0.075 + output_price: 0.25 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: z-ai/glm-5.3-flash:batch + type: chat + max_input_tokens: 1048575 + input_price: 0.15 + output_price: 0.5 + max_output_tokens: 943717 + supports_vision: true + supports_function_calling: true + - name: z-ai/glm-5v-turbo + type: chat + max_input_tokens: 202752 + input_price: 1.2 + output_price: 4.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: ~anthropic/claude-fable-latest + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ~anthropic/claude-haiku-latest + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: ~anthropic/claude-opus-latest + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ~anthropic/claude-sonnet-latest + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ~deepseek/deepseek-v4-flash-latest + type: chat + max_input_tokens: 1310720 + input_price: 0.05 + output_price: 0.16 + max_output_tokens: 393216 + supports_function_calling: true + - name: ~google/gemini-flash-latest + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: ~google/gemini-pro-latest + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: ~moonshotai/kimi-latest + type: chat + max_input_tokens: 1048576 + input_price: 2.55 + output_price: 12.75 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: ~openai/gpt-latest + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ~openai/gpt-mini-latest + type: chat + max_input_tokens: 400000 + input_price: 0.75 + output_price: 4.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ~x-ai/grok-latest + type: chat + max_input_tokens: 500000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 450000 + supports_vision: true + supports_function_calling: true + - name: ~z-ai/glm-flash-latest + type: chat + max_input_tokens: 1310720 + input_price: 0.075 + output_price: 0.25 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: ~z-ai/glm-latest + type: chat + max_input_tokens: 1310720 + input_price: 1.15 + output_price: 3.5 + max_output_tokens: 235929 + supports_function_calling: true - provider: github models: - - name: gpt-5 - max_input_tokens: 400000 - max_output_tokens: 128000 - supports_vision: true - supports_function_calling: true - - name: gpt-5-mini - max_input_tokens: 400000 - max_output_tokens: 128000 - supports_vision: true - supports_function_calling: true - - name: gpt-5-nano - max_input_tokens: 400000 - max_output_tokens: 128000 - supports_vision: true - supports_function_calling: true - - name: gpt-4.1 - max_input_tokens: 1047576 - max_output_tokens: 32768 - supports_vision: true - supports_function_calling: true - - name: gpt-4o - max_input_tokens: 128000 - max_output_tokens: 16384 - supports_function_calling: true - - name: text-embedding-3-large - type: embedding - max_tokens_per_chunk: 8191 - default_chunk_size: 2000 - max_batch_size: 100 - - name: text-embedding-3-small - type: embedding - max_tokens_per_chunk: 8191 - default_chunk_size: 2000 - max_batch_size: 100 - - name: llama-4-maverick-17b-128e-instruct-fp8 - max_input_tokens: 1048576 - supports_vision: true - - name: llama-4-scout-17b-16e-instruct - max_input_tokens: 327680 - supports_vision: true - - name: llama-3.3-70b-instruct - max_input_tokens: 131072 - - name: mistral-medium-2505 - max_input_tokens: 131072 - supports_function_calling: true - - name: mistral-small-2503 - max_input_tokens: 131072 - supports_function_calling: true - - name: codestral-2501 - max_input_tokens: 256000 - supports_function_calling: true - - name: cohere-embed-v3-english - type: embedding - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: cohere-embed-v3-multilingual - type: embedding - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: deepseek-r1-0528 - max_input_tokens: 163840 - - name: deepseek-v3-0324 - max_input_tokens: 163840 - - name: mai-ds-r1 - max_input_tokens: 163840 - - name: phi-4 - max_input_tokens: 16384 - - name: phi-4-mini-instruct - max_input_tokens: 131072 - - name: phi-4-reasoning - max_input_tokens: 33792 - - name: phi-4-mini-reasoning - max_input_tokens: 131072 - - name: grok-3 - max_input_tokens: 131072 - - name: grok-3-mini - max_input_tokens: 131072 - -# Links: -# - https://deepinfra.com/models -# - https://deepinfra.com/docs/openai_api + - name: gpt-5 + type: chat + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-5-mini + type: chat + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-5-nano + type: chat + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-4.1 + type: chat + max_input_tokens: 1047576 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gpt-4o + type: chat + max_input_tokens: 128000 + max_output_tokens: 16384 + supports_function_calling: true + - name: text-embedding-3-large + type: embedding + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: text-embedding-3-small + type: embedding + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: llama-4-maverick-17b-128e-instruct-fp8 + type: chat + max_input_tokens: 1048576 + supports_vision: true + - name: llama-4-scout-17b-16e-instruct + type: chat + max_input_tokens: 327680 + supports_vision: true + - name: llama-3.3-70b-instruct + type: chat + max_input_tokens: 131072 + - name: mistral-medium-2505 + type: chat + max_input_tokens: 131072 + supports_function_calling: true + - name: mistral-small-2503 + type: chat + max_input_tokens: 131072 + supports_function_calling: true + - name: codestral-2501 + type: chat + max_input_tokens: 256000 + supports_function_calling: true + - name: cohere-embed-v3-english + type: embedding + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: cohere-embed-v3-multilingual + type: embedding + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: deepseek-r1-0528 + type: chat + max_input_tokens: 163840 + - name: deepseek-v3-0324 + type: chat + max_input_tokens: 163840 + - name: mai-ds-r1 + type: chat + max_input_tokens: 163840 + - name: phi-4 + type: chat + max_input_tokens: 16384 + - name: phi-4-mini-instruct + type: chat + max_input_tokens: 131072 + - name: phi-4-reasoning + type: chat + max_input_tokens: 33792 + - name: phi-4-mini-reasoning + type: chat + max_input_tokens: 131072 + - name: grok-3 + type: chat + max_input_tokens: 131072 + - name: grok-3-mini + type: chat + max_input_tokens: 131072 - provider: deepinfra models: - - name: openai/gpt-oss-120b - max_input_tokens: 131072 - input_price: 0.09 - output_price: 0.45 - supports_function_calling: true - - name: openai/gpt-oss-20b - max_input_tokens: 131072 - input_price: 0.04 - output_price: 0.16 - supports_function_calling: true - - name: meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8 - max_input_tokens: 1048576 - input_price: 0.18 - output_price: 0.6 - supports_vision: true - - name: meta-llama/Llama-4-Scout-17B-16E-Instruct - max_input_tokens: 327680 - input_price: 0.08 - output_price: 0.3 - supports_vision: true - - name: Qwen/Qwen3-Max - max_input_tokens: 262144 - input_price: 1.2 - output_price: 6 - supports_function_calling: true - - name: Qwen/Qwen3-Max-Thinking - max_input_tokens: 262144 - input_price: 1.2 - output_price: 6 - supports_function_calling: true - - name: Qwen/Qwen3-Next-80B-A3B-Instruct - max_input_tokens: 262144 - input_price: 0.14 - output_price: 1.4 - supports_function_calling: true - - name: Qwen/Qwen3-Next-80B-A3B-Thinking - max_input_tokens: 262144 - input_price: 0.14 - output_price: 1.4 - - name: Qwen/Qwen3-235B-A22B-Instruct-2507 - max_input_tokens: 131072 - input_price: 0.13 - output_price: 0.6 - supports_function_calling: true - - name: Qwen/Qwen3-235B-A22B-Thinking-2507 - max_input_tokens: 131072 - input_price: 0.13 - output_price: 0.6 - - name: Qwen/Qwen3-Coder-480B-A35B-Instruct - max_input_tokens: 131072 - input_price: 0.4 - output_price: 1.6 - supports_function_calling: true - - name: Qwen/Qwen3-Coder-30B-A3B-Instruct - max_input_tokens: 262144 - input_price: 0.07 - output_price: 0.27 - supports_function_calling: true - - name: Qwen/Qwen3-30B-A3B - max_input_tokens: 40960 - input_price: 0.1 - output_price: 0.3 - - name: Qwen/Qwen3-VL-8B-Instruct - max_input_tokens: 262144 - input_price: 0.18 - output_price: 0.69 - supports_vision: true - - name: deepseek-ai/DeepSeek-V3.2 - max_input_tokens: 163840 - input_price: 0.26 - output_price: 0.39 - supports_function_calling: true - - name: google/gemma-3-27b-it - max_input_tokens: 131072 - input_price: 0.1 - output_price: 0.2 - - name: mistralai/Mistral-Small-3.2-24B-Instruct-2506 - max_input_tokens: 32768 - input_price: 0.06 - output_price: 0.12 - - name: moonshotai/Kimi-K2.5 - max_input_tokens: 262144 - input_price: 0.5 - output_price: 2.8 - supports_function_calling: true - - name: moonshotai/Kimi-K2-Instruct-0905 - max_input_tokens: 262144 - input_price: 0.5 - output_price: 2.0 - supports_function_calling: true - - name: moonshotai/Kimi-K2-Thinking - max_input_tokens: 262144 - input_price: 0.55 - output_price: 2.5 - supports_function_calling: true - - name: MiniMaxAI/MiniMax-M2.5 - max_input_tokens: 196608 - input_price: 0.27 - output_price: 0.95 - supports_function_calling: true - - name: MiniMaxAI/MiniMax-M2.1 - max_input_tokens: 196608 - input_price: 0.27 - output_price: 0.95 - supports_function_calling: true - - name: zai-org/GLM-5 - max_input_tokens: 202752 - input_price: 0.8 - output_price: 2.56 - supports_function_calling: true - - name: zai-org/GLM-4.7 - max_input_tokens: 202752 - input_price: 0.43 - output_price: 1.75 - supports_function_calling: true - - name: zai-org/GLM-4.7-Flash - max_input_tokens: 202752 - input_price: 0.06 - output_price: 0.4 - supports_function_calling: true - - name: zai-org/GLM-4.6V - max_input_tokens: 131072 - input_price: 0.3 - output_price: 0.9 - supports_vision: true - - name: BAAI/bge-large-en-v1.5 - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 100 - - name: BAAI/bge-m3 - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 8192 - default_chunk_size: 2000 - max_batch_size: 100 - - name: intfloat/e5-large-v2 - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 100 - - name: intfloat/multilingual-e5-large - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 100 - - name: thenlper/gte-large - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 100 - -# Links: -# - https://jina.ai/models -# - https://api.jina.ai/redoc + - name: BAAI/bge-large-en-v1.5 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: BAAI/bge-m3 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 100 + - name: intfloat/e5-large-v2 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: intfloat/multilingual-e5-large + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: thenlper/gte-large + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: Qwen/Qwen3-Max-Thinking + type: chat + max_input_tokens: 262144 + input_price: 1.2 + output_price: 6.0 + supports_function_calling: true + - name: Qwen/Qwen3-Next-80B-A3B-Thinking + type: chat + max_input_tokens: 262144 + input_price: 0.14 + output_price: 1.4 + - name: Qwen/Qwen3-235B-A22B-Thinking-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.13 + output_price: 0.6 + - name: Qwen/Qwen3-Coder-480B-A35B-Instruct + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 1.6 + supports_function_calling: true + - name: Qwen/Qwen3-Coder-30B-A3B-Instruct + type: chat + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.27 + supports_function_calling: true + - name: Qwen/Qwen3-VL-8B-Instruct + type: chat + max_input_tokens: 262144 + input_price: 0.18 + output_price: 0.69 + supports_vision: true + - name: google/gemma-3-27b-it + type: chat + max_input_tokens: 131072 + input_price: 0.1 + output_price: 0.2 + - name: mistralai/Mistral-Small-3.2-24B-Instruct-2506 + type: chat + max_input_tokens: 32768 + input_price: 0.06 + output_price: 0.12 + - name: moonshotai/Kimi-K2-Instruct-0905 + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 2.0 + supports_function_calling: true + - name: moonshotai/Kimi-K2-Thinking + type: chat + max_input_tokens: 262144 + input_price: 0.55 + output_price: 2.5 + supports_function_calling: true + - name: MiniMaxAI/MiniMax-M2.1 + type: chat + max_input_tokens: 196608 + input_price: 0.27 + output_price: 0.95 + supports_function_calling: true + - name: zai-org/GLM-4.6V + type: chat + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.9 + supports_vision: true + - name: zai-org/GLM-5.3-Flash + type: chat + max_input_tokens: 1048576 + input_price: 0.15 + output_price: 0.5 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: Qwen/Qwen3.8-27B + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 3.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - xhigh + - name: zai-org/GLM-5.3 + type: chat + max_input_tokens: 1048576 + input_price: 1.2 + output_price: 4.0 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: Qwen/Qwen3.8-2.4T-A95B + type: chat + max_input_tokens: 262144 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - low + - medium + - xhigh + - name: deepseek-ai/DeepSeek-V4-Pro-0813 + type: chat + max_input_tokens: 1048576 + input_price: 1.3 + output_price: 2.6 + max_output_tokens: 384000 + supports_function_calling: true + reasoning_levels: + - high + - max + - name: Qwen/Qwen3.8-Max + type: chat + max_input_tokens: 256000 + input_price: 1.65 + output_price: 4.951 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V4-Flash-0731 + type: chat + max_input_tokens: 1048576 + input_price: 0.08 + output_price: 0.18 + max_output_tokens: 384000 + supports_function_calling: true + - name: thinkingmachines/Inkling-Small + type: chat + max_input_tokens: 524288 + input_price: 0.45 + output_price: 1.2 + max_output_tokens: 1048576 + supports_vision: true + supports_function_calling: true + - name: moonshotai/Kimi-K3 + type: chat + max_input_tokens: 1048576 + input_price: 2.85 + output_price: 14.25 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: thinkingmachines/Inkling + type: chat + max_input_tokens: 524288 + input_price: 0.95 + output_price: 4.05 + max_output_tokens: 1048576 + supports_vision: true + supports_function_calling: true + - name: tencent/Hy3 + type: chat + max_input_tokens: 262144 + input_price: 0.14 + output_price: 0.58 + max_output_tokens: 64000 + supports_function_calling: true + - name: zai-org/GLM-5.2 + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 2.4 + max_output_tokens: 32768 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - name: moonshotai/Kimi-K2.7-Code + type: chat + max_input_tokens: 262144 + input_price: 0.68 + output_price: 3.4 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: MiniMaxAI/MiniMax-M3 + type: chat + max_input_tokens: 524288 + input_price: 0.28 + output_price: 1.1 + max_output_tokens: 512000 + supports_vision: true + supports_function_calling: true + - name: stepfun-ai/Step-3.7-Flash + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 1.15 + max_output_tokens: 256000 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3.7-Max + type: chat + max_input_tokens: 256000 + input_price: 2.5 + output_price: 7.5 + max_output_tokens: 65536 + supports_function_calling: true + - name: nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 0.8 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V4-Flash + type: chat + max_input_tokens: 1048576 + input_price: 0.09 + output_price: 0.18 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - name: deepseek-ai/DeepSeek-V4-Pro + type: chat + max_input_tokens: 1048576 + input_price: 1.3 + output_price: 2.6 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - name: Qwen/Qwen3.6-27B + type: chat + max_input_tokens: 262144 + input_price: 0.32 + output_price: 3.2 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: XiaomiMiMo/MiMo-V2.5 + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: XiaomiMiMo/MiMo-V2.5-Pro + type: chat + max_input_tokens: 1048576 + input_price: 1.0 + output_price: 3.0 + max_output_tokens: 16384 + supports_function_calling: true + - name: moonshotai/Kimi-K2.6 + type: chat + max_input_tokens: 262144 + input_price: 0.75 + output_price: 3.5 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: zai-org/GLM-5.1 + type: chat + max_input_tokens: 202752 + input_price: 1.05 + output_price: 3.5 + max_output_tokens: 16384 + supports_function_calling: true + - name: google/gemma-4-26B-A4B-it + type: chat + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.34 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-31B-it + type: chat + max_input_tokens: 262144 + input_price: 0.13 + output_price: 0.38 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-E4B-it + type: chat + max_input_tokens: 131072 + input_price: 0.02 + output_price: 0.1 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3.6-35B-A3B + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.95 + max_output_tokens: 81920 + supports_vision: true + supports_function_calling: true + - name: MiniMaxAI/MiniMax-M2.7 + type: chat + max_input_tokens: 196608 + input_price: 0.25 + output_price: 1.0 + max_output_tokens: 131072 + supports_function_calling: true + - name: Qwen/Qwen3.5-122B-A10B + type: chat + max_input_tokens: 262144 + input_price: 0.29 + output_price: 2.4 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3.5-27B + type: chat + max_input_tokens: 262144 + input_price: 0.26 + output_price: 2.6 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3.5-9B + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.15 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: ByteDance/Seed-2.0-code + type: chat + max_input_tokens: 256000 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - name: ByteDance/Seed-2.0-mini + type: chat + max_input_tokens: 256000 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 32000 + supports_vision: true + supports_function_calling: true + - name: ByteDance/Seed-2.0-pro + type: chat + max_input_tokens: 256000 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: MiniMaxAI/MiniMax-M2.5 + type: chat + max_input_tokens: 196608 + input_price: 0.15 + output_price: 1.15 + max_output_tokens: 131072 + supports_function_calling: true + - name: zai-org/GLM-5 + type: chat + max_input_tokens: 202752 + input_price: 0.6 + output_price: 2.08 + max_output_tokens: 16384 + supports_function_calling: true + - name: Qwen/Qwen3.5-35B-A3B + type: chat + max_input_tokens: 262144 + input_price: 0.14 + output_price: 1.0 + max_output_tokens: 81920 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3.5-397B-A17B + type: chat + max_input_tokens: 262144 + input_price: 0.45 + output_price: 3.0 + max_output_tokens: 81920 + supports_vision: true + supports_function_calling: true + - name: moonshotai/Kimi-K2.5 + type: chat + max_input_tokens: 262144 + input_price: 0.45 + output_price: 2.25 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: zai-org/GLM-4.7-Flash + type: chat + max_input_tokens: 202752 + input_price: 0.06 + output_price: 0.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: zai-org/GLM-4.7 + type: chat + max_input_tokens: 202752 + input_price: 0.4 + output_price: 1.75 + max_output_tokens: 16384 + supports_function_calling: true + - name: nvidia/Nemotron-3-Nano-30B-A3B + type: chat + max_input_tokens: 262144 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 262144 + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V3.2 + type: chat + max_input_tokens: 163840 + input_price: 0.26 + output_price: 0.38 + max_output_tokens: 64000 + supports_function_calling: true + - name: zai-org/GLM-4.6 + type: chat + max_input_tokens: 202752 + input_price: 0.5 + output_price: 2.0 + max_output_tokens: 131072 + supports_function_calling: true + - name: Qwen/Qwen3-Max + type: chat + max_input_tokens: 256000 + input_price: 1.2 + output_price: 6.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: Qwen/Qwen3-VL-235B-A22B-Instruct + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 0.88 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3-Next-80B-A3B-Instruct + type: chat + max_input_tokens: 262144 + input_price: 0.09 + output_price: 1.1 + max_output_tokens: 32768 + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V3.1 + type: chat + max_input_tokens: 163840 + input_price: 0.25 + output_price: 0.95 + max_output_tokens: 8192 + supports_function_calling: true + - name: openai/gpt-oss-120b + type: chat + max_input_tokens: 131072 + input_price: 0.037 + output_price: 0.17 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai/gpt-oss-20b + type: chat + max_input_tokens: 131072 + input_price: 0.03 + output_price: 0.14 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: nvidia/Llama-3.3-Nemotron-Super-49B-v1.5 + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 0.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo + type: chat + max_input_tokens: 262144 + input_price: 0.3 + output_price: 1.0 + max_output_tokens: 66536 + supports_function_calling: true + - name: Qwen/Qwen3-235B-A22B-Instruct-2507 + type: chat + max_input_tokens: 262144 + input_price: 0.09 + output_price: 0.55 + max_output_tokens: 16384 + supports_function_calling: true + - name: deepseek-ai/DeepSeek-R1-0528 + type: chat + max_input_tokens: 163840 + input_price: 0.5 + output_price: 2.15 + max_output_tokens: 64000 + supports_function_calling: true + - name: Qwen/Qwen3-30B-A3B + type: chat + max_input_tokens: 40960 + input_price: 0.12 + output_price: 0.5 + max_output_tokens: 16384 + supports_function_calling: true + - name: meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8 + type: chat + max_input_tokens: 1048576 + input_price: 0.2 + output_price: 0.8 + max_output_tokens: 16384 + supports_vision: true + - name: meta-llama/Llama-4-Scout-17B-16E-Instruct + type: chat + max_input_tokens: 327680 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3-32B + type: chat + max_input_tokens: 40960 + input_price: 0.08 + output_price: 0.28 + max_output_tokens: 16384 + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V3-0324 + type: chat + max_input_tokens: 163840 + input_price: 0.24 + output_price: 0.9 + max_output_tokens: 163840 + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V3 + type: chat + max_input_tokens: 163840 + input_price: 0.32 + output_price: 0.89 + max_output_tokens: 8192 + supports_function_calling: true + - name: meta-llama/Llama-3.3-70B-Instruct-Turbo + type: chat + max_input_tokens: 131072 + input_price: 0.1 + output_price: 0.32 + max_output_tokens: 16384 + supports_function_calling: true - provider: jina models: - - name: jina-embeddings-v3 - type: embedding - input_price: 0 - max_tokens_per_chunk: 8192 - default_chunk_size: 2000 - max_batch_size: 100 - - name: jina-clip-v2 - type: embedding - input_price: 0 - max_tokens_per_chunk: 8192 - default_chunk_size: 1500 - max_batch_size: 100 - - name: jina-colbert-v2 - type: embedding - input_price: 0 - max_tokens_per_chunk: 8192 - default_chunk_size: 1500 - max_batch_size: 100 - - name: jina-reranker-v2-base-multilingual - type: reranker - max_input_tokens: 8192 - input_price: 0 - - name: jina-colbert-v2 - type: reranker - max_input_tokens: 8192 - input_price: 0 - -# Links: -# - https://docs.voyageai.com/docs/embeddings -# - https://docs.voyageai.com/docs/pricing -# - https://docs.voyageai.com/reference/ + - name: jina-embeddings-v3 + type: embedding + input_price: 0.0 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 100 + - name: jina-clip-v2 + type: embedding + input_price: 0.0 + max_tokens_per_chunk: 8192 + default_chunk_size: 1500 + max_batch_size: 100 + - name: jina-colbert-v2 + type: embedding + input_price: 0.0 + max_tokens_per_chunk: 8192 + default_chunk_size: 1500 + max_batch_size: 100 + - name: jina-reranker-v2-base-multilingual + type: reranker + max_input_tokens: 8192 + input_price: 0.0 + - name: jina-colbert-v2 + type: reranker + max_input_tokens: 8192 + input_price: 0.0 - provider: voyageai models: - - name: voyage-3-large - type: embedding - max_input_tokens: 120000 - input_price: 0.18 - max_tokens_per_chunk: 32000 - default_chunk_size: 2000 - max_batch_size: 128 - - name: voyage-3 - type: embedding - max_input_tokens: 320000 - input_price: 0.06 - max_tokens_per_chunk: 32000 - default_chunk_size: 2000 - max_batch_size: 128 - - name: voyage-3-lite - type: embedding - max_input_tokens: 1000000 - input_price: 0.02 - max_tokens_per_chunk: 32000 - default_chunk_size: 1000 - max_batch_size: 128 - - name: rerank-2 - type: reranker - max_input_tokens: 16000 - input_price: 0.05 - - name: rerank-2-lite - type: reranker - max_input_tokens: 8000 - input_price: 0.02 + - name: voyage-3-large + type: embedding + max_input_tokens: 120000 + input_price: 0.18 + max_tokens_per_chunk: 32000 + default_chunk_size: 2000 + max_batch_size: 128 + - name: voyage-3 + type: embedding + max_input_tokens: 320000 + input_price: 0.06 + max_tokens_per_chunk: 32000 + default_chunk_size: 2000 + max_batch_size: 128 + - name: voyage-3-lite + type: embedding + max_input_tokens: 1000000 + input_price: 0.02 + max_tokens_per_chunk: 32000 + default_chunk_size: 1000 + max_batch_size: 128 + - name: rerank-2 + type: reranker + max_input_tokens: 16000 + input_price: 0.05 + - name: rerank-2-lite + type: reranker + max_input_tokens: 8000 + input_price: 0.02 diff --git a/quirks.yaml b/quirks.yaml index 65b2653..75e68c0 100644 --- a/quirks.yaml +++ b/quirks.yaml @@ -9,6 +9,11 @@ # entries are grouped under a `# legacy — pruning candidates` comment. - provider: openai + exclude: + - "text-embedding-*" + - "gpt-image-*" + - "chatgpt-image-*" + - "gpt-realtime*" rules: - match: "gpt-5.6-*" default_reasoning_effort: medium @@ -118,8 +123,39 @@ max_tokens: null temperature: null top_p: null + - name: chat-latest + max_input_tokens: 400000 + max_output_tokens: 128000 + input_price: 5 + output_price: 30 + supports_vision: true + supports_function_calling: true + - name: gpt-5.1-chat-latest + max_input_tokens: 400000 + max_output_tokens: 128000 + input_price: 1.25 + output_price: 10 + supports_vision: true + supports_function_calling: true + reasoning_levels: [none, low, medium, high] + default_reasoning_effort: none + - name: gpt-5-chat-latest + max_input_tokens: 400000 + max_output_tokens: 128000 + input_price: 1.25 + output_price: 10 + supports_vision: true + supports_function_calling: true + reasoning_levels: [minimal, low, medium, high] + default_reasoning_effort: medium - provider: gemini + exclude: + - "gemini-embedding-*" + - "veo-*" + - "*-tts" + - "*-tts-preview" + - "*-image-preview" rules: - match: "gemini-3.6-flash" default_reasoning_effort: medium @@ -135,6 +171,7 @@ default_reasoning_effort: high - match: "gemini-2.5-pro" default_reasoning_effort: high + reasoning_levels: [low, medium, high] models: - name: text-embedding-004 type: embedding @@ -142,6 +179,18 @@ max_tokens_per_chunk: 2048 default_chunk_size: 1500 max_batch_size: 100 + # legacy — pruning candidates + - name: gemini-3-pro-preview + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, high] + default_reasoning_effort: high + - name: gemma-3-27b-it + max_input_tokens: 131072 + max_output_tokens: 8192 + input_price: 0 + output_price: 0 - provider: claude rules: @@ -246,8 +295,28 @@ thinking: type: enabled budget_tokens: 16000 + models: + # legacy — pruning candidates + - name: claude-opus-4-1-20250805 + max_input_tokens: 200000 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 15 + output_price: 75 + supports_vision: true + supports_function_calling: true + - name: claude-opus-4-20250514 + max_input_tokens: 200000 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 15 + output_price: 75 + supports_vision: true + supports_function_calling: true - provider: mistral + exclude: + - "voxtral-*" models: - name: mistral-embed type: embedding @@ -255,6 +324,21 @@ input_price: 0.1 max_tokens_per_chunk: 8092 default_chunk_size: 2000 + # legacy — pruning candidates + - name: magistral-small-latest + max_input_tokens: 131072 + input_price: 0.5 + output_price: 1.5 + - name: devstral-small-latest + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.3 + supports_function_calling: true + - name: ministral-14b-latest + max_input_tokens: 262144 + input_price: 0.2 + output_price: 0.2 + supports_function_calling: true - provider: ai21 models: @@ -300,6 +384,8 @@ max_input_tokens: 4096 - provider: xai + exclude: + - "grok-imagine-*" oauth: client_id: b1a00492-073a-47ea-816f-4c329264a828 authorize_url: https://auth.x.ai/oauth2/authorize @@ -337,8 +423,71 @@ output_price: 2.5 max_input_tokens: 1000000 supports_function_calling: true + - name: grok-4-1-fast-non-reasoning + max_input_tokens: 1000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: grok-4-1-fast-reasoning + max_input_tokens: 1000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: grok-code-fast-1 + max_input_tokens: 256000 + input_price: 0.2 + output_price: 1.5 + supports_function_calling: true + - name: grok-3 + max_input_tokens: 131072 + input_price: 3 + output_price: 15 + supports_function_calling: true + - name: grok-3-mini + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.5 + supports_function_calling: true + +- provider: perplexity + models: + # legacy — pruning candidates + - name: sonar-reasoning + max_input_tokens: 128000 + input_price: 1 + output_price: 5 + +- provider: groq + exclude: + - "whisper-*" + models: + # legacy — pruning candidates + - name: meta-llama/llama-4-maverick-17b-128e-instruct + max_input_tokens: 131072 + input_price: 0 + output_price: 0 + supports_vision: true + supports_function_calling: true + - name: meta-llama/llama-4-scout-17b-16e-instruct + max_input_tokens: 131072 + input_price: 0 + output_price: 0 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k2-instruct-0905 + max_input_tokens: 262144 + input_price: 0 + output_price: 0 + supports_function_calling: true + - name: qwen/qwen3-32b + max_input_tokens: 131072 + input_price: 0 + output_price: 0 - provider: vertexai + exclude: + - "gemini-embedding-*" + - "*-tts" rules: - match: "claude-*" require_max_tokens: true @@ -354,8 +503,10 @@ default_reasoning_effort: high - match: "gemini-2.5-flash" default_reasoning_effort: medium + reasoning_levels: [low, medium, high] - match: "gemini-2.5-pro" default_reasoning_effort: high + reasoning_levels: [low, medium, high] - match: "claude-opus-5" default_reasoning_effort: high - match: "claude-fable-*" @@ -449,6 +600,109 @@ max_tokens_per_chunk: 2048 default_chunk_size: 1500 max_batch_size: 5 + # legacy — pruning candidates + - name: gemini-3-pro-preview + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, high] + default_reasoning_effort: high + - name: claude-opus-5 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_function_calling: true + supports_vision: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-fable-5-1 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 10 + output_price: 50 + supports_function_calling: true + supports_vision: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-fable-5 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 10 + output_price: 50 + supports_function_calling: true + supports_vision: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-opus-4-8 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-opus-4-7 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-opus-4-6 + max_input_tokens: 200000 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-5 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 3 + output_price: 15 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-sonnet-4-6 + max_input_tokens: 200000 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 3 + output_price: 15 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, max] + default_reasoning_effort: high + - name: claude-3-5-haiku@20241022 + max_input_tokens: 200000 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 0.8 + output_price: 4 + supports_vision: true + supports_function_calling: true + - name: mistral-small-2503 + max_input_tokens: 32000 + input_price: 0.1 + output_price: 0.3 + supports_function_calling: true + - name: codestral-2501 + max_input_tokens: 256000 + input_price: 0.3 + output_price: 0.9 + supports_function_calling: true - provider: bedrock rules: @@ -559,6 +813,36 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 96 + # legacy — pruning candidates + - name: us.meta.llama3-3-70b-instruct-v1:0 + max_input_tokens: 131072 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 0.72 + output_price: 0.72 + supports_function_calling: true + - name: us.amazon.nova-premier-v1:0 + max_input_tokens: 300000 + max_output_tokens: 5120 + input_price: 2.5 + output_price: 12.5 + - name: us.amazon.nova-pro-v1:0 + max_input_tokens: 300000 + max_output_tokens: 5120 + input_price: 0.8 + output_price: 3.2 + supports_vision: true + - name: us.amazon.nova-lite-v1:0 + max_input_tokens: 300000 + max_output_tokens: 5120 + input_price: 0.06 + output_price: 0.24 + supports_vision: true + - name: us.amazon.nova-micro-v1:0 + max_input_tokens: 128000 + max_output_tokens: 5120 + input_price: 0.035 + output_price: 0.14 - provider: cloudflare rules: @@ -571,6 +855,13 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 100 + # legacy — pruning candidates + - name: '@cf/google/gemma-3-12b-it' + max_input_tokens: 131072 + max_output_tokens: 2048 + require_max_tokens: true + input_price: 0 + output_price: 0 - provider: ernie models: @@ -609,6 +900,8 @@ input_price: 0.07 - provider: qianwen + exclude: + - "*-realtime" rules: - match: "qwen3.5-plus" patch: @@ -646,6 +939,40 @@ max_tokens_per_chunk: 8192 default_chunk_size: 2000 max_batch_size: 10 + # legacy — pruning candidates + - name: qwen3-vl-flash + max_input_tokens: 262144 + supports_vision: true + - name: qwen3-235b-a22b-instruct-2507 + max_input_tokens: 131072 + input_price: 0.28 + output_price: 1.12 + supports_function_calling: true + - name: qwen3-235b-a22b-thinking-2507 + max_input_tokens: 131072 + input_price: 0.28 + output_price: 2.8 + - name: qwen3-30b-a3b-instruct-2507 + max_input_tokens: 131072 + input_price: 0.105 + output_price: 0.42 + supports_function_calling: true + - name: qwen3-30b-a3b-thinking-2507 + max_input_tokens: 131072 + input_price: 0.105 + output_price: 1.05 + - name: qwen3-vl-32b-instruct + max_input_tokens: 131072 + input_price: 0.28 + output_price: 1.12 + supports_vision: true + - name: qwen3-vl-8b-instruct + max_input_tokens: 131072 + input_price: 0.07 + output_price: 0.28 + supports_vision: true + - name: qwen3-coder-next + max_input_tokens: 262144 - provider: hunyuan models: @@ -671,6 +998,21 @@ default_chunk_size: 1000 max_batch_size: 100 +- provider: deepseek + models: + # legacy — pruning candidates + - name: deepseek-chat + max_input_tokens: 64000 + max_output_tokens: 8192 + input_price: 0.56 + output_price: 1.68 + supports_function_calling: true + - name: deepseek-reasoner + max_input_tokens: 64000 + max_output_tokens: 32768 + input_price: 0.56 + output_price: 1.68 + - provider: zhipuai variants: - match: "glm-5" @@ -698,6 +1040,46 @@ type: reranker max_input_tokens: 4096 input_price: 0.112 + # legacy — pruning candidates + - name: glm-4.6v-flash + max_input_tokens: 65536 + input_price: 0 + output_price: 0 + supports_vision: true + +- provider: minimax + models: + # legacy — pruning candidates + - name: minimax-m3 + max_input_tokens: 1000000 + input_price: 4.2 + output_price: 16.8 + supports_function_calling: true + - name: minimax-m2.7 + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.5 + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.5-highspeed + max_input_tokens: 204800 + input_price: 0.588 + output_price: 2.352 + supports_function_calling: true + - name: minimax-m2.1 + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.1-highspeed + max_input_tokens: 204800 + input_price: 0.588 + output_price: 2.352 + supports_function_calling: true - provider: openrouter rules: @@ -705,54 +1087,76 @@ require_max_tokens: true - match: "openai/gpt-5.6-*" default_reasoning_effort: medium + reasoning_levels: [none, low, medium, high, xhigh, max] - match: "openai/gpt-5.5" default_reasoning_effort: medium + reasoning_levels: [none, low, medium, high, xhigh] - match: "openai/gpt-5.5-pro" default_reasoning_effort: high + reasoning_levels: [medium, high, xhigh] - match: "openai/gpt-5.4" default_reasoning_effort: none + reasoning_levels: [none, low, medium, high, xhigh] - match: "openai/gpt-5.4-pro" default_reasoning_effort: medium + reasoning_levels: [medium, high, xhigh] - match: "openai/gpt-5.4-mini" default_reasoning_effort: none + reasoning_levels: [none, low, medium, high, xhigh] - match: "openai/gpt-5.4-nano" default_reasoning_effort: none + reasoning_levels: [none, low, medium, high, xhigh] - match: "openai/gpt-5.3-codex" default_reasoning_effort: medium + reasoning_levels: [low, medium, high, xhigh] - match: "openai/gpt-5.2" default_reasoning_effort: none + reasoning_levels: [none, low, medium, high, xhigh] - match: "openai/gpt-5.2-pro" default_reasoning_effort: medium + reasoning_levels: [medium, high, xhigh] - match: "openai/gpt-5" default_reasoning_effort: medium + reasoning_levels: [minimal, low, medium, high] - match: "google/gemini-3.5-flash" default_reasoning_effort: medium + reasoning_levels: [minimal, low, medium, high] - match: "google/gemini-3-flash-preview" default_reasoning_effort: high + reasoning_levels: [minimal, low, medium, high] - match: "google/gemini-3.1-flash-lite" default_reasoning_effort: minimal + reasoning_levels: [minimal, low, medium, high] - match: "google/gemini-3.1-pro-preview" default_reasoning_effort: high + reasoning_levels: [low, medium, high] - match: "google/gemini-3-pro-preview" default_reasoning_effort: high - match: "google/gemini-2.5-flash" default_reasoning_effort: low + reasoning_levels: [low, medium, high] - match: "google/gemini-2.5-pro" default_reasoning_effort: high + reasoning_levels: [low, medium, high] - match: "anthropic/claude-opus-5" default_reasoning_effort: high + reasoning_levels: [low, medium, high, xhigh, max] - match: "anthropic/claude-fable-*" default_reasoning_effort: high + reasoning_levels: [low, medium, high, xhigh, max] - match: "anthropic/claude-opus-4-8" default_reasoning_effort: high - match: "anthropic/claude-opus-4-7" default_reasoning_effort: high - match: "anthropic/claude-opus-4.6" default_reasoning_effort: high + reasoning_levels: [low, medium, high, max] - match: "anthropic/claude-sonnet-5" default_reasoning_effort: high + reasoning_levels: [low, medium, high, xhigh, max] - match: "anthropic/claude-sonnet-4.6" default_reasoning_effort: high + reasoning_levels: [low, medium, high, max] - match: "perplexity/sonar-reasoning-pro" patch: body: @@ -761,9 +1165,105 @@ patch: body: include_reasoning: true + models: + # legacy — pruning candidates + - name: google/gemini-3-pro-preview + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, high] + default_reasoning_effort: high + - name: google/gemini-2.0-flash-001 + max_input_tokens: 1000000 + input_price: 0.15 + output_price: 0.6 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.0-flash-lite-001 + max_input_tokens: 1048576 + input_price: 0.075 + output_price: 0.3 + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-fable-5-1 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 10 + output_price: 50 + supports_function_calling: true + supports_vision: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: anthropic/claude-opus-4-8 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: anthropic/claude-opus-4-7 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: mistralai/devstral-small + max_input_tokens: 131072 + input_price: 0.07 + output_price: 0.28 + supports_function_calling: true + - name: ai21/jamba-large-1.7 + max_input_tokens: 256000 + input_price: 2 + output_price: 8 + supports_function_calling: true + - name: x-ai/grok-4.1-fast + max_input_tokens: 2000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: x-ai/grok-code-fast-1 + max_input_tokens: 256000 + input_price: 0.2 + output_price: 1.5 + supports_function_calling: true +# Fully hand-owned: upstream catalogs track GitHub Copilot ids, which do not +# match the GitHub Models API ids coyote uses. - provider: github models: + - name: gpt-5 + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-5-mini + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-5-nano + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-4.1 + max_input_tokens: 1047576 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gpt-4o + max_input_tokens: 128000 + max_output_tokens: 16384 + supports_function_calling: true - name: text-embedding-3-large type: embedding max_tokens_per_chunk: 8191 @@ -774,6 +1274,23 @@ max_tokens_per_chunk: 8191 default_chunk_size: 2000 max_batch_size: 100 + - name: llama-4-maverick-17b-128e-instruct-fp8 + max_input_tokens: 1048576 + supports_vision: true + - name: llama-4-scout-17b-16e-instruct + max_input_tokens: 327680 + supports_vision: true + - name: llama-3.3-70b-instruct + max_input_tokens: 131072 + - name: mistral-medium-2505 + max_input_tokens: 131072 + supports_function_calling: true + - name: mistral-small-2503 + max_input_tokens: 131072 + supports_function_calling: true + - name: codestral-2501 + max_input_tokens: 256000 + supports_function_calling: true - name: cohere-embed-v3-english type: embedding max_tokens_per_chunk: 512 @@ -784,6 +1301,24 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 96 + - name: deepseek-r1-0528 + max_input_tokens: 163840 + - name: deepseek-v3-0324 + max_input_tokens: 163840 + - name: mai-ds-r1 + max_input_tokens: 163840 + - name: phi-4 + max_input_tokens: 16384 + - name: phi-4-mini-instruct + max_input_tokens: 131072 + - name: phi-4-reasoning + max_input_tokens: 33792 + - name: phi-4-mini-reasoning + max_input_tokens: 131072 + - name: grok-3 + max_input_tokens: 131072 + - name: grok-3-mini + max_input_tokens: 131072 - provider: deepinfra models: @@ -817,6 +1352,63 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 100 + # legacy — pruning candidates + - name: Qwen/Qwen3-Max-Thinking + max_input_tokens: 262144 + input_price: 1.2 + output_price: 6 + supports_function_calling: true + - name: Qwen/Qwen3-Next-80B-A3B-Thinking + max_input_tokens: 262144 + input_price: 0.14 + output_price: 1.4 + - name: Qwen/Qwen3-235B-A22B-Thinking-2507 + max_input_tokens: 131072 + input_price: 0.13 + output_price: 0.6 + - name: Qwen/Qwen3-Coder-480B-A35B-Instruct + max_input_tokens: 131072 + input_price: 0.4 + output_price: 1.6 + supports_function_calling: true + - name: Qwen/Qwen3-Coder-30B-A3B-Instruct + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.27 + supports_function_calling: true + - name: Qwen/Qwen3-VL-8B-Instruct + max_input_tokens: 262144 + input_price: 0.18 + output_price: 0.69 + supports_vision: true + - name: google/gemma-3-27b-it + max_input_tokens: 131072 + input_price: 0.1 + output_price: 0.2 + - name: mistralai/Mistral-Small-3.2-24B-Instruct-2506 + max_input_tokens: 32768 + input_price: 0.06 + output_price: 0.12 + - name: moonshotai/Kimi-K2-Instruct-0905 + max_input_tokens: 262144 + input_price: 0.5 + output_price: 2.0 + supports_function_calling: true + - name: moonshotai/Kimi-K2-Thinking + max_input_tokens: 262144 + input_price: 0.55 + output_price: 2.5 + supports_function_calling: true + - name: MiniMaxAI/MiniMax-M2.1 + max_input_tokens: 196608 + input_price: 0.27 + output_price: 0.95 + supports_function_calling: true + - name: zai-org/GLM-4.6V + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.9 + supports_vision: true - provider: jina models: diff --git a/scripts/requirements.txt b/scripts/requirements.txt deleted file mode 100644 index 69a538b..0000000 --- a/scripts/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests -ruamel.yaml diff --git a/scripts/update_models.py b/scripts/update_models.py deleted file mode 100644 index fab2014..0000000 --- a/scripts/update_models.py +++ /dev/null @@ -1,255 +0,0 @@ -import requests -import sys -import re -import json - -# Provider mapping from models.yaml to OpenRouter prefixes -PROVIDER_MAPPING = { - "openai": "openai", - "claude": "anthropic", - "gemini": "google", - "mistral": "mistralai", - "cohere": "cohere", - "perplexity": "perplexity", - "xai": "x-ai", - "openrouter": "openrouter", - "ai21": "ai21", - "deepseek": "deepseek", - "moonshot": "moonshotai", - "qianwen": "qwen", - "zhipuai": "zhipuai", - "minimax": "minimax", - "vertexai": "google", - "groq": "groq", - "bedrock": "amazon", - "hunyuan": "tencent", - "ernie": "baidu", - "github": "github", -} - -def fetch_openrouter_models(): - print("Fetching models from OpenRouter...") - try: - response = requests.get("https://openrouter.ai/api/v1/models") - response.raise_for_status() - data = response.json()["data"] - print(f"Fetched {len(data)} models.") - return data - except Exception as e: - print(f"Error fetching models: {e}") - sys.exit(1) - -def get_openrouter_model(models_data, provider_prefix, model_name, is_openrouter_provider=False): - if is_openrouter_provider: - # For openrouter provider, the model_name in yaml is usually the full ID - for model in models_data: - if model["id"] == model_name: - return model - return None - - expected_id = f"{provider_prefix}/{model_name}" - - # 1. Try exact match on ID - for model in models_data: - if model["id"] == expected_id: - return model - - # 2. Try match by suffix - for model in models_data: - if model["id"].split("/")[-1] == model_name: - if model["id"].startswith(f"{provider_prefix}/"): - return model - - return None - -def format_price(price_per_token): - if price_per_token is None: - return None - try: - price_per_1m = float(price_per_token) * 1_000_000 - if price_per_1m.is_integer(): - return str(int(price_per_1m)) - else: - return str(round(price_per_1m, 4)) - except: - return None - -def get_indentation(line): - return len(line) - len(line.lstrip()) - -def process_model_block(block_lines, current_provider, or_models): - if not block_lines: - return [] - - # 1. Identify model name and indentation - name_line = block_lines[0] - name_match = re.match(r"^(\s*)-\s*name:\s*(.+)$", name_line) - if not name_match: - return block_lines - - name_indent_str = name_match.group(1) - model_name = name_match.group(2).strip() - - # 2. Find OpenRouter model - or_prefix = PROVIDER_MAPPING.get(current_provider) - is_openrouter_provider = (current_provider == "openrouter") - - if not or_prefix and not is_openrouter_provider: - return block_lines - - or_model = get_openrouter_model(or_models, or_prefix, model_name, is_openrouter_provider) - if not or_model: - return block_lines - - print(f" Updating {model_name}...") - - # 3. Prepare updates - updates = {} - - # Pricing - pricing = or_model.get("pricing", {}) - p_in = format_price(pricing.get("prompt")) - p_out = format_price(pricing.get("completion")) - if p_in: updates["input_price"] = p_in - if p_out: updates["output_price"] = p_out - - # Context - ctx = or_model.get("context_length") - if ctx: updates["max_input_tokens"] = str(ctx) - - max_out = None - if "top_provider" in or_model and or_model["top_provider"]: - max_out = or_model["top_provider"].get("max_completion_tokens") - if max_out: updates["max_output_tokens"] = str(max_out) - - # Capabilities - arch = or_model.get("architecture", {}) - modality = arch.get("modality", "") - if "image" in modality: - updates["supports_vision"] = "true" - - # 4. Detect field indentation - field_indent_str = None - existing_fields = {} # key -> line_index - - for i, line in enumerate(block_lines): - if i == 0: continue # Skip name line - - # Skip comments - if line.strip().startswith("#"): - continue - - # Look for "key: value" - m = re.match(r"^(\s*)([\w_-]+):", line) - if m: - indent = m.group(1) - key = m.group(2) - # Must be deeper than name line - if len(indent) > len(name_indent_str): - if field_indent_str is None: - field_indent_str = indent - existing_fields[key] = i - - if field_indent_str is None: - field_indent_str = name_indent_str + " " - - # 5. Apply updates - new_block = list(block_lines) - - # Update existing fields - for key, value in updates.items(): - if key in existing_fields: - idx = existing_fields[key] - # Preserve original key indentation exactly - original_line = new_block[idx] - m = re.match(r"^(\s*)([\w_-]+):", original_line) - if m: - current_indent = m.group(1) - new_block[idx] = f"{current_indent}{key}: {value}\n" - - # Insert missing fields - # Insert after the name line - insertion_idx = 1 - - for key, value in updates.items(): - if key not in existing_fields: - new_line = f"{field_indent_str}{key}: {value}\n" - new_block.insert(insertion_idx, new_line) - insertion_idx += 1 - - return new_block - -def main(): - or_models = fetch_openrouter_models() - - print("Reading models.yaml...") - with open("models.yaml", "r") as f: - lines = f.readlines() - - new_lines = [] - current_provider = None - - i = 0 - while i < len(lines): - line = lines[i] - - # Check for provider - # - provider: name - p_match = re.match(r"^\s*-?\s*provider:\s*(.+)$", line) - if p_match: - current_provider = p_match.group(1).strip() - new_lines.append(line) - i += 1 - continue - - # Check for model start - # - name: ... - m_match = re.match(r"^(\s*)-\s*name:\s*.+$", line) - if m_match: - # Start of a model block - start_indent = len(m_match.group(1)) - - # Collect block lines - block_lines = [line] - j = i + 1 - while j < len(lines): - next_line = lines[j] - stripped = next_line.strip() - - # If empty or comment, include it - if not stripped or stripped.startswith("#"): - block_lines.append(next_line) - j += 1 - continue - - # Check indentation - next_indent = get_indentation(next_line) - - # If indentation is greater, it's part of the block (property) - if next_indent > start_indent: - block_lines.append(next_line) - j += 1 - continue - - # If indentation is equal or less, it's the end of the block - break - - # Process the block - processed_block = process_model_block(block_lines, current_provider, or_models) - new_lines.extend(processed_block) - - # Advance i - i = j - continue - - # Otherwise, just a regular line - new_lines.append(line) - i += 1 - - print("Saving models.yaml...") - with open("models.yaml", "w") as f: - f.writelines(new_lines) - print("Done.") - -if __name__ == "__main__": - main() diff --git a/src/cli/mod.rs b/src/cli/mod.rs index fbd3d92..f53658b 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -12,6 +12,7 @@ use clap_complete::ArgValueCompleter; use is_terminal::IsTerminal; use std::collections::HashSet; use std::io::{Read, stdin}; +use std::path::PathBuf; #[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)] pub enum McpTransportArg { @@ -53,7 +54,7 @@ pub enum McpScopeArg { "install_builtins", "sync_models", "list_models", "list_roles", "list_sessions", "list_agents", "list_rags", "list_macros", "list_skills", "list_bundles", "skill", "tail_logs", "completions", - "update", "update_bundle", "uninstall", + "update", "update_bundle", "uninstall", "generate_models", ]) ), group( @@ -253,6 +254,15 @@ pub struct Cli { /// Sync models updates #[arg(long, help_heading = "Installation & Updates")] pub sync_models: bool, + /// Regenerate the models catalog file from live API data + #[arg( + long, + hide = true, + value_name = "PATH", + num_args = 0..=1, + default_missing_value = "models.yaml" + )] + pub generate_models: Option, /// Update Coyote to the latest release, or to a specific version #[arg(long, value_name = "VERSION", help_heading = "Installation & Updates")] pub update: Option>, @@ -810,6 +820,19 @@ mod tests { assert!(cli.sync_models); } + #[test] + fn parse_generate_models_flag() { + assert_eq!(parse(&[]).generate_models, None); + assert_eq!( + parse(&["--generate-models"]).generate_models, + Some(PathBuf::from("models.yaml")) + ); + assert_eq!( + parse(&["--generate-models", "out.yaml"]).generate_models, + Some(PathBuf::from("out.yaml")) + ); + } + #[test] fn parse_model_with_role() { let cli = parse(&["-m", "gpt-4o", "-r", "coder"]); diff --git a/src/client/catalog.rs b/src/client/catalog.rs index 4d18162..cec2d46 100644 --- a/src/client/catalog.rs +++ b/src/client/catalog.rs @@ -13,7 +13,6 @@ use std::sync::LazyLock; pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); -#[allow(dead_code)] pub static ALL_QUIRKS: LazyLock> = LazyLock::new(|| serde_yaml::from_str(QUIRKS_YAML).expect("quirks.yaml must be valid")); @@ -133,8 +132,8 @@ const PROVIDER_SOURCES: [SourceIds; 24] = [ }, SourceIds { provider: "github", - modelsdev: Some("github-copilot"), - openrouter: Some("github"), + modelsdev: None, + openrouter: None, }, SourceIds { provider: "deepinfra", @@ -162,7 +161,6 @@ pub struct FetchedSources { /// Fetches both upstream catalogs concurrently. A failed fetch is logged and /// surfaces as `None` rather than an error. -#[allow(dead_code)] pub async fn fetch_sources() -> FetchedSources { let (modelsdev, openrouter) = tokio::join!(fetch(MODELSDEV_API_URL), fetch(OPENROUTER_API_URL)); FetchedSources { @@ -478,7 +476,6 @@ pub fn build_catalog(sources: &FetchedSources, quirks: &[ProviderQuirks]) -> Cat /// Like [`build_catalog`], but errors unless every provider was assembled /// from live API data (or is quirks-owned by design). -#[allow(dead_code)] pub fn build_catalog_strict( sources: &FetchedSources, quirks: &[ProviderQuirks], @@ -494,6 +491,32 @@ pub fn build_catalog_strict( Ok(build.providers) } +const GENERATED_HEADER: &str = "# GENERATED by 'coyote --generate-models' — DO NOT hand-edit model data.\n\ +# Hand-owned behavior lives in quirks.yaml; this file is the embedded offline fallback.\n"; + +/// Builds the catalog from live API data and writes it to `path`. Both +/// upstream sources must respond and every provider must fully resolve; +/// identical source data always produces identical bytes. +pub async fn generate_models_file(path: &std::path::Path) -> Result<()> { + let sources = fetch_sources().await; + if sources.modelsdev.is_none() || sources.openrouter.is_none() { + bail!( + "generating the models file requires both catalog sources; see the log for the failed fetch" + ); + } + let providers = build_catalog_strict(&sources, &ALL_QUIRKS)?; + let model_count: usize = providers.iter().map(|provider| provider.models.len()).sum(); + let yaml = serde_yaml::to_string(&providers)?; + std::fs::write(path, format!("{GENERATED_HEADER}{yaml}"))?; + println!( + "Wrote {} models across {} providers to {}", + model_count, + providers.len(), + path.display() + ); + Ok(()) +} + /// Fraction of `old` names that also appear in `new`; empty `old` yields 1.0. /// Used to guard against upstream id-shape drift: a low overlap means the /// upstream catalog renamed its models out from under us. @@ -1333,6 +1356,8 @@ rules: models: [{name: e}] - provider: hunyuan models: [{name: h}] +- provider: github + models: [{name: g}] - provider: jina models: [{name: j}] - provider: voyageai @@ -1358,6 +1383,96 @@ rules: assert_eq!(name_overlap(&old, &[]), 0.0); } + fn embedded_catalog() -> Vec { + serde_yaml::from_str(crate::client::MODELS_YAML).unwrap() + } + + fn embedded_provider<'a>(providers: &'a [ProviderModels], name: &str) -> &'a ProviderModels { + providers + .iter() + .find(|provider| provider.provider == name) + .unwrap_or_else(|| panic!("provider '{name}' missing from models.yaml")) + } + + #[test] + fn embedded_catalog_invariants() { + let providers = embedded_catalog(); + assert!(providers.len() >= 24); + let order: Vec<&str> = providers + .iter() + .map(|provider| provider.provider.as_str()) + .collect(); + assert_eq!(order, KNOWN_PROVIDERS); + + assert!(embedded_provider(&providers, "xai").oauth.is_some()); + for name in ["jina", "voyageai"] { + assert!( + embedded_provider(&providers, name) + .models + .iter() + .any(|model| model.model_type == "embedding"), + "{name} lost its embedding models" + ); + } + assert!( + embedded_provider(&providers, "cohere") + .models + .iter() + .any(|model| model.model_type == "reranker"), + "cohere lost its reranker models" + ); + + for provider in &providers { + for model in &provider.models { + // A :thinking model without a patch must at least alias a real + // wire name (qianwen twins invert a base-model patch instead). + if model.name.ends_with(":thinking") { + assert!( + model.patch.is_some() || model.real_name.is_some(), + "{}:{} has neither patch nor real_name", + provider.provider, + model.name + ); + } + if model.model_type == "chat" { + let serialized = serde_yaml::to_value(model).unwrap(); + assert!( + serialized + .as_mapping() + .unwrap() + .keys() + .any(|key| !matches!(key.as_str(), Some("name" | "type"))), + "{}:{} is an all-default chat entry", + provider.provider, + model.name + ); + } + } + } + + for model in &embedded_provider(&providers, "claude").models { + if model.model_type == "chat" { + assert!( + model.require_max_tokens, + "claude:{} is missing require_max_tokens", + model.name + ); + } + } + } + + #[test] + fn embedded_catalog_roundtrip() { + let providers = embedded_catalog(); + let yaml = serde_yaml::to_string(&providers).unwrap(); + let reparsed: Vec = serde_yaml::from_str(&yaml).unwrap(); + assert_eq!(reparsed.len(), providers.len()); + for (reparsed, original) in reparsed.iter().zip(&providers) { + assert_eq!(reparsed.provider, original.provider); + assert_eq!(reparsed.models.len(), original.models.len()); + } + } + /// Guards against upstream id-shape drift by comparing live API model /// names against the embedded models.yaml. Run manually: /// `cargo test idshape_guard_live -- --ignored --nocapture` diff --git a/src/client/model.rs b/src/client/model.rs index 2c84ec8..cbd9623 100644 --- a/src/client/model.rs +++ b/src/client/model.rs @@ -358,7 +358,7 @@ impl ModelData { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProviderModels { pub provider: String, - #[serde(default)] + #[serde(default, skip_serializing_if = "Option::is_none")] pub oauth: Option, pub models: Vec, } diff --git a/src/main.rs b/src/main.rs index 9309149..605c689 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,7 @@ extern crate log; use crate::cli::Cli; use crate::client::{ - ModelType, call_chat_completions, call_chat_completions_streaming, list_models, oauth, + ModelType, call_chat_completions, call_chat_completions_streaming, catalog, list_models, oauth, }; use crate::config::instructions::WORKSPACE_INSTRUCTIONS_FILE_NAME; use crate::config::{ @@ -101,6 +101,7 @@ async fn main() -> Result<()> { let info_flag = cli.info || cli.sync_models + || cli.generate_models.is_some() || cli.list_models || cli.list_roles || cli.list_agents @@ -295,6 +296,10 @@ async fn run( return sync_models(&url, abort_signal.clone()).await; } + if let Some(path) = &cli.generate_models { + return catalog::generate_models_file(path).await; + } + if cli.list_models { for model in list_models(ctx.app.config.as_ref(), ModelType::Chat) { println!("{}", model.id()); From 141289d973171114133d6fb644844df0c6fcc704 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 13:53:12 -0600 Subject: [PATCH 04/30] feat: Improved the architect, explore, and sisyphus agents to have a team mode so they can all work in parallel as a team and updated the parallel-research skill to make explicit mention of this functionality --- assets/agents/architect/config.yaml | 8 ++++- assets/agents/explore/config.yaml | 11 +++++- assets/agents/sisyphus/config.yaml | 7 +++- assets/skills/parallel-research/SKILL.md | 45 ++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/assets/agents/architect/config.yaml b/assets/agents/architect/config.yaml index 25768e0..a8a4c65 100644 --- a/assets/agents/architect/config.yaml +++ b/assets/agents/architect/config.yaml @@ -6,7 +6,7 @@ description: | plan-conformance check (plus a black-box usage-pattern probe for consumer-facing surface), and finishes with ONE draft PR (CI checks watched to green) plus tracked follow-up tasks. Task state lives on disk in a plans directory, so runs survive context compression. -version: 2.1.0 +version: 2.2.0 agent_session: temp auto_continue: true max_auto_continues: 100 @@ -150,6 +150,12 @@ instructions: | {{project_dir}} — and `librarian` for unfamiliar external libraries — to confirm the design's assumptions before sizing. Do NOT guess file/symbol names — verify them. + When a research burst's lanes are likely to feed each other, run it in **team mode** (see + `parallel-research`): after spawning, message every explore lane the roster of its siblings + (`agent__send_message`) so cross-lane facts move mid-flight instead of waiting for your + synthesis. (`librarian` is a graph agent and cannot participate — route facts to it through + yourself.) + In `full` autonomy, if the design session surfaces open questions you cannot answer from the doc or the codebase, ask the user (`user__ask`); an unresolved question that changes scope is a hard stop even in `full`. diff --git a/assets/agents/explore/config.yaml b/assets/agents/explore/config.yaml index d2ea20a..2a69e3f 100644 --- a/assets/agents/explore/config.yaml +++ b/assets/agents/explore/config.yaml @@ -1,6 +1,6 @@ name: explore description: Fast codebase exploration agent - finds patterns, structures, and relevant files. Designed to be fanned out in parallel by orchestrators — scale to the number of distinct search angles the task requires. -version: 3.1.0 +version: 3.2.0 skills_enabled: true enabled_skills: @@ -42,6 +42,15 @@ instructions: | If the prompt says "find auth middleware", you find auth middleware. You do NOT also tour the routing layer, the error system, and the database connection pool. Narrow scope is the contract. + ## Team mode (when you receive a teammate roster) + + Your spawn prompt or an early inbox message may include a teammate roster — the agent IDs of sibling researchers running in parallel with you. If it does: + + - When you find a specific fact that belongs to a TEAMMATE's slice (a file they should read, a symbol they're hunting, a config flag that redirects their search), send it directly: `agent__send_message --id --message ""`. Facts only — file:line, exact names, verbatim snippets. Never chat or status updates. + - Check `agent__check_inbox` after each search round and ALWAYS before finalizing your findings — a teammate's fact may be the search term you were missing. + - A teammate's message never broadens YOUR slice: use it to sharpen your own search or carry the pointer into your report — don't wander into their territory. + - No roster → no messaging. Work your slice and report as usual. + ## Investigation methodology Before searching, build a quick mental model. Then narrow in. Then read. diff --git a/assets/agents/sisyphus/config.yaml b/assets/agents/sisyphus/config.yaml index c27d856..843b68a 100644 --- a/assets/agents/sisyphus/config.yaml +++ b/assets/agents/sisyphus/config.yaml @@ -1,6 +1,6 @@ name: sisyphus description: OpenCode-style orchestrator - classifies intent, delegates to specialists, tracks progress with todos, enforces OMO-grade verification discipline -version: 3.9.0 +version: 3.10.0 agent_session: temp auto_continue: true @@ -229,6 +229,11 @@ instructions: | Never artificially cap at a small number. If there are 10 distinct things to find, spawn 10 agents. The system limit is the only ceiling that matters. + If the slices are likely to FEED each other (adjacent modules; one lane's discovery redirects another's search), run the burst in team mode per `parallel-research`: after spawning, send + every explorer the roster of its siblings (`agent__send_message --id --message "Teammate roster: =, ... Message a teammate directly when you find a fact in THEIR slice; + check your inbox after each search round and before finalizing."`) so cross-slice facts move directly between lanes instead of waiting for your synthesis. Never do this with reviewer + lanes — reviewer verdicts stay independent. + ### The wait protocol After spawning background agents: diff --git a/assets/skills/parallel-research/SKILL.md b/assets/skills/parallel-research/SKILL.md index 43e426b..93a5dab 100644 --- a/assets/skills/parallel-research/SKILL.md +++ b/assets/skills/parallel-research/SKILL.md @@ -39,6 +39,51 @@ Allowed: Duplicate searches waste tokens, may contradict the delegate, and defeat the point of parallelism. +## Team mode (optional): let lanes message each other + +Every spawned LLM-loop agent has a built-in mailbox (`agent__send_message` / +`agent__check_inbox`) — no configuration needed. By default parallel lanes are isolated, and for +most bursts that is correct. Turn on team mode when one lane's discovery is likely to CHANGE what +another lane should look for: + +- **Adjacent slices of one system** — the routes lane finds the middleware file the token lane + is hunting for. +- **Mixed-angle bursts** — one lane discovers the project already vendors library X, which + should redirect another lane's search immediately, not after synthesis. +- **Long-running lanes** where routing a fact through you costs a full collect/re-spawn round + trip. + +Skip team mode when lanes are genuinely independent (survey-style sweeps, one-agent-per-module +audits). And NEVER wire reviewer lanes together (`adversary`, `code-reviewer`, +`security-reviewer`, `probe`): their verdicts derive value from independence — shared chatter +biases them. + +### Orchestrator protocol + +Agent IDs exist only after spawning, so the roster travels as a message: + +1. Spawn the whole burst as usual. +2. Immediately send EVERY lane the roster: + + `agent__send_message --id --message "Teammate roster: middleware=, + tokens=. If you find a specific fact that belongs to a teammate's lane, message them + directly; check your inbox after each search round and before finalizing."` + +3. Synthesis is unchanged: every lane still reports its findings back to YOU. Teammate messages + move facts sideways mid-flight; they never replace the final report. + +Graph agents (`librarian`) cannot participate — their nodes have no inbox tools. A fact that +affects a graph lane routes through you: collect, then re-spawn or refine that lane's prompt. + +### Worker rules (for agents that receive a roster) + +- A message is one or two sentences of specific fact: file:line, exact symbol/term, verbatim + quote — never chat, status updates, or opinions. +- A teammate's message never broadens YOUR slice: use it to sharpen your own search, or carry + the pointer into your report. Entirely outside your slice → ignore it. +- Check your inbox after each search round and ALWAYS before finalizing — a teammate's fact may + be the search term you were missing. + ## Stop conditions Stop searching when: From af7d37890ce7bd4ad96f14d41d1d3c126c22629a Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 14:18:16 -0600 Subject: [PATCH 05/30] fix: children subagents couldn't properly send messages to parent inboxes --- assets/agents/code-reviewer/config.yaml | 2 +- assets/agents/file-reviewer/config.yaml | 2 +- src/config/macros.rs | 1 + src/config/prompts.rs | 2 +- src/config/request_context.rs | 47 +++++++ src/function/agents.rs | 177 ++++++++++++++++++------ 6 files changed, 188 insertions(+), 43 deletions(-) diff --git a/assets/agents/code-reviewer/config.yaml b/assets/agents/code-reviewer/config.yaml index c6168e5..f9f9ea7 100644 --- a/assets/agents/code-reviewer/config.yaml +++ b/assets/agents/code-reviewer/config.yaml @@ -147,7 +147,7 @@ instructions: | After spawning ALL file-reviewers (collecting their IDs), send each one a message with the roster: ``` - agent__send_message --to --message "SIBLING_ROSTER: + agent__send_message --id --message "SIBLING_ROSTER: - : reviewing - : reviewing ... diff --git a/assets/agents/file-reviewer/config.yaml b/assets/agents/file-reviewer/config.yaml index a52f805..0e13e10 100644 --- a/assets/agents/file-reviewer/config.yaml +++ b/assets/agents/file-reviewer/config.yaml @@ -71,7 +71,7 @@ instructions: | To alert a sibling: ``` - agent__send_message --to --message "ALERT: " + agent__send_message --id --message "ALERT: " ``` Check your inbox periodically for alerts from siblings: diff --git a/src/config/macros.rs b/src/config/macros.rs index 43776cc..c2c160d 100644 --- a/src/config/macros.rs +++ b/src/config/macros.rs @@ -69,6 +69,7 @@ pub async fn macro_execute( macro_ctx.parent_supervisor = ctx.parent_supervisor.clone(); macro_ctx.self_agent_id = ctx.self_agent_id.clone(); macro_ctx.inbox = ctx.inbox.clone(); + macro_ctx.parent_inbox = ctx.parent_inbox.clone(); macro_ctx.escalation_queue = ctx.escalation_queue.clone(); macro_ctx.current_depth = ctx.current_depth; macro_ctx.auto_continue_count = ctx.auto_continue_count; diff --git a/src/config/prompts.rs b/src/config/prompts.rs index db1e3ce..fa7d908 100644 --- a/src/config/prompts.rs +++ b/src/config/prompts.rs @@ -212,7 +212,7 @@ pub(in crate::config) const DEFAULT_TEAMMATE_INSTRUCTIONS: &str = indoc! {" ## Teammate Messaging You have tools to communicate with other agents running alongside you: - - `agent__send_message --id --message \"...\"`: Send a message to a sibling or parent agent. + - `agent__send_message --id --message \"...\"`: Send a message to a child or sibling agent by its id, or to your parent with the reserved id `parent`. - `agent__check_inbox`: Check for messages sent to you by other agents. If you are working alongside other agents (e.g. reviewing different files, exploring different areas): diff --git a/src/config/request_context.rs b/src/config/request_context.rs index 6e97778..f614356 100644 --- a/src/config/request_context.rs +++ b/src/config/request_context.rs @@ -327,6 +327,7 @@ pub struct RequestContext { pub parent_supervisor: Option>>, pub self_agent_id: Option, pub inbox: Option>, + pub parent_inbox: Option>, pub escalation_queue: Option>, pub notification_queue: Arc, pub current_depth: usize, @@ -364,6 +365,7 @@ impl RequestContext { parent_supervisor: None, self_agent_id: None, inbox: None, + parent_inbox: None, escalation_queue: None, notification_queue: Arc::new(NotificationQueue::new()), current_depth: 0, @@ -427,6 +429,7 @@ impl RequestContext { parent_supervisor: None, self_agent_id: None, inbox: None, + parent_inbox: None, escalation_queue: None, notification_queue: Arc::new(NotificationQueue::new()), current_depth: 0, @@ -479,6 +482,7 @@ impl RequestContext { parent_supervisor: self.parent_supervisor.clone(), self_agent_id: self.self_agent_id.clone(), inbox: self.inbox.clone(), + parent_inbox: self.parent_inbox.clone(), escalation_queue: self.escalation_queue.clone(), notification_queue: self.notification_queue.clone(), current_depth: self.current_depth, @@ -527,6 +531,7 @@ impl RequestContext { parent_supervisor: parent.supervisor.clone(), self_agent_id: Some(self_agent_id), inbox: Some(inbox), + parent_inbox: parent.inbox.clone(), escalation_queue: parent.escalation_queue.clone(), notification_queue: Arc::new(NotificationQueue::new()), current_depth, @@ -559,6 +564,12 @@ impl RequestContext { .clone() } + pub fn ensure_inbox(&mut self) -> Arc { + self.inbox + .get_or_insert_with(|| Arc::new(Inbox::new())) + .clone() + } + pub fn rag_cache(&self) -> &Arc { &self.app.rag_cache } @@ -4651,6 +4662,7 @@ impl RequestContext { } self.supervisor = supervisor; self.inbox = None; + self.parent_inbox = None; self.escalation_queue = None; self.notification_queue = Arc::new(NotificationQueue::new()); self.self_agent_id = None; @@ -4694,6 +4706,7 @@ impl RequestContext { self.parent_supervisor = None; self.self_agent_id = None; self.inbox = None; + self.parent_inbox = None; self.escalation_queue = None; self.notification_queue = Arc::new(NotificationQueue::new()); self.current_depth = 0; @@ -5958,6 +5971,40 @@ mod tests { assert!(ctx.inbox.is_none()); } + #[test] + fn parent_inbox_defaults_to_none() { + let ctx = create_test_ctx(); + + assert!(ctx.parent_inbox.is_none()); + } + + #[test] + fn ensure_inbox_allocates_once_and_is_stable() { + let mut ctx = create_test_ctx(); + let first = ctx.ensure_inbox(); + let second = ctx.ensure_inbox(); + + assert!(Arc::ptr_eq(&first, &second)); + assert!(ctx.inbox.is_some()); + } + + #[test] + fn new_for_child_inherits_parent_inbox() { + let mut parent = create_test_ctx(); + let parent_inbox = parent.ensure_inbox(); + let child = RequestContext::new_for_child( + Arc::clone(&parent.app), + &parent, + 1, + Arc::new(Inbox::new()), + "agent_test_1".to_string(), + ); + + let child_parent_inbox = child.parent_inbox.expect("child should see parent's inbox"); + + assert!(Arc::ptr_eq(&parent_inbox, &child_parent_inbox)); + } + #[test] fn escalation_queue_defaults_to_none() { let ctx = create_test_ctx(); diff --git a/src/function/agents.rs b/src/function/agents.rs index da795dd..0743611 100644 --- a/src/function/agents.rs +++ b/src/function/agents.rs @@ -27,6 +27,8 @@ pub const AGENT_FUNCTION_PREFIX: &str = "agent__"; pub const PENDING_TASKS_GUARDRAIL_MAX: u32 = 3; +pub const PARENT_ALIAS: &str = "parent"; + fn agent_permitted(whitelist: Option<&[String]>, target: &str) -> bool { match whitelist { None => true, @@ -462,7 +464,7 @@ pub fn teammate_function_declarations() -> Vec { vec![ FunctionDeclaration { name: format!("{AGENT_FUNCTION_PREFIX}send_message"), - description: "Send a text message to a sibling or child agent's inbox. Use to share cross-cutting findings or coordinate with teammates.".to_string(), + description: "Send a text message to another agent's inbox: a child or sibling by its agent id, or your parent via the reserved id 'parent'. Use to share cross-cutting findings or coordinate with teammates.".to_string(), parameters: JsonSchema { type_value: Some("object".to_string()), properties: Some(IndexMap::from([ @@ -470,7 +472,7 @@ pub fn teammate_function_declarations() -> Vec { "id".to_string(), JsonSchema { type_value: Some("string".to_string()), - description: Some("The target agent ID".into()), + description: Some("The target agent ID, or the reserved id 'parent' to message the agent that spawned you".into()), ..Default::default() }, ), @@ -625,6 +627,7 @@ pub async fn run_agent_for_graph( let child_inbox = Arc::new(Inbox::new()); parent_ctx.ensure_root_escalation_queue(); + parent_ctx.ensure_inbox(); let child_abort = create_abort_signal(); let app_config = Arc::clone(&parent_ctx.app.config); @@ -807,6 +810,7 @@ async fn handle_spawn(ctx: &mut RequestContext, args: &Value) -> Result { let child_inbox = Arc::new(Inbox::new()); ctx.ensure_root_escalation_queue(); + ctx.ensure_inbox(); let child_abort = create_abort_signal(); @@ -1224,52 +1228,74 @@ fn handle_send_message(ctx: &mut RequestContext, args: &Value) -> Result .and_then(Value::as_str) .ok_or_else(|| anyhow!("'message' is required"))?; - let sender = ctx + // The routable identity of this sender from the recipient's point of view. + // Only spawned agents have a `self_agent_id`; the name fallback covers + // contexts that cannot be replied to anyway (e.g. macro contexts). + let self_label = ctx .self_agent_id .clone() .or_else(|| ctx.agent.as_ref().map(|a| a.name().to_string())) - .unwrap_or_else(|| "parent".to_string()); + .unwrap_or_else(|| PARENT_ALIAS.to_string()); - let inbox = ctx + let deliver = |inbox: &Arc, from: String| { + inbox.deliver(Envelope { + from, + to: id.to_string(), + payload: EnvelopePayload::Text { + content: message.to_string(), + }, + timestamp: Utc::now(), + }); + }; + + if id == PARENT_ALIAS { + return match ctx.parent_inbox.as_ref() { + Some(inbox) => { + deliver(inbox, self_label); + Ok(json!({ + "status": "ok", + "message": "Message delivered to your parent agent's inbox; it will be read on the parent's next check_inbox call.", + })) + } + None => Ok(json!({ + "status": "error", + "message": "You have no parent agent — the reserved id 'parent' is only routable from a spawned subagent.", + })), + }; + } + + let own_child_inbox = ctx .supervisor .as_ref() .and_then(|sup| sup.read().inbox(id).cloned()); - - let inbox = inbox.or_else(|| { - ctx.parent_supervisor - .as_ref() - .and_then(|sup| sup.read().inbox(id).cloned()) - }); - - match inbox { - Some(inbox) => { - inbox.deliver(Envelope { - from: sender, - to: id.to_string(), - payload: EnvelopePayload::Text { - content: message.to_string(), - }, - timestamp: Utc::now(), - }); - - Ok(json!({ - "status": "ok", - "message": format!("Message delivered to agent '{id}'"), - })) - } - None => { - if is_job_task(ctx.supervisor.as_ref(), id) - || is_job_task(ctx.parent_supervisor.as_ref(), id) - { - return Ok(job_id_teaching_error(id)); - } - - Ok(json!({ - "status": "error", - "message": format!("No agent found with id '{id}'. Agent may not exist or may have already completed."), - })) - } + if let Some(inbox) = own_child_inbox { + deliver(&inbox, PARENT_ALIAS.to_string()); + return Ok(json!({ + "status": "ok", + "message": format!("Message delivered to agent '{id}'"), + })); } + + let sibling_inbox = ctx + .parent_supervisor + .as_ref() + .and_then(|sup| sup.read().inbox(id).cloned()); + if let Some(inbox) = sibling_inbox { + deliver(&inbox, self_label); + return Ok(json!({ + "status": "ok", + "message": format!("Message delivered to agent '{id}'"), + })); + } + + if is_job_task(ctx.supervisor.as_ref(), id) || is_job_task(ctx.parent_supervisor.as_ref(), id) { + return Ok(job_id_teaching_error(id)); + } + + Ok(json!({ + "status": "error", + "message": format!("No agent found with id '{id}'. Agent may not exist or may have already completed."), + })) } fn handle_check_inbox(ctx: &mut RequestContext) -> Result { @@ -2019,6 +2045,9 @@ mod tests { EnvelopePayload::Text { content } => assert_eq!(content, "hello from parent"), _ => panic!("expected text payload"), } + // A child can only route replies through the reserved alias, so + // messages from the spawning agent must be labeled with it. + assert_eq!(msgs[0].from, PARENT_ALIAS); } #[test] @@ -2029,6 +2058,74 @@ mod tests { assert_eq!(result["status"], "error"); } + #[test] + fn handle_send_message_parent_alias_delivers_to_parent_inbox() { + let mut ctx = ctx_with_supervisor(4, 3); + let parent_inbox = Arc::new(Inbox::new()); + ctx.parent_inbox = Some(Arc::clone(&parent_inbox)); + ctx.self_agent_id = Some("agent_explore_abc123".to_string()); + + let result = handle_send_message( + &mut ctx, + &json!({"id": "parent", "message": "hello from child"}), + ) + .unwrap(); + assert_eq!(result["status"], "ok"); + + let msgs = parent_inbox.drain(); + assert_eq!(msgs.len(), 1); + // The parent can reply through its own supervisor, so the sender must + // be the child's routable agent id. + assert_eq!(msgs[0].from, "agent_explore_abc123"); + match &msgs[0].payload { + EnvelopePayload::Text { content } => assert_eq!(content, "hello from child"), + _ => panic!("expected text payload"), + } + } + + #[test] + fn handle_send_message_parent_alias_without_parent_errors() { + let mut ctx = ctx_with_supervisor(4, 3); + let result = + handle_send_message(&mut ctx, &json!({"id": "parent", "message": "hi"})).unwrap(); + assert_eq!(result["status"], "error"); + assert!( + result["message"] + .as_str() + .unwrap() + .contains("no parent agent") + ); + } + + #[test] + fn handle_send_message_to_sibling_labels_sender_with_own_id() { + let mut ctx = RequestContext::new(default_app_state(), WorkingMode::Cmd); + ctx.self_agent_id = Some("agent_explore_sender".to_string()); + + let mut sibling_ctx = ctx_with_supervisor(4, 3); + register_fake_agent(&mut sibling_ctx, "agent_explore_receiver", "explore"); + ctx.parent_supervisor = sibling_ctx.supervisor.clone(); + + let result = handle_send_message( + &mut ctx, + &json!({"id": "agent_explore_receiver", "message": "hi sibling"}), + ) + .unwrap(); + assert_eq!(result["status"], "ok"); + + let inbox = ctx + .parent_supervisor + .as_ref() + .unwrap() + .read() + .inbox("agent_explore_receiver") + .unwrap() + .clone(); + let msgs = inbox.drain(); + assert_eq!(msgs.len(), 1); + assert_eq!(msgs[0].from, "agent_explore_sender"); + } + #[test] fn handle_check_inbox_with_messages() { let mut ctx = ctx_with_supervisor(4, 3); From b19ac807834b566c9d9d746b17e0c9318f6a3ecd Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 14:34:05 -0600 Subject: [PATCH 06/30] feat: refresh the model catalog from live API data at runtime --- assets/config-template.yaml | 7 +- assets/sbx-kit/spec.yaml | 1 + config.example.yaml | 9 +- src/client/catalog.rs | 138 ++++++++++++++++++- src/config/app_config.rs | 85 +++++++++++- src/config/mod.rs | 265 +++++++++++++++++++++++++++++++++--- src/main.rs | 6 +- 7 files changed, 481 insertions(+), 30 deletions(-) diff --git a/assets/config-template.yaml b/assets/config-template.yaml index 02ca30f..cd18c10 100644 --- a/assets/config-template.yaml +++ b/assets/config-template.yaml @@ -244,8 +244,13 @@ right_prompt: null # If null, uses the built-in default: # ---- Miscellaneous ---- user_agent: null # Set User-Agent HTTP header, use `auto` for coyote/ save_shell_history: true # Whether to save shell execution command to the history file -sync_models_url: null # URL to sync model changes from. If null, uses the built-in default: +sync_models_url: null # Where `--sync-models` and the automatic refresh get model data from. + # If null, fetches fresh model data from models.dev + OpenRouter. + # Set a URL to mirror a full models.yaml instead, e.g.: # https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml +sync_models_auto: true # Whether to refresh the model catalog in the background on REPL startup + # once it goes stale +sync_models_ttl_hours: 24 # Hours before the synced model catalog is considered stale # ---- Clients ---- # See the [Clients documentation](https://github.com/Dark-Alex-17/coyote/wiki/Clients) for more details diff --git a/assets/sbx-kit/spec.yaml b/assets/sbx-kit/spec.yaml index 5c972d3..4b17b70 100644 --- a/assets/sbx-kit/spec.yaml +++ b/assets/sbx-kit/spec.yaml @@ -67,6 +67,7 @@ permissions: - 'api.cohere.ai' - 'api.groq.com' - 'openrouter.ai' + - 'models.dev' - 'api.ai21.com' - 'api.cloudflare.com' - 'api.deepinfra.com' diff --git a/config.example.yaml b/config.example.yaml index 2b2a8eb..ebb5d8c 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -26,8 +26,13 @@ light_theme: false # Activates a light color theme when true. env: # ---- Miscellaneous ---- user_agent: null # Set User-Agent HTTP header, use `auto` for coyote/ save_shell_history: true # Whether to save shell execution command to the history file -sync_models_url: > # URL to sync model changes from - https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml +sync_models_url: null # Where `--sync-models` and the automatic refresh get model data from. + # If null, fetches fresh model data from models.dev + OpenRouter. + # Set a URL to mirror a full models.yaml instead, e.g.: + # https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml +sync_models_auto: true # Whether to refresh the model catalog in the background on REPL startup + # once it goes stale +sync_models_ttl_hours: 24 # Hours before the synced model catalog is considered stale # ---- REPL Prompt ---- # Custom REPL left/right prompts; see the [REPL Prompt Documentation](https://github.com/Dark-Alex-17/coyote/wiki/REPL-Prompt) for more information diff --git a/src/client/catalog.rs b/src/client/catalog.rs index cec2d46..e1b3aa5 100644 --- a/src/client/catalog.rs +++ b/src/client/catalog.rs @@ -8,7 +8,7 @@ use fancy_regex::Regex; use log::warn; use serde::{Deserialize, Serialize}; use serde_json::Value; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::sync::LazyLock; pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); @@ -491,6 +491,43 @@ pub fn build_catalog_strict( Ok(build.providers) } +/// Replaces or inserts the entry of every degraded/failed provider from the +/// embedded catalog, preserving the fixed provider order of +/// [`PROVIDER_SOURCES`]. A synced override must never carry fewer models for a +/// provider than the embedded catalog: an upstream outage would otherwise +/// shrink the model list until the next successful sync. +pub fn backfill_embedded_floor(build: &mut CatalogBuild, embedded: &[ProviderModels]) { + let needs_backfill: HashSet<&str> = build + .degraded + .iter() + .chain(build.failed.iter()) + .map(String::as_str) + .collect(); + if needs_backfill.is_empty() { + return; + } + let mut providers = Vec::with_capacity(PROVIDER_SOURCES.len()); + for source in &PROVIDER_SOURCES { + let built = build + .providers + .iter() + .position(|provider| provider.provider == source.provider) + .map(|position| build.providers.remove(position)); + if needs_backfill.contains(source.provider) + && let Some(entry) = embedded + .iter() + .find(|provider| provider.provider == source.provider) + { + providers.push(entry.clone()); + continue; + } + if let Some(built) = built { + providers.push(built); + } + } + build.providers = providers; +} + const GENERATED_HEADER: &str = "# GENERATED by 'coyote --generate-models' — DO NOT hand-edit model data.\n\ # Hand-owned behavior lives in quirks.yaml; this file is the embedded offline fallback.\n"; @@ -1473,6 +1510,105 @@ rules: } } + fn provider_entry(provider: &str, model_names: &[&str]) -> ProviderModels { + ProviderModels { + provider: provider.to_string(), + oauth: None, + models: model_names + .iter() + .map(|name| ModelData::new(name)) + .collect(), + } + } + + #[test] + fn backfill_replaces_degraded_and_inserts_failed_providers() { + let mut build = CatalogBuild { + providers: vec![ + provider_entry("openai", &["gpt-fresh"]), + provider_entry("gemini", &["quirks-only"]), + ], + degraded: vec!["gemini".to_string()], + failed: vec!["claude".to_string()], + }; + let embedded = vec![ + provider_entry("openai", &["gpt-old-1", "gpt-old-2"]), + provider_entry("gemini", &["gem-1", "gem-2", "gem-3"]), + provider_entry("claude", &["claude-1"]), + ]; + + backfill_embedded_floor(&mut build, &embedded); + + let order: Vec<&str> = build + .providers + .iter() + .map(|provider| provider.provider.as_str()) + .collect(); + assert_eq!(order, ["openai", "gemini", "claude"]); + assert_eq!(find_provider(&build, "openai").models.len(), 1); + assert_eq!(find_provider(&build, "gemini").models.len(), 3); + assert_eq!(find_provider(&build, "claude").models.len(), 1); + assert_eq!( + find_provider(&build, "openai").models[0].name, + "gpt-fresh", + "providers with live API data must not be touched" + ); + } + + #[test] + fn backfill_keeps_built_entry_when_embedded_lacks_the_provider() { + let mut build = CatalogBuild { + providers: vec![provider_entry("gemini", &["quirks-only"])], + degraded: vec!["gemini".to_string()], + failed: vec![], + }; + + backfill_embedded_floor(&mut build, &[]); + + assert_eq!(find_provider(&build, "gemini").models.len(), 1); + } + + #[test] + fn backfill_without_degraded_or_failed_is_a_no_op() { + let mut build = CatalogBuild { + providers: vec![provider_entry("openai", &["gpt-fresh"])], + degraded: vec![], + failed: vec![], + }; + + backfill_embedded_floor(&mut build, &[provider_entry("openai", &["a", "b"])]); + + assert_eq!(find_provider(&build, "openai").models.len(), 1); + } + + #[test] + fn backfill_restores_embedded_floor_for_unresolved_providers() { + let embedded: Vec = + serde_yaml::from_str(crate::client::MODELS_YAML).unwrap(); + let mut build = build_catalog(&make_sources(Some(MODELSDEV_FIXTURE), None), &ALL_QUIRKS); + let unresolved: Vec = build + .degraded + .iter() + .chain(build.failed.iter()) + .cloned() + .collect(); + assert!(!unresolved.is_empty()); + + backfill_embedded_floor(&mut build, &embedded); + + for provider in unresolved { + let built = find_provider(&build, &provider); + let floor = embedded + .iter() + .find(|embedded| embedded.provider == provider) + .unwrap(); + assert!( + built.models.len() >= floor.models.len(), + "provider '{provider}' shrank below the embedded floor" + ); + } + } + /// Guards against upstream id-shape drift by comparing live API model /// names against the embedded models.yaml. Run manually: /// `cargo test idshape_guard_live -- --ignored --nocapture` diff --git a/src/config/app_config.rs b/src/config/app_config.rs index 6235c1d..4cf4ef4 100644 --- a/src/config/app_config.rs +++ b/src/config/app_config.rs @@ -102,6 +102,8 @@ pub struct AppConfig { pub no_workspace_mcp: bool, pub no_workspace_macros: bool, pub sync_models_url: Option, + pub sync_models_auto: bool, + pub sync_models_ttl_hours: u64, pub clients: Vec, } @@ -188,6 +190,8 @@ impl Default for AppConfig { no_workspace_mcp: false, no_workspace_macros: false, sync_models_url: None, + sync_models_auto: true, + sync_models_ttl_hours: 24, clients: vec![], } @@ -276,6 +280,8 @@ impl AppConfig { no_workspace_mcp: false, no_workspace_macros: false, sync_models_url: config.sync_models_url, + sync_models_auto: config.sync_models_auto, + sync_models_ttl_hours: config.sync_models_ttl_hours, clients: config.clients, }; @@ -388,10 +394,8 @@ impl AppConfig { .ok_or_else(|| anyhow!("Editor not found. Please add the `editor` configuration or set the $EDITOR or $VISUAL environment variable.")) } - pub fn sync_models_url(&self) -> String { - self.sync_models_url - .clone() - .unwrap_or_else(|| super::SYNC_MODELS_URL.into()) + pub fn sync_models_url(&self) -> Option { + self.sync_models_url.clone() } pub fn light_theme(&self) -> bool { @@ -665,6 +669,13 @@ impl AppConfig { if let Some(v) = super::read_env_value::(&get_env_name("sync_models_url")) { self.sync_models_url = v; } + if let Some(Some(v)) = super::read_env_bool(&get_env_name("sync_models_auto")) { + self.sync_models_auto = v; + } + if let Some(Some(v)) = super::read_env_value::(&get_env_name("sync_models_ttl_hours")) + { + self.sync_models_ttl_hours = v; + } } } @@ -951,10 +962,70 @@ mod tests { } #[test] - fn sync_models_url_has_default() { + fn sync_models_url_defaults_to_none() { let app = AppConfig::default(); - let url = app.sync_models_url(); - assert!(!url.is_empty()); + assert!(app.sync_models_url().is_none()); + } + + #[test] + fn sync_models_auto_and_ttl_have_defaults() { + let app = AppConfig::default(); + assert!(app.sync_models_auto); + assert_eq!(app.sync_models_ttl_hours, 24); + } + + #[test] + #[serial_test::serial] + fn load_envs_overrides_sync_models_auto() { + let env_name = get_env_name("sync_models_auto"); + let prev = env::var_os(&env_name); + + let mut app = AppConfig::default(); + + unsafe { env::set_var(&env_name, "false") }; + app.load_envs(); + assert!(!app.sync_models_auto); + + unsafe { env::set_var(&env_name, "true") }; + app.load_envs(); + assert!(app.sync_models_auto); + + unsafe { env::remove_var(&env_name) }; + app.sync_models_auto = false; + app.load_envs(); + assert!(!app.sync_models_auto); + + unsafe { + match prev { + Some(v) => env::set_var(&env_name, v), + None => env::remove_var(&env_name), + } + } + } + + #[test] + #[serial_test::serial] + fn load_envs_overrides_sync_models_ttl_hours() { + let env_name = get_env_name("sync_models_ttl_hours"); + let prev = env::var_os(&env_name); + + let mut app = AppConfig::default(); + + unsafe { env::set_var(&env_name, "6") }; + app.load_envs(); + assert_eq!(app.sync_models_ttl_hours, 6); + + unsafe { env::remove_var(&env_name) }; + app.sync_models_ttl_hours = 48; + app.load_envs(); + assert_eq!(app.sync_models_ttl_hours, 48); + + unsafe { + match prev { + Some(v) => env::set_var(&env_name, v), + None => env::remove_var(&env_name), + } + } } #[test] diff --git a/src/config/mod.rs b/src/config/mod.rs index 0649765..372c59b 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -69,8 +69,9 @@ pub use self::tool_scope::{ }; pub use self::update::run_self_update; use crate::client::{ - self, ClientConfig, MessageContentToolCalls, Model, ModelType, OPENAI_COMPATIBLE_PROVIDERS, - ProviderModels, create_client_config, list_client_types, oauth, set_client_models_config, + self, ClientConfig, MODELS_YAML, MessageContentToolCalls, Model, ModelType, + OPENAI_COMPATIBLE_PROVIDERS, ProviderModels, catalog, create_client_config, list_client_types, + oauth, set_client_models_config, }; use crate::function::{FunctionDeclaration, Functions}; use crate::rag::Rag; @@ -99,6 +100,7 @@ use std::{ path::{Path, PathBuf}, process, sync::{Arc, OnceLock}, + time::SystemTime, }; pub const TEMP_ROLE_NAME: &str = "temp"; @@ -187,9 +189,6 @@ const GITIGNORE_FILE_NAME: &str = ".gitignore"; const CLIENTS_FIELD: &str = "clients"; -const SYNC_MODELS_URL: &str = - "https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml"; - const SUMMARIZATION_PROMPT: &str = "Summarize the discussion briefly in 200 words or less to use as a prompt for future context."; const SUMMARY_CONTEXT_PROMPT: &str = "This is a summary of the chat history as a recap: "; @@ -287,6 +286,8 @@ pub struct Config { pub user_agent: Option, pub save_shell_history: bool, pub sync_models_url: Option, + pub sync_models_auto: bool, + pub sync_models_ttl_hours: u64, pub clients: Vec, } @@ -370,6 +371,8 @@ impl Default for Config { user_agent: None, save_shell_history: true, sync_models_url: None, + sync_models_auto: true, + sync_models_ttl_hours: 24, clients: vec![], } @@ -513,26 +516,158 @@ pub fn list_sessions() -> Vec { list_file_names(default_sessions_dir(), ".yaml") } -pub async fn sync_models(url: &str, abort_signal: AbortSignal) -> Result<()> { - let content = abortable_run_with_spinner(fetch(url), "Fetching models.yaml", abort_signal) - .await - .with_context(|| format!("Failed to fetch '{url}'"))?; - println!("✓ Fetched '{url}'"); - let list = serde_yaml::from_str::>(&content) - .with_context(|| "Failed to parse models.yaml")?; +/// Refreshes the models override. With a URL, mirrors a full models.yaml from +/// it; without one, rebuilds the catalog from the live models.dev + OpenRouter +/// APIs merged with the embedded quirks. +pub async fn sync_models(url: Option<&str>, abort_signal: AbortSignal) -> Result<()> { + let list = match url { + Some(url) => { + let content = + abortable_run_with_spinner(fetch(url), "Fetching models.yaml", abort_signal) + .await + .with_context(|| format!("Failed to fetch '{url}'"))?; + println!("✓ Fetched '{url}'"); + parse_mirrored_models(&content)? + } + None => { + let sources = abortable_run_with_spinner( + async { Ok(catalog::fetch_sources().await) }, + "Fetching model data", + abort_signal, + ) + .await?; + let (list, warnings) = build_refreshed_catalog(&sources)?; + for warning in &warnings { + println!("{} {warning}", warning_text("WARNING:")); + } + list + } + }; + let model_override_path = write_models_override(list)?; + println!("✓ Updated '{}'", model_override_path.display()); + Ok(()) +} + +/// [`sync_models`] without a spinner or terminal output, for background use; +/// failures are returned for the caller to log. +pub async fn sync_models_quiet(url: Option<&str>) -> Result<()> { + let list = match url { + Some(url) => { + let content = fetch(url) + .await + .with_context(|| format!("Failed to fetch '{url}'"))?; + parse_mirrored_models(&content)? + } + None => { + let sources = catalog::fetch_sources().await; + let (list, warnings) = build_refreshed_catalog(&sources)?; + for warning in warnings { + debug!("{warning}"); + } + list + } + }; + write_models_override(list)?; + Ok(()) +} + +fn parse_mirrored_models(content: &str) -> Result> { + serde_yaml::from_str::>(content) + .with_context(|| "Failed to parse models.yaml") +} + +fn build_refreshed_catalog( + sources: &catalog::FetchedSources, +) -> Result<(Vec, Vec)> { + if sources.modelsdev.is_none() && sources.openrouter.is_none() { + bail!("Failed to fetch model data from both models.dev and OpenRouter"); + } + let mut warnings = Vec::new(); + if sources.modelsdev.is_none() { + warnings.push("Failed to fetch models.dev; continuing with OpenRouter data only".into()); + } + if sources.openrouter.is_none() { + warnings.push("Failed to fetch OpenRouter; continuing with models.dev data only".into()); + } + let mut build = catalog::build_catalog(sources, &catalog::ALL_QUIRKS); + if !build.degraded.is_empty() { + warnings.push(format!( + "No live API data for: {}; keeping their embedded models", + build.degraded.join(", ") + )); + } + if !build.failed.is_empty() { + warnings.push(format!( + "No models resolved for: {}; keeping their embedded models", + build.failed.join(", ") + )); + } + let embedded = serde_yaml::from_str::>(MODELS_YAML) + .with_context(|| "Failed to parse the embedded models.yaml")?; + catalog::backfill_embedded_floor(&mut build, &embedded); + if build.providers.is_empty() { + bail!("Model sync produced no providers"); + } + Ok((build.providers, warnings)) +} + +fn write_models_override(list: Vec) -> Result { let models_override = ModelsOverride { version: env!("CARGO_PKG_VERSION").to_string(), list, }; - let models_override_data = - serde_yaml::to_string(&models_override).with_context(|| "Failed to serde {}")?; + let models_override_data = serde_yaml::to_string(&models_override) + .with_context(|| "Failed to serialize the models override")?; let model_override_path = paths::models_override_file(); ensure_parent_exists(&model_override_path)?; - std::fs::write(&model_override_path, models_override_data) + // Write through a sibling .tmp file and rename it over the target so an + // interrupted write can never leave a truncated override behind. + let mut tmp_name = model_override_path.as_os_str().to_os_string(); + tmp_name.push(".tmp"); + let tmp_path = PathBuf::from(tmp_name); + std::fs::write(&tmp_path, models_override_data) + .with_context(|| format!("Failed to write to '{}'", tmp_path.display()))?; + std::fs::rename(&tmp_path, &model_override_path) .with_context(|| format!("Failed to write to '{}'", model_override_path.display()))?; - println!("✓ Updated '{}'", model_override_path.display()); - Ok(()) + Ok(model_override_path) +} + +/// A missing, unreadable, corrupt, or version-mismatched override is always +/// stale; a loadable one is stale once its mtime is older than the TTL. +fn models_override_is_stale(loadable: bool, mtime: Option, ttl_hours: u64) -> bool { + if !loadable { + return true; + } + let Some(mtime) = mtime else { + return true; + }; + match SystemTime::now().duration_since(mtime) { + Ok(age) => age.as_secs() > ttl_hours.saturating_mul(3600), + Err(_) => false, + } +} + +/// Fire-and-forget refresh of the models override on REPL startup. The +/// running session keeps its already-loaded catalog; the next start picks up +/// the refreshed file. +pub fn maybe_spawn_models_refresh(app: &AppConfig) { + if !app.sync_models_auto || app.dry_run { + return; + } + let loadable = paths::local_models_override().is_ok(); + let mtime = std::fs::metadata(paths::models_override_file()) + .and_then(|metadata| metadata.modified()) + .ok(); + if !models_override_is_stale(loadable, mtime, app.sync_models_ttl_hours) { + return; + } + let url = app.sync_models_url.clone(); + tokio::spawn(async move { + if let Err(err) = sync_models_quiet(url.as_deref()).await { + debug!("Background models refresh failed: {err}"); + } + }); } impl Config { @@ -1381,4 +1516,100 @@ clients: "placeholder should be preserved as a literal string in sandbox mode" ); } + + #[test] + fn unloadable_override_is_stale() { + assert!(models_override_is_stale(false, Some(SystemTime::now()), 24)); + assert!(models_override_is_stale(false, None, 24)); + } + + #[test] + fn unreadable_mtime_is_stale() { + assert!(models_override_is_stale(true, None, 24)); + } + + #[test] + fn fresh_loadable_override_is_not_stale() { + assert!(!models_override_is_stale(true, Some(SystemTime::now()), 24)); + } + + #[test] + fn ttl_boundary_controls_staleness() { + let ttl_hours = 24; + let ttl = std::time::Duration::from_secs(ttl_hours * 3600); + let margin = std::time::Duration::from_secs(300); + let just_fresh = SystemTime::now() - (ttl - margin); + let just_stale = SystemTime::now() - (ttl + margin); + assert!(!models_override_is_stale(true, Some(just_fresh), ttl_hours)); + assert!(models_override_is_stale(true, Some(just_stale), ttl_hours)); + } + + #[test] + fn future_mtime_is_not_stale() { + let future = SystemTime::now() + std::time::Duration::from_secs(3600); + assert!(!models_override_is_stale(true, Some(future), 24)); + } + + #[test] + #[serial_test::serial] + fn models_override_loading_and_atomic_write() { + use std::fs; + + let unique = SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos(); + let tmp_dir = std::env::temp_dir().join(format!("coyote-models-override-{unique}")); + fs::create_dir_all(&tmp_dir).unwrap(); + + let env_name = get_env_name("config_dir"); + let prev = env::var_os(&env_name); + unsafe { env::set_var(&env_name, &tmp_dir) }; + + let result = std::panic::catch_unwind(|| { + let path = paths::models_override_file(); + let entry = || ProviderModels { + provider: "openai".into(), + oauth: None, + models: vec![client::ModelData::new("gpt-test")], + }; + + // Missing override is unloadable. + assert!(paths::local_models_override().is_err()); + + // Corrupt override is unloadable. + fs::write(&path, "not: [valid").unwrap(); + assert!(paths::local_models_override().is_err()); + + // Version-mismatched override is unloadable. + let mismatched = ModelsOverride { + version: "0.0.0".into(), + list: vec![entry()], + }; + fs::write(&path, serde_yaml::to_string(&mismatched).unwrap()).unwrap(); + assert!(paths::local_models_override().is_err()); + + // write_models_override goes through the sibling .tmp file and + // leaves a loadable override behind. + let written = write_models_override(vec![entry()]).unwrap(); + assert_eq!(written, path); + let mut tmp_name = path.as_os_str().to_os_string(); + tmp_name.push(".tmp"); + assert!(!PathBuf::from(tmp_name).exists()); + let loaded = paths::local_models_override().unwrap(); + assert_eq!(loaded.len(), 1); + assert_eq!(loaded[0].provider, "openai"); + }); + + unsafe { + match prev { + Some(v) => env::set_var(&env_name, v), + None => env::remove_var(&env_name), + } + } + let _ = fs::remove_dir_all(&tmp_dir); + if let Err(panic) = result { + std::panic::resume_unwind(panic); + } + } } diff --git a/src/main.rs b/src/main.rs index 605c689..7181aa3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,8 @@ use crate::config::instructions::WORKSPACE_INSTRUCTIONS_FILE_NAME; use crate::config::{ Agent, AppConfig, AppState, CODE_ROLE, Config, EXPLAIN_SHELL_ROLE, Input, MemoryScope, RenderMode, RequestContext, SHELL_ROLE, TEMP_SESSION_NAME, WorkingMode, ensure_parent_exists, - install_builtins, list_agents, load_env_file, macro_execute, sync_models, + install_builtins, list_agents, load_env_file, macro_execute, maybe_spawn_models_refresh, + sync_models, }; use crate::config::{memory, paths}; use crate::function::agents::{GuardrailAction, check_pending_tasks_guardrail}; @@ -293,7 +294,7 @@ async fn run( ) -> Result<()> { if cli.sync_models { let url = ctx.app.config.sync_models_url(); - return sync_models(&url, abort_signal.clone()).await; + return sync_models(url.as_deref(), abort_signal.clone()).await; } if let Some(path) = &cli.generate_models { @@ -549,6 +550,7 @@ async fn run( if !*IS_STDOUT_TERMINAL { bail!("No TTY for REPL") } + maybe_spawn_models_refresh(&ctx.app.config); start_interactive(ctx).await } } From e137866c0eb705b93d8e1ae48d0511a388b71d41 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 14:57:06 -0600 Subject: [PATCH 07/30] feat: apply provider quirks to models created from a name Unknown chat/reranker models resolved through the create-from-name fallback now inherit their provider's family rules (require_max_tokens, reasoning defaults, etc.) from the quirks overlay, and the model completion table marks reasoning-capable models with a brain glyph. --- src/client/model.rs | 51 ++++++++++++++++++++++++++++++++++- src/config/request_context.rs | 13 +++++++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/client/model.rs b/src/client/model.rs index cbd9623..51d3a20 100644 --- a/src/client/model.rs +++ b/src/client/model.rs @@ -1,5 +1,7 @@ use super::{ - ApiPatch, MessageContentToolCalls, RequestPatch, list_all_models, list_client_names, + ApiPatch, MessageContentToolCalls, RequestPatch, + catalog::{ALL_QUIRKS, apply_rules, quirks_for}, + list_all_models, list_client_names, message::{Message, MessageContent, MessageContentPart}, }; @@ -77,6 +79,9 @@ impl Model { { let mut new_model = Self::new(client_name, model_name); new_model.data.model_type = model_type.to_string(); + if let Some(quirks) = quirks_for(&ALL_QUIRKS, client_name) { + apply_rules(quirks, &mut new_model.data); + } return Ok(new_model); } } @@ -132,6 +137,7 @@ impl Model { output_price, supports_vision, supports_function_calling, + reasoning_levels, .. } = &self.data; let max_input_tokens = stringify_option_value(max_input_tokens); @@ -145,6 +151,9 @@ impl Model { if *supports_function_calling { capabilities.push('⚒'); }; + if !reasoning_levels.is_empty() { + capabilities.push('🧠'); + }; let capabilities: String = capabilities .into_iter() .map(|v| format!("{v} ")) @@ -419,3 +428,43 @@ where None => "-".to_string(), } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::client::ClientConfig; + + #[test] + fn created_from_name_inherits_family_rules() { + // The model registries are process-wide caches seeded by a ctor (see + // the request_context tests) that registers a "claude" client; the + // config below documents the shape a real lookup would use. + let claude: ClientConfig = serde_yaml::from_str( + "type: claude\nmodels:\n - name: test-claude-embedder\n type: embedding\n", + ) + .unwrap(); + let config = AppConfig { + clients: vec![claude], + ..AppConfig::default() + }; + let model = Model::retrieve_model( + &config, + "claude:claude-definitely-not-a-real-model", + ModelType::Chat, + ) + .unwrap(); + assert_eq!(model.name(), "claude-definitely-not-a-real-model"); + assert_eq!(model.model_type(), ModelType::Chat); + assert!(model.data.require_max_tokens); + } + + #[test] + fn description_shows_brain_glyph() { + let mut model = Model::new("claude", "some-model"); + model.data.reasoning_levels = vec!["low".to_string(), "high".to_string()]; + assert!(model.description().contains('🧠')); + + model.data.reasoning_levels.clear(); + assert!(!model.description().contains('🧠')); + } +} diff --git a/src/config/request_context.rs b/src/config/request_context.rs index 6e97778..b2bdc09 100644 --- a/src/config/request_context.rs +++ b/src/config/request_context.rs @@ -5219,7 +5219,7 @@ mod tests { // with an empty client list, which would permanently pin every later // model lookup in this process to "unknown model" and make any test that // needs a resolvable model dependent on test ordering. Seed the caches - // before any test runs with one client, "test-seeded", exposing a single + // before any test runs with a client, "test-seeded", exposing a single // embedding model. Deliberately NO chat model: some tests assert that no // chat model is available, and chat lookups don't need one — a // "test-seeded:" chat id resolves through the create-from-name @@ -5239,8 +5239,17 @@ mod tests { embedder.model_type = "embedding".to_string(); config.models = vec![embedder]; } + // A claude client is seeded alongside it so tests can exercise + // provider-quirk inheritance through the create-from-name fallback. + // It deliberately carries one embedding model: an empty `models` list + // would fall back to the full embedded claude catalog and register + // real chat models, breaking the no-chat-model invariant above. + let claude: ClientConfig = serde_yaml::from_str( + "type: claude\nmodels:\n - name: test-claude-embedder\n type: embedding\n", + ) + .unwrap(); let config = AppConfig { - clients: vec![client], + clients: vec![client, claude], ..AppConfig::default() }; let _ = list_client_names(&config); From 01d0081321a2cbecd23f6361a32ad66cac8a4928 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 15:11:01 -0600 Subject: [PATCH 08/30] ci: regenerate the model catalog on a 12-hour schedule A scheduled workflow refreshes models.yaml from live API data, validates the result (rebuild, full test suite, catalog smoke test), and commits it back, so every build embeds a recent catalog. Also documents the dynamic model catalog in the README quick links. --- .github/workflows/update-models.yaml | 95 ++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 96 insertions(+) create mode 100644 .github/workflows/update-models.yaml diff --git a/.github/workflows/update-models.yaml b/.github/workflows/update-models.yaml new file mode 100644 index 0000000..99ee468 --- /dev/null +++ b/.github/workflows/update-models.yaml @@ -0,0 +1,95 @@ +name: Update models + +on: + schedule: + - cron: '0 */12 * * *' + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: update-models + cancel-in-progress: false + +defaults: + run: + shell: bash + +jobs: + update: + name: Update models + + runs-on: ubuntu-latest + + env: + RUSTFLAGS: --deny warnings + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: true + + - name: Install Rust Toolchain Components + uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Cache DuckDB Extensions + id: duckdb-extensions + uses: actions/cache@v4 + with: + path: ~/.duckdb/extensions + key: duckdb-ext-ubuntu-latest-${{ hashFiles('Cargo.lock') }} + + - name: Install DuckDB Extensions + if: steps.duckdb-extensions.outputs.cache-hit != 'true' + run: cargo test --all duckdb + + # Coyote loads its config before handling --generate-models/--list-models, and a + # bare non-TTY runner has no config file (and no way to answer the first-run + # prompt), so point COYOTE_CONFIG_DIR at a throwaway dir with a minimal stub. + - name: Write stub config + run: | + COYOTE_CONFIG_DIR="$(mktemp -d)" + printf 'clients:\n - type: openai\n - type: claude\n' > "$COYOTE_CONFIG_DIR/config.yaml" + echo "COYOTE_CONFIG_DIR=$COYOTE_CONFIG_DIR" >> "$GITHUB_ENV" + + - name: Regenerate model catalog + run: cargo run --release -- --generate-models + + - name: Detect catalog changes + id: diff + run: echo "changed=$(git diff --quiet models.yaml && echo false || echo true)" >> "$GITHUB_OUTPUT" + + # The binary built by the regeneration step embedded the OLD models.yaml (the file + # was rewritten after that build), so rebuild before smoke-testing: cargo tracks + # include_str! inputs, making this a cheap incremental rebuild of the final crate. + - name: Rebuild with the refreshed catalog + if: steps.diff.outputs.changed == 'true' + run: cargo build --release + + - name: Test + if: steps.diff.outputs.changed == 'true' + run: cargo test --all + + - name: Smoke test the refreshed catalog + if: steps.diff.outputs.changed == 'true' + run: | + COUNT=$(./target/release/coyote --list-models | wc -l) + echo "Listed $COUNT chat models" + test "$COUNT" -ge 40 + + # Pushes made with the default GITHUB_TOKEN do not trigger other workflows, + # so this push cannot start a workflow loop. The pull --rebase covers a human + # push landing mid-run; if the race is lost anyway, the next cron run retries. + - name: Commit and push + if: steps.diff.outputs.changed == 'true' + run: | + git add models.yaml + git checkout -- . + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git commit -m 'chore: refresh generated model catalog' + git pull --rebase origin main + git push origin main diff --git a/README.md b/README.md index 4d5030e..fbcfd8a 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration g * [Client Configurations](https://github.com/Dark-Alex-17/coyote/wiki/Clients): Configuration instructions for various LLM providers. * [Authentication (API Key & OAuth)](https://github.com/Dark-Alex-17/coyote/wiki/Clients#authentication): Authenticate with API keys or OAuth for subscription-based access. * [Patching API Requests](https://github.com/Dark-Alex-17/coyote/wiki/Patches): Learn how to patch API requests for advanced customization. +* [Dynamic Model Catalog](https://github.com/Dark-Alex-17/coyote/wiki/Model-Catalog): Coyote's model catalog (context windows, pricing, capabilities) is generated from live API data, refreshed automatically in the background, and mirrorable for air-gapped setups — new models show up without waiting for a release. * [Custom Themes](https://github.com/Dark-Alex-17/coyote/wiki/Themes): Change the look and feel of Coyote to your preferences with custom themes. * [History](#history): A history of how Coyote came to be. From 34d79651a8f6979f4df6f926642d257a36bb7e57 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 15:48:47 -0600 Subject: [PATCH 09/30] docs: Corrected typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fbcfd8a..f3a39e8 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration g * [Client Configurations](https://github.com/Dark-Alex-17/coyote/wiki/Clients): Configuration instructions for various LLM providers. * [Authentication (API Key & OAuth)](https://github.com/Dark-Alex-17/coyote/wiki/Clients#authentication): Authenticate with API keys or OAuth for subscription-based access. * [Patching API Requests](https://github.com/Dark-Alex-17/coyote/wiki/Patches): Learn how to patch API requests for advanced customization. -* [Dynamic Model Catalog](https://github.com/Dark-Alex-17/coyote/wiki/Model-Catalog): Coyote's model catalog (context windows, pricing, capabilities) is generated from live API data, refreshed automatically in the background, and mirrorable for air-gapped setups — new models show up without waiting for a release. +* [Dynamic Model Catalog](https://github.com/Dark-Alex-17/coyote/wiki/Model-Catalog): Coyote's model catalog (context windows, pricing, capabilities) is generated from live API data, refreshed automatically in the background, and mirrorable for air-gapped setups. New models show up without waiting for a release. * [Custom Themes](https://github.com/Dark-Alex-17/coyote/wiki/Themes): Change the look and feel of Coyote to your preferences with custom themes. * [History](#history): A history of how Coyote came to be. From 47d2edf303d174fb5fb1b1a00149c02a4bfbcb7e Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 16:02:41 -0600 Subject: [PATCH 10/30] fix: memoize quirk glob patterns and isolate concurrent override writes --- src/client/catalog.rs | 19 +++++++++++++++++-- src/config/mod.rs | 19 ++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/client/catalog.rs b/src/client/catalog.rs index e1b3aa5..9742849 100644 --- a/src/client/catalog.rs +++ b/src/client/catalog.rs @@ -9,7 +9,7 @@ use log::warn; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::{HashMap, HashSet}; -use std::sync::LazyLock; +use std::sync::{Arc, LazyLock, Mutex}; pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); @@ -815,8 +815,23 @@ pub fn quirks_for<'a>( }) } +/// Compiled patterns are memoized: catalog builds evaluate every rule, exclude, +/// and variant glob against every model, so recompiling per call would dominate +/// the merge as the catalog grows. +static GLOB_CACHE: LazyLock>>> = + LazyLock::new(|| Mutex::new(HashMap::new())); + fn glob_matches(pattern: &str, name: &str) -> bool { - compile_glob(pattern).is_match(name).unwrap_or(false) + let regex = { + let mut cache = GLOB_CACHE + .lock() + .unwrap_or_else(|poison| poison.into_inner()); + cache + .entry(pattern.to_string()) + .or_insert_with(|| Arc::new(compile_glob(pattern))) + .clone() + }; + regex.is_match(name).unwrap_or(false) } /// Translates a glob pattern (`*` = any run of characters, `?` = exactly one) diff --git a/src/config/mod.rs b/src/config/mod.rs index 372c59b..2873817 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -621,15 +621,20 @@ fn write_models_override(list: Vec) -> Result { let model_override_path = paths::models_override_file(); ensure_parent_exists(&model_override_path)?; - // Write through a sibling .tmp file and rename it over the target so an - // interrupted write can never leave a truncated override behind. + // Write through a sibling temp file and rename it over the target so an + // interrupted write can never leave a truncated override behind. The temp + // name embeds the pid so concurrent coyote processes (auto-refresh racing + // a manual --sync-models) never interleave writes into the same file. let mut tmp_name = model_override_path.as_os_str().to_os_string(); - tmp_name.push(".tmp"); + tmp_name.push(format!(".{}.tmp", std::process::id())); let tmp_path = PathBuf::from(tmp_name); std::fs::write(&tmp_path, models_override_data) .with_context(|| format!("Failed to write to '{}'", tmp_path.display()))?; - std::fs::rename(&tmp_path, &model_override_path) - .with_context(|| format!("Failed to write to '{}'", model_override_path.display()))?; + if let Err(error) = std::fs::rename(&tmp_path, &model_override_path) { + let _ = std::fs::remove_file(&tmp_path); + return Err(error) + .with_context(|| format!("Failed to write to '{}'", model_override_path.display())); + } Ok(model_override_path) } @@ -1589,12 +1594,12 @@ clients: fs::write(&path, serde_yaml::to_string(&mismatched).unwrap()).unwrap(); assert!(paths::local_models_override().is_err()); - // write_models_override goes through the sibling .tmp file and + // write_models_override goes through the sibling temp file and // leaves a loadable override behind. let written = write_models_override(vec![entry()]).unwrap(); assert_eq!(written, path); let mut tmp_name = path.as_os_str().to_os_string(); - tmp_name.push(".tmp"); + tmp_name.push(format!(".{}.tmp", std::process::id())); assert!(!PathBuf::from(tmp_name).exists()); let loaded = paths::local_models_override().unwrap(); assert_eq!(loaded.len(), 1); From 078e5c757debb928121979194e158ba0cc93cc48 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 12:49:56 -0600 Subject: [PATCH 11/30] feat: add hand-owned model quirks overlay --- quirks.yaml | 880 ++++++++++++++++++++++++++++++++++++++++++ src/client/catalog.rs | 327 ++++++++++++++++ src/client/mod.rs | 1 + src/client/model.rs | 6 +- 4 files changed, 1211 insertions(+), 3 deletions(-) create mode 100644 quirks.yaml create mode 100644 src/client/catalog.rs diff --git a/quirks.yaml b/quirks.yaml new file mode 100644 index 0000000..65b2653 --- /dev/null +++ b/quirks.yaml @@ -0,0 +1,880 @@ +# Hand-owned model behavior overlay. +# +# Everything in this file is curated by hand and layered on top of API-sourced +# model data: oauth blocks, request patches, wire-name aliases, behavioral +# flags, and models that no public catalog provides. +# +# `models:` entries are self-complete — `rules` apply ONLY to API-sourced +# models, never to hand-owned `models:` entries. Legacy verbatim-preserved +# entries are grouped under a `# legacy — pruning candidates` comment. + +- provider: openai + rules: + - match: "gpt-5.6-*" + default_reasoning_effort: medium + - match: "gpt-5.5" + default_reasoning_effort: medium + - match: "gpt-5.5-pro" + default_reasoning_effort: high + - match: "gpt-5.4" + default_reasoning_effort: none + - match: "gpt-5.4-pro" + default_reasoning_effort: medium + - match: "gpt-5.4-mini" + default_reasoning_effort: none + - match: "gpt-5.4-nano" + default_reasoning_effort: none + - match: "gpt-5.3-codex" + default_reasoning_effort: medium + - match: "gpt-5.2" + default_reasoning_effort: none + - match: "gpt-5.2-pro" + default_reasoning_effort: medium + - match: "gpt-5.1" + default_reasoning_effort: none + - match: "gpt-5.1-chat-latest" + default_reasoning_effort: none + - match: "gpt-5" + default_reasoning_effort: medium + - match: "gpt-5-chat-latest" + default_reasoning_effort: medium + - match: "o4-mini" + system_prompt_prefix: Formatting re-enabled + default_reasoning_effort: medium + patch: + body: + max_tokens: null + temperature: null + top_p: null + - match: "o3" + system_prompt_prefix: Formatting re-enabled + patch: + body: + max_tokens: null + temperature: null + top_p: null + - match: "o3-mini" + system_prompt_prefix: Formatting re-enabled + patch: + body: + max_tokens: null + temperature: null + top_p: null + models: + - name: text-embedding-3-large + type: embedding + input_price: 0.13 + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: text-embedding-3-small + type: embedding + input_price: 0.02 + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + # legacy — pruning candidates + - name: o4-mini-high + max_output_tokens: 100000 + real_name: o4-mini + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + - name: o3-high + real_name: o3 + max_input_tokens: 200000 + input_price: 2 + output_price: 8 + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + - name: o3-mini-high + max_output_tokens: 100000 + real_name: o3-mini + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + +- provider: gemini + rules: + - match: "gemini-3.6-flash" + default_reasoning_effort: medium + - match: "gemini-3.5-flash" + default_reasoning_effort: medium + - match: "gemini-3-flash-preview" + default_reasoning_effort: high + - match: "gemini-3.1-flash-lite" + default_reasoning_effort: minimal + - match: "gemini-3.1-pro-preview" + default_reasoning_effort: high + - match: "gemini-3-pro-preview" + default_reasoning_effort: high + - match: "gemini-2.5-pro" + default_reasoning_effort: high + models: + - name: text-embedding-004 + type: embedding + input_price: 0 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 100 + +- provider: claude + rules: + - match: "claude-*" + require_max_tokens: true + - match: "claude-opus-5" + default_reasoning_effort: high + - match: "claude-fable-*" + default_reasoning_effort: high + - match: "claude-opus-4-8" + default_reasoning_effort: high + - match: "claude-opus-4-7" + default_reasoning_effort: high + - match: "claude-opus-4-6" + default_reasoning_effort: high + - match: "claude-sonnet-5" + default_reasoning_effort: high + - match: "claude-sonnet-4-6" + default_reasoning_effort: high + variants: + - match: "claude-opus-4-6" + suffix: ":thinking" + real_name: claude-opus-4-6 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-sonnet-4-6" + suffix: ":thinking" + real_name: claude-sonnet-4-6 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-sonnet-4-5-20250929" + suffix: ":thinking" + real_name: claude-sonnet-4-5-20250929 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-haiku-4-5-20251001" + suffix: ":thinking" + real_name: claude-haiku-4-5-20251001 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-opus-4-5-20251101" + suffix: ":thinking" + real_name: claude-opus-4-5-20251101 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-opus-4-1-20250805" + suffix: ":thinking" + real_name: claude-opus-4-1-20250805 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-opus-4-20250514" + suffix: ":thinking" + real_name: claude-opus-4-20250514 + max_output_tokens: 24000 + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + +- provider: mistral + models: + - name: mistral-embed + type: embedding + max_input_tokens: 8092 + input_price: 0.1 + max_tokens_per_chunk: 8092 + default_chunk_size: 2000 + +- provider: ai21 + models: + - name: jamba-large + max_input_tokens: 256000 + input_price: 2 + output_price: 8 + supports_function_calling: true + - name: jamba-mini + max_input_tokens: 256000 + input_price: 0.2 + output_price: 0.4 + supports_function_calling: true + +- provider: cohere + models: + - name: embed-v4.0 + type: embedding + input_price: 0.12 + max_tokens_per_chunk: 2048 + default_chunk_size: 2000 + max_batch_size: 96 + - name: embed-english-v3.0 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: embed-multilingual-v3.0 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: rerank-v3.5 + type: reranker + max_input_tokens: 4096 + - name: rerank-english-v3.0 + type: reranker + max_input_tokens: 4096 + - name: rerank-multilingual-v3.0 + type: reranker + max_input_tokens: 4096 + +- provider: xai + oauth: + client_id: b1a00492-073a-47ea-816f-4c329264a828 + authorize_url: https://auth.x.ai/oauth2/authorize + token_url: https://auth.x.ai/oauth2/token + scopes: + - openid + - profile + - email + - offline_access + - grok-cli:access + - api:access + redirect_port: 56121 + flow: pkce + token_request_format: form_url_encoded + extra_authorize_params: + plan: generic + referrer: coyote + echo_pkce_in_token_exchange: true + models: + - name: grok-4.20 + real_name: grok-4.20-multi-agent-0309 + input_price: 1.25 + output_price: 2.5 + max_input_tokens: 1000000 + supports_function_calling: true + - name: grok-4.20-reasoning + real_name: grok-4.20-0309-reasoning + input_price: 1.25 + output_price: 2.5 + max_input_tokens: 1000000 + supports_function_calling: true + - name: grok-4.20-non-reasoning + real_name: grok-4.20-0309-non-reasoning + input_price: 1.25 + output_price: 2.5 + max_input_tokens: 1000000 + supports_function_calling: true + +- provider: vertexai + rules: + - match: "claude-*" + require_max_tokens: true + - match: "gemini-3.5-flash" + default_reasoning_effort: medium + - match: "gemini-3-flash-preview" + default_reasoning_effort: high + - match: "gemini-3.1-flash-lite" + default_reasoning_effort: minimal + - match: "gemini-3.1-pro-preview" + default_reasoning_effort: high + - match: "gemini-3-pro-preview" + default_reasoning_effort: high + - match: "gemini-2.5-flash" + default_reasoning_effort: medium + - match: "gemini-2.5-pro" + default_reasoning_effort: high + - match: "claude-opus-5" + default_reasoning_effort: high + - match: "claude-fable-*" + default_reasoning_effort: high + - match: "claude-opus-4-8" + default_reasoning_effort: high + - match: "claude-opus-4-7" + default_reasoning_effort: high + - match: "claude-sonnet-5" + default_reasoning_effort: high + - match: "claude-sonnet-4-6" + default_reasoning_effort: high + variants: + - match: "claude-opus-4-6" + suffix: ":thinking" + real_name: claude-opus-4-6 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-sonnet-4-6" + suffix: ":thinking" + real_name: claude-sonnet-4-6 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-opus-4-5@20251101" + suffix: ":thinking" + real_name: claude-opus-4-5@20251101 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-sonnet-4-5@20250929" + suffix: ":thinking" + real_name: claude-sonnet-4-5@20250929 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + - match: "claude-haiku-4-5@20251001" + suffix: ":thinking" + real_name: claude-haiku-4-5@20251001 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + models: + - name: text-embedding-005 + type: embedding + max_input_tokens: 20000 + input_price: 0.025 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 5 + - name: text-multilingual-embedding-002 + type: embedding + max_input_tokens: 20000 + input_price: 0.2 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 5 + +- provider: bedrock + rules: + - match: "us.anthropic.claude-*" + require_max_tokens: true + - match: "us.meta.llama*" + require_max_tokens: true + - match: "us.anthropic.claude-opus-5" + default_reasoning_effort: high + - match: "us.anthropic.claude-fable-*" + default_reasoning_effort: high + - match: "us.anthropic.claude-opus-4-8" + default_reasoning_effort: high + - match: "us.anthropic.claude-opus-4-7" + default_reasoning_effort: high + - match: "us.anthropic.claude-opus-4-6-v1" + default_reasoning_effort: high + - match: "us.anthropic.claude-sonnet-5" + default_reasoning_effort: high + - match: "us.anthropic.claude-sonnet-4-6" + default_reasoning_effort: high + variants: + - match: "us.anthropic.claude-opus-4-6-v1" + suffix: ":thinking" + real_name: us.anthropic.claude-opus-4-6-v1 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + - match: "us.anthropic.claude-sonnet-4-6" + suffix: ":thinking" + real_name: us.anthropic.claude-sonnet-4-6 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + - match: "us.anthropic.claude-opus-4-5-20251101-v1:0" + suffix: ":thinking" + real_name: us.anthropic.claude-opus-4-5-20251101-v1:0 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + - match: "us.anthropic.claude-sonnet-4-5-20250929-v1:0" + suffix: ":thinking" + real_name: us.anthropic.claude-sonnet-4-5-20250929-v1:0 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + - match: "us.anthropic.claude-haiku-4-5-20251001-v1:0" + suffix: ":thinking" + real_name: us.anthropic.claude-haiku-4-5-20251001-v1:0 + max_output_tokens: 24000 + supports_function_calling: false + reasoning_levels: [] + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + models: + - name: cohere.embed-english-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: cohere.embed-multilingual-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + +- provider: cloudflare + rules: + - match: "@cf/*" + require_max_tokens: true + models: + - name: '@cf/baai/bge-large-en-v1.5' + type: embedding + input_price: 0 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + +- provider: ernie + models: + - name: ernie-4.5-turbo-128k + max_input_tokens: 131072 + input_price: 0.112 + output_price: 0.448 + - name: ernie-4.5-turbo-vl-32k + max_input_tokens: 32768 + input_price: 0.42 + output_price: 1.26 + supports_vision: true + - name: ernie-5.0-thinking-preview + max_input_tokens: 131072 + input_price: 1.4 + output_price: 5.6 + - name: ernie-x1.1-preview + max_input_tokens: 65536 + input_price: 0.14 + output_price: 0.56 + - name: bge-large-zh + type: embedding + input_price: 0.07 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 16 + - name: bge-large-en + type: embedding + input_price: 0.07 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 16 + - name: bce-reranker-base + type: reranker + max_input_tokens: 1024 + input_price: 0.07 + +- provider: qianwen + rules: + - match: "qwen3.5-plus" + patch: + body: + enable_thinking: false + - match: "qwen3.5-397b-a17b" + patch: + body: + enable_thinking: false + variants: + - match: "qwen3-max" + suffix: ":thinking" + real_name: qwen3-max + patch: + body: + enable_thinking: true + models: + - name: qwen3.5-plus:thinking + real_name: qwen3.5-plus + max_input_tokens: 262144 + supports_function_calling: true + - name: qwen3.5-397b-a17b:thinking + real_name: qwen3.5-397b-a17b + max_input_tokens: 262144 + supports_function_calling: true + - name: text-embedding-v4 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 10 + - name: text-embedding-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 10 + +- provider: hunyuan + models: + - name: hunyuan-2.0-instruct-20251111 + max_input_tokens: 131072 + input_price: 0.112 + output_price: 0.28 + supports_function_calling: true + - name: hunyuan-2.0-thinking-20251109 + max_input_tokens: 131072 + input_price: 0.14 + output_price: 0.56 + supports_function_calling: true + - name: hunyuan-vision-1.5-instruct + max_input_tokens: 24576 + input_price: 0.42 + output_price: 1.26 + supports_vision: true + - name: hunyuan-embedding + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 1024 + default_chunk_size: 1000 + max_batch_size: 100 + +- provider: zhipuai + variants: + - match: "glm-5" + suffix: ":instruct" + real_name: glm-5 + patch: + body: + thinking: + type: disabled + - match: "glm-4.7" + suffix: ":instruct" + real_name: glm-4.7 + patch: + body: + thinking: + type: disabled + models: + - name: embedding-3 + type: embedding + max_input_tokens: 8192 + input_price: 0.07 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + - name: rerank + type: reranker + max_input_tokens: 4096 + input_price: 0.112 + +- provider: openrouter + rules: + - match: "anthropic/claude-*" + require_max_tokens: true + - match: "openai/gpt-5.6-*" + default_reasoning_effort: medium + - match: "openai/gpt-5.5" + default_reasoning_effort: medium + - match: "openai/gpt-5.5-pro" + default_reasoning_effort: high + - match: "openai/gpt-5.4" + default_reasoning_effort: none + - match: "openai/gpt-5.4-pro" + default_reasoning_effort: medium + - match: "openai/gpt-5.4-mini" + default_reasoning_effort: none + - match: "openai/gpt-5.4-nano" + default_reasoning_effort: none + - match: "openai/gpt-5.3-codex" + default_reasoning_effort: medium + - match: "openai/gpt-5.2" + default_reasoning_effort: none + - match: "openai/gpt-5.2-pro" + default_reasoning_effort: medium + - match: "openai/gpt-5" + default_reasoning_effort: medium + - match: "google/gemini-3.5-flash" + default_reasoning_effort: medium + - match: "google/gemini-3-flash-preview" + default_reasoning_effort: high + - match: "google/gemini-3.1-flash-lite" + default_reasoning_effort: minimal + - match: "google/gemini-3.1-pro-preview" + default_reasoning_effort: high + - match: "google/gemini-3-pro-preview" + default_reasoning_effort: high + - match: "google/gemini-2.5-flash" + default_reasoning_effort: low + - match: "google/gemini-2.5-pro" + default_reasoning_effort: high + - match: "anthropic/claude-opus-5" + default_reasoning_effort: high + - match: "anthropic/claude-fable-*" + default_reasoning_effort: high + - match: "anthropic/claude-opus-4-8" + default_reasoning_effort: high + - match: "anthropic/claude-opus-4-7" + default_reasoning_effort: high + - match: "anthropic/claude-opus-4.6" + default_reasoning_effort: high + - match: "anthropic/claude-sonnet-5" + default_reasoning_effort: high + - match: "anthropic/claude-sonnet-4.6" + default_reasoning_effort: high + - match: "perplexity/sonar-reasoning-pro" + patch: + body: + include_reasoning: true + - match: "perplexity/sonar-deep-research" + patch: + body: + include_reasoning: true + +- provider: github + models: + - name: text-embedding-3-large + type: embedding + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: text-embedding-3-small + type: embedding + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: cohere-embed-v3-english + type: embedding + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: cohere-embed-v3-multilingual + type: embedding + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + +- provider: deepinfra + models: + - name: BAAI/bge-large-en-v1.5 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: BAAI/bge-m3 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 100 + - name: intfloat/e5-large-v2 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: intfloat/multilingual-e5-large + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: thenlper/gte-large + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + +- provider: jina + models: + - name: jina-embeddings-v3 + type: embedding + input_price: 0 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 100 + - name: jina-clip-v2 + type: embedding + input_price: 0 + max_tokens_per_chunk: 8192 + default_chunk_size: 1500 + max_batch_size: 100 + - name: jina-colbert-v2 + type: embedding + input_price: 0 + max_tokens_per_chunk: 8192 + default_chunk_size: 1500 + max_batch_size: 100 + - name: jina-reranker-v2-base-multilingual + type: reranker + max_input_tokens: 8192 + input_price: 0 + - name: jina-colbert-v2 + type: reranker + max_input_tokens: 8192 + input_price: 0 + +- provider: voyageai + models: + - name: voyage-3-large + type: embedding + max_input_tokens: 120000 + input_price: 0.18 + max_tokens_per_chunk: 32000 + default_chunk_size: 2000 + max_batch_size: 128 + - name: voyage-3 + type: embedding + max_input_tokens: 320000 + input_price: 0.06 + max_tokens_per_chunk: 32000 + default_chunk_size: 2000 + max_batch_size: 128 + - name: voyage-3-lite + type: embedding + max_input_tokens: 1000000 + input_price: 0.02 + max_tokens_per_chunk: 32000 + default_chunk_size: 1000 + max_batch_size: 128 + - name: rerank-2 + type: reranker + max_input_tokens: 16000 + input_price: 0.05 + - name: rerank-2-lite + type: reranker + max_input_tokens: 8000 + input_price: 0.02 diff --git a/src/client/catalog.rs b/src/client/catalog.rs new file mode 100644 index 0000000..27e0cda --- /dev/null +++ b/src/client/catalog.rs @@ -0,0 +1,327 @@ +use super::model::ModelData; +use super::oauth::OAuthConfig; + +use fancy_regex::Regex; +use log::warn; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::sync::LazyLock; + +pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); + +pub static ALL_QUIRKS: LazyLock> = + LazyLock::new(|| serde_yaml::from_str(QUIRKS_YAML).expect("quirks.yaml must be valid")); + +/// Hand-owned catalog overrides for a single provider: oauth configuration, +/// curation excludes, field overlays for API-sourced models, suffixed model +/// variants, and fully hand-owned model entries. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ProviderQuirks { + pub provider: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub oauth: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub exclude: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub rules: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub variants: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub models: Vec, +} + +/// A field overlay applied to every API-sourced model whose name matches the +/// glob in `match` (`*` = any run of characters, `?` = exactly one). Only +/// explicitly present fields are written; absent fields leave the model +/// untouched. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct QuirkRule { + pub r#match: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub real_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_input_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_output_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub input_price: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub output_price: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub patch: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub require_max_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub supports_vision: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub supports_function_calling: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub reasoning_levels: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub default_reasoning_effort: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub no_stream: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub no_system_message: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub system_prompt_prefix: Option, +} + +/// Clones every model whose name matches the glob in `match`, appends `suffix` +/// to the clone's name, and overlays any explicitly present fields onto the +/// clone. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct QuirkVariant { + pub r#match: String, + pub suffix: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub real_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_input_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_output_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub input_price: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub output_price: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub patch: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub require_max_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub supports_vision: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub supports_function_calling: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub reasoning_levels: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub default_reasoning_effort: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub no_stream: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub no_system_message: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub system_prompt_prefix: Option, +} + +/// Applies every rule whose glob matches `data.name`, in file order; later +/// rules overwrite earlier ones on the same field. +#[allow(dead_code)] +pub fn apply_rules(provider_quirks: &ProviderQuirks, data: &mut ModelData) { + for rule in &provider_quirks.rules { + if !glob_matches(&rule.r#match, &data.name) { + continue; + } + if let Some(real_name) = &rule.real_name { + data.real_name = Some(real_name.clone()); + } + if let Some(max_input_tokens) = rule.max_input_tokens { + data.max_input_tokens = Some(max_input_tokens); + } + if let Some(max_output_tokens) = rule.max_output_tokens { + data.max_output_tokens = Some(max_output_tokens); + } + if let Some(input_price) = rule.input_price { + data.input_price = Some(input_price); + } + if let Some(output_price) = rule.output_price { + data.output_price = Some(output_price); + } + if let Some(patch) = &rule.patch { + data.patch = Some(patch.clone()); + } + if let Some(require_max_tokens) = rule.require_max_tokens { + data.require_max_tokens = require_max_tokens; + } + if let Some(supports_vision) = rule.supports_vision { + data.supports_vision = supports_vision; + } + if let Some(supports_function_calling) = rule.supports_function_calling { + data.supports_function_calling = supports_function_calling; + } + if let Some(reasoning_levels) = &rule.reasoning_levels { + data.reasoning_levels = reasoning_levels.clone(); + } + if let Some(default_reasoning_effort) = &rule.default_reasoning_effort { + data.default_reasoning_effort = Some(default_reasoning_effort.clone()); + } + if let Some(no_stream) = rule.no_stream { + data.no_stream = no_stream; + } + if let Some(no_system_message) = rule.no_system_message { + data.no_system_message = no_system_message; + } + if let Some(system_prompt_prefix) = &rule.system_prompt_prefix { + data.system_prompt_prefix = Some(system_prompt_prefix.clone()); + } + } +} + +/// Finds the quirks for a client: an exact provider match wins, otherwise a +/// client whose name starts with the provider name (openai-compatible clients +/// are conventionally named after the provider they wrap). +#[allow(dead_code)] +pub fn quirks_for(client_name: &str) -> Option<&'static ProviderQuirks> { + ALL_QUIRKS + .iter() + .find(|quirks| quirks.provider == client_name) + .or_else(|| { + ALL_QUIRKS + .iter() + .find(|quirks| client_name.starts_with(&quirks.provider)) + }) +} + +fn glob_matches(pattern: &str, name: &str) -> bool { + compile_glob(pattern).is_match(name).unwrap_or(false) +} + +/// Translates a glob pattern (`*` = any run of characters, `?` = exactly one) +/// into an anchored regex. Patterns that fail to compile match nothing. +fn compile_glob(pattern: &str) -> Regex { + let translated = format!( + "^{}$", + fancy_regex::escape(pattern) + .replace("\\*", ".*") + .replace("\\?", ".") + ); + Regex::new(&translated).unwrap_or_else(|error| { + warn!("Invalid model quirk pattern '{pattern}': {error}. It will match nothing."); + Regex::new("(?!)").expect("'(?!)' is a valid never-matching regex") + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + const KNOWN_PROVIDERS: [&str; 24] = [ + "openai", + "gemini", + "claude", + "mistral", + "ai21", + "cohere", + "xai", + "perplexity", + "groq", + "vertexai", + "bedrock", + "cloudflare", + "ernie", + "qianwen", + "hunyuan", + "moonshot", + "deepseek", + "zhipuai", + "minimax", + "openrouter", + "github", + "deepinfra", + "jina", + "voyageai", + ]; + + #[test] + fn quirks_yaml_parses() { + assert!(!ALL_QUIRKS.is_empty()); + for quirks in ALL_QUIRKS.iter() { + assert!( + KNOWN_PROVIDERS.contains(&quirks.provider.as_str()), + "unknown provider '{}' in quirks.yaml", + quirks.provider + ); + } + } + + #[test] + fn quirks_models_no_dropped_keys() { + let skipped_when_default = [ + "type", + "require_max_tokens", + "supports_vision", + "supports_function_calling", + "reasoning_levels", + "no_stream", + "no_system_message", + ]; + let raw: serde_yaml::Value = serde_yaml::from_str(QUIRKS_YAML).unwrap(); + for provider in raw.as_sequence().unwrap() { + let Some(models) = provider + .get("models") + .and_then(|models| models.as_sequence()) + else { + continue; + }; + for entry in models { + let parsed: ModelData = serde_yaml::from_value(entry.clone()).unwrap(); + let reserialized = serde_yaml::to_value(&parsed).unwrap(); + let reserialized_keys: Vec<&str> = reserialized + .as_mapping() + .unwrap() + .keys() + .map(|key| key.as_str().unwrap()) + .collect(); + for key in entry.as_mapping().unwrap().keys() { + let key = key.as_str().unwrap(); + assert!( + reserialized_keys.contains(&key) || skipped_when_default.contains(&key), + "key '{key}' on model '{}' was dropped by ModelData", + parsed.name + ); + } + } + } + } + + #[test] + fn rules_apply_in_order() { + let quirks: ProviderQuirks = serde_yaml::from_str( + r#" +provider: openai +rules: + - match: "model-*" + default_reasoning_effort: low + require_max_tokens: true + - match: "model-a" + default_reasoning_effort: high +"#, + ) + .unwrap(); + + let mut matched_by_both = ModelData::new("model-a"); + apply_rules(&quirks, &mut matched_by_both); + assert_eq!( + matched_by_both.default_reasoning_effort.as_deref(), + Some("high") + ); + assert!(matched_by_both.require_max_tokens); + + let mut matched_by_glob = ModelData::new("model-b"); + apply_rules(&quirks, &mut matched_by_glob); + assert_eq!( + matched_by_glob.default_reasoning_effort.as_deref(), + Some("low") + ); + + let mut unmatched = ModelData::new("other"); + apply_rules(&quirks, &mut unmatched); + assert_eq!(unmatched.default_reasoning_effort, None); + assert!(!unmatched.require_max_tokens); + } + + #[test] + fn glob_semantics() { + assert!(glob_matches("claude-*", "claude-opus-4-8")); + assert!(!glob_matches("claude-*", "gpt-4")); + assert!(glob_matches("claude-*", "claude-")); + assert!(glob_matches("gpt-?", "gpt-4")); + assert!(!glob_matches("gpt-?", "gpt-45")); + assert!(!glob_matches("gpt-?", "gpt-")); + assert!(glob_matches("o3", "o3")); + assert!(!glob_matches("o3", "o3-mini")); + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs index c82c561..54cd832 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,4 +1,5 @@ mod access_token; +pub mod catalog; mod claude_oauth; mod common; mod gemini_oauth; diff --git a/src/client/model.rs b/src/client/model.rs index 0137be9..2c84ec8 100644 --- a/src/client/model.rs +++ b/src/client/model.rs @@ -330,11 +330,11 @@ pub struct ModelData { #[serde(skip_serializing_if = "Option::is_none")] pub default_reasoning_effort: Option, #[serde(default, skip_serializing_if = "std::ops::Not::not")] - no_stream: bool, + pub(crate) no_stream: bool, #[serde(default, skip_serializing_if = "std::ops::Not::not")] - no_system_message: bool, + pub(crate) no_system_message: bool, #[serde(skip_serializing_if = "Option::is_none")] - system_prompt_prefix: Option, + pub(crate) system_prompt_prefix: Option, // embedding-only properties #[serde(skip_serializing_if = "Option::is_none")] From 2d7a759fc035835ddf714fb4734ee125f235283f Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 13:17:48 -0600 Subject: [PATCH 12/30] feat: add model catalog source adapters and merge engine --- src/client/catalog.rs | 1111 ++++++++++++++++++- src/client/testdata/modelsdev_fixture.json | 457 ++++++++ src/client/testdata/openrouter_fixture.json | 538 +++++++++ 3 files changed, 2100 insertions(+), 6 deletions(-) create mode 100644 src/client/testdata/modelsdev_fixture.json create mode 100644 src/client/testdata/openrouter_fixture.json diff --git a/src/client/catalog.rs b/src/client/catalog.rs index 27e0cda..4d18162 100644 --- a/src/client/catalog.rs +++ b/src/client/catalog.rs @@ -1,17 +1,549 @@ -use super::model::ModelData; +use super::model::{ModelData, ProviderModels}; use super::oauth::OAuthConfig; +use crate::utils::fetch; + +use anyhow::{Result, bail}; use fancy_regex::Regex; use log::warn; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::collections::HashMap; use std::sync::LazyLock; pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); +#[allow(dead_code)] pub static ALL_QUIRKS: LazyLock> = LazyLock::new(|| serde_yaml::from_str(QUIRKS_YAML).expect("quirks.yaml must be valid")); +const MODELSDEV_API_URL: &str = "https://models.dev/api.json"; +const OPENROUTER_API_URL: &str = "https://openrouter.ai/api/v1/models"; + +/// Upstream ids for one coyote provider. The table order is the fixed output +/// order of the built catalog. Providers with neither upstream id are fully +/// quirks-owned; `openrouter` is special-cased in the merge: its models come +/// from the OpenRouter list natively instead of via a prefix join. +struct SourceIds { + provider: &'static str, + modelsdev: Option<&'static str>, + openrouter: Option<&'static str>, +} + +const PROVIDER_SOURCES: [SourceIds; 24] = [ + SourceIds { + provider: "openai", + modelsdev: Some("openai"), + openrouter: Some("openai"), + }, + SourceIds { + provider: "gemini", + modelsdev: Some("google"), + openrouter: Some("google"), + }, + SourceIds { + provider: "claude", + modelsdev: Some("anthropic"), + openrouter: Some("anthropic"), + }, + SourceIds { + provider: "mistral", + modelsdev: Some("mistral"), + openrouter: Some("mistralai"), + }, + SourceIds { + provider: "ai21", + modelsdev: None, + openrouter: None, + }, + SourceIds { + provider: "cohere", + modelsdev: Some("cohere"), + openrouter: Some("cohere"), + }, + SourceIds { + provider: "xai", + modelsdev: Some("xai"), + openrouter: Some("x-ai"), + }, + SourceIds { + provider: "perplexity", + modelsdev: Some("perplexity"), + openrouter: Some("perplexity"), + }, + SourceIds { + provider: "groq", + modelsdev: Some("groq"), + openrouter: Some("groq"), + }, + SourceIds { + provider: "vertexai", + modelsdev: Some("google-vertex"), + openrouter: Some("google"), + }, + SourceIds { + provider: "bedrock", + modelsdev: Some("amazon-bedrock"), + openrouter: Some("amazon"), + }, + SourceIds { + provider: "cloudflare", + modelsdev: Some("cloudflare-workers-ai"), + openrouter: None, + }, + SourceIds { + provider: "ernie", + modelsdev: None, + openrouter: None, + }, + SourceIds { + provider: "qianwen", + modelsdev: Some("alibaba"), + openrouter: Some("qwen"), + }, + SourceIds { + provider: "hunyuan", + modelsdev: None, + openrouter: None, + }, + SourceIds { + provider: "moonshot", + modelsdev: Some("moonshotai"), + openrouter: Some("moonshotai"), + }, + SourceIds { + provider: "deepseek", + modelsdev: Some("deepseek"), + openrouter: Some("deepseek"), + }, + SourceIds { + provider: "zhipuai", + modelsdev: Some("zhipuai"), + openrouter: Some("zhipuai"), + }, + SourceIds { + provider: "minimax", + modelsdev: Some("minimax"), + openrouter: Some("minimax"), + }, + SourceIds { + provider: "openrouter", + modelsdev: None, + openrouter: None, + }, + SourceIds { + provider: "github", + modelsdev: Some("github-copilot"), + openrouter: Some("github"), + }, + SourceIds { + provider: "deepinfra", + modelsdev: Some("deepinfra"), + openrouter: None, + }, + SourceIds { + provider: "jina", + modelsdev: None, + openrouter: None, + }, + SourceIds { + provider: "voyageai", + modelsdev: None, + openrouter: None, + }, +]; + +/// Raw payloads from the two upstream catalogs. Each source is independently +/// fallible; `None` means that fetch failed and the merge degrades gracefully. +pub struct FetchedSources { + pub modelsdev: Option, + pub openrouter: Option, +} + +/// Fetches both upstream catalogs concurrently. A failed fetch is logged and +/// surfaces as `None` rather than an error. +#[allow(dead_code)] +pub async fn fetch_sources() -> FetchedSources { + let (modelsdev, openrouter) = tokio::join!(fetch(MODELSDEV_API_URL), fetch(OPENROUTER_API_URL)); + FetchedSources { + modelsdev: modelsdev + .inspect_err(|err| warn!("Failed to fetch {MODELSDEV_API_URL}: {err}")) + .ok(), + openrouter: openrouter + .inspect_err(|err| warn!("Failed to fetch {OPENROUTER_API_URL}: {err}")) + .ok(), + } +} + +/// An API-sourced model plus the release date used for ordering. The date is +/// deliberately kept out of [`ModelData`] and never serialized. +struct SourcedModel { + data: ModelData, + release_date: Option, +} + +fn clamp_input_tokens(value: u64) -> Option { + (1..=50_000_000).contains(&value).then_some(value as usize) +} + +fn clamp_output_tokens(value: i64) -> Option { + (1..=10_000_000).contains(&value).then_some(value as isize) +} + +fn clamp_price(value: f64) -> Option { + (0.0..=10_000.0).contains(&value).then_some(value) +} + +/// Extracts the models of one models.dev provider. Tolerant by design: +/// missing or malformed fields are skipped while the model is kept, and +/// out-of-range numbers are dropped field-wise. +fn modelsdev_provider_models(api: &Value, provider_id: &str) -> Vec { + let Some(models) = api[provider_id]["models"].as_object() else { + return Vec::new(); + }; + models + .iter() + .map(|(name, entry)| { + let mut data = ModelData::new(name); + data.max_input_tokens = entry["limit"]["context"] + .as_u64() + .and_then(clamp_input_tokens); + data.max_output_tokens = entry["limit"]["output"] + .as_i64() + .and_then(clamp_output_tokens); + data.input_price = entry["cost"]["input"].as_f64().and_then(clamp_price); + data.output_price = entry["cost"]["output"].as_f64().and_then(clamp_price); + data.supports_vision = entry["modalities"]["input"] + .as_array() + .is_some_and(|modes| modes.iter().any(|mode| mode.as_str() == Some("image"))); + data.supports_function_calling = entry["tool_call"].as_bool().unwrap_or(false); + data.reasoning_levels = entry["reasoning_options"] + .as_array() + .and_then(|options| { + options + .iter() + .find(|option| option["type"].as_str() == Some("effort")) + }) + .and_then(|option| option["values"].as_array()) + .map(|values| { + values + .iter() + .filter_map(|value| value.as_str().map(str::to_string)) + .collect() + }) + .unwrap_or_default(); + let release_date = entry["release_date"].as_str().map(str::to_string); + SourcedModel { data, release_date } + }) + .collect() +} + +/// OpenRouter prices are USD per token; the catalog stores USD per million +/// tokens, rounded to 4 decimals. +fn openrouter_price(value: &Value) -> Option { + let per_token: f64 = value.as_str()?.parse().ok()?; + clamp_price((per_token * 1_000_000.0 * 10_000.0).round() / 10_000.0) +} + +/// Maps one OpenRouter list entry onto [`ModelData`], with the same field-wise +/// tolerance and clamping as the models.dev adapter. +fn openrouter_model_data(name: &str, entry: &Value) -> ModelData { + let mut data = ModelData::new(name); + data.max_input_tokens = entry["context_length"] + .as_u64() + .and_then(clamp_input_tokens); + data.max_output_tokens = entry["top_provider"]["max_completion_tokens"] + .as_i64() + .and_then(clamp_output_tokens); + data.input_price = openrouter_price(&entry["pricing"]["prompt"]); + data.output_price = openrouter_price(&entry["pricing"]["completion"]); + data.supports_vision = entry["architecture"]["modality"] + .as_str() + .is_some_and(|modality| modality.contains("image")); + data.supports_function_calling = entry["supported_parameters"] + .as_array() + .is_some_and(|params| params.iter().any(|param| param.as_str() == Some("tools"))); + data +} + +fn openrouter_index(payload: &Value) -> HashMap<&str, &Value> { + payload["data"] + .as_array() + .map(|entries| { + entries + .iter() + .filter_map(|entry| Some((entry["id"].as_str()?, entry))) + .collect() + }) + .unwrap_or_default() +} + +/// Every OpenRouter list entry as a model of the `openrouter` provider, named +/// by its full id. OpenRouter publishes no release date, so ordering falls +/// back to name-ascending. +fn openrouter_native_models(payload: &Value) -> Vec { + payload["data"] + .as_array() + .map(|entries| { + entries + .iter() + .filter_map(|entry| { + let id = entry["id"].as_str()?; + Some(SourcedModel { + data: openrouter_model_data(id, entry), + release_date: None, + }) + }) + .collect() + }) + .unwrap_or_default() +} + +/// Fills fields that models.dev left unset on the same model: OpenRouter data +/// never overrides a present models.dev value. +fn fill_from_openrouter(data: &mut ModelData, entry: &Value) { + let filler = openrouter_model_data(&data.name, entry); + if data.max_input_tokens.is_none() { + data.max_input_tokens = filler.max_input_tokens; + } + if data.max_output_tokens.is_none() { + data.max_output_tokens = filler.max_output_tokens; + } + if data.input_price.is_none() { + data.input_price = filler.input_price; + } + if data.output_price.is_none() { + data.output_price = filler.output_price; + } + if !data.supports_vision { + data.supports_vision = filler.supports_vision; + } + if !data.supports_function_calling { + data.supports_function_calling = filler.supports_function_calling; + } +} + +/// Outcome of a catalog merge. `degraded` lists API-mapped providers that fell +/// back to their hand-owned quirks models because API data was unavailable or +/// empty; `failed` lists providers that emitted nothing at all. +pub struct CatalogBuild { + pub providers: Vec, + pub degraded: Vec, + pub failed: Vec, +} + +/// Merges API-sourced model data with hand-owned quirks into the full +/// provider catalog, in the fixed provider order of [`PROVIDER_SOURCES`]. +/// +/// Per provider: API models (models.dev primary, OpenRouter gap-fill) are +/// curated by `exclude` globs and `rules`, hand-owned `models:` entries are +/// merged in field-wise (hand-owned wins) and placed first, API models are +/// sorted newest-first by release date (name-ascending tiebreak, undated +/// last), and `variants` clones are inserted immediately after their base +/// model. Providers without usable data are routed to `degraded` or `failed` +/// instead of erroring; a provider never emits zero models. +pub fn build_catalog(sources: &FetchedSources, quirks: &[ProviderQuirks]) -> CatalogBuild { + let modelsdev: Option = sources + .modelsdev + .as_deref() + .and_then(|payload| serde_json::from_str(payload).ok()); + let openrouter: Option = sources + .openrouter + .as_deref() + .and_then(|payload| serde_json::from_str(payload).ok()); + let or_index = openrouter + .as_ref() + .map(openrouter_index) + .unwrap_or_default(); + + let mut providers = Vec::new(); + let mut degraded = Vec::new(); + let mut failed = Vec::new(); + + for source in &PROVIDER_SOURCES { + let provider_quirks = quirks_for(quirks, source.provider); + let api_mapped = source.provider == "openrouter" || source.modelsdev.is_some(); + + let mut api_models: Vec = if source.provider == "openrouter" { + openrouter + .as_ref() + .map(openrouter_native_models) + .unwrap_or_default() + } else if let (Some(id), Some(api)) = (source.modelsdev, modelsdev.as_ref()) { + let mut models = modelsdev_provider_models(api, id); + if let Some(prefix) = source.openrouter { + for model in &mut models { + if let Some(entry) = + or_index.get(format!("{prefix}/{}", model.data.name).as_str()) + { + fill_from_openrouter(&mut model.data, entry); + } + } + } + models + } else { + Vec::new() + }; + + if let Some(provider_quirks) = provider_quirks { + api_models.retain(|model| { + !provider_quirks + .exclude + .iter() + .any(|pattern| glob_matches(pattern, &model.data.name)) + }); + for model in &mut api_models { + apply_rules(provider_quirks, &mut model.data); + } + } + + let hand_owned = provider_quirks + .map(|provider_quirks| provider_quirks.models.clone()) + .unwrap_or_default(); + + if api_models.is_empty() { + if hand_owned.is_empty() { + failed.push(source.provider.to_string()); + continue; + } + if api_mapped { + degraded.push(source.provider.to_string()); + } + } + + let mut merged_hand = Vec::with_capacity(hand_owned.len()); + for hand in hand_owned { + match api_models + .iter() + .position(|model| model.data.name == hand.name) + { + Some(position) => { + let api = api_models.remove(position); + merged_hand.push(overlay_hand_owned(hand, api.data)); + } + None => merged_hand.push(hand), + } + } + + api_models.sort_by(|a, b| match (&a.release_date, &b.release_date) { + (Some(a_date), Some(b_date)) => b_date + .cmp(a_date) + .then_with(|| a.data.name.cmp(&b.data.name)), + (Some(_), None) => std::cmp::Ordering::Less, + (None, Some(_)) => std::cmp::Ordering::Greater, + (None, None) => a.data.name.cmp(&b.data.name), + }); + + let bases = merged_hand + .into_iter() + .chain(api_models.into_iter().map(|model| model.data)); + + let variants = provider_quirks + .map(|provider_quirks| provider_quirks.variants.as_slice()) + .unwrap_or_default(); + let mut models = Vec::new(); + for base in bases { + let mut clones = Vec::new(); + for variant in variants { + if !glob_matches(&variant.r#match, &base.name) { + continue; + } + let mut clone = base.clone(); + clone.name = format!("{}{}", base.name, variant.suffix); + // Without an explicit real_name the clone would send its + // suffixed name on the wire; default to the base's wire name. + if clone.real_name.is_none() { + clone.real_name = Some(base.name.clone()); + } + apply_variant(variant, &mut clone); + clones.push(clone); + } + models.push(base); + models.append(&mut clones); + } + + providers.push(ProviderModels { + provider: source.provider.to_string(), + oauth: provider_quirks.and_then(|provider_quirks| provider_quirks.oauth.clone()), + models, + }); + } + + CatalogBuild { + providers, + degraded, + failed, + } +} + +/// Like [`build_catalog`], but errors unless every provider was assembled +/// from live API data (or is quirks-owned by design). +#[allow(dead_code)] +pub fn build_catalog_strict( + sources: &FetchedSources, + quirks: &[ProviderQuirks], +) -> Result> { + let build = build_catalog(sources, quirks); + if !build.degraded.is_empty() || !build.failed.is_empty() { + bail!( + "model catalog build incomplete: degraded providers [{}], failed providers [{}]", + build.degraded.join(", "), + build.failed.join(", ") + ); + } + Ok(build.providers) +} + +/// Fraction of `old` names that also appear in `new`; empty `old` yields 1.0. +/// Used to guard against upstream id-shape drift: a low overlap means the +/// upstream catalog renamed its models out from under us. +#[allow(dead_code)] +pub(crate) fn name_overlap(old: &[String], new: &[String]) -> f64 { + if old.is_empty() { + return 1.0; + } + let new_names: std::collections::HashSet<&str> = new.iter().map(String::as_str).collect(); + let kept = old + .iter() + .filter(|name| new_names.contains(name.as_str())) + .count(); + kept as f64 / old.len() as f64 +} + +/// Overlays a hand-owned quirks entry onto the API-sourced model of the same +/// name. Every field the hand-owned entry carries (Some, true, or non-empty) +/// wins; absent fields keep the API value. `model_type` is authoritative on +/// the hand-owned entry. +fn overlay_hand_owned(hand: ModelData, api: ModelData) -> ModelData { + ModelData { + name: hand.name, + model_type: hand.model_type, + real_name: hand.real_name.or(api.real_name), + max_input_tokens: hand.max_input_tokens.or(api.max_input_tokens), + input_price: hand.input_price.or(api.input_price), + output_price: hand.output_price.or(api.output_price), + patch: hand.patch.or(api.patch), + max_output_tokens: hand.max_output_tokens.or(api.max_output_tokens), + require_max_tokens: hand.require_max_tokens || api.require_max_tokens, + supports_vision: hand.supports_vision || api.supports_vision, + supports_function_calling: hand.supports_function_calling || api.supports_function_calling, + reasoning_levels: if hand.reasoning_levels.is_empty() { + api.reasoning_levels + } else { + hand.reasoning_levels + }, + default_reasoning_effort: hand + .default_reasoning_effort + .or(api.default_reasoning_effort), + no_stream: hand.no_stream || api.no_stream, + no_system_message: hand.no_system_message || api.no_system_message, + system_prompt_prefix: hand.system_prompt_prefix.or(api.system_prompt_prefix), + max_tokens_per_chunk: hand.max_tokens_per_chunk.or(api.max_tokens_per_chunk), + default_chunk_size: hand.default_chunk_size.or(api.default_chunk_size), + max_batch_size: hand.max_batch_size.or(api.max_batch_size), + } +} + /// Hand-owned catalog overrides for a single provider: oauth configuration, /// curation excludes, field overlays for API-sourced models, suffixed model /// variants, and fully hand-owned model entries. @@ -109,7 +641,6 @@ pub struct QuirkVariant { /// Applies every rule whose glob matches `data.name`, in file order; later /// rules overwrite earlier ones on the same field. -#[allow(dead_code)] pub fn apply_rules(provider_quirks: &ProviderQuirks, data: &mut ModelData) { for rule in &provider_quirks.rules { if !glob_matches(&rule.r#match, &data.name) { @@ -160,16 +691,65 @@ pub fn apply_rules(provider_quirks: &ProviderQuirks, data: &mut ModelData) { } } +/// Overlays a variant's explicitly present fields onto a cloned model; same +/// field semantics as [`apply_rules`]. +fn apply_variant(variant: &QuirkVariant, data: &mut ModelData) { + if let Some(real_name) = &variant.real_name { + data.real_name = Some(real_name.clone()); + } + if let Some(max_input_tokens) = variant.max_input_tokens { + data.max_input_tokens = Some(max_input_tokens); + } + if let Some(max_output_tokens) = variant.max_output_tokens { + data.max_output_tokens = Some(max_output_tokens); + } + if let Some(input_price) = variant.input_price { + data.input_price = Some(input_price); + } + if let Some(output_price) = variant.output_price { + data.output_price = Some(output_price); + } + if let Some(patch) = &variant.patch { + data.patch = Some(patch.clone()); + } + if let Some(require_max_tokens) = variant.require_max_tokens { + data.require_max_tokens = require_max_tokens; + } + if let Some(supports_vision) = variant.supports_vision { + data.supports_vision = supports_vision; + } + if let Some(supports_function_calling) = variant.supports_function_calling { + data.supports_function_calling = supports_function_calling; + } + if let Some(reasoning_levels) = &variant.reasoning_levels { + data.reasoning_levels = reasoning_levels.clone(); + } + if let Some(default_reasoning_effort) = &variant.default_reasoning_effort { + data.default_reasoning_effort = Some(default_reasoning_effort.clone()); + } + if let Some(no_stream) = variant.no_stream { + data.no_stream = no_stream; + } + if let Some(no_system_message) = variant.no_system_message { + data.no_system_message = no_system_message; + } + if let Some(system_prompt_prefix) = &variant.system_prompt_prefix { + data.system_prompt_prefix = Some(system_prompt_prefix.clone()); + } +} + /// Finds the quirks for a client: an exact provider match wins, otherwise a /// client whose name starts with the provider name (openai-compatible clients /// are conventionally named after the provider they wrap). -#[allow(dead_code)] -pub fn quirks_for(client_name: &str) -> Option<&'static ProviderQuirks> { - ALL_QUIRKS +pub fn quirks_for<'a>( + quirks: &'a [ProviderQuirks], + client_name: &str, +) -> Option<&'a ProviderQuirks> { + quirks .iter() .find(|quirks| quirks.provider == client_name) .or_else(|| { - ALL_QUIRKS + quirks .iter() .find(|quirks| client_name.starts_with(&quirks.provider)) }) @@ -198,6 +778,8 @@ fn compile_glob(pattern: &str) -> Regex { mod tests { use super::*; + use serde_json::json; + const KNOWN_PROVIDERS: [&str; 24] = [ "openai", "gemini", @@ -225,6 +807,40 @@ mod tests { "voyageai", ]; + const MODELSDEV_FIXTURE: &str = include_str!("testdata/modelsdev_fixture.json"); + const OPENROUTER_FIXTURE: &str = include_str!("testdata/openrouter_fixture.json"); + + fn make_sources(modelsdev: Option<&str>, openrouter: Option<&str>) -> FetchedSources { + FetchedSources { + modelsdev: modelsdev.map(str::to_string), + openrouter: openrouter.map(str::to_string), + } + } + + fn fixture_sources() -> FetchedSources { + make_sources(Some(MODELSDEV_FIXTURE), Some(OPENROUTER_FIXTURE)) + } + + fn parse_quirks(yaml: &str) -> Vec { + serde_yaml::from_str(yaml).unwrap() + } + + fn find_provider<'a>(build: &'a CatalogBuild, provider: &str) -> &'a ProviderModels { + build + .providers + .iter() + .find(|candidate| candidate.provider == provider) + .unwrap_or_else(|| panic!("provider '{provider}' missing")) + } + + fn find_model<'a>(provider: &'a ProviderModels, name: &str) -> &'a ModelData { + provider + .models + .iter() + .find(|model| model.name == name) + .unwrap_or_else(|| panic!("model '{name}' missing")) + } + #[test] fn quirks_yaml_parses() { assert!(!ALL_QUIRKS.is_empty()); @@ -237,6 +853,15 @@ mod tests { } } + #[test] + fn provider_sources_match_known_providers() { + let order: Vec<&str> = PROVIDER_SOURCES + .iter() + .map(|source| source.provider) + .collect(); + assert_eq!(order, KNOWN_PROVIDERS); + } + #[test] fn quirks_models_no_dropped_keys() { let skipped_when_default = [ @@ -324,4 +949,478 @@ rules: assert!(glob_matches("o3", "o3")); assert!(!glob_matches("o3", "o3-mini")); } + + #[test] + fn modelsdev_adapter_maps_fields() { + let api: Value = serde_json::from_str(MODELSDEV_FIXTURE).unwrap(); + + let openai = modelsdev_provider_models(&api, "openai"); + let gpt56 = openai + .iter() + .find(|model| model.data.name == "gpt-5.6") + .unwrap(); + assert_eq!(gpt56.data.max_input_tokens, Some(1_050_000)); + assert_eq!(gpt56.data.max_output_tokens, Some(128_000)); + assert_eq!(gpt56.data.input_price, Some(4.0)); + assert_eq!(gpt56.data.output_price, Some(20.0)); + assert!(gpt56.data.supports_vision); + assert!(gpt56.data.supports_function_calling); + assert_eq!( + gpt56.data.reasoning_levels, + ["none", "low", "medium", "high", "xhigh", "max"] + ); + assert_eq!(gpt56.release_date.as_deref(), Some("2026-07-09")); + + let alibaba = modelsdev_provider_models(&api, "alibaba"); + assert_eq!(alibaba.len(), 1); + let qwen = &alibaba[0]; + assert_eq!(qwen.data.name, "qwen-max"); + assert_eq!(qwen.data.max_input_tokens, Some(32_768)); + assert_eq!(qwen.data.max_output_tokens, Some(8_192)); + assert_eq!(qwen.data.input_price, Some(1.6)); + assert_eq!(qwen.data.output_price, Some(6.4)); + assert!(!qwen.data.supports_vision); + assert!(qwen.data.supports_function_calling); + assert!(qwen.data.reasoning_levels.is_empty()); + assert_eq!(qwen.release_date.as_deref(), Some("2024-04-03")); + + assert!(modelsdev_provider_models(&api, "no-such-provider").is_empty()); + } + + #[test] + fn openrouter_adapter_maps_fields() { + let payload: Value = serde_json::from_str(OPENROUTER_FIXTURE).unwrap(); + let index = openrouter_index(&payload); + + let data = openrouter_model_data("gpt-4.1", index["openai/gpt-4.1"]); + assert_eq!(data.max_input_tokens, Some(1_047_576)); + assert_eq!(data.max_output_tokens, Some(32_768)); + assert_eq!(data.input_price, Some(2.0)); + assert_eq!(data.output_price, Some(8.0)); + assert!(data.supports_vision); + assert!(data.supports_function_calling); + + let rounded = openrouter_model_data( + "m", + &json!({"pricing": {"prompt": "0.00000123456789", "completion": "0.0000025"}}), + ); + assert_eq!(rounded.input_price, Some(1.2346)); + assert_eq!(rounded.output_price, Some(2.5)); + } + + #[test] + fn clamps_drop_out_of_range_fields() { + let api = json!({ + "p": {"models": {"m": { + "limit": {"context": 100_000_000, "output": 0}, + "cost": {"input": -1.0, "output": 20_000.0}, + }}} + }); + let models = modelsdev_provider_models(&api, "p"); + assert_eq!(models.len(), 1); + let data = &models[0].data; + assert_eq!(data.name, "m"); + assert_eq!(data.max_input_tokens, None); + assert_eq!(data.max_output_tokens, None); + assert_eq!(data.input_price, None); + assert_eq!(data.output_price, None); + + let data = openrouter_model_data( + "m", + &json!({ + "context_length": 60_000_000, + "top_provider": {"max_completion_tokens": 20_000_000}, + "pricing": {"prompt": "-0.000001", "completion": "999"}, + }), + ); + assert_eq!(data.max_input_tokens, None); + assert_eq!(data.max_output_tokens, None); + assert_eq!(data.input_price, None); + assert_eq!(data.output_price, None); + } + + #[test] + fn adapters_never_set_quirks_owned_fields() { + let api: Value = serde_json::from_str(MODELSDEV_FIXTURE).unwrap(); + let payload: Value = serde_json::from_str(OPENROUTER_FIXTURE).unwrap(); + let mut all: Vec = ["openai", "anthropic", "xai", "alibaba"] + .iter() + .flat_map(|id| modelsdev_provider_models(&api, id)) + .collect(); + all.extend(openrouter_native_models(&payload)); + assert!(!all.is_empty()); + for model in &all { + let data = &model.data; + assert_eq!(data.model_type, "chat", "{}", data.name); + assert!(data.patch.is_none(), "{}", data.name); + assert!(data.real_name.is_none(), "{}", data.name); + assert!(data.system_prompt_prefix.is_none(), "{}", data.name); + assert!(data.max_tokens_per_chunk.is_none(), "{}", data.name); + assert!(data.default_chunk_size.is_none(), "{}", data.name); + assert!(data.max_batch_size.is_none(), "{}", data.name); + } + } + + #[test] + fn oauth_comes_only_from_quirks() { + let without_quirks = build_catalog(&fixture_sources(), &[]); + assert!( + without_quirks + .providers + .iter() + .all(|provider| provider.oauth.is_none()) + ); + + let quirks = parse_quirks( + r#" +- provider: xai + oauth: + client_id: id + token_url: https://example.com/token +"#, + ); + let build = build_catalog(&fixture_sources(), &quirks); + assert!(find_provider(&build, "xai").oauth.is_some()); + assert!(find_provider(&build, "openai").oauth.is_none()); + } + + #[test] + fn merge_precedence_hand_owned_beats_rule_beats_modelsdev_beats_openrouter() { + let modelsdev = json!({ + "xai": {"models": { + "m-1": {"limit": {"context": 1000}, "cost": {"input": 1.0}}, + "m-2": {"limit": {"context": 1000}}, + }} + }) + .to_string(); + let openrouter = json!({"data": [ + { + "id": "x-ai/m-1", + "context_length": 2000, + "pricing": {"prompt": "0.000003", "completion": "0.000004"}, + "top_provider": {"max_completion_tokens": 512}, + }, + {"id": "x-ai/m-2", "context_length": 2000}, + ]}) + .to_string(); + let quirks = parse_quirks( + r#" +- provider: xai + rules: + - match: "m-1" + input_price: 5 + max_input_tokens: 3000 + models: + - name: m-1 + input_price: 7 +"#, + ); + let build = build_catalog(&make_sources(Some(&modelsdev), Some(&openrouter)), &quirks); + let xai = find_provider(&build, "xai"); + + let merged = find_model(xai, "m-1"); + // hand-owned beats the rule, which beat models.dev, which beat openrouter + assert_eq!(merged.input_price, Some(7.0)); + // rule beats models.dev + assert_eq!(merged.max_input_tokens, Some(3000)); + // openrouter fills fields models.dev left unset + assert_eq!(merged.output_price, Some(4.0)); + assert_eq!(merged.max_output_tokens, Some(512)); + + // models.dev beats openrouter on the same field + assert_eq!(find_model(xai, "m-2").max_input_tokens, Some(1000)); + } + + #[test] + fn excludes_drop_api_models_but_never_hand_owned() { + let quirks = parse_quirks( + r#" +- provider: openai + exclude: + - "gpt-4*" + models: + - name: gpt-4.1 + input_price: 9 +"#, + ); + let build = build_catalog(&fixture_sources(), &quirks); + let openai = find_provider(&build, "openai"); + assert!(openai.models.iter().all(|model| model.name != "gpt-4o")); + find_model(openai, "gpt-5.6"); + let hand = find_model(openai, "gpt-4.1"); + assert_eq!(hand.input_price, Some(9.0)); + // the API copy was excluded before the merge, so nothing leaked in + assert_eq!(hand.max_input_tokens, None); + } + + #[test] + fn variant_expansion() { + let quirks = parse_quirks( + r#" +- provider: xai + variants: + - match: "grok-4.6" + suffix: ":thinking" + max_output_tokens: 24000 + patch: + body: + thinking: true + - match: "grok-4.5" + suffix: ":fast" + real_name: grok-4.5-fast +"#, + ); + let build = build_catalog(&fixture_sources(), &quirks); + let xai = find_provider(&build, "xai"); + let names: Vec<&str> = xai.models.iter().map(|model| model.name.as_str()).collect(); + assert_eq!( + names, + ["grok-4.6", "grok-4.6:thinking", "grok-4.5", "grok-4.5:fast"] + ); + + let thinking = find_model(xai, "grok-4.6:thinking"); + assert_eq!(thinking.real_name.as_deref(), Some("grok-4.6")); + assert_eq!(thinking.max_output_tokens, Some(24_000)); + assert_eq!(thinking.patch, Some(json!({"body": {"thinking": true}}))); + + assert_eq!( + find_model(xai, "grok-4.5:fast").real_name.as_deref(), + Some("grok-4.5-fast") + ); + assert_eq!(find_model(xai, "grok-4.6").real_name, None); + } + + #[test] + fn ordering_hand_owned_first_then_release_date_desc_name_asc() { + let shuffled_payloads = [ + r#"{"openai":{"models":{ + "b":{"release_date":"2026-05-01"}, + "a":{"release_date":"2026-05-01"}, + "c":{"release_date":"2026-06-01"}, + "z":{}, + "y":{} + }}}"#, + r#"{"openai":{"models":{ + "y":{}, + "c":{"release_date":"2026-06-01"}, + "z":{}, + "a":{"release_date":"2026-05-01"}, + "b":{"release_date":"2026-05-01"} + }}}"#, + ]; + let quirks = parse_quirks( + r#" +- provider: openai + models: + - name: n2 + - name: n1 +"#, + ); + for payload in shuffled_payloads { + let build = build_catalog(&make_sources(Some(payload), None), &quirks); + let names: Vec<&str> = find_provider(&build, "openai") + .models + .iter() + .map(|model| model.name.as_str()) + .collect(); + assert_eq!(names, ["n2", "n1", "c", "a", "b", "y", "z"]); + } + } + + #[test] + fn catalog_round_trips_through_yaml() { + let build = build_catalog(&fixture_sources(), &ALL_QUIRKS); + assert!(!build.providers.is_empty()); + let yaml = serde_yaml::to_string(&build.providers).unwrap(); + let parsed: Vec = serde_yaml::from_str(&yaml).unwrap(); + assert_eq!(serde_yaml::to_string(&parsed).unwrap(), yaml); + } + + #[test] + fn openrouter_native_models_use_full_ids() { + let build = build_catalog(&fixture_sources(), &[]); + let openrouter = find_provider(&build, "openrouter"); + assert_eq!(openrouter.models.len(), 8); + + let auto = find_model(openrouter, "openrouter/auto"); + assert_eq!(auto.max_input_tokens, Some(2_000_000)); + // null max_completion_tokens and the "-1" sentinel prices are dropped + assert_eq!(auto.max_output_tokens, None); + assert_eq!(auto.input_price, None); + assert_eq!(auto.output_price, None); + assert!(auto.supports_vision); + assert!(auto.supports_function_calling); + + let free = find_model(openrouter, "openrouter/free"); + assert_eq!(free.max_input_tokens, Some(200_000)); + assert_eq!(free.input_price, Some(0.0)); + assert_eq!(free.output_price, Some(0.0)); + + // no release dates upstream, so full ids sort ascending + let names: Vec<&str> = openrouter + .models + .iter() + .map(|model| model.name.as_str()) + .collect(); + let mut sorted = names.clone(); + sorted.sort_unstable(); + assert_eq!(names, sorted); + } + + #[test] + fn failure_routing_degraded_and_failed() { + let quirks = parse_quirks( + r#" +- provider: gemini + models: [{name: g-1}] +- provider: ai21 + models: [{name: jamba}] +"#, + ); + let build = build_catalog(&make_sources(None, None), &quirks); + assert_eq!(build.degraded, ["gemini"]); + assert!(build.failed.contains(&"openai".to_string())); + assert!(build.failed.contains(&"openrouter".to_string())); + assert!(build.failed.contains(&"jina".to_string())); + assert!(!build.failed.contains(&"gemini".to_string())); + assert!(!build.failed.contains(&"ai21".to_string())); + assert!(!build.degraded.contains(&"ai21".to_string())); + + let providers: Vec<&str> = build + .providers + .iter() + .map(|provider| provider.provider.as_str()) + .collect(); + assert_eq!(providers, ["gemini", "ai21"]); + assert!( + build + .providers + .iter() + .all(|provider| !provider.models.is_empty()) + ); + } + + #[test] + fn strict_errors_on_degraded_or_failed() { + let quirks = parse_quirks( + r#" +- provider: gemini + models: [{name: g-1}] +"#, + ); + let err = build_catalog_strict(&make_sources(None, None), &quirks) + .unwrap_err() + .to_string(); + assert!(err.contains("gemini"), "{err}"); + assert!(err.contains("openai"), "{err}"); + } + + #[test] + fn strict_succeeds_when_every_provider_resolves() { + let mut api = serde_json::Map::new(); + for source in &PROVIDER_SOURCES { + if let Some(id) = source.modelsdev { + api.insert(id.to_string(), json!({"models": {"m": {}}})); + } + } + let modelsdev = Value::Object(api).to_string(); + let openrouter = json!({"data": [{"id": "openrouter/auto"}]}).to_string(); + let quirks = parse_quirks( + r#" +- provider: ai21 + models: [{name: a}] +- provider: ernie + models: [{name: e}] +- provider: hunyuan + models: [{name: h}] +- provider: jina + models: [{name: j}] +- provider: voyageai + models: [{name: v}] +"#, + ); + let providers = + build_catalog_strict(&make_sources(Some(&modelsdev), Some(&openrouter)), &quirks) + .unwrap(); + let order: Vec<&str> = providers + .iter() + .map(|provider| provider.provider.as_str()) + .collect(); + assert_eq!(order, KNOWN_PROVIDERS); + } + + #[test] + fn name_overlap_fraction_of_old_names_still_present() { + let old: Vec = ["a", "b", "c", "d"].map(str::to_string).into(); + let new: Vec = ["a", "c", "x"].map(str::to_string).into(); + assert_eq!(name_overlap(&old, &new), 0.5); + assert_eq!(name_overlap(&[], &new), 1.0); + assert_eq!(name_overlap(&old, &[]), 0.0); + } + + /// Guards against upstream id-shape drift by comparing live API model + /// names against the embedded models.yaml. Run manually: + /// `cargo test idshape_guard_live -- --ignored --nocapture` + #[tokio::test] + #[ignore] + async fn idshape_guard_live() { + let sources = fetch_sources().await; + let modelsdev: Value = serde_json::from_str( + sources + .modelsdev + .as_deref() + .expect("models.dev fetch failed"), + ) + .unwrap(); + let openrouter: Value = serde_json::from_str( + sources + .openrouter + .as_deref() + .expect("openrouter fetch failed"), + ) + .unwrap(); + let embedded: Vec = + serde_yaml::from_str(crate::client::MODELS_YAML).unwrap(); + + println!( + "{:<12} {:>7} {:>5} {:>5}", + "provider", "overlap", "old", "new" + ); + for source in &PROVIDER_SOURCES { + let new: Vec = if source.provider == "openrouter" { + openrouter_native_models(&openrouter) + .into_iter() + .map(|model| model.data.name) + .collect() + } else if let Some(id) = source.modelsdev { + modelsdev_provider_models(&modelsdev, id) + .into_iter() + .map(|model| model.data.name) + .collect() + } else { + continue; + }; + let old: Vec = embedded + .iter() + .find(|provider| provider.provider == source.provider) + .map(|provider| { + provider + .models + .iter() + .filter(|model| { + model.model_type == "chat" && !model.name.ends_with(":thinking") + }) + .map(|model| model.name.clone()) + .collect() + }) + .unwrap_or_default(); + println!( + "{:<12} {:>7.2} {:>5} {:>5}", + source.provider, + name_overlap(&old, &new), + old.len(), + new.len() + ); + } + } } diff --git a/src/client/testdata/modelsdev_fixture.json b/src/client/testdata/modelsdev_fixture.json new file mode 100644 index 0000000..d91242a --- /dev/null +++ b/src/client/testdata/modelsdev_fixture.json @@ -0,0 +1,457 @@ +{ + "openai": { + "id": "openai", + "env": [ + "OPENAI_API_KEY" + ], + "npm": "@ai-sdk/openai", + "name": "OpenAI", + "doc": "https://platform.openai.com/docs/models", + "models": { + "gpt-5.6": { + "id": "gpt-5.6", + "name": "GPT-5.6", + "description": "Frontier GPT-5.6 model for complex professional work, coding, and agentic workflows", + "family": "gpt-sol", + "attachment": true, + "reasoning": true, + "reasoning_options": [ + { + "type": "effort", + "values": [ + "none", + "low", + "medium", + "high", + "xhigh", + "max" + ] + } + ], + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2026-02-16", + "release_date": "2026-07-09", + "last_updated": "2026-07-09", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "experimental": { + "modes": { + "fast": { + "cost": { + "input": 8, + "output": 40, + "cache_read": 0.8, + "cache_write": 10 + }, + "provider": { + "body": { + "service_tier": "priority" + } + } + }, + "pro": { + "provider": { + "body": { + "reasoning": { + "mode": "pro" + } + } + } + } + } + }, + "cost": { + "input": 4, + "output": 20, + "cache_read": 0.4, + "cache_write": 5, + "tiers": [ + { + "input": 8, + "output": 30, + "cache_read": 0.8, + "cache_write": 10, + "tier": { + "type": "context", + "size": 272000 + } + } + ], + "context_over_200k": { + "input": 8, + "output": 30, + "cache_read": 0.8, + "cache_write": 10 + } + } + }, + "gpt-4.1": { + "id": "gpt-4.1", + "name": "GPT-4.1", + "description": "Long-lived GPT workhorse for coding, instruction following, and production apps", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } + }, + "gpt-4o": { + "id": "gpt-4o", + "name": "GPT-4o", + "description": "Omni-era GPT for multimodal chat, practical coding, and general assistants", + "family": "gpt", + "attachment": true, + "reasoning": false, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-08-06", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } + } + } + }, + "anthropic": { + "id": "anthropic", + "env": [ + "ANTHROPIC_API_KEY" + ], + "npm": "@ai-sdk/anthropic", + "name": "Anthropic", + "doc": "https://docs.anthropic.com/en/docs/about-claude/models", + "models": { + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "description": "Claude workhorse for coding agents, careful analysis, and production cost control", + "family": "claude-sonnet", + "attachment": true, + "reasoning": true, + "reasoning_options": [ + { + "type": "effort", + "values": [ + "low", + "medium", + "high", + "max" + ] + }, + { + "type": "budget_tokens", + "min": 1024 + } + ], + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-08-31", + "release_date": "2026-02-17", + "last_updated": "2026-03-13", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "claude-opus-4-6": { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "description": "High-end Claude for difficult coding, planning, and slower expert reasoning", + "family": "claude-opus", + "attachment": true, + "reasoning": true, + "reasoning_options": [ + { + "type": "effort", + "values": [ + "low", + "medium", + "high", + "max" + ] + }, + { + "type": "budget_tokens", + "min": 1024 + } + ], + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05-31", + "release_date": "2026-02-04", + "last_updated": "2026-03-13", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 1000000, + "output": 128000 + }, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + } + } + }, + "xai": { + "id": "xai", + "env": [ + "XAI_API_KEY" + ], + "npm": "@ai-sdk/xai", + "name": "xAI", + "doc": "https://docs.x.ai/docs/models", + "models": { + "grok-4.6": { + "id": "grok-4.6", + "name": "Grok 4.6", + "description": "xAI's frontier model for long-running agents, coding, knowledge work, and visual projects", + "family": "grok", + "attachment": true, + "reasoning": true, + "reasoning_options": [ + { + "type": "effort", + "values": [ + "low", + "medium", + "high", + "xhigh" + ] + } + ], + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2026-02-01", + "release_date": "2026-08-12", + "last_updated": "2026-08-12", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 500000, + "output": 500000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.5, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 1, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 1 + } + } + }, + "grok-4.5": { + "id": "grok-4.5", + "name": "Grok 4.5", + "description": "xAI's Grok model for chat, coding, agentic tools, and lower hallucination risk", + "family": "grok", + "attachment": true, + "reasoning": true, + "reasoning_options": [ + { + "type": "effort", + "values": [ + "low", + "medium", + "high" + ] + } + ], + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2026-07-08", + "last_updated": "2026-07-08", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 500000, + "output": 500000 + }, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.3, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 0.6, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 0.6 + } + } + } + } + }, + "alibaba": { + "id": "alibaba", + "env": [ + "DASHSCOPE_API_KEY" + ], + "npm": "@ai-sdk/openai-compatible", + "name": "Alibaba", + "doc": "https://www.alibabacloud.com/help/en/model-studio/models", + "models": { + "qwen-max": { + "id": "qwen-max", + "name": "Qwen Max", + "description": "Flagship Qwen model for complex reasoning, coding, and agentic workflows", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-04", + "release_date": "2024-04-03", + "last_updated": "2025-01-25", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "limit": { + "context": 32768, + "output": 8192 + }, + "cost": { + "input": 1.6, + "output": 6.4 + } + } + } + } +} diff --git a/src/client/testdata/openrouter_fixture.json b/src/client/testdata/openrouter_fixture.json new file mode 100644 index 0000000..956cbdd --- /dev/null +++ b/src/client/testdata/openrouter_fixture.json @@ -0,0 +1,538 @@ +{ + "data": [ + { + "id": "x-ai/grok-4.6", + "canonical_slug": "x-ai/grok-4.6-20260810", + "hugging_face_id": null, + "name": "SpaceXAI: Grok 4.6", + "created": 1786548957, + "description": "Grok 4.6 is SpaceXAI's smartest model with frontier performance on coding, knowledge work, and STEM.", + "context_length": 500000, + "architecture": { + "modality": "text+image+file->text", + "input_modalities": [ + "text", + "image", + "file" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "Grok", + "instruct_type": null + }, + "pricing": { + "prompt": "0.000002", + "completion": "0.000006", + "web_search": "0.005", + "input_cache_read": "0.0000005", + "overrides": [ + { + "min_prompt_tokens": 200000, + "prompt": "0.000004", + "completion": "0.000012", + "input_cache_read": "0.000001" + } + ] + }, + "top_provider": { + "context_length": 500000, + "max_completion_tokens": 450000, + "is_moderated": false + }, + "per_request_limits": null, + "supported_parameters": [ + "include_reasoning", + "logprobs", + "max_tokens", + "reasoning", + "reasoning_effort", + "response_format", + "seed", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/x-ai/grok-4.6-20260810/endpoints" + }, + "reasoning": { + "mandatory": true, + "default_enabled": true, + "supported_efforts": [ + "xhigh", + "high", + "medium", + "low" + ], + "default_effort": "high" + } + }, + { + "id": "qwen/qwen3.8-max", + "canonical_slug": "qwen/qwen3.8-max-20260803", + "hugging_face_id": null, + "name": "Qwen: Qwen3.8 Max", + "created": 1785731612, + "description": "Qwen3.8 Max is the flagship model in Alibaba's Qwen3.8 series, the general-availability successor to the Qwen3.8 Max Preview. It is a multimodal reasoning model intended for complex reasoning, visual understanding,...", + "context_length": 1000000, + "architecture": { + "modality": "text+image+video->text", + "input_modalities": [ + "text", + "image", + "video" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "Qwen", + "instruct_type": null + }, + "pricing": { + "prompt": "0.000002", + "completion": "0.000006", + "input_cache_read": "0.00000025", + "input_cache_write": "0.0000025" + }, + "top_provider": { + "context_length": 1000000, + "max_completion_tokens": 131072, + "is_moderated": false + }, + "per_request_limits": null, + "supported_parameters": [ + "frequency_penalty", + "include_reasoning", + "logprobs", + "max_tokens", + "presence_penalty", + "reasoning", + "reasoning_effort", + "response_format", + "seed", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/qwen/qwen3.8-max-20260803/endpoints" + }, + "reasoning": { + "mandatory": true, + "default_enabled": true, + "supported_efforts": [ + "xhigh", + "high", + "medium", + "low", + "minimal" + ], + "default_effort": "xhigh" + } + }, + { + "id": "x-ai/grok-4.5", + "canonical_slug": "x-ai/grok-4.5-20260708", + "hugging_face_id": null, + "name": "SpaceXAI: Grok 4.5", + "created": 1783523154, + "description": "Grok 4.5 is a model from SpaceXAI with frontier performance on coding, knowledge work, and STEM.", + "context_length": 500000, + "architecture": { + "modality": "text+image+file->text", + "input_modalities": [ + "text", + "image", + "file" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "Grok", + "instruct_type": null + }, + "pricing": { + "prompt": "0.000002", + "completion": "0.000006", + "web_search": "0.005", + "input_cache_read": "0.0000003", + "overrides": [ + { + "min_prompt_tokens": 200000, + "prompt": "0.000004", + "completion": "0.000012", + "input_cache_read": "0.0000006" + } + ] + }, + "top_provider": { + "context_length": 500000, + "max_completion_tokens": 450000, + "is_moderated": false + }, + "per_request_limits": null, + "supported_parameters": [ + "include_reasoning", + "logprobs", + "max_tokens", + "reasoning", + "reasoning_effort", + "response_format", + "seed", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_logprobs", + "top_p" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/x-ai/grok-4.5-20260708/endpoints" + }, + "reasoning": { + "mandatory": true, + "default_enabled": true, + "supported_efforts": [ + "high", + "medium", + "low" + ], + "default_effort": "high" + } + }, + { + "id": "anthropic/claude-sonnet-4.6", + "canonical_slug": "anthropic/claude-4.6-sonnet-20260217", + "hugging_face_id": "", + "name": "Anthropic: Claude Sonnet 4.6", + "created": 1771342990, + "description": "Sonnet 4.6 is Anthropic's most capable Sonnet-class model yet, with frontier performance across coding, agents, and professional work. It excels at iterative development, complex codebase navigation, end-to-end project management with...", + "context_length": 1000000, + "architecture": { + "modality": "text+image+file->text", + "input_modalities": [ + "text", + "image", + "file" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "Claude", + "instruct_type": null + }, + "pricing": { + "prompt": "0.000003", + "completion": "0.000015", + "web_search": "0.01", + "input_cache_read": "0.0000003", + "input_cache_write": "0.00000375", + "input_cache_write_1h": "0.000006" + }, + "top_provider": { + "context_length": 1000000, + "max_completion_tokens": 128000, + "is_moderated": true + }, + "per_request_limits": null, + "supported_parameters": [ + "include_reasoning", + "max_completion_tokens", + "max_tokens", + "reasoning", + "reasoning_effort", + "response_format", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_k", + "top_p", + "verbosity" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/anthropic/claude-4.6-sonnet-20260217/endpoints" + }, + "reasoning": { + "mandatory": false, + "supported_efforts": [ + "max", + "high", + "medium", + "low" + ], + "default_effort": "medium" + } + }, + { + "id": "openrouter/free", + "canonical_slug": "openrouter/free", + "hugging_face_id": "", + "name": "Free Models Router", + "created": 1769917427, + "description": "The simplest way to get free inference. openrouter/free is a router that selects free models at random from the models available on OpenRouter. The router smartly filters for models that...", + "context_length": 200000, + "architecture": { + "modality": "text+image->text", + "input_modalities": [ + "text", + "image" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "Router", + "instruct_type": null + }, + "pricing": { + "prompt": "0", + "completion": "0" + }, + "top_provider": { + "context_length": null, + "max_completion_tokens": null, + "is_moderated": false + }, + "per_request_limits": null, + "supported_parameters": [ + "frequency_penalty", + "include_reasoning", + "logprobs", + "max_completion_tokens", + "max_tokens", + "min_p", + "presence_penalty", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "seed", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_k", + "top_logprobs", + "top_p" + ], + "default_parameters": { + "temperature": null, + "top_p": null, + "frequency_penalty": null + }, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/openrouter/free/endpoints" + } + }, + { + "id": "openai/gpt-4.1", + "canonical_slug": "openai/gpt-4.1-2025-04-14", + "hugging_face_id": "", + "name": "OpenAI: GPT-4.1", + "created": 1744651385, + "description": "GPT-4.1 is a flagship large language model optimized for advanced instruction following, real-world software engineering, and long-context reasoning. It supports a 1 million token context window and outperforms GPT-4o and...", + "context_length": 1047576, + "architecture": { + "modality": "text+image+file->text", + "input_modalities": [ + "image", + "text", + "file" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "GPT", + "instruct_type": null + }, + "pricing": { + "prompt": "0.000002", + "completion": "0.000008", + "web_search": "0.01", + "input_cache_read": "0.0000005" + }, + "top_provider": { + "context_length": 1047576, + "max_completion_tokens": 32768, + "is_moderated": true + }, + "per_request_limits": null, + "supported_parameters": [ + "max_completion_tokens", + "max_tokens", + "response_format", + "seed", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_p" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": "2024-06-30", + "expiration_date": null, + "links": { + "details": "/api/v1/models/openai/gpt-4.1-2025-04-14/endpoints" + } + }, + { + "id": "openai/gpt-4o", + "canonical_slug": "openai/gpt-4o", + "hugging_face_id": null, + "name": "OpenAI: GPT-4o", + "created": 1715558400, + "description": "GPT-4o (\"o\" for \"omni\") is OpenAI's latest AI model, supporting both text and image inputs with text outputs. It maintains the intelligence level of [GPT-4 Turbo](/models/openai/gpt-4-turbo) while being twice as...", + "context_length": 128000, + "architecture": { + "modality": "text+image+file->text", + "input_modalities": [ + "text", + "image", + "file" + ], + "output_modalities": [ + "text" + ], + "tokenizer": "GPT", + "instruct_type": null + }, + "pricing": { + "prompt": "0.0000025", + "completion": "0.00001", + "input_cache_read": "0.00000125" + }, + "top_provider": { + "context_length": 128000, + "max_completion_tokens": 16384, + "is_moderated": true + }, + "per_request_limits": null, + "supported_parameters": [ + "frequency_penalty", + "logit_bias", + "logprobs", + "max_completion_tokens", + "max_tokens", + "prediction", + "presence_penalty", + "response_format", + "seed", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_logprobs", + "top_p", + "web_search_options" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": "2023-10-31", + "expiration_date": null, + "links": { + "details": "/api/v1/models/openai/gpt-4o/endpoints" + } + }, + { + "id": "openrouter/auto", + "canonical_slug": "openrouter/auto", + "hugging_face_id": null, + "name": "Auto Router", + "created": 1699401600, + "description": "The Auto Router automatically selects the best model for your prompt, powered by the wisdom of the market. It routes you based on what the OpenRouter community collectively spends on...", + "context_length": 2000000, + "architecture": { + "modality": "text+image+file+audio+video->text+image", + "input_modalities": [ + "text", + "image", + "audio", + "file", + "video" + ], + "output_modalities": [ + "text", + "image" + ], + "tokenizer": "Router", + "instruct_type": null + }, + "pricing": { + "prompt": "-1", + "completion": "-1" + }, + "top_provider": { + "context_length": null, + "max_completion_tokens": null, + "is_moderated": false + }, + "per_request_limits": null, + "supported_parameters": [ + "frequency_penalty", + "include_reasoning", + "logit_bias", + "logprobs", + "max_tokens", + "min_p", + "prediction", + "presence_penalty", + "reasoning", + "reasoning_effort", + "repetition_penalty", + "response_format", + "seed", + "stop", + "structured_outputs", + "temperature", + "tool_choice", + "tools", + "top_a", + "top_k", + "top_logprobs", + "top_p", + "web_search_options" + ], + "default_parameters": {}, + "supported_voices": null, + "knowledge_cutoff": null, + "expiration_date": null, + "links": { + "details": "/api/v1/models/openrouter/auto/endpoints" + } + } + ] +} From 539b57aded2b8f4404adca62cdfcd9703c7317f3 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 13:38:56 -0600 Subject: [PATCH 13/30] feat: generate the model catalog from live API data Add a hidden --generate-models flag that rebuilds models.yaml from models.dev and OpenRouter merged with the hand-owned quirks overlay, regenerate the committed catalog with it (393 -> 1090 models), and retire the Python updater. GitHub becomes fully quirks-owned since upstream only tracks Copilot ids, legacy ids upstream dropped are preserved as hand-owned quirks entries, and non-chat model families (image/tts/audio/realtime) are excluded from generation. --- models.yaml | 12326 ++++++++++++++++++++++++++++--------- quirks.yaml | 592 ++ scripts/requirements.txt | 2 - scripts/update_models.py | 255 - src/cli/mod.rs | 25 +- src/client/catalog.rs | 125 +- src/client/model.rs | 2 +- src/main.rs | 7 +- 8 files changed, 10276 insertions(+), 3058 deletions(-) delete mode 100644 scripts/requirements.txt delete mode 100644 scripts/update_models.py diff --git a/models.yaml b/models.yaml index 2502e8c..a368855 100644 --- a/models.yaml +++ b/models.yaml @@ -1,2850 +1,9590 @@ -# Links: -# - https://platform.openai.com/docs/models -# - https://platform.openai.com/docs/api-reference/chat +# GENERATED by 'coyote --generate-models' — DO NOT hand-edit model data. +# Hand-owned behavior lives in quirks.yaml; this file is the embedded offline fallback. - provider: openai models: - - name: gpt-5.6-sol - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: gpt-5.6-terra - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: gpt-5.6-luna - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: gpt-5.5 - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: medium - - name: gpt-5.5-pro - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 30 - output_price: 180 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: high - - name: gpt-5.4 - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 2.5 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: gpt-5.4-pro - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 30 - output_price: 180 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: medium - - name: gpt-5.4-mini - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.75 - output_price: 4.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: gpt-5.4-nano - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.2 - output_price: 1.25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: gpt-5.3-codex - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.75 - output_price: 14 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh] - default_reasoning_effort: medium - - name: chat-latest - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - - name: gpt-5.2 - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.75 - output_price: 14 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: gpt-5.2-pro - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 21 - output_price: 168 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: medium - - name: gpt-5.1 - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high] - default_reasoning_effort: none - - name: gpt-5.1-chat-latest - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high] - default_reasoning_effort: none - - name: gpt-5 - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: gpt-5-chat-latest - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: gpt-5-mini - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.25 - output_price: 2 - supports_vision: true - supports_function_calling: true - - name: gpt-5-nano - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.05 - output_price: 0.4 - supports_vision: true - supports_function_calling: true - - name: gpt-4.1 - max_input_tokens: 1047576 - max_output_tokens: 32768 - input_price: 2 - output_price: 8 - supports_vision: true - supports_function_calling: true - - name: gpt-4.1-mini - max_input_tokens: 1047576 - max_output_tokens: 32768 - input_price: 0.4 - output_price: 1.6 - supports_vision: true - supports_function_calling: true - - name: gpt-4.1-nano - max_input_tokens: 1047576 - max_output_tokens: 32768 - input_price: 0.1 - output_price: 0.4 - supports_vision: true - supports_function_calling: true - - name: gpt-4o - max_input_tokens: 128000 - max_output_tokens: 16384 - input_price: 2.5 - output_price: 10 - supports_vision: true - supports_function_calling: true - - name: gpt-4o-mini - max_input_tokens: 128000 - max_output_tokens: 16384 - input_price: 0.15 - output_price: 0.6 - supports_vision: true - supports_function_calling: true - - name: o4-mini - max_output_tokens: 100000 - max_input_tokens: 200000 - input_price: 1.1 - output_price: 4.4 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - reasoning_levels: [low, medium, high] - default_reasoning_effort: medium - patch: - body: - max_tokens: null - temperature: null - top_p: null - - name: o4-mini-high - max_output_tokens: 100000 - real_name: o4-mini - max_input_tokens: 200000 - input_price: 1.1 - output_price: 4.4 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - patch: - body: - reasoning_effort: high - max_tokens: null - temperature: null - top_p: null - - name: o3 - max_output_tokens: 100000 - max_input_tokens: 200000 - input_price: 2 - output_price: 8 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - patch: - body: - max_tokens: null - temperature: null - top_p: null - - name: o3-high - real_name: o3 - max_input_tokens: 200000 - input_price: 2 - output_price: 8 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - patch: - body: - reasoning_effort: high - max_tokens: null - temperature: null - top_p: null - - name: o3-mini - max_output_tokens: 100000 - max_input_tokens: 200000 - input_price: 1.1 - output_price: 4.4 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - patch: - body: - max_tokens: null - temperature: null - top_p: null - - name: o3-mini-high - max_output_tokens: 100000 - real_name: o3-mini - max_input_tokens: 200000 - input_price: 1.1 - output_price: 4.4 - supports_vision: true - supports_function_calling: true - system_prompt_prefix: Formatting re-enabled - patch: - body: - reasoning_effort: high - max_tokens: null - temperature: null - top_p: null - - name: gpt-4-turbo - max_input_tokens: 128000 - max_output_tokens: 4096 - input_price: 10 - output_price: 30 - supports_vision: true - supports_function_calling: true - - name: gpt-3.5-turbo - max_input_tokens: 16385 - max_output_tokens: 4096 - input_price: 0.5 - output_price: 1.5 - supports_function_calling: true - - name: text-embedding-3-large - type: embedding - input_price: 0.13 - max_tokens_per_chunk: 8191 - default_chunk_size: 2000 - max_batch_size: 100 - - name: text-embedding-3-small - type: embedding - input_price: 0.02 - max_tokens_per_chunk: 8191 - default_chunk_size: 2000 - max_batch_size: 100 - -# Links: -# - https://ai.google.dev/models/gemini -# - https://ai.google.dev/pricing -# - https://ai.google.dev/api/rest/v1beta/models/streamGenerateContent + - name: text-embedding-3-large + type: embedding + input_price: 0.13 + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: text-embedding-3-small + type: embedding + input_price: 0.02 + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: o4-mini-high + type: chat + real_name: o4-mini + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + - name: o3-high + type: chat + real_name: o3 + max_input_tokens: 200000 + input_price: 2.0 + output_price: 8.0 + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + - name: o3-mini-high + type: chat + real_name: o3-mini + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + patch: + body: + reasoning_effort: high + max_tokens: null + temperature: null + top_p: null + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + system_prompt_prefix: Formatting re-enabled + - name: chat-latest + type: chat + max_input_tokens: 400000 + input_price: 5.0 + output_price: 30.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-5.1-chat-latest + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + default_reasoning_effort: none + - name: gpt-5-chat-latest + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: gpt-5.6 + type: chat + max_input_tokens: 1050000 + input_price: 4.0 + output_price: 20.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: gpt-5.6-luna + type: chat + max_input_tokens: 1050000 + input_price: 0.2 + output_price: 1.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: gpt-5.6-sol + type: chat + max_input_tokens: 1050000 + input_price: 4.0 + output_price: 20.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: gpt-5.6-terra + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: gpt-5.5 + type: chat + max_input_tokens: 1050000 + input_price: 5.0 + output_price: 30.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: gpt-5.5-pro + type: chat + max_input_tokens: 1050000 + input_price: 30.0 + output_price: 180.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: high + - name: gpt-5.4-mini + type: chat + max_input_tokens: 400000 + input_price: 0.75 + output_price: 4.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: gpt-5.4-nano + type: chat + max_input_tokens: 400000 + input_price: 0.2 + output_price: 1.25 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: gpt-5.4 + type: chat + max_input_tokens: 1050000 + input_price: 2.5 + output_price: 15.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: gpt-5.4-pro + type: chat + max_input_tokens: 1050000 + input_price: 30.0 + output_price: 180.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: gpt-5.3-chat-latest + type: chat + max_input_tokens: 128000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: gpt-5.3-codex + type: chat + max_input_tokens: 400000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: gpt-5.3-codex-spark + type: chat + max_input_tokens: 128000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 32000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - name: gpt-5.2 + type: chat + max_input_tokens: 400000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: gpt-5.2-chat-latest + type: chat + max_input_tokens: 128000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - name: gpt-5.2-pro + type: chat + max_input_tokens: 400000 + input_price: 21.0 + output_price: 168.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: gpt-5.1 + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + default_reasoning_effort: none + - name: gpt-5-pro + type: chat + max_input_tokens: 400000 + input_price: 15.0 + output_price: 120.0 + max_output_tokens: 272000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - high + - name: gpt-5 + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: gpt-5-mini + type: chat + max_input_tokens: 400000 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gpt-5-nano + type: chat + max_input_tokens: 400000 + input_price: 0.05 + output_price: 0.4 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: o3-pro + type: chat + max_input_tokens: 200000 + input_price: 20.0 + output_price: 80.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: o3 + type: chat + max_input_tokens: 200000 + input_price: 2.0 + output_price: 8.0 + patch: + body: + max_tokens: null + temperature: null + top_p: null + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + system_prompt_prefix: Formatting re-enabled + - name: o4-mini + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + patch: + body: + max_tokens: null + temperature: null + top_p: null + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: medium + system_prompt_prefix: Formatting re-enabled + - name: gpt-4.1 + type: chat + max_input_tokens: 1047576 + input_price: 2.0 + output_price: 8.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gpt-4.1-mini + type: chat + max_input_tokens: 1047576 + input_price: 0.4 + output_price: 1.6 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gpt-4.1-nano + type: chat + max_input_tokens: 1047576 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: o1-pro + type: chat + max_input_tokens: 200000 + input_price: 150.0 + output_price: 600.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: o3-mini + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + patch: + body: + max_tokens: null + temperature: null + top_p: null + max_output_tokens: 100000 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + system_prompt_prefix: Formatting re-enabled + - name: o1 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 60.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gpt-4o-2024-11-20 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: gpt-4o-2024-08-06 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: gpt-4o-mini + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: gpt-4o + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: gpt-4o-2024-05-13 + type: chat + max_input_tokens: 128000 + input_price: 5.0 + output_price: 15.0 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: gpt-4 + type: chat + max_input_tokens: 8192 + input_price: 30.0 + output_price: 60.0 + max_output_tokens: 8192 + supports_function_calling: true + - name: gpt-4-turbo + type: chat + max_input_tokens: 128000 + input_price: 10.0 + output_price: 30.0 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: gpt-3.5-turbo + type: chat + max_input_tokens: 16385 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 4096 + supports_function_calling: true - provider: gemini models: - - name: gemini-3.6-flash - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 1.5 - output_price: 7.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_level: medium - - name: gemini-3.5-flash - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: gemini-3-flash-preview - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: high - - name: gemini-3.1-flash-lite - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: minimal - - name: gemini-3.1-pro-preview - max_input_tokens: 1048576 - max_output_tokens: 65535 - input_price: 0.3 - output_price: 2.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: gemini-2.5-flash - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0 - output_price: 0 - supports_vision: true - supports_function_calling: true - - name: gemini-2.5-pro - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0 - output_price: 0 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: gemini-2.5-flash-lite - max_input_tokens: 1000000 - max_output_tokens: 64000 - input_price: 0 - output_price: 0 - supports_vision: true - supports_function_calling: true - - name: gemini-3-pro-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, high] - default_reasoning_level: high - - name: gemini-3-flash-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_level: high - - name: gemma-3-27b-it - max_input_tokens: 131072 - max_output_tokens: 8192 - input_price: 0 - output_price: 0 - - name: text-embedding-004 - type: embedding - input_price: 0 - max_tokens_per_chunk: 2048 - default_chunk_size: 1500 - max_batch_size: 100 - -# Links: -# - https://docs.anthropic.com/en/docs/about-claude/models/all-models -# - https://docs.anthropic.com/en/api/messages + - name: text-embedding-004 + type: embedding + input_price: 0.0 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 100 + - name: gemini-3-pro-preview + type: chat + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + default_reasoning_effort: high + - name: gemma-3-27b-it + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 8192 + - name: gemini-3.8-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-3.7-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-flash-latest + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-3.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-3.6-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: gemini-flash-lite-latest + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-3.1-flash-lite-image + type: chat + max_input_tokens: 65536 + input_price: 0.25 + output_price: 30.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - high + - name: gemini-omni-flash-preview + type: chat + max_input_tokens: 131072 + input_price: 1.5 + output_price: 17.5 + max_output_tokens: 65536 + supports_vision: true + - name: gemini-3.5-live-translate-preview + type: chat + max_input_tokens: 16384 + input_price: 3.5 + output_price: 21.0 + max_output_tokens: 32768 + - name: gemini-3-pro-image + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 120.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - name: gemini-3.1-flash-image + type: chat + max_input_tokens: 65536 + input_price: 0.5 + output_price: 60.0 + max_output_tokens: 65536 + supports_vision: true + reasoning_levels: + - minimal + - high + - name: gemini-3.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 1.5 + output_price: 9.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: gemini-3.1-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: minimal + - name: deep-research-max-preview-04-2026 + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: deep-research-preview-04-2026 + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: gemma-4-26b-a4b-it + type: chat + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.34 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gemma-4-31b-it + type: chat + max_input_tokens: 262144 + input_price: 0.09 + output_price: 0.34 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gemini-3.1-flash-live-preview + type: chat + max_input_tokens: 131072 + input_price: 0.75 + output_price: 4.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: lyria-3-clip-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_vision: true + - name: lyria-3-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_vision: true + - name: gemini-3.1-flash-lite-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-3.1-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high + - name: gemini-3.1-pro-preview-customtools + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-3-flash-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: high + - name: gemini-2.5-computer-use-preview-10-2025 + type: chat + max_input_tokens: 131072 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: gemini-2.5-flash-image + type: chat + max_input_tokens: 32768 + input_price: 0.3 + output_price: 30.0 + max_output_tokens: 32768 + supports_vision: true + - name: gemini-2.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: gemini-2.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: gemini-2.5-pro + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high - provider: claude models: - - name: claude-opus-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-fable-5-1 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-fable-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-8 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-7 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: claude-opus-4-6:thinking - real_name: claude-opus-4-6 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-sonnet-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: claude-sonnet-4-6:thinking - real_name: claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-sonnet-4-5-20250929 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: claude-sonnet-4-5-20250929:thinking - real_name: claude-sonnet-4-5-20250929 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-haiku-4-5-20251001 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - - name: claude-haiku-4-5-20251001:thinking - real_name: claude-haiku-4-5-20251001 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-opus-4-5-20251101 - type: chat - max_input_tokens: 200000 - input_price: 15.0 - output_price: 75.0 - max_output_tokens: 8192 - require_max_tokens: true - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-5-20251101:thinking - type: chat - real_name: claude-opus-4-5-20251101 - max_input_tokens: 200000 - input_price: 15.0 - output_price: 75.0 - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - max_output_tokens: 24000 - require_max_tokens: true - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-1-20250805 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 15 - output_price: 75 - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-1-20250805:thinking - real_name: claude-opus-4-1-20250805 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 15 - output_price: 75 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-opus-4-20250514 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 15 - output_price: 75 - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-20250514:thinking - real_name: claude-opus-4-20250514 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 15 - output_price: 75 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-sonnet-4-5-20250929 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: claude-sonnet-4-5-20250929:thinking - real_name: claude-sonnet-4-5-20250929 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-haiku-4-5-20251001 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - - name: claude-haiku-4-5-20251001:thinking - real_name: claude-haiku-4-5-20251001 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - -# Links: -# - https://docs.mistral.ai/getting-started/models/models_overview/ -# - https://mistral.ai/pricing#api-pricing -# - https://docs.mistral.ai/api/ + - name: claude-opus-4-1-20250805 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 8192 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-opus-4-1-20250805:thinking + type: chat + real_name: claude-opus-4-1-20250805 + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-opus-4-20250514 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 8192 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-opus-4-20250514:thinking + type: chat + real_name: claude-opus-4-20250514 + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: claude-sonnet-4-6:thinking + type: chat + real_name: claude-sonnet-4-6 + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + default_reasoning_effort: high + - name: claude-opus-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: claude-opus-4-6:thinking + type: chat + real_name: claude-opus-4-6 + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + default_reasoning_effort: high + - name: claude-opus-4-5 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: claude-opus-4-5-20251101 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: claude-opus-4-5-20251101:thinking + type: chat + real_name: claude-opus-4-5-20251101 + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-haiku-4-5 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-haiku-4-5-20251001 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-haiku-4-5-20251001:thinking + type: chat + real_name: claude-haiku-4-5-20251001 + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-4-5 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-4-5-20250929 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-4-5-20250929:thinking + type: chat + real_name: claude-sonnet-4-5-20250929 + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true - provider: mistral models: - - name: mistral-large-latest - max_output_tokens: 262144 - input_price: 0.5 - output_price: 1.5 - supports_function_calling: true - supports_vision: true - - name: mistral-medium-latest - max_input_tokens: 131072 - input_price: 0.4 - output_price: 2 - supports_function_calling: true - supports_vision: true - - name: mistral-small-latest - max_input_tokens: 32768 - input_price: 0.1 - output_price: 0.3 - supports_function_calling: true - supports_vision: true - - name: magistral-medium-latest - max_input_tokens: 131072 - input_price: 2 - output_price: 5 - - name: magistral-small-latest - max_input_tokens: 131072 - input_price: 0.5 - output_price: 1.5 - - name: devstral-medium-latest - max_input_tokens: 262144 - input_price: 0.4 - output_price: 2 - supports_function_calling: true - - name: devstral-small-latest - max_input_tokens: 262144 - input_price: 0.1 - output_price: 0.3 - supports_function_calling: true - - name: codestral-latest - max_input_tokens: 262144 - input_price: 0.3 - output_price: 0.9 - supports_function_calling: true - - name: ministral-14b-latest - max_input_tokens: 262144 - input_price: 0.2 - output_price: 0.2 - supports_function_calling: true - - name: mistral-embed - type: embedding - max_input_tokens: 8092 - input_price: 0.1 - max_tokens_per_chunk: 8092 - default_chunk_size: 2000 - -# Links: -# - https://docs.ai21.com/docs/jamba-foundation-models -# - https://www.ai21.com/pricing -# - https://docs.ai21.com/reference/jamba-1-6-api-ref + - name: mistral-embed + type: embedding + max_input_tokens: 8092 + input_price: 0.1 + output_price: 0.0 + max_output_tokens: 3072 + max_tokens_per_chunk: 8092 + default_chunk_size: 2000 + - name: magistral-small-latest + type: chat + max_input_tokens: 131072 + input_price: 0.5 + output_price: 1.5 + - name: devstral-small-latest + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.3 + supports_function_calling: true + - name: ministral-14b-latest + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 0.2 + supports_function_calling: true + - name: zai-glm-5-2 + type: chat + max_input_tokens: 1000000 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - high + - max + - name: mistral-medium-2604 + type: chat + max_input_tokens: 262144 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - high + - name: mistral-medium-latest + type: chat + max_input_tokens: 262144 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - high + - name: mistral-small-2603 + type: chat + max_input_tokens: 256000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 256000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - high + - name: mistral-small-latest + type: chat + max_input_tokens: 256000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 256000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - high + - name: devstral-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 262144 + supports_function_calling: true + - name: devstral-latest + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 262144 + supports_function_calling: true + - name: labs-devstral-small-2512 + type: chat + max_input_tokens: 256000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 256000 + supports_vision: true + supports_function_calling: true + - name: devstral-medium-latest + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 262144 + supports_function_calling: true + - name: mistral-medium-2508 + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: devstral-medium-2507 + type: chat + max_input_tokens: 128000 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 128000 + supports_function_calling: true + - name: devstral-small-2507 + type: chat + max_input_tokens: 128000 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 128000 + supports_function_calling: true + - name: mistral-small-2506 + type: chat + max_input_tokens: 128000 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: devstral-small-2505 + type: chat + max_input_tokens: 128000 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 128000 + supports_function_calling: true + - name: mistral-medium-2505 + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: magistral-medium-latest + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 5.0 + max_output_tokens: 16384 + supports_function_calling: true + - name: magistral-small + type: chat + max_input_tokens: 128000 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 128000 + supports_function_calling: true + - name: mistral-large-2411 + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 16384 + supports_function_calling: true + - name: mistral-large-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: mistral-large-latest + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: pixtral-large-latest + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ministral-3b-latest + type: chat + max_input_tokens: 128000 + input_price: 0.04 + output_price: 0.04 + max_output_tokens: 128000 + supports_function_calling: true + - name: ministral-8b-latest + type: chat + max_input_tokens: 128000 + input_price: 0.1 + output_price: 0.1 + max_output_tokens: 128000 + supports_function_calling: true + - name: pixtral-12b + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.15 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: mistral-nemo + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.15 + max_output_tokens: 128000 + supports_function_calling: true + - name: open-mistral-nemo + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.15 + max_output_tokens: 128000 + supports_function_calling: true + - name: codestral-latest + type: chat + max_input_tokens: 256000 + input_price: 0.3 + output_price: 0.9 + max_output_tokens: 4096 + supports_function_calling: true + - name: open-mixtral-8x22b + type: chat + max_input_tokens: 64000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 64000 + supports_function_calling: true + - name: open-mixtral-8x7b + type: chat + max_input_tokens: 32000 + input_price: 0.7 + output_price: 0.7 + max_output_tokens: 32000 + supports_function_calling: true + - name: open-mistral-7b + type: chat + max_input_tokens: 8000 + input_price: 0.25 + output_price: 0.25 + max_output_tokens: 8000 + supports_function_calling: true - provider: ai21 models: - - name: jamba-large - max_input_tokens: 256000 - input_price: 2 - output_price: 8 - supports_function_calling: true - - name: jamba-mini - max_input_tokens: 256000 - input_price: 0.2 - output_price: 0.4 - supports_function_calling: true - -# Links: -# - https://docs.cohere.com/docs/models -# - https://cohere.com/pricing -# - https://docs.cohere.com/reference/chat + - name: jamba-large + type: chat + max_input_tokens: 256000 + input_price: 2.0 + output_price: 8.0 + supports_function_calling: true + - name: jamba-mini + type: chat + max_input_tokens: 256000 + input_price: 0.2 + output_price: 0.4 + supports_function_calling: true - provider: cohere models: - - name: command-a-03-2025 - max_input_tokens: 262144 - max_output_tokens: 8192 - input_price: 2.5 - output_price: 10 - supports_function_calling: true - - name: command-a-reasoning-08-2025 - max_input_tokens: 262144 - max_output_tokens: 32768 - input_price: 2.5 - output_price: 10 - - name: command-a-vision-07-2025 - max_input_tokens: 131072 - max_output_tokens: 8192 - input_price: 2.5 - output_price: 10 - supports_vision: true - - name: command-r7b-12-2024 - max_input_tokens: 131072 - max_output_tokens: 4096 - input_price: 0.0375 - output_price: 0.15 - - name: embed-v4.0 - type: embedding - input_price: 0.12 - max_tokens_per_chunk: 2048 - default_chunk_size: 2000 - max_batch_size: 96 - - name: embed-english-v3.0 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: embed-multilingual-v3.0 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: rerank-v3.5 - type: reranker - max_input_tokens: 4096 - - name: rerank-english-v3.0 - type: reranker - max_input_tokens: 4096 - - name: rerank-multilingual-v3.0 - type: reranker - max_input_tokens: 4096 - -# Links: -# - https://docs.x.ai/docs/models -# - https://docs.x.ai/docs/api-reference#chat-completions + - name: embed-v4.0 + type: embedding + input_price: 0.12 + max_tokens_per_chunk: 2048 + default_chunk_size: 2000 + max_batch_size: 96 + - name: embed-english-v3.0 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: embed-multilingual-v3.0 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: rerank-v3.5 + type: reranker + max_input_tokens: 4096 + - name: rerank-english-v3.0 + type: reranker + max_input_tokens: 4096 + - name: rerank-multilingual-v3.0 + type: reranker + max_input_tokens: 4096 + - name: north-mini-code-1-0 + type: chat + max_input_tokens: 256000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 64000 + supports_function_calling: true + reasoning_levels: + - none + - high + - name: command-a-plus-05-2026 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: command-a-translate-08-2025 + type: chat + max_input_tokens: 8000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 8000 + supports_function_calling: true + - name: command-a-reasoning-08-2025 + type: chat + max_input_tokens: 256000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 32000 + supports_function_calling: true + - name: command-a-vision-07-2025 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 8000 + supports_vision: true + - name: command-a-03-2025 + type: chat + max_input_tokens: 256000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 8000 + supports_function_calling: true + - name: c4ai-aya-vision-32b + type: chat + max_input_tokens: 16000 + max_output_tokens: 4000 + supports_vision: true + - name: c4ai-aya-vision-8b + type: chat + max_input_tokens: 16000 + max_output_tokens: 4000 + supports_vision: true + - name: command-r7b-arabic-02-2025 + type: chat + max_input_tokens: 128000 + input_price: 0.0375 + output_price: 0.15 + max_output_tokens: 4000 + supports_function_calling: true + - name: command-r7b-12-2024 + type: chat + max_input_tokens: 128000 + input_price: 0.0375 + output_price: 0.15 + max_output_tokens: 4000 + supports_function_calling: true + - name: c4ai-aya-expanse-32b + type: chat + max_input_tokens: 128000 + max_output_tokens: 4000 + - name: c4ai-aya-expanse-8b + type: chat + max_input_tokens: 8000 + max_output_tokens: 4000 + - name: command-r-08-2024 + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 4000 + supports_function_calling: true + - name: command-r-plus-08-2024 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 4000 + supports_function_calling: true - provider: xai oauth: client_id: b1a00492-073a-47ea-816f-4c329264a828 - authorize_url: https://auth.x.ai/oauth2/authorize token_url: https://auth.x.ai/oauth2/token - scopes: - - openid - - profile - - email - - offline_access - - grok-cli:access - - api:access - redirect_port: 56121 flow: pkce + client_secret: null + authorize_url: https://auth.x.ai/oauth2/authorize + redirect_uri: null + redirect_port: 56121 + device_authorization_url: null + scopes: + - openid + - profile + - email + - offline_access + - grok-cli:access + - api:access token_request_format: form_url_encoded extra_authorize_params: plan: generic referrer: coyote + extra_token_headers: {} + extra_request_headers: {} echo_pkce_in_token_exchange: true + use_pkce_in_device_flow: false + include_state_in_token_exchange: true models: - - name: grok-4.6 - input_price: 2 - output_price: 6 - max_input_tokens: 500000 - supports_function_calling: true - - name: grok-4.5 - input_price: 2 - output_price: 6 - max_input_tokens: 256000 - supports_function_calling: true - - name: grok-build-0.1 - input_price: 1 - output_price: 2 - max_input_tokens: 256000 - supports_function_calling: true - - name: grok-4.3 - input_price: 1.25 - output_price: 2.5 - max_input_tokens: 1000000 - supports_function_calling: true - - name: grok-4.20 - real_name: grok-4.20-multi-agent-0309 - input_price: 1.25 - output_price: 2.5 - max_input_tokens: 1000000 - supports_function_calling: true - - name: grok-4.20-reasoning - real_name: grok-4.20-0309-reasoning - input_price: 1.25 - output_price: 2.5 - max_input_tokens: 1000000 - supports_function_calling: true - - name: grok-4.20-non-reasoning - real_name: grok-4.20-0309-non-reasoning - input_price: 1.25 - output_price: 2.5 - max_input_tokens: 1000000 - supports_function_calling: true - - name: grok-4-1-fast-non-reasoning - max_input_tokens: 1000000 - input_price: 0.2 - output_price: 0.5 - supports_function_calling: true - - name: grok-4-1-fast-reasoning - max_input_tokens: 1000000 - input_price: 0.2 - output_price: 0.5 - supports_function_calling: true - - name: grok-code-fast-1 - max_input_tokens: 256000 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - - name: grok-3 - max_input_tokens: 131072 - input_price: 3 - output_price: 15 - supports_function_calling: true - - name: grok-3-mini - max_input_tokens: 131072 - input_price: 0.3 - output_price: 0.5 - supports_function_calling: true - -# Links: -# - https://docs.perplexity.ai/getting-started/models -# - https://docs.perplexity.ai/api-reference/chat-completions + - name: grok-4.20 + type: chat + real_name: grok-4.20-multi-agent-0309 + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + supports_function_calling: true + - name: grok-4.20-reasoning + type: chat + real_name: grok-4.20-0309-reasoning + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + supports_function_calling: true + - name: grok-4.20-non-reasoning + type: chat + real_name: grok-4.20-0309-non-reasoning + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + supports_function_calling: true + - name: grok-4-1-fast-non-reasoning + type: chat + max_input_tokens: 1000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: grok-4-1-fast-reasoning + type: chat + max_input_tokens: 1000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: grok-code-fast-1 + type: chat + max_input_tokens: 256000 + input_price: 0.2 + output_price: 1.5 + supports_function_calling: true + - name: grok-3 + type: chat + max_input_tokens: 131072 + input_price: 3.0 + output_price: 15.0 + supports_function_calling: true + - name: grok-3-mini + type: chat + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.5 + supports_function_calling: true + - name: grok-4.6 + type: chat + max_input_tokens: 500000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 500000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - name: grok-4.5 + type: chat + max_input_tokens: 500000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 500000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: grok-4.3 + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 30000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - name: grok-build-0.1 + type: chat + max_input_tokens: 256000 + input_price: 1.0 + output_price: 2.0 + max_output_tokens: 256000 + supports_vision: true + supports_function_calling: true + - name: grok-4.20-0309-non-reasoning + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 30000 + supports_vision: true + supports_function_calling: true + - name: grok-4.20-0309-reasoning + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 30000 + supports_vision: true + supports_function_calling: true + - name: grok-4.20-multi-agent-0309 + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 30000 + supports_vision: true + reasoning_levels: + - low + - medium + - high + - xhigh - provider: perplexity models: - - name: sonar-pro - max_output_tokens: 8000 - supports_vision: true - max_input_tokens: 200000 - input_price: 3 - output_price: 15 - - name: sonar - supports_vision: true - max_input_tokens: 127072 - input_price: 1 - output_price: 1 - - name: sonar-reasoning-pro - supports_vision: true - max_input_tokens: 128000 - input_price: 2 - output_price: 8 - - name: sonar-reasoning - max_input_tokens: 128000 - input_price: 1 - output_price: 5 - - name: sonar-deep-research - max_input_tokens: 128000 - input_price: 2 - output_price: 8 - -# Links: -# - https://console.groq.com/docs/models -# - https://console.groq.com/docs/api-reference#chat + - name: sonar-reasoning + type: chat + max_input_tokens: 128000 + input_price: 1.0 + output_price: 5.0 + - name: sonar-deep-research + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 8.0 + max_output_tokens: 32768 + reasoning_levels: + - minimal + - low + - medium + - high + - name: sonar + type: chat + max_input_tokens: 128000 + input_price: 1.0 + output_price: 1.0 + max_output_tokens: 4096 + supports_vision: true + - name: sonar-pro + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 8192 + supports_vision: true + - name: sonar-reasoning-pro + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 8.0 + max_output_tokens: 4096 + supports_vision: true + reasoning_levels: + - minimal + - low + - medium + - high - provider: groq models: - - name: openai/gpt-oss-120b - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - supports_function_calling: true - - name: openai/gpt-oss-20b - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - supports_function_calling: true - - name: meta-llama/llama-4-maverick-17b-128e-instruct - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - supports_vision: true - supports_function_calling: true - - name: meta-llama/llama-4-scout-17b-16e-instruct - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - supports_vision: true - supports_function_calling: true - - name: llama-3.3-70b-versatile - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - supports_function_calling: true - - name: moonshotai/kimi-k2-instruct-0905 - max_input_tokens: 262144 - input_price: 0 - output_price: 0 - supports_function_calling: true - - name: qwen/qwen3-32b - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - - name: groq/compound - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - - name: groq/compound-mini - max_input_tokens: 131072 - input_price: 0 - output_price: 0 - -# Links: -# - https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models -# - https://cloud.google.com/vertex-ai/generative-ai/pricing -# - https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/gemini + - name: meta-llama/llama-4-maverick-17b-128e-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + supports_vision: true + supports_function_calling: true + - name: meta-llama/llama-4-scout-17b-16e-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k2-instruct-0905 + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + supports_function_calling: true + - name: qwen/qwen3-32b + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + - name: qwen/qwen3.8-27b + type: chat + max_input_tokens: 131042 + input_price: 0.8 + output_price: 4.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - default + - low + - medium + - high + - name: qwen/qwen3.6-27b + type: chat + max_input_tokens: 131072 + input_price: 0.6 + output_price: 3.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - default + - name: canopylabs/orpheus-v1-english + type: chat + max_input_tokens: 4000 + max_output_tokens: 50000 + - name: canopylabs/orpheus-arabic-saudi + type: chat + max_input_tokens: 4000 + max_output_tokens: 50000 + - name: openai/gpt-oss-safeguard-20b + type: chat + max_input_tokens: 131072 + input_price: 0.075 + output_price: 0.3 + max_output_tokens: 65536 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: groq/compound + type: chat + max_input_tokens: 131072 + max_output_tokens: 8192 + - name: groq/compound-mini + type: chat + max_input_tokens: 131072 + max_output_tokens: 8192 + - name: openai/gpt-oss-120b + type: chat + max_input_tokens: 131072 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 65536 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai/gpt-oss-20b + type: chat + max_input_tokens: 131072 + input_price: 0.075 + output_price: 0.3 + max_output_tokens: 65536 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: meta-llama/llama-prompt-guard-2-22m + type: chat + max_input_tokens: 512 + input_price: 0.03 + output_price: 0.03 + max_output_tokens: 512 + - name: meta-llama/llama-prompt-guard-2-86m + type: chat + max_input_tokens: 512 + input_price: 0.04 + output_price: 0.04 + max_output_tokens: 512 + - name: allam-2-7b + type: chat + max_input_tokens: 4096 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 4096 + - name: llama-3.3-70b-versatile + type: chat + max_input_tokens: 131072 + input_price: 0.59 + output_price: 0.79 + max_output_tokens: 32768 + supports_function_calling: true + - name: llama-3.1-8b-instant + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.08 + max_output_tokens: 131072 + supports_function_calling: true - provider: vertexai models: - - name: gemini-3.5-flash - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: gemini-3-flash-preview - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: high - - name: gemini-3.1-flash-lite - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, high] - default_reasoning_effort: minimal - - name: gemini-3.1-pro-preview - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 2 - output_price: 12 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: gemini-2.5-flash - max_input_tokens: 1048576 - max_output_tokens: 65535 - input_price: 0.3 - output_price: 2.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: medium - - name: gemini-2.5-pro - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: gemini-2.5-flash-lite - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.3 - output_price: 0.4 - supports_vision: true - supports_function_calling: true - - name: gemini-3-pro-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, high] - default_reasoning_effort: high - - name: gemini-3-flash-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: high - - name: claude-opus-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-fable-5-1 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-fable-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-8 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-7 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-opus-4-6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-6:thinking - real_name: claude-opus-4-6 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-sonnet-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: claude-sonnet-4-6:thinking - real_name: claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-opus-4-5@20251101 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - - name: claude-opus-4-5@20251101:thinking - real_name: claude-opus-4-5@20251101 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-sonnet-4-5@20250929 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: claude-sonnet-4-5@20250929:thinking - real_name: claude-sonnet-4-5@20250929 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-haiku-4-5@20251001 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - - name: claude-haiku-4-5@20251001:thinking - real_name: claude-haiku-4-5@20251001 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - patch: - body: - temperature: null - top_p: null - thinking: - type: enabled - budget_tokens: 16000 - - name: claude-3-5-haiku@20241022 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 0.8 - output_price: 4 - supports_vision: true - supports_function_calling: true - - name: mistral-small-2503 - max_input_tokens: 32000 - input_price: 0.1 - output_price: 0.3 - supports_function_calling: true - - name: codestral-2501 - max_input_tokens: 256000 - input_price: 0.3 - output_price: 0.9 - supports_function_calling: true - - name: text-embedding-005 - type: embedding - max_input_tokens: 20000 - input_price: 0.025 - max_tokens_per_chunk: 2048 - default_chunk_size: 1500 - max_batch_size: 5 - - name: text-multilingual-embedding-002 - type: embedding - max_input_tokens: 20000 - input_price: 0.2 - max_tokens_per_chunk: 2048 - default_chunk_size: 1500 - max_batch_size: 5 - -# Links: -# - https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html#model-ids-arns -# - https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html -# - https://aws.amazon.com/bedrock/pricing/ -# - https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-call.html + - name: text-embedding-005 + type: embedding + max_input_tokens: 20000 + input_price: 0.025 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 5 + - name: text-multilingual-embedding-002 + type: embedding + max_input_tokens: 20000 + input_price: 0.2 + max_tokens_per_chunk: 2048 + default_chunk_size: 1500 + max_batch_size: 5 + - name: gemini-3-pro-preview + type: chat + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + default_reasoning_effort: high + - name: claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-6 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 8192 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-opus-4-6:thinking + type: chat + real_name: claude-opus-4-6 + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-sonnet-4-6 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 8192 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: claude-sonnet-4-6:thinking + type: chat + real_name: claude-sonnet-4-6 + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + default_reasoning_effort: high + - name: claude-3-5-haiku@20241022 + type: chat + max_input_tokens: 200000 + input_price: 0.8 + output_price: 4.0 + max_output_tokens: 8192 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: mistral-small-2503 + type: chat + max_input_tokens: 32000 + input_price: 0.1 + output_price: 0.3 + supports_function_calling: true + - name: codestral-2501 + type: chat + max_input_tokens: 256000 + input_price: 0.3 + output_price: 0.9 + supports_function_calling: true + - name: gemini-3.8-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: claude-fable-5-1@default + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: gemini-3.7-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-flash-latest + type: chat + max_input_tokens: 1048576 + input_price: 1.5 + output_price: 9.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: claude-opus-5@default + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: gemini-3.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-3.6-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-flash-lite-latest + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: claude-sonnet-5@default + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: claude-fable-5@default + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: claude-opus-4-8@default + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: gemini-3-pro-image + type: chat + max_input_tokens: 65536 + input_price: 2.0 + output_price: 120.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gemini-3.1-flash-image + type: chat + max_input_tokens: 131072 + input_price: 0.5 + output_price: 60.0 + max_output_tokens: 32768 + supports_vision: true + reasoning_levels: + - minimal + - high + - name: gemini-3.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 1.5 + output_price: 9.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: gemini-3.1-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: minimal + - name: claude-opus-4-7@default + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: gemini-3.1-flash-lite-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + - name: gemini-3.1-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high + - name: gemini-3.1-pro-preview-customtools + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: claude-sonnet-4-6@default + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: zai-org/glm-5-maas + type: chat + max_input_tokens: 202752 + input_price: 1.0 + output_price: 3.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: claude-opus-4-6@default + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: zai-org/glm-4.7-maas + type: chat + max_input_tokens: 200000 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 128000 + supports_function_calling: true + - name: deepseek-ai/deepseek-v3.2-maas + type: chat + max_input_tokens: 163840 + input_price: 0.56 + output_price: 1.68 + max_output_tokens: 65536 + supports_function_calling: true + - name: gemini-3-flash-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: high + - name: moonshotai/kimi-k2-thinking-maas + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 262144 + supports_function_calling: true + - name: claude-opus-4-5@20251101 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: claude-opus-4-5@20251101:thinking + type: chat + real_name: claude-opus-4-5@20251101 + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: claude-haiku-4-5@20251001 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-haiku-4-5@20251001:thinking + type: chat + real_name: claude-haiku-4-5@20251001 + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: claude-sonnet-4-5@20250929 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-4-5@20250929:thinking + type: chat + real_name: claude-sonnet-4-5@20250929 + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + temperature: null + top_p: null + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: deepseek-ai/deepseek-v3.1-maas + type: chat + max_input_tokens: 163840 + input_price: 0.6 + output_price: 1.7 + max_output_tokens: 32768 + supports_function_calling: true + - name: gemini-2.5-flash-image + type: chat + max_input_tokens: 32768 + input_price: 0.3 + output_price: 30.0 + max_output_tokens: 32768 + supports_vision: true + - name: qwen/qwen3-235b-a22b-instruct-2507-maas + type: chat + max_input_tokens: 262144 + input_price: 0.22 + output_price: 0.88 + max_output_tokens: 16384 + supports_function_calling: true + - name: claude-opus-4-1@20250805 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: openai/gpt-oss-120b-maas + type: chat + max_input_tokens: 131072 + input_price: 0.09 + output_price: 0.36 + max_output_tokens: 32768 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai/gpt-oss-20b-maas + type: chat + max_input_tokens: 131072 + input_price: 0.07 + output_price: 0.25 + max_output_tokens: 32768 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: gemini-2.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: medium + - name: gemini-2.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: gemini-2.5-pro + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high + - name: claude-opus-4@20250514 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-4@20250514 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: meta/llama-3.3-70b-instruct-maas + type: chat + max_input_tokens: 128000 + input_price: 0.72 + output_price: 0.72 + max_output_tokens: 8192 + supports_function_calling: true + - name: meta/llama-4-maverick-17b-128e-instruct-maas + type: chat + max_input_tokens: 524288 + input_price: 0.35 + output_price: 1.15 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true - provider: bedrock models: - - name: us.anthropic.claude-opus-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-fable-5-1 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-fable-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-opus-4-8 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-opus-4-7 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-opus-4-6-v1 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: us.anthropic.claude-opus-4-6-v1:thinking - real_name: us.anthropic.claude-opus-4-6-v1 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - patch: - body: - inferenceConfig: - temperature: null - topP: null - additionalModelRequestFields: - thinking: - type: enabled - budget_tokens: 16000 - - name: us.anthropic.claude-sonnet-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: us.anthropic.claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: us.anthropic.claude-sonnet-4-6:thinking - real_name: us.anthropic.claude-sonnet-4-6 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - patch: - body: - inferenceConfig: - temperature: null - topP: null - additionalModelRequestFields: - thinking: - type: enabled - budget_tokens: 16000 - - name: us.anthropic.claude-opus-4-5-20251101-v1:0 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - - name: us.anthropic.claude-opus-4-5-20251101-v1:0:thinking - real_name: us.anthropic.claude-opus-4-5-20251101-v1:0 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - patch: - body: - inferenceConfig: - temperature: null - topP: null - additionalModelRequestFields: - thinking: - type: enabled - budget_tokens: 16000 - - name: us.anthropic.claude-sonnet-4-5-20250929-v1:0 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: us.anthropic.claude-sonnet-4-5-20250929-v1:0:thinking - real_name: us.anthropic.claude-sonnet-4-5-20250929-v1:0 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - patch: - body: - inferenceConfig: - temperature: null - topP: null - additionalModelRequestFields: - thinking: - type: enabled - budget_tokens: 16000 - - name: us.anthropic.claude-haiku-4-5-20251001-v1:0 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - - name: us.anthropic.claude-haiku-4-5-20251001-v1:0:thinking - real_name: us.anthropic.claude-haiku-4-5-20251001-v1:0 - max_input_tokens: 200000 - max_output_tokens: 24000 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - patch: - body: - inferenceConfig: - temperature: null - topP: null - additionalModelRequestFields: - thinking: - type: enabled - budget_tokens: 16000 - - name: us.meta.llama4-maverick-17b-instruct-v1:0 - max_input_tokens: 131072 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 0.24 - output_price: 0.97 - supports_function_calling: true - supports_vision: true - - name: us.meta.llama4-scout-17b-instruct-v1:0 - max_input_tokens: 131072 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 0.17 - output_price: 0.66 - supports_function_calling: true - supports_vision: true - - name: us.meta.llama3-3-70b-instruct-v1:0 - max_input_tokens: 131072 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 0.72 - output_price: 0.72 - supports_function_calling: true - - name: us.amazon.nova-premier-v1:0 - max_input_tokens: 300000 - max_output_tokens: 5120 - input_price: 2.5 - output_price: 12.5 - - name: us.amazon.nova-pro-v1:0 - max_input_tokens: 300000 - max_output_tokens: 5120 - input_price: 0.8 - output_price: 3.2 - supports_vision: true - - name: us.amazon.nova-lite-v1:0 - max_input_tokens: 300000 - max_output_tokens: 5120 - input_price: 0.06 - output_price: 0.24 - supports_vision: true - - name: us.amazon.nova-micro-v1:0 - max_input_tokens: 128000 - max_output_tokens: 5120 - input_price: 0.035 - output_price: 0.14 - - name: cohere.embed-english-v3 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: cohere.embed-multilingual-v3 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: us.deepseek.r1-v1:0 - max_input_tokens: 128000 - input_price: 1.35 - output_price: 5.4 - -# Links: -# - https://developers.cloudflare.com/workers-ai/models/ -# - https://developers.cloudflare.com/workers-ai/configuration/open-ai-compatibility/ + - name: cohere.embed-english-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: cohere.embed-multilingual-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: us.meta.llama3-3-70b-instruct-v1:0 + type: chat + max_input_tokens: 131072 + input_price: 0.72 + output_price: 0.72 + max_output_tokens: 8192 + require_max_tokens: true + supports_function_calling: true + - name: us.amazon.nova-premier-v1:0 + type: chat + max_input_tokens: 300000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 5120 + - name: us.amazon.nova-pro-v1:0 + type: chat + max_input_tokens: 300000 + input_price: 0.8 + output_price: 3.2 + max_output_tokens: 5120 + supports_vision: true + - name: us.amazon.nova-lite-v1:0 + type: chat + max_input_tokens: 300000 + input_price: 0.06 + output_price: 0.24 + max_output_tokens: 5120 + supports_vision: true + - name: us.amazon.nova-micro-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.035 + output_price: 0.14 + max_output_tokens: 5120 + - name: anthropic.claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 11.0 + output_price: 55.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: xai.grok-4.6 + type: chat + max_input_tokens: 500000 + input_price: 2.2 + output_price: 6.6 + max_output_tokens: 500000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - name: anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: au.anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: eu.anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.5 + output_price: 27.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: jp.anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: global.openai.gpt-5.6-luna + type: chat + max_input_tokens: 1050000 + input_price: 0.2 + output_price: 1.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: global.openai.gpt-5.6-sol + type: chat + max_input_tokens: 1050000 + input_price: 4.0 + output_price: 20.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: global.openai.gpt-5.6-terra + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: openai.gpt-5.6-luna + type: chat + max_input_tokens: 1050000 + input_price: 0.22 + output_price: 1.32 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: openai.gpt-5.6-sol + type: chat + max_input_tokens: 1050000 + input_price: 4.4 + output_price: 22.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: openai.gpt-5.6-terra + type: chat + max_input_tokens: 1050000 + input_price: 2.2 + output_price: 13.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + - name: anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: au.anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: eu.anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.2 + output_price: 11.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: jp.anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic.claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: eu.anthropic.claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 11.0 + output_price: 55.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: au.anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: eu.anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.5 + output_price: 27.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: jp.anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: openai.gpt-5.5 + type: chat + max_input_tokens: 272000 + input_price: 5.5 + output_price: 33.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - name: xai.grok-4.3 + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - name: anthropic.claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: eu.anthropic.claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.5 + output_price: 27.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: global.anthropic.claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: jp.anthropic.claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + - name: us.anthropic.claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: minimax.minimax-m2.5 + type: chat + max_input_tokens: 196608 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 98304 + supports_function_calling: true + - name: zai.glm-5 + type: chat + max_input_tokens: 202752 + input_price: 1.0 + output_price: 3.2 + max_output_tokens: 101376 + supports_function_calling: true + - name: nvidia.nemotron-super-3-120b + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 0.65 + max_output_tokens: 131072 + supports_function_calling: true + - name: openai.gpt-5.4 + type: chat + max_input_tokens: 272000 + input_price: 2.75 + output_price: 16.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - name: anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: au.anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.3 + output_price: 16.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: eu.anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.3 + output_price: 16.5 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: global.anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: jp.anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: mistral.devstral-2-123b + type: chat + max_input_tokens: 256000 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 8192 + supports_function_calling: true + - name: us.anthropic.claude-sonnet-4-6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: us.anthropic.claude-sonnet-4-6:thinking + type: chat + real_name: us.anthropic.claude-sonnet-4-6 + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + default_reasoning_effort: high + - name: deepseek.v3.2 + type: chat + max_input_tokens: 163840 + input_price: 0.62 + output_price: 1.85 + max_output_tokens: 81920 + supports_function_calling: true + - name: moonshotai.kimi-k2.5 + type: chat + max_input_tokens: 262143 + input_price: 0.6 + output_price: 3.0 + max_output_tokens: 16000 + supports_vision: true + supports_function_calling: true + - name: qwen.qwen3-coder-next + type: chat + max_input_tokens: 131072 + input_price: 0.22 + output_price: 1.8 + max_output_tokens: 65536 + supports_function_calling: true + - name: anthropic.claude-opus-4-6-v1 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: au.anthropic.claude-opus-4-6-v1 + type: chat + max_input_tokens: 1000000 + input_price: 16.5 + output_price: 82.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: eu.anthropic.claude-opus-4-6-v1 + type: chat + max_input_tokens: 1000000 + input_price: 5.5 + output_price: 27.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: global.anthropic.claude-opus-4-6-v1 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + - name: us.anthropic.claude-opus-4-6-v1 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: us.anthropic.claude-opus-4-6-v1:thinking + type: chat + real_name: us.anthropic.claude-opus-4-6-v1 + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + default_reasoning_effort: high + - name: zai.glm-4.7-flash + type: chat + max_input_tokens: 200000 + input_price: 0.07 + output_price: 0.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: minimax.minimax-m2.1 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: nvidia.nemotron-nano-3-30b + type: chat + max_input_tokens: 128000 + input_price: 0.06 + output_price: 0.24 + max_output_tokens: 4096 + supports_function_calling: true + - name: zai.glm-4.7 + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: mistral.magistral-small-2509 + type: chat + max_input_tokens: 128000 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 40000 + supports_vision: true + supports_function_calling: true + - name: mistral.ministral-3-3b-instruct + type: chat + max_input_tokens: 256000 + input_price: 0.1 + output_price: 0.1 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: mistral.mistral-large-3-675b-instruct + type: chat + max_input_tokens: 256000 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: moonshot.kimi-k2-thinking + type: chat + max_input_tokens: 262143 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 16000 + supports_function_calling: true + - name: anthropic.claude-opus-4-5-20251101-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: eu.anthropic.claude-opus-4-5-20251101-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 5.5 + output_price: 27.5 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: global.anthropic.claude-opus-4-5-20251101-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: us.anthropic.claude-opus-4-5-20251101-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: us.anthropic.claude-opus-4-5-20251101-v1:0:thinking + type: chat + real_name: us.anthropic.claude-opus-4-5-20251101-v1:0 + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: openai.gpt-oss-safeguard-120b + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_function_calling: true + - name: openai.gpt-oss-safeguard-20b + type: chat + max_input_tokens: 128000 + input_price: 0.07 + output_price: 0.2 + max_output_tokens: 16384 + supports_function_calling: true + - name: minimax.minimax-m2 + type: chat + max_input_tokens: 204608 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 128000 + supports_function_calling: true + - name: anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: au.anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: eu.anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 5.5 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: global.anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: jp.anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: us.anthropic.claude-haiku-4-5-20251001-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: us.anthropic.claude-haiku-4-5-20251001-v1:0:thinking + type: chat + real_name: us.anthropic.claude-haiku-4-5-20251001-v1:0 + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: qwen.qwen3-vl-235b-a22b + type: chat + max_input_tokens: 262000 + input_price: 0.3 + output_price: 1.5 + max_output_tokens: 262000 + supports_vision: true + supports_function_calling: true + - name: anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: au.anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: eu.anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.3 + output_price: 16.5 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: global.anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: jp.anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: us.anthropic.claude-sonnet-4-5-20250929-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: us.anthropic.claude-sonnet-4-5-20250929-v1:0:thinking + type: chat + real_name: us.anthropic.claude-sonnet-4-5-20250929-v1:0 + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + patch: + body: + inferenceConfig: + temperature: null + topP: null + additionalModelRequestFields: + thinking: + type: enabled + budget_tokens: 16000 + max_output_tokens: 24000 + require_max_tokens: true + supports_vision: true + - name: deepseek.v3-v1:0 + type: chat + max_input_tokens: 163840 + input_price: 0.58 + output_price: 1.68 + max_output_tokens: 81920 + supports_function_calling: true + - name: qwen.qwen3-235b-a22b-2507-v1:0 + type: chat + max_input_tokens: 262144 + input_price: 0.22 + output_price: 0.88 + max_output_tokens: 131072 + supports_function_calling: true + - name: qwen.qwen3-32b-v1:0 + type: chat + max_input_tokens: 16384 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen.qwen3-coder-30b-a3b-v1:0 + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 131072 + supports_function_calling: true + - name: qwen.qwen3-coder-480b-a35b-v1:0 + type: chat + max_input_tokens: 131072 + input_price: 0.22 + output_price: 1.8 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen.qwen3-next-80b-a3b + type: chat + max_input_tokens: 262000 + input_price: 0.14 + output_price: 1.4 + max_output_tokens: 262000 + supports_function_calling: true + - name: anthropic.claude-opus-4-1-20250805-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + supports_vision: true + supports_function_calling: true + - name: openai.gpt-oss-120b + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai.gpt-oss-120b-1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai.gpt-oss-20b + type: chat + max_input_tokens: 128000 + input_price: 0.07 + output_price: 0.3 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai.gpt-oss-20b-1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.07 + output_price: 0.3 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: us.anthropic.claude-opus-4-1-20250805-v1:0 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: google.gemma-3-27b-it + type: chat + max_input_tokens: 202752 + input_price: 0.12 + output_price: 0.2 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: mistral.voxtral-small-24b-2507 + type: chat + max_input_tokens: 32000 + input_price: 0.15 + output_price: 0.35 + max_output_tokens: 8192 + supports_function_calling: true + - name: writer.palmyra-x4-v1:0 + type: chat + max_input_tokens: 122880 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 8192 + supports_function_calling: true + - name: writer.palmyra-x5-v1:0 + type: chat + max_input_tokens: 1040000 + input_price: 0.6 + output_price: 6.0 + max_output_tokens: 8192 + supports_function_calling: true + - name: mistral.pixtral-large-2502-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: meta.llama4-maverick-17b-instruct-v1:0 + type: chat + max_input_tokens: 1000000 + input_price: 0.24 + output_price: 0.97 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: meta.llama4-scout-17b-instruct-v1:0 + type: chat + max_input_tokens: 3500000 + input_price: 0.17 + output_price: 0.66 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: us.meta.llama4-maverick-17b-instruct-v1:0 + type: chat + max_input_tokens: 1000000 + input_price: 0.24 + output_price: 0.97 + max_output_tokens: 16384 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: us.meta.llama4-scout-17b-instruct-v1:0 + type: chat + max_input_tokens: 3500000 + input_price: 0.17 + output_price: 0.66 + max_output_tokens: 16384 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: deepseek.r1-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 1.35 + output_price: 5.4 + max_output_tokens: 32768 + supports_function_calling: true + - name: us.deepseek.r1-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 1.35 + output_price: 5.4 + max_output_tokens: 32768 + supports_function_calling: true + - name: meta.llama3-3-70b-instruct-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.72 + output_price: 0.72 + max_output_tokens: 4096 + supports_function_calling: true + - name: amazon.nova-lite-v1:0 + type: chat + max_input_tokens: 300000 + input_price: 0.06 + output_price: 0.24 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: amazon.nova-micro-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.035 + output_price: 0.14 + max_output_tokens: 8192 + supports_function_calling: true + - name: amazon.nova-pro-v1:0 + type: chat + max_input_tokens: 300000 + input_price: 0.8 + output_price: 3.2 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: amazon.nova-2-lite-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.33 + output_price: 2.75 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: google.gemma-3-12b-it + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.1 + max_output_tokens: 8192 + supports_vision: true + - name: google.gemma-3-4b-it + type: chat + max_input_tokens: 128000 + input_price: 0.04 + output_price: 0.08 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: mistral.ministral-3-14b-instruct + type: chat + max_input_tokens: 128000 + input_price: 0.2 + output_price: 0.2 + max_output_tokens: 4096 + supports_function_calling: true + - name: mistral.ministral-3-8b-instruct + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.15 + max_output_tokens: 4096 + supports_function_calling: true + - name: mistral.voxtral-mini-3b-2507 + type: chat + max_input_tokens: 128000 + input_price: 0.04 + output_price: 0.04 + max_output_tokens: 4096 + supports_function_calling: true + - name: nvidia.nemotron-nano-12b-v2 + type: chat + max_input_tokens: 128000 + input_price: 0.2 + output_price: 0.6 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: nvidia.nemotron-nano-9b-v2 + type: chat + max_input_tokens: 128000 + input_price: 0.06 + output_price: 0.23 + max_output_tokens: 4096 + supports_function_calling: true + - name: meta.llama3-1-70b-instruct-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.72 + output_price: 0.72 + max_output_tokens: 4096 + supports_function_calling: true + - name: meta.llama3-1-8b-instruct-v1:0 + type: chat + max_input_tokens: 128000 + input_price: 0.22 + output_price: 0.22 + max_output_tokens: 4096 + supports_function_calling: true - provider: cloudflare models: - - name: '@cf/meta/llama-4-scout-17b-16e-instruct' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/meta/llama-3.3-70b-instruct-fp8-fast' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/qwen/qwen3-30b-a3b-fp8' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/qwen/qwen2.5-coder-32b-instruct' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/zai-org/glm-4.7-flash' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/google/gemma-3-12b-it' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/mistralai/mistral-small-3.1-24b-instruct' - max_input_tokens: 131072 - max_output_tokens: 2048 - require_max_tokens: true - input_price: 0 - output_price: 0 - - name: '@cf/baai/bge-large-en-v1.5' - type: embedding - input_price: 0 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 100 - -# Links: -# - https://cloud.baidu.com/doc/qianfan/s/rmh4stp0j -# - https://cloud.baidu.com/doc/qianfan/s/wmh4sv6ya + - name: '@cf/baai/bge-large-en-v1.5' + type: embedding + input_price: 0.0 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: '@cf/google/gemma-3-12b-it' + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 2048 + require_max_tokens: true + - name: '@cf/zai-org/glm-5.3-flash' + type: chat + max_input_tokens: 1310720 + input_price: 0.15 + output_price: 0.5 + max_output_tokens: 1048576 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: '@cf/qwen/qwen3.8-27b' + type: chat + max_input_tokens: 262144 + input_price: 0.45 + output_price: 3.2 + max_output_tokens: 262144 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - xhigh + - name: '@cf/zai-org/glm-5.3' + type: chat + max_input_tokens: 1310720 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 1310720 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/deepseek-ai/deepseek-v4-pro-0813' + type: chat + max_input_tokens: 1048576 + input_price: 1.32 + output_price: 3.96 + max_output_tokens: 1048576 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - high + - max + - name: '@cf/deepseek-ai/deepseek-v4-flash-0731' + type: chat + max_input_tokens: 1310720 + input_price: 0.44 + output_price: 1.32 + max_output_tokens: 1048576 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - high + - max + - name: '@cf/zai-org/glm-5.2' + type: chat + max_input_tokens: 262144 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 256000 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/moonshotai/kimi-k2.7-code' + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 262144 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/moonshotai/kimi-k2.6' + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 256000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/google/gemma-4-26b-a4b-it' + type: chat + max_input_tokens: 256000 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 16384 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/nvidia/nemotron-3-120b-a12b' + type: chat + max_input_tokens: 256000 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 256000 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/zai-org/glm-4.7-flash' + type: chat + max_input_tokens: 131072 + input_price: 0.0605 + output_price: 0.4 + max_output_tokens: 131072 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/ibm-granite/granite-4.0-h-micro' + type: chat + max_input_tokens: 131000 + input_price: 0.017 + output_price: 0.112 + max_output_tokens: 131000 + require_max_tokens: true + supports_function_calling: true + - name: '@cf/aisingapore/gemma-sea-lion-v4-27b-it' + type: chat + max_input_tokens: 128000 + input_price: 0.351 + output_price: 0.555 + max_output_tokens: 128000 + require_max_tokens: true + - name: '@cf/openai/gpt-oss-120b' + type: chat + max_input_tokens: 128000 + input_price: 0.35 + output_price: 0.75 + max_output_tokens: 16384 + require_max_tokens: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: '@cf/openai/gpt-oss-20b' + type: chat + max_input_tokens: 128000 + input_price: 0.2 + output_price: 0.3 + max_output_tokens: 16384 + require_max_tokens: true + supports_function_calling: true + - name: '@cf/qwen/qwen3-30b-a3b-fp8' + type: chat + max_input_tokens: 32768 + input_price: 0.0509 + output_price: 0.335 + max_output_tokens: 32768 + require_max_tokens: true + supports_function_calling: true + - name: '@cf/meta/llama-4-scout-17b-16e-instruct' + type: chat + max_input_tokens: 131000 + input_price: 0.27 + output_price: 0.85 + max_output_tokens: 16384 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: '@cf/mistralai/mistral-small-3.1-24b-instruct' + type: chat + max_input_tokens: 128000 + input_price: 0.351 + output_price: 0.555 + max_output_tokens: 128000 + require_max_tokens: true + supports_function_calling: true + - name: '@cf/qwen/qwq-32b' + type: chat + max_input_tokens: 24000 + input_price: 0.66 + output_price: 1.0 + max_output_tokens: 24000 + require_max_tokens: true + - name: '@cf/deepseek-ai/deepseek-r1-distill-qwen-32b' + type: chat + max_input_tokens: 80000 + input_price: 0.497 + output_price: 4.881 + max_output_tokens: 80000 + require_max_tokens: true + - name: '@cf/meta/llama-3.3-70b-instruct-fp8-fast' + type: chat + max_input_tokens: 24000 + input_price: 0.293 + output_price: 2.253 + max_output_tokens: 24000 + require_max_tokens: true + supports_function_calling: true + - name: '@cf/qwen/qwen2.5-coder-32b-instruct' + type: chat + max_input_tokens: 32768 + input_price: 0.66 + output_price: 1.0 + max_output_tokens: 32768 + require_max_tokens: true + - name: '@cf/meta/llama-3.2-11b-vision-instruct' + type: chat + max_input_tokens: 128000 + input_price: 0.0485 + output_price: 0.676 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + - name: '@cf/meta/llama-3.2-1b-instruct' + type: chat + max_input_tokens: 60000 + input_price: 0.027 + output_price: 0.201 + max_output_tokens: 60000 + require_max_tokens: true + - name: '@cf/meta/llama-3.2-3b-instruct' + type: chat + max_input_tokens: 80000 + input_price: 0.0509 + output_price: 0.335 + max_output_tokens: 80000 + require_max_tokens: true + - name: '@cf/meta/llama-3.1-8b-instruct-fp8' + type: chat + max_input_tokens: 32000 + input_price: 0.152 + output_price: 0.287 + max_output_tokens: 32000 + require_max_tokens: true + - name: '@cf/meta/llama-guard-3-8b' + type: chat + max_input_tokens: 131072 + input_price: 0.484 + output_price: 0.03 + max_output_tokens: 131072 + require_max_tokens: true - provider: ernie models: - - name: ernie-4.5-turbo-128k - max_input_tokens: 131072 - input_price: 0.112 - output_price: 0.448 - - name: ernie-4.5-turbo-vl-32k - max_input_tokens: 32768 - input_price: 0.42 - output_price: 1.26 - supports_vision: true - - name: ernie-5.0-thinking-preview - max_input_tokens: 131072 - input_price: 1.4 - output_price: 5.6 - - name: ernie-x1.1-preview - max_input_tokens: 65536 - input_price: 0.14 - output_price: 0.56 - - name: bge-large-zh - type: embedding - input_price: 0.07 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 16 - - name: bge-large-en - type: embedding - input_price: 0.07 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 16 - - name: bce-reranker-base - type: reranker - max_input_tokens: 1024 - input_price: 0.07 - -# Links: -# - https://help.aliyun.com/zh/model-studio/getting-started/models -# - https://help.aliyun.com/zh/model-studio/developer-reference/use-qwen-by-calling-api + - name: ernie-4.5-turbo-128k + type: chat + max_input_tokens: 131072 + input_price: 0.112 + output_price: 0.448 + - name: ernie-4.5-turbo-vl-32k + type: chat + max_input_tokens: 32768 + input_price: 0.42 + output_price: 1.26 + supports_vision: true + - name: ernie-5.0-thinking-preview + type: chat + max_input_tokens: 131072 + input_price: 1.4 + output_price: 5.6 + - name: ernie-x1.1-preview + type: chat + max_input_tokens: 65536 + input_price: 0.14 + output_price: 0.56 + - name: bge-large-zh + type: embedding + input_price: 0.07 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 16 + - name: bge-large-en + type: embedding + input_price: 0.07 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 16 + - name: bce-reranker-base + type: reranker + max_input_tokens: 1024 + input_price: 0.07 - provider: qianwen models: - - name: qwen3.5-plus - max_input_tokens: 262144 - supports_function_calling: true - patch: - body: - enable_thinking: false - - name: qwen3.5-plus:thinking - real_name: qwen3.5-plus - max_input_tokens: 262144 - supports_function_calling: true - - name: qwen3-max - max_input_tokens: 262144 - supports_function_calling: true - - name: qwen3-max:thinking - real_name: qwen3-max - max_input_tokens: 262144 - supports_function_calling: true - patch: - body: - enable_thinking: true - - name: qwen3-vl-plus - max_input_tokens: 262144 - supports_vision: true - - name: qwen3-vl-flash - max_input_tokens: 262144 - supports_vision: true - - name: qwen3-coder-plus - max_input_tokens: 1000000 - - name: qwen3-coder-flash - max_input_tokens: 1000000 - - name: qwen3.5-397b-a17b - max_input_tokens: 262144 - supports_function_calling: true - patch: - body: - enable_thinking: false - - name: qwen3.5-397b-a17b:thinking - real_name: qwen3.5-397b-a17b - max_input_tokens: 262144 - supports_function_calling: true - - name: qwen3-next-80b-a3b-instruct - max_input_tokens: 131072 - input_price: 0.14 - output_price: 0.56 - supports_function_calling: true - - name: qwen3-next-80b-a3b-thinking - max_input_tokens: 131072 - input_price: 0.14 - output_price: 1.4 - - name: qwen3-235b-a22b-instruct-2507 - max_input_tokens: 131072 - input_price: 0.28 - output_price: 1.12 - supports_function_calling: true - - name: qwen3-235b-a22b-thinking-2507 - max_input_tokens: 131072 - input_price: 0.28 - output_price: 2.8 - - name: qwen3-30b-a3b-instruct-2507 - max_input_tokens: 131072 - input_price: 0.105 - output_price: 0.42 - supports_function_calling: true - - name: qwen3-30b-a3b-thinking-2507 - max_input_tokens: 131072 - input_price: 0.105 - output_price: 1.05 - - name: qwen3-vl-32b-instruct - max_input_tokens: 131072 - input_price: 0.28 - output_price: 1.12 - supports_vision: true - - name: qwen3-vl-8b-instruct - max_input_tokens: 131072 - input_price: 0.07 - output_price: 0.28 - supports_vision: true - - name: qwen3-coder-next - max_input_tokens: 262144 - - name: qwen3-coder-480b-a35b-instruct - max_input_tokens: 262144 - - name: qwen3-coder-30b-a3b-instruct - max_input_tokens: 262144 - - name: text-embedding-v4 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 8192 - default_chunk_size: 2000 - max_batch_size: 10 - - name: text-embedding-v3 - type: embedding - input_price: 0.1 - max_tokens_per_chunk: 8192 - default_chunk_size: 2000 - max_batch_size: 10 - -# links: -# - https://cloud.tencent.com/document/product/1729/104753 -# - https://cloud.tencent.com/document/product/1729/97731 -# - https://cloud.tencent.com/document/product/1729/111007 + - name: qwen3.5-plus:thinking + type: chat + real_name: qwen3.5-plus + max_input_tokens: 262144 + supports_function_calling: true + - name: qwen3.5-397b-a17b:thinking + type: chat + real_name: qwen3.5-397b-a17b + max_input_tokens: 262144 + supports_function_calling: true + - name: text-embedding-v4 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 10 + - name: text-embedding-v3 + type: embedding + input_price: 0.1 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 10 + - name: qwen3-vl-flash + type: chat + max_input_tokens: 262144 + supports_vision: true + - name: qwen3-235b-a22b-instruct-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.28 + output_price: 1.12 + supports_function_calling: true + - name: qwen3-235b-a22b-thinking-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.28 + output_price: 2.8 + - name: qwen3-30b-a3b-instruct-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.105 + output_price: 0.42 + supports_function_calling: true + - name: qwen3-30b-a3b-thinking-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.105 + output_price: 1.05 + - name: qwen3-vl-32b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.28 + output_price: 1.12 + supports_vision: true + - name: qwen3-vl-8b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.07 + output_price: 0.28 + supports_vision: true + - name: qwen3-coder-next + type: chat + max_input_tokens: 262144 + - name: qwen3.8-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.15 + output_price: 0.47 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - xhigh + - name: qwen3.8-max + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - xhigh + - name: deepseek-v4-flash-0731 + type: chat + max_input_tokens: 1000000 + input_price: 0.2 + output_price: 0.4 + max_output_tokens: 384000 + supports_function_calling: true + reasoning_levels: + - high + - max + - name: glm-5.2 + type: chat + max_input_tokens: 1000000 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - none + - minimal + - low + - medium + - high + - xhigh + - max + - name: qwen3.7-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.7-max + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 7.5 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3.6-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.1875 + output_price: 1.125 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.6-27b + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 3.6 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.6-max-preview + type: chat + max_input_tokens: 262144 + input_price: 1.3 + output_price: 7.8 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3.6-35b-a3b + type: chat + max_input_tokens: 262144 + input_price: 0.248 + output_price: 1.485 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.6-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.5-122b-a10b + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 3.2 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.5-27b + type: chat + max_input_tokens: 262144 + input_price: 0.3 + output_price: 2.4 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.5-35b-a3b + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.5-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.4 + output_price: 2.4 + patch: + body: + enable_thinking: false + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3.5-397b-a17b + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 3.6 + patch: + body: + enable_thinking: false + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen3-max + type: chat + max_input_tokens: 262144 + input_price: 1.2 + output_price: 6.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-max:thinking + type: chat + real_name: qwen3-max + max_input_tokens: 262144 + input_price: 1.2 + output_price: 6.0 + patch: + body: + enable_thinking: true + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-vl-plus + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 1.6 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen3-omni-flash + type: chat + max_input_tokens: 65536 + input_price: 0.43 + output_price: 1.66 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: qwen3-asr-flash + type: chat + max_input_tokens: 53248 + input_price: 0.035 + output_price: 0.035 + max_output_tokens: 4096 + - name: qwen3-next-80b-a3b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.5 + output_price: 2.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen3-next-80b-a3b-thinking + type: chat + max_input_tokens: 131072 + input_price: 0.5 + output_price: 6.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.05 + output_price: 0.4 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen3-coder-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.3 + output_price: 1.5 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-coder-plus + type: chat + max_input_tokens: 1048576 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-14b + type: chat + max_input_tokens: 131072 + input_price: 0.35 + output_price: 1.4 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen3-235b-a22b + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 2.8 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen3-32b + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 2.8 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen3-8b + type: chat + max_input_tokens: 131072 + input_price: 0.18 + output_price: 0.7 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen3-coder-30b-a3b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.45 + output_price: 2.25 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-coder-480b-a35b-instruct + type: chat + max_input_tokens: 262144 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen3-vl-235b-a22b + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 2.8 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen3-vl-30b-a3b + type: chat + max_input_tokens: 131072 + input_price: 0.2 + output_price: 0.8 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qvq-max + type: chat + max_input_tokens: 131072 + input_price: 1.2 + output_price: 4.8 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: qwq-plus + type: chat + max_input_tokens: 131072 + input_price: 0.8 + output_price: 2.4 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen-omni-turbo + type: chat + max_input_tokens: 32768 + input_price: 0.07 + output_price: 0.27 + max_output_tokens: 2048 + supports_vision: true + supports_function_calling: true + - name: qwen-mt-plus + type: chat + max_input_tokens: 16384 + input_price: 2.46 + output_price: 7.37 + max_output_tokens: 8192 + - name: qwen-mt-turbo + type: chat + max_input_tokens: 16384 + input_price: 0.16 + output_price: 0.49 + max_output_tokens: 8192 + - name: qwen2-5-omni-7b + type: chat + max_input_tokens: 32768 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 2048 + supports_vision: true + supports_function_calling: true + - name: qwen-turbo + type: chat + max_input_tokens: 1000000 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen-vl-ocr + type: chat + max_input_tokens: 34096 + input_price: 0.72 + output_price: 0.72 + max_output_tokens: 4096 + supports_vision: true + - name: qwen2-5-14b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.35 + output_price: 1.4 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen2-5-32b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 2.8 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen2-5-72b-instruct + type: chat + max_input_tokens: 131072 + input_price: 1.4 + output_price: 5.6 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen2-5-7b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.175 + output_price: 0.7 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen2-5-vl-72b-instruct + type: chat + max_input_tokens: 131072 + input_price: 2.8 + output_price: 8.4 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: qwen2-5-vl-7b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.35 + output_price: 1.05 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: qwen-vl-max + type: chat + max_input_tokens: 131072 + input_price: 0.8 + output_price: 3.2 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: qwen-max + type: chat + max_input_tokens: 32768 + input_price: 1.6 + output_price: 6.4 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.4 + output_price: 1.2 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen-vl-plus + type: chat + max_input_tokens: 131072 + input_price: 0.21 + output_price: 0.63 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: qwen-plus-character-ja + type: chat + max_input_tokens: 8192 + input_price: 0.5 + output_price: 1.4 + max_output_tokens: 512 + supports_function_calling: true - provider: hunyuan models: - - name: hunyuan-2.0-instruct-20251111 - max_input_tokens: 131072 - input_price: 0.112 - output_price: 0.28 - supports_function_calling: true - - name: hunyuan-2.0-thinking-20251109 - max_input_tokens: 131072 - input_price: 0.14 - output_price: 0.56 - supports_function_calling: true - - name: hunyuan-vision-1.5-instruct - max_input_tokens: 24576 - input_price: 0.42 - output_price: 1.26 - supports_vision: true - - name: hunyuan-embedding - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 1024 - default_chunk_size: 1000 - max_batch_size: 100 - -# Links: -# - https://platform.moonshot.cn/docs/pricing/chat#%E8%AE%A1%E8%B4%B9%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5 -# - https://platform.moonshot.cn/docs/api/chat#%E5%85%AC%E5%BC%80%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%9C%B0%E5%9D%80 + - name: hunyuan-2.0-instruct-20251111 + type: chat + max_input_tokens: 131072 + input_price: 0.112 + output_price: 0.28 + supports_function_calling: true + - name: hunyuan-2.0-thinking-20251109 + type: chat + max_input_tokens: 131072 + input_price: 0.14 + output_price: 0.56 + supports_function_calling: true + - name: hunyuan-vision-1.5-instruct + type: chat + max_input_tokens: 24576 + input_price: 0.42 + output_price: 1.26 + supports_vision: true + - name: hunyuan-embedding + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 1024 + default_chunk_size: 1000 + max_batch_size: 100 - provider: moonshot models: - - name: kimi-k3 - max_input_tokens: 1048576 - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: kimi-k2.7-code - max_input_tokens: 262144 - input_price: 0.95 - output_price: 4 - supports_vision: true - supports_function_calling: true - - name: kimi-k2.7-code-highspeed - max_input_tokens: 262144 - input_price: 1.9 - output_price: 8 - supports_vision: true - supports_function_calling: true - - name: kimi-k2.6 - max_input_tokens: 262144 - input_price: 0.95 - output_price: 4 - supports_vision: true - supports_function_calling: true - - name: kimi-k2.5 - max_input_tokens: 262144 - input_price: 0.56 - output_price: 2.94 - supports_vision: true - supports_function_calling: true - - name: kimi-k2-turbo-preview - max_input_tokens: 262144 - input_price: 1.12 - output_price: 8.12 - supports_vision: true - supports_function_calling: true - - name: kimi-k2-0905-preview - max_input_tokens: 262144 - input_price: 0.56 - output_price: 2.24 - supports_vision: true - supports_function_calling: true - - name: kimi-k2-thinking-turbo - max_input_tokens: 262144 - input_price: 1.12 - output_price: 8.12 - supports_vision: true - - name: kimi-k2-thinking - max_input_tokens: 262144 - input_price: 0.56 - output_price: 2.24 - supports_vision: true - -# Links: -# - https://api-docs.deepseek.com/quick_start/pricing -# - https://platform.deepseek.com/api-docs/api/create-chat-completion + - name: kimi-k3 + type: chat + max_input_tokens: 1048576 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: kimi-k2.7-code + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: kimi-k2.7-code-highspeed + type: chat + max_input_tokens: 262144 + input_price: 1.9 + output_price: 8.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: kimi-k2.6 + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: kimi-k2.5 + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 3.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: kimi-k2-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 262144 + supports_function_calling: true + - name: kimi-k2-thinking-turbo + type: chat + max_input_tokens: 262144 + input_price: 1.15 + output_price: 8.0 + max_output_tokens: 262144 + supports_function_calling: true + - name: kimi-k2-0905-preview + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 262144 + supports_function_calling: true + - name: kimi-k2-turbo-preview + type: chat + max_input_tokens: 262144 + input_price: 2.4 + output_price: 10.0 + max_output_tokens: 262144 + supports_function_calling: true + - name: kimi-k2-0711-preview + type: chat + max_input_tokens: 131072 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 16384 + supports_function_calling: true - provider: deepseek models: - - name: deepseek-v4-pro - max_input_tokens: 1000000 - max_output_tokens: 384000 - input_price: 0.435 - output_price: 0.87 - supports_function_calling: true - - name: deepseek-v4-flash - max_input_tokens: 1000000 - max_output_tokens: 384000 - input_price: 0.14 - output_price: 0.28 - supports_function_calling: true - - name: deepseek-chat - max_input_tokens: 64000 - max_output_tokens: 8192 - input_price: 0.56 - output_price: 1.68 - supports_function_calling: true - - name: deepseek-reasoner - max_input_tokens: 64000 - max_output_tokens: 32768 - input_price: 0.56 - output_price: 1.68 - -# Links: -# - https://open.bigmodel.cn/pricing -# - https://open.bigmodel.cn/dev/api#glm-4 + - name: deepseek-chat + type: chat + max_input_tokens: 64000 + input_price: 0.56 + output_price: 1.68 + max_output_tokens: 8192 + supports_function_calling: true + - name: deepseek-reasoner + type: chat + max_input_tokens: 64000 + input_price: 0.56 + output_price: 1.68 + max_output_tokens: 32768 + - name: deepseek-v4-flash-vision-exp + type: chat + max_input_tokens: 1000000 + input_price: 0.14 + output_price: 0.28 + max_output_tokens: 384000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: deepseek-v4-pro + type: chat + max_input_tokens: 1000000 + input_price: 0.435 + output_price: 0.87 + max_output_tokens: 384000 + supports_function_calling: true + reasoning_levels: + - high + - max + - name: deepseek-v4-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.14 + output_price: 0.28 + max_output_tokens: 384000 + supports_function_calling: true + reasoning_levels: + - low + - high + - max - provider: zhipuai models: - - name: glm-5 - max_input_tokens: 202752 - supports_function_calling: true - - name: glm-5:instruct - real_name: glm-5 - max_input_tokens: 202752 - supports_function_calling: true - patch: - body: - thinking: - type: disabled - - name: glm-4.7 - max_input_tokens: 202752 - supports_function_calling: true - - name: glm-4.7:instruct - real_name: glm-4.7 - max_input_tokens: 202752 - supports_function_calling: true - patch: - body: - thinking: - type: disabled - - name: glm-4.7-flash - max_input_tokens: 202752 - input_price: 0 - output_price: 0 - supports_function_calling: true - - name: glm-4.6v - max_input_tokens: 65536 - supports_vision: true - - name: glm-4.6v-flash - max_input_tokens: 65536 - input_price: 0 - output_price: 0 - supports_vision: true - - name: embedding-3 - type: embedding - max_input_tokens: 8192 - input_price: 0.07 - max_tokens_per_chunk: 8192 - default_chunk_size: 2000 - - name: rerank - type: reranker - max_input_tokens: 4096 - input_price: 0.112 - -# Links: -# - https://platform.minimaxi.com/docs/guides/pricing-paygo -# - https://platform.minimaxi.com/document/ChatCompletion%20v2 + - name: embedding-3 + type: embedding + max_input_tokens: 8192 + input_price: 0.07 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + - name: rerank + type: reranker + max_input_tokens: 4096 + input_price: 0.112 + - name: glm-4.6v-flash + type: chat + max_input_tokens: 65536 + input_price: 0.0 + output_price: 0.0 + supports_vision: true + - name: glm-5.3-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.075 + output_price: 0.25 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: glm-5.3 + type: chat + max_input_tokens: 1000000 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: glm-5.2 + type: chat + max_input_tokens: 1000000 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - high + - max + - name: glm-5v-turbo + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 22.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: glm-5.1 + type: chat + max_input_tokens: 200000 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-5 + type: chat + max_input_tokens: 204800 + input_price: 1.0 + output_price: 3.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-5:instruct + type: chat + real_name: glm-5 + max_input_tokens: 204800 + input_price: 1.0 + output_price: 3.2 + patch: + body: + thinking: + type: disabled + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.7-flash + type: chat + max_input_tokens: 200000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.7-flashx + type: chat + max_input_tokens: 200000 + input_price: 0.07 + output_price: 0.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.7 + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.7:instruct + type: chat + real_name: glm-4.7 + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.2 + patch: + body: + thinking: + type: disabled + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.6v + type: chat + max_input_tokens: 128000 + input_price: 0.3 + output_price: 0.9 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: glm-4.6 + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: glm-4.5v + type: chat + max_input_tokens: 64000 + input_price: 0.6 + output_price: 1.8 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: glm-4.5 + type: chat + max_input_tokens: 131072 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 98304 + supports_function_calling: true + - name: glm-4.5-air + type: chat + max_input_tokens: 131072 + input_price: 0.2 + output_price: 1.1 + max_output_tokens: 98304 + supports_function_calling: true + - name: glm-4.5-flash + type: chat + max_input_tokens: 131072 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 98304 + supports_function_calling: true - provider: minimax models: - - name: minimax-m3 - max_input_tokens: 1000000 - input_price: 4.2 - output_price: 16.8 - supports_function_calling: true - - name: minimax-m2.7 - max_input_tokens: 204800 - input_price: 0.294 - output_price: 1.176 - supports_function_calling: true - - name: minimax-m2.5 - max_input_tokens: 204800 - input_price: 0.294 - output_price: 1.176 - supports_function_calling: true - - name: minimax-m2.5-highspeed - max_input_tokens: 204800 - input_price: 0.588 - output_price: 2.352 - supports_function_calling: true - - name: minimax-m2.1 - max_input_tokens: 204800 - input_price: 0.294 - output_price: 1.176 - supports_function_calling: true - - name: minimax-m2.1-highspeed - max_input_tokens: 204800 - input_price: 0.588 - output_price: 2.352 - supports_function_calling: true - -# Links: -# - https://openrouter.ai/models -# - https://openrouter.ai/docs/api-reference/chat-completion + - name: minimax-m3 + type: chat + max_input_tokens: 1000000 + input_price: 4.2 + output_price: 16.8 + supports_function_calling: true + - name: minimax-m2.7 + type: chat + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.5 + type: chat + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.5-highspeed + type: chat + max_input_tokens: 204800 + input_price: 0.588 + output_price: 2.352 + supports_function_calling: true + - name: minimax-m2.1 + type: chat + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.1-highspeed + type: chat + max_input_tokens: 204800 + input_price: 0.588 + output_price: 2.352 + supports_function_calling: true + - name: MiniMax-M3 + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 512000 + supports_vision: true + supports_function_calling: true + - name: MiniMax-M2.7 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: MiniMax-M2.7-highspeed + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: MiniMax-M2.5-highspeed + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 2.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: MiniMax-M2.5 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: MiniMax-M2.1 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: MiniMax-M2 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true - provider: openrouter models: - - name: openai/gpt-5.6-sol - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: openai/gpt-5.6-terra - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: openai/gpt-5.6-luna - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh, max] - default_reasoning_effort: medium - - name: openai/gpt-5.5 - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 5 - output_price: 30 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: medium - - name: openai/gpt-5.5-pro - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 30 - output_price: 180 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: high - - name: openai/gpt-5.4 - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 2.5 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: openai/gpt-5.4-pro - max_input_tokens: 1050000 - max_output_tokens: 128000 - input_price: 30 - output_price: 180 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: medium - - name: openai/gpt-5.4-mini - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.75 - output_price: 4.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: openai/gpt-5.4-nano - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.2 - output_price: 1.25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: openai/gpt-5.3-codex - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.75 - output_price: 14 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh] - default_reasoning_effort: medium - - name: openai/gpt-5.2 - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.75 - output_price: 14 - supports_vision: true - supports_function_calling: true - reasoning_levels: [none, low, medium, high, xhigh] - default_reasoning_effort: none - - name: openai/gpt-5.2-pro - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 21 - output_price: 168 - supports_vision: true - supports_function_calling: true - reasoning_levels: [medium, high, xhigh] - default_reasoning_effort: medium - - name: openai/gpt-5 - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: openai/gpt-5-mini - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.25 - output_price: 2 - supports_vision: true - supports_function_calling: true - - name: openai/gpt-5-nano - max_input_tokens: 400000 - max_output_tokens: 128000 - input_price: 0.05 - output_price: 0.4 - supports_vision: true - supports_function_calling: true - - name: openai/gpt-4.1 - max_input_tokens: 1047576 - max_output_tokens: 32768 - input_price: 2 - output_price: 8 - supports_vision: true - supports_function_calling: true - - name: openai/gpt-4o - max_input_tokens: 128000 - input_price: 2.5 - output_price: 10 - supports_vision: true - supports_function_calling: true - - name: openai/gpt-oss-120b - max_input_tokens: 131072 - input_price: 0.09 - output_price: 0.45 - supports_function_calling: true - - name: openai/gpt-oss-20b - max_input_tokens: 131072 - input_price: 0.04 - output_price: 0.16 - supports_function_calling: true - - name: google/gemini-3.5-flash - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: medium - - name: google/gemini-3-flash-preview - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: high - - name: google/gemini-3.1-flash-lite - max_input_tokens: 1048576 - max_output_tokens: 65536 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_effort: minimal - - name: google/gemini-3.1-pro-preview - max_input_tokens: 1048576 - max_output_tokens: 65535 - input_price: 0.3 - output_price: 2.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: google/gemini-3-pro-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, high] - default_reasoning_level: high - - name: google/gemini-3-flash-preview - max_input_tokens: 1048576 - supports_vision: true - supports_function_calling: true - reasoning_levels: [minimal, low, medium, high] - default_reasoning_level: high - - name: google/gemini-2.5-flash - max_input_tokens: 1048576 - input_price: 0.3 - output_price: 2.5 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: low - - name: google/gemini-2.5-pro - max_input_tokens: 1048576 - input_price: 1.25 - output_price: 10 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high] - default_reasoning_effort: high - - name: google/gemini-2.5-flash-lite - max_input_tokens: 1048576 - input_price: 0.3 - output_price: 0.4 - supports_vision: true - - name: google/gemini-2.0-flash-001 - max_input_tokens: 1000000 - input_price: 0.15 - output_price: 0.6 - supports_vision: true - supports_function_calling: true - - name: google/gemini-2.0-flash-lite-001 - max_input_tokens: 1048576 - input_price: 0.075 - output_price: 0.3 - supports_vision: true - supports_function_calling: true - - name: google/gemma-3-27b-it - max_input_tokens: 131072 - input_price: 0.1 - output_price: 0.2 - - name: anthropic/claude-opus-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-fable-5-1 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-fable-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 10 - output_price: 50 - supports_function_calling: true - supports_vision: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-opus-4-8 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-opus-4-7 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-opus-4.6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: anthropic/claude-sonnet-5 - max_input_tokens: 1000000 - max_output_tokens: 128000 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, xhigh, max] - default_reasoning_effort: high - - name: anthropic/claude-sonnet-4.6 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - reasoning_levels: [low, medium, high, max] - default_reasoning_effort: high - - name: anthropic/claude-opus-4.5 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 5 - output_price: 25 - supports_vision: true - supports_function_calling: true - - name: anthropic/claude-sonnet-4.5 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 3 - output_price: 15 - supports_vision: true - supports_function_calling: true - - name: anthropic/claude-haiku-4.5 - max_input_tokens: 200000 - max_output_tokens: 8192 - require_max_tokens: true - input_price: 1 - output_price: 5 - supports_vision: true - supports_function_calling: true - - name: meta-llama/llama-4-maverick - max_input_tokens: 1048576 - input_price: 0.18 - output_price: 0.6 - supports_vision: true - supports_function_calling: true - - name: meta-llama/llama-4-scout - max_input_tokens: 327680 - input_price: 0.08 - output_price: 0.3 - supports_vision: true - supports_function_calling: true - - name: meta-llama/llama-3.3-70b-instruct - max_input_tokens: 131072 - input_price: 0.12 - output_price: 0.3 - - name: mistralai/mistral-large-2512 - max_input_tokens: 262144 - input_price: 0.5 - output_price: 1.5 - supports_function_calling: true - - name: mistralai/mistral-medium-3.1 - max_input_tokens: 131072 - input_price: 0.4 - output_price: 2 - supports_function_calling: true - supports_vision: true - - name: mistralai/mistral-small-3.2-24b-instruct - max_input_tokens: 131072 - input_price: 0.1 - output_price: 0.3 - supports_vision: true - - name: mistralai/devstral-2512 - max_input_tokens: 262144 - input_price: 0.5 - output_price: 0.22 - supports_function_calling: true - - name: mistralai/devstral-small - max_input_tokens: 131072 - input_price: 0.07 - output_price: 0.28 - supports_function_calling: true - - name: mistralai/codestral-2508 - max_input_tokens: 256000 - input_price: 0.3 - output_price: 0.9 - supports_function_calling: true - - name: mistralai/ministral-14b-2512 - max_input_tokens: 262144 - input_price: 0.2 - output_price: 0.2 - supports_function_calling: true - - name: ai21/jamba-large-1.7 - max_input_tokens: 256000 - input_price: 2 - output_price: 8 - supports_function_calling: true - - name: cohere/command-a - max_input_tokens: 256000 - input_price: 2.5 - output_price: 10 - supports_function_calling: true - - name: cohere/command-r7b-12-2024 - max_input_tokens: 128000 - max_output_tokens: 4096 - input_price: 0.0375 - output_price: 0.15 - - name: deepseek/deepseek-v3.2 - max_input_tokens: 163840 - input_price: 0.25 - output_price: 0.38 - - name: qwen/qwen3-max - max_input_tokens: 262144 - input_price: 1.2 - output_price: 6 - supports_function_calling: true - - name: qwen/qwen3-max-thinking - max_input_tokens: 262144 - input_price: 1.2 - output_price: 6 - supports_function_calling: true - - name: qwen/qwen3.5-plus-02-15 - max_input_tokens: 1000000 - max_output_tokens: 8192 - input_price: 0.4 - output_price: 2.4 - supports_function_calling: true - - name: qwen/qwen3.5-397b-a17b - max_input_tokens: 262144 - max_output_tokens: 8192 - input_price: 0.15 - output_price: 1 - supports_function_calling: true - - name: qwen/qwen3-next-80b-a3b-instruct - max_input_tokens: 262144 - input_price: 0.1 - output_price: 0.8 - supports_function_calling: true - - name: qwen/qwen3-next-80b-a3b-thinking - max_input_tokens: 262144 - input_price: 0.1 - output_price: 0.8 - - name: qwen/qwen3-235b-a22b-2507 # Qwen3 235B A22B Instruct 2507 - max_input_tokens: 262144 - input_price: 0.12 - output_price: 0.59 - supports_function_calling: true - - name: qwen/qwen3-235b-a22b-thinking-2507 - max_input_tokens: 262144 - input_price: 0.118 - output_price: 0.118 - - name: qwen/qwen3-30b-a3b-instruct-2507 - max_input_tokens: 131072 - input_price: 0.2 - output_price: 0.8 - - name: qwen/qwen3-30b-a3b-thinking-2507 - max_input_tokens: 262144 - input_price: 0.071 - output_price: 0.285 - - name: qwen/qwen3-vl-32b-instruct - max_input_tokens: 262144 - input_price: 0.35 - output_price: 1.1 - supports_vision: true - - name: qwen/qwen3-vl-8b-instruct - max_input_tokens: 262144 - input_price: 0.08 - output_price: 0.50 - supports_vision: true - - name: qwen/qwen3-coder-next - max_input_tokens: 262144 - input_price: 0.12 - output_price: 0.75 - supports_function_calling: true - - name: qwen/qwen3-coder-plus - max_input_tokens: 128000 - input_price: 1 - output_price: 5 - supports_function_calling: true - - name: qwen/qwen3-coder-flash - max_input_tokens: 128000 - input_price: 0.3 - output_price: 1.5 - supports_function_calling: true - - name: qwen/qwen3-coder # Qwen3 Coder 480B A35B - max_input_tokens: 262144 - input_price: 0.22 - output_price: 0.95 - supports_function_calling: true - - name: qwen/qwen3-coder-30b-a3b-instruct - max_input_tokens: 262144 - input_price: 0.052 - output_price: 0.207 - supports_function_calling: true - - name: moonshotai/kimi-k2.5 - max_input_tokens: 262144 - input_price: 0.57 - output_price: 2.85 - supports_vision: true - supports_function_calling: true - - name: moonshotai/kimi-k2-0905 - max_input_tokens: 262144 - input_price: 0.296 - output_price: 1.185 - supports_vision: true - supports_function_calling: true - - name: moonshotai/kimi-k2-thinking - max_input_tokens: 262144 - input_price: 0.45 - output_price: 2.35 - supports_function_calling: true - - name: x-ai/grok-4.1-fast - max_input_tokens: 2000000 - input_price: 0.2 - output_price: 0.5 - supports_function_calling: true - - name: x-ai/grok-code-fast-1 - max_input_tokens: 256000 - input_price: 0.2 - output_price: 1.5 - supports_function_calling: true - - name: amazon/nova-premier-v1 - max_input_tokens: 1000000 - input_price: 2.5 - output_price: 12.5 - supports_vision: true - - name: amazon/nova-pro-v1 - max_input_tokens: 300000 - max_output_tokens: 5120 - input_price: 0.8 - output_price: 3.2 - supports_vision: true - - name: amazon/nova-lite-v1 - max_input_tokens: 300000 - max_output_tokens: 5120 - input_price: 0.06 - output_price: 0.24 - supports_vision: true - - name: amazon/nova-micro-v1 - max_input_tokens: 128000 - max_output_tokens: 5120 - input_price: 0.035 - output_price: 0.14 - - name: perplexity/sonar-pro - max_input_tokens: 200000 - input_price: 3 - output_price: 15 - - name: perplexity/sonar - max_input_tokens: 127072 - input_price: 1 - output_price: 1 - - name: perplexity/sonar-reasoning-pro - max_input_tokens: 128000 - input_price: 2 - output_price: 8 - patch: - body: - include_reasoning: true - - name: perplexity/sonar-deep-research - max_input_tokens: 200000 - input_price: 2 - output_price: 8 - patch: - body: - include_reasoning: true - - name: minimax/minimax-m2.5 - max_input_tokens: 196608 - input_price: 0.3 - output_price: 1.1 - supports_function_calling: true - - name: minimax/minimax-m2.1 - max_input_tokens: 196608 - input_price: 0.12 - output_price: 0.48 - supports_function_calling: true - - name: z-ai/glm-5 - max_input_tokens: 204800 - input_price: 0.95 - output_price: 2.55 - supports_function_calling: true - - name: z-ai/glm-4.7 - max_input_tokens: 202752 - input_price: 0.16 - output_price: 0.80 - supports_function_calling: true - - name: z-ai/glm-4.7-flash - max_input_tokens: 202752 - input_price: 0.07 - output_price: 0.40 - supports_function_calling: true - - name: z-ai/glm-4.6v - max_input_tokens: 131072 - input_price: 0.3 - output_price: 0.9 - supports_vision: true - -# Links: -# - https://github.com/marketplace?type=models + - name: google/gemini-3-pro-preview + type: chat + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + default_reasoning_effort: high + - name: google/gemini-2.0-flash-001 + type: chat + max_input_tokens: 1000000 + input_price: 0.15 + output_price: 0.6 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.0-flash-lite-001 + type: chat + max_input_tokens: 1048576 + input_price: 0.075 + output_price: 0.3 + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-fable-5-1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-opus-4-8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-opus-4-7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: mistralai/devstral-small + type: chat + max_input_tokens: 131072 + input_price: 0.07 + output_price: 0.28 + supports_function_calling: true + - name: ai21/jamba-large-1.7 + type: chat + max_input_tokens: 256000 + input_price: 2.0 + output_price: 8.0 + supports_function_calling: true + - name: x-ai/grok-4.1-fast + type: chat + max_input_tokens: 2000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: x-ai/grok-code-fast-1 + type: chat + max_input_tokens: 256000 + input_price: 0.2 + output_price: 1.5 + supports_function_calling: true + - name: aion-labs/aion-2.0 + type: chat + max_input_tokens: 131072 + input_price: 0.8 + output_price: 1.6 + max_output_tokens: 32768 + supports_function_calling: true + - name: aion-labs/aion-3.0 + type: chat + max_input_tokens: 131072 + input_price: 3.0 + output_price: 6.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: aion-labs/aion-3.0-mini + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 1.4 + max_output_tokens: 32768 + supports_function_calling: true + - name: aion-labs/aion-rp-llama-3.1-8b + type: chat + max_input_tokens: 32768 + input_price: 0.8 + output_price: 1.6 + max_output_tokens: 29491 + - name: amazon/nova-2-lite-v1 + type: chat + max_input_tokens: 1000000 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + - name: amazon/nova-lite-v1 + type: chat + max_input_tokens: 300000 + input_price: 0.06 + output_price: 0.24 + max_output_tokens: 5120 + supports_vision: true + supports_function_calling: true + - name: amazon/nova-micro-v1 + type: chat + max_input_tokens: 128000 + input_price: 0.035 + output_price: 0.14 + max_output_tokens: 5120 + supports_function_calling: true + - name: amazon/nova-premier-v1 + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 32000 + supports_vision: true + supports_function_calling: true + - name: amazon/nova-pro-v1 + type: chat + max_input_tokens: 300000 + input_price: 0.8 + output_price: 3.2 + max_output_tokens: 5120 + supports_vision: true + supports_function_calling: true + - name: anthracite-org/magnum-v4-72b + type: chat + max_input_tokens: 32768 + input_price: 2.5 + output_price: 5.0 + max_output_tokens: 4096 + - name: anthropic/claude-3-haiku + type: chat + max_input_tokens: 200000 + input_price: 0.25 + output_price: 1.25 + max_output_tokens: 4096 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-fable-5 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-fable-5.1 + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-fable-5.1:batch + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-fable-5:batch + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-haiku-4.5 + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-haiku-4.5:batch + type: chat + max_input_tokens: 200000 + input_price: 0.5 + output_price: 2.5 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.1 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 75.0 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.1:batch + type: chat + max_input_tokens: 200000 + input_price: 7.5 + output_price: 37.5 + max_output_tokens: 32000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.5 + type: chat + max_input_tokens: 200000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.5:batch + type: chat + max_input_tokens: 200000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.6 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: anthropic/claude-opus-4.6:batch + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.7 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.7:batch + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.8 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-4.8:batch + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-opus-5 + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-opus-5:batch + type: chat + max_input_tokens: 1000000 + input_price: 2.5 + output_price: 12.5 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-sonnet-4 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-sonnet-4.5 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-sonnet-4.5:batch + type: chat + max_input_tokens: 1000000 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 64000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-sonnet-4.6 + type: chat + max_input_tokens: 1000000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - max + default_reasoning_effort: high + - name: anthropic/claude-sonnet-4.6:batch + type: chat + max_input_tokens: 1000000 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-sonnet-5 + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: high + - name: anthropic/claude-sonnet-5:batch + type: chat + max_input_tokens: 1000000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 128000 + require_max_tokens: true + supports_vision: true + supports_function_calling: true + - name: arcee-ai/trinity-large-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 0.8 + max_output_tokens: 80000 + supports_function_calling: true + - name: baidu/ernie-4.5-vl-424b-a47b + type: chat + max_input_tokens: 123000 + input_price: 0.42 + output_price: 1.25 + max_output_tokens: 16000 + supports_vision: true + - name: bytedance-seed/seed-1.6 + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: bytedance-seed/seed-1.6-flash + type: chat + max_input_tokens: 262144 + input_price: 0.075 + output_price: 0.3 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: bytedance-seed/seed-2-1-turbo + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 2.5 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: bytedance-seed/seed-2.0-code + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: bytedance-seed/seed-2.0-lite + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: bytedance-seed/seed-2.0-mini + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: bytedance/ui-tars-1.5-7b + type: chat + max_input_tokens: 128000 + input_price: 0.1 + output_price: 0.2 + max_output_tokens: 2048 + supports_vision: true + - name: cognitivecomputations/dolphin-mistral-24b-venice-edition + type: chat + max_input_tokens: 128000 + input_price: 0.2 + output_price: 0.9 + max_output_tokens: 8192 + - name: cohere/command-a + type: chat + max_input_tokens: 256000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 8192 + - name: cohere/command-r-08-2024 + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 4000 + supports_function_calling: true + - name: cohere/command-r-plus-08-2024 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 4000 + supports_function_calling: true + - name: cohere/command-r7b-12-2024 + type: chat + max_input_tokens: 128000 + input_price: 0.0375 + output_price: 0.15 + max_output_tokens: 4000 + - name: cohere/north-mini-code:free + type: chat + max_input_tokens: 256000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 64000 + supports_function_calling: true + - name: deepseek/deepseek-chat + type: chat + max_input_tokens: 163840 + input_price: 0.2574 + output_price: 1.0287 + max_output_tokens: 16000 + supports_function_calling: true + - name: deepseek/deepseek-chat-v3-0324 + type: chat + max_input_tokens: 163840 + input_price: 0.25 + output_price: 1.0 + max_output_tokens: 147456 + supports_function_calling: true + - name: deepseek/deepseek-chat-v3.1 + type: chat + max_input_tokens: 163840 + input_price: 0.25 + output_price: 0.95 + max_output_tokens: 32768 + supports_function_calling: true + - name: deepseek/deepseek-r1 + type: chat + max_input_tokens: 64000 + input_price: 0.7 + output_price: 2.5 + max_output_tokens: 16000 + supports_function_calling: true + - name: deepseek/deepseek-r1-0528 + type: chat + max_input_tokens: 163840 + input_price: 0.5 + output_price: 2.15 + max_output_tokens: 32768 + supports_function_calling: true + - name: deepseek/deepseek-r1-distill-llama-70b + type: chat + max_input_tokens: 8192 + input_price: 0.8 + output_price: 0.8 + max_output_tokens: 7372 + - name: deepseek/deepseek-v3.1-terminus + type: chat + max_input_tokens: 163840 + input_price: 0.27 + output_price: 1.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: deepseek/deepseek-v3.2 + type: chat + max_input_tokens: 163840 + input_price: 0.269 + output_price: 0.4 + max_output_tokens: 65536 + supports_function_calling: true + - name: deepseek/deepseek-v3.2-exp + type: chat + max_input_tokens: 163840 + input_price: 0.27 + output_price: 0.41 + max_output_tokens: 65536 + supports_function_calling: true + - name: deepseek/deepseek-v4-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.0825 + output_price: 0.1649 + max_output_tokens: 384000 + supports_function_calling: true + - name: deepseek/deepseek-v4-flash-0731 + type: chat + max_input_tokens: 1310720 + input_price: 0.065 + output_price: 0.18 + max_output_tokens: 943718 + supports_function_calling: true + - name: deepseek/deepseek-v4-flash-0731:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.14 + output_price: 0.28 + max_output_tokens: 943718 + supports_function_calling: true + - name: deepseek/deepseek-v4-flash-vision-exp + type: chat + max_input_tokens: 1048576 + input_price: 0.22 + output_price: 0.66 + max_output_tokens: 384000 + supports_vision: true + supports_function_calling: true + - name: deepseek/deepseek-v4-pro + type: chat + max_input_tokens: 1048576 + input_price: 1.0308 + output_price: 2.0616 + max_output_tokens: 384000 + supports_function_calling: true + - name: deepseek/deepseek-v4-pro-0813 + type: chat + max_input_tokens: 1048576 + input_price: 0.66 + output_price: 1.98 + max_output_tokens: 384000 + supports_function_calling: true + - name: deepseek/deepseek-v4-pro-0813:batch + type: chat + max_input_tokens: 1048576 + input_price: 1.32 + output_price: 3.96 + max_output_tokens: 943718 + supports_function_calling: true + - name: dots-studio/dots-3-note-preview:free + type: chat + max_input_tokens: 512000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 460800 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: low + - name: google/gemini-2.5-flash-image + type: chat + max_input_tokens: 32768 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 8192 + supports_vision: true + - name: google/gemini-2.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-flash-lite:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-flash:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.15 + output_price: 1.25 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-pro + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high + - name: google/gemini-2.5-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-pro-preview-05-06 + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 65535 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.5-pro:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.625 + output_price: 5.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3-flash-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: high + - name: google/gemini-3-flash-preview:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3-pro-image + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3-pro-image-preview + type: chat + max_input_tokens: 65536 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 32768 + supports_vision: true + - name: google/gemini-3.1-flash-image + type: chat + max_input_tokens: 131072 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 32768 + supports_vision: true + - name: google/gemini-3.1-flash-image-preview + type: chat + max_input_tokens: 65536 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 58982 + supports_vision: true + - name: google/gemini-3.1-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: minimal + - name: google/gemini-3.1-flash-lite-image + type: chat + max_input_tokens: 65536 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 58982 + supports_vision: true + - name: google/gemini-3.1-flash-lite-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.25 + output_price: 1.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.1-flash-lite:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.125 + output_price: 0.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.1-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + default_reasoning_effort: high + - name: google/gemini-3.1-pro-preview-customtools + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.1-pro-preview:batch + type: chat + max_input_tokens: 1048576 + input_price: 1.0 + output_price: 6.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.5-flash + type: chat + max_input_tokens: 1048576 + input_price: 1.5 + output_price: 9.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: google/gemini-3.5-flash-lite + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 2.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.5-flash-lite:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.15 + output_price: 1.25 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.5-flash:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 4.5 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.6-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.6-flash:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.375 + output_price: 1.875 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.7-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.7-flash:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.375 + output_price: 1.875 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.8-flash + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemini-3.8-flash:batch + type: chat + max_input_tokens: 1048576 + input_price: 0.375 + output_price: 1.875 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: google/gemma-2-27b-it + type: chat + max_input_tokens: 8192 + input_price: 0.65 + output_price: 0.65 + max_output_tokens: 2048 + - name: google/gemma-3-12b-it + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.15 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: google/gemma-3-27b-it + type: chat + max_input_tokens: 131072 + input_price: 0.08 + output_price: 0.45 + max_output_tokens: 117964 + supports_vision: true + supports_function_calling: true + - name: google/gemma-3-4b-it + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.1 + max_output_tokens: 16384 + supports_vision: true + - name: google/gemma-4-26b-a4b-it + type: chat + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.34 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-26b-a4b-it:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-31b-it + type: chat + max_input_tokens: 262144 + input_price: 0.09 + output_price: 0.34 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-31b-it:batch + type: chat + max_input_tokens: 262144 + input_price: 0.39 + output_price: 0.97 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-31b-it:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: google/lyria-3-clip-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_vision: true + - name: google/lyria-3-pro-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_vision: true + - name: gryphe/mythomax-l2-13b + type: chat + max_input_tokens: 8192 + input_price: 0.06 + output_price: 0.06 + max_output_tokens: 3686 + - name: ibm-granite/granite-4.0-h-micro + type: chat + max_input_tokens: 131000 + input_price: 0.017 + output_price: 0.112 + max_output_tokens: 117900 + - name: ibm-granite/granite-4.1-8b + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.1 + max_output_tokens: 117964 + supports_function_calling: true + - name: ibm-granite/granite-4.2-8b + type: chat + max_input_tokens: 131072 + input_price: 0.1 + output_price: 0.15 + max_output_tokens: 117964 + supports_function_calling: true + - name: inception/mercury-2 + type: chat + max_input_tokens: 128000 + input_price: 0.25 + output_price: 0.75 + max_output_tokens: 50000 + supports_function_calling: true + - name: inception/mercury-2.5-preview + type: chat + max_input_tokens: 260000 + input_price: 0.04 + output_price: 0.15 + max_output_tokens: 65536 + supports_function_calling: true + - name: inclusionai/ling-3.0-flash + type: chat + max_input_tokens: 262144 + input_price: 0.021 + output_price: 0.063 + max_output_tokens: 32768 + supports_function_calling: true + - name: inclusionai/ling-3.0-flash-fin:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: kwaipilot/kat-coder-pro-v2 + type: chat + max_input_tokens: 262144 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 144000 + supports_function_calling: true + - name: kwaipilot/kat-coder-pro-v2.5 + type: chat + max_input_tokens: 262144 + input_price: 0.74 + output_price: 2.96 + max_output_tokens: 235929 + supports_function_calling: true + - name: liquid/lfm-2.5-2.6b:free + type: chat + max_input_tokens: 65536 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 8192 + supports_function_calling: true + - name: mancer/weaver + type: chat + max_input_tokens: 8000 + input_price: 0.4 + output_price: 0.75 + max_output_tokens: 6000 + - name: meituan/longcat-2.0 + type: chat + max_input_tokens: 1048756 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 262144 + supports_function_calling: true + - name: meta-llama/llama-3.1-70b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 0.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: meta-llama/llama-3.1-8b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.08 + max_output_tokens: 117964 + supports_function_calling: true + - name: meta-llama/llama-3.2-1b-instruct + type: chat + max_input_tokens: 60000 + input_price: 0.027 + output_price: 0.201 + max_output_tokens: 54000 + - name: meta-llama/llama-3.2-3b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.33 + max_output_tokens: 117964 + - name: meta-llama/llama-3.3-70b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.71 + output_price: 0.71 + max_output_tokens: 115200 + supports_function_calling: true + - name: meta-llama/llama-4-maverick + type: chat + max_input_tokens: 1048576 + input_price: 0.2 + output_price: 0.696 + max_output_tokens: 115200 + supports_vision: true + supports_function_calling: true + - name: meta-llama/llama-4-scout + type: chat + max_input_tokens: 1310720 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: meta-llama/llama-guard-4-12b + type: chat + max_input_tokens: 163840 + input_price: 0.18 + output_price: 0.18 + max_output_tokens: 16384 + supports_vision: true + - name: meta/muse-glimmer-30b + type: chat + max_input_tokens: 131072 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: meta/muse-glimmer-30b:batch + type: chat + max_input_tokens: 131072 + input_price: 0.35 + output_price: 1.5 + max_output_tokens: 117964 + supports_vision: true + supports_function_calling: true + - name: meta/muse-spark-1.1 + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 4.25 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: meta/muse-spark-1.2 + type: chat + max_input_tokens: 1048576 + input_price: 1.25 + output_price: 4.25 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: meta/muse-spark-1.2-contributor + type: chat + max_input_tokens: 1048576 + input_price: 0.1 + output_price: 0.2 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: microsoft/phi-4 + type: chat + max_input_tokens: 16384 + input_price: 0.07 + output_price: 0.14 + max_output_tokens: 14745 + - name: microsoft/wizardlm-2-8x22b + type: chat + max_input_tokens: 65535 + input_price: 0.62 + output_price: 0.62 + max_output_tokens: 8000 + - name: minimax/minimax-01 + type: chat + max_input_tokens: 1000192 + input_price: 0.2 + output_price: 1.1 + max_output_tokens: 900172 + supports_vision: true + - name: minimax/minimax-m1 + type: chat + max_input_tokens: 1000000 + input_price: 0.55 + output_price: 2.2 + max_output_tokens: 40000 + supports_function_calling: true + - name: minimax/minimax-m2 + type: chat + max_input_tokens: 204800 + input_price: 0.255 + output_price: 1.02 + max_output_tokens: 131072 + supports_function_calling: true + - name: minimax/minimax-m2-her + type: chat + max_input_tokens: 65536 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 2048 + - name: minimax/minimax-m2.1 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: minimax/minimax-m2.5 + type: chat + max_input_tokens: 204800 + input_price: 0.27 + output_price: 1.08 + max_output_tokens: 128000 + supports_function_calling: true + - name: minimax/minimax-m2.7 + type: chat + max_input_tokens: 204800 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: minimax/minimax-m2.7:free + type: chat + max_input_tokens: 196608 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 176947 + supports_function_calling: true + - name: minimax/minimax-m3 + type: chat + max_input_tokens: 1048576 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 512000 + supports_vision: true + supports_function_calling: true + - name: minimax/minimax-m3:batch + type: chat + max_input_tokens: 524288 + input_price: 0.3 + output_price: 1.2 + max_output_tokens: 471859 + supports_vision: true + supports_function_calling: true + - name: minimax/minimax-m3:free + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: mistralai/codestral-2508 + type: chat + max_input_tokens: 256000 + input_price: 0.3 + output_price: 0.9 + max_output_tokens: 204800 + supports_function_calling: true + - name: mistralai/devstral-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 209715 + supports_function_calling: true + - name: mistralai/ministral-14b-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 0.2 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/ministral-3b-2512 + type: chat + max_input_tokens: 131072 + input_price: 0.1 + output_price: 0.1 + max_output_tokens: 104857 + supports_vision: true + supports_function_calling: true + - name: mistralai/ministral-8b-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 0.15 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-large + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 102400 + supports_function_calling: true + - name: mistralai/mistral-large-2407 + type: chat + max_input_tokens: 131072 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 104857 + supports_function_calling: true + - name: mistralai/mistral-large-2512 + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-medium-3 + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 104857 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-medium-3-5 + type: chat + max_input_tokens: 262144 + input_price: 1.5 + output_price: 7.5 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-medium-3-5:batch + type: chat + max_input_tokens: 262144 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-medium-3.1 + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 104857 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-nemo + type: chat + max_input_tokens: 131072 + input_price: 0.019 + output_price: 0.03 + max_output_tokens: 16384 + supports_function_calling: true + - name: mistralai/mistral-saba + type: chat + max_input_tokens: 32768 + input_price: 0.2 + output_price: 0.6 + max_output_tokens: 26214 + supports_function_calling: true + - name: mistralai/mistral-small-24b-instruct-2501 + type: chat + max_input_tokens: 32768 + input_price: 0.05 + output_price: 0.08 + max_output_tokens: 16384 + - name: mistralai/mistral-small-2603 + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 209715 + supports_vision: true + supports_function_calling: true + - name: mistralai/mistral-small-3.1-24b-instruct + type: chat + max_input_tokens: 128000 + input_price: 0.351 + output_price: 0.555 + max_output_tokens: 102400 + supports_vision: true + - name: mistralai/mistral-small-3.2-24b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.075 + output_price: 0.2 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: mistralai/mixtral-8x22b-instruct + type: chat + max_input_tokens: 65536 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 52428 + supports_function_calling: true + - name: mistralai/voxtral-small-24b-2507 + type: chat + max_input_tokens: 32768 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 26214 + supports_function_calling: true + - name: moonshotai/kimi-k2 + type: chat + max_input_tokens: 131072 + input_price: 0.57 + output_price: 2.3 + max_output_tokens: 100352 + supports_function_calling: true + - name: moonshotai/kimi-k2-0905 + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 100352 + supports_function_calling: true + - name: moonshotai/kimi-k2-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 2.5 + max_output_tokens: 100352 + supports_function_calling: true + - name: moonshotai/kimi-k2.5 + type: chat + max_input_tokens: 262144 + input_price: 0.45 + output_price: 2.25 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k2.6 + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k2.7-code + type: chat + max_input_tokens: 262144 + input_price: 0.66 + output_price: 3.4 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k3 + type: chat + max_input_tokens: 1048576 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k3:batch + type: chat + max_input_tokens: 1048576 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: morph/morph-v3-fast + type: chat + max_input_tokens: 81920 + input_price: 0.8 + output_price: 1.2 + max_output_tokens: 38000 + - name: morph/morph-v3-large + type: chat + max_input_tokens: 262144 + input_price: 0.9 + output_price: 1.9 + max_output_tokens: 131072 + - name: nex-agi/nex-n2-mini + type: chat + max_input_tokens: 262144 + input_price: 0.025 + output_price: 0.1 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: nex-agi/nex-n2-pro + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 1.0 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: nousresearch/hermes-3-llama-3.1-405b + type: chat + max_input_tokens: 131072 + input_price: 1.0 + output_price: 1.0 + max_output_tokens: 16384 + - name: nousresearch/hermes-3-llama-3.1-70b + type: chat + max_input_tokens: 131072 + input_price: 0.7 + output_price: 0.7 + max_output_tokens: 16384 + - name: nousresearch/hermes-4-405b + type: chat + max_input_tokens: 131072 + input_price: 1.0 + output_price: 3.0 + max_output_tokens: 117964 + - name: nousresearch/hermes-4-70b + type: chat + max_input_tokens: 131072 + input_price: 0.13 + output_price: 0.4 + max_output_tokens: 117964 + - name: nvidia/nemotron-3-nano-30b-a3b + type: chat + max_input_tokens: 262144 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 228000 + supports_function_calling: true + - name: nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free + type: chat + max_input_tokens: 256000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: nvidia/nemotron-3-super-120b-a12b + type: chat + max_input_tokens: 1000000 + input_price: 0.085 + output_price: 0.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: nvidia/nemotron-3-super-120b-a12b:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 235929 + supports_function_calling: true + - name: nvidia/nemotron-3-ultra-550b-a55b + type: chat + max_input_tokens: 262144 + input_price: 0.625 + output_price: 3.125 + max_output_tokens: 32768 + supports_function_calling: true + - name: nvidia/nemotron-3-ultra-550b-a55b:batch + type: chat + max_input_tokens: 512288 + input_price: 0.6 + output_price: 3.6 + max_output_tokens: 461059 + supports_function_calling: true + - name: nvidia/nemotron-3-ultra-550b-a55b:free + type: chat + max_input_tokens: 1000000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: nvidia/nemotron-3.5-content-safety:free + type: chat + max_input_tokens: 128000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 8192 + supports_vision: true + - name: nvidia/nemotron-3.5-lightning + type: chat + max_input_tokens: 262144 + input_price: 0.08 + output_price: 0.2 + max_output_tokens: 131072 + supports_function_calling: true + - name: nvidia/nemotron-3.5-lightning:free + type: chat + max_input_tokens: 1000000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: openai/gpt-3.5-turbo + type: chat + max_input_tokens: 16385 + input_price: 0.5 + output_price: 1.5 + max_output_tokens: 4096 + supports_function_calling: true + - name: openai/gpt-3.5-turbo-0613 + type: chat + max_input_tokens: 4095 + input_price: 1.0 + output_price: 2.0 + max_output_tokens: 3685 + supports_function_calling: true + - name: openai/gpt-3.5-turbo-16k + type: chat + max_input_tokens: 16385 + input_price: 3.0 + output_price: 4.0 + max_output_tokens: 4096 + supports_function_calling: true + - name: openai/gpt-3.5-turbo-instruct + type: chat + max_input_tokens: 4095 + input_price: 1.5 + output_price: 2.0 + max_output_tokens: 3685 + - name: openai/gpt-3.5-turbo:batch + type: chat + max_input_tokens: 16385 + input_price: 0.25 + output_price: 0.75 + max_output_tokens: 4096 + supports_function_calling: true + - name: openai/gpt-4 + type: chat + max_input_tokens: 8191 + input_price: 30.0 + output_price: 60.0 + max_output_tokens: 4096 + supports_function_calling: true + - name: openai/gpt-4-turbo + type: chat + max_input_tokens: 128000 + input_price: 10.0 + output_price: 30.0 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4-turbo-preview + type: chat + max_input_tokens: 128000 + input_price: 10.0 + output_price: 30.0 + max_output_tokens: 4096 + supports_function_calling: true + - name: openai/gpt-4-turbo:batch + type: chat + max_input_tokens: 128000 + input_price: 5.0 + output_price: 15.0 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1 + type: chat + max_input_tokens: 1047576 + input_price: 2.0 + output_price: 8.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1-mini + type: chat + max_input_tokens: 1047576 + input_price: 0.4 + output_price: 1.6 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1-mini:batch + type: chat + max_input_tokens: 1047576 + input_price: 0.2 + output_price: 0.8 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1-nano + type: chat + max_input_tokens: 1047576 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1-nano:batch + type: chat + max_input_tokens: 1047576 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4.1:batch + type: chat + max_input_tokens: 1047576 + input_price: 1.0 + output_price: 4.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-2024-05-13 + type: chat + max_input_tokens: 128000 + input_price: 5.0 + output_price: 15.0 + max_output_tokens: 4096 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-2024-08-06 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-2024-11-20 + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-mini + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-mini-2024-07-18 + type: chat + max_input_tokens: 128000 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o-mini:batch + type: chat + max_input_tokens: 128000 + input_price: 0.075 + output_price: 0.3 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-4o:batch + type: chat + max_input_tokens: 128000 + input_price: 1.25 + output_price: 5.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5 + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - minimal + - low + - medium + - high + default_reasoning_effort: medium + - name: openai/gpt-5-image + type: chat + max_input_tokens: 400000 + input_price: 10.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + - name: openai/gpt-5-image-mini + type: chat + max_input_tokens: 400000 + input_price: 2.5 + output_price: 2.0 + max_output_tokens: 128000 + supports_vision: true + - name: openai/gpt-5-mini + type: chat + max_input_tokens: 400000 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5-mini:batch + type: chat + max_input_tokens: 400000 + input_price: 0.125 + output_price: 1.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5-nano + type: chat + max_input_tokens: 400000 + input_price: 0.05 + output_price: 0.4 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5-nano:batch + type: chat + max_input_tokens: 400000 + input_price: 0.025 + output_price: 0.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5-pro + type: chat + max_input_tokens: 400000 + input_price: 15.0 + output_price: 120.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5-pro:batch + type: chat + max_input_tokens: 400000 + input_price: 7.5 + output_price: 60.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.1 + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.1-codex + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.1-codex-max + type: chat + max_input_tokens: 400000 + input_price: 1.25 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.1-codex-mini + type: chat + max_input_tokens: 400000 + input_price: 0.25 + output_price: 2.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.1:batch + type: chat + max_input_tokens: 400000 + input_price: 0.625 + output_price: 5.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.2 + type: chat + max_input_tokens: 400000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: openai/gpt-5.2-chat + type: chat + max_input_tokens: 128000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 32000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.2-codex + type: chat + max_input_tokens: 400000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.2-pro + type: chat + max_input_tokens: 400000 + input_price: 21.0 + output_price: 168.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: openai/gpt-5.2-pro:batch + type: chat + max_input_tokens: 400000 + input_price: 10.5 + output_price: 84.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.2:batch + type: chat + max_input_tokens: 400000 + input_price: 0.875 + output_price: 7.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.3-codex + type: chat + max_input_tokens: 400000 + input_price: 1.75 + output_price: 14.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: openai/gpt-5.4 + type: chat + max_input_tokens: 1050000 + input_price: 2.5 + output_price: 15.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: openai/gpt-5.4-image-2 + type: chat + max_input_tokens: 272000 + input_price: 8.0 + output_price: 15.0 + max_output_tokens: 128000 + supports_vision: true + - name: openai/gpt-5.4-mini + type: chat + max_input_tokens: 400000 + input_price: 0.75 + output_price: 4.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: openai/gpt-5.4-mini:batch + type: chat + max_input_tokens: 400000 + input_price: 0.375 + output_price: 2.25 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.4-nano + type: chat + max_input_tokens: 400000 + input_price: 0.2 + output_price: 1.25 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: none + - name: openai/gpt-5.4-nano:batch + type: chat + max_input_tokens: 400000 + input_price: 0.1 + output_price: 0.625 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.4-pro + type: chat + max_input_tokens: 1050000 + input_price: 30.0 + output_price: 180.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: openai/gpt-5.4-pro:batch + type: chat + max_input_tokens: 1050000 + input_price: 15.0 + output_price: 90.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.4:batch + type: chat + max_input_tokens: 1050000 + input_price: 1.25 + output_price: 7.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.5 + type: chat + max_input_tokens: 1050000 + input_price: 5.0 + output_price: 30.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + default_reasoning_effort: medium + - name: openai/gpt-5.5-pro + type: chat + max_input_tokens: 1050000 + input_price: 30.0 + output_price: 180.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - medium + - high + - xhigh + default_reasoning_effort: high + - name: openai/gpt-5.5-pro:batch + type: chat + max_input_tokens: 1050000 + input_price: 15.0 + output_price: 90.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.5:batch + type: chat + max_input_tokens: 1050000 + input_price: 2.5 + output_price: 15.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-5.6-luna + type: chat + max_input_tokens: 1050000 + input_price: 0.2 + output_price: 1.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-luna-pro + type: chat + max_input_tokens: 1050000 + input_price: 0.2 + output_price: 1.2 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-luna-pro:batch + type: chat + max_input_tokens: 1050000 + input_price: 0.1 + output_price: 0.6 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-luna:batch + type: chat + max_input_tokens: 1050000 + input_price: 0.1 + output_price: 0.6 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-sol + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-sol-pro + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-sol-pro:batch + type: chat + max_input_tokens: 1050000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-sol:batch + type: chat + max_input_tokens: 1050000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-terra + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-terra-pro + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-terra-pro:batch + type: chat + max_input_tokens: 1050000 + input_price: 1.0 + output_price: 6.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5.6-terra:batch + type: chat + max_input_tokens: 1050000 + input_price: 1.0 + output_price: 6.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - xhigh + - max + default_reasoning_effort: medium + - name: openai/gpt-5:batch + type: chat + max_input_tokens: 400000 + input_price: 0.625 + output_price: 5.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-audio + type: chat + max_input_tokens: 128000 + input_price: 2.5 + output_price: 10.0 + max_output_tokens: 16384 + supports_function_calling: true + - name: openai/gpt-audio-mini + type: chat + max_input_tokens: 128000 + input_price: 0.6 + output_price: 2.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: openai/gpt-chat-latest + type: chat + max_input_tokens: 400000 + input_price: 5.0 + output_price: 30.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: openai/gpt-oss-120b + type: chat + max_input_tokens: 131072 + input_price: 0.037 + output_price: 0.17 + max_output_tokens: 117964 + supports_function_calling: true + - name: openai/gpt-oss-120b:batch + type: chat + max_input_tokens: 131072 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 117964 + supports_function_calling: true + - name: openai/gpt-oss-20b + type: chat + max_input_tokens: 131072 + input_price: 0.03 + output_price: 0.13 + max_output_tokens: 117964 + supports_function_calling: true + - name: openai/gpt-oss-20b:batch + type: chat + max_input_tokens: 131072 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 117964 + - name: openai/gpt-oss-safeguard-20b + type: chat + max_input_tokens: 131072 + input_price: 0.075 + output_price: 0.3 + max_output_tokens: 65536 + supports_function_calling: true + - name: openai/o1 + type: chat + max_input_tokens: 200000 + input_price: 15.0 + output_price: 60.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o1-pro + type: chat + max_input_tokens: 200000 + input_price: 150.0 + output_price: 600.0 + max_output_tokens: 100000 + supports_vision: true + - name: openai/o3 + type: chat + max_input_tokens: 200000 + input_price: 2.0 + output_price: 8.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o3-mini + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + max_output_tokens: 100000 + supports_function_calling: true + - name: openai/o3-mini-high + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + max_output_tokens: 100000 + supports_function_calling: true + - name: openai/o3-mini:batch + type: chat + max_input_tokens: 200000 + input_price: 0.55 + output_price: 2.2 + max_output_tokens: 100000 + supports_function_calling: true + - name: openai/o3-pro + type: chat + max_input_tokens: 200000 + input_price: 20.0 + output_price: 80.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o3:batch + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 4.0 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o4-mini + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o4-mini-high + type: chat + max_input_tokens: 200000 + input_price: 1.1 + output_price: 4.4 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openai/o4-mini:batch + type: chat + max_input_tokens: 200000 + input_price: 0.55 + output_price: 2.2 + max_output_tokens: 100000 + supports_vision: true + supports_function_calling: true + - name: openrouter/auto + type: chat + max_input_tokens: 2000000 + supports_vision: true + supports_function_calling: true + - name: openrouter/auto-beta + type: chat + max_input_tokens: 2000000 + supports_vision: true + supports_function_calling: true + - name: openrouter/bodybuilder + type: chat + max_input_tokens: 128000 + - name: openrouter/free + type: chat + max_input_tokens: 200000 + input_price: 0.0 + output_price: 0.0 + supports_vision: true + supports_function_calling: true + - name: openrouter/fusion + type: chat + max_input_tokens: 1000000 + - name: openrouter/pareto-code + type: chat + max_input_tokens: 2000000 + - name: perceptron/perceptron-mk1 + type: chat + max_input_tokens: 32768 + input_price: 0.15 + output_price: 1.5 + max_output_tokens: 8192 + supports_vision: true + - name: perplexity/sonar + type: chat + max_input_tokens: 127072 + input_price: 1.0 + output_price: 1.0 + max_output_tokens: 114364 + supports_vision: true + - name: perplexity/sonar-deep-research + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 8.0 + patch: + body: + include_reasoning: true + max_output_tokens: 115200 + - name: perplexity/sonar-pro + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 8000 + supports_vision: true + - name: perplexity/sonar-pro-search + type: chat + max_input_tokens: 200000 + input_price: 3.0 + output_price: 15.0 + max_output_tokens: 8000 + supports_vision: true + - name: perplexity/sonar-reasoning-pro + type: chat + max_input_tokens: 128000 + input_price: 2.0 + output_price: 8.0 + patch: + body: + include_reasoning: true + max_output_tokens: 115200 + supports_vision: true + - name: poolside/laguna-s-2.1 + type: chat + max_input_tokens: 1048576 + input_price: 0.09 + output_price: 0.18 + max_output_tokens: 131072 + supports_function_calling: true + - name: poolside/laguna-s-2.1:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: poolside/laguna-xs-2.1 + type: chat + max_input_tokens: 262144 + input_price: 0.06 + output_price: 0.12 + max_output_tokens: 32768 + supports_function_calling: true + - name: poolside/laguna-xs-2.1:free + type: chat + max_input_tokens: 262144 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen/qwen-2.5-72b-instruct + type: chat + max_input_tokens: 32768 + input_price: 0.36 + output_price: 0.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen/qwen-2.5-7b-instruct + type: chat + max_input_tokens: 32768 + input_price: 0.1 + output_price: 0.2 + max_output_tokens: 29491 + supports_function_calling: true + - name: qwen/qwen-2.5-coder-32b-instruct + type: chat + max_input_tokens: 32768 + input_price: 0.66 + output_price: 1.0 + max_output_tokens: 29491 + - name: qwen/qwen-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.26 + output_price: 0.78 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen/qwen-plus-2025-07-28 + type: chat + max_input_tokens: 1000000 + input_price: 0.26 + output_price: 0.78 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen/qwen2.5-vl-72b-instruct + type: chat + max_input_tokens: 128000 + input_price: 0.25 + output_price: 0.75 + max_output_tokens: 28800 + supports_vision: true + - name: qwen/qwen3-14b + type: chat + max_input_tokens: 131072 + input_price: 0.12 + output_price: 0.24 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen/qwen3-235b-a22b + type: chat + max_input_tokens: 131072 + input_price: 0.455 + output_price: 1.82 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen/qwen3-235b-a22b-2507 + type: chat + max_input_tokens: 262144 + input_price: 0.0875 + output_price: 0.35 + max_output_tokens: 235929 + supports_function_calling: true + - name: qwen/qwen3-235b-a22b-thinking-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.23 + output_price: 2.3 + max_output_tokens: 117964 + supports_function_calling: true + - name: qwen/qwen3-30b-a3b + type: chat + max_input_tokens: 131072 + input_price: 0.12 + output_price: 0.5 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen/qwen3-30b-a3b-instruct-2507 + type: chat + max_input_tokens: 262144 + input_price: 0.0482 + output_price: 0.1931 + max_output_tokens: 32000 + supports_function_calling: true + - name: qwen/qwen3-30b-a3b-thinking-2507 + type: chat + max_input_tokens: 81920 + input_price: 0.2 + output_price: 2.4 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen/qwen3-32b + type: chat + max_input_tokens: 131072 + input_price: 0.08 + output_price: 0.28 + max_output_tokens: 16384 + supports_function_calling: true + - name: qwen/qwen3-8b + type: chat + max_input_tokens: 131072 + input_price: 0.117 + output_price: 0.455 + max_output_tokens: 8192 + supports_function_calling: true + - name: qwen/qwen3-coder + type: chat + max_input_tokens: 262144 + input_price: 0.3 + output_price: 1.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3-coder-30b-a3b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.28 + max_output_tokens: 235929 + supports_function_calling: true + - name: qwen/qwen3-coder-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.195 + output_price: 0.975 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3-coder-next + type: chat + max_input_tokens: 262144 + input_price: 0.12 + output_price: 0.8 + max_output_tokens: 235929 + supports_function_calling: true + - name: qwen/qwen3-coder-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.65 + output_price: 3.25 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3-max + type: chat + max_input_tokens: 262144 + input_price: 0.78 + output_price: 3.9 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3-max-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.78 + output_price: 3.9 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3-next-80b-a3b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 1.1 + max_output_tokens: 235929 + supports_function_calling: true + - name: qwen/qwen3-next-80b-a3b-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 1.2 + max_output_tokens: 32768 + supports_function_calling: true + - name: qwen/qwen3-vl-235b-a22b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.21 + output_price: 1.9 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-235b-a22b-thinking + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 4.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-30b-a3b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-30b-a3b-thinking + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 2.4 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-32b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.104 + output_price: 0.416 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-8b-instruct + type: chat + max_input_tokens: 262144 + input_price: 0.117 + output_price: 0.455 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3-vl-8b-thinking + type: chat + max_input_tokens: 131072 + input_price: 0.18 + output_price: 2.1 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-122b-a10b + type: chat + max_input_tokens: 262144 + input_price: 0.29 + output_price: 2.4 + max_output_tokens: 81920 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-27b + type: chat + max_input_tokens: 262144 + input_price: 0.195 + output_price: 1.56 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-35b-a3b + type: chat + max_input_tokens: 262144 + input_price: 0.25 + output_price: 1.25 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-397b-a17b + type: chat + max_input_tokens: 262144 + input_price: 0.39 + output_price: 2.34 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-9b + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.15 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-9b:batch + type: chat + max_input_tokens: 262144 + input_price: 0.17 + output_price: 0.25 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-flash-02-23 + type: chat + max_input_tokens: 1000000 + input_price: 0.065 + output_price: 0.26 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-plus-02-15 + type: chat + max_input_tokens: 1000000 + input_price: 0.26 + output_price: 1.56 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.5-plus-20260420 + type: chat + max_input_tokens: 1000000 + input_price: 0.3 + output_price: 1.8 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.6-27b + type: chat + max_input_tokens: 262144 + input_price: 0.6 + output_price: 3.6 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.6-35b-a3b + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.9 + max_output_tokens: 235929 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.6-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.1875 + output_price: 1.125 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.6-max-preview + type: chat + max_input_tokens: 262144 + input_price: 1.027 + output_price: 6.162 + max_output_tokens: 65536 + supports_function_calling: true + - name: qwen/qwen3.6-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.325 + output_price: 1.95 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.7-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.03 + output_price: 0.13 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.7-max + type: chat + max_input_tokens: 1000000 + input_price: 1.475 + output_price: 4.425 + max_output_tokens: 131072 + supports_function_calling: true + - name: qwen/qwen3.7-plus + type: chat + max_input_tokens: 1000000 + input_price: 0.32 + output_price: 1.28 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.8-2.4t-a95b + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 131072 + supports_function_calling: true + - name: qwen/qwen3.8-2.4t-a95b:batch + type: chat + max_input_tokens: 1010000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 909000 + supports_function_calling: true + - name: qwen/qwen3.8-27b + type: chat + max_input_tokens: 1000000 + input_price: 0.425 + output_price: 2.55 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.8-flash + type: chat + max_input_tokens: 1000000 + input_price: 0.15 + output_price: 0.47 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: qwen/qwen3.8-max + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: rekaai/reka-edge + type: chat + max_input_tokens: 16384 + input_price: 0.1 + output_price: 0.1 + max_output_tokens: 14745 + supports_vision: true + supports_function_calling: true + - name: rekaai/reka-flash-3 + type: chat + max_input_tokens: 65536 + input_price: 0.1 + output_price: 0.2 + max_output_tokens: 58982 + - name: relace/relace-apply-3 + type: chat + max_input_tokens: 256000 + input_price: 0.85 + output_price: 1.25 + max_output_tokens: 128000 + - name: relace/relace-search + type: chat + max_input_tokens: 256000 + input_price: 1.0 + output_price: 3.0 + max_output_tokens: 128000 + supports_function_calling: true + - name: sakana/fugu-ultra + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 30.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: sakana/sakana-namazu + type: chat + max_input_tokens: 262144 + input_price: 0.95 + output_price: 4.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: sao10k/l3-lunaris-8b + type: chat + max_input_tokens: 8192 + input_price: 0.04 + output_price: 0.05 + max_output_tokens: 7372 + - name: sao10k/l3.1-euryale-70b + type: chat + max_input_tokens: 131072 + input_price: 0.85 + output_price: 0.85 + max_output_tokens: 16384 + supports_function_calling: true + - name: sao10k/l3.3-euryale-70b + type: chat + max_input_tokens: 131072 + input_price: 0.65 + output_price: 0.75 + max_output_tokens: 16384 + - name: stepfun/step-3.5-flash + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 65536 + supports_function_calling: true + - name: stepfun/step-3.7-flash + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 1.15 + max_output_tokens: 230400 + supports_vision: true + supports_function_calling: true + - name: tencent/hunyuan-a13b-instruct + type: chat + max_input_tokens: 131072 + input_price: 0.14 + output_price: 0.57 + max_output_tokens: 117964 + - name: tencent/hy-mt2-1.8b + type: chat + max_input_tokens: 8192 + input_price: 0.044 + output_price: 0.177 + max_output_tokens: 4096 + - name: tencent/hy-mt2-30b-a3b + type: chat + max_input_tokens: 8192 + input_price: 0.074 + output_price: 0.295 + max_output_tokens: 4096 + - name: tencent/hy-mt2-7b + type: chat + max_input_tokens: 8192 + input_price: 0.074 + output_price: 0.295 + max_output_tokens: 4096 + - name: tencent/hy3 + type: chat + max_input_tokens: 262144 + input_price: 0.0825 + output_price: 0.33 + max_output_tokens: 128000 + supports_function_calling: true + - name: tencent/hy3-preview + type: chat + max_input_tokens: 262144 + input_price: 0.18 + output_price: 0.6 + max_output_tokens: 235929 + supports_function_calling: true + - name: tencent/hy4-preview + type: chat + max_input_tokens: 1048576 + input_price: 0.834 + output_price: 2.501 + max_output_tokens: 64000 + supports_function_calling: true + - name: thedrummer/cydonia-24b-v4.1 + type: chat + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.5 + max_output_tokens: 117964 + - name: thedrummer/skyfall-36b-v2 + type: chat + max_input_tokens: 32768 + input_price: 0.55 + output_price: 0.8 + max_output_tokens: 29491 + - name: thedrummer/unslopnemo-12b + type: chat + max_input_tokens: 1024000 + input_price: 0.4 + output_price: 0.4 + max_output_tokens: 26214 + supports_function_calling: true + - name: thinkingmachines/inkling + type: chat + max_input_tokens: 1048576 + input_price: 1.0 + output_price: 4.05 + max_output_tokens: 471859 + supports_vision: true + supports_function_calling: true + - name: thinkingmachines/inkling-small + type: chat + max_input_tokens: 1048576 + input_price: 0.45 + output_price: 1.2 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: thinkingmachines/inkling-small:batch + type: chat + max_input_tokens: 524288 + input_price: 0.5 + output_price: 1.2 + max_output_tokens: 471859 + supports_vision: true + supports_function_calling: true + - name: thinkingmachines/inkling-small:free + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: thinkingmachines/inkling:batch + type: chat + max_input_tokens: 524288 + input_price: 1.0 + output_price: 4.05 + max_output_tokens: 471859 + supports_vision: true + supports_function_calling: true + - name: thinkingmachines/inkling:free + type: chat + max_input_tokens: 1048576 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: undi95/remm-slerp-l2-13b + type: chat + max_input_tokens: 6144 + input_price: 0.45 + output_price: 0.65 + max_output_tokens: 4096 + - name: upstage/solar-pro-3 + type: chat + max_input_tokens: 131072 + input_price: 0.15 + output_price: 0.6 + max_output_tokens: 117964 + supports_function_calling: true + - name: upstage/solar-pro4 + type: chat + max_input_tokens: 524288 + input_price: 0.03 + output_price: 0.12 + max_output_tokens: 131072 + supports_function_calling: true + - name: writer/palmyra-x5 + type: chat + max_input_tokens: 1040000 + input_price: 0.6 + output_price: 6.0 + max_output_tokens: 8192 + - name: x-ai/grok-4.20 + type: chat + max_input_tokens: 2000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 1800000 + supports_vision: true + supports_function_calling: true + - name: x-ai/grok-4.20-multi-agent + type: chat + max_input_tokens: 2000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 1800000 + supports_vision: true + - name: x-ai/grok-4.3 + type: chat + max_input_tokens: 1000000 + input_price: 1.25 + output_price: 2.5 + max_output_tokens: 900000 + supports_vision: true + supports_function_calling: true + - name: x-ai/grok-4.5 + type: chat + max_input_tokens: 500000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 450000 + supports_vision: true + supports_function_calling: true + - name: x-ai/grok-4.6 + type: chat + max_input_tokens: 500000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 450000 + supports_vision: true + supports_function_calling: true + - name: x-ai/grok-build-0.1 + type: chat + max_input_tokens: 256000 + input_price: 1.0 + output_price: 2.0 + max_output_tokens: 230400 + supports_vision: true + supports_function_calling: true + - name: xiaomi/mimo-v2.5 + type: chat + max_input_tokens: 1050000 + input_price: 0.14 + output_price: 0.28 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: xiaomi/mimo-v2.5-pro + type: chat + max_input_tokens: 1050000 + input_price: 0.435 + output_price: 0.87 + max_output_tokens: 131072 + supports_function_calling: true + - name: z-ai/glm-4.5 + type: chat + max_input_tokens: 131072 + input_price: 0.6 + output_price: 2.2 + max_output_tokens: 98304 + supports_function_calling: true + - name: z-ai/glm-4.5-air + type: chat + max_input_tokens: 131072 + input_price: 0.13 + output_price: 0.85 + max_output_tokens: 98304 + supports_function_calling: true + - name: z-ai/glm-4.5v + type: chat + max_input_tokens: 65536 + input_price: 0.6 + output_price: 1.8 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: z-ai/glm-4.6 + type: chat + max_input_tokens: 204800 + input_price: 0.43 + output_price: 1.75 + max_output_tokens: 16384 + supports_function_calling: true + - name: z-ai/glm-4.6v + type: chat + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.9 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: z-ai/glm-4.7 + type: chat + max_input_tokens: 204800 + input_price: 0.4 + output_price: 1.75 + max_output_tokens: 131072 + supports_function_calling: true + - name: z-ai/glm-4.7-flash + type: chat + max_input_tokens: 202752 + input_price: 0.06 + output_price: 0.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: z-ai/glm-5 + type: chat + max_input_tokens: 204800 + input_price: 0.6 + output_price: 1.92 + max_output_tokens: 128000 + supports_function_calling: true + - name: z-ai/glm-5-turbo + type: chat + max_input_tokens: 202752 + input_price: 1.2 + output_price: 4.0 + max_output_tokens: 131072 + supports_function_calling: true + - name: z-ai/glm-5.1 + type: chat + max_input_tokens: 204800 + input_price: 0.966 + output_price: 3.036 + max_output_tokens: 128000 + supports_function_calling: true + - name: z-ai/glm-5.2 + type: chat + max_input_tokens: 1048576 + input_price: 0.966 + output_price: 3.036 + max_output_tokens: 131072 + supports_function_calling: true + - name: z-ai/glm-5.2:free + type: chat + max_input_tokens: 256000 + input_price: 0.0 + output_price: 0.0 + max_output_tokens: 230400 + supports_function_calling: true + - name: z-ai/glm-5.3 + type: chat + max_input_tokens: 1310720 + input_price: 1.4 + output_price: 4.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: z-ai/glm-5.3-flash + type: chat + max_input_tokens: 1310720 + input_price: 0.075 + output_price: 0.25 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: z-ai/glm-5.3-flash:batch + type: chat + max_input_tokens: 1048575 + input_price: 0.15 + output_price: 0.5 + max_output_tokens: 943717 + supports_vision: true + supports_function_calling: true + - name: z-ai/glm-5v-turbo + type: chat + max_input_tokens: 202752 + input_price: 1.2 + output_price: 4.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: ~anthropic/claude-fable-latest + type: chat + max_input_tokens: 1000000 + input_price: 10.0 + output_price: 50.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ~anthropic/claude-haiku-latest + type: chat + max_input_tokens: 200000 + input_price: 1.0 + output_price: 5.0 + max_output_tokens: 64000 + supports_vision: true + supports_function_calling: true + - name: ~anthropic/claude-opus-latest + type: chat + max_input_tokens: 1000000 + input_price: 5.0 + output_price: 25.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ~anthropic/claude-sonnet-latest + type: chat + max_input_tokens: 1000000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ~deepseek/deepseek-v4-flash-latest + type: chat + max_input_tokens: 1310720 + input_price: 0.05 + output_price: 0.16 + max_output_tokens: 393216 + supports_function_calling: true + - name: ~google/gemini-flash-latest + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 3.75 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: ~google/gemini-pro-latest + type: chat + max_input_tokens: 1048576 + input_price: 2.0 + output_price: 12.0 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: ~moonshotai/kimi-latest + type: chat + max_input_tokens: 1048576 + input_price: 2.55 + output_price: 12.75 + max_output_tokens: 943718 + supports_vision: true + supports_function_calling: true + - name: ~openai/gpt-latest + type: chat + max_input_tokens: 1050000 + input_price: 2.0 + output_price: 10.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ~openai/gpt-mini-latest + type: chat + max_input_tokens: 400000 + input_price: 0.75 + output_price: 4.5 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: ~x-ai/grok-latest + type: chat + max_input_tokens: 500000 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 450000 + supports_vision: true + supports_function_calling: true + - name: ~z-ai/glm-flash-latest + type: chat + max_input_tokens: 1310720 + input_price: 0.075 + output_price: 0.25 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: ~z-ai/glm-latest + type: chat + max_input_tokens: 1310720 + input_price: 1.15 + output_price: 3.5 + max_output_tokens: 235929 + supports_function_calling: true - provider: github models: - - name: gpt-5 - max_input_tokens: 400000 - max_output_tokens: 128000 - supports_vision: true - supports_function_calling: true - - name: gpt-5-mini - max_input_tokens: 400000 - max_output_tokens: 128000 - supports_vision: true - supports_function_calling: true - - name: gpt-5-nano - max_input_tokens: 400000 - max_output_tokens: 128000 - supports_vision: true - supports_function_calling: true - - name: gpt-4.1 - max_input_tokens: 1047576 - max_output_tokens: 32768 - supports_vision: true - supports_function_calling: true - - name: gpt-4o - max_input_tokens: 128000 - max_output_tokens: 16384 - supports_function_calling: true - - name: text-embedding-3-large - type: embedding - max_tokens_per_chunk: 8191 - default_chunk_size: 2000 - max_batch_size: 100 - - name: text-embedding-3-small - type: embedding - max_tokens_per_chunk: 8191 - default_chunk_size: 2000 - max_batch_size: 100 - - name: llama-4-maverick-17b-128e-instruct-fp8 - max_input_tokens: 1048576 - supports_vision: true - - name: llama-4-scout-17b-16e-instruct - max_input_tokens: 327680 - supports_vision: true - - name: llama-3.3-70b-instruct - max_input_tokens: 131072 - - name: mistral-medium-2505 - max_input_tokens: 131072 - supports_function_calling: true - - name: mistral-small-2503 - max_input_tokens: 131072 - supports_function_calling: true - - name: codestral-2501 - max_input_tokens: 256000 - supports_function_calling: true - - name: cohere-embed-v3-english - type: embedding - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: cohere-embed-v3-multilingual - type: embedding - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 96 - - name: deepseek-r1-0528 - max_input_tokens: 163840 - - name: deepseek-v3-0324 - max_input_tokens: 163840 - - name: mai-ds-r1 - max_input_tokens: 163840 - - name: phi-4 - max_input_tokens: 16384 - - name: phi-4-mini-instruct - max_input_tokens: 131072 - - name: phi-4-reasoning - max_input_tokens: 33792 - - name: phi-4-mini-reasoning - max_input_tokens: 131072 - - name: grok-3 - max_input_tokens: 131072 - - name: grok-3-mini - max_input_tokens: 131072 - -# Links: -# - https://deepinfra.com/models -# - https://deepinfra.com/docs/openai_api + - name: gpt-5 + type: chat + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-5-mini + type: chat + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-5-nano + type: chat + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-4.1 + type: chat + max_input_tokens: 1047576 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gpt-4o + type: chat + max_input_tokens: 128000 + max_output_tokens: 16384 + supports_function_calling: true + - name: text-embedding-3-large + type: embedding + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: text-embedding-3-small + type: embedding + max_tokens_per_chunk: 8191 + default_chunk_size: 2000 + max_batch_size: 100 + - name: llama-4-maverick-17b-128e-instruct-fp8 + type: chat + max_input_tokens: 1048576 + supports_vision: true + - name: llama-4-scout-17b-16e-instruct + type: chat + max_input_tokens: 327680 + supports_vision: true + - name: llama-3.3-70b-instruct + type: chat + max_input_tokens: 131072 + - name: mistral-medium-2505 + type: chat + max_input_tokens: 131072 + supports_function_calling: true + - name: mistral-small-2503 + type: chat + max_input_tokens: 131072 + supports_function_calling: true + - name: codestral-2501 + type: chat + max_input_tokens: 256000 + supports_function_calling: true + - name: cohere-embed-v3-english + type: embedding + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: cohere-embed-v3-multilingual + type: embedding + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 96 + - name: deepseek-r1-0528 + type: chat + max_input_tokens: 163840 + - name: deepseek-v3-0324 + type: chat + max_input_tokens: 163840 + - name: mai-ds-r1 + type: chat + max_input_tokens: 163840 + - name: phi-4 + type: chat + max_input_tokens: 16384 + - name: phi-4-mini-instruct + type: chat + max_input_tokens: 131072 + - name: phi-4-reasoning + type: chat + max_input_tokens: 33792 + - name: phi-4-mini-reasoning + type: chat + max_input_tokens: 131072 + - name: grok-3 + type: chat + max_input_tokens: 131072 + - name: grok-3-mini + type: chat + max_input_tokens: 131072 - provider: deepinfra models: - - name: openai/gpt-oss-120b - max_input_tokens: 131072 - input_price: 0.09 - output_price: 0.45 - supports_function_calling: true - - name: openai/gpt-oss-20b - max_input_tokens: 131072 - input_price: 0.04 - output_price: 0.16 - supports_function_calling: true - - name: meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8 - max_input_tokens: 1048576 - input_price: 0.18 - output_price: 0.6 - supports_vision: true - - name: meta-llama/Llama-4-Scout-17B-16E-Instruct - max_input_tokens: 327680 - input_price: 0.08 - output_price: 0.3 - supports_vision: true - - name: Qwen/Qwen3-Max - max_input_tokens: 262144 - input_price: 1.2 - output_price: 6 - supports_function_calling: true - - name: Qwen/Qwen3-Max-Thinking - max_input_tokens: 262144 - input_price: 1.2 - output_price: 6 - supports_function_calling: true - - name: Qwen/Qwen3-Next-80B-A3B-Instruct - max_input_tokens: 262144 - input_price: 0.14 - output_price: 1.4 - supports_function_calling: true - - name: Qwen/Qwen3-Next-80B-A3B-Thinking - max_input_tokens: 262144 - input_price: 0.14 - output_price: 1.4 - - name: Qwen/Qwen3-235B-A22B-Instruct-2507 - max_input_tokens: 131072 - input_price: 0.13 - output_price: 0.6 - supports_function_calling: true - - name: Qwen/Qwen3-235B-A22B-Thinking-2507 - max_input_tokens: 131072 - input_price: 0.13 - output_price: 0.6 - - name: Qwen/Qwen3-Coder-480B-A35B-Instruct - max_input_tokens: 131072 - input_price: 0.4 - output_price: 1.6 - supports_function_calling: true - - name: Qwen/Qwen3-Coder-30B-A3B-Instruct - max_input_tokens: 262144 - input_price: 0.07 - output_price: 0.27 - supports_function_calling: true - - name: Qwen/Qwen3-30B-A3B - max_input_tokens: 40960 - input_price: 0.1 - output_price: 0.3 - - name: Qwen/Qwen3-VL-8B-Instruct - max_input_tokens: 262144 - input_price: 0.18 - output_price: 0.69 - supports_vision: true - - name: deepseek-ai/DeepSeek-V3.2 - max_input_tokens: 163840 - input_price: 0.26 - output_price: 0.39 - supports_function_calling: true - - name: google/gemma-3-27b-it - max_input_tokens: 131072 - input_price: 0.1 - output_price: 0.2 - - name: mistralai/Mistral-Small-3.2-24B-Instruct-2506 - max_input_tokens: 32768 - input_price: 0.06 - output_price: 0.12 - - name: moonshotai/Kimi-K2.5 - max_input_tokens: 262144 - input_price: 0.5 - output_price: 2.8 - supports_function_calling: true - - name: moonshotai/Kimi-K2-Instruct-0905 - max_input_tokens: 262144 - input_price: 0.5 - output_price: 2.0 - supports_function_calling: true - - name: moonshotai/Kimi-K2-Thinking - max_input_tokens: 262144 - input_price: 0.55 - output_price: 2.5 - supports_function_calling: true - - name: MiniMaxAI/MiniMax-M2.5 - max_input_tokens: 196608 - input_price: 0.27 - output_price: 0.95 - supports_function_calling: true - - name: MiniMaxAI/MiniMax-M2.1 - max_input_tokens: 196608 - input_price: 0.27 - output_price: 0.95 - supports_function_calling: true - - name: zai-org/GLM-5 - max_input_tokens: 202752 - input_price: 0.8 - output_price: 2.56 - supports_function_calling: true - - name: zai-org/GLM-4.7 - max_input_tokens: 202752 - input_price: 0.43 - output_price: 1.75 - supports_function_calling: true - - name: zai-org/GLM-4.7-Flash - max_input_tokens: 202752 - input_price: 0.06 - output_price: 0.4 - supports_function_calling: true - - name: zai-org/GLM-4.6V - max_input_tokens: 131072 - input_price: 0.3 - output_price: 0.9 - supports_vision: true - - name: BAAI/bge-large-en-v1.5 - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 100 - - name: BAAI/bge-m3 - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 8192 - default_chunk_size: 2000 - max_batch_size: 100 - - name: intfloat/e5-large-v2 - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 100 - - name: intfloat/multilingual-e5-large - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 100 - - name: thenlper/gte-large - type: embedding - input_price: 0.01 - max_tokens_per_chunk: 512 - default_chunk_size: 1000 - max_batch_size: 100 - -# Links: -# - https://jina.ai/models -# - https://api.jina.ai/redoc + - name: BAAI/bge-large-en-v1.5 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: BAAI/bge-m3 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 100 + - name: intfloat/e5-large-v2 + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: intfloat/multilingual-e5-large + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: thenlper/gte-large + type: embedding + input_price: 0.01 + max_tokens_per_chunk: 512 + default_chunk_size: 1000 + max_batch_size: 100 + - name: Qwen/Qwen3-Max-Thinking + type: chat + max_input_tokens: 262144 + input_price: 1.2 + output_price: 6.0 + supports_function_calling: true + - name: Qwen/Qwen3-Next-80B-A3B-Thinking + type: chat + max_input_tokens: 262144 + input_price: 0.14 + output_price: 1.4 + - name: Qwen/Qwen3-235B-A22B-Thinking-2507 + type: chat + max_input_tokens: 131072 + input_price: 0.13 + output_price: 0.6 + - name: Qwen/Qwen3-Coder-480B-A35B-Instruct + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 1.6 + supports_function_calling: true + - name: Qwen/Qwen3-Coder-30B-A3B-Instruct + type: chat + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.27 + supports_function_calling: true + - name: Qwen/Qwen3-VL-8B-Instruct + type: chat + max_input_tokens: 262144 + input_price: 0.18 + output_price: 0.69 + supports_vision: true + - name: google/gemma-3-27b-it + type: chat + max_input_tokens: 131072 + input_price: 0.1 + output_price: 0.2 + - name: mistralai/Mistral-Small-3.2-24B-Instruct-2506 + type: chat + max_input_tokens: 32768 + input_price: 0.06 + output_price: 0.12 + - name: moonshotai/Kimi-K2-Instruct-0905 + type: chat + max_input_tokens: 262144 + input_price: 0.5 + output_price: 2.0 + supports_function_calling: true + - name: moonshotai/Kimi-K2-Thinking + type: chat + max_input_tokens: 262144 + input_price: 0.55 + output_price: 2.5 + supports_function_calling: true + - name: MiniMaxAI/MiniMax-M2.1 + type: chat + max_input_tokens: 196608 + input_price: 0.27 + output_price: 0.95 + supports_function_calling: true + - name: zai-org/GLM-4.6V + type: chat + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.9 + supports_vision: true + - name: zai-org/GLM-5.3-Flash + type: chat + max_input_tokens: 1048576 + input_price: 0.15 + output_price: 0.5 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: Qwen/Qwen3.8-27B + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 3.0 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - medium + - xhigh + - name: zai-org/GLM-5.3 + type: chat + max_input_tokens: 1048576 + input_price: 1.2 + output_price: 4.0 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: Qwen/Qwen3.8-2.4T-A95B + type: chat + max_input_tokens: 262144 + input_price: 2.0 + output_price: 6.0 + max_output_tokens: 131072 + supports_function_calling: true + reasoning_levels: + - low + - medium + - xhigh + - name: deepseek-ai/DeepSeek-V4-Pro-0813 + type: chat + max_input_tokens: 1048576 + input_price: 1.3 + output_price: 2.6 + max_output_tokens: 384000 + supports_function_calling: true + reasoning_levels: + - high + - max + - name: Qwen/Qwen3.8-Max + type: chat + max_input_tokens: 256000 + input_price: 1.65 + output_price: 4.951 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V4-Flash-0731 + type: chat + max_input_tokens: 1048576 + input_price: 0.08 + output_price: 0.18 + max_output_tokens: 384000 + supports_function_calling: true + - name: thinkingmachines/Inkling-Small + type: chat + max_input_tokens: 524288 + input_price: 0.45 + output_price: 1.2 + max_output_tokens: 1048576 + supports_vision: true + supports_function_calling: true + - name: moonshotai/Kimi-K3 + type: chat + max_input_tokens: 1048576 + input_price: 2.85 + output_price: 14.25 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - low + - high + - max + - name: thinkingmachines/Inkling + type: chat + max_input_tokens: 524288 + input_price: 0.95 + output_price: 4.05 + max_output_tokens: 1048576 + supports_vision: true + supports_function_calling: true + - name: tencent/Hy3 + type: chat + max_input_tokens: 262144 + input_price: 0.14 + output_price: 0.58 + max_output_tokens: 64000 + supports_function_calling: true + - name: zai-org/GLM-5.2 + type: chat + max_input_tokens: 1048576 + input_price: 0.75 + output_price: 2.4 + max_output_tokens: 32768 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - name: moonshotai/Kimi-K2.7-Code + type: chat + max_input_tokens: 262144 + input_price: 0.68 + output_price: 3.4 + max_output_tokens: 262144 + supports_vision: true + supports_function_calling: true + - name: MiniMaxAI/MiniMax-M3 + type: chat + max_input_tokens: 524288 + input_price: 0.28 + output_price: 1.1 + max_output_tokens: 512000 + supports_vision: true + supports_function_calling: true + - name: stepfun-ai/Step-3.7-Flash + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 1.15 + max_output_tokens: 256000 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3.7-Max + type: chat + max_input_tokens: 256000 + input_price: 2.5 + output_price: 7.5 + max_output_tokens: 65536 + supports_function_calling: true + - name: nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 0.8 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V4-Flash + type: chat + max_input_tokens: 1048576 + input_price: 0.09 + output_price: 0.18 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - name: deepseek-ai/DeepSeek-V4-Pro + type: chat + max_input_tokens: 1048576 + input_price: 1.3 + output_price: 2.6 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - xhigh + - name: Qwen/Qwen3.6-27B + type: chat + max_input_tokens: 262144 + input_price: 0.32 + output_price: 3.2 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: XiaomiMiMo/MiMo-V2.5 + type: chat + max_input_tokens: 262144 + input_price: 0.4 + output_price: 2.0 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: XiaomiMiMo/MiMo-V2.5-Pro + type: chat + max_input_tokens: 1048576 + input_price: 1.0 + output_price: 3.0 + max_output_tokens: 16384 + supports_function_calling: true + - name: moonshotai/Kimi-K2.6 + type: chat + max_input_tokens: 262144 + input_price: 0.75 + output_price: 3.5 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: zai-org/GLM-5.1 + type: chat + max_input_tokens: 202752 + input_price: 1.05 + output_price: 3.5 + max_output_tokens: 16384 + supports_function_calling: true + - name: google/gemma-4-26B-A4B-it + type: chat + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.34 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-31B-it + type: chat + max_input_tokens: 262144 + input_price: 0.13 + output_price: 0.38 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: google/gemma-4-E4B-it + type: chat + max_input_tokens: 131072 + input_price: 0.02 + output_price: 0.1 + max_output_tokens: 8192 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3.6-35B-A3B + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.95 + max_output_tokens: 81920 + supports_vision: true + supports_function_calling: true + - name: MiniMaxAI/MiniMax-M2.7 + type: chat + max_input_tokens: 196608 + input_price: 0.25 + output_price: 1.0 + max_output_tokens: 131072 + supports_function_calling: true + - name: Qwen/Qwen3.5-122B-A10B + type: chat + max_input_tokens: 262144 + input_price: 0.29 + output_price: 2.4 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3.5-27B + type: chat + max_input_tokens: 262144 + input_price: 0.26 + output_price: 2.6 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3.5-9B + type: chat + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.15 + max_output_tokens: 65536 + supports_vision: true + supports_function_calling: true + - name: ByteDance/Seed-2.0-code + type: chat + max_input_tokens: 256000 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 131072 + supports_vision: true + supports_function_calling: true + reasoning_levels: + - none + - low + - medium + - high + - name: ByteDance/Seed-2.0-mini + type: chat + max_input_tokens: 256000 + input_price: 0.1 + output_price: 0.4 + max_output_tokens: 32000 + supports_vision: true + supports_function_calling: true + - name: ByteDance/Seed-2.0-pro + type: chat + max_input_tokens: 256000 + input_price: 0.5 + output_price: 3.0 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: MiniMaxAI/MiniMax-M2.5 + type: chat + max_input_tokens: 196608 + input_price: 0.15 + output_price: 1.15 + max_output_tokens: 131072 + supports_function_calling: true + - name: zai-org/GLM-5 + type: chat + max_input_tokens: 202752 + input_price: 0.6 + output_price: 2.08 + max_output_tokens: 16384 + supports_function_calling: true + - name: Qwen/Qwen3.5-35B-A3B + type: chat + max_input_tokens: 262144 + input_price: 0.14 + output_price: 1.0 + max_output_tokens: 81920 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3.5-397B-A17B + type: chat + max_input_tokens: 262144 + input_price: 0.45 + output_price: 3.0 + max_output_tokens: 81920 + supports_vision: true + supports_function_calling: true + - name: moonshotai/Kimi-K2.5 + type: chat + max_input_tokens: 262144 + input_price: 0.45 + output_price: 2.25 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: zai-org/GLM-4.7-Flash + type: chat + max_input_tokens: 202752 + input_price: 0.06 + output_price: 0.4 + max_output_tokens: 16384 + supports_function_calling: true + - name: zai-org/GLM-4.7 + type: chat + max_input_tokens: 202752 + input_price: 0.4 + output_price: 1.75 + max_output_tokens: 16384 + supports_function_calling: true + - name: nvidia/Nemotron-3-Nano-30B-A3B + type: chat + max_input_tokens: 262144 + input_price: 0.05 + output_price: 0.2 + max_output_tokens: 262144 + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V3.2 + type: chat + max_input_tokens: 163840 + input_price: 0.26 + output_price: 0.38 + max_output_tokens: 64000 + supports_function_calling: true + - name: zai-org/GLM-4.6 + type: chat + max_input_tokens: 202752 + input_price: 0.5 + output_price: 2.0 + max_output_tokens: 131072 + supports_function_calling: true + - name: Qwen/Qwen3-Max + type: chat + max_input_tokens: 256000 + input_price: 1.2 + output_price: 6.0 + max_output_tokens: 65536 + supports_function_calling: true + - name: Qwen/Qwen3-VL-235B-A22B-Instruct + type: chat + max_input_tokens: 262144 + input_price: 0.2 + output_price: 0.88 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3-Next-80B-A3B-Instruct + type: chat + max_input_tokens: 262144 + input_price: 0.09 + output_price: 1.1 + max_output_tokens: 32768 + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V3.1 + type: chat + max_input_tokens: 163840 + input_price: 0.25 + output_price: 0.95 + max_output_tokens: 8192 + supports_function_calling: true + - name: openai/gpt-oss-120b + type: chat + max_input_tokens: 131072 + input_price: 0.037 + output_price: 0.17 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: openai/gpt-oss-20b + type: chat + max_input_tokens: 131072 + input_price: 0.03 + output_price: 0.14 + max_output_tokens: 16384 + supports_function_calling: true + reasoning_levels: + - low + - medium + - high + - name: nvidia/Llama-3.3-Nemotron-Super-49B-v1.5 + type: chat + max_input_tokens: 131072 + input_price: 0.4 + output_price: 0.4 + max_output_tokens: 131072 + supports_function_calling: true + - name: Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo + type: chat + max_input_tokens: 262144 + input_price: 0.3 + output_price: 1.0 + max_output_tokens: 66536 + supports_function_calling: true + - name: Qwen/Qwen3-235B-A22B-Instruct-2507 + type: chat + max_input_tokens: 262144 + input_price: 0.09 + output_price: 0.55 + max_output_tokens: 16384 + supports_function_calling: true + - name: deepseek-ai/DeepSeek-R1-0528 + type: chat + max_input_tokens: 163840 + input_price: 0.5 + output_price: 2.15 + max_output_tokens: 64000 + supports_function_calling: true + - name: Qwen/Qwen3-30B-A3B + type: chat + max_input_tokens: 40960 + input_price: 0.12 + output_price: 0.5 + max_output_tokens: 16384 + supports_function_calling: true + - name: meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8 + type: chat + max_input_tokens: 1048576 + input_price: 0.2 + output_price: 0.8 + max_output_tokens: 16384 + supports_vision: true + - name: meta-llama/Llama-4-Scout-17B-16E-Instruct + type: chat + max_input_tokens: 327680 + input_price: 0.1 + output_price: 0.3 + max_output_tokens: 16384 + supports_vision: true + supports_function_calling: true + - name: Qwen/Qwen3-32B + type: chat + max_input_tokens: 40960 + input_price: 0.08 + output_price: 0.28 + max_output_tokens: 16384 + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V3-0324 + type: chat + max_input_tokens: 163840 + input_price: 0.24 + output_price: 0.9 + max_output_tokens: 163840 + supports_function_calling: true + - name: deepseek-ai/DeepSeek-V3 + type: chat + max_input_tokens: 163840 + input_price: 0.32 + output_price: 0.89 + max_output_tokens: 8192 + supports_function_calling: true + - name: meta-llama/Llama-3.3-70B-Instruct-Turbo + type: chat + max_input_tokens: 131072 + input_price: 0.1 + output_price: 0.32 + max_output_tokens: 16384 + supports_function_calling: true - provider: jina models: - - name: jina-embeddings-v3 - type: embedding - input_price: 0 - max_tokens_per_chunk: 8192 - default_chunk_size: 2000 - max_batch_size: 100 - - name: jina-clip-v2 - type: embedding - input_price: 0 - max_tokens_per_chunk: 8192 - default_chunk_size: 1500 - max_batch_size: 100 - - name: jina-colbert-v2 - type: embedding - input_price: 0 - max_tokens_per_chunk: 8192 - default_chunk_size: 1500 - max_batch_size: 100 - - name: jina-reranker-v2-base-multilingual - type: reranker - max_input_tokens: 8192 - input_price: 0 - - name: jina-colbert-v2 - type: reranker - max_input_tokens: 8192 - input_price: 0 - -# Links: -# - https://docs.voyageai.com/docs/embeddings -# - https://docs.voyageai.com/docs/pricing -# - https://docs.voyageai.com/reference/ + - name: jina-embeddings-v3 + type: embedding + input_price: 0.0 + max_tokens_per_chunk: 8192 + default_chunk_size: 2000 + max_batch_size: 100 + - name: jina-clip-v2 + type: embedding + input_price: 0.0 + max_tokens_per_chunk: 8192 + default_chunk_size: 1500 + max_batch_size: 100 + - name: jina-colbert-v2 + type: embedding + input_price: 0.0 + max_tokens_per_chunk: 8192 + default_chunk_size: 1500 + max_batch_size: 100 + - name: jina-reranker-v2-base-multilingual + type: reranker + max_input_tokens: 8192 + input_price: 0.0 + - name: jina-colbert-v2 + type: reranker + max_input_tokens: 8192 + input_price: 0.0 - provider: voyageai models: - - name: voyage-3-large - type: embedding - max_input_tokens: 120000 - input_price: 0.18 - max_tokens_per_chunk: 32000 - default_chunk_size: 2000 - max_batch_size: 128 - - name: voyage-3 - type: embedding - max_input_tokens: 320000 - input_price: 0.06 - max_tokens_per_chunk: 32000 - default_chunk_size: 2000 - max_batch_size: 128 - - name: voyage-3-lite - type: embedding - max_input_tokens: 1000000 - input_price: 0.02 - max_tokens_per_chunk: 32000 - default_chunk_size: 1000 - max_batch_size: 128 - - name: rerank-2 - type: reranker - max_input_tokens: 16000 - input_price: 0.05 - - name: rerank-2-lite - type: reranker - max_input_tokens: 8000 - input_price: 0.02 + - name: voyage-3-large + type: embedding + max_input_tokens: 120000 + input_price: 0.18 + max_tokens_per_chunk: 32000 + default_chunk_size: 2000 + max_batch_size: 128 + - name: voyage-3 + type: embedding + max_input_tokens: 320000 + input_price: 0.06 + max_tokens_per_chunk: 32000 + default_chunk_size: 2000 + max_batch_size: 128 + - name: voyage-3-lite + type: embedding + max_input_tokens: 1000000 + input_price: 0.02 + max_tokens_per_chunk: 32000 + default_chunk_size: 1000 + max_batch_size: 128 + - name: rerank-2 + type: reranker + max_input_tokens: 16000 + input_price: 0.05 + - name: rerank-2-lite + type: reranker + max_input_tokens: 8000 + input_price: 0.02 diff --git a/quirks.yaml b/quirks.yaml index 65b2653..75e68c0 100644 --- a/quirks.yaml +++ b/quirks.yaml @@ -9,6 +9,11 @@ # entries are grouped under a `# legacy — pruning candidates` comment. - provider: openai + exclude: + - "text-embedding-*" + - "gpt-image-*" + - "chatgpt-image-*" + - "gpt-realtime*" rules: - match: "gpt-5.6-*" default_reasoning_effort: medium @@ -118,8 +123,39 @@ max_tokens: null temperature: null top_p: null + - name: chat-latest + max_input_tokens: 400000 + max_output_tokens: 128000 + input_price: 5 + output_price: 30 + supports_vision: true + supports_function_calling: true + - name: gpt-5.1-chat-latest + max_input_tokens: 400000 + max_output_tokens: 128000 + input_price: 1.25 + output_price: 10 + supports_vision: true + supports_function_calling: true + reasoning_levels: [none, low, medium, high] + default_reasoning_effort: none + - name: gpt-5-chat-latest + max_input_tokens: 400000 + max_output_tokens: 128000 + input_price: 1.25 + output_price: 10 + supports_vision: true + supports_function_calling: true + reasoning_levels: [minimal, low, medium, high] + default_reasoning_effort: medium - provider: gemini + exclude: + - "gemini-embedding-*" + - "veo-*" + - "*-tts" + - "*-tts-preview" + - "*-image-preview" rules: - match: "gemini-3.6-flash" default_reasoning_effort: medium @@ -135,6 +171,7 @@ default_reasoning_effort: high - match: "gemini-2.5-pro" default_reasoning_effort: high + reasoning_levels: [low, medium, high] models: - name: text-embedding-004 type: embedding @@ -142,6 +179,18 @@ max_tokens_per_chunk: 2048 default_chunk_size: 1500 max_batch_size: 100 + # legacy — pruning candidates + - name: gemini-3-pro-preview + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, high] + default_reasoning_effort: high + - name: gemma-3-27b-it + max_input_tokens: 131072 + max_output_tokens: 8192 + input_price: 0 + output_price: 0 - provider: claude rules: @@ -246,8 +295,28 @@ thinking: type: enabled budget_tokens: 16000 + models: + # legacy — pruning candidates + - name: claude-opus-4-1-20250805 + max_input_tokens: 200000 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 15 + output_price: 75 + supports_vision: true + supports_function_calling: true + - name: claude-opus-4-20250514 + max_input_tokens: 200000 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 15 + output_price: 75 + supports_vision: true + supports_function_calling: true - provider: mistral + exclude: + - "voxtral-*" models: - name: mistral-embed type: embedding @@ -255,6 +324,21 @@ input_price: 0.1 max_tokens_per_chunk: 8092 default_chunk_size: 2000 + # legacy — pruning candidates + - name: magistral-small-latest + max_input_tokens: 131072 + input_price: 0.5 + output_price: 1.5 + - name: devstral-small-latest + max_input_tokens: 262144 + input_price: 0.1 + output_price: 0.3 + supports_function_calling: true + - name: ministral-14b-latest + max_input_tokens: 262144 + input_price: 0.2 + output_price: 0.2 + supports_function_calling: true - provider: ai21 models: @@ -300,6 +384,8 @@ max_input_tokens: 4096 - provider: xai + exclude: + - "grok-imagine-*" oauth: client_id: b1a00492-073a-47ea-816f-4c329264a828 authorize_url: https://auth.x.ai/oauth2/authorize @@ -337,8 +423,71 @@ output_price: 2.5 max_input_tokens: 1000000 supports_function_calling: true + - name: grok-4-1-fast-non-reasoning + max_input_tokens: 1000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: grok-4-1-fast-reasoning + max_input_tokens: 1000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: grok-code-fast-1 + max_input_tokens: 256000 + input_price: 0.2 + output_price: 1.5 + supports_function_calling: true + - name: grok-3 + max_input_tokens: 131072 + input_price: 3 + output_price: 15 + supports_function_calling: true + - name: grok-3-mini + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.5 + supports_function_calling: true + +- provider: perplexity + models: + # legacy — pruning candidates + - name: sonar-reasoning + max_input_tokens: 128000 + input_price: 1 + output_price: 5 + +- provider: groq + exclude: + - "whisper-*" + models: + # legacy — pruning candidates + - name: meta-llama/llama-4-maverick-17b-128e-instruct + max_input_tokens: 131072 + input_price: 0 + output_price: 0 + supports_vision: true + supports_function_calling: true + - name: meta-llama/llama-4-scout-17b-16e-instruct + max_input_tokens: 131072 + input_price: 0 + output_price: 0 + supports_vision: true + supports_function_calling: true + - name: moonshotai/kimi-k2-instruct-0905 + max_input_tokens: 262144 + input_price: 0 + output_price: 0 + supports_function_calling: true + - name: qwen/qwen3-32b + max_input_tokens: 131072 + input_price: 0 + output_price: 0 - provider: vertexai + exclude: + - "gemini-embedding-*" + - "*-tts" rules: - match: "claude-*" require_max_tokens: true @@ -354,8 +503,10 @@ default_reasoning_effort: high - match: "gemini-2.5-flash" default_reasoning_effort: medium + reasoning_levels: [low, medium, high] - match: "gemini-2.5-pro" default_reasoning_effort: high + reasoning_levels: [low, medium, high] - match: "claude-opus-5" default_reasoning_effort: high - match: "claude-fable-*" @@ -449,6 +600,109 @@ max_tokens_per_chunk: 2048 default_chunk_size: 1500 max_batch_size: 5 + # legacy — pruning candidates + - name: gemini-3-pro-preview + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, high] + default_reasoning_effort: high + - name: claude-opus-5 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_function_calling: true + supports_vision: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-fable-5-1 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 10 + output_price: 50 + supports_function_calling: true + supports_vision: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-fable-5 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 10 + output_price: 50 + supports_function_calling: true + supports_vision: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-opus-4-8 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-opus-4-7 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-opus-4-6 + max_input_tokens: 200000 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_vision: true + supports_function_calling: true + - name: claude-sonnet-5 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 3 + output_price: 15 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: claude-sonnet-4-6 + max_input_tokens: 200000 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 3 + output_price: 15 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, max] + default_reasoning_effort: high + - name: claude-3-5-haiku@20241022 + max_input_tokens: 200000 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 0.8 + output_price: 4 + supports_vision: true + supports_function_calling: true + - name: mistral-small-2503 + max_input_tokens: 32000 + input_price: 0.1 + output_price: 0.3 + supports_function_calling: true + - name: codestral-2501 + max_input_tokens: 256000 + input_price: 0.3 + output_price: 0.9 + supports_function_calling: true - provider: bedrock rules: @@ -559,6 +813,36 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 96 + # legacy — pruning candidates + - name: us.meta.llama3-3-70b-instruct-v1:0 + max_input_tokens: 131072 + max_output_tokens: 8192 + require_max_tokens: true + input_price: 0.72 + output_price: 0.72 + supports_function_calling: true + - name: us.amazon.nova-premier-v1:0 + max_input_tokens: 300000 + max_output_tokens: 5120 + input_price: 2.5 + output_price: 12.5 + - name: us.amazon.nova-pro-v1:0 + max_input_tokens: 300000 + max_output_tokens: 5120 + input_price: 0.8 + output_price: 3.2 + supports_vision: true + - name: us.amazon.nova-lite-v1:0 + max_input_tokens: 300000 + max_output_tokens: 5120 + input_price: 0.06 + output_price: 0.24 + supports_vision: true + - name: us.amazon.nova-micro-v1:0 + max_input_tokens: 128000 + max_output_tokens: 5120 + input_price: 0.035 + output_price: 0.14 - provider: cloudflare rules: @@ -571,6 +855,13 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 100 + # legacy — pruning candidates + - name: '@cf/google/gemma-3-12b-it' + max_input_tokens: 131072 + max_output_tokens: 2048 + require_max_tokens: true + input_price: 0 + output_price: 0 - provider: ernie models: @@ -609,6 +900,8 @@ input_price: 0.07 - provider: qianwen + exclude: + - "*-realtime" rules: - match: "qwen3.5-plus" patch: @@ -646,6 +939,40 @@ max_tokens_per_chunk: 8192 default_chunk_size: 2000 max_batch_size: 10 + # legacy — pruning candidates + - name: qwen3-vl-flash + max_input_tokens: 262144 + supports_vision: true + - name: qwen3-235b-a22b-instruct-2507 + max_input_tokens: 131072 + input_price: 0.28 + output_price: 1.12 + supports_function_calling: true + - name: qwen3-235b-a22b-thinking-2507 + max_input_tokens: 131072 + input_price: 0.28 + output_price: 2.8 + - name: qwen3-30b-a3b-instruct-2507 + max_input_tokens: 131072 + input_price: 0.105 + output_price: 0.42 + supports_function_calling: true + - name: qwen3-30b-a3b-thinking-2507 + max_input_tokens: 131072 + input_price: 0.105 + output_price: 1.05 + - name: qwen3-vl-32b-instruct + max_input_tokens: 131072 + input_price: 0.28 + output_price: 1.12 + supports_vision: true + - name: qwen3-vl-8b-instruct + max_input_tokens: 131072 + input_price: 0.07 + output_price: 0.28 + supports_vision: true + - name: qwen3-coder-next + max_input_tokens: 262144 - provider: hunyuan models: @@ -671,6 +998,21 @@ default_chunk_size: 1000 max_batch_size: 100 +- provider: deepseek + models: + # legacy — pruning candidates + - name: deepseek-chat + max_input_tokens: 64000 + max_output_tokens: 8192 + input_price: 0.56 + output_price: 1.68 + supports_function_calling: true + - name: deepseek-reasoner + max_input_tokens: 64000 + max_output_tokens: 32768 + input_price: 0.56 + output_price: 1.68 + - provider: zhipuai variants: - match: "glm-5" @@ -698,6 +1040,46 @@ type: reranker max_input_tokens: 4096 input_price: 0.112 + # legacy — pruning candidates + - name: glm-4.6v-flash + max_input_tokens: 65536 + input_price: 0 + output_price: 0 + supports_vision: true + +- provider: minimax + models: + # legacy — pruning candidates + - name: minimax-m3 + max_input_tokens: 1000000 + input_price: 4.2 + output_price: 16.8 + supports_function_calling: true + - name: minimax-m2.7 + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.5 + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.5-highspeed + max_input_tokens: 204800 + input_price: 0.588 + output_price: 2.352 + supports_function_calling: true + - name: minimax-m2.1 + max_input_tokens: 204800 + input_price: 0.294 + output_price: 1.176 + supports_function_calling: true + - name: minimax-m2.1-highspeed + max_input_tokens: 204800 + input_price: 0.588 + output_price: 2.352 + supports_function_calling: true - provider: openrouter rules: @@ -705,54 +1087,76 @@ require_max_tokens: true - match: "openai/gpt-5.6-*" default_reasoning_effort: medium + reasoning_levels: [none, low, medium, high, xhigh, max] - match: "openai/gpt-5.5" default_reasoning_effort: medium + reasoning_levels: [none, low, medium, high, xhigh] - match: "openai/gpt-5.5-pro" default_reasoning_effort: high + reasoning_levels: [medium, high, xhigh] - match: "openai/gpt-5.4" default_reasoning_effort: none + reasoning_levels: [none, low, medium, high, xhigh] - match: "openai/gpt-5.4-pro" default_reasoning_effort: medium + reasoning_levels: [medium, high, xhigh] - match: "openai/gpt-5.4-mini" default_reasoning_effort: none + reasoning_levels: [none, low, medium, high, xhigh] - match: "openai/gpt-5.4-nano" default_reasoning_effort: none + reasoning_levels: [none, low, medium, high, xhigh] - match: "openai/gpt-5.3-codex" default_reasoning_effort: medium + reasoning_levels: [low, medium, high, xhigh] - match: "openai/gpt-5.2" default_reasoning_effort: none + reasoning_levels: [none, low, medium, high, xhigh] - match: "openai/gpt-5.2-pro" default_reasoning_effort: medium + reasoning_levels: [medium, high, xhigh] - match: "openai/gpt-5" default_reasoning_effort: medium + reasoning_levels: [minimal, low, medium, high] - match: "google/gemini-3.5-flash" default_reasoning_effort: medium + reasoning_levels: [minimal, low, medium, high] - match: "google/gemini-3-flash-preview" default_reasoning_effort: high + reasoning_levels: [minimal, low, medium, high] - match: "google/gemini-3.1-flash-lite" default_reasoning_effort: minimal + reasoning_levels: [minimal, low, medium, high] - match: "google/gemini-3.1-pro-preview" default_reasoning_effort: high + reasoning_levels: [low, medium, high] - match: "google/gemini-3-pro-preview" default_reasoning_effort: high - match: "google/gemini-2.5-flash" default_reasoning_effort: low + reasoning_levels: [low, medium, high] - match: "google/gemini-2.5-pro" default_reasoning_effort: high + reasoning_levels: [low, medium, high] - match: "anthropic/claude-opus-5" default_reasoning_effort: high + reasoning_levels: [low, medium, high, xhigh, max] - match: "anthropic/claude-fable-*" default_reasoning_effort: high + reasoning_levels: [low, medium, high, xhigh, max] - match: "anthropic/claude-opus-4-8" default_reasoning_effort: high - match: "anthropic/claude-opus-4-7" default_reasoning_effort: high - match: "anthropic/claude-opus-4.6" default_reasoning_effort: high + reasoning_levels: [low, medium, high, max] - match: "anthropic/claude-sonnet-5" default_reasoning_effort: high + reasoning_levels: [low, medium, high, xhigh, max] - match: "anthropic/claude-sonnet-4.6" default_reasoning_effort: high + reasoning_levels: [low, medium, high, max] - match: "perplexity/sonar-reasoning-pro" patch: body: @@ -761,9 +1165,105 @@ patch: body: include_reasoning: true + models: + # legacy — pruning candidates + - name: google/gemini-3-pro-preview + max_input_tokens: 1048576 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, high] + default_reasoning_effort: high + - name: google/gemini-2.0-flash-001 + max_input_tokens: 1000000 + input_price: 0.15 + output_price: 0.6 + supports_vision: true + supports_function_calling: true + - name: google/gemini-2.0-flash-lite-001 + max_input_tokens: 1048576 + input_price: 0.075 + output_price: 0.3 + supports_vision: true + supports_function_calling: true + - name: anthropic/claude-fable-5-1 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 10 + output_price: 50 + supports_function_calling: true + supports_vision: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: anthropic/claude-opus-4-8 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: anthropic/claude-opus-4-7 + max_input_tokens: 1000000 + max_output_tokens: 128000 + require_max_tokens: true + input_price: 5 + output_price: 25 + supports_vision: true + supports_function_calling: true + reasoning_levels: [low, medium, high, xhigh, max] + default_reasoning_effort: high + - name: mistralai/devstral-small + max_input_tokens: 131072 + input_price: 0.07 + output_price: 0.28 + supports_function_calling: true + - name: ai21/jamba-large-1.7 + max_input_tokens: 256000 + input_price: 2 + output_price: 8 + supports_function_calling: true + - name: x-ai/grok-4.1-fast + max_input_tokens: 2000000 + input_price: 0.2 + output_price: 0.5 + supports_function_calling: true + - name: x-ai/grok-code-fast-1 + max_input_tokens: 256000 + input_price: 0.2 + output_price: 1.5 + supports_function_calling: true +# Fully hand-owned: upstream catalogs track GitHub Copilot ids, which do not +# match the GitHub Models API ids coyote uses. - provider: github models: + - name: gpt-5 + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-5-mini + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-5-nano + max_input_tokens: 400000 + max_output_tokens: 128000 + supports_vision: true + supports_function_calling: true + - name: gpt-4.1 + max_input_tokens: 1047576 + max_output_tokens: 32768 + supports_vision: true + supports_function_calling: true + - name: gpt-4o + max_input_tokens: 128000 + max_output_tokens: 16384 + supports_function_calling: true - name: text-embedding-3-large type: embedding max_tokens_per_chunk: 8191 @@ -774,6 +1274,23 @@ max_tokens_per_chunk: 8191 default_chunk_size: 2000 max_batch_size: 100 + - name: llama-4-maverick-17b-128e-instruct-fp8 + max_input_tokens: 1048576 + supports_vision: true + - name: llama-4-scout-17b-16e-instruct + max_input_tokens: 327680 + supports_vision: true + - name: llama-3.3-70b-instruct + max_input_tokens: 131072 + - name: mistral-medium-2505 + max_input_tokens: 131072 + supports_function_calling: true + - name: mistral-small-2503 + max_input_tokens: 131072 + supports_function_calling: true + - name: codestral-2501 + max_input_tokens: 256000 + supports_function_calling: true - name: cohere-embed-v3-english type: embedding max_tokens_per_chunk: 512 @@ -784,6 +1301,24 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 96 + - name: deepseek-r1-0528 + max_input_tokens: 163840 + - name: deepseek-v3-0324 + max_input_tokens: 163840 + - name: mai-ds-r1 + max_input_tokens: 163840 + - name: phi-4 + max_input_tokens: 16384 + - name: phi-4-mini-instruct + max_input_tokens: 131072 + - name: phi-4-reasoning + max_input_tokens: 33792 + - name: phi-4-mini-reasoning + max_input_tokens: 131072 + - name: grok-3 + max_input_tokens: 131072 + - name: grok-3-mini + max_input_tokens: 131072 - provider: deepinfra models: @@ -817,6 +1352,63 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 100 + # legacy — pruning candidates + - name: Qwen/Qwen3-Max-Thinking + max_input_tokens: 262144 + input_price: 1.2 + output_price: 6 + supports_function_calling: true + - name: Qwen/Qwen3-Next-80B-A3B-Thinking + max_input_tokens: 262144 + input_price: 0.14 + output_price: 1.4 + - name: Qwen/Qwen3-235B-A22B-Thinking-2507 + max_input_tokens: 131072 + input_price: 0.13 + output_price: 0.6 + - name: Qwen/Qwen3-Coder-480B-A35B-Instruct + max_input_tokens: 131072 + input_price: 0.4 + output_price: 1.6 + supports_function_calling: true + - name: Qwen/Qwen3-Coder-30B-A3B-Instruct + max_input_tokens: 262144 + input_price: 0.07 + output_price: 0.27 + supports_function_calling: true + - name: Qwen/Qwen3-VL-8B-Instruct + max_input_tokens: 262144 + input_price: 0.18 + output_price: 0.69 + supports_vision: true + - name: google/gemma-3-27b-it + max_input_tokens: 131072 + input_price: 0.1 + output_price: 0.2 + - name: mistralai/Mistral-Small-3.2-24B-Instruct-2506 + max_input_tokens: 32768 + input_price: 0.06 + output_price: 0.12 + - name: moonshotai/Kimi-K2-Instruct-0905 + max_input_tokens: 262144 + input_price: 0.5 + output_price: 2.0 + supports_function_calling: true + - name: moonshotai/Kimi-K2-Thinking + max_input_tokens: 262144 + input_price: 0.55 + output_price: 2.5 + supports_function_calling: true + - name: MiniMaxAI/MiniMax-M2.1 + max_input_tokens: 196608 + input_price: 0.27 + output_price: 0.95 + supports_function_calling: true + - name: zai-org/GLM-4.6V + max_input_tokens: 131072 + input_price: 0.3 + output_price: 0.9 + supports_vision: true - provider: jina models: diff --git a/scripts/requirements.txt b/scripts/requirements.txt deleted file mode 100644 index 69a538b..0000000 --- a/scripts/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests -ruamel.yaml diff --git a/scripts/update_models.py b/scripts/update_models.py deleted file mode 100644 index fab2014..0000000 --- a/scripts/update_models.py +++ /dev/null @@ -1,255 +0,0 @@ -import requests -import sys -import re -import json - -# Provider mapping from models.yaml to OpenRouter prefixes -PROVIDER_MAPPING = { - "openai": "openai", - "claude": "anthropic", - "gemini": "google", - "mistral": "mistralai", - "cohere": "cohere", - "perplexity": "perplexity", - "xai": "x-ai", - "openrouter": "openrouter", - "ai21": "ai21", - "deepseek": "deepseek", - "moonshot": "moonshotai", - "qianwen": "qwen", - "zhipuai": "zhipuai", - "minimax": "minimax", - "vertexai": "google", - "groq": "groq", - "bedrock": "amazon", - "hunyuan": "tencent", - "ernie": "baidu", - "github": "github", -} - -def fetch_openrouter_models(): - print("Fetching models from OpenRouter...") - try: - response = requests.get("https://openrouter.ai/api/v1/models") - response.raise_for_status() - data = response.json()["data"] - print(f"Fetched {len(data)} models.") - return data - except Exception as e: - print(f"Error fetching models: {e}") - sys.exit(1) - -def get_openrouter_model(models_data, provider_prefix, model_name, is_openrouter_provider=False): - if is_openrouter_provider: - # For openrouter provider, the model_name in yaml is usually the full ID - for model in models_data: - if model["id"] == model_name: - return model - return None - - expected_id = f"{provider_prefix}/{model_name}" - - # 1. Try exact match on ID - for model in models_data: - if model["id"] == expected_id: - return model - - # 2. Try match by suffix - for model in models_data: - if model["id"].split("/")[-1] == model_name: - if model["id"].startswith(f"{provider_prefix}/"): - return model - - return None - -def format_price(price_per_token): - if price_per_token is None: - return None - try: - price_per_1m = float(price_per_token) * 1_000_000 - if price_per_1m.is_integer(): - return str(int(price_per_1m)) - else: - return str(round(price_per_1m, 4)) - except: - return None - -def get_indentation(line): - return len(line) - len(line.lstrip()) - -def process_model_block(block_lines, current_provider, or_models): - if not block_lines: - return [] - - # 1. Identify model name and indentation - name_line = block_lines[0] - name_match = re.match(r"^(\s*)-\s*name:\s*(.+)$", name_line) - if not name_match: - return block_lines - - name_indent_str = name_match.group(1) - model_name = name_match.group(2).strip() - - # 2. Find OpenRouter model - or_prefix = PROVIDER_MAPPING.get(current_provider) - is_openrouter_provider = (current_provider == "openrouter") - - if not or_prefix and not is_openrouter_provider: - return block_lines - - or_model = get_openrouter_model(or_models, or_prefix, model_name, is_openrouter_provider) - if not or_model: - return block_lines - - print(f" Updating {model_name}...") - - # 3. Prepare updates - updates = {} - - # Pricing - pricing = or_model.get("pricing", {}) - p_in = format_price(pricing.get("prompt")) - p_out = format_price(pricing.get("completion")) - if p_in: updates["input_price"] = p_in - if p_out: updates["output_price"] = p_out - - # Context - ctx = or_model.get("context_length") - if ctx: updates["max_input_tokens"] = str(ctx) - - max_out = None - if "top_provider" in or_model and or_model["top_provider"]: - max_out = or_model["top_provider"].get("max_completion_tokens") - if max_out: updates["max_output_tokens"] = str(max_out) - - # Capabilities - arch = or_model.get("architecture", {}) - modality = arch.get("modality", "") - if "image" in modality: - updates["supports_vision"] = "true" - - # 4. Detect field indentation - field_indent_str = None - existing_fields = {} # key -> line_index - - for i, line in enumerate(block_lines): - if i == 0: continue # Skip name line - - # Skip comments - if line.strip().startswith("#"): - continue - - # Look for "key: value" - m = re.match(r"^(\s*)([\w_-]+):", line) - if m: - indent = m.group(1) - key = m.group(2) - # Must be deeper than name line - if len(indent) > len(name_indent_str): - if field_indent_str is None: - field_indent_str = indent - existing_fields[key] = i - - if field_indent_str is None: - field_indent_str = name_indent_str + " " - - # 5. Apply updates - new_block = list(block_lines) - - # Update existing fields - for key, value in updates.items(): - if key in existing_fields: - idx = existing_fields[key] - # Preserve original key indentation exactly - original_line = new_block[idx] - m = re.match(r"^(\s*)([\w_-]+):", original_line) - if m: - current_indent = m.group(1) - new_block[idx] = f"{current_indent}{key}: {value}\n" - - # Insert missing fields - # Insert after the name line - insertion_idx = 1 - - for key, value in updates.items(): - if key not in existing_fields: - new_line = f"{field_indent_str}{key}: {value}\n" - new_block.insert(insertion_idx, new_line) - insertion_idx += 1 - - return new_block - -def main(): - or_models = fetch_openrouter_models() - - print("Reading models.yaml...") - with open("models.yaml", "r") as f: - lines = f.readlines() - - new_lines = [] - current_provider = None - - i = 0 - while i < len(lines): - line = lines[i] - - # Check for provider - # - provider: name - p_match = re.match(r"^\s*-?\s*provider:\s*(.+)$", line) - if p_match: - current_provider = p_match.group(1).strip() - new_lines.append(line) - i += 1 - continue - - # Check for model start - # - name: ... - m_match = re.match(r"^(\s*)-\s*name:\s*.+$", line) - if m_match: - # Start of a model block - start_indent = len(m_match.group(1)) - - # Collect block lines - block_lines = [line] - j = i + 1 - while j < len(lines): - next_line = lines[j] - stripped = next_line.strip() - - # If empty or comment, include it - if not stripped or stripped.startswith("#"): - block_lines.append(next_line) - j += 1 - continue - - # Check indentation - next_indent = get_indentation(next_line) - - # If indentation is greater, it's part of the block (property) - if next_indent > start_indent: - block_lines.append(next_line) - j += 1 - continue - - # If indentation is equal or less, it's the end of the block - break - - # Process the block - processed_block = process_model_block(block_lines, current_provider, or_models) - new_lines.extend(processed_block) - - # Advance i - i = j - continue - - # Otherwise, just a regular line - new_lines.append(line) - i += 1 - - print("Saving models.yaml...") - with open("models.yaml", "w") as f: - f.writelines(new_lines) - print("Done.") - -if __name__ == "__main__": - main() diff --git a/src/cli/mod.rs b/src/cli/mod.rs index fbd3d92..f53658b 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -12,6 +12,7 @@ use clap_complete::ArgValueCompleter; use is_terminal::IsTerminal; use std::collections::HashSet; use std::io::{Read, stdin}; +use std::path::PathBuf; #[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)] pub enum McpTransportArg { @@ -53,7 +54,7 @@ pub enum McpScopeArg { "install_builtins", "sync_models", "list_models", "list_roles", "list_sessions", "list_agents", "list_rags", "list_macros", "list_skills", "list_bundles", "skill", "tail_logs", "completions", - "update", "update_bundle", "uninstall", + "update", "update_bundle", "uninstall", "generate_models", ]) ), group( @@ -253,6 +254,15 @@ pub struct Cli { /// Sync models updates #[arg(long, help_heading = "Installation & Updates")] pub sync_models: bool, + /// Regenerate the models catalog file from live API data + #[arg( + long, + hide = true, + value_name = "PATH", + num_args = 0..=1, + default_missing_value = "models.yaml" + )] + pub generate_models: Option, /// Update Coyote to the latest release, or to a specific version #[arg(long, value_name = "VERSION", help_heading = "Installation & Updates")] pub update: Option>, @@ -810,6 +820,19 @@ mod tests { assert!(cli.sync_models); } + #[test] + fn parse_generate_models_flag() { + assert_eq!(parse(&[]).generate_models, None); + assert_eq!( + parse(&["--generate-models"]).generate_models, + Some(PathBuf::from("models.yaml")) + ); + assert_eq!( + parse(&["--generate-models", "out.yaml"]).generate_models, + Some(PathBuf::from("out.yaml")) + ); + } + #[test] fn parse_model_with_role() { let cli = parse(&["-m", "gpt-4o", "-r", "coder"]); diff --git a/src/client/catalog.rs b/src/client/catalog.rs index 4d18162..cec2d46 100644 --- a/src/client/catalog.rs +++ b/src/client/catalog.rs @@ -13,7 +13,6 @@ use std::sync::LazyLock; pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); -#[allow(dead_code)] pub static ALL_QUIRKS: LazyLock> = LazyLock::new(|| serde_yaml::from_str(QUIRKS_YAML).expect("quirks.yaml must be valid")); @@ -133,8 +132,8 @@ const PROVIDER_SOURCES: [SourceIds; 24] = [ }, SourceIds { provider: "github", - modelsdev: Some("github-copilot"), - openrouter: Some("github"), + modelsdev: None, + openrouter: None, }, SourceIds { provider: "deepinfra", @@ -162,7 +161,6 @@ pub struct FetchedSources { /// Fetches both upstream catalogs concurrently. A failed fetch is logged and /// surfaces as `None` rather than an error. -#[allow(dead_code)] pub async fn fetch_sources() -> FetchedSources { let (modelsdev, openrouter) = tokio::join!(fetch(MODELSDEV_API_URL), fetch(OPENROUTER_API_URL)); FetchedSources { @@ -478,7 +476,6 @@ pub fn build_catalog(sources: &FetchedSources, quirks: &[ProviderQuirks]) -> Cat /// Like [`build_catalog`], but errors unless every provider was assembled /// from live API data (or is quirks-owned by design). -#[allow(dead_code)] pub fn build_catalog_strict( sources: &FetchedSources, quirks: &[ProviderQuirks], @@ -494,6 +491,32 @@ pub fn build_catalog_strict( Ok(build.providers) } +const GENERATED_HEADER: &str = "# GENERATED by 'coyote --generate-models' — DO NOT hand-edit model data.\n\ +# Hand-owned behavior lives in quirks.yaml; this file is the embedded offline fallback.\n"; + +/// Builds the catalog from live API data and writes it to `path`. Both +/// upstream sources must respond and every provider must fully resolve; +/// identical source data always produces identical bytes. +pub async fn generate_models_file(path: &std::path::Path) -> Result<()> { + let sources = fetch_sources().await; + if sources.modelsdev.is_none() || sources.openrouter.is_none() { + bail!( + "generating the models file requires both catalog sources; see the log for the failed fetch" + ); + } + let providers = build_catalog_strict(&sources, &ALL_QUIRKS)?; + let model_count: usize = providers.iter().map(|provider| provider.models.len()).sum(); + let yaml = serde_yaml::to_string(&providers)?; + std::fs::write(path, format!("{GENERATED_HEADER}{yaml}"))?; + println!( + "Wrote {} models across {} providers to {}", + model_count, + providers.len(), + path.display() + ); + Ok(()) +} + /// Fraction of `old` names that also appear in `new`; empty `old` yields 1.0. /// Used to guard against upstream id-shape drift: a low overlap means the /// upstream catalog renamed its models out from under us. @@ -1333,6 +1356,8 @@ rules: models: [{name: e}] - provider: hunyuan models: [{name: h}] +- provider: github + models: [{name: g}] - provider: jina models: [{name: j}] - provider: voyageai @@ -1358,6 +1383,96 @@ rules: assert_eq!(name_overlap(&old, &[]), 0.0); } + fn embedded_catalog() -> Vec { + serde_yaml::from_str(crate::client::MODELS_YAML).unwrap() + } + + fn embedded_provider<'a>(providers: &'a [ProviderModels], name: &str) -> &'a ProviderModels { + providers + .iter() + .find(|provider| provider.provider == name) + .unwrap_or_else(|| panic!("provider '{name}' missing from models.yaml")) + } + + #[test] + fn embedded_catalog_invariants() { + let providers = embedded_catalog(); + assert!(providers.len() >= 24); + let order: Vec<&str> = providers + .iter() + .map(|provider| provider.provider.as_str()) + .collect(); + assert_eq!(order, KNOWN_PROVIDERS); + + assert!(embedded_provider(&providers, "xai").oauth.is_some()); + for name in ["jina", "voyageai"] { + assert!( + embedded_provider(&providers, name) + .models + .iter() + .any(|model| model.model_type == "embedding"), + "{name} lost its embedding models" + ); + } + assert!( + embedded_provider(&providers, "cohere") + .models + .iter() + .any(|model| model.model_type == "reranker"), + "cohere lost its reranker models" + ); + + for provider in &providers { + for model in &provider.models { + // A :thinking model without a patch must at least alias a real + // wire name (qianwen twins invert a base-model patch instead). + if model.name.ends_with(":thinking") { + assert!( + model.patch.is_some() || model.real_name.is_some(), + "{}:{} has neither patch nor real_name", + provider.provider, + model.name + ); + } + if model.model_type == "chat" { + let serialized = serde_yaml::to_value(model).unwrap(); + assert!( + serialized + .as_mapping() + .unwrap() + .keys() + .any(|key| !matches!(key.as_str(), Some("name" | "type"))), + "{}:{} is an all-default chat entry", + provider.provider, + model.name + ); + } + } + } + + for model in &embedded_provider(&providers, "claude").models { + if model.model_type == "chat" { + assert!( + model.require_max_tokens, + "claude:{} is missing require_max_tokens", + model.name + ); + } + } + } + + #[test] + fn embedded_catalog_roundtrip() { + let providers = embedded_catalog(); + let yaml = serde_yaml::to_string(&providers).unwrap(); + let reparsed: Vec = serde_yaml::from_str(&yaml).unwrap(); + assert_eq!(reparsed.len(), providers.len()); + for (reparsed, original) in reparsed.iter().zip(&providers) { + assert_eq!(reparsed.provider, original.provider); + assert_eq!(reparsed.models.len(), original.models.len()); + } + } + /// Guards against upstream id-shape drift by comparing live API model /// names against the embedded models.yaml. Run manually: /// `cargo test idshape_guard_live -- --ignored --nocapture` diff --git a/src/client/model.rs b/src/client/model.rs index 2c84ec8..cbd9623 100644 --- a/src/client/model.rs +++ b/src/client/model.rs @@ -358,7 +358,7 @@ impl ModelData { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProviderModels { pub provider: String, - #[serde(default)] + #[serde(default, skip_serializing_if = "Option::is_none")] pub oauth: Option, pub models: Vec, } diff --git a/src/main.rs b/src/main.rs index 9309149..605c689 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,7 @@ extern crate log; use crate::cli::Cli; use crate::client::{ - ModelType, call_chat_completions, call_chat_completions_streaming, list_models, oauth, + ModelType, call_chat_completions, call_chat_completions_streaming, catalog, list_models, oauth, }; use crate::config::instructions::WORKSPACE_INSTRUCTIONS_FILE_NAME; use crate::config::{ @@ -101,6 +101,7 @@ async fn main() -> Result<()> { let info_flag = cli.info || cli.sync_models + || cli.generate_models.is_some() || cli.list_models || cli.list_roles || cli.list_agents @@ -295,6 +296,10 @@ async fn run( return sync_models(&url, abort_signal.clone()).await; } + if let Some(path) = &cli.generate_models { + return catalog::generate_models_file(path).await; + } + if cli.list_models { for model in list_models(ctx.app.config.as_ref(), ModelType::Chat) { println!("{}", model.id()); From f1cb6589bc32d752cc13a0f17fcc2cdcdbc78aed Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 14:34:05 -0600 Subject: [PATCH 14/30] feat: refresh the model catalog from live API data at runtime --- assets/config-template.yaml | 7 +- assets/sbx-kit/spec.yaml | 1 + config.example.yaml | 9 +- src/client/catalog.rs | 138 ++++++++++++++++++- src/config/app_config.rs | 85 +++++++++++- src/config/mod.rs | 265 +++++++++++++++++++++++++++++++++--- src/main.rs | 6 +- 7 files changed, 481 insertions(+), 30 deletions(-) diff --git a/assets/config-template.yaml b/assets/config-template.yaml index 02ca30f..cd18c10 100644 --- a/assets/config-template.yaml +++ b/assets/config-template.yaml @@ -244,8 +244,13 @@ right_prompt: null # If null, uses the built-in default: # ---- Miscellaneous ---- user_agent: null # Set User-Agent HTTP header, use `auto` for coyote/ save_shell_history: true # Whether to save shell execution command to the history file -sync_models_url: null # URL to sync model changes from. If null, uses the built-in default: +sync_models_url: null # Where `--sync-models` and the automatic refresh get model data from. + # If null, fetches fresh model data from models.dev + OpenRouter. + # Set a URL to mirror a full models.yaml instead, e.g.: # https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml +sync_models_auto: true # Whether to refresh the model catalog in the background on REPL startup + # once it goes stale +sync_models_ttl_hours: 24 # Hours before the synced model catalog is considered stale # ---- Clients ---- # See the [Clients documentation](https://github.com/Dark-Alex-17/coyote/wiki/Clients) for more details diff --git a/assets/sbx-kit/spec.yaml b/assets/sbx-kit/spec.yaml index 5c972d3..4b17b70 100644 --- a/assets/sbx-kit/spec.yaml +++ b/assets/sbx-kit/spec.yaml @@ -67,6 +67,7 @@ permissions: - 'api.cohere.ai' - 'api.groq.com' - 'openrouter.ai' + - 'models.dev' - 'api.ai21.com' - 'api.cloudflare.com' - 'api.deepinfra.com' diff --git a/config.example.yaml b/config.example.yaml index 2b2a8eb..ebb5d8c 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -26,8 +26,13 @@ light_theme: false # Activates a light color theme when true. env: # ---- Miscellaneous ---- user_agent: null # Set User-Agent HTTP header, use `auto` for coyote/ save_shell_history: true # Whether to save shell execution command to the history file -sync_models_url: > # URL to sync model changes from - https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml +sync_models_url: null # Where `--sync-models` and the automatic refresh get model data from. + # If null, fetches fresh model data from models.dev + OpenRouter. + # Set a URL to mirror a full models.yaml instead, e.g.: + # https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml +sync_models_auto: true # Whether to refresh the model catalog in the background on REPL startup + # once it goes stale +sync_models_ttl_hours: 24 # Hours before the synced model catalog is considered stale # ---- REPL Prompt ---- # Custom REPL left/right prompts; see the [REPL Prompt Documentation](https://github.com/Dark-Alex-17/coyote/wiki/REPL-Prompt) for more information diff --git a/src/client/catalog.rs b/src/client/catalog.rs index cec2d46..e1b3aa5 100644 --- a/src/client/catalog.rs +++ b/src/client/catalog.rs @@ -8,7 +8,7 @@ use fancy_regex::Regex; use log::warn; use serde::{Deserialize, Serialize}; use serde_json::Value; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::sync::LazyLock; pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); @@ -491,6 +491,43 @@ pub fn build_catalog_strict( Ok(build.providers) } +/// Replaces or inserts the entry of every degraded/failed provider from the +/// embedded catalog, preserving the fixed provider order of +/// [`PROVIDER_SOURCES`]. A synced override must never carry fewer models for a +/// provider than the embedded catalog: an upstream outage would otherwise +/// shrink the model list until the next successful sync. +pub fn backfill_embedded_floor(build: &mut CatalogBuild, embedded: &[ProviderModels]) { + let needs_backfill: HashSet<&str> = build + .degraded + .iter() + .chain(build.failed.iter()) + .map(String::as_str) + .collect(); + if needs_backfill.is_empty() { + return; + } + let mut providers = Vec::with_capacity(PROVIDER_SOURCES.len()); + for source in &PROVIDER_SOURCES { + let built = build + .providers + .iter() + .position(|provider| provider.provider == source.provider) + .map(|position| build.providers.remove(position)); + if needs_backfill.contains(source.provider) + && let Some(entry) = embedded + .iter() + .find(|provider| provider.provider == source.provider) + { + providers.push(entry.clone()); + continue; + } + if let Some(built) = built { + providers.push(built); + } + } + build.providers = providers; +} + const GENERATED_HEADER: &str = "# GENERATED by 'coyote --generate-models' — DO NOT hand-edit model data.\n\ # Hand-owned behavior lives in quirks.yaml; this file is the embedded offline fallback.\n"; @@ -1473,6 +1510,105 @@ rules: } } + fn provider_entry(provider: &str, model_names: &[&str]) -> ProviderModels { + ProviderModels { + provider: provider.to_string(), + oauth: None, + models: model_names + .iter() + .map(|name| ModelData::new(name)) + .collect(), + } + } + + #[test] + fn backfill_replaces_degraded_and_inserts_failed_providers() { + let mut build = CatalogBuild { + providers: vec![ + provider_entry("openai", &["gpt-fresh"]), + provider_entry("gemini", &["quirks-only"]), + ], + degraded: vec!["gemini".to_string()], + failed: vec!["claude".to_string()], + }; + let embedded = vec![ + provider_entry("openai", &["gpt-old-1", "gpt-old-2"]), + provider_entry("gemini", &["gem-1", "gem-2", "gem-3"]), + provider_entry("claude", &["claude-1"]), + ]; + + backfill_embedded_floor(&mut build, &embedded); + + let order: Vec<&str> = build + .providers + .iter() + .map(|provider| provider.provider.as_str()) + .collect(); + assert_eq!(order, ["openai", "gemini", "claude"]); + assert_eq!(find_provider(&build, "openai").models.len(), 1); + assert_eq!(find_provider(&build, "gemini").models.len(), 3); + assert_eq!(find_provider(&build, "claude").models.len(), 1); + assert_eq!( + find_provider(&build, "openai").models[0].name, + "gpt-fresh", + "providers with live API data must not be touched" + ); + } + + #[test] + fn backfill_keeps_built_entry_when_embedded_lacks_the_provider() { + let mut build = CatalogBuild { + providers: vec![provider_entry("gemini", &["quirks-only"])], + degraded: vec!["gemini".to_string()], + failed: vec![], + }; + + backfill_embedded_floor(&mut build, &[]); + + assert_eq!(find_provider(&build, "gemini").models.len(), 1); + } + + #[test] + fn backfill_without_degraded_or_failed_is_a_no_op() { + let mut build = CatalogBuild { + providers: vec![provider_entry("openai", &["gpt-fresh"])], + degraded: vec![], + failed: vec![], + }; + + backfill_embedded_floor(&mut build, &[provider_entry("openai", &["a", "b"])]); + + assert_eq!(find_provider(&build, "openai").models.len(), 1); + } + + #[test] + fn backfill_restores_embedded_floor_for_unresolved_providers() { + let embedded: Vec = + serde_yaml::from_str(crate::client::MODELS_YAML).unwrap(); + let mut build = build_catalog(&make_sources(Some(MODELSDEV_FIXTURE), None), &ALL_QUIRKS); + let unresolved: Vec = build + .degraded + .iter() + .chain(build.failed.iter()) + .cloned() + .collect(); + assert!(!unresolved.is_empty()); + + backfill_embedded_floor(&mut build, &embedded); + + for provider in unresolved { + let built = find_provider(&build, &provider); + let floor = embedded + .iter() + .find(|embedded| embedded.provider == provider) + .unwrap(); + assert!( + built.models.len() >= floor.models.len(), + "provider '{provider}' shrank below the embedded floor" + ); + } + } + /// Guards against upstream id-shape drift by comparing live API model /// names against the embedded models.yaml. Run manually: /// `cargo test idshape_guard_live -- --ignored --nocapture` diff --git a/src/config/app_config.rs b/src/config/app_config.rs index 6235c1d..4cf4ef4 100644 --- a/src/config/app_config.rs +++ b/src/config/app_config.rs @@ -102,6 +102,8 @@ pub struct AppConfig { pub no_workspace_mcp: bool, pub no_workspace_macros: bool, pub sync_models_url: Option, + pub sync_models_auto: bool, + pub sync_models_ttl_hours: u64, pub clients: Vec, } @@ -188,6 +190,8 @@ impl Default for AppConfig { no_workspace_mcp: false, no_workspace_macros: false, sync_models_url: None, + sync_models_auto: true, + sync_models_ttl_hours: 24, clients: vec![], } @@ -276,6 +280,8 @@ impl AppConfig { no_workspace_mcp: false, no_workspace_macros: false, sync_models_url: config.sync_models_url, + sync_models_auto: config.sync_models_auto, + sync_models_ttl_hours: config.sync_models_ttl_hours, clients: config.clients, }; @@ -388,10 +394,8 @@ impl AppConfig { .ok_or_else(|| anyhow!("Editor not found. Please add the `editor` configuration or set the $EDITOR or $VISUAL environment variable.")) } - pub fn sync_models_url(&self) -> String { - self.sync_models_url - .clone() - .unwrap_or_else(|| super::SYNC_MODELS_URL.into()) + pub fn sync_models_url(&self) -> Option { + self.sync_models_url.clone() } pub fn light_theme(&self) -> bool { @@ -665,6 +669,13 @@ impl AppConfig { if let Some(v) = super::read_env_value::(&get_env_name("sync_models_url")) { self.sync_models_url = v; } + if let Some(Some(v)) = super::read_env_bool(&get_env_name("sync_models_auto")) { + self.sync_models_auto = v; + } + if let Some(Some(v)) = super::read_env_value::(&get_env_name("sync_models_ttl_hours")) + { + self.sync_models_ttl_hours = v; + } } } @@ -951,10 +962,70 @@ mod tests { } #[test] - fn sync_models_url_has_default() { + fn sync_models_url_defaults_to_none() { let app = AppConfig::default(); - let url = app.sync_models_url(); - assert!(!url.is_empty()); + assert!(app.sync_models_url().is_none()); + } + + #[test] + fn sync_models_auto_and_ttl_have_defaults() { + let app = AppConfig::default(); + assert!(app.sync_models_auto); + assert_eq!(app.sync_models_ttl_hours, 24); + } + + #[test] + #[serial_test::serial] + fn load_envs_overrides_sync_models_auto() { + let env_name = get_env_name("sync_models_auto"); + let prev = env::var_os(&env_name); + + let mut app = AppConfig::default(); + + unsafe { env::set_var(&env_name, "false") }; + app.load_envs(); + assert!(!app.sync_models_auto); + + unsafe { env::set_var(&env_name, "true") }; + app.load_envs(); + assert!(app.sync_models_auto); + + unsafe { env::remove_var(&env_name) }; + app.sync_models_auto = false; + app.load_envs(); + assert!(!app.sync_models_auto); + + unsafe { + match prev { + Some(v) => env::set_var(&env_name, v), + None => env::remove_var(&env_name), + } + } + } + + #[test] + #[serial_test::serial] + fn load_envs_overrides_sync_models_ttl_hours() { + let env_name = get_env_name("sync_models_ttl_hours"); + let prev = env::var_os(&env_name); + + let mut app = AppConfig::default(); + + unsafe { env::set_var(&env_name, "6") }; + app.load_envs(); + assert_eq!(app.sync_models_ttl_hours, 6); + + unsafe { env::remove_var(&env_name) }; + app.sync_models_ttl_hours = 48; + app.load_envs(); + assert_eq!(app.sync_models_ttl_hours, 48); + + unsafe { + match prev { + Some(v) => env::set_var(&env_name, v), + None => env::remove_var(&env_name), + } + } } #[test] diff --git a/src/config/mod.rs b/src/config/mod.rs index 0649765..372c59b 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -69,8 +69,9 @@ pub use self::tool_scope::{ }; pub use self::update::run_self_update; use crate::client::{ - self, ClientConfig, MessageContentToolCalls, Model, ModelType, OPENAI_COMPATIBLE_PROVIDERS, - ProviderModels, create_client_config, list_client_types, oauth, set_client_models_config, + self, ClientConfig, MODELS_YAML, MessageContentToolCalls, Model, ModelType, + OPENAI_COMPATIBLE_PROVIDERS, ProviderModels, catalog, create_client_config, list_client_types, + oauth, set_client_models_config, }; use crate::function::{FunctionDeclaration, Functions}; use crate::rag::Rag; @@ -99,6 +100,7 @@ use std::{ path::{Path, PathBuf}, process, sync::{Arc, OnceLock}, + time::SystemTime, }; pub const TEMP_ROLE_NAME: &str = "temp"; @@ -187,9 +189,6 @@ const GITIGNORE_FILE_NAME: &str = ".gitignore"; const CLIENTS_FIELD: &str = "clients"; -const SYNC_MODELS_URL: &str = - "https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml"; - const SUMMARIZATION_PROMPT: &str = "Summarize the discussion briefly in 200 words or less to use as a prompt for future context."; const SUMMARY_CONTEXT_PROMPT: &str = "This is a summary of the chat history as a recap: "; @@ -287,6 +286,8 @@ pub struct Config { pub user_agent: Option, pub save_shell_history: bool, pub sync_models_url: Option, + pub sync_models_auto: bool, + pub sync_models_ttl_hours: u64, pub clients: Vec, } @@ -370,6 +371,8 @@ impl Default for Config { user_agent: None, save_shell_history: true, sync_models_url: None, + sync_models_auto: true, + sync_models_ttl_hours: 24, clients: vec![], } @@ -513,26 +516,158 @@ pub fn list_sessions() -> Vec { list_file_names(default_sessions_dir(), ".yaml") } -pub async fn sync_models(url: &str, abort_signal: AbortSignal) -> Result<()> { - let content = abortable_run_with_spinner(fetch(url), "Fetching models.yaml", abort_signal) - .await - .with_context(|| format!("Failed to fetch '{url}'"))?; - println!("✓ Fetched '{url}'"); - let list = serde_yaml::from_str::>(&content) - .with_context(|| "Failed to parse models.yaml")?; +/// Refreshes the models override. With a URL, mirrors a full models.yaml from +/// it; without one, rebuilds the catalog from the live models.dev + OpenRouter +/// APIs merged with the embedded quirks. +pub async fn sync_models(url: Option<&str>, abort_signal: AbortSignal) -> Result<()> { + let list = match url { + Some(url) => { + let content = + abortable_run_with_spinner(fetch(url), "Fetching models.yaml", abort_signal) + .await + .with_context(|| format!("Failed to fetch '{url}'"))?; + println!("✓ Fetched '{url}'"); + parse_mirrored_models(&content)? + } + None => { + let sources = abortable_run_with_spinner( + async { Ok(catalog::fetch_sources().await) }, + "Fetching model data", + abort_signal, + ) + .await?; + let (list, warnings) = build_refreshed_catalog(&sources)?; + for warning in &warnings { + println!("{} {warning}", warning_text("WARNING:")); + } + list + } + }; + let model_override_path = write_models_override(list)?; + println!("✓ Updated '{}'", model_override_path.display()); + Ok(()) +} + +/// [`sync_models`] without a spinner or terminal output, for background use; +/// failures are returned for the caller to log. +pub async fn sync_models_quiet(url: Option<&str>) -> Result<()> { + let list = match url { + Some(url) => { + let content = fetch(url) + .await + .with_context(|| format!("Failed to fetch '{url}'"))?; + parse_mirrored_models(&content)? + } + None => { + let sources = catalog::fetch_sources().await; + let (list, warnings) = build_refreshed_catalog(&sources)?; + for warning in warnings { + debug!("{warning}"); + } + list + } + }; + write_models_override(list)?; + Ok(()) +} + +fn parse_mirrored_models(content: &str) -> Result> { + serde_yaml::from_str::>(content) + .with_context(|| "Failed to parse models.yaml") +} + +fn build_refreshed_catalog( + sources: &catalog::FetchedSources, +) -> Result<(Vec, Vec)> { + if sources.modelsdev.is_none() && sources.openrouter.is_none() { + bail!("Failed to fetch model data from both models.dev and OpenRouter"); + } + let mut warnings = Vec::new(); + if sources.modelsdev.is_none() { + warnings.push("Failed to fetch models.dev; continuing with OpenRouter data only".into()); + } + if sources.openrouter.is_none() { + warnings.push("Failed to fetch OpenRouter; continuing with models.dev data only".into()); + } + let mut build = catalog::build_catalog(sources, &catalog::ALL_QUIRKS); + if !build.degraded.is_empty() { + warnings.push(format!( + "No live API data for: {}; keeping their embedded models", + build.degraded.join(", ") + )); + } + if !build.failed.is_empty() { + warnings.push(format!( + "No models resolved for: {}; keeping their embedded models", + build.failed.join(", ") + )); + } + let embedded = serde_yaml::from_str::>(MODELS_YAML) + .with_context(|| "Failed to parse the embedded models.yaml")?; + catalog::backfill_embedded_floor(&mut build, &embedded); + if build.providers.is_empty() { + bail!("Model sync produced no providers"); + } + Ok((build.providers, warnings)) +} + +fn write_models_override(list: Vec) -> Result { let models_override = ModelsOverride { version: env!("CARGO_PKG_VERSION").to_string(), list, }; - let models_override_data = - serde_yaml::to_string(&models_override).with_context(|| "Failed to serde {}")?; + let models_override_data = serde_yaml::to_string(&models_override) + .with_context(|| "Failed to serialize the models override")?; let model_override_path = paths::models_override_file(); ensure_parent_exists(&model_override_path)?; - std::fs::write(&model_override_path, models_override_data) + // Write through a sibling .tmp file and rename it over the target so an + // interrupted write can never leave a truncated override behind. + let mut tmp_name = model_override_path.as_os_str().to_os_string(); + tmp_name.push(".tmp"); + let tmp_path = PathBuf::from(tmp_name); + std::fs::write(&tmp_path, models_override_data) + .with_context(|| format!("Failed to write to '{}'", tmp_path.display()))?; + std::fs::rename(&tmp_path, &model_override_path) .with_context(|| format!("Failed to write to '{}'", model_override_path.display()))?; - println!("✓ Updated '{}'", model_override_path.display()); - Ok(()) + Ok(model_override_path) +} + +/// A missing, unreadable, corrupt, or version-mismatched override is always +/// stale; a loadable one is stale once its mtime is older than the TTL. +fn models_override_is_stale(loadable: bool, mtime: Option, ttl_hours: u64) -> bool { + if !loadable { + return true; + } + let Some(mtime) = mtime else { + return true; + }; + match SystemTime::now().duration_since(mtime) { + Ok(age) => age.as_secs() > ttl_hours.saturating_mul(3600), + Err(_) => false, + } +} + +/// Fire-and-forget refresh of the models override on REPL startup. The +/// running session keeps its already-loaded catalog; the next start picks up +/// the refreshed file. +pub fn maybe_spawn_models_refresh(app: &AppConfig) { + if !app.sync_models_auto || app.dry_run { + return; + } + let loadable = paths::local_models_override().is_ok(); + let mtime = std::fs::metadata(paths::models_override_file()) + .and_then(|metadata| metadata.modified()) + .ok(); + if !models_override_is_stale(loadable, mtime, app.sync_models_ttl_hours) { + return; + } + let url = app.sync_models_url.clone(); + tokio::spawn(async move { + if let Err(err) = sync_models_quiet(url.as_deref()).await { + debug!("Background models refresh failed: {err}"); + } + }); } impl Config { @@ -1381,4 +1516,100 @@ clients: "placeholder should be preserved as a literal string in sandbox mode" ); } + + #[test] + fn unloadable_override_is_stale() { + assert!(models_override_is_stale(false, Some(SystemTime::now()), 24)); + assert!(models_override_is_stale(false, None, 24)); + } + + #[test] + fn unreadable_mtime_is_stale() { + assert!(models_override_is_stale(true, None, 24)); + } + + #[test] + fn fresh_loadable_override_is_not_stale() { + assert!(!models_override_is_stale(true, Some(SystemTime::now()), 24)); + } + + #[test] + fn ttl_boundary_controls_staleness() { + let ttl_hours = 24; + let ttl = std::time::Duration::from_secs(ttl_hours * 3600); + let margin = std::time::Duration::from_secs(300); + let just_fresh = SystemTime::now() - (ttl - margin); + let just_stale = SystemTime::now() - (ttl + margin); + assert!(!models_override_is_stale(true, Some(just_fresh), ttl_hours)); + assert!(models_override_is_stale(true, Some(just_stale), ttl_hours)); + } + + #[test] + fn future_mtime_is_not_stale() { + let future = SystemTime::now() + std::time::Duration::from_secs(3600); + assert!(!models_override_is_stale(true, Some(future), 24)); + } + + #[test] + #[serial_test::serial] + fn models_override_loading_and_atomic_write() { + use std::fs; + + let unique = SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos(); + let tmp_dir = std::env::temp_dir().join(format!("coyote-models-override-{unique}")); + fs::create_dir_all(&tmp_dir).unwrap(); + + let env_name = get_env_name("config_dir"); + let prev = env::var_os(&env_name); + unsafe { env::set_var(&env_name, &tmp_dir) }; + + let result = std::panic::catch_unwind(|| { + let path = paths::models_override_file(); + let entry = || ProviderModels { + provider: "openai".into(), + oauth: None, + models: vec![client::ModelData::new("gpt-test")], + }; + + // Missing override is unloadable. + assert!(paths::local_models_override().is_err()); + + // Corrupt override is unloadable. + fs::write(&path, "not: [valid").unwrap(); + assert!(paths::local_models_override().is_err()); + + // Version-mismatched override is unloadable. + let mismatched = ModelsOverride { + version: "0.0.0".into(), + list: vec![entry()], + }; + fs::write(&path, serde_yaml::to_string(&mismatched).unwrap()).unwrap(); + assert!(paths::local_models_override().is_err()); + + // write_models_override goes through the sibling .tmp file and + // leaves a loadable override behind. + let written = write_models_override(vec![entry()]).unwrap(); + assert_eq!(written, path); + let mut tmp_name = path.as_os_str().to_os_string(); + tmp_name.push(".tmp"); + assert!(!PathBuf::from(tmp_name).exists()); + let loaded = paths::local_models_override().unwrap(); + assert_eq!(loaded.len(), 1); + assert_eq!(loaded[0].provider, "openai"); + }); + + unsafe { + match prev { + Some(v) => env::set_var(&env_name, v), + None => env::remove_var(&env_name), + } + } + let _ = fs::remove_dir_all(&tmp_dir); + if let Err(panic) = result { + std::panic::resume_unwind(panic); + } + } } diff --git a/src/main.rs b/src/main.rs index 605c689..7181aa3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,8 @@ use crate::config::instructions::WORKSPACE_INSTRUCTIONS_FILE_NAME; use crate::config::{ Agent, AppConfig, AppState, CODE_ROLE, Config, EXPLAIN_SHELL_ROLE, Input, MemoryScope, RenderMode, RequestContext, SHELL_ROLE, TEMP_SESSION_NAME, WorkingMode, ensure_parent_exists, - install_builtins, list_agents, load_env_file, macro_execute, sync_models, + install_builtins, list_agents, load_env_file, macro_execute, maybe_spawn_models_refresh, + sync_models, }; use crate::config::{memory, paths}; use crate::function::agents::{GuardrailAction, check_pending_tasks_guardrail}; @@ -293,7 +294,7 @@ async fn run( ) -> Result<()> { if cli.sync_models { let url = ctx.app.config.sync_models_url(); - return sync_models(&url, abort_signal.clone()).await; + return sync_models(url.as_deref(), abort_signal.clone()).await; } if let Some(path) = &cli.generate_models { @@ -549,6 +550,7 @@ async fn run( if !*IS_STDOUT_TERMINAL { bail!("No TTY for REPL") } + maybe_spawn_models_refresh(&ctx.app.config); start_interactive(ctx).await } } From 012212a9986f0dd784fe55f7da877b454a13def3 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 14:57:06 -0600 Subject: [PATCH 15/30] feat: apply provider quirks to models created from a name Unknown chat/reranker models resolved through the create-from-name fallback now inherit their provider's family rules (require_max_tokens, reasoning defaults, etc.) from the quirks overlay, and the model completion table marks reasoning-capable models with a brain glyph. --- src/client/model.rs | 51 ++++++++++++++++++++++++++++++++++- src/config/request_context.rs | 13 +++++++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/client/model.rs b/src/client/model.rs index cbd9623..51d3a20 100644 --- a/src/client/model.rs +++ b/src/client/model.rs @@ -1,5 +1,7 @@ use super::{ - ApiPatch, MessageContentToolCalls, RequestPatch, list_all_models, list_client_names, + ApiPatch, MessageContentToolCalls, RequestPatch, + catalog::{ALL_QUIRKS, apply_rules, quirks_for}, + list_all_models, list_client_names, message::{Message, MessageContent, MessageContentPart}, }; @@ -77,6 +79,9 @@ impl Model { { let mut new_model = Self::new(client_name, model_name); new_model.data.model_type = model_type.to_string(); + if let Some(quirks) = quirks_for(&ALL_QUIRKS, client_name) { + apply_rules(quirks, &mut new_model.data); + } return Ok(new_model); } } @@ -132,6 +137,7 @@ impl Model { output_price, supports_vision, supports_function_calling, + reasoning_levels, .. } = &self.data; let max_input_tokens = stringify_option_value(max_input_tokens); @@ -145,6 +151,9 @@ impl Model { if *supports_function_calling { capabilities.push('⚒'); }; + if !reasoning_levels.is_empty() { + capabilities.push('🧠'); + }; let capabilities: String = capabilities .into_iter() .map(|v| format!("{v} ")) @@ -419,3 +428,43 @@ where None => "-".to_string(), } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::client::ClientConfig; + + #[test] + fn created_from_name_inherits_family_rules() { + // The model registries are process-wide caches seeded by a ctor (see + // the request_context tests) that registers a "claude" client; the + // config below documents the shape a real lookup would use. + let claude: ClientConfig = serde_yaml::from_str( + "type: claude\nmodels:\n - name: test-claude-embedder\n type: embedding\n", + ) + .unwrap(); + let config = AppConfig { + clients: vec![claude], + ..AppConfig::default() + }; + let model = Model::retrieve_model( + &config, + "claude:claude-definitely-not-a-real-model", + ModelType::Chat, + ) + .unwrap(); + assert_eq!(model.name(), "claude-definitely-not-a-real-model"); + assert_eq!(model.model_type(), ModelType::Chat); + assert!(model.data.require_max_tokens); + } + + #[test] + fn description_shows_brain_glyph() { + let mut model = Model::new("claude", "some-model"); + model.data.reasoning_levels = vec!["low".to_string(), "high".to_string()]; + assert!(model.description().contains('🧠')); + + model.data.reasoning_levels.clear(); + assert!(!model.description().contains('🧠')); + } +} diff --git a/src/config/request_context.rs b/src/config/request_context.rs index f614356..38172ca 100644 --- a/src/config/request_context.rs +++ b/src/config/request_context.rs @@ -5232,7 +5232,7 @@ mod tests { // with an empty client list, which would permanently pin every later // model lookup in this process to "unknown model" and make any test that // needs a resolvable model dependent on test ordering. Seed the caches - // before any test runs with one client, "test-seeded", exposing a single + // before any test runs with a client, "test-seeded", exposing a single // embedding model. Deliberately NO chat model: some tests assert that no // chat model is available, and chat lookups don't need one — a // "test-seeded:" chat id resolves through the create-from-name @@ -5252,8 +5252,17 @@ mod tests { embedder.model_type = "embedding".to_string(); config.models = vec![embedder]; } + // A claude client is seeded alongside it so tests can exercise + // provider-quirk inheritance through the create-from-name fallback. + // It deliberately carries one embedding model: an empty `models` list + // would fall back to the full embedded claude catalog and register + // real chat models, breaking the no-chat-model invariant above. + let claude: ClientConfig = serde_yaml::from_str( + "type: claude\nmodels:\n - name: test-claude-embedder\n type: embedding\n", + ) + .unwrap(); let config = AppConfig { - clients: vec![client], + clients: vec![client, claude], ..AppConfig::default() }; let _ = list_client_names(&config); From 73e9bad412b7c6cffc80b7bd3fb47ce78305941b Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 15:11:01 -0600 Subject: [PATCH 16/30] ci: regenerate the model catalog on a 12-hour schedule A scheduled workflow refreshes models.yaml from live API data, validates the result (rebuild, full test suite, catalog smoke test), and commits it back, so every build embeds a recent catalog. Also documents the dynamic model catalog in the README quick links. --- .github/workflows/update-models.yaml | 95 ++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 96 insertions(+) create mode 100644 .github/workflows/update-models.yaml diff --git a/.github/workflows/update-models.yaml b/.github/workflows/update-models.yaml new file mode 100644 index 0000000..99ee468 --- /dev/null +++ b/.github/workflows/update-models.yaml @@ -0,0 +1,95 @@ +name: Update models + +on: + schedule: + - cron: '0 */12 * * *' + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: update-models + cancel-in-progress: false + +defaults: + run: + shell: bash + +jobs: + update: + name: Update models + + runs-on: ubuntu-latest + + env: + RUSTFLAGS: --deny warnings + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: true + + - name: Install Rust Toolchain Components + uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Cache DuckDB Extensions + id: duckdb-extensions + uses: actions/cache@v4 + with: + path: ~/.duckdb/extensions + key: duckdb-ext-ubuntu-latest-${{ hashFiles('Cargo.lock') }} + + - name: Install DuckDB Extensions + if: steps.duckdb-extensions.outputs.cache-hit != 'true' + run: cargo test --all duckdb + + # Coyote loads its config before handling --generate-models/--list-models, and a + # bare non-TTY runner has no config file (and no way to answer the first-run + # prompt), so point COYOTE_CONFIG_DIR at a throwaway dir with a minimal stub. + - name: Write stub config + run: | + COYOTE_CONFIG_DIR="$(mktemp -d)" + printf 'clients:\n - type: openai\n - type: claude\n' > "$COYOTE_CONFIG_DIR/config.yaml" + echo "COYOTE_CONFIG_DIR=$COYOTE_CONFIG_DIR" >> "$GITHUB_ENV" + + - name: Regenerate model catalog + run: cargo run --release -- --generate-models + + - name: Detect catalog changes + id: diff + run: echo "changed=$(git diff --quiet models.yaml && echo false || echo true)" >> "$GITHUB_OUTPUT" + + # The binary built by the regeneration step embedded the OLD models.yaml (the file + # was rewritten after that build), so rebuild before smoke-testing: cargo tracks + # include_str! inputs, making this a cheap incremental rebuild of the final crate. + - name: Rebuild with the refreshed catalog + if: steps.diff.outputs.changed == 'true' + run: cargo build --release + + - name: Test + if: steps.diff.outputs.changed == 'true' + run: cargo test --all + + - name: Smoke test the refreshed catalog + if: steps.diff.outputs.changed == 'true' + run: | + COUNT=$(./target/release/coyote --list-models | wc -l) + echo "Listed $COUNT chat models" + test "$COUNT" -ge 40 + + # Pushes made with the default GITHUB_TOKEN do not trigger other workflows, + # so this push cannot start a workflow loop. The pull --rebase covers a human + # push landing mid-run; if the race is lost anyway, the next cron run retries. + - name: Commit and push + if: steps.diff.outputs.changed == 'true' + run: | + git add models.yaml + git checkout -- . + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git commit -m 'chore: refresh generated model catalog' + git pull --rebase origin main + git push origin main diff --git a/README.md b/README.md index 4d5030e..fbcfd8a 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration g * [Client Configurations](https://github.com/Dark-Alex-17/coyote/wiki/Clients): Configuration instructions for various LLM providers. * [Authentication (API Key & OAuth)](https://github.com/Dark-Alex-17/coyote/wiki/Clients#authentication): Authenticate with API keys or OAuth for subscription-based access. * [Patching API Requests](https://github.com/Dark-Alex-17/coyote/wiki/Patches): Learn how to patch API requests for advanced customization. +* [Dynamic Model Catalog](https://github.com/Dark-Alex-17/coyote/wiki/Model-Catalog): Coyote's model catalog (context windows, pricing, capabilities) is generated from live API data, refreshed automatically in the background, and mirrorable for air-gapped setups — new models show up without waiting for a release. * [Custom Themes](https://github.com/Dark-Alex-17/coyote/wiki/Themes): Change the look and feel of Coyote to your preferences with custom themes. * [History](#history): A history of how Coyote came to be. From 5e7a1f1de6dc69d7e667ae732c9236c78dae2524 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 15:48:47 -0600 Subject: [PATCH 17/30] docs: Corrected typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fbcfd8a..f3a39e8 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration g * [Client Configurations](https://github.com/Dark-Alex-17/coyote/wiki/Clients): Configuration instructions for various LLM providers. * [Authentication (API Key & OAuth)](https://github.com/Dark-Alex-17/coyote/wiki/Clients#authentication): Authenticate with API keys or OAuth for subscription-based access. * [Patching API Requests](https://github.com/Dark-Alex-17/coyote/wiki/Patches): Learn how to patch API requests for advanced customization. -* [Dynamic Model Catalog](https://github.com/Dark-Alex-17/coyote/wiki/Model-Catalog): Coyote's model catalog (context windows, pricing, capabilities) is generated from live API data, refreshed automatically in the background, and mirrorable for air-gapped setups — new models show up without waiting for a release. +* [Dynamic Model Catalog](https://github.com/Dark-Alex-17/coyote/wiki/Model-Catalog): Coyote's model catalog (context windows, pricing, capabilities) is generated from live API data, refreshed automatically in the background, and mirrorable for air-gapped setups. New models show up without waiting for a release. * [Custom Themes](https://github.com/Dark-Alex-17/coyote/wiki/Themes): Change the look and feel of Coyote to your preferences with custom themes. * [History](#history): A history of how Coyote came to be. From 41d5fa9800af4b708e97d946ca0c15fc0b1a333b Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 2 Sep 2026 16:02:41 -0600 Subject: [PATCH 18/30] fix: memoize quirk glob patterns and isolate concurrent override writes --- src/client/catalog.rs | 19 +++++++++++++++++-- src/config/mod.rs | 19 ++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/client/catalog.rs b/src/client/catalog.rs index e1b3aa5..9742849 100644 --- a/src/client/catalog.rs +++ b/src/client/catalog.rs @@ -9,7 +9,7 @@ use log::warn; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::{HashMap, HashSet}; -use std::sync::LazyLock; +use std::sync::{Arc, LazyLock, Mutex}; pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); @@ -815,8 +815,23 @@ pub fn quirks_for<'a>( }) } +/// Compiled patterns are memoized: catalog builds evaluate every rule, exclude, +/// and variant glob against every model, so recompiling per call would dominate +/// the merge as the catalog grows. +static GLOB_CACHE: LazyLock>>> = + LazyLock::new(|| Mutex::new(HashMap::new())); + fn glob_matches(pattern: &str, name: &str) -> bool { - compile_glob(pattern).is_match(name).unwrap_or(false) + let regex = { + let mut cache = GLOB_CACHE + .lock() + .unwrap_or_else(|poison| poison.into_inner()); + cache + .entry(pattern.to_string()) + .or_insert_with(|| Arc::new(compile_glob(pattern))) + .clone() + }; + regex.is_match(name).unwrap_or(false) } /// Translates a glob pattern (`*` = any run of characters, `?` = exactly one) diff --git a/src/config/mod.rs b/src/config/mod.rs index 372c59b..2873817 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -621,15 +621,20 @@ fn write_models_override(list: Vec) -> Result { let model_override_path = paths::models_override_file(); ensure_parent_exists(&model_override_path)?; - // Write through a sibling .tmp file and rename it over the target so an - // interrupted write can never leave a truncated override behind. + // Write through a sibling temp file and rename it over the target so an + // interrupted write can never leave a truncated override behind. The temp + // name embeds the pid so concurrent coyote processes (auto-refresh racing + // a manual --sync-models) never interleave writes into the same file. let mut tmp_name = model_override_path.as_os_str().to_os_string(); - tmp_name.push(".tmp"); + tmp_name.push(format!(".{}.tmp", std::process::id())); let tmp_path = PathBuf::from(tmp_name); std::fs::write(&tmp_path, models_override_data) .with_context(|| format!("Failed to write to '{}'", tmp_path.display()))?; - std::fs::rename(&tmp_path, &model_override_path) - .with_context(|| format!("Failed to write to '{}'", model_override_path.display()))?; + if let Err(error) = std::fs::rename(&tmp_path, &model_override_path) { + let _ = std::fs::remove_file(&tmp_path); + return Err(error) + .with_context(|| format!("Failed to write to '{}'", model_override_path.display())); + } Ok(model_override_path) } @@ -1589,12 +1594,12 @@ clients: fs::write(&path, serde_yaml::to_string(&mismatched).unwrap()).unwrap(); assert!(paths::local_models_override().is_err()); - // write_models_override goes through the sibling .tmp file and + // write_models_override goes through the sibling temp file and // leaves a loadable override behind. let written = write_models_override(vec![entry()]).unwrap(); assert_eq!(written, path); let mut tmp_name = path.as_os_str().to_os_string(); - tmp_name.push(".tmp"); + tmp_name.push(format!(".{}.tmp", std::process::id())); assert!(!PathBuf::from(tmp_name).exists()); let loaded = paths::local_models_override().unwrap(); assert_eq!(loaded.len(), 1); From 49bd5d86b4fef1069751861ff11b232fd00b499d Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 12:35:12 -0600 Subject: [PATCH 19/30] fix: skip terminal color-scheme queries in test builds --- src/config/app_config.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/config/app_config.rs b/src/config/app_config.rs index 4cf4ef4..6bf1576 100644 --- a/src/config/app_config.rs +++ b/src/config/app_config.rs @@ -640,7 +640,10 @@ impl AppConfig { if self.highlight && self.theme.is_none() { if let Some(v) = super::read_env_value::(&get_env_name("theme")) { self.theme = v; - } else if *IS_STDOUT_TERMINAL { + } else if !cfg!(test) && *IS_STDOUT_TERMINAL { + // Never query the terminal from test builds: the test harness + // shares the process TTY, so parallel raw-mode color queries + // garble the runner's output and stall on stolen replies. if let Ok(color_scheme) = color_scheme(QueryOptions::default()) { let theme = match color_scheme { ColorScheme::Dark => "dark", From dc41e9d5df98e569f8c9ba8b85181b07dc677b5b Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 12:40:08 -0600 Subject: [PATCH 20/30] style: Cleaned up comments and organization of quirks.yaml file --- quirks.yaml | 34 +++++++-------- src/client/catalog.rs | 97 +++++++++++++------------------------------ src/config/mod.rs | 35 ++++++++-------- 3 files changed, 63 insertions(+), 103 deletions(-) diff --git a/quirks.yaml b/quirks.yaml index 75e68c0..3db303c 100644 --- a/quirks.yaml +++ b/quirks.yaml @@ -4,9 +4,9 @@ # model data: oauth blocks, request patches, wire-name aliases, behavioral # flags, and models that no public catalog provides. # -# `models:` entries are self-complete — `rules` apply ONLY to API-sourced +# `models:` entries are self-complete. `rules` apply ONLY to API-sourced # models, never to hand-owned `models:` entries. Legacy verbatim-preserved -# entries are grouped under a `# legacy — pruning candidates` comment. +# entries are grouped under a `# legacy -- pruning candidates` comment. - provider: openai exclude: @@ -78,7 +78,7 @@ max_tokens_per_chunk: 8191 default_chunk_size: 2000 max_batch_size: 100 - # legacy — pruning candidates + # legacy -- pruning candidates - name: o4-mini-high max_output_tokens: 100000 real_name: o4-mini @@ -179,7 +179,7 @@ max_tokens_per_chunk: 2048 default_chunk_size: 1500 max_batch_size: 100 - # legacy — pruning candidates + # legacy -- pruning candidates - name: gemini-3-pro-preview max_input_tokens: 1048576 supports_vision: true @@ -296,7 +296,7 @@ type: enabled budget_tokens: 16000 models: - # legacy — pruning candidates + # legacy -- pruning candidates - name: claude-opus-4-1-20250805 max_input_tokens: 200000 max_output_tokens: 8192 @@ -324,7 +324,7 @@ input_price: 0.1 max_tokens_per_chunk: 8092 default_chunk_size: 2000 - # legacy — pruning candidates + # legacy -- pruning candidates - name: magistral-small-latest max_input_tokens: 131072 input_price: 0.5 @@ -451,7 +451,7 @@ - provider: perplexity models: - # legacy — pruning candidates + # legacy -- pruning candidates - name: sonar-reasoning max_input_tokens: 128000 input_price: 1 @@ -461,7 +461,7 @@ exclude: - "whisper-*" models: - # legacy — pruning candidates + # legacy -- pruning candidates - name: meta-llama/llama-4-maverick-17b-128e-instruct max_input_tokens: 131072 input_price: 0 @@ -600,7 +600,7 @@ max_tokens_per_chunk: 2048 default_chunk_size: 1500 max_batch_size: 5 - # legacy — pruning candidates + # legacy -- pruning candidates - name: gemini-3-pro-preview max_input_tokens: 1048576 supports_vision: true @@ -813,7 +813,7 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 96 - # legacy — pruning candidates + # legacy -- pruning candidates - name: us.meta.llama3-3-70b-instruct-v1:0 max_input_tokens: 131072 max_output_tokens: 8192 @@ -855,7 +855,7 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 100 - # legacy — pruning candidates + # legacy -- pruning candidates - name: '@cf/google/gemma-3-12b-it' max_input_tokens: 131072 max_output_tokens: 2048 @@ -939,7 +939,7 @@ max_tokens_per_chunk: 8192 default_chunk_size: 2000 max_batch_size: 10 - # legacy — pruning candidates + # legacy -- pruning candidates - name: qwen3-vl-flash max_input_tokens: 262144 supports_vision: true @@ -1000,7 +1000,7 @@ - provider: deepseek models: - # legacy — pruning candidates + # legacy -- pruning candidates - name: deepseek-chat max_input_tokens: 64000 max_output_tokens: 8192 @@ -1040,7 +1040,7 @@ type: reranker max_input_tokens: 4096 input_price: 0.112 - # legacy — pruning candidates + # legacy -- pruning candidates - name: glm-4.6v-flash max_input_tokens: 65536 input_price: 0 @@ -1049,7 +1049,7 @@ - provider: minimax models: - # legacy — pruning candidates + # legacy -- pruning candidates - name: minimax-m3 max_input_tokens: 1000000 input_price: 4.2 @@ -1166,7 +1166,7 @@ body: include_reasoning: true models: - # legacy — pruning candidates + # legacy -- pruning candidates - name: google/gemini-3-pro-preview max_input_tokens: 1048576 supports_vision: true @@ -1352,7 +1352,7 @@ max_tokens_per_chunk: 512 default_chunk_size: 1000 max_batch_size: 100 - # legacy — pruning candidates + # legacy -- pruning candidates - name: Qwen/Qwen3-Max-Thinking max_input_tokens: 262144 input_price: 1.2 diff --git a/src/client/catalog.rs b/src/client/catalog.rs index 9742849..b6b8456 100644 --- a/src/client/catalog.rs +++ b/src/client/catalog.rs @@ -1,5 +1,6 @@ use super::model::{ModelData, ProviderModels}; use super::oauth::OAuthConfig; +use std::cmp::Ordering; use crate::utils::fetch; @@ -9,6 +10,8 @@ use log::warn; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::{HashMap, HashSet}; +use std::fs; +use std::path::Path; use std::sync::{Arc, LazyLock, Mutex}; pub const QUIRKS_YAML: &str = include_str!("../../quirks.yaml"); @@ -19,10 +22,6 @@ pub static ALL_QUIRKS: LazyLock> = const MODELSDEV_API_URL: &str = "https://models.dev/api.json"; const OPENROUTER_API_URL: &str = "https://openrouter.ai/api/v1/models"; -/// Upstream ids for one coyote provider. The table order is the fixed output -/// order of the built catalog. Providers with neither upstream id are fully -/// quirks-owned; `openrouter` is special-cased in the merge: its models come -/// from the OpenRouter list natively instead of via a prefix join. struct SourceIds { provider: &'static str, modelsdev: Option<&'static str>, @@ -152,15 +151,11 @@ const PROVIDER_SOURCES: [SourceIds; 24] = [ }, ]; -/// Raw payloads from the two upstream catalogs. Each source is independently -/// fallible; `None` means that fetch failed and the merge degrades gracefully. pub struct FetchedSources { pub modelsdev: Option, pub openrouter: Option, } -/// Fetches both upstream catalogs concurrently. A failed fetch is logged and -/// surfaces as `None` rather than an error. pub async fn fetch_sources() -> FetchedSources { let (modelsdev, openrouter) = tokio::join!(fetch(MODELSDEV_API_URL), fetch(OPENROUTER_API_URL)); FetchedSources { @@ -173,8 +168,6 @@ pub async fn fetch_sources() -> FetchedSources { } } -/// An API-sourced model plus the release date used for ordering. The date is -/// deliberately kept out of [`ModelData`] and never serialized. struct SourcedModel { data: ModelData, release_date: Option, @@ -192,9 +185,6 @@ fn clamp_price(value: f64) -> Option { (0.0..=10_000.0).contains(&value).then_some(value) } -/// Extracts the models of one models.dev provider. Tolerant by design: -/// missing or malformed fields are skipped while the model is kept, and -/// out-of-range numbers are dropped field-wise. fn modelsdev_provider_models(api: &Value, provider_id: &str) -> Vec { let Some(models) = api[provider_id]["models"].as_object() else { return Vec::new(); @@ -236,15 +226,11 @@ fn modelsdev_provider_models(api: &Value, provider_id: &str) -> Vec Option { let per_token: f64 = value.as_str()?.parse().ok()?; clamp_price((per_token * 1_000_000.0 * 10_000.0).round() / 10_000.0) } -/// Maps one OpenRouter list entry onto [`ModelData`], with the same field-wise -/// tolerance and clamping as the models.dev adapter. fn openrouter_model_data(name: &str, entry: &Value) -> ModelData { let mut data = ModelData::new(name); data.max_input_tokens = entry["context_length"] @@ -276,9 +262,6 @@ fn openrouter_index(payload: &Value) -> HashMap<&str, &Value> { .unwrap_or_default() } -/// Every OpenRouter list entry as a model of the `openrouter` provider, named -/// by its full id. OpenRouter publishes no release date, so ordering falls -/// back to name-ascending. fn openrouter_native_models(payload: &Value) -> Vec { payload["data"] .as_array() @@ -297,8 +280,6 @@ fn openrouter_native_models(payload: &Value) -> Vec { .unwrap_or_default() } -/// Fills fields that models.dev left unset on the same model: OpenRouter data -/// never overrides a present models.dev value. fn fill_from_openrouter(data: &mut ModelData, entry: &Value) { let filler = openrouter_model_data(&data.name, entry); if data.max_input_tokens.is_none() { @@ -427,8 +408,8 @@ pub fn build_catalog(sources: &FetchedSources, quirks: &[ProviderQuirks]) -> Cat (Some(a_date), Some(b_date)) => b_date .cmp(a_date) .then_with(|| a.data.name.cmp(&b.data.name)), - (Some(_), None) => std::cmp::Ordering::Less, - (None, Some(_)) => std::cmp::Ordering::Greater, + (Some(_), None) => Ordering::Less, + (None, Some(_)) => Ordering::Greater, (None, None) => a.data.name.cmp(&b.data.name), }); @@ -474,8 +455,6 @@ pub fn build_catalog(sources: &FetchedSources, quirks: &[ProviderQuirks]) -> Cat } } -/// Like [`build_catalog`], but errors unless every provider was assembled -/// from live API data (or is quirks-owned by design). pub fn build_catalog_strict( sources: &FetchedSources, quirks: &[ProviderQuirks], @@ -488,6 +467,7 @@ pub fn build_catalog_strict( build.failed.join(", ") ); } + Ok(build.providers) } @@ -528,13 +508,10 @@ pub fn backfill_embedded_floor(build: &mut CatalogBuild, embedded: &[ProviderMod build.providers = providers; } -const GENERATED_HEADER: &str = "# GENERATED by 'coyote --generate-models' — DO NOT hand-edit model data.\n\ +const GENERATED_HEADER: &str = "# GENERATED by 'coyote --generate-models' \n#DO NOT hand-edit model data.\n\ # Hand-owned behavior lives in quirks.yaml; this file is the embedded offline fallback.\n"; -/// Builds the catalog from live API data and writes it to `path`. Both -/// upstream sources must respond and every provider must fully resolve; -/// identical source data always produces identical bytes. -pub async fn generate_models_file(path: &std::path::Path) -> Result<()> { +pub async fn generate_models_file(path: &Path) -> Result<()> { let sources = fetch_sources().await; if sources.modelsdev.is_none() || sources.openrouter.is_none() { bail!( @@ -544,25 +521,24 @@ pub async fn generate_models_file(path: &std::path::Path) -> Result<()> { let providers = build_catalog_strict(&sources, &ALL_QUIRKS)?; let model_count: usize = providers.iter().map(|provider| provider.models.len()).sum(); let yaml = serde_yaml::to_string(&providers)?; - std::fs::write(path, format!("{GENERATED_HEADER}{yaml}"))?; + + fs::write(path, format!("{GENERATED_HEADER}{yaml}"))?; println!( "Wrote {} models across {} providers to {}", model_count, providers.len(), path.display() ); + Ok(()) } -/// Fraction of `old` names that also appear in `new`; empty `old` yields 1.0. -/// Used to guard against upstream id-shape drift: a low overlap means the -/// upstream catalog renamed its models out from under us. -#[allow(dead_code)] +#[cfg(test)] pub(crate) fn name_overlap(old: &[String], new: &[String]) -> f64 { if old.is_empty() { return 1.0; } - let new_names: std::collections::HashSet<&str> = new.iter().map(String::as_str).collect(); + let new_names: HashSet<&str> = new.iter().map(String::as_str).collect(); let kept = old .iter() .filter(|name| new_names.contains(name.as_str())) @@ -570,10 +546,6 @@ pub(crate) fn name_overlap(old: &[String], new: &[String]) -> f64 { kept as f64 / old.len() as f64 } -/// Overlays a hand-owned quirks entry onto the API-sourced model of the same -/// name. Every field the hand-owned entry carries (Some, true, or non-empty) -/// wins; absent fields keep the API value. `model_type` is authoritative on -/// the hand-owned entry. fn overlay_hand_owned(hand: ModelData, api: ModelData) -> ModelData { ModelData { name: hand.name, @@ -604,9 +576,6 @@ fn overlay_hand_owned(hand: ModelData, api: ModelData) -> ModelData { } } -/// Hand-owned catalog overrides for a single provider: oauth configuration, -/// curation excludes, field overlays for API-sourced models, suffixed model -/// variants, and fully hand-owned model entries. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct ProviderQuirks { @@ -623,10 +592,6 @@ pub struct ProviderQuirks { pub models: Vec, } -/// A field overlay applied to every API-sourced model whose name matches the -/// glob in `match` (`*` = any run of characters, `?` = exactly one). Only -/// explicitly present fields are written; absent fields leave the model -/// untouched. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct QuirkRule { @@ -661,9 +626,6 @@ pub struct QuirkRule { pub system_prompt_prefix: Option, } -/// Clones every model whose name matches the glob in `match`, appends `suffix` -/// to the clone's name, and overlays any explicitly present fields onto the -/// clone. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct QuirkVariant { @@ -699,8 +661,6 @@ pub struct QuirkVariant { pub system_prompt_prefix: Option, } -/// Applies every rule whose glob matches `data.name`, in file order; later -/// rules overwrite earlier ones on the same field. pub fn apply_rules(provider_quirks: &ProviderQuirks, data: &mut ModelData) { for rule in &provider_quirks.rules { if !glob_matches(&rule.r#match, &data.name) { @@ -751,8 +711,6 @@ pub fn apply_rules(provider_quirks: &ProviderQuirks, data: &mut ModelData) { } } -/// Overlays a variant's explicitly present fields onto a cloned model; same -/// field semantics as [`apply_rules`]. fn apply_variant(variant: &QuirkVariant, data: &mut ModelData) { if let Some(real_name) = &variant.real_name { data.real_name = Some(real_name.clone()); @@ -798,9 +756,6 @@ fn apply_variant(variant: &QuirkVariant, data: &mut ModelData) { } } -/// Finds the quirks for a client: an exact provider match wins, otherwise a -/// client whose name starts with the provider name (openai-compatible clients -/// are conventionally named after the provider they wrap). pub fn quirks_for<'a>( quirks: &'a [ProviderQuirks], client_name: &str, @@ -815,9 +770,6 @@ pub fn quirks_for<'a>( }) } -/// Compiled patterns are memoized: catalog builds evaluate every rule, exclude, -/// and variant glob against every model, so recompiling per call would dominate -/// the merge as the catalog grows. static GLOB_CACHE: LazyLock>>> = LazyLock::new(|| Mutex::new(HashMap::new())); @@ -834,8 +786,6 @@ fn glob_matches(pattern: &str, name: &str) -> bool { regex.is_match(name).unwrap_or(false) } -/// Translates a glob pattern (`*` = any run of characters, `?` = exactly one) -/// into an anchored regex. Patterns that fail to compile match nothing. fn compile_glob(pattern: &str) -> Regex { let translated = format!( "^{}$", @@ -853,6 +803,7 @@ fn compile_glob(pattern: &str) -> Regex { mod tests { use super::*; + use crate::client::MODELS_YAML; use serde_json::json; const KNOWN_PROVIDERS: [&str; 24] = [ @@ -934,6 +885,7 @@ mod tests { .iter() .map(|source| source.provider) .collect(); + assert_eq!(order, KNOWN_PROVIDERS); } @@ -1122,7 +1074,9 @@ rules: .iter() .flat_map(|id| modelsdev_provider_models(&api, id)) .collect(); + all.extend(openrouter_native_models(&payload)); + assert!(!all.is_empty()); for model in &all { let data = &model.data; @@ -1154,7 +1108,9 @@ rules: token_url: https://example.com/token "#, ); + let build = build_catalog(&fixture_sources(), &quirks); + assert!(find_provider(&build, "xai").oauth.is_some()); assert!(find_provider(&build, "openai").oauth.is_none()); } @@ -1352,7 +1308,9 @@ rules: models: [{name: jamba}] "#, ); + let build = build_catalog(&make_sources(None, None), &quirks); + assert_eq!(build.degraded, ["gemini"]); assert!(build.failed.contains(&"openai".to_string())); assert!(build.failed.contains(&"openrouter".to_string())); @@ -1383,9 +1341,11 @@ rules: models: [{name: g-1}] "#, ); + let err = build_catalog_strict(&make_sources(None, None), &quirks) .unwrap_err() .to_string(); + assert!(err.contains("gemini"), "{err}"); assert!(err.contains("openai"), "{err}"); } @@ -1416,6 +1376,7 @@ rules: models: [{name: v}] "#, ); + let providers = build_catalog_strict(&make_sources(Some(&modelsdev), Some(&openrouter)), &quirks) .unwrap(); @@ -1423,6 +1384,7 @@ rules: .iter() .map(|provider| provider.provider.as_str()) .collect(); + assert_eq!(order, KNOWN_PROVIDERS); } @@ -1430,13 +1392,14 @@ rules: fn name_overlap_fraction_of_old_names_still_present() { let old: Vec = ["a", "b", "c", "d"].map(str::to_string).into(); let new: Vec = ["a", "c", "x"].map(str::to_string).into(); + assert_eq!(name_overlap(&old, &new), 0.5); assert_eq!(name_overlap(&[], &new), 1.0); assert_eq!(name_overlap(&old, &[]), 0.0); } fn embedded_catalog() -> Vec { - serde_yaml::from_str(crate::client::MODELS_YAML).unwrap() + serde_yaml::from_str(MODELS_YAML).unwrap() } fn embedded_provider<'a>(providers: &'a [ProviderModels], name: &str) -> &'a ProviderModels { @@ -1598,8 +1561,7 @@ rules: #[test] fn backfill_restores_embedded_floor_for_unresolved_providers() { - let embedded: Vec = - serde_yaml::from_str(crate::client::MODELS_YAML).unwrap(); + let embedded: Vec = serde_yaml::from_str(MODELS_YAML).unwrap(); let mut build = build_catalog(&make_sources(Some(MODELSDEV_FIXTURE), None), &ALL_QUIRKS); let unresolved: Vec = build .degraded @@ -1645,8 +1607,7 @@ rules: .expect("openrouter fetch failed"), ) .unwrap(); - let embedded: Vec = - serde_yaml::from_str(crate::client::MODELS_YAML).unwrap(); + let embedded: Vec = serde_yaml::from_str(MODELS_YAML).unwrap(); println!( "{:<12} {:>7} {:>5} {:>5}", diff --git a/src/config/mod.rs b/src/config/mod.rs index 2873817..96ac24a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -94,7 +94,7 @@ use serde_json::json; use std::collections::{HashMap, HashSet}; use std::sync::LazyLock; use std::{ - env, + env, fs, fs::{File, create_dir_all, read_dir, read_to_string}, io::Write, path::{Path, PathBuf}, @@ -548,8 +548,6 @@ pub async fn sync_models(url: Option<&str>, abort_signal: AbortSignal) -> Result Ok(()) } -/// [`sync_models`] without a spinner or terminal output, for background use; -/// failures are returned for the caller to log. pub async fn sync_models_quiet(url: Option<&str>) -> Result<()> { let list = match url { Some(url) => { @@ -589,6 +587,7 @@ fn build_refreshed_catalog( if sources.openrouter.is_none() { warnings.push("Failed to fetch OpenRouter; continuing with models.dev data only".into()); } + let mut build = catalog::build_catalog(sources, &catalog::ALL_QUIRKS); if !build.degraded.is_empty() { warnings.push(format!( @@ -602,12 +601,15 @@ fn build_refreshed_catalog( build.failed.join(", ") )); } + let embedded = serde_yaml::from_str::>(MODELS_YAML) .with_context(|| "Failed to parse the embedded models.yaml")?; catalog::backfill_embedded_floor(&mut build, &embedded); + if build.providers.is_empty() { bail!("Model sync produced no providers"); } + Ok((build.providers, warnings)) } @@ -626,20 +628,18 @@ fn write_models_override(list: Vec) -> Result { // name embeds the pid so concurrent coyote processes (auto-refresh racing // a manual --sync-models) never interleave writes into the same file. let mut tmp_name = model_override_path.as_os_str().to_os_string(); - tmp_name.push(format!(".{}.tmp", std::process::id())); + tmp_name.push(format!(".{}.tmp", process::id())); let tmp_path = PathBuf::from(tmp_name); - std::fs::write(&tmp_path, models_override_data) + fs::write(&tmp_path, models_override_data) .with_context(|| format!("Failed to write to '{}'", tmp_path.display()))?; - if let Err(error) = std::fs::rename(&tmp_path, &model_override_path) { - let _ = std::fs::remove_file(&tmp_path); + if let Err(error) = fs::rename(&tmp_path, &model_override_path) { + let _ = fs::remove_file(&tmp_path); return Err(error) .with_context(|| format!("Failed to write to '{}'", model_override_path.display())); } Ok(model_override_path) } -/// A missing, unreadable, corrupt, or version-mismatched override is always -/// stale; a loadable one is stale once its mtime is older than the TTL. fn models_override_is_stale(loadable: bool, mtime: Option, ttl_hours: u64) -> bool { if !loadable { return true; @@ -653,15 +653,12 @@ fn models_override_is_stale(loadable: bool, mtime: Option, ttl_hours } } -/// Fire-and-forget refresh of the models override on REPL startup. The -/// running session keeps its already-loaded catalog; the next start picks up -/// the refreshed file. pub fn maybe_spawn_models_refresh(app: &AppConfig) { if !app.sync_models_auto || app.dry_run { return; } let loadable = paths::local_models_override().is_ok(); - let mtime = std::fs::metadata(paths::models_override_file()) + let mtime = fs::metadata(paths::models_override_file()) .and_then(|metadata| metadata.modified()) .ok(); if !models_override_is_stale(loadable, mtime, app.sync_models_ttl_hours) { @@ -1030,13 +1027,13 @@ fn render_config_template_from( fn write_config_file(config_path: &Path, config_data: &str) -> Result<()> { ensure_parent_exists(config_path)?; - std::fs::write(config_path, config_data) + fs::write(config_path, config_data) .with_context(|| format!("Failed to write to '{}'", config_path.display()))?; #[cfg(unix)] { use std::os::unix::prelude::PermissionsExt; - let perms = std::fs::Permissions::from_mode(0o600); - std::fs::set_permissions(config_path, perms)?; + let perms = fs::Permissions::from_mode(0o600); + fs::set_permissions(config_path, perms)?; } println!("✓ Saved the config file to '{}'.\n", config_path.display()); @@ -1545,6 +1542,7 @@ clients: let margin = std::time::Duration::from_secs(300); let just_fresh = SystemTime::now() - (ttl - margin); let just_stale = SystemTime::now() - (ttl + margin); + assert!(!models_override_is_stale(true, Some(just_fresh), ttl_hours)); assert!(models_override_is_stale(true, Some(just_stale), ttl_hours)); } @@ -1552,6 +1550,7 @@ clients: #[test] fn future_mtime_is_not_stale() { let future = SystemTime::now() + std::time::Duration::from_secs(3600); + assert!(!models_override_is_stale(true, Some(future), 24)); } @@ -1564,8 +1563,8 @@ clients: .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_nanos(); - let tmp_dir = std::env::temp_dir().join(format!("coyote-models-override-{unique}")); - fs::create_dir_all(&tmp_dir).unwrap(); + let tmp_dir = env::temp_dir().join(format!("coyote-models-override-{unique}")); + create_dir_all(&tmp_dir).unwrap(); let env_name = get_env_name("config_dir"); let prev = env::var_os(&env_name); From 3b01ae8f18892139e2e0b24e07a782ac585f455e Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 13:03:10 -0600 Subject: [PATCH 21/30] fix: let ctrl-c interrupt blocking job and agent collects --- src/config/request_context.rs | 9 ++++ src/function/agents.rs | 90 ++++++++++++++++++++++++++++++++++- src/function/jobs.rs | 87 ++++++++++++++++++++++++++++++++- src/main.rs | 1 + src/repl/mod.rs | 1 + src/utils/abort_signal.rs | 38 +++++++++++++++ 6 files changed, 223 insertions(+), 3 deletions(-) diff --git a/src/config/request_context.rs b/src/config/request_context.rs index 38172ca..edae565 100644 --- a/src/config/request_context.rs +++ b/src/config/request_context.rs @@ -338,6 +338,11 @@ pub struct RequestContext { pub last_continuation_response: Option, pub pending_prefill: Option, + /// The turn's abort signal, set by the REPL/cmd entry points. Lets blocking + /// tools (job__collect, agent__collect) notice a user interrupt -- SIGINT is + /// otherwise swallowed in cooked mode during tool execution. + pub session_abort: Option, + pub render_mode: RenderMode, } @@ -375,6 +380,7 @@ impl RequestContext { skill_registry: SkillRegistry::default(), last_continuation_response: None, pending_prefill: None, + session_abort: None, render_mode: RenderMode::default(), } } @@ -439,6 +445,7 @@ impl RequestContext { skill_registry: SkillRegistry::default(), last_continuation_response: None, pending_prefill: None, + session_abort: None, render_mode: RenderMode::default(), }; ctx.refresh_mcp_tool_filters(); @@ -492,6 +499,7 @@ impl RequestContext { skill_registry: self.skill_registry.clone(), last_continuation_response: None, pending_prefill: None, + session_abort: self.session_abort.clone(), render_mode: self.render_mode, } } @@ -541,6 +549,7 @@ impl RequestContext { skill_registry: SkillRegistry::default(), last_continuation_response: None, pending_prefill: None, + session_abort: None, render_mode: parent.render_mode, } } diff --git a/src/function/agents.rs b/src/function/agents.rs index 0743611..a03f49a 100644 --- a/src/function/agents.rs +++ b/src/function/agents.rs @@ -7,7 +7,7 @@ use crate::config::{ use crate::supervisor::mailbox::{Envelope, EnvelopePayload, Inbox}; use crate::supervisor::notification::agent_notification; use crate::supervisor::{AgentExitStatus, AgentHandle, AgentResult, Supervisor, TaskKind}; -use crate::utils::{AbortSignal, create_abort_signal, wait_abort_signal}; +use crate::utils::{AbortSignal, create_abort_signal, wait_abort_signal, wait_user_interrupt}; use crate::graph; use anyhow::{Context, Result, anyhow, bail}; @@ -1031,6 +1031,14 @@ async fn handle_collect(ctx: &mut RequestContext, args: &Value) -> Result sup.abort_signal_for(id) }; + let interrupted = || { + json!({ + "status": "interrupted", + "id": id, + "message": format!("agent__collect was interrupted by the user; agent '{id}' is still running in the background. Collect it again later, cancel it with agent__cancel, or continue with other work."), + }) + }; + loop { let is_finished = { let sup = supervisor.read(); @@ -1041,6 +1049,10 @@ async fn handle_collect(ctx: &mut RequestContext, args: &Value) -> Result break; } + if ctx.session_abort.as_ref().is_some_and(|s| s.aborted()) { + return Ok(interrupted()); + } + if let Some(queue) = ctx.root_escalation_queue() && queue.has_pending() { @@ -1068,10 +1080,18 @@ async fn handle_collect(ctx: &mut RequestContext, args: &Value) -> Result tokio::select! { _ = time::sleep(Duration::from_millis(200)) => {} _ = wait_abort_signal(abort) => {} + _ = wait_user_interrupt(ctx.session_abort.as_ref()) => { + return Ok(interrupted()); + } } } None => { - time::sleep(Duration::from_millis(200)).await; + tokio::select! { + _ = time::sleep(Duration::from_millis(200)) => {} + _ = wait_user_interrupt(ctx.session_abort.as_ref()) => { + return Ok(interrupted()); + } + } } } } @@ -2557,6 +2577,72 @@ mod tests { ); } + #[test] + fn handle_collect_session_interrupt_returns_and_keeps_agent() { + run_async(async { + let mut ctx = ctx_with_supervisor(4, 3); + let (tx, rx) = tokio::sync::oneshot::channel::<()>(); + let join_handle = tokio::spawn(async move { + let _ = rx.await; + Ok(AgentResult { + id: "a1".to_string(), + agent_name: "explore".to_string(), + output: "late output".to_string(), + exit_status: AgentExitStatus::Completed, + }) + }); + let handle = AgentHandle { + id: "a1".to_string(), + agent_name: "explore".to_string(), + depth: 1, + inbox: Arc::new(Inbox::new()), + abort_signal: create_abort_signal(), + join_handle, + child_supervisor: None, + }; + ctx.supervisor + .as_ref() + .unwrap() + .write() + .register(handle) + .unwrap(); + + let session = create_abort_signal(); + session.set_ctrlc(); + ctx.session_abort = Some(session.clone()); + + let result = time::timeout( + Duration::from_secs(2), + handle_collect(&mut ctx, &json!({"id": "a1"})), + ) + .await + .expect("interrupted collect must return promptly") + .unwrap(); + + assert_eq!(result["status"], "interrupted"); + assert_eq!(result["id"], "a1"); + assert!( + result["message"] + .as_str() + .unwrap() + .contains("still running") + ); + assert_eq!( + ctx.supervisor.as_ref().unwrap().read().is_finished("a1"), + Some(false) + ); + + session.reset(); + tx.send(()).unwrap(); + + let collected = handle_collect(&mut ctx, &json!({"id": "a1"})) + .await + .unwrap(); + assert_eq!(collected["status"], "completed"); + assert_eq!(collected["output"], "late output"); + }); + } + #[test] fn handle_collect_pending_escalations_early_out_keeps_handle() { let rt = tokio::runtime::Builder::new_current_thread() diff --git a/src/function/jobs.rs b/src/function/jobs.rs index 9e35e80..b191051 100644 --- a/src/function/jobs.rs +++ b/src/function/jobs.rs @@ -16,7 +16,9 @@ use crate::mcp::{ }; use crate::supervisor::notification::job_notification; use crate::supervisor::{JobHandle, JobResult, JobState, JobStatus, Supervisor}; -use crate::utils::{create_abort_signal, muted_warning_text, temp_file, wait_abort_signal}; +use crate::utils::{ + create_abort_signal, muted_warning_text, temp_file, wait_abort_signal, wait_user_interrupt, +}; use anyhow::{Context, Result, anyhow, bail}; use indexmap::IndexMap; @@ -623,6 +625,14 @@ async fn handle_collect(ctx: &RequestContext, args: &Value) -> Result { job.abort_signal.clone() }; + let interrupted = || { + json!({ + "status": "interrupted", + "id": id, + "message": format!("job__collect was interrupted by the user; job '{id}' is still running in the background. Collect it again later, cancel it with job__cancel, or continue with other work."), + }) + }; + loop { let is_finished = { let sup = supervisor.read(); @@ -633,6 +643,10 @@ async fn handle_collect(ctx: &RequestContext, args: &Value) -> Result { break; } + if ctx.session_abort.as_ref().is_some_and(|s| s.aborted()) { + return Ok(interrupted()); + } + if let Some(queue) = ctx.root_escalation_queue() && queue.has_pending() { @@ -666,6 +680,9 @@ async fn handle_collect(ctx: &RequestContext, args: &Value) -> Result { tokio::select! { _ = time::sleep(Duration::from_millis(200)) => {} _ = wait_abort_signal(&target_abort) => {} + _ = wait_user_interrupt(ctx.session_abort.as_ref()) => { + return Ok(interrupted()); + } } } @@ -2140,6 +2157,74 @@ mod tests { }); } + #[test] + fn handle_collect_session_interrupt_returns_and_keeps_job() { + run_async(async { + let mut ctx = ctx_with_job_supervisor(4); + let state = Arc::new(Mutex::new(JobState { + status: JobStatus::Running, + pgid: None, + })); + let (tx, rx) = tokio::sync::oneshot::channel::<()>(); + let task_state = Arc::clone(&state); + let join_handle = tokio::spawn(async move { + let _ = rx.await; + task_state.lock().status = JobStatus::Completed; + Ok(JobResult { + output: json!("late result"), + exit_code: Some(0), + output_bytes_captured: 0, + }) + }); + let handle = JobHandle { + id: "j1".to_string(), + tool: "execute_command".to_string(), + started_at: Instant::now(), + join_handle, + abort_signal: create_abort_signal(), + state, + output_buf: Arc::new(Mutex::new(RingBuf::default())), + no_change_checks: 0, + last_check_state: None, + }; + ctx.supervisor + .as_ref() + .unwrap() + .write() + .register(handle) + .unwrap(); + + let session = create_abort_signal(); + session.set_ctrlc(); + ctx.session_abort = Some(session.clone()); + + let result = time::timeout( + Duration::from_secs(2), + handle_collect(&ctx, &json!({"id": "j1"})), + ) + .await + .expect("interrupted collect must return promptly") + .unwrap(); + + assert_eq!(result["status"], "interrupted"); + assert_eq!(result["id"], "j1"); + assert!( + result["message"] + .as_str() + .unwrap() + .contains("still running") + ); + assert!(ctx.supervisor.as_ref().unwrap().read().has_job("j1")); + + session.reset(); + tx.send(()).unwrap(); + + let collected = handle_collect(&ctx, &json!({"id": "j1"})).await.unwrap(); + assert_eq!(collected["status"], "completed"); + assert_eq!(collected["result"], "late result"); + }); + } + #[test] fn handle_collect_full_result_returns_uncapped() { run_async(async { diff --git a/src/main.rs b/src/main.rs index 7181aa3..6db2fa9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -564,6 +564,7 @@ async fn start_directive( abort_signal: AbortSignal, ) -> Result<()> { let app: Arc = Arc::clone(&ctx.app.config); + ctx.session_abort = Some(abort_signal.clone()); if graph::active_agent_graph_name(ctx).is_some() { ctx.before_chat_completion(&input)?; diff --git a/src/repl/mod.rs b/src/repl/mod.rs index 847e896..a57efd9 100644 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -1432,6 +1432,7 @@ async fn ask( if input.is_empty() { return Ok(()); } + ctx.session_abort = Some(abort_signal.clone()); if with_embeddings { input.use_embeddings(abort_signal.clone()).await?; } diff --git a/src/utils/abort_signal.rs b/src/utils/abort_signal.rs index 36f4bf1..eebca96 100644 --- a/src/utils/abort_signal.rs +++ b/src/utils/abort_signal.rs @@ -68,6 +68,29 @@ pub async fn wait_abort_signal(abort_signal: &AbortSignal) { } } +/// Completes when the user interrupts: a SIGINT arrives, or the session's turn +/// signal is (or becomes) aborted. Needed because tools run in cooked mode -- +/// tokio's global SIGINT handler (installed by the first `ctrl_c()` poll) +/// swallows the signal with nobody listening. On SIGINT, sets ctrl-c on the +/// session signal so the surrounding turn aborts too. With `None`, only the +/// SIGINT arm applies. +pub async fn wait_user_interrupt(session: Option<&AbortSignal>) { + let ctrl_c = async { + if tokio::signal::ctrl_c().await.is_err() { + std::future::pending::<()>().await; + } + }; + match session { + Some(signal) => { + tokio::select! { + _ = ctrl_c => signal.set_ctrlc(), + _ = wait_abort_signal(signal) => {} + } + } + None => ctrl_c.await, + } +} + pub fn poll_abort_signal(abort_signal: &AbortSignal) -> Result { if event::poll(Duration::from_millis(25))? && let Event::Key(key) = event::read()? @@ -86,3 +109,18 @@ pub fn poll_abort_signal(abort_signal: &AbortSignal) -> Result { } Ok(false) } + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn wait_user_interrupt_returns_promptly_on_preset_session_signal() { + let signal = create_abort_signal(); + signal.set_ctrlc(); + + tokio::time::timeout(Duration::from_secs(1), wait_user_interrupt(Some(&signal))) + .await + .expect("must return promptly when the session signal is already set"); + } +} From b50b033520c8c1cd4379f6ac60e4f76bc0e54489 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 13:09:42 -0600 Subject: [PATCH 22/30] style: Removed redundant comments --- src/config/request_context.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/config/request_context.rs b/src/config/request_context.rs index edae565..f0e4b45 100644 --- a/src/config/request_context.rs +++ b/src/config/request_context.rs @@ -338,9 +338,6 @@ pub struct RequestContext { pub last_continuation_response: Option, pub pending_prefill: Option, - /// The turn's abort signal, set by the REPL/cmd entry points. Lets blocking - /// tools (job__collect, agent__collect) notice a user interrupt -- SIGINT is - /// otherwise swallowed in cooked mode during tool execution. pub session_abort: Option, pub render_mode: RenderMode, From 154330c5a0c5c4345ed7f93fdb230d1dc90628af Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 13:25:37 -0600 Subject: [PATCH 23/30] fix: resolve duplicate model ids by requested type --- src/client/model.rs | 45 ++++++++++++++++++++++++++++++----- src/config/request_context.rs | 4 +++- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/client/model.rs b/src/client/model.rs index 51d3a20..6d9408e 100644 --- a/src/client/model.rs +++ b/src/client/model.rs @@ -65,12 +65,16 @@ impl Model { }; match model_name { Some(model_name) => { - if let Some(model) = models.iter().find(|v| v.id() == model_id) { - if model.model_type() == model_type { - return Ok((*model).clone()); - } else { - bail!("Model '{model_id}' is not a {model_type} model") - } + // Ids are not unique across types (e.g. a model that is both + // an embedder and a reranker), so match on id AND type. + if let Some(model) = models + .iter() + .find(|v| v.id() == model_id && v.model_type() == model_type) + { + return Ok((*model).clone()); + } + if models.iter().any(|v| v.id() == model_id) { + bail!("Model '{model_id}' is not a {model_type} model") } if list_client_names(config) .into_iter() @@ -458,6 +462,35 @@ mod tests { assert!(model.data.require_max_tokens); } + #[test] + fn duplicate_id_across_types_resolves_by_requested_type() { + // Mirrors jina:jina-colbert-v2, which is both an embedder and a + // reranker under the same id. + let claude: ClientConfig = serde_yaml::from_str( + "type: claude\nmodels:\n - name: test-dual-model\n type: embedding\n - name: test-dual-model\n type: reranker\n", + ) + .unwrap(); + let config = AppConfig { + clients: vec![claude], + ..AppConfig::default() + }; + + let embedder = + Model::retrieve_model(&config, "claude:test-dual-model", ModelType::Embedding).unwrap(); + assert_eq!(embedder.model_type(), ModelType::Embedding); + + let reranker = + Model::retrieve_model(&config, "claude:test-dual-model", ModelType::Reranker).unwrap(); + assert_eq!(reranker.model_type(), ModelType::Reranker); + + // An id that exists only under other types still teaches instead of + // silently creating a chat model from the name. + let err = Model::retrieve_model(&config, "claude:test-dual-model", ModelType::Chat) + .unwrap_err() + .to_string(); + assert!(err.contains("is not a chat model"), "got: {err}"); + } + #[test] fn description_shows_brain_glyph() { let mut model = Model::new("claude", "some-model"); diff --git a/src/config/request_context.rs b/src/config/request_context.rs index f0e4b45..e2f2547 100644 --- a/src/config/request_context.rs +++ b/src/config/request_context.rs @@ -5263,8 +5263,10 @@ mod tests { // It deliberately carries one embedding model: an empty `models` list // would fall back to the full embedded claude catalog and register // real chat models, breaking the no-chat-model invariant above. + // The test-dual-model pair shares one id across two types, mirroring + // jina:jina-colbert-v2, for the type-aware lookup tests. let claude: ClientConfig = serde_yaml::from_str( - "type: claude\nmodels:\n - name: test-claude-embedder\n type: embedding\n", + "type: claude\nmodels:\n - name: test-claude-embedder\n type: embedding\n - name: test-dual-model\n type: embedding\n - name: test-dual-model\n type: reranker\n", ) .unwrap(); let config = AppConfig { From 67cf8e42d90e57d9b3d5d9ece07172c3edcc159f Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 13:38:10 -0600 Subject: [PATCH 24/30] docs: removed trailing whitespace in models.yaml generated header --- src/client/catalog.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/catalog.rs b/src/client/catalog.rs index b6b8456..29ac6ed 100644 --- a/src/client/catalog.rs +++ b/src/client/catalog.rs @@ -508,7 +508,7 @@ pub fn backfill_embedded_floor(build: &mut CatalogBuild, embedded: &[ProviderMod build.providers = providers; } -const GENERATED_HEADER: &str = "# GENERATED by 'coyote --generate-models' \n#DO NOT hand-edit model data.\n\ +const GENERATED_HEADER: &str = "# GENERATED by 'coyote --generate-models'\n# DO NOT hand-edit model data.\n\ # Hand-owned behavior lives in quirks.yaml; this file is the embedded offline fallback.\n"; pub async fn generate_models_file(path: &Path) -> Result<()> { From 74b8c17392d6d83cdc88ee89e5218e89e4b5ffa2 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 13:48:02 -0600 Subject: [PATCH 25/30] perf: probe models-override staleness in the background refresh task --- src/config/mod.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 96ac24a..36d16ae 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -657,15 +657,18 @@ pub fn maybe_spawn_models_refresh(app: &AppConfig) { if !app.sync_models_auto || app.dry_run { return; } - let loadable = paths::local_models_override().is_ok(); - let mtime = fs::metadata(paths::models_override_file()) - .and_then(|metadata| metadata.modified()) - .ok(); - if !models_override_is_stale(loadable, mtime, app.sync_models_ttl_hours) { - return; - } + let ttl_hours = app.sync_models_ttl_hours; let url = app.sync_models_url.clone(); tokio::spawn(async move { + // The loadable probe deserializes the whole override catalog; keep it + // off the startup path. + let loadable = paths::local_models_override().is_ok(); + let mtime = fs::metadata(paths::models_override_file()) + .and_then(|metadata| metadata.modified()) + .ok(); + if !models_override_is_stale(loadable, mtime, ttl_hours) { + return; + } if let Err(err) = sync_models_quiet(url.as_deref()).await { debug!("Background models refresh failed: {err}"); } From 4e358667406c36de7974b5a3859a5dbba2d0af7e Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 13:48:02 -0600 Subject: [PATCH 26/30] chore: align the models.yaml header with the current generator --- models.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models.yaml b/models.yaml index a368855..d302d1c 100644 --- a/models.yaml +++ b/models.yaml @@ -1,4 +1,5 @@ -# GENERATED by 'coyote --generate-models' — DO NOT hand-edit model data. +# GENERATED by 'coyote --generate-models' +# DO NOT hand-edit model data. # Hand-owned behavior lives in quirks.yaml; this file is the embedded offline fallback. - provider: openai models: From 9a5f587e7438f08329cb4456426db0acf6a65999 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 14:09:14 -0600 Subject: [PATCH 27/30] test: deflake windows graph script tests python3 first-start on fresh Windows runners (AV interpreter scans) blew the 5s/10s/30s script timeouts. Drop the incidental python3 spawns from the bash fixtures (pure-bash JSON extraction) and give the genuinely python tests 60s headroom. --- src/graph/executor.rs | 1 + src/graph/script.rs | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/graph/executor.rs b/src/graph/executor.rs index 0d63daf..9a178f9 100644 --- a/src/graph/executor.rs +++ b/src/graph/executor.rs @@ -695,6 +695,7 @@ nodes: type: script script: doubler.py state_updates: {} + timeout: 60 done: type: end output: "{{doubled}}" diff --git a/src/graph/script.rs b/src/graph/script.rs index 9d41c4c..688d6ad 100644 --- a/src/graph/script.rs +++ b/src/graph/script.rs @@ -262,13 +262,16 @@ echo '{"quality": 0.85, "issues": 3, "_next": "approve"}' #[tokio::test] async fn bash_script_can_read_state_from_env() { - if !cmd_available("bash") || !cmd_available("python3") { - eprintln!("skipping: bash or python3 not available"); + if !cmd_available("bash") { + eprintln!("skipping: bash not available"); return; } + // Pure-bash JSON extraction: spawning python3 here blew tight + // timeouts on Windows CI (first-start AV scans of the interpreter). let (dir, path) = write_script( r#"#!/bin/bash -NAME=$(python3 -c 'import json,os; print(json.loads(os.environ["GRAPH_STATE"])["name"])') +t=${GRAPH_STATE#*'"name":"'} +NAME=${t%%'"'*} printf '{"greeting": "hello %s"}' "$NAME" "#, "sh", @@ -280,7 +283,7 @@ printf '{"greeting": "hello %s"}' "$NAME" let _ = executor .execute( - &node_for(path.file_name().unwrap().to_str().unwrap(), 5), + &node_for(path.file_name().unwrap().to_str().unwrap(), 30), &mut state, ) .await @@ -466,7 +469,7 @@ echo '{"ok":true}' #[tokio::test] async fn large_state_is_delivered_via_file_env_var() { - if !cmd_available("bash") || !cmd_available("python3") { + if !cmd_available("bash") { return; } let big = "x".repeat(MAX_STATE_SIZE_BYTES + 1024); @@ -476,7 +479,10 @@ echo '{"ok":true}' let (dir, path) = write_script( r#"#!/bin/bash if [ -n "$GRAPH_STATE_FILE" ]; then - LEN=$(python3 -c 'import json,os; print(len(json.load(open(os.environ["GRAPH_STATE_FILE"]))["blob"]))') + CONTENT=$(cat "$GRAPH_STATE_FILE") + t=${CONTENT#*'"blob":"'} + BLOB=${t%%'"'*} + LEN=${#BLOB} printf '{"blob_len": %s, "via_file": true}' "$LEN" elif [ -n "$GRAPH_STATE" ]; then echo '{"via_file": false}' @@ -489,7 +495,7 @@ fi let executor = ScriptExecutor::new(&dir); executor .execute( - &node_for(path.file_name().unwrap().to_str().unwrap(), 10), + &node_for(path.file_name().unwrap().to_str().unwrap(), 30), &mut state, ) .await @@ -524,7 +530,7 @@ print(json.dumps({ let executor = ScriptExecutor::new(&dir); let next = executor .execute( - &node_for(path.file_name().unwrap().to_str().unwrap(), 5), + &node_for(path.file_name().unwrap().to_str().unwrap(), 60), &mut state, ) .await From 101b706be533bafad9f459d471121d3333ec9cfd Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 15:12:49 -0600 Subject: [PATCH 28/30] fix: session replays now include mid-turn messages so the full session gets replayed --- src/repl/replay.rs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/repl/replay.rs b/src/repl/replay.rs index 79a02c8..fb9f9e9 100644 --- a/src/repl/replay.rs +++ b/src/repl/replay.rs @@ -1,6 +1,6 @@ use anyhow::Result; -use crate::client::{Message, MessageRole}; +use crate::client::{Message, MessageContent, MessageContentToolCalls, MessageRole}; use crate::config::{AppConfig, Session}; use crate::utils::{dimmed_text, replay_label_text}; @@ -52,9 +52,44 @@ fn render_messages(app: &AppConfig, messages: &[Message]) -> Result<()> { println!(); } } + MessageRole::Tool => { + if let MessageContent::ToolCalls(tool_calls) = &message.content { + render_tool_call_rounds(app, tool_calls)?; + } + } _ => {} } } Ok(()) } + +fn render_tool_call_rounds(app: &AppConfig, tool_calls: &MessageContentToolCalls) -> Result<()> { + let mut needs_gap_after_calls = false; + if !tool_calls.text.trim().is_empty() { + println!("{}", replay_label_text("Assistant:")); + app.print_markdown(&tool_calls.text)?; + println!(); + } + + for result in &tool_calls.tool_results { + if let Some(text) = result.text.as_deref().filter(|t| !t.trim().is_empty()) { + if needs_gap_after_calls { + println!(); + } + + println!("{}", replay_label_text("Assistant:")); + app.print_markdown(text)?; + println!(); + } + + println!("{}", dimmed_text(&format!("⚙ {}", result.call.name))); + needs_gap_after_calls = true; + } + + if needs_gap_after_calls { + println!(); + } + + Ok(()) +} From cb5b46800f61e5300ffdd5ccd64927ce4c3b4e57 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 15:50:00 -0600 Subject: [PATCH 29/30] feat: support auto continuation in subagents and prevent them from calling todo__pause --- src/config/agent.rs | 5 ++ src/config/macros.rs | 1 + src/config/prompts.rs | 3 + src/config/request_context.rs | 17 +++++ src/function/agents.rs | 124 +++++++++++++++++++++++++++++++++- src/function/mod.rs | 78 +++++++++++++++++++++ src/function/todo.rs | 59 ++++++++++++++++ src/repl/mod.rs | 21 +++++- 8 files changed, 305 insertions(+), 3 deletions(-) diff --git a/src/config/agent.rs b/src/config/agent.rs index 87d6fe6..09540f8 100644 --- a/src/config/agent.rs +++ b/src/config/agent.rs @@ -648,6 +648,11 @@ impl Agent { enabled_tools: None, } } + + #[cfg(test)] + pub fn functions_mut(&mut self) -> &mut Functions { + &mut self.functions + } } impl RoleLike for Agent { diff --git a/src/config/macros.rs b/src/config/macros.rs index c2c160d..8ae1f1c 100644 --- a/src/config/macros.rs +++ b/src/config/macros.rs @@ -74,6 +74,7 @@ pub async fn macro_execute( macro_ctx.current_depth = ctx.current_depth; macro_ctx.auto_continue_count = ctx.auto_continue_count; macro_ctx.todo_list = ctx.todo_list.clone(); + macro_ctx.auto_continue_paused = ctx.auto_continue_paused.clone(); macro_ctx.tool_scope.tool_tracker = ctx.tool_scope.tool_tracker.clone(); macro_ctx.discontinuous_last_message(); diff --git a/src/config/prompts.rs b/src/config/prompts.rs index fa7d908..4baa732 100644 --- a/src/config/prompts.rs +++ b/src/config/prompts.rs @@ -63,11 +63,14 @@ pub(in crate::config) const DEFAULT_TODO_INSTRUCTIONS: &str = indoc! {" - `todo__done`: Mark a task done by id. Call this immediately after completing each step. - `todo__list`: Show the current todo list. - `todo__clear`: Clear the entire todo list and reset the goal. Use when the user cancels or changes direction. + - `todo__pause`: Pause auto-continue and yield the turn to the user, keeping incomplete todos intact. Use when you are blocked on user input or a user-side action. Auto-continue resumes on the user's next message. RULES: - Always create a todo list before starting work. - Mark each task done as soon as you finish it; do not batch. - If the user cancels the current task or changes direction, call `todo__clear` immediately. + - If you need user input (or the user must do something on their end) before you can continue, call `todo__pause` with the reason, ask the user, and end your turn. NEVER mark unfinished todos done just to stop the auto-continue prompts. + - `todo__pause` is for top-level sessions only. Subagents are driven to completion autonomously and cannot pause. If you are a subagent and need input, use a `user__*` tool (it escalates to the root agent mid-turn and blocks until answered, so you keep working without ending your turn). - If you stop with incomplete tasks, the system will automatically prompt you to continue." }; diff --git a/src/config/request_context.rs b/src/config/request_context.rs index e2f2547..9b4682d 100644 --- a/src/config/request_context.rs +++ b/src/config/request_context.rs @@ -336,6 +336,7 @@ pub struct RequestContext { pub todo_list: TodoList, pub skill_registry: SkillRegistry, pub last_continuation_response: Option, + pub auto_continue_paused: Option, pub pending_prefill: Option, pub session_abort: Option, @@ -376,6 +377,7 @@ impl RequestContext { todo_list: TodoList::default(), skill_registry: SkillRegistry::default(), last_continuation_response: None, + auto_continue_paused: None, pending_prefill: None, session_abort: None, render_mode: RenderMode::default(), @@ -441,6 +443,7 @@ impl RequestContext { todo_list: TodoList::default(), skill_registry: SkillRegistry::default(), last_continuation_response: None, + auto_continue_paused: None, pending_prefill: None, session_abort: None, render_mode: RenderMode::default(), @@ -495,6 +498,7 @@ impl RequestContext { todo_list: self.todo_list.clone(), skill_registry: self.skill_registry.clone(), last_continuation_response: None, + auto_continue_paused: self.auto_continue_paused.clone(), pending_prefill: None, session_abort: self.session_abort.clone(), render_mode: self.render_mode, @@ -545,6 +549,7 @@ impl RequestContext { todo_list: TodoList::default(), skill_registry: SkillRegistry::default(), last_continuation_response: None, + auto_continue_paused: None, pending_prefill: None, session_abort: None, render_mode: parent.render_mode, @@ -582,6 +587,7 @@ impl RequestContext { pub fn init_todo_list(&mut self, goal: &str) { self.todo_list = TodoList::new(goal); + self.auto_continue_paused = None; } pub fn add_todo(&mut self, task: &str) -> usize { @@ -595,12 +601,21 @@ impl RequestContext { pub fn clear_todo_list(&mut self) { self.todo_list.clear(); self.auto_continue_count = 0; + self.auto_continue_paused = None; } pub fn increment_auto_continue_count(&mut self) { self.auto_continue_count += 1; } + pub fn pause_auto_continue(&mut self, reason: &str) { + self.auto_continue_paused = Some(reason.to_string()); + } + + pub fn resume_auto_continue(&mut self) { + self.auto_continue_paused = None; + } + pub fn reset_continuation_count(&mut self) { self.auto_continue_count = 0; self.last_continuation_response = None; @@ -4676,6 +4691,7 @@ impl RequestContext { self.current_depth = 0; self.auto_continue_count = 0; self.todo_list = TodoList::default(); + self.auto_continue_paused = None; if let Some(session_name) = session_name.as_deref() { self.use_session(app, Some(session_name), abort_signal) @@ -4719,6 +4735,7 @@ impl RequestContext { self.auto_continue_count = 0; self.pending_tasks_guardrail_count = 0; self.todo_list = TodoList::default(); + self.auto_continue_paused = None; self.rag.take(); // Cleared alongside `rag` so the pair never disagrees: an agent RAG is // cached under `RagKey::Agent()`, and leaving that key behind diff --git a/src/function/agents.rs b/src/function/agents.rs index a03f49a..1b753c9 100644 --- a/src/function/agents.rs +++ b/src/function/agents.rs @@ -1,3 +1,4 @@ +use super::todo::TODO_FUNCTION_PREFIX; use super::{FunctionDeclaration, JsonSchema}; use crate::client::{Model, ModelType, call_chat_completions}; use crate::config::{ @@ -10,6 +11,7 @@ use crate::supervisor::{AgentExitStatus, AgentHandle, AgentResult, Supervisor, T use crate::utils::{AbortSignal, create_abort_signal, wait_abort_signal, wait_user_interrupt}; use crate::graph; +use crate::repl::DEFAULT_CONTINUATION_PROMPT; use anyhow::{Context, Result, anyhow, bail}; use chrono::Utc; use indexmap::IndexMap; @@ -574,7 +576,13 @@ pub fn run_child_agent( if tool_results.is_empty() { match check_pending_tasks_guardrail(&mut child_ctx) { - GuardrailAction::NoAction => break, + GuardrailAction::NoAction => { + if let Some(prompt) = todo_continuation_prompt(&mut child_ctx) { + input = Input::from_str(&child_ctx, &prompt, None)?; + continue; + } + break; + } GuardrailAction::ForceTerminate(ids) => { warn!( "Pending-agent guardrail force-cancelled {} agent(s) after max reminders: {:?}", @@ -601,6 +609,33 @@ pub fn run_child_agent( }) } +fn todo_continuation_prompt(ctx: &mut RequestContext) -> Option { + let config = ctx.auto_continue_config(); + if !ctx.app.config.function_calling_support + || !config.enabled + || ctx.auto_continue_count >= config.max_continues + || !ctx.todo_list.has_incomplete() + || ctx.auto_continue_paused.is_some() + { + return None; + } + + ctx.increment_auto_continue_count(); + debug!( + "Auto-continuing child agent ({}/{}): {} incomplete todo(s) remain", + ctx.auto_continue_count, + config.max_continues, + ctx.todo_list.incomplete_count() + ); + + let prompt = config + .continuation_prompt + .as_deref() + .unwrap_or(DEFAULT_CONTINUATION_PROMPT); + + Some(format!("{prompt}\n\n{}", ctx.todo_list.render_for_model())) +} + /// Spawn an agent synchronously from a graph node and return its accumulated /// output. This is similar to `handle_spawn` but runs the child agent in the /// current task (no tokio::spawn, no supervisor handle registration) so the @@ -749,6 +784,14 @@ fn sync_agent_functions_to_ctx(ctx: &mut RequestContext) -> Result<()> { }; ctx.tool_scope.functions = functions; + // Agent::init builds one function set regardless of how the agent runs; + // this sync is only ever called for spawned children. + if ctx.self_agent_id.is_some() { + ctx.tool_scope + .functions + .remove_function(&format!("{TODO_FUNCTION_PREFIX}pause")); + } + Ok(()) } @@ -1650,7 +1693,7 @@ async fn summarize_output(ctx: &RequestContext, agent_name: &str, output: &str) mod tests { use super::*; use crate::config::test_fixtures::{FixtureServer, fixture_runtime}; - use crate::config::{AgentConfig, AppState, WorkingMode}; + use crate::config::{AgentConfig, AppConfig, AppState, WorkingMode}; use crate::function::jobs::RingBuf; use crate::mcp::{McpServer, McpServersConfig, McpTransportType}; use crate::supervisor::escalation::{EscalationQueue, EscalationRequest}; @@ -1673,6 +1716,58 @@ mod tests { ctx } + fn auto_continue_ctx() -> RequestContext { + let app = AppState { + config: Arc::new(AppConfig { + auto_continue: true, + ..Default::default() + }), + ..AppState::test_default() + }; + RequestContext::new(Arc::new(app), WorkingMode::Cmd) + } + + #[test] + fn todo_continuation_fires_for_child_with_incomplete_todos() { + let mut ctx = auto_continue_ctx(); + ctx.init_todo_list("ship feature"); + ctx.add_todo("build the thing"); + + let prompt = todo_continuation_prompt(&mut ctx).expect("child should auto-continue"); + + assert!(prompt.contains("TODO CONTINUATION"), "{prompt}"); + assert!(prompt.contains("build the thing"), "{prompt}"); + assert_eq!(ctx.auto_continue_count, 1); + } + + #[test] + fn todo_continuation_stops_when_done_paused_capped_or_disabled() { + // All todos complete → run may end. + let mut ctx = auto_continue_ctx(); + ctx.init_todo_list("ship feature"); + let id = ctx.add_todo("build the thing"); + ctx.mark_todo_done(id); + assert!(todo_continuation_prompt(&mut ctx).is_none()); + + // Defense-in-depth: children cannot call todo__pause, but a set flag + // must still stop the continuation loop. + ctx.add_todo("deploy the thing"); + ctx.pause_auto_continue("blocked on credentials"); + assert!(todo_continuation_prompt(&mut ctx).is_none()); + ctx.resume_auto_continue(); + assert!(todo_continuation_prompt(&mut ctx).is_some()); + + // Continuation cap reached → run may end. + ctx.auto_continue_count = ctx.auto_continue_config().max_continues; + assert!(todo_continuation_prompt(&mut ctx).is_none()); + + // Auto-continue disabled → run may end even with incomplete todos. + let mut ctx = RequestContext::new(default_app_state(), WorkingMode::Cmd); + ctx.init_todo_list("ship feature"); + ctx.add_todo("build the thing"); + assert!(todo_continuation_prompt(&mut ctx).is_none()); + } + fn ctx_with_job_capable_supervisor() -> RequestContext { let mut ctx = RequestContext::new(default_app_state(), WorkingMode::Cmd); ctx.supervisor = Some(Arc::new(RwLock::new( @@ -1840,6 +1935,31 @@ mod tests { assert!(!functions.contains("mcp_invoke_fixture")); } + /// `Agent::init` registers the full todo tool set (incl. `todo__pause`) + /// whenever the agent config enables auto_continue, because the same + /// function set serves top-level `--agent` sessions. Spawned children + /// must never advertise `todo__pause` (they are driven to completion + /// autonomously), so the child-only sync strips that one declaration + /// while leaving the rest of the todo tools intact. + #[tokio::test] + async fn sync_strips_todo_pause_declaration_for_child_contexts() { + let mut ctx = RequestContext::new(default_app_state(), WorkingMode::Cmd); + let mut agent = Agent::test_new(AgentConfig::default()); + agent.functions_mut().append_todo_functions(); + ctx.agent = Some(agent); + ctx.self_agent_id = Some("agent_sisyphus_abc123".to_string()); + + sync_agent_functions_to_ctx(&mut ctx).unwrap(); + + let functions = &ctx.tool_scope.functions; + assert!(!functions.contains("todo__pause")); + assert!(functions.contains("todo__init")); + assert!(functions.contains("todo__add")); + assert!(functions.contains("todo__done")); + assert!(functions.contains("todo__list")); + assert!(functions.contains("todo__clear")); + } + fn app_state_with_fixture_mcp_config() -> Arc { let mut state = AppState::test_default(); state.mcp_config = Some(McpServersConfig { diff --git a/src/function/mod.rs b/src/function/mod.rs index 66dadd2..5511ea5 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -727,6 +727,10 @@ impl Functions { .retain(|f| !f.name.starts_with(TODO_FUNCTION_PREFIX)); } + pub fn remove_function(&mut self, name: &str) { + self.declarations.retain(|f| f.name != name); + } + pub fn append_memory_functions(&mut self) { self.declarations .extend(memory::memory_function_declarations()); @@ -3371,6 +3375,7 @@ mod tests { assert!(f.contains("todo__done")); assert!(f.contains("todo__list")); assert!(f.contains("todo__clear")); + assert!(f.contains("todo__pause")); } #[test] @@ -4914,6 +4919,79 @@ mod tests { assert!(err.contains("Auto-continue is not enabled"), "{err}"); } + #[test] + fn todo_pause_sets_and_clears_pause_flag() { + let app = AppState { + config: Arc::new(AppConfig { + auto_continue: true, + ..Default::default() + }), + ..AppState::test_default() + }; + let mut ctx = RequestContext::new(Arc::new(app), WorkingMode::Cmd); + ctx.tool_scope.functions.append_todo_functions(); + + // A blank reason is rejected and does not pause. + let out = run_async(call_with_args("todo__pause", json!({"reason": " "})).eval(&mut ctx)) + .unwrap(); + assert_eq!(out["error"], "reason is required"); + assert!(ctx.auto_continue_paused.is_none()); + + // A real reason pauses auto-continue, preserving the todo list. + run_async(call_with_args("todo__init", json!({"goal": "ship"})).eval(&mut ctx)).unwrap(); + run_async(call_with_args("todo__add", json!({"task": "deploy"})).eval(&mut ctx)).unwrap(); + let out = run_async( + call_with_args("todo__pause", json!({"reason": "need prod API key"})).eval(&mut ctx), + ) + .unwrap(); + assert_eq!(out["status"], "ok"); + assert_eq!( + ctx.auto_continue_paused.as_deref(), + Some("need prod API key") + ); + assert!(ctx.todo_list.has_incomplete()); + + // The next real user message lifts the pause. + ctx.resume_auto_continue(); + assert!(ctx.auto_continue_paused.is_none()); + + // Re-initializing or clearing the list also lifts a stale pause. + ctx.pause_auto_continue("stale"); + run_async(call_with_args("todo__init", json!({"goal": "new"})).eval(&mut ctx)).unwrap(); + assert!(ctx.auto_continue_paused.is_none()); + ctx.pause_auto_continue("stale again"); + run_async(call_with_args("todo__clear", json!({})).eval(&mut ctx)).unwrap(); + assert!(ctx.auto_continue_paused.is_none()); + } + + /// Subagents are driven to completion autonomously: their turn boundary + /// has no interactive user, and user__* escalations work mid-turn, so + /// pausing is never the right move for a child. The handler must reject + /// the call, leave the pause flag unset, and redirect to escalation. + #[test] + fn todo_pause_is_rejected_for_subagents() { + let app = AppState { + config: Arc::new(AppConfig { + auto_continue: true, + ..Default::default() + }), + ..AppState::test_default() + }; + let mut ctx = RequestContext::new(Arc::new(app), WorkingMode::Cmd); + ctx.tool_scope.functions.append_todo_functions(); + ctx.self_agent_id = Some("agent_sisyphus_abc123".to_string()); + + let out = run_async( + call_with_args("todo__pause", json!({"reason": "need user input"})).eval(&mut ctx), + ) + .unwrap(); + + let err = out["error"].as_str().unwrap(); + assert!(err.contains("unavailable to subagents"), "{err}"); + assert!(err.contains("user__"), "{err}"); + assert!(ctx.auto_continue_paused.is_none()); + } + #[test] fn eval_routes_memory_prefix_to_memory_handler() { let mut ctx = RequestContext::new(Arc::new(AppState::test_default()), WorkingMode::Cmd); diff --git a/src/function/todo.rs b/src/function/todo.rs index 9415849..3f8f4da 100644 --- a/src/function/todo.rs +++ b/src/function/todo.rs @@ -86,6 +86,33 @@ pub fn todo_function_declarations() -> Vec { }, agent: false, }, + FunctionDeclaration { + name: format!("{TODO_FUNCTION_PREFIX}pause"), + description: "Pause auto-continue and yield the turn back to the user while keeping \ + incomplete todos intact. Use when you cannot proceed without user input \ + or a user-side action. NEVER mark unfinished todos as done just to stop \ + auto-continuation. Auto-continue resumes automatically on the user's \ + next message. Unavailable to subagents: they are driven to completion \ + autonomously — a subagent needing input must use a user__* tool, which \ + escalates to the root agent mid-turn and blocks until answered." + .to_string(), + parameters: JsonSchema { + type_value: Some("object".to_string()), + properties: Some(IndexMap::from([( + "reason".to_string(), + JsonSchema { + type_value: Some("string".to_string()), + description: Some( + "What you are waiting on from the user (shown to the user)".into(), + ), + ..Default::default() + }, + )])), + required: Some(vec!["reason".to_string()]), + ..Default::default() + }, + agent: false, + }, ] } @@ -149,6 +176,38 @@ pub fn handle_todo_tool(ctx: &mut RequestContext, cmd_name: &str, args: &Value) ctx.clear_todo_list(); Ok(json!({"status": "ok", "message": "Todo list cleared"})) } + "pause" => { + if ctx.self_agent_id.is_some() { + return Ok(json!({ + "error": "todo__pause is unavailable to subagents: you are driven to \ + completion autonomously and there is no interactive user at your \ + turn boundary. If you need input, ask via a user__* tool — it \ + escalates to the root agent mid-turn and blocks until answered, so \ + you can keep working. If you are blocked on an action outside your \ + scope, finish what you can and report the blocker in your final \ + output." + })); + } + + let reason = args + .get("reason") + .and_then(Value::as_str) + .unwrap_or_default() + .trim() + .to_string(); + if reason.is_empty() { + return Ok(json!({"error": "reason is required"})); + } + + ctx.pause_auto_continue(&reason); + + Ok(json!({ + "status": "ok", + "message": "Auto-continue paused; incomplete todos are preserved. Tell the user \ + what you need from them, then end your turn. Auto-continue resumes \ + on their next message." + })) + } _ => bail!("Unknown todo action: {action}"), } } diff --git a/src/repl/mod.rs b/src/repl/mod.rs index a57efd9..cf53aeb 100644 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -52,7 +52,8 @@ pub const DEFAULT_CONTINUATION_PROMPT: &str = indoc! {" 1. BEFORE marking a todo done: verify the work compiles/works. No premature completion. 2. If a todo is broad (e.g. \"implement X and implement Y\"): break it into specific subtasks FIRST using todo__add, then work on those.\n\ 3. Each todo should be atomic and be \"single responsibility\" - completable in one focused action. - 4. Continue with the next pending item now. Call tools immediately." + 4. If you are blocked on user input or a user-side action: call todo__pause with the reason, ask the user, and end your turn. NEVER mark unfinished todos done just to stop this reminder. + 5. Otherwise, continue with the next pending item now. Call tools immediately." }; static REPL_COMMANDS: LazyLock<[ReplCommand; 63]> = LazyLock::new(|| { @@ -1409,6 +1410,7 @@ pub async fn run_repl_command( handle_shell_passthrough(cmd)?; } else { reset_continuation(ctx); + ctx.resume_auto_continue(); let input = Input::from_str(ctx, line, None)?; ask(ctx, abort_signal.clone(), input, true).await?; } @@ -1535,6 +1537,22 @@ async fn ask( ask(ctx, abort_signal, continuation_input, false).await } else { reset_continuation(ctx); + if let Some(reason) = ctx.auto_continue_paused.clone() + && ctx.todo_list.has_incomplete() + { + let remaining = ctx.todo_list.incomplete_count(); + let color = if app.light_theme() { + nu_ansi_term::Color::LightGray + } else { + nu_ansi_term::Color::DarkGray + }; + eprintln!( + "\n⏸ {}", + color.italic().paint(format!( + "Auto-continue paused, awaiting user ({remaining} incomplete todo(s) preserved): {reason}" + )) + ); + } if ctx.maybe_autoname_session() { let color = if app.light_theme() { nu_ansi_term::Color::LightGray @@ -1621,6 +1639,7 @@ fn should_continue(ctx: &RequestContext) -> bool { && config.enabled && ctx.auto_continue_count < config.max_continues && ctx.todo_list.has_incomplete() + && ctx.auto_continue_paused.is_none() } fn reset_continuation(ctx: &mut RequestContext) { From 206f36d826de060ef7ab6b50a2e54f6ae9e6bea8 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 16:09:41 -0600 Subject: [PATCH 30/30] fix: drive tool rounds iteratively instead of recursively to prevent stack overflow on long agentic turns to fix latent stack overflow recursion bug --- src/main.rs | 56 ++++----- src/repl/mod.rs | 311 ++++++++++++++++++++++++------------------------ 2 files changed, 183 insertions(+), 184 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6db2fa9..8f99d3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -556,7 +556,6 @@ async fn run( } } -#[async_recursion::async_recursion] async fn start_directive( ctx: &mut RequestContext, input: Input, @@ -576,37 +575,36 @@ async fn start_directive( return Ok(()); } - let client = input.create_client()?; let extract_code = !*IS_STDOUT_TERMINAL && code_mode; - ctx.before_chat_completion(&input)?; - let (output, tool_results) = if !input.stream() || extract_code { - call_chat_completions( - &input, - true, - extract_code, - client.as_ref(), - ctx, - abort_signal.clone(), - ) - .await? - } else { - call_chat_completions_streaming(&input, client.as_ref(), ctx, abort_signal.clone()).await? - }; - ctx.after_chat_completion(app.as_ref(), &input, &output, &tool_results)?; + let mut input = input; + loop { + let client = input.create_client()?; + ctx.before_chat_completion(&input)?; + let (output, tool_results) = if !input.stream() || extract_code { + call_chat_completions( + &input, + true, + extract_code, + client.as_ref(), + ctx, + abort_signal.clone(), + ) + .await? + } else { + call_chat_completions_streaming(&input, client.as_ref(), ctx, abort_signal.clone()) + .await? + }; + ctx.after_chat_completion(app.as_ref(), &input, &output, &tool_results)?; + + if !tool_results.is_empty() { + input = input.merge_tool_results(output, tool_results); + continue; + } - if !tool_results.is_empty() { - start_directive( - ctx, - input.merge_tool_results(output, tool_results), - code_mode, - abort_signal, - ) - .await?; - } else { match check_pending_tasks_guardrail(ctx) { GuardrailAction::Inject(prompt) => { - let guardrail_input = Input::from_str(ctx, &prompt, None)?; - return start_directive(ctx, guardrail_input, code_mode, abort_signal).await; + input = Input::from_str(ctx, &prompt, None)?; + continue; } GuardrailAction::ForceTerminate(ids) => { warn!( @@ -617,6 +615,8 @@ async fn start_directive( } GuardrailAction::NoAction => {} } + + break; } ctx.exit_session()?; diff --git a/src/repl/mod.rs b/src/repl/mod.rs index cf53aeb..3aa5a05 100644 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -1424,74 +1424,73 @@ pub async fn run_repl_command( Ok(false) } -#[async_recursion::async_recursion] async fn ask( ctx: &mut RequestContext, abort_signal: AbortSignal, - mut input: Input, + input: Input, with_embeddings: bool, ) -> Result<()> { - if input.is_empty() { - return Ok(()); - } - ctx.session_abort = Some(abort_signal.clone()); - if with_embeddings { - input.use_embeddings(abort_signal.clone()).await?; - } - while ctx.is_compressing_session() { - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - } - - let app = Arc::clone(&ctx.app.config); - - if graph::active_agent_graph_name(ctx).is_some() { - ctx.before_chat_completion(&input)?; - let output = - graph::run_active_agent_graph(ctx, &input.text(), abort_signal.clone()).await?; - app.print_markdown(&output)?; - ctx.after_chat_completion(app.as_ref(), &input, &output, &[])?; - return Ok(()); - } - - let client = input.create_client()?; - ctx.before_chat_completion(&input)?; - let (output, tool_results) = { - let result = if input.stream() { - call_chat_completions_streaming(&input, client.as_ref(), ctx, abort_signal.clone()) - .await - } else { - call_chat_completions( - &input, - true, - false, - client.as_ref(), - ctx, - abort_signal.clone(), - ) - .await - }; - match result { - Ok(v) => v, - Err(err) => { - ctx.on_chat_completion_error(app.as_ref(), &input); - return Err(err); - } + let mut input = input; + let mut with_embeddings = with_embeddings; + loop { + if input.is_empty() { + return Ok(()); } - }; - ctx.after_chat_completion(app.as_ref(), &input, &output, &tool_results)?; - if !tool_results.is_empty() { - ask( - ctx, - abort_signal, - input.merge_tool_results(output, tool_results), - false, - ) - .await - } else { + ctx.session_abort = Some(abort_signal.clone()); + if with_embeddings { + input.use_embeddings(abort_signal.clone()).await?; + with_embeddings = false; + } + while ctx.is_compressing_session() { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + + let app = Arc::clone(&ctx.app.config); + + if graph::active_agent_graph_name(ctx).is_some() { + ctx.before_chat_completion(&input)?; + let output = + graph::run_active_agent_graph(ctx, &input.text(), abort_signal.clone()).await?; + app.print_markdown(&output)?; + ctx.after_chat_completion(app.as_ref(), &input, &output, &[])?; + return Ok(()); + } + + let client = input.create_client()?; + ctx.before_chat_completion(&input)?; + let (output, tool_results) = { + let result = if input.stream() { + call_chat_completions_streaming(&input, client.as_ref(), ctx, abort_signal.clone()) + .await + } else { + call_chat_completions( + &input, + true, + false, + client.as_ref(), + ctx, + abort_signal.clone(), + ) + .await + }; + match result { + Ok(v) => v, + Err(err) => { + ctx.on_chat_completion_error(app.as_ref(), &input); + return Err(err); + } + } + }; + ctx.after_chat_completion(app.as_ref(), &input, &output, &tool_results)?; + if !tool_results.is_empty() { + input = input.merge_tool_results(output, tool_results); + continue; + } + match check_pending_tasks_guardrail(ctx) { GuardrailAction::Inject(prompt) => { - let guardrail_input = Input::from_str(ctx, &prompt, None)?; - return ask(ctx, abort_signal, guardrail_input, false).await; + input = Input::from_str(ctx, &prompt, None)?; + continue; } GuardrailAction::ForceTerminate(ids) => { warn!( @@ -1533,103 +1532,103 @@ async fn ask( format!("{prompt}\n\n{todo_state}") }; - let continuation_input = Input::from_str(ctx, &full_prompt, None)?; - ask(ctx, abort_signal, continuation_input, false).await - } else { - reset_continuation(ctx); - if let Some(reason) = ctx.auto_continue_paused.clone() - && ctx.todo_list.has_incomplete() - { - let remaining = ctx.todo_list.incomplete_count(); - let color = if app.light_theme() { - nu_ansi_term::Color::LightGray - } else { - nu_ansi_term::Color::DarkGray - }; - eprintln!( - "\n⏸ {}", - color.italic().paint(format!( - "Auto-continue paused, awaiting user ({remaining} incomplete todo(s) preserved): {reason}" - )) - ); - } - if ctx.maybe_autoname_session() { - let color = if app.light_theme() { - nu_ansi_term::Color::LightGray - } else { - nu_ansi_term::Color::DarkGray - }; - eprintln!("\n📢 {}", color.italic().paint("Autonaming the session."),); - if let Err(err) = ctx.autoname_session(app.as_ref()).await { - warn!("Failed to autonaming the session: {err}"); - } - if let Some(session) = ctx.session.as_mut() { - session.set_autonaming(false); - } - } - - let needs_compression = ctx - .session - .as_ref() - .is_some_and(|s| s.needs_compression(app.compression_threshold)); - - if needs_compression { - let agent_can_continue_after_compress = - should_continue(ctx) && !abort_signal.aborted_ctrlc(); - - if let Some(session) = ctx.session.as_mut() { - session.set_compressing(true); - } - - let color = if app.light_theme() { - nu_ansi_term::Color::LightGray - } else { - nu_ansi_term::Color::DarkGray - }; - eprintln!("\n📢 {}", color.italic().paint("Compressing the session."),); - - if let Err(err) = ctx.compress_session().await { - warn!("Failed to compress the session: {err}"); - } - if let Some(session) = ctx.session.as_mut() { - session.set_compressing(false); - } - - if agent_can_continue_after_compress { - let full_prompt = { - let config = ctx.auto_continue_config(); - let todo_state = ctx.todo_list.render_for_model(); - let remaining = ctx.todo_list.incomplete_count(); - ctx.increment_auto_continue_count(); - let count = ctx.auto_continue_count; - let max = config.max_continues; - - let prompt = config - .continuation_prompt - .as_deref() - .unwrap_or(DEFAULT_CONTINUATION_PROMPT); - - let color = if app.light_theme() { - nu_ansi_term::Color::LightGray - } else { - nu_ansi_term::Color::DarkGray - }; - eprintln!( - "\n📋 {}", - color.italic().paint(format!( - "Auto-continuing after compression ({count}/{max}): {remaining} incomplete todo(s) remain" - )) - ); - - format!("{prompt}\n\n{todo_state}") - }; - let continuation_input = Input::from_str(ctx, &full_prompt, None)?; - return ask(ctx, abort_signal, continuation_input, false).await; - } - } - - Ok(()) + input = Input::from_str(ctx, &full_prompt, None)?; + continue; } + + reset_continuation(ctx); + if let Some(reason) = ctx.auto_continue_paused.clone() + && ctx.todo_list.has_incomplete() + { + let remaining = ctx.todo_list.incomplete_count(); + let color = if app.light_theme() { + nu_ansi_term::Color::LightGray + } else { + nu_ansi_term::Color::DarkGray + }; + eprintln!( + "\n⏸ {}", + color.italic().paint(format!( + "Auto-continue paused, awaiting user ({remaining} incomplete todo(s) preserved): {reason}" + )) + ); + } + if ctx.maybe_autoname_session() { + let color = if app.light_theme() { + nu_ansi_term::Color::LightGray + } else { + nu_ansi_term::Color::DarkGray + }; + eprintln!("\n📢 {}", color.italic().paint("Autonaming the session."),); + if let Err(err) = ctx.autoname_session(app.as_ref()).await { + warn!("Failed to autonaming the session: {err}"); + } + if let Some(session) = ctx.session.as_mut() { + session.set_autonaming(false); + } + } + + let needs_compression = ctx + .session + .as_ref() + .is_some_and(|s| s.needs_compression(app.compression_threshold)); + + if needs_compression { + let agent_can_continue_after_compress = + should_continue(ctx) && !abort_signal.aborted_ctrlc(); + + if let Some(session) = ctx.session.as_mut() { + session.set_compressing(true); + } + + let color = if app.light_theme() { + nu_ansi_term::Color::LightGray + } else { + nu_ansi_term::Color::DarkGray + }; + eprintln!("\n📢 {}", color.italic().paint("Compressing the session."),); + + if let Err(err) = ctx.compress_session().await { + warn!("Failed to compress the session: {err}"); + } + if let Some(session) = ctx.session.as_mut() { + session.set_compressing(false); + } + + if agent_can_continue_after_compress { + let full_prompt = { + let config = ctx.auto_continue_config(); + let todo_state = ctx.todo_list.render_for_model(); + let remaining = ctx.todo_list.incomplete_count(); + ctx.increment_auto_continue_count(); + let count = ctx.auto_continue_count; + let max = config.max_continues; + + let prompt = config + .continuation_prompt + .as_deref() + .unwrap_or(DEFAULT_CONTINUATION_PROMPT); + + let color = if app.light_theme() { + nu_ansi_term::Color::LightGray + } else { + nu_ansi_term::Color::DarkGray + }; + eprintln!( + "\n📋 {}", + color.italic().paint(format!( + "Auto-continuing after compression ({count}/{max}): {remaining} incomplete todo(s) remain" + )) + ); + + format!("{prompt}\n\n{todo_state}") + }; + input = Input::from_str(ctx, &full_prompt, None)?; + continue; + } + } + + return Ok(()); } }