mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
## Description Refactors provider-specific proxy routing into provider-owned helper modules so `headroom/providers/proxy_routes.py` primarily registers routes and delegates behavior. This keeps Codex, OpenAI Responses/images, model metadata, Vertex, Cloud Code, passthrough target selection, and request path normalization logic testable outside the route table. Closes # ## Type of Change - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update - [ ] Performance improvement - [x] Code refactoring (no functional changes) ## Changes Made - Extracted Codex routing helpers for headers, endpoint URLs, image forwarding, response subpaths, and model metadata. - Moved provider target selection, route specs, OpenAI Responses/images helpers, Vertex runtime helpers, Cloud Code path normalization, passthrough telemetry, and request scope normalization into focused modules. - Kept `proxy_routes.py` as route registration/delegation and preserved current-main `/v1/messages` custom-base behavior. - Added focused provider/proxy tests for the extracted modules and route delegation behavior. ## 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 python -m pytest tests/test_package_init_lazy.py::test_codex_package_import_stays_runtime_only tests/test_provider_cloudcode_runtime.py tests/test_provider_codex_endpoints.py tests/test_provider_codex_headers.py tests/test_provider_codex_images.py tests/test_provider_codex_model_metadata.py tests/test_provider_codex_responses.py tests/test_provider_model_metadata.py tests/test_provider_openai_images.py tests/test_provider_openai_responses.py tests/test_provider_proxy_targets.py tests/test_provider_route_specs.py tests/test_provider_vertex_runtime.py tests/test_proxy_request_scope.py tests/test_provider_proxy_routes.py::test_provider_passthrough_routes_forward_expected_targets tests/test_provider_proxy_routes.py::test_proxy_route_helpers_prefer_legacy_targets_and_gemini_passthrough tests/test_provider_proxy_routes.py::test_provider_specific_routes_delegate_to_expected_proxy_handlers tests/test_provider_proxy_routes.py::test_openai_response_websocket_aliases_delegate_to_openai_ws_handler tests/test_provider_proxy_routes.py::test_openai_response_subpath_passthrough_returns_502_on_http_failure tests/test_provider_proxy_routes.py::test_openai_response_subpath_passthrough_uses_openai_target tests/test_provider_proxy_routes.py::test_openai_response_subpath_aliases_and_chatgpt_auth_use_expected_targets tests/test_provider_proxy_routes.py::test_openai_image_routes_use_codex_backend_under_chatgpt_auth tests/test_provider_proxy_routes.py::test_openai_image_codex_response_strips_stale_compression_headers tests/test_provider_proxy_routes.py::test_openai_image_edits_api_key_auth_falls_through_to_openai_passthrough tests/test_provider_proxy_routes.py::test_openai_image_edits_preserves_multipart_body_under_chatgpt_auth tests/test_provider_proxy_routes.py::test_gemini_batch_embed_contents_passthrough_uses_gemini_target tests/test_provider_proxy_routes.py::test_v1_models_fetches_codex_registry_under_chatgpt_auth tests/test_provider_proxy_routes.py::test_v1_models_falls_back_to_synthetic_list_under_chatgpt_auth tests/test_provider_proxy_routes.py::test_v1_models_get_single_dynamic_under_chatgpt_auth tests/test_provider_proxy_routes.py::test_v1_models_still_forwards_under_non_chatgpt_auth tests/test_provider_proxy_routes.py::test_v1_models_routes_claude_code_gateway_discovery_to_anthropic tests/test_provider_proxy_routes.py::test_anthropic_model_metadata_strips_ansi_model_ids tests/test_custom_base_passthrough_telemetry.py tests/test_proxy_passthrough.py tests/test_proxy_google_cloudcode_route_aliases.py tests/test_proxy_project_savings.py::test_with_project_prefix_round_trips_through_split tests/test_vertex_claude_compression.py ============================ 102 passed in 34.83s ============================= python -m ruff check headroom/providers/cloudcode headroom/providers/codex headroom/providers/vertex headroom/providers/model_metadata.py headroom/providers/openai_images.py headroom/providers/openai_responses.py headroom/providers/proxy_targets.py headroom/providers/route_specs.py headroom/providers/proxy_routes.py headroom/proxy/handlers/openai.py headroom/proxy/passthrough.py headroom/proxy/request_scope.py headroom/proxy/project_context.py tests/test_package_init_lazy.py tests/test_provider_cloudcode_runtime.py tests/test_provider_codex_endpoints.py tests/test_provider_codex_headers.py tests/test_provider_codex_images.py tests/test_provider_codex_model_metadata.py tests/test_provider_codex_responses.py tests/test_provider_model_metadata.py tests/test_provider_openai_images.py tests/test_provider_openai_responses.py tests/test_provider_proxy_targets.py tests/test_provider_route_specs.py tests/test_provider_vertex_runtime.py tests/test_proxy_request_scope.py tests/test_provider_proxy_routes.py tests/test_custom_base_passthrough_telemetry.py tests/test_proxy_passthrough.py tests/test_proxy_google_cloudcode_route_aliases.py tests/test_proxy_project_savings.py tests/test_vertex_claude_compression.py All checks passed! python -m compileall -q headroom\providers\cloudcode headroom\providers\codex headroom\providers\vertex headroom\providers\model_metadata.py headroom\providers\openai_images.py headroom\providers\openai_responses.py headroom\providers\proxy_targets.py headroom\providers\route_specs.py headroom\providers\proxy_routes.py headroom\proxy\handlers\openai.py headroom\proxy\passthrough.py headroom\proxy\request_scope.py headroom\proxy\project_context.py # no output; exited 0 git commit -m "refactor(providers): split proxy route adapters" Sync plugin versions.....................................................Passed check for merge conflicts................................................Passed ruff.....................................................................Passed ruff-format..............................................................Passed mypy.....................................................................Passed ``` ## Real Behavior Proof - Environment: Windows PowerShell, Python 3.13.13, branch `jd/provider-route-slices` based on `headroomlabs/main`. - Exact command / steps: Ran the focused provider/proxy pytest suite, focused ruff command, compileall over changed Python modules, and commit hooks. - Observed result: Provider/proxy route behavior tests passed; lint, formatting, and mypy passed. - Not tested: Full pytest suite, live upstream provider calls, and manual end-to-end proxy traffic. ## 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) N/A ## Additional Notes Documentation and CHANGELOG updates are N/A for this internal refactor. The full pytest suite was not run; coverage here is focused on provider/proxy routing behavior touched by this slice. --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
62 lines
1.8 KiB
Python
62 lines
1.8 KiB
Python
import json
|
|
|
|
from headroom.providers.codex.images import (
|
|
codex_image_forward_error_response,
|
|
codex_image_url,
|
|
normalize_codex_image_headers,
|
|
sanitize_codex_image_response_headers,
|
|
)
|
|
|
|
|
|
def test_codex_image_url_includes_optional_query() -> None:
|
|
assert (
|
|
codex_image_url("generations", "client_version=0.142.0")
|
|
== "https://chatgpt.com/backend-api/codex/images/generations?client_version=0.142.0"
|
|
)
|
|
assert codex_image_url("edits") == "https://chatgpt.com/backend-api/codex/images/edits"
|
|
|
|
|
|
def test_codex_image_headers_drop_proxy_only_headers_and_resolve_auth() -> None:
|
|
headers, is_chatgpt_auth = normalize_codex_image_headers(
|
|
{
|
|
"Host": "localhost:8787",
|
|
"Authorization": "Bearer token",
|
|
"Accept-Encoding": "gzip",
|
|
"X-Headroom-Bypass": "1",
|
|
"ChatGPT-Account-ID": "acct",
|
|
"Content-Type": "application/json",
|
|
}
|
|
)
|
|
|
|
assert is_chatgpt_auth is True
|
|
assert headers == {
|
|
"Authorization": "Bearer token",
|
|
"ChatGPT-Account-ID": "acct",
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
|
|
def test_codex_image_response_headers_drop_stale_framing_case_insensitive() -> None:
|
|
assert sanitize_codex_image_response_headers(
|
|
{
|
|
"Content-Encoding": "gzip",
|
|
"Content-Length": "9999",
|
|
"Content-Type": "application/json",
|
|
"x-upstream": "kept",
|
|
}
|
|
) == {
|
|
"Content-Type": "application/json",
|
|
"x-upstream": "kept",
|
|
}
|
|
|
|
|
|
def test_codex_image_forward_error_response_shape() -> None:
|
|
response = codex_image_forward_error_response()
|
|
|
|
assert response.status_code == 502
|
|
assert json.loads(response.body) == {
|
|
"error": {
|
|
"type": "upstream_error",
|
|
"message": "Failed to forward Codex image request",
|
|
}
|
|
}
|