mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
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:
parent
7edb27ab24
commit
e20f16b1a6
2 changed files with 42 additions and 0 deletions
|
|
@ -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)),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue