mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-16 14:32:34 +00:00
Close the remaining end-to-end gaps so the full gpt-5.6 family (sol/ terra/luna + their -pro high-effort modes, 6 slugs) works on every surface a user can reach them through: - agent/auxiliary_client.py: the Codex OAuth backend hard-caps context at 272K for gpt-5.6 exactly as it does for 5.4/5.5, but the default 50% compaction trigger would summarize at ~136K and waste half the usable window. Extend the existing _is_codex_gpt54_or_gpt55 chokepoint (single enforced predicate feeding _compression_threshold_for_model) to match gpt-5.6* on the openai-codex route so those sessions get the same 0.85 auto-raise. Direct-API/OpenRouter routes (full 1.05M window) are unaffected; the historical codex_gpt55_autoraise opt-out still applies. The one-time notice banner is model-dynamic and already renders the correct slug/cap. - hermes_cli/config.py, agent/agent_init.py: refresh the autoraise comments/notice to mention the 5.6 family. - hermes_cli/codex_models.py: add the -pro variants to DEFAULT_CODEX_MODELS + forward-compat so ChatGPT-OAuth (openai-codex) Pro users see the full family in /model, not just the base tiers. Supersedes the earlier commit's note that 5.6 was intentionally kept out of the codex catalog: the slugs are confirmed routable (OpenRouter live + codex backend), so they belong there like every other codex-capable gpt-5.x slug. E2E verified across all 6 slugs: direct-API ctx 1.05M, codex ctx 272K, pricing reachable from openai + openai-api routes, codex compaction override 0.85 (and None on direct-API + when opted out), present in openai-api picker + codex catalog, /model gpt resolves to sol on both native routes. Guard tests added for the compaction route matrix.
133 lines
5.2 KiB
Python
133 lines
5.2 KiB
Python
"""Behavior contracts for the GPT-5.6 (Sol/Terra/Luna) registration.
|
|
|
|
Invariant tests only — no list snapshots (per the no-change-detector-tests
|
|
policy). These pin the two behaviors that would silently regress:
|
|
|
|
1. Version/tier sorting: the flagship Sol must outrank Terra/Luna and the
|
|
whole 5.6 series must outrank 5.5, so `/model gpt` resolves to the
|
|
flagship rather than an alphabetical-first cheap tier.
|
|
2. Pricing reachability: the ("openai", <model>) official-docs pricing keys
|
|
must be reachable from BOTH the bare "openai" provider and the
|
|
"openai-api" picker slug (resolve_billing_route normalizes the latter).
|
|
"""
|
|
|
|
from decimal import Decimal
|
|
|
|
from agent.usage_pricing import (
|
|
_OFFICIAL_DOCS_PRICING,
|
|
_lookup_official_docs_pricing,
|
|
resolve_billing_route,
|
|
)
|
|
from hermes_cli.model_switch import _model_sort_key
|
|
|
|
|
|
class TestGpt56SortInvariants:
|
|
def test_sol_outranks_terra_and_luna(self):
|
|
models = ["gpt-5.6-luna", "gpt-5.6-terra", "gpt-5.6-sol"]
|
|
models.sort(key=lambda m: _model_sort_key(m, "gpt"))
|
|
assert models[0] == "gpt-5.6-sol"
|
|
|
|
def test_56_series_outranks_55(self):
|
|
models = ["gpt-5.5", "gpt-5.5-pro", "gpt-5.6-sol"]
|
|
models.sort(key=lambda m: _model_sort_key(m, "gpt"))
|
|
assert models[0] == "gpt-5.6-sol"
|
|
|
|
def test_aggregator_prefix_form(self):
|
|
models = ["openai/gpt-5.5-pro", "openai/gpt-5.6-sol"]
|
|
models.sort(key=lambda m: _model_sort_key(m, "openai/gpt"))
|
|
assert models[0] == "openai/gpt-5.6-sol"
|
|
|
|
|
|
def test_base_sol_outranks_sol_pro_for_alias_default(self):
|
|
# "-pro" high-effort variants parse as suffix "sol-pro" (rank 1), so
|
|
# `/model gpt` defaults to base Sol rather than the high-effort mode.
|
|
models = ["gpt-5.6-sol-pro", "gpt-5.6-sol"]
|
|
models.sort(key=lambda m: _model_sort_key(m, "gpt"))
|
|
assert models[0] == "gpt-5.6-sol"
|
|
|
|
|
|
class TestGpt56PricingRoute:
|
|
def test_official_pricing_reachable_from_openai(self):
|
|
route = resolve_billing_route("gpt-5.6-sol", provider="openai")
|
|
entry = _lookup_official_docs_pricing(route)
|
|
assert entry is not None
|
|
assert entry.input_cost_per_million == Decimal("5.00")
|
|
|
|
def test_official_pricing_reachable_from_openai_api_slug(self):
|
|
# "openai-api" is the picker slug for direct api.openai.com and must
|
|
# normalize to the "openai" pricing key space.
|
|
route = resolve_billing_route("gpt-5.6-sol", provider="openai-api")
|
|
assert route.provider == "openai"
|
|
entry = _lookup_official_docs_pricing(route)
|
|
assert entry is not None
|
|
assert entry.input_cost_per_million == Decimal("5.00")
|
|
|
|
def test_cache_write_is_1_25x_input_for_56_series(self):
|
|
for slug in ("gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"):
|
|
entry = _OFFICIAL_DOCS_PRICING[("openai", slug)]
|
|
assert entry.input_cost_per_million is not None, slug
|
|
assert entry.cache_write_cost_per_million == (
|
|
entry.input_cost_per_million * Decimal("1.25")
|
|
), slug
|
|
assert entry.cache_read_cost_per_million == (
|
|
entry.input_cost_per_million * Decimal("0.10")
|
|
), slug
|
|
|
|
def test_pro_variants_alias_to_base_tier_pricing(self):
|
|
# -pro high-effort modes bill at the same per-token rates as their
|
|
# base tiers; the snapshot aliases them rather than duplicating rows.
|
|
for base in ("gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"):
|
|
assert (
|
|
_OFFICIAL_DOCS_PRICING[("openai", f"{base}-pro")]
|
|
is _OFFICIAL_DOCS_PRICING[("openai", base)]
|
|
), base
|
|
|
|
|
|
class TestGpt56CodexCompaction:
|
|
"""Codex OAuth caps the whole gpt-5.6 family at 272K, same as 5.4/5.5, so
|
|
the compaction auto-raise (0.85) must fire for every 5.6 variant on the
|
|
openai-codex route and NOT on the direct-API/OpenRouter routes."""
|
|
|
|
def test_autoraise_applies_to_all_56_on_codex(self):
|
|
from agent.auxiliary_client import _compression_threshold_for_model
|
|
|
|
for slug in (
|
|
"gpt-5.6-sol",
|
|
"gpt-5.6-sol-pro",
|
|
"gpt-5.6-terra",
|
|
"gpt-5.6-terra-pro",
|
|
"gpt-5.6-luna",
|
|
"gpt-5.6-luna-pro",
|
|
):
|
|
assert (
|
|
_compression_threshold_for_model(slug, provider="openai-codex")
|
|
== 0.85
|
|
), slug
|
|
|
|
def test_no_autoraise_on_direct_api_route(self):
|
|
from agent.auxiliary_client import _compression_threshold_for_model
|
|
|
|
# Direct OpenAI API / OpenRouter expose the full 1.05M window, so the
|
|
# 272K-cap override must NOT apply there.
|
|
assert (
|
|
_compression_threshold_for_model("gpt-5.6-sol", provider="openai")
|
|
is None
|
|
)
|
|
assert (
|
|
_compression_threshold_for_model(
|
|
"openai/gpt-5.6-sol", provider="openrouter"
|
|
)
|
|
is None
|
|
)
|
|
|
|
def test_autoraise_respects_opt_out(self):
|
|
from agent.auxiliary_client import _compression_threshold_for_model
|
|
|
|
assert (
|
|
_compression_threshold_for_model(
|
|
"gpt-5.6-sol",
|
|
provider="openai-codex",
|
|
allow_codex_gpt55_autoraise=False,
|
|
)
|
|
is None
|
|
)
|