Metadata-Version: 2.4
Name: pulseon
Version: 0.1.0a5
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Scientific/Engineering
Classifier: Typing :: Typed
License-File: LICENSE
Summary: Local-first training metrics tracker backed by Rust, PyO3, DuckDB, and DuckLake.
Author: PulseOn contributors
License-Expression: MIT
Requires-Python: >=3.11
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Documentation, https://github.com/reformovo/pulseon#readme
Project-URL: Homepage, https://github.com/reformovo/pulseon
Project-URL: Issues, https://github.com/reformovo/pulseon/issues
Project-URL: Repository, https://github.com/reformovo/pulseon

# PulseOn

PulseOn is a local-first training metrics tracker backed by Rust, PyO3, DuckDB,
and DuckLake.

Upcoming alpha: 0.1.0a5 / v5 headless read surface:

- discover projects, runs, metrics, and persisted metric points
- query running runs after reports have reached persistent storage
- return Python objects or Arrow PyCapsule-compatible tables
- inspect existing stores through a dependency-free, read-only CLI
- use DuckDB or SQLite catalogs with local or S3-compatible Parquet data
- keep the Parquet schema as the long-term compatibility boundary

Quickstart:

```python
import pulseon

client = pulseon.init()
project = client.create_project("local training")
run = client.create_run(project.project_id, "baseline")
run.log("train/loss", 0, 0.25)
client.finish_run(run.run_id)

projects = client.list_projects()
runs = client.list_runs(project.project_id, status="finished", limit=20)
metrics = client.list_metrics(run.run_id)
points = client.query_metric(
    run.run_id,
    "train/loss",
    start_step=0,
    end_step=100,  # Exclusive: the query range is [0, 100).
)
table = client.query_metric_table(run.run_id, "train/loss")
client.shutdown()
```

`ArrowTable` does not require PyArrow, pandas, or Polars. Consumers that support
the Arrow PyCapsule protocol can import it through `__arrow_c_stream__`.

The `pulseon` command opens an existing store and never creates a missing one:

```console
pulseon --path runs projects list
pulseon --path runs runs list <project-id> --status finished --limit 20
pulseon --path runs metrics list <run-id>
pulseon --path runs --format json metrics query <run-id> train/loss --all
```

By default, PulseOn stores local state under `./.pulseon`. Pass an explicit
root path when a project should use a different local store:

```python
client = pulseon.init("runs")
```

The existing storage keywords remain available: `data_path`,
`catalog_backend`, `catalog_path`, and `metric_queue_capacity`. `catalog_path`
must be a local filesystem path. `data_path` may be local, or it may use an
S3-compatible URI such as `s3://bucket/prefix`.

Project-local storage settings can live in `./.pulseon/config.toml`. Relative
`data_path` and `catalog_path` values in this file are resolved from the project
root passed to `pulseon.init(...)` or `pulseon --path`:

```toml
data_path = "s3://example-bucket/pulseon/demo"

[s3]
endpoint = "https://s3.example.com"
region = "us-east-1"
access_key_id = "<access-key-id>"
secret_access_key = "<secret-access-key>"
path_style = true
use_ssl = true
```

Do not commit real S3 credentials. Explicit `pulseon.init(...)` keyword
arguments override values from `config.toml`:

```python
client = pulseon.init(
    data_path="s3://example-bucket/pulseon/demo",
    s3_endpoint="https://s3.example.com",
    s3_access_key_id="<access-key-id>",
    s3_secret_access_key="<secret-access-key>",
    s3_path_style=True,
)
```

For bounded teardown, stop active logging threads before calling
`client.shutdown(timeout=...)`; PulseOn keeps admission open while bounded
shutdown is draining, so concurrent `run.log(...)` calls can prevent that drain
from completing before the timeout.

Architecture entry points:

- [Docs index](docs/README.md)
- [Native storage boundary](docs/native-storage-boundary.md)
- [Glossary](docs/glossary.md)
- [Roadmap](docs/ROADMAP.md)
- [ADRs](docs/adr/)

Runtime extensions:

- DuckLake is installed and loaded by the native engine because it is required
  for native storage.
- DuckDB LTTB is optional and is not bundled into PulseOn wheels. Downsampling
  first uses an already installed `lttb` extension. Set
  `PULSEON_LTTB_AUTO_INSTALL=1` to let the first downsampled query run the
  official `INSTALL lttb FROM community; LOAD lttb;` flow and cache the
  extension in DuckDB's user extension directory.
- PulseOn embeds DuckDB 1.5.4. The release gate verifies the LTTB 0.1.0
  community build for DuckDB 1.5.4. DuckDB extension binaries are specific to
  a DuckDB version and platform; for offline deployment, set
  `PULSEON_LTTB_EXTENSION_PATH=/path/to/lttb.duckdb_extension` to a compatible,
  signed binary rather than reusing one built for another DuckDB version.

See DuckDB's [LTTB extension page][lttb] and [extension installation
guide][duckdb-extension-install] for the upstream commands and compatibility
rules.

[lttb]: https://duckdb.org/community_extensions/extensions/lttb.html
[duckdb-extension-install]: https://duckdb.org/docs/current/extensions/installing_extensions.html

