fix(model): require confirmation for expensive model selections
Rebased onto current main and re-ported across the restructured surfaces: model flows now thread confirm_provider/base_url/api_key through hermes_cli/model_setup_flows.py, the Discord picker lives in plugins/platforms/discord/adapter.py, and the web dashboard picker applies chat-mode switches via config.set so the expensive-model confirmation can ride the response. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Teknium
co-authored by
Claude Fable 5
parent
4eadef18a9
commit
af978ecb17
@@ -2397,7 +2397,7 @@ def test_config_set_model_waits_for_lazy_agent_before_switch(monkeypatch):
|
||||
target["agent"] = agent
|
||||
agent_ready.set()
|
||||
|
||||
def fake_apply(sid, target, raw):
|
||||
def fake_apply(sid, target, raw, **kwargs):
|
||||
calls.append(("apply", sid, target.get("agent"), raw))
|
||||
if target.get("agent") is not agent:
|
||||
raise AssertionError("model switch ran before lazy agent was ready")
|
||||
@@ -2424,7 +2424,7 @@ def test_config_set_model_uses_live_switch_path(monkeypatch):
|
||||
server._sessions["sid"] = _session()
|
||||
seen = {}
|
||||
|
||||
def _fake_apply(sid, session, raw):
|
||||
def _fake_apply(sid, session, raw, **_kwargs):
|
||||
seen["args"] = (sid, session["session_key"], raw)
|
||||
return {"value": "new/model", "warning": "catalog unreachable"}
|
||||
|
||||
@@ -2442,6 +2442,74 @@ def test_config_set_model_uses_live_switch_path(monkeypatch):
|
||||
assert seen["args"] == ("sid", "session-key", "new/model")
|
||||
|
||||
|
||||
def test_config_set_model_requires_confirmation_for_expensive_model(monkeypatch):
|
||||
class _Agent:
|
||||
provider = "openrouter"
|
||||
model = "old/model"
|
||||
base_url = ""
|
||||
api_key = "sk-or"
|
||||
switched = False
|
||||
|
||||
def switch_model(self, **_kwargs):
|
||||
self.switched = True
|
||||
|
||||
result = types.SimpleNamespace(
|
||||
success=True,
|
||||
new_model="openai/gpt-5.5-pro",
|
||||
target_provider="openrouter",
|
||||
api_key="sk-or",
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
api_mode="chat_completions",
|
||||
warning_message="",
|
||||
model_info=types.SimpleNamespace(
|
||||
has_cost_data=lambda: True,
|
||||
cost_input=25.0,
|
||||
cost_output=125.0,
|
||||
),
|
||||
)
|
||||
|
||||
agent = _Agent()
|
||||
server._sessions["sid"] = _session(agent=agent)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.model_switch.switch_model", lambda **_kwargs: result
|
||||
)
|
||||
monkeypatch.setattr(server, "_restart_slash_worker", lambda sid, session: None)
|
||||
monkeypatch.setattr(server, "_emit", lambda *args, **kwargs: None)
|
||||
|
||||
resp = server.handle_request(
|
||||
{
|
||||
"id": "1",
|
||||
"method": "config.set",
|
||||
"params": {
|
||||
"session_id": "sid",
|
||||
"key": "model",
|
||||
"value": "openai/gpt-5.5-pro --provider openrouter",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
assert resp["result"]["confirm_required"] is True
|
||||
assert "did you mean to select openai/gpt-5.5?" in resp["result"]["confirm_message"]
|
||||
assert agent.switched is False
|
||||
|
||||
confirmed = server.handle_request(
|
||||
{
|
||||
"id": "2",
|
||||
"method": "config.set",
|
||||
"params": {
|
||||
"session_id": "sid",
|
||||
"key": "model",
|
||||
"value": "openai/gpt-5.5-pro --provider openrouter",
|
||||
"confirm_expensive_model": True,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
assert confirmed["result"]["confirm_required"] is False
|
||||
assert confirmed["result"]["value"] == "openai/gpt-5.5-pro"
|
||||
assert agent.switched is True
|
||||
|
||||
|
||||
def test_config_set_model_global_persists(monkeypatch):
|
||||
class _Agent:
|
||||
provider = "openrouter"
|
||||
@@ -3944,7 +4012,7 @@ def test_config_set_model_rejects_while_running(monkeypatch):
|
||||
"""/model via config.set must reject during an in-flight turn."""
|
||||
seen = {"called": False}
|
||||
|
||||
def _fake_apply(sid, session, raw):
|
||||
def _fake_apply(sid, session, raw, **_kwargs):
|
||||
seen["called"] = True
|
||||
return {"value": raw, "warning": ""}
|
||||
|
||||
@@ -3978,7 +4046,7 @@ def test_config_set_model_allowed_when_idle(monkeypatch):
|
||||
"""Regression guard: idle sessions can still switch models."""
|
||||
seen = {"called": False}
|
||||
|
||||
def _fake_apply(sid, session, raw):
|
||||
def _fake_apply(sid, session, raw, **_kwargs):
|
||||
seen["called"] = True
|
||||
return {"value": "newmodel", "warning": ""}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user