diff --git a/.github/workflows/sync-pricing.yml b/.github/workflows/sync-pricing.yml new file mode 100644 index 0000000..94c3cf5 --- /dev/null +++ b/.github/workflows/sync-pricing.yml @@ -0,0 +1,42 @@ +name: Sync Model Pricing + +on: + schedule: + # Run daily at 06:00 UTC + - cron: '0 6 * * *' + workflow_dispatch: # Allow manual trigger + +permissions: + contents: write + pull-requests: write + +jobs: + sync-pricing: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Sync pricing from OpenRouter API + run: python scripts/sync-pricing.py --create-missing + + - name: Check for changes + id: changes + run: | + git diff --quiet && echo "changed=false" >> "$GITHUB_OUTPUT" || echo "changed=true" >> "$GITHUB_OUTPUT" + + - name: Create PR if prices changed + if: steps.changes.outputs.changed == 'true' + uses: peter-evans/create-pull-request@v7 + with: + commit-message: "chore: sync model pricing from OpenRouter API" + title: "chore: sync model pricing from OpenRouter API" + body: | + Automated pricing sync from [OpenRouter API](https://openrouter.ai/api/v1/models). + + Run by `sync-pricing.yml` cron job. + branch: chore/auto-sync-pricing + delete-branch: true diff --git a/providers/aion-labs.toml b/providers/aion-labs.toml new file mode 100644 index 0000000..8503f93 --- /dev/null +++ b/providers/aion-labs.toml @@ -0,0 +1,48 @@ +# aion-labs — auto-generated from OpenRouter API + +[provider] +id = "aion-labs" +display_name = "Aion Labs" +api_key_env = "AION_LABS_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "aion-1.0" +display_name = "AionLabs: Aion-1.0" +tier = "frontier" +context_window = 131072 +max_output_tokens = 32768 +input_cost_per_m = 4.0 +output_cost_per_m = 8.0 +supports_streaming = true + +[[models]] +id = "aion-1.0-mini" +display_name = "AionLabs: Aion-1.0-Mini" +tier = "smart" +context_window = 131072 +max_output_tokens = 32768 +input_cost_per_m = 0.7 +output_cost_per_m = 1.4 +supports_streaming = true + +[[models]] +id = "aion-2.0" +display_name = "AionLabs: Aion-2.0" +tier = "smart" +context_window = 131072 +max_output_tokens = 32768 +input_cost_per_m = 0.8 +output_cost_per_m = 1.6 +supports_streaming = true + +[[models]] +id = "aion-rp-llama-3.1-8b" +display_name = "AionLabs: Aion-RP 1.0 (8B)" +tier = "smart" +context_window = 32768 +max_output_tokens = 32768 +input_cost_per_m = 0.8 +output_cost_per_m = 1.6 +supports_streaming = true diff --git a/providers/alibaba.toml b/providers/alibaba.toml new file mode 100644 index 0000000..e48a1c4 --- /dev/null +++ b/providers/alibaba.toml @@ -0,0 +1,18 @@ +# alibaba — auto-generated from OpenRouter API + +[provider] +id = "alibaba" +display_name = "Alibaba" +api_key_env = "ALIBABA_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "tongyi-deepresearch-30b-a3b" +display_name = "Tongyi DeepResearch 30B A3B" +tier = "fast" +context_window = 131072 +max_output_tokens = 131072 +input_cost_per_m = 0.09 +output_cost_per_m = 0.45 +supports_streaming = true diff --git a/providers/allenai.toml b/providers/allenai.toml new file mode 100644 index 0000000..96efd3f --- /dev/null +++ b/providers/allenai.toml @@ -0,0 +1,48 @@ +# allenai — auto-generated from OpenRouter API + +[provider] +id = "allenai" +display_name = "Allenai" +api_key_env = "ALLENAI_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "olmo-2-0325-32b-instruct" +display_name = "AllenAI: Olmo 2 32B Instruct" +tier = "fast" +context_window = 128000 +max_output_tokens = 16384 +input_cost_per_m = 0.05 +output_cost_per_m = 0.2 +supports_streaming = true + +[[models]] +id = "olmo-3-32b-think" +display_name = "AllenAI: Olmo 3 32B Think" +tier = "fast" +context_window = 65536 +max_output_tokens = 65536 +input_cost_per_m = 0.15 +output_cost_per_m = 0.5 +supports_streaming = true + +[[models]] +id = "olmo-3.1-32b-instruct" +display_name = "AllenAI: Olmo 3.1 32B Instruct" +tier = "fast" +context_window = 65536 +max_output_tokens = 16384 +input_cost_per_m = 0.2 +output_cost_per_m = 0.6 +supports_streaming = true + +[[models]] +id = "olmo-3.1-32b-think" +display_name = "AllenAI: Olmo 3.1 32B Think" +tier = "fast" +context_window = 65536 +max_output_tokens = 65536 +input_cost_per_m = 0.15 +output_cost_per_m = 0.5 +supports_streaming = true diff --git a/providers/amazon.toml b/providers/amazon.toml new file mode 100644 index 0000000..f361e5a --- /dev/null +++ b/providers/amazon.toml @@ -0,0 +1,58 @@ +# amazon — auto-generated from OpenRouter API + +[provider] +id = "amazon" +display_name = "Amazon" +api_key_env = "AMAZON_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "nova-2-lite-v1" +display_name = "Amazon: Nova 2 Lite" +tier = "fast" +context_window = 1000000 +max_output_tokens = 65535 +input_cost_per_m = 0.3 +output_cost_per_m = 2.5 +supports_streaming = true + +[[models]] +id = "nova-lite-v1" +display_name = "Amazon: Nova Lite 1.0" +tier = "fast" +context_window = 300000 +max_output_tokens = 5120 +input_cost_per_m = 0.06 +output_cost_per_m = 0.24 +supports_streaming = true + +[[models]] +id = "nova-micro-v1" +display_name = "Amazon: Nova Micro 1.0" +tier = "fast" +context_window = 128000 +max_output_tokens = 5120 +input_cost_per_m = 0.035 +output_cost_per_m = 0.14 +supports_streaming = true + +[[models]] +id = "nova-premier-v1" +display_name = "Amazon: Nova Premier 1.0" +tier = "smart" +context_window = 1000000 +max_output_tokens = 32000 +input_cost_per_m = 2.5 +output_cost_per_m = 12.5 +supports_streaming = true + +[[models]] +id = "nova-pro-v1" +display_name = "Amazon: Nova Pro 1.0" +tier = "smart" +context_window = 300000 +max_output_tokens = 5120 +input_cost_per_m = 0.8 +output_cost_per_m = 3.2 +supports_streaming = true diff --git a/providers/arcee-ai.toml b/providers/arcee-ai.toml new file mode 100644 index 0000000..6677058 --- /dev/null +++ b/providers/arcee-ai.toml @@ -0,0 +1,78 @@ +# arcee-ai — auto-generated from OpenRouter API + +[provider] +id = "arcee-ai" +display_name = "Arcee Ai" +api_key_env = "ARCEE_AI_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "coder-large" +display_name = "Arcee AI: Coder Large" +tier = "smart" +context_window = 32768 +max_output_tokens = 8192 +input_cost_per_m = 0.5 +output_cost_per_m = 0.8 +supports_streaming = true + +[[models]] +id = "maestro-reasoning" +display_name = "Arcee AI: Maestro Reasoning" +tier = "smart" +context_window = 131072 +max_output_tokens = 32000 +input_cost_per_m = 0.9 +output_cost_per_m = 3.3 +supports_streaming = true + +[[models]] +id = "spotlight" +display_name = "Arcee AI: Spotlight" +tier = "fast" +context_window = 131072 +max_output_tokens = 65537 +input_cost_per_m = 0.18 +output_cost_per_m = 0.18 +supports_streaming = true + +[[models]] +id = "trinity-large-preview:free" +display_name = "Arcee AI: Trinity Large Preview (free)" +tier = "free" +context_window = 131000 +max_output_tokens = 16384 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true + +[[models]] +id = "trinity-mini" +display_name = "Arcee AI: Trinity Mini" +tier = "fast" +context_window = 131072 +max_output_tokens = 131072 +input_cost_per_m = 0.045 +output_cost_per_m = 0.15 +supports_streaming = true + +[[models]] +id = "trinity-mini:free" +display_name = "Arcee AI: Trinity Mini (free)" +tier = "free" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true + +[[models]] +id = "virtuoso-large" +display_name = "Arcee AI: Virtuoso Large" +tier = "smart" +context_window = 131072 +max_output_tokens = 64000 +input_cost_per_m = 0.75 +output_cost_per_m = 1.2 +supports_streaming = true diff --git a/providers/bytedance.toml b/providers/bytedance.toml new file mode 100644 index 0000000..2f0e2b2 --- /dev/null +++ b/providers/bytedance.toml @@ -0,0 +1,18 @@ +# bytedance — auto-generated from OpenRouter API + +[provider] +id = "bytedance" +display_name = "Bytedance" +api_key_env = "BYTEDANCE_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "ui-tars-1.5-7b" +display_name = "ByteDance: UI-TARS 7B " +tier = "fast" +context_window = 128000 +max_output_tokens = 2048 +input_cost_per_m = 0.1 +output_cost_per_m = 0.2 +supports_streaming = true diff --git a/providers/chatgpt.toml b/providers/chatgpt.toml index cb063df..f670eb6 100644 --- a/providers/chatgpt.toml +++ b/providers/chatgpt.toml @@ -27,8 +27,8 @@ display_name = "GPT-5.3 Codex" tier = "frontier" context_window = 200000 max_output_tokens = 65536 -input_cost_per_m = 0.0 -output_cost_per_m = 0.0 +input_cost_per_m = 1.75 +output_cost_per_m = 14.0 supports_tools = true supports_vision = false supports_streaming = true @@ -40,8 +40,8 @@ display_name = "GPT-5.2 Codex" tier = "smart" context_window = 200000 max_output_tokens = 65536 -input_cost_per_m = 0.0 -output_cost_per_m = 0.0 +input_cost_per_m = 1.75 +output_cost_per_m = 14.0 supports_tools = true supports_vision = false supports_streaming = true @@ -53,8 +53,8 @@ display_name = "GPT-5.1 Codex" tier = "smart" context_window = 200000 max_output_tokens = 65536 -input_cost_per_m = 0.0 -output_cost_per_m = 0.0 +input_cost_per_m = 1.25 +output_cost_per_m = 10.0 supports_tools = true supports_vision = false supports_streaming = true @@ -66,8 +66,8 @@ display_name = "GPT-5.1 Codex Mini" tier = "balanced" context_window = 200000 max_output_tokens = 65536 -input_cost_per_m = 0.0 -output_cost_per_m = 0.0 +input_cost_per_m = 0.25 +output_cost_per_m = 2.0 supports_tools = true supports_vision = false supports_streaming = true diff --git a/providers/deepcogito.toml b/providers/deepcogito.toml new file mode 100644 index 0000000..b9f4731 --- /dev/null +++ b/providers/deepcogito.toml @@ -0,0 +1,18 @@ +# deepcogito — auto-generated from OpenRouter API + +[provider] +id = "deepcogito" +display_name = "Deepcogito" +api_key_env = "DEEPCOGITO_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "cogito-v2.1-671b" +display_name = "Deep Cogito: Cogito v2.1 671B" +tier = "smart" +context_window = 128000 +max_output_tokens = 16384 +input_cost_per_m = 1.25 +output_cost_per_m = 1.25 +supports_streaming = true diff --git a/providers/deepseek.toml b/providers/deepseek.toml index c5b9735..ea0aab6 100644 --- a/providers/deepseek.toml +++ b/providers/deepseek.toml @@ -14,8 +14,8 @@ display_name = "DeepSeek V3" tier = "smart" context_window = 64000 max_output_tokens = 8192 -input_cost_per_m = 0.27 -output_cost_per_m = 1.10 +input_cost_per_m = 0.15 +output_cost_per_m = 0.75 supports_tools = true supports_vision = false supports_streaming = true @@ -53,8 +53,8 @@ display_name = "DeepSeek V3 0324" tier = "smart" context_window = 64000 max_output_tokens = 8192 -input_cost_per_m = 0.27 -output_cost_per_m = 1.10 +input_cost_per_m = 0.19999999999999998 +output_cost_per_m = 0.77 supports_tools = true supports_vision = false supports_streaming = true diff --git a/providers/eleutherai.toml b/providers/eleutherai.toml new file mode 100644 index 0000000..afd24ac --- /dev/null +++ b/providers/eleutherai.toml @@ -0,0 +1,18 @@ +# eleutherai — auto-generated from OpenRouter API + +[provider] +id = "eleutherai" +display_name = "Eleutherai" +api_key_env = "ELEUTHERAI_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "llemma_7b" +display_name = "EleutherAI: Llemma 7b" +tier = "smart" +context_window = 4096 +max_output_tokens = 4096 +input_cost_per_m = 0.8 +output_cost_per_m = 1.2 +supports_streaming = true diff --git a/providers/essentialai.toml b/providers/essentialai.toml new file mode 100644 index 0000000..7672b6e --- /dev/null +++ b/providers/essentialai.toml @@ -0,0 +1,18 @@ +# essentialai — auto-generated from OpenRouter API + +[provider] +id = "essentialai" +display_name = "Essentialai" +api_key_env = "ESSENTIALAI_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "rnj-1-instruct" +display_name = "EssentialAI: Rnj 1 Instruct" +tier = "fast" +context_window = 32768 +max_output_tokens = 8192 +input_cost_per_m = 0.15 +output_cost_per_m = 0.15 +supports_streaming = true diff --git a/providers/gemini.toml b/providers/gemini.toml index f9ea0e7..e4e5eb2 100644 --- a/providers/gemini.toml +++ b/providers/gemini.toml @@ -15,8 +15,8 @@ display_name = "Gemini 3.1 Pro Preview" tier = "frontier" context_window = 1048576 max_output_tokens = 65536 -input_cost_per_m = 2.50 -output_cost_per_m = 15.0 +input_cost_per_m = 2.0 +output_cost_per_m = 12.0 supports_tools = true supports_vision = true supports_streaming = true @@ -28,8 +28,8 @@ display_name = "Gemini 3 Flash Preview" tier = "smart" context_window = 1048576 max_output_tokens = 65536 -input_cost_per_m = 0.15 -output_cost_per_m = 0.60 +input_cost_per_m = 0.5 +output_cost_per_m = 3.0 supports_tools = true supports_vision = true supports_streaming = true @@ -41,8 +41,8 @@ display_name = "Gemini 3.1 Flash Lite Preview" tier = "fast" context_window = 1048576 max_output_tokens = 8192 -input_cost_per_m = 0.04 -output_cost_per_m = 0.15 +input_cost_per_m = 0.25 +output_cost_per_m = 1.5 supports_tools = true supports_vision = true supports_streaming = true @@ -54,8 +54,8 @@ display_name = "Gemini 2.5 Flash Lite" tier = "fast" context_window = 1048576 max_output_tokens = 8192 -input_cost_per_m = 0.04 -output_cost_per_m = 0.15 +input_cost_per_m = 0.09999999999999999 +output_cost_per_m = 0.39999999999999997 supports_tools = true supports_vision = true supports_streaming = true @@ -80,8 +80,8 @@ display_name = "Gemini 2.5 Flash" tier = "smart" context_window = 1048576 max_output_tokens = 65536 -input_cost_per_m = 0.15 -output_cost_per_m = 0.60 +input_cost_per_m = 0.3 +output_cost_per_m = 2.5 supports_tools = true supports_vision = true supports_streaming = true @@ -93,8 +93,8 @@ display_name = "Gemini 2.0 Flash" tier = "fast" context_window = 1048576 max_output_tokens = 8192 -input_cost_per_m = 0.10 -output_cost_per_m = 0.40 +input_cost_per_m = 0.075 +output_cost_per_m = 0.3 supports_tools = true supports_vision = true supports_streaming = true diff --git a/providers/ibm-granite.toml b/providers/ibm-granite.toml new file mode 100644 index 0000000..4bc6490 --- /dev/null +++ b/providers/ibm-granite.toml @@ -0,0 +1,18 @@ +# ibm-granite — auto-generated from OpenRouter API + +[provider] +id = "ibm-granite" +display_name = "Ibm Granite" +api_key_env = "IBM_GRANITE_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "granite-4.0-h-micro" +display_name = "IBM: Granite 4.0 Micro" +tier = "fast" +context_window = 131000 +max_output_tokens = 16384 +input_cost_per_m = 0.017 +output_cost_per_m = 0.11 +supports_streaming = true diff --git a/providers/inception.toml b/providers/inception.toml new file mode 100644 index 0000000..0c486f8 --- /dev/null +++ b/providers/inception.toml @@ -0,0 +1,38 @@ +# inception — auto-generated from OpenRouter API + +[provider] +id = "inception" +display_name = "Inception" +api_key_env = "INCEPTION_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "mercury" +display_name = "Inception: Mercury" +tier = "fast" +context_window = 128000 +max_output_tokens = 32000 +input_cost_per_m = 0.25 +output_cost_per_m = 0.75 +supports_streaming = true + +[[models]] +id = "mercury-2" +display_name = "Inception: Mercury 2" +tier = "fast" +context_window = 128000 +max_output_tokens = 50000 +input_cost_per_m = 0.25 +output_cost_per_m = 0.75 +supports_streaming = true + +[[models]] +id = "mercury-coder" +display_name = "Inception: Mercury Coder" +tier = "fast" +context_window = 128000 +max_output_tokens = 32000 +input_cost_per_m = 0.25 +output_cost_per_m = 0.75 +supports_streaming = true diff --git a/providers/inflection.toml b/providers/inflection.toml new file mode 100644 index 0000000..8ef3c6c --- /dev/null +++ b/providers/inflection.toml @@ -0,0 +1,28 @@ +# inflection — auto-generated from OpenRouter API + +[provider] +id = "inflection" +display_name = "Inflection" +api_key_env = "INFLECTION_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "inflection-3-pi" +display_name = "Inflection: Inflection 3 Pi" +tier = "smart" +context_window = 8000 +max_output_tokens = 1024 +input_cost_per_m = 2.5 +output_cost_per_m = 10.0 +supports_streaming = true + +[[models]] +id = "inflection-3-productivity" +display_name = "Inflection: Inflection 3 Productivity" +tier = "smart" +context_window = 8000 +max_output_tokens = 1024 +input_cost_per_m = 2.5 +output_cost_per_m = 10.0 +supports_streaming = true diff --git a/providers/kwaipilot.toml b/providers/kwaipilot.toml new file mode 100644 index 0000000..92cea1f --- /dev/null +++ b/providers/kwaipilot.toml @@ -0,0 +1,18 @@ +# kwaipilot — auto-generated from OpenRouter API + +[provider] +id = "kwaipilot" +display_name = "Kwaipilot" +api_key_env = "KWAIPILOT_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "kat-coder-pro" +display_name = "Kwaipilot: KAT-Coder-Pro V1" +tier = "fast" +context_window = 256000 +max_output_tokens = 128000 +input_cost_per_m = 0.207 +output_cost_per_m = 0.828 +supports_streaming = true diff --git a/providers/liquid.toml b/providers/liquid.toml new file mode 100644 index 0000000..822eccc --- /dev/null +++ b/providers/liquid.toml @@ -0,0 +1,58 @@ +# liquid — auto-generated from OpenRouter API + +[provider] +id = "liquid" +display_name = "Liquid" +api_key_env = "LIQUID_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "lfm-2-24b-a2b" +display_name = "LiquidAI: LFM2-24B-A2B" +tier = "fast" +context_window = 32768 +max_output_tokens = 8192 +input_cost_per_m = 0.03 +output_cost_per_m = 0.12 +supports_streaming = true + +[[models]] +id = "lfm-2.2-6b" +display_name = "LiquidAI: LFM2-2.6B" +tier = "fast" +context_window = 32768 +max_output_tokens = 8192 +input_cost_per_m = 0.01 +output_cost_per_m = 0.02 +supports_streaming = true + +[[models]] +id = "lfm-2.5-1.2b-instruct:free" +display_name = "LiquidAI: LFM2.5-1.2B-Instruct (free)" +tier = "free" +context_window = 32768 +max_output_tokens = 8192 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true + +[[models]] +id = "lfm-2.5-1.2b-thinking:free" +display_name = "LiquidAI: LFM2.5-1.2B-Thinking (free)" +tier = "free" +context_window = 32768 +max_output_tokens = 8192 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true + +[[models]] +id = "lfm2-8b-a1b" +display_name = "LiquidAI: LFM2-8B-A1B" +tier = "fast" +context_window = 32768 +max_output_tokens = 8192 +input_cost_per_m = 0.01 +output_cost_per_m = 0.02 +supports_streaming = true diff --git a/providers/meituan.toml b/providers/meituan.toml new file mode 100644 index 0000000..5520f78 --- /dev/null +++ b/providers/meituan.toml @@ -0,0 +1,18 @@ +# meituan — auto-generated from OpenRouter API + +[provider] +id = "meituan" +display_name = "Meituan" +api_key_env = "MEITUAN_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "longcat-flash-chat" +display_name = "Meituan: LongCat Flash Chat" +tier = "fast" +context_window = 131072 +max_output_tokens = 131072 +input_cost_per_m = 0.2 +output_cost_per_m = 0.8 +supports_streaming = true diff --git a/providers/meta-llama.toml b/providers/meta-llama.toml new file mode 100644 index 0000000..1802fe2 --- /dev/null +++ b/providers/meta-llama.toml @@ -0,0 +1,148 @@ +# meta-llama — auto-generated from OpenRouter API + +[provider] +id = "meta-llama" +display_name = "Meta Llama" +api_key_env = "META_LLAMA_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "llama-3-70b-instruct" +display_name = "Meta: Llama 3 70B Instruct" +tier = "smart" +context_window = 8192 +max_output_tokens = 8000 +input_cost_per_m = 0.51 +output_cost_per_m = 0.74 +supports_streaming = true + +[[models]] +id = "llama-3-8b-instruct" +display_name = "Meta: Llama 3 8B Instruct" +tier = "fast" +context_window = 8192 +max_output_tokens = 16384 +input_cost_per_m = 0.03 +output_cost_per_m = 0.04 +supports_streaming = true + +[[models]] +id = "llama-3.1-70b-instruct" +display_name = "Meta: Llama 3.1 70B Instruct" +tier = "fast" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.4 +output_cost_per_m = 0.4 +supports_streaming = true + +[[models]] +id = "llama-3.1-8b-instruct" +display_name = "Meta: Llama 3.1 8B Instruct" +tier = "fast" +context_window = 16384 +max_output_tokens = 16384 +input_cost_per_m = 0.02 +output_cost_per_m = 0.05 +supports_streaming = true + +[[models]] +id = "llama-3.2-11b-vision-instruct" +display_name = "Meta: Llama 3.2 11B Vision Instruct" +tier = "fast" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.049 +output_cost_per_m = 0.049 +supports_streaming = true + +[[models]] +id = "llama-3.2-1b-instruct" +display_name = "Meta: Llama 3.2 1B Instruct" +tier = "fast" +context_window = 60000 +max_output_tokens = 15000 +input_cost_per_m = 0.027 +output_cost_per_m = 0.2 +supports_streaming = true + +[[models]] +id = "llama-3.2-3b-instruct" +display_name = "Meta: Llama 3.2 3B Instruct" +tier = "fast" +context_window = 80000 +max_output_tokens = 16384 +input_cost_per_m = 0.051 +output_cost_per_m = 0.34 +supports_streaming = true + +[[models]] +id = "llama-3.2-3b-instruct:free" +display_name = "Meta: Llama 3.2 3B Instruct (free)" +tier = "free" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true + +[[models]] +id = "llama-3.3-70b-instruct" +display_name = "Meta: Llama 3.3 70B Instruct" +tier = "fast" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.1 +output_cost_per_m = 0.32 +supports_streaming = true + +[[models]] +id = "llama-3.3-70b-instruct:free" +display_name = "Meta: Llama 3.3 70B Instruct (free)" +tier = "free" +context_window = 65536 +max_output_tokens = 16384 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true + +[[models]] +id = "llama-4-maverick" +display_name = "Meta: Llama 4 Maverick" +tier = "fast" +context_window = 1048576 +max_output_tokens = 16384 +input_cost_per_m = 0.15 +output_cost_per_m = 0.6 +supports_streaming = true + +[[models]] +id = "llama-4-scout" +display_name = "Meta: Llama 4 Scout" +tier = "fast" +context_window = 327680 +max_output_tokens = 16384 +input_cost_per_m = 0.08 +output_cost_per_m = 0.3 +supports_streaming = true + +[[models]] +id = "llama-guard-3-8b" +display_name = "Llama Guard 3 8B" +tier = "fast" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.02 +output_cost_per_m = 0.06 +supports_streaming = true + +[[models]] +id = "llama-guard-4-12b" +display_name = "Meta: Llama Guard 4 12B" +tier = "fast" +context_window = 163840 +max_output_tokens = 16384 +input_cost_per_m = 0.18 +output_cost_per_m = 0.18 +supports_streaming = true diff --git a/providers/microsoft.toml b/providers/microsoft.toml new file mode 100644 index 0000000..0176c29 --- /dev/null +++ b/providers/microsoft.toml @@ -0,0 +1,28 @@ +# microsoft — auto-generated from OpenRouter API + +[provider] +id = "microsoft" +display_name = "Microsoft" +api_key_env = "MICROSOFT_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "phi-4" +display_name = "Microsoft: Phi 4" +tier = "fast" +context_window = 16384 +max_output_tokens = 16384 +input_cost_per_m = 0.065 +output_cost_per_m = 0.14 +supports_streaming = true + +[[models]] +id = "wizardlm-2-8x22b" +display_name = "WizardLM-2 8x22B" +tier = "smart" +context_window = 65535 +max_output_tokens = 8000 +input_cost_per_m = 0.62 +output_cost_per_m = 0.62 +supports_streaming = true diff --git a/providers/moonshot.toml b/providers/moonshot.toml index 38a44b0..622154a 100644 --- a/providers/moonshot.toml +++ b/providers/moonshot.toml @@ -53,8 +53,8 @@ display_name = "Kimi K2" tier = "frontier" context_window = 131072 max_output_tokens = 16384 -input_cost_per_m = 0.60 -output_cost_per_m = 2.50 +input_cost_per_m = 0.44999999999999996 +output_cost_per_m = 2.2 supports_tools = true supports_vision = true supports_streaming = true diff --git a/providers/morph.toml b/providers/morph.toml new file mode 100644 index 0000000..f6d24ff --- /dev/null +++ b/providers/morph.toml @@ -0,0 +1,28 @@ +# morph — auto-generated from OpenRouter API + +[provider] +id = "morph" +display_name = "Morph" +api_key_env = "MORPH_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "morph-v3-fast" +display_name = "Morph: Morph V3 Fast" +tier = "smart" +context_window = 81920 +max_output_tokens = 38000 +input_cost_per_m = 0.8 +output_cost_per_m = 1.2 +supports_streaming = true + +[[models]] +id = "morph-v3-large" +display_name = "Morph: Morph V3 Large" +tier = "smart" +context_window = 262144 +max_output_tokens = 131072 +input_cost_per_m = 0.9 +output_cost_per_m = 1.9 +supports_streaming = true diff --git a/providers/nex-agi.toml b/providers/nex-agi.toml new file mode 100644 index 0000000..488622d --- /dev/null +++ b/providers/nex-agi.toml @@ -0,0 +1,18 @@ +# nex-agi — auto-generated from OpenRouter API + +[provider] +id = "nex-agi" +display_name = "Nex Agi" +api_key_env = "NEX_AGI_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "deepseek-v3.1-nex-n1" +display_name = "Nex AGI: DeepSeek V3.1 Nex N1" +tier = "fast" +context_window = 131072 +max_output_tokens = 163840 +input_cost_per_m = 0.135 +output_cost_per_m = 0.5 +supports_streaming = true diff --git a/providers/nousresearch.toml b/providers/nousresearch.toml new file mode 100644 index 0000000..71b82b1 --- /dev/null +++ b/providers/nousresearch.toml @@ -0,0 +1,68 @@ +# nousresearch — auto-generated from OpenRouter API + +[provider] +id = "nousresearch" +display_name = "Nousresearch" +api_key_env = "NOUSRESEARCH_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "hermes-2-pro-llama-3-8b" +display_name = "NousResearch: Hermes 2 Pro - Llama-3 8B" +tier = "fast" +context_window = 8192 +max_output_tokens = 8192 +input_cost_per_m = 0.14 +output_cost_per_m = 0.14 +supports_streaming = true + +[[models]] +id = "hermes-3-llama-3.1-405b" +display_name = "Nous: Hermes 3 405B Instruct" +tier = "smart" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 1.0 +output_cost_per_m = 1.0 +supports_streaming = true + +[[models]] +id = "hermes-3-llama-3.1-405b:free" +display_name = "Nous: Hermes 3 405B Instruct (free)" +tier = "free" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true + +[[models]] +id = "hermes-3-llama-3.1-70b" +display_name = "Nous: Hermes 3 70B Instruct" +tier = "fast" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.3 +output_cost_per_m = 0.3 +supports_streaming = true + +[[models]] +id = "hermes-4-405b" +display_name = "Nous: Hermes 4 405B" +tier = "smart" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 1.0 +output_cost_per_m = 3.0 +supports_streaming = true + +[[models]] +id = "hermes-4-70b" +display_name = "Nous: Hermes 4 70B" +tier = "fast" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.13 +output_cost_per_m = 0.4 +supports_streaming = true diff --git a/providers/nvidia.toml b/providers/nvidia.toml new file mode 100644 index 0000000..e22c959 --- /dev/null +++ b/providers/nvidia.toml @@ -0,0 +1,118 @@ +# nvidia — auto-generated from OpenRouter API + +[provider] +id = "nvidia" +display_name = "Nvidia" +api_key_env = "NVIDIA_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "llama-3.1-nemotron-70b-instruct" +display_name = "NVIDIA: Llama 3.1 Nemotron 70B Instruct" +tier = "smart" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 1.2 +output_cost_per_m = 1.2 +supports_streaming = true + +[[models]] +id = "llama-3.1-nemotron-ultra-253b-v1" +display_name = "NVIDIA: Llama 3.1 Nemotron Ultra 253B v1" +tier = "smart" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.6 +output_cost_per_m = 1.8 +supports_streaming = true + +[[models]] +id = "llama-3.3-nemotron-super-49b-v1.5" +display_name = "NVIDIA: Llama 3.3 Nemotron Super 49B V1.5" +tier = "fast" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.1 +output_cost_per_m = 0.4 +supports_streaming = true + +[[models]] +id = "nemotron-3-nano-30b-a3b" +display_name = "NVIDIA: Nemotron 3 Nano 30B A3B" +tier = "fast" +context_window = 262144 +max_output_tokens = 16384 +input_cost_per_m = 0.05 +output_cost_per_m = 0.2 +supports_streaming = true + +[[models]] +id = "nemotron-3-nano-30b-a3b:free" +display_name = "NVIDIA: Nemotron 3 Nano 30B A3B (free)" +tier = "free" +context_window = 256000 +max_output_tokens = 16384 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true + +[[models]] +id = "nemotron-3-super-120b-a12b" +display_name = "NVIDIA: Nemotron 3 Super" +tier = "fast" +context_window = 262144 +max_output_tokens = 16384 +input_cost_per_m = 0.1 +output_cost_per_m = 0.5 +supports_streaming = true + +[[models]] +id = "nemotron-3-super-120b-a12b:free" +display_name = "NVIDIA: Nemotron 3 Super (free)" +tier = "free" +context_window = 262144 +max_output_tokens = 262144 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true + +[[models]] +id = "nemotron-nano-12b-v2-vl" +display_name = "NVIDIA: Nemotron Nano 12B 2 VL" +tier = "fast" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.2 +output_cost_per_m = 0.6 +supports_streaming = true + +[[models]] +id = "nemotron-nano-12b-v2-vl:free" +display_name = "NVIDIA: Nemotron Nano 12B 2 VL (free)" +tier = "free" +context_window = 128000 +max_output_tokens = 128000 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true + +[[models]] +id = "nemotron-nano-9b-v2" +display_name = "NVIDIA: Nemotron Nano 9B V2" +tier = "fast" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.04 +output_cost_per_m = 0.16 +supports_streaming = true + +[[models]] +id = "nemotron-nano-9b-v2:free" +display_name = "NVIDIA: Nemotron Nano 9B V2 (free)" +tier = "free" +context_window = 128000 +max_output_tokens = 16384 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 +supports_streaming = true diff --git a/providers/ollama.toml b/providers/ollama.toml index 7d94bdc..a6453b9 100644 --- a/providers/ollama.toml +++ b/providers/ollama.toml @@ -53,8 +53,8 @@ display_name = "Qwen 2.5 (Ollama)" tier = "local" context_window = 32768 max_output_tokens = 4096 -input_cost_per_m = 0.0 -output_cost_per_m = 0.0 +input_cost_per_m = 0.03 +output_cost_per_m = 0.09 supports_tools = true supports_vision = false supports_streaming = true diff --git a/providers/openai.toml b/providers/openai.toml index 796e5fb..74df6a5 100644 --- a/providers/openai.toml +++ b/providers/openai.toml @@ -80,8 +80,8 @@ display_name = "o3" tier = "frontier" context_window = 200000 max_output_tokens = 100000 -input_cost_per_m = 2.00 -output_cost_per_m = 8.00 +input_cost_per_m = 10.0 +output_cost_per_m = 40.0 supports_tools = true supports_vision = true supports_streaming = true @@ -106,8 +106,8 @@ display_name = "o4-mini" tier = "smart" context_window = 200000 max_output_tokens = 100000 -input_cost_per_m = 1.10 -output_cost_per_m = 4.40 +input_cost_per_m = 2.0 +output_cost_per_m = 8.0 supports_tools = true supports_vision = true supports_streaming = true @@ -132,8 +132,8 @@ display_name = "GPT-3.5 Turbo" tier = "fast" context_window = 16385 max_output_tokens = 4096 -input_cost_per_m = 0.50 -output_cost_per_m = 1.50 +input_cost_per_m = 1.0 +output_cost_per_m = 2.0 supports_tools = true supports_vision = false supports_streaming = true @@ -145,8 +145,8 @@ display_name = "GPT-5" tier = "frontier" context_window = 400000 max_output_tokens = 128000 -input_cost_per_m = 1.25 -output_cost_per_m = 10.0 +input_cost_per_m = 0.19999999999999998 +output_cost_per_m = 1.25 supports_tools = true supports_vision = true supports_streaming = true @@ -210,8 +210,8 @@ display_name = "GPT-5.2 Pro" tier = "frontier" context_window = 400000 max_output_tokens = 128000 -input_cost_per_m = 1.75 -output_cost_per_m = 14.0 +input_cost_per_m = 21.0 +output_cost_per_m = 168.0 supports_tools = true supports_vision = true supports_streaming = true diff --git a/providers/perplexity.toml b/providers/perplexity.toml index c7f7883..5ca0f42 100644 --- a/providers/perplexity.toml +++ b/providers/perplexity.toml @@ -40,8 +40,8 @@ display_name = "Sonar Reasoning" tier = "balanced" context_window = 128000 max_output_tokens = 8192 -input_cost_per_m = 1.0 -output_cost_per_m = 5.0 +input_cost_per_m = 2.0 +output_cost_per_m = 8.0 supports_tools = false supports_vision = false supports_streaming = true diff --git a/providers/prime-intellect.toml b/providers/prime-intellect.toml new file mode 100644 index 0000000..d181611 --- /dev/null +++ b/providers/prime-intellect.toml @@ -0,0 +1,18 @@ +# prime-intellect — auto-generated from OpenRouter API + +[provider] +id = "prime-intellect" +display_name = "Prime Intellect" +api_key_env = "PRIME_INTELLECT_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "intellect-3" +display_name = "Prime Intellect: INTELLECT-3" +tier = "fast" +context_window = 131072 +max_output_tokens = 131072 +input_cost_per_m = 0.2 +output_cost_per_m = 1.1 +supports_streaming = true diff --git a/providers/qwen.toml b/providers/qwen.toml index 06bf5d9..6d68987 100644 --- a/providers/qwen.toml +++ b/providers/qwen.toml @@ -21,8 +21,8 @@ display_name = "Qwen Max" tier = "frontier" context_window = 32768 max_output_tokens = 8192 -input_cost_per_m = 4.00 -output_cost_per_m = 12.00 +input_cost_per_m = 1.04 +output_cost_per_m = 4.16 supports_tools = true supports_vision = false supports_streaming = true @@ -34,8 +34,8 @@ display_name = "Qwen Plus" tier = "smart" context_window = 131072 max_output_tokens = 8192 -input_cost_per_m = 0.80 -output_cost_per_m = 2.00 +input_cost_per_m = 0.26 +output_cost_per_m = 0.78 supports_tools = true supports_vision = false supports_streaming = true @@ -47,8 +47,8 @@ display_name = "Qwen Turbo" tier = "fast" context_window = 131072 max_output_tokens = 8192 -input_cost_per_m = 0.30 -output_cost_per_m = 0.60 +input_cost_per_m = 0.0325 +output_cost_per_m = 0.13 supports_tools = true supports_vision = false supports_streaming = true @@ -60,8 +60,8 @@ display_name = "Qwen VL Plus" tier = "smart" context_window = 32768 max_output_tokens = 8192 -input_cost_per_m = 1.50 -output_cost_per_m = 4.50 +input_cost_per_m = 0.1365 +output_cost_per_m = 0.40950000000000003 supports_tools = false supports_vision = true supports_streaming = true @@ -99,8 +99,8 @@ display_name = "Qwen3 235B" tier = "frontier" context_window = 131072 max_output_tokens = 8192 -input_cost_per_m = 4.00 -output_cost_per_m = 12.00 +input_cost_per_m = 0.14950000000000002 +output_cost_per_m = 1.495 supports_tools = true supports_vision = false supports_streaming = true @@ -112,8 +112,8 @@ display_name = "Qwen3 30B" tier = "fast" context_window = 131072 max_output_tokens = 8192 -input_cost_per_m = 0.30 -output_cost_per_m = 0.60 +input_cost_per_m = 0.08 +output_cost_per_m = 0.39999999999999997 supports_tools = true supports_vision = false supports_streaming = true @@ -151,8 +151,8 @@ display_name = "Qwen VL Max" tier = "frontier" context_window = 32768 max_output_tokens = 8192 -input_cost_per_m = 3.00 -output_cost_per_m = 9.00 +input_cost_per_m = 0.52 +output_cost_per_m = 2.08 supports_tools = false supports_vision = true supports_streaming = true diff --git a/providers/relace.toml b/providers/relace.toml new file mode 100644 index 0000000..d454745 --- /dev/null +++ b/providers/relace.toml @@ -0,0 +1,28 @@ +# relace — auto-generated from OpenRouter API + +[provider] +id = "relace" +display_name = "Relace" +api_key_env = "RELACE_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "relace-apply-3" +display_name = "Relace: Relace Apply 3" +tier = "smart" +context_window = 256000 +max_output_tokens = 128000 +input_cost_per_m = 0.85 +output_cost_per_m = 1.25 +supports_streaming = true + +[[models]] +id = "relace-search" +display_name = "Relace: Relace Search" +tier = "smart" +context_window = 256000 +max_output_tokens = 128000 +input_cost_per_m = 1.0 +output_cost_per_m = 3.0 +supports_streaming = true diff --git a/providers/stepfun.toml b/providers/stepfun.toml index 433e619..7716392 100644 --- a/providers/stepfun.toml +++ b/providers/stepfun.toml @@ -14,8 +14,8 @@ display_name = "Step 3.5 Flash" tier = "smart" context_window = 256000 max_output_tokens = 256000 -input_cost_per_m = 0.10 -output_cost_per_m = 0.30 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 supports_tools = true supports_vision = false supports_streaming = true @@ -27,8 +27,8 @@ display_name = "Step 3" tier = "frontier" context_window = 65536 max_output_tokens = 16384 -input_cost_per_m = 0.57 -output_cost_per_m = 1.43 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 supports_tools = true supports_vision = true supports_streaming = true diff --git a/providers/switchpoint.toml b/providers/switchpoint.toml new file mode 100644 index 0000000..b98b865 --- /dev/null +++ b/providers/switchpoint.toml @@ -0,0 +1,18 @@ +# switchpoint — auto-generated from OpenRouter API + +[provider] +id = "switchpoint" +display_name = "Switchpoint" +api_key_env = "SWITCHPOINT_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "router" +display_name = "Switchpoint Router" +tier = "smart" +context_window = 131072 +max_output_tokens = 16384 +input_cost_per_m = 0.85 +output_cost_per_m = 3.4 +supports_streaming = true diff --git a/providers/tencent.toml b/providers/tencent.toml new file mode 100644 index 0000000..8702200 --- /dev/null +++ b/providers/tencent.toml @@ -0,0 +1,18 @@ +# tencent — auto-generated from OpenRouter API + +[provider] +id = "tencent" +display_name = "Tencent" +api_key_env = "TENCENT_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "hunyuan-a13b-instruct" +display_name = "Tencent: Hunyuan A13B Instruct" +tier = "fast" +context_window = 131072 +max_output_tokens = 131072 +input_cost_per_m = 0.14 +output_cost_per_m = 0.57 +supports_streaming = true diff --git a/providers/tngtech.toml b/providers/tngtech.toml new file mode 100644 index 0000000..47a4634 --- /dev/null +++ b/providers/tngtech.toml @@ -0,0 +1,18 @@ +# tngtech — auto-generated from OpenRouter API + +[provider] +id = "tngtech" +display_name = "Tngtech" +api_key_env = "TNGTECH_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "deepseek-r1t2-chimera" +display_name = "TNG: DeepSeek R1T2 Chimera" +tier = "fast" +context_window = 163840 +max_output_tokens = 163840 +input_cost_per_m = 0.3 +output_cost_per_m = 1.1 +supports_streaming = true diff --git a/providers/upstage.toml b/providers/upstage.toml new file mode 100644 index 0000000..d86a3dc --- /dev/null +++ b/providers/upstage.toml @@ -0,0 +1,18 @@ +# upstage — auto-generated from OpenRouter API + +[provider] +id = "upstage" +display_name = "Upstage" +api_key_env = "UPSTAGE_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "solar-pro-3" +display_name = "Upstage: Solar Pro 3" +tier = "fast" +context_window = 128000 +max_output_tokens = 16384 +input_cost_per_m = 0.15 +output_cost_per_m = 0.6 +supports_streaming = true diff --git a/providers/venice.toml b/providers/venice.toml index ca9cde6..16a1e1e 100644 --- a/providers/venice.toml +++ b/providers/venice.toml @@ -27,8 +27,8 @@ display_name = "Llama 3.3 70B (Venice)" tier = "balanced" context_window = 128000 max_output_tokens = 8192 -input_cost_per_m = 0.20 -output_cost_per_m = 0.90 +input_cost_per_m = 0.0 +output_cost_per_m = 0.0 supports_tools = true supports_vision = false supports_streaming = true diff --git a/providers/xai.toml b/providers/xai.toml index 39a1485..a7704f7 100644 --- a/providers/xai.toml +++ b/providers/xai.toml @@ -79,8 +79,8 @@ display_name = "Grok 3" tier = "frontier" context_window = 131072 max_output_tokens = 32768 -input_cost_per_m = 3.0 -output_cost_per_m = 15.0 +input_cost_per_m = 0.3 +output_cost_per_m = 0.5 supports_tools = true supports_vision = true supports_streaming = true diff --git a/providers/xiaomi.toml b/providers/xiaomi.toml new file mode 100644 index 0000000..7634e09 --- /dev/null +++ b/providers/xiaomi.toml @@ -0,0 +1,38 @@ +# xiaomi — auto-generated from OpenRouter API + +[provider] +id = "xiaomi" +display_name = "Xiaomi" +api_key_env = "XIAOMI_API_KEY" +base_url = "" +key_required = true + +[[models]] +id = "mimo-v2-flash" +display_name = "Xiaomi: MiMo-V2-Flash" +tier = "fast" +context_window = 262144 +max_output_tokens = 65536 +input_cost_per_m = 0.09 +output_cost_per_m = 0.29 +supports_streaming = true + +[[models]] +id = "mimo-v2-omni" +display_name = "Xiaomi: MiMo-V2-Omni" +tier = "fast" +context_window = 262144 +max_output_tokens = 65536 +input_cost_per_m = 0.4 +output_cost_per_m = 2.0 +supports_streaming = true + +[[models]] +id = "mimo-v2-pro" +display_name = "Xiaomi: MiMo-V2-Pro" +tier = "smart" +context_window = 1048576 +max_output_tokens = 131072 +input_cost_per_m = 1.0 +output_cost_per_m = 3.0 +supports_streaming = true diff --git a/providers/zhipu.toml b/providers/zhipu.toml index 87e0ad0..266b5ef 100644 --- a/providers/zhipu.toml +++ b/providers/zhipu.toml @@ -79,8 +79,8 @@ display_name = "GLM-4.7" tier = "smart" context_window = 131072 max_output_tokens = 16384 -input_cost_per_m = 0.60 -output_cost_per_m = 2.20 +input_cost_per_m = 0.06 +output_cost_per_m = 0.39999999999999997 supports_tools = true supports_vision = true supports_streaming = true diff --git a/scripts/sync-pricing.py b/scripts/sync-pricing.py new file mode 100644 index 0000000..f70b89c --- /dev/null +++ b/scripts/sync-pricing.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 +"""Sync model pricing and providers from OpenRouter API. + +Usage: + python scripts/sync-pricing.py [--dry-run] [--create-missing] + +Fetches current pricing from https://openrouter.ai/api/v1/models and: +1. Updates input_cost_per_m / output_cost_per_m in existing providers/*.toml +2. With --create-missing: generates TOML files for providers not yet in registry +""" + +import json +import re +import sys +import urllib.request +from pathlib import Path + +OPENROUTER_API = "https://openrouter.ai/api/v1/models" +PROVIDERS_DIR = Path(__file__).parent.parent / "providers" + +# Map OpenRouter provider prefixes → our TOML filenames (when they differ) +PROVIDER_ALIAS = { + "google": "gemini", + "mistralai": "mistral", + "x-ai": "xai", + "moonshotai": "moonshot", + "z-ai": "zhipu", + "bytedance-seed": "volcengine", + "baidu": "qianfan", + "meta-llama": "meta-llama", +} + +# Skip these — community finetunes, not real providers +SKIP_PROVIDERS = { + "sao10k", "thedrummer", "undi95", "gryphe", "cognitivecomputations", + "anthracite-org", "alpindale", "alfredpros", "mancer", +} + + +def fetch_openrouter_models(): + """Fetch all models from OpenRouter.""" + req = urllib.request.Request(OPENROUTER_API) + req.add_header("User-Agent", "librefang-registry/sync-pricing") + with urllib.request.urlopen(req, timeout=30) as resp: + return json.loads(resp.read()).get("data", []) + + +def parse_pricing(model): + """Extract per-million pricing from an OpenRouter model entry.""" + p = model.get("pricing", {}) + prompt = p.get("prompt") + completion = p.get("completion") + if not prompt or not completion: + return None, None + try: + return round(float(prompt) * 1_000_000, 4), round(float(completion) * 1_000_000, 4) + except (ValueError, TypeError): + return None, None + + +def update_toml_prices(toml_path, models_by_id, dry_run=False): + """Update pricing fields in an existing TOML file.""" + content = toml_path.read_text() + lines = content.split("\n") + updated = 0 + current_model_id = None + new_lines = [] + + for line in lines: + id_match = re.match(r'^id\s*=\s*"([^"]+)"', line) + if id_match: + current_model_id = id_match.group(1) + + cost_match = re.match(r'^(input_cost_per_m|output_cost_per_m)\s*=\s*([\d.]+)', line) + if cost_match and current_model_id: + field = cost_match.group(1) + old_val = float(cost_match.group(2)) + + new_val = None + for or_id, (inp, outp) in models_by_id.items(): + or_model = or_id.split("/")[-1] if "/" in or_id else or_id + if current_model_id == or_model or current_model_id in or_id: + new_val = inp if field == "input_cost_per_m" else outp + break + + if new_val is not None and abs(new_val - old_val) > 0.001: + new_lines.append(f"{field} = {new_val}") + print(f" {toml_path.name}: {current_model_id}.{field}: {old_val} -> {new_val}") + updated += 1 + continue + + new_lines.append(line) + + if updated > 0 and not dry_run: + toml_path.write_text("\n".join(new_lines)) + return updated + + +def generate_provider_toml(provider_id, models, dry_run=False): + """Generate a new provider TOML file from OpenRouter data.""" + our_name = PROVIDER_ALIAS.get(provider_id, provider_id) + toml_path = PROVIDERS_DIR / f"{our_name}.toml" + + if toml_path.exists(): + return 0 + + # Derive api_key_env from provider name: FOO_BAR -> FOO_BAR_API_KEY + env_key = our_name.upper().replace("-", "_") + "_API_KEY" + + lines = [ + f'# {provider_id} — auto-generated from OpenRouter API', + f"", + f"[provider]", + f'id = "{our_name}"', + f'display_name = "{provider_id.replace("-", " ").title()}"', + f'api_key_env = "{env_key}"', + f'base_url = ""', + f"key_required = true", + f"", + ] + + count = 0 + for m in sorted(models, key=lambda x: x.get("id", "")): + model_id = m["id"].split("/")[-1] if "/" in m["id"] else m["id"] + display = m.get("name", model_id) + ctx = m.get("context_length", 0) + max_out = m.get("top_provider", {}).get("max_completion_tokens", 0) + inp, outp = parse_pricing(m) + if inp is None: + continue + + supports_tools = "tool_use" in str(m.get("supported_parameters", [])) + supports_vision = "vision" in str(m.get("architecture", {}).get("modality", "")) + + # Infer tier from pricing + if inp == 0 and outp == 0: + tier = "free" + elif inp < 0.5: + tier = "fast" + elif inp < 3.0: + tier = "smart" + else: + tier = "frontier" + + # Default max_output_tokens if not provided + if not max_out: + max_out = min(ctx // 4, 16384) if ctx > 0 else 4096 + + lines.append("[[models]]") + lines.append(f'id = "{model_id}"') + lines.append(f'display_name = "{display}"') + lines.append(f'tier = "{tier}"') + lines.append(f"context_window = {ctx}") + lines.append(f"max_output_tokens = {max_out}") + lines.append(f"input_cost_per_m = {inp}") + lines.append(f"output_cost_per_m = {outp}") + if supports_tools: + lines.append("supports_tools = true") + if supports_vision: + lines.append("supports_vision = true") + lines.append("supports_streaming = true") + lines.append("") + count += 1 + + if count == 0: + return 0 + + print(f" NEW: {our_name}.toml ({count} models)") + + if not dry_run: + toml_path.write_text("\n".join(lines)) + return count + + +def main(): + dry_run = "--dry-run" in sys.argv + create_missing = "--create-missing" in sys.argv + + print("Fetching models from OpenRouter API...") + all_models = fetch_openrouter_models() + print(f"Got {len(all_models)} models") + + # Build pricing index + pricing = {} + by_provider = {} + for m in all_models: + mid = m.get("id", "") + inp, outp = parse_pricing(m) + if inp is not None: + pricing[mid] = (inp, outp) + if "/" in mid: + p = mid.split("/")[0] + by_provider.setdefault(p, []).append(m) + + # Update existing providers + total_updated = 0 + for toml_file in sorted(PROVIDERS_DIR.glob("*.toml")): + count = update_toml_prices(toml_file, pricing, dry_run=dry_run) + total_updated += count + + # Create missing providers + total_created = 0 + if create_missing: + print("\nChecking for missing providers...") + for provider_id, models in sorted(by_provider.items()): + if provider_id in SKIP_PROVIDERS: + continue + our_name = PROVIDER_ALIAS.get(provider_id, provider_id) + if not (PROVIDERS_DIR / f"{our_name}.toml").exists(): + count = generate_provider_toml(provider_id, models, dry_run=dry_run) + total_created += count + + action = "Would" if dry_run else "Done:" + print(f"\n{action} updated {total_updated} prices, created {total_created} new model entries") + + +if __name__ == "__main__": + main()