feat(fastapi): measurement capability tiers for per-request energy - #1381
Merged
davidberenstein1957 merged 8 commits intoAug 19, 2026
Merged
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## feat/add-fastapi-middleware #1381 +/- ##
===============================================================
+ Coverage 92.01% 92.06% +0.04%
===============================================================
Files 53 54 +1
Lines 5451 5598 +147
===============================================================
+ Hits 5016 5154 +138
- Misses 435 444 +9 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Report per-request energy only when the detected backend can resolve a request; otherwise report unavailable (never 0.0) and expose per-endpoint totals instead. - MEASURED: Linux RAPL (1 ms counter, 15.3 uJ quantum); NVML only >= 1 s - ESTIMATED: constant/TDP + RAM (analytic in duration) - AGGREGATE_ONLY: cpu_load, powermetrics, amdsmi/EMI (unprobed) Tier is part of the output (RequestMeasurement on request.state.codecarbon, middleware.measurement_tier, X-CodeCarbon-Tier when headers are opted in), not a log line. Detection is duck-typed so tests inject known hardware. Also: bound tracker._tasks via discard_task() after persistence (one Task per request leaked for the process lifetime), and fix the negative per-request duration in finish_http_request. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The middleware base replaced _TrackerRunner with a ThreadPoolExecutor and now resolves the task label inside finish_http_request, so the tier layer folds into that flow instead of carrying its own runner and stop_task fallback. The duplicated finish_http_request duration fix and discard_task are already in the base and are dropped here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
detect_measurement_tier was always called without a window, so it defaulted to 0.0 and every NVML machine fell to AGGREGATE_ONLY with no per-request emissions at all. Pass the tracker's measure_power_secs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n "RAM" The voting filter tested the repr string, so renaming the RAM class would have silently made it vote and dragged every detection down a tier. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The tracker scales _total_energy by PUE, so the P x hours estimate has to scale too or endpoint_totals() stops summing to the run total on any pue > 1 configuration. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nsity Three defaulted getattrs and a try/except for exceptions a float division of two floats cannot raise, replaced by one guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ier entries available/emissions/energy_consumed each restated "emissions_data is None", and three CPU_MODE_TIERS entries mapped to the value .get() already returns as its default. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
davidberenstein1957
force-pushed
the
feat/fastapi-measurement-tiers
branch
from
August 19, 2026 09:50
c94fe6c to
7e363ba
Compare
davidberenstein1957
merged commit Aug 19, 2026
3fa148f
into
feat/add-fastapi-middleware
7 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Targets
feat/add-fastapi-middleware(PR #1203), not master.Problem
The middleware reported a per-request energy figure regardless of whether the
backend could resolve a request. Measured: 27 of 30 short requests reported
exactly
0.0. Zero is a factual claim that the request was free.Tiers
Resolved at startup from the hardware the tracker actually detected
(
tracker._hardware), weakest component wins (RAM is analytic and does not vote).measuredestimatedaggregate_onlycpu_load, macOS powermetrics, amdsmi, Windows EMI, intel_power_gadgetendpoint_totals()onlyMeasurements behind this:
os.pread). Genuinely resolves a 30 ms request.get_details()spawnssudo powermetrics -n 10 -i 100, blocks ~1 s per read. Cannot be in a request path.power = tdp * 0.5, so energy is exact at any resolution but only restates wall time.psutil.cpu_percent(interval=None)is tick-quantized; 98% of 5 ms reads return zero load, and0.1 + 0.9*(load/100)**3turns that into a 10%-of-TDP floor. Verified: 30 requests on a core pegged at 100% all reported exactly 0.0900 J.API
MeasurementTierenum,detect_measurement_tier(hardware, window_seconds)->TierDetection(tier, components).RequestMeasurementonrequest.state.codecarbon:tier,available,emissions,energy_consumed,unavailable_reason,emissions_data(Nonewhen not reportable).on_request_completekeeps its signature and receivesNoneforemissions_datawhen the tier cannot report.middleware.measurement_tier,middleware.tier_detection,middleware.endpoint_totals()(valid in every tier).X-CodeCarbon-Tieralongside the number, and the number readsunavailablerather than0.docs/how-to/fastapi.md.Unavailable, not zero
A request that spans no completed sampling window yields
available=False/unavailable_reason="request spanned no completed sampling window".In the ESTIMATED tier a zero sampled delta is instead filled analytically
(
P x elapsedwith the tracker's implied kg/kWh); if intensity is not yet knownit reports unavailable rather than 0.
Also in here
_tasks:EmissionsTracker.discard_task()evicts a finished task; the middleware calls it afterpersist_completed_task. Test drives 10,000 request cycles through the real tracker methods and asserts the map stays at 0.finish_http_requestsetdurationto the request duration and then subtracted the baseline duration, producing negative per-request durations (observed -29 s end to end). Now passes absolute elapsed so the delta is the request duration andemissions_rateis computed against it.Test evidence
tests/integrations/test_fastapi_tiers.py(new, 24 tests): every test injects known hardware stubs and knownEmissionsDatavalues — nothing depends on the test machine (see PR feat(cli): live local dashboard for monitor #1365: a< 90 Wassertion that passed on Apple Silicon and failed on Linux CI at 280 W).tests/integrations/test_fastapi_middleware.py: mock trackers now inject_hardware=[intel_rapl]explicitly instead of inheriting whatever the runner has.uv run pytest tests/ -q --ignore=tests/test_viz_data.py: 709 passed, 21 skipped.uv run pre-commit run --all-files: all hooks pass.examples/fastapi_middleware.pyrun end to end on Apple Silicon (aggregate_only):X-CodeCarbon-Tier: aggregate_only,X-CodeCarbon-Emissions-kg: unavailable/totals->{"tier":"aggregate_only","endpoints":{"GET /predict":{"count":4,"duration":3.05,"energy_consumed":3.69e-05,"emissions":6.46e-06}}}Deliberately deferred
RequestMeasurementandEndpointTotalsare per-endpoint aggregates, so per-window attribution can be added under_build_measurement/_record_totalswithout reshaping the API.detect_measurement_tiertakeswindow_secondsand returns MEASURED for NVML at >= 1 s, but the middleware only ever calls it with a per-request window (0). Wiring a real aggregation window is a follow-up.EmissionsData/ CSV / API schema — kept out of the shared output schema; the tier lives in the FastAPI-side result object.🤖 Generated with Claude Code