dsh-plugin-local-infer 0.1.3 → 0.1.4

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 (250) hide show
  1. package/README.md +22 -4
  2. package/cordis.patch.yml +7 -0
  3. package/docs/zh/README.md +14 -2
  4. package/dsh/client.js +4195 -4522
  5. package/package.json +28 -11
  6. package/packages/core/lib/adapter.d.ts +133 -9
  7. package/packages/core/lib/adapter.js +74 -1
  8. package/packages/core/lib/config.d.ts +1 -1
  9. package/packages/core/lib/config.js +1 -1
  10. package/packages/core/lib/debug.d.ts +1 -1
  11. package/packages/core/lib/debug.js +1 -1
  12. package/packages/core/lib/defaults.d.ts +1 -1
  13. package/packages/core/lib/defaults.js +1 -1
  14. package/packages/core/lib/deps.d.ts +44 -17
  15. package/packages/core/lib/deps.js +24 -8
  16. package/packages/core/lib/estimator.d.ts +1 -1
  17. package/packages/core/lib/estimator.js +1 -1
  18. package/packages/core/lib/guideline.d.ts +1 -1
  19. package/packages/core/lib/guideline.js +1 -1
  20. package/packages/core/lib/hardware.d.ts +1 -1
  21. package/packages/core/lib/hardware.js +1 -1
  22. package/packages/core/lib/hf-cache.d.ts +70 -0
  23. package/packages/core/lib/hf-cache.js +83 -0
  24. package/packages/core/lib/index.d.ts +16 -9
  25. package/packages/core/lib/index.js +10 -6
  26. package/packages/core/lib/infer-cache.d.ts +39 -0
  27. package/packages/core/lib/infer-cache.js +53 -0
  28. package/packages/core/lib/launch-command.d.ts +8 -4
  29. package/packages/core/lib/launch-command.js +13 -6
  30. package/packages/core/lib/launch-record.d.ts +1 -1
  31. package/packages/core/lib/launch-record.js +1 -1
  32. package/packages/core/lib/logs.d.ts +8 -1
  33. package/packages/core/lib/logs.js +12 -4
  34. package/packages/core/lib/models.d.ts +120 -14
  35. package/packages/core/lib/models.js +209 -17
  36. package/packages/core/lib/parameter.d.ts +31 -3
  37. package/packages/core/lib/parameter.js +1 -1
  38. package/packages/core/lib/progress.d.ts +54 -0
  39. package/packages/core/lib/progress.js +120 -0
  40. package/packages/core/lib/registrar.d.ts +20 -9
  41. package/packages/core/lib/registrar.js +27 -7
  42. package/packages/core/lib/reserve.d.ts +11 -3
  43. package/packages/core/lib/reserve.js +6 -4
  44. package/packages/core/lib/status.d.ts +12 -6
  45. package/packages/core/lib/status.js +21 -10
  46. package/packages/core/lib/sudo.d.ts +80 -0
  47. package/packages/core/lib/sudo.js +78 -0
  48. package/packages/core/package.json +1 -1
  49. package/packages/orchestrator/lib/config.d.ts +63 -0
  50. package/packages/orchestrator/lib/config.js +111 -0
  51. package/packages/orchestrator/lib/index.d.ts +51 -0
  52. package/packages/orchestrator/lib/index.js +73 -0
  53. package/packages/orchestrator/lib/route.d.ts +60 -0
  54. package/packages/orchestrator/lib/route.js +159 -0
  55. package/packages/orchestrator/locale/en.json +6 -0
  56. package/packages/orchestrator/package.json +39 -0
  57. package/packages/orchestrator/prompt.md +87 -0
  58. package/packages/providers/lib/acquire.d.ts +92 -14
  59. package/packages/providers/lib/acquire.js +215 -37
  60. package/packages/providers/lib/adapters.d.ts +136 -12
  61. package/packages/providers/lib/adapters.js +597 -70
  62. package/packages/providers/lib/debug.d.ts +1 -1
  63. package/packages/providers/lib/debug.js +1 -1
  64. package/packages/providers/lib/deps.d.ts +27 -230
  65. package/packages/providers/lib/deps.js +61 -214
  66. package/packages/providers/lib/engines/basalt/basalt.d.ts +130 -0
  67. package/packages/providers/lib/engines/basalt/basalt.js +304 -0
  68. package/packages/providers/lib/engines/basalt/basaltAdapter.d.ts +85 -0
  69. package/packages/providers/lib/engines/basalt/basaltAdapter.js +439 -0
  70. package/packages/providers/lib/engines/basalt/basaltLinux.d.ts +23 -0
  71. package/packages/providers/lib/engines/basalt/basaltLinux.js +47 -0
  72. package/packages/providers/lib/engines/basalt/basaltMacOS.d.ts +17 -0
  73. package/packages/providers/lib/engines/basalt/basaltMacOS.js +19 -0
  74. package/packages/providers/lib/engines/basalt/basaltWindows.d.ts +24 -0
  75. package/packages/providers/lib/engines/basalt/basaltWindows.js +49 -0
  76. package/packages/providers/lib/engines/engineOs.d.ts +24 -0
  77. package/packages/providers/lib/engines/engineOs.js +17 -0
  78. package/packages/providers/lib/engines/{hyperqwen.d.ts → hyperqwen/hyperqwen.d.ts} +2 -2
  79. package/packages/providers/lib/engines/{hyperqwen.js → hyperqwen/hyperqwen.js} +14 -2
  80. package/packages/providers/lib/engines/{hyperqwenAdapter.d.ts → hyperqwen/hyperqwenAdapter.d.ts} +6 -6
  81. package/packages/providers/lib/engines/{hyperqwenAdapter.js → hyperqwen/hyperqwenAdapter.js} +24 -14
  82. package/packages/providers/lib/engines/hyperqwen/hyperqwenArch.d.ts +9 -0
  83. package/packages/providers/lib/engines/hyperqwen/hyperqwenArch.js +9 -0
  84. package/packages/providers/lib/engines/hyperqwen/hyperqwenDebian.d.ts +9 -0
  85. package/packages/providers/lib/engines/hyperqwen/hyperqwenDebian.js +9 -0
  86. package/packages/providers/lib/engines/hyperqwen/hyperqwenFedora.d.ts +9 -0
  87. package/packages/providers/lib/engines/hyperqwen/hyperqwenFedora.js +9 -0
  88. package/packages/providers/lib/engines/hyperqwen/hyperqwenLinux.d.ts +10 -0
  89. package/packages/providers/lib/engines/hyperqwen/hyperqwenLinux.js +14 -0
  90. package/packages/providers/lib/engines/hyperqwen/hyperqwenMacOS.d.ts +9 -0
  91. package/packages/providers/lib/engines/hyperqwen/hyperqwenMacOS.js +9 -0
  92. package/packages/providers/lib/engines/hyperqwen/hyperqwenWindows.d.ts +9 -0
  93. package/packages/providers/lib/engines/hyperqwen/hyperqwenWindows.js +9 -0
  94. package/packages/providers/lib/engines/{infernix.d.ts → infernix/infernix.d.ts} +7 -4
  95. package/packages/providers/lib/engines/{infernix.js → infernix/infernix.js} +52 -7
  96. package/packages/providers/lib/engines/{infernixAdapter.d.ts → infernix/infernixAdapter.d.ts} +5 -6
  97. package/packages/providers/lib/engines/{infernixAdapter.js → infernix/infernixAdapter.js} +75 -42
  98. package/packages/providers/lib/engines/infernix/infernixArch.d.ts +9 -0
  99. package/packages/providers/lib/engines/infernix/infernixArch.js +9 -0
  100. package/packages/providers/lib/engines/infernix/infernixDebian.d.ts +9 -0
  101. package/packages/providers/lib/engines/infernix/infernixDebian.js +9 -0
  102. package/packages/providers/lib/engines/infernix/infernixFedora.d.ts +9 -0
  103. package/packages/providers/lib/engines/infernix/infernixFedora.js +9 -0
  104. package/packages/providers/lib/engines/infernix/infernixLinux.d.ts +17 -0
  105. package/packages/providers/lib/engines/infernix/infernixLinux.js +35 -0
  106. package/packages/providers/lib/engines/infernix/infernixMacOS.d.ts +16 -0
  107. package/packages/providers/lib/engines/infernix/infernixMacOS.js +18 -0
  108. package/packages/providers/lib/engines/infernix/infernixWindows.d.ts +16 -0
  109. package/packages/providers/lib/engines/infernix/infernixWindows.js +34 -0
  110. package/packages/providers/lib/engines/{llamacpp.d.ts → llamacpp/llamacpp.d.ts} +2 -2
  111. package/packages/providers/lib/engines/{llamacpp.js → llamacpp/llamacpp.js} +12 -2
  112. package/packages/providers/lib/engines/{llamacppAdapter.d.ts → llamacpp/llamacppAdapter.d.ts} +5 -6
  113. package/packages/providers/lib/engines/{llamacppAdapter.js → llamacpp/llamacppAdapter.js} +87 -45
  114. package/packages/providers/lib/engines/llamacpp/llamacppArch.d.ts +9 -0
  115. package/packages/providers/lib/engines/llamacpp/llamacppArch.js +9 -0
  116. package/packages/providers/lib/engines/llamacpp/llamacppDebian.d.ts +9 -0
  117. package/packages/providers/lib/engines/llamacpp/llamacppDebian.js +9 -0
  118. package/packages/providers/lib/engines/llamacpp/llamacppFedora.d.ts +9 -0
  119. package/packages/providers/lib/engines/llamacpp/llamacppFedora.js +9 -0
  120. package/packages/providers/lib/engines/llamacpp/llamacppLinux.d.ts +17 -0
  121. package/packages/providers/lib/engines/llamacpp/llamacppLinux.js +28 -0
  122. package/packages/providers/lib/engines/llamacpp/llamacppMacOS.d.ts +15 -0
  123. package/packages/providers/lib/engines/llamacpp/llamacppMacOS.js +17 -0
  124. package/packages/providers/lib/engines/llamacpp/llamacppWindows.d.ts +15 -0
  125. package/packages/providers/lib/engines/llamacpp/llamacppWindows.js +26 -0
  126. package/packages/providers/lib/engines/{ninfer.d.ts → ninfer/ninfer.d.ts} +2 -2
  127. package/packages/providers/lib/engines/{ninfer.js → ninfer/ninfer.js} +24 -2
  128. package/packages/providers/lib/engines/{ninferAdapter.d.ts → ninfer/ninferAdapter.d.ts} +5 -6
  129. package/packages/providers/lib/engines/{ninferAdapter.js → ninfer/ninferAdapter.js} +123 -55
  130. package/packages/providers/lib/engines/ninfer/ninferArch.d.ts +9 -0
  131. package/packages/providers/lib/engines/ninfer/ninferArch.js +9 -0
  132. package/packages/providers/lib/engines/ninfer/ninferDebian.d.ts +9 -0
  133. package/packages/providers/lib/engines/ninfer/ninferDebian.js +9 -0
  134. package/packages/providers/lib/engines/ninfer/ninferFedora.d.ts +9 -0
  135. package/packages/providers/lib/engines/ninfer/ninferFedora.js +9 -0
  136. package/packages/providers/lib/engines/ninfer/ninferLinux.d.ts +17 -0
  137. package/packages/providers/lib/engines/ninfer/ninferLinux.js +37 -0
  138. package/packages/providers/lib/engines/ninfer/ninferMacOS.d.ts +15 -0
  139. package/packages/providers/lib/engines/ninfer/ninferMacOS.js +17 -0
  140. package/packages/providers/lib/engines/ninfer/ninferWindows.d.ts +29 -0
  141. package/packages/providers/lib/engines/ninfer/ninferWindows.js +59 -0
  142. package/packages/providers/lib/engines/strata/strata.d.ts +1 -0
  143. package/packages/providers/lib/engines/{strata.js → strata/strata.js} +70 -3
  144. package/packages/providers/lib/engines/{strataAdapter.d.ts → strata/strataAdapter.d.ts} +5 -6
  145. package/packages/providers/lib/engines/{strataAdapter.js → strata/strataAdapter.js} +147 -111
  146. package/packages/providers/lib/engines/strata/strataArch.d.ts +9 -0
  147. package/packages/providers/lib/engines/strata/strataArch.js +9 -0
  148. package/packages/providers/lib/engines/strata/strataDebian.d.ts +9 -0
  149. package/packages/providers/lib/engines/strata/strataDebian.js +9 -0
  150. package/packages/providers/lib/engines/strata/strataFedora.d.ts +9 -0
  151. package/packages/providers/lib/engines/strata/strataFedora.js +9 -0
  152. package/packages/providers/lib/engines/strata/strataLinux.d.ts +21 -0
  153. package/packages/providers/lib/engines/strata/strataLinux.js +30 -0
  154. package/packages/providers/lib/engines/strata/strataMacOS.d.ts +16 -0
  155. package/packages/providers/lib/engines/strata/strataMacOS.js +17 -0
  156. package/packages/providers/lib/engines/strata/strataWindows.d.ts +27 -0
  157. package/packages/providers/lib/engines/strata/strataWindows.js +37 -0
  158. package/packages/providers/lib/engines.d.ts +7 -5
  159. package/packages/providers/lib/engines.js +21 -10
  160. package/packages/providers/lib/guidelines.d.ts +5 -4
  161. package/packages/providers/lib/guidelines.js +18 -5
  162. package/packages/providers/lib/index.d.ts +33 -29
  163. package/packages/providers/lib/index.js +45 -33
  164. package/packages/providers/lib/model-defaults.d.ts +5 -1
  165. package/packages/providers/lib/model-defaults.js +25 -1
  166. package/packages/providers/lib/platform.d.ts +8 -5
  167. package/packages/providers/lib/platform.js +23 -9
  168. package/packages/providers/lib/presets.d.ts +6 -4
  169. package/packages/providers/lib/presets.js +53 -5
  170. package/packages/providers/lib/systemDeps.d.ts +313 -0
  171. package/packages/providers/lib/systemDeps.js +284 -0
  172. package/packages/providers/lib/vram.d.ts +34 -8
  173. package/packages/providers/lib/vram.js +81 -8
  174. package/packages/providers/package.json +2 -2
  175. package/packages/service/lib/index.d.ts +176 -21
  176. package/packages/service/lib/index.js +589 -86
  177. package/packages/service/lib/launch-target.d.ts +19 -0
  178. package/packages/service/lib/launch-target.js +28 -0
  179. package/packages/service/lib/run-logs.d.ts +72 -0
  180. package/packages/service/lib/run-logs.js +125 -0
  181. package/packages/service/lib/types.d.ts +163 -6
  182. package/packages/service/lib/types.js +1 -1
  183. package/packages/service/package.json +2 -2
  184. package/packages/tool/lib/index.d.ts +1 -1
  185. package/packages/tool/lib/index.js +1 -1
  186. package/packages/tool/package.json +2 -2
  187. package/packages/ui/lib/config.d.ts +12 -2
  188. package/packages/ui/lib/config.js +18 -2
  189. package/packages/ui/lib/deps.d.ts +1 -1
  190. package/packages/ui/lib/deps.js +1 -1
  191. package/packages/ui/lib/form.d.ts +1 -1
  192. package/packages/ui/lib/form.js +1 -1
  193. package/packages/ui/lib/index.d.ts +7 -1
  194. package/packages/ui/lib/index.js +12 -2
  195. package/packages/ui/lib/meter.d.ts +1 -1
  196. package/packages/ui/lib/meter.js +1 -1
  197. package/packages/ui/lib/ops/engine.d.ts +142 -34
  198. package/packages/ui/lib/ops/engine.js +469 -265
  199. package/packages/ui/lib/ops/get-steps.d.ts +98 -0
  200. package/packages/ui/lib/ops/get-steps.js +359 -0
  201. package/packages/ui/lib/ops/hf.d.ts +26 -11
  202. package/packages/ui/lib/ops/hf.js +115 -25
  203. package/packages/ui/lib/ops/logs.d.ts +70 -15
  204. package/packages/ui/lib/ops/logs.js +96 -35
  205. package/packages/ui/lib/ops/model.d.ts +2 -10
  206. package/packages/ui/lib/ops/model.js +9 -16
  207. package/packages/ui/lib/request.d.ts +1 -1
  208. package/packages/ui/lib/request.js +1 -1
  209. package/packages/ui/lib/route.d.ts +22 -6
  210. package/packages/ui/lib/route.js +32 -9
  211. package/packages/ui/lib/routes/chat-template.d.ts +1 -14
  212. package/packages/ui/lib/routes/chat-template.js +1 -37
  213. package/packages/ui/lib/routes/deps.d.ts +1 -16
  214. package/packages/ui/lib/routes/deps.js +1 -27
  215. package/packages/ui/lib/routes/download.d.ts +1 -16
  216. package/packages/ui/lib/routes/download.js +1 -48
  217. package/packages/ui/lib/routes/get.d.ts +1 -18
  218. package/packages/ui/lib/routes/get.js +66 -17
  219. package/packages/ui/lib/routes/install.d.ts +1 -1
  220. package/packages/ui/lib/routes/install.js +1 -1
  221. package/packages/ui/lib/routes/launch.d.ts +13 -7
  222. package/packages/ui/lib/routes/launch.js +33 -9
  223. package/packages/ui/lib/routes/logs.d.ts +57 -0
  224. package/packages/ui/lib/routes/logs.js +117 -0
  225. package/packages/ui/lib/routes/provider-delete.d.ts +1 -1
  226. package/packages/ui/lib/routes/provider-delete.js +1 -1
  227. package/packages/ui/lib/routes/register.d.ts +1 -1
  228. package/packages/ui/lib/routes/register.js +1 -1
  229. package/packages/ui/lib/routes/save.d.ts +1 -1
  230. package/packages/ui/lib/routes/save.js +22 -2
  231. package/packages/ui/lib/routes/stop.d.ts +7 -4
  232. package/packages/ui/lib/routes/stop.js +7 -4
  233. package/packages/ui/lib/routes/stream.d.ts +40 -0
  234. package/packages/ui/lib/routes/stream.js +68 -0
  235. package/packages/ui/lib/routes/summary.d.ts +1 -1
  236. package/packages/ui/lib/routes/summary.js +1 -1
  237. package/packages/ui/lib/routes/unregister.d.ts +1 -1
  238. package/packages/ui/lib/routes/unregister.js +1 -1
  239. package/packages/ui/lib/viewmodels/deps.d.ts +1 -31
  240. package/packages/ui/lib/viewmodels/deps.js +1 -89
  241. package/packages/ui/lib/viewmodels/depsHelpers.d.ts +1 -28
  242. package/packages/ui/lib/viewmodels/depsHelpers.js +72 -11
  243. package/packages/ui/lib/viewmodels/summary.d.ts +7 -2
  244. package/packages/ui/lib/viewmodels/summary.js +55 -35
  245. package/packages/ui/lib/viewmodels/summaryHelpers.d.ts +1 -1
  246. package/packages/ui/lib/viewmodels/summaryHelpers.js +1 -1
  247. package/packages/ui/lib/warnings.d.ts +1 -1
  248. package/packages/ui/lib/warnings.js +1 -1
  249. package/packages/ui/package.json +4 -4
  250. package/packages/providers/lib/engines/strata.d.ts +0 -53
@@ -1,22 +1,53 @@
1
1
  /**
2
2
  * The hf CLI download operations: downloadFor runs the download for a default
3
3
  * model, and chatTemplateDownloadFor pulls a selected chat template into the
4
- * model directory. Both resolve the directory against the request, the
5
- * stored config, and the plugin's default model directory in that order.
4
+ * model directory. Both resolve the directory through resolveDownloadTarget
5
+ * (the request, the stored config, and the hub cache root in that order,
6
+ * through the shared override rule), so the hub-cache layout rule has one
7
+ * home across the download routes and the card.
6
8
  * @module dsh-plugin-local-infer/ui/ops/hf
7
9
  */
10
+ import { isModelDirectoryOverridden, modelDownloadTarget } from '../../../core/lib/index.js';
11
+ import { runLogKey } from '../../../service/lib/index.js';
8
12
  import { defaultModelFor } from '../../../providers/lib/index.js';
9
13
  import { readConfig } from "../config.js";
14
+ import { CANCELED_LINE, DOWNLOAD_RUN_SEED, persistProcessLog } from "./logs.js";
10
15
  import { RouteError } from "../route.js";
11
16
  import { isHfRepoReference } from "../viewmodels/summaryHelpers.js";
17
+ /**
18
+ * resolveDownloadTarget resolves the directory a download route targets: the
19
+ * request's directory, the stored config's model directory, and the hub
20
+ * cache root, in that order. The first named directory wins (the shared
21
+ * override predicate), then the shared modelDownloadTarget rule decides the
22
+ * layout: a named directory keeps the flat --local-dir target; only when
23
+ * neither names one does the hub-cache layout apply, based on the hub cache
24
+ * root (the service seam: the hf CLI's own download home, which resolves its
25
+ * own snapshot destination and creates the directory when absent), so a
26
+ * download never materializes a snapshot-directory or hub-root override into
27
+ * the config.
28
+ */
29
+ function resolveDownloadTarget(localInfer, requestDirectory, stored) {
30
+ const named = isModelDirectoryOverridden(requestDirectory)
31
+ ? requestDirectory
32
+ : stored.config.modelDirectory;
33
+ return modelDownloadTarget(named, localInfer.defaultModelDirectory());
34
+ }
12
35
  /**
13
36
  * downloadFor runs the hf CLI download for the request's default model: it
14
37
  * resolves the entry through the providers' default list (a 404 when no
15
- * entry names the pair), resolves the directory against the request, the
16
- * stored config, and the plugin's default model directory in that order, and
17
- * runs the download through the service.
38
+ * entry names the pair), resolves the directory through
39
+ * resolveDownloadTarget, and runs the download through the service. The run
40
+ * brackets one live hub run under the runLogKey rule keyed on the entry's
41
+ * engine and model (a draft falls back to the engine id): the seed is the
42
+ * "Downloading..." separator, the hf CLI's streamed chunks append as they
43
+ * land, and the settled completion or failure line reaches the hub and the
44
+ * persisted run from one text before the run ends. The optional signal is
45
+ * the card's cancel (the download route's response-close abort, the Get and
46
+ * launch routes' rule): an abort interrupts the in-flight hf child (SIGINT,
47
+ * then SIGTERM) and the run carries the shared "Canceled" line, live and
48
+ * persisted.
18
49
  */
19
- export async function downloadFor(localInfer, request) {
50
+ export async function downloadFor(localInfer, request, signal) {
20
51
  const stored = readConfig();
21
52
  const engineId = request.engine ?? stored.engine;
22
53
  const modelId = request.model ?? (typeof stored.config.model === 'string' ? stored.config.model : '');
@@ -24,23 +55,62 @@ export async function downloadFor(localInfer, request) {
24
55
  if (entry === undefined) {
25
56
  throw new RouteError(404, `no default model is registered for "${engineId}/${modelId}"`);
26
57
  }
27
- const storedDir = stored.config.modelDirectory;
28
- const directory = typeof request.directory === 'string' && request.directory.trim() !== ''
29
- ? request.directory
30
- : typeof storedDir === 'string' && storedDir.trim() !== ''
31
- ? storedDir
32
- : localInfer.defaultModelDirectory();
33
- return localInfer.downloadModel(entry, directory);
58
+ const { baseDir, useHubCache } = resolveDownloadTarget(localInfer, request.directory, stored);
59
+ // The key rule prices the model this download fetches, so the run lands on
60
+ // the stored entry for that engine and model, not the draft's.
61
+ const runKey = runLogKey(stored.providers, engineId, undefined, {
62
+ ...stored.config,
63
+ ...(modelId === '' ? {} : { model: modelId }),
64
+ });
65
+ localInfer.beginRun(runKey, DOWNLOAD_RUN_SEED);
66
+ // The service writes the rendered hf command's `$ <command>` line through
67
+ // onLog before it spawns the child: it streams into the live hub right away
68
+ // and rides the persisted section below, so the command line reads exactly
69
+ // once per run in both readers, ahead of the CLI's output.
70
+ let commandLine = '';
71
+ try {
72
+ const result = await localInfer.downloadModel(entry, baseDir, {
73
+ useHubCache,
74
+ ...(signal !== undefined ? { signal } : {}),
75
+ onChunk: (chunk) => {
76
+ localInfer.appendRunLog(runKey, chunk);
77
+ },
78
+ onLog: (line) => {
79
+ commandLine += line;
80
+ localInfer.appendRunLog(runKey, line);
81
+ },
82
+ });
83
+ // The settle line reads the same in the live run and the persisted one:
84
+ // the "Downloading..." separator leads the stored run so the Logs
85
+ // section and the log file read the download as a fresh process section.
86
+ // A cancel appends the shared "Canceled" line, the Get and launch
87
+ // routes' mark, so the run reads as canceled wherever it is read.
88
+ const line = result.ok
89
+ ? 'download complete\n'
90
+ : `!! download failed${result.detail !== undefined ? `: ${result.detail}` : ''}\n`;
91
+ const settled = signal?.aborted === true ? `${line}${CANCELED_LINE}` : line;
92
+ localInfer.appendRunLog(runKey, settled);
93
+ persistProcessLog(`${DOWNLOAD_RUN_SEED}${commandLine}${settled}`);
94
+ return result;
95
+ }
96
+ finally {
97
+ localInfer.endRun(runKey);
98
+ }
34
99
  }
35
100
  /**
36
101
  * chatTemplateDownloadFor runs the hf CLI download for the request's chat
37
102
  * template: it resolves the template repo reference from the request or the
38
103
  * stored config's chatTemplate (a 400 when neither names an HF repo
39
- * reference), resolves the directory against the request, the stored config,
40
- * and the plugin's default model directory in that order, and runs the
41
- * download through the service.
104
+ * reference), resolves the directory through resolveDownloadTarget, and
105
+ * runs the download through the service. The run brackets one live hub run
106
+ * under the "Downloading..." seed and the runLogKey rule (the stored entry
107
+ * matching the engine and model, else the engine id): the hf CLI's streamed
108
+ * chunks append as they land, and the run ends at settle. The optional
109
+ * signal is the card's cancel (the chat-template route's response-close
110
+ * abort, the Get and launch routes' rule): an abort interrupts the in-flight
111
+ * hf child and the live run carries the shared "Canceled" line.
42
112
  */
43
- export async function chatTemplateDownloadFor(localInfer, request) {
113
+ export async function chatTemplateDownloadFor(localInfer, request, signal) {
44
114
  const stored = readConfig();
45
115
  let template = typeof request.template === 'string' ? request.template : '';
46
116
  if (template.trim() === '') {
@@ -49,12 +119,32 @@ export async function chatTemplateDownloadFor(localInfer, request) {
49
119
  if (!isHfRepoReference(template)) {
50
120
  throw new RouteError(400, `the chat template must be an HF repo reference, got "${template || '(none)'}"`);
51
121
  }
52
- const storedDir = stored.config.modelDirectory;
53
- const directory = typeof request.directory === 'string' && request.directory.trim() !== ''
54
- ? request.directory
55
- : typeof storedDir === 'string' && storedDir.trim() !== ''
56
- ? storedDir
57
- : localInfer.defaultModelDirectory();
58
- return localInfer.downloadChatTemplate(template, directory);
122
+ const { baseDir, useHubCache } = resolveDownloadTarget(localInfer, request.directory, stored);
123
+ const runKey = runLogKey(stored.providers, request.engine ?? stored.engine, undefined, stored.config);
124
+ localInfer.beginRun(runKey, DOWNLOAD_RUN_SEED);
125
+ try {
126
+ const result = await localInfer.downloadChatTemplate(template, baseDir, {
127
+ useHubCache,
128
+ ...(signal !== undefined ? { signal } : {}),
129
+ onChunk: (chunk) => {
130
+ localInfer.appendRunLog(runKey, chunk);
131
+ },
132
+ // The service writes the rendered chat-template pull's `$ <command>`
133
+ // line through onLog before it spawns the child, so the live run reads
134
+ // the command ahead of the CLI's output, exactly once.
135
+ onLog: (line) => {
136
+ localInfer.appendRunLog(runKey, line);
137
+ },
138
+ });
139
+ // A cancel marks the live run canceled; the template pull's outcome
140
+ // rides the card's template cell, not the persisted run, so no persist.
141
+ if (signal?.aborted === true) {
142
+ localInfer.appendRunLog(runKey, CANCELED_LINE);
143
+ }
144
+ return result;
145
+ }
146
+ finally {
147
+ localInfer.endRun(runKey);
148
+ }
59
149
  }
60
- //# sourceMappingURL=hf.js.map
150
+ //# sourceMappingURL=hf.js.map
@@ -1,23 +1,78 @@
1
1
  /**
2
- * The last-run log persistence the card's process steps use: appendProcessLog
2
+ * The last-run log persistence the card's process steps use: persistProcessLog
3
3
  * appends one run's output (a Get's commands and output, a model download's
4
- * result) to the stored last-run logs (the card's Logs section shows it after a
5
- * reload) and writes the run to the configured log file, the same per-run file
6
- * rule the launch step applies. persistGetLogs and persistDownloadLog are the
7
- * step-specific wrappers.
4
+ * result, a stop's failure note) to the stored last-run logs (the card's Logs
5
+ * section shows it after a reload) and writes the run to the configured log
6
+ * file, the same per-run file rule the launch step applies; captureRunBaseline
7
+ * and finishRunSection close a streamed run so its provisional mid-run notes
8
+ * and its final text persist once.
8
9
  * @module dsh-plugin-local-infer/ui/ops/logs
9
10
  */
11
+ import type { StoredConfig } from '../config.ts';
10
12
  /**
11
- * persistGetLogs appends one Get run's streamed output (the commands and their
12
- * output) to the stored last-run logs and writes the run to the configured log
13
- * file, the same per-run file rule the launch step applies.
13
+ * The run seeds are the `--- <process>...` separator lines that lead each
14
+ * provider process's run: the persisted section and the live run-log buffer
15
+ * share the constant, so the streamed run reads like the persisted one. The
16
+ * launch, get, and download steps already wrote these lines; the live hub
17
+ * reuses them.
14
18
  */
15
- export declare function persistGetLogs(output: string): void;
19
+ export declare const GET_RUN_SEED = "--- Getting engine and model...\n";
20
+ export declare const LAUNCH_RUN_SEED = "--- Launching...\n";
21
+ export declare const DOWNLOAD_RUN_SEED = "--- Downloading...\n";
22
+ export declare const INSTALL_RUN_SEED = "--- Installing deps...\n";
23
+ export declare const STOP_RUN_SEED = "--- Stopping...\n";
16
24
  /**
17
- * persistDownloadLog appends one model or chat-template download's result to the
18
- * stored last-run logs and writes the run to the configured log file, so the
19
- * card's Logs section and the log file show the download beside the Get and
20
- * launch runs.
25
+ * CANCELED_LINE is the "Canceled" line a client-disconnect cancel appends to
26
+ * the persisted run, so the Logs section and the log file read the run as
27
+ * canceled, the same line the card writes to its Logs buffer on the Cancel.
28
+ * Every canceled run (Get, launch, download, chat template) writes it from
29
+ * here.
21
30
  */
22
- export declare function persistDownloadLog(output: string): void;
23
- //# sourceMappingURL=logs.d.ts.map
31
+ export declare const CANCELED_LINE = "--- Canceled\n";
32
+ /**
33
+ * persistProcessLog appends one process run's output to the stored last-run
34
+ * logs (capped to the launch logs' tail, so a long build cannot grow the
35
+ * config without bound) and writes the run to the configured log file. A
36
+ * missing log location (the field is optional) skips the file, the same rule
37
+ * the launch step uses. An empty output is a no-op.
38
+ */
39
+ export declare function persistProcessLog(output: string): void;
40
+ /**
41
+ * captureRunBaseline reads the stored last-run logs at the start of a run.
42
+ * The route passes it to finishRunSection when the run settles, so the
43
+ * finished run replaces the run's own provisional mid-run appends instead of
44
+ * appending the same text a second time under the section (the sudo CLI note
45
+ * persists while the run hangs on the password prompt and then rides the
46
+ * collected run text too).
47
+ */
48
+ export declare function captureRunBaseline(): string;
49
+ /**
50
+ * finishRunSection closes one settled run: the stored last-run logs become
51
+ * the run's baseline plus the finished run section, replacing (not stacking
52
+ * over) the run's provisional mid-run appends, so a line that both persisted
53
+ * mid-run and rides the collected section persists exactly once. Tradeoff,
54
+ * accepted on purpose: another writer's append that landed between the
55
+ * baseline and the close is overwritten; the run owns the card's control
56
+ * while it runs, and the launch path already overwrites the stored logs
57
+ * wholesale (ops/engine.ts persistLaunchLogs). The section lands in the
58
+ * configured log file under the same per-run file rule persistProcessLog
59
+ * uses. An empty section is a no-op.
60
+ */
61
+ export declare function finishRunSection(baseline: string, section: string): void;
62
+ /**
63
+ * capRunLogs keeps one stored run's text within the shared LOG_TAIL_BYTES
64
+ * tail, the same bound the live hub buffer and every lastLogs writer apply.
65
+ * joinRunLogs and the wholesale launch persisters (ops/engine.ts
66
+ * persistStoredLogs, persistLaunchLogs) share this one cap rule.
67
+ */
68
+ export declare function capRunLogs(logs: string): string;
69
+ /**
70
+ * writeRunFile writes one run's text to the configured log file named for
71
+ * its engine and model. The launch persisters pass the launch's own engine
72
+ * and config; the append paths pass the stored ones (the defaults). A
73
+ * missing log location (the field is optional) skips the file; a file that
74
+ * cannot be written must not fail the run, since the in-memory last-run logs
75
+ * still reach the card. Every run writer shares this one file rule.
76
+ */
77
+ export declare function writeRunFile(stored: StoredConfig, output: string, engineId?: string, modelId?: string | undefined): void;
78
+ //# sourceMappingURL=logs.d.ts.map
@@ -1,34 +1,112 @@
1
1
  /**
2
- * The last-run log persistence the card's process steps use: appendProcessLog
2
+ * The last-run log persistence the card's process steps use: persistProcessLog
3
3
  * appends one run's output (a Get's commands and output, a model download's
4
- * result) to the stored last-run logs (the card's Logs section shows it after a
5
- * reload) and writes the run to the configured log file, the same per-run file
6
- * rule the launch step applies. persistGetLogs and persistDownloadLog are the
7
- * step-specific wrappers.
4
+ * result, a stop's failure note) to the stored last-run logs (the card's Logs
5
+ * section shows it after a reload) and writes the run to the configured log
6
+ * file, the same per-run file rule the launch step applies; captureRunBaseline
7
+ * and finishRunSection close a streamed run so its provisional mid-run notes
8
+ * and its final text persist once.
8
9
  * @module dsh-plugin-local-infer/ui/ops/logs
9
10
  */
10
11
  import { mkdirSync, writeFileSync } from 'node:fs';
11
12
  import { homedir } from 'node:os';
12
13
  import { dirname } from 'node:path';
13
- import { resolveLogPath } from '../../../core/lib/index.js';
14
- import { readConfig, writeConfig } from "../config.js";
14
+ import { LOG_TAIL_BYTES, resolveLogPath } from '../../../core/lib/index.js';
15
+ import { readConfig, writeStored } from "../config.js";
15
16
  /**
16
- * appendProcessLog appends one process run's output to the stored last-run logs
17
- * (capped to the launch logs' tail, so a long build cannot grow the config
18
- * without bound) and writes the run to the configured log file. A missing log
19
- * location (the field is optional) skips the file, the same rule the launch step
20
- * uses. An empty output is a no-op.
17
+ * The run seeds are the `--- <process>...` separator lines that lead each
18
+ * provider process's run: the persisted section and the live run-log buffer
19
+ * share the constant, so the streamed run reads like the persisted one. The
20
+ * launch, get, and download steps already wrote these lines; the live hub
21
+ * reuses them.
21
22
  */
22
- function appendProcessLog(output) {
23
+ export const GET_RUN_SEED = '--- Getting engine and model...\n';
24
+ export const LAUNCH_RUN_SEED = '--- Launching...\n';
25
+ export const DOWNLOAD_RUN_SEED = '--- Downloading...\n';
26
+ export const INSTALL_RUN_SEED = '--- Installing deps...\n';
27
+ export const STOP_RUN_SEED = '--- Stopping...\n';
28
+ /**
29
+ * CANCELED_LINE is the "Canceled" line a client-disconnect cancel appends to
30
+ * the persisted run, so the Logs section and the log file read the run as
31
+ * canceled, the same line the card writes to its Logs buffer on the Cancel.
32
+ * Every canceled run (Get, launch, download, chat template) writes it from
33
+ * here.
34
+ */
35
+ export const CANCELED_LINE = '--- Canceled\n';
36
+ /**
37
+ * persistProcessLog appends one process run's output to the stored last-run
38
+ * logs (capped to the launch logs' tail, so a long build cannot grow the
39
+ * config without bound) and writes the run to the configured log file. A
40
+ * missing log location (the field is optional) skips the file, the same rule
41
+ * the launch step uses. An empty output is a no-op.
42
+ */
43
+ export function persistProcessLog(output) {
23
44
  if (output === '')
24
45
  return;
25
46
  const stored = readConfig();
26
- const joined = `${stored.lastLogs}${stored.lastLogs === '' ? '' : '\n'}${output}`;
27
- writeConfig(stored.engine, stored.config, stored.allowRunDeps, stored.platform, stored.showHelp, stored.providers, stored.logPath, joined.slice(-65536));
47
+ writeStored(stored, { lastLogs: joinRunLogs(stored.lastLogs, output) });
48
+ writeRunFile(stored, output);
49
+ }
50
+ /**
51
+ * captureRunBaseline reads the stored last-run logs at the start of a run.
52
+ * The route passes it to finishRunSection when the run settles, so the
53
+ * finished run replaces the run's own provisional mid-run appends instead of
54
+ * appending the same text a second time under the section (the sudo CLI note
55
+ * persists while the run hangs on the password prompt and then rides the
56
+ * collected run text too).
57
+ */
58
+ export function captureRunBaseline() {
59
+ return readConfig().lastLogs;
60
+ }
61
+ /**
62
+ * finishRunSection closes one settled run: the stored last-run logs become
63
+ * the run's baseline plus the finished run section, replacing (not stacking
64
+ * over) the run's provisional mid-run appends, so a line that both persisted
65
+ * mid-run and rides the collected section persists exactly once. Tradeoff,
66
+ * accepted on purpose: another writer's append that landed between the
67
+ * baseline and the close is overwritten; the run owns the card's control
68
+ * while it runs, and the launch path already overwrites the stored logs
69
+ * wholesale (ops/engine.ts persistLaunchLogs). The section lands in the
70
+ * configured log file under the same per-run file rule persistProcessLog
71
+ * uses. An empty section is a no-op.
72
+ */
73
+ export function finishRunSection(baseline, section) {
74
+ if (section === '')
75
+ return;
76
+ const stored = readConfig();
77
+ writeStored(stored, { lastLogs: joinRunLogs(baseline, section) });
78
+ writeRunFile(stored, section);
79
+ }
80
+ /**
81
+ * capRunLogs keeps one stored run's text within the shared LOG_TAIL_BYTES
82
+ * tail, the same bound the live hub buffer and every lastLogs writer apply.
83
+ * joinRunLogs and the wholesale launch persisters (ops/engine.ts
84
+ * persistStoredLogs, persistLaunchLogs) share this one cap rule.
85
+ */
86
+ export function capRunLogs(logs) {
87
+ return logs.slice(-LOG_TAIL_BYTES);
88
+ }
89
+ /**
90
+ * joinRunLogs appends one run's text under the stored last-run logs (or the
91
+ * finished run's baseline), newline-separated, capped to the launch logs'
92
+ * tail so a long build cannot grow the config without bound. persistProcessLog
93
+ * and finishRunSection share the join and cap rule.
94
+ */
95
+ function joinRunLogs(base, output) {
96
+ return capRunLogs(`${base}${base === '' ? '' : '\n'}${output}`);
97
+ }
98
+ /**
99
+ * writeRunFile writes one run's text to the configured log file named for
100
+ * its engine and model. The launch persisters pass the launch's own engine
101
+ * and config; the append paths pass the stored ones (the defaults). A
102
+ * missing log location (the field is optional) skips the file; a file that
103
+ * cannot be written must not fail the run, since the in-memory last-run logs
104
+ * still reach the card. Every run writer shares this one file rule.
105
+ */
106
+ export function writeRunFile(stored, output, engineId = stored.engine, modelId = typeof stored.config.model === 'string' ? stored.config.model : undefined) {
28
107
  if (stored.logPath === undefined || stored.logPath === '')
29
108
  return;
30
- const modelId = typeof stored.config.model === 'string' ? stored.config.model : undefined;
31
- const logPath = resolveLogPath(stored.logPath, stored.engine, modelId, new Date(), homedir());
109
+ const logPath = resolveLogPath(stored.logPath, engineId, modelId, new Date(), homedir());
32
110
  try {
33
111
  mkdirSync(dirname(logPath), { recursive: true });
34
112
  writeFileSync(logPath, output);
@@ -38,21 +116,4 @@ function appendProcessLog(output) {
38
116
  // fail the run; the in-memory last-run logs still reach the card.
39
117
  }
40
118
  }
41
- /**
42
- * persistGetLogs appends one Get run's streamed output (the commands and their
43
- * output) to the stored last-run logs and writes the run to the configured log
44
- * file, the same per-run file rule the launch step applies.
45
- */
46
- export function persistGetLogs(output) {
47
- appendProcessLog(output);
48
- }
49
- /**
50
- * persistDownloadLog appends one model or chat-template download's result to the
51
- * stored last-run logs and writes the run to the configured log file, so the
52
- * card's Logs section and the log file show the download beside the Get and
53
- * launch runs.
54
- */
55
- export function persistDownloadLog(output) {
56
- appendProcessLog(output);
57
- }
58
- //# sourceMappingURL=logs.js.map
119
+ //# sourceMappingURL=logs.js.map
@@ -4,8 +4,7 @@
4
4
  * the registration wrote, and syncRegisteredModel re-runs the registration on
5
5
  * a save so the registered model's input types stay in step with the config.
6
6
  * resolveModelRow and resolveContextWindow resolve the row and context window
7
- * the registrar publishes; resolveModelDirectory picks the model directory a
8
- * Get run downloads into.
7
+ * the registrar publishes.
9
8
  * @module dsh-plugin-local-infer/ui/ops/model
10
9
  */
11
10
  import type { Config, ModelRow, RegisterResult, VramModel } from 'dsh-plugin-local-infer/core';
@@ -69,11 +68,4 @@ export declare function syncRegisteredModel(localInfer: LocalInferService, engin
69
68
  export declare function unregisterFor(localInfer: LocalInferService, request?: RegisterRequest): Promise<{
70
69
  removed: boolean;
71
70
  }>;
72
- /**
73
- * resolveModelDirectory picks the model directory a Get run downloads into:
74
- * the config's modelDirectory override when it names a path, else the
75
- * plugin's default model directory. The same rule the summary and deps routes
76
- * apply, so a Get and the card's Model Directory field agree on the target.
77
- */
78
- export declare function resolveModelDirectory(localInfer: LocalInferService, config: Config): string;
79
- //# sourceMappingURL=model.d.ts.map
71
+ //# sourceMappingURL=model.d.ts.map
@@ -4,11 +4,16 @@
4
4
  * the registration wrote, and syncRegisteredModel re-runs the registration on
5
5
  * a save so the registered model's input types stay in step with the config.
6
6
  * resolveModelRow and resolveContextWindow resolve the row and context window
7
- * the registrar publishes; resolveModelDirectory picks the model directory a
8
- * Get run downloads into.
7
+ * the registrar publishes.
9
8
  * @module dsh-plugin-local-infer/ui/ops/model
10
9
  */
11
10
  import { buildModelEntry, providerIdFor } from '../../../core/lib/index.js';
11
+ /**
12
+ * DEFAULT_CONTEXT_WINDOW is the context window the registrar publishes when
13
+ * neither the config nor the resolved VramModel names one: the 32K spec
14
+ * default, a context the engines can serve without a measured ceiling.
15
+ */
16
+ const DEFAULT_CONTEXT_WINDOW = 32_768;
12
17
  import { readConfig } from "../config.js";
13
18
  import { resolveAdapter } from "./engine.js";
14
19
  /**
@@ -31,7 +36,7 @@ export function resolveContextWindow(config, models) {
31
36
  else if (models.length === 1) {
32
37
  model = models[0];
33
38
  }
34
- return model?.nativeMaxContext ?? 32768;
39
+ return model?.nativeMaxContext ?? DEFAULT_CONTEXT_WINDOW;
35
40
  }
36
41
  /**
37
42
  * resolveModelRow picks the model row the registrar publishes for an engine
@@ -115,16 +120,4 @@ export async function unregisterFor(localInfer, request) {
115
120
  await localInfer.removeModel(entry.providerId, row.id);
116
121
  return { removed: true };
117
122
  }
118
- /**
119
- * resolveModelDirectory picks the model directory a Get run downloads into:
120
- * the config's modelDirectory override when it names a path, else the
121
- * plugin's default model directory. The same rule the summary and deps routes
122
- * apply, so a Get and the card's Model Directory field agree on the target.
123
- */
124
- export function resolveModelDirectory(localInfer, config) {
125
- const rawModelDir = config.modelDirectory;
126
- return typeof rawModelDir === 'string' && rawModelDir.trim() !== ''
127
- ? rawModelDir
128
- : localInfer.defaultModelDirectory();
129
- }
130
- //# sourceMappingURL=model.js.map
123
+ //# sourceMappingURL=model.js.map
@@ -103,4 +103,4 @@ export declare function launchRequestFromBody(body: Record<string, unknown>): La
103
103
  * route, falling back to the stored config per key.
104
104
  */
105
105
  export declare function registerRequestFromBody(body: Record<string, unknown>): RegisterRequest;
106
- //# sourceMappingURL=request.d.ts.map
106
+ //# sourceMappingURL=request.d.ts.map
@@ -77,4 +77,4 @@ export function registerRequestFromBody(body) {
77
77
  }
78
78
  return request;
79
79
  }
80
- //# sourceMappingURL=request.js.map
80
+ //# sourceMappingURL=request.js.map
@@ -60,6 +60,14 @@ interface OutgoingLike {
60
60
  */
61
61
  write?(chunk: string): unknown;
62
62
  end(body?: string): void;
63
+ /**
64
+ * Optional: watch the response for the client disconnecting (the node
65
+ * ServerResponse's 'close' before the route ends), so a stream route can
66
+ * abort the in-flight run when the card cancels it. The web server hands
67
+ * the handler a node ServerResponse; the tests' fake may omit it, and the
68
+ * route degrades to no abort.
69
+ */
70
+ on?(event: string, listener: (...args: unknown[]) => void): unknown;
63
71
  }
64
72
  /**
65
73
  * RouteError is a route failure that carries its own HTTP status, so the
@@ -87,6 +95,13 @@ export declare class RouteResponder {
87
95
  ndjsonStream(): void;
88
96
  /** Stream one chunk; a fake response without write() collects nothing. */
89
97
  write(chunk: string): void;
98
+ /**
99
+ * Watch the response's client disconnect (the node 'close' before the
100
+ * route ends): the listener fires once when the card's Cancel closes the
101
+ * stream, so the route aborts the in-flight run. A fake response without
102
+ * on() never fires it, and the run degrades to running to completion.
103
+ */
104
+ onClose(listener: () => void): void;
90
105
  /** End the response, optionally with a final body chunk. */
91
106
  end(body?: string): void;
92
107
  }
@@ -129,17 +144,18 @@ export type RouteSpec = {
129
144
  export declare function defineRoute(spec: RouteSpec): LocalInferRoute;
130
145
  /**
131
146
  * registerLocalInferRoute registers the summary, save, provider-delete, deps,
132
- * install, download, chat-template, get, launch, stop, register, and
147
+ * install, download, chat-template, get, launch, stop, logs, register, and
133
148
  * unregister routes on the web server and holds them by an effect, so a
134
149
  * bundle switch off removes them before the next switch on re-registers. The
135
150
  * scoped context carries the webServer; the localInfer service is passed in
136
151
  * because the scope only injects what it names. The optional getHost is the
137
- * lifecycle host the Get route's git and build steps run through; it
138
- * defaults to the node host, and a test injects a stub so the route never
139
- * clones a real repo in CI.
152
+ * lifecycle host the Get route's git and build steps run through, and the
153
+ * optional launchHost the launch route's Get steps run through; both default
154
+ * to the node host, and a test injects a stub so the routes never clone a
155
+ * real repo in CI.
140
156
  */
141
157
  export declare function registerLocalInferRoute(scope: Context & {
142
158
  webServer: WebServerService;
143
- }, localInfer: LocalInferService, getHost?: LifecycleHost): void;
159
+ }, localInfer: LocalInferService, getHost?: LifecycleHost, launchHost?: LifecycleHost): void;
144
160
  export {};
145
- //# sourceMappingURL=route.d.ts.map
161
+ //# sourceMappingURL=route.d.ts.map