From 03f12a95ef22538fe6173ec6772aeb38f583b590 Mon Sep 17 00:00:00 2001 From: Heidi Ho Date: Mon, 8 Jun 2026 18:57:24 -0400 Subject: [PATCH 1/2] feat(api): add X-Reflex-Request-ID header middleware --- src/tether/runtime/server.py | 17 +++++++ tests/test_request_id_header.py | 86 +++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 tests/test_request_id_header.py diff --git a/src/tether/runtime/server.py b/src/tether/runtime/server.py index 8318b70..499de77 100644 --- a/src/tether/runtime/server.py +++ b/src/tether/runtime/server.py @@ -19,10 +19,12 @@ from __future__ import annotations import base64 +import contextvars import io import json import logging import time +import uuid from pathlib import Path from typing import Any @@ -65,6 +67,7 @@ def track_in_flight(*args, **kwargs): logger = logging.getLogger(__name__) _tracer = get_tracer(__name__) +_request_id_var: contextvars.ContextVar[str] = contextvars.ContextVar("request_id", default="") try: from tether import __version__ as _TETHER_VERSION @@ -2180,6 +2183,17 @@ async def _heartbeat_loop(): lifespan=lifespan, ) + @app.middleware("http") + async def _request_id_middleware(request, call_next): + req_id = str(uuid.uuid4()) + token = _request_id_var.set(req_id) + try: + response = await call_next(request) + finally: + _request_id_var.reset(token) + response.headers["X-Reflex-Request-ID"] = req_id + return response + # Bearer auth dependency (Phase 1 auth-bearer feature). # If api_key is set at app-creation time, every protected route requires # the caller to pass `Authorization: Bearer ` (preferred) OR the @@ -2274,6 +2288,9 @@ async def act(request: PredictRequest, _auth: None = Depends(_require_api_key)): # Non-standard attrs under gen_ai.action.* — proposed for upstream # OTel GenAI working group contribution (Phase 2 per spec). span.set_attribute("gen_ai.action.embodiment", _emb_label) + _req_id = _request_id_var.get() + if _req_id: + span.set_attribute("tether.request_id", _req_id) # chunk_size + denoise_steps are set AFTER predict returns (we don't # know them until the result is in hand). See ~line 1590 below. span.set_attribute( diff --git a/tests/test_request_id_header.py b/tests/test_request_id_header.py new file mode 100644 index 0000000..9df2ad2 --- /dev/null +++ b/tests/test_request_id_header.py @@ -0,0 +1,86 @@ +"""Tests for the X-Reflex-Request-ID response header middleware. + +Verifies that every HTTP response carries a unique UUID4 in the +X-Reflex-Request-ID header, and that the value is available to the +OTel span via the _request_id_var context variable. + +Uses FastAPI's TestClient — no model loading, no ONNX runtime. +""" +from __future__ import annotations + +import contextvars +import uuid + +import pytest +from fastapi import FastAPI +from fastapi.responses import JSONResponse +from fastapi.testclient import TestClient + + +def _build_app() -> tuple[FastAPI, contextvars.ContextVar[str]]: + """Minimal FastAPI app with only the request-ID middleware wired in.""" + request_id_var: contextvars.ContextVar[str] = contextvars.ContextVar( + "request_id", default="" + ) + + app = FastAPI() + + @app.middleware("http") + async def _request_id_middleware(request, call_next): + req_id = str(uuid.uuid4()) + token = request_id_var.set(req_id) + try: + response = await call_next(request) + finally: + request_id_var.reset(token) + response.headers["X-Reflex-Request-ID"] = req_id + return response + + @app.get("/health") + async def health(): + return JSONResponse({"status": "ok"}) + + @app.post("/act") + async def act(): + return JSONResponse({"actions": []}) + + return app, request_id_var + + +@pytest.fixture +def client(): + app, _ = _build_app() + return TestClient(app) + + +class TestRequestIDHeader: + def test_health_response_has_header(self, client): + assert "X-Reflex-Request-ID" in client.get("/health").headers + + def test_act_response_has_header(self, client): + assert "X-Reflex-Request-ID" in client.post("/act").headers + + def test_header_value_is_valid_uuid(self, client): + value = client.get("/health").headers["X-Reflex-Request-ID"] + assert str(uuid.UUID(value)) == value # raises ValueError if malformed + + def test_each_request_gets_a_unique_id(self, client): + ids = {client.get("/health").headers["X-Reflex-Request-ID"] for _ in range(5)} + assert len(ids) == 5 + + def test_request_id_is_accessible_inside_route(self): + """Context var holds the same ID that ends up in the response header.""" + app, request_id_var = _build_app() + + captured: dict[str, str] = {} + + @app.post("/act_span") + async def act_span(): + captured["id"] = request_id_var.get() + return JSONResponse({"actions": []}) + + c = TestClient(app) + response = c.post("/act_span") + + assert captured["id"] != "" + assert response.headers["X-Reflex-Request-ID"] == captured["id"] From dbdfb0145fbeef07a9553eddeb98104dd88446c5 Mon Sep 17 00:00:00 2001 From: RomirJ Date: Wed, 17 Jun 2026 15:48:37 -0700 Subject: [PATCH 2/2] Use Tether request id header --- src/tether/runtime/server.py | 24 +++++- tests/test_request_id_header.py | 134 ++++++++++++++++++-------------- 2 files changed, 94 insertions(+), 64 deletions(-) diff --git a/src/tether/runtime/server.py b/src/tether/runtime/server.py index 421677a..90cc073 100644 --- a/src/tether/runtime/server.py +++ b/src/tether/runtime/server.py @@ -27,7 +27,6 @@ import logging import os import time -import uuid from pathlib import Path from types import SimpleNamespace from typing import Any @@ -45,6 +44,7 @@ compute_config_hash, compute_model_hash, ) +from .auth import generate_request_id from .tracing import get_tracer, setup_tracing, shutdown_tracing # Optional Prometheus metrics — gated on the [serve] extra (prometheus-client). @@ -80,7 +80,12 @@ def track_in_flight(*args, **kwargs): logger = logging.getLogger(__name__) _tracer = get_tracer(__name__) -_request_id_var: contextvars.ContextVar[str] = contextvars.ContextVar("request_id", default="") +REQUEST_ID_HEADER = "X-Tether-Request-ID" +REQUEST_ID_ALIASES = (REQUEST_ID_HEADER, "X-Request-ID") +_request_id_var: contextvars.ContextVar[str] = contextvars.ContextVar( + "tether_request_id", + default="", +) try: from tether import __version__ as _TETHER_VERSION @@ -100,6 +105,16 @@ def _coerce_optional_float(value: Any) -> float | None: return out +def _resolve_http_request_id(request: Any) -> str: + for header in REQUEST_ID_ALIASES: + value = request.headers.get(header) + if value: + value = value.strip() + if value: + return value[:128] + return generate_request_id() + + def _call_accepts_keyword(fn: Any, keyword: str) -> bool: try: params = inspect.signature(fn).parameters @@ -3017,13 +3032,14 @@ async def _heartbeat_loop(): @app.middleware("http") async def _request_id_middleware(request, call_next): - req_id = str(uuid.uuid4()) + req_id = _resolve_http_request_id(request) + request.state.request_id = req_id token = _request_id_var.set(req_id) try: response = await call_next(request) finally: _request_id_var.reset(token) - response.headers["X-Reflex-Request-ID"] = req_id + response.headers[REQUEST_ID_HEADER] = req_id return response # Bearer auth dependency (Phase 1 auth-bearer feature). diff --git a/tests/test_request_id_header.py b/tests/test_request_id_header.py index 9df2ad2..6d84b8f 100644 --- a/tests/test_request_id_header.py +++ b/tests/test_request_id_header.py @@ -1,86 +1,100 @@ -"""Tests for the X-Reflex-Request-ID response header middleware. - -Verifies that every HTTP response carries a unique UUID4 in the -X-Reflex-Request-ID header, and that the value is available to the -OTel span via the _request_id_var context variable. - -Uses FastAPI's TestClient — no model loading, no ONNX runtime. -""" +"""Request-id middleware coverage for the real Tether FastAPI app.""" from __future__ import annotations -import contextvars -import uuid +from pathlib import Path import pytest -from fastapi import FastAPI -from fastapi.responses import JSONResponse from fastapi.testclient import TestClient -def _build_app() -> tuple[FastAPI, contextvars.ContextVar[str]]: - """Minimal FastAPI app with only the request-ID middleware wired in.""" - request_id_var: contextvars.ContextVar[str] = contextvars.ContextVar( - "request_id", default="" - ) +class _StubServer: + def __init__(self, export_dir, *args, **kwargs): + self.export_dir = Path(export_dir) + self._ready = True + self.health_state = "ready" + self._inference_mode = "stub" + self._vlm_loaded = False + self.consecutive_crash_count = 0 + self.max_consecutive_crashes = 5 + self.robot_id = "" - app = FastAPI() + @property + def ready(self): + return self._ready - @app.middleware("http") - async def _request_id_middleware(request, call_next): - req_id = str(uuid.uuid4()) - token = request_id_var.set(req_id) - try: - response = await call_next(request) - finally: - request_id_var.reset(token) - response.headers["X-Reflex-Request-ID"] = req_id - return response + async def load(self): + self._ready = True + self.health_state = "ready" - @app.get("/health") - async def health(): - return JSONResponse({"status": "ok"}) - @app.post("/act") - async def act(): - return JSONResponse({"actions": []}) +@pytest.fixture +def app(tmp_path, monkeypatch): + from tether.runtime import server as runtime_server + + monkeypatch.setattr(runtime_server, "TetherServer", _StubServer) + export_dir = tmp_path / "export" + export_dir.mkdir() + app = runtime_server.create_app(str(export_dir), device="cpu") - return app, request_id_var + @app.get("/_test/request-id") + async def request_id_probe(): + return {"request_id": runtime_server._request_id_var.get()} + + return app @pytest.fixture -def client(): - app, _ = _build_app() +def client(app): return TestClient(app) -class TestRequestIDHeader: - def test_health_response_has_header(self, client): - assert "X-Reflex-Request-ID" in client.get("/health").headers +def test_health_response_has_tether_request_id_header(client): + response = client.get("/health") + + assert response.headers["X-Tether-Request-ID"].startswith("req-") + assert "X-Reflex-Request-ID" not in response.headers + + +def test_each_request_gets_a_unique_generated_id(client): + ids = {client.get("/health").headers["X-Tether-Request-ID"] for _ in range(5)} - def test_act_response_has_header(self, client): - assert "X-Reflex-Request-ID" in client.post("/act").headers + assert len(ids) == 5 - def test_header_value_is_valid_uuid(self, client): - value = client.get("/health").headers["X-Reflex-Request-ID"] - assert str(uuid.UUID(value)) == value # raises ValueError if malformed - def test_each_request_gets_a_unique_id(self, client): - ids = {client.get("/health").headers["X-Reflex-Request-ID"] for _ in range(5)} - assert len(ids) == 5 +def test_tether_request_id_header_is_echoed(client): + response = client.get( + "/health", + headers={"X-Tether-Request-ID": " req-user-supplied "}, + ) + + assert response.headers["X-Tether-Request-ID"] == "req-user-supplied" + - def test_request_id_is_accessible_inside_route(self): - """Context var holds the same ID that ends up in the response header.""" - app, request_id_var = _build_app() +def test_generic_request_id_header_is_accepted_when_tether_header_missing(client): + response = client.get("/health", headers={"X-Request-ID": "edge-proxy-123"}) - captured: dict[str, str] = {} + assert response.headers["X-Tether-Request-ID"] == "edge-proxy-123" - @app.post("/act_span") - async def act_span(): - captured["id"] = request_id_var.get() - return JSONResponse({"actions": []}) - c = TestClient(app) - response = c.post("/act_span") +def test_tether_header_wins_over_generic_request_id(client): + response = client.get( + "/health", + headers={ + "X-Tether-Request-ID": "req-tether", + "X-Request-ID": "proxy-request", + }, + ) + + assert response.headers["X-Tether-Request-ID"] == "req-tether" + + +def test_request_id_is_available_inside_route_context(app): + client = TestClient(app) + + response = client.get( + "/_test/request-id", + headers={"X-Tether-Request-ID": "req-route-context"}, + ) - assert captured["id"] != "" - assert response.headers["X-Reflex-Request-ID"] == captured["id"] + assert response.json()["request_id"] == "req-route-context" + assert response.headers["X-Tether-Request-ID"] == "req-route-context"