Metadata-Version: 2.5
Name: artitrack
Version: 2026.9.1
Summary: ML experiment tracking: xmanager-compatible launcher API, task-spooler integration, MLMD persistence, CLI, and MCP server
Project-URL: Repository, https://github.com/artefactory/argimi
Author-email: chicham <hicham.randrianarivo@artefact.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: <3.13,>=3.10
Requires-Dist: absl-py>=1.4.0
Requires-Dist: attrs>=23.1.0
Requires-Dist: beartype>=0.22.9
Requires-Dist: click>=8.0.0
Requires-Dist: etils[epath]>=1.10.0
Requires-Dist: gitpython>=3.1.45
Requires-Dist: mcp[cli]>=1.25.0
Requires-Dist: ml-metadata<1.17,>=1.16.0; sys_platform == 'linux'
Requires-Dist: orjson>=3.11.7
Requires-Dist: paramiko>=4.0.0
Requires-Dist: polars
Requires-Dist: pyyaml
Requires-Dist: toolz>=1.1.0
Requires-Dist: xmanager>=0.7.1
Provides-Extra: tensorboard
Requires-Dist: tensorboard; extra == 'tensorboard'
Description-Content-Type: text/markdown

# Artitrack

Artitrack is a local-first experiment tracker and launcher built on
xmanager, task-spooler, and ML Metadata. It provides a Python API for
defining experiments and a CLI for querying and managing runs, executions,
artifacts, and metrics. CLI output is plain text and fzf-friendly.

## Core Concepts

- Experiment: A top-level grouping of related pipeline runs.
- Run: A tracked execution of a pipeline within an experiment.
- Execution (job): A single step submitted to task-spooler.
- Artifact: Inputs/outputs tied to executions (datasets, models, reports).
- Metric: Values extracted from job logs or registered manually.

## Requirements

- Python 3.10 or 3.11
- task-spooler `q` command on PATH
- ml-metadata for local tracking (Linux only)

## Data Directory

Artitrack stores metadata in `~/.at` by default. Override with
`ARTITRACK_DATA_DIR`.

## Python API (xmanager style)

Artitrack's launcher subclasses `xm.Experiment`/`xm.WorkUnit` — it is genuinely
xmanager-compatible, not just vocabulary-compatible. Two surfaces are available,
trading portability for artitrack's DAG/dedup features:

**Portable subset** — `xm.Job`/`JobGroup`, `args`, `role`, `identity`, `exp.add()`,
`wait_until_complete()` follow real xmanager's own contract, so a script written to
this shape ports to real xmanager by swapping the import for xmanager's own
`xm_local` (or another real backend) and its executor — no other code changes.
`xm_local.TaskSpooler()` itself, and the `experiment_title=` kwarg spelling, are
artitrack-specific and have no real-xmanager equivalent to swap in verbatim:

```python
from xmanager import xm

from artitrack.launcher import xm_local


def main() -> None:
    with xm_local.create_experiment(experiment_title="quick_test") as exp:
        exp.add(
            xm.Job(
                name="train",
                executable=xm.Binary(path="python train.py"),
                executor=xm_local.TaskSpooler(),
                args={"learning_rate": 0.001},
            )
        )
```

**Artitrack-native surface** — adds dependency ordering and artifact-based
dedup, at the cost of portability to real xmanager:

```python
from xmanager import xm

from artitrack.launcher import xm_local


def main() -> None:
    with xm_local.experiment(name="quick_test") as exp:
        with exp.run(title="train_then_eval") as run:
            train_handle = run.add(
                xm.Job(
                    name="train",
                    executable=xm.Binary(path="python train.py"),
                    executor=xm_local.TaskSpooler(),
                    args={"learning_rate": 0.001},
                ),
                output_artifacts={"model": {"path": "/data/model.pt", "type": "Model"}},
            )
            run.add(
                xm.Job(
                    name="eval",
                    executable=xm.Binary(path="python eval.py"),
                    executor=xm_local.TaskSpooler(),
                ),
                depends_on=train_handle,
                input_artifacts={"model": {"path": "/data/model.pt", "type": "Model"}},
            )
```

`depends_on`/`input_artifacts`/`output_artifacts`/`force`/`at_resume`/`est_time`
are keyword-only extras on `run.add()` — they are not part of `exp.add()`'s
inherited xmanager signature, so mixing surfaces per-job is a `TypeError`, not a
silent no-op.

Examples live in `examples/`.

Quick start:

```sh
python examples/quick_pipeline.py
```

A sync-sentinel job is submitted automatically after all pipeline jobs
to record terminal state in MLMD when the experiment context exits.

## CLI

The CLI reads from the local metadata store and provides management tools.

### Quick Commands

```sh
# What's running right now?
at status

# Show recent runs
at runs
at runs --failed
at runs -e my-experiment -n 5

# Show a specific run
at run <run-id>

# Most recent run
at run last

# Why did it fail?  Grab the job log
at runs --failed
at log <job-id>

# Jobs and artifacts
at jobs
at job <job-id>
at query --entity artifact
```

### Management

- `at info` shows database stats.
- `at sync` refreshes run/job status from task-spooler.
- `at cancel <run-id>...` cancels one or more runs.
- `at delete` cleans runs from the store.
- `at update` edits runs or executions.
- `at metrics --run <run-id>` shows metrics for a run.

### Artifact Provenance

Find all runs that produced or consumed a given artifact:

```sh
# Which runs produced this model?
at query --entity run --output "models/model.pt"

# Which runs used this dataset as input?
at query --entity run --input "data/train.csv"

# Combine input and output filters
at query --entity run --input "data/train.csv" --output "models/model.pt"
```

### Run Commands and Git Info

Inspect the exact commands, repository, and commit for each job in a run:

```sh
# Show full run detail (jobs, commands, artifacts, metrics)
at query --entity run <run-id>

# Show a single job's command, git repo, and commit
at query --entity execution <ts-job-id>
```

The job detail view includes:

- **command**: the full shell command used to launch the job
- **git_repo**: the remote URL of the repository (for python/uv scripts)
- **git_commit**: the exact commit SHA at submission time
- **git_dirty**: whether the repo had uncommitted changes

### Dirty Repo Guard

When submitting from a git repository with uncommitted changes, artitrack
prompts for confirmation. You can either auto-commit (recommended) or
skip the check:

```sh
# Default: prompts to auto-commit if dirty
at submit -n train -- python train.py

# Skip the check entirely
at submit -n train --allow-dirty -- python train.py
```

The auto-commit creates a `chore: artitrack auto-commit <timestamp>` commit
with all modified tracked files, so the recorded `git_commit` always points
to reproducible code. The same guard applies to the Python launcher API —
it is controlled by the same `--at_allow_dirty` CLI flag (not a Python
kwarg), off by default, read once when `experiment()`/`create_experiment()`
runs.

### Advanced Queries

The `query` command supports full filtering, ordering, and lineage queries:

```sh
# Filter by status
at query --entity run --status failed

# Filter by artifact lineage
at query --entity run --input "data/train.csv"
at query --entity run --output "models/model.pt"

# List jobs or artifacts
at query --entity execution
at query --entity artifact

# fzf-friendly output
at query --entity run --format fzf
```

Run `at query --help` for the full filter syntax.

### Update Cheatsheet

```sh
# Change run status or tags
at update run <run-id> --status cancelled
at update run <run-id> --tag baseline --tag sweep
at update run <run-id> --remove-tag baseline

# Add artifacts or metrics to a job
at update execution <ts-job-id> --artifact /data/model.pt:model
at update execution <ts-job-id> --metric accuracy=0.95
```

### Shell Completion

The `completion` command prints a shell snippet you can source:

```sh
# Print the snippet for the current shell
at completion
```

Use `--shell fish` to target a specific shell.

To print the fzf alias snippet:

```sh
at completion fzf-aliases --shell fish
```

## fzf Workflow

Artitrack provides tab-delimited output that works well with fzf. Use the
`--format fzf` flag on `query` to get one-line rows without headers.

```sh
# Pick a run and preview details
at query --entity run --format fzf \
  | fzf --delimiter='\t' --with-nth=2,3,4 \
    --preview 'at query --entity run {1}'

# Pick a job and preview details
at query --entity execution --format fzf \
  | fzf --delimiter='\t' --with-nth=1,3,4 \
    --preview 'at query --entity execution {1}'
```

The fzf alias snippet (fish only) defines these functions:

- `at-runs` — browse runs, sorted finished-first
- `at-jobs RUN_ID` — browse jobs/steps for a run; Enter opens the log, ctrl-i shows job detail
- `at-tb` — pick metrics logdirs and print a `tensorboard --logdir_spec` string
- `at-trace PATH` — trace a file path to the run that produced or consumed it
- `at-watch [RUN_ID | QUERY]` — live-monitor a running pipeline, refreshing every 5s

## MCP Server

Use the MCP server to expose Artitrack data to LLM clients:

```sh
at-mcp
```

The server uses the same local metadata store as the CLI. CLI output is plain
text (or fzf tab-delimited), while MCP tools return JSON. For MCP list calls,
some large fields are omitted (for example `config` and
`resolved_pipeline_path`), so use `get_run` for full details.

### Remote Access via SSH Tunnel

ml-metadata only runs on Linux. To query experiments on obelix from a Mac,
tunnel the MCP server over SSH:

```sh
at mcp-tunnel obelix
```

This starts `at-mcp` on obelix (if not already running), opens an SSH
port forward on localhost:8000, and prints the config snippet for
`~/.claude/mcp_servers.json`:

```json
{
  "artitrack-remote": {
    "type": "streamable-http",
    "url": "http://localhost:8000/mcp"
  }
}
```

Custom ports and remote working directory:

```sh
at mcp-tunnel obelix --port 9000 --remote-port 8001
at mcp-tunnel obelix:/home/hicham/project
```

Press Ctrl+C to stop the tunnel.
