fix: route v1internal code assist requests to cloudcode-pa.googleapis… (#821)

## Description

This PR fixes routing of Google Cloud Code Assist authentication,
onboarding, and experiment list endpoints.
Specifically, endpoints under `/v1/v1internal:*` (e.g.
`/v1/v1internal:fetchAvailableModels`) are now correctly routed to the
Cloud Code target (`https://cloudcode-pa.googleapis.com`) and
**normalized** to `/v1internal:*` prior to forwarding. This resolves
404/403 errors on the upstream service which does not accept
`/v1/v1internal:*` request paths.

Closes #821

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Modified `headroom/providers/proxy_routes.py` to strip the `v1/`
prefix and normalize the path to `/v1internal:*` for Cloud Code routes.
- Modified `tests/test_provider_proxy_routes.py` to add assertions
verifying route and path normalization.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom/providers/proxy_routes.py`)
- [x] New tests added for new functionality
- [x] Manual testing performed

### Test Output

```text
================================= test session starts =================================
platform linux -- Python 3.14.5, pytest-9.0.3, pluggy-1.6.0 -- /home/alex/projects/github.com/Djabx/headroom/.venv/bin/python3
cachedir: .pytest_cache
rootdir: /home/alex/projects/github.com/Djabx/headroom
configfile: pyproject.toml
plugins: anyio-4.12.1, cov-7.1.0, asyncio-1.4.0, langsmith-0.8.15
asyncio: mode=Mode.AUTO, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collected 13 items

tests/test_provider_proxy_routes.py::test_provider_passthrough_routes_forward_expected_targets PASSED [  7%]
tests/test_provider_proxy_routes.py::test_proxy_route_helpers_prefer_legacy_targets_and_gemini_passthrough PASSED [ 15%]
tests/test_provider_proxy_routes.py::test_provider_specific_routes_delegate_to_expected_proxy_handlers PASSED [ 23%]
tests/test_provider_proxy_routes.py::test_openai_response_websocket_aliases_delegate_to_openai_ws_handler PASSED [ 30%]
tests/test_provider_proxy_routes.py::test_openai_response_subpath_passthrough_returns_502_on_http_failure PASSED [ 38%]
tests/test_provider_proxy_routes.py::test_openai_response_subpath_passthrough_uses_openai_target PASSED [ 46%]
tests/test_provider_proxy_routes.py::test_openai_response_subpath_aliases_and_chatgpt_auth_use_expected_targets PASSED [ 53%]
tests/test_provider_proxy_routes.py::test_gemini_batch_embed_contents_passthrough_uses_gemini_target PASSED [ 61%]
tests/test_provider_proxy_routes.py::test_v1_models_fetches_codex_registry_under_chatgpt_auth PASSED [ 69%]
tests/test_provider_proxy_routes.py::test_v1_models_falls_back_to_synthetic_list_under_chatgpt_auth PASSED [ 76%]
tests/test_provider_proxy_routes.py::test_v1_models_get_single_dynamic_under_chatgpt_auth PASSED [ 84%]
tests/test_provider_proxy_routes.py::test_v1_models_still_forwards_under_non_chatgpt_auth PASSED [ 92%]
tests/test_provider_proxy_routes.py::test_v1_models_routes_claude_code_gateway_discovery_to_anthropic PASSED [100%]

================================= 13 passed in 0.63s =================================
```

## Real Behavior Proof

- Environment: Linux, Python 3.14.5
- Exact command / steps: `pytest tests/test_provider_proxy_routes.py`
which utilizes `fastapi.testclient.TestClient` to dispatch requests.
- Observed result: Both `/v1internal` and `/v1/v1internal` endpoints are
correctly routed to the Cloud Code target (`https://cloudcode.test`) and
normalize their paths to `/v1internal`, avoiding 404/403 errors on the
upstream service.
- Not tested: Actual production Cloud Code endpoints (simulated via
TestClient/fakes).

## 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
- [ ] I have updated the CHANGELOG.md if applicable

## Screenshots (if applicable)

Add screenshots to help explain your changes.

## Additional Notes

<!-- Mention any N/A checklist items, tradeoffs, follow-ups, or
maintainer context. -->


<!-- headroom-maintainer-template-completion:start -->

## Description

This PR prepares `fix: route v1internal code assist requests to
cloudcode-pa.googleapis…` for review by documenting the intended change,
validation evidence, and remaining merge-readiness context.

Linked issues: None declared.

## Type of Change

- [x] Bug fix
- [ ] New feature
- [ ] Documentation
- [ ] Refactor
- [ ] Tests only

## Changes Made

- Commit: fix: route v1internal code assist requests to
cloudcode-pa.googleapis…
- Touches `headroom/providers/proxy_routes.py`
- Touches `tests/test_provider_proxy_routes.py`

## Testing

- [x] GitHub checks reviewed
- [x] Metadata/template validation
- [ ] Local functional testing

### Test Output

```text
gh pr view 821 --repo chopratejas/headroom --json statusCheckRollup
- PR Governance / template: FAILURE
- PR Governance / template: FAILURE
- PR Governance / template: FAILURE
- PR Governance / template: FAILURE
- PR Governance / label: SUCCESS
- PR Governance / label: SUCCESS
- PR Governance / label: SUCCESS
- PR Governance / label: SUCCESS
- external / GitGuardian Security Checks: SUCCESS
```

## Real Behavior Proof

- Environment: GitHub PR metadata and checks for `chopratejas/headroom`
PR #821.
- Exact command / steps: Reviewed PR title, commits, changed files,
linked issues, labels, and check rollup; appended this maintainer
template completion block without replacing the author's original
description.
- Observed result: PR body now contains all required governance
sections, checked readiness fields, and a non-placeholder validation
evidence block.
- Not tested: This pass updated PR metadata only; code validation
remains represented by the linked GitHub checks and any author-provided
evidence above.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

<!-- headroom-maintainer-template-completion:end -->
This commit is contained in:
Djabx 2026-06-16 21:58:44 +02:00 committed by GitHub
parent 7edb27ab24
commit e20f16b1a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 42 additions and 0 deletions

View file

@ -850,6 +850,30 @@ def register_provider_routes(app: FastAPI, proxy: Any) -> None:
custom_base = request.headers.get("x-headroom-base-url")
if custom_base:
return await proxy.handle_passthrough(request, custom_base.rstrip("/"))
# Intercept Code Assist authentication and onboarding routes
clean_path = path.lstrip("/")
if clean_path.startswith(("v1internal:", "v1/v1internal:")):
# Normalize path (remove v1/ prefix if present to avoid 404 on cloudcode-pa upstream)
normalized_path = clean_path
if normalized_path.startswith("v1/"):
normalized_path = normalized_path[3:]
normalized_path = f"/{normalized_path}"
# Mutate request scope so handle_passthrough uses the normalized path
request.scope["path"] = normalized_path
if "raw_path" in request.scope:
from urllib.parse import quote
request.scope["raw_path"] = quote(normalized_path).encode("ascii")
if hasattr(request, "_url"):
delattr(request, "_url")
return await proxy.handle_passthrough(
request,
_api_target(proxy, "cloudcode"),
)
return await proxy.handle_passthrough(
request,
_select_passthrough_base_url(proxy, dict(request.headers)),

View file

@ -179,6 +179,24 @@ def test_provider_passthrough_routes_forward_expected_targets(monkeypatch) -> No
"base_url"
] == ("https://api.gemini.test")
# Prove Code Assist routes go to the cloudcode target and normalize paths
res1 = client.post("/v1internal:loadCodeAssist").json()
assert res1["base_url"] == "https://cloudcode.test"
assert res1["path"] == "/v1internal:loadCodeAssist"
res2 = client.post("/v1/v1internal:fetchAvailableModels").json()
assert res2["base_url"] == "https://cloudcode.test"
assert res2["path"] == "/v1internal:fetchAvailableModels"
# Prove a non-Code-Assist passthrough path containing a similar substring does not get rerouted
assert (
client.get(
"/unrelated/path/containing/v1internal:someAction",
headers={"x-goog-api-key": "test"},
).json()["base_url"]
== "https://api.gemini.test"
)
assert len(calls) >= 16
assert len(gemini_calls) >= 1
assert len(gemini_count_calls) >= 1