feat(fastapi): measurement capability tiers for per-request energy - #1381
Draft
davidberenstein1957 wants to merge 1 commit into
Draft
feat(fastapi): measurement capability tiers for per-request energy#1381davidberenstein1957 wants to merge 1 commit into
davidberenstein1957 wants to merge 1 commit into
Conversation
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>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## feat/add-fastapi-middleware #1381 +/- ##
===============================================================
+ Coverage 92.06% 92.08% +0.02%
===============================================================
Files 53 54 +1
Lines 5530 5700 +170
===============================================================
+ Hits 5091 5249 +158
- Misses 439 451 +12 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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