@tuned-tensor/cli 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -7
- package/dist/{chunk-4Q54NHKR.js → chunk-4SUXBHIY.js} +1715 -392
- package/dist/chunk-4SUXBHIY.js.map +1 -0
- package/dist/index.js +1910 -356
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.js +147 -49
- package/dist/local-runtime.js.map +1 -1
- package/docs/local-runtime/architecture.md +44 -1
- package/docs/local-runtime/aws-gpu.md +2 -0
- package/docs/local-runtime/decision.md +142 -0
- package/docs/spec-architecture.html +2 -2
- package/docs/spec-workflow.md +18 -3
- package/examples/single-spec/decision/tunedtensor.json +630 -0
- package/package.json +5 -2
- package/training/adapter/src/evaluate.py +14 -7
- package/training/adapter/src/hub_download.py +384 -0
- package/training/adapter/src/model_contract.py +38 -5
- package/training/adapter/src/prefetch.py +26 -32
- package/training/adapter/src/prefetch_dataset.py +116 -0
- package/training/adapter/src/serve.py +42 -12
- package/training/adapter/src/train.py +8 -0
- package/training/decision/pyproject.toml +25 -0
- package/training/decision/src/common.py +127 -0
- package/training/decision/src/evaluate.py +70 -0
- package/training/decision/src/train.py +181 -0
- package/training/decision/uv.lock +1175 -0
- package/dist/chunk-4Q54NHKR.js.map +0 -1
|
@@ -44,7 +44,7 @@ The local adapter runtime excludes:
|
|
|
44
44
|
The next model or method should arrive with its own locked dependencies,
|
|
45
45
|
resource defaults, data contract, and real CUDA acceptance test.
|
|
46
46
|
|
|
47
|
-
TT bundles
|
|
47
|
+
TT bundles three local training methods:
|
|
48
48
|
|
|
49
49
|
- **Adapter** (`training/adapter`): certified Transformers checkpoints and
|
|
50
50
|
PEFT LoRA SFT. Driven by `tt pipeline run`.
|
|
@@ -55,6 +55,11 @@ TT bundles two local training methods:
|
|
|
55
55
|
rejects `nproc_per_node` values above 1 and unsupported steps such as
|
|
56
56
|
`compare` before creating run artifacts.
|
|
57
57
|
|
|
58
|
+
- **Decision** (`training/decision`): fine-tunes a pinned typed decision
|
|
59
|
+
model (`convaiinnovations/laya`) on labelled examples. Driven by
|
|
60
|
+
`tt pipeline run` from a decision `tunedtensor.json`. Runs on CPU, Apple MPS
|
|
61
|
+
or CUDA; see [decision.md](decision.md).
|
|
62
|
+
|
|
58
63
|
The foundation engine is a readable single-GPU trainer inspired by nanochat,
|
|
59
64
|
not a port of nanochat's distributed or compute-optimal training stack. It can
|
|
60
65
|
stream configured text/JSONL shards into a deterministic token cache, use a
|
|
@@ -92,6 +97,8 @@ Node owns input validation, state, process lifecycle, manifests, and reports.
|
|
|
92
97
|
The bundled Python project owns four small operations:
|
|
93
98
|
|
|
94
99
|
- `prefetch.py`: download and verify the pinned Hugging Face snapshot;
|
|
100
|
+
- `prefetch_dataset.py`: download and verify the spec's Hugging Face dataset
|
|
101
|
+
files (`dataset_prebuilt.huggingface`);
|
|
95
102
|
- `evaluate.py`: generate predictions without receiving reference answers;
|
|
96
103
|
- `train.py`: perform CUDA-only, model-specific LoRA SFT;
|
|
97
104
|
- `serve.py`: prepare the verified base model and adapter for the vLLM serving runtime.
|
|
@@ -120,6 +127,19 @@ Canonical JSON and JSONL files are the state store; there is no database or
|
|
|
120
127
|
mirrored index. Writes are atomic and listings scan the small local metadata
|
|
121
128
|
tree deterministically.
|
|
122
129
|
|
|
130
|
+
Run events (`progress.jsonl` in the store's run directory) form a hash chain.
|
|
131
|
+
Each event carries `seq`, `prev_hash` and `hash`, a SHA-256 over its canonical
|
|
132
|
+
JSON. Appends take a short cross-process lock so concurrent writers, such as a
|
|
133
|
+
cancel from another terminal, extend one chain. The lock records its owner's
|
|
134
|
+
pid and host, so a crashed owner's lock is reclaimed at once and a live owner
|
|
135
|
+
is never broken. Appends read only the log's tail. A line torn by a crash is
|
|
136
|
+
closed off and skipped, and the audit counts it as unreadable. The completion event records
|
|
137
|
+
the report's SHA-256 and the artifact manifest's SHA-256. The report embeds a
|
|
138
|
+
`provenance` block (software, execution target, spec, base model, data
|
|
139
|
+
digests, hyperparameters). `tt runs audit` verifies the chain and report
|
|
140
|
+
digests and reports the first broken link. Events written by older versions
|
|
141
|
+
have no hashes and are reported as legacy.
|
|
142
|
+
|
|
123
143
|
Each real adapter has an atomic `artifact-manifest.json` with the expected PEFT
|
|
124
144
|
files, byte sizes, and SHA-256 hashes. A model is registered immediately after
|
|
125
145
|
training and manifest verification, so a later evaluation failure does not
|
|
@@ -134,6 +154,29 @@ The runner config controls:
|
|
|
134
154
|
- CUDA or CPU evaluation, with local serving through the CUDA vLLM runtime;
|
|
135
155
|
- deterministic generation and scoring limits.
|
|
136
156
|
|
|
157
|
+
## Hugging Face downloads
|
|
158
|
+
|
|
159
|
+
Model and dataset prefetch share `hub_download.py`. It resolves the requested
|
|
160
|
+
revision to one commit before downloading, so retries never mix files from two
|
|
161
|
+
commits. Network and server errors are retried with exponential backoff (2s up
|
|
162
|
+
to 60s, `TT_HF_DOWNLOAD_MAX_ATTEMPTS` attempts, default 10). Repo, auth,
|
|
163
|
+
revision and disk-full errors fail at once. Completed files stay in the cache,
|
|
164
|
+
so a rerun after Ctrl-C or a crash downloads only what is missing. A file
|
|
165
|
+
interrupted mid-transfer restarts from its beginning.
|
|
166
|
+
|
|
167
|
+
Progress is measured from the cache blob directory against the repo's file
|
|
168
|
+
sizes and printed as `@@tt-progress {json}` lines. The process runner sends
|
|
169
|
+
them to the reporter instead of the log stream. A terminal shows an in-place
|
|
170
|
+
bar with rate and ETA. Captured output gets a line every 10% or 30 seconds.
|
|
171
|
+
The parent `tt` process sets `TT_PROGRESS_TTY` for the local runtime child,
|
|
172
|
+
whose stderr is a pipe.
|
|
173
|
+
|
|
174
|
+
Dataset runs resolve the cached snapshot in Node without network access.
|
|
175
|
+
Each split file must link into the repo's blob store, and its digest must match
|
|
176
|
+
the blob name. With `columns`, records are converted to chat rows under
|
|
177
|
+
`<artifactRoot>/datasets/huggingface/`. The conversion path is keyed by the
|
|
178
|
+
source digests, the mapping and the system message.
|
|
179
|
+
|
|
137
180
|
The training project, Python entrypoints, working directory, and forwarded
|
|
138
181
|
runtime settings are internal and fixed. Every stage uses the locked `uv`
|
|
139
182
|
project included in the npm package. Locally, the mutable virtual environment
|
|
@@ -140,6 +140,8 @@ Run these commands on your laptop in the project directory. Adapter workflow:
|
|
|
140
140
|
# Fetch the base model locally first.
|
|
141
141
|
tt validate tunedtensor.json
|
|
142
142
|
tt models prefetch tunedtensor.json
|
|
143
|
+
# Only when dataset_prebuilt.huggingface is set:
|
|
144
|
+
tt datasets prefetch tunedtensor.json
|
|
143
145
|
tt doctor tunedtensor.json
|
|
144
146
|
tt pipeline run --spec tunedtensor.json --dry-run
|
|
145
147
|
tt pipeline run --spec tunedtensor.json
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Decision models
|
|
2
|
+
|
|
3
|
+
A decision model answers one typed question about an input — which queue,
|
|
4
|
+
yes or no, how urgent — in a single encoder forward pass. It returns a
|
|
5
|
+
probability for every allowed answer instead of generating text, so the output
|
|
6
|
+
always parses and its confidence can be thresholded. Use the decision engine
|
|
7
|
+
for classification, routing, triage, guardrails and score gates where a
|
|
8
|
+
generative LLM is more than the job needs.
|
|
9
|
+
|
|
10
|
+
TT fine-tunes [Laya](https://huggingface.co/convaiinnovations/laya)
|
|
11
|
+
(`convaiinnovations/laya`, Apache-2.0): a 421M-parameter ModernBERT-large
|
|
12
|
+
encoder with a typed decision head. Every stage uses Laya revision
|
|
13
|
+
`55cf4c4ebb4ebe31b2550e8bdf3bd21b99753851`, the reviewed commit that Laya 0.3.22
|
|
14
|
+
pins. Training and evaluation run on CPU, Apple MPS or CUDA; no GPU is required.
|
|
15
|
+
|
|
16
|
+
## Spec
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"engine": "decision",
|
|
21
|
+
"name": "Card support triage",
|
|
22
|
+
"system_prompt": "Which card-support queue should handle this customer message?",
|
|
23
|
+
"guidelines": ["Choose the queue for the customer's main problem."],
|
|
24
|
+
"constraints": [],
|
|
25
|
+
"base_model": "convaiinnovations/laya",
|
|
26
|
+
"decision": {
|
|
27
|
+
"type": "choice",
|
|
28
|
+
"criteria": {
|
|
29
|
+
"lost_or_stolen": "customer lost their card or it was stolen",
|
|
30
|
+
"payment_declined": "a card payment was declined or rejected"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"examples": [
|
|
34
|
+
{ "input": "I think I left my card at the bar last night.", "output": "lost_or_stolen" },
|
|
35
|
+
{ "input": "Why was my payment at the grocery store refused?", "output": "payment_declined" }
|
|
36
|
+
],
|
|
37
|
+
"hyperparameters": { "n_epochs": 3 }
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`tt init --engine decision` writes a starter spec. A complete example lives in
|
|
42
|
+
[`examples/single-spec/decision`](../../examples/single-spec/decision/tunedtensor.json).
|
|
43
|
+
|
|
44
|
+
`system_prompt`, `guidelines` and `constraints` compile into the question's
|
|
45
|
+
instructions, as they compile into the system message for the other engines.
|
|
46
|
+
The model reads roughly 190 tokens of instructions and option descriptions, so
|
|
47
|
+
keep them short; validation warns above 600 characters.
|
|
48
|
+
|
|
49
|
+
| `decision.type` | `criteria` | Example `output` |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| `choice` | Object of label → description (2–32 labels, in option order) | A label, e.g. `payment_declined` |
|
|
52
|
+
| `noul` | Optional `{ "true": "...", "false": "..." }` | `true` or `false` |
|
|
53
|
+
| `score` | Array of level descriptions, level 0 first (2–10) | A level index, e.g. `2` |
|
|
54
|
+
|
|
55
|
+
Outputs match labels ignoring case and surrounding spaces. Validation rejects
|
|
56
|
+
an output that is not a label and warns when a label has no examples.
|
|
57
|
+
|
|
58
|
+
Optional `hyperparameters`:
|
|
59
|
+
|
|
60
|
+
| Field | Default | Meaning |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| `n_epochs` | 3 | Passes over the training split |
|
|
63
|
+
| `learning_rate` | 2e-5 | AdamW learning rate, linear warmup and decay |
|
|
64
|
+
| `batch_size` | 8 | Examples per optimizer step |
|
|
65
|
+
| `freeze_encoder` | `false` | Train only the decision head (faster, usually less accurate) |
|
|
66
|
+
| `shuffle_options` | `true` | Shuffle option order per row so the head learns meaning, not position |
|
|
67
|
+
| `seed` | 0 | Training seed |
|
|
68
|
+
| `device` | `auto` | `auto`, `cpu`, `mps` or `cuda` |
|
|
69
|
+
|
|
70
|
+
`runtime.artifactRoot` is supported. `runtime.gpu`, `evaluation` and adapter
|
|
71
|
+
runtime paths are rejected: the model is small enough to run where the CLI runs.
|
|
72
|
+
|
|
73
|
+
## Run
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
tt validate
|
|
77
|
+
tt pipeline run --spec tunedtensor.json --dry-run
|
|
78
|
+
tt pipeline run --spec tunedtensor.json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The pipeline is the adapter recipe: `baseline` evaluates the pinned base model,
|
|
82
|
+
`train` fine-tunes it, `candidate` evaluates the tuned model and `compare`
|
|
83
|
+
pairs the two. TT splits the spec examples once with a seed derived from the
|
|
84
|
+
spec `id` (20% held out, at least one). Training sees only the training split.
|
|
85
|
+
The evaluator receives held-out IDs and inputs only; Node joins its predictions
|
|
86
|
+
to the expected labels and scores them.
|
|
87
|
+
|
|
88
|
+
Each evaluation reports accuracy, log-loss and Brier score of the expected
|
|
89
|
+
label, mean confidence and per-label recall. The comparison adds improved and
|
|
90
|
+
regressed examples and the log-loss and Brier deltas. Because Laya answers
|
|
91
|
+
zero-shot from the label descriptions, the baseline is a real model, not an
|
|
92
|
+
empty one: the report shows what fine-tuning added on top of the descriptions.
|
|
93
|
+
|
|
94
|
+
Runs are written to `.tuned-tensor/decision-runs/<run-id>` beside the spec (or
|
|
95
|
+
`--output <dir>`), with owner-only permissions:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
report.json summary: model, revision, split, every step's metrics
|
|
99
|
+
resolved-workflow.json spec, plan, question and split used
|
|
100
|
+
data/ train.jsonl and unlabeled eval-inputs.jsonl
|
|
101
|
+
baseline/ candidate/ config, log, predictions.jsonl, report.json
|
|
102
|
+
train/model/ the tuned checkpoint
|
|
103
|
+
compare/comparison.json
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
On a 4-core CPU a step of 8 examples takes about 15 seconds, so 120 examples
|
|
107
|
+
for 3 epochs trains in about 12 minutes. The tuned checkpoint is about 1.7 GB
|
|
108
|
+
because weights are saved in float32; small fine-tuning updates would be lost
|
|
109
|
+
in bfloat16.
|
|
110
|
+
|
|
111
|
+
## Use the tuned model
|
|
112
|
+
|
|
113
|
+
`train/model` is a complete Laya checkpoint. The fitted temperatures of the
|
|
114
|
+
base checkpoint are reset to 1.0, because they described the base model's
|
|
115
|
+
logits; cross-entropy training is itself the calibration objective.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
import laya
|
|
119
|
+
|
|
120
|
+
agent = laya.load("path/to/decision-runs/<run-id>/train/model")
|
|
121
|
+
question = {
|
|
122
|
+
"type": "choice",
|
|
123
|
+
"instructions": "Which card-support queue should handle this customer message?",
|
|
124
|
+
"criteria": {"lost_or_stolen": "...", "payment_declined": "..."},
|
|
125
|
+
}
|
|
126
|
+
answer = agent.predict("My card was refused at the shop.", {"queue": question})["answers"]["queue"]
|
|
127
|
+
print(answer["choice"], answer["probabilities"])
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Pass the same instructions and criteria the model was trained with: they are
|
|
131
|
+
part of its input. `resolved-workflow.json` records them under `question`.
|
|
132
|
+
The tuned model is not registered with `tt models` and is not served through
|
|
133
|
+
vLLM.
|
|
134
|
+
|
|
135
|
+
## Limits
|
|
136
|
+
|
|
137
|
+
- One question per spec. Laya can answer several questions per input; TT
|
|
138
|
+
trains and evaluates one.
|
|
139
|
+
- `convaiinnovations/laya` is the English checkpoint. Its multilingual and
|
|
140
|
+
typed-decisions variants are not certified yet.
|
|
141
|
+
- The held-out split comes from the spec examples. With few examples the
|
|
142
|
+
comparison is noisy; add examples before trusting a small delta.
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
<div class="tt-level">
|
|
74
74
|
<div class="tt-unit"><button class="btn" type="button" data-component="agent" aria-pressed="false">User intent → agent</button><small class="text-small">get_local_spec → field patch</small></div>
|
|
75
75
|
<div class="tt-unit"><button class="btn" type="button" data-component="approval" aria-pressed="false">Diff → /approve</button><small class="text-small">validate → atomic save → history</small></div>
|
|
76
|
-
<div class="tt-unit"><button class="btn" type="button" data-component="review" aria-pressed="false">/spec review</button><small class="text-small">show · diff · validate · history</small></div>
|
|
76
|
+
<div class="tt-unit"><button class="btn" type="button" data-component="review" aria-pressed="false">/spec review</button><small class="text-small">overview · sections · show · diff · validate · history</small></div>
|
|
77
77
|
</div>
|
|
78
78
|
<div class="tt-arrow text-small">↕ inspect the recipe · save only reviewed edits</div>
|
|
79
79
|
<div class="tt-recipe tt-unit">
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
"src/local-runtime/local-project.ts",
|
|
183
183
|
"src/pipeline.ts"
|
|
184
184
|
],
|
|
185
|
-
"example": "/spec\n/spec diff\n/spec validate\n/spec history\n\n# Outside the TT shell:\ntt spec show --json"
|
|
185
|
+
"example": "/spec\n/spec examples\n/spec diff\n/spec validate\n/spec history\n\n# Outside the TT shell:\ntt spec view\ntt spec show --json"
|
|
186
186
|
},
|
|
187
187
|
"config": {
|
|
188
188
|
"title": "Spec runtime and evaluation",
|
package/docs/spec-workflow.md
CHANGED
|
@@ -15,19 +15,27 @@ Inside the TT shell, in your project directory:
|
|
|
15
15
|
|
|
16
16
|
```text
|
|
17
17
|
/spec
|
|
18
|
+
/spec examples
|
|
19
|
+
/spec show
|
|
18
20
|
/spec validate
|
|
19
21
|
/spec diff
|
|
20
22
|
/spec history
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
`/spec` shows
|
|
25
|
+
`/spec` shows a readable overview: name, engine, base model and example count,
|
|
26
|
+
then highlighted sections for behavior (system prompt, guidelines, constraints),
|
|
27
|
+
examples, training settings, evaluation, runtime and pipeline, followed by
|
|
28
|
+
validation results. `/spec <section>` shows one section in full; sections are
|
|
29
|
+
`identity`, `behavior`, `examples`, `training`, `evaluation`, `runtime` and
|
|
30
|
+
`pipeline`. `/spec show` prints the complete file with JSON highlighting,
|
|
31
|
+
validation results and SHA-256.
|
|
24
32
|
`/spec diff` shows pending spec edits in the current conversation; otherwise it
|
|
25
33
|
shows the latest approved edit, or external changes since that revision.
|
|
26
34
|
`/spec history` lists up to 50 recent revision records. These commands require
|
|
27
35
|
no credentials and work before the agent is configured.
|
|
28
36
|
|
|
29
|
-
Outside the shell, use `tt spec
|
|
30
|
-
`tt spec history`. Add a workspace-relative path, for example
|
|
37
|
+
Outside the shell, use `tt spec view` (or `tt spec <section>`), `tt spec show`,
|
|
38
|
+
`tt spec validate`, `tt spec diff`, and `tt spec history`. Add a workspace-relative path, for example
|
|
31
39
|
`tt spec show feedback/tunedtensor.json`. For continued work on a nested
|
|
32
40
|
project, `/cd feedback` makes its `tunedtensor.json` the current recipe.
|
|
33
41
|
`--json` returns structured review results. `tt spec validate` exits nonzero
|
|
@@ -114,6 +122,13 @@ resolve beside the spec. New projects default to `.tuned-tensor/artifacts` and
|
|
|
114
122
|
Foundation supports `runtime.artifactRoot` and `runtime.gpu`; its evaluation
|
|
115
123
|
and training settings remain under `foundation`.
|
|
116
124
|
|
|
125
|
+
`dataset_prebuilt.huggingface` names a Hugging Face dataset repo, an optional
|
|
126
|
+
pinned `revision` and optional `columns` (`input`/`output` field names). Its
|
|
127
|
+
split paths are then files inside that repo. `tt datasets prefetch` downloads
|
|
128
|
+
them, and `tt pipeline run` downloads them automatically if they are missing.
|
|
129
|
+
Runs record the resolved commit and file digests in the report. See the README
|
|
130
|
+
section "Train on a Hugging Face dataset".
|
|
131
|
+
|
|
117
132
|
Only `tunedtensor.json` is required. `tt pipeline init --spec tunedtensor.json`
|
|
118
133
|
embeds an advanced recipe when needed; omit this step to keep automatic derivation.
|
|
119
134
|
An explicit `--file` cannot override a conflicting embedded recipe.
|