feat: add capability and admin introspection endpoints

Expose capability discovery plus admin-only config and request inspection endpoints so clients and operators can understand gateway behavior without reading code.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
mmc
2026-05-12 14:30:08 +08:00
parent 94a8025ae5
commit b719bdeaa2
5 changed files with 780 additions and 93 deletions

View File

@@ -1,14 +1,37 @@
from __future__ import annotations
import asyncio
import sys
import types
import unittest
from unittest.mock import patch
from fastapi import HTTPException
from fastapi.testclient import TestClient
from starlette.requests import Request
from app.auth import AnthropicAuthError, require_anthropic_key, require_bearer, require_metrics_access
from app.concurrency import BackpressureRejected, InFlightGuard
_playwright = types.ModuleType("playwright")
_playwright_async = types.ModuleType("playwright.async_api")
class _StubPlaywrightTimeoutError(Exception):
pass
async def _stub_async_playwright():
raise RuntimeError("playwright is stubbed in unit tests")
_playwright_async.TimeoutError = _StubPlaywrightTimeoutError
_playwright_async.async_playwright = _stub_async_playwright
sys.modules.setdefault("playwright", _playwright)
sys.modules.setdefault("playwright.async_api", _playwright_async)
import app.main as main
def _req(headers: dict[str, str] | None = None) -> Request:
pairs = []
@@ -82,5 +105,48 @@ class AuthAndConcurrencyTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(guard.in_flight, 0)
class DebugRequestRecordingTests(unittest.TestCase):
def setUp(self) -> None:
main._DEBUG_REQUEST_LOG.clear()
def test_redacts_sensitive_fields_and_data_urls(self) -> None:
body = {
"authorization": "Bearer abc",
"x-api-key": "secret",
"session_bundle": "very-secret",
"images": ["data:image/png;base64,ABC"],
"tool": {"args": "x" * 3000},
}
redacted = main._redact_debug_value((), body)
self.assertEqual(redacted["authorization"], "***")
self.assertEqual(redacted["x-api-key"], "***")
self.assertEqual(redacted["session_bundle"], "***")
self.assertEqual(redacted["images"][0], "[redacted-data-url]")
self.assertIn("[truncated]", redacted["tool"]["args"])
def test_internal_debug_requests_requires_admin_and_returns_items(self) -> None:
with patch.object(main.settings, "api_keys", ["k1"]), patch.object(main.settings, "admin_token", "admin-1"):
client = TestClient(main.app)
req_payload = {
"model": "org_auto",
"messages": [{"role": "user", "content": "hello"}],
}
main._record_debug_request("openai", "/v1/chat/completions", req_payload, _req({"x-request-id": "req-1"}))
denied = client.get("/internal/debug/requests")
self.assertEqual(denied.status_code, 401)
ok = client.get(
"/internal/debug/requests?limit=1",
headers={"Authorization": "Bearer admin-1"},
)
self.assertEqual(ok.status_code, 200)
data = ok.json()
self.assertTrue(data["ok"])
self.assertEqual(data["count"], 1)
self.assertEqual(data["items"][0]["protocol"], "openai")
if __name__ == "__main__":
unittest.main()