From 9b8836f950adfecfa84b797b4e6a4642d01510dd Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 8 Sep 2026 11:09:01 -0500 Subject: [PATCH 1/7] fix(posit): switch API flavor when chat.model crosses model families ChatPosit() picks its provider class from the model name (Claude models get the Anthropic flavor, everything else the OpenAI-compatible one), but the Chat.model setter only updated the model name, so switching to a model from the other family sent requests in the wrong wire format to the wrong gateway endpoint. Adds a Provider.set_model() hook that the Chat.model setter consults; the default returns the provider unchanged. The two Posit providers override it to swap in their sibling class on a family switch, carrying over credentials, the gateway base URL, and the cache setting (stored inertly on PositOpenAIProvider so it survives a round trip). Mirrors tidyverse/ellmer#1139. Also drops unsigned thinking blocks when replaying history to Anthropic-backed providers: reasoning emitted by a non-Claude model has no signature, and the API rejects it with "Invalid signature in thinking block". Family switching makes this reachable; Claude's own (signed) thinking is unaffected. Verified end-to-end against the Posit gateway with family switches in both directions, including a conversation containing GLM thinking content replayed to Claude. --- CHANGELOG.md | 2 + chatlas/_chat.py | 6 ++- chatlas/_provider.py | 18 ++++++++ chatlas/_provider_anthropic.py | 16 ++++++- chatlas/_provider_posit.py | 31 +++++++++++++ tests/test_provider_anthropic.py | 38 ++++++++++++++++ tests/test_provider_posit.py | 75 ++++++++++++++++++++++++++++++++ 7 files changed, 182 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27c82e06..20139caf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Bug fixes * `ChatBedrock()` (with the default `api="converse"`) no longer sends assistant turns with an empty `content` array, which Converse rejects. This happens when a response carries no content blocks, for example when a guardrail intervenes before the model produces any. A `"[empty string]"` placeholder is sent instead, matching how empty text content is already normalized. (#426) +* `ChatPosit()` now switches API flavors when `chat.model` is set to a model from the other family: setting a non-Claude model on a Claude-backed chat (or vice versa) swaps the underlying provider so requests use the correct wire format and gateway endpoint, rather than failing with a mismatched request. The `cache` setting is preserved across flavor switches. (Mirrors tidyverse/ellmer#1139.) +* Anthropic-backed providers (`ChatAnthropic()`, `ChatPosit()`, etc.) now drop thinking blocks that lack a signature (e.g., reasoning emitted by a non-Claude model) when replaying conversation history, instead of failing with `Invalid signature in thinking block`. ## [0.23.0] - 2026-09-04 diff --git a/chatlas/_chat.py b/chatlas/_chat.py index fb4729d9..9e3b21c8 100644 --- a/chatlas/_chat.py +++ b/chatlas/_chat.py @@ -536,7 +536,9 @@ def model(self) -> str: Setting this updates the model for subsequent requests. The model name is not validated, so make sure it's a valid model for the chat's - provider. + provider. Note that some providers (e.g., `ChatPosit()`) dispatch to a + different API flavor based on the model name; setting a model from + another flavor swaps out the underlying provider accordingly. Returns ------- @@ -547,7 +549,7 @@ def model(self) -> str: @model.setter def model(self, value: str): - self.provider.model = value + self.provider = self.provider.set_model(value) @property def conversation_id(self) -> str | None: diff --git a/chatlas/_provider.py b/chatlas/_provider.py index f30eea97..aab38d8d 100644 --- a/chatlas/_provider.py +++ b/chatlas/_provider.py @@ -197,6 +197,24 @@ def model(self): def model(self, value: str): self._model = value + def set_model(self, value: str) -> "Provider[Any, Any]": + """ + Set the model used by the provider. + + The default implementation just updates the model name and returns + the provider unchanged. Providers that dispatch to a different API + flavor based on the model name (e.g., Posit AI) override this to + return a different provider instance when the new model belongs to + another flavor. + + Returns + ------- + Provider + The provider to use for subsequent requests (possibly `self`). + """ + self._model = value + return self + def close(self) -> None: """ Release resources held by this provider (e.g., HTTP connection pools, diff --git a/chatlas/_provider_anthropic.py b/chatlas/_provider_anthropic.py index dd304aba..259c86cc 100644 --- a/chatlas/_provider_anthropic.py +++ b/chatlas/_provider_anthropic.py @@ -894,8 +894,11 @@ def _as_message_params(self, turns: Sequence[Turn]) -> list["MessageParam"]: content = [ self._as_content_block(c) for c in turn.contents - if not isinstance(c, PROVIDER_ANNOTATION_TYPES) - or anthropic_replayable(c) + if ( + not isinstance(c, PROVIDER_ANNOTATION_TYPES) + or anthropic_replayable(c) + ) + and self._is_replayable_thinking(c) ] # Drop empty assistant turns to avoid an API error @@ -915,6 +918,15 @@ def _as_message_params(self, turns: Sequence[Turn]) -> list["MessageParam"]: messages.append({"role": role, "content": content}) return messages + @staticmethod + def _is_replayable_thinking(content: Content) -> bool: + # Thinking blocks without a signature (e.g., reasoning emitted by a + # non-Claude model, replayed after a provider switch) are rejected by + # the API ("Invalid `signature` in `thinking` block"), so drop them. + if not isinstance(content, ContentThinking): + return True + return bool((content.extra or {}).get("signature")) + @staticmethod def _as_content_block(content: Content) -> "ContentBlockParam": if isinstance(content, ContentText): diff --git a/chatlas/_provider_posit.py b/chatlas/_provider_posit.py index 3e694eb0..90e367a5 100644 --- a/chatlas/_provider_posit.py +++ b/chatlas/_provider_posit.py @@ -309,6 +309,19 @@ def __init__( ), ) + def set_model( + self, value: str + ) -> "PositAnthropicProvider | PositOpenAIProvider": + if value.startswith("claude"): + self._model = value + return self + return PositOpenAIProvider( + base_url=self._gateway_base_url, + model=value, + credentials=self._credentials, + cache=self._cache, + ) + def list_models(self) -> list[ModelInfo]: return list_models_posit(self._gateway_base_url, self._credentials) @@ -320,12 +333,16 @@ def __init__( base_url: str, model: str, credentials: Callable[[], str], + cache: Literal["5m", "1h", "none"] = "5m", name: str = "Posit", ): super().__init__(model=model, api_key="not-used", name=name) self._gateway_base_url = base_url.rstrip("/") self._credentials = credentials + # Inert on this flavor (caching is Claude-only); stored so the + # setting survives a round trip through `set_model()`. + self._cache = cache auth = PositHttpx2Auth(credentials) flavor_base_url = f"{self._gateway_base_url}/openai/v1" @@ -349,6 +366,19 @@ def __init__( ), ) + def set_model( + self, value: str + ) -> "PositAnthropicProvider | PositOpenAIProvider": + if value.startswith("claude"): + return PositAnthropicProvider( + base_url=self._gateway_base_url, + model=value, + credentials=self._credentials, + cache=self._cache, + ) + self._model = value + return self + def list_models(self) -> list[ModelInfo]: return list_models_posit(self._gateway_base_url, self._credentials) @@ -445,6 +475,7 @@ def ChatPosit( base_url=base_url, model=model, credentials=token_provider, + cache=cache, ) return Chat(provider=provider, system_prompt=system_prompt) diff --git a/tests/test_provider_anthropic.py b/tests/test_provider_anthropic.py index d878b2fa..2710e90b 100644 --- a/tests/test_provider_anthropic.py +++ b/tests/test_provider_anthropic.py @@ -1145,3 +1145,41 @@ def test_anthropic_value_cost_prices_fallback_at_serving_models_rate(): # opus-4-8 rates ($5/$25 per 1M), not fable-5's ($10/$50). assert cost == pytest.approx((1000 * 5 + 50 * 25) / 1e6) + + +def test_anthropic_message_params_drop_unsigned_thinking_blocks(): + from chatlas._content import ContentText, ContentThinking + + provider = AnthropicProvider(model="claude-sonnet-4-5", api_key="dummy") + + # Thinking without a signature (e.g., reasoning from a non-Claude model, + # replayed after a provider switch) can't be sent to the API, so it's + # dropped; the text content of the turn is kept. + turns = [ + UserTurn("hi"), + AssistantTurn( + [ContentThinking(thinking="hmm"), ContentText(text="hello")] + ), + UserTurn("again"), + ] + messages = provider._as_message_params(turns) + assert messages[1]["content"] == [{"text": "hello", "type": "text"}] + + # Signed thinking (Claude's own) is preserved. + turns[1] = AssistantTurn( + [ + ContentThinking(thinking="hmm", extra={"signature": "sig"}), + ContentText(text="hello"), + ] + ) + messages = provider._as_message_params(turns) + assert messages[1]["content"][0] == { + "type": "thinking", + "thinking": "hmm", + "signature": "sig", + } + + # A turn consisting solely of unsigned thinking is dropped entirely. + turns[1] = AssistantTurn([ContentThinking(thinking="hmm")]) + messages = provider._as_message_params(turns) + assert len(messages) == 2 diff --git a/tests/test_provider_posit.py b/tests/test_provider_posit.py index e780c661..6d71fac5 100644 --- a/tests/test_provider_posit.py +++ b/tests/test_provider_posit.py @@ -537,3 +537,78 @@ def test_chat_posit_importable_from_package_root(): import chatlas assert chatlas.ChatPosit is ChatPosit + + +def test_chat_posit_set_model_switches_from_anthropic_to_openai(): + chat = ChatPosit(model="claude-sonnet-4-6", credentials=lambda: "test-token") + chat.model = "qwen3-8b" + + assert isinstance(chat.provider, PositOpenAIProvider) + assert chat.provider.model == "qwen3-8b" + assert ( + str(chat.provider._client.base_url).rstrip("/") + == "https://gateway.posit.ai/openai/v1" + ) + + +def test_chat_posit_set_model_switches_from_openai_to_anthropic(): + chat = ChatPosit(model="qwen3-8b", credentials=lambda: "test-token") + chat.model = "claude-sonnet-4-6" + + assert isinstance(chat.provider, PositAnthropicProvider) + assert chat.provider.model == "claude-sonnet-4-6" + assert ( + str(chat.provider._client.base_url).rstrip("/") + == "https://gateway.posit.ai/anthropic" + ) + + +def test_chat_posit_set_model_within_family_is_a_noop(): + chat = ChatPosit(model="claude-sonnet-4-6", credentials=lambda: "test-token") + provider = chat.provider + + chat.model = "claude-opus-4-1" + + assert chat.provider is provider + assert chat.provider.model == "claude-opus-4-1" + + chat = ChatPosit(model="qwen3-8b", credentials=lambda: "test-token") + provider = chat.provider + + chat.model = "gpt-4o" + + assert chat.provider is provider + assert chat.provider.model == "gpt-4o" + + +def test_chat_posit_set_model_carries_over_credentials(): + chat = ChatPosit(model="claude-sonnet-4-6", credentials=lambda: "test-token") + chat.model = "qwen3-8b" + assert chat.provider._credentials() == "test-token" + + chat.model = "claude-sonnet-4-6" + assert chat.provider._credentials() == "test-token" + + +def test_chat_posit_set_model_preserves_cache_across_family_switches(): + chat = ChatPosit( + model="claude-sonnet-4-6", cache="1h", credentials=lambda: "test-token" + ) + + chat.model = "qwen3-8b" + assert isinstance(chat.provider, PositOpenAIProvider) + + chat.model = "claude-sonnet-4-6" + assert isinstance(chat.provider, PositAnthropicProvider) + assert chat.provider._cache == "1h" + + +def test_chat_posit_set_model_openai_start_honors_cache_on_switch(): + chat = ChatPosit( + model="qwen3-8b", cache="none", credentials=lambda: "test-token" + ) + + chat.model = "claude-sonnet-4-6" + + assert isinstance(chat.provider, PositAnthropicProvider) + assert chat.provider._cache == "none" From f9dfa79517e226d25ece87d42298dd3d2d72c6c7 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 8 Sep 2026 11:11:31 -0500 Subject: [PATCH 2/7] chore: fix pyright errors and match ruff format in set_model hook --- chatlas/_provider.py | 2 +- chatlas/_provider_posit.py | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/chatlas/_provider.py b/chatlas/_provider.py index aab38d8d..eac98e85 100644 --- a/chatlas/_provider.py +++ b/chatlas/_provider.py @@ -197,7 +197,7 @@ def model(self): def model(self, value: str): self._model = value - def set_model(self, value: str) -> "Provider[Any, Any]": + def set_model(self, value: str) -> "Provider[Any, Any, Any, Any]": """ Set the model used by the provider. diff --git a/chatlas/_provider_posit.py b/chatlas/_provider_posit.py index 90e367a5..20cc5733 100644 --- a/chatlas/_provider_posit.py +++ b/chatlas/_provider_posit.py @@ -309,9 +309,7 @@ def __init__( ), ) - def set_model( - self, value: str - ) -> "PositAnthropicProvider | PositOpenAIProvider": + def set_model(self, value: str) -> "PositAnthropicProvider | PositOpenAIProvider": if value.startswith("claude"): self._model = value return self @@ -342,7 +340,7 @@ def __init__( self._credentials = credentials # Inert on this flavor (caching is Claude-only); stored so the # setting survives a round trip through `set_model()`. - self._cache = cache + self._cache: Literal["5m", "1h", "none"] = cache auth = PositHttpx2Auth(credentials) flavor_base_url = f"{self._gateway_base_url}/openai/v1" @@ -366,9 +364,7 @@ def __init__( ), ) - def set_model( - self, value: str - ) -> "PositAnthropicProvider | PositOpenAIProvider": + def set_model(self, value: str) -> "PositAnthropicProvider | PositOpenAIProvider": if value.startswith("claude"): return PositAnthropicProvider( base_url=self._gateway_base_url, From d53d1eab1998a2e35b5512e543bf2ffc913b1f27 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 8 Sep 2026 16:15:00 +0000 Subject: [PATCH 3/7] chore: update provider types and pricing data --- chatlas/data/prices.json | 2263 ++++++++++++++++++++++++---- chatlas/types/anthropic/_submit.py | 1 + 2 files changed, 2004 insertions(+), 260 deletions(-) diff --git a/chatlas/data/prices.json b/chatlas/data/prices.json index e5cb4e40..e8c153f9 100644 --- a/chatlas/data/prices.json +++ b/chatlas/data/prices.json @@ -1,7 +1,7 @@ { "schema_version": 1, "min_ellmer_version": "0.5.0", - "updated_at": "2026-09-02T12:13:24Z", + "updated_at": "2026-09-07T11:15:25Z", "data": [ { "provider": "AWS/Bedrock", @@ -2054,7 +2054,7 @@ "provider": "AWS/Bedrock", "model": "qwen.qwen3-coder-480b-a35b-v1:0", "variant": "", - "input": 0.22, + "input": 0.45, "output": 1.8 }, { @@ -2360,6 +2360,14 @@ "output": 6, "cached_input": 0.12 }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-east-1/anthropic.claude-opus-4-8", + "variant": "", + "input": 6, + "output": 30, + "cached_input": 0.6 + }, { "provider": "AWS/Bedrock", "model": "us-gov-east-1/anthropic.claude-sonnet-4-5-20250929-v1:0", @@ -2368,6 +2376,14 @@ "output": 18, "cached_input": 0.36 }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-east-1/anthropic.claude-sonnet-5", + "variant": "", + "input": 2.4, + "output": 12, + "cached_input": 0.24 + }, { "provider": "AWS/Bedrock", "model": "us-gov-east-1/claude-sonnet-4-5-20250929-v1:0", @@ -2390,6 +2406,49 @@ "input": 0.3, "output": 2.65 }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-east-1/nvidia.nemotron-nano-12b-v2", + "variant": "", + "input": 0.24, + "output": 0.72 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-east-1/nvidia.nemotron-nano-3-30b", + "variant": "", + "input": 0.072, + "output": 0.288 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-east-1/nvidia.nemotron-super-3-120b", + "variant": "", + "input": 0.18, + "output": 0.78 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-east-1/openai.gpt-5.4", + "variant": "", + "input": 3.3, + "output": 19.8, + "cached_input": 0.33 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-east-1/openai.gpt-oss-120b-1:0", + "variant": "", + "input": 0.18, + "output": 0.72 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-east-1/openai.gpt-oss-20b-1:0", + "variant": "", + "input": 0.084, + "output": 0.36 + }, { "provider": "AWS/Bedrock", "model": "us-gov-west-1/amazon.nova-pro-v1:0", @@ -2464,6 +2523,14 @@ "output": 6, "cached_input": 0.12 }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/anthropic.claude-opus-4-8", + "variant": "", + "input": 6, + "output": 30, + "cached_input": 0.6 + }, { "provider": "AWS/Bedrock", "model": "us-gov-west-1/anthropic.claude-sonnet-4-5-20250929-v1:0", @@ -2472,6 +2539,14 @@ "output": 18, "cached_input": 0.36 }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/anthropic.claude-sonnet-5", + "variant": "", + "input": 2.4, + "output": 12, + "cached_input": 0.24 + }, { "provider": "AWS/Bedrock", "model": "us-gov-west-1/claude-sonnet-4-5-20250929-v1:0", @@ -2492,7 +2567,98 @@ "model": "us-gov-west-1/meta.llama3-8b-instruct-v1:0", "variant": "", "input": 0.3, - "output": 2.65 + "output": 0.6 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/nvidia.nemotron-nano-12b-v2", + "variant": "", + "input": 0.24, + "output": 0.72 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/nvidia.nemotron-nano-3-30b", + "variant": "", + "input": 0.072, + "output": 0.288 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/nvidia.nemotron-super-3-120b", + "variant": "", + "input": 0.18, + "output": 0.78 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/openai.gpt-5.4", + "variant": "", + "input": 3.3, + "output": 19.8, + "cached_input": 0.33 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/openai.gpt-5.6-luna", + "variant": "", + "input": 0.264, + "output": 1.584, + "cached_input": 0.0264 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/openai.gpt-5.6-luna", + "variant": "above_272k_tokens", + "input": 0.528, + "output": 2.376, + "cached_input": 0.0528 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/openai.gpt-5.6-terra", + "variant": "", + "input": 2.64, + "output": 15.84, + "cached_input": 0.264 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/openai.gpt-5.6-terra", + "variant": "above_272k_tokens", + "input": 5.28, + "output": 23.76, + "cached_input": 0.528 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/openai.gpt-oss-120b-1:0", + "variant": "", + "input": 0.18, + "output": 0.72 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/openai.gpt-oss-20b-1:0", + "variant": "", + "input": 0.084, + "output": 0.36 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov-west-1/xai.grok-4.3", + "variant": "", + "input": 1.5, + "output": 3, + "cached_input": 0.24 + }, + { + "provider": "AWS/Bedrock", + "model": "us-gov.anthropic.claude-opus-4-8", + "variant": "", + "input": 6, + "output": 30, + "cached_input": 0.6 }, { "provider": "AWS/Bedrock", @@ -2510,6 +2676,14 @@ "output": 27, "cached_input": 0.72 }, + { + "provider": "AWS/Bedrock", + "model": "us-gov.anthropic.claude-sonnet-5", + "variant": "", + "input": 2.4, + "output": 12, + "cached_input": 0.24 + }, { "provider": "AWS/Bedrock", "model": "us-west-1/meta.llama3-70b-instruct-v1:0", @@ -3184,6 +3358,14 @@ "output": 50, "cached_input": 1 }, + { + "provider": "Anthropic", + "model": "claude-mythos-5-1", + "variant": "", + "input": 10, + "output": 50, + "cached_input": 0.25 + }, { "provider": "Anthropic", "model": "claude-mythos-preview", @@ -4768,6 +4950,22 @@ "output": 24, "cached_input": 0.4 }, + { + "provider": "Azure/OpenAI", + "model": "gpt-6-astra", + "variant": "", + "input": 10, + "output": 50, + "cached_input": 1 + }, + { + "provider": "Azure/OpenAI", + "model": "gpt-6-astra", + "variant": "above_272k_tokens", + "input": 20, + "output": 75, + "cached_input": 2 + }, { "provider": "Azure/OpenAI", "model": "gpt-audio-1.5-2026-02-23", @@ -4829,7 +5027,6 @@ "model": "gpt-image-2", "variant": "", "input": 5, - "output": 10, "cached_input": 1.25 }, { @@ -4837,7 +5034,6 @@ "model": "gpt-image-2-2026-04-21", "variant": "", "input": 5, - "output": 10, "cached_input": 1.25 }, { @@ -4848,6 +5044,30 @@ "output": 16, "cached_input": 4 }, + { + "provider": "Azure/OpenAI", + "model": "gpt-realtime-2", + "variant": "", + "input": 4, + "output": 24, + "cached_input": 0.4 + }, + { + "provider": "Azure/OpenAI", + "model": "gpt-realtime-2.1", + "variant": "", + "input": 4, + "output": 24, + "cached_input": 0.4 + }, + { + "provider": "Azure/OpenAI", + "model": "gpt-realtime-2.1-mini", + "variant": "", + "input": 0.6, + "output": 2.4, + "cached_input": 0.06 + }, { "provider": "Azure/OpenAI", "model": "gpt-realtime-2025-08-28", @@ -5039,6 +5259,36 @@ "input": 0.1, "output": 0 }, + { + "provider": "Azure/OpenAI", + "model": "us-gov/gpt-5.1", + "variant": "", + "input": 1.71875, + "output": 13.75, + "cached_input": 0.171875 + }, + { + "provider": "Azure/OpenAI", + "model": "us-gov/o3-mini", + "variant": "", + "input": 1.513, + "output": 6.05, + "cached_input": 0.757 + }, + { + "provider": "Azure/OpenAI", + "model": "us-gov/text-embedding-3-large", + "variant": "", + "input": 0.163, + "output": 0 + }, + { + "provider": "Azure/OpenAI", + "model": "us-gov/text-embedding-3-small", + "variant": "", + "input": 0.025, + "output": 0 + }, { "provider": "Azure/OpenAI", "model": "us/gpt-4.1-2025-04-14", @@ -5395,6 +5645,22 @@ "output": 26.4, "cached_input": 0.44 }, + { + "provider": "Azure/OpenAI", + "model": "us/gpt-6-astra", + "variant": "", + "input": 11, + "output": 55, + "cached_input": 1.1 + }, + { + "provider": "Azure/OpenAI", + "model": "us/gpt-6-astra", + "variant": "above_272k_tokens", + "input": 22, + "output": 82.5, + "cached_input": 2.2 + }, { "provider": "Azure/OpenAI", "model": "us/o1-2024-12-17", @@ -6045,6 +6311,37 @@ "output": 6.75, "cached_input": 0.135 }, + { + "provider": "Google/Gemini", + "model": "gemini-3.8-flash", + "variant": "", + "input": 0.75, + "output": 3.75, + "cached_input": 0.075 + }, + { + "provider": "Google/Gemini", + "model": "gemini-3.8-flash", + "variant": "batches", + "input": 0.375, + "output": 1.875 + }, + { + "provider": "Google/Gemini", + "model": "gemini-3.8-flash", + "variant": "flex", + "input": 0.375, + "output": 1.875, + "cached_input": 0.0375 + }, + { + "provider": "Google/Gemini", + "model": "gemini-3.8-flash", + "variant": "priority", + "input": 1.35, + "output": 6.75, + "cached_input": 0.135 + }, { "provider": "Google/Gemini", "model": "gemini-embedding-001", @@ -6657,30 +6954,61 @@ }, { "provider": "Google/Vertex", - "model": "gemini-live-2.5-flash-native-audio", - "variant": "", - "input": 0.5, - "output": 2 - }, - { - "provider": "Google/Vertex", - "model": "gemini-live-2.5-flash-preview-native-audio-09-2025", + "model": "gemini-3.8-flash", "variant": "", - "input": 0.5, - "output": 2, + "input": 0.75, + "output": 3.75, "cached_input": 0.075 }, { "provider": "Google/Vertex", - "model": "gemini-omni-flash-preview", - "variant": "", - "input": 1.5, - "output": 9 + "model": "gemini-3.8-flash", + "variant": "batches", + "input": 0.375, + "output": 1.875 }, { "provider": "Google/Vertex", - "model": "gemini-robotics-er-1.5-preview", - "variant": "", + "model": "gemini-3.8-flash", + "variant": "flex", + "input": 0.375, + "output": 1.875, + "cached_input": 0.0375 + }, + { + "provider": "Google/Vertex", + "model": "gemini-3.8-flash", + "variant": "priority", + "input": 1.35, + "output": 6.75, + "cached_input": 0.135 + }, + { + "provider": "Google/Vertex", + "model": "gemini-live-2.5-flash-native-audio", + "variant": "", + "input": 0.5, + "output": 2 + }, + { + "provider": "Google/Vertex", + "model": "gemini-live-2.5-flash-preview-native-audio-09-2025", + "variant": "", + "input": 0.5, + "output": 2, + "cached_input": 0.075 + }, + { + "provider": "Google/Vertex", + "model": "gemini-omni-flash-preview", + "variant": "", + "input": 1.5, + "output": 9 + }, + { + "provider": "Google/Vertex", + "model": "gemini-robotics-er-1.5-preview", + "variant": "", "input": 0.3, "output": 2.5, "cached_input": 0 @@ -7076,8 +7404,9 @@ "provider": "Mistral", "model": "magistral-medium-latest", "variant": "", - "input": 2, - "output": 5 + "input": 1.5, + "output": 7.5, + "cached_input": 0.15 }, { "provider": "Mistral", @@ -7097,22 +7426,25 @@ "provider": "Mistral", "model": "magistral-small-latest", "variant": "", - "input": 0.5, - "output": 1.5 + "input": 0.15, + "output": 0.6, + "cached_input": 0.015 }, { "provider": "Mistral", "model": "ministral-14b-2512", "variant": "", "input": 0.2, - "output": 0.2 + "output": 0.2, + "cached_input": 0.02 }, { "provider": "Mistral", "model": "ministral-14b-latest", "variant": "", "input": 0.2, - "output": 0.2 + "output": 0.2, + "cached_input": 0.02 }, { "provider": "Mistral", @@ -7143,14 +7475,16 @@ "model": "ministral-3b-2512", "variant": "", "input": 0.1, - "output": 0.1 + "output": 0.1, + "cached_input": 0.01 }, { "provider": "Mistral", "model": "ministral-3b-latest", "variant": "", "input": 0.1, - "output": 0.1 + "output": 0.1, + "cached_input": 0.01 }, { "provider": "Mistral", @@ -7252,8 +7586,9 @@ "provider": "Mistral", "model": "mistral-medium", "variant": "", - "input": 2.7, - "output": 8.1 + "input": 1.5, + "output": 7.5, + "cached_input": 0.15 }, { "provider": "Mistral", @@ -8640,6 +8975,14 @@ "output": 22.5, "cached_input": 0.5 }, + { + "provider": "OpenAI", + "model": "gpt-5.4", + "variant": "above_272k_tokens_flex", + "input": 2.5, + "output": 11.25, + "cached_input": 0.25 + }, { "provider": "OpenAI", "model": "gpt-5.4", @@ -8679,6 +9022,14 @@ "output": 22.5, "cached_input": 0.5 }, + { + "provider": "OpenAI", + "model": "gpt-5.4-2026-03-05", + "variant": "above_272k_tokens_flex", + "input": 2.5, + "output": 11.25, + "cached_input": 0.25 + }, { "provider": "OpenAI", "model": "gpt-5.4-2026-03-05", @@ -8826,6 +9177,13 @@ "output": 270, "cached_input": 6 }, + { + "provider": "OpenAI", + "model": "gpt-5.4-pro", + "variant": "above_272k_tokens_flex", + "input": 30, + "output": 135 + }, { "provider": "OpenAI", "model": "gpt-5.4-pro", @@ -8856,6 +9214,13 @@ "output": 270, "cached_input": 6 }, + { + "provider": "OpenAI", + "model": "gpt-5.4-pro-2026-03-05", + "variant": "above_272k_tokens_flex", + "input": 30, + "output": 135 + }, { "provider": "OpenAI", "model": "gpt-5.4-pro-2026-03-05", @@ -8886,6 +9251,14 @@ "output": 45, "cached_input": 1 }, + { + "provider": "OpenAI", + "model": "gpt-5.5", + "variant": "above_272k_tokens_flex", + "input": 5, + "output": 22.5, + "cached_input": 0.5 + }, { "provider": "OpenAI", "model": "gpt-5.5", @@ -8905,9 +9278,9 @@ "provider": "OpenAI", "model": "gpt-5.5", "variant": "priority", - "input": 10, - "output": 60, - "cached_input": 1 + "input": 12.5, + "output": 75, + "cached_input": 1.25 }, { "provider": "OpenAI", @@ -8925,6 +9298,14 @@ "output": 45, "cached_input": 1 }, + { + "provider": "OpenAI", + "model": "gpt-5.5-2026-04-23", + "variant": "above_272k_tokens_flex", + "input": 5, + "output": 22.5, + "cached_input": 0.5 + }, { "provider": "OpenAI", "model": "gpt-5.5-2026-04-23", @@ -8944,9 +9325,9 @@ "provider": "OpenAI", "model": "gpt-5.5-2026-04-23", "variant": "priority", - "input": 10, - "output": 60, - "cached_input": 1 + "input": 12.5, + "output": 75, + "cached_input": 1.25 }, { "provider": "OpenAI", @@ -9032,6 +9413,14 @@ "output": 15, "cached_input": 0.4 }, + { + "provider": "OpenAI", + "model": "gpt-5.6", + "variant": "above_272k_tokens_priority", + "input": 16, + "output": 60, + "cached_input": 1.6 + }, { "provider": "OpenAI", "model": "gpt-5.6", @@ -9095,6 +9484,14 @@ "output": 0.9, "cached_input": 0.02 }, + { + "provider": "OpenAI", + "model": "gpt-5.6-luna", + "variant": "above_272k_tokens_priority", + "input": 0.8, + "output": 3.6, + "cached_input": 0.08 + }, { "provider": "OpenAI", "model": "gpt-5.6-luna", @@ -9142,6 +9539,14 @@ "output": 15, "cached_input": 0.4 }, + { + "provider": "OpenAI", + "model": "gpt-5.6-sol", + "variant": "above_272k_tokens_priority", + "input": 16, + "output": 60, + "cached_input": 1.6 + }, { "provider": "OpenAI", "model": "gpt-5.6-sol", @@ -9189,6 +9594,14 @@ "output": 9, "cached_input": 0.2 }, + { + "provider": "OpenAI", + "model": "gpt-5.6-terra", + "variant": "above_272k_tokens_priority", + "input": 8, + "output": 36, + "cached_input": 0.8 + }, { "provider": "OpenAI", "model": "gpt-5.6-terra", @@ -9212,6 +9625,61 @@ "output": 24, "cached_input": 0.4 }, + { + "provider": "OpenAI", + "model": "gpt-6-astra", + "variant": "", + "input": 10, + "output": 50, + "cached_input": 1 + }, + { + "provider": "OpenAI", + "model": "gpt-6-astra", + "variant": "above_272k_tokens", + "input": 20, + "output": 75, + "cached_input": 2 + }, + { + "provider": "OpenAI", + "model": "gpt-6-astra", + "variant": "above_272k_tokens_flex", + "input": 10, + "output": 37.5, + "cached_input": 1 + }, + { + "provider": "OpenAI", + "model": "gpt-6-astra", + "variant": "above_272k_tokens_priority", + "input": 40, + "output": 150, + "cached_input": 4 + }, + { + "provider": "OpenAI", + "model": "gpt-6-astra", + "variant": "batches", + "input": 5, + "output": 25 + }, + { + "provider": "OpenAI", + "model": "gpt-6-astra", + "variant": "flex", + "input": 5, + "output": 25, + "cached_input": 0.5 + }, + { + "provider": "OpenAI", + "model": "gpt-6-astra", + "variant": "priority", + "input": 20, + "output": 100, + "cached_input": 2 + }, { "provider": "OpenAI", "model": "gpt-audio", @@ -9254,6 +9722,38 @@ "input": 0.6, "output": 2.4 }, + { + "provider": "OpenAI", + "model": "gpt-daybreak-blue-latest", + "variant": "", + "input": 4, + "output": 20, + "cached_input": 0.4 + }, + { + "provider": "OpenAI", + "model": "gpt-daybreak-blue-latest", + "variant": "above_272k_tokens", + "input": 8, + "output": 30, + "cached_input": 0.8 + }, + { + "provider": "OpenAI", + "model": "gpt-daybreak-red-latest", + "variant": "", + "input": 12.5, + "output": 75, + "cached_input": 1.25 + }, + { + "provider": "OpenAI", + "model": "gpt-daybreak-red-latest", + "variant": "above_272k_tokens", + "input": 25, + "output": 112.5, + "cached_input": 2.5 + }, { "provider": "OpenAI", "model": "gpt-image-1", @@ -9289,7 +9789,6 @@ "model": "gpt-image-2", "variant": "", "input": 5, - "output": 10, "cached_input": 1.25 }, { @@ -9297,7 +9796,6 @@ "model": "gpt-image-2-2026-04-21", "variant": "", "input": 5, - "output": 10, "cached_input": 1.25 }, { @@ -9321,7 +9819,7 @@ "model": "gpt-realtime-2", "variant": "", "input": 4, - "output": 16, + "output": 24, "cached_input": 0.4 }, { @@ -9685,6 +10183,22 @@ "input": 3, "output": 15 }, + { + "provider": "OpenRouter", + "model": "anthropic/claude-fable-5", + "variant": "", + "input": 10, + "output": 50, + "cached_input": 1 + }, + { + "provider": "OpenRouter", + "model": "anthropic/claude-fable-5.1", + "variant": "", + "input": 10, + "output": 50, + "cached_input": 0.25 + }, { "provider": "OpenRouter", "model": "anthropic/claude-haiku-4.5", @@ -9733,6 +10247,14 @@ "output": 25, "cached_input": 0.5 }, + { + "provider": "OpenRouter", + "model": "anthropic/claude-opus-4.8", + "variant": "", + "input": 5, + "output": 25, + "cached_input": 0.5 + }, { "provider": "OpenRouter", "model": "anthropic/claude-opus-5", @@ -9789,6 +10311,14 @@ "output": 22.5, "cached_input": 0.6 }, + { + "provider": "OpenRouter", + "model": "anthropic/claude-sonnet-5", + "variant": "", + "input": 2, + "output": 10, + "cached_input": 0.2 + }, { "provider": "OpenRouter", "model": "bytedance/ui-tars-1.5-7b", @@ -9800,15 +10330,15 @@ "provider": "OpenRouter", "model": "deepseek/deepseek-chat", "variant": "", - "input": 0.14, - "output": 0.28 + "input": 0.32, + "output": 0.89 }, { "provider": "OpenRouter", "model": "deepseek/deepseek-chat-v3-0324", "variant": "", - "input": 0.14, - "output": 0.28 + "input": 0.25, + "output": 1 }, { "provider": "OpenRouter", @@ -9827,8 +10357,8 @@ "provider": "OpenRouter", "model": "deepseek/deepseek-r1", "variant": "", - "input": 0.55, - "output": 2.19 + "input": 0.7, + "output": 2.5 }, { "provider": "OpenRouter", @@ -9849,11 +10379,26 @@ "variant": "cache_hit", "input": 0.14 }, + { + "provider": "OpenRouter", + "model": "deepseek/deepseek-r1-distill-llama-70b", + "variant": "", + "input": 0.8, + "output": 0.8 + }, + { + "provider": "OpenRouter", + "model": "deepseek/deepseek-v3.1-terminus", + "variant": "", + "input": 0.27, + "output": 1, + "cached_input": 0.135 + }, { "provider": "OpenRouter", "model": "deepseek/deepseek-v3.2", "variant": "", - "input": 0.28, + "input": 0.269, "output": 0.4 }, { @@ -9866,8 +10411,8 @@ "provider": "OpenRouter", "model": "deepseek/deepseek-v3.2-exp", "variant": "", - "input": 0.2, - "output": 0.4 + "input": 0.27, + "output": 0.41 }, { "provider": "OpenRouter", @@ -9875,6 +10420,30 @@ "variant": "cache_hit", "input": 0.02 }, + { + "provider": "OpenRouter", + "model": "deepseek/deepseek-v4-flash", + "variant": "", + "input": 0.08778, + "output": 0.17556, + "cached_input": 0.017556 + }, + { + "provider": "OpenRouter", + "model": "deepseek/deepseek-v4-flash-0731", + "variant": "", + "input": 0.065, + "output": 0.18, + "cached_input": 0.016 + }, + { + "provider": "OpenRouter", + "model": "deepseek/deepseek-v4-flash-vision-exp", + "variant": "", + "input": 0.22, + "output": 0.66, + "cached_input": 0.007 + }, { "provider": "OpenRouter", "model": "deepseek/deepseek-v4-pro", @@ -9913,14 +10482,64 @@ "model": "google/gemini-2.5-flash", "variant": "", "input": 0.3, - "output": 2.5 + "output": 2.5, + "cached_input": 0.03 + }, + { + "provider": "OpenRouter", + "model": "google/gemini-2.5-flash-image", + "variant": "", + "input": 0.3, + "output": 2.5, + "cached_input": 0.03 + }, + { + "provider": "OpenRouter", + "model": "google/gemini-2.5-flash-lite", + "variant": "", + "input": 0.1, + "output": 0.4, + "cached_input": 0.01 }, { "provider": "OpenRouter", "model": "google/gemini-2.5-pro", "variant": "", "input": 1.25, - "output": 10 + "output": 10, + "cached_input": 0.125 + }, + { + "provider": "OpenRouter", + "model": "google/gemini-2.5-pro-preview", + "variant": "", + "input": 1.25, + "output": 10, + "cached_input": 0.125 + }, + { + "provider": "OpenRouter", + "model": "google/gemini-2.5-pro-preview", + "variant": "above_200k_tokens", + "input": 2.5, + "output": 15, + "cached_input": 0.25 + }, + { + "provider": "OpenRouter", + "model": "google/gemini-2.5-pro-preview-05-06", + "variant": "", + "input": 1.25, + "output": 10, + "cached_input": 0.125 + }, + { + "provider": "OpenRouter", + "model": "google/gemini-2.5-pro-preview-05-06", + "variant": "above_200k_tokens", + "input": 2.5, + "output": 15, + "cached_input": 0.25 }, { "provider": "OpenRouter", @@ -9930,6 +10549,22 @@ "output": 3, "cached_input": 0.05 }, + { + "provider": "OpenRouter", + "model": "google/gemini-3-pro-image", + "variant": "", + "input": 2, + "output": 12, + "cached_input": 0.2 + }, + { + "provider": "OpenRouter", + "model": "google/gemini-3-pro-image-preview", + "variant": "", + "input": 2, + "output": 12, + "cached_input": 0.2 + }, { "provider": "OpenRouter", "model": "google/gemini-3-pro-preview", @@ -9953,6 +10588,20 @@ "input": 1, "output": 6 }, + { + "provider": "OpenRouter", + "model": "google/gemini-3.1-flash-image", + "variant": "", + "input": 0.5, + "output": 3 + }, + { + "provider": "OpenRouter", + "model": "google/gemini-3.1-flash-image-preview", + "variant": "", + "input": 0.5, + "output": 3 + }, { "provider": "OpenRouter", "model": "google/gemini-3.1-flash-lite", @@ -9961,6 +10610,13 @@ "output": 1.5, "cached_input": 0.025 }, + { + "provider": "OpenRouter", + "model": "google/gemini-3.1-flash-lite-image", + "variant": "", + "input": 0.25, + "output": 1.5 + }, { "provider": "OpenRouter", "model": "google/gemini-3.1-flash-lite-preview", @@ -9987,195 +10643,660 @@ }, { "provider": "OpenRouter", - "model": "gryphe/mythomax-l2-13b", + "model": "google/gemini-3.1-pro-preview-customtools", "variant": "", - "input": 1.875, - "output": 1.875 + "input": 2, + "output": 12, + "cached_input": 0.2 }, { "provider": "OpenRouter", - "model": "mancer/weaver", - "variant": "", - "input": 5.625, - "output": 5.625 + "model": "google/gemini-3.1-pro-preview-customtools", + "variant": "above_200k_tokens", + "input": 4, + "output": 18, + "cached_input": 0.4 }, { "provider": "OpenRouter", - "model": "meta-llama/llama-3-70b-instruct", + "model": "google/gemini-3.5-flash", "variant": "", - "input": 0.59, - "output": 0.79 + "input": 1.5, + "output": 9, + "cached_input": 0.15 }, { "provider": "OpenRouter", - "model": "minimax/minimax-m2", + "model": "google/gemini-3.5-flash-lite", "variant": "", - "input": 0.255, - "output": 1.02 + "input": 0.3, + "output": 2.5, + "cached_input": 0.03 }, { "provider": "OpenRouter", - "model": "minimax/minimax-m2.1", + "model": "google/gemini-3.6-flash", "variant": "", - "input": 0.27, - "output": 1.2, - "cached_input": 0 + "input": 0.75, + "output": 3.75, + "cached_input": 0.075 }, { "provider": "OpenRouter", - "model": "minimax/minimax-m2.5", + "model": "google/gemini-3.7-flash", "variant": "", - "input": 0.3, - "output": 1.1, - "cached_input": 0.15 + "input": 0.75, + "output": 3.75, + "cached_input": 0.075 }, { "provider": "OpenRouter", - "model": "mistralai/devstral-2512", + "model": "google/gemini-3.8-flash", "variant": "", - "input": 0.15, - "output": 0.6 + "input": 0.75, + "output": 3.75, + "cached_input": 0.075 }, { "provider": "OpenRouter", - "model": "mistralai/ministral-14b-2512", + "model": "google/gemma-2-27b-it", "variant": "", - "input": 0.2, - "output": 0.2 + "input": 0.65, + "output": 0.65 }, { "provider": "OpenRouter", - "model": "mistralai/ministral-3b-2512", + "model": "google/gemma-3-12b-it", "variant": "", - "input": 0.1, - "output": 0.1 + "input": 0.05, + "output": 0.15 }, { "provider": "OpenRouter", - "model": "mistralai/ministral-8b-2512", + "model": "google/gemma-3-27b-it", "variant": "", - "input": 0.15, - "output": 0.15 + "input": 0.08, + "output": 0.45, + "cached_input": 0.04 }, { "provider": "OpenRouter", - "model": "mistralai/mistral-7b-instruct", + "model": "google/gemma-3-4b-it", "variant": "", - "input": 0.13, - "output": 0.13 + "input": 0.05, + "output": 0.1 }, { "provider": "OpenRouter", - "model": "mistralai/mistral-large", + "model": "google/gemma-4-26b-a4b-it", "variant": "", - "input": 8, - "output": 24 + "input": 0.07, + "output": 0.34 }, { "provider": "OpenRouter", - "model": "mistralai/mistral-large-2512", + "model": "google/gemma-4-31b-it", "variant": "", - "input": 0.5, - "output": 1.5 + "input": 0.09, + "output": 0.34, + "cached_input": 0.05 }, { "provider": "OpenRouter", - "model": "mistralai/mistral-small-3.1-24b-instruct", + "model": "gryphe/mythomax-l2-13b", "variant": "", - "input": 0.1, - "output": 0.3 + "input": 0.06, + "output": 0.06 }, { "provider": "OpenRouter", - "model": "mistralai/mistral-small-3.2-24b-instruct", + "model": "mancer/weaver", "variant": "", - "input": 0.1, - "output": 0.3 + "input": 0.4, + "output": 0.75 }, { "provider": "OpenRouter", - "model": "mistralai/mixtral-8x22b-instruct", + "model": "meta-llama/llama-3-70b-instruct", "variant": "", - "input": 0.65, - "output": 0.65 + "input": 0.59, + "output": 0.79 }, { "provider": "OpenRouter", - "model": "moonshotai/kimi-k2.5", + "model": "meta-llama/llama-3.1-70b-instruct", "variant": "", - "input": 0.6, - "output": 3, - "cached_input": 0.1 + "input": 0.4, + "output": 0.4 }, { "provider": "OpenRouter", - "model": "nvidia/nemotron-3.5-lightning", + "model": "meta-llama/llama-3.1-8b-instruct", "variant": "", "input": 0.05, - "output": 0.2 + "output": 0.08, + "cached_input": 0.025 }, { "provider": "OpenRouter", - "model": "openai/gpt-3.5-turbo", + "model": "meta-llama/llama-3.2-1b-instruct", "variant": "", - "input": 1.5, - "output": 2 + "input": 0.027, + "output": 0.201 }, { "provider": "OpenRouter", - "model": "openai/gpt-3.5-turbo-16k", + "model": "meta-llama/llama-3.2-3b-instruct", "variant": "", - "input": 3, - "output": 4 + "input": 0.05, + "output": 0.33 }, { "provider": "OpenRouter", - "model": "openai/gpt-4", + "model": "meta-llama/llama-3.3-70b-instruct", "variant": "", - "input": 30, - "output": 60 + "input": 0.1, + "output": 0.32 }, { "provider": "OpenRouter", - "model": "openai/gpt-4.1", + "model": "meta-llama/llama-4-maverick", "variant": "", - "input": 2, - "output": 8, - "cached_input": 0.5 + "input": 0.2, + "output": 0.696 }, { "provider": "OpenRouter", - "model": "openai/gpt-4.1-mini", + "model": "meta-llama/llama-4-scout", "variant": "", - "input": 0.4, - "output": 1.6, - "cached_input": 0.1 + "input": 0.1, + "output": 0.3 }, { "provider": "OpenRouter", - "model": "openai/gpt-4.1-nano", + "model": "meta-llama/llama-guard-4-12b", "variant": "", - "input": 0.1, - "output": 0.4, - "cached_input": 0.025 + "input": 0.18, + "output": 0.18 }, { "provider": "OpenRouter", - "model": "openai/gpt-4o", + "model": "minimax/minimax-01", "variant": "", - "input": 2.5, - "output": 10 + "input": 0.2, + "output": 1.1 }, { "provider": "OpenRouter", - "model": "openai/gpt-4o-2024-05-13", + "model": "minimax/minimax-m1", "variant": "", - "input": 5, - "output": 15 + "input": 0.55, + "output": 2.2 }, { "provider": "OpenRouter", - "model": "openai/gpt-5", + "model": "minimax/minimax-m2", + "variant": "", + "input": 0.255, + "output": 1.02 + }, + { + "provider": "OpenRouter", + "model": "minimax/minimax-m2-her", + "variant": "", + "input": 0.3, + "output": 1.2, + "cached_input": 0.03 + }, + { + "provider": "OpenRouter", + "model": "minimax/minimax-m2.1", + "variant": "", + "input": 0.3, + "output": 1.2, + "cached_input": 0.03 + }, + { + "provider": "OpenRouter", + "model": "minimax/minimax-m2.5", + "variant": "", + "input": 0.27, + "output": 1.08, + "cached_input": 0.027 + }, + { + "provider": "OpenRouter", + "model": "minimax/minimax-m2.7", + "variant": "", + "input": 0.3, + "output": 1.2, + "cached_input": 0.06 + }, + { + "provider": "OpenRouter", + "model": "minimax/minimax-m3", + "variant": "", + "input": 0.3, + "output": 1.2, + "cached_input": 0.06 + }, + { + "provider": "OpenRouter", + "model": "mistralai/codestral-2508", + "variant": "", + "input": 0.3, + "output": 0.9, + "cached_input": 0.03 + }, + { + "provider": "OpenRouter", + "model": "mistralai/devstral-2512", + "variant": "", + "input": 0.4, + "output": 2 + }, + { + "provider": "OpenRouter", + "model": "mistralai/ministral-14b-2512", + "variant": "", + "input": 0.2, + "output": 0.2 + }, + { + "provider": "OpenRouter", + "model": "mistralai/ministral-3b-2512", + "variant": "", + "input": 0.1, + "output": 0.1 + }, + { + "provider": "OpenRouter", + "model": "mistralai/ministral-8b-2512", + "variant": "", + "input": 0.15, + "output": 0.15 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-7b-instruct", + "variant": "", + "input": 0.13, + "output": 0.13 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-large", + "variant": "", + "input": 2, + "output": 6 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-large-2407", + "variant": "", + "input": 2, + "output": 6, + "cached_input": 0.2 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-large-2512", + "variant": "", + "input": 0.5, + "output": 1.5 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-medium-3", + "variant": "", + "input": 0.4, + "output": 2, + "cached_input": 0.04 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-medium-3-5", + "variant": "", + "input": 1.5, + "output": 7.5 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-medium-3.1", + "variant": "", + "input": 0.4, + "output": 2, + "cached_input": 0.04 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-nemo", + "variant": "", + "input": 0.019, + "output": 0.03 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-saba", + "variant": "", + "input": 0.2, + "output": 0.6, + "cached_input": 0.02 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-small-24b-instruct-2501", + "variant": "", + "input": 0.05, + "output": 0.08 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-small-2603", + "variant": "", + "input": 0.15, + "output": 0.6, + "cached_input": 0.015 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-small-3.1-24b-instruct", + "variant": "", + "input": 0.351, + "output": 0.555 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mistral-small-3.2-24b-instruct", + "variant": "", + "input": 0.075, + "output": 0.2 + }, + { + "provider": "OpenRouter", + "model": "mistralai/mixtral-8x22b-instruct", + "variant": "", + "input": 2, + "output": 6 + }, + { + "provider": "OpenRouter", + "model": "mistralai/voxtral-small-24b-2507", + "variant": "", + "input": 0.1, + "output": 0.3, + "cached_input": 0.01 + }, + { + "provider": "OpenRouter", + "model": "moonshotai/kimi-k2", + "variant": "", + "input": 0.57, + "output": 2.3 + }, + { + "provider": "OpenRouter", + "model": "moonshotai/kimi-k2-0905", + "variant": "", + "input": 0.6, + "output": 2.5 + }, + { + "provider": "OpenRouter", + "model": "moonshotai/kimi-k2-thinking", + "variant": "", + "input": 0.6, + "output": 2.5, + "cached_input": 0.15 + }, + { + "provider": "OpenRouter", + "model": "moonshotai/kimi-k2.5", + "variant": "", + "input": 0.45, + "output": 2.25, + "cached_input": 0.07 + }, + { + "provider": "OpenRouter", + "model": "moonshotai/kimi-k2.6", + "variant": "", + "input": 0.95, + "output": 4, + "cached_input": 0.16 + }, + { + "provider": "OpenRouter", + "model": "moonshotai/kimi-k2.7-code", + "variant": "", + "input": 0.66, + "output": 3.4, + "cached_input": 0.18 + }, + { + "provider": "OpenRouter", + "model": "moonshotai/kimi-k3", + "variant": "", + "input": 3, + "output": 15, + "cached_input": 0.3 + }, + { + "provider": "OpenRouter", + "model": "nvidia/nemotron-3-nano-30b-a3b", + "variant": "", + "input": 0.05, + "output": 0.2, + "cached_input": 0.03 + }, + { + "provider": "OpenRouter", + "model": "nvidia/nemotron-3-super-120b-a12b", + "variant": "", + "input": 0.085, + "output": 0.4 + }, + { + "provider": "OpenRouter", + "model": "nvidia/nemotron-3-ultra-550b-a55b", + "variant": "", + "input": 0.625, + "output": 3.125, + "cached_input": 0.1875 + }, + { + "provider": "OpenRouter", + "model": "nvidia/nemotron-3.5-content-safety", + "variant": "", + "input": 0.2, + "output": 0.2 + }, + { + "provider": "OpenRouter", + "model": "nvidia/nemotron-3.5-lightning", + "variant": "", + "input": 0.08, + "output": 0.2 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-3.5-turbo", + "variant": "", + "input": 0.5, + "output": 1.5 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-3.5-turbo-16k", + "variant": "", + "input": 3, + "output": 4 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-3.5-turbo-instruct", + "variant": "", + "input": 1.5, + "output": 2 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4", + "variant": "", + "input": 30, + "output": 60 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4-turbo", + "variant": "", + "input": 10, + "output": 30 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4-turbo-preview", + "variant": "", + "input": 10, + "output": 30 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4.1", + "variant": "", + "input": 2, + "output": 8, + "cached_input": 0.5 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4.1-mini", + "variant": "", + "input": 0.4, + "output": 1.6, + "cached_input": 0.1 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4.1-nano", + "variant": "", + "input": 0.1, + "output": 0.4, + "cached_input": 0.025 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4o", + "variant": "", + "input": 2.5, + "output": 10, + "cached_input": 1.25 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4o-2024-05-13", + "variant": "", + "input": 5, + "output": 15 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4o-2024-08-06", + "variant": "", + "input": 2.5, + "output": 10, + "cached_input": 1.25 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4o-2024-11-20", + "variant": "", + "input": 2.5, + "output": 10, + "cached_input": 1.25 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4o-mini", + "variant": "", + "input": 0.15, + "output": 0.6, + "cached_input": 0.075 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-4o-mini-2024-07-18", + "variant": "", + "input": 0.15, + "output": 0.6, + "cached_input": 0.075 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5", + "variant": "", + "input": 1.25, + "output": 10, + "cached_input": 0.125 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5-chat", + "variant": "", + "input": 1.25, + "output": 10, + "cached_input": 0.125 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5-codex", + "variant": "", + "input": 1.25, + "output": 10, + "cached_input": 0.125 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5-mini", + "variant": "", + "input": 0.25, + "output": 2, + "cached_input": 0.025 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5-nano", + "variant": "", + "input": 0.05, + "output": 0.4, + "cached_input": 0.005 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5-pro", + "variant": "", + "input": 15, + "output": 120 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.1", + "variant": "", + "input": 1.25, + "output": 10, + "cached_input": 0.125 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.1-codex", + "variant": "", + "input": 1.25, + "output": 10, + "cached_input": 0.13 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.1-codex-max", "variant": "", "input": 1.25, "output": 10, @@ -10183,229 +11304,779 @@ }, { "provider": "OpenRouter", - "model": "openai/gpt-5-chat", + "model": "openai/gpt-5.1-codex-mini", + "variant": "", + "input": 0.25, + "output": 2, + "cached_input": 0.03 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.2", + "variant": "", + "input": 1.75, + "output": 14, + "cached_input": 0.175 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.2-chat", + "variant": "", + "input": 1.75, + "output": 14, + "cached_input": 0.175 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.2-codex", + "variant": "", + "input": 1.75, + "output": 14, + "cached_input": 0.175 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.2-pro", + "variant": "", + "input": 21, + "output": 168 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.3-codex", + "variant": "", + "input": 1.75, + "output": 14, + "cached_input": 0.175 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.4", + "variant": "", + "input": 2.5, + "output": 15, + "cached_input": 0.25 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.4-mini", + "variant": "", + "input": 0.75, + "output": 4.5, + "cached_input": 0.075 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.4-nano", + "variant": "", + "input": 0.2, + "output": 1.25, + "cached_input": 0.02 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.4-pro", + "variant": "", + "input": 30, + "output": 180 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.4-pro", + "variant": "above_272k_tokens", + "input": 60, + "output": 270 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.5", + "variant": "", + "input": 5, + "output": 30, + "cached_input": 0.5 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.5-pro", + "variant": "", + "input": 30, + "output": 180 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.5-pro", + "variant": "above_272k_tokens", + "input": 60, + "output": 270 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.6-luna", + "variant": "", + "input": 0.2, + "output": 1.2, + "cached_input": 0.02 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-5.6-terra", + "variant": "", + "input": 2, + "output": 12, + "cached_input": 0.2 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-6-astra", + "variant": "", + "input": 10, + "output": 50, + "cached_input": 1 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-6-astra", + "variant": "above_272k_tokens", + "input": 20, + "output": 75, + "cached_input": 2 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-audio", + "variant": "", + "input": 2.5, + "output": 10 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-audio-mini", + "variant": "", + "input": 0.6, + "output": 2.4 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-oss-120b", + "variant": "", + "input": 0.037, + "output": 0.17 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-oss-20b", + "variant": "", + "input": 0.03, + "output": 0.13 + }, + { + "provider": "OpenRouter", + "model": "openai/gpt-oss-safeguard-20b", + "variant": "", + "input": 0.075, + "output": 0.3, + "cached_input": 0.0375 + }, + { + "provider": "OpenRouter", + "model": "openai/o1", + "variant": "", + "input": 15, + "output": 60, + "cached_input": 7.5 + }, + { + "provider": "OpenRouter", + "model": "openai/o1-pro", + "variant": "", + "input": 150, + "output": 600 + }, + { + "provider": "OpenRouter", + "model": "openai/o3", + "variant": "", + "input": 2, + "output": 8, + "cached_input": 0.5 + }, + { + "provider": "OpenRouter", + "model": "openai/o3-mini", + "variant": "", + "input": 1.1, + "output": 4.4, + "cached_input": 0.55 + }, + { + "provider": "OpenRouter", + "model": "openai/o3-mini-high", + "variant": "", + "input": 1.1, + "output": 4.4, + "cached_input": 0.55 + }, + { + "provider": "OpenRouter", + "model": "openai/o3-pro", "variant": "", - "input": 1.25, - "output": 10, - "cached_input": 0.125 + "input": 20, + "output": 80 + }, + { + "provider": "OpenRouter", + "model": "openai/o4-mini", + "variant": "", + "input": 1.1, + "output": 4.4, + "cached_input": 0.275 + }, + { + "provider": "OpenRouter", + "model": "openai/o4-mini-high", + "variant": "", + "input": 1.1, + "output": 4.4, + "cached_input": 0.275 + }, + { + "provider": "OpenRouter", + "model": "poolside/laguna-s-2.1", + "variant": "", + "input": 0.09, + "output": 0.18, + "cached_input": 0.009 + }, + { + "provider": "OpenRouter", + "model": "poolside/laguna-xs-2.1", + "variant": "", + "input": 0.06, + "output": 0.12, + "cached_input": 0.03 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen-2.5-72b-instruct", + "variant": "", + "input": 0.36, + "output": 0.4 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen-2.5-7b-instruct", + "variant": "", + "input": 0.1, + "output": 0.2 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen-2.5-coder-32b-instruct", + "variant": "", + "input": 0.66, + "output": 1 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen-plus", + "variant": "", + "input": 0.26, + "output": 0.78, + "cached_input": 0.052 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen-plus", + "variant": "above_256k_tokens", + "input": 0.78, + "output": 2.34, + "cached_input": 0.156 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen-plus-2025-07-28", + "variant": "", + "input": 0.26, + "output": 0.78 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen-plus-2025-07-28", + "variant": "above_256k_tokens", + "input": 0.78, + "output": 2.34 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen-vl-plus", + "variant": "", + "input": 0.21, + "output": 0.63 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen2.5-vl-72b-instruct", + "variant": "", + "input": 0.8, + "output": 1, + "cached_input": 0.4 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-14b", + "variant": "", + "input": 0.12, + "output": 0.24 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-235b-a22b", + "variant": "", + "input": 0.455, + "output": 1.82 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-235b-a22b-2507", + "variant": "", + "input": 0.0875, + "output": 0.35 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-235b-a22b-thinking-2507", + "variant": "", + "input": 0.23, + "output": 2.3 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-30b-a3b", + "variant": "", + "input": 0.12, + "output": 0.5 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-30b-a3b-instruct-2507", + "variant": "", + "input": 0.04815, + "output": 0.19305 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-30b-a3b-thinking-2507", + "variant": "", + "input": 0.2, + "output": 2.4 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-32b", + "variant": "", + "input": 0.08, + "output": 0.28 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-8b", + "variant": "", + "input": 0.117, + "output": 0.455 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-coder", + "variant": "", + "input": 0.3, + "output": 1 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-coder-30b-a3b-instruct", + "variant": "", + "input": 0.07, + "output": 0.28 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-coder-flash", + "variant": "", + "input": 0.195, + "output": 0.975, + "cached_input": 0.039 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-coder-flash", + "variant": "above_128k_tokens", + "input": 0.52, + "output": 2.6, + "cached_input": 0.104 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-coder-next", + "variant": "", + "input": 0.12, + "output": 0.8, + "cached_input": 0.07 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-coder-plus", + "variant": "", + "input": 0.65, + "output": 3.25 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-max", + "variant": "", + "input": 0.78, + "output": 3.9, + "cached_input": 0.156 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-max", + "variant": "above_128k_tokens", + "input": 1.95, + "output": 9.75, + "cached_input": 0.39 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-max-thinking", + "variant": "", + "input": 0.78, + "output": 3.9 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-max-thinking", + "variant": "above_128k_tokens", + "input": 1.95, + "output": 9.75 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-next-80b-a3b-instruct", + "variant": "", + "input": 0.1, + "output": 1.1, + "cached_input": 0.07 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-next-80b-a3b-thinking", + "variant": "", + "input": 0.15, + "output": 1.2 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-vl-235b-a22b-instruct", + "variant": "", + "input": 0.21, + "output": 1.9, + "cached_input": 0.1 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-vl-235b-a22b-thinking", + "variant": "", + "input": 0.4, + "output": 4 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-vl-30b-a3b-instruct", + "variant": "", + "input": 0.15, + "output": 0.6 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-vl-30b-a3b-thinking", + "variant": "", + "input": 0.2, + "output": 2.4 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-vl-32b-instruct", + "variant": "", + "input": 0.104, + "output": 0.416 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-vl-8b-instruct", + "variant": "", + "input": 0.117, + "output": 0.455 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3-vl-8b-thinking", + "variant": "", + "input": 0.18, + "output": 2.1 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3.5-122b-a10b", + "variant": "", + "input": 0.29, + "output": 2.4 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3.5-27b", + "variant": "", + "input": 0.195, + "output": 1.56 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3.5-35b-a3b", + "variant": "", + "input": 0.25, + "output": 1.25 }, { "provider": "OpenRouter", - "model": "openai/gpt-5-codex", + "model": "qwen/qwen3.5-397b-a17b", "variant": "", - "input": 1.25, - "output": 10, - "cached_input": 0.125 + "input": 0.55, + "output": 3.5 }, { "provider": "OpenRouter", - "model": "openai/gpt-5-mini", + "model": "qwen/qwen3.5-9b", "variant": "", - "input": 0.25, - "output": 2, - "cached_input": 0.025 + "input": 0.1, + "output": 0.15 }, { "provider": "OpenRouter", - "model": "openai/gpt-5-nano", + "model": "qwen/qwen3.5-flash-02-23", "variant": "", - "input": 0.05, - "output": 0.4, - "cached_input": 0.005 + "input": 0.065, + "output": 0.26 }, { "provider": "OpenRouter", - "model": "openai/gpt-5.1-codex-max", + "model": "qwen/qwen3.5-plus-02-15", "variant": "", - "input": 1.25, - "output": 10, - "cached_input": 0.125 + "input": 0.26, + "output": 1.56 }, { "provider": "OpenRouter", - "model": "openai/gpt-5.2", - "variant": "", - "input": 1.75, - "output": 14, - "cached_input": 0.175 + "model": "qwen/qwen3.5-plus-02-15", + "variant": "above_256k_tokens", + "input": 0.5, + "output": 3 }, { "provider": "OpenRouter", - "model": "openai/gpt-5.2-chat", + "model": "qwen/qwen3.5-plus-20260420", "variant": "", - "input": 1.75, - "output": 14, - "cached_input": 0.175 + "input": 0.3, + "output": 1.8 }, { "provider": "OpenRouter", - "model": "openai/gpt-5.2-codex", - "variant": "", - "input": 1.75, - "output": 14, - "cached_input": 0.175 + "model": "qwen/qwen3.5-plus-20260420", + "variant": "above_256k_tokens", + "input": 0.375, + "output": 2.25 }, { "provider": "OpenRouter", - "model": "openai/gpt-5.2-pro", + "model": "qwen/qwen3.6-27b", "variant": "", - "input": 21, - "output": 168 + "input": 0.3, + "output": 2, + "cached_input": 0.03 }, { "provider": "OpenRouter", - "model": "openai/gpt-oss-120b", + "model": "qwen/qwen3.6-35b-a3b", "variant": "", - "input": 0.18, - "output": 0.8 + "input": 0.1, + "output": 0.9, + "cached_input": 0.05 }, { "provider": "OpenRouter", - "model": "openai/gpt-oss-20b", + "model": "qwen/qwen3.6-flash", "variant": "", - "input": 0.02, - "output": 0.1 + "input": 0.1875, + "output": 1.125 }, { "provider": "OpenRouter", - "model": "openai/o1", - "variant": "", - "input": 15, - "output": 60, - "cached_input": 7.5 + "model": "qwen/qwen3.6-flash", + "variant": "above_256k_tokens", + "input": 0.75, + "output": 3 }, { "provider": "OpenRouter", - "model": "openai/o3-mini", + "model": "qwen/qwen3.6-max-preview", "variant": "", - "input": 1.1, - "output": 4.4 + "input": 1.027, + "output": 6.162 }, { "provider": "OpenRouter", - "model": "openai/o3-mini-high", - "variant": "", - "input": 1.1, - "output": 4.4 + "model": "qwen/qwen3.6-max-preview", + "variant": "above_128k_tokens", + "input": 1.58, + "output": 9.48 }, { "provider": "OpenRouter", - "model": "qwen/qwen-2.5-coder-32b-instruct", + "model": "qwen/qwen3.6-plus", "variant": "", - "input": 0.18, - "output": 0.18 + "input": 0.325, + "output": 1.95 }, { "provider": "OpenRouter", - "model": "qwen/qwen-vl-plus", + "model": "qwen/qwen3.7-flash", "variant": "", - "input": 0.21, - "output": 0.63 + "input": 0.03, + "output": 0.13, + "cached_input": 0.006 }, { "provider": "OpenRouter", - "model": "qwen/qwen3-235b-a22b-2507", + "model": "qwen/qwen3.7-flash", + "variant": "above_256k_tokens", + "input": 0.2, + "output": 0.8, + "cached_input": 0.04 + }, + { + "provider": "OpenRouter", + "model": "qwen/qwen3.7-max", "variant": "", - "input": 0.071, - "output": 0.1 + "input": 1.475, + "output": 4.425, + "cached_input": 0.295 }, { "provider": "OpenRouter", - "model": "qwen/qwen3-235b-a22b-thinking-2507", + "model": "qwen/qwen3.7-plus", "variant": "", - "input": 0.11, - "output": 0.6 + "input": 0.32, + "output": 1.28, + "cached_input": 0.064 }, { "provider": "OpenRouter", - "model": "qwen/qwen3-coder", + "model": "qwen/qwen3.8-2.4t-a95b", "variant": "", - "input": 0.22, - "output": 0.95 + "input": 2, + "output": 6, + "cached_input": 0.25 }, { "provider": "OpenRouter", - "model": "qwen/qwen3-coder-plus", + "model": "qwen/qwen3.8-27b", "variant": "", - "input": 1, - "output": 5 + "input": 0.42, + "output": 3, + "cached_input": 0.085 }, { "provider": "OpenRouter", - "model": "qwen/qwen3.5-122b-a10b", + "model": "qwen/qwen3.8-flash", "variant": "", - "input": 0.4, - "output": 2 + "input": 0.15, + "output": 0.47, + "cached_input": 0.016 }, { "provider": "OpenRouter", - "model": "qwen/qwen3.5-27b", + "model": "qwen/qwen3.8-max", "variant": "", - "input": 0.3, - "output": 2.4 + "input": 2, + "output": 6, + "cached_input": 0.25 }, { "provider": "OpenRouter", - "model": "qwen/qwen3.5-35b-a3b", + "model": "switchpoint/router", "variant": "", - "input": 0.25, - "output": 2 + "input": 0.85, + "output": 3.4 }, { "provider": "OpenRouter", - "model": "qwen/qwen3.5-397b-a17b", + "model": "undi95/remm-slerp-l2-13b", "variant": "", - "input": 0.6, - "output": 3.6 + "input": 0.45, + "output": 0.65 }, { "provider": "OpenRouter", - "model": "qwen/qwen3.5-flash-02-23", + "model": "x-ai/grok-4", "variant": "", - "input": 0.1, - "output": 0.4 + "input": 3, + "output": 15 }, { "provider": "OpenRouter", - "model": "qwen/qwen3.5-plus-02-15", + "model": "x-ai/grok-4.20", "variant": "", - "input": 0.4, - "output": 2.4 + "input": 1.25, + "output": 2.5, + "cached_input": 0.2 }, { "provider": "OpenRouter", - "model": "qwen/qwen3.5-plus-02-15", - "variant": "above_256k_tokens", - "input": 0.5, - "output": 3 + "model": "x-ai/grok-4.20-multi-agent", + "variant": "", + "input": 1.25, + "output": 2.5, + "cached_input": 0.2 }, { "provider": "OpenRouter", - "model": "qwen/qwen3.6-plus", + "model": "x-ai/grok-4.3", "variant": "", - "input": 0.325, - "output": 1.95 + "input": 1.25, + "output": 2.5, + "cached_input": 0.2 }, { "provider": "OpenRouter", - "model": "switchpoint/router", + "model": "x-ai/grok-4.5", "variant": "", - "input": 0.85, - "output": 3.4 + "input": 2, + "output": 6, + "cached_input": 0.3 }, { "provider": "OpenRouter", - "model": "undi95/remm-slerp-l2-13b", + "model": "x-ai/grok-4.6", "variant": "", - "input": 1.875, - "output": 1.875 + "input": 2, + "output": 6, + "cached_input": 0.5 }, { "provider": "OpenRouter", - "model": "x-ai/grok-4", + "model": "x-ai/grok-build-0.1", "variant": "", - "input": 3, - "output": 15 + "input": 1, + "output": 2, + "cached_input": 0.2 }, { "provider": "OpenRouter", @@ -10419,24 +12090,48 @@ "provider": "OpenRouter", "model": "xiaomi/mimo-v2.5", "variant": "", - "input": 0.4, - "output": 2, - "cached_input": 0.08 + "input": 0.14, + "output": 0.28, + "cached_input": 0.0028 }, { "provider": "OpenRouter", "model": "xiaomi/mimo-v2.5-pro", "variant": "", - "input": 1, - "output": 3, - "cached_input": 0.2 + "input": 0.435, + "output": 0.87, + "cached_input": 0.0036 + }, + { + "provider": "OpenRouter", + "model": "z-ai/glm-4.5", + "variant": "", + "input": 0.6, + "output": 2.2, + "cached_input": 0.11 + }, + { + "provider": "OpenRouter", + "model": "z-ai/glm-4.5-air", + "variant": "", + "input": 0.13, + "output": 0.85, + "cached_input": 0.025 + }, + { + "provider": "OpenRouter", + "model": "z-ai/glm-4.5v", + "variant": "", + "input": 0.6, + "output": 1.8, + "cached_input": 0.11 }, { "provider": "OpenRouter", "model": "z-ai/glm-4.6", "variant": "", - "input": 0.4, - "output": 1.75 + "input": 0.55, + "output": 2.2 }, { "provider": "OpenRouter", @@ -10445,36 +12140,84 @@ "input": 0.45, "output": 1.9 }, + { + "provider": "OpenRouter", + "model": "z-ai/glm-4.6v", + "variant": "", + "input": 0.3, + "output": 0.9, + "cached_input": 0.055 + }, { "provider": "OpenRouter", "model": "z-ai/glm-4.7", "variant": "", "input": 0.4, - "output": 1.5, - "cached_input": 0 + "output": 1.75, + "cached_input": 0.08 }, { "provider": "OpenRouter", "model": "z-ai/glm-4.7-flash", "variant": "", - "input": 0.07, + "input": 0.06, "output": 0.4, - "cached_input": 0 + "cached_input": 0.01 }, { "provider": "OpenRouter", "model": "z-ai/glm-5", "variant": "", - "input": 0.8, - "output": 2.56 + "input": 0.6, + "output": 1.92 + }, + { + "provider": "OpenRouter", + "model": "z-ai/glm-5-turbo", + "variant": "", + "input": 1.2, + "output": 4, + "cached_input": 0.24 }, { "provider": "OpenRouter", "model": "z-ai/glm-5.1", "variant": "", - "input": 1.05, - "output": 3.5, - "cached_input": 0.525 + "input": 0.966, + "output": 3.036, + "cached_input": 0.1794 + }, + { + "provider": "OpenRouter", + "model": "z-ai/glm-5.2", + "variant": "", + "input": 0.966, + "output": 3.036, + "cached_input": 0.1932 + }, + { + "provider": "OpenRouter", + "model": "z-ai/glm-5.3", + "variant": "", + "input": 1.4, + "output": 4.4, + "cached_input": 0.14 + }, + { + "provider": "OpenRouter", + "model": "z-ai/glm-5.3-flash", + "variant": "", + "input": 0.075, + "output": 0.25, + "cached_input": 0.015 + }, + { + "provider": "OpenRouter", + "model": "z-ai/glm-5v-turbo", + "variant": "", + "input": 1.2, + "output": 4, + "cached_input": 0.24 }, { "provider": "Posit", diff --git a/chatlas/types/anthropic/_submit.py b/chatlas/types/anthropic/_submit.py index af13d8b7..b4361c5d 100644 --- a/chatlas/types/anthropic/_submit.py +++ b/chatlas/types/anthropic/_submit.py @@ -126,6 +126,7 @@ class SubmitInputArgs(TypedDict, total=False): anthropic.Omit, ] user_profile_id: str | anthropic.Omit + workspace_id: str | anthropic.Omit extra_headers: Optional[Mapping[str, Union[str, anthropic.Omit]]] extra_query: Optional[Mapping[str, object]] extra_body: object | None From 2d715c879f191beb381153631dc5af1edd455025 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 8 Sep 2026 11:25:25 -0500 Subject: [PATCH 4/7] chore: silence pyright reportPrivateImportUsage for pillow_heif CI's pyright resolves pillow_heif's stubs and flags register_heif_opener as a private re-export; this failure is present on main as well. --- chatlas/_content_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatlas/_content_image.py b/chatlas/_content_image.py index 70a68a43..351b3797 100644 --- a/chatlas/_content_image.py +++ b/chatlas/_content_image.py @@ -156,7 +156,7 @@ def content_image_file( "Install it with `pip install pillow-heif`, or pass " "`resize='none'` to send the original bytes without resizing." ) - pillow_heif.register_heif_opener() + pillow_heif.register_heif_opener() # pyright: ignore[reportPrivateImportUsage] if resize == "none": with open(path, "rb") as image_file: From ba9fc1a29c9bf55d96422dac42cd78583dc2538d Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 8 Sep 2026 11:51:04 -0500 Subject: [PATCH 5/7] fix(posit): close old provider and preserve name on family switch Addresses Copilot review feedback on #430: - Chat.model setter now closes the previous provider's HTTP clients when set_model() swaps in a new provider instance (async resources are closed via close_async() when an event loop is running). - Posit set_model() now carries over a custom provider name across Anthropic/OpenAI flavor switches. --- chatlas/_chat.py | 16 +++++++++++++- chatlas/_provider_posit.py | 2 ++ tests/test_provider_posit.py | 41 ++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/chatlas/_chat.py b/chatlas/_chat.py index 9e3b21c8..a483b17b 100644 --- a/chatlas/_chat.py +++ b/chatlas/_chat.py @@ -1,5 +1,6 @@ from __future__ import annotations +import asyncio import copy import inspect import os @@ -549,7 +550,20 @@ def model(self) -> str: @model.setter def model(self, value: str): - self.provider = self.provider.set_model(value) + old_provider = self.provider + new_provider = old_provider.set_model(value) + if new_provider is not old_provider: + # A provider swap (e.g., ChatPosit switching API flavors) + # abandons the old provider, so release its HTTP clients. + # Best-effort for async resources: schedule close_async() + # when an event loop is running. + try: + loop = asyncio.get_running_loop() + except RuntimeError: + old_provider.close() + else: + loop.create_task(old_provider.close_async()) + self.provider = new_provider @property def conversation_id(self) -> str | None: diff --git a/chatlas/_provider_posit.py b/chatlas/_provider_posit.py index 20cc5733..efaa4195 100644 --- a/chatlas/_provider_posit.py +++ b/chatlas/_provider_posit.py @@ -318,6 +318,7 @@ def set_model(self, value: str) -> "PositAnthropicProvider | PositOpenAIProvider model=value, credentials=self._credentials, cache=self._cache, + name=self.name, ) def list_models(self) -> list[ModelInfo]: @@ -371,6 +372,7 @@ def set_model(self, value: str) -> "PositAnthropicProvider | PositOpenAIProvider model=value, credentials=self._credentials, cache=self._cache, + name=self.name, ) self._model = value return self diff --git a/tests/test_provider_posit.py b/tests/test_provider_posit.py index 6d71fac5..73753c7c 100644 --- a/tests/test_provider_posit.py +++ b/tests/test_provider_posit.py @@ -612,3 +612,44 @@ def test_chat_posit_set_model_openai_start_honors_cache_on_switch(): assert isinstance(chat.provider, PositAnthropicProvider) assert chat.provider._cache == "none" + + +def test_chat_posit_set_model_preserves_custom_name_across_family_switches(): + provider = PositAnthropicProvider( + base_url="https://gateway.posit.ai", + model="claude-sonnet-4-6", + credentials=lambda: "test-token", + name="Custom", + ) + + openai_provider = provider.set_model("qwen3-8b") + assert isinstance(openai_provider, PositOpenAIProvider) + assert openai_provider.name == "Custom" + + anthropic_provider = openai_provider.set_model("claude-sonnet-4-6") + assert isinstance(anthropic_provider, PositAnthropicProvider) + assert anthropic_provider.name == "Custom" + + +def test_chat_posit_set_model_closes_old_provider_on_family_switch(): + chat = ChatPosit(model="claude-sonnet-4-6", credentials=lambda: "test-token") + old_provider = chat.provider + + chat.model = "qwen3-8b" + + assert old_provider._client.is_closed() + + chat = ChatPosit(model="qwen3-8b", credentials=lambda: "test-token") + old_provider = chat.provider + + chat.model = "claude-sonnet-4-6" + + assert old_provider._client.is_closed() + + +def test_chat_posit_set_model_within_family_does_not_close_provider(): + chat = ChatPosit(model="claude-sonnet-4-6", credentials=lambda: "test-token") + + chat.model = "claude-opus-4-1" + + assert not chat.provider._client.is_closed() From a82b9b834b61cf69d982d377c109af147c12f72e Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 8 Sep 2026 11:58:23 -0500 Subject: [PATCH 6/7] refactor: simplify model setter to sync-only provider close Async clients can't be closed from sync code (their transport requires a running event loop), so deterministically closing them from the Chat.model setter would require fire-and-forget task scheduling with its own edge cases. The leak scenario (repeated provider swaps after async usage in a long-lived process) is narrow enough that relying on GC for async resources is an acceptable, documented tradeoff. --- chatlas/_chat.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/chatlas/_chat.py b/chatlas/_chat.py index a483b17b..532dbcab 100644 --- a/chatlas/_chat.py +++ b/chatlas/_chat.py @@ -1,6 +1,5 @@ from __future__ import annotations -import asyncio import copy import inspect import os @@ -555,14 +554,10 @@ def model(self, value: str): if new_provider is not old_provider: # A provider swap (e.g., ChatPosit switching API flavors) # abandons the old provider, so release its HTTP clients. - # Best-effort for async resources: schedule close_async() - # when an event loop is running. - try: - loop = asyncio.get_running_loop() - except RuntimeError: - old_provider.close() - else: - loop.create_task(old_provider.close_async()) + # Only sync resources are closed deterministically: async + # clients can't be closed from sync code (their transport + # requires a running event loop), so they're left to GC. + old_provider.close() self.provider = new_provider @property From 8179f86314e972184dbc169a6b76d1f93dd3a2c0 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 8 Sep 2026 12:02:00 -0500 Subject: [PATCH 7/7] chore: drop explanatory comment from model setter --- chatlas/_chat.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/chatlas/_chat.py b/chatlas/_chat.py index 532dbcab..36e592ab 100644 --- a/chatlas/_chat.py +++ b/chatlas/_chat.py @@ -552,11 +552,6 @@ def model(self, value: str): old_provider = self.provider new_provider = old_provider.set_model(value) if new_provider is not old_provider: - # A provider swap (e.g., ChatPosit switching API flavors) - # abandons the old provider, so release its HTTP clients. - # Only sync resources are closed deterministically: async - # clients can't be closed from sync code (their transport - # requires a running event loop), so they're left to GC. old_provider.close() self.provider = new_provider