fix(wrap/claude): keep --1m effective when an explicit --model is passed through
Ensure explicit Claude model arguments retain the 1M context suffix (#2915).
This commit is contained in:
+39
-4
@@ -303,6 +303,33 @@ def _resolve_1m_model(current: str | None) -> str:
|
||||
return base if base.endswith(_CONTEXT_1M_SUFFIX) else f"{base}{_CONTEXT_1M_SUFFIX}"
|
||||
|
||||
|
||||
def _apply_1m_to_claude_args(args: tuple[str, ...]) -> tuple[tuple[str, ...], str | None]:
|
||||
"""Add the ``[1m]`` suffix to an explicit ``--model`` in pass-through args.
|
||||
|
||||
Claude Code gives the ``--model`` CLI flag precedence over the
|
||||
``ANTHROPIC_MODEL`` env var, so when a user passes both ``--1m`` and
|
||||
``--model X`` the env-var suffix is silently shadowed and the session caps at
|
||||
200k (#2915). Rewriting the flag's value the same way ``_resolve_1m_model``
|
||||
rewrites the env var keeps ``--1m`` effective on the higher-precedence flag.
|
||||
|
||||
Handles ``--model VALUE`` and ``--model=VALUE`` (the first occurrence only, as
|
||||
Claude Code honours the first). Idempotent via ``_resolve_1m_model``. Returns
|
||||
``(new_args, rewritten_value)``; ``rewritten_value`` is ``None`` when no
|
||||
``--model`` was present (the env-var path already covers that case).
|
||||
"""
|
||||
out = list(args)
|
||||
for i, arg in enumerate(out):
|
||||
if arg == "--model" and i + 1 < len(out):
|
||||
rewritten = _resolve_1m_model(out[i + 1])
|
||||
out[i + 1] = rewritten
|
||||
return tuple(out), rewritten
|
||||
if arg.startswith("--model="):
|
||||
rewritten = _resolve_1m_model(arg.split("=", 1)[1])
|
||||
out[i] = f"--model={rewritten}"
|
||||
return tuple(out), rewritten
|
||||
return tuple(out), None
|
||||
|
||||
|
||||
def _normalize_tool_search_mode(value: str) -> str:
|
||||
"""Validate an ``ENABLE_TOOL_SEARCH`` value and return it normalized.
|
||||
|
||||
@@ -4728,10 +4755,18 @@ def claude(
|
||||
# force it via ANTHROPIC_MODEL on the launched process.
|
||||
if context_1m:
|
||||
env[_ANTHROPIC_MODEL_ENV] = _resolve_1m_model(env.get(_ANTHROPIC_MODEL_ENV))
|
||||
click.echo(
|
||||
f" {_ANTHROPIC_MODEL_ENV}={env[_ANTHROPIC_MODEL_ENV]} "
|
||||
"(1M context window; issue #1158)"
|
||||
)
|
||||
# An explicit pass-through --model outranks ANTHROPIC_MODEL in Claude
|
||||
# Code, so add the suffix there too or the env var is silently
|
||||
# shadowed and the window stays 200k (#2915). Report what will
|
||||
# actually take effect rather than the shadowed env value.
|
||||
claude_args, _model_flag_1m = _apply_1m_to_claude_args(claude_args)
|
||||
if _model_flag_1m is not None:
|
||||
click.echo(f" --model {_model_flag_1m} (1M context window; issue #1158)")
|
||||
else:
|
||||
click.echo(
|
||||
f" {_ANTHROPIC_MODEL_ENV}={env[_ANTHROPIC_MODEL_ENV]} "
|
||||
"(1M context window; issue #1158)"
|
||||
)
|
||||
|
||||
result = subprocess.run([claude_bin, *claude_args], env=env)
|
||||
raise SystemExit(result.returncode)
|
||||
|
||||
@@ -160,6 +160,46 @@ def test_wrap_claude_sibling_note_accurate_under_1m_and_tool_search_optouts(
|
||||
assert "kept on" not in output
|
||||
|
||||
|
||||
def test_wrap_claude_1m_adds_suffix_to_passthrough_model_flag(
|
||||
runner: CliRunner, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
# #2915: Claude Code's --model CLI flag outranks ANTHROPIC_MODEL, so with
|
||||
# both --1m and an explicit --model the env-var [1m] suffix is shadowed and
|
||||
# the window silently caps at 200k. The wrapper must add the suffix to the
|
||||
# pass-through flag so the 1M window actually activates.
|
||||
captured, output = _invoke_wrap_claude(
|
||||
runner,
|
||||
monkeypatch,
|
||||
env={},
|
||||
extra_args=("--1m", "--model", "opusplan"),
|
||||
)
|
||||
assert captured["child_cmd"] == ["/usr/bin/claude", "--model", "opusplan[1m]"]
|
||||
# The banner reports what actually takes effect, not the shadowed env value.
|
||||
assert "--model opusplan[1m]" in output
|
||||
|
||||
|
||||
def test_wrap_claude_1m_adds_suffix_to_equals_model_flag(
|
||||
runner: CliRunner, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
captured, _output = _invoke_wrap_claude(
|
||||
runner,
|
||||
monkeypatch,
|
||||
env={},
|
||||
extra_args=("--1m", "--model=opusplan"),
|
||||
)
|
||||
assert captured["child_cmd"] == ["/usr/bin/claude", "--model=opusplan[1m]"]
|
||||
|
||||
|
||||
def test_wrap_claude_1m_without_model_flag_still_uses_env(
|
||||
runner: CliRunner, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
# No pass-through --model: ANTHROPIC_MODEL carries the suffix as before, and
|
||||
# the launched command is untouched.
|
||||
captured, _output = _invoke_wrap_claude(runner, monkeypatch, env={}, extra_args=("--1m",))
|
||||
assert captured["child_cmd"] == ["/usr/bin/claude"]
|
||||
assert captured["child_env"]["ANTHROPIC_MODEL"].endswith("[1m]")
|
||||
|
||||
|
||||
def test_wrap_claude_tool_search_banner_line_still_accurate_when_active(
|
||||
runner: CliRunner, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
|
||||
@@ -109,6 +109,35 @@ def test_wrap_claude_allows_claude_print_short_flag_in_passthrough_args() -> Non
|
||||
assert result.exit_code == 0, result.output
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _apply_1m_to_claude_args — add the [1m] suffix to an explicit pass-through
|
||||
# --model so it survives Claude Code's CLI-over-env precedence (#2915).
|
||||
# ---------------------------------------------------------------------------
|
||||
def test_apply_1m_rewrites_model_flag_value() -> None:
|
||||
args, rewritten = wrap_mod._apply_1m_to_claude_args(("--model", "opusplan"))
|
||||
assert args == ("--model", "opusplan[1m]")
|
||||
assert rewritten == "opusplan[1m]"
|
||||
|
||||
|
||||
def test_apply_1m_rewrites_equals_model_flag() -> None:
|
||||
args, rewritten = wrap_mod._apply_1m_to_claude_args(("--model=opusplan",))
|
||||
assert args == ("--model=opusplan[1m]",)
|
||||
assert rewritten == "opusplan[1m]"
|
||||
|
||||
|
||||
def test_apply_1m_is_idempotent_on_already_suffixed_model() -> None:
|
||||
args, rewritten = wrap_mod._apply_1m_to_claude_args(("--model", "opusplan[1m]"))
|
||||
assert args == ("--model", "opusplan[1m]")
|
||||
assert rewritten == "opusplan[1m]"
|
||||
|
||||
|
||||
def test_apply_1m_noop_without_model_flag() -> None:
|
||||
original = ("--permission-mode", "auto", "--resume")
|
||||
args, rewritten = wrap_mod._apply_1m_to_claude_args(original)
|
||||
assert args == original
|
||||
assert rewritten is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _run_proxy_only_watcher — must print banner, call setup callback, install
|
||||
# signal handlers, and clean up. Heavily mocked since the real watcher
|
||||
|
||||
Reference in New Issue
Block a user