Files
chopratejas--headroom/tests/test_custom_base_passthrough_telemetry.py
wehub-resource-sync 0ef5fcb1c5
Security / Dependency audit (pip-audit) (push) Has been cancelled
Security / CodeQL (javascript-typescript) (push) Has been cancelled
Security / CodeQL (python) (push) Has been cancelled
Security / Secret scan (gitleaks) (push) Has been cancelled
rust / test (ubuntu) (push) Has been cancelled
rust / simulator e2e (macos-latest) (push) Has been cancelled
rust / simulator e2e (ubuntu-latest) (push) Has been cancelled
rust / simulator e2e (windows-latest) (push) Has been cancelled
rust / wheels (aarch64-apple-darwin) (push) Has been cancelled
rust / wheels (x86_64-unknown-linux-gnu) (push) Has been cancelled
rust / wheels (x86_64-apple-darwin) (push) Has been cancelled
rust / audit (push) Has been cancelled
rust / parity (nightly, allowed to fail during Phase 0) (push) Has been cancelled
CI / commitlint (push) Has been skipped
Dev Containers / validate (.devcontainer/devcontainer.json, default) (push) Failing after 0s
Dev Containers / validate (.devcontainer/memory-stack/devcontainer.json, memory-stack) (push) Failing after 0s
Dev Containers / validate-worktree (push) Failing after 0s
CI / changes (push) Failing after 4s
Deploy Documentation / validate (push) Has been skipped
Deploy Documentation / deploy (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, claude) (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, codex) (push) Failing after 1s
Install Native E2E / install-native (ubuntu-latest) (push) Failing after 1s
OpenCode Plugin / typecheck + build + test (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, copilot) (push) Failing after 1s
Release Please / release-please (push) Failing after 1s
Wrap E2E / docker-wrap-e2e (push) Failing after 1s
Wrap Native E2E / wrap-native (ubuntu-latest) (push) Failing after 1s
Init E2E / docker-init-e2e (push) Failing after 4s
Merge Conflicts / merge-conflicts (push) Failing after 4s
CI / lint (push) Has been cancelled
CI / build-wheel (push) Has been cancelled
CI / build-wheel-windows (push) Has been cancelled
CI / prefetch-model (push) Has been cancelled
CI / test-dashboard-ui (push) Has been cancelled
CI / test (1) (push) Has been cancelled
CI / test (2) (push) Has been cancelled
CI / test (3) (push) Has been cancelled
CI / test (4) (push) Has been cancelled
CI / test-extras (push) Has been cancelled
CI / test-agno (push) Has been cancelled
CI / build (push) Has been cancelled
CI / workflow-validation (push) Has been cancelled
CI / docker-native-e2e (push) Has been cancelled
CI / windows-native-wrapper (push) Has been cancelled
CI / macos-native-wrapper (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / promote-latest (push) Has been cancelled
Init Native E2E / init-native (macos-latest, claude) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, codex) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, copilot) (push) Has been cancelled
Install Native E2E / install-native (macos-latest) (push) Has been cancelled
Wrap Native E2E / wrap-native (macos-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:03:20 +08:00

213 lines
6.4 KiB
Python

from __future__ import annotations
import asyncio
import json
from types import SimpleNamespace
from typing import Any
import httpx
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from fastapi.testclient import TestClient
from headroom.providers.proxy_routes import register_provider_routes
from headroom.proxy.handlers.openai import OpenAIHandlerMixin
class _Runtime:
@staticmethod
def api_target(provider: str) -> str:
return f"https://{provider}.example.test"
@staticmethod
def model_metadata_provider(headers: dict[str, str]) -> str:
return "anthropic"
class _Proxy:
ANTHROPIC_API_URL = "https://anthropic.example.test"
OPENAI_API_URL = "https://openai.example.test"
GEMINI_API_URL = "https://gemini.example.test"
CLOUDCODE_API_URL = "https://cloudcode.example.test"
VERTEX_API_URL = "https://vertex.example.test"
def __init__(self) -> None:
self.config = SimpleNamespace(bedrock_api_url=None)
self.provider_runtime = _Runtime()
self.calls: list[dict[str, Any]] = []
async def handle_passthrough(
self,
request: Any,
base_url: str,
endpoint_name: str = "",
provider: str = "",
) -> JSONResponse:
self.calls.append(
{
"path": request.url.path,
"base_url": base_url,
"endpoint_name": endpoint_name,
"provider": provider,
}
)
return JSONResponse(self.calls[-1])
class _ChatCompletionsRequest:
method = "POST"
headers = {}
url = SimpleNamespace(path="/zen/v1/chat/completions", query="")
async def body(self) -> bytes:
return b'{"model":"zen"}'
class _OpenAIUsageClient:
def __init__(self) -> None:
self.calls: list[dict[str, Any]] = []
async def request(self, **kwargs: Any) -> httpx.Response:
self.calls.append(kwargs)
request = httpx.Request(kwargs["method"], kwargs["url"])
return httpx.Response(
200,
request=request,
headers={"content-type": "application/json"},
json={
"usage": {
"prompt_tokens": 21,
"completion_tokens": 8,
"prompt_tokens_details": {"cached_tokens": 5},
}
},
)
def test_custom_base_provider_prefixed_chat_completions_gets_telemetry() -> None:
app = FastAPI()
proxy = _Proxy()
register_provider_routes(app, proxy)
with TestClient(app) as client:
for base_url, expected_base_url in (
("https://opencode.ai/", "https://opencode.ai"),
("https://www.opencode.ai/", "https://www.opencode.ai"),
):
response = client.post(
"/zen/v1/chat/completions",
headers={"x-headroom-base-url": base_url},
json={"model": "zen"},
)
assert response.status_code == 200
assert response.json() == {
"path": "/zen/v1/chat/completions",
"base_url": expected_base_url,
"endpoint_name": "chat/completions",
"provider": "zen",
}
def test_custom_base_unrelated_passthrough_paths_stay_unclassified() -> None:
app = FastAPI()
proxy = _Proxy()
register_provider_routes(app, proxy)
with TestClient(app) as client:
for path in (
"/mcp",
"/mcp/v1/chat/completions",
"/npm/v1/chat/completions",
"/context7/v1/chat/completions",
):
response = client.post(
path,
headers={"x-headroom-base-url": "https://opencode.ai/"},
json={},
)
assert response.status_code == 200
assert response.json() == {
"path": path,
"base_url": "https://opencode.ai",
"endpoint_name": "",
"provider": "",
}
def test_custom_base_chat_completions_telemetry_is_post_and_opencode_zen_only() -> None:
app = FastAPI()
proxy = _Proxy()
register_provider_routes(app, proxy)
with TestClient(app) as client:
get_response = client.get(
"/zen/v1/chat/completions",
headers={"x-headroom-base-url": "https://opencode.ai/"},
)
other_host_response = client.post(
"/zen/v1/chat/completions",
headers={"x-headroom-base-url": "https://custom.example/"},
json={"model": "zen"},
)
double_slash_response = client.post(
"/zen//v1/chat/completions",
headers={"x-headroom-base-url": "https://opencode.ai/"},
json={"model": "zen"},
)
trailing_slash_response = client.post(
"/zen/v1/chat/completions/",
headers={"x-headroom-base-url": "https://opencode.ai/"},
json={"model": "zen"},
)
for response in (
get_response,
other_host_response,
double_slash_response,
trailing_slash_response,
):
assert response.status_code == 200
assert response.json()["endpoint_name"] == ""
assert response.json()["provider"] == ""
def test_classified_custom_base_passthrough_records_telemetry_usage() -> None:
handler = object.__new__(OpenAIHandlerMixin)
handler.http_client = _OpenAIUsageClient()
outcomes = []
async def next_request_id() -> str:
return "req_zen"
async def record(outcome: Any) -> None:
outcomes.append(outcome)
handler._next_request_id = next_request_id
handler._record_request_outcome = record
response = asyncio.run(
handler.handle_passthrough(
_ChatCompletionsRequest(),
"https://opencode.ai",
"chat/completions",
"zen",
)
)
assert response.status_code == 200
assert json.loads(response.body) == {
"usage": {
"prompt_tokens": 21,
"completion_tokens": 8,
"prompt_tokens_details": {"cached_tokens": 5},
}
}
assert handler.http_client.calls[0]["url"] == ("https://opencode.ai/zen/v1/chat/completions")
assert len(outcomes) == 1
outcome = outcomes[0]
assert outcome.provider == "zen"
assert outcome.model == "passthrough:chat/completions"
assert outcome.optimized_tokens == 21
assert outcome.output_tokens == 8
assert outcome.cache_read_tokens == 5