Honor interactive model override after current CLI changes (#111)
Browse filesThe interactive CLI accepted --model but did not pass it into main(), so the REPL used the configured default while headless mode honored the flag. Reapply the override on top of the current token/session plumbing by changing only the interactive main signature, config override, and CLI dispatch.
Constraint: Current main already passes user_id into submission_loop and uses shared HF token resolution; this fix must preserve both paths.
Rejected: Reuse the old PR diff verbatim | it drops newer user_id/session identity changes and conflicts with current main.
Confidence: high
Scope-risk: narrow
Directive: Keep --model behavior identical for interactive and headless paths; banner should render the resolved model.
Tested: python -m py_compile agent/main.py tests/unit/test_cli_rendering.py
Tested: UV_CACHE_DIR=/tmp/uv-cache uv run --extra dev pytest tests/unit/test_cli_rendering.py -q
Tested: UV_CACHE_DIR=/tmp/uv-cache uv run --extra dev pytest tests/unit/test_llm_params.py tests/unit/test_hf_access.py tests/unit/test_user_quotas.py -q
Co-authored-by: akseljoonas <aksel.reedi@gmail.com>
- agent/main.py +4 -2
- tests/unit/test_cli_rendering.py +46 -0
|
@@ -807,7 +807,7 @@ async def _handle_slash_command(
|
|
| 807 |
return None
|
| 808 |
|
| 809 |
|
| 810 |
-
async def main():
|
| 811 |
"""Interactive chat with the agent"""
|
| 812 |
|
| 813 |
# Clear screen
|
|
@@ -822,6 +822,8 @@ async def main():
|
|
| 822 |
hf_token = await _prompt_and_save_hf_token(prompt_session)
|
| 823 |
|
| 824 |
config = load_config(CLI_CONFIG_PATH)
|
|
|
|
|
|
|
| 825 |
|
| 826 |
# Resolve username for banner
|
| 827 |
hf_user = _get_hf_user(hf_token)
|
|
@@ -1240,7 +1242,7 @@ def cli():
|
|
| 1240 |
max_iter = 10_000 # effectively unlimited
|
| 1241 |
asyncio.run(headless_main(args.prompt, model=args.model, max_iterations=max_iter, stream=not args.no_stream))
|
| 1242 |
else:
|
| 1243 |
-
asyncio.run(main())
|
| 1244 |
except KeyboardInterrupt:
|
| 1245 |
print("\n\nGoodbye!")
|
| 1246 |
|
|
|
|
| 807 |
return None
|
| 808 |
|
| 809 |
|
| 810 |
+
async def main(model: str | None = None):
|
| 811 |
"""Interactive chat with the agent"""
|
| 812 |
|
| 813 |
# Clear screen
|
|
|
|
| 822 |
hf_token = await _prompt_and_save_hf_token(prompt_session)
|
| 823 |
|
| 824 |
config = load_config(CLI_CONFIG_PATH)
|
| 825 |
+
if model:
|
| 826 |
+
config.model_name = model
|
| 827 |
|
| 828 |
# Resolve username for banner
|
| 829 |
hf_user = _get_hf_user(hf_token)
|
|
|
|
| 1242 |
max_iter = 10_000 # effectively unlimited
|
| 1243 |
asyncio.run(headless_main(args.prompt, model=args.model, max_iterations=max_iter, stream=not args.no_stream))
|
| 1244 |
else:
|
| 1245 |
+
asyncio.run(main(model=args.model))
|
| 1246 |
except KeyboardInterrupt:
|
| 1247 |
print("\n\nGoodbye!")
|
| 1248 |
|
|
@@ -1,8 +1,12 @@
|
|
| 1 |
"""Regression tests for interactive CLI rendering and research model routing."""
|
| 2 |
|
|
|
|
| 3 |
from io import StringIO
|
| 4 |
from types import SimpleNamespace
|
| 5 |
|
|
|
|
|
|
|
|
|
|
| 6 |
from agent.tools.research_tool import _get_research_model
|
| 7 |
from agent.utils import terminal_display
|
| 8 |
|
|
@@ -42,3 +46,45 @@ def test_subagent_display_does_not_spawn_background_redraw(monkeypatch):
|
|
| 42 |
mgr.clear("agent-1")
|
| 43 |
|
| 44 |
assert calls == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""Regression tests for interactive CLI rendering and research model routing."""
|
| 2 |
|
| 3 |
+
import sys
|
| 4 |
from io import StringIO
|
| 5 |
from types import SimpleNamespace
|
| 6 |
|
| 7 |
+
import pytest
|
| 8 |
+
|
| 9 |
+
import agent.main as main_mod
|
| 10 |
from agent.tools.research_tool import _get_research_model
|
| 11 |
from agent.utils import terminal_display
|
| 12 |
|
|
|
|
| 46 |
mgr.clear("agent-1")
|
| 47 |
|
| 48 |
assert calls == []
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def test_cli_forwards_model_flag_to_interactive_main(monkeypatch):
|
| 52 |
+
seen: dict[str, str | None] = {}
|
| 53 |
+
|
| 54 |
+
async def fake_main(*, model=None):
|
| 55 |
+
seen["model"] = model
|
| 56 |
+
|
| 57 |
+
monkeypatch.setattr(sys, "argv", ["ml-intern", "--model", "openai/gpt-5.5"])
|
| 58 |
+
monkeypatch.setattr(main_mod, "main", fake_main)
|
| 59 |
+
|
| 60 |
+
main_mod.cli()
|
| 61 |
+
|
| 62 |
+
assert seen["model"] == "openai/gpt-5.5"
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@pytest.mark.asyncio
|
| 66 |
+
async def test_interactive_main_applies_model_override_before_banner(monkeypatch):
|
| 67 |
+
class StopAfterBanner(Exception):
|
| 68 |
+
pass
|
| 69 |
+
|
| 70 |
+
def fake_banner(*, model=None, hf_user=None):
|
| 71 |
+
assert model == "openai/gpt-5.5"
|
| 72 |
+
assert hf_user == "tester"
|
| 73 |
+
raise StopAfterBanner
|
| 74 |
+
|
| 75 |
+
monkeypatch.setattr(main_mod.os, "system", lambda *_args, **_kwargs: 0)
|
| 76 |
+
monkeypatch.setattr(main_mod, "PromptSession", lambda: object())
|
| 77 |
+
monkeypatch.setattr(main_mod, "resolve_hf_token", lambda: "hf-token")
|
| 78 |
+
monkeypatch.setattr(main_mod, "_get_hf_user", lambda _token: "tester")
|
| 79 |
+
monkeypatch.setattr(
|
| 80 |
+
main_mod,
|
| 81 |
+
"load_config",
|
| 82 |
+
lambda _path: SimpleNamespace(
|
| 83 |
+
model_name="moonshotai/Kimi-K2.6",
|
| 84 |
+
mcpServers={},
|
| 85 |
+
),
|
| 86 |
+
)
|
| 87 |
+
monkeypatch.setattr(main_mod, "print_banner", fake_banner)
|
| 88 |
+
|
| 89 |
+
with pytest.raises(StopAfterBanner):
|
| 90 |
+
await main_mod.main(model="openai/gpt-5.5")
|