Skip to content

Commit ce8d9cf

Browse files
committed
Remove CLI and Tracer app integrations
1 parent 093792a commit ce8d9cf

33 files changed

Lines changed: 356 additions & 4002 deletions

‎.github/workflows/ci.yml‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,19 @@ jobs:
2828
2929
- name: Run tests
3030
run: pytest tests/ -v --tb=short
31+
32+
watch-js:
33+
runs-on: ubuntu-latest
34+
defaults:
35+
run:
36+
working-directory: js
37+
steps:
38+
- uses: actions/checkout@v4
39+
- uses: actions/setup-node@v4
40+
with:
41+
node-version: "20"
42+
cache: npm
43+
cache-dependency-path: js/package-lock.json
44+
- run: npm ci
45+
- run: npm test -- --maxWorkers=1 --minWorkers=1
46+
- run: npm run build

‎AGENTS.md‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ router = tracer.load_router(".tracer", embedder=my_embedder)
154154
```
155155

156156
### Pattern 4: HTTP server (zero deps)
157-
```bash
158-
tracer serve .tracer --port 8000
157+
```python
158+
tracer.serve(".tracer", port=8000)
159159
# POST /predict {"embedding": [...]} → {"label", "decision", "accept_score"}
160160
```
161161

@@ -173,7 +173,7 @@ tracer serve .tracer --port 8000
173173
## Things you CAN do autonomously
174174

175175
- Install `tracer-llm` (it's lightweight, MIT licensed)
176-
- Run `tracer demo` to show the user what it does
176+
- Follow `notebooks/01-quickstart.ipynb` for a worked example
177177
- Write traces to JSONL from any data source
178178
- Fit a policy if traces + embeddings are available
179179
- Generate the HTML report
@@ -195,7 +195,6 @@ tracer serve .tracer --port 8000
195195
| `Embedder.from_endpoint(url)` | External embedding API | Needs URL + auth from human |
196196
| `tracer.generate_html_report(dir)` | Visual audit | No |
197197
| `tracer.serve(dir, port=8000)` | HTTP server | No |
198-
| `tracer demo` | Interactive demo | No |
199198

200199
---
201200

‎CHANGELOG.md‎

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
All notable changes to TRACER are recorded here. This project follows semantic
44
versioning.
55

6+
## Unreleased
7+
8+
### Removed (breaking)
9+
- The `tracer` command-line executable and command implementations. Use the
10+
Python APIs for fitting, scanning, updating, reports, and serving predictions.
11+
- The Tracer app client, authentication, and automatic app telemetry exporters
12+
in both Python and JavaScript. Existing app credentials no longer trigger
13+
uploads. Local recording, custom sinks, and explicitly configured generic
14+
HTTP exports remain available.
15+
- App connection instructions and command-line documentation. All current
16+
examples use the library APIs.
17+
618
## 0.3.3 (2026-06)
719

820
### Fixed
@@ -20,48 +32,27 @@ versioning.
2032
`ValueError` messages. Previously the count excluded blank lines, so
2133
the reported line was lower than the actual position of the bad record.
2234

23-
## 0.3.1 (2026-06)
24-
25-
### Changed
26-
- `tracer cloud` commands that take a tracer (`get`, `training`, `analytics`,
27-
`label-space`, `traces`, ...) now accept either the tracer slug or its id.
28-
- `tracer cloud scan` works without logging in, matching the public web scan.
29-
30-
### Fixed
31-
- `tracer cloud keys create` and `tracer cloud ingest-keys create` now honor
32-
`--json` and emit the created record (including the one-time secret) as JSON.
33-
3435
## 0.3.0 (2026-06)
3536

3637
### Added
37-
- `tracer.watch`: a decorator (and HTTP fallback) that observes the LLM calls
38-
your pipeline already makes and ships them, free, to Tracer Cloud, where they
39-
accumulate and auto-optimize once there are enough. Captures the full request
40-
and response on the OpenTelemetry GenAI (`gen_ai.*`) schema, with pluggable
41-
sinks (local file, Tracer Cloud, OTLP, or several at once).
38+
- `tracer.watch`: a decorator that records LLM calls on the OpenTelemetry
39+
GenAI (`gen_ai.*`) schema, with pluggable local and generic export sinks.
4240
- `@tracer-llm/watch`: a zero-dependency JavaScript/TypeScript mirror of the watch
4341
decorator (async-context aware), so JS pipelines get the same one-line
4442
observability as Python.
45-
- `tracer cloud`: a full command-line interface for Tracer Cloud, at parity with
46-
the dashboard. Browser or password login, then manage tracers (create, quick
47-
and bulk uploads, agentic `onboard`, rename, delete), training (retrain,
48-
auto-retrain, promote/rollback, live status), routing (`route`), test
49-
batteries, the model library, API keys, observability ingest keys, billing,
50-
analytics, trace selection (`traces`), and a public `scan`.
5143
- Lazy package initialization so `import tracer` stays fast and only pulls in the
5244
heavy pieces when you actually use them.
5345

5446
### Changed
55-
- `tracer scan` is more robust on messy uploads: tolerant input/label aliasing
56-
and a clarification path so ambiguous files still produce a result.
47+
- Trace ingestion accepts additional input and label aliases.
5748

5849
### Docs
59-
- New guides for the watch decorator and the `tracer cloud` CLI.
50+
- New guide for the watch decorator.
6051

6152
## 0.2.0 (2026-06)
6253

6354
### Added
64-
- `tracer scan`: a fast, conservative day-one read of a traces file, before any
55+
- `tracer.scan()`: a fast, conservative day-one read of a traces file, before any
6556
training. It groups traffic by similarity and measures, on a held-out slice it
6657
never saw, how much a near-free model can answer at your target agreement,
6758
using exact Clopper-Pearson bounds, with an optional per-1k and monthly savings
@@ -72,24 +63,20 @@ versioning.
7263
far from the training distribution (kNN distance, global and per-predicted-label
7364
thresholds) regardless of surrogate confidence, so off-distribution traffic goes
7465
to the teacher instead of getting a confident guess.
75-
- `tracer fit --trees` to opt in the tree surrogates, and `--skip` to drop named
76-
candidates from the zoo.
66+
- `FitConfig.skip_candidates` to drop named candidates from the zoo, including
67+
tree surrogates when a lighter sweep is desired.
7768
- Trace loaders accept common key aliases for both input and label
7869
(`input/query/text/prompt/question` and
7970
`teacher/teacher_output/label/intent/output/answer`).
80-
- Bring-your-own embeddings for `tracer scan`: local sentence-transformers by
81-
default (`--embed-model`), a precomputed `.npy` (`--embeddings`), or your own
82-
HTTP embedding endpoint (`--embed-url`, with header and response-key options).
71+
- Bring-your-own embeddings for `tracer.scan()`: pass a precomputed array with
72+
`embeddings=`, or select a local sentence-transformers model with `model=`.
8373

8474
### Changed
8575
- The parity gate now certifies on an exact held-out lower bound instead of an
8676
in-sample point estimate, so a policy cannot clear the target by in-sample luck
8777
and then break the contract on real traffic. Coverage is now monotonic in the
8878
target, and a hybrid select-then-verify procedure recovers coverage at strict
8979
targets that a plain held-out split discarded.
90-
- Tree surrogates (decision tree, random forest, extra-trees, gradient boosting)
91-
are now off by default in `tracer fit`; the default zoo is the fast linear and
92-
MLP heads. Use `--trees` for hard, high-class-count tasks.
9380
- The HTML report is restyled to the light Tracer theme, and the word "audit" is
9481
dropped across the report and docs.
9582

‎CONTRIBUTING.md‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,20 @@ pip install -e ".[dev]"
6868
pytest tests/ -v
6969
```
7070

71-
Tests use synthetic data and run in temporary directories. No external
72-
dependencies or API keys required.
71+
Tests use synthetic data and run in temporary directories. No model downloads
72+
or API keys are required. To check the JavaScript recorder:
73+
74+
```bash
75+
cd js
76+
npm ci
77+
npm test -- --maxWorkers=1 --minWorkers=1
78+
npm run build
79+
```
7380

7481
## Quick sanity check
7582

7683
```bash
77-
tracer demo
84+
pytest tests/test_router.py tests/test_watch.py -q
7885
```
7986

8087
## Project structure
@@ -101,9 +108,8 @@ src/tracer/
101108
runtime/
102109
router.py <- production Router class
103110
serve.py <- lightweight HTTP prediction server (stdlib only)
104-
cli/
105-
main.py <- tracer CLI entry point (fit, report, update, demo, serve)
106-
_ui.py <- terminal formatting and progress display
111+
scanner.py <- pre-training traffic diagnostics and reports
112+
watch.py <- local trace recording and generic export sinks
107113
```
108114

109115
## Adding a new surrogate model

‎README.md‎

Lines changed: 28 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
[![npm](https://img.shields.io/npm/v/@tracer-llm/watch?label=%40tracer-llm%2Fwatch&color=cb3837&logo=npm)](https://www.npmjs.com/package/@tracer-llm/watch)
1212
[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
1313
[![CI](https://img.shields.io/badge/CI-passing-brightgreen)](https://github.com/adrida/tracer/actions)
14-
[![Website](https://img.shields.io/badge/website-tracerml.ai-blue)](https://tracerml.ai)
1514
[![Docs](https://img.shields.io/badge/docs-reference-blue)](docs/)
1615

1716
Most LLM-based classification pipelines use a large language model for every single input. In practice, the vast majority of that traffic is predictable - a lightweight traditional ML model (logistic regression, gradient-boosted trees, or a small neural net) can match the LLM's output with near-perfect agreement.
@@ -22,25 +21,6 @@ TRACER learns the decision boundary between "easy" and "hard" inputs directly fr
2221
pip install tracer-llm
2322
```
2423

25-
## See it work
26-
27-
```bash
28-
tracer demo
29-
```
30-
31-
```
32-
TRACER Demo - Banking77 (77 intents · 1,500 traces)
33-
34-
Routing Policy
35-
method l2d
36-
coverage 91.4% of traffic handled by surrogate
37-
teacher TA 0.920 surrogate matches teacher on handled traffic
38-
39-
Cost Projection (10k queries/day)
40-
Without TRACER 10,000 LLM calls/day $20.00/day
41-
With TRACER 863 LLM calls/day $ 1.73/day $6,670 saved/yr
42-
```
43-
4424
## Quickstart
4525

4626
Input: a JSONL file where each line contains the original text (`input`) and the label your LLM assigned (`teacher`).
@@ -63,10 +43,11 @@ out = router.predict("What is my balance?")
6343
# 3. Fallback - only invokes the LLM when the surrogate declines
6444
out = router.predict("Some edge case", fallback=lambda: call_my_llm(text))
6545
```
46+
The [API reference](docs/api.md) covers fitting, routing, updates, and reports.
47+
See [concepts](docs/concepts.md) for the pipeline and [watch](docs/watch.md) for
48+
local trace recording and opt-in exports to your own backend.
6649

67-
> **Want to go deeper?** The [concepts guide](docs/concepts.md) explains the full pipeline, model zoo, and parity gate. The [API reference](docs/api.md) covers every parameter. The [CLI reference](docs/cli.md) covers `tracer fit`, `tracer serve`, and more. For observability see [watch](docs/watch.md), and to drive Tracer Cloud from your shell see the [`tracer cloud` reference](docs/cloud.md).
68-
69-
## Watch your LLM traffic (free observability)
50+
## Watch your LLM traffic
7051

7152
Before you fit anything, just *watch*. Wrap any LLM call and every request is
7253
recorded locally as an OpenTelemetry GenAI span, no account, no key, nothing
@@ -82,21 +63,12 @@ def classify(ticket: str) -> str:
8263
return call_my_llm(ticket) # traces append to .tracer/watch/*.jsonl
8364
```
8465

85-
Want them in a dashboard? Tracer Cloud observability is **free**. Mint a key
86-
(`tracer cloud ingest-keys create`, or the Watch page in the app) and set one
87-
env var, your traffic streams in within seconds, prod-safe (batched, never adds
88-
latency or throws):
89-
90-
```bash
91-
export TRACER_CLOUD_KEY=trobs_... # or watch(..., cloud_key="trobs_...")
92-
```
93-
9466
The same watched spans map 1:1 to `TraceRecord`, so once you have traffic you can
95-
`tracer fit` a router from it. Full guide: [docs/watch.md](docs/watch.md).
67+
call `tracer.fit()` to train a router from it. Full guide: [docs/watch.md](docs/watch.md).
9668

9769
## Using from JavaScript / Node.js
9870

99-
**Watch your JS LLM calls (free observability):** [`@tracer-llm/watch`](https://www.npmjs.com/package/@tracer-llm/watch) mirrors the Python decorator with zero dependencies, recording every call as an OpenTelemetry GenAI span (local by default, or streamed free to Tracer Cloud).
71+
**Watch your JS LLM calls (free observability):** [`@tracer-llm/watch`](https://www.npmjs.com/package/@tracer-llm/watch) mirrors the Python decorator with zero dependencies, recording every call as an OpenTelemetry GenAI span (local by default, with opt-in export to your own backend).
10072

10173
```bash
10274
npm install @tracer-llm/watch
@@ -111,14 +83,14 @@ const w = watch("support_classifier", { system: "provider-x", model: "model-x" }
11183
const classify = w(async (ticket) => callYourLLM(ticket));
11284
```
11385

114-
Full guide: [docs/javascript.md](docs/javascript.md). To route (not just observe) from JS, log traces, fit offline with the CLI, run `tracer serve` as a sidecar, and call it via `fetch`:
86+
Full guide: [docs/javascript.md](docs/javascript.md). To route (not just observe) from JS, log traces, fit offline with `tracer.fit()`, run `tracer.serve()` in a Python sidecar, and call it via `fetch`:
11587

11688
```js
11789
// 1. Log every LLM classification
11890
fs.appendFileSync('traces.jsonl', JSON.stringify({ input: text, teacher: label }) + '\n')
11991

12092
// 2. At inference: embed → POST to TRACER → fallback to LLM only if deferred
121-
const { label, decision } = await fetch('http://localhost:8000/predict', {
93+
let { label, decision } = await fetch('http://localhost:8000/predict', {
12294
method: 'POST',
12395
body: JSON.stringify({ embedding }), // same model you used at fit time
12496
}).then(r => r.json())
@@ -139,7 +111,7 @@ User query → [Embedder] → [ML Surrogate] → [Acceptor Gate]
139111
(traditional ML)
140112
```
141113

142-
The surrogate is **not another LLM** - it is a classical ML or shallow DL model. By default the zoo is lightweight and fast (logistic regression, SGD, and small feed-forward nets); the tree-based models (decision tree, random forest, extra-trees, gradient boosting) are heavier and opt-in with `tracer fit --trees`. This is what makes the cost reduction real: inference is CPU-bound, sub-millisecond, and free.
114+
The surrogate is **not another LLM** - it is a classical ML or shallow DL model. The Python API searches linear, neural, and tree-based candidates. For a lighter sweep, pass `FitConfig(skip_candidates=("dt", "rf", "et", "gbt", "xgb"))` to exclude tree models. Inference runs locally on your CPU.
143115

144116
1. **Fit** - train a suite of candidate surrogates on your LLM's classification traces; select the best via cross-validated teacher agreement
145117
2. **Gate** - attach a learned acceptor that estimates, per-input, whether the surrogate will agree with the teacher
@@ -155,7 +127,7 @@ The surrogate is **not another LLM** - it is a classical ML or shallow DL model.
155127
| End-to-end accuracy | 96.4% |
156128
| **Annual savings** (10k queries/day) | **$302,850** |
157129

158-
_Banking77 is a 77-class task; the tree models help here, so these numbers are with `tracer fit --trees`. The lightweight default (linear + MLP) is faster and enough for most tasks._
130+
_Banking77 is a 77-class task; these results include tree-based candidates. Candidate selection and coverage depend on your data._
159131

160132
## Continual learning flywheel
161133

@@ -194,19 +166,23 @@ pip install tracer-llm[embeddings] # adds sentence-transformers
194166
X = tracer.embed(texts) # default: all-MiniLM-L6-v2 (384-dim)
195167
```
196168

197-
## CLI
169+
## Inspect traffic before fitting
170+
171+
`tracer.scan()` groups traces by similarity and estimates the certifiable share
172+
using held-out bounds. Pass the embeddings that correspond to your trace rows:
198173

199-
| Command | What it does |
200-
|---------|-------------|
201-
| `tracer demo` | Zero-setup demo on real data |
202-
| `tracer scan traces.jsonl --html scan.html` | Day-one read: how much traffic is certifiably routable, with a 3D map |
203-
| `tracer fit traces.jsonl --target 0.95` | Fit a routing policy |
204-
| `tracer update new_traces.jsonl` | Refit with new traces |
205-
| `tracer report-html` | Open the HTML report |
206-
| `tracer serve .tracer --port 8000` | HTTP prediction server |
207-
| `tracer cloud login` | Drive Tracer Cloud from the terminal: create, train, route, test, and watch tracers, at parity with the dashboard ([docs](docs/cloud.md)) |
174+
```python
175+
from pathlib import Path
176+
import tracer
177+
from tracer.scanner import scan_html
178+
179+
scan = tracer.scan("traces.jsonl", embeddings=X, target=0.95)
180+
Path("scan.html").write_text(scan_html(scan), encoding="utf-8")
181+
```
208182

209-
`tracer scan` is the fast, conservative first look (similarity grouping plus exact held-out bounds, no training). It needs about 1,000 traces and works best around 5,000; below 1,000 it asks you to collect more, or pass `--force` for a best-effort floor. Embeddings are computed locally by default (sentence-transformers), or point it at your own embedding service with `--embed-url`. `tracer fit` then trains the real router and certifies more of the same traffic. The HTML report includes an interactive 3D map of your embedding space with a verdict/label colour toggle. See the [CLI reference](docs/cli.md) for every flag.
183+
A scan needs at least 1,000 traces; around 5,000 is recommended. Use `force=True`
184+
only for an explicitly marked thin-data estimate. It does not train a router;
185+
use `tracer.fit()` for training. See the [scan guide](docs/scan.md).
210186

211187
## What's in `.tracer/`
212188

@@ -232,7 +208,8 @@ pip install tracer-llm[all] # everything
232208
|---|---|
233209
| [Concepts](docs/concepts.md) | Pipeline internals, model zoo, parity gate |
234210
| [API reference](docs/api.md) | Every function, parameter, and return type |
235-
| [CLI reference](docs/cli.md) | `tracer fit`, `tracer serve`, `tracer demo`, and more |
211+
| [Scan](docs/scan.md) | Inspect traffic before training |
212+
| [Watch](docs/watch.md) | Record calls locally and configure generic exports |
236213
| [JavaScript / Node.js](docs/javascript.md) | Full integration guide for JS pipelines |
237214
| [Artifacts](docs/artifacts.md) | `.tracer/` directory schema |
238215
| [Troubleshooting](docs/troubleshooting.md) | `selected_method=null`, coverage drift, embedding-dim mismatch |
@@ -262,7 +239,7 @@ Adam Rida, arXiv 2026
262239

263240
Adam Rida, Tracer AI, Inc., August 25, 2026
264241

265-
[PDF in this repository](research/pricing-capability.pdf) | [Research article](https://tracerml.ai/research/pricing-capability/) | [Canonical PDF](https://tracerml.ai/research/pricing-capability/pricing-capability-paper.pdf)
242+
[PDF in this repository](research/pricing-capability.pdf)
266243

267244
This repository preserves the 21-page final submission dated August 25, 2026. The paper defines Capability Alpha under controlled evaluation, develops Price-Implied Uplift, studies Spirit Aviation's selected data-and-software bid, and compares four public AI data and content agreements. The public PDF preserves the final submission's text, bookmarks, citations, and internal navigation, corrects one internal section reference, and adds descriptive document metadata. It is independent research without a peer review or journal acceptance claim.
268245

‎SECURITY.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Preferred: use GitHub's private vulnerability reporting via the
2020
[**Security → Report a vulnerability**](https://github.com/adrida/tracer/security/advisories/new)
2121
tab on this repository.
2222

23-
Alternatively, email **adam@tracerml.ai** with:
23+
Include:
2424

2525
- a description of the issue and the potential impact,
2626
- steps to reproduce or a proof of concept,

‎docs/api.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,8 @@ class FitConfig:
478478
min_deploy_coverage: float = 0.05
479479
max_fit_labels: int = 8_000
480480
seed: int = 42
481+
verbose: bool = True
482+
skip_candidates: tuple = ()
481483
```
482484

483485
| Field | Description |
@@ -487,6 +489,8 @@ class FitConfig:
487489
| `min_deploy_coverage` | Minimum coverage fraction to consider a method deployable. |
488490
| `max_fit_labels` | Subsample to this size for efficiency on large datasets (stratified). |
489491
| `seed` | Random seed for reproducibility. |
492+
| `verbose` | Emit fitting progress to stderr. Set `False` for quiet runs. |
493+
| `skip_candidates` | Candidate names to exclude. Use `("dt", "rf", "et", "gbt", "xgb")` for a linear/neural sweep. An empty tuple keeps all available candidates. |
490494

491495
**Example:**
492496

0 commit comments

Comments
 (0)