From 79592af186aa29bcec03b487586d83705f8c95e7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 21 Sep 2026 14:47:25 +0000 Subject: [PATCH] Remove the delisted hermes-4-70b; charge OpenRouter's real rate for hy3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenRouter has delisted nousresearch/hermes-4-70b — it is absent from /api/v1/models, so every request returned "404 No endpoints found". Its pricing page still quotes $0.13/$0.40, so a price check does not establish that a model is served. Removed the enum entry and both lookup aliases; the gateway now rejects the name instead of forwarding a request that cannot succeed. hermes-4-405b is unaffected. hy3 was underpriced: OpenRouter charges $0.132/$0.528 per MTok for tencent/hy3, against 0.0825/0.33 here, so the gateway absorbed 1.6x. Two provider-probe cases in the integration test named models nobody should send any more — the OpenRouter probe used hermes-4-70b (now hy3), the xAI probe used grok-4-fast, retired on 2026-05-15 (now grok-4.3). 457 passed, 6 skipped; make lint clean. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01QmnaiLePKAQEkNfR8VPfja --- CLAUDE.md | 8 +++++- README.md | 2 +- tee_gateway/model_registry.py | 21 ++++++-------- .../test/test_provider_usage_integration.py | 4 +-- tests/test_pricing.py | 28 ++++++++++--------- 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 45f4e71..6001c33 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -131,7 +131,7 @@ prefixes determine routing: - **Google**: gemini-3.8-flash, gemini-3.7-flash, gemini-3.6-flash, gemini-3.5-flash, gemini-3.5-flash-lite, gemini-2.5-flash, gemini-2.5-flash-lite, gemini-2.5-pro, gemini-3-flash-preview, gemini-3.1-pro-preview; image generation: gemini-2.5-flash-image, gemini-3.1-flash-image - **xAI**: grok-4.6, grok-4.5, grok-4.3, grok-4.20-reasoning, grok-4.20-non-reasoning; image generation: grok-2-image, grok-imagine-image-2.0 - **ByteDance** (BytePlus ModelArk, OpenAI-compatible, ap-southeast): seed-1.6, seed-1.8, seed-2.0-lite, deepseek-v4-flash, deepseek-v4-pro, glm-5.2 (Z.ai's model served via a ModelArk deployment endpoint); image generation: seedream-4.0, seedream-5.0-lite, seedance-4.5, seedance-5.0 -- **OpenRouter** (OpenAI-compatible): hy4-preview, hermes-4-405b, hermes-4-70b, hy3 +- **OpenRouter** (OpenAI-compatible): hy4-preview, hermes-4-405b, hy3 - **Z.ai** (Model API, OpenAI-compatible): image generation: glm-image (glm-5.2 chat is routed through BytePlus ModelArk, see ByteDance above) Models kept registered but no longer offered to new clients — each still @@ -149,6 +149,12 @@ provider actually bills, not at its pre-retirement rate: repointed or retired with its snapshot; if repointed, they become mispriced the way the xAI slugs were. +A model the provider does not serve at all is removed outright rather than +repriced — no request to it can succeed. `hermes-4-70b` went this way when +OpenRouter delisted it. For OpenRouter models, `openrouter.ai/api/v1/models` +is authoritative for availability as well as price; a pricing page keeps +quoting a rate after the endpoints are gone. + Image generation via OpenAI (gpt-image-2.5-flare, gpt-image-2.5-sunburst, gpt-image-2), xAI (grok-2-image, grok-imagine-image-2.0), ByteDance (seedream-4.0, seedream-5.0-lite, seedance-4.5, seedance-5.0), and Z.ai (glm-image) is served through a provider `/images/generations` endpoint rather than the chat path (see diff --git a/README.md b/README.md index 25fffd1..242d1aa 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ The gateway solves this by running inside a hardware-isolated Nitro Enclave wher | Google | gemini-3.8-flash, gemini-3.7-flash, gemini-3.6-flash, gemini-3.5-flash, gemini-3.5-flash-lite, gemini-2.5-flash, gemini-2.5-flash-lite, gemini-2.5-pro, gemini-3-flash-preview, gemini-3.1-pro-preview | | xAI | grok-4.6, grok-4.5, grok-4.3, grok-4.20-reasoning, grok-4.20-non-reasoning | | ByteDance | seed-1.6, seed-1.8, seed-2.0-lite, dola-seed-2.0-mini, deepseek-v4-flash, deepseek-v4-pro, glm-5.2 | -| OpenRouter | hy4-preview, hermes-4-405b, hermes-4-70b, hy3 | +| OpenRouter | hy4-preview, hermes-4-405b, hy3 | Image generation: gpt-image-2, gpt-image-2.5-flare, gpt-image-2.5-sunburst, gemini-2.5-flash-image, gemini-3.1-flash-image, grok-2-image, diff --git a/tee_gateway/model_registry.py b/tee_gateway/model_registry.py index cec3cfc..9438d16 100644 --- a/tee_gateway/model_registry.py +++ b/tee_gateway/model_registry.py @@ -939,25 +939,24 @@ class SupportedModel(Enum): input_price_usd=Decimal("0.000000834"), output_price_usd=Decimal("0.000002501"), ) - # Nous no longer serves Hermes 4 through Nous Portal, so both models route - # through their canonical OpenRouter slugs and use OpenRouter list pricing. + # Nous no longer serves Hermes 4 through Nous Portal, so it routes through + # its canonical OpenRouter slug and uses OpenRouter list pricing. Only the + # 405B is served — OpenRouter has delisted hermes-4-70b. A model's pricing + # page outlives its endpoints, so check /api/v1/models for availability + # rather than trusting a quoted rate. HERMES_4_405B = ModelConfig( provider="openrouter", api_name="nousresearch/hermes-4-405b", input_price_usd=Decimal("0.000001"), output_price_usd=Decimal("0.000003"), ) - HERMES_4_70B = ModelConfig( - provider="openrouter", - api_name="nousresearch/hermes-4-70b", - input_price_usd=Decimal("0.00000013"), - output_price_usd=Decimal("0.0000004"), - ) + # Rate from openrouter.ai/api/v1/models, which is authoritative for both + # price and availability. HY3 = ModelConfig( provider="openrouter", api_name="tencent/hy3", - input_price_usd=Decimal("0.0000000825"), - output_price_usd=Decimal("0.00000033"), + input_price_usd=Decimal("0.000000132"), + output_price_usd=Decimal("0.000000528"), ) # ── Z.ai (Model API, OpenAI-compatible) ───────────────────────────── @@ -1118,8 +1117,6 @@ class SupportedModel(Enum): # Nous Research models routed through OpenRouter "hermes-4-405b": SupportedModel.HERMES_4_405B, "nousresearch/hermes-4-405b": SupportedModel.HERMES_4_405B, - "hermes-4-70b": SupportedModel.HERMES_4_70B, - "nousresearch/hermes-4-70b": SupportedModel.HERMES_4_70B, "hy3": SupportedModel.HY3, "tencent/hy3": SupportedModel.HY3, "tencent/hy3:floor": SupportedModel.HY3, diff --git a/tee_gateway/test/test_provider_usage_integration.py b/tee_gateway/test/test_provider_usage_integration.py index d6318f6..e9f057b 100644 --- a/tee_gateway/test/test_provider_usage_integration.py +++ b/tee_gateway/test/test_provider_usage_integration.py @@ -52,9 +52,9 @@ class _ProviderCase: _ProviderCase("OpenAI", "gpt-4.1-nano", "OPENAI_API_KEY"), _ProviderCase("Anthropic", "claude-haiku-4-5", "ANTHROPIC_API_KEY"), _ProviderCase("Google", "gemini-3.5-flash-lite", "GOOGLE_API_KEY"), - _ProviderCase("xAI", "grok-4-fast", "XAI_API_KEY"), + _ProviderCase("xAI", "grok-4.3", "XAI_API_KEY"), _ProviderCase("ByteDance", "deepseek-v4-flash", "ARK_API_KEY"), - _ProviderCase("OpenRouter", "hermes-4-70b", "OPENROUTER_API_KEY"), + _ProviderCase("OpenRouter", "hy3", "OPENROUTER_API_KEY"), _ProviderCase("Z.ai", "glm-5.2", "ZAI_API_KEY"), ) diff --git a/tests/test_pricing.py b/tests/test_pricing.py index 05f3b82..e2b3bed 100644 --- a/tests/test_pricing.py +++ b/tests/test_pricing.py @@ -578,29 +578,30 @@ def test_hermes_4_405b_resolves(self): self.assertEqual(cfg.input_price_usd, Decimal("0.000001")) self.assertEqual(cfg.output_price_usd, Decimal("0.000003")) - def test_hermes_4_70b_resolves(self): - cfg = get_model_config("hermes-4-70b") - self.assertEqual(cfg.provider, "openrouter") - self.assertEqual(cfg.api_name, "nousresearch/hermes-4-70b") - self.assertEqual(cfg.input_price_usd, Decimal("0.00000013")) - self.assertEqual(cfg.output_price_usd, Decimal("0.0000004")) + def test_hermes_4_70b_is_gone(self): + """OpenRouter delisted nousresearch/hermes-4-70b. + + It is absent from openrouter.ai/api/v1/models, so every request + returned "404 No endpoints found". Rejecting it here beats forwarding + a request that cannot succeed. + """ + for name in ("hermes-4-70b", "nousresearch/hermes-4-70b"): + with self.subTest(model=name): + with self.assertRaises(ValueError): + get_model_config(name) def test_openrouter_canonical_hermes_aliases_resolve(self): self.assertEqual( get_model_config("nousresearch/hermes-4-405b"), get_model_config("hermes-4-405b"), ) - self.assertEqual( - get_model_config("nousresearch/hermes-4-70b"), - get_model_config("hermes-4-70b"), - ) def test_hy3_resolves(self): cfg = get_model_config("hy3") self.assertEqual(cfg.provider, "openrouter") self.assertEqual(cfg.api_name, "tencent/hy3") - self.assertEqual(cfg.input_price_usd, Decimal("0.0000000825")) - self.assertEqual(cfg.output_price_usd, Decimal("0.00000033")) + self.assertEqual(cfg.input_price_usd, Decimal("0.000000132")) + self.assertEqual(cfg.output_price_usd, Decimal("0.000000528")) self.assertEqual(cfg, get_model_config("tencent/hy3")) self.assertEqual(cfg, get_model_config("tencent/hy3:floor")) @@ -986,9 +987,10 @@ def test_deepseek_v4_pro_cost(self): # ── OpenRouter ───────────────────────────────────────────────────────── def test_hy3_cost(self): + # 1000*0.000000132 + 500*0.000000528 = 0.000132 + 0.000264 = 0.000396 USD self.assertEqual( self._calc("hy3", 1000, 500), - 247_500_000_000_000, + 396_000_000_000_000, ) def test_hy4_preview_cost(self):