@tuned-tensor/local 0.2.9 → 0.4.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.
Files changed (89) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +110 -201
  3. package/dist/active-model.d.ts +16 -0
  4. package/dist/active-model.js +88 -0
  5. package/dist/active-model.js.map +1 -0
  6. package/dist/artifacts.d.ts +5 -4
  7. package/dist/artifacts.js +57 -33
  8. package/dist/artifacts.js.map +1 -1
  9. package/dist/compare.d.ts +1 -8
  10. package/dist/compare.js +1 -23
  11. package/dist/compare.js.map +1 -1
  12. package/dist/contracts.d.ts +234 -365
  13. package/dist/contracts.js +95 -224
  14. package/dist/contracts.js.map +1 -1
  15. package/dist/dataset.d.ts +11 -4
  16. package/dist/dataset.js +67 -125
  17. package/dist/dataset.js.map +1 -1
  18. package/dist/doctor.d.ts +2 -3
  19. package/dist/doctor.js +23 -161
  20. package/dist/doctor.js.map +1 -1
  21. package/dist/evaluation.d.ts +11 -71
  22. package/dist/evaluation.js +212 -572
  23. package/dist/evaluation.js.map +1 -1
  24. package/dist/general-regression.d.ts +10 -0
  25. package/dist/general-regression.js +16 -0
  26. package/dist/general-regression.js.map +1 -0
  27. package/dist/huggingface-cache.d.ts +8 -7
  28. package/dist/huggingface-cache.js +16 -8
  29. package/dist/huggingface-cache.js.map +1 -1
  30. package/dist/index.d.ts +2 -10
  31. package/dist/index.js +358 -627
  32. package/dist/index.js.map +1 -1
  33. package/dist/local-project.d.ts +1 -11
  34. package/dist/local-project.js +33 -61
  35. package/dist/local-project.js.map +1 -1
  36. package/dist/model-registry.d.ts +3 -16
  37. package/dist/model-registry.js +76 -292
  38. package/dist/model-registry.js.map +1 -1
  39. package/dist/model-server.d.ts +7 -3
  40. package/dist/model-server.js +34 -25
  41. package/dist/model-server.js.map +1 -1
  42. package/dist/orchestrator.d.ts +11 -25
  43. package/dist/orchestrator.js +393 -563
  44. package/dist/orchestrator.js.map +1 -1
  45. package/dist/prefetch.d.ts +1 -5
  46. package/dist/prefetch.js +14 -19
  47. package/dist/prefetch.js.map +1 -1
  48. package/dist/process-runner.d.ts +10 -29
  49. package/dist/process-runner.js +63 -169
  50. package/dist/process-runner.js.map +1 -1
  51. package/dist/process-training.d.ts +0 -1
  52. package/dist/process-training.js +10 -87
  53. package/dist/process-training.js.map +1 -1
  54. package/dist/store.d.ts +1 -3
  55. package/dist/store.js +92 -290
  56. package/dist/store.js.map +1 -1
  57. package/docs/architecture.md +87 -152
  58. package/docs/spark.md +66 -97
  59. package/examples/dry-runner.json +14 -0
  60. package/examples/general-regression.jsonl +4 -0
  61. package/examples/local-runner.json +13 -6
  62. package/examples/smoke-spec.json +41 -0
  63. package/package.json +6 -6
  64. package/training/local-runner/pyproject.toml +0 -5
  65. package/training/local-runner/src/evaluate.py +89 -234
  66. package/training/local-runner/src/model_contract.py +47 -0
  67. package/training/local-runner/src/prefetch.py +18 -4
  68. package/training/local-runner/src/serve.py +60 -117
  69. package/training/local-runner/src/sft_data.py +97 -0
  70. package/training/local-runner/src/train.py +146 -346
  71. package/training/local-runner/uv.lock +0 -1197
  72. package/dist/labeling-sanitize.d.ts +0 -31
  73. package/dist/labeling-sanitize.js +0 -158
  74. package/dist/labeling-sanitize.js.map +0 -1
  75. package/dist/labeling.d.ts +0 -155
  76. package/dist/labeling.js +0 -496
  77. package/dist/labeling.js.map +0 -1
  78. package/dist/openrouter.d.ts +0 -29
  79. package/dist/openrouter.js +0 -66
  80. package/dist/openrouter.js.map +0 -1
  81. package/dist/server.d.ts +0 -9
  82. package/dist/server.js +0 -211
  83. package/dist/server.js.map +0 -1
  84. package/docs/local-workflow-remediation-2026-07-13.md +0 -130
  85. package/docs/local-workflow-ux-review-2026-07-13.md +0 -458
  86. package/examples/dpo-preferences.jsonl +0 -2
  87. package/examples/dpo-run-request.json +0 -34
  88. package/examples/smoke-run-request.json +0 -34
  89. package/training/local-runner/src/train_dpo.py +0 -286
package/dist/dataset.js CHANGED
@@ -1,89 +1,54 @@
1
1
  import { readFile } from "node:fs/promises";
2
- import { homedir } from "node:os";
3
- import { dirname, isAbsolute, join, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- function normalizeAssetReference(value, baseDirectory) {
6
- if (value.startsWith("~/"))
7
- return join(homedir(), value.slice(2));
8
- if (!value || isAbsolute(value) || /^[a-z][a-z0-9+.-]*:/i.test(value))
9
- return value;
10
- return resolve(baseDirectory, value);
11
- }
12
- function localAssetPath(value) {
13
- if (value.startsWith("file://")) {
14
- try {
15
- return fileURLToPath(new URL(value));
16
- }
17
- catch {
18
- return null;
19
- }
20
- }
21
- return /^[a-z][a-z0-9+.-]*:/i.test(value) ? null : value;
22
- }
23
2
  async function loadNormalizedChatJsonl(path) {
24
3
  const text = await readFile(path, "utf8");
25
- const baseDirectory = dirname(resolve(path));
26
4
  const rows = [];
27
5
  const jsonLines = [];
28
- const localAssets = new Set();
29
- const normalize = (value) => {
30
- if (typeof value !== "string" || !value)
31
- return value;
32
- const normalized = normalizeAssetReference(value, baseDirectory);
33
- const local = localAssetPath(normalized);
34
- if (local)
35
- localAssets.add(resolve(local));
36
- return normalized;
37
- };
38
6
  for (const [index, line] of text.split(/\r?\n/).entries()) {
39
7
  if (!line.trim())
40
8
  continue;
41
- let row;
9
+ let value;
42
10
  try {
43
- row = JSON.parse(line);
11
+ value = JSON.parse(line);
44
12
  }
45
13
  catch (error) {
46
14
  throw new Error(`Invalid chat JSONL row ${index + 1}: malformed JSON`, { cause: error });
47
15
  }
48
- if (Array.isArray(row.images))
49
- row.images = row.images.map((value) => normalize(value));
50
- if (Array.isArray(row.messages)) {
51
- for (const message of row.messages) {
52
- if (!Array.isArray(message.content))
53
- continue;
54
- for (const part of message.content) {
55
- if (!part || typeof part !== "object" || (part.type !== "image" && part.type !== "image_url"))
56
- continue;
57
- for (const key of ["image", "path", "uri", "data_uri"]) {
58
- if (part[key] !== undefined)
59
- part[key] = normalize(part[key]);
60
- }
61
- if (typeof part.image_url === "string")
62
- part.image_url = normalize(part.image_url);
63
- if (part.image_url && typeof part.image_url === "object" && !Array.isArray(part.image_url)) {
64
- const imageUrl = part.image_url;
65
- if (imageUrl.url !== undefined)
66
- imageUrl.url = normalize(imageUrl.url);
67
- }
68
- }
69
- }
16
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
17
+ throw new Error(`Invalid chat JSONL row ${index + 1}: expected an object`);
18
+ }
19
+ const messages = value.messages;
20
+ if (!Array.isArray(messages)
21
+ || ![2, 3].includes(messages.length)
22
+ || messages.some((message) => !message
23
+ || typeof message !== "object"
24
+ || Array.isArray(message)
25
+ || !["system", "user", "assistant"].includes(String(message.role))
26
+ || typeof message.content !== "string")) {
27
+ throw new Error(`Invalid chat JSONL row ${index + 1}: expected an optional system message, one user message, and one assistant answer`);
28
+ }
29
+ const row = { messages };
30
+ const offset = row.messages.length === 3 ? 1 : 0;
31
+ if ((offset === 1 && row.messages[0]?.role !== "system")
32
+ || row.messages[offset]?.role !== "user"
33
+ || !row.messages[offset]?.content.trim()
34
+ || row.messages[offset + 1]?.role !== "assistant"
35
+ || !row.messages[offset + 1]?.content.trim()) {
36
+ throw new Error(`Invalid chat JSONL row ${index + 1}: expected an optional system message, one non-empty user message, and one non-empty assistant answer`);
70
37
  }
71
38
  rows.push(row);
72
39
  jsonLines.push(JSON.stringify(row));
73
40
  }
41
+ if (rows.length === 0)
42
+ throw new Error(`Chat JSONL contains no examples: ${path}`);
74
43
  return {
75
44
  rows,
76
45
  jsonl: jsonLines.join("\n"),
77
- localAssetPaths: [...localAssets].sort(),
78
46
  };
79
47
  }
80
- /** Rewrites relative image references so a copied training JSONL stays valid. */
48
+ /** Parses and canonically rewrites text chat JSONL before copying it. */
81
49
  export async function normalizeChatJsonlForRelocation(path) {
82
50
  return (await loadNormalizedChatJsonl(path)).jsonl;
83
51
  }
84
- export async function localAssetPathsFromChatJsonl(path) {
85
- return (await loadNormalizedChatJsonl(path)).localAssetPaths;
86
- }
87
52
  export function buildSystemMessage(spec) {
88
53
  const parts = [];
89
54
  if (spec.system_prompt.trim())
@@ -94,25 +59,14 @@ export function buildSystemMessage(spec) {
94
59
  if (spec.constraints.length > 0) {
95
60
  parts.push(`Constraints:\n${spec.constraints.map((constraint) => `- ${constraint}`).join("\n")}`);
96
61
  }
97
- return parts.join("\n\n");
62
+ return parts.join("\n\n") || "Follow the demonstrated behavior.";
98
63
  }
99
64
  export function exampleToChatRow(spec, example) {
100
65
  const system = buildSystemMessage(spec);
101
- const userContent = example.input_assets?.length
102
- ? [
103
- ...example.input_assets.map((asset) => ({
104
- type: "image",
105
- image: asset.image ?? asset.data_uri ?? asset.uri ?? asset.path,
106
- ...(asset.mime_type ? { mime_type: asset.mime_type } : {}),
107
- ...(asset.page ? { page: asset.page } : {}),
108
- })),
109
- { type: "text", text: example.input },
110
- ]
111
- : example.input;
112
66
  return {
113
67
  messages: [
114
68
  { role: "system", content: system },
115
- { role: "user", content: userContent },
69
+ { role: "user", content: example.input },
116
70
  { role: "assistant", content: example.output },
117
71
  ],
118
72
  };
@@ -127,61 +81,49 @@ export async function examplesFromChatJsonl(path) {
127
81
  const normalized = await loadNormalizedChatJsonl(path);
128
82
  const examples = [];
129
83
  for (const [index, row] of normalized.rows.entries()) {
130
- const user = row.messages.find((message) => message.role === "user");
131
- const assistant = [...row.messages].reverse().find((message) => message.role === "assistant");
132
- if (!user || !assistant || typeof assistant.content !== "string") {
133
- throw new Error(`Invalid chat JSONL row ${index + 1}: expected user and assistant messages`);
134
- }
135
- const inputAssets = [];
136
- let textInput = "";
137
- if (typeof user.content === "string") {
138
- textInput = user.content;
139
- if (Array.isArray(row.images)) {
140
- for (const image of row.images) {
141
- if (typeof image === "string" && image) {
142
- inputAssets.push({ type: "image", image });
143
- }
144
- }
145
- }
146
- }
147
- else {
148
- const topLevelImages = Array.isArray(row.images) ? row.images : [];
149
- let imageIndex = 0;
150
- const textParts = [];
151
- for (const part of user.content) {
152
- if (part.type === "text" && typeof part.text === "string") {
153
- textParts.push(part.text);
154
- continue;
155
- }
156
- if (part.type !== "image")
157
- continue;
158
- const image = typeof part.image === "string"
159
- ? part.image
160
- : typeof part.path === "string"
161
- ? part.path
162
- : typeof part.uri === "string"
163
- ? part.uri
164
- : typeof part.data_uri === "string"
165
- ? part.data_uri
166
- : topLevelImages[imageIndex];
167
- imageIndex += 1;
168
- if (image) {
169
- inputAssets.push({
170
- type: "image",
171
- image,
172
- ...(typeof part.mime_type === "string" ? { mime_type: part.mime_type } : {}),
173
- ...(typeof part.page === "number" ? { page: part.page } : {}),
174
- });
175
- }
176
- }
177
- textInput = textParts.join("\n").trim() || JSON.stringify(user.content);
178
- }
84
+ const user = row.messages.at(-2);
85
+ const assistant = row.messages.at(-1);
179
86
  examples.push({
180
- input: textInput,
87
+ input: user.content,
181
88
  output: assistant.content,
182
- ...(inputAssets.length ? { input_assets: inputAssets } : {}),
183
89
  });
184
90
  }
185
91
  return examples;
186
92
  }
93
+ /**
94
+ * Loads an evaluation-only chat JSONL suite. Rows may omit the system message,
95
+ * but when they include one it must be identical across the suite.
96
+ */
97
+ export async function evaluationSuiteFromChatJsonl(path, configuredSystem) {
98
+ const normalized = await loadNormalizedChatJsonl(path);
99
+ const inputIdentities = new Set();
100
+ for (const row of normalized.rows) {
101
+ const input = row.messages.at(-2).content;
102
+ const identity = input.trim().replace(/\s+/g, " ").toLowerCase();
103
+ if (inputIdentities.has(identity)) {
104
+ throw new Error("General regression data contains duplicate inputs; "
105
+ + "evaluation prompts must be unique.");
106
+ }
107
+ inputIdentities.add(identity);
108
+ }
109
+ const systems = new Set(normalized.rows
110
+ .map((row) => row.messages.find((message) => message.role === "system")?.content)
111
+ .filter((value) => value !== undefined));
112
+ if (systems.size > 1) {
113
+ throw new Error("General regression rows must use one consistent system message.");
114
+ }
115
+ const embeddedSystem = [...systems][0];
116
+ if (configuredSystem !== undefined
117
+ && embeddedSystem !== undefined
118
+ && configuredSystem !== embeddedSystem) {
119
+ throw new Error("evaluation.generalRegression.systemPrompt must match the system message embedded in the dataset.");
120
+ }
121
+ return {
122
+ examples: normalized.rows.map((row) => ({
123
+ input: row.messages.at(-2).content,
124
+ output: row.messages.at(-1).content,
125
+ })),
126
+ system: configuredSystem ?? embeddedSystem ?? "You are a helpful assistant.",
127
+ };
128
+ }
187
129
  //# sourceMappingURL=dataset.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dataset.js","sourceRoot":"","sources":["../src/dataset.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dataset.js","sourceRoot":"","sources":["../src/dataset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAe5C,KAAK,UAAU,uBAAuB,CAAC,IAAY;IACjD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAmB,EAAE,CAAC;IAChC,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC1D,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAC3B,IAAI,KAAc,CAAC;QACnB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,GAAG,CAAC,kBAAkB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3F,CAAC;QACD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,GAAG,CAAC,sBAAsB,CAAC,CAAC;QAC7E,CAAC;QACD,MAAM,QAAQ,GAAI,KAAgC,CAAC,QAAQ,CAAC;QAC5D,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;eACrB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;eACjC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAC3B,CAAC,OAAO;mBACL,OAAO,OAAO,KAAK,QAAQ;mBAC3B,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;mBACtB,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAE,OAA8B,CAAC,IAAI,CAAC,CAAC;mBACvF,OAAQ,OAAiC,CAAC,OAAO,KAAK,QAAQ,CAClE,EACD,CAAC;YACD,MAAM,IAAI,KAAK,CACb,0BAA0B,KAAK,GAAG,CAAC,mFAAmF,CACvH,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,GAAG,EAAE,QAAQ,EAAkB,CAAC;QACzC,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD,IACE,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,QAAQ,CAAC;eACjD,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,KAAK,MAAM;eACrC,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE;eACrC,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,IAAI,KAAK,WAAW;eAC9C,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,EAC5C,CAAC;YACD,MAAM,IAAI,KAAK,CACb,0BAA0B,KAAK,GAAG,CAAC,uGAAuG,CAC3I,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAC;IACnF,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;KAC5B,CAAC;AACJ,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,+BAA+B,CAAC,IAAY;IAChE,OAAO,CAAC,MAAM,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAkB;IACnD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC;IACrE,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,KAAK,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,KAAK,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,mCAAmC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAkB,EAAE,OAA4B;IAC/E,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACxC,OAAO;QACL,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE;YACnC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE;YACxC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE;SAC/C;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAkB;IACnD,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpG,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAkB;IACjD,OAAO,IAAI,CAAC,QAAQ,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAY;IACtD,MAAM,UAAU,GAAG,MAAM,uBAAuB,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC;QAClC,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC;QACvC,QAAQ,CAAC,IAAI,CAAC;YACZ,KAAK,EAAE,IAAI,CAAC,OAAO;YACnB,MAAM,EAAE,SAAS,CAAC,OAAO;SAC1B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAOD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,IAAY,EACZ,gBAAyB;IAEzB,MAAM,UAAU,GAAG,MAAM,uBAAuB,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC;QAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QACjE,IAAI,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACb,qDAAqD;kBACnD,oCAAoC,CACvC,CAAC;QACJ,CAAC;QACD,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,UAAU,CAAC,IAAI;SACZ,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,OAAO,CAAC;SAChF,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAC3D,CAAC;IACF,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,cAAc,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,IACE,gBAAgB,KAAK,SAAS;WAC3B,cAAc,KAAK,SAAS;WAC5B,gBAAgB,KAAK,cAAc,EACtC,CAAC;QACD,MAAM,IAAI,KAAK,CACb,kGAAkG,CACnG,CAAC;IACJ,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACtC,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,OAAO;YACnC,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,OAAO;SACrC,CAAC,CAAC;QACH,MAAM,EAAE,gBAAgB,IAAI,cAAc,IAAI,8BAA8B;KAC7E,CAAC;AACJ,CAAC"}
package/dist/doctor.d.ts CHANGED
@@ -6,13 +6,12 @@ export interface DoctorCheck {
6
6
  details?: Record<string, unknown>;
7
7
  }
8
8
  interface PythonProbePlan {
9
- name: "training-python" | "evaluation-python";
9
+ name: "python-runtime";
10
10
  command: string;
11
11
  args: string[];
12
- cwd?: string;
13
12
  env: NodeJS.ProcessEnv;
14
13
  }
15
- /** Build the exact uv environments that a configured run will use. */
14
+ /** Build the exact bundled uv environment that every real stage uses. */
16
15
  export declare function buildDoctorPythonPlans(config: LocalRunnerConfig): PythonProbePlan[];
17
16
  export declare function runDoctor(config: LocalRunnerConfig, request?: FineTuneRunRequest): Promise<DoctorCheck[]>;
18
17
  export {};
package/dist/doctor.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { constants } from "node:fs";
2
2
  import { access, mkdir, statfs, unlink, writeFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
- import { delimiter, isAbsolute, join, resolve } from "node:path";
4
+ import { join, resolve } from "node:path";
5
5
  import { spawn } from "node:child_process";
6
- import { withHuggingFaceCacheEnvironment } from "./huggingface-cache.js";
7
- import { buildEntrypointCommand } from "./process-runner.js";
6
+ import { minimalMachineLearningEnvironment, withHuggingFaceCacheEnvironment, } from "./huggingface-cache.js";
7
+ import { buildBundledPythonCommand, withBundledPythonEnvironment, } from "./process-runner.js";
8
8
  import { resolveTrainingModel } from "./model-registry.js";
9
9
  import { defaultLocalHome } from "./store.js";
10
10
  import { verifyLocalBaseModel } from "./prefetch.js";
@@ -91,27 +91,6 @@ function firstLine(value) {
91
91
  function commandText(command, args) {
92
92
  return [command, ...args].map((part) => /\s/.test(part) ? JSON.stringify(part) : part).join(" ");
93
93
  }
94
- async function executableCheck(command, options = {}) {
95
- const env = { ...process.env, ...options.env };
96
- const cwd = options.cwd ? resolve(options.cwd) : process.cwd();
97
- const candidates = isAbsolute(command) || command.includes("/")
98
- ? [isAbsolute(command) ? command : resolve(cwd, command)]
99
- : (env.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => join(isAbsolute(directory) ? directory : resolve(cwd, directory), command));
100
- for (const candidate of candidates) {
101
- try {
102
- await access(candidate, constants.X_OK);
103
- return { name: `executable:${command}`, ok: true, message: candidate };
104
- }
105
- catch {
106
- // Continue searching PATH.
107
- }
108
- }
109
- return {
110
- name: `executable:${command}`,
111
- ok: false,
112
- message: `${command} is not executable or was not found on PATH`,
113
- };
114
- }
115
94
  async function writableDirectoryCheck(name, path) {
116
95
  const resolvedPath = resolve(path);
117
96
  const probePath = join(resolvedPath, `.tt-local-write-probe-${process.pid}-${Date.now()}`);
@@ -144,80 +123,31 @@ function pythonProbeSource(device) {
144
123
  "import json",
145
124
  "import torch, transformers, peft, huggingface_hub",
146
125
  `requested = ${JSON.stringify(device)}`,
147
- "assert requested != 'cuda' or torch.cuda.is_available(), 'CUDA was requested but torch.cuda.is_available() is false'",
148
- "assert requested != 'mps' or (hasattr(torch.backends, 'mps') and torch.backends.mps.is_available()), 'MPS was requested but is unavailable'",
149
- "print(json.dumps({'python_ok': True, 'torch': torch.__version__, 'transformers': transformers.__version__, 'cuda_available': torch.cuda.is_available(), 'cuda_device': torch.cuda.get_device_name(0) if torch.cuda.is_available() else None}))",
126
+ "assert torch.cuda.is_available(), 'TT Local training requires CUDA but torch.cuda.is_available() is false'",
127
+ "props = torch.cuda.get_device_properties(0)",
128
+ "print(json.dumps({'python_ok': True, 'torch': torch.__version__, 'transformers': transformers.__version__, 'cuda_available': True, 'cuda_device': torch.cuda.get_device_name(0), 'compute_capability': list(torch.cuda.get_device_capability(0)), 'total_memory_bytes': props.total_memory, 'evaluation_device': requested}))",
150
129
  ].join("; ");
151
130
  }
152
131
  function pythonProbePlan(args) {
153
- const entrypoint = buildEntrypointCommand({
154
- ...args.entrypoint,
155
- backend: "uv",
156
- module: undefined,
157
- script: "-c",
158
- args: [],
159
- }, {
160
- extraArgs: [pythonProbeSource(args.device)],
161
- });
162
- const env = withHuggingFaceCacheEnvironment({
163
- ...process.env,
164
- ...args.env,
165
- }, args.modelCache);
132
+ const entrypoint = buildBundledPythonCommand("-c", [pythonProbeSource(args.device)]);
133
+ const env = withBundledPythonEnvironment(withHuggingFaceCacheEnvironment(minimalMachineLearningEnvironment(process.env), args.modelCache));
166
134
  return {
167
- name: args.name,
135
+ name: "python-runtime",
168
136
  command: entrypoint.command,
169
137
  args: entrypoint.commandArgs,
170
- cwd: args.cwd,
171
138
  env,
172
139
  };
173
140
  }
174
- /** Build the exact uv environments that a configured run will use. */
141
+ /** Build the exact bundled uv environment that every real stage uses. */
175
142
  export function buildDoctorPythonPlans(config) {
176
143
  if (config.dryRun)
177
144
  return [];
178
- const plans = [];
179
- if (config.training.backend === "uv") {
180
- plans.push(pythonProbePlan({
181
- name: "training-python",
182
- entrypoint: config.training,
183
- cwd: config.training.cwd,
184
- env: config.training.env,
185
- device: config.evaluation.inference.device,
186
- modelCache: config.paths.modelCache,
187
- }));
188
- }
189
- if (config.evaluation.inference.provider === "transformers") {
190
- plans.push(pythonProbePlan({
191
- name: "evaluation-python",
192
- entrypoint: config.evaluation.inference,
193
- cwd: config.evaluation.inference.cwd,
194
- env: config.evaluation.inference.env,
145
+ return [
146
+ pythonProbePlan({
195
147
  device: config.evaluation.inference.device,
196
148
  modelCache: config.paths.modelCache,
197
- }));
198
- }
199
- return plans;
200
- }
201
- function configuredCommandChecks(config) {
202
- const checks = [];
203
- if (config.training.backend === "command" && !config.dryRun) {
204
- checks.push({
205
- name: "training-command",
206
- command: config.training.command?.[0],
207
- cwd: config.training.cwd,
208
- env: config.training.env,
209
- });
210
- }
211
- const inference = config.evaluation.inference;
212
- if ((inference.provider === "command" || inference.provider === "batch_command") && !config.dryRun) {
213
- checks.push({
214
- name: "evaluation-command",
215
- command: inference.command?.[0],
216
- cwd: inference.cwd,
217
- env: inference.env,
218
- });
219
- }
220
- return checks;
149
+ }),
150
+ ];
221
151
  }
222
152
  function placeholderSpecCheck(request) {
223
153
  const placeholder = request.spec_snapshot.examples.some((example) => /replace this with/i.test(example.input) || /replace this with/i.test(example.output)) || /describe the behavior this local model should learn/i.test(request.spec_snapshot.system_prompt);
@@ -229,51 +159,6 @@ function placeholderSpecCheck(request) {
229
159
  : `${request.spec_snapshot.examples.length} spec example(s); base model ${request.spec_snapshot.base_model}`,
230
160
  };
231
161
  }
232
- function gatedModelTokenCheck(request) {
233
- let model;
234
- try {
235
- model = resolveTrainingModel(request.spec_snapshot.base_model);
236
- }
237
- catch {
238
- return null;
239
- }
240
- if (!model.requiresHfToken)
241
- return null;
242
- const available = Boolean(process.env.HF_TOKEN?.trim());
243
- return {
244
- name: "hugging-face-token",
245
- ok: available,
246
- message: available
247
- ? `HF_TOKEN is configured for gated model ${model.id}.`
248
- : `${model.id} is gated and requires a non-empty HF_TOKEN.`,
249
- };
250
- }
251
- function judgeCheck(config) {
252
- if (config.evaluation.scoring.mode !== "llm_judge")
253
- return null;
254
- if (config.evaluation.scoring.fallback === "exact_match" && !config.llm) {
255
- return {
256
- name: "llm-judge",
257
- ok: true,
258
- message: "LLM judge is not configured; explicit exact_match fallback will be used.",
259
- };
260
- }
261
- if (!config.llm) {
262
- return {
263
- name: "llm-judge",
264
- ok: false,
265
- message: "evaluation.scoring.mode=llm_judge requires an llm configuration, or explicitly set fallback=exact_match.",
266
- };
267
- }
268
- const available = Boolean(process.env[config.llm.apiKeyEnv]);
269
- return {
270
- name: "llm-judge",
271
- ok: available || config.evaluation.scoring.fallback === "exact_match",
272
- message: available
273
- ? `${config.llm.model} credentials found in ${config.llm.apiKeyEnv}`
274
- : `${config.llm.apiKeyEnv} is not set${config.evaluation.scoring.fallback === "exact_match" ? "; explicit exact_match fallback will be used" : ""}`,
275
- };
276
- }
277
162
  async function localBaseModelCheck(path) {
278
163
  const resolvedPath = resolve(path);
279
164
  try {
@@ -306,13 +191,8 @@ export async function runDoctor(config, request) {
306
191
  checks.push(await localBaseModelCheck(config.paths.baseModel));
307
192
  if (request) {
308
193
  checks.push(placeholderSpecCheck(request));
309
- const gatedToken = gatedModelTokenCheck(request);
310
- if (gatedToken)
311
- checks.push(gatedToken);
194
+ resolveTrainingModel(request.spec_snapshot.base_model);
312
195
  }
313
- const judge = judgeCheck(config);
314
- if (judge)
315
- checks.push(judge);
316
196
  const pythonPlans = buildDoctorPythonPlans(config);
317
197
  if (pythonPlans.length > 0) {
318
198
  const uvVersion = await runCommand("uv", ["--version"]);
@@ -326,13 +206,12 @@ export async function runDoctor(config, request) {
326
206
  if (uvVersion.code === 0) {
327
207
  const uniquePlans = new Map();
328
208
  for (const plan of pythonPlans) {
329
- const key = JSON.stringify([plan.command, plan.args, plan.cwd, plan.env]);
209
+ const key = JSON.stringify([plan.command, plan.args, plan.env]);
330
210
  if (!uniquePlans.has(key))
331
211
  uniquePlans.set(key, plan);
332
212
  }
333
213
  for (const plan of uniquePlans.values()) {
334
214
  const result = await runCommand(plan.command, plan.args, {
335
- cwd: plan.cwd,
336
215
  env: plan.env,
337
216
  timeoutMs: 1_800_000,
338
217
  });
@@ -342,7 +221,7 @@ export async function runDoctor(config, request) {
342
221
  message: result.code === 0
343
222
  ? firstLine(result.stdout)
344
223
  : result.error ?? (firstLine(result.stderr) || `${plan.command} exited ${result.code}`),
345
- details: { command: commandText(plan.command, plan.args), cwd: plan.cwd ?? process.cwd() },
224
+ details: { command: commandText(plan.command, plan.args) },
346
225
  });
347
226
  }
348
227
  }
@@ -353,29 +232,18 @@ export async function runDoctor(config, request) {
353
232
  ok: true,
354
233
  message: config.dryRun
355
234
  ? "Python dependency checks skipped because dryRun is enabled."
356
- : "The configured command/none backends do not require the bundled Python runtime.",
235
+ : "Python dependency checks were skipped.",
357
236
  });
358
237
  }
359
- for (const entry of configuredCommandChecks(config)) {
360
- if (!entry.command) {
361
- checks.push({ name: entry.name, ok: false, message: `${entry.name} has no command configured` });
362
- }
363
- else {
364
- const check = await executableCheck(entry.command, { cwd: entry.cwd, env: entry.env });
365
- checks.push({ ...check, name: entry.name });
366
- }
367
- }
368
238
  const device = config.evaluation.inference.device;
369
- if (!config.dryRun && (device === "cuda" || device === "auto")) {
239
+ if (!config.dryRun) {
370
240
  const nvidiaSmi = await runCommand("nvidia-smi", []);
371
241
  checks.push({
372
242
  name: "nvidia-smi",
373
- ok: device === "auto" || nvidiaSmi.code === 0,
243
+ ok: nvidiaSmi.code === 0,
374
244
  message: nvidiaSmi.code === 0
375
245
  ? firstLine(nvidiaSmi.stdout)
376
- : device === "auto"
377
- ? "nvidia-smi is unavailable; device=auto may use MPS or CPU."
378
- : nvidiaSmi.error ?? (firstLine(nvidiaSmi.stderr) || "nvidia-smi not available"),
246
+ : nvidiaSmi.error ?? (firstLine(nvidiaSmi.stderr) || "nvidia-smi not available"),
379
247
  });
380
248
  }
381
249
  else {
@@ -387,20 +255,14 @@ export async function runDoctor(config, request) {
387
255
  : `nvidia-smi is not required for device=${device}.`,
388
256
  });
389
257
  }
390
- const trainingCommand = config.training.backend === "command"
391
- ? config.training.command
392
- : buildEntrypointCommand(config.training, {
393
- defaultScript: request?.training_method === "dpo"
394
- ? "training/local-runner/src/train_dpo.py"
395
- : "training/local-runner/src/train.py",
396
- }).displayCommand;
258
+ const trainingCommand = buildBundledPythonCommand("train.py").displayCommand;
397
259
  checks.push({
398
260
  name: "effective-plan",
399
261
  ok: true,
400
262
  message: "Resolved the configured training and evaluation plan.",
401
263
  details: {
402
264
  training_command: trainingCommand ?? null,
403
- evaluation_provider: config.evaluation.inference.provider,
265
+ evaluation_provider: "transformers",
404
266
  evaluation_device: device,
405
267
  artifact_root: resolve(config.artifactRoot),
406
268
  store_root: resolve(config.storeRoot ?? defaultLocalHome()),