fix(doctor): flag ollama launch claude proxy bypass instead of misdirecting (#2566)
## Description Addresses the diagnostic half of #2199. `ollama launch claude` sets `ANTHROPIC_BASE_URL=http://127.0.0.1:11434` in the launched Claude Code child. That process env outranks the `env` block a persistent Headroom install writes to `~/.claude/settings.json`, so Claude Code talks to Ollama and never reaches the proxy — 0% savings, nothing on the dashboard, no error. `headroom doctor`'s routing classifier made it worse: seeing a loopback `:11434`, it reported `routed to port 11434, but doctor probed port 8787` and hinted `re-run with: headroom doctor --port 11434` — sending the user to re-probe Ollama's endpoint as if it were their proxy. Closes #2199 ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) ## Changes Made - `_classify_routing_url` now recognizes Ollama's fixed default port: the check names the `ollama launch claude` bypass and points at the proxy-chaining path instead of the red-herring `--port 11434` re-probe hint. - Fires for both the shell-env and settings-file routing checks that share the classifier. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [x] Type checking passes (`mypy headroom`) - [x] New tests added for new functionality - [ ] Manual testing performed ### Test Output ```text $ pytest tests/test_cli_doctor.py -q 1 failed, 68 passed in 3.05s # The lone failure is test_remote_control_warning_exits_1 — pre-existing and # unrelated: it reads real ~/.headroom stats and fails on a clean tree with or # without this change (does not exist / does not pass on main either). $ pytest tests/test_cli_doctor.py -k ollama -q 1 passed, 68 deselected $ ruff check headroom/cli/doctor.py tests/test_cli_doctor.py All checks passed! $ mypy headroom Success: no issues found in 509 source files ``` ## Real Behavior Proof - Environment: local checkout, Python venv, `pytest`/`ruff`/`mypy` as above. - Exact command / steps: `tests/test_cli_doctor.py` pins the Ollama-aware message + hint emitted by `_classify_routing_url` for a loopback `:11434` routing URL. - Observed result: doctor now reports the `ollama launch claude` bypass and the proxy-chaining fix instead of `re-run with: headroom doctor --port 11434`. - Not tested: no live `ollama launch claude` run; verified at the classifier boundary. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] I did **not** edit `CHANGELOG.md` — it is generated by release-please from my Conventional Commit PR title (a CI guard enforces this) ## Additional Notes Scope: this is only the *diagnostic* ask (#2199 part 3, requested as the minimum). The launcher-composition and model-aware routing halves depend on #1279's direction and are left for a maintainer steer. Documentation item is N/A (diagnostic message change, no docs surface). The pre-existing `test_remote_control_warning_exits_1` failure is unrelated as noted above.
This commit is contained in:
@@ -48,6 +48,13 @@ SKIP = "skip"
|
||||
_LOOPBACK_URL_RE = re.compile(r"https?://(?:127\.0\.0\.1|localhost):(\d+)")
|
||||
_CODEX_BASE_URL_RE = re.compile(r'base_url\s*=\s*"https?://(?:127\.0\.0\.1|localhost):(\d+)')
|
||||
|
||||
# Ollama's fixed default port. `ollama launch claude` writes
|
||||
# ``ANTHROPIC_BASE_URL=http://127.0.0.1:11434`` into the launched Claude Code
|
||||
# child, which outranks the persistent-install env block and silently bypasses
|
||||
# the Headroom proxy (issue #2199). Recognized so the routing diagnostic names
|
||||
# the collision instead of telling the user to re-probe port 11434.
|
||||
_OLLAMA_DEFAULT_PORT = 11434
|
||||
|
||||
|
||||
@dataclass
|
||||
class CheckResult:
|
||||
@@ -344,6 +351,22 @@ def _classify_routing_url(name: str, url: str, port: int, *, source: str) -> Che
|
||||
)
|
||||
found_port = int(match.group(1))
|
||||
if found_port != port:
|
||||
if found_port == _OLLAMA_DEFAULT_PORT:
|
||||
# Not a mis-probed Headroom port — this is Ollama's endpoint, so
|
||||
# `headroom doctor --port 11434` would only chase a red herring.
|
||||
return CheckResult(
|
||||
name=name,
|
||||
status=WARN,
|
||||
summary=(
|
||||
f"points at Ollama ({url}), not the Headroom proxy ({source}) — "
|
||||
"`ollama launch claude` bypasses the persistent Headroom route"
|
||||
),
|
||||
hint=(
|
||||
"both claim ANTHROPIC_BASE_URL; run Ollama-backed sessions "
|
||||
"through Headroom by chaining the proxy at its Ollama upstream "
|
||||
"(see issue #2199)"
|
||||
),
|
||||
)
|
||||
return CheckResult(
|
||||
name=name,
|
||||
status=WARN,
|
||||
|
||||
@@ -390,6 +390,17 @@ class TestShellEnv:
|
||||
env = {"ANTHROPIC_BASE_URL": "https://api.anthropic.com"}
|
||||
assert check_shell_env(env, 8787).status == WARN
|
||||
|
||||
def test_ollama_launch_url_names_the_collision(self):
|
||||
# `ollama launch claude` points Claude Code at Ollama's :11434, which
|
||||
# outranks the persistent Headroom route (issue #2199). The diagnostic
|
||||
# must name Ollama, not tell the user to re-probe port 11434.
|
||||
env = {"ANTHROPIC_BASE_URL": "http://127.0.0.1:11434"}
|
||||
result = check_shell_env(env, 8787)
|
||||
assert result.status == WARN
|
||||
assert "Ollama" in result.summary
|
||||
assert "#2199" in (result.hint or "")
|
||||
assert "--port 11434" not in (result.hint or "")
|
||||
|
||||
|
||||
class TestSavings:
|
||||
def test_from_stats_passes_with_totals(self, tmp_path):
|
||||
|
||||
Reference in New Issue
Block a user