mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
fix(bedrock): size GPT-6.1 Terra, GPT-5.4 and third-party models from their AWS cards
An operator running GPT-6.1 Terra on Bedrock saw a 128K window and compression at 96K
(the 75% small-window floor). The AWS card states 1,050,000 tokens
(model-card-openai-gpt-6-1-terra). The model launched on Bedrock on 2026-10-26.
a382af23f4 added gpt-6.1-terra to model_metadata.DEFAULT_CONTEXT_LENGTHS the same day,
but the Bedrock table, routing allowlist and picker were never updated.
Every AWS model-card page for a text model (100 cards, read 2026-11-04) was compared
with get_bedrock_context_length(id, probe=False). On main, 45 cards miss. This commit
adds rows only where the card states the value:
- GPT-6.1 Terra and GPT-5.4 (both cards: 1,050,000) go into the table, the Mantle /
Converse allowlist BEDROCK_OPENAI_RESPONSES_MODEL_IDS and the static picker. Both
cards list the same endpoint matrix as their siblings: bare ID on bedrock-mantle,
us./global. profiles on bedrock-runtime with Converse. GPT-6.1 Terra keeps the gpt-6
"issuer" reasoning-replay policy through the existing "openai.gpt-6" prefix.
- Third-party rows from each card's "Context window" field: Nova 2 Lite 1M, Jamba 1.5
256K, Gemma 4 26B/31B 256K, MiniMax M2.1/M2.5 196K, Devstral 2 and Mistral Large 3
256K, Kimi K2.5/K2 Thinking 256K, Kimi K3 1M, Qwen3 Coder 256K, GLM 4.7 203K, GLM 5
200K, GLM 5.3 1M.
Every entry stays at or below the model_metadata value for the same bare ID. That
invariant was stated in a comment; a test now enforces it for every row. Cards above
their model_metadata row (MiniMax M2, DeepSeek V3.2, Nemotron 3, Qwen3 235B/Next/VL)
are left for a catalog fix first. Llama 4 is left to #106492, whose measured windows
differ from the cards.
This commit is contained in:
1 parent
27cbcb122f
commit
4b3fba6942
4 files changed
+144
-7
No files matched your search
@@ -86,8 +86,8 @@ _BEDROCK_OPENAI_PROFILE_PREFIXES: tuple[str, ...] = _BEDROCK_PROFILE_PREFIXES +
|
||||
# OpenAI-compatible endpoint; its geo/global profile form is served by bedrock-runtime, where these
|
||||
# cards list Converse as supported. Narrow allowlist so GPT-OSS models stay on the native path.
|
||||
BEDROCK_OPENAI_RESPONSES_MODEL_IDS: tuple[str, ...] = (
|
||||
"openai.gpt-6-astra", "openai.gpt-6.1-sol", "openai.gpt-6-sol", "openai.gpt-6-luna",
|
||||
"openai.gpt-5.5", "openai.gpt-5.6-sol", "openai.gpt-5.6-terra", "openai.gpt-5.6-luna",
|
||||
"openai.gpt-6-astra", "openai.gpt-6.1-sol", "openai.gpt-6.1-terra", "openai.gpt-6-sol", "openai.gpt-6-luna",
|
||||
"openai.gpt-5.5", "openai.gpt-5.6-sol", "openai.gpt-5.6-terra", "openai.gpt-5.6-luna", "openai.gpt-5.4",
|
||||
)
|
||||
_BEDROCK_OPENAI_MODEL_IDS_LOWER = frozenset(m.lower() for m in BEDROCK_OPENAI_RESPONSES_MODEL_IDS)
|
||||
_BEDROCK_OPENAI_HOST_RE = re.compile(r"^bedrock-mantle\.([a-z0-9-]+)\.api\.aws$", re.IGNORECASE)
|
||||
@@ -1504,13 +1504,25 @@ BEDROCK_CONTEXT_LENGTHS: dict[str, int] = {
|
||||
**dict.fromkeys((
|
||||
"meta.llama4-maverick", "meta.llama4-scout", "meta.llama3-3-70b-instruct", "mistral.mistral-large", "deepseek.v3",
|
||||
), 128_000),
|
||||
# Open-weight and third-party models: the "Context window" field of each AWS card
|
||||
# (docs.aws.amazon.com/bedrock/latest/userguide/model-card-<vendor>-<model>.html, read 2026-11-04). Their
|
||||
# model_metadata rows are vendor-direct windows (often larger), so they get their own rows. A card above the
|
||||
# model_metadata row for the same id (MiniMax M2, DeepSeek V3.2, Nemotron 3, Qwen3 235B/Next/VL) waits for that
|
||||
# row first, as every entry here stays <= it; Llama 4 is #106492 (measured windows differ from the cards).
|
||||
"amazon.nova-2-lite": 1_000_000, "ai21.jamba-1-5": 256_000,
|
||||
**dict.fromkeys(("google.gemma-4-26b", "google.gemma-4-31b"), 256_000),
|
||||
**dict.fromkeys(("minimax.minimax-m2.1", "minimax.minimax-m2.5"), 196_000),
|
||||
**dict.fromkeys(("mistral.devstral-2", "mistral.mistral-large-3"), 256_000),
|
||||
**dict.fromkeys(("moonshotai.kimi-k2.5", "moonshot.kimi-k2-thinking", "moonshotai.kimi-k2-thinking"), 256_000),
|
||||
"moonshotai.kimi-k3": 1_000_000, "qwen.qwen3-coder": 256_000,
|
||||
"zai.glm-4.7": 203_000, "zai.glm-5": 200_000, "zai.glm-5.3": 1_000_000,
|
||||
# OpenAI on Bedrock: docs.aws.amazon.com/bedrock/latest/userguide/model-cards-openai.html. GPT-6.1 Sol's
|
||||
# card states "1M tokens"; the other cards state 1,050,000. 272K is the long-context PRICING tier
|
||||
# boundary on these cards, not the window. Each entry stays <= model_metadata.DEFAULT_CONTEXT_LENGTHS.
|
||||
"openai.gpt-6.1-sol": 1_000_000,
|
||||
**dict.fromkeys((
|
||||
"openai.gpt-6-astra", "openai.gpt-6-sol", "openai.gpt-6-luna",
|
||||
"openai.gpt-5.5", "openai.gpt-5.6-sol", "openai.gpt-5.6-terra", "openai.gpt-5.6-luna",
|
||||
"openai.gpt-6-astra", "openai.gpt-6.1-terra", "openai.gpt-6-sol", "openai.gpt-6-luna",
|
||||
"openai.gpt-5.5", "openai.gpt-5.6-sol", "openai.gpt-5.6-terra", "openai.gpt-5.6-luna", "openai.gpt-5.4",
|
||||
), 1_050_000),
|
||||
}
|
||||
|
||||
|
||||
@@ -333,8 +333,8 @@ _PROVIDER_MODELS: dict[str, list[str]] = _CuratedCatalog({
|
||||
"us.anthropic.claude-sonnet-5", "us.anthropic.claude-opus-5-5", "us.anthropic.claude-sonnet-4-6",
|
||||
"us.anthropic.claude-opus-4-6-v1",
|
||||
"us.anthropic.claude-haiku-4-5-20251001-v1:0", "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
|
||||
"openai.gpt-6-astra", "openai.gpt-6.1-sol", "openai.gpt-6-sol", "openai.gpt-6-luna",
|
||||
"openai.gpt-5.5", "openai.gpt-5.6-sol", "openai.gpt-5.6-terra", "openai.gpt-5.6-luna",
|
||||
"openai.gpt-6-astra", "openai.gpt-6.1-sol", "openai.gpt-6.1-terra", "openai.gpt-6-sol", "openai.gpt-6-luna",
|
||||
"openai.gpt-5.5", "openai.gpt-5.6-sol", "openai.gpt-5.6-terra", "openai.gpt-5.6-luna", "openai.gpt-5.4",
|
||||
"us.amazon.nova-pro-v1:0", "us.amazon.nova-lite-v1:0", "us.amazon.nova-micro-v1:0", "deepseek.v3.2",
|
||||
"us.meta.llama4-maverick-17b-instruct-v1:0", "us.meta.llama4-scout-17b-instruct-v1:0",
|
||||
],
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
"""Bedrock context windows for models the static table lagged on, and the family fall-through.
|
||||
|
||||
Field report: GPT-6.1 Terra on Bedrock resolved to the 128K default and compressed at 96K (75% small-window
|
||||
floor) although its AWS card states 1,050,000 tokens. Card:
|
||||
https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-6-1-terra.html
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
TERRA_CARD_WINDOW = 1_050_000 # "Context window: 1,050,000 tokens" (model-card-openai-gpt-6-1-terra)
|
||||
GPT54_CARD_WINDOW = 1_050_000 # "Context window: 1,050,000 tokens, shared between input and output"
|
||||
RUNTIME_URL = "https://bedrock-runtime.us-east-1.amazonaws.com"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _fresh_bedrock_state():
|
||||
from agent import bedrock_adapter
|
||||
bedrock_adapter.reset_client_cache()
|
||||
yield
|
||||
bedrock_adapter.reset_client_cache()
|
||||
|
||||
|
||||
TERRA_FORMS = [
|
||||
"openai.gpt-6.1-terra",
|
||||
"us.openai.gpt-6.1-terra",
|
||||
"global.openai.gpt-6.1-terra",
|
||||
"arn:aws:bedrock:us-east-1:123456789012:inference-profile/us.openai.gpt-6.1-terra",
|
||||
"arn:aws:bedrock:us-east-1::foundation-model/openai.gpt-6.1-terra",
|
||||
]
|
||||
|
||||
|
||||
class TestCardSizedOpenAIModels:
|
||||
@pytest.mark.parametrize("model_id", TERRA_FORMS)
|
||||
def test_gpt61_terra_resolves_to_its_card_window_in_every_id_form(self, model_id):
|
||||
from agent.bedrock_adapter import get_bedrock_context_length
|
||||
assert get_bedrock_context_length(model_id, probe=False) == TERRA_CARD_WINDOW
|
||||
|
||||
def test_application_profile_wrapping_terra_resolves_to_the_card_window(self):
|
||||
from agent.bedrock_adapter import get_bedrock_context_length
|
||||
arn = "arn:aws:bedrock:us-west-2:123456789012:application-inference-profile/terraprof01"
|
||||
client = MagicMock()
|
||||
client.get_inference_profile.return_value = {"models": [
|
||||
{"modelArn": "arn:aws:bedrock:us-west-2::foundation-model/openai.gpt-6.1-terra"}]}
|
||||
with patch("agent.bedrock_adapter._get_bedrock_control_client", return_value=client):
|
||||
assert get_bedrock_context_length(arn, probe=False) == TERRA_CARD_WINDOW
|
||||
|
||||
@pytest.mark.parametrize("model_id", ["openai.gpt-5.4", "us.openai.gpt-5.4", "global.openai.gpt-5.4"])
|
||||
def test_gpt54_resolves_to_its_card_window(self, model_id):
|
||||
from agent.bedrock_adapter import get_bedrock_context_length
|
||||
assert get_bedrock_context_length(model_id, probe=False) == GPT54_CARD_WINDOW
|
||||
|
||||
@pytest.mark.parametrize("bare", ["openai.gpt-6.1-terra", "openai.gpt-5.4"])
|
||||
def test_terra_and_gpt54_route_like_their_siblings(self, bare):
|
||||
"""Cards: bare ID on bedrock-mantle only, us./global. profiles on bedrock-runtime (Converse)."""
|
||||
from agent.bedrock_adapter import bedrock_openai_uses_mantle, is_openai_bedrock_model
|
||||
assert is_openai_bedrock_model(bare) and bedrock_openai_uses_mantle(bare)
|
||||
for profile in (f"us.{bare}", f"global.{bare}"):
|
||||
assert is_openai_bedrock_model(profile) and not bedrock_openai_uses_mantle(profile)
|
||||
|
||||
def test_terra_keeps_the_gpt6_reasoning_replay_policy(self):
|
||||
from agent.bedrock_adapter import _converse_replay_policy
|
||||
assert _converse_replay_policy("us.openai.gpt-6.1-terra") == "issuer"
|
||||
|
||||
def test_every_routed_openai_model_has_its_own_card_row(self):
|
||||
"""Routed (allowlisted) models are sized from their AWS card, not borrowed from the catalog."""
|
||||
from agent.bedrock_adapter import BEDROCK_CONTEXT_LENGTHS, BEDROCK_OPENAI_RESPONSES_MODEL_IDS
|
||||
assert not [m for m in BEDROCK_OPENAI_RESPONSES_MODEL_IDS if m not in BEDROCK_CONTEXT_LENGTHS]
|
||||
|
||||
def test_terra_compression_trigger_uses_the_card_window(self):
|
||||
"""Through the real resolver and compressor with no live probe: main compressed at 96,000."""
|
||||
from agent.context_compressor import ContextCompressor
|
||||
compressor = ContextCompressor(
|
||||
model="us.openai.gpt-6.1-terra", provider="bedrock", threshold_percent=0.5,
|
||||
base_url=RUNTIME_URL, api_key="aws-sdk", quiet_mode=True,
|
||||
)
|
||||
with patch("agent.bedrock_adapter.probe_bedrock_context_length", return_value=None):
|
||||
assert compressor.context_length == TERRA_CARD_WINDOW
|
||||
assert compressor.threshold_tokens == TERRA_CARD_WINDOW // 2
|
||||
|
||||
|
||||
class TestThirdPartyCardRows:
|
||||
"""Rows read from each model's AWS card ("Context window" field, 2026-11-04). On main all of these resolved to
|
||||
the 128K default, and the ones above 128K compressed early."""
|
||||
|
||||
@pytest.mark.parametrize("model_id, card_window", [
|
||||
("us.amazon.nova-2-lite-v1:0", 1_000_000),
|
||||
("ai21.jamba-1-5-large-v1:0", 256_000),
|
||||
("google.gemma-4-31b", 256_000),
|
||||
("minimax.minimax-m2.5", 196_000),
|
||||
("mistral.devstral-2-123b", 256_000),
|
||||
("mistral.mistral-large-3-675b-instruct", 256_000),
|
||||
("moonshotai.kimi-k2.5", 256_000),
|
||||
("global.moonshotai.kimi-k3", 1_000_000),
|
||||
("qwen.qwen3-coder-480b-a35b-v1:0", 256_000),
|
||||
("zai.glm-4.7", 203_000),
|
||||
("zai.glm-5", 200_000),
|
||||
("global.zai.glm-5.3", 1_000_000),
|
||||
])
|
||||
def test_card_window(self, model_id, card_window):
|
||||
from agent.bedrock_adapter import get_bedrock_context_length
|
||||
assert get_bedrock_context_length(model_id, probe=False) == card_window
|
||||
|
||||
def test_older_rows_are_not_shadowed(self):
|
||||
"""Longest-key match: the new mistral-large-3 row must not capture Mistral Large (2402, card 32K, row 128K)."""
|
||||
from agent.bedrock_adapter import get_bedrock_context_length
|
||||
assert get_bedrock_context_length("mistral.mistral-large-2402-v1:0", probe=False) == 128_000
|
||||
assert get_bedrock_context_length("zai.glm-4.7-flash", probe=False) == 203_000
|
||||
|
||||
def test_every_row_stays_within_the_model_metadata_window(self):
|
||||
"""The table's stated invariant, now enforced for every row: a hosted window never exceeds the
|
||||
model_metadata.DEFAULT_CONTEXT_LENGTHS value for the same bare id when that table knows the model."""
|
||||
from agent.bedrock_adapter import BEDROCK_CONTEXT_LENGTHS
|
||||
from agent.model_metadata import DEFAULT_CONTEXT_LENGTHS, _longest_key_match
|
||||
above = []
|
||||
for key, hosted in BEDROCK_CONTEXT_LENGTHS.items():
|
||||
hit = _longest_key_match(DEFAULT_CONTEXT_LENGTHS, key.split(".", 1)[1])
|
||||
if hit and hosted > hit[1]:
|
||||
above.append((key, hosted, hit))
|
||||
assert not above, above
|
||||
@@ -17,7 +17,7 @@ Hermes routes each model family through the API that serves it best:
|
||||
| Model family | API route | Why |
|
||||
|---|---|---|
|
||||
| Anthropic Claude | Anthropic SDK (`AnthropicBedrock`) | Prompt caching, thinking budgets, adaptive thinking — features not exposed via Converse |
|
||||
| OpenAI GPT-6 (Astra, Sol, Luna), GPT-6.1 Sol, GPT-5.6 (Sol, Terra, Luna), GPT-5.5 with a bare `openai.*` ID | Bedrock Mantle **OpenAI Responses** endpoint (`bedrock-mantle.<region>.api.aws/openai/v1`) | Bedrock Mantle serves the bare in-Region ID. `bedrock-runtime` does not accept it |
|
||||
| OpenAI GPT-6 (Astra, Sol, Luna), GPT-6.1 (Sol, Terra), GPT-5.6 (Sol, Terra, Luna), GPT-5.5, GPT-5.4 with a bare `openai.*` ID | Bedrock Mantle **OpenAI Responses** endpoint (`bedrock-mantle.<region>.api.aws/openai/v1`) | Bedrock Mantle serves the bare in-Region ID. `bedrock-runtime` does not accept it |
|
||||
| The same OpenAI models with a `us.` or `global.` inference-profile ID | Native **Converse API** (`bedrock-runtime`) | Cross-Region profiles exist only on `bedrock-runtime`, and the model cards list Converse as supported there |
|
||||
| Everything else (Nova, DeepSeek, Llama, GPT-OSS, …) | Native **Converse API** (`bedrock-runtime`) | Full Bedrock feature set: Guardrails, inference profiles, streaming |
|
||||
|
||||
@@ -131,12 +131,14 @@ Bedrock models use **inference profile IDs** for on-demand invocation. The `herm
|
||||
| Claude Haiku 4.5 | `us.anthropic.claude-haiku-4-5-20251001-v1:0` | Fastest Claude |
|
||||
| OpenAI GPT-6 Astra | `openai.gpt-6-astra` | Most capable OpenAI model, 1,050,000-token window |
|
||||
| OpenAI GPT-6.1 Sol | `openai.gpt-6.1-sol` | 1M-token window. Mantle in-Region access is `us-east-1` only |
|
||||
| OpenAI GPT-6.1 Terra | `openai.gpt-6.1-terra` | Balanced Terra tier, 1,050,000-token window |
|
||||
| OpenAI GPT-6 Sol | `openai.gpt-6-sol` | Coding and agentic work, 1,050,000-token window |
|
||||
| OpenAI GPT-6 Luna | `openai.gpt-6-luna` | Focused, high-volume tasks, 1,050,000-token window |
|
||||
| OpenAI GPT-5.6 Sol | `openai.gpt-5.6-sol` | 1,050,000-token window |
|
||||
| OpenAI GPT-5.6 Terra | `openai.gpt-5.6-terra` | Balanced, 1,050,000-token window |
|
||||
| OpenAI GPT-5.6 Luna | `openai.gpt-5.6-luna` | Fast, affordable, 1,050,000-token window |
|
||||
| OpenAI GPT-5.5 | `openai.gpt-5.5` | Mantle only, no inference profiles |
|
||||
| OpenAI GPT-5.4 | `openai.gpt-5.4` | 1,050,000-token window |
|
||||
| Amazon Nova Pro | `us.amazon.nova-pro-v1:0` | Amazon's flagship |
|
||||
| Amazon Nova Micro | `us.amazon.nova-micro-v1:0` | Fastest, cheapest |
|
||||
| DeepSeek V3.2 | `deepseek.v3.2` | Strong open model |
|
||||
|
||||
Reference in new issue
Block a user