Background
We currently have an AuditLogMiddleware registered on the FastAPI app, but request log lines from the inference pipeline (src/climatevision/inference/pipeline.py) and from any helper modules don't carry the same identifier as the incoming HTTP request. That makes it hard to correlate a user-reported failure to the inference run that produced it.
Acceptance criteria
Add a small middleware (in src/climatevision/api/middleware.py) that:
- Reads the
X-Request-ID header from the incoming request. If absent, generates a new UUID4.
- Sets the value on a
contextvars.ContextVar (e.g. request_id_var) so anything in the request lifecycle can read it.
- Adds the same value to the response as
X-Request-ID.
- Wires up a
logging.Filter (or LoggerAdapter) so every log record emitted during the request includes the request ID — the existing log format string can be extended with %(request_id)s.
Then add a pytest that:
- Sends a request without
X-Request-ID and asserts the response contains an X-Request-ID header with a UUID-shaped value.
- Sends a request with an explicit
X-Request-ID and asserts the response echoes the same value back.
- Captures a log line emitted from a fake route handler and asserts the request ID appears in it.
Pointers
- The middleware should be a
BaseHTTPMiddleware subclass; we already have one (AuditLogMiddleware) for reference in api/main.py.
- Use
contextvars.ContextVar not threading.local — FastAPI is async.
- For the log filter, a simple
class RequestIdFilter(logging.Filter): def filter(self, record): record.request_id = request_id_var.get("-"); return True is enough.
Why this is a good first issue
No model code, no DB, no GEE. Touches only the API middleware layer plus one test. ~60 lines including the test.
Background
We currently have an
AuditLogMiddlewareregistered on the FastAPI app, but request log lines from the inference pipeline (src/climatevision/inference/pipeline.py) and from any helper modules don't carry the same identifier as the incoming HTTP request. That makes it hard to correlate a user-reported failure to the inference run that produced it.Acceptance criteria
Add a small middleware (in
src/climatevision/api/middleware.py) that:X-Request-IDheader from the incoming request. If absent, generates a new UUID4.contextvars.ContextVar(e.g.request_id_var) so anything in the request lifecycle can read it.X-Request-ID.logging.Filter(orLoggerAdapter) so every log record emitted during the request includes the request ID — the existing log format string can be extended with%(request_id)s.Then add a pytest that:
X-Request-IDand asserts the response contains anX-Request-IDheader with a UUID-shaped value.X-Request-IDand asserts the response echoes the same value back.Pointers
BaseHTTPMiddlewaresubclass; we already have one (AuditLogMiddleware) for reference inapi/main.py.contextvars.ContextVarnot threading.local — FastAPI is async.class RequestIdFilter(logging.Filter): def filter(self, record): record.request_id = request_id_var.get("-"); return Trueis enough.Why this is a good first issue
No model code, no DB, no GEE. Touches only the API middleware layer plus one test. ~60 lines including the test.