2776813df3
The Sep 2026 decomposition (PR #102117) makes internal import paths a non-API: names now live in the focused modules that define them. This commit is the ONLY thing keeping the old paths alive, so external plugins have time to update. It is deliberately a single, unsquashed commit: git revert <this sha> removes every shim, stub and manifest at once on the announced date. Nothing in-tree may depend on these pointers: scripts/check_compat_pointers.py (wired into lint.yml) fails CI if it does. What it adds (see COMPAT_MANIFEST.md, compat_manifest.json): - 332 facade modules get one delimited `PLUGIN-COMPAT` block appended at the end of the file - 1,172 moved names resolved lazily via a module `__getattr__` (PEP 562) — never a top-level import, so no import cycles; facades that already had `__getattr__` get a chained one - 592 third-party/stdlib names the old modules used to expose, with their original import statements - 266 public definitions that had been deleted as unused, restored byte-for-byte from the pre-decomposition tree (+40 private helpers and 16 imports pulled in only because a restored definition needs them) - 3 deleted modules recreated as re-export stubs (gateway/startup_watchdog, hermes_cli/observability/ relay_runtime, tools/environments/modal_utils) - private names (`_x`) get no pointer: they were never API (3,792 skipped) Verified: all 335 touched modules import under a fresh HERMES_HOME and every manifest name resolves; the lint reports zero in-tree uses; ruff clean; targeted suites unchanged.
145 lines
7.7 KiB
Python
145 lines
7.7 KiB
Python
"""Canonical reasoning-effort vocabulary and wire clamping.
|
|
|
|
Hermes' internal effort ladder (``VALID_REASONING_EFFORTS`` plus ``none``) is wider than any
|
|
single provider wire accepts; hand-rolled per-transport maps leaked new levels (``ultra``) to
|
|
wires that 400 and inverted the ladder (unknown → weak default). Single source of truth:
|
|
:data:`EFFORT_LADDER` (low→high), :func:`clamp_effort` (verbatim if supported, else the
|
|
nearest WEAKER level; only when nothing weaker exists the weakest supported), and named
|
|
wire-vocabulary constants so call sites declare data. Rules: wire shape stays local, only the
|
|
vocabulary math lives here; unset stays unset (never invent an effort); when a provider
|
|
rejects a level fix its declared set, never a predicate.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Optional, Sequence
|
|
|
|
#: Matches ``k3`` as a delimited token (``k3``, ``k3-256k``, ``kimi-k3-cot``), never K2-era names (``kimi-k2.6``).
|
|
# From #76427 by @ruizanthony.
|
|
_KIMI_K3_SLUG_RE = re.compile(r"(?:^|[^a-z0-9])k3(?:[^a-z0-9]|$)")
|
|
|
|
# Canonical low→high ordering for nearest-level clamping. Includes "none" so an explicit
|
|
# disable can be clamped when a provider publishes it as a level. ``ultra`` is Hermes-internal
|
|
# (the Codex product tier): no wire accepts it, every declared set stops at ``max``.
|
|
EFFORT_LADDER: tuple[str, ...] = ("none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra")
|
|
|
|
#: Widest OpenAI-compatible wire vocabulary (OpenRouter, Nous Portal).
|
|
OPENAI_COMPAT_WIRE_EFFORTS: tuple[str, ...] = ("none", "minimal", "low", "medium", "high", "xhigh", "max")
|
|
|
|
#: OpenAI/Codex Responses per model generation (live-verified): ``minimal`` is rejected by
|
|
#: both (clamps to low); ``max`` is gpt-5.6-only.
|
|
CODEX_GPT56_EFFORTS: tuple[str, ...] = ("none", "low", "medium", "high", "xhigh", "max")
|
|
CODEX_LEGACY_EFFORTS: tuple[str, ...] = ("none", "low", "medium", "high", "xhigh")
|
|
|
|
#: xAI Responses — Grok 4.6+ accepts xhigh; older Grok tops out at high.
|
|
XAI_GROK46_EFFORTS: tuple[str, ...] = ("low", "medium", "high", "xhigh")
|
|
XAI_LEGACY_EFFORTS: tuple[str, ...] = ("low", "medium", "high")
|
|
|
|
#: Actual Computer relays (SGLang/vLLM).
|
|
ACTUAL_RELAY_EFFORTS: tuple[str, ...] = ("none", "low", "medium", "high", "max")
|
|
|
|
#: Moonshot/Kimi K3 (server default high) vs K2-era models. K3 quirks: ``high`` is K3's
|
|
#: positional middle AND server default, so ``medium`` rounds to it rather than down to
|
|
#: ``low``; ``xhigh`` rounds up to ``max`` (K3's top tier).
|
|
KIMI_K3_EFFORTS: tuple[str, ...] = ("low", "high", "max")
|
|
KIMI_K2_EFFORTS: tuple[str, ...] = ("low", "medium", "high")
|
|
KIMI_K3_OVERRIDES: dict[str, str] = {"medium": "high", "xhigh": "max"}
|
|
|
|
#: OpenCode "Ox Alpha" (x-preview-f-free): thinking cannot be disabled and the wire accepts
|
|
#: exactly low/high/max (medium/none/xhigh 400); xhigh rounds up.
|
|
OX_ALPHA_EFFORTS: tuple[str, ...] = ("low", "high", "max")
|
|
OX_ALPHA_OVERRIDES: dict[str, str] = {"xhigh": "max"}
|
|
|
|
#: Tencent TokenHub / Nebius Token Factory / Upstage Solar: plain three-level knobs.
|
|
TOKENHUB_EFFORTS: tuple[str, ...] = ("low", "medium", "high")
|
|
NEBIUS_EFFORTS: tuple[str, ...] = ("low", "medium", "high")
|
|
SOLAR_EFFORTS: tuple[str, ...] = ("low", "medium", "high")
|
|
|
|
#: GLM-5.2 native knob: exactly ``high`` (its minimum thinking level) and ``max``; GLM-5.3
|
|
#: widens it to a graded scale (live-verified, monotonic). ``xhigh`` requests the top tier.
|
|
GLM52_EFFORTS: tuple[str, ...] = ("high", "max")
|
|
GLM52_OVERRIDES: dict[str, str] = {"xhigh": "max"}
|
|
# : GLM-5.3 widens the knob to a graded low/medium/high/max scale — verified : live on
|
|
# api.z.ai/api/coding/paas/v4 (issue #91789, 2026-08-21): every : level accepted with monotonic
|
|
# reasoning-token scaling (low=4, medium=11, : high=98, max=125 on the probe prompt).
|
|
GLM53_EFFORTS: tuple[str, ...] = ("low", "medium", "high", "max")
|
|
GLM53_OVERRIDES: dict[str, str] = {"xhigh": "max"}
|
|
|
|
#: DeepSeek V4 OpenAI-compat endpoint; ``xhigh`` requests the top tier.
|
|
DEEPSEEK_V4_EFFORTS: tuple[str, ...] = ("low", "medium", "high", "max")
|
|
DEEPSEEK_V4_OVERRIDES: dict[str, str] = {"xhigh": "max"}
|
|
|
|
#: Ollama Cloud /v1/chat/completions: rejects ``minimal`` with HTTP 400.
|
|
OLLAMA_CLOUD_EFFORTS: tuple[str, ...] = ("none", "low", "medium", "high", "max")
|
|
OLLAMA_CLOUD_OVERRIDES: dict[str, str] = {"xhigh": "max"}
|
|
|
|
#: Meta Model API (Muse): rejects ``none``.
|
|
META_AI_EFFORTS: tuple[str, ...] = ("minimal", "low", "medium", "high", "xhigh")
|
|
|
|
|
|
def codex_supported_efforts(model: Optional[str]) -> tuple[str, ...]:
|
|
"""Supported effort set for an OpenAI/Codex Responses model."""
|
|
return CODEX_GPT56_EFFORTS if "gpt-5.6" in (model or "").lower() else CODEX_LEGACY_EFFORTS
|
|
|
|
|
|
def kimi_supported_efforts(model: Optional[str]) -> tuple[str, ...]:
|
|
"""Supported effort set for a Moonshot/Kimi slug (bare ``k3``, ``k3-256k``, ``kimi-k3*`` → K3).
|
|
|
|
K3 is served as the bare slug ``k3``, plan variants like ``k3-256k``, and the ``kimi-k3*`` aliases; its
|
|
documented set is low/high/max. Everything earlier speaks low/medium/high. Boundary-matched so K2-era
|
|
names (``kimi-k2.6``) never match (detection regex from #76427 by @ruizanthony).
|
|
"""
|
|
m = (model or "").strip().lower().split("/")[-1]
|
|
return KIMI_K3_EFFORTS if _KIMI_K3_SLUG_RE.search(m) else KIMI_K2_EFFORTS
|
|
|
|
|
|
def clamp_effort(
|
|
effort: Optional[str], supported: Optional[Sequence[str]], overrides: Optional[dict[str, str]] = None,
|
|
) -> Optional[str]:
|
|
"""Clamp a requested reasoning effort onto a wire's supported levels.
|
|
|
|
``overrides`` (a declared vendor mapping, e.g. Kimi K3 ``medium → high``) is consulted
|
|
first. Otherwise the request passes through unchanged when it is supported, when the
|
|
supported set is unknown/empty, or when it isn't a recognized ladder level (custom
|
|
providers may use bespoke names). Else the **nearest weaker** supported level is returned
|
|
so a clamp never escalates cost; when nothing weaker exists, the weakest supported level
|
|
(the provider's floor is the closest honest match). Monotonic: a stronger request never
|
|
resolves weaker than a weaker request would.
|
|
"""
|
|
requested = str(effort or "").strip().lower()
|
|
if not requested or not supported:
|
|
return effort
|
|
supported_norm = [lvl for lvl in (str(s).strip().lower() for s in supported) if lvl in EFFORT_LADDER]
|
|
if not supported_norm or requested in supported_norm:
|
|
return effort
|
|
if overrides and overrides.get(requested) in supported_norm:
|
|
return overrides[requested]
|
|
if requested not in EFFORT_LADDER:
|
|
return effort
|
|
# "none" disables reasoning — never a degradation target for an enabled ask
|
|
# (clamping "minimal" to "none" would silently switch thinking off).
|
|
candidates = [level for level in supported_norm if level != "none"]
|
|
if not candidates:
|
|
return effort
|
|
requested_idx = EFFORT_LADDER.index(requested)
|
|
below = [level for level in candidates if EFFORT_LADDER.index(level) < requested_idx]
|
|
return max(below, key=EFFORT_LADDER.index) if below else min(candidates, key=EFFORT_LADDER.index)
|
|
|
|
|
|
def requested_effort(reasoning_config: Optional[dict]) -> Optional[str]:
|
|
"""The user's explicit effort, or None (absent/malformed config, no effort, or reasoning
|
|
disabled) — callers then omit the wire field."""
|
|
if not isinstance(reasoning_config, dict) or reasoning_config.get("enabled") is False:
|
|
return None
|
|
return str(reasoning_config.get("effort") or "").strip().lower() or None
|
|
|
|
|
|
# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ----
|
|
# Names external plugins imported from this module before the Sep 2026 decomposition.
|
|
# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does).
|
|
# The whole block is removed by reverting the commit that added it.
|
|
|
|
CODEX_RESPONSES_EFFORTS: tuple[str, ...] = CODEX_GPT56_EFFORTS
|
|
# ---- END PLUGIN-COMPAT ----
|