Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion codecarbon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@
OfflineEmissionsTracker,
track_emissions,
)
from .core.telemetry import (
TelemetryConfig,
TelemetryTier,
init_telemetry,
set_telemetry,
)

__all__ = ["EmissionsTracker", "OfflineEmissionsTracker", "track_emissions"]
__all__ = [
"EmissionsTracker",
"OfflineEmissionsTracker",
"track_emissions",
"TelemetryConfig",
"TelemetryTier",
"init_telemetry",
"set_telemetry",
]
__app_name__ = "codecarbon"
78 changes: 78 additions & 0 deletions codecarbon/cli/cli_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,81 @@ def create_new_config_file():
f.write("[codecarbon]\n")
typer.echo(f"Config file created at {file_path}")
return file_path


def save_telemetry_config_to_file(
tier: str = None,
project_token: str = None,
api_endpoint: str = None,
path: Path = None
) -> None:
"""
Save telemetry configuration as JSON in the existing config file.

Args:
tier: Telemetry tier (off, internal, public)
project_token: Project token for Tier 2
api_endpoint: API endpoint for telemetry
path: Path to config file (defaults to ~/.codecarbon.config)
"""
import json

p = path or Path.home() / ".codecarbon.config"

# Read existing config or create new
config = configparser.ConfigParser()
if p.exists():
config.read(str(p))

if "codecarbon" not in config.sections():
config.add_section("codecarbon")

# Build JSON config for telemetry
telemetry_config = {}
if tier:
telemetry_config["telemetry_tier"] = tier
if project_token:
telemetry_config["telemetry_project_token"] = project_token
if api_endpoint:
telemetry_config["telemetry_api_endpoint"] = api_endpoint

# Save as JSON string
if telemetry_config:
config["codecarbon"]["telemetry"] = json.dumps(telemetry_config)

with p.open("w") as f:
config.write(f)
logger.info(f"Telemetry config saved to {p}")


def load_telemetry_config_from_file(path: Path = None) -> dict:
"""
Load telemetry configuration from the existing config file.

Args:
path: Path to config file (defaults to ~/.codecarbon.config)

Returns:
Dictionary with telemetry configuration
"""
import json

p = path or Path.home() / ".codecarbon.config"

if not p.exists():
return {}

config = configparser.ConfigParser()
config.read(str(p))

if "codecarbon" not in config.sections():
return {}

telemetry_str = config["codecarbon"].get("telemetry")
if telemetry_str:
try:
return json.loads(telemetry_str)
except json.JSONDecodeError:
return {}

return {}
95 changes: 95 additions & 0 deletions codecarbon/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,5 +436,100 @@ def questionary_prompt(prompt, list_options, default):
return value


@codecarbon.command("telemetry", short_help="Configure CodeCarbon telemetry")
def telemetry():
"""
Manage CodeCarbon telemetry settings.

Use subcommands to configure or view telemetry settings:
- codecarbon telemetry setup : Interactive setup wizard
- codecarbon telemetry config : Show current configuration
- codecarbon telemetry enable : Enable telemetry
"""
print("CodeCarbon Telemetry")
print("Use --help for more information on subcommands.")


@telemetry.command("setup", short_help="Interactive telemetry setup wizard")
def telemetry_setup():
"""
Interactive wizard to configure CodeCarbon telemetry.
"""
from codecarbon.core.telemetry.config import (
TelemetryTier,
get_telemetry_config,
set_telemetry_tier,
)
from codecarbon.core.telemetry.config import (
TELEMETRY_PROJECT_TOKEN_ENV_VAR,
TELEMETRY_API_ENDPOINT_ENV_VAR,
TELEMETRY_ENV_VAR,
)

print("\n=== CodeCarbon Telemetry Setup ===\n")

# Show current config
config = get_telemetry_config()
print(f"Current tier: {config.tier.value}")
print(f"Current project token: {'set' if config.project_token else 'not set'}")
print(f"Current API endpoint: {config.api_endpoint or 'default'}")

# Ask for tier
print("\nChoose telemetry tier:")
tier_choice = questionary.select(
"Telemetry tier:",
["off", "internal", "public"],
default=config.tier.value,
).ask()

# Save tier preference
set_telemetry_tier(TelemetryTier(tier_choice), dont_ask_again=True)
print(f"\nTelemetry tier set to: {tier_choice}")

# Ask for project token if public tier
if tier_choice == "public":
project_token = typer.prompt(
f"Project token (from {TELEMETRY_PROJECT_TOKEN_ENV_VAR} env var)",
default=config.project_token or "",
)
if project_token:
print(f"\nTo enable Tier 2 (public) telemetry, set:")
print(f" export {TELEMETRY_PROJECT_TOKEN_ENV_VAR}={project_token}")

# Ask for API endpoint
api_endpoint = typer.prompt(
f"API endpoint (default: https://api.codecarbon.io)",
default=config.api_endpoint or "https://api.codecarbon.io",
)
if api_endpoint and api_endpoint != "https://api.codecarbon.io":
print(f"\nTo use custom API endpoint, set:")
print(f" export {TELEMETRY_API_ENDPOINT_ENV_VAR}={api_endpoint}")

print("\n=== Setup Complete ===")
print("\nEnvironment variables to configure:")
print(f" export {TELEMETRY_ENV_VAR}={tier_choice}")
if tier_choice == "public" and project_token:
print(f" export {TELEMETRY_PROJECT_TOKEN_ENV_VAR}=<your_token>")
print(f" export {TELEMETRY_API_ENDPOINT_ENV_VAR}={api_endpoint}")


@telemetry.command("config", short_help="Show current telemetry configuration")
def telemetry_config():
"""
Display current telemetry configuration.
"""
from codecarbon.core.telemetry.config import get_telemetry_config

config = get_telemetry_config()

print("\n=== Current Telemetry Configuration ===\n")
print(f"Tier: {config.tier.value}")
print(f"Enabled: {config.is_enabled}")
print(f"Project Token: {'configured' if config.project_token else 'not configured'}")
print(f"API Endpoint: {config.api_endpoint or 'default (https://api.codecarbon.io)'}")
print(f"First Run: {config.first_run}")
print(f"Has Consent: {config.has_consent}")


if __name__ == "__main__":
main()
29 changes: 29 additions & 0 deletions codecarbon/core/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,35 @@ def close_experiment(self):
Tell the API that the experiment has ended.
"""

def add_telemetry(self, telemetry_data: dict, api_key: str = None) -> bool:
"""
Send telemetry data to the /telemetry endpoint (Tier 1).

Args:
telemetry_data: Dictionary containing telemetry payload
api_key: Optional API key for authentication

Returns:
True if successful, False otherwise
"""
try:
url = self.url + "/telemetry"
headers = self._get_headers()

# Use provided api_key or fall back to instance api_key
if api_key:
headers["x-api-token"] = api_key

r = requests.post(url=url, json=telemetry_data, timeout=5, headers=headers)
if r.status_code not in (200, 201):
self._log_error(url, telemetry_data, r)
return False
logger.debug(f"Telemetry data sent successfully to {url}")
return True
except Exception as e:
logger.error(f"Failed to send telemetry data: {e}")
return False


class simple_utc(tzinfo):
def tzname(self, **kwargs):
Expand Down
Loading
Loading