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
8 changes: 7 additions & 1 deletion codecarbon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
OfflineEmissionsTracker,
track_emissions,
)
from .output import OutputMethod

__all__ = ["EmissionsTracker", "OfflineEmissionsTracker", "track_emissions"]
__all__ = [
"EmissionsTracker",
"OfflineEmissionsTracker",
"OutputMethod",
"track_emissions",
]
__app_name__ = "codecarbon"
420 changes: 260 additions & 160 deletions codecarbon/emissions_tracker.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion codecarbon/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Provides functionality for persistence of data
"""

from codecarbon.output_methods.base_output import BaseOutput # noqa: F401
from codecarbon.output_methods.base_output import BaseOutput, OutputMethod # noqa: F401

# Output to BoAmps format
from codecarbon.output_methods.boamps import BoAmpsOutput # noqa: F401
Expand Down
24 changes: 24 additions & 0 deletions codecarbon/output_methods/base_output.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
from enum import Enum
from typing import List

from codecarbon.output_methods.emissions_data import EmissionsData, TaskEmissionsData


class OutputMethod(str, Enum):
"""
Enum listing the available output methods.

Usage::

tracker = EmissionsTracker(
output_methods=[OutputMethod.CSV, OutputMethod.API]
)

Available values: ``CSV``, ``API``, ``LOGGER``, ``PROMETHEUS``,
``LOGFIRE``, ``BOAMPS``, ``HTTP``.
"""

CSV = "csv"
API = "api"
LOGGER = "logger"
PROMETHEUS = "prometheus"
LOGFIRE = "logfire"
BOAMPS = "boamps"
HTTP = "http"


class BaseOutput:
"""
An abstract class defining possible contracts for an output strategy, a strategy implementation can save emissions
Expand Down
25 changes: 25 additions & 0 deletions docs/reference/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,31 @@ tracker.stop()

The first time it will ask you to log in to Logfire. Once you log in and set the default Logfire project, the metrics will appear following the format `codecarbon_*`.

## BoAmps

[BoAmps](https://github.com/Boavizta/BoAmps) is a standardized JSON format for reporting AI and ML energy consumption.

### How to use it

Run your EmissionsTracker as usual, with `save_to_boamps=True`:

```python-skip
from codecarbon import OfflineEmissionsTracker

tracker = OfflineEmissionsTracker(
Copy link
Copy Markdown
Collaborator

@davidberenstein1957 davidberenstein1957 Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benoit-cty do you think it might be worth refactoring something to save_format or save_to allowing to pass a string for the format as the number of arguments seem to be growing quite large. WDYT?

tracker = OfflineEmissionsTracker(
    save_to="boamps"
)
# or if we really want to enable duplciates
tracker = OfflineEmissionsTracker(
    save_to=["boamps", "logfire"]
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we definitely needs better arguments readability.

I did what you suggest with an Enum:

from codecarbon import EmissionsTracker, OutputMethod
tracker = EmissionsTracker(
    output_methods=[OutputMethod.BOAMPS],
)

project_name="my_project",
country_iso_code="USA",
save_to_boamps=True,
)
tracker.start()
# Your code here
tracker.stop()
```

CodeCarbon writes a final report named `boamps_report_<run_id>.json` in `output_dir`.

If you need to enrich the report with task metadata, datasets, or publisher information, use `BoAmpsOutput` directly through `output_handlers` or start from [examples/boamps_output.py](../../examples/boamps_output.py).

## HTTP Output

The HTTP Output allows calling a webhook with emission data when the tracker is stopped. Use the `emissions_endpoint` parameter to specify your endpoint.
Expand Down
28 changes: 28 additions & 0 deletions examples/boamps_output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import multiprocessing

from codecarbon import EmissionsTracker, OutputMethod


def cpu_load_task(number):
a = 0
for i in range(5):
for i in range(int(1e6)):
a = a + i**number


tracker = EmissionsTracker(
measure_power_secs=10,
force_mode_cpu_load=False,
log_level="debug",
output_methods=[OutputMethod.BOAMPS],
)
try:
tracker.start()
with multiprocessing.Pool() as pool:
# call the function for each item in parallel
pool.map(cpu_load_task, [i for i in range(100)])
finally:
emissions = tracker.stop()

print(f"Emissions: {emissions} kg")
print(f"BoAmps report written to ./boamps_report_{tracker.run_id}.json")
52 changes: 52 additions & 0 deletions tests/test_emissions_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
track_emissions,
)
from codecarbon.external.geography import CloudMetadata
from codecarbon.output import BoAmpsOutput, OutputMethod
from tests.fake_modules import pynvml as fake_pynvml
from tests.testdata import (
GEO_METADATA_CANADA,
Expand Down Expand Up @@ -212,6 +213,28 @@ def raise_exception(*args, **kwargs):
tracker._measure_power = raise_exception
tracker.stop()

def test_output_methods_boamps_adds_boamps_output_handler(
self,
mock_cli_setup,
mock_log_values,
mocked_get_gpu_details,
mocked_env_cloud_details,
mocked_is_gpu_details_available,
mocked_is_nvidia_system,
):
tracker = EmissionsTracker(
output_dir=self.temp_path,
output_handlers=[],
output_methods=[OutputMethod.BOAMPS],
)

self.assertTrue(
any(
isinstance(handler, BoAmpsOutput)
for handler in tracker._output_handlers
)
)

@responses.activate
def test_decorator_ONLINE_NO_ARGS(
self,
Expand Down Expand Up @@ -268,6 +291,35 @@ def dummy_train_model():
# THEN
self.verify_output_file(self.emissions_file_path, 2)

def test_decorator_online_passes_output_methods(
self,
mock_cli_setup,
mock_log_values,
mocked_get_gpu_details,
mocked_env_cloud_details,
mocked_is_gpu_details_available,
mocked_is_nvidia_system,
):
mocked_tracker = mock.Mock()

with mock.patch(
"codecarbon.emissions_tracker.EmissionsTracker",
return_value=mocked_tracker,
) as mocked_tracker_cls:

@track_emissions(output_methods=[OutputMethod.BOAMPS])
def dummy_train_model():
return 42

self.assertEqual(dummy_train_model(), 42)

self.assertEqual(
mocked_tracker_cls.call_args.kwargs["output_methods"],
[OutputMethod.BOAMPS],
)
mocked_tracker.start.assert_called_once()
mocked_tracker.stop.assert_called_once()

def test_decorator_OFFLINE_NO_COUNTRY(
self,
mock_cli_setup,
Expand Down
Loading