diff --git a/headroom/providers/proxy_routes.py b/headroom/providers/proxy_routes.py index d215edf8c..ce6b5fe08 100644 --- a/headroom/providers/proxy_routes.py +++ b/headroom/providers/proxy_routes.py @@ -474,6 +474,13 @@ async def _handle_chatgpt_codex_images( def register_provider_routes(app: FastAPI, proxy: Any) -> None: """Register provider-specific proxy endpoints.""" + def normalize_request_path(request: Request, path: str) -> None: + request.scope["path"] = path + if "raw_path" in request.scope: + request.scope["raw_path"] = quote(path).encode("ascii") + if hasattr(request, "_url"): + delattr(request, "_url") + async def vertex_publisher_passthrough(request: Request, publisher: str, action: str): return await proxy.handle_passthrough( request, @@ -486,6 +493,11 @@ def register_provider_routes(app: FastAPI, proxy: Any) -> None: async def anthropic_messages(request: Request): return await proxy.handle_anthropic_messages(request) + @app.post("/anthropic/v1/messages") + async def foundry_anthropic_messages(request: Request): + normalize_request_path(request, "/v1/messages") + return await proxy.handle_anthropic_messages(request, _api_target(proxy, "anthropic")) + # AWS Bedrock InvokeModel passthrough. Registered ONLY when an upstream is # configured (`--bedrock-api-url` / BEDROCK_TARGET_API_URL): without it, # `/model/{id}/invoke` keeps falling through to the catch-all (verbatim, diff --git a/tests/test_provider_proxy_routes.py b/tests/test_provider_proxy_routes.py index c80c03701..be588a3c1 100644 --- a/tests/test_provider_proxy_routes.py +++ b/tests/test_provider_proxy_routes.py @@ -173,6 +173,14 @@ def test_provider_passthrough_routes_forward_expected_targets(monkeypatch) -> No "model": "claude-3-5-sonnet@20240620", "force_stream": False, } + assert client.post("/anthropic/v1/messages?beta=true").json() == { + "handler": "handle_anthropic_messages", + "path": "/v1/messages", + "upstream_base_url": "https://api.anthropic.test", + "provider": "anthropic", + "model": None, + "force_stream": False, + } non_anthropic_raw = client.post( "/projects/p/locations/us-central1/publishers/google/models/gemini-2.0-flash:rawPredict" ).json() @@ -336,6 +344,11 @@ def test_provider_specific_routes_delegate_to_expected_proxy_handlers(monkeypatc with TestClient(_app()) as client: assert client.post("/v1/messages").json()["handler"] == "handle_anthropic_messages" + assert client.post("/anthropic/v1/messages").json() == { + "handler": "handle_anthropic_messages", + "path": "/v1/messages", + "args": ["https://api.anthropic.test"], + } assert ( client.post("/v1/messages/batches").json()["handler"] == "handle_anthropic_batch_create" )