@nodetool-ai/agents 0.8.4-a24.3 → 0.8.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.
- package/dist/.nodetool-build-stamp +1 -1
- package/dist/_skills/LICENSE-iart-motion-design-skills +21 -0
- package/dist/_skills/README.md +211 -0
- package/dist/_skills/api-agents/SKILL.md +58 -0
- package/dist/_skills/api-apps/SKILL.md +54 -0
- package/dist/_skills/api-assets/SKILL.md +66 -0
- package/dist/_skills/api-collections/SKILL.md +33 -0
- package/dist/_skills/api-games/SKILL.md +81 -0
- package/dist/_skills/api-media/SKILL.md +131 -0
- package/dist/_skills/api-memory/SKILL.md +72 -0
- package/dist/_skills/api-models/SKILL.md +78 -0
- package/dist/_skills/api-scripts/SKILL.md +79 -0
- package/dist/_skills/api-settings/SKILL.md +49 -0
- package/dist/_skills/api-sketches/SKILL.md +61 -0
- package/dist/_skills/api-storyboards/SKILL.md +94 -0
- package/dist/_skills/api-timelines/SKILL.md +151 -0
- package/dist/_skills/api-web/SKILL.md +57 -0
- package/dist/_skills/api-workflows/SKILL.md +123 -0
- package/dist/_skills/beat-sync-editing/SKILL.md +208 -0
- package/dist/_skills/caption-titles/SKILL.md +94 -0
- package/dist/_skills/color-motion/SKILL.md +185 -0
- package/dist/_skills/commercial-beat-sheet/SKILL.md +338 -0
- package/dist/_skills/elevenlabs-audio-prompting/SKILL.md +163 -0
- package/dist/_skills/explainer-storyboard/SKILL.md +63 -0
- package/dist/_skills/flux-2-klein-prompting/SKILL.md +107 -0
- package/dist/_skills/frame-composition/SKILL.md +291 -0
- package/dist/_skills/game-direction/SKILL.md +99 -0
- package/dist/_skills/gpt-image-2-prompting/SKILL.md +161 -0
- package/dist/_skills/hailuo-prompting/SKILL.md +107 -0
- package/dist/_skills/kling-video-prompting/SKILL.md +188 -0
- package/dist/_skills/launch-commercial/SKILL.md +267 -0
- package/dist/_skills/launch-kit/SKILL.md +120 -0
- package/dist/_skills/logo-reveal/SKILL.md +225 -0
- package/dist/_skills/manimgx/SKILL.md +123 -0
- package/dist/_skills/minimax-h3-prompting/SKILL.md +134 -0
- package/dist/_skills/motion-ad/SKILL.md +281 -0
- package/dist/_skills/motion-background/SKILL.md +145 -0
- package/dist/_skills/motion-curves/SKILL.md +302 -0
- package/dist/_skills/motion-direction/SKILL.md +259 -0
- package/dist/_skills/motion-graphics/SKILL.md +217 -0
- package/dist/_skills/motion-principles/SKILL.md +255 -0
- package/dist/_skills/music-video-treatment/SKILL.md +40 -0
- package/dist/_skills/nano-banana-pro-prompting/SKILL.md +133 -0
- package/dist/_skills/native-game/SKILL.md +310 -0
- package/dist/_skills/native-game-reference/SKILL.md +879 -0
- package/dist/_skills/nodetool-3d-scene/SKILL.md +110 -0
- package/dist/_skills/nodetool-api-reference/SKILL.md +338 -0
- package/dist/_skills/nodetool-app-builder/SKILL.md +291 -0
- package/dist/_skills/nodetool-browser-agent/SKILL.md +119 -0
- package/dist/_skills/nodetool-chat-cli/SKILL.md +184 -0
- package/dist/_skills/nodetool-custom-node-developer/SKILL.md +294 -0
- package/dist/_skills/nodetool-deployment/SKILL.md +195 -0
- package/dist/_skills/nodetool-js-scripting/SKILL.md +223 -0
- package/dist/_skills/nodetool-model-provider-config/SKILL.md +348 -0
- package/dist/_skills/nodetool-rag-indexing/SKILL.md +153 -0
- package/dist/_skills/nodetool-sketch/SKILL.md +138 -0
- package/dist/_skills/nodetool-skill-author/SKILL.md +125 -0
- package/dist/_skills/nodetool-troubleshooter/SKILL.md +370 -0
- package/dist/_skills/nodetool-video-post/SKILL.md +149 -0
- package/dist/_skills/nodetool-workflow-builder/SKILL.md +295 -0
- package/dist/_skills/product-commercial/SKILL.md +132 -0
- package/dist/_skills/qwen-image-prompting/SKILL.md +122 -0
- package/dist/_skills/script-video/SKILL.md +106 -0
- package/dist/_skills/seedance-2-prompting/SKILL.md +194 -0
- package/dist/_skills/seedream-prompting/SKILL.md +117 -0
- package/dist/_skills/short-film/SKILL.md +123 -0
- package/dist/_skills/stable-audio-prompting/SKILL.md +130 -0
- package/dist/_skills/storyboard-core/SKILL.md +716 -0
- package/dist/_skills/timeline-edit-ops/SKILL.md +736 -0
- package/dist/_skills/trailer-template/SKILL.md +291 -0
- package/dist/_skills/ugc-video/SKILL.md +186 -0
- package/dist/_skills/veo-3-prompting/SKILL.md +140 -0
- package/dist/_skills/video-audio-continuity/SKILL.md +90 -0
- package/dist/_skills/video-clone/SKILL.md +96 -0
- package/dist/_skills/video-workflow/SKILL.md +119 -0
- package/dist/_skills/wan-2-6-prompting/SKILL.md +107 -0
- package/dist/app-build/bridge.d.ts.map +1 -1
- package/dist/app-build/bridge.js +44 -17
- package/dist/app-build/bridge.js.map +1 -1
- package/dist/capabilities/apps.d.ts.map +1 -1
- package/dist/capabilities/apps.js +55 -13
- package/dist/capabilities/apps.js.map +1 -1
- package/dist/capabilities/apps.specs.d.ts.map +1 -1
- package/dist/capabilities/apps.specs.js +5 -3
- package/dist/capabilities/apps.specs.js.map +1 -1
- package/dist/capabilities/game-asset-browser.d.ts.map +1 -1
- package/dist/capabilities/game-asset-browser.js +6 -1
- package/dist/capabilities/game-asset-browser.js.map +1 -1
- package/dist/capabilities/game.d.ts +1 -1
- package/dist/capabilities/game.d.ts.map +1 -1
- package/dist/capabilities/game.js +71 -56
- package/dist/capabilities/game.js.map +1 -1
- package/dist/capabilities/game.specs.js +1 -1
- package/dist/capabilities/game.specs.js.map +1 -1
- package/dist/capabilities/game3d.d.ts.map +1 -1
- package/dist/capabilities/game3d.js +14 -19
- package/dist/capabilities/game3d.js.map +1 -1
- package/dist/capabilities/gameAssertions.d.ts +52 -0
- package/dist/capabilities/gameAssertions.d.ts.map +1 -0
- package/dist/capabilities/gameAssertions.js +32 -0
- package/dist/capabilities/gameAssertions.js.map +1 -0
- package/dist/capabilities/models.d.ts.map +1 -1
- package/dist/capabilities/models.js +9 -0
- package/dist/capabilities/models.js.map +1 -1
- package/dist/capabilities/storyboards.d.ts.map +1 -1
- package/dist/capabilities/storyboards.js +36 -11
- package/dist/capabilities/storyboards.js.map +1 -1
- package/dist/capabilities/storyboards.specs.d.ts.map +1 -1
- package/dist/capabilities/storyboards.specs.js +11 -1
- package/dist/capabilities/storyboards.specs.js.map +1 -1
- package/dist/capabilities/workflows.d.ts.map +1 -1
- package/dist/capabilities/workflows.js +1 -0
- package/dist/capabilities/workflows.js.map +1 -1
- package/dist/capabilities/workflows.specs.d.ts.map +1 -1
- package/dist/capabilities/workflows.specs.js +4 -3
- package/dist/capabilities/workflows.specs.js.map +1 -1
- package/dist/evals/surfaces/game.d.ts.map +1 -1
- package/dist/evals/surfaces/game.js +29 -1
- package/dist/evals/surfaces/game.js.map +1 -1
- package/dist/game-authoring.d.ts +120 -0
- package/dist/game-authoring.d.ts.map +1 -1
- package/dist/game-build-integrity.d.ts +12 -0
- package/dist/game-build-integrity.d.ts.map +1 -0
- package/dist/game-build-integrity.js +78 -0
- package/dist/game-build-integrity.js.map +1 -0
- package/dist/game-examples.d.ts +44 -3
- package/dist/game-examples.d.ts.map +1 -1
- package/dist/game-examples.js +60 -10
- package/dist/game-examples.js.map +1 -1
- package/dist/system-skills.d.ts +1 -1
- package/dist/system-skills.js +1 -1
- package/dist/tools/builtin-tools.d.ts.map +1 -1
- package/dist/tools/builtin-tools.js +2 -0
- package/dist/tools/builtin-tools.js.map +1 -1
- package/package.json +19 -19
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nodetool-js-scripting
|
|
3
|
+
description: "Write JavaScript that runs in NodeTool's QuickJS sandbox: Code node bodies, saved JS script documents, sandbox package imports, and calling nodes from code."
|
|
4
|
+
featured: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Write NodeTool sandbox JavaScript
|
|
8
|
+
|
|
9
|
+
Three places take the same guest JavaScript, with the same globals, the same
|
|
10
|
+
imports and the same limits.
|
|
11
|
+
|
|
12
|
+
| Where | What it is | Reach it with |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| **Code node** | A body inside a workflow graph | `validate_code`, `run_code`, `test_code` |
|
|
15
|
+
| **JS script document** | A named, versioned script with declared ports, secrets, a timeout and saved tests | `list_js_scripts`, `get_js_script`, `save_js_script`, `edit_js_script`, `validate_js_script`, `run_js_script`, `test_js_script` |
|
|
16
|
+
| **Native flow** | Guest code calling nodes as async functions, no graph | `import "@nodetool-ai/sandbox-nodetool/flow"` |
|
|
17
|
+
|
|
18
|
+
Pick a Code node when the logic belongs to one graph. Pick a script document
|
|
19
|
+
when the same logic is called from several places, needs its own tests, or needs
|
|
20
|
+
version history. Pick native flow when the control flow is easier to write than
|
|
21
|
+
to wire.
|
|
22
|
+
|
|
23
|
+
## The body contract
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
// inputs: { rows: [...], threshold: 10 }
|
|
27
|
+
const kept = inputs.rows.filter((r) => r.score > inputs.threshold);
|
|
28
|
+
progress(50, `kept ${kept.length}`);
|
|
29
|
+
for (const row of kept) await emit("row", row); // streams as it goes
|
|
30
|
+
await output("count", kept.length); // final, posted at the end
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- **Inputs arrive on the `inputs` object**, never as globals of their own name.
|
|
34
|
+
Values are deep-copied through JSON before entering the guest.
|
|
35
|
+
- **Outputs leave through `await output(name, value)` and `await emit(name,
|
|
36
|
+
value)`, and nothing else.** `name` must be a declared output handle. A second
|
|
37
|
+
`output` on the same handle throws. `return` is ordinary control flow and its
|
|
38
|
+
value is ignored. For a script document this is enforced: a body that declares
|
|
39
|
+
outputs and returns them instead of emitting them is an **error**, because a
|
|
40
|
+
script has no legacy return contract. A Code node still accepts the old
|
|
41
|
+
return/yield contract for one more release, with a deprecation warning.
|
|
42
|
+
- **Media inputs arrive as refs.** Read one with `media.bytes` / `media.text` /
|
|
43
|
+
`media.info`, and build one with `media.toDocument` / `toImage` / `toAudio` /
|
|
44
|
+
`toVideo`. Pass the whole input object, not its `uri`, so the ref's own type
|
|
45
|
+
travels with it. Every `media.*` call is async.
|
|
46
|
+
- **`stream` is the input side of `emit`.** A body that mentions `stream` runs
|
|
47
|
+
**once** over the whole stream and pulls its own items. A body that never
|
|
48
|
+
mentions it runs once per incoming item. Nothing is configured: deleting the
|
|
49
|
+
last `stream` call flips the node back.
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
for await (const item of stream(name)) // one handle, in order, until EOS
|
|
53
|
+
for await (const [handle, item] of stream.any()) // every handle, arrival order
|
|
54
|
+
const item = await stream.first(name) // next value, undefined at EOS
|
|
55
|
+
stream.open(name) // could more still arrive?
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- **`state`** survives across streaming invocations and resets at each run.
|
|
59
|
+
- **`progress(percent, message)`** drives the node's progress bar.
|
|
60
|
+
|
|
61
|
+
## Capabilities are globals, libraries are imports
|
|
62
|
+
|
|
63
|
+
Globals need no declaration: `console`, `fetch`, `workspace`, `getSecret`,
|
|
64
|
+
`nodetool.secrets.*`, `sleep`, `crypto`, `format`, `image`, `audio`, `video`,
|
|
65
|
+
`media`, `canvas`, `assetToSandbox` / `sandboxToAsset`, `progress`, `emit` /
|
|
66
|
+
`output`, and the pure helpers `toBase64` / `fromBase64` / `toHex` / `fromHex` /
|
|
67
|
+
`parallelMap`. `media.*` and `workspace.*` need a `ProcessingContext`, so they
|
|
68
|
+
throw in a bare sandbox call with no run behind it.
|
|
69
|
+
|
|
70
|
+
Everything else is a **sandbox package** the body imports:
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
import yaml from "@nodetool-ai/sandbox-yaml";
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Discover them with `list_sandbox_packages` and read one's API with
|
|
77
|
+
`get_sandbox_package_docs`. Do not guess a specifier: a pack this host does not
|
|
78
|
+
carry fails validation with "Install `<pack>`". `nodetool packs
|
|
79
|
+
compile` builds one for a dependency that is not shipped.
|
|
80
|
+
|
|
81
|
+
`image.*`, `audio.*` and `video.*` transforms return a `sandbox://media/<id>`
|
|
82
|
+
handle, not bytes. Chain the calls, then `<type>.toAsset(handle)` before the run
|
|
83
|
+
ends. A handle does not survive into a later run.
|
|
84
|
+
|
|
85
|
+
## Running an agent from code
|
|
86
|
+
|
|
87
|
+
Use `run_agent` when the work needs more than one model call: look something
|
|
88
|
+
up, act, read the result, correct.
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
import { run_agent } from "@nodetool-ai/sandbox-nodetool/agents";
|
|
92
|
+
|
|
93
|
+
const answer = await run_agent({
|
|
94
|
+
prompt: "Read storyboard " + inputs.storyboardId + " and list its shot titles.",
|
|
95
|
+
model: inputs.model, // {provider, id} from a model selector
|
|
96
|
+
tools: ["get_storyboard"], // capability wire names, nothing else
|
|
97
|
+
output_schema: { type: "object", properties: { titles: { type: "array", items: { type: "string" } } } }
|
|
98
|
+
});
|
|
99
|
+
await output("titles", answer.result.titles);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Without `output_schema`, `answer.text` is the agent's final message. The agent
|
|
103
|
+
reports its text and tool calls while it runs. A mini app shows them in an
|
|
104
|
+
Agent Activity widget bound to `op:<id>/exec#transcript`. `generate_text` stays
|
|
105
|
+
the right call for one prompt with no tools.
|
|
106
|
+
|
|
107
|
+
## Calling nodes from code
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
import "@nodetool-ai/sandbox-nodetool/flow"; // mounts the bridge, required
|
|
111
|
+
import { concat } from "@nodetool-ai/sandbox-flow/nodetool.text";
|
|
112
|
+
|
|
113
|
+
const r = await concat({ a: inputs.left, b: inputs.right });
|
|
114
|
+
await output("joined", r.output);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`await` is the edge, a variable is the wire, `Promise.all` is the fan-out. Both
|
|
118
|
+
imports are required: the facade does not mount without the capability module.
|
|
119
|
+
Streaming-output nodes carry `.stream(inputs)`, an async iterable where an early
|
|
120
|
+
`break` closes the stream and runs node cleanup. Every call passes the per-call
|
|
121
|
+
permission gate and bills through the invoking run, bounded by a recursion depth
|
|
122
|
+
cap of 4 and 16 concurrently open streams per run. Streaming *inputs* accept
|
|
123
|
+
arrays only in v1.
|
|
124
|
+
|
|
125
|
+
A program that must open in the editor, be validated, or run on the server still
|
|
126
|
+
builds a graph. Use `nodetool-workflow-builder`
|
|
127
|
+
for that.
|
|
128
|
+
|
|
129
|
+
## Building a timeline from code
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
import { video } from "@nodetool-ai/sandbox-timeline";
|
|
133
|
+
|
|
134
|
+
const v = video({ width: 1920, height: 1080, fps: 30 });
|
|
135
|
+
const title = v.scene("title", 3, (s) => {
|
|
136
|
+
const t = s.text("Hello", { size: 120, weight: 600, color: "#ffffff" });
|
|
137
|
+
t.enter({ from: { opacity: 0 }, at: 0, dur: 0.5 });
|
|
138
|
+
});
|
|
139
|
+
v.series([title]);
|
|
140
|
+
const saved = await v.save(nodetool.timelines, { name: "Hello" });
|
|
141
|
+
await output("timeline_id", saved.timeline_id);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The pack authors a whole motion-graphics cut in seconds, local to each scene,
|
|
145
|
+
and saves it with one `set_timeline_document`, instead of one `edit_timeline`
|
|
146
|
+
op per clip. Pass the body's `nodetool.timelines` to `v.save`: a module cannot
|
|
147
|
+
see the belt. `get_sandbox_package_docs` on the specifier returns the full
|
|
148
|
+
authoring API.
|
|
149
|
+
|
|
150
|
+
Save the build script itself as a JS script document and it links to the
|
|
151
|
+
timeline it makes automatically: `v.save(nodetool.timelines, {name})` with no
|
|
152
|
+
`timeline_id` the first time creates the timeline and links this script to
|
|
153
|
+
it; every later run of the same script updates that timeline in place
|
|
154
|
+
instead of making another. Revise with `edit_js_script`'s string-replacement
|
|
155
|
+
ops (below) rather than resending the whole body.
|
|
156
|
+
|
|
157
|
+
## The loop
|
|
158
|
+
|
|
159
|
+
1. **Write or read the body.** `get_js_script` for a saved script.
|
|
160
|
+
2. **`validate_code` / `validate_js_script` after every edit.** It catches
|
|
161
|
+
syntax, imports against the installed catalog, undefined names, undeclared
|
|
162
|
+
`inputs.*` reads, outputs no `emit`/`output` call reaches, duplicate or
|
|
163
|
+
non-identifier port names, and tests naming ports the script does not
|
|
164
|
+
declare. It is far cheaper than running.
|
|
165
|
+
3. **`run_code` / `run_js_script`** with real inputs. `output` values come back
|
|
166
|
+
as `outputs`, `emit` values as `streamed`, an ordered list of `{name, value}`.
|
|
167
|
+
Stage a streamed input with `input_streams`, keyed by handle.
|
|
168
|
+
4. **`test_code` / `test_js_script`** as the regression check. A case supplies
|
|
169
|
+
`inputs` (or `input_streams`) and optionally `expect` — final values per
|
|
170
|
+
handle, compared structurally, with unnamed outputs ignored, and
|
|
171
|
+
`expected_streamed`, the full ordered emit list. A case with neither passes
|
|
172
|
+
when the body runs without error.
|
|
173
|
+
5. **Save.** `save_js_script` validates first and is CAS on update. For a
|
|
174
|
+
small revision, `edit_js_script(js_script_id, ops)` is cheaper: each op is
|
|
175
|
+
`{old, new}`, and `old` must match exactly once in the current code — like
|
|
176
|
+
a code editor's find/replace, refused by name on zero or on an ambiguous
|
|
177
|
+
match. It validates the result the same way `save_js_script` does.
|
|
178
|
+
|
|
179
|
+
Script documents get the same version family as the other documents:
|
|
180
|
+
`list_js_script_versions`, `get_js_script_version`, `create_js_script_version`,
|
|
181
|
+
`restore_js_script_version`, `delete_js_script_version`.
|
|
182
|
+
|
|
183
|
+
## Secrets and limits
|
|
184
|
+
|
|
185
|
+
Read a credential with `nodetool.secrets.get(name)` — never inline one, and
|
|
186
|
+
never write one into a body or a test case. A script's declared secrets are
|
|
187
|
+
intersected with whatever allowance the invoking context carries. There is no
|
|
188
|
+
`set_secret`: `request_secret` asks the user's own client for one and the value
|
|
189
|
+
never enters the guest, the transcript, or the model's context. A headless run
|
|
190
|
+
carries no secret prompt, so the call is refused by name rather than quietly
|
|
191
|
+
writing something nobody approved.
|
|
192
|
+
|
|
193
|
+
Script composition is bounded like sub-agents: depth cap 4 with a script id
|
|
194
|
+
chain, so a cycle fails the call naming it. Execution time defaults to 30s and
|
|
195
|
+
every limit is overridable per invocation and clamped to a ceiling.
|
|
196
|
+
|
|
197
|
+
## Verify from a shell
|
|
198
|
+
|
|
199
|
+
These are the `nodetool` CLI. From a NodeTool checkout the same commands run
|
|
200
|
+
as `npm run dev:nodetool -- <command>`.
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
nodetool jsscript validate <id|file.json> --json
|
|
204
|
+
nodetool jsscript run <id|file.json> --inputs '{"numbers":[1,2,3]}'
|
|
205
|
+
nodetool jsscript run <id|file.json> --input-streams '{"numbers":[1,2,3]}'
|
|
206
|
+
nodetool jsscript test <id|file.json> --json
|
|
207
|
+
nodetool jsscript versions list|show|create|restore|delete <id>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
A path that exists on disk wins over an id, and a file target needs no database.
|
|
211
|
+
`jsscript test` exits non-zero on any failure and is the keyless selfcheck the
|
|
212
|
+
harness gate runs.
|
|
213
|
+
|
|
214
|
+
## Reference
|
|
215
|
+
|
|
216
|
+
In a NodeTool checkout, these repository sources go further:
|
|
217
|
+
|
|
218
|
+
- `docs/javascript-sandbox.md` — the guest surface, marshaling, limits,
|
|
219
|
+
concurrency and the security model.
|
|
220
|
+
- `docs/js-script-document-design.md` — document shape, storage, invocation
|
|
221
|
+
from agents, Code nodes and mini apps.
|
|
222
|
+
- `docs/harnesses.md` § nodetool jsscript
|
|
223
|
+
- `packages/sandbox-packs/README.md`
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nodetool-model-provider-config
|
|
3
|
+
description: "Configure NodeTool model providers and API keys (list_secrets, request_secret), pick a model with find_model, or set up local Ollama, llama.cpp and Hugging Face models."
|
|
4
|
+
featured: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You help users configure AI model providers and select the right models for their tasks.
|
|
8
|
+
|
|
9
|
+
# In the product
|
|
10
|
+
|
|
11
|
+
The agent has no shell. Configure keys and pick models with these tools:
|
|
12
|
+
|
|
13
|
+
1. `list_secrets` shows which credentials are configured. It never returns a
|
|
14
|
+
value.
|
|
15
|
+
2. `request_secret {key, reason, help_url}` opens a dialog where the user types
|
|
16
|
+
a missing key. The value is saved from the dialog and never passes through
|
|
17
|
+
the agent. Never ask the user to paste a key into chat. When the run has no
|
|
18
|
+
interactive client, name the missing key and point the user to
|
|
19
|
+
Settings > Secrets.
|
|
20
|
+
3. `find_model {capability}` returns a real `{provider, model_id}` and a `ref`
|
|
21
|
+
to assign verbatim to a node's model property. Call it before adding any
|
|
22
|
+
generic AI node. Never write a model id from memory, including the examples
|
|
23
|
+
below.
|
|
24
|
+
4. `list_provider_models {provider}` lists one provider's language models.
|
|
25
|
+
|
|
26
|
+
Inside a code action the same calls are `nodetool.secrets` /
|
|
27
|
+
`nodetool.settings` (load `api-settings`) and `nodetool.models` (load
|
|
28
|
+
`api-models`).
|
|
29
|
+
|
|
30
|
+
The `nodetool` provider is NodeTool's managed service. Its models run on
|
|
31
|
+
platform keys and draw from the user's credit balance, so it needs no key of
|
|
32
|
+
the user's own. Every other provider bills the user's own account.
|
|
33
|
+
|
|
34
|
+
# Provider Overview
|
|
35
|
+
|
|
36
|
+
| Provider | Type | Key Env Var | Models |
|
|
37
|
+
|----------|------|-------------|--------|
|
|
38
|
+
| **OpenAI** | Cloud | `OPENAI_API_KEY` | GPT, GPT-Image, TTS, Whisper |
|
|
39
|
+
| **Anthropic** | Cloud | `ANTHROPIC_API_KEY` | Claude Opus, Sonnet, Haiku |
|
|
40
|
+
| **Gemini (Google)** | Cloud | `GEMINI_API_KEY` | Gemini, Veo, Nano Banana |
|
|
41
|
+
| **xAI** | Cloud | `XAI_API_KEY` | Grok |
|
|
42
|
+
| **Ollama** | Local | `OLLAMA_API_URL` | Qwen, Llama 3, Mistral, any GGUF. Separate install, must be running |
|
|
43
|
+
| **HuggingFace** | Cloud | `HF_TOKEN` | Hosted Inference Providers |
|
|
44
|
+
| **HuggingFace local** (`huggingface-local`) | Local | — | Diffusers/Transformers models via the HuggingFace Python pack |
|
|
45
|
+
| **NodeTool** (`nodetool`) | Cloud, managed | — (credits) | Curated models |
|
|
46
|
+
| **FAL** (`fal_ai`) | Cloud | `FAL_API_KEY` | Fast image/video generation |
|
|
47
|
+
| **KIE** (`kie`) | Cloud | `KIE_API_KEY` | Image, video and music models from many vendors |
|
|
48
|
+
| **ElevenLabs** (`elevenlabs`) | Cloud | `ELEVENLABS_API_KEY` | TTS, voice, sound effects |
|
|
49
|
+
| **Replicate** | Cloud | `REPLICATE_API_TOKEN` | Community models |
|
|
50
|
+
| **Comfy Cloud** | Cloud | `COMFY_API_KEY` | No model list; runs a ComfyUI workflow on Comfy's GPUs via `lib.comfy.RunWorkflowOnCloud` |
|
|
51
|
+
| **vLLM** | Local | `VLLM_BASE_URL` (required) | Self-hosted, OpenAI-compatible |
|
|
52
|
+
| **LM Studio** | Local | `LMSTUDIO_API_URL` | Models loaded in LM Studio's local server |
|
|
53
|
+
| **llama.cpp server** (`llama_cpp`) | Local | `LLAMA_CPP_URL` (required) | A `llama-server` you run |
|
|
54
|
+
| **llama.cpp local** (`node_llama_cpp`) | Local | — | GGUF files in-process; runtime from Package Manager → Engines & tools |
|
|
55
|
+
| **MLX** | Local | — | MLX Python pack, Apple Silicon only |
|
|
56
|
+
|
|
57
|
+
Other registered chat providers (any of these is valid for the CLI's `-p/--provider`):
|
|
58
|
+
`groq`, `mistral`, `deepseek`, `moonshot`, `minimax`, `cerebras`, `alibaba`
|
|
59
|
+
(`DASHSCOPE_API_KEY`, Qwen via Alibaba Cloud Model Studio), `together`,
|
|
60
|
+
`openrouter`, `requesty`, `opper`, `api_route`, `codex`, `claude_agent_sdk`, `lmstudio`,
|
|
61
|
+
`azure_openai` (`AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_ENDPOINT`, models from
|
|
62
|
+
`AZURE_OPENAI_DEPLOYMENTS`), `bedrock` (`BEDROCK_ACCESS_KEY_ID`,
|
|
63
|
+
`BEDROCK_SECRET_ACCESS_KEY`, `BEDROCK_REGION`).
|
|
64
|
+
|
|
65
|
+
# From a shell (self-host)
|
|
66
|
+
|
|
67
|
+
These run in the user's terminal, not in the product. `nodetool models
|
|
68
|
+
providers` shows configured providers, `nodetool models recommended` the curated
|
|
69
|
+
list, and `nodetool models by-provider <provider>` one provider's ids.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Via CLI (encrypted storage)
|
|
73
|
+
nodetool secrets store OPENAI_API_KEY
|
|
74
|
+
nodetool secrets store ANTHROPIC_API_KEY
|
|
75
|
+
nodetool secrets store GEMINI_API_KEY
|
|
76
|
+
nodetool secrets store HF_TOKEN
|
|
77
|
+
nodetool secrets store FAL_API_KEY
|
|
78
|
+
nodetool secrets store REPLICATE_API_TOKEN
|
|
79
|
+
nodetool secrets store COMFY_API_KEY
|
|
80
|
+
|
|
81
|
+
# Via environment variables
|
|
82
|
+
export OPENAI_API_KEY=sk-...
|
|
83
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
84
|
+
export GEMINI_API_KEY=AI...
|
|
85
|
+
export HF_TOKEN=hf_...
|
|
86
|
+
export FAL_API_KEY=...
|
|
87
|
+
export REPLICATE_API_TOKEN=r8_...
|
|
88
|
+
export COMFY_API_KEY=comfyui-...
|
|
89
|
+
export OLLAMA_API_URL=http://localhost:11434
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
# Model Selection by Task
|
|
93
|
+
|
|
94
|
+
The tables name model families as starting points. `find_model` ranks what is
|
|
95
|
+
actually available on this install and returns the exact id.
|
|
96
|
+
|
|
97
|
+
## whisper.cpp speech recognition
|
|
98
|
+
|
|
99
|
+
Both providers are local-only and unavailable on the cloud profile.
|
|
100
|
+
`whisper_cpp` runs speech recognition in the backend through the optional
|
|
101
|
+
`@fugood/whisper.node@1.1.3` runtime package. Install **whisper.cpp** from the
|
|
102
|
+
Package Manager, then download a GGML model from Models. It serves
|
|
103
|
+
`nodetool.text.AutomaticSpeechRecognition` and `whisper_cpp.LiveTranscription`.
|
|
104
|
+
Use the absolute model path returned by discovery unchanged as the ASR model id.
|
|
105
|
+
|
|
106
|
+
Models use the Hugging Face hub cache (see HuggingFace models below for how its
|
|
107
|
+
location resolves). `WHISPER_CPP_MODELS_DIR` adds another directory.
|
|
108
|
+
`WHISPER_CPP_GPU_BACKEND` accepts `auto`, `metal`, `cuda`, `vulkan`, or `cpu`.
|
|
109
|
+
`auto` uses Metal on macOS. On Windows and Linux it tries the CUDA build, then
|
|
110
|
+
the Vulkan build, then the default CPU build. Restart the backend after
|
|
111
|
+
changing the backend setting. Live transcription accepts base64 PCM16
|
|
112
|
+
mono chunks with an optional `content_metadata.sample_rate` and uses Silero
|
|
113
|
+
VAD when installed, otherwise fixed windows.
|
|
114
|
+
|
|
115
|
+
`whisper_cpp_server` calls a user-run `whisper-server`. Set
|
|
116
|
+
`WHISPER_CPP_SERVER_URL` to its base URL. The model id is `default`, representing
|
|
117
|
+
the model loaded by that server. It requires no native runtime package.
|
|
118
|
+
|
|
119
|
+
## Language / Chat
|
|
120
|
+
|
|
121
|
+
| Need | Family | Provider | Notes |
|
|
122
|
+
|------|--------|----------|-------|
|
|
123
|
+
| Best quality | GPT flagship, Claude Opus or Sonnet | OpenAI, Anthropic | Highest capability |
|
|
124
|
+
| Good balance | GPT mini, Gemini Flash | OpenAI, Gemini | Fast + cheap |
|
|
125
|
+
| Local/private | Llama, Qwen | Ollama | No data leaves machine |
|
|
126
|
+
| Lightweight local | Small Llama, Mistral 7B | Ollama | Low memory |
|
|
127
|
+
| Code | Claude Sonnet, GPT flagship | Anthropic, OpenAI | Best for coding |
|
|
128
|
+
|
|
129
|
+
## Image Generation
|
|
130
|
+
|
|
131
|
+
| Need | Model | Provider | Notes |
|
|
132
|
+
|------|-------|----------|-------|
|
|
133
|
+
| Best quality | FLUX.2 Dev | HuggingFace, FAL | State-of-art |
|
|
134
|
+
| Fast | FLUX Schnell | HuggingFace | Quick iterations |
|
|
135
|
+
| Versatile | SDXL | HuggingFace | Many LoRAs available |
|
|
136
|
+
| API-based | GPT Image 2, Nano Banana | OpenAI, Gemini/KIE | No local GPU needed |
|
|
137
|
+
|
|
138
|
+
## Video Generation
|
|
139
|
+
|
|
140
|
+
| Need | Model | Provider |
|
|
141
|
+
|------|-------|----------|
|
|
142
|
+
| Best quality | Sora 2 Pro | OpenAI (KIE) |
|
|
143
|
+
| Fast | Wan 2.6 | KIE |
|
|
144
|
+
| Image-to-video | Kling 2.6 | KIE |
|
|
145
|
+
| Talking avatar | Kling AI Avatar | KIE |
|
|
146
|
+
|
|
147
|
+
## Speech & Audio
|
|
148
|
+
|
|
149
|
+
| Need | Model | Provider |
|
|
150
|
+
|------|-------|----------|
|
|
151
|
+
| TTS (quality) | ElevenLabs | ElevenLabs |
|
|
152
|
+
| TTS (fast/free) | Kokoro | HuggingFace pack, MLX pack, Transformers.js |
|
|
153
|
+
| ASR (accuracy) | Whisper Large V3 | HuggingFace pack, whisper.cpp |
|
|
154
|
+
| ASR (fast) | Whisper Turbo | HuggingFace pack, MLX pack, whisper.cpp |
|
|
155
|
+
|
|
156
|
+
## Embeddings
|
|
157
|
+
|
|
158
|
+
| Need | Model | Provider |
|
|
159
|
+
|------|-------|----------|
|
|
160
|
+
| General text | text-embedding-3-small | OpenAI |
|
|
161
|
+
| Best quality | text-embedding-3-large | OpenAI |
|
|
162
|
+
| Local/free | sentence-transformers | HuggingFace |
|
|
163
|
+
|
|
164
|
+
# Local Model Setup
|
|
165
|
+
|
|
166
|
+
## Ollama (Easiest)
|
|
167
|
+
|
|
168
|
+
Ollama is a separate program. NodeTool does not ship or start it, so it must be
|
|
169
|
+
running before its models appear. On macOS and Windows, download it from
|
|
170
|
+
ollama.com. On Linux:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# Install
|
|
174
|
+
curl -fsSL https://ollama.com/install.sh | sh
|
|
175
|
+
|
|
176
|
+
# Pull models
|
|
177
|
+
ollama pull llama3
|
|
178
|
+
ollama pull mistral
|
|
179
|
+
ollama pull qwen2
|
|
180
|
+
|
|
181
|
+
# Verify
|
|
182
|
+
ollama list
|
|
183
|
+
|
|
184
|
+
# NodeTool finds Ollama at 127.0.0.1:11434
|
|
185
|
+
# Override: OLLAMA_API_URL in Settings → Integrations → Local Model Servers,
|
|
186
|
+
# or export OLLAMA_API_URL=http://host:11434
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`OLLAMA_CONTEXT_LENGTH` sets the context window sent with each request.
|
|
190
|
+
Unset, NodeTool uses the model's own value, capped at 32768.
|
|
191
|
+
|
|
192
|
+
## Python packs (HuggingFace local, MLX, Wan2GP)
|
|
193
|
+
|
|
194
|
+
Local Diffusers/Transformers models, MLX models and Wan2GP video run in the
|
|
195
|
+
Python worker, and each needs its pack.
|
|
196
|
+
|
|
197
|
+
- Desktop app: **Tools → Package Manager → Python nodes**. Installing a pack
|
|
198
|
+
sets up Python first if it is missing. The HuggingFace pack pulls a PyTorch
|
|
199
|
+
2.14 build matched to the GPU. MLX is offered only on Apple Silicon Macs.
|
|
200
|
+
On a Mac, both packs need Apple Silicon and macOS 14 or newer.
|
|
201
|
+
- Without the desktop app: a Python 3.11+ env with
|
|
202
|
+
`pip install nodetool-core nodetool-huggingface` (plus `nodetool-mlx` on
|
|
203
|
+
Apple Silicon), then start the server with `NODETOOL_PYTHON` pointing at that
|
|
204
|
+
env's `python`. `curl -fsSL https://raw.githubusercontent.com/nodetool-ai/nodetool/main/install.sh | bash -s -- -y --pack huggingface`
|
|
205
|
+
builds that env on Linux and macOS. `--pack` takes `huggingface`, `mlx`, or
|
|
206
|
+
`wan2gp`. On macOS, always set `NODETOOL_PYTHON` to the env it prints.
|
|
207
|
+
- `NODETOOL_TORCH_DEVICE` (`cuda`, `cuda:1`, `mps`, `cpu`) picks the device.
|
|
208
|
+
- Wan2GP nodes call a Wan2GP server the user runs. `WAN2GP_MCP_URL` (default
|
|
209
|
+
`http://127.0.0.1:7866/mcp`) or the node's `server_url` points at it.
|
|
210
|
+
|
|
211
|
+
## HuggingFace models
|
|
212
|
+
|
|
213
|
+
Download local models in the Model Manager before running the workflow. A node
|
|
214
|
+
that downloads on first use can fail on a gated repo or stall a run.
|
|
215
|
+
|
|
216
|
+
For gated models:
|
|
217
|
+
1. Accept the license on the model page at huggingface.co
|
|
218
|
+
2. Store `HF_TOKEN` in Settings (or `nodetool secrets store HF_TOKEN`). Model
|
|
219
|
+
Manager downloads use it
|
|
220
|
+
3. Download the model in the Model Manager
|
|
221
|
+
|
|
222
|
+
Models go to the Hugging Face hub cache: `HF_HUB_CACHE`, else
|
|
223
|
+
`HUGGINGFACE_HUB_CACHE`, else `$HF_HOME/hub`, else
|
|
224
|
+
`$XDG_CACHE_HOME/huggingface/hub`, else `~/.cache/huggingface/hub`. The
|
|
225
|
+
server and the Python worker (through `huggingface_hub`) apply this one order.
|
|
226
|
+
The desktop app keeps `~/.cache/huggingface` when `XDG_CACHE_HOME` is set (as
|
|
227
|
+
in Flatpak) but that cache already holds models.
|
|
228
|
+
|
|
229
|
+
## llama.cpp
|
|
230
|
+
|
|
231
|
+
Two providers. `node_llama_cpp` loads GGUF files in-process after the runtime
|
|
232
|
+
is installed from Package Manager → Engines & tools. `llama_cpp` calls a
|
|
233
|
+
`llama-server` the user runs, at `LLAMA_CPP_URL`. Best for CPU inference and
|
|
234
|
+
quantized models.
|
|
235
|
+
|
|
236
|
+
## whisper.cpp and Transformers.js
|
|
237
|
+
|
|
238
|
+
whisper.cpp is speech recognition only (see above). Transformers.js runs small
|
|
239
|
+
ONNX models in-process after its runtime is installed from Package Manager →
|
|
240
|
+
Software. Its cache is `TRANSFORMERS_JS_CACHE_DIR`.
|
|
241
|
+
|
|
242
|
+
## MLX (Apple Silicon only)
|
|
243
|
+
|
|
244
|
+
Needs the MLX pack and macOS 14 or newer. Lower memory usage than standard
|
|
245
|
+
PyTorch.
|
|
246
|
+
|
|
247
|
+
# Local Inference Performance
|
|
248
|
+
|
|
249
|
+
| Framework | Throughput | Memory | Hardware |
|
|
250
|
+
|-----------|-----------|--------|----------|
|
|
251
|
+
| **llama.cpp** | Medium | Excellent | CPU, GPU |
|
|
252
|
+
| **MLX** | Good | Excellent | Apple Silicon |
|
|
253
|
+
| **Transformers** | Medium | Good | Any |
|
|
254
|
+
|
|
255
|
+
# Provider-Agnostic Nodes
|
|
256
|
+
|
|
257
|
+
These nodes work with any provider — just select the model:
|
|
258
|
+
|
|
259
|
+
| Node | Purpose |
|
|
260
|
+
|------|---------|
|
|
261
|
+
| `nodetool.agents.Agent` | Any LLM for chat/reasoning |
|
|
262
|
+
| `nodetool.image.TextToImage` | Any image generation model |
|
|
263
|
+
| `nodetool.image.ImageToImage` | Any image transformation model |
|
|
264
|
+
| `nodetool.video.TextToVideo` | Any video generation model |
|
|
265
|
+
| `nodetool.video.ImageToVideo` | Any image-to-video model |
|
|
266
|
+
| `nodetool.audio.TextToSpeech` | Any TTS model |
|
|
267
|
+
| `nodetool.text.AutomaticSpeechRecognition` | Any ASR model |
|
|
268
|
+
|
|
269
|
+
# Provider Capabilities
|
|
270
|
+
|
|
271
|
+
| Capability | OpenAI | Anthropic | Google | Ollama | HF |
|
|
272
|
+
|-----------|--------|-----------|--------|--------|-----|
|
|
273
|
+
| Chat/Text | yes | yes | yes | yes | yes |
|
|
274
|
+
| Vision | yes | yes | yes | some | yes |
|
|
275
|
+
| Image Gen | yes (GPT-Image) | no | no | no | yes |
|
|
276
|
+
| Video Gen | no | no | yes (Veo) | no | some |
|
|
277
|
+
| TTS | yes | no | no | no | yes |
|
|
278
|
+
| ASR | yes (Whisper) | no | no | no | yes |
|
|
279
|
+
| Embeddings | yes | no | yes | yes | yes |
|
|
280
|
+
| Tool Calling | yes | yes | yes | some | no |
|
|
281
|
+
|
|
282
|
+
# Common Pitfalls
|
|
283
|
+
|
|
284
|
+
- **Wrong key env var name**: Each provider has a specific name (see table above)
|
|
285
|
+
- **Ollama not running**: NodeTool does not start it. Start the Ollama app or `ollama serve`
|
|
286
|
+
- **Gated HF models**: Must accept terms on huggingface.co first
|
|
287
|
+
- **GPU memory**: Large models need 8-24GB VRAM; use quantized versions
|
|
288
|
+
- **PyTorch on the CPU despite a GPU**: the desktop app installs CUDA 12.8 for Turing (RTX 20xx) and newer (driver 570+), CUDA 12.6 for Maxwell to Volta, and CPU for Kepler, pre-RDNA 2 AMD and AMD on Windows. It also falls back to CPU, with a warning, when no GPU build resolves. A manual `pip install torch` on Linux gets CUDA 13.0, which needs driver 580+ and Turing or newer, as does the Docker worker image
|
|
289
|
+
- **No local provider in the model menu**: the Python pack is missing, or `NODETOOL_PYTHON_ON_DEMAND=true` and no workflow has started the worker yet
|
|
290
|
+
- **Rate limits**: Cloud providers have rate limits; implement retries or use local
|
|
291
|
+
- **Model ID mismatch**: Take ids from `find_model` or `list_provider_models`, never from memory. From a shell, `nodetool models by-provider <provider>` lists them
|
|
292
|
+
- **Key pasted into chat**: Use `request_secret`. A key in a chat message is stored in the thread
|
|
293
|
+
|
|
294
|
+
# Repository only: custom provider
|
|
295
|
+
|
|
296
|
+
This is TypeScript for contributors working in a NodeTool checkout. The
|
|
297
|
+
in-product agent cannot add a provider. Providers extend `BaseProvider` from `@nodetool-ai/runtime` (not `@nodetool-ai/core`).
|
|
298
|
+
Both `generateMessage` and `generateMessages` take a single **args object**.
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import {
|
|
302
|
+
BaseProvider,
|
|
303
|
+
type ProviderId,
|
|
304
|
+
type Message,
|
|
305
|
+
type ProviderStreamItem,
|
|
306
|
+
type ProviderTool,
|
|
307
|
+
type LanguageModel,
|
|
308
|
+
} from "@nodetool-ai/runtime";
|
|
309
|
+
|
|
310
|
+
export class MyProvider extends BaseProvider {
|
|
311
|
+
private apiKey: string;
|
|
312
|
+
|
|
313
|
+
constructor(kwargs: Record<string, unknown> = {}) {
|
|
314
|
+
super("my_provider" as ProviderId);
|
|
315
|
+
this.apiKey = String(kwargs["MY_API_KEY"] ?? process.env.MY_API_KEY ?? "");
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
static override requiredSecrets(): string[] {
|
|
319
|
+
return ["MY_API_KEY"];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Non-streaming: return a single assistant Message.
|
|
323
|
+
async generateMessage(args: {
|
|
324
|
+
messages: Message[];
|
|
325
|
+
model: string;
|
|
326
|
+
tools?: ProviderTool[];
|
|
327
|
+
}): Promise<Message> {
|
|
328
|
+
// Call your API with args.messages / args.model …
|
|
329
|
+
return { role: "assistant", content: "response text" };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Streaming: yield ProviderStreamItem chunks.
|
|
333
|
+
async *generateMessages(args: {
|
|
334
|
+
messages: Message[];
|
|
335
|
+
model: string;
|
|
336
|
+
tools?: ProviderTool[];
|
|
337
|
+
}): AsyncGenerator<ProviderStreamItem> {
|
|
338
|
+
yield { type: "chunk", content: "response text", done: false };
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
override async getAvailableLanguageModels(): Promise<LanguageModel[]> {
|
|
342
|
+
return [{ id: "my-model", name: "My Model", provider: "my_provider" }];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Register it with `registerProvider("my_provider", MyProvider)` from
|
|
348
|
+
`@nodetool-ai/runtime`.
|