feat: add SDK telemetry into emissions tracker - #1200
davidberenstein1957 wants to merge 3 commits into
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1200 +/- ##
==========================================
+ Coverage 91.70% 91.77% +0.07%
==========================================
Files 49 54 +5
Lines 5157 5579 +422
==========================================
+ Hits 4729 5120 +391
- Misses 428 459 +31 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@inimaz, before merging, we should still add the hardcoded experiment and project for the codecarbon api. |
Add CLI helper and interactive-flow tests, cover coordinate rounding in ApiClient, and extend collect environment probes to satisfy Codecov patch and project thresholds on PR #1200. Co-authored-by: Cursor <cursoragent@cursor.com>
| .codecarbon.config* | ||
| scripts/agent-vm.personal.config.sh | ||
|
|
||
| # Added by ggshield |
There was a problem hiding this comment.
Maybe this is not needed as discussed
There was a problem hiding this comment.
Dropped, the PR no longer touches .gitignore.
inimaz
left a comment
There was a problem hiding this comment.
Thanks for this PR @davidberenstein1957 . I have left some comments
| raise typer.BadParameter(str(error)) from error | ||
|
|
||
|
|
||
| def resolve_config_path(config: Optional[Path], *, create: bool = False) -> Path: |
There was a problem hiding this comment.
Maybe this is not needed. get_hierarchical_config handles it?
There was a problem hiding this comment.
Kept on purpose: set has to pick one file to write to, while get_hierarchical_config only merges what it reads.
|
|
||
| # logger.info("base tracker init") | ||
| self._external_conf = get_hierarchical_config() | ||
| self._config_file_conf = get_config_file_settings() |
There was a problem hiding this comment.
Not needed
| self._config_file_conf = get_config_file_settings() |
There was a problem hiding this comment.
Gone, _config_file_conf was removed.
| project_token_service: ProjectTokenService = Depends( | ||
| Provide[ServerContainer.project_token_service] | ||
| ), | ||
| x_api_token: str = Header(None), |
There was a problem hiding this comment.
does this work? Should it not be x_api_token: str = Header(None, alias="x-api-token")?
There was a problem hiding this comment.
It works: FastAPI maps x_api_token to the x-api-token header by default, same as the emissions router.
| ), | ||
| x_api_token: str = Header(None), | ||
| ) -> UUID: | ||
| project_token_service.project_token_has_access( |
There was a problem hiding this comment.
you are not doing anything with the response here, it should be if allowed--> do the action n?
There was a problem hiding this comment.
project_token_has_access raises a 403 when access is denied, so the call is the guard. Whether this endpoint should take a token at all is still open (see my PR comment).
| return importlib.util.find_spec(name) is not None | ||
|
|
||
|
|
||
| def _round_coordinate(value: Any) -> float | None: |
There was a problem hiding this comment.
maybe reuse the same function of _round_coordinate ApiClient so that both return the same if None.
There was a problem hiding this comment.
Done in cd8ccf6: it uses _round_or_none now, so unknown coordinates are left out instead of sent as 0,0.
506030a to
e102d91
Compare
Send product telemetry at tracker stop, with the tier resolved from config, environment, or the EmissionsTracker(telemetry_level=...) kwarg. The send runs on a daemon thread so stop() never blocks on the network, and every request of one send shares a single 2s wall-clock budget, so the extensive tier cannot cost more just because it makes two calls. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
e102d91 to
fd7a63b
Compare
Verdict: 🔧 Request changesMaintainer direction on the telemetry policy (@benoit-cty): telemetry should be opt-out by default, and the user should be asked the question explicitly. A suggested implementation:
Must fix:
Housekeeping: Nits:
|
# Conflicts: # codecarbon/cli/main.py # codecarbon/core/config.py
- Revert the default experiment_id: telemetry no longer changes the experiment written to every CSV row or used by save_to_api. - stop() never blocks: the payload is built on the send thread, and cloud fields reuse what the tracker detected instead of probing the metadata endpoints again. - Opt-out by default, and ask: interactive `codecarbon config` / `monitor` ask once for a level and save it in ~/.codecarbon.config; non-interactive runs log an accurate notice once per machine (marker in ~/.codecarbon/). `telemetry status` says whether anything is actually sent. - Unknown coordinates are omitted instead of sent as 0,0; a config key holding None no longer masks the detected value. - Pending sends get an atexit join capped at 1 s; logs from the telemetry thread (ApiClient in extensive mode) drop to DEBUG. - telemetry_level no longer leaks into _conf; Telemetry imported at module level. - docs: list every field sent per tier, the prompt/notice flow and opt-out paths. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Made the changes in cd8ccf6: opt-out with a one-time prompt/notice, stop() non-blocking, experiment_id reverted, null coords, atexit join, docs list every field. Merged master, redid config.py on top of get_hierarchical_config, refreshed the description. |
Description
Adds product telemetry, sent from
EmissionsTracker.stop(), opt-out by default. Telemetry has three levels (disabled,minimaldefault,extensive) resolved from thetelemetry_level=argument /codecarbon monitor --telemetry-level, thentelemetry_levelin.codecarbon.config, thenCODECARBON_TELEMETRY_LEVEL, then the default. It is only sent on astop()of a run lasting at least 1 s, and only when a telemetry API key is configured; there is no built-in key, so nothing is sent by default today.Related Issue
Fixes #1106
Motivation and Context
The project wants visibility into how CodeCarbon is used (environment, hardware, and optionally run metrics/usage hints) to guide development, while keeping the feature opt-out by default, never blocking a run, and disclosing exactly what is sent.
How Has This Been Tested?
uv run task test-packageuv run task test-api-unit/test-api-integfor the server sideScreenshots (if appropriate):
N/A
Details
POST /telemetrywith environment and hardware fields. extensive: adds run metrics and usage hints, plus a public run summary throughApiClient. Every field is listed indocs/how-to/telemetry.md.codecarbon config/codecarbon monitorask once and save the answer to~/.codecarbon.config. Non-interactive runs never prompt; they log a notice once per machine saying what is sent and how to opt out.codecarbon telemetry(wizard),codecarbon telemetry set <level>,codecarbon telemetry status./telemetryroute, schema and tests incarbonserver.Open for maintainers
/telemetryauthentication. A write token shipped in the public package is not a secret. An unauthenticated ingestion endpoint with strict schema validation and rate limiting may fit better.Types of changes
AI Usage Disclosure
Checklist: