@tuned-tensor/cli 0.16.0 → 0.18.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.
@@ -0,0 +1,131 @@
1
+ # Work from the behavior spec
2
+
3
+ Explore the [interactive architecture diagram](spec-architecture.html) for source
4
+ excerpts, code structure and command examples. Open the HTML file in a browser.
5
+
6
+ `tunedtensor.json` is the local recipe for the model's behavior, examples and
7
+ training settings. Ask the agent to improve that recipe, review the proposed
8
+ diff, approve the edit, then preview or execute a pipeline against the saved spec.
9
+ The agent reads the actual file before proposing changes; it does not use the
10
+ conversation transcript as a replacement for the current spec.
11
+
12
+ ## Review without a model call
13
+
14
+ Inside the TT shell, in your project directory:
15
+
16
+ ```text
17
+ /spec
18
+ /spec validate
19
+ /spec diff
20
+ /spec history
21
+ ```
22
+
23
+ `/spec` shows the complete current file, validation results and SHA-256.
24
+ `/spec diff` shows pending spec edits in the current conversation; otherwise it
25
+ shows the latest approved edit, or external changes since that revision.
26
+ `/spec history` lists up to 50 recent revision records. These commands require
27
+ no credentials and work before the agent is configured.
28
+
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
31
+ `tt spec show feedback/tunedtensor.json`. For continued work on a nested
32
+ project, `/cd feedback` makes its `tunedtensor.json` the current recipe.
33
+ `--json` returns structured review results. `tt spec validate` exits nonzero
34
+ for an invalid spec.
35
+
36
+ ## Edit, review, approve
37
+
38
+ ```text
39
+ › 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
43
+ › /spec
44
+ › /spec history
45
+ ```
46
+
47
+ `get_local_spec` returns the document, validation diagnostics and content hash.
48
+ `prepare_update_local_spec` requires that hash and a patch of supported fields.
49
+ Arrays replace their whole field; `hyperparameters` and `foundation` merge by
50
+ key. `runtime` and `evaluation` merge recursively; null removes optional settings.
51
+ `pipeline` replaces the entire recipe (null restores the derived default). Unmentioned fields remain unchanged. Identity and engine cannot change
52
+ through this edit tool. The full resulting document must pass the runtime's
53
+ strict schema, readiness checks, nonblank content checks and distinct-example
54
+ checks. Small example sets produce a warning; validation is not a claim of model
55
+ quality or GPU readiness.
56
+
57
+ Proposing an edit does not write anything. `/approve` revalidates the workspace,
58
+ parent directory, current file bytes and proposed result, acquires the spec's
59
+ writer lock and atomically replaces the file. `/reject` leaves it unchanged.
60
+ If the file changes after review, prepare a fresh edit. A saved edit invalidates
61
+ any previously prepared pipeline preview for the old spec.
62
+
63
+ Secure agent edits currently require Linux filesystem handles. Reads work on
64
+ other supported CLI platforms; edit the JSON with your editor there. Spec reads
65
+ and edits are bounded to 200 KB. Large model-facing proposals may hit the
66
+ agent's existing smaller review budget; split the request or use your editor.
67
+
68
+ Revision records live beside the spec under `.tuned-tensor/spec-history/` and
69
+ contain before/after snapshots and hashes. They can contain your training
70
+ examples. They use private permissions. History records changes made through
71
+ this edit workflow; it is not a file watcher or a complete history of external
72
+ editor changes. A `prepared` record without an `applied` record is an incomplete
73
+ journal entry: inspect the current file and its hash before attempting another
74
+ edit. A crashed writer can leave `.tunedtensor-spec.lock`; after confirming no
75
+ TT writer is still running, remove that lock manually. The lock coordinates TT
76
+ writers; avoid concurrent external editor saves during approval.
77
+
78
+ ## Execute the saved recipe
79
+
80
+ ```bash
81
+ tt spec validate
82
+ tt pipeline run --spec tunedtensor.json --dry-run
83
+ tt pipeline run --spec tunedtensor.json
84
+ ```
85
+
86
+ The pipeline uses the optional `pipeline` section or derives from the current spec. An adjacent
87
+ `tunedtensor.pipeline.json` is no longer loaded implicitly. To select an advanced
88
+ recipe explicitly, pass `--file`; a missing explicit recipe or `--spec` file fails. Foundation
89
+ training parameters in an explicit recipe must agree with the spec, including
90
+ in previews and the runtime's own execution guard. Change the spec and regenerate
91
+ the recipe when they disagree. Explicit recipes may still select supported
92
+ stages; `--only` and `--skip` remain available.
93
+
94
+ CLI planning, validation, execution and agent pipeline proposals share
95
+ `pipelineForRunInput` in `src/pipeline.ts`. It uses the same
96
+ `validateBehaviorSpec` rules as spec review and editing, defined beside the
97
+ runtime parser in `src/local-runtime/local-project.ts`. The edit module only
98
+ owns workspace access, patches and revision history.
99
+
100
+ The command reads one spec snapshot for planning, hardware warnings and execution. Plan/dry-run
101
+ JSON and final result JSON identify its path and SHA-256. The spec's compiled
102
+ instructions and examples feed training and task evaluation; the independently
103
+ configured general-regression suite keeps its own prompt. Provider choice and
104
+ GPU placement is configured in `runtime.gpu` in the spec. Model-provider choice
105
+ remains in agent configuration. Evaluation policy belongs in `evaluation`;
106
+ artifact/store locations and model paths belong in `runtime`. All relative paths
107
+ resolve beside the spec. New projects default to `.tuned-tensor/artifacts` and
108
+ `.tuned-tensor/store` beside it (unless `TT_LOCAL_HOME` overrides the store).
109
+ Foundation supports `runtime.artifactRoot` and `runtime.gpu`; its evaluation
110
+ and training settings remain under `foundation`.
111
+
112
+ Only `tunedtensor.json` is required. `tt pipeline init --spec tunedtensor.json`
113
+ embeds an advanced recipe when needed; omit this step to keep automatic derivation.
114
+ An explicit `--file` cannot override a conflicting embedded recipe.
115
+
116
+ For existing projects, run `tt pipeline migrate --spec tunedtensor.json` to
117
+ consolidate adjacent pipeline and runner files. It validates the result first,
118
+ keeps exclusive `.bak` copies, and preserves resolved paths and the old store
119
+ location. Conflicting sources, enabled legacy `dryRun`, or unsupported foundation
120
+ runner settings require reconciliation first. Existing `.bak` files are never
121
+ overwritten. Avoid concurrent edits during migration. Legacy runner files remain
122
+ readable with a warning, but cannot coexist with inline runtime/evaluation settings.
123
+ A legacy adjacent pipeline is used only with explicit `--file` or after migration.
124
+
125
+ Runs write generated `resolved-workflow.json` snapshots for diagnosis and
126
+ reproducibility; these are outputs, not additional user-authored configuration.
127
+
128
+
129
+ Agent `/approve` for a pipeline still runs a preview only. Real training uses
130
+ the direct command above. The spec is the behavior recipe; approval of a spec
131
+ edit is not approval to launch training.
@@ -0,0 +1,49 @@
1
+ {
2
+ "id": "22222222-2222-4222-8222-222222222222",
3
+ "name": "Local Smoke Assistant",
4
+ "description": "Tiny local runner smoke test.",
5
+ "system_prompt": "Return exactly one sentiment label.",
6
+ "guidelines": [
7
+ "Reply with positive or negative."
8
+ ],
9
+ "constraints": [
10
+ "Do not include extra commentary."
11
+ ],
12
+ "base_model": "Qwen/Qwen3.5-2B",
13
+ "examples": [
14
+ {
15
+ "input": "Classify sentiment: I love this product.",
16
+ "output": "positive"
17
+ },
18
+ {
19
+ "input": "Classify sentiment: This was disappointing.",
20
+ "output": "negative"
21
+ },
22
+ {
23
+ "input": "Classify sentiment: The update works perfectly.",
24
+ "output": "positive"
25
+ },
26
+ {
27
+ "input": "Classify sentiment: Support never replied.",
28
+ "output": "negative"
29
+ }
30
+ ],
31
+ "hyperparameters": {
32
+ "n_epochs": 1,
33
+ "learning_rate": 0.0002,
34
+ "batch_size": 1,
35
+ "gradient_accumulation_steps": 1,
36
+ "lora_rank": 4,
37
+ "lora_alpha": 8,
38
+ "max_seq_length": 256,
39
+ "max_eval_examples": 2
40
+ },
41
+ "evaluation": {
42
+ "inference": {
43
+ "maxNewTokens": 32
44
+ },
45
+ "scoring": {
46
+ "mode": "exact_match"
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "engine": "foundation",
3
+ "name": "Foundation smoke chat",
4
+ "description": "Tiny from-scratch tokenizer, pretrain, chat SFT, and eval.",
5
+ "system_prompt": "You are a helpful assistant.",
6
+ "guidelines": [
7
+ "Answer the user request directly."
8
+ ],
9
+ "constraints": [],
10
+ "examples": [
11
+ {
12
+ "input": "Say hello in one sentence.",
13
+ "output": "Hello \u2014 how can I help you today?"
14
+ },
15
+ {
16
+ "input": "What is 2 + 2?",
17
+ "output": "2 + 2 = 4."
18
+ }
19
+ ],
20
+ "foundation": {
21
+ "vocab_size": 256,
22
+ "max_chars": 20000,
23
+ "depth": 2,
24
+ "pretrain_steps": 2,
25
+ "finetune_steps": 2,
26
+ "rl_steps": 0,
27
+ "batch_size": 2,
28
+ "sequence_length": 64,
29
+ "nproc_per_node": 1
30
+ }
31
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuned-tensor/cli",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Local TT agent and CUDA fine-tuning CLI for Tuned Tensor workflows",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,8 +34,11 @@
34
34
  "training/foundation/uv.lock",
35
35
  "training/foundation/src/*.py",
36
36
  "docs/local-runtime",
37
+ "docs/spec-workflow.md",
38
+ "docs/spec-architecture.html",
37
39
  "examples/local-runtime/*.json",
38
- "examples/local-runtime/*.jsonl"
40
+ "examples/local-runtime/*.jsonl",
41
+ "examples/single-spec/**/*.json"
39
42
  ],
40
43
  "engines": {
41
44
  "node": ">=22.19.0"
@@ -66,6 +69,6 @@
66
69
  "tsup": "^8.5.1",
67
70
  "tsx": "^4.23.12",
68
71
  "typescript": "^5.9.3",
69
- "vitest": "^4.1.8"
72
+ "vitest": "^4.1.11"
70
73
  }
71
74
  }
@@ -93,7 +93,13 @@ def build_vllm_args(model_source: str, adapter_path: str | None, temp_dir: Path)
93
93
  # tool calls/results verbatim while merging leading system context.
94
94
  prefix = "{%- set tt = namespace(system=" + json.dumps(prompt) + ", history=[]) -%}"
95
95
  prefix += "{%- for message in messages -%}{%- if message.role == 'system' -%}"
96
+ # vLLM may normalize text into OpenAI content parts for the model's
97
+ # native template. Merge those parts without stringifying the list.
98
+ prefix += "{%- if message.content is string -%}"
96
99
  prefix += "{%- set tt.system = tt.system + '\\n\\n' + message.content -%}"
100
+ prefix += "{%- else -%}{%- for part in message.content -%}{%- if part.type == 'text' -%}"
101
+ prefix += "{%- set tt.system = tt.system + '\\n\\n' + part.text -%}"
102
+ prefix += "{%- endif -%}{%- endfor -%}{%- endif -%}"
97
103
  prefix += "{%- else -%}{%- set tt.history = tt.history + [message] -%}{%- endif -%}{%- endfor -%}"
98
104
  prefix += "{%- set messages = [{'role': 'system', 'content': tt.system}] + tt.history -%}"
99
105
  path = temp_dir / "chat-template.jinja"