dsh-runnpu 0.1.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 (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +16 -0
  3. package/compute-mode/preset/agent.cordis.yml +368 -0
  4. package/compute-mode/preset/preset.yml +3 -0
  5. package/compute-mode/preset/skills/runnpu/SKILL.md +233 -0
  6. package/compute-mode/preset/skills/runnpu/VENDOR.txt +11 -0
  7. package/compute-mode/preset/skills/runnpu/examples/01-dev-environment.sh +26 -0
  8. package/compute-mode/preset/skills/runnpu/examples/02-training-job.sh +26 -0
  9. package/compute-mode/preset/skills/runnpu/examples/03-inference-service.sh +24 -0
  10. package/compute-mode/preset/skills/runnpu/examples/04-fractional-vnpu.sh +14 -0
  11. package/compute-mode/preset/skills/runnpu/examples/05-troubleshoot.sh +17 -0
  12. package/compute-mode/preset/skills/runnpu/references/admin.md +60 -0
  13. package/compute-mode/preset/skills/runnpu/references/data-sources.md +35 -0
  14. package/compute-mode/preset/skills/runnpu/references/templates-assets.md +61 -0
  15. package/compute-mode/preset/skills/runnpu/references/training-runs.md +56 -0
  16. package/compute-mode/preset/skills/runnpu/references/troubleshooting.md +75 -0
  17. package/compute-mode/preset/skills/runnpu/references/workload-types.md +47 -0
  18. package/cordis.patch.yml +80 -0
  19. package/dist/chunk-JLZMOSQC.mjs +1 -0
  20. package/dist/chunk-JUBIWGKW.mjs +4 -0
  21. package/dist/chunk-JZXLCA2E.mjs +1 -0
  22. package/dist/chunk-KQOFBYGE.mjs +9 -0
  23. package/dist/chunk-XMZG2NP7.mjs +2 -0
  24. package/dist/chunk-XQPU3AJN.mjs +1 -0
  25. package/dist/chunk-YKASEMP3.mjs +1 -0
  26. package/dist/chunk-YPL4VUYF.mjs +4 -0
  27. package/dist/compute-node.mjs +1 -0
  28. package/dist/compute-tools.mjs +41 -0
  29. package/dist/connectors.mjs +3 -0
  30. package/dist/dsh-runnpu.mjs +43 -0
  31. package/dist/json.mjs +1 -0
  32. package/dist/pins.mjs +1 -0
  33. package/dist/series.mjs +1 -0
  34. package/dist/training-node.mjs +2 -0
  35. package/dist/training-tools.mjs +30 -0
  36. package/dist/ui-host.mjs +1 -0
  37. package/lib/client.js +189 -0
  38. package/package.json +78 -0
  39. package/training-mode/preset/agent.cordis.yml +343 -0
  40. package/training-mode/preset/preset.yml +3 -0
  41. package/training-mode/preset/skills/runnpu/SKILL.md +233 -0
  42. package/training-mode/preset/skills/runnpu/VENDOR.txt +11 -0
  43. package/training-mode/preset/skills/runnpu/examples/01-dev-environment.sh +26 -0
  44. package/training-mode/preset/skills/runnpu/examples/02-training-job.sh +26 -0
  45. package/training-mode/preset/skills/runnpu/examples/03-inference-service.sh +24 -0
  46. package/training-mode/preset/skills/runnpu/examples/04-fractional-vnpu.sh +14 -0
  47. package/training-mode/preset/skills/runnpu/examples/05-troubleshoot.sh +17 -0
  48. package/training-mode/preset/skills/runnpu/references/admin.md +60 -0
  49. package/training-mode/preset/skills/runnpu/references/data-sources.md +35 -0
  50. package/training-mode/preset/skills/runnpu/references/templates-assets.md +61 -0
  51. package/training-mode/preset/skills/runnpu/references/training-runs.md +56 -0
  52. package/training-mode/preset/skills/runnpu/references/troubleshooting.md +75 -0
  53. package/training-mode/preset/skills/runnpu/references/workload-types.md +47 -0
  54. package/training-mode/preset/skills/swanlab-skill/LICENSE +21 -0
  55. package/training-mode/preset/skills/swanlab-skill/SKILL.md +163 -0
  56. package/training-mode/preset/skills/swanlab-skill/VENDOR.txt +9 -0
  57. package/training-mode/preset/skills/swanlab-skill/references/ANALYSIS_GUIDE.md +83 -0
  58. package/training-mode/preset/skills/swanlab-skill/references/CLI_REFERENCE.md +510 -0
  59. package/training-mode/preset/skills/swanlab-skill/references/SDK_QUICKSTART.md +341 -0
  60. package/training-mode/preset/skills/swanlab-skill/references/SWANLAB_CONCEPTS.md +523 -0
  61. package/training-mode/preset/skills/swanlab-skill/scripts/plot_metrics.py +353 -0
  62. package/training-mode/preset/skills/swanlab-skill/scripts/runs_benchmark.py +550 -0
  63. package/training-mode/preset/skills/training-research/SKILL.md +33 -0
  64. package/training-mode/preset/skills/training-research/references/pipeline.md +45 -0
  65. package/training-mode/preset/skills/training-research/references/research.md +76 -0
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "dsh-runnpu",
3
+ "version": "0.1.0",
4
+ "description": "DeepSeek Harness plugin adding two agent modes over RunNPU: compute management (cluster dashboard, pinned charts) and training (SwanLab-instrumented runs on a NPU cluster).",
5
+ "keywords": [
6
+ "deepseek-harness",
7
+ "dsh",
8
+ "dsh-plugin",
9
+ "runnpu",
10
+ "ascend",
11
+ "npu",
12
+ "swanlab"
13
+ ],
14
+ "homepage": "https://github.com/EM-GeekLab/run-npu-harness#readme",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/EM-GeekLab/run-npu-harness.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/EM-GeekLab/run-npu-harness/issues"
21
+ },
22
+ "license": "MIT",
23
+ "type": "module",
24
+ "bin": {
25
+ "dsh-runnpu": "dist/dsh-runnpu.mjs"
26
+ },
27
+ "exports": {
28
+ ".": "./dist/ui-host.mjs",
29
+ "./shared/connectors": "./dist/connectors.mjs",
30
+ "./compute/node": "./dist/compute-node.mjs",
31
+ "./training/node": "./dist/training-node.mjs",
32
+ "./compute/tools": "./dist/compute-tools.mjs",
33
+ "./training/tools": "./dist/training-tools.mjs",
34
+ "./shared/json": "./dist/json.mjs",
35
+ "./shared/pins": "./dist/pins.mjs",
36
+ "./shared/series": "./dist/series.mjs",
37
+ "./client": "./lib/client.js",
38
+ "./cordis.patch.yml": "./cordis.patch.yml",
39
+ "./package.json": "./package.json"
40
+ },
41
+ "engines": {
42
+ "node": ">=20"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "dsh": {
48
+ "bundle": {
49
+ "patch": "./cordis.patch.yml"
50
+ },
51
+ "client": {
52
+ "platform": "web",
53
+ "inject": [
54
+ "@deepseek-ai/dsh-client-runtime",
55
+ "@deepseek-ai/dsh-client-locale"
56
+ ]
57
+ }
58
+ },
59
+ "cliDeps": {
60
+ "$comment": "Host CLIs the modes drive; checked/installed by `dsh-runnpu doctor` / `dsh-runnpu cli`. runnpu.version is a release tag of EM-GeekLab/run-npu-skill (CLI + skill ship together there) and must equal the version in skills/runnpu/VENDOR.txt.",
61
+ "runnpu": {
62
+ "version": "v0.1.1",
63
+ "releaseBase": "https://github.com/EM-GeekLab/run-npu-skill/releases/download"
64
+ },
65
+ "swanlab": {
66
+ "min": "0.9.0",
67
+ "package": "swanlab"
68
+ }
69
+ },
70
+ "peerDependencies": {
71
+ "@deepseek-ai/dsh-tools": "*"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "@deepseek-ai/dsh-tools": {
75
+ "optional": true
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,343 @@
1
+ # The `training` agent preset — 训练模式.
2
+ #
3
+ # Derived from the shipped `standard` preset. Differences:
4
+ # 1. an ML-platform persona that knows about `.train/`, SSH upload, and the
5
+ # SwanLab offline degradation;
6
+ # 2. `skill-filesystem` scans this preset's own `skills/` tree, which carries
7
+ # BOTH the RunNPU skill and the vendored SwanLab skill.
8
+ #
9
+ # The metric strip above the composer and the connection forms live in the
10
+ # `dsh-runnpu` bundle on the host plane, keyed off this preset's id.
11
+
12
+ # ── identity ────────────────────────────────────────────────────────────────
13
+
14
+ # The preset's own persona, shadowing the deployment default for this agent.
15
+ # `{{model}}` and `{{cwd}}` resolve from the agent's own route and workspace.
16
+ - id: persona
17
+ name: '@deepseek-ai/dsh-persona'
18
+ config:
19
+ # dsh ≥ 0.1.5 reads `prefix` (+ optional `suffix`); older harnesses read `text`.
20
+ # Both point at the same block, so one preset serves both.
21
+ prefix: &persona >-
22
+ You are a senior ML platform engineer running training jobs on a RunNPU
23
+ Ascend NPU cluster with SwanLab instrumentation, powered by the {{model}}
24
+ model. Your working directory is {{cwd}} and it is the subject of this
25
+ session: the training project lives there.
26
+
27
+ Three skills carry the domain knowledge and you read them before acting:
28
+ `training-research` is the METHODOLOGY and it leads — the pipeline
29
+ discipline before taking any project to the cluster, and the full
30
+ auto-research loop (curve diagnosis rules, one-variable experiments,
31
+ stop criteria, long-horizon schedules, parallel sweeps). `runnpu` holds
32
+ the PLATFORM facts (CLI operations, Ascend image traps — read its
33
+ `references/training-runs.md` before touching the cluster), and
34
+ `swanlab-skill` the tracking specifics (SDK for instrumentation,
35
+ `swanlab api` for reading experiments back). When comparing runs, call
36
+ the `training_compare` tool instead of pulling numbers and comparing
37
+ them yourself: who won and by how much is arithmetic, and arithmetic
38
+ the model does needs re-checking — your part is why it won and what to
39
+ try next.
40
+
41
+ The project keeps its own ledger in `.train/`: `config.json` (non-secret
42
+ connection settings — register the `swanlab.project` you see in
43
+ `swanlab.init(project=…)` there, it is what lets the run id be looked up
44
+ for you), `secrets.json` (0600, git-ignored — never upload it into a
45
+ container, never echo it), `metrics.json` (the at-most-five key metrics
46
+ you infer by READING the training code, each with a direction and the
47
+ source line it came from), and `runs/NNNN.json` (one file per run: seq,
48
+ hyperparameters, template id+version, codeRev, workload ref, SwanLab run,
49
+ status, decision). Write the ledger entry when you submit, not after.
50
+
51
+ The mechanical half of a run entry maintains itself: a background sync
52
+ copies the workload status, the end instant and the SwanLab run path into
53
+ `runs/NNNN.json` every refresh round, so once you have submitted you do
54
+ NOT have to go back and fill in a run id or a final status. Call
55
+ `training_run_update` when there is a JUDGMENT to record — the decision
56
+ (promote / discard / next), the note that says why, a hyperparameter
57
+ correction — and use its status / run-path parameters only to repair a
58
+ sync that demonstrably did not happen.
59
+
60
+ Your metric keys are an inference, so the panel checks them: when it
61
+ reports that a key does not exist in the experiment, or that a key never
62
+ got any data, re-read the training code and call `training_metrics_set`
63
+ again instead of leaving an empty chart on screen.
64
+
65
+ Getting a project onto the cluster: start a `training-env` workload with
66
+ SSH enabled, then upload with the `training_upload` TOOL — never with your
67
+ own scp/sftp/rsync command line. The tool is where the exclusions are a
68
+ mechanism instead of a thing you have to remember: `.train/secrets.json`
69
+ and the rest of `.train/` can never leave this machine, and
70
+ `extraExcludes` only ever adds. It also probes what the remote image has
71
+ (rsync, a tar pipe, or the SFTP subsystem compiled into the SSH helper,
72
+ which works even on a distroless image) and picks the transport for you.
73
+ Code and dataset are two separate lanes: the default lane is code, and
74
+ `dataset: true` sends a dataset to its own destination — upload it ONCE
75
+ and never re-send it alongside a code change, which is the actual reason
76
+ the two are separated. Use `dryRun: true` when you want to show the user
77
+ the plan first: which files, which exclusions, which SSH entry, and no
78
+ bytes moved. The code lane returns a `codeRev` content hash; pass it to
79
+ `training_submit` so "which code ran" stays answerable three weeks later.
80
+ When the cluster has no reachable node address, `runnpu workload
81
+ port-forward` through the control panel is the path that works; do not
82
+ conclude a service is down because `ready` reads false.
83
+
84
+ SwanLab credentials reach the container only as environment variables at
85
+ submit time. Before submitting, check the API host is reachable from the
86
+ cluster: if it is not, run `swanlab offline` ahead of training and tell
87
+ the user the run will need `swanlab sync` afterwards — never leave them
88
+ waiting for a curve that will never arrive.
89
+
90
+ Training is long-running: submit, return the job id, the SwanLab run URL
91
+ and a short human summary, then let the dashboard follow it. When you
92
+ analyze dynamics, produce a plot and read the plot; do not paste arrays of
93
+ numbers into the conversation. Treat every run as input to the next
94
+ decision and propose concrete next steps.
95
+ text: *persona
96
+ - id: agent-instructions
97
+ name: '@deepseek-ai/dsh-agent-instructions'
98
+ config:
99
+ maxBytes: 65536
100
+
101
+ # ── shell ───────────────────────────────────────────────────────────────────
102
+
103
+ # `shell-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
104
+ # publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
105
+ # the criterion for host-plane ownership — injection resolves before any session
106
+ # exists, so there is no agent to key by. Behind a preset realm those variables
107
+ # never reached the model's shell at all. Both shell tools consume the host
108
+ # registry from here; their executors (`bash-sandbox`/`pwsh-sandbox`) are
109
+ # host-plane too.
110
+ - id: tool-bash
111
+ name: '@deepseek-ai/dsh-tool-bash'
112
+ disabled: !!js process.platform === 'win32'
113
+
114
+ - id: tool-pwsh
115
+ name: '@deepseek-ai/dsh-tool-pwsh'
116
+ disabled: !!js process.platform !== 'win32'
117
+
118
+ # ── filesystem ──────────────────────────────────────────────────────────────
119
+
120
+ # Both register into the host `tools` registry and provide nothing, so
121
+ # they need no realm. The `fs` service and its policy stay in the host.
122
+ - id: tool-fs
123
+ name: '@deepseek-ai/dsh-tool-fs'
124
+
125
+ - id: tool-fs-search
126
+ name: '@deepseek-ai/dsh-tool-fs-search'
127
+ config:
128
+ sampleOverCapGlobResults: false
129
+
130
+ # ── background jobs ────────────────────────────────────────────────────────
131
+
132
+ # Only the model-facing controls. The task REGISTRY stays on the host plane:
133
+ # its producers sit outside any realm this file could put it in — `tool-bash`
134
+ # above resolves it with `ctx.get`, and an entry-local realm here is invisible
135
+ # to every sibling row, so `run_in_background` would answer "background jobs
136
+ # unavailable" while these controls sat in the catalog. The registry is keyed by
137
+ # owning agent anyway, so one host instance serves every session. What a preset
138
+ # chooses is whether its agent can collect and stop background work at all.
139
+ - id: tool-jobs
140
+ name: '@deepseek-ai/dsh-tool-jobs'
141
+
142
+ # ── skills ──────────────────────────────────────────────────────────────────
143
+
144
+ # The skill REGISTRY lives in the host composition and is layered per scope:
145
+ # these rows register into THIS preset's layer of it, so they need no realm.
146
+ # `skill-filesystem` contributes local-root discovery for agents on this preset, and
147
+ # `tool-skill` gives them the catalog and loader; the merged catalog also
148
+ # carries whatever the deployment registered globally (repository plugins).
149
+ - id: skill-filesystem
150
+ name: '@deepseek-ai/dsh-skill-filesystem'
151
+ config:
152
+ # Rank 300 `custom`: scanned after project roots, before user roots.
153
+ # These directories ship WITH the preset, so the mode carries its own
154
+ # skills instead of depending on what the machine happens to have.
155
+ # `__DSH_PRESET_ROOT__` is substituted with the machine's real preset root
156
+ # by scripts/install-presets.mjs at copy time — an absolute path baked at
157
+ # authoring time only works on the author's machine.
158
+ customSkillDirs:
159
+ - __DSH_PRESET_ROOT__/training/skills
160
+
161
+ - id: tool-skill
162
+ name: '@deepseek-ai/dsh-tool-skill'
163
+
164
+ # ── goals ───────────────────────────────────────────────────────────────────
165
+
166
+ # Only the model-facing tool. The goal SERVICE, its session driver, and the
167
+ # `/goal` command stay on the host plane: the Gateway serves the goal domain as
168
+ # Remote endpoints whose receiver comes from a generated descriptor, so it
169
+ # resolves `goals` on the host and an entry-local realm here would hide it. The
170
+ # registry is keyed by session anyway, so one host instance serves every
171
+ # session. What a preset chooses is whether its agent can call the goal tool.
172
+ - id: tool-goal
173
+ name: '@deepseek-ai/dsh-tool-goal'
174
+
175
+ # ── plan mode ───────────────────────────────────────────────────────────────
176
+
177
+ # Plan state is per-agent by nature, so an entry-local realm is not a
178
+ # workaround here — it is the correct lifetime.
179
+ - id: planning
180
+ name: cordis:group
181
+ group: true
182
+ isolate:
183
+ planMode: true
184
+ config:
185
+ - id: plan-mode
186
+ name: '@deepseek-ai/dsh-plan-mode'
187
+ config:
188
+ section: |
189
+ You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
190
+
191
+ Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
192
+
193
+ The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
194
+
195
+ Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
196
+
197
+ Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
198
+
199
+ When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
200
+
201
+ # ── compaction ──────────────────────────────────────────────────────────────
202
+
203
+ # `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
204
+ # share this realm rather than sit outside it.
205
+ #
206
+ # `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
207
+ # plane, and the rows here resolve that one instance. It takes no configuration,
208
+ # keys every fold by Session, and owns the context-meter projection units the
209
+ # browser reads for every session — behind a realm those units would come and go
210
+ # with whichever presets happen to be mounted. What a preset chooses is whether
211
+ # its agent compacts at all, which is `compaction-basic` below.
212
+ - id: compaction
213
+ name: cordis:group
214
+ group: true
215
+ isolate:
216
+ compaction: true
217
+ toolResultPruner: true
218
+ config:
219
+ - id: compaction-basic
220
+ name: '@deepseek-ai/dsh-compaction-basic'
221
+
222
+ - id: command-compact
223
+ name: '@deepseek-ai/dsh-command-compact'
224
+
225
+ - id: tool-result-pruner
226
+ name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
227
+ config:
228
+ thresholdChars: 8192
229
+ headChars: 4096
230
+ tailChars: 1024
231
+
232
+ # ── delegation and workflows ────────────────────────────────────────────────
233
+
234
+ # The `subagents` registry and its spawn/fork backends live in the HOST
235
+ # composition: the registry is a process singleton whose cross-session queries
236
+ # the api-proxy serves to the browser, and a provider name may only be
237
+ # registered once. This preset contributes the delegation TOOLS, which resolve
238
+ # that host registry.
239
+ #
240
+ # `workflows` is different — nothing outside an agent reads it — so every row
241
+ # that reaches it shares one entry-local realm here, and a consumer left
242
+ # outside would resolve a host registry this preset does not populate.
243
+ #
244
+ # `tool-subagent-report` is host-plane for the same reason as the registry,
245
+ # not because a preset may not want it: it registers a CONTINUABLE SETUP on
246
+ # that singleton rather than a tool this agent calls, and the setup list is
247
+ # not scope-aware — one copy per mounted preset means every child gets
248
+ # `report` registered once per live session, which throws on the second.
249
+ - id: delegation
250
+ name: cordis:group
251
+ group: true
252
+ isolate:
253
+ workflowEngine: true
254
+ config:
255
+ - id: tool-subagent-control
256
+ name: '@deepseek-ai/dsh-tool-subagent-control'
257
+
258
+ - id: tool-subagent-list-agents
259
+ name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
260
+
261
+ - id: tool-subagent
262
+ name: '@deepseek-ai/dsh-tool-subagent'
263
+ config:
264
+ provider: spawn
265
+ toolName: subagent
266
+ backgroundMode: continuable
267
+
268
+ - id: tool-subagent-fork
269
+ name: '@deepseek-ai/dsh-tool-subagent'
270
+ config:
271
+ provider: fork
272
+ toolName: subagent_fork
273
+ backgroundMode: continuable
274
+
275
+ # Production dsh does not install these optional providers. Install the
276
+ # matching Bundle in this Profile and restart the Host, then copy this
277
+ # preset and remove `disabled` from the matching tool row. Host availability
278
+ # alone grants no tool.
279
+ - id: tool-subagent-codex
280
+ name: '@deepseek-ai/dsh-tool-subagent'
281
+ disabled: true
282
+ config:
283
+ provider: codex
284
+ toolName: subagent_codex
285
+ backgroundMode: one-shot
286
+ maxDepth: provider-managed
287
+
288
+ - id: tool-subagent-claude-code
289
+ name: '@deepseek-ai/dsh-tool-subagent'
290
+ disabled: true
291
+ config:
292
+ provider: claude-code
293
+ toolName: subagent_claude_code
294
+ backgroundMode: one-shot
295
+ maxDepth: provider-managed
296
+
297
+ - id: workflow-worker-thread
298
+ name: '@deepseek-ai/dsh-workflow-worker-thread'
299
+ config:
300
+ provider: spawn
301
+
302
+ - id: tool-workflow
303
+ name: '@deepseek-ai/dsh-tool-workflow'
304
+
305
+ - id: tool-ralph
306
+ name: '@deepseek-ai/dsh-tool-ralph'
307
+ config:
308
+ subagentProvider: spawn
309
+ maxRounds: 64
310
+
311
+ # ── remaining model-facing rows ─────────────────────────────────────────────
312
+
313
+ - id: tool-ask-user
314
+ name: '@deepseek-ai/dsh-tool-ask-user'
315
+
316
+ - id: tool-todo
317
+ name: '@deepseek-ai/dsh-tool-todo'
318
+ config:
319
+ allowParallelInProgress: true
320
+
321
+ # The `web` service and its search provider stay in the host composition; only
322
+ # the model-facing tool is per-session.
323
+ - id: tool-web
324
+ name: '@deepseek-ai/dsh-tool-web'
325
+ config:
326
+ fetch: false
327
+ searchTimeoutMs: 60000
328
+
329
+ # ── training harness tools ──────────────────────────────────────────────────
330
+
331
+ # The model-facing training tools. They register into the host `tools` registry
332
+ # and publish nothing, so they need no realm — same posture as `tool-todo`.
333
+ # They resolve the host-plane connectors (ctx.runnpu / ctx.swanlab, provided by
334
+ # the dsh-runnpu bundle) with `ctx.get` and fail with a readable reason
335
+ # when the bundle is not installed in this profile.
336
+ #
337
+ # Why these are tools and not just skill instructions: `training_submit` puts
338
+ # the SwanLab API key into the container's environment WITHOUT it passing
339
+ # through a tool call — an agent composing that command line itself would write
340
+ # the key into the durable session log — and it writes .train/runs/NNNN.json as
341
+ # part of submitting, so the ledger cannot drift from what actually ran.
342
+ - id: training-tools
343
+ name: 'dsh-runnpu/training/tools'
@@ -0,0 +1,3 @@
1
+ name: 训练模式
2
+ description: 把当前工作目录里的训练工程搬上 RunNPU 跑起来,用 SwanLab 记录,关键指标曲线常驻在输入框上方,并在 .train/ 里维护训练版本账本。
3
+ order: 11
@@ -0,0 +1,233 @@
1
+ ---
2
+ name: runnpu
3
+ description: 通过 runnpu CLI 操作 RunNPU 昇腾算力平台——查看总览 / 节点 / 集群,管理项目、配额与成员,创建、排查、删除四类工作负载(开发环境、训练环境、训练任务、推理服务),使用工作负载模板与资产,挂载数据源与数据卷,读日志、事件、指标与 YAML,管理用户 / Access Key / 系统设置 / 审计。凡是用户要"在 RunNPU 上做某事"或询问平台 / 集群 / 负载现状时使用。
4
+ ---
5
+
6
+ # RunNPU CLI Skill
7
+
8
+ RunNPU 是面向昇腾 NPU 的算力管理平台。本 Skill 通过 `runnpu` 命令行完成一切操作——**不要自己拼 HTTP 请求**,
9
+ CLI 已经封装了认证、错误格式和输出格式;未封装的端点用 `runnpu api <METHOD> <路径>`。
10
+
11
+ 深入主题按需读同目录文件:`references/workload-types.md`(四类负载全参数)、
12
+ `references/templates-assets.md`(模板与资产的全部命令 + `--from-template` 的合并规则)、
13
+ `references/data-sources.md`(挂载与数据卷)、`references/troubleshooting.md`(排查树 + 已知集群行为)、
14
+ `references/training-runs.md`(**用户丢来训练工程说「帮我跑起来」时必读**:工程搬运、密钥注入、
15
+ 后台运行、loss 监控与收敛判断、CUDA→NPU 迁移炸点)、
16
+ `references/admin.md`(项目 / 配额 / 用户管理)。`examples/` 里是可直接照抄的完整场景。
17
+
18
+ ## 前置:确认 CLI 可用与已登录
19
+
20
+ ```bash
21
+ runnpu status # 平台版本、是否初始化、集群连通
22
+ runnpu me # 当前身份与有效权限(决定能做什么)
23
+ ```
24
+
25
+ - `runnpu` 不存在(command not found)时先装,Linux / macOS 一行:
26
+ `curl -fsSL https://github.com/EM-GeekLab/run-npu-skill/releases/latest/download/install.sh | sh -s -- --no-skill`
27
+ (Windows:`irm https://github.com/EM-GeekLab/run-npu-skill/releases/latest/download/install.ps1 | iex`)。
28
+ 离线环境到不了 GitHub,问用户要控制台提供的安装包。CLI 与本 Skill 同版本发布,
29
+ `runnpu --version` 应与本文件末尾的版本注释一致,不一致时去掉 `--no-skill` 重装一次把两者对齐。
30
+
31
+ - 未登录时报 `unauthenticated`。二选一:交互式 `runnpu login --server <URL> -u <email>`;
32
+ 非交互设 `RUNNPU_SERVER` + `RUNNPU_TOKEN`(管理员在「个人设置 → 访问密钥」签发)。
33
+ - **绝不要**把密码或 token 写进命令行参数回显给用户,也不要写入任何文件。
34
+
35
+ ## 输出约定(对解析很重要)
36
+
37
+ - 非终端(被程序调用)下所有命令输出 **JSON**;给人看表格加 `-o table`。
38
+ - 错误走 **stderr**:`{"error":"<code>","status":<http>,"message":"…","details":[…]}`,退出码 1;参数错误 2。
39
+ 常见 code:`unauthenticated` / `forbidden` / `not_found` / `validation_failed`(看 `details` 字段级原因)/ `cluster_error`。
40
+ - 列表响应 `{"items":[…]}`,分页 `next_cursor` / `total`;**默认只取一页**(`--limit N`,默认 50),
41
+ 回答"有多少 / 全部"先加 `--all`(自动翻页),否则按 `total` 说明只看了一页。
42
+ - 时间 RFC3339;容量单位在字段名里(`_gib` / `_bytes` / `_millis`)。
43
+
44
+ ## 核心概念(回答问题前先对好口径)
45
+
46
+ - **项目**是唯一组织层(没有部门)。配额、成员、策略都挂项目上。
47
+ - **负载类型(四类都可用)**:`workspace`(开发环境,可抢占)/ `training-env`(训练环境,不可抢占)/
48
+ `training-job`(训练任务,跑完即止,可多机)/ `inference`(推理服务,Deployment + 端口)。
49
+ 开发/训练环境的区分轴是**可抢占性**,不是单机多机。
50
+ - **配额单位是卡(张数)**:`guaranteed` 保底张数、`limit` 额外可借张数。集群内部记账用核当量
51
+ (100 核 = 1 卡),分卡负载按份额扣(25 核 = 0.25 卡);`cards_equivalent_used` 是实际占用卡当量。
52
+ - **分卡(vNPU)**:`--xpu 1 --xpu-core C --xpu-memory-mib M` = 单张卡切 C%(1..100)算力 + M MiB 显存。只支持单卡切分:`--xpu` 必须为 1(多卡请按整卡申请)。
53
+ 两个字段必须成对;整卡就都不给。创建后不可改规格。
54
+ - **负载状态**:`submitting → pending/queued → admitted → running`;终态 `completed / stopped / failed / rejected`;
55
+ `cleanup_blocked` / 长期 Terminating 见 troubleshooting(已知上游问题,**不要反复删**)。
56
+ - 指标为 `null` 或 `metrics.available=false` = **数据源未接入**(不是 0),转述时说"未接入",原因在 `metrics.reason`。
57
+
58
+ ## 创建负载前:先查模板与资产
59
+
60
+ 用户要创建负载时,**第一步是查模板,不是从零拼参数**——模板是平台/管理员存好的整张创建表单,
61
+ 往往带着踩过坑才知道的预设(如昇腾官方镜像必需的 LD_LIBRARY_PATH 覆写、就绪探针延迟):
62
+
63
+ ```bash
64
+ runnpu template list --type training-env # ★ 开头的是系统预置;-q 按名称/描述/镜像搜
65
+ runnpu template get <名称> # 看 payload 里都预设了什么
66
+ runnpu asset list --kind environment # 资产:environment / compute / data-source / credential
67
+ ```
68
+
69
+ **有匹配的模板就用 `--from-template`,不要自己读 payload 再手拼参数**:
70
+
71
+ ```bash
72
+ runnpu workload create --project main --name nb6 --from-template <名称> --xpu 2 --wait
73
+ ```
74
+
75
+ - 合并规则由 CLI 保证:模板 payload 作基底,**只有你在命令行上显式写了的字段才覆盖它**
76
+ (`--xpu 0`、`--jupyter=false` 这种「显式的零值」也算数);`--mount` 与模板预设的数据源取并集,
77
+ 挂载点冲突会直接报错。**模板里的 env / 探针等预设不会丢**——那正是模板存在的理由。
78
+ - 模板类型不可覆盖:`--type` 与模板类型不一致会报错,换模板而不是改类型。
79
+ - 模板标记的 `pending_fields`(待填字段)要问用户,别编;提交后服务端 422 会把缺的字段指出来。
80
+ - 如果 CLI 报「模板里有本版本不认识的字段」,说明服务端比 CLI 新——升级 CLI,
81
+ 或退回 `runnpu api POST /workloads -d '<payload + project_id + name>'`。
82
+ - **资产是单个配置段的值拷贝**:environment(镜像+env)/ compute(池+卡数)按段并入;
83
+ credential / data-source 资产在挂载里按名引用(如 `--mount s3:...,credential=<资产名>`)。
84
+ - 列表为空或确实不匹配再手拼参数;用户自带全量参数时以用户为准,但提一句「有现成模板 X」。
85
+
86
+ ## 常用流程
87
+
88
+ **用户给了一个训练工程(文件夹/仓库)要「跑起来」** → 先读 `references/training-runs.md`
89
+ 再动手(搬运方式、密钥注入、后台运行、loss 监控的判断经验都在那)。
90
+
91
+ ### 看现状
92
+
93
+ ```bash
94
+ runnpu overview # KPI、项目资源分布、存储池、排队/闲置负载
95
+ runnpu node list / runnpu node get 910b2 # 物理卡健康与占用,逐卡到负载
96
+ runnpu cluster get # 组件健康(Operator/Kueue/HAMi/Prometheus…)、算力池、存储池
97
+ runnpu workload list [--project X] [--type training-job] [--status running] [--all]
98
+ runnpu workload list --deleted # 删除历史(谁删的、何时)
99
+ ```
100
+
101
+ ### 创建开发环境并拿到入口
102
+
103
+ ```bash
104
+ runnpu workload create --project main --name nb5 --type workspace \
105
+ --image m.daocloud.io/quay.io/ascend/vllm-ascend:v0.22.1rc1 \
106
+ --xpu 1 --security baseline --jupyter --workspace-pool shared-cephfs --workspace-gib 50 --wait
107
+ runnpu workload get main/nb5 # tools[].url 是 Jupyter 地址(平台登录态直达,无需 token)
108
+ ```
109
+
110
+ - `--wait` 阻塞到 `running / failed / rejected`(最多 10 分钟;**大镜像首次拉取可能超过它**,超时后用
111
+ `runnpu workload get` 继续看,不代表失败)。
112
+ - 受管工作盘(`--workspace-*`)非 root 也可写(平台以 GID 20000 + ACL 初始化卷根)。
113
+ - SSH:加 `--ssh`(注入用户已登记公钥)即可,**与安全级无关**——restricted / baseline 都支持
114
+ (run-npu#595 起放行;旧版 operator 才会以 `SshRootInitUnavailable` 拒绝 restricted+SSH)。
115
+ SSH 登录后的身份 = 容器主进程的身份(restricted 下就是你 `--run-as-uid` 指定的非 root 用户)。
116
+ 连接命令用 `runnpu workload endpoints` 拿:
117
+ 登录用户名是 `<负载名>.<命名空间>`(SSHPiper 路由标识,API `ssh.username`),不是平台用户名。
118
+ - restricted 下 root 镜像要 `--run-as-uid`(安全级本身的要求,与 SSH 无关)。
119
+ - `runnpu workload exec main/nb5` 连入终端(交互式;管道喂 stdin 必须以 `exit` 结尾)。
120
+
121
+ ### 训练任务(跑完即止)
122
+
123
+ ```bash
124
+ runnpu workload create --project main --name job1 --type training-job --image img:tag --xpu 2 \
125
+ --command bash --command -c --arg "python train.py" \
126
+ --backoff-limit 2 --active-deadline-seconds 7200 --ttl-after-finished 86400 --wait
127
+ # 多机:--nodes N(N ≥ 2,没有中间值);多机没有 --completions/--parallelism(规模由节点数决定)
128
+ runnpu workload get main/job1 # status=completed 后 batch 进度在 progress 里(succeeded/failed/retries)
129
+ ```
130
+
131
+ ### 推理服务(部署完记得给用户访问地址,见下一节)
132
+
133
+ ```bash
134
+ runnpu workload create --project main --name svc1 --type inference \
135
+ --image m.daocloud.io/quay.io/ascend/vllm-ascend:v0.22.1rc1 --xpu 1 --security baseline \
136
+ --command bash --command -c \
137
+ --arg "python -m vllm.entrypoints.openai.api_server --model /models/qwen --served-model-name qwen --port 8000" \
138
+ --mount pvc:/models,name=model-weights,ro \
139
+ --port http:8000,expose=nodePort --replicas 1 --wait
140
+ runnpu workload get main/svc1 # 工具行显示 nodePort=3xxxx;访问 http://<任一节点IP>:<nodePort>
141
+ ```
142
+
143
+ 三个必须想清楚的点:
144
+
145
+ 1. **不给 `--port` 的推理服务不对外,等于白建**。`expose=clusterIP` 只在集群内可达;对外用 `nodePort`。
146
+ 2. **模型权重从哪来是真正的卡点**:镜像里没有权重。挂 PVC / NFS / 数据卷进来,或问用户有没有内网镜像源——
147
+ 私有化环境**别默认能连 HuggingFace**。用户说"部署一个 X 模型"时先问权重来源,这比拼命令重要。
148
+ 3. **`ready=false (EndpointUnavailable)` 不代表不可达**(已知上游观测缺陷 run-npu#553):`node_port` 已分配、
149
+ Pod Running 时端口通常已可用,用 `curl http://<节点IP>:<nodePort>/` 实测判断,别等 ready 翻真。
150
+
151
+ ### 部署完了怎么访问(用户问得最多的下一个问题)
152
+
153
+ ```bash
154
+ runnpu workload endpoints main/svc1 # 一条命令给出所有入口 + 现在能不能用
155
+ ```
156
+
157
+ 三条路径,按可用性排:
158
+
159
+ 1. **泛域名入口**(`--jupyter` / `--code-server` / `--port …,expose=externalUrl`):`url` 非空即可点,
160
+ 平台登录态直达,不用再输 token;
161
+ 2. **NodePort**(`--port name:端口,expose=nodePort`):`endpoints` 会给出 `http://<节点地址>:<端口>` 直接访问。
162
+ ⚠️ **`workload get` 里这条的 `ready` 会长期是 false**——Operator 只认节点 ExternalIP,私有化集群没有
163
+ (上游 run-npu#553)。端口是真的通的,**别因为 ready=false 就告诉用户"没起来"**,以 `endpoints` 的判断为准;
164
+ 3. **端口转发**(前两条都没有时的兜底,不依赖任何集群地址):
165
+
166
+ ```bash
167
+ runnpu workload port-forward main/svc1 8000:8000 # 然后访问 http://127.0.0.1:8000
168
+ runnpu workload port-forward main/nb5 2222:2222 # 转发 SSH 端口,本地再 ssh 进去
169
+ ```
170
+
171
+ 它走控制面板的 WebSocket 隧道,只要 CLI 能连上平台就能用;Ctrl-C 结束。
172
+
173
+ `workload create --wait` 成功后会自动打印可用地址,不用再问一遍。
174
+
175
+ ### 挂载数据(速查;细节与易错点见 references/data-sources.md)
176
+
177
+ ```bash
178
+ --mount pvc:/models,name=weights,ro # ⚠️ PVC 必须带 npu.run/project-id=<项目slug> 标签,否则报"不存在"
179
+ --mount datavolume:/data,name=<集群卷名>,ro # 跨项目必须 ro;集群卷名用 runnpu datavolume list 查
180
+ --mount newvolume:/out,pool=shared-cephfs,size=20Gi[,persistency=project]
181
+ --mount nfs:/data,server=10.0.0.1,path=/export/ds
182
+ --mount emptydir:/scratch,size=1Gi
183
+ ```
184
+
185
+ **数据源整段创建后不可修改**。用户说"再挂一个盘"= 重建负载,不是找修改命令。
186
+
187
+ ### 排查一个负载(完整决策树见 references/troubleshooting.md)
188
+
189
+ ```bash
190
+ runnpu workload get main/nb5 # 状态、reason.text、Pods、工具入口
191
+ runnpu workload events main/nb5 # 调度/准入/运行时事件
192
+ runnpu workload logs main/nb5 --tail 200 # -f 跟随
193
+ runnpu workload yaml main/nb5 # 实际 CR 含 status
194
+ ```
195
+
196
+ 顺序:`status`+`reason.text` → `events` → `logs`。`queued` 看项目配额;`admitted` 不 `running` 看事件里的
197
+ 镜像拉取/调度;**容器 CrashLoop 且日志有 `symbol lookup error`** → 镜像 LD 与平台注入不兼容,
198
+ 见 troubleshooting 的「镜像兼容性」一节(有一行 env 的救援配方)。
199
+
200
+ ### 停止 / 删除
201
+
202
+ ```bash
203
+ runnpu workload stop|start main/nb5
204
+ runnpu workload delete main/nb5 --yes # 破坏性:先向用户确认
205
+ ```
206
+
207
+ 删除后负载可能停留 Terminating 数分钟乃至更久(上游 run-npu#549:带工具 Service / 受管卷的负载
208
+ teardown 卡住)——**不要反复删、不要 --force 循环**,如实告知用户需等待或运维介入。
209
+
210
+ ### 项目与配额(管理员;全量见 references/admin.md)
211
+
212
+ ```bash
213
+ runnpu project create --slug asr --name 语音识别 --pool ascend910b4 --guaranteed 4 --limit 4 \
214
+ --storage-pool shared-cephfs --storage-gib 200
215
+ runnpu project quota get asr
216
+ runnpu project quota set asr --pool ascend910b4=8,4 --storage-pool shared-cephfs=500 # 整份替换语义!先 get 再 set
217
+ ```
218
+
219
+ 单位是**卡**。⚠️ 建项目后配额落到集群有秒级延迟且可能失败(集群配额被占满时被拒):
220
+ 建完项目**先 `runnpu project quota get <slug>` 确认 `cluster` 段有数据**再建负载,
221
+ 否则负载可能拿到**终态** `rejected / QuotaGrantNotFound`(不会自愈,要删掉重建)。
222
+
223
+ ## 行为准则
224
+
225
+ 1. 只读命令直接执行;**创建/停止/删除/改配额/改成员/禁用用户先复述参数并取得用户确认**,
226
+ 删除、移除、吊销类必须用户明确同意后才加 `--yes`。一次性凭据(初始密码、token 明文)只转给用户,不复述到其它地方。
227
+ 2. 引用负载一律 `<project-slug>/<name>`;项目可用 slug 或 ID。
228
+ 3. 用户点名的东西(模型名/镜像 tag/池名)**可能不存在**:能核对的核对(池用 `runnpu cluster get`),
229
+ 核对不了的明说无法确认、要用户拍板,**绝不静默替换**。
230
+ 4. 不猜状态含义:以 `status`、`reason.text`、`events` 为准;`metrics.available=false` 按"未接入"转述。
231
+ 5. 结果给出用户能直接用的关键字段(名字、状态、入口 URL / nodePort、原因);数字带单位与口径(卡 / 卡当量 / GiB)。
232
+
233
+ <!-- runnpu-skill v0.1.1:与 runnpu CLI 同版本发布,`runnpu --version` 应一致 -->