@tuned-tensor/cli 0.19.0 → 0.21.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.
@@ -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 two local training methods:
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
@@ -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",
@@ -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 the complete current file, validation results and SHA-256.
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 show`, `tt spec validate`, `tt spec diff`, and
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
@@ -37,9 +45,8 @@ for an invalid spec.
37
45
 
38
46
  ```text
39
47
  › Keep my training settings, but require a single lowercase sentiment label.
40
- [agent reads the spec and proposes a diff]
41
- › /spec diff
42
- › /approve
48
+ [agent reads the spec and shows the proposed diff]
49
+ ? Save this spec edit? y yes · n no · esc decide later
43
50
  › /spec
44
51
  › /spec history
45
52
  ```
@@ -58,7 +65,9 @@ strict schema, readiness checks, nonblank content checks and distinct-example
58
65
  checks. Small example sets produce a warning; validation is not a claim of model
59
66
  quality or GPU readiness.
60
67
 
61
- Proposing an edit does not write anything. `/approve` revalidates the workspace,
68
+ Proposing an edit does not write anything. tt asks for a decision right after the
69
+ answer; pressing `Esc` leaves the edit pending, and `/spec diff` then shows it again
70
+ until you run `/approve` or `/reject`. Approval revalidates the workspace,
62
71
  parent directory, current file bytes and proposed result, acquires the spec's
63
72
  writer lock and atomically replaces the file. `/reject` leaves it unchanged.
64
73
  If the file changes after review, prepare a fresh edit. A saved edit invalidates