@themoltnet/node-red-contrib-core 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.
@@ -0,0 +1,398 @@
1
+ [
2
+ {
3
+ "disabled": false,
4
+ "id": "weather_tab",
5
+ "info": "Free-text request → INTENT agent (classify + extract slots) → Open-Meteo forecast → ADVISOR agent (reason → recommend) → JUDGE agent (evaluate vs rubric) → decision.\n\nDemonstrates: external API → agent, passing context via the `task: build` node's context rows, agent output→input chaining via the `task: read` node's pre-computed `outputRef`, and judgment/eval via a freeform task with a rubric.\n\nThe `task: build` nodes compose the create body from the SDK builder; `task: read` parses each completed snapshot into typed output (`msg.payload`) + a flat `msg.result` (summary, outputRef, artifactBody).\n\nFill the moltnet-agent config (clientId/secret + teamId/diaryId) and run a daemon. See the comment node for model-specialization notes.",
6
+ "label": "MoltNet Weather Advisor (multi-agent)",
7
+ "type": "tab"
8
+ },
9
+ {
10
+ "apiUrl": "https://api.themolt.net",
11
+ "clientId": "",
12
+ "diaryId": "",
13
+ "id": "weather_agent_cfg",
14
+ "name": "weather-agent",
15
+ "teamId": "",
16
+ "type": "moltnet-agent"
17
+ },
18
+ {
19
+ "id": "note_specialization",
20
+ "info": "By default every task runs on whatever ONE daemon is up — no `allowedProfiles` set.\n\nTo route INTENT to a small/fast model and ADVISOR/JUDGE to a stronger one: attach a `moltnet-runtime-profile` config node to each `tasks: create`, and run one daemon per profile (`--profile <id>`). `allowedProfiles` is a routing gate, not a model picker.",
21
+ "name": "Model specialization (optional)",
22
+ "type": "comment",
23
+ "x": 200,
24
+ "y": 40,
25
+ "z": "weather_tab"
26
+ },
27
+ {
28
+ "id": "inject_go",
29
+ "name": "go",
30
+ "once": false,
31
+ "payload": "",
32
+ "payloadType": "date",
33
+ "props": [{ "p": "payload" }],
34
+ "repeat": "",
35
+ "topic": "",
36
+ "type": "inject",
37
+ "wires": [["tmpl_request"]],
38
+ "x": 130,
39
+ "y": 120,
40
+ "z": "weather_tab"
41
+ },
42
+ {
43
+ "field": "payload",
44
+ "fieldType": "msg",
45
+ "format": "handlebars",
46
+ "id": "tmpl_request",
47
+ "name": "user request",
48
+ "output": "str",
49
+ "syntax": "plain",
50
+ "template": "good day to bbq in Lyon this weekend?",
51
+ "type": "template",
52
+ "wires": [["build_intent"]],
53
+ "x": 300,
54
+ "y": 120,
55
+ "z": "weather_tab"
56
+ },
57
+ {
58
+ "agent": "weather_agent_cfg",
59
+ "brief": "Classify this request and extract slots. Return your structured answer as a freeform artifact with kind 'json' whose body is a JSON object with fields: proceed (boolean — true only if this is a weather-activity question), location (string), timeframe (string), activity (string). If it is not a weather-activity question, set proceed=false.",
60
+ "contexts": [
61
+ {
62
+ "binding": "context_inline",
63
+ "slug": "user-request",
64
+ "value": "payload",
65
+ "valueType": "msg"
66
+ }
67
+ ],
68
+ "expectedOutput": "",
69
+ "id": "build_intent",
70
+ "name": "build INTENT",
71
+ "referencesFrom": "",
72
+ "referencesRole": "context",
73
+ "schemaCid": "",
74
+ "submitOutputGate": true,
75
+ "taskType": "freeform",
76
+ "type": "moltnet-task-builder",
77
+ "wires": [["create_intent"]],
78
+ "workspace": "none",
79
+ "x": 500,
80
+ "y": 120,
81
+ "z": "weather_tab"
82
+ },
83
+ {
84
+ "agent": "weather_agent_cfg",
85
+ "generateCorrelationId": true,
86
+ "id": "create_intent",
87
+ "name": "INTENT",
88
+ "type": "moltnet-tasks-create",
89
+ "wires": [["wait_intent"]],
90
+ "x": 690,
91
+ "y": 120,
92
+ "z": "weather_tab"
93
+ },
94
+ {
95
+ "agent": "weather_agent_cfg",
96
+ "id": "wait_intent",
97
+ "kinds": "",
98
+ "name": "wait INTENT",
99
+ "pollIntervalSec": 5,
100
+ "tail": true,
101
+ "taskId": "",
102
+ "timeoutSec": 1800,
103
+ "type": "moltnet-task-wait",
104
+ "wires": [["debug_tail"], ["read_intent"]],
105
+ "x": 860,
106
+ "y": 120,
107
+ "z": "weather_tab"
108
+ },
109
+ {
110
+ "artifactKind": "json",
111
+ "artifactTitle": "",
112
+ "id": "read_intent",
113
+ "name": "read INTENT",
114
+ "role": "context",
115
+ "source": "",
116
+ "type": "moltnet-task-reader",
117
+ "wires": [["fn_intent_slots"]],
118
+ "x": 130,
119
+ "y": 220,
120
+ "z": "weather_tab"
121
+ },
122
+ {
123
+ "func": "// task: read parsed the JSON artifact into msg.result.artifactBody and the\n// chaining ref into msg.result.outputRef. Lift the slots onto msg for the\n// switch + the forecast URL builder; keep the outputRef for ADVISOR to chain.\nconst slots = (msg.result && msg.result.artifactBody) || {};\nmsg.intent = slots;\nmsg.intentRef = msg.result && msg.result.outputRef;\nmsg.proceed = slots.proceed === true;\nreturn msg;",
124
+ "id": "fn_intent_slots",
125
+ "name": "intent slots",
126
+ "noerr": 0,
127
+ "outputs": 1,
128
+ "type": "function",
129
+ "wires": [["switch_proceed"]],
130
+ "x": 320,
131
+ "y": 220,
132
+ "z": "weather_tab"
133
+ },
134
+ {
135
+ "checkall": "true",
136
+ "id": "switch_proceed",
137
+ "name": "weather-activity?",
138
+ "outputs": 2,
139
+ "property": "proceed",
140
+ "propertyType": "msg",
141
+ "rules": [{ "t": "true" }, { "t": "else" }],
142
+ "type": "switch",
143
+ "wires": [["fn_build_url"], ["debug_unsupported"]],
144
+ "x": 520,
145
+ "y": 220,
146
+ "z": "weather_tab"
147
+ },
148
+ {
149
+ "func": "// Geocoding is out of scope for the demo: a tiny lookup table, default Lyon.\nconst COORDS = { lyon: [45.76, 4.84], paris: [48.85, 2.35], london: [51.51, -0.13], berlin: [52.52, 13.40] };\nconst key = String((msg.intent && msg.intent.location) || 'lyon').toLowerCase();\nconst [lat, lon] = COORDS[key] || COORDS.lyon;\nmsg.url =\n 'https://api.open-meteo.com/v1/forecast?latitude=' + lat + '&longitude=' + lon +\n '&daily=temperature_2m_max,precipitation_probability_max&forecast_days=7&timezone=auto';\nreturn msg;",
150
+ "id": "fn_build_url",
151
+ "name": "forecast URL",
152
+ "noerr": 0,
153
+ "outputs": 1,
154
+ "type": "function",
155
+ "wires": [["http_forecast"]],
156
+ "x": 710,
157
+ "y": 220,
158
+ "z": "weather_tab"
159
+ },
160
+ {
161
+ "id": "http_forecast",
162
+ "method": "GET",
163
+ "name": "Open-Meteo",
164
+ "persist": false,
165
+ "ret": "obj",
166
+ "tls": "",
167
+ "type": "http request",
168
+ "url": "",
169
+ "wires": [["build_advisor"]],
170
+ "x": 130,
171
+ "y": 320,
172
+ "z": "weather_tab"
173
+ },
174
+ {
175
+ "agent": "weather_agent_cfg",
176
+ "brief": "Given the 7-day forecast and the user's activity, recommend the best day(s) with concrete reasoning (temperature, rain probability), or advise against if the weather is poor. Be specific about which day and why.",
177
+ "contexts": [
178
+ {
179
+ "binding": "context_inline",
180
+ "slug": "intent",
181
+ "value": "intent",
182
+ "valueType": "msg"
183
+ },
184
+ {
185
+ "binding": "context_inline",
186
+ "slug": "forecast",
187
+ "value": "payload",
188
+ "valueType": "msg"
189
+ }
190
+ ],
191
+ "expectedOutput": "",
192
+ "id": "build_advisor",
193
+ "name": "build ADVISOR",
194
+ "referencesFrom": "intentRef",
195
+ "referencesRole": "context",
196
+ "schemaCid": "",
197
+ "submitOutputGate": false,
198
+ "taskType": "freeform",
199
+ "type": "moltnet-task-builder",
200
+ "wires": [["create_advisor"]],
201
+ "workspace": "none",
202
+ "x": 330,
203
+ "y": 320,
204
+ "z": "weather_tab"
205
+ },
206
+ {
207
+ "agent": "weather_agent_cfg",
208
+ "id": "create_advisor",
209
+ "name": "ADVISOR",
210
+ "type": "moltnet-tasks-create",
211
+ "wires": [["wait_advisor"]],
212
+ "x": 520,
213
+ "y": 320,
214
+ "z": "weather_tab"
215
+ },
216
+ {
217
+ "agent": "weather_agent_cfg",
218
+ "id": "wait_advisor",
219
+ "kinds": "",
220
+ "name": "wait ADVISOR",
221
+ "pollIntervalSec": 5,
222
+ "tail": true,
223
+ "taskId": "",
224
+ "timeoutSec": 1800,
225
+ "type": "moltnet-task-wait",
226
+ "wires": [["debug_tail"], ["read_advisor"]],
227
+ "x": 700,
228
+ "y": 320,
229
+ "z": "weather_tab"
230
+ },
231
+ {
232
+ "artifactKind": "",
233
+ "artifactTitle": "",
234
+ "id": "read_advisor",
235
+ "name": "read ADVISOR",
236
+ "role": "judged_work",
237
+ "source": "",
238
+ "type": "moltnet-task-reader",
239
+ "wires": [["fn_carry_recommendation"]],
240
+ "x": 870,
241
+ "y": 320,
242
+ "z": "weather_tab"
243
+ },
244
+ {
245
+ "func": "// Carry the recommendation summary past the JUDGE payload overwrite, and keep\n// the advisor outputRef (role: judged_work) for the JUDGE task to reference.\nmsg.recommendation = (msg.result && msg.result.summary) || (msg.payload && msg.payload.summary);\nmsg.advisorRef = msg.result && msg.result.outputRef;\nreturn msg;",
246
+ "id": "fn_carry_recommendation",
247
+ "name": "carry recommendation",
248
+ "noerr": 0,
249
+ "outputs": 1,
250
+ "type": "function",
251
+ "wires": [["build_judge"]],
252
+ "x": 130,
253
+ "y": 420,
254
+ "z": "weather_tab"
255
+ },
256
+ {
257
+ "agent": "weather_agent_cfg",
258
+ "brief": "Evaluate the weather recommendation against the rubric. Return a freeform artifact with kind 'json' whose body is a JSON object with fields: pass (boolean), score (0-1), notes (string). pass=true only if the advice is sound, matches the forecast, and is actionable.",
259
+ "contexts": [
260
+ {
261
+ "binding": "context_inline",
262
+ "slug": "recommendation",
263
+ "value": "recommendation",
264
+ "valueType": "msg"
265
+ }
266
+ ],
267
+ "expectedOutput": "",
268
+ "id": "build_judge",
269
+ "name": "build JUDGE",
270
+ "referencesFrom": "advisorRef",
271
+ "referencesRole": "judged_work",
272
+ "schemaCid": "",
273
+ "submitOutputGate": true,
274
+ "taskType": "freeform",
275
+ "type": "moltnet-task-builder",
276
+ "wires": [["create_judge"]],
277
+ "workspace": "none",
278
+ "x": 330,
279
+ "y": 420,
280
+ "z": "weather_tab"
281
+ },
282
+ {
283
+ "agent": "weather_agent_cfg",
284
+ "id": "create_judge",
285
+ "name": "JUDGE",
286
+ "type": "moltnet-tasks-create",
287
+ "wires": [["wait_judge"]],
288
+ "x": 520,
289
+ "y": 420,
290
+ "z": "weather_tab"
291
+ },
292
+ {
293
+ "agent": "weather_agent_cfg",
294
+ "id": "wait_judge",
295
+ "kinds": "",
296
+ "name": "wait JUDGE",
297
+ "pollIntervalSec": 5,
298
+ "tail": true,
299
+ "taskId": "",
300
+ "timeoutSec": 1800,
301
+ "type": "moltnet-task-wait",
302
+ "wires": [["debug_tail"], ["read_judge"]],
303
+ "x": 700,
304
+ "y": 420,
305
+ "z": "weather_tab"
306
+ },
307
+ {
308
+ "artifactKind": "json",
309
+ "artifactTitle": "",
310
+ "id": "read_judge",
311
+ "name": "read JUDGE",
312
+ "role": "context",
313
+ "source": "",
314
+ "type": "moltnet-task-reader",
315
+ "wires": [["fn_verdict"]],
316
+ "x": 870,
317
+ "y": 420,
318
+ "z": "weather_tab"
319
+ },
320
+ {
321
+ "func": "// task: read parsed the verdict JSON into msg.result.artifactBody.\nconst verdict = (msg.result && msg.result.artifactBody) || {};\nmsg.verdict = verdict;\nmsg.pass = verdict.pass === true;\nreturn msg;",
322
+ "id": "fn_verdict",
323
+ "name": "verdict",
324
+ "noerr": 0,
325
+ "outputs": 1,
326
+ "type": "function",
327
+ "wires": [["switch_verdict"]],
328
+ "x": 130,
329
+ "y": 520,
330
+ "z": "weather_tab"
331
+ },
332
+ {
333
+ "checkall": "true",
334
+ "id": "switch_verdict",
335
+ "name": "judge passed?",
336
+ "outputs": 2,
337
+ "property": "pass",
338
+ "propertyType": "msg",
339
+ "rules": [{ "t": "true" }, { "t": "else" }],
340
+ "type": "switch",
341
+ "wires": [["debug_final"], ["debug_flagged"]],
342
+ "x": 330,
343
+ "y": 520,
344
+ "z": "weather_tab"
345
+ },
346
+ {
347
+ "active": true,
348
+ "complete": "payload",
349
+ "console": false,
350
+ "id": "debug_tail",
351
+ "name": "tail (live)",
352
+ "tosidebar": true,
353
+ "type": "debug",
354
+ "wires": [],
355
+ "x": 1060,
356
+ "y": 200,
357
+ "z": "weather_tab"
358
+ },
359
+ {
360
+ "active": true,
361
+ "complete": "intent",
362
+ "console": false,
363
+ "id": "debug_unsupported",
364
+ "name": "not a weather-activity question",
365
+ "tosidebar": true,
366
+ "type": "debug",
367
+ "wires": [],
368
+ "x": 720,
369
+ "y": 160,
370
+ "z": "weather_tab"
371
+ },
372
+ {
373
+ "active": true,
374
+ "complete": "recommendation",
375
+ "console": false,
376
+ "id": "debug_final",
377
+ "name": "RECOMMENDATION (judge passed)",
378
+ "tosidebar": true,
379
+ "type": "debug",
380
+ "wires": [],
381
+ "x": 560,
382
+ "y": 500,
383
+ "z": "weather_tab"
384
+ },
385
+ {
386
+ "active": true,
387
+ "complete": "verdict",
388
+ "console": false,
389
+ "id": "debug_flagged",
390
+ "name": "FLAGGED (judge rejected)",
391
+ "tosidebar": true,
392
+ "type": "debug",
393
+ "wires": [],
394
+ "x": 560,
395
+ "y": 560,
396
+ "z": "weather_tab"
397
+ }
398
+ ]
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@themoltnet/node-red-contrib-core",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Node-RED nodes for the MoltNet API",
6
+ "keywords": [
7
+ "node-red",
8
+ "moltnet"
9
+ ],
10
+ "license": "MIT",
11
+ "author": "getlarge",
12
+ "homepage": "https://github.com/getlarge/themoltnet/tree/main/libs/node-red-contrib-core#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/getlarge/themoltnet/issues"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/getlarge/themoltnet.git",
19
+ "directory": "libs/node-red-contrib-core"
20
+ },
21
+ "node-red": {
22
+ "version": ">=5.0.0",
23
+ "nodes": {
24
+ "moltnet-agent": "dist/nodes/agent.js",
25
+ "moltnet-runtime-profile": "dist/nodes/runtime-profile.js",
26
+ "moltnet-tasks-create": "dist/nodes/tasks-create.js",
27
+ "moltnet-task-get": "dist/nodes/task-get.js",
28
+ "moltnet-task-wait": "dist/nodes/task-wait.js",
29
+ "moltnet-workflow-status": "dist/nodes/workflow-status.js",
30
+ "moltnet-task-builder": "dist/nodes/task-builder.js",
31
+ "moltnet-task-reader": "dist/nodes/task-reader.js"
32
+ }
33
+ },
34
+ "engines": {
35
+ "node": ">=20"
36
+ },
37
+ "main": "dist/nodes/agent.js",
38
+ "dependencies": {
39
+ "@themoltnet/sdk": "^0.112.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^22.19.0",
43
+ "@types/node-red": "1.3.5",
44
+ "typescript": "~5.9.2",
45
+ "vite": "^8.0.0",
46
+ "vitest": "^3.0.0"
47
+ },
48
+ "files": [
49
+ "dist",
50
+ "examples",
51
+ "README.md"
52
+ ],
53
+ "nx": {
54
+ "tags": [
55
+ "type:client",
56
+ "scope:node-red",
57
+ "platform:server"
58
+ ]
59
+ },
60
+ "scripts": {
61
+ "build": "vite build",
62
+ "dev": "node scripts/dev.mjs",
63
+ "check:pack": "tsx scripts/check-pack.ts"
64
+ }
65
+ }