hermes: validate canonical Claude model aliases

This commit is contained in:
jenkins 2026-09-29 08:04:27 -05:00
parent d7d2d5430b
commit 089fe21384
2 changed files with 13 additions and 7 deletions

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import json import json
import os import os
from pathlib import Path from pathlib import Path
import re
import signal import signal
import subprocess import subprocess
import tempfile import tempfile
@ -129,6 +130,7 @@ def stop(process):
def parse_claude(raw, expected_model): def parse_claude(raw, expected_model):
"""Normalize only a completed, uncompacted CLI result with a pinned model.""" """Normalize only a completed, uncompacted CLI result with a pinned model."""
final, initialized = None, False final, initialized = None, False
aliases = {expected_model, expected_model + "[1m]"}
for line in raw.splitlines(): for line in raw.splitlines():
try: try:
event = json.loads(line) event = json.loads(line)
@ -140,7 +142,7 @@ def parse_claude(raw, expected_model):
raise Problem("compaction_detected", 502) raise Problem("compaction_detected", 502)
if event.get("type") == "system" and event.get("subtype") == "init": if event.get("type") == "system" and event.get("subtype") == "init":
initialized = True initialized = True
if (event.get("model") != expected_model or event.get("mcp_servers") or if (event.get("model") not in aliases or event.get("mcp_servers") or
event.get("plugins") or set(event.get("tools", [])) - {"StructuredOutput"}): event.get("plugins") or set(event.get("tools", [])) - {"StructuredOutput"}):
raise Problem("worker_isolation_failed", 502) raise Problem("worker_isolation_failed", 502)
if event.get("type") == "result": if event.get("type") == "result":
@ -156,11 +158,15 @@ def parse_claude(raw, expected_model):
if final.get("stop_reason") in {"max_tokens", "model_context_window_exceeded"}: if final.get("stop_reason") in {"max_tokens", "model_context_window_exceeded"}:
raise Problem("incomplete_generation", 502) raise Problem("incomplete_generation", 502)
models = final.get("modelUsage", {}) models = final.get("modelUsage", {})
if set(models) != {expected_model}: if not models or set(models) - aliases:
raise Problem("model_changed", 502) observed = [name if re.fullmatch(r"claude-[a-z0-9.-]+(?:\[1m\])?", name)
limits = models[expected_model] else "unrecognized" for name in models]
if limits.get("contextWindow") != 1000000 or limits.get("maxOutputTokens") != 64000: raise Problem("model_changed", 502, observed_models=observed)
raise Problem("backend_capabilities_changed", 502) for limits in models.values():
if (limits.get("contextWindow") != 1000000 or limits.get("maxOutputTokens") != 64000
or limits.get("canonicalModel") != expected_model
or limits.get("provider") != "firstParty"):
raise Problem("backend_capabilities_changed", 502)
usage = final.get("usage", {}) usage = final.get("usage", {})
if any(usage.get("server_tool_use", {}).values()): if any(usage.get("server_tool_use", {}).values()):
raise Problem("worker_isolation_failed", 502) raise Problem("worker_isolation_failed", 502)

View File

@ -36,7 +36,7 @@ spec:
app: hermes-suite-planner app: hermes-suite-planner
annotations: annotations:
fluentbit.io/exclude: "true" fluentbit.io/exclude: "true"
ai.bstein.dev/config-rev: suite-v2-20260929 ai.bstein.dev/config-rev: suite-v3-20260929
vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-pre-populate-only: "true" vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/agent-init-first: "true" vault.hashicorp.com/agent-init-first: "true"