diff --git a/.github/workflows/update-pricing.yml b/.github/workflows/update-pricing.yml index 6924057..f7f0cf8 100644 --- a/.github/workflows/update-pricing.yml +++ b/.github/workflows/update-pricing.yml @@ -24,12 +24,12 @@ jobs: - name: Commit if changed run: | - git diff --quiet assets/model_pricing.json && { + git diff --quiet assets/model_pricing.json crates/proxy/assets/model_pricing.json && { echo "Pricing already up to date." exit 0 } git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add assets/model_pricing.json + git add assets/model_pricing.json crates/proxy/assets/model_pricing.json git commit -m "bot: update model pricing from LiteLLM [skip ci]" git push diff --git a/AGENTS.md b/AGENTS.md index 02a5381..417b7ab 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -115,7 +115,7 @@ Five-crate Cargo workspace: `providers` (metadata catalog), `client` (Anthropic - Test files live alongside source (`#[cfg(test)]`) and in `crates/proxy/tests/` for integration tests. - Error types use `thiserror` derive macros. - Fixture-based golden tests for translation correctness. -- **Model pricing is auto-updated.** `scripts/update_pricing.py` pulls from LiteLLM's `model_prices_and_context_window.json` and writes `assets/model_pricing.json`. Run manually or via `.github/workflows/update-pricing.yml` (weekly, Monday 06:00 UTC). The file is embedded at compile time (`include_str!` in `crates/proxy/src/cost/mod.rs`); editing it requires recompile. Override at runtime with `MODEL_PRICING_FILE`. +- **Model pricing is auto-updated.** `scripts/update_pricing.py` pulls from LiteLLM's `model_prices_and_context_window.json` and writes `assets/model_pricing.json` plus the packaged proxy copy at `crates/proxy/assets/model_pricing.json`. Run manually or via `.github/workflows/update-pricing.yml` (weekly, Monday 06:00 UTC). The proxy copy is embedded at compile time (`include_str!` in `crates/proxy/src/cost/mod.rs`); editing it requires recompile. Override at runtime with `MODEL_PRICING_FILE`. ## Active Technologies diff --git a/CLAUDE.md b/CLAUDE.md index 94e7372..23925e5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -115,7 +115,7 @@ Five-crate Cargo workspace: `providers` (metadata catalog), `client` (Anthropic - Test files live alongside source (`#[cfg(test)]`) and in `crates/proxy/tests/` for integration tests. - Error types use `thiserror` derive macros. - Fixture-based golden tests for translation correctness. -- **Model pricing is auto-updated.** `scripts/update_pricing.py` pulls from LiteLLM's `model_prices_and_context_window.json` and writes `assets/model_pricing.json`. Run manually or via `.github/workflows/update-pricing.yml` (weekly, Monday 06:00 UTC). The file is embedded at compile time (`include_str!` in `crates/proxy/src/cost/mod.rs`); editing it requires recompile. Override at runtime with `MODEL_PRICING_FILE`. +- **Model pricing is auto-updated.** `scripts/update_pricing.py` pulls from LiteLLM's `model_prices_and_context_window.json` and writes `assets/model_pricing.json` plus the packaged proxy copy at `crates/proxy/assets/model_pricing.json`. Run manually or via `.github/workflows/update-pricing.yml` (weekly, Monday 06:00 UTC). The proxy copy is embedded at compile time (`include_str!` in `crates/proxy/src/cost/mod.rs`); editing it requires recompile. Override at runtime with `MODEL_PRICING_FILE`. ## Active Technologies diff --git a/Cargo.lock b/Cargo.lock index 7855d92..a2ca243 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,7 +46,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "anyllm_batch_engine" -version = "0.9.3" +version = "0.9.4" dependencies = [ "anyllm_translate", "async-trait", @@ -68,7 +68,7 @@ dependencies = [ [[package]] name = "anyllm_client" -version = "0.9.3" +version = "0.9.4" dependencies = [ "anyllm_translate", "bytes", @@ -87,14 +87,14 @@ dependencies = [ [[package]] name = "anyllm_providers" -version = "0.9.3" +version = "0.9.4" dependencies = [ "serde", ] [[package]] name = "anyllm_proxy" -version = "0.9.3" +version = "0.9.4" dependencies = [ "anyllm_batch_engine", "anyllm_client", @@ -147,7 +147,7 @@ dependencies = [ [[package]] name = "anyllm_translate" -version = "0.9.3" +version = "0.9.4" dependencies = [ "axum 0.8.8", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 0b0c98e..49c845c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["crates/translator", "crates/client", "crates/batch_engine", "crates/ resolver = "2" [workspace.package] -version = "0.9.3" +version = "0.9.4" edition = "2021" license = "MIT" repository = "https://github.com/whit3rabbit/anyllm-proxy" diff --git a/crates/batch_engine/Cargo.toml b/crates/batch_engine/Cargo.toml index f9f64e7..eb28996 100644 --- a/crates/batch_engine/Cargo.toml +++ b/crates/batch_engine/Cargo.toml @@ -7,7 +7,7 @@ license.workspace = true repository.workspace = true [dependencies] -anyllm_translate = { path = "../translator", version = "0.9.3" } +anyllm_translate = { path = "../translator", version = "0.9.4" } rusqlite = { version = "0.32", features = ["bundled"] } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index a5ca9a5..055a390 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "anyllm_client" description = "Async HTTP client for Anthropic-to-OpenAI translation with retry, SSRF protection, and SSE streaming" -version = "0.9.3" +version = "0.9.4" edition.workspace = true license.workspace = true repository.workspace = true @@ -13,7 +13,7 @@ default = ["ssrf-protection"] ssrf-protection = [] [dependencies] -anyllm_translate = { path = "../translator", version = "0.9.3" } +anyllm_translate = { path = "../translator", version = "0.9.4" } reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "native-tls", "http2"] } tokio = { version = "1", features = ["rt", "sync", "time"] } serde = { version = "1", features = ["derive"] } diff --git a/crates/providers/README.md b/crates/providers/README.md index 1ad21c3..cf41781 100644 --- a/crates/providers/README.md +++ b/crates/providers/README.md @@ -13,7 +13,7 @@ The crate answers questions like: - For a given provider id, which backend protocol (OpenAI-compat, Vertex, Bedrock, ...) should the proxy route through? - Which provider owns the LiteLLM prefix `together_ai/`? -What it deliberately does **not** contain: pricing data (lives in `assets/model_pricing.json` at the workspace root, embedded into the proxy at compile time and overridable at runtime via `MODEL_PRICING_FILE`). +What it deliberately does **not** contain: pricing data (lives in `assets/model_pricing.json` at the workspace root and a packaged proxy copy, embedded into the proxy at compile time and overridable at runtime via `MODEL_PRICING_FILE`). ## Where it fits diff --git a/crates/providers/src/model.rs b/crates/providers/src/model.rs index a6e569a..791f422 100644 --- a/crates/providers/src/model.rs +++ b/crates/providers/src/model.rs @@ -17,7 +17,7 @@ pub enum ModelStatus { Stub, } -/// Metadata for a specific model. No pricing data — that lives in `assets/model_pricing.json`. +/// Metadata for a specific model. No pricing data; that lives in the model pricing JSON assets. /// /// All fields are `'static` so model definitions can be compile-time constants. #[derive(Debug, Clone)] diff --git a/crates/proxy/Cargo.toml b/crates/proxy/Cargo.toml index dea0d61..ac5e805 100644 --- a/crates/proxy/Cargo.toml +++ b/crates/proxy/Cargo.toml @@ -7,10 +7,10 @@ license.workspace = true repository.workspace = true [dependencies] -anyllm_translate = { path = "../translator", version = "0.9.3" } -anyllm_client = { path = "../client", version = "0.9.3" } -anyllm_batch_engine = { path = "../batch_engine", version = "0.9.3" } -anyllm_providers = { path = "../providers", version = "0.9.3" } +anyllm_translate = { path = "../translator", version = "0.9.4" } +anyllm_client = { path = "../client", version = "0.9.4" } +anyllm_batch_engine = { path = "../batch_engine", version = "0.9.4" } +anyllm_providers = { path = "../providers", version = "0.9.4" } axum = { version = "0.8", features = ["ws", "multipart"] } tokio = { version = "1", features = ["full"] } reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "native-tls", "http2", "multipart"] } diff --git a/crates/proxy/assets/model_pricing.json b/crates/proxy/assets/model_pricing.json new file mode 100644 index 0000000..353a160 --- /dev/null +++ b/crates/proxy/assets/model_pricing.json @@ -0,0 +1,986 @@ +[ + { + "model_pattern": "claude-3-7-sonnet-20250219", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-3-haiku-20240307", + "input_cost_per_token": 2.5e-07, + "output_cost_per_token": 1.25e-06, + "provider": "anthropic" + }, + { + "model_pattern": "claude-3-opus-20240229", + "input_cost_per_token": 1.5e-05, + "output_cost_per_token": 7.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-4-opus-20250514", + "input_cost_per_token": 1.5e-05, + "output_cost_per_token": 7.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-4-sonnet-20250514", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-haiku-4-5", + "input_cost_per_token": 1e-06, + "output_cost_per_token": 5e-06, + "provider": "anthropic" + }, + { + "model_pattern": "claude-haiku-4-5-20251001", + "input_cost_per_token": 1e-06, + "output_cost_per_token": 5e-06, + "provider": "anthropic" + }, + { + "model_pattern": "claude-opus-4-1", + "input_cost_per_token": 1.5e-05, + "output_cost_per_token": 7.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-opus-4-1-20250805", + "input_cost_per_token": 1.5e-05, + "output_cost_per_token": 7.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-opus-4-20250514", + "input_cost_per_token": 1.5e-05, + "output_cost_per_token": 7.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-opus-4-5", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-opus-4-5-20251101", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-opus-4-6", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-opus-4-6-20260205", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-opus-4-7", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-opus-4-7-20260416", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-sonnet-4-20250514", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-sonnet-4-5", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-sonnet-4-5-20250929", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "claude-sonnet-4-6", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "provider": "anthropic" + }, + { + "model_pattern": "gemini-1.5-flash", + "input_cost_per_token": 7.5e-08, + "output_cost_per_token": 0, + "provider": "google" + }, + { + "model_pattern": "gemini-2.0-flash", + "input_cost_per_token": 1e-07, + "output_cost_per_token": 4e-07, + "provider": "google" + }, + { + "model_pattern": "gemini-2.0-flash-001", + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "provider": "google" + }, + { + "model_pattern": "gemini-2.0-flash-lite", + "input_cost_per_token": 7.5e-08, + "output_cost_per_token": 3e-07, + "provider": "google" + }, + { + "model_pattern": "gemini-2.0-flash-lite-001", + "input_cost_per_token": 7.5e-08, + "output_cost_per_token": 3e-07, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-computer-use-preview-10-2025", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-flash", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 2.5e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-flash-lite", + "input_cost_per_token": 1e-07, + "output_cost_per_token": 4e-07, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-flash-lite-preview-06-17", + "input_cost_per_token": 1e-07, + "output_cost_per_token": 4e-07, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-flash-lite-preview-09-2025", + "input_cost_per_token": 1e-07, + "output_cost_per_token": 4e-07, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-flash-native-audio-latest", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 2.5e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-flash-native-audio-preview-09-2025", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 2.5e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-flash-native-audio-preview-12-2025", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 2.5e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-flash-preview-09-2025", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 2.5e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-pro", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "google" + }, + { + "model_pattern": "gemini-2.5-pro-preview-tts", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "google" + }, + { + "model_pattern": "gemini-3-flash-preview", + "input_cost_per_token": 5e-07, + "output_cost_per_token": 3e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-3-pro-preview", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 1.2e-05, + "provider": "google" + }, + { + "model_pattern": "gemini-3.1-flash-lite-preview", + "input_cost_per_token": 2.5e-07, + "output_cost_per_token": 1.5e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-3.1-flash-live-preview", + "input_cost_per_token": 7.5e-07, + "output_cost_per_token": 4.5e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-3.1-pro-preview", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 1.2e-05, + "provider": "google" + }, + { + "model_pattern": "gemini-3.1-pro-preview-customtools", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 1.2e-05, + "provider": "google" + }, + { + "model_pattern": "gemini-embedding-001", + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 0, + "provider": "google" + }, + { + "model_pattern": "gemini-embedding-2", + "input_cost_per_token": 2e-07, + "output_cost_per_token": 0, + "provider": "google" + }, + { + "model_pattern": "gemini-embedding-2-preview", + "input_cost_per_token": 2e-07, + "output_cost_per_token": 0, + "provider": "google" + }, + { + "model_pattern": "gemini-exp-1206", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 2.5e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-flash-latest", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 2.5e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-flash-lite-latest", + "input_cost_per_token": 1e-07, + "output_cost_per_token": 4e-07, + "provider": "google" + }, + { + "model_pattern": "gemini-gemma-2-27b-it", + "input_cost_per_token": 3.5e-07, + "output_cost_per_token": 1.05e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-gemma-2-9b-it", + "input_cost_per_token": 3.5e-07, + "output_cost_per_token": 1.05e-06, + "provider": "google" + }, + { + "model_pattern": "gemini-pro-latest", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "google" + }, + { + "model_pattern": "gemini-robotics-er-1.5-preview", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 2.5e-06, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/gemini-3-flash-preview", + "input_cost_per_token": 5e-07, + "output_cost_per_token": 3e-06, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/gemini-3-pro-preview", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 1.2e-05, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/gemini-3.1-flash-lite-preview", + "input_cost_per_token": 2.5e-07, + "output_cost_per_token": 1.5e-06, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/gemini-3.1-pro-preview", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 1.2e-05, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/gemini-3.1-pro-preview-customtools", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 1.2e-05, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/gemini-embedding-2", + "input_cost_per_token": 2e-07, + "output_cost_per_token": 0, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/gemini-embedding-2-preview", + "input_cost_per_token": 2e-07, + "output_cost_per_token": 0, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/xai/grok-4.1-fast-non-reasoning", + "input_cost_per_token": 2e-07, + "output_cost_per_token": 5e-07, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/xai/grok-4.1-fast-reasoning", + "input_cost_per_token": 2e-07, + "output_cost_per_token": 5e-07, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/xai/grok-4.20-non-reasoning", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 6e-06, + "provider": "google" + }, + { + "model_pattern": "vertex_ai/xai/grok-4.20-reasoning", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 6e-06, + "provider": "google" + }, + { + "model_pattern": "chatgpt-4o-latest", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 1.5e-05, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-3.5-turbo", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 6e-06, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-3.5-turbo-0125", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 6e-06, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-3.5-turbo-0613", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 6e-06, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-3.5-turbo-1106", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 6e-06, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-4-0613", + "input_cost_per_token": 3e-05, + "output_cost_per_token": 6e-05, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-4.1-2025-04-14", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.2e-05, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-4.1-mini-2025-04-14", + "input_cost_per_token": 8e-07, + "output_cost_per_token": 3.2e-06, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-4.1-nano-2025-04-14", + "input_cost_per_token": 2e-07, + "output_cost_per_token": 8e-07, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-4o-2024-08-06", + "input_cost_per_token": 3.75e-06, + "output_cost_per_token": 1.5e-05, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-4o-2024-11-20", + "input_cost_per_token": 3.75e-06, + "output_cost_per_token": 1.5e-05, + "provider": "openai" + }, + { + "model_pattern": "ft:gpt-4o-mini-2024-07-18", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, + "provider": "openai" + }, + { + "model_pattern": "ft:o4-mini-2025-04-16", + "input_cost_per_token": 4e-06, + "output_cost_per_token": 1.6e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-3.5-turbo", + "input_cost_per_token": 5e-07, + "output_cost_per_token": 1.5e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-3.5-turbo-0125", + "input_cost_per_token": 5e-07, + "output_cost_per_token": 1.5e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-3.5-turbo-1106", + "input_cost_per_token": 1e-06, + "output_cost_per_token": 2e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-3.5-turbo-16k", + "input_cost_per_token": 3e-06, + "output_cost_per_token": 4e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-4", + "input_cost_per_token": 3e-05, + "output_cost_per_token": 6e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4-0125-preview", + "input_cost_per_token": 1e-05, + "output_cost_per_token": 3e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4-0314", + "input_cost_per_token": 3e-05, + "output_cost_per_token": 6e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4-0613", + "input_cost_per_token": 3e-05, + "output_cost_per_token": 6e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4-1106-preview", + "input_cost_per_token": 1e-05, + "output_cost_per_token": 3e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4-turbo", + "input_cost_per_token": 1e-05, + "output_cost_per_token": 3e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4-turbo-2024-04-09", + "input_cost_per_token": 1e-05, + "output_cost_per_token": 3e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4-turbo-preview", + "input_cost_per_token": 1e-05, + "output_cost_per_token": 3e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4.1", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 8e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-4.1-2025-04-14", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 8e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-4.1-mini", + "input_cost_per_token": 4e-07, + "output_cost_per_token": 1.6e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-4.1-mini-2025-04-14", + "input_cost_per_token": 4e-07, + "output_cost_per_token": 1.6e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-4.1-nano", + "input_cost_per_token": 1e-07, + "output_cost_per_token": 4e-07, + "provider": "openai" + }, + { + "model_pattern": "gpt-4.1-nano-2025-04-14", + "input_cost_per_token": 1e-07, + "output_cost_per_token": 4e-07, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-2024-05-13", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 1.5e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-2024-08-06", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-2024-11-20", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-audio-preview", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-audio-preview-2024-12-17", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-audio-preview-2025-06-03", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-mini", + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-mini-2024-07-18", + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-mini-audio-preview", + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-mini-audio-preview-2024-12-17", + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-mini-realtime-preview", + "input_cost_per_token": 6e-07, + "output_cost_per_token": 2.4e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-mini-realtime-preview-2024-12-17", + "input_cost_per_token": 6e-07, + "output_cost_per_token": 2.4e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-mini-search-preview", + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-mini-search-preview-2025-03-11", + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-realtime-preview", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-realtime-preview-2024-12-17", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-realtime-preview-2025-06-03", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-search-preview", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-4o-search-preview-2025-03-11", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5-2025-08-07", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5-chat", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5-chat-latest", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5-mini", + "input_cost_per_token": 2.5e-07, + "output_cost_per_token": 2e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-5-mini-2025-08-07", + "input_cost_per_token": 2.5e-07, + "output_cost_per_token": 2e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-5-nano", + "input_cost_per_token": 5e-08, + "output_cost_per_token": 4e-07, + "provider": "openai" + }, + { + "model_pattern": "gpt-5-nano-2025-08-07", + "input_cost_per_token": 5e-08, + "output_cost_per_token": 4e-07, + "provider": "openai" + }, + { + "model_pattern": "gpt-5-search-api", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5-search-api-2025-10-14", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.1", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.1-2025-11-13", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.1-chat-latest", + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.2", + "input_cost_per_token": 1.75e-06, + "output_cost_per_token": 1.4e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.2-2025-12-11", + "input_cost_per_token": 1.75e-06, + "output_cost_per_token": 1.4e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.2-chat-latest", + "input_cost_per_token": 1.75e-06, + "output_cost_per_token": 1.4e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.3-chat-latest", + "input_cost_per_token": 1.75e-06, + "output_cost_per_token": 1.4e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.4", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1.5e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.4-2026-03-05", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1.5e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.4-mini", + "input_cost_per_token": 7.5e-07, + "output_cost_per_token": 4.5e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.4-mini-2026-03-17", + "input_cost_per_token": 7.5e-07, + "output_cost_per_token": 4.5e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.4-nano", + "input_cost_per_token": 2e-07, + "output_cost_per_token": 1.25e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.4-nano-2026-03-17", + "input_cost_per_token": 2e-07, + "output_cost_per_token": 1.25e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.5", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 3e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-5.5-2026-04-23", + "input_cost_per_token": 5e-06, + "output_cost_per_token": 3e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-audio", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-audio-1.5", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-audio-2025-08-28", + "input_cost_per_token": 2.5e-06, + "output_cost_per_token": 1e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-audio-mini", + "input_cost_per_token": 6e-07, + "output_cost_per_token": 2.4e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-audio-mini-2025-10-06", + "input_cost_per_token": 6e-07, + "output_cost_per_token": 2.4e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-audio-mini-2025-12-15", + "input_cost_per_token": 6e-07, + "output_cost_per_token": 2.4e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-realtime", + "input_cost_per_token": 4e-06, + "output_cost_per_token": 1.6e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-realtime-1.5", + "input_cost_per_token": 4e-06, + "output_cost_per_token": 1.6e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-realtime-2", + "input_cost_per_token": 4e-06, + "output_cost_per_token": 1.6e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-realtime-2025-08-28", + "input_cost_per_token": 4e-06, + "output_cost_per_token": 1.6e-05, + "provider": "openai" + }, + { + "model_pattern": "gpt-realtime-mini", + "input_cost_per_token": 6e-07, + "output_cost_per_token": 2.4e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-realtime-mini-2025-10-06", + "input_cost_per_token": 6e-07, + "output_cost_per_token": 2.4e-06, + "provider": "openai" + }, + { + "model_pattern": "gpt-realtime-mini-2025-12-15", + "input_cost_per_token": 6e-07, + "output_cost_per_token": 2.4e-06, + "provider": "openai" + }, + { + "model_pattern": "o1", + "input_cost_per_token": 1.5e-05, + "output_cost_per_token": 6e-05, + "provider": "openai" + }, + { + "model_pattern": "o1-2024-12-17", + "input_cost_per_token": 1.5e-05, + "output_cost_per_token": 6e-05, + "provider": "openai" + }, + { + "model_pattern": "o3", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 8e-06, + "provider": "openai" + }, + { + "model_pattern": "o3-2025-04-16", + "input_cost_per_token": 2e-06, + "output_cost_per_token": 8e-06, + "provider": "openai" + }, + { + "model_pattern": "o3-mini", + "input_cost_per_token": 1.1e-06, + "output_cost_per_token": 4.4e-06, + "provider": "openai" + }, + { + "model_pattern": "o3-mini-2025-01-31", + "input_cost_per_token": 1.1e-06, + "output_cost_per_token": 4.4e-06, + "provider": "openai" + }, + { + "model_pattern": "o4-mini", + "input_cost_per_token": 1.1e-06, + "output_cost_per_token": 4.4e-06, + "provider": "openai" + }, + { + "model_pattern": "o4-mini-2025-04-16", + "input_cost_per_token": 1.1e-06, + "output_cost_per_token": 4.4e-06, + "provider": "openai" + }, + { + "model_pattern": "text-embedding-3-large", + "input_cost_per_token": 1.3e-07, + "output_cost_per_token": 0.0, + "provider": "openai" + }, + { + "model_pattern": "text-embedding-3-small", + "input_cost_per_token": 2e-08, + "output_cost_per_token": 0.0, + "provider": "openai" + }, + { + "model_pattern": "text-embedding-ada-002", + "input_cost_per_token": 1e-07, + "output_cost_per_token": 0.0, + "provider": "openai" + }, + { + "model_pattern": "text-embedding-ada-002-v2", + "input_cost_per_token": 1e-07, + "output_cost_per_token": 0.0, + "provider": "openai" + } +] diff --git a/crates/proxy/src/cost/mod.rs b/crates/proxy/src/cost/mod.rs index f720e7f..e309c89 100644 --- a/crates/proxy/src/cost/mod.rs +++ b/crates/proxy/src/cost/mod.rs @@ -149,11 +149,11 @@ impl ModelPricing { error = %e, "failed to read MODEL_PRICING_FILE; falling back to embedded pricing" ); - include_str!("../../../../assets/model_pricing.json").to_string() + include_str!("../../assets/model_pricing.json").to_string() } } } else { - include_str!("../../../../assets/model_pricing.json").to_string() + include_str!("../../assets/model_pricing.json").to_string() }; let entries: Vec = serde_json::from_str(&json).expect("invalid model_pricing.json"); diff --git a/scripts/update_pricing.py b/scripts/update_pricing.py index bfb663a..f9888d5 100644 --- a/scripts/update_pricing.py +++ b/scripts/update_pricing.py @@ -3,7 +3,7 @@ Update assets/model_pricing.json from LiteLLM's canonical pricing file. Usage: - python scripts/update_pricing.py # write assets/model_pricing.json + python scripts/update_pricing.py # write assets/model_pricing.json and crate copy python scripts/update_pricing.py --dry-run # print diff, no write python scripts/update_pricing.py --output /path/to/file.json """ @@ -36,6 +36,7 @@ ALLOWED_MODES = {"chat", "embedding", "completion"} # Repo root is one level up from this script. REPO_ROOT = Path(__file__).parent.parent DEFAULT_OUTPUT = REPO_ROOT / "assets" / "model_pricing.json" +PACKAGE_OUTPUT = REPO_ROOT / "crates" / "proxy" / "assets" / "model_pricing.json" def fetch_litellm_pricing() -> dict: @@ -177,9 +178,15 @@ def main(): return output = json.dumps(new_entries, indent=2) + "\n" + output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_text(output) print(f"Written: {output_path}", file=sys.stderr) + if output_path.resolve() == DEFAULT_OUTPUT.resolve(): + PACKAGE_OUTPUT.parent.mkdir(parents=True, exist_ok=True) + PACKAGE_OUTPUT.write_text(output) + print(f"Written: {PACKAGE_OUTPUT}", file=sys.stderr) + if __name__ == "__main__": main()