Spaces:
Sleeping
Sleeping
File size: 3,249 Bytes
2415446 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | """Ingress-owned HTTP request correlation."""
import uuid
from fastapi import Request, Response
from loguru import logger
from starlette.datastructures import Headers, MutableHeaders
from starlette.types import ASGIApp, Message, Receive, Scope, Send
from free_claude_code.core.trace import extract_claude_session_id_from_headers
REQUEST_ID_HEADER = "request-id"
OPENAI_REQUEST_ID_HEADER = "x-request-id"
_REQUEST_ID_STATE_ATTRIBUTE = "fcc_request_id"
_OPENAI_REQUEST_ID_PATHS = frozenset({"/v1/responses", "/v1/models"})
class RequestCorrelationMiddleware:
"""Own one request id and logging context for the full ASGI response."""
def __init__(self, app: ASGIApp) -> None:
self._app = app
async def __call__(
self,
scope: Scope,
receive: Receive,
send: Send,
) -> None:
if scope["type"] != "http":
await self._app(scope, receive, send)
return
request_id = new_request_id()
state = scope.setdefault("state", {})
state[_REQUEST_ID_STATE_ATTRIBUTE] = request_id
method = scope.get("method", "")
path = scope.get("path", "")
request_headers = Headers(scope=scope)
claude_sid = extract_claude_session_id_from_headers(request_headers)
async def send_with_correlation(message: Message) -> None:
if message["type"] == "http.response.start":
message = dict(message)
raw_headers = list(message.get("headers", ()))
_set_request_id_headers(
MutableHeaders(raw=raw_headers),
request_id=request_id,
path=path,
)
message["headers"] = raw_headers
await send(message)
with logger.contextualize(
http_method=method,
http_path=path,
claude_session_id=claude_sid,
request_id=request_id,
):
await self._app(scope, receive, send_with_correlation)
def new_request_id() -> str:
"""Return a new opaque FCC request identifier."""
return f"req_{uuid.uuid4().hex}"
def set_request_id(request: Request, request_id: str) -> None:
"""Attach the ingress correlation identifier to request state."""
setattr(request.state, _REQUEST_ID_STATE_ATTRIBUTE, request_id)
def get_request_id(request: Request) -> str:
"""Return the ingress correlation identifier, creating a fallback if needed."""
request_id = getattr(request.state, _REQUEST_ID_STATE_ATTRIBUTE, None)
if isinstance(request_id, str) and request_id:
return request_id
request_id = new_request_id()
set_request_id(request, request_id)
return request_id
def attach_request_id_headers(
response: Response, *, request_id: str, path: str
) -> None:
"""Attach correlation when an outer server-error boundary bypasses middleware."""
_set_request_id_headers(response.headers, request_id=request_id, path=path)
def _set_request_id_headers(
headers: MutableHeaders,
*,
request_id: str,
path: str,
) -> None:
headers[REQUEST_ID_HEADER] = request_id
if path in _OPENAI_REQUEST_ID_PATHS:
headers[OPENAI_REQUEST_ID_HEADER] = request_id
|