Files
hermes-agent/tests/agent/test_opencode_session_affinity.py
Teknium cc81e436ce fix(models): delist hy3-free and laguna-s-2.1-free — OpenCode relay dropped them (anon 401)
The OpenCode Zen relay no longer serves hy3-free (since ~2026-08-31) or
laguna-s-2.1-free (new, verified 2026-09-09): both are gone from the live
GET /zen/v1/models catalog and anonymous chat completions return
401 {"type":"ModelError","message":"Model <id> is not supported"}
(2 probes >=60s apart, x-opencode-session header present).

- hermes_cli/models_catalog_static.py: remove both slugs from the
  opencode-free offline floor and the opencode-zen discovery floor;
  document the delist dates in the catalog comment.
- plugins/model-providers/opencode-free: default_aux_model moves from the
  dead laguna-s-2.1-free to nemotron-3.5-lightning-free (fastest surviving
  anonymous model).
- tests: swap fixtures off the dead slugs; extend the floor-exclusion
  invariant to cover both.

The live revalidation path already hides them when the relay is reachable;
this fixes the OFFLINE floor and the aux default, which would otherwise
offer/route to models that 401.
2026-09-13 21:06:39 -07:00

63 lines
2.5 KiB
Python

"""x-opencode-session rides on every OpenCode request, on every transport."""
from __future__ import annotations
import pytest
from agent import auxiliary_client as aux
from agent.chat_completion_helpers import build_api_kwargs
from run_agent import AIAgent
_MSGS = [{"role": "user", "content": "hi"}]
def _agent(provider, model, base_url, api_mode=None):
agent = AIAgent(
api_key="test-key",
base_url=base_url,
model=model,
provider=provider,
quiet_mode=True,
skip_context_files=True,
skip_memory=True,
session_id="sess-affinity-1",
)
if api_mode:
agent.api_mode = api_mode
agent._transport = None
agent._anthropic_base_url = base_url
return agent
@pytest.mark.parametrize(
"provider, model, base_url, api_mode",
[
("opencode-go", "glm-5", "https://opencode.ai/zen/go/v1", None), # chat_completions
("opencode-go", "gpt-5.6-luna", "https://opencode.ai/zen/go/v1", None), # codex_responses
("opencode-go", "minimax-m2.7", "https://opencode.ai/zen/go/v1", "anthropic_messages"),
("opencode-free", "nemotron-3.5-lightning-free", "https://opencode.ai/zen/v1", None),
("custom", "glm-5", "https://opencode.ai/zen/go/v1", None), # URL-only detection
],
)
def test_main_turn_sends_stable_session_header_on_every_transport(provider, model, base_url, api_mode):
agent = _agent(provider, model, base_url, api_mode)
first = build_api_kwargs(agent, _MSGS)["extra_headers"]["x-opencode-session"]
second = build_api_kwargs(agent, _MSGS)["extra_headers"]["x-opencode-session"]
assert first == second == "sess-affinity-1"
other = _agent("openrouter", "anthropic/claude-sonnet-4.6", "https://openrouter.ai/api/v1")
assert "x-opencode-session" not in (build_api_kwargs(other, _MSGS).get("extra_headers") or {})
def test_auxiliary_calls_share_the_main_turn_session_key():
token = aux.set_runtime_main(
"opencode-go", "glm-5", base_url="https://opencode.ai/zen/go/v1", session_id="sess-affinity-1"
)
try:
kwargs = aux._build_call_kwargs("opencode-go", "glm-5", _MSGS, base_url="https://opencode.ai/zen/go/v1")
assert kwargs["extra_headers"]["x-opencode-session"] == "sess-affinity-1"
other = aux._build_call_kwargs("openrouter", "x", _MSGS, base_url="https://openrouter.ai/api/v1")
assert "x-opencode-session" not in (other.get("extra_headers") or {})
finally:
aux._RUNTIME_MAIN_CONTEXT.reset(token)