@nodetool-ai/base-nodes 0.7.0-rc.22 → 0.8.1
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/README.md +21 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/index.d.ts +61 -67
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +179 -173
- package/dist/index.js.map +1 -1
- package/dist/platforms/browser.d.ts +19 -0
- package/dist/platforms/browser.d.ts.map +1 -0
- package/dist/platforms/browser.js +29 -0
- package/dist/platforms/browser.js.map +1 -0
- package/package.json +53 -65
- package/dist/lib/audio-wav.d.ts +0 -66
- package/dist/lib/audio-wav.d.ts.map +0 -1
- package/dist/lib/audio-wav.js +0 -215
- package/dist/lib/audio-wav.js.map +0 -1
- package/dist/nodes/agent-tool-hydration.d.ts +0 -10
- package/dist/nodes/agent-tool-hydration.d.ts.map +0 -1
- package/dist/nodes/agent-tool-hydration.js +0 -43
- package/dist/nodes/agent-tool-hydration.js.map +0 -1
- package/dist/nodes/agents.d.ts +0 -262
- package/dist/nodes/agents.d.ts.map +0 -1
- package/dist/nodes/agents.js +0 -2856
- package/dist/nodes/agents.js.map +0 -1
- package/dist/nodes/anthropic.d.ts +0 -26
- package/dist/nodes/anthropic.d.ts.map +0 -1
- package/dist/nodes/anthropic.js +0 -133
- package/dist/nodes/anthropic.js.map +0 -1
- package/dist/nodes/apify.d.ts +0 -120
- package/dist/nodes/apify.d.ts.map +0 -1
- package/dist/nodes/apify.js +0 -618
- package/dist/nodes/apify.js.map +0 -1
- package/dist/nodes/audio.d.ts +0 -301
- package/dist/nodes/audio.d.ts.map +0 -1
- package/dist/nodes/audio.js +0 -1274
- package/dist/nodes/audio.js.map +0 -1
- package/dist/nodes/code-node.d.ts +0 -34
- package/dist/nodes/code-node.d.ts.map +0 -1
- package/dist/nodes/code-node.js +0 -343
- package/dist/nodes/code-node.js.map +0 -1
- package/dist/nodes/code.d.ts +0 -301
- package/dist/nodes/code.d.ts.map +0 -1
- package/dist/nodes/code.js +0 -850
- package/dist/nodes/code.js.map +0 -1
- package/dist/nodes/compare.d.ts +0 -18
- package/dist/nodes/compare.d.ts.map +0 -1
- package/dist/nodes/compare.js +0 -122
- package/dist/nodes/compare.js.map +0 -1
- package/dist/nodes/constant.d.ts +0 -315
- package/dist/nodes/constant.d.ts.map +0 -1
- package/dist/nodes/constant.js +0 -715
- package/dist/nodes/constant.js.map +0 -1
- package/dist/nodes/control.d.ts +0 -83
- package/dist/nodes/control.d.ts.map +0 -1
- package/dist/nodes/control.js +0 -212
- package/dist/nodes/control.js.map +0 -1
- package/dist/nodes/data.d.ts +0 -369
- package/dist/nodes/data.d.ts.map +0 -1
- package/dist/nodes/data.js +0 -1590
- package/dist/nodes/data.js.map +0 -1
- package/dist/nodes/document.d.ts +0 -134
- package/dist/nodes/document.d.ts.map +0 -1
- package/dist/nodes/document.js +0 -984
- package/dist/nodes/document.js.map +0 -1
- package/dist/nodes/extended-placeholders.d.ts +0 -6
- package/dist/nodes/extended-placeholders.d.ts.map +0 -1
- package/dist/nodes/extended-placeholders.js +0 -19
- package/dist/nodes/extended-placeholders.js.map +0 -1
- package/dist/nodes/gemini.d.ts +0 -110
- package/dist/nodes/gemini.d.ts.map +0 -1
- package/dist/nodes/gemini.js +0 -871
- package/dist/nodes/gemini.js.map +0 -1
- package/dist/nodes/generators.d.ts +0 -84
- package/dist/nodes/generators.d.ts.map +0 -1
- package/dist/nodes/generators.js +0 -968
- package/dist/nodes/generators.js.map +0 -1
- package/dist/nodes/image.d.ts +0 -232
- package/dist/nodes/image.d.ts.map +0 -1
- package/dist/nodes/image.js +0 -1395
- package/dist/nodes/image.js.map +0 -1
- package/dist/nodes/input.d.ts +0 -445
- package/dist/nodes/input.d.ts.map +0 -1
- package/dist/nodes/input.js +0 -1459
- package/dist/nodes/input.js.map +0 -1
- package/dist/nodes/kie-dynamic.d.ts +0 -31
- package/dist/nodes/kie-dynamic.d.ts.map +0 -1
- package/dist/nodes/kie-dynamic.js +0 -270
- package/dist/nodes/kie-dynamic.js.map +0 -1
- package/dist/nodes/lib-audio-dsp.d.ts +0 -85
- package/dist/nodes/lib-audio-dsp.d.ts.map +0 -1
- package/dist/nodes/lib-audio-dsp.js +0 -450
- package/dist/nodes/lib-audio-dsp.js.map +0 -1
- package/dist/nodes/lib-audio-effects.d.ts +0 -116
- package/dist/nodes/lib-audio-effects.d.ts.map +0 -1
- package/dist/nodes/lib-audio-effects.js +0 -905
- package/dist/nodes/lib-audio-effects.js.map +0 -1
- package/dist/nodes/lib-browser.d.ts +0 -99
- package/dist/nodes/lib-browser.d.ts.map +0 -1
- package/dist/nodes/lib-browser.js +0 -692
- package/dist/nodes/lib-browser.js.map +0 -1
- package/dist/nodes/lib-charts.d.ts +0 -20
- package/dist/nodes/lib-charts.d.ts.map +0 -1
- package/dist/nodes/lib-charts.js +0 -245
- package/dist/nodes/lib-charts.js.map +0 -1
- package/dist/nodes/lib-datetime.d.ts +0 -108
- package/dist/nodes/lib-datetime.d.ts.map +0 -1
- package/dist/nodes/lib-datetime.js +0 -421
- package/dist/nodes/lib-datetime.js.map +0 -1
- package/dist/nodes/lib-doc-convert.d.ts +0 -19
- package/dist/nodes/lib-doc-convert.d.ts.map +0 -1
- package/dist/nodes/lib-doc-convert.js +0 -257
- package/dist/nodes/lib-doc-convert.js.map +0 -1
- package/dist/nodes/lib-doc-transform.d.ts +0 -39
- package/dist/nodes/lib-doc-transform.d.ts.map +0 -1
- package/dist/nodes/lib-doc-transform.js +0 -335
- package/dist/nodes/lib-doc-transform.js.map +0 -1
- package/dist/nodes/lib-docx.d.ts +0 -106
- package/dist/nodes/lib-docx.d.ts.map +0 -1
- package/dist/nodes/lib-docx.js +0 -595
- package/dist/nodes/lib-docx.js.map +0 -1
- package/dist/nodes/lib-epub.d.ts +0 -55
- package/dist/nodes/lib-epub.d.ts.map +0 -1
- package/dist/nodes/lib-epub.js +0 -220
- package/dist/nodes/lib-epub.js.map +0 -1
- package/dist/nodes/lib-excel.d.ts +0 -74
- package/dist/nodes/lib-excel.d.ts.map +0 -1
- package/dist/nodes/lib-excel.js +0 -448
- package/dist/nodes/lib-excel.js.map +0 -1
- package/dist/nodes/lib-graphql.d.ts +0 -66
- package/dist/nodes/lib-graphql.d.ts.map +0 -1
- package/dist/nodes/lib-graphql.js +0 -333
- package/dist/nodes/lib-graphql.js.map +0 -1
- package/dist/nodes/lib-grid.d.ts +0 -27
- package/dist/nodes/lib-grid.d.ts.map +0 -1
- package/dist/nodes/lib-grid.js +0 -172
- package/dist/nodes/lib-grid.js.map +0 -1
- package/dist/nodes/lib-html-parse.d.ts +0 -108
- package/dist/nodes/lib-html-parse.d.ts.map +0 -1
- package/dist/nodes/lib-html-parse.js +0 -365
- package/dist/nodes/lib-html-parse.js.map +0 -1
- package/dist/nodes/lib-http.d.ts +0 -98
- package/dist/nodes/lib-http.d.ts.map +0 -1
- package/dist/nodes/lib-http.js +0 -267
- package/dist/nodes/lib-http.js.map +0 -1
- package/dist/nodes/lib-image-color-grading.d.ts +0 -3
- package/dist/nodes/lib-image-color-grading.d.ts.map +0 -1
- package/dist/nodes/lib-image-color-grading.js +0 -1386
- package/dist/nodes/lib-image-color-grading.js.map +0 -1
- package/dist/nodes/lib-image-draw.d.ts +0 -3
- package/dist/nodes/lib-image-draw.d.ts.map +0 -1
- package/dist/nodes/lib-image-draw.js +0 -470
- package/dist/nodes/lib-image-draw.js.map +0 -1
- package/dist/nodes/lib-image-enhance.d.ts +0 -3
- package/dist/nodes/lib-image-enhance.d.ts.map +0 -1
- package/dist/nodes/lib-image-enhance.js +0 -731
- package/dist/nodes/lib-image-enhance.js.map +0 -1
- package/dist/nodes/lib-image-filter.d.ts +0 -3
- package/dist/nodes/lib-image-filter.d.ts.map +0 -1
- package/dist/nodes/lib-image-filter.js +0 -684
- package/dist/nodes/lib-image-filter.js.map +0 -1
- package/dist/nodes/lib-image-utils.d.ts +0 -24
- package/dist/nodes/lib-image-utils.d.ts.map +0 -1
- package/dist/nodes/lib-image-utils.js +0 -150
- package/dist/nodes/lib-image-utils.js.map +0 -1
- package/dist/nodes/lib-mail.d.ts +0 -74
- package/dist/nodes/lib-mail.d.ts.map +0 -1
- package/dist/nodes/lib-mail.js +0 -452
- package/dist/nodes/lib-mail.js.map +0 -1
- package/dist/nodes/lib-markdown.d.ts +0 -70
- package/dist/nodes/lib-markdown.d.ts.map +0 -1
- package/dist/nodes/lib-markdown.js +0 -236
- package/dist/nodes/lib-markdown.js.map +0 -1
- package/dist/nodes/lib-nlp.d.ts +0 -98
- package/dist/nodes/lib-nlp.d.ts.map +0 -1
- package/dist/nodes/lib-nlp.js +0 -374
- package/dist/nodes/lib-nlp.js.map +0 -1
- package/dist/nodes/lib-notion.d.ts +0 -99
- package/dist/nodes/lib-notion.d.ts.map +0 -1
- package/dist/nodes/lib-notion.js +0 -601
- package/dist/nodes/lib-notion.js.map +0 -1
- package/dist/nodes/lib-ocr.d.ts +0 -31
- package/dist/nodes/lib-ocr.d.ts.map +0 -1
- package/dist/nodes/lib-ocr.js +0 -143
- package/dist/nodes/lib-ocr.js.map +0 -1
- package/dist/nodes/lib-os.d.ts +0 -312
- package/dist/nodes/lib-os.d.ts.map +0 -1
- package/dist/nodes/lib-os.js +0 -909
- package/dist/nodes/lib-os.js.map +0 -1
- package/dist/nodes/lib-pdf.d.ts +0 -142
- package/dist/nodes/lib-pdf.d.ts.map +0 -1
- package/dist/nodes/lib-pdf.js +0 -871
- package/dist/nodes/lib-pdf.js.map +0 -1
- package/dist/nodes/lib-pptx.d.ts +0 -29
- package/dist/nodes/lib-pptx.d.ts.map +0 -1
- package/dist/nodes/lib-pptx.js +0 -122
- package/dist/nodes/lib-pptx.js.map +0 -1
- package/dist/nodes/lib-rss.d.ts +0 -31
- package/dist/nodes/lib-rss.d.ts.map +0 -1
- package/dist/nodes/lib-rss.js +0 -163
- package/dist/nodes/lib-rss.js.map +0 -1
- package/dist/nodes/lib-s3.d.ts +0 -112
- package/dist/nodes/lib-s3.d.ts.map +0 -1
- package/dist/nodes/lib-s3.js +0 -521
- package/dist/nodes/lib-s3.js.map +0 -1
- package/dist/nodes/lib-secret.d.ts +0 -15
- package/dist/nodes/lib-secret.d.ts.map +0 -1
- package/dist/nodes/lib-secret.js +0 -42
- package/dist/nodes/lib-secret.js.map +0 -1
- package/dist/nodes/lib-sqlite.d.ts +0 -102
- package/dist/nodes/lib-sqlite.d.ts.map +0 -1
- package/dist/nodes/lib-sqlite.js +0 -532
- package/dist/nodes/lib-sqlite.js.map +0 -1
- package/dist/nodes/lib-supabase.d.ts +0 -86
- package/dist/nodes/lib-supabase.d.ts.map +0 -1
- package/dist/nodes/lib-supabase.js +0 -425
- package/dist/nodes/lib-supabase.js.map +0 -1
- package/dist/nodes/lib-svg.d.ts +0 -199
- package/dist/nodes/lib-svg.d.ts.map +0 -1
- package/dist/nodes/lib-svg.js +0 -1027
- package/dist/nodes/lib-svg.js.map +0 -1
- package/dist/nodes/lib-tensorflow.d.ts +0 -71
- package/dist/nodes/lib-tensorflow.d.ts.map +0 -1
- package/dist/nodes/lib-tensorflow.js +0 -351
- package/dist/nodes/lib-tensorflow.js.map +0 -1
- package/dist/nodes/lib-twilio.d.ts +0 -71
- package/dist/nodes/lib-twilio.d.ts.map +0 -1
- package/dist/nodes/lib-twilio.js +0 -469
- package/dist/nodes/lib-twilio.js.map +0 -1
- package/dist/nodes/lib-validate.d.ts +0 -84
- package/dist/nodes/lib-validate.d.ts.map +0 -1
- package/dist/nodes/lib-validate.js +0 -215
- package/dist/nodes/lib-validate.js.map +0 -1
- package/dist/nodes/lib-video-download.d.ts +0 -26
- package/dist/nodes/lib-video-download.d.ts.map +0 -1
- package/dist/nodes/lib-video-download.js +0 -299
- package/dist/nodes/lib-video-download.js.map +0 -1
- package/dist/nodes/messaging.d.ts +0 -88
- package/dist/nodes/messaging.d.ts.map +0 -1
- package/dist/nodes/messaging.js +0 -418
- package/dist/nodes/messaging.js.map +0 -1
- package/dist/nodes/mistral.d.ts +0 -83
- package/dist/nodes/mistral.d.ts.map +0 -1
- package/dist/nodes/mistral.js +0 -450
- package/dist/nodes/mistral.js.map +0 -1
- package/dist/nodes/model3d/analysis.d.ts +0 -13
- package/dist/nodes/model3d/analysis.d.ts.map +0 -1
- package/dist/nodes/model3d/analysis.js +0 -36
- package/dist/nodes/model3d/analysis.js.map +0 -1
- package/dist/nodes/model3d/base.d.ts +0 -11
- package/dist/nodes/model3d/base.d.ts.map +0 -1
- package/dist/nodes/model3d/base.js +0 -20
- package/dist/nodes/model3d/base.js.map +0 -1
- package/dist/nodes/model3d/boolean-ops.d.ts +0 -2
- package/dist/nodes/model3d/boolean-ops.d.ts.map +0 -1
- package/dist/nodes/model3d/boolean-ops.js +0 -172
- package/dist/nodes/model3d/boolean-ops.js.map +0 -1
- package/dist/nodes/model3d/cleanup.d.ts +0 -47
- package/dist/nodes/model3d/cleanup.d.ts.map +0 -1
- package/dist/nodes/model3d/cleanup.js +0 -136
- package/dist/nodes/model3d/cleanup.js.map +0 -1
- package/dist/nodes/model3d/convert.d.ts +0 -14
- package/dist/nodes/model3d/convert.d.ts.map +0 -1
- package/dist/nodes/model3d/convert.js +0 -64
- package/dist/nodes/model3d/convert.js.map +0 -1
- package/dist/nodes/model3d/defaults.d.ts +0 -34
- package/dist/nodes/model3d/defaults.d.ts.map +0 -1
- package/dist/nodes/model3d/defaults.js +0 -28
- package/dist/nodes/model3d/defaults.js.map +0 -1
- package/dist/nodes/model3d/document-ops.d.ts +0 -4
- package/dist/nodes/model3d/document-ops.d.ts.map +0 -1
- package/dist/nodes/model3d/document-ops.js +0 -40
- package/dist/nodes/model3d/document-ops.js.map +0 -1
- package/dist/nodes/model3d/generation.d.ts +0 -39
- package/dist/nodes/model3d/generation.d.ts.map +0 -1
- package/dist/nodes/model3d/generation.js +0 -179
- package/dist/nodes/model3d/generation.js.map +0 -1
- package/dist/nodes/model3d/glb.d.ts +0 -29
- package/dist/nodes/model3d/glb.d.ts.map +0 -1
- package/dist/nodes/model3d/glb.js +0 -255
- package/dist/nodes/model3d/glb.js.map +0 -1
- package/dist/nodes/model3d/index.d.ts +0 -20
- package/dist/nodes/model3d/index.d.ts.map +0 -1
- package/dist/nodes/model3d/index.js +0 -38
- package/dist/nodes/model3d/index.js.map +0 -1
- package/dist/nodes/model3d/io.d.ts +0 -38
- package/dist/nodes/model3d/io.d.ts.map +0 -1
- package/dist/nodes/model3d/io.js +0 -162
- package/dist/nodes/model3d/io.js.map +0 -1
- package/dist/nodes/model3d/mesh-ops.d.ts +0 -40
- package/dist/nodes/model3d/mesh-ops.d.ts.map +0 -1
- package/dist/nodes/model3d/mesh-ops.js +0 -775
- package/dist/nodes/model3d/mesh-ops.js.map +0 -1
- package/dist/nodes/model3d/nodes.d.ts +0 -2
- package/dist/nodes/model3d/nodes.d.ts.map +0 -1
- package/dist/nodes/model3d/nodes.js +0 -2
- package/dist/nodes/model3d/nodes.js.map +0 -1
- package/dist/nodes/model3d/ops.d.ts +0 -37
- package/dist/nodes/model3d/ops.d.ts.map +0 -1
- package/dist/nodes/model3d/ops.js +0 -137
- package/dist/nodes/model3d/ops.js.map +0 -1
- package/dist/nodes/model3d/transforms.d.ts +0 -49
- package/dist/nodes/model3d/transforms.d.ts.map +0 -1
- package/dist/nodes/model3d/transforms.js +0 -142
- package/dist/nodes/model3d/transforms.js.map +0 -1
- package/dist/nodes/model3d/types.d.ts +0 -81
- package/dist/nodes/model3d/types.d.ts.map +0 -1
- package/dist/nodes/model3d/types.js +0 -2
- package/dist/nodes/model3d/types.js.map +0 -1
- package/dist/nodes/model3d/utils.d.ts +0 -34
- package/dist/nodes/model3d/utils.d.ts.map +0 -1
- package/dist/nodes/model3d/utils.js +0 -185
- package/dist/nodes/model3d/utils.js.map +0 -1
- package/dist/nodes/model3d.d.ts +0 -2
- package/dist/nodes/model3d.d.ts.map +0 -1
- package/dist/nodes/model3d.js +0 -2
- package/dist/nodes/model3d.js.map +0 -1
- package/dist/nodes/openai.d.ts +0 -172
- package/dist/nodes/openai.d.ts.map +0 -1
- package/dist/nodes/openai.js +0 -1294
- package/dist/nodes/openai.js.map +0 -1
- package/dist/nodes/output.d.ts +0 -29
- package/dist/nodes/output.d.ts.map +0 -1
- package/dist/nodes/output.js +0 -87
- package/dist/nodes/output.js.map +0 -1
- package/dist/nodes/sandbox.d.ts +0 -63
- package/dist/nodes/sandbox.d.ts.map +0 -1
- package/dist/nodes/sandbox.js +0 -633
- package/dist/nodes/sandbox.js.map +0 -1
- package/dist/nodes/search.d.ts +0 -117
- package/dist/nodes/search.d.ts.map +0 -1
- package/dist/nodes/search.js +0 -607
- package/dist/nodes/search.js.map +0 -1
- package/dist/nodes/team.d.ts +0 -74
- package/dist/nodes/team.d.ts.map +0 -1
- package/dist/nodes/team.js +0 -309
- package/dist/nodes/team.js.map +0 -1
- package/dist/nodes/text-extra.d.ts +0 -633
- package/dist/nodes/text-extra.d.ts.map +0 -1
- package/dist/nodes/text-extra.js +0 -2226
- package/dist/nodes/text-extra.js.map +0 -1
- package/dist/nodes/tool-agents.d.ts +0 -334
- package/dist/nodes/tool-agents.d.ts.map +0 -1
- package/dist/nodes/tool-agents.js +0 -1861
- package/dist/nodes/tool-agents.js.map +0 -1
- package/dist/nodes/triggers.d.ts +0 -107
- package/dist/nodes/triggers.d.ts.map +0 -1
- package/dist/nodes/triggers.js +0 -706
- package/dist/nodes/triggers.js.map +0 -1
- package/dist/nodes/vector.d.ts +0 -168
- package/dist/nodes/vector.d.ts.map +0 -1
- package/dist/nodes/vector.js +0 -1121
- package/dist/nodes/vector.js.map +0 -1
- package/dist/nodes/video.d.ts +0 -387
- package/dist/nodes/video.d.ts.map +0 -1
- package/dist/nodes/video.js +0 -2383
- package/dist/nodes/video.js.map +0 -1
- package/dist/nodes/workflow.d.ts +0 -24
- package/dist/nodes/workflow.d.ts.map +0 -1
- package/dist/nodes/workflow.js +0 -146
- package/dist/nodes/workflow.js.map +0 -1
- package/dist/nodes/workspace.d.ts +0 -203
- package/dist/nodes/workspace.d.ts.map +0 -1
- package/dist/nodes/workspace.js +0 -795
- package/dist/nodes/workspace.js.map +0 -1
package/dist/nodes/agents.js
DELETED
|
@@ -1,2856 +0,0 @@
|
|
|
1
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
};
|
|
7
|
-
import { createLogger } from "@nodetool-ai/config";
|
|
8
|
-
import { BaseNode, prop } from "@nodetool-ai/node-sdk";
|
|
9
|
-
import { MultiModeAgent, Tool as AgentTool } from "@nodetool-ai/agents";
|
|
10
|
-
import { hydrateBuiltinAgentTool } from "./agent-tool-hydration.js";
|
|
11
|
-
const THREAD_STORE = new Map();
|
|
12
|
-
const log = createLogger("nodetool.base-nodes.agents");
|
|
13
|
-
const DEFAULT_SYSTEM_PROMPT = "You are a friendly assistant";
|
|
14
|
-
const EXTRACTOR_SYSTEM_PROMPT = [
|
|
15
|
-
"You are a precise structured data extractor.",
|
|
16
|
-
"Return exactly one JSON object and no additional prose.",
|
|
17
|
-
"Use only information present in the input."
|
|
18
|
-
].join(" ");
|
|
19
|
-
const CLASSIFIER_SYSTEM_PROMPT = [
|
|
20
|
-
"You are a precise classifier.",
|
|
21
|
-
"Choose exactly one category from the allowed list.",
|
|
22
|
-
'Return only JSON matching {"category":"<allowed-category>"} with no extra text.'
|
|
23
|
-
].join(" ");
|
|
24
|
-
const SUMMARIZER_SYSTEM_PROMPT = "You are an expert summarizer. Produce a concise, accurate summary.";
|
|
25
|
-
function asText(value) {
|
|
26
|
-
if (typeof value === "string")
|
|
27
|
-
return value;
|
|
28
|
-
if (typeof value === "number" || typeof value === "boolean")
|
|
29
|
-
return String(value);
|
|
30
|
-
if (!value)
|
|
31
|
-
return "";
|
|
32
|
-
if (Array.isArray(value))
|
|
33
|
-
return value.map(asText).join(" ");
|
|
34
|
-
if (typeof value === "object") {
|
|
35
|
-
const msg = value;
|
|
36
|
-
if (typeof msg.content === "string")
|
|
37
|
-
return msg.content;
|
|
38
|
-
if (Array.isArray(msg.content)) {
|
|
39
|
-
return msg.content
|
|
40
|
-
.map((part) => (part && part.type === "text" ? (part.text ?? "") : ""))
|
|
41
|
-
.join(" ")
|
|
42
|
-
.trim();
|
|
43
|
-
}
|
|
44
|
-
return JSON.stringify(value);
|
|
45
|
-
}
|
|
46
|
-
return "";
|
|
47
|
-
}
|
|
48
|
-
function summarize(text, maxSentences) {
|
|
49
|
-
const parts = text
|
|
50
|
-
.split(/(?<=[.!?])\s+/)
|
|
51
|
-
.map((s) => s.trim())
|
|
52
|
-
.filter((s) => s.length > 0);
|
|
53
|
-
if (parts.length === 0)
|
|
54
|
-
return "";
|
|
55
|
-
return parts.slice(0, Math.max(1, maxSentences)).join(" ");
|
|
56
|
-
}
|
|
57
|
-
function tokenize(text) {
|
|
58
|
-
return text
|
|
59
|
-
.toLowerCase()
|
|
60
|
-
.split(/[^a-z0-9]+/)
|
|
61
|
-
.map((t) => t.trim())
|
|
62
|
-
.filter((t) => t.length > 0);
|
|
63
|
-
}
|
|
64
|
-
function extractJson(text) {
|
|
65
|
-
try {
|
|
66
|
-
const parsed = JSON.parse(text);
|
|
67
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
68
|
-
? parsed
|
|
69
|
-
: null;
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
const start = text.indexOf("{");
|
|
73
|
-
const end = text.lastIndexOf("}");
|
|
74
|
-
if (start >= 0 && end > start) {
|
|
75
|
-
try {
|
|
76
|
-
const parsed = JSON.parse(text.slice(start, end + 1));
|
|
77
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
78
|
-
? parsed
|
|
79
|
-
: null;
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
function makeThreadId() {
|
|
89
|
-
return `thread_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
90
|
-
}
|
|
91
|
-
function getCategories(value) {
|
|
92
|
-
if (!Array.isArray(value))
|
|
93
|
-
return [];
|
|
94
|
-
return value.map((v) => String(v)).filter((v) => v.trim().length > 0);
|
|
95
|
-
}
|
|
96
|
-
function getModelConfig(props) {
|
|
97
|
-
const model = (props.model ?? {}) ?? {};
|
|
98
|
-
return {
|
|
99
|
-
providerId: typeof model.provider === "string" ? model.provider : "",
|
|
100
|
-
modelId: typeof model.id === "string" ? model.id : ""
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function hasProviderSupport(context, providerId, modelId) {
|
|
104
|
-
return (!!context &&
|
|
105
|
-
typeof context.getProvider === "function" &&
|
|
106
|
-
!!providerId &&
|
|
107
|
-
!!modelId);
|
|
108
|
-
}
|
|
109
|
-
async function generateProviderMessage(provider, args) {
|
|
110
|
-
const call = typeof provider.generateMessageTraced === "function"
|
|
111
|
-
? provider.generateMessageTraced.bind(provider)
|
|
112
|
-
: provider.generateMessage.bind(provider);
|
|
113
|
-
const result = await call(args);
|
|
114
|
-
return messageContentText(result.content);
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Call a provider with a result tool to get structured output.
|
|
118
|
-
* The model is forced to call the tool via toolChoice, and the
|
|
119
|
-
* parsed args are returned directly.
|
|
120
|
-
*/
|
|
121
|
-
async function generateStructured(provider, args) {
|
|
122
|
-
const call = typeof provider.generateMessageTraced === "function"
|
|
123
|
-
? provider.generateMessageTraced.bind(provider)
|
|
124
|
-
: provider.generateMessage.bind(provider);
|
|
125
|
-
const result = await call({
|
|
126
|
-
messages: args.messages,
|
|
127
|
-
model: args.model,
|
|
128
|
-
maxTokens: args.maxTokens,
|
|
129
|
-
tools: [
|
|
130
|
-
{
|
|
131
|
-
name: args.toolName,
|
|
132
|
-
description: args.toolDescription,
|
|
133
|
-
inputSchema: args.schema
|
|
134
|
-
}
|
|
135
|
-
],
|
|
136
|
-
toolChoice: args.toolName
|
|
137
|
-
});
|
|
138
|
-
const tc = result.toolCalls?.[0];
|
|
139
|
-
if (tc && tc.name === args.toolName) {
|
|
140
|
-
return tc.args;
|
|
141
|
-
}
|
|
142
|
-
// Fallback: try to parse JSON from text content
|
|
143
|
-
return extractJson(messageContentText(result.content));
|
|
144
|
-
}
|
|
145
|
-
function normalizeProviderStreamItem(item) {
|
|
146
|
-
if (!item ||
|
|
147
|
-
typeof item !== "object" ||
|
|
148
|
-
!("type" in item) ||
|
|
149
|
-
item.type !== "chunk") {
|
|
150
|
-
return item;
|
|
151
|
-
}
|
|
152
|
-
const chunk = item;
|
|
153
|
-
if (typeof chunk.content_type === "string" && chunk.content_type.length > 0) {
|
|
154
|
-
return chunk;
|
|
155
|
-
}
|
|
156
|
-
return {
|
|
157
|
-
...chunk,
|
|
158
|
-
content_type: "text"
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Render a tool-call streaming item as a Chunk so callers see tool dispatches
|
|
163
|
-
* inline with the assistant's text/thinking stream. The structured payload
|
|
164
|
-
* lives in `content_metadata`; `content` is a human-readable summary.
|
|
165
|
-
*/
|
|
166
|
-
function toolCallChunk(toolCall) {
|
|
167
|
-
const argsJson = (() => {
|
|
168
|
-
try {
|
|
169
|
-
return JSON.stringify(toolCall.args ?? {});
|
|
170
|
-
}
|
|
171
|
-
catch {
|
|
172
|
-
return "{}";
|
|
173
|
-
}
|
|
174
|
-
})();
|
|
175
|
-
return {
|
|
176
|
-
type: "chunk",
|
|
177
|
-
content: `${toolCall.name}(${argsJson})`,
|
|
178
|
-
content_type: "tool_call",
|
|
179
|
-
content_metadata: {
|
|
180
|
-
tool_call_id: toolCall.id,
|
|
181
|
-
tool_name: toolCall.name,
|
|
182
|
-
args: toolCall.args ?? {}
|
|
183
|
-
},
|
|
184
|
-
done: false
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
export async function* streamProviderMessages(provider, args) {
|
|
188
|
-
const request = {
|
|
189
|
-
...args,
|
|
190
|
-
messages: [...args.messages],
|
|
191
|
-
tools: args.tools ? [...args.tools] : undefined
|
|
192
|
-
};
|
|
193
|
-
if (typeof provider.generateMessagesTraced === "function") {
|
|
194
|
-
for await (const item of provider.generateMessagesTraced(request)) {
|
|
195
|
-
yield normalizeProviderStreamItem(item);
|
|
196
|
-
}
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
if (typeof provider.generateMessages === "function") {
|
|
200
|
-
for await (const item of provider.generateMessages(request)) {
|
|
201
|
-
yield normalizeProviderStreamItem(item);
|
|
202
|
-
}
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
const result = await provider.generateMessage(request);
|
|
206
|
-
const content = messageContentText(result.content);
|
|
207
|
-
if (content || (result.toolCalls?.length ?? 0) === 0) {
|
|
208
|
-
yield {
|
|
209
|
-
type: "chunk",
|
|
210
|
-
content,
|
|
211
|
-
content_type: "text",
|
|
212
|
-
done: true
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
for (const toolCall of result.toolCalls ?? []) {
|
|
216
|
-
yield toolCall;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Check whether a provider supports native agentic tool execution (MCP).
|
|
221
|
-
* When true, the provider handles tool calls internally via onToolCall callback.
|
|
222
|
-
*/
|
|
223
|
-
function isAgenticProvider(provider) {
|
|
224
|
-
return (provider.provider === "claude_agent");
|
|
225
|
-
}
|
|
226
|
-
function parseCategory(raw, categories) {
|
|
227
|
-
if (categories.length === 0)
|
|
228
|
-
return "Unknown";
|
|
229
|
-
const parsed = extractJson(raw);
|
|
230
|
-
const categoryValue = typeof parsed?.category === "string" ? parsed.category : "";
|
|
231
|
-
for (const category of categories) {
|
|
232
|
-
if (categoryValue.trim().toLowerCase() === category.trim().toLowerCase()) {
|
|
233
|
-
return category;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
const lowered = raw.toLowerCase();
|
|
237
|
-
for (const category of categories) {
|
|
238
|
-
if (category.toLowerCase() && lowered.includes(category.toLowerCase())) {
|
|
239
|
-
return category;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
for (const fallback of ["other", "unknown"]) {
|
|
243
|
-
for (const category of categories) {
|
|
244
|
-
if (category.trim().toLowerCase() === fallback)
|
|
245
|
-
return category;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return categories[0];
|
|
249
|
-
}
|
|
250
|
-
function messageContentText(content) {
|
|
251
|
-
if (typeof content === "string")
|
|
252
|
-
return content;
|
|
253
|
-
if (!Array.isArray(content))
|
|
254
|
-
return asText(content);
|
|
255
|
-
return content
|
|
256
|
-
.map((part) => {
|
|
257
|
-
if (!part || typeof part !== "object")
|
|
258
|
-
return asText(part);
|
|
259
|
-
if (part.type === "text") {
|
|
260
|
-
return String(part.text ?? "");
|
|
261
|
-
}
|
|
262
|
-
return "";
|
|
263
|
-
})
|
|
264
|
-
.join("")
|
|
265
|
-
.trim();
|
|
266
|
-
}
|
|
267
|
-
function normalizeRole(role) {
|
|
268
|
-
if (role === "system" ||
|
|
269
|
-
role === "user" ||
|
|
270
|
-
role === "assistant" ||
|
|
271
|
-
role === "tool") {
|
|
272
|
-
return role;
|
|
273
|
-
}
|
|
274
|
-
return null;
|
|
275
|
-
}
|
|
276
|
-
function normalizeBinaryRef(value) {
|
|
277
|
-
if (!value || typeof value !== "object")
|
|
278
|
-
return null;
|
|
279
|
-
const record = value;
|
|
280
|
-
const out = {};
|
|
281
|
-
if (typeof record.uri === "string" && record.uri)
|
|
282
|
-
out.uri = record.uri;
|
|
283
|
-
if (record.data instanceof Uint8Array || typeof record.data === "string")
|
|
284
|
-
out.data = record.data;
|
|
285
|
-
if (typeof record.mimeType === "string" && record.mimeType)
|
|
286
|
-
out.mimeType = record.mimeType;
|
|
287
|
-
if (typeof record.mime_type === "string" && record.mime_type)
|
|
288
|
-
out.mimeType = record.mime_type;
|
|
289
|
-
return out.uri || out.data ? out : null;
|
|
290
|
-
}
|
|
291
|
-
function normalizeMessageContent(value) {
|
|
292
|
-
if (value == null || typeof value === "string")
|
|
293
|
-
return value ?? null;
|
|
294
|
-
if (!Array.isArray(value))
|
|
295
|
-
return asText(value);
|
|
296
|
-
const parts = [];
|
|
297
|
-
for (const part of value) {
|
|
298
|
-
if (!part || typeof part !== "object") {
|
|
299
|
-
const text = asText(part);
|
|
300
|
-
if (text)
|
|
301
|
-
parts.push({ type: "text", text });
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
const record = part;
|
|
305
|
-
const kind = typeof record.type === "string" ? record.type : "";
|
|
306
|
-
if (kind === "text") {
|
|
307
|
-
parts.push({ type: "text", text: asText(record.text ?? "") });
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
if (kind === "image" || kind === "image_url") {
|
|
311
|
-
const image = normalizeBinaryRef(record.image ?? record.image_url ?? record.imageUrl);
|
|
312
|
-
if (image)
|
|
313
|
-
parts.push({ type: "image_url", image });
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
if (kind === "audio") {
|
|
317
|
-
const audio = normalizeBinaryRef(record.audio);
|
|
318
|
-
if (audio)
|
|
319
|
-
parts.push({ type: "audio", audio });
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
const text = asText(part);
|
|
323
|
-
if (text)
|
|
324
|
-
parts.push({ type: "text", text });
|
|
325
|
-
}
|
|
326
|
-
return parts;
|
|
327
|
-
}
|
|
328
|
-
function normalizeToolCalls(value) {
|
|
329
|
-
if (!Array.isArray(value))
|
|
330
|
-
return null;
|
|
331
|
-
const toolCalls = value
|
|
332
|
-
.filter((item) => !!item && typeof item === "object")
|
|
333
|
-
.map((item, index) => ({
|
|
334
|
-
id: typeof item.id === "string" && item.id ? item.id : `tool_${index + 1}`,
|
|
335
|
-
name: typeof item.name === "string" ? item.name : "",
|
|
336
|
-
args: item.args && typeof item.args === "object"
|
|
337
|
-
? item.args
|
|
338
|
-
: {}
|
|
339
|
-
}))
|
|
340
|
-
.filter((item) => item.name.length > 0);
|
|
341
|
-
return toolCalls.length > 0 ? toolCalls : null;
|
|
342
|
-
}
|
|
343
|
-
function normalizeMessage(value) {
|
|
344
|
-
if (!value || typeof value !== "object")
|
|
345
|
-
return null;
|
|
346
|
-
const record = value;
|
|
347
|
-
const role = normalizeRole(record.role);
|
|
348
|
-
if (!role)
|
|
349
|
-
return null;
|
|
350
|
-
return {
|
|
351
|
-
role,
|
|
352
|
-
content: normalizeMessageContent(record.content),
|
|
353
|
-
toolCalls: normalizeToolCalls(record.toolCalls ?? record.tool_calls),
|
|
354
|
-
toolCallId: typeof record.toolCallId === "string"
|
|
355
|
-
? record.toolCallId
|
|
356
|
-
: typeof record.tool_call_id === "string"
|
|
357
|
-
? record.tool_call_id
|
|
358
|
-
: null,
|
|
359
|
-
threadId: typeof record.threadId === "string"
|
|
360
|
-
? record.threadId
|
|
361
|
-
: typeof record.thread_id === "string"
|
|
362
|
-
? record.thread_id
|
|
363
|
-
: null
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
function threadMessages(threadId) {
|
|
367
|
-
const thread = THREAD_STORE.get(threadId);
|
|
368
|
-
if (!thread)
|
|
369
|
-
return [];
|
|
370
|
-
return thread.messages.map((message) => ({ ...message }));
|
|
371
|
-
}
|
|
372
|
-
function logThreadWarning(message, error, details) {
|
|
373
|
-
if (process.env["NODE_ENV"] === "test")
|
|
374
|
-
return;
|
|
375
|
-
console.warn(`[AgentNode] ${message}`, {
|
|
376
|
-
...details,
|
|
377
|
-
error: String(error)
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
function buildUserMessage(prompt, image, audio) {
|
|
381
|
-
const content = [{ type: "text", text: prompt }];
|
|
382
|
-
const imageRef = normalizeBinaryRef(image);
|
|
383
|
-
if (imageRef) {
|
|
384
|
-
content.push({ type: "image_url", image: imageRef });
|
|
385
|
-
}
|
|
386
|
-
const audioRef = normalizeBinaryRef(audio);
|
|
387
|
-
if (audioRef) {
|
|
388
|
-
content.push({ type: "audio", audio: audioRef });
|
|
389
|
-
}
|
|
390
|
-
return { role: "user", content };
|
|
391
|
-
}
|
|
392
|
-
async function loadThreadMessages(context, threadId) {
|
|
393
|
-
if (!threadId)
|
|
394
|
-
return [];
|
|
395
|
-
const threadedContext = context;
|
|
396
|
-
const getMessages = threadedContext?.get_messages?.bind(threadedContext) ??
|
|
397
|
-
threadedContext?.getThreadMessages?.bind(threadedContext);
|
|
398
|
-
if (getMessages) {
|
|
399
|
-
try {
|
|
400
|
-
const result = await getMessages(threadId, 1000, null, false);
|
|
401
|
-
const messages = (result.messages ?? [])
|
|
402
|
-
.map((item) => normalizeMessage(item))
|
|
403
|
-
.filter((message) => message !== null && message.role !== "system");
|
|
404
|
-
log.info("Agent thread history loaded from context", {
|
|
405
|
-
threadId,
|
|
406
|
-
messageCount: messages.length
|
|
407
|
-
});
|
|
408
|
-
return messages;
|
|
409
|
-
}
|
|
410
|
-
catch (error) {
|
|
411
|
-
logThreadWarning("Failed to load thread messages", error, { threadId });
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
const fallbackMessages = threadMessages(threadId).filter((message) => message.role !== "system");
|
|
415
|
-
log.info("Agent thread history loaded from fallback store", {
|
|
416
|
-
threadId,
|
|
417
|
-
messageCount: fallbackMessages.length
|
|
418
|
-
});
|
|
419
|
-
return fallbackMessages;
|
|
420
|
-
}
|
|
421
|
-
async function saveThreadMessage(context, threadId, message) {
|
|
422
|
-
if (!threadId)
|
|
423
|
-
return;
|
|
424
|
-
const threadedContext = context;
|
|
425
|
-
const createMessage = threadedContext?.create_message?.bind(threadedContext) ??
|
|
426
|
-
threadedContext?.createMessage?.bind(threadedContext);
|
|
427
|
-
if (createMessage) {
|
|
428
|
-
try {
|
|
429
|
-
await createMessage({
|
|
430
|
-
thread_id: threadId,
|
|
431
|
-
role: message.role,
|
|
432
|
-
content: message.content ?? null,
|
|
433
|
-
tool_calls: message.toolCalls ?? null,
|
|
434
|
-
tool_call_id: message.toolCallId ?? null
|
|
435
|
-
});
|
|
436
|
-
log.info("Agent thread message saved via context", {
|
|
437
|
-
threadId,
|
|
438
|
-
role: message.role,
|
|
439
|
-
hasToolCalls: (message.toolCalls?.length ?? 0) > 0,
|
|
440
|
-
textLength: messageContentText(message.content).length
|
|
441
|
-
});
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
catch (error) {
|
|
445
|
-
logThreadWarning("Failed to save thread message", error, {
|
|
446
|
-
threadId,
|
|
447
|
-
role: message.role
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
const thread = THREAD_STORE.get(threadId) ?? {
|
|
452
|
-
id: threadId,
|
|
453
|
-
title: "Agent Conversation",
|
|
454
|
-
messages: []
|
|
455
|
-
};
|
|
456
|
-
thread.messages.push({
|
|
457
|
-
...message,
|
|
458
|
-
threadId
|
|
459
|
-
});
|
|
460
|
-
THREAD_STORE.set(threadId, thread);
|
|
461
|
-
log.info("Agent thread message saved via fallback store", {
|
|
462
|
-
threadId,
|
|
463
|
-
role: message.role,
|
|
464
|
-
threadSize: thread.messages.length,
|
|
465
|
-
hasToolCalls: (message.toolCalls?.length ?? 0) > 0,
|
|
466
|
-
textLength: messageContentText(message.content).length
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
export function isChunkItem(item) {
|
|
470
|
-
return (!!item &&
|
|
471
|
-
typeof item === "object" &&
|
|
472
|
-
"type" in item &&
|
|
473
|
-
item.type === "chunk");
|
|
474
|
-
}
|
|
475
|
-
export function isToolCallItem(item) {
|
|
476
|
-
return (!!item &&
|
|
477
|
-
typeof item === "object" &&
|
|
478
|
-
"id" in item &&
|
|
479
|
-
"name" in item &&
|
|
480
|
-
!("type" in item));
|
|
481
|
-
}
|
|
482
|
-
function normalizeTools(value) {
|
|
483
|
-
if (!Array.isArray(value))
|
|
484
|
-
return [];
|
|
485
|
-
return value
|
|
486
|
-
.filter((tool) => !!tool &&
|
|
487
|
-
typeof tool === "object" &&
|
|
488
|
-
typeof tool.name === "string")
|
|
489
|
-
.map((tool) => hydrateBuiltinAgentTool(tool));
|
|
490
|
-
}
|
|
491
|
-
function uniqueToolName(baseName, existingNames) {
|
|
492
|
-
const used = new Set(existingNames);
|
|
493
|
-
if (!used.has(baseName))
|
|
494
|
-
return baseName;
|
|
495
|
-
let suffix = 2;
|
|
496
|
-
while (used.has(`${baseName}_${suffix}`))
|
|
497
|
-
suffix += 1;
|
|
498
|
-
return `${baseName}_${suffix}`;
|
|
499
|
-
}
|
|
500
|
-
export function toProviderTools(tools) {
|
|
501
|
-
return tools.map((tool) => typeof tool.toProviderTool === "function"
|
|
502
|
-
? tool.toProviderTool()
|
|
503
|
-
: {
|
|
504
|
-
name: tool.name,
|
|
505
|
-
description: tool.description,
|
|
506
|
-
inputSchema: tool.inputSchema
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
export function serializeToolResult(value) {
|
|
510
|
-
if (value == null)
|
|
511
|
-
return value;
|
|
512
|
-
if (Array.isArray(value))
|
|
513
|
-
return value.map(serializeToolResult);
|
|
514
|
-
if (typeof value !== "object")
|
|
515
|
-
return value;
|
|
516
|
-
if (value instanceof Uint8Array) {
|
|
517
|
-
return Buffer.from(value).toString("base64");
|
|
518
|
-
}
|
|
519
|
-
const record = value;
|
|
520
|
-
return Object.fromEntries(Object.entries(record).map(([key, item]) => [
|
|
521
|
-
key,
|
|
522
|
-
serializeToolResult(item)
|
|
523
|
-
]));
|
|
524
|
-
}
|
|
525
|
-
// ---------------------------------------------------------------------------
|
|
526
|
-
// Control tool support for Agent nodes with outgoing control edges
|
|
527
|
-
// ---------------------------------------------------------------------------
|
|
528
|
-
/**
|
|
529
|
-
* Marker symbol to identify control tools built from _control_context.
|
|
530
|
-
*/
|
|
531
|
-
const CONTROL_TOOL_MARKER = Symbol("controlTool");
|
|
532
|
-
function isControlTool(tool) {
|
|
533
|
-
return (CONTROL_TOOL_MARKER in tool &&
|
|
534
|
-
tool[CONTROL_TOOL_MARKER] === true);
|
|
535
|
-
}
|
|
536
|
-
/** Opening tag and closers models may emit (legacy `</think>` was a typo). */
|
|
537
|
-
const REDACTED_THINKING_OPEN = "<think>";
|
|
538
|
-
const REDACTED_THINKING_CLOSES = [
|
|
539
|
-
"</think>",
|
|
540
|
-
"</" + "think>"
|
|
541
|
-
];
|
|
542
|
-
function findEarliestThinkClose(buf) {
|
|
543
|
-
let best = null;
|
|
544
|
-
for (const c of REDACTED_THINKING_CLOSES) {
|
|
545
|
-
const i = buf.indexOf(c);
|
|
546
|
-
if (i !== -1 && (best === null || i < best.idx)) {
|
|
547
|
-
best = { idx: i, len: c.length };
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
return best;
|
|
551
|
-
}
|
|
552
|
-
/** Longest suffix of buf that is a proper prefix of one of the candidate strings (for streaming). */
|
|
553
|
-
function holdSuffixForPartialTag(buf, candidates) {
|
|
554
|
-
const maxCheck = Math.max(0, ...candidates.map((c) => Math.max(0, c.length - 1)));
|
|
555
|
-
const limit = Math.min(buf.length, maxCheck);
|
|
556
|
-
for (let len = limit; len >= 1; len--) {
|
|
557
|
-
const suf = buf.slice(-len);
|
|
558
|
-
if (candidates.some((c) => c.startsWith(suf)))
|
|
559
|
-
return len;
|
|
560
|
-
}
|
|
561
|
-
return 0;
|
|
562
|
-
}
|
|
563
|
-
function extractThinkTags(text) {
|
|
564
|
-
const parts = [];
|
|
565
|
-
const re = /<think>([\s\S]*?)<\/(?:redacted_thinking|think)>/g;
|
|
566
|
-
let cleaned = text.replace(re, (_, content) => {
|
|
567
|
-
parts.push(content.trim());
|
|
568
|
-
return "";
|
|
569
|
-
});
|
|
570
|
-
const orphan = cleaned.match(/<think>([\s\S]*)$/);
|
|
571
|
-
if (orphan && orphan.index !== undefined) {
|
|
572
|
-
parts.push(orphan[1].trim());
|
|
573
|
-
cleaned = cleaned.slice(0, orphan.index);
|
|
574
|
-
}
|
|
575
|
-
return { thinking: parts.filter((p) => p.length > 0).join("\n\n"), text: cleaned.trim() };
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* Splits streamed text so `<think>…` never appears on the text channel:
|
|
579
|
-
* reasoning goes to `thinking`, user-visible text to `text` chunks only.
|
|
580
|
-
*/
|
|
581
|
-
class RedactedThinkingStreamSplitter {
|
|
582
|
-
buf = "";
|
|
583
|
-
inThink = false;
|
|
584
|
-
*feed(incoming) {
|
|
585
|
-
if (!incoming)
|
|
586
|
-
return;
|
|
587
|
-
this.buf += incoming;
|
|
588
|
-
while (true) {
|
|
589
|
-
if (!this.inThink) {
|
|
590
|
-
const openIdx = this.buf.indexOf(REDACTED_THINKING_OPEN);
|
|
591
|
-
if (openIdx === -1) {
|
|
592
|
-
const keep = holdSuffixForPartialTag(this.buf, [REDACTED_THINKING_OPEN]);
|
|
593
|
-
if (this.buf.length > keep) {
|
|
594
|
-
const emitEnd = this.buf.length - keep;
|
|
595
|
-
const out = this.buf.slice(0, emitEnd);
|
|
596
|
-
this.buf = this.buf.slice(emitEnd);
|
|
597
|
-
if (out)
|
|
598
|
-
yield { kind: "text", content: out };
|
|
599
|
-
}
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
if (openIdx > 0) {
|
|
603
|
-
const out = this.buf.slice(0, openIdx);
|
|
604
|
-
if (out)
|
|
605
|
-
yield { kind: "text", content: out };
|
|
606
|
-
}
|
|
607
|
-
this.buf = this.buf.slice(openIdx + REDACTED_THINKING_OPEN.length);
|
|
608
|
-
this.inThink = true;
|
|
609
|
-
continue;
|
|
610
|
-
}
|
|
611
|
-
const close = findEarliestThinkClose(this.buf);
|
|
612
|
-
if (!close) {
|
|
613
|
-
const keep = holdSuffixForPartialTag(this.buf, REDACTED_THINKING_CLOSES);
|
|
614
|
-
if (this.buf.length > keep) {
|
|
615
|
-
const emitEnd = this.buf.length - keep;
|
|
616
|
-
const out = this.buf.slice(0, emitEnd);
|
|
617
|
-
this.buf = this.buf.slice(emitEnd);
|
|
618
|
-
if (out)
|
|
619
|
-
yield { kind: "thinking", content: out };
|
|
620
|
-
}
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
const thinkBody = this.buf.slice(0, close.idx);
|
|
624
|
-
this.buf = this.buf.slice(close.idx + close.len);
|
|
625
|
-
this.inThink = false;
|
|
626
|
-
if (thinkBody)
|
|
627
|
-
yield { kind: "thinking", content: thinkBody };
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
*flush() {
|
|
631
|
-
if (!this.buf && !this.inThink)
|
|
632
|
-
return;
|
|
633
|
-
if (this.inThink) {
|
|
634
|
-
if (this.buf)
|
|
635
|
-
yield { kind: "thinking", content: this.buf };
|
|
636
|
-
}
|
|
637
|
-
else if (this.buf) {
|
|
638
|
-
yield { kind: "text", content: this.buf };
|
|
639
|
-
}
|
|
640
|
-
this.buf = "";
|
|
641
|
-
this.inThink = false;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
function* yieldSplitThinkChunks(item, rawPiece, splitter) {
|
|
645
|
-
for (const part of splitter.feed(rawPiece)) {
|
|
646
|
-
if (part.kind === "text" && part.content.length > 0) {
|
|
647
|
-
yield {
|
|
648
|
-
chunk: { ...item, content: part.content },
|
|
649
|
-
thinking: null,
|
|
650
|
-
text: null,
|
|
651
|
-
audio: null
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
else if (part.kind === "thinking" && part.content.length > 0) {
|
|
655
|
-
yield {
|
|
656
|
-
chunk: null,
|
|
657
|
-
thinking: {
|
|
658
|
-
type: "chunk",
|
|
659
|
-
content: part.content,
|
|
660
|
-
thinking: true
|
|
661
|
-
},
|
|
662
|
-
text: null,
|
|
663
|
-
audio: null
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
/**
|
|
669
|
-
* Sanitize a node title to a valid tool name (snake_case, max 64 chars).
|
|
670
|
-
*/
|
|
671
|
-
function sanitizeControlToolName(name) {
|
|
672
|
-
if (!name)
|
|
673
|
-
return "control_node";
|
|
674
|
-
let s = name
|
|
675
|
-
.replace(/[^a-zA-Z0-9]/g, "_")
|
|
676
|
-
.replace(/([a-z])([A-Z])/g, "$1_$2")
|
|
677
|
-
.toLowerCase()
|
|
678
|
-
.replace(/_+/g, "_")
|
|
679
|
-
.replace(/^_+|_+$/g, "");
|
|
680
|
-
if (!s)
|
|
681
|
-
return "control_node";
|
|
682
|
-
if (s.length > 64)
|
|
683
|
-
s = s.slice(0, 64);
|
|
684
|
-
return s;
|
|
685
|
-
}
|
|
686
|
-
/**
|
|
687
|
-
* Build ControlToolLike instances from `_control_context` input.
|
|
688
|
-
* These tools allow the agent's LLM to call controlled nodes via tool-calling.
|
|
689
|
-
*/
|
|
690
|
-
function buildControlTools(controlContext) {
|
|
691
|
-
if (!controlContext || typeof controlContext !== "object")
|
|
692
|
-
return [];
|
|
693
|
-
const tools = [];
|
|
694
|
-
for (const [targetId, info] of Object.entries(controlContext)) {
|
|
695
|
-
if (!info || typeof info !== "object")
|
|
696
|
-
continue;
|
|
697
|
-
const nodeInfo = info;
|
|
698
|
-
const nodeTitle = String(nodeInfo.node_title ?? nodeInfo.node_type ?? targetId);
|
|
699
|
-
const toolName = sanitizeControlToolName(nodeTitle);
|
|
700
|
-
// Build input schema from control_actions.run.properties
|
|
701
|
-
const actions = (nodeInfo.control_actions ?? {});
|
|
702
|
-
const runAction = (actions.run ?? {});
|
|
703
|
-
const rawProperties = (runAction.properties ?? {});
|
|
704
|
-
const properties = {};
|
|
705
|
-
for (const [key, schema] of Object.entries(rawProperties)) {
|
|
706
|
-
if (typeof schema === "object" && schema !== null) {
|
|
707
|
-
properties[key] = { ...schema };
|
|
708
|
-
}
|
|
709
|
-
else {
|
|
710
|
-
properties[key] = { type: "string", description: String(schema) };
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
const inputSchema = {
|
|
714
|
-
type: "object",
|
|
715
|
-
properties,
|
|
716
|
-
required: []
|
|
717
|
-
};
|
|
718
|
-
let description = `Control ${nodeTitle}: trigger execution with optional property overrides`;
|
|
719
|
-
const propNames = Object.keys(properties);
|
|
720
|
-
if (propNames.length > 0) {
|
|
721
|
-
description += `. Available properties: ${propNames.join(", ")}`;
|
|
722
|
-
}
|
|
723
|
-
tools.push({
|
|
724
|
-
[CONTROL_TOOL_MARKER]: true,
|
|
725
|
-
targetNodeId: targetId,
|
|
726
|
-
name: toolName,
|
|
727
|
-
description,
|
|
728
|
-
inputSchema,
|
|
729
|
-
// Stub process — actual execution goes through sendControlEvent
|
|
730
|
-
async process(_ctx, _params) {
|
|
731
|
-
return { status: "dispatched", target: targetId };
|
|
732
|
-
},
|
|
733
|
-
toProviderTool() {
|
|
734
|
-
return { name: toolName, description, inputSchema };
|
|
735
|
-
}
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
return tools;
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Adapter that wraps a ToolLike (from base-nodes) as an AgentTool (from @nodetool-ai/agents).
|
|
742
|
-
* This bridges the tool systems so MultiModeAgent can use tools defined in the node graph.
|
|
743
|
-
*/
|
|
744
|
-
class ToolLikeAdapter extends AgentTool {
|
|
745
|
-
name;
|
|
746
|
-
description;
|
|
747
|
-
inputSchema;
|
|
748
|
-
_process;
|
|
749
|
-
constructor(toolLike) {
|
|
750
|
-
super();
|
|
751
|
-
this.name = toolLike.name;
|
|
752
|
-
this.description = toolLike.description ?? "";
|
|
753
|
-
this.inputSchema = toolLike.inputSchema ?? {
|
|
754
|
-
type: "object",
|
|
755
|
-
properties: {}
|
|
756
|
-
};
|
|
757
|
-
this._process =
|
|
758
|
-
toolLike.process ??
|
|
759
|
-
(async () => ({ error: "Tool has no process implementation" }));
|
|
760
|
-
}
|
|
761
|
-
async process(context, params) {
|
|
762
|
-
return this._process(context, params);
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
export async function runAgentLoop(options) {
|
|
766
|
-
const { context, providerId, modelId, systemPrompt, prompt, tools, contentParts, maxTokens = 4096, maxIterations = 10 } = options;
|
|
767
|
-
if (!context || typeof context.getProvider !== "function") {
|
|
768
|
-
throw new Error("Processing context with provider access is required");
|
|
769
|
-
}
|
|
770
|
-
const userContent = [{ type: "text", text: prompt }];
|
|
771
|
-
if (contentParts) {
|
|
772
|
-
userContent.push(...contentParts);
|
|
773
|
-
}
|
|
774
|
-
const messages = [
|
|
775
|
-
{ role: "system", content: systemPrompt },
|
|
776
|
-
{ role: "user", content: userContent }
|
|
777
|
-
];
|
|
778
|
-
const providerTools = tools.length > 0 ? toProviderTools(tools) : undefined;
|
|
779
|
-
const provider = await context.getProvider(providerId);
|
|
780
|
-
let lastAssistantText = "";
|
|
781
|
-
// --- Agentic provider fast-path ---
|
|
782
|
-
if (isAgenticProvider(provider) && tools.length > 0) {
|
|
783
|
-
const onToolCall = async (name, args) => {
|
|
784
|
-
const tool = tools.find((t) => t.name === name);
|
|
785
|
-
if (!tool || typeof tool.process !== "function") {
|
|
786
|
-
return JSON.stringify({ error: `Unknown tool: ${name}` });
|
|
787
|
-
}
|
|
788
|
-
try {
|
|
789
|
-
const result = await tool.process(context, args);
|
|
790
|
-
return typeof result === "string"
|
|
791
|
-
? result
|
|
792
|
-
: JSON.stringify(serializeToolResult(result));
|
|
793
|
-
}
|
|
794
|
-
catch (e) {
|
|
795
|
-
return JSON.stringify({ error: String(e) });
|
|
796
|
-
}
|
|
797
|
-
};
|
|
798
|
-
let assistantText = "";
|
|
799
|
-
for await (const item of streamProviderMessages(provider, {
|
|
800
|
-
messages,
|
|
801
|
-
model: modelId,
|
|
802
|
-
tools: providerTools,
|
|
803
|
-
maxTokens,
|
|
804
|
-
threadId: options.threadId,
|
|
805
|
-
onToolCall
|
|
806
|
-
})) {
|
|
807
|
-
if (isChunkItem(item) && !item.thinking) {
|
|
808
|
-
assistantText += item.content ?? "";
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
if (assistantText) {
|
|
812
|
-
lastAssistantText = assistantText;
|
|
813
|
-
messages.push({
|
|
814
|
-
role: "assistant",
|
|
815
|
-
content: [{ type: "text", text: assistantText }]
|
|
816
|
-
});
|
|
817
|
-
}
|
|
818
|
-
return { text: lastAssistantText, messages };
|
|
819
|
-
}
|
|
820
|
-
// --- Standard multi-iteration loop ---
|
|
821
|
-
let iteration = 0;
|
|
822
|
-
let shouldContinue = true;
|
|
823
|
-
while (shouldContinue && iteration < maxIterations) {
|
|
824
|
-
shouldContinue = false;
|
|
825
|
-
iteration++;
|
|
826
|
-
const assistantToolCalls = [];
|
|
827
|
-
let assistantText = "";
|
|
828
|
-
for await (const item of streamProviderMessages(provider, {
|
|
829
|
-
messages,
|
|
830
|
-
model: modelId,
|
|
831
|
-
tools: providerTools,
|
|
832
|
-
maxTokens,
|
|
833
|
-
threadId: options.threadId
|
|
834
|
-
})) {
|
|
835
|
-
if (isChunkItem(item)) {
|
|
836
|
-
if (!item.thinking) {
|
|
837
|
-
assistantText += item.content ?? "";
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
if (isToolCallItem(item)) {
|
|
841
|
-
assistantToolCalls.push(item);
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
if (assistantText) {
|
|
845
|
-
lastAssistantText = assistantText;
|
|
846
|
-
}
|
|
847
|
-
if (assistantText || assistantToolCalls.length > 0) {
|
|
848
|
-
const assistantMsg = {
|
|
849
|
-
role: "assistant",
|
|
850
|
-
content: [{ type: "text", text: assistantText }],
|
|
851
|
-
toolCalls: assistantToolCalls.length > 0 ? assistantToolCalls : null
|
|
852
|
-
};
|
|
853
|
-
// Propagate raw Gemini parts for thought signature replay
|
|
854
|
-
const rawParts = assistantToolCalls.find((tc) => tc._rawGeminiParts)?._rawGeminiParts;
|
|
855
|
-
if (rawParts) {
|
|
856
|
-
assistantMsg._rawGeminiParts = rawParts;
|
|
857
|
-
}
|
|
858
|
-
messages.push(assistantMsg);
|
|
859
|
-
}
|
|
860
|
-
for (const toolCall of assistantToolCalls) {
|
|
861
|
-
const tool = tools.find((t) => t.name === toolCall.name);
|
|
862
|
-
if (!tool || typeof tool.process !== "function") {
|
|
863
|
-
messages.push({
|
|
864
|
-
role: "tool",
|
|
865
|
-
toolCallId: toolCall.id,
|
|
866
|
-
content: JSON.stringify({ error: `Unknown tool: ${toolCall.name}` })
|
|
867
|
-
});
|
|
868
|
-
shouldContinue = true;
|
|
869
|
-
continue;
|
|
870
|
-
}
|
|
871
|
-
const result = await tool.process(context, toolCall.args);
|
|
872
|
-
messages.push({
|
|
873
|
-
role: "tool",
|
|
874
|
-
toolCallId: toolCall.id,
|
|
875
|
-
content: JSON.stringify(serializeToolResult(result))
|
|
876
|
-
});
|
|
877
|
-
shouldContinue = true;
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
if (iteration >= maxIterations) {
|
|
881
|
-
log.warn("runAgentLoop reached max iterations", {
|
|
882
|
-
maxIterations,
|
|
883
|
-
providerId,
|
|
884
|
-
modelId
|
|
885
|
-
});
|
|
886
|
-
}
|
|
887
|
-
return { text: lastAssistantText, messages };
|
|
888
|
-
}
|
|
889
|
-
function getStructuredOutputSchema(node) {
|
|
890
|
-
const outputs = node._dynamic_outputs;
|
|
891
|
-
if (!outputs || typeof outputs !== "object" || Array.isArray(outputs))
|
|
892
|
-
return null;
|
|
893
|
-
const properties = {};
|
|
894
|
-
const required = [];
|
|
895
|
-
for (const [name, spec] of Object.entries(outputs)) {
|
|
896
|
-
required.push(name);
|
|
897
|
-
const value = spec && typeof spec === "object" ? spec : {};
|
|
898
|
-
const declared = typeof value.type === "string" ? value.type.toLowerCase() : "str";
|
|
899
|
-
let type = "string";
|
|
900
|
-
if (["int", "integer"].includes(declared))
|
|
901
|
-
type = "integer";
|
|
902
|
-
else if (["float", "number"].includes(declared))
|
|
903
|
-
type = "number";
|
|
904
|
-
else if (["bool", "boolean"].includes(declared))
|
|
905
|
-
type = "boolean";
|
|
906
|
-
else if (["list", "array"].includes(declared))
|
|
907
|
-
type = "array";
|
|
908
|
-
else if (["dict", "object"].includes(declared))
|
|
909
|
-
type = "object";
|
|
910
|
-
properties[name] = { type };
|
|
911
|
-
}
|
|
912
|
-
if (required.length === 0)
|
|
913
|
-
return null;
|
|
914
|
-
return {
|
|
915
|
-
type: "object",
|
|
916
|
-
additionalProperties: false,
|
|
917
|
-
required,
|
|
918
|
-
properties
|
|
919
|
-
};
|
|
920
|
-
}
|
|
921
|
-
function hasContentType(message, type) {
|
|
922
|
-
return Array.isArray(message?.content)
|
|
923
|
-
? message.content.some((part) => part.type === type)
|
|
924
|
-
: false;
|
|
925
|
-
}
|
|
926
|
-
export class SummarizerNode extends BaseNode {
|
|
927
|
-
static nodeType = "nodetool.agents.Summarizer";
|
|
928
|
-
static title = "Summarizer";
|
|
929
|
-
static description = "Generate concise summaries of text content using LLM providers with streaming output.\n text, summarization, nlp, content, streaming\n\n Specialized for creating high-quality summaries with real-time streaming:\n - Condensing long documents into key points\n - Creating executive summaries with live output\n - Extracting main ideas from text as they're generated\n - Maintaining factual accuracy while reducing length";
|
|
930
|
-
static metadataOutputTypes = {
|
|
931
|
-
text: "str",
|
|
932
|
-
chunk: "chunk"
|
|
933
|
-
};
|
|
934
|
-
static basicFields = ["text", "model", "image", "audio"];
|
|
935
|
-
static recommendedModels = [
|
|
936
|
-
{
|
|
937
|
-
id: "phi3.5:latest",
|
|
938
|
-
type: "llama_model",
|
|
939
|
-
name: "Phi3.5",
|
|
940
|
-
repo_id: "phi3.5:latest",
|
|
941
|
-
description: "Lightweight 3.8B model tuned for crisp instruction following and compact summaries on modest hardware.",
|
|
942
|
-
size_on_disk: 2362232012
|
|
943
|
-
},
|
|
944
|
-
{
|
|
945
|
-
id: "mistral-small:latest",
|
|
946
|
-
type: "llama_model",
|
|
947
|
-
name: "Mistral Small",
|
|
948
|
-
repo_id: "mistral-small:latest",
|
|
949
|
-
description: "Efficient mixture-of-experts model that delivers reliable abstractive summaries with low latency.",
|
|
950
|
-
size_on_disk: 7730941132
|
|
951
|
-
},
|
|
952
|
-
{
|
|
953
|
-
id: "llama3.2:3b",
|
|
954
|
-
type: "llama_model",
|
|
955
|
-
name: "Llama 3.2 - 3B",
|
|
956
|
-
repo_id: "llama3.2:3b",
|
|
957
|
-
description: "Compact Llama variant that balances coverage and brevity for everyday summarization workloads.",
|
|
958
|
-
size_on_disk: 2040109465
|
|
959
|
-
},
|
|
960
|
-
{
|
|
961
|
-
id: "gemma3:4b",
|
|
962
|
-
type: "llama_model",
|
|
963
|
-
name: "Gemma3 - 4B",
|
|
964
|
-
repo_id: "gemma3:4b",
|
|
965
|
-
description: "Google's 4B multimodal model performs strong factual summaries while staying resource friendly.",
|
|
966
|
-
size_on_disk: 2791728742
|
|
967
|
-
},
|
|
968
|
-
{
|
|
969
|
-
id: "granite3.1-moe:3b",
|
|
970
|
-
type: "llama_model",
|
|
971
|
-
name: "Granite 3.1 MOE - 3B",
|
|
972
|
-
repo_id: "granite3.1-moe:3b",
|
|
973
|
-
description: "IBM Granite MoE delivers focused meeting notes and bullet summaries with minimal VRAM needs.",
|
|
974
|
-
size_on_disk: 1717986918
|
|
975
|
-
},
|
|
976
|
-
{
|
|
977
|
-
id: "qwen3:4b",
|
|
978
|
-
type: "llama_model",
|
|
979
|
-
name: "Qwen3 - 4B",
|
|
980
|
-
repo_id: "qwen3:4b",
|
|
981
|
-
description: "Qwen3 4B offers multilingual summarization with tight, well-structured outputs.",
|
|
982
|
-
size_on_disk: 2684354560
|
|
983
|
-
},
|
|
984
|
-
{
|
|
985
|
-
id: "ggml-org/gemma-3-4b-it-GGUF:gemma-3-4b-it-Q4_K_M.gguf",
|
|
986
|
-
type: "llama_cpp_model",
|
|
987
|
-
name: "Gemma 3 4B IT (GGUF)",
|
|
988
|
-
repo_id: "ggml-org/gemma-3-4b-it-GGUF",
|
|
989
|
-
path: "gemma-3-4b-it-Q4_K_M.gguf",
|
|
990
|
-
description: "Efficient Gemma 3 for summarization via llama.cpp.",
|
|
991
|
-
size_on_disk: 3113851289,
|
|
992
|
-
pipeline_tag: "image-text-to-text",
|
|
993
|
-
tags: [
|
|
994
|
-
"gguf",
|
|
995
|
-
"image-text-to-text",
|
|
996
|
-
"arxiv:1905.07830",
|
|
997
|
-
"arxiv:1905.10044",
|
|
998
|
-
"arxiv:1911.11641",
|
|
999
|
-
"arxiv:1904.09728",
|
|
1000
|
-
"arxiv:1705.03551",
|
|
1001
|
-
"arxiv:1911.01547",
|
|
1002
|
-
"arxiv:1907.10641",
|
|
1003
|
-
"arxiv:1903.00161",
|
|
1004
|
-
"arxiv:2009.03300",
|
|
1005
|
-
"arxiv:2304.06364",
|
|
1006
|
-
"arxiv:2103.03874",
|
|
1007
|
-
"arxiv:2110.14168",
|
|
1008
|
-
"arxiv:2311.12022",
|
|
1009
|
-
"arxiv:2108.07732",
|
|
1010
|
-
"arxiv:2107.03374",
|
|
1011
|
-
"arxiv:2210.03057",
|
|
1012
|
-
"arxiv:2106.03193",
|
|
1013
|
-
"arxiv:1910.11856",
|
|
1014
|
-
"arxiv:2502.12404",
|
|
1015
|
-
"arxiv:2502.21228",
|
|
1016
|
-
"arxiv:2404.16816",
|
|
1017
|
-
"arxiv:2104.12756",
|
|
1018
|
-
"arxiv:2311.16502",
|
|
1019
|
-
"arxiv:2203.10244",
|
|
1020
|
-
"arxiv:2404.12390",
|
|
1021
|
-
"arxiv:1810.12440",
|
|
1022
|
-
"arxiv:1908.02660",
|
|
1023
|
-
"arxiv:2312.11805",
|
|
1024
|
-
"base_model:google/gemma-3-4b-it",
|
|
1025
|
-
"base_model:quantized:google/gemma-3-4b-it",
|
|
1026
|
-
"license:gemma",
|
|
1027
|
-
"endpoints_compatible",
|
|
1028
|
-
"region:us",
|
|
1029
|
-
"conversational"
|
|
1030
|
-
],
|
|
1031
|
-
has_model_index: false,
|
|
1032
|
-
downloads: 25779,
|
|
1033
|
-
likes: 48
|
|
1034
|
-
}
|
|
1035
|
-
];
|
|
1036
|
-
static isStreamingOutput = true;
|
|
1037
|
-
async process(context) {
|
|
1038
|
-
const text = asText(this.text ?? this.text ?? "");
|
|
1039
|
-
const maxSentences = Number(this.max_sentences ?? 3);
|
|
1040
|
-
const { providerId, modelId } = getModelConfig(this.serialize());
|
|
1041
|
-
if (hasProviderSupport(context, providerId, modelId)) {
|
|
1042
|
-
const provider = await context.getProvider(providerId);
|
|
1043
|
-
const result = await generateProviderMessage(provider, {
|
|
1044
|
-
model: modelId,
|
|
1045
|
-
maxTokens: Number.isFinite(maxSentences)
|
|
1046
|
-
? Math.max(64, maxSentences * 128)
|
|
1047
|
-
: 384,
|
|
1048
|
-
messages: [
|
|
1049
|
-
{ role: "system", content: SUMMARIZER_SYSTEM_PROMPT },
|
|
1050
|
-
{
|
|
1051
|
-
role: "user",
|
|
1052
|
-
content: `Summarize the following text in about ${Math.max(1, maxSentences)} sentence(s):\n\n${text}`
|
|
1053
|
-
}
|
|
1054
|
-
]
|
|
1055
|
-
});
|
|
1056
|
-
return { text: result, output: result };
|
|
1057
|
-
}
|
|
1058
|
-
const result = summarize(text, Number.isFinite(maxSentences) ? maxSentences : 3);
|
|
1059
|
-
return { text: result, output: result };
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
__decorate([
|
|
1063
|
-
prop({
|
|
1064
|
-
type: "str",
|
|
1065
|
-
default: "\n You are an expert summarizer. Your task is to create clear, accurate, and concise summaries using Markdown for structuring.\n Follow these guidelines:\n 1. Identify and include only the most important information.\n 2. Maintain factual accuracy - do not add or modify information.\n 3. Use clear, direct language.\n 4. Aim for approximately {self.max_tokens} tokens.\n ",
|
|
1066
|
-
title: "System Prompt",
|
|
1067
|
-
description: "The system prompt for the summarizer"
|
|
1068
|
-
})
|
|
1069
|
-
], SummarizerNode.prototype, "system_prompt", void 0);
|
|
1070
|
-
__decorate([
|
|
1071
|
-
prop({
|
|
1072
|
-
type: "language_model",
|
|
1073
|
-
default: {
|
|
1074
|
-
type: "language_model",
|
|
1075
|
-
provider: "empty",
|
|
1076
|
-
id: "",
|
|
1077
|
-
name: "",
|
|
1078
|
-
path: null,
|
|
1079
|
-
supported_tasks: []
|
|
1080
|
-
},
|
|
1081
|
-
title: "Model",
|
|
1082
|
-
description: "Model to use for summarization"
|
|
1083
|
-
})
|
|
1084
|
-
], SummarizerNode.prototype, "model", void 0);
|
|
1085
|
-
__decorate([
|
|
1086
|
-
prop({
|
|
1087
|
-
type: "str",
|
|
1088
|
-
default: "",
|
|
1089
|
-
title: "Text",
|
|
1090
|
-
description: "The text to summarize"
|
|
1091
|
-
})
|
|
1092
|
-
], SummarizerNode.prototype, "text", void 0);
|
|
1093
|
-
__decorate([
|
|
1094
|
-
prop({
|
|
1095
|
-
type: "image",
|
|
1096
|
-
default: {
|
|
1097
|
-
type: "image",
|
|
1098
|
-
uri: "",
|
|
1099
|
-
asset_id: null,
|
|
1100
|
-
data: null,
|
|
1101
|
-
metadata: null
|
|
1102
|
-
},
|
|
1103
|
-
title: "Image",
|
|
1104
|
-
description: "Optional image to condition the summary"
|
|
1105
|
-
})
|
|
1106
|
-
], SummarizerNode.prototype, "image", void 0);
|
|
1107
|
-
__decorate([
|
|
1108
|
-
prop({
|
|
1109
|
-
type: "audio",
|
|
1110
|
-
default: {
|
|
1111
|
-
type: "audio",
|
|
1112
|
-
uri: "",
|
|
1113
|
-
asset_id: null,
|
|
1114
|
-
data: null,
|
|
1115
|
-
metadata: null
|
|
1116
|
-
},
|
|
1117
|
-
title: "Audio",
|
|
1118
|
-
description: "Optional audio to condition the summary"
|
|
1119
|
-
})
|
|
1120
|
-
], SummarizerNode.prototype, "audio", void 0);
|
|
1121
|
-
export class CreateThreadNode extends BaseNode {
|
|
1122
|
-
static nodeType = "nodetool.agents.CreateThread";
|
|
1123
|
-
static title = "Create Thread";
|
|
1124
|
-
static description = "Create a new conversation thread and return its ID.\n threads, chat, conversation, context\n\n Use this to seed a thread_id that downstream Agent nodes can reuse for\n persistent history across the graph or multiple runs.";
|
|
1125
|
-
static metadataOutputTypes = {
|
|
1126
|
-
thread_id: "str"
|
|
1127
|
-
};
|
|
1128
|
-
async process() {
|
|
1129
|
-
const requested = String(this.thread_id ?? this.thread_id ?? "").trim();
|
|
1130
|
-
if (requested) {
|
|
1131
|
-
if (!THREAD_STORE.has(requested)) {
|
|
1132
|
-
THREAD_STORE.set(requested, {
|
|
1133
|
-
id: requested,
|
|
1134
|
-
title: String(this.title ?? this.title ?? "Agent Conversation"),
|
|
1135
|
-
messages: []
|
|
1136
|
-
});
|
|
1137
|
-
}
|
|
1138
|
-
return { thread_id: requested };
|
|
1139
|
-
}
|
|
1140
|
-
const id = makeThreadId();
|
|
1141
|
-
THREAD_STORE.set(id, {
|
|
1142
|
-
id,
|
|
1143
|
-
title: String(this.title ?? this.title ?? "Agent Conversation"),
|
|
1144
|
-
messages: []
|
|
1145
|
-
});
|
|
1146
|
-
return { thread_id: id };
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
__decorate([
|
|
1150
|
-
prop({
|
|
1151
|
-
type: "str",
|
|
1152
|
-
default: "Agent Conversation",
|
|
1153
|
-
title: "Title",
|
|
1154
|
-
description: "Optional title for the new thread"
|
|
1155
|
-
})
|
|
1156
|
-
], CreateThreadNode.prototype, "title", void 0);
|
|
1157
|
-
__decorate([
|
|
1158
|
-
prop({
|
|
1159
|
-
type: "str",
|
|
1160
|
-
default: "",
|
|
1161
|
-
title: "Thread Id",
|
|
1162
|
-
description: "Optional custom thread ID. If provided and owned by the user, it will be reused; otherwise a new thread is created."
|
|
1163
|
-
})
|
|
1164
|
-
], CreateThreadNode.prototype, "thread_id", void 0);
|
|
1165
|
-
export class ExtractorNode extends BaseNode {
|
|
1166
|
-
static nodeType = "nodetool.agents.Extractor";
|
|
1167
|
-
static title = "Extractor";
|
|
1168
|
-
static description = "Extract structured data from text content using LLM providers.\n data-extraction, structured-data, nlp, parsing\n\n Specialized for extracting structured information:\n - Converting unstructured text into structured data\n - Identifying and extracting specific fields from documents\n - Parsing text according to predefined schemas\n - Creating structured records from natural language content";
|
|
1169
|
-
static basicFields = ["text", "model", "image", "audio"];
|
|
1170
|
-
static supportsDynamicOutputs = true;
|
|
1171
|
-
static recommendedModels = [
|
|
1172
|
-
{
|
|
1173
|
-
id: "phi3.5:latest",
|
|
1174
|
-
type: "llama_model",
|
|
1175
|
-
name: "Phi3.5",
|
|
1176
|
-
repo_id: "phi3.5:latest",
|
|
1177
|
-
description: "Small Phi variant excels at JSON-style outputs and faithful field extraction on laptops.",
|
|
1178
|
-
size_on_disk: 2362232012
|
|
1179
|
-
},
|
|
1180
|
-
{
|
|
1181
|
-
id: "mistral-small:latest",
|
|
1182
|
-
type: "llama_model",
|
|
1183
|
-
name: "Mistral Small",
|
|
1184
|
-
repo_id: "mistral-small:latest",
|
|
1185
|
-
description: "MoE architecture keeps structured extraction consistent while staying resource efficient.",
|
|
1186
|
-
size_on_disk: 7730941132
|
|
1187
|
-
},
|
|
1188
|
-
{
|
|
1189
|
-
id: "granite3.1-moe:3b",
|
|
1190
|
-
type: "llama_model",
|
|
1191
|
-
name: "Granite 3.1 MOE - 3B",
|
|
1192
|
-
repo_id: "granite3.1-moe:3b",
|
|
1193
|
-
description: "Granite MoE models are tuned for business document parsing and schema-following tasks.",
|
|
1194
|
-
size_on_disk: 1717986918
|
|
1195
|
-
},
|
|
1196
|
-
{
|
|
1197
|
-
id: "gemma3:4b",
|
|
1198
|
-
type: "llama_model",
|
|
1199
|
-
name: "Gemma3 - 4B",
|
|
1200
|
-
repo_id: "gemma3:4b",
|
|
1201
|
-
description: "Gemma 3 4B handles multilingual extraction and adheres to required JSON schemas.",
|
|
1202
|
-
size_on_disk: 2791728742
|
|
1203
|
-
},
|
|
1204
|
-
{
|
|
1205
|
-
id: "qwen2.5-coder:3b",
|
|
1206
|
-
type: "llama_model",
|
|
1207
|
-
name: "Qwen2.5-Coder - 3B",
|
|
1208
|
-
repo_id: "qwen2.5-coder:3b",
|
|
1209
|
-
description: "Code-focused Qwen variant generates precise structured outputs and respects schema rules.",
|
|
1210
|
-
size_on_disk: 1932735283
|
|
1211
|
-
},
|
|
1212
|
-
{
|
|
1213
|
-
id: "deepseek-r1:7b",
|
|
1214
|
-
type: "llama_model",
|
|
1215
|
-
name: "Deepseek R1 - 7B",
|
|
1216
|
-
repo_id: "deepseek-r1:7b",
|
|
1217
|
-
description: "Reasoning-oriented DeepSeek shines when extraction needs cross-field validation.",
|
|
1218
|
-
size_on_disk: 4617089843
|
|
1219
|
-
},
|
|
1220
|
-
{
|
|
1221
|
-
id: "ggml-org/gemma-3-4b-it-GGUF:gemma-3-4b-it-Q4_K_M.gguf",
|
|
1222
|
-
type: "llama_cpp_model",
|
|
1223
|
-
name: "Gemma 3 4B IT (GGUF)",
|
|
1224
|
-
repo_id: "ggml-org/gemma-3-4b-it-GGUF",
|
|
1225
|
-
path: "gemma-3-4b-it-Q4_K_M.gguf",
|
|
1226
|
-
description: "Efficient Gemma 3 for extraction via llama.cpp.",
|
|
1227
|
-
size_on_disk: 3113851289,
|
|
1228
|
-
pipeline_tag: "image-text-to-text",
|
|
1229
|
-
tags: [
|
|
1230
|
-
"gguf",
|
|
1231
|
-
"image-text-to-text",
|
|
1232
|
-
"arxiv:1905.07830",
|
|
1233
|
-
"arxiv:1905.10044",
|
|
1234
|
-
"arxiv:1911.11641",
|
|
1235
|
-
"arxiv:1904.09728",
|
|
1236
|
-
"arxiv:1705.03551",
|
|
1237
|
-
"arxiv:1911.01547",
|
|
1238
|
-
"arxiv:1907.10641",
|
|
1239
|
-
"arxiv:1903.00161",
|
|
1240
|
-
"arxiv:2009.03300",
|
|
1241
|
-
"arxiv:2304.06364",
|
|
1242
|
-
"arxiv:2103.03874",
|
|
1243
|
-
"arxiv:2110.14168",
|
|
1244
|
-
"arxiv:2311.12022",
|
|
1245
|
-
"arxiv:2108.07732",
|
|
1246
|
-
"arxiv:2107.03374",
|
|
1247
|
-
"arxiv:2210.03057",
|
|
1248
|
-
"arxiv:2106.03193",
|
|
1249
|
-
"arxiv:1910.11856",
|
|
1250
|
-
"arxiv:2502.12404",
|
|
1251
|
-
"arxiv:2502.21228",
|
|
1252
|
-
"arxiv:2404.16816",
|
|
1253
|
-
"arxiv:2104.12756",
|
|
1254
|
-
"arxiv:2311.16502",
|
|
1255
|
-
"arxiv:2203.10244",
|
|
1256
|
-
"arxiv:2404.12390",
|
|
1257
|
-
"arxiv:1810.12440",
|
|
1258
|
-
"arxiv:1908.02660",
|
|
1259
|
-
"arxiv:2312.11805",
|
|
1260
|
-
"base_model:google/gemma-3-4b-it",
|
|
1261
|
-
"base_model:quantized:google/gemma-3-4b-it",
|
|
1262
|
-
"license:gemma",
|
|
1263
|
-
"endpoints_compatible",
|
|
1264
|
-
"region:us",
|
|
1265
|
-
"conversational"
|
|
1266
|
-
],
|
|
1267
|
-
has_model_index: false,
|
|
1268
|
-
downloads: 25779,
|
|
1269
|
-
likes: 48
|
|
1270
|
-
}
|
|
1271
|
-
];
|
|
1272
|
-
async process(context) {
|
|
1273
|
-
const text = asText(this.text ?? this.text ?? "");
|
|
1274
|
-
const { providerId, modelId } = getModelConfig(this.serialize());
|
|
1275
|
-
if (hasProviderSupport(context, providerId, modelId)) {
|
|
1276
|
-
const provider = await context.getProvider(providerId);
|
|
1277
|
-
const schema = getStructuredOutputSchema(this) ?? {
|
|
1278
|
-
type: "object",
|
|
1279
|
-
properties: { output: { type: "string" } },
|
|
1280
|
-
required: ["output"],
|
|
1281
|
-
additionalProperties: true
|
|
1282
|
-
};
|
|
1283
|
-
const result = await generateStructured(provider, {
|
|
1284
|
-
model: modelId,
|
|
1285
|
-
maxTokens: Number(this.max_tokens ?? 1024),
|
|
1286
|
-
messages: [
|
|
1287
|
-
{ role: "system", content: EXTRACTOR_SYSTEM_PROMPT },
|
|
1288
|
-
{ role: "user", content: text }
|
|
1289
|
-
],
|
|
1290
|
-
toolName: "extraction_result",
|
|
1291
|
-
toolDescription: "Submit the extracted data.",
|
|
1292
|
-
schema
|
|
1293
|
-
});
|
|
1294
|
-
if (result)
|
|
1295
|
-
return result;
|
|
1296
|
-
}
|
|
1297
|
-
const parsed = extractJson(text);
|
|
1298
|
-
if (parsed)
|
|
1299
|
-
return parsed;
|
|
1300
|
-
return { output: text };
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
__decorate([
|
|
1304
|
-
prop({
|
|
1305
|
-
type: "str",
|
|
1306
|
-
default: '\nYou are a precise structured data extractor.\n\nGoal\n- Extract exactly the fields described in <JSON_SCHEMA> from the content in <TEXT> (and any attached media).\n\nOutput format (MANDATORY)\n- Output exactly ONE fenced code block labeled json containing ONLY the JSON object:\n\n ```json\n { ...single JSON object matching <JSON_SCHEMA>... }\n ```\n\n- No additional prose before or after the block.\n\nExtraction rules\n- Use only information found in <TEXT> or attached media. Do not invent facts.\n- Preserve source values; normalize internal whitespace and trim leading/trailing spaces.\n- If a required field is missing or not explicitly stated, return the closest reasonable default consistent with its type:\n - string: ""\n - number: 0\n - boolean: false\n - array/object: empty value of that type (only if allowed by the schema)\n- Dates/times: prefer ISO 8601 when the schema type is string and the value represents a date/time.\n- If multiple candidates exist, choose the most precise and unambiguous one.\n\nValidation\n- Ensure the final JSON validates against <JSON_SCHEMA> exactly.\n',
|
|
1307
|
-
title: "System Prompt",
|
|
1308
|
-
description: "The system prompt for the data extractor"
|
|
1309
|
-
})
|
|
1310
|
-
], ExtractorNode.prototype, "system_prompt", void 0);
|
|
1311
|
-
__decorate([
|
|
1312
|
-
prop({
|
|
1313
|
-
type: "language_model",
|
|
1314
|
-
default: {
|
|
1315
|
-
type: "language_model",
|
|
1316
|
-
provider: "empty",
|
|
1317
|
-
id: "",
|
|
1318
|
-
name: "",
|
|
1319
|
-
path: null,
|
|
1320
|
-
supported_tasks: []
|
|
1321
|
-
},
|
|
1322
|
-
title: "Model",
|
|
1323
|
-
description: "Model to use for data extraction"
|
|
1324
|
-
})
|
|
1325
|
-
], ExtractorNode.prototype, "model", void 0);
|
|
1326
|
-
__decorate([
|
|
1327
|
-
prop({
|
|
1328
|
-
type: "str",
|
|
1329
|
-
default: "",
|
|
1330
|
-
title: "Text",
|
|
1331
|
-
description: "The text to extract data from"
|
|
1332
|
-
})
|
|
1333
|
-
], ExtractorNode.prototype, "text", void 0);
|
|
1334
|
-
__decorate([
|
|
1335
|
-
prop({
|
|
1336
|
-
type: "image",
|
|
1337
|
-
default: {
|
|
1338
|
-
type: "image",
|
|
1339
|
-
uri: "",
|
|
1340
|
-
asset_id: null,
|
|
1341
|
-
data: null,
|
|
1342
|
-
metadata: null
|
|
1343
|
-
},
|
|
1344
|
-
title: "Image",
|
|
1345
|
-
description: "Optional image to assist extraction"
|
|
1346
|
-
})
|
|
1347
|
-
], ExtractorNode.prototype, "image", void 0);
|
|
1348
|
-
__decorate([
|
|
1349
|
-
prop({
|
|
1350
|
-
type: "audio",
|
|
1351
|
-
default: {
|
|
1352
|
-
type: "audio",
|
|
1353
|
-
uri: "",
|
|
1354
|
-
asset_id: null,
|
|
1355
|
-
data: null,
|
|
1356
|
-
metadata: null
|
|
1357
|
-
},
|
|
1358
|
-
title: "Audio",
|
|
1359
|
-
description: "Optional audio to assist extraction"
|
|
1360
|
-
})
|
|
1361
|
-
], ExtractorNode.prototype, "audio", void 0);
|
|
1362
|
-
export class ClassifierNode extends BaseNode {
|
|
1363
|
-
static nodeType = "nodetool.agents.Classifier";
|
|
1364
|
-
static title = "Classifier";
|
|
1365
|
-
static description = "Classify text into predefined or dynamic categories using LLM.\n classification, nlp, categorization\n\n Use cases:\n - Sentiment analysis\n - Topic classification\n - Intent detection\n - Content categorization";
|
|
1366
|
-
static metadataOutputTypes = {
|
|
1367
|
-
output: "str"
|
|
1368
|
-
};
|
|
1369
|
-
static basicFields = [
|
|
1370
|
-
"text",
|
|
1371
|
-
"categories",
|
|
1372
|
-
"model",
|
|
1373
|
-
"image",
|
|
1374
|
-
"audio"
|
|
1375
|
-
];
|
|
1376
|
-
static recommendedModels = [
|
|
1377
|
-
{
|
|
1378
|
-
id: "phi3.5:latest",
|
|
1379
|
-
type: "llama_model",
|
|
1380
|
-
name: "Phi3.5",
|
|
1381
|
-
repo_id: "phi3.5:latest",
|
|
1382
|
-
description: "Reliable small model for intent and sentiment classification when VRAM is tight.",
|
|
1383
|
-
size_on_disk: 2362232012
|
|
1384
|
-
},
|
|
1385
|
-
{
|
|
1386
|
-
id: "mistral-small:latest",
|
|
1387
|
-
type: "llama_model",
|
|
1388
|
-
name: "Mistral Small",
|
|
1389
|
-
repo_id: "mistral-small:latest",
|
|
1390
|
-
description: "Fast MoE model that keeps category predictions consistent across batches.",
|
|
1391
|
-
size_on_disk: 7730941132
|
|
1392
|
-
},
|
|
1393
|
-
{
|
|
1394
|
-
id: "granite3.1-moe:1b",
|
|
1395
|
-
type: "llama_model",
|
|
1396
|
-
name: "Granite 3.1 MOE - 1B",
|
|
1397
|
-
repo_id: "granite3.1-moe:1b",
|
|
1398
|
-
description: "IBM Granite 1B excels at classification and routing tasks on CPUs and edge devices.",
|
|
1399
|
-
size_on_disk: 751619276
|
|
1400
|
-
},
|
|
1401
|
-
{
|
|
1402
|
-
id: "qwen3:1.7b",
|
|
1403
|
-
type: "llama_model",
|
|
1404
|
-
name: "Qwen3 - 1.7B",
|
|
1405
|
-
repo_id: "qwen3:1.7b",
|
|
1406
|
-
description: "Compact Qwen variant provides multilingual label understanding with low latency.",
|
|
1407
|
-
size_on_disk: 1073741824
|
|
1408
|
-
},
|
|
1409
|
-
{
|
|
1410
|
-
id: "gemma3:1b",
|
|
1411
|
-
type: "llama_model",
|
|
1412
|
-
name: "Gemma3 - 1B",
|
|
1413
|
-
repo_id: "gemma3:1b",
|
|
1414
|
-
description: "Gemma 3 1B offers deterministic small-footprint classification for mobile scenarios.",
|
|
1415
|
-
size_on_disk: 805306368
|
|
1416
|
-
},
|
|
1417
|
-
{
|
|
1418
|
-
id: "deepseek-r1:1.5b",
|
|
1419
|
-
type: "llama_model",
|
|
1420
|
-
name: "Deepseek R1 - 1.5B",
|
|
1421
|
-
repo_id: "deepseek-r1:1.5b",
|
|
1422
|
-
description: "Reasoning-focused DeepSeek variant is great for multi-step label decisions.",
|
|
1423
|
-
size_on_disk: 912680550
|
|
1424
|
-
},
|
|
1425
|
-
{
|
|
1426
|
-
id: "ggml-org/gemma-3-4b-it-GGUF:gemma-3-4b-it-Q4_K_M.gguf",
|
|
1427
|
-
type: "llama_cpp_model",
|
|
1428
|
-
name: "Gemma 3 4B IT (GGUF)",
|
|
1429
|
-
repo_id: "ggml-org/gemma-3-4b-it-GGUF",
|
|
1430
|
-
path: "gemma-3-4b-it-Q4_K_M.gguf",
|
|
1431
|
-
description: "Efficient Gemma 3 for classification via llama.cpp.",
|
|
1432
|
-
size_on_disk: 3113851289,
|
|
1433
|
-
pipeline_tag: "image-text-to-text",
|
|
1434
|
-
tags: [
|
|
1435
|
-
"gguf",
|
|
1436
|
-
"image-text-to-text",
|
|
1437
|
-
"arxiv:1905.07830",
|
|
1438
|
-
"arxiv:1905.10044",
|
|
1439
|
-
"arxiv:1911.11641",
|
|
1440
|
-
"arxiv:1904.09728",
|
|
1441
|
-
"arxiv:1705.03551",
|
|
1442
|
-
"arxiv:1911.01547",
|
|
1443
|
-
"arxiv:1907.10641",
|
|
1444
|
-
"arxiv:1903.00161",
|
|
1445
|
-
"arxiv:2009.03300",
|
|
1446
|
-
"arxiv:2304.06364",
|
|
1447
|
-
"arxiv:2103.03874",
|
|
1448
|
-
"arxiv:2110.14168",
|
|
1449
|
-
"arxiv:2311.12022",
|
|
1450
|
-
"arxiv:2108.07732",
|
|
1451
|
-
"arxiv:2107.03374",
|
|
1452
|
-
"arxiv:2210.03057",
|
|
1453
|
-
"arxiv:2106.03193",
|
|
1454
|
-
"arxiv:1910.11856",
|
|
1455
|
-
"arxiv:2502.12404",
|
|
1456
|
-
"arxiv:2502.21228",
|
|
1457
|
-
"arxiv:2404.16816",
|
|
1458
|
-
"arxiv:2104.12756",
|
|
1459
|
-
"arxiv:2311.16502",
|
|
1460
|
-
"arxiv:2203.10244",
|
|
1461
|
-
"arxiv:2404.12390",
|
|
1462
|
-
"arxiv:1810.12440",
|
|
1463
|
-
"arxiv:1908.02660",
|
|
1464
|
-
"arxiv:2312.11805",
|
|
1465
|
-
"base_model:google/gemma-3-4b-it",
|
|
1466
|
-
"base_model:quantized:google/gemma-3-4b-it",
|
|
1467
|
-
"license:gemma",
|
|
1468
|
-
"endpoints_compatible",
|
|
1469
|
-
"region:us",
|
|
1470
|
-
"conversational"
|
|
1471
|
-
],
|
|
1472
|
-
has_model_index: false,
|
|
1473
|
-
downloads: 25779,
|
|
1474
|
-
likes: 48
|
|
1475
|
-
}
|
|
1476
|
-
];
|
|
1477
|
-
async process(context) {
|
|
1478
|
-
const text = asText(this.text ?? this.text ?? "");
|
|
1479
|
-
const categories = getCategories(this.categories ?? this.categories);
|
|
1480
|
-
if (categories.length < 2) {
|
|
1481
|
-
throw new Error("At least 2 categories are required");
|
|
1482
|
-
}
|
|
1483
|
-
const { providerId, modelId } = getModelConfig(this.serialize());
|
|
1484
|
-
if (hasProviderSupport(context, providerId, modelId)) {
|
|
1485
|
-
const provider = await context.getProvider(providerId);
|
|
1486
|
-
const result = await generateStructured(provider, {
|
|
1487
|
-
model: modelId,
|
|
1488
|
-
maxTokens: Number(this.max_tokens ?? 256),
|
|
1489
|
-
messages: [
|
|
1490
|
-
{ role: "system", content: CLASSIFIER_SYSTEM_PROMPT },
|
|
1491
|
-
{
|
|
1492
|
-
role: "user",
|
|
1493
|
-
content: `Allowed categories: ${categories.join(", ")}\n\nText: ${text}`
|
|
1494
|
-
}
|
|
1495
|
-
],
|
|
1496
|
-
toolName: "classification_result",
|
|
1497
|
-
toolDescription: "Submit the classification result.",
|
|
1498
|
-
schema: {
|
|
1499
|
-
type: "object",
|
|
1500
|
-
properties: {
|
|
1501
|
-
category: { type: "string", enum: categories }
|
|
1502
|
-
},
|
|
1503
|
-
required: ["category"]
|
|
1504
|
-
}
|
|
1505
|
-
});
|
|
1506
|
-
const category = parseCategory(result ? String(result.category ?? "") : "", categories);
|
|
1507
|
-
return { output: category, category };
|
|
1508
|
-
}
|
|
1509
|
-
const tokens = tokenize(text);
|
|
1510
|
-
let best = categories[0];
|
|
1511
|
-
let bestScore = -1;
|
|
1512
|
-
for (const category of categories) {
|
|
1513
|
-
const catTokens = tokenize(category);
|
|
1514
|
-
let score = 0;
|
|
1515
|
-
for (const token of catTokens) {
|
|
1516
|
-
if (tokens.includes(token))
|
|
1517
|
-
score += 1;
|
|
1518
|
-
}
|
|
1519
|
-
if (score > bestScore) {
|
|
1520
|
-
best = category;
|
|
1521
|
-
bestScore = score;
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
return { output: best, category: best };
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
__decorate([
|
|
1528
|
-
prop({
|
|
1529
|
-
type: "str",
|
|
1530
|
-
default: '\nYou are a precise classifier.\n\nGoal\n- Select exactly one category from the list provided by the user.\n\nOutput format (MANDATORY)\n- Return ONLY a single JSON object with this exact schema and nothing else:\n {"category": "<one-of-the-allowed-categories>"}\n- No prose, no Markdown, no code fences, no explanations, no extra keys.\n\nSelection criteria\n- Choose the single best category that captures the main intent of the text.\n- If multiple categories seem plausible, pick the most probable one; do not return multiple.\n- If none fit perfectly, choose the closest allowed category. If the list includes "Other" or "Unknown", prefer it when appropriate.\n- Be robust to casing, punctuation, emojis, and minor typos. Handle negation correctly (e.g., "not spam" ≠ spam).\n- Never invent categories that are not in the provided list.\n\nBehavior\n- Be deterministic for the same input.\n- Do not ask clarifying questions; make the best choice with what\'s given.\n',
|
|
1531
|
-
title: "System Prompt",
|
|
1532
|
-
description: "The system prompt for the classifier"
|
|
1533
|
-
})
|
|
1534
|
-
], ClassifierNode.prototype, "system_prompt", void 0);
|
|
1535
|
-
__decorate([
|
|
1536
|
-
prop({
|
|
1537
|
-
type: "language_model",
|
|
1538
|
-
default: {
|
|
1539
|
-
type: "language_model",
|
|
1540
|
-
provider: "empty",
|
|
1541
|
-
id: "",
|
|
1542
|
-
name: "",
|
|
1543
|
-
path: null,
|
|
1544
|
-
supported_tasks: []
|
|
1545
|
-
},
|
|
1546
|
-
title: "Model",
|
|
1547
|
-
description: "Model to use for classification"
|
|
1548
|
-
})
|
|
1549
|
-
], ClassifierNode.prototype, "model", void 0);
|
|
1550
|
-
__decorate([
|
|
1551
|
-
prop({
|
|
1552
|
-
type: "str",
|
|
1553
|
-
default: "",
|
|
1554
|
-
title: "Text",
|
|
1555
|
-
description: "Text to classify"
|
|
1556
|
-
})
|
|
1557
|
-
], ClassifierNode.prototype, "text", void 0);
|
|
1558
|
-
__decorate([
|
|
1559
|
-
prop({
|
|
1560
|
-
type: "image",
|
|
1561
|
-
default: {
|
|
1562
|
-
type: "image",
|
|
1563
|
-
uri: "",
|
|
1564
|
-
asset_id: null,
|
|
1565
|
-
data: null,
|
|
1566
|
-
metadata: null
|
|
1567
|
-
},
|
|
1568
|
-
title: "Image",
|
|
1569
|
-
description: "Optional image to classify in context"
|
|
1570
|
-
})
|
|
1571
|
-
], ClassifierNode.prototype, "image", void 0);
|
|
1572
|
-
__decorate([
|
|
1573
|
-
prop({
|
|
1574
|
-
type: "audio",
|
|
1575
|
-
default: {
|
|
1576
|
-
type: "audio",
|
|
1577
|
-
uri: "",
|
|
1578
|
-
asset_id: null,
|
|
1579
|
-
data: null,
|
|
1580
|
-
metadata: null
|
|
1581
|
-
},
|
|
1582
|
-
title: "Audio",
|
|
1583
|
-
description: "Optional audio to classify in context"
|
|
1584
|
-
})
|
|
1585
|
-
], ClassifierNode.prototype, "audio", void 0);
|
|
1586
|
-
__decorate([
|
|
1587
|
-
prop({
|
|
1588
|
-
type: "list[str]",
|
|
1589
|
-
default: [],
|
|
1590
|
-
title: "Categories",
|
|
1591
|
-
description: "List of possible categories. If empty, LLM will determine categories."
|
|
1592
|
-
})
|
|
1593
|
-
], ClassifierNode.prototype, "categories", void 0);
|
|
1594
|
-
export class AgentNode extends BaseNode {
|
|
1595
|
-
static nodeType = "nodetool.agents.Agent";
|
|
1596
|
-
static title = "Agent";
|
|
1597
|
-
static description = "Generate natural language responses using LLM providers and streams output.\n llm, text-generation, chatbot, question-answering, streaming";
|
|
1598
|
-
static metadataOutputTypes = {
|
|
1599
|
-
text: "str",
|
|
1600
|
-
chunk: "chunk",
|
|
1601
|
-
thinking: "chunk",
|
|
1602
|
-
audio: "audio"
|
|
1603
|
-
};
|
|
1604
|
-
static basicFields = ["prompt", "model", "tools", "image", "audio"];
|
|
1605
|
-
static supportsDynamicOutputs = true;
|
|
1606
|
-
static recommendedModels = [
|
|
1607
|
-
{
|
|
1608
|
-
id: "gpt-oss:20b",
|
|
1609
|
-
type: "llama_model",
|
|
1610
|
-
name: "GPT - OSS",
|
|
1611
|
-
repo_id: "gpt-oss:20b",
|
|
1612
|
-
description: "OpenAI's open-weight model excels at multi-tool routing and reasoning.",
|
|
1613
|
-
size_on_disk: 15032385536
|
|
1614
|
-
},
|
|
1615
|
-
{
|
|
1616
|
-
id: "qwen3-vl:4b",
|
|
1617
|
-
type: "llama_model",
|
|
1618
|
-
name: "Qwen3 VL - 4B",
|
|
1619
|
-
repo_id: "qwen3-vl:4b",
|
|
1620
|
-
description: "The most powerful vision-language model in the Qwen model family to date.",
|
|
1621
|
-
size_on_disk: 3543348019
|
|
1622
|
-
},
|
|
1623
|
-
{
|
|
1624
|
-
id: "qwen3-vl:8b",
|
|
1625
|
-
type: "llama_model",
|
|
1626
|
-
name: "Qwen3 VL - 8B",
|
|
1627
|
-
repo_id: "qwen3-vl:8b",
|
|
1628
|
-
description: "The most powerful vision-language model in the Qwen model family to date.",
|
|
1629
|
-
size_on_disk: 6549825126
|
|
1630
|
-
},
|
|
1631
|
-
{
|
|
1632
|
-
id: "gemma3:1b",
|
|
1633
|
-
type: "llama_model",
|
|
1634
|
-
name: "Gemma3 - 1B",
|
|
1635
|
-
repo_id: "gemma3:1b",
|
|
1636
|
-
description: "Gemma3 1B is a small model that can process text and images.",
|
|
1637
|
-
size_on_disk: 875099586
|
|
1638
|
-
},
|
|
1639
|
-
{
|
|
1640
|
-
id: "gemma3:4b",
|
|
1641
|
-
type: "llama_model",
|
|
1642
|
-
name: "Gemma3 - 4B",
|
|
1643
|
-
repo_id: "gemma3:4b",
|
|
1644
|
-
description: "Gemma3 4B is a small model that can process text and images.",
|
|
1645
|
-
size_on_disk: 3543348019
|
|
1646
|
-
},
|
|
1647
|
-
{
|
|
1648
|
-
id: "llama3.2:3b",
|
|
1649
|
-
type: "llama_model",
|
|
1650
|
-
name: "Llama 3.2 - 3B",
|
|
1651
|
-
repo_id: "llama3.2:3b",
|
|
1652
|
-
description: "Compact Llama 3.2 variant keeps latency low while following tool schemas accurately.",
|
|
1653
|
-
size_on_disk: 2040109465
|
|
1654
|
-
},
|
|
1655
|
-
{
|
|
1656
|
-
id: "qwen3:4b",
|
|
1657
|
-
type: "llama_model",
|
|
1658
|
-
name: "Qwen3 - 4B",
|
|
1659
|
-
repo_id: "qwen3:4b",
|
|
1660
|
-
description: "Qwen3 4B ships strong function-calling primitives and dependable multi-turn tool use.",
|
|
1661
|
-
size_on_disk: 2684354560
|
|
1662
|
-
},
|
|
1663
|
-
{
|
|
1664
|
-
id: "qwen3:8b",
|
|
1665
|
-
type: "llama_model",
|
|
1666
|
-
name: "Qwen3 - 8B",
|
|
1667
|
-
repo_id: "qwen3:8b",
|
|
1668
|
-
description: "Qwen3 8B ships strong function-calling primitives and dependable multi-turn tool use.",
|
|
1669
|
-
size_on_disk: 5583457484
|
|
1670
|
-
},
|
|
1671
|
-
{
|
|
1672
|
-
id: "deepseek-r1:8b",
|
|
1673
|
-
type: "mistral_model",
|
|
1674
|
-
name: "Deepseek R1 - 8B",
|
|
1675
|
-
repo_id: "deepseek-r1:8",
|
|
1676
|
-
description: "DeepSeek R1 8B balances reasoning with precise function calls for iterative agents.",
|
|
1677
|
-
size_on_disk: 5583457484
|
|
1678
|
-
},
|
|
1679
|
-
{
|
|
1680
|
-
id: "ggml-org/gpt-oss-20b-GGUF:gpt-oss-20b-mxfp4.gguf",
|
|
1681
|
-
type: "llama_cpp_model",
|
|
1682
|
-
name: "GPT-OSS 20B (GGUF)",
|
|
1683
|
-
repo_id: "ggml-org/gpt-oss-20b-GGUF",
|
|
1684
|
-
path: "gpt-oss-20b-mxfp4.gguf",
|
|
1685
|
-
description: "OpenAI's open-weight model in efficient MXFP4 format for llama.cpp.",
|
|
1686
|
-
size_on_disk: 9191230013,
|
|
1687
|
-
tags: [
|
|
1688
|
-
"gguf",
|
|
1689
|
-
"base_model:openai/gpt-oss-20b",
|
|
1690
|
-
"base_model:quantized:openai/gpt-oss-20b",
|
|
1691
|
-
"endpoints_compatible",
|
|
1692
|
-
"region:us",
|
|
1693
|
-
"conversational"
|
|
1694
|
-
],
|
|
1695
|
-
has_model_index: false,
|
|
1696
|
-
downloads: 156909,
|
|
1697
|
-
likes: 135
|
|
1698
|
-
},
|
|
1699
|
-
{
|
|
1700
|
-
id: "ggml-org/gemma-3-4b-it-GGUF:gemma-3-4b-it-Q4_K_M.gguf",
|
|
1701
|
-
type: "llama_cpp_model",
|
|
1702
|
-
name: "Gemma 3 4B IT (GGUF)",
|
|
1703
|
-
repo_id: "ggml-org/gemma-3-4b-it-GGUF",
|
|
1704
|
-
path: "gemma-3-4b-it-Q4_K_M.gguf",
|
|
1705
|
-
description: "Google's Gemma 3 4B in Q4_K_M quantization for efficient inference.",
|
|
1706
|
-
size_on_disk: 3113851289,
|
|
1707
|
-
pipeline_tag: "image-text-to-text",
|
|
1708
|
-
tags: [
|
|
1709
|
-
"gguf",
|
|
1710
|
-
"image-text-to-text",
|
|
1711
|
-
"arxiv:1905.07830",
|
|
1712
|
-
"arxiv:1905.10044",
|
|
1713
|
-
"arxiv:1911.11641",
|
|
1714
|
-
"arxiv:1904.09728",
|
|
1715
|
-
"arxiv:1705.03551",
|
|
1716
|
-
"arxiv:1911.01547",
|
|
1717
|
-
"arxiv:1907.10641",
|
|
1718
|
-
"arxiv:1903.00161",
|
|
1719
|
-
"arxiv:2009.03300",
|
|
1720
|
-
"arxiv:2304.06364",
|
|
1721
|
-
"arxiv:2103.03874",
|
|
1722
|
-
"arxiv:2110.14168",
|
|
1723
|
-
"arxiv:2311.12022",
|
|
1724
|
-
"arxiv:2108.07732",
|
|
1725
|
-
"arxiv:2107.03374",
|
|
1726
|
-
"arxiv:2210.03057",
|
|
1727
|
-
"arxiv:2106.03193",
|
|
1728
|
-
"arxiv:1910.11856",
|
|
1729
|
-
"arxiv:2502.12404",
|
|
1730
|
-
"arxiv:2502.21228",
|
|
1731
|
-
"arxiv:2404.16816",
|
|
1732
|
-
"arxiv:2104.12756",
|
|
1733
|
-
"arxiv:2311.16502",
|
|
1734
|
-
"arxiv:2203.10244",
|
|
1735
|
-
"arxiv:2404.12390",
|
|
1736
|
-
"arxiv:1810.12440",
|
|
1737
|
-
"arxiv:1908.02660",
|
|
1738
|
-
"arxiv:2312.11805",
|
|
1739
|
-
"base_model:google/gemma-3-4b-it",
|
|
1740
|
-
"base_model:quantized:google/gemma-3-4b-it",
|
|
1741
|
-
"license:gemma",
|
|
1742
|
-
"endpoints_compatible",
|
|
1743
|
-
"region:us",
|
|
1744
|
-
"conversational"
|
|
1745
|
-
],
|
|
1746
|
-
has_model_index: false,
|
|
1747
|
-
downloads: 25779,
|
|
1748
|
-
likes: 48
|
|
1749
|
-
},
|
|
1750
|
-
{
|
|
1751
|
-
id: "ggml-org/gemma-3-12b-it-GGUF:gemma-3-12b-it-Q4_K_M.gguf",
|
|
1752
|
-
type: "llama_cpp_model",
|
|
1753
|
-
name: "Gemma 3 12B IT (GGUF)",
|
|
1754
|
-
repo_id: "ggml-org/gemma-3-12b-it-GGUF",
|
|
1755
|
-
path: "gemma-3-12b-it-Q4_K_M.gguf",
|
|
1756
|
-
description: "Google's Gemma 3 12B in Q4_K_M quantization with strong reasoning.",
|
|
1757
|
-
size_on_disk: 7838315315,
|
|
1758
|
-
pipeline_tag: "image-text-to-text",
|
|
1759
|
-
tags: [
|
|
1760
|
-
"gguf",
|
|
1761
|
-
"image-text-to-text",
|
|
1762
|
-
"arxiv:1905.07830",
|
|
1763
|
-
"arxiv:1905.10044",
|
|
1764
|
-
"arxiv:1911.11641",
|
|
1765
|
-
"arxiv:1904.09728",
|
|
1766
|
-
"arxiv:1705.03551",
|
|
1767
|
-
"arxiv:1911.01547",
|
|
1768
|
-
"arxiv:1907.10641",
|
|
1769
|
-
"arxiv:1903.00161",
|
|
1770
|
-
"arxiv:2009.03300",
|
|
1771
|
-
"arxiv:2304.06364",
|
|
1772
|
-
"arxiv:2103.03874",
|
|
1773
|
-
"arxiv:2110.14168",
|
|
1774
|
-
"arxiv:2311.12022",
|
|
1775
|
-
"arxiv:2108.07732",
|
|
1776
|
-
"arxiv:2107.03374",
|
|
1777
|
-
"arxiv:2210.03057",
|
|
1778
|
-
"arxiv:2106.03193",
|
|
1779
|
-
"arxiv:1910.11856",
|
|
1780
|
-
"arxiv:2502.12404",
|
|
1781
|
-
"arxiv:2502.21228",
|
|
1782
|
-
"arxiv:2404.16816",
|
|
1783
|
-
"arxiv:2104.12756",
|
|
1784
|
-
"arxiv:2311.16502",
|
|
1785
|
-
"arxiv:2203.10244",
|
|
1786
|
-
"arxiv:2404.12390",
|
|
1787
|
-
"arxiv:1810.12440",
|
|
1788
|
-
"arxiv:1908.02660",
|
|
1789
|
-
"arxiv:2312.11805",
|
|
1790
|
-
"base_model:google/gemma-3-12b-it",
|
|
1791
|
-
"base_model:quantized:google/gemma-3-12b-it",
|
|
1792
|
-
"license:gemma",
|
|
1793
|
-
"endpoints_compatible",
|
|
1794
|
-
"region:us",
|
|
1795
|
-
"conversational"
|
|
1796
|
-
],
|
|
1797
|
-
has_model_index: false,
|
|
1798
|
-
downloads: 214667,
|
|
1799
|
-
likes: 30
|
|
1800
|
-
},
|
|
1801
|
-
{
|
|
1802
|
-
id: "ggml-org/Kimi-VL-A3B-Thinking-2506-GGUF:Kimi-VL-A3B-Thinking-2506-Q4_K_M.gguf",
|
|
1803
|
-
type: "llama_cpp_model",
|
|
1804
|
-
name: "Kimi VL A3B Thinking (GGUF)",
|
|
1805
|
-
repo_id: "ggml-org/Kimi-VL-A3B-Thinking-2506-GGUF",
|
|
1806
|
-
path: "Kimi-VL-A3B-Thinking-2506-Q4_K_M.gguf",
|
|
1807
|
-
description: "Moonshot AI's vision-language model with enhanced reasoning capabilities.",
|
|
1808
|
-
size_on_disk: 2362232012,
|
|
1809
|
-
tags: [
|
|
1810
|
-
"gguf",
|
|
1811
|
-
"base_model:moonshotai/Kimi-VL-A3B-Thinking-2506",
|
|
1812
|
-
"base_model:quantized:moonshotai/Kimi-VL-A3B-Thinking-2506",
|
|
1813
|
-
"endpoints_compatible",
|
|
1814
|
-
"region:us",
|
|
1815
|
-
"conversational"
|
|
1816
|
-
],
|
|
1817
|
-
has_model_index: false,
|
|
1818
|
-
downloads: 3039,
|
|
1819
|
-
likes: 29
|
|
1820
|
-
},
|
|
1821
|
-
{
|
|
1822
|
-
id: "ggml-org/Qwen3-Coder-30B-A3B-Instruct-Q8_0-GGUF:qwen3-coder-30b-a3b-instruct-q8_0.gguf",
|
|
1823
|
-
type: "llama_cpp_model",
|
|
1824
|
-
name: "Qwen3 Coder 30B A3B (GGUF)",
|
|
1825
|
-
repo_id: "ggml-org/Qwen3-Coder-30B-A3B-Instruct-Q8_0-GGUF",
|
|
1826
|
-
path: "qwen3-coder-30b-a3b-instruct-q8_0.gguf",
|
|
1827
|
-
description: "MoE coding model with 3B active params, excellent for code generation.",
|
|
1828
|
-
size_on_disk: 3865470566,
|
|
1829
|
-
pipeline_tag: "text-generation",
|
|
1830
|
-
tags: [
|
|
1831
|
-
"transformers",
|
|
1832
|
-
"gguf",
|
|
1833
|
-
"llama-cpp",
|
|
1834
|
-
"gguf-my-repo",
|
|
1835
|
-
"text-generation",
|
|
1836
|
-
"base_model:Qwen/Qwen3-Coder-30B-A3B-Instruct",
|
|
1837
|
-
"base_model:quantized:Qwen/Qwen3-Coder-30B-A3B-Instruct",
|
|
1838
|
-
"license:apache-2.0",
|
|
1839
|
-
"endpoints_compatible",
|
|
1840
|
-
"region:us",
|
|
1841
|
-
"conversational"
|
|
1842
|
-
],
|
|
1843
|
-
has_model_index: false,
|
|
1844
|
-
downloads: 80721,
|
|
1845
|
-
likes: 7
|
|
1846
|
-
},
|
|
1847
|
-
{
|
|
1848
|
-
id: "ggml-org/Qwen3-0.6B-GGUF:Qwen3-0.6B-Q4_0.gguf",
|
|
1849
|
-
type: "llama_cpp_model",
|
|
1850
|
-
name: "Qwen3 0.6B (GGUF)",
|
|
1851
|
-
repo_id: "ggml-org/Qwen3-0.6B-GGUF",
|
|
1852
|
-
path: "Qwen3-0.6B-Q4_0.gguf",
|
|
1853
|
-
description: "Ultra-lightweight Qwen3 for edge devices and fast inference.",
|
|
1854
|
-
size_on_disk: 429496729,
|
|
1855
|
-
tags: [
|
|
1856
|
-
"gguf",
|
|
1857
|
-
"base_model:Qwen/Qwen3-0.6B",
|
|
1858
|
-
"base_model:quantized:Qwen/Qwen3-0.6B",
|
|
1859
|
-
"license:apache-2.0",
|
|
1860
|
-
"endpoints_compatible",
|
|
1861
|
-
"region:us",
|
|
1862
|
-
"conversational"
|
|
1863
|
-
],
|
|
1864
|
-
has_model_index: false,
|
|
1865
|
-
downloads: 44304,
|
|
1866
|
-
likes: 13
|
|
1867
|
-
},
|
|
1868
|
-
{
|
|
1869
|
-
id: "ggml-org/gemma-3-270m-GGUF:gemma-3-270m-Q8_0.gguf",
|
|
1870
|
-
type: "llama_cpp_model",
|
|
1871
|
-
name: "Gemma 3 270M (GGUF)",
|
|
1872
|
-
repo_id: "ggml-org/gemma-3-270m-GGUF",
|
|
1873
|
-
path: "gemma-3-270m-Q8_0.gguf",
|
|
1874
|
-
description: "Tiny Gemma 3 for ultra-fast inference on CPU.",
|
|
1875
|
-
size_on_disk: 375809638,
|
|
1876
|
-
tags: [
|
|
1877
|
-
"gguf",
|
|
1878
|
-
"base_model:google/gemma-3-270m",
|
|
1879
|
-
"base_model:quantized:google/gemma-3-270m",
|
|
1880
|
-
"endpoints_compatible",
|
|
1881
|
-
"region:us"
|
|
1882
|
-
],
|
|
1883
|
-
has_model_index: false,
|
|
1884
|
-
downloads: 595,
|
|
1885
|
-
likes: 19
|
|
1886
|
-
},
|
|
1887
|
-
{
|
|
1888
|
-
id: "ggml-org/gemma-3-27b-it-GGUF:gemma-3-27b-it-Q4_K_M.gguf",
|
|
1889
|
-
type: "llama_cpp_model",
|
|
1890
|
-
name: "Gemma 3 27B IT (GGUF)",
|
|
1891
|
-
repo_id: "ggml-org/gemma-3-27b-it-GGUF",
|
|
1892
|
-
path: "gemma-3-27b-it-Q4_K_M.gguf",
|
|
1893
|
-
description: "Google's largest Gemma 3 with strong reasoning and tool use.",
|
|
1894
|
-
size_on_disk: 16965120819,
|
|
1895
|
-
pipeline_tag: "image-text-to-text",
|
|
1896
|
-
tags: [
|
|
1897
|
-
"gguf",
|
|
1898
|
-
"image-text-to-text",
|
|
1899
|
-
"arxiv:1905.07830",
|
|
1900
|
-
"arxiv:1905.10044",
|
|
1901
|
-
"arxiv:1911.11641",
|
|
1902
|
-
"arxiv:1904.09728",
|
|
1903
|
-
"arxiv:1705.03551",
|
|
1904
|
-
"arxiv:1911.01547",
|
|
1905
|
-
"arxiv:1907.10641",
|
|
1906
|
-
"arxiv:1903.00161",
|
|
1907
|
-
"arxiv:2009.03300",
|
|
1908
|
-
"arxiv:2304.06364",
|
|
1909
|
-
"arxiv:2103.03874",
|
|
1910
|
-
"arxiv:2110.14168",
|
|
1911
|
-
"arxiv:2311.12022",
|
|
1912
|
-
"arxiv:2108.07732",
|
|
1913
|
-
"arxiv:2107.03374",
|
|
1914
|
-
"arxiv:2210.03057",
|
|
1915
|
-
"arxiv:2106.03193",
|
|
1916
|
-
"arxiv:1910.11856",
|
|
1917
|
-
"arxiv:2502.12404",
|
|
1918
|
-
"arxiv:2502.21228",
|
|
1919
|
-
"arxiv:2404.16816",
|
|
1920
|
-
"arxiv:2104.12756",
|
|
1921
|
-
"arxiv:2311.16502",
|
|
1922
|
-
"arxiv:2203.10244",
|
|
1923
|
-
"arxiv:2404.12390",
|
|
1924
|
-
"arxiv:1810.12440",
|
|
1925
|
-
"arxiv:1908.02660",
|
|
1926
|
-
"arxiv:2312.11805",
|
|
1927
|
-
"base_model:google/gemma-3-27b-it",
|
|
1928
|
-
"base_model:quantized:google/gemma-3-27b-it",
|
|
1929
|
-
"license:gemma",
|
|
1930
|
-
"endpoints_compatible",
|
|
1931
|
-
"region:us",
|
|
1932
|
-
"conversational"
|
|
1933
|
-
],
|
|
1934
|
-
has_model_index: false,
|
|
1935
|
-
downloads: 2604,
|
|
1936
|
-
likes: 23
|
|
1937
|
-
},
|
|
1938
|
-
{
|
|
1939
|
-
id: "Qwen/Qwen3-30B-A3B-GGUF:Qwen3-30B-A3B-Q4_K_M.gguf",
|
|
1940
|
-
type: "llama_cpp_model",
|
|
1941
|
-
name: "Qwen3 30B A3B (GGUF)",
|
|
1942
|
-
repo_id: "Qwen/Qwen3-30B-A3B-GGUF",
|
|
1943
|
-
path: "Qwen3-30B-A3B-Q4_K_M.gguf",
|
|
1944
|
-
description: "Qwen3 30B MoE model (3B active) in Q4_K_M quantization.",
|
|
1945
|
-
size_on_disk: 19327352832,
|
|
1946
|
-
pipeline_tag: "text-generation",
|
|
1947
|
-
tags: [
|
|
1948
|
-
"gguf",
|
|
1949
|
-
"text-generation",
|
|
1950
|
-
"arxiv:2309.00071",
|
|
1951
|
-
"arxiv:2505.09388",
|
|
1952
|
-
"base_model:Qwen/Qwen3-30B-A3B",
|
|
1953
|
-
"base_model:quantized:Qwen/Qwen3-30B-A3B",
|
|
1954
|
-
"license:apache-2.0",
|
|
1955
|
-
"endpoints_compatible",
|
|
1956
|
-
"region:us",
|
|
1957
|
-
"conversational"
|
|
1958
|
-
],
|
|
1959
|
-
has_model_index: false,
|
|
1960
|
-
downloads: 17644,
|
|
1961
|
-
likes: 65
|
|
1962
|
-
},
|
|
1963
|
-
{
|
|
1964
|
-
id: "Qwen/Qwen3-32B-GGUF:Qwen3-32B-Q4_K_M.gguf",
|
|
1965
|
-
type: "llama_cpp_model",
|
|
1966
|
-
name: "Qwen3 32B (GGUF)",
|
|
1967
|
-
repo_id: "Qwen/Qwen3-32B-GGUF",
|
|
1968
|
-
path: "Qwen3-32B-Q4_K_M.gguf",
|
|
1969
|
-
description: "Qwen3 32B dense model in Q4_K_M quantization.",
|
|
1970
|
-
size_on_disk: 20401094656,
|
|
1971
|
-
pipeline_tag: "text-generation",
|
|
1972
|
-
tags: [
|
|
1973
|
-
"gguf",
|
|
1974
|
-
"text-generation",
|
|
1975
|
-
"arxiv:2309.00071",
|
|
1976
|
-
"base_model:Qwen/Qwen3-32B",
|
|
1977
|
-
"base_model:quantized:Qwen/Qwen3-32B",
|
|
1978
|
-
"license:apache-2.0",
|
|
1979
|
-
"endpoints_compatible",
|
|
1980
|
-
"region:us",
|
|
1981
|
-
"conversational"
|
|
1982
|
-
],
|
|
1983
|
-
has_model_index: false,
|
|
1984
|
-
downloads: 27381,
|
|
1985
|
-
likes: 64
|
|
1986
|
-
},
|
|
1987
|
-
{
|
|
1988
|
-
id: "Qwen/Qwen3-14B-GGUF:Qwen3-14B-Q4_K_M.gguf",
|
|
1989
|
-
type: "llama_cpp_model",
|
|
1990
|
-
name: "Qwen3 14B (GGUF)",
|
|
1991
|
-
repo_id: "Qwen/Qwen3-14B-GGUF",
|
|
1992
|
-
path: "Qwen3-14B-Q4_K_M.gguf",
|
|
1993
|
-
description: "Qwen3 14B dense model in Q4_K_M quantization.",
|
|
1994
|
-
size_on_disk: 9663676416,
|
|
1995
|
-
pipeline_tag: "text-generation",
|
|
1996
|
-
tags: [
|
|
1997
|
-
"gguf",
|
|
1998
|
-
"text-generation",
|
|
1999
|
-
"arxiv:2309.00071",
|
|
2000
|
-
"base_model:Qwen/Qwen3-14B",
|
|
2001
|
-
"base_model:quantized:Qwen/Qwen3-14B",
|
|
2002
|
-
"license:apache-2.0",
|
|
2003
|
-
"endpoints_compatible",
|
|
2004
|
-
"region:us",
|
|
2005
|
-
"conversational"
|
|
2006
|
-
],
|
|
2007
|
-
has_model_index: false,
|
|
2008
|
-
downloads: 61212,
|
|
2009
|
-
likes: 73
|
|
2010
|
-
},
|
|
2011
|
-
{
|
|
2012
|
-
id: "Qwen/Qwen3-8B-GGUF:Qwen3-8B-Q4_K_M.gguf",
|
|
2013
|
-
type: "llama_cpp_model",
|
|
2014
|
-
name: "Qwen3 8B (GGUF)",
|
|
2015
|
-
repo_id: "Qwen/Qwen3-8B-GGUF",
|
|
2016
|
-
path: "Qwen3-8B-Q4_K_M.gguf",
|
|
2017
|
-
description: "Qwen3 8B dense model in Q4_K_M quantization.",
|
|
2018
|
-
size_on_disk: 5368709120,
|
|
2019
|
-
pipeline_tag: "text-generation",
|
|
2020
|
-
tags: [
|
|
2021
|
-
"gguf",
|
|
2022
|
-
"text-generation",
|
|
2023
|
-
"arxiv:2309.00071",
|
|
2024
|
-
"arxiv:2505.09388",
|
|
2025
|
-
"base_model:Qwen/Qwen3-8B",
|
|
2026
|
-
"base_model:quantized:Qwen/Qwen3-8B",
|
|
2027
|
-
"license:apache-2.0",
|
|
2028
|
-
"endpoints_compatible",
|
|
2029
|
-
"region:us",
|
|
2030
|
-
"conversational"
|
|
2031
|
-
],
|
|
2032
|
-
has_model_index: false,
|
|
2033
|
-
downloads: 94189,
|
|
2034
|
-
likes: 156
|
|
2035
|
-
},
|
|
2036
|
-
{
|
|
2037
|
-
id: "Qwen/Qwen3-4B-GGUF:Qwen3-4B-Q4_K_M.gguf",
|
|
2038
|
-
type: "llama_cpp_model",
|
|
2039
|
-
name: "Qwen3 4B (GGUF)",
|
|
2040
|
-
repo_id: "Qwen/Qwen3-4B-GGUF",
|
|
2041
|
-
path: "Qwen3-4B-Q4_K_M.gguf",
|
|
2042
|
-
description: "Qwen3 4B dense model in Q4_K_M quantization.",
|
|
2043
|
-
size_on_disk: 2684354560,
|
|
2044
|
-
pipeline_tag: "text-generation",
|
|
2045
|
-
tags: [
|
|
2046
|
-
"gguf",
|
|
2047
|
-
"text-generation",
|
|
2048
|
-
"arxiv:2309.00071",
|
|
2049
|
-
"arxiv:2505.09388",
|
|
2050
|
-
"base_model:Qwen/Qwen3-4B",
|
|
2051
|
-
"base_model:quantized:Qwen/Qwen3-4B",
|
|
2052
|
-
"license:apache-2.0",
|
|
2053
|
-
"endpoints_compatible",
|
|
2054
|
-
"region:us",
|
|
2055
|
-
"conversational"
|
|
2056
|
-
],
|
|
2057
|
-
has_model_index: false,
|
|
2058
|
-
downloads: 48252,
|
|
2059
|
-
likes: 84
|
|
2060
|
-
}
|
|
2061
|
-
];
|
|
2062
|
-
static isStreamingOutput = true;
|
|
2063
|
-
/**
|
|
2064
|
-
* Build the tool list for this run. Override in subclasses to inject
|
|
2065
|
-
* additional tools (e.g. sandbox shell/file/browser tools) alongside the
|
|
2066
|
-
* user-selected ones. Control tools from kernel control edges are
|
|
2067
|
-
* appended separately by the genProcess paths and don't need to be
|
|
2068
|
-
* handled here.
|
|
2069
|
-
*/
|
|
2070
|
-
async buildTools(_context) {
|
|
2071
|
-
return normalizeTools(this.tools ?? []);
|
|
2072
|
-
}
|
|
2073
|
-
async *genProcess(context) {
|
|
2074
|
-
const { providerId, modelId } = getModelConfig(this.serialize());
|
|
2075
|
-
log.info("AgentNode starting", {
|
|
2076
|
-
nodeId: this.__node_id ?? null,
|
|
2077
|
-
providerId,
|
|
2078
|
-
modelId,
|
|
2079
|
-
hasContext: Boolean(context),
|
|
2080
|
-
hasGetProvider: Boolean(context && typeof context.getProvider === "function"),
|
|
2081
|
-
propKeys: Object.keys(this.serialize()),
|
|
2082
|
-
inputKeys: Object.keys(this.serialize())
|
|
2083
|
-
});
|
|
2084
|
-
if (!providerId || !modelId) {
|
|
2085
|
-
log.error("AgentNode missing model selection", {
|
|
2086
|
-
nodeId: this.__node_id ?? null,
|
|
2087
|
-
providerId,
|
|
2088
|
-
modelId,
|
|
2089
|
-
modelInput: this.model ?? null,
|
|
2090
|
-
modelProp: this.model ?? null
|
|
2091
|
-
});
|
|
2092
|
-
throw new Error("Select a model");
|
|
2093
|
-
}
|
|
2094
|
-
if (!context || typeof context.getProvider !== "function") {
|
|
2095
|
-
log.error("AgentNode missing processing context or provider access", {
|
|
2096
|
-
nodeId: this.__node_id ?? null,
|
|
2097
|
-
providerId,
|
|
2098
|
-
modelId
|
|
2099
|
-
});
|
|
2100
|
-
throw new Error("Processing context is required");
|
|
2101
|
-
}
|
|
2102
|
-
// --- Multi-mode dispatch for "plan" and "multi-agent" modes ---
|
|
2103
|
-
const agentMode = String(this.mode ?? "loop").trim();
|
|
2104
|
-
if (agentMode === "plan" || agentMode === "multi-agent") {
|
|
2105
|
-
yield* this.genProcessMultiMode(context, providerId, modelId, agentMode);
|
|
2106
|
-
return;
|
|
2107
|
-
}
|
|
2108
|
-
const prompt = asText(this.prompt ?? this.prompt ?? "");
|
|
2109
|
-
let system = asText(this.system ?? this.system ?? DEFAULT_SYSTEM_PROMPT);
|
|
2110
|
-
const image = this.image ?? this.image;
|
|
2111
|
-
const audio = this.audio ?? this.audio;
|
|
2112
|
-
const historyInput = this.history ?? this.history;
|
|
2113
|
-
const history = Array.isArray(historyInput)
|
|
2114
|
-
? historyInput
|
|
2115
|
-
.map((item) => normalizeMessage(item))
|
|
2116
|
-
.filter((item) => item !== null)
|
|
2117
|
-
: [];
|
|
2118
|
-
const threadId = String(this.thread_id ?? this.thread_id ?? "").trim();
|
|
2119
|
-
const maxTokens = Number(this.max_tokens ?? this.max_tokens ?? 8192);
|
|
2120
|
-
const maxTurns = Math.max(1, Number(this.max_turns ?? 100));
|
|
2121
|
-
const tools = await this.buildTools(context);
|
|
2122
|
-
// Build control tools from _control_context (injected by the kernel
|
|
2123
|
-
// for nodes that have outgoing control edges). This lets the LLM
|
|
2124
|
-
// call controlled nodes as tools.
|
|
2125
|
-
const controlContext = this.getDynamic("_control_context");
|
|
2126
|
-
const controlTools = buildControlTools(controlContext);
|
|
2127
|
-
if (controlTools.length > 0) {
|
|
2128
|
-
tools.push(...controlTools);
|
|
2129
|
-
log.info("AgentNode added control tools", {
|
|
2130
|
-
nodeId: this.__node_id ?? null,
|
|
2131
|
-
controlToolNames: controlTools.map((t) => t.name),
|
|
2132
|
-
controlTargets: controlTools.map((t) => t.targetNodeId)
|
|
2133
|
-
});
|
|
2134
|
-
}
|
|
2135
|
-
const structuredSchema = getStructuredOutputSchema(this);
|
|
2136
|
-
let structuredResult = null;
|
|
2137
|
-
const structuredToolName = structuredSchema
|
|
2138
|
-
? uniqueToolName("submit_result", tools.map((tool) => tool.name))
|
|
2139
|
-
: null;
|
|
2140
|
-
if (structuredSchema && structuredToolName) {
|
|
2141
|
-
tools.push({
|
|
2142
|
-
name: structuredToolName,
|
|
2143
|
-
description: "Submit the final structured result for this agent node's dynamic outputs. Call this exactly once when you have the final answer.",
|
|
2144
|
-
inputSchema: structuredSchema,
|
|
2145
|
-
process: async (_context, params) => {
|
|
2146
|
-
structuredResult = params ?? {};
|
|
2147
|
-
return { status: "completed" };
|
|
2148
|
-
}
|
|
2149
|
-
});
|
|
2150
|
-
system = `${system}\n\nWhen the final answer is ready, call the ${structuredToolName} tool with values for the dynamic outputs. Do not format the final result as JSON text.`;
|
|
2151
|
-
}
|
|
2152
|
-
const messages = [
|
|
2153
|
-
{ role: "system", content: system },
|
|
2154
|
-
...(await loadThreadMessages(context, threadId)),
|
|
2155
|
-
...history,
|
|
2156
|
-
buildUserMessage(prompt, image, audio)
|
|
2157
|
-
];
|
|
2158
|
-
log.info("AgentNode prepared messages", {
|
|
2159
|
-
nodeId: this.__node_id ?? null,
|
|
2160
|
-
providerId,
|
|
2161
|
-
modelId,
|
|
2162
|
-
threadId: threadId || null,
|
|
2163
|
-
promptLength: prompt.length,
|
|
2164
|
-
historyCount: history.length,
|
|
2165
|
-
toolCount: tools.length,
|
|
2166
|
-
messageCount: messages.length,
|
|
2167
|
-
hasImage: hasContentType(messages[messages.length - 1], "image_url"),
|
|
2168
|
-
hasAudio: hasContentType(messages[messages.length - 1], "audio")
|
|
2169
|
-
});
|
|
2170
|
-
if (threadId) {
|
|
2171
|
-
await saveThreadMessage(context, threadId, messages[messages.length - 1]);
|
|
2172
|
-
}
|
|
2173
|
-
let lastTextOutput = null;
|
|
2174
|
-
const providerTools = tools.length > 0 ? toProviderTools(tools) : undefined;
|
|
2175
|
-
const provider = await context.getProvider(providerId);
|
|
2176
|
-
// --- Agentic provider fast-path (e.g. Claude Agent SDK with MCP) ---
|
|
2177
|
-
// The provider handles the full tool-calling loop internally.
|
|
2178
|
-
// We provide an onToolCall callback so tools execute natively via MCP.
|
|
2179
|
-
if (isAgenticProvider(provider) && tools.length > 0) {
|
|
2180
|
-
log.info("AgentNode using agentic provider path", {
|
|
2181
|
-
nodeId: this.__node_id ?? null,
|
|
2182
|
-
providerId,
|
|
2183
|
-
modelId,
|
|
2184
|
-
toolCount: tools.length
|
|
2185
|
-
});
|
|
2186
|
-
// Build onToolCall callback that bridges into our tool set
|
|
2187
|
-
const onToolCall = async (name, args) => {
|
|
2188
|
-
const tool = tools.find((t) => t.name === name);
|
|
2189
|
-
if (!tool || typeof tool.process !== "function") {
|
|
2190
|
-
return JSON.stringify({ error: `Unknown tool: ${name}` });
|
|
2191
|
-
}
|
|
2192
|
-
if (isControlTool(tool)) {
|
|
2193
|
-
const callArgs = args ?? {};
|
|
2194
|
-
if (context.hasControlEventSupport) {
|
|
2195
|
-
try {
|
|
2196
|
-
const controlResult = await context.sendControlEvent(tool.targetNodeId, callArgs);
|
|
2197
|
-
return JSON.stringify({
|
|
2198
|
-
status: "completed",
|
|
2199
|
-
target_node_id: tool.targetNodeId,
|
|
2200
|
-
result: controlResult
|
|
2201
|
-
});
|
|
2202
|
-
}
|
|
2203
|
-
catch (err) {
|
|
2204
|
-
return JSON.stringify({
|
|
2205
|
-
status: "error",
|
|
2206
|
-
target_node_id: tool.targetNodeId,
|
|
2207
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2208
|
-
});
|
|
2209
|
-
}
|
|
2210
|
-
}
|
|
2211
|
-
return JSON.stringify({
|
|
2212
|
-
status: "error",
|
|
2213
|
-
error: "Control event dispatch unavailable"
|
|
2214
|
-
});
|
|
2215
|
-
}
|
|
2216
|
-
try {
|
|
2217
|
-
const result = await tool.process(context, args);
|
|
2218
|
-
return typeof result === "string"
|
|
2219
|
-
? result
|
|
2220
|
-
: JSON.stringify(serializeToolResult(result));
|
|
2221
|
-
}
|
|
2222
|
-
catch (e) {
|
|
2223
|
-
return JSON.stringify({ error: String(e) });
|
|
2224
|
-
}
|
|
2225
|
-
};
|
|
2226
|
-
let assistantText = "";
|
|
2227
|
-
const assistantToolCalls = [];
|
|
2228
|
-
const thinkSplitter = new RedactedThinkingStreamSplitter();
|
|
2229
|
-
let streamedRedactedThinking = false;
|
|
2230
|
-
for await (const item of streamProviderMessages(provider, {
|
|
2231
|
-
messages,
|
|
2232
|
-
model: modelId,
|
|
2233
|
-
tools: providerTools,
|
|
2234
|
-
maxTokens,
|
|
2235
|
-
maxTurns,
|
|
2236
|
-
threadId: threadId || undefined,
|
|
2237
|
-
onToolCall
|
|
2238
|
-
})) {
|
|
2239
|
-
if (isChunkItem(item)) {
|
|
2240
|
-
if (item.thinking) {
|
|
2241
|
-
yield { chunk: null, thinking: item, text: null, audio: null };
|
|
2242
|
-
}
|
|
2243
|
-
else if (item.content_type === "audio") {
|
|
2244
|
-
yield { chunk: item, thinking: null, text: null, audio: null };
|
|
2245
|
-
const audioBytes = item.content
|
|
2246
|
-
? Buffer.from(item.content, "base64")
|
|
2247
|
-
: Buffer.alloc(0);
|
|
2248
|
-
yield {
|
|
2249
|
-
chunk: null,
|
|
2250
|
-
thinking: null,
|
|
2251
|
-
text: null,
|
|
2252
|
-
audio: { data: new Uint8Array(audioBytes) }
|
|
2253
|
-
};
|
|
2254
|
-
}
|
|
2255
|
-
else {
|
|
2256
|
-
const rawPiece = item.content ?? "";
|
|
2257
|
-
assistantText += rawPiece;
|
|
2258
|
-
for (const y of yieldSplitThinkChunks(item, rawPiece, thinkSplitter)) {
|
|
2259
|
-
if (y.thinking != null)
|
|
2260
|
-
streamedRedactedThinking = true;
|
|
2261
|
-
yield y;
|
|
2262
|
-
}
|
|
2263
|
-
}
|
|
2264
|
-
}
|
|
2265
|
-
if (isToolCallItem(item)) {
|
|
2266
|
-
assistantToolCalls.push(item);
|
|
2267
|
-
log.info("AgentNode MCP tool executed", {
|
|
2268
|
-
nodeId: this.__node_id ?? null,
|
|
2269
|
-
toolName: item.name
|
|
2270
|
-
});
|
|
2271
|
-
yield {
|
|
2272
|
-
chunk: toolCallChunk(item),
|
|
2273
|
-
thinking: null,
|
|
2274
|
-
text: null,
|
|
2275
|
-
audio: null
|
|
2276
|
-
};
|
|
2277
|
-
}
|
|
2278
|
-
}
|
|
2279
|
-
for (const part of thinkSplitter.flush()) {
|
|
2280
|
-
if (part.kind === "thinking" && part.content.length > 0) {
|
|
2281
|
-
streamedRedactedThinking = true;
|
|
2282
|
-
yield {
|
|
2283
|
-
chunk: null,
|
|
2284
|
-
thinking: { type: "chunk", content: part.content, thinking: true },
|
|
2285
|
-
text: null,
|
|
2286
|
-
audio: null
|
|
2287
|
-
};
|
|
2288
|
-
}
|
|
2289
|
-
else if (part.kind === "text" && part.content.length > 0) {
|
|
2290
|
-
yield {
|
|
2291
|
-
chunk: {
|
|
2292
|
-
type: "chunk",
|
|
2293
|
-
content: part.content,
|
|
2294
|
-
content_type: "text",
|
|
2295
|
-
thinking: false,
|
|
2296
|
-
done: false
|
|
2297
|
-
},
|
|
2298
|
-
thinking: null,
|
|
2299
|
-
text: null,
|
|
2300
|
-
audio: null
|
|
2301
|
-
};
|
|
2302
|
-
}
|
|
2303
|
-
}
|
|
2304
|
-
if (assistantText) {
|
|
2305
|
-
const { thinking: thinkingText, text: cleanText } = extractThinkTags(assistantText);
|
|
2306
|
-
const trimmed = cleanText.trim();
|
|
2307
|
-
if (trimmed) {
|
|
2308
|
-
lastTextOutput = trimmed;
|
|
2309
|
-
}
|
|
2310
|
-
if (thinkingText && !streamedRedactedThinking) {
|
|
2311
|
-
yield {
|
|
2312
|
-
chunk: null,
|
|
2313
|
-
thinking: { type: "chunk", content: thinkingText, thinking: true },
|
|
2314
|
-
text: null,
|
|
2315
|
-
audio: null
|
|
2316
|
-
};
|
|
2317
|
-
}
|
|
2318
|
-
yield { chunk: null, thinking: null, text: cleanText, audio: null };
|
|
2319
|
-
}
|
|
2320
|
-
// Save messages to thread
|
|
2321
|
-
if (assistantText || assistantToolCalls.length > 0) {
|
|
2322
|
-
const assistantMessage = {
|
|
2323
|
-
role: "assistant",
|
|
2324
|
-
content: [{ type: "text", text: assistantText }],
|
|
2325
|
-
toolCalls: assistantToolCalls.length > 0 ? assistantToolCalls : null
|
|
2326
|
-
};
|
|
2327
|
-
messages.push(assistantMessage);
|
|
2328
|
-
await saveThreadMessage(context, threadId, assistantMessage);
|
|
2329
|
-
}
|
|
2330
|
-
log.info("AgentNode agentic path completed", {
|
|
2331
|
-
nodeId: this.__node_id ?? null,
|
|
2332
|
-
textLength: assistantText.length,
|
|
2333
|
-
toolCallCount: assistantToolCalls.length
|
|
2334
|
-
});
|
|
2335
|
-
}
|
|
2336
|
-
else {
|
|
2337
|
-
// --- Standard multi-iteration loop (for non-agentic providers) ---
|
|
2338
|
-
let shouldContinue = false;
|
|
2339
|
-
let firstIteration = true;
|
|
2340
|
-
let turn = 0;
|
|
2341
|
-
while ((firstIteration || shouldContinue) && turn < maxTurns) {
|
|
2342
|
-
firstIteration = false;
|
|
2343
|
-
shouldContinue = false;
|
|
2344
|
-
turn += 1;
|
|
2345
|
-
log.info("AgentNode provider iteration starting", {
|
|
2346
|
-
nodeId: this.__node_id ?? null,
|
|
2347
|
-
providerId,
|
|
2348
|
-
modelId,
|
|
2349
|
-
threadId: threadId || null,
|
|
2350
|
-
messageCount: messages.length,
|
|
2351
|
-
turn,
|
|
2352
|
-
maxTurns
|
|
2353
|
-
});
|
|
2354
|
-
const assistantToolCalls = [];
|
|
2355
|
-
let assistantText = "";
|
|
2356
|
-
let chunkCount = 0;
|
|
2357
|
-
let thinkingCount = 0;
|
|
2358
|
-
let audioChunkCount = 0;
|
|
2359
|
-
const thinkSplitter = new RedactedThinkingStreamSplitter();
|
|
2360
|
-
let streamedRedactedThinking = false;
|
|
2361
|
-
for await (const item of streamProviderMessages(provider, {
|
|
2362
|
-
messages,
|
|
2363
|
-
model: modelId,
|
|
2364
|
-
tools: providerTools,
|
|
2365
|
-
maxTokens,
|
|
2366
|
-
maxTurns,
|
|
2367
|
-
threadId: threadId || undefined
|
|
2368
|
-
})) {
|
|
2369
|
-
if (isChunkItem(item)) {
|
|
2370
|
-
chunkCount += 1;
|
|
2371
|
-
if (item.thinking) {
|
|
2372
|
-
thinkingCount += 1;
|
|
2373
|
-
yield { chunk: null, thinking: item, text: null, audio: null };
|
|
2374
|
-
continue;
|
|
2375
|
-
}
|
|
2376
|
-
if (item.content_type === "audio") {
|
|
2377
|
-
audioChunkCount += 1;
|
|
2378
|
-
yield { chunk: item, thinking: null, text: null, audio: null };
|
|
2379
|
-
const audioBytes = item.content
|
|
2380
|
-
? Buffer.from(item.content, "base64")
|
|
2381
|
-
: Buffer.alloc(0);
|
|
2382
|
-
yield {
|
|
2383
|
-
chunk: null,
|
|
2384
|
-
thinking: null,
|
|
2385
|
-
text: null,
|
|
2386
|
-
audio: { data: new Uint8Array(audioBytes) }
|
|
2387
|
-
};
|
|
2388
|
-
}
|
|
2389
|
-
else {
|
|
2390
|
-
const rawPiece = item.content ?? "";
|
|
2391
|
-
assistantText += rawPiece;
|
|
2392
|
-
for (const y of yieldSplitThinkChunks(item, rawPiece, thinkSplitter)) {
|
|
2393
|
-
if (y.thinking != null)
|
|
2394
|
-
streamedRedactedThinking = true;
|
|
2395
|
-
yield y;
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
|
-
continue;
|
|
2399
|
-
}
|
|
2400
|
-
if (isToolCallItem(item)) {
|
|
2401
|
-
assistantToolCalls.push(item);
|
|
2402
|
-
log.info("AgentNode received tool call", {
|
|
2403
|
-
nodeId: this.__node_id ?? null,
|
|
2404
|
-
providerId,
|
|
2405
|
-
modelId,
|
|
2406
|
-
toolCallId: item.id,
|
|
2407
|
-
toolName: item.name,
|
|
2408
|
-
argKeys: Object.keys(item.args ?? {})
|
|
2409
|
-
});
|
|
2410
|
-
yield {
|
|
2411
|
-
chunk: toolCallChunk(item),
|
|
2412
|
-
thinking: null,
|
|
2413
|
-
text: null,
|
|
2414
|
-
audio: null
|
|
2415
|
-
};
|
|
2416
|
-
}
|
|
2417
|
-
}
|
|
2418
|
-
log.info("AgentNode provider iteration completed", {
|
|
2419
|
-
nodeId: this.__node_id ?? null,
|
|
2420
|
-
providerId,
|
|
2421
|
-
modelId,
|
|
2422
|
-
chunkCount,
|
|
2423
|
-
thinkingCount,
|
|
2424
|
-
audioChunkCount,
|
|
2425
|
-
toolCallCount: assistantToolCalls.length,
|
|
2426
|
-
assistantTextLength: assistantText.length
|
|
2427
|
-
});
|
|
2428
|
-
for (const part of thinkSplitter.flush()) {
|
|
2429
|
-
if (part.kind === "thinking" && part.content.length > 0) {
|
|
2430
|
-
streamedRedactedThinking = true;
|
|
2431
|
-
yield {
|
|
2432
|
-
chunk: null,
|
|
2433
|
-
thinking: { type: "chunk", content: part.content, thinking: true },
|
|
2434
|
-
text: null,
|
|
2435
|
-
audio: null
|
|
2436
|
-
};
|
|
2437
|
-
}
|
|
2438
|
-
else if (part.kind === "text" && part.content.length > 0) {
|
|
2439
|
-
yield {
|
|
2440
|
-
chunk: {
|
|
2441
|
-
type: "chunk",
|
|
2442
|
-
content: part.content,
|
|
2443
|
-
content_type: "text",
|
|
2444
|
-
done: false
|
|
2445
|
-
},
|
|
2446
|
-
thinking: null,
|
|
2447
|
-
text: null,
|
|
2448
|
-
audio: null
|
|
2449
|
-
};
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
if (assistantText) {
|
|
2453
|
-
const { thinking: thinkingText, text: cleanText } = extractThinkTags(assistantText);
|
|
2454
|
-
const trimmed = cleanText.trim();
|
|
2455
|
-
if (trimmed) {
|
|
2456
|
-
lastTextOutput = trimmed;
|
|
2457
|
-
}
|
|
2458
|
-
if (thinkingText && !streamedRedactedThinking) {
|
|
2459
|
-
yield {
|
|
2460
|
-
chunk: null,
|
|
2461
|
-
thinking: { type: "chunk", content: thinkingText, thinking: true },
|
|
2462
|
-
text: null,
|
|
2463
|
-
audio: null
|
|
2464
|
-
};
|
|
2465
|
-
}
|
|
2466
|
-
yield {
|
|
2467
|
-
chunk: null,
|
|
2468
|
-
thinking: null,
|
|
2469
|
-
text: cleanText,
|
|
2470
|
-
audio: null
|
|
2471
|
-
};
|
|
2472
|
-
}
|
|
2473
|
-
if (assistantText || assistantToolCalls.length > 0) {
|
|
2474
|
-
const assistantMessage = {
|
|
2475
|
-
role: "assistant",
|
|
2476
|
-
content: [{ type: "text", text: assistantText }],
|
|
2477
|
-
toolCalls: assistantToolCalls.length > 0 ? assistantToolCalls : null
|
|
2478
|
-
};
|
|
2479
|
-
const rawParts = assistantToolCalls.find((tc) => tc._rawGeminiParts)?._rawGeminiParts;
|
|
2480
|
-
if (rawParts) {
|
|
2481
|
-
assistantMessage._rawGeminiParts = rawParts;
|
|
2482
|
-
}
|
|
2483
|
-
messages.push(assistantMessage);
|
|
2484
|
-
await saveThreadMessage(context, threadId, assistantMessage);
|
|
2485
|
-
}
|
|
2486
|
-
for (const toolCall of assistantToolCalls) {
|
|
2487
|
-
const tool = tools.find((candidate) => candidate.name === toolCall.name);
|
|
2488
|
-
if (!tool || typeof tool.process !== "function") {
|
|
2489
|
-
const message = `Unknown or non-executable tool: ${toolCall.name}`;
|
|
2490
|
-
log.warn("AgentNode tool call had no matching executable tool", {
|
|
2491
|
-
nodeId: this.__node_id ?? null,
|
|
2492
|
-
toolCallId: toolCall.id,
|
|
2493
|
-
toolName: toolCall.name,
|
|
2494
|
-
availableTools: tools.map((candidate) => candidate.name)
|
|
2495
|
-
});
|
|
2496
|
-
const toolMessage = {
|
|
2497
|
-
role: "tool",
|
|
2498
|
-
toolCallId: toolCall.id,
|
|
2499
|
-
content: JSON.stringify({ status: "error", error: message })
|
|
2500
|
-
};
|
|
2501
|
-
messages.push(toolMessage);
|
|
2502
|
-
await saveThreadMessage(context, threadId, toolMessage);
|
|
2503
|
-
shouldContinue = true;
|
|
2504
|
-
continue;
|
|
2505
|
-
}
|
|
2506
|
-
let result;
|
|
2507
|
-
if (isControlTool(tool)) {
|
|
2508
|
-
const callArgs = toolCall.args ?? {};
|
|
2509
|
-
log.info("AgentNode dispatching control tool", {
|
|
2510
|
-
nodeId: this.__node_id ?? null,
|
|
2511
|
-
toolCallId: toolCall.id,
|
|
2512
|
-
toolName: toolCall.name,
|
|
2513
|
-
targetNodeId: tool.targetNodeId,
|
|
2514
|
-
argKeys: Object.keys(callArgs)
|
|
2515
|
-
});
|
|
2516
|
-
if (context.hasControlEventSupport) {
|
|
2517
|
-
try {
|
|
2518
|
-
const controlResult = await context.sendControlEvent(tool.targetNodeId, callArgs);
|
|
2519
|
-
result = {
|
|
2520
|
-
status: "completed",
|
|
2521
|
-
target_node_id: tool.targetNodeId,
|
|
2522
|
-
result: controlResult
|
|
2523
|
-
};
|
|
2524
|
-
}
|
|
2525
|
-
catch (err) {
|
|
2526
|
-
result = {
|
|
2527
|
-
status: "error",
|
|
2528
|
-
target_node_id: tool.targetNodeId,
|
|
2529
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2530
|
-
};
|
|
2531
|
-
}
|
|
2532
|
-
}
|
|
2533
|
-
else {
|
|
2534
|
-
result = {
|
|
2535
|
-
status: "error",
|
|
2536
|
-
target_node_id: tool.targetNodeId,
|
|
2537
|
-
error: "Control event dispatch is not available in this execution context"
|
|
2538
|
-
};
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
else {
|
|
2542
|
-
log.info("AgentNode executing tool", {
|
|
2543
|
-
nodeId: this.__node_id ?? null,
|
|
2544
|
-
toolCallId: toolCall.id,
|
|
2545
|
-
toolName: toolCall.name
|
|
2546
|
-
});
|
|
2547
|
-
try {
|
|
2548
|
-
result = await tool.process(context, toolCall.args);
|
|
2549
|
-
}
|
|
2550
|
-
catch (err) {
|
|
2551
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2552
|
-
log.warn("AgentNode tool execution failed", {
|
|
2553
|
-
nodeId: this.__node_id ?? null,
|
|
2554
|
-
toolCallId: toolCall.id,
|
|
2555
|
-
toolName: toolCall.name,
|
|
2556
|
-
error: message
|
|
2557
|
-
});
|
|
2558
|
-
result = { status: "error", error: message };
|
|
2559
|
-
}
|
|
2560
|
-
}
|
|
2561
|
-
const toolMessage = {
|
|
2562
|
-
role: "tool",
|
|
2563
|
-
toolCallId: toolCall.id,
|
|
2564
|
-
content: JSON.stringify(serializeToolResult(result))
|
|
2565
|
-
};
|
|
2566
|
-
messages.push(toolMessage);
|
|
2567
|
-
await saveThreadMessage(context, threadId, toolMessage);
|
|
2568
|
-
if (structuredToolName && toolCall.name === structuredToolName) {
|
|
2569
|
-
shouldContinue = false;
|
|
2570
|
-
break;
|
|
2571
|
-
}
|
|
2572
|
-
shouldContinue = true;
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
if (shouldContinue && turn >= maxTurns) {
|
|
2576
|
-
log.warn("AgentNode hit max_turns cap with pending tool calls", {
|
|
2577
|
-
nodeId: this.__node_id ?? null,
|
|
2578
|
-
turn,
|
|
2579
|
-
maxTurns
|
|
2580
|
-
});
|
|
2581
|
-
}
|
|
2582
|
-
}
|
|
2583
|
-
if (structuredSchema && structuredResult) {
|
|
2584
|
-
log.info("AgentNode yielding dynamic output tool result", {
|
|
2585
|
-
nodeId: this.__node_id ?? null,
|
|
2586
|
-
keys: Object.keys(structuredResult)
|
|
2587
|
-
});
|
|
2588
|
-
yield structuredResult;
|
|
2589
|
-
}
|
|
2590
|
-
log.info("AgentNode completed", {
|
|
2591
|
-
nodeId: this.__node_id ?? null,
|
|
2592
|
-
providerId,
|
|
2593
|
-
modelId,
|
|
2594
|
-
finalTextLength: lastTextOutput?.length ?? 0,
|
|
2595
|
-
returnedStructured: Boolean(structuredResult)
|
|
2596
|
-
});
|
|
2597
|
-
}
|
|
2598
|
-
async process(context) {
|
|
2599
|
-
let lastText = "";
|
|
2600
|
-
let lastAudio = null;
|
|
2601
|
-
let structuredResult = null;
|
|
2602
|
-
for await (const item of this.genProcess(context)) {
|
|
2603
|
-
if ("chunk" in item ||
|
|
2604
|
-
"thinking" in item ||
|
|
2605
|
-
"text" in item ||
|
|
2606
|
-
"audio" in item) {
|
|
2607
|
-
if (typeof item.text === "string") {
|
|
2608
|
-
lastText = item.text;
|
|
2609
|
-
}
|
|
2610
|
-
if (item.audio && typeof item.audio === "object") {
|
|
2611
|
-
lastAudio = item.audio;
|
|
2612
|
-
}
|
|
2613
|
-
}
|
|
2614
|
-
else {
|
|
2615
|
-
structuredResult = item;
|
|
2616
|
-
}
|
|
2617
|
-
}
|
|
2618
|
-
if (structuredResult) {
|
|
2619
|
-
log.info("AgentNode process() returning structured result", {
|
|
2620
|
-
nodeId: this.__node_id ?? null,
|
|
2621
|
-
keys: Object.keys(structuredResult)
|
|
2622
|
-
});
|
|
2623
|
-
return structuredResult;
|
|
2624
|
-
}
|
|
2625
|
-
log.info("AgentNode process() returning aggregate result", {
|
|
2626
|
-
nodeId: this.__node_id ?? null,
|
|
2627
|
-
textLength: lastText.length,
|
|
2628
|
-
hasAudio: Boolean(lastAudio)
|
|
2629
|
-
});
|
|
2630
|
-
return {
|
|
2631
|
-
text: lastText,
|
|
2632
|
-
output: lastText,
|
|
2633
|
-
chunk: null,
|
|
2634
|
-
thinking: null,
|
|
2635
|
-
audio: lastAudio
|
|
2636
|
-
};
|
|
2637
|
-
}
|
|
2638
|
-
/**
|
|
2639
|
-
* Dispatch to MultiModeAgent for "plan" and "multi-agent" modes.
|
|
2640
|
-
* Converts ToolLike[] to AgentTool[] and bridges ProcessingMessage to node outputs.
|
|
2641
|
-
*/
|
|
2642
|
-
async *genProcessMultiMode(context, providerId, modelId, mode) {
|
|
2643
|
-
const prompt = asText(this.prompt ?? "");
|
|
2644
|
-
const system = asText(this.system ?? DEFAULT_SYSTEM_PROMPT);
|
|
2645
|
-
const rawTools = await this.buildTools(context);
|
|
2646
|
-
// Build control tools
|
|
2647
|
-
const controlContext = this.getDynamic("_control_context");
|
|
2648
|
-
const controlTools = buildControlTools(controlContext);
|
|
2649
|
-
if (controlTools.length > 0) {
|
|
2650
|
-
rawTools.push(...controlTools);
|
|
2651
|
-
}
|
|
2652
|
-
// Convert ToolLike[] to AgentTool[] for MultiModeAgent
|
|
2653
|
-
const agentTools = rawTools.map((t) => new ToolLikeAdapter(t));
|
|
2654
|
-
const provider = await context.getProvider(providerId);
|
|
2655
|
-
const agent = new MultiModeAgent({
|
|
2656
|
-
name: `AgentNode_${this.__node_id ?? "default"}`,
|
|
2657
|
-
objective: prompt,
|
|
2658
|
-
provider,
|
|
2659
|
-
model: modelId,
|
|
2660
|
-
mode,
|
|
2661
|
-
tools: agentTools,
|
|
2662
|
-
systemPrompt: system,
|
|
2663
|
-
maxTokenLimit: Number(this.max_tokens ?? 8192),
|
|
2664
|
-
outputSchema: getStructuredOutputSchema(this) ?? undefined,
|
|
2665
|
-
// Plan mode options
|
|
2666
|
-
planningModel: modelId,
|
|
2667
|
-
maxSteps: 10,
|
|
2668
|
-
maxStepIterations: 5,
|
|
2669
|
-
// Multi-agent options
|
|
2670
|
-
numSubAgents: Number(this.num_agents ?? 3),
|
|
2671
|
-
teamStrategy: (this.team_strategy ?? "coordinator"),
|
|
2672
|
-
maxConcurrency: 5
|
|
2673
|
-
});
|
|
2674
|
-
let lastText = "";
|
|
2675
|
-
for await (const msg of agent.execute(context)) {
|
|
2676
|
-
const pmsg = msg;
|
|
2677
|
-
if (pmsg.type === "chunk") {
|
|
2678
|
-
const chunk = pmsg;
|
|
2679
|
-
const content = chunk.content ?? "";
|
|
2680
|
-
lastText += content;
|
|
2681
|
-
yield {
|
|
2682
|
-
chunk: { type: "chunk", content, done: false },
|
|
2683
|
-
thinking: null,
|
|
2684
|
-
text: null,
|
|
2685
|
-
audio: null
|
|
2686
|
-
};
|
|
2687
|
-
}
|
|
2688
|
-
else if (pmsg.type === "step_result") {
|
|
2689
|
-
const result = pmsg.result;
|
|
2690
|
-
if (result != null) {
|
|
2691
|
-
const resultText = typeof result === "string" ? result : JSON.stringify(result);
|
|
2692
|
-
lastText = resultText;
|
|
2693
|
-
}
|
|
2694
|
-
}
|
|
2695
|
-
else if (pmsg.type === "log_update") {
|
|
2696
|
-
log.info("MultiModeAgent log", {
|
|
2697
|
-
nodeId: this.__node_id ?? null,
|
|
2698
|
-
content: pmsg.content
|
|
2699
|
-
});
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
|
-
const resultText = lastText ||
|
|
2703
|
-
(agent.getResults() != null
|
|
2704
|
-
? typeof agent.getResults() === "string"
|
|
2705
|
-
? agent.getResults()
|
|
2706
|
-
: JSON.stringify(agent.getResults())
|
|
2707
|
-
: "");
|
|
2708
|
-
yield { chunk: null, thinking: null, text: resultText, audio: null };
|
|
2709
|
-
return {
|
|
2710
|
-
text: resultText,
|
|
2711
|
-
output: resultText,
|
|
2712
|
-
chunk: null,
|
|
2713
|
-
thinking: null,
|
|
2714
|
-
audio: null
|
|
2715
|
-
};
|
|
2716
|
-
}
|
|
2717
|
-
}
|
|
2718
|
-
__decorate([
|
|
2719
|
-
prop({
|
|
2720
|
-
type: "language_model",
|
|
2721
|
-
default: {
|
|
2722
|
-
type: "language_model",
|
|
2723
|
-
provider: "empty",
|
|
2724
|
-
id: "",
|
|
2725
|
-
name: "",
|
|
2726
|
-
path: null,
|
|
2727
|
-
supported_tasks: []
|
|
2728
|
-
},
|
|
2729
|
-
title: "Model",
|
|
2730
|
-
description: "Model to use for execution"
|
|
2731
|
-
})
|
|
2732
|
-
], AgentNode.prototype, "model", void 0);
|
|
2733
|
-
__decorate([
|
|
2734
|
-
prop({
|
|
2735
|
-
type: "enum",
|
|
2736
|
-
default: "loop",
|
|
2737
|
-
title: "Mode",
|
|
2738
|
-
description: "How the agent runs.\n\n• loop: standard tool‑calling loop — the LLM responds to the prompt and may iteratively call the connected tools until it produces a final answer. Use this for chat, Q&A, and most tool‑using tasks.\n• plan: the LLM first drafts a multi‑step task plan from the objective, then executes the steps in dependency order (independent steps run in parallel). Best for longer, structured jobs with clear sub‑tasks.\n• multi-agent: auto‑specialises a team of sub‑agents (count controlled by Num Agents) that collaborate on the objective using the chosen Team Strategy. Best for open‑ended objectives that benefit from different roles working together.",
|
|
2739
|
-
values: ["loop", "plan", "multi-agent"]
|
|
2740
|
-
})
|
|
2741
|
-
], AgentNode.prototype, "mode", void 0);
|
|
2742
|
-
__decorate([
|
|
2743
|
-
prop({
|
|
2744
|
-
type: "str",
|
|
2745
|
-
default: "You are a friendly assistant",
|
|
2746
|
-
title: "System",
|
|
2747
|
-
description: "Instructions that define the agent's persona, role, tone, and global behaviour. Sent to the model as the system message at the start of every run, before any history or user prompt. Use it for things that should always hold (e.g. \"You are a senior Python reviewer. Reply in Markdown.\"). Leave the prompt itself for the per‑run task."
|
|
2748
|
-
})
|
|
2749
|
-
], AgentNode.prototype, "system", void 0);
|
|
2750
|
-
__decorate([
|
|
2751
|
-
prop({
|
|
2752
|
-
type: "str",
|
|
2753
|
-
default: "",
|
|
2754
|
-
title: "Prompt",
|
|
2755
|
-
description: "The user message for this run — the actual question, task, or content the agent should act on. Appended after the system prompt and conversation history as the latest user turn. Any connected Image or Audio inputs are attached to this message. In plan and multi-agent modes this is treated as the objective for planning."
|
|
2756
|
-
})
|
|
2757
|
-
], AgentNode.prototype, "prompt", void 0);
|
|
2758
|
-
__decorate([
|
|
2759
|
-
prop({
|
|
2760
|
-
type: "list[tool_name]",
|
|
2761
|
-
default: [],
|
|
2762
|
-
title: "Tools",
|
|
2763
|
-
description: "Tools to enable for the agent. Select workspace tools (read_file, write_file, list_directory) to enable file operations."
|
|
2764
|
-
})
|
|
2765
|
-
], AgentNode.prototype, "tools", void 0);
|
|
2766
|
-
__decorate([
|
|
2767
|
-
prop({
|
|
2768
|
-
type: "image",
|
|
2769
|
-
default: {
|
|
2770
|
-
type: "image",
|
|
2771
|
-
uri: "",
|
|
2772
|
-
asset_id: null,
|
|
2773
|
-
data: null,
|
|
2774
|
-
metadata: null
|
|
2775
|
-
},
|
|
2776
|
-
title: "Image",
|
|
2777
|
-
description: "The image to analyze"
|
|
2778
|
-
})
|
|
2779
|
-
], AgentNode.prototype, "image", void 0);
|
|
2780
|
-
__decorate([
|
|
2781
|
-
prop({
|
|
2782
|
-
type: "audio",
|
|
2783
|
-
default: {
|
|
2784
|
-
type: "audio",
|
|
2785
|
-
uri: "",
|
|
2786
|
-
asset_id: null,
|
|
2787
|
-
data: null,
|
|
2788
|
-
metadata: null
|
|
2789
|
-
},
|
|
2790
|
-
title: "Audio",
|
|
2791
|
-
description: "The audio to analyze"
|
|
2792
|
-
})
|
|
2793
|
-
], AgentNode.prototype, "audio", void 0);
|
|
2794
|
-
__decorate([
|
|
2795
|
-
prop({
|
|
2796
|
-
type: "list[message]",
|
|
2797
|
-
default: [],
|
|
2798
|
-
title: "Messages",
|
|
2799
|
-
description: "Prior conversation turns to include before the current prompt, in chronological order (oldest first). Each item is a Message with a role (user/assistant/tool) and content. Use this to supply ad‑hoc context — for example, few‑shot examples, a previous chat transcript piped in from another node, or the messages output of an upstream Agent. Inserted between the system prompt and the new user prompt. If a Thread ID is also set, history loaded from the thread comes first, then this list, then the current prompt."
|
|
2800
|
-
})
|
|
2801
|
-
], AgentNode.prototype, "history", void 0);
|
|
2802
|
-
__decorate([
|
|
2803
|
-
prop({
|
|
2804
|
-
type: "str",
|
|
2805
|
-
default: "",
|
|
2806
|
-
title: "Thread ID",
|
|
2807
|
-
description: "Identifier for a persistent conversation thread. When set, the agent loads all earlier messages stored under this ID before this turn and saves the new user message, assistant reply, and any tool messages back to it — giving the agent long‑term memory across runs and across nodes that share the same ID. Leave empty for a stateless one‑shot call. Use the Create Thread node to mint a fresh ID, or wire in the same string from upstream to continue an existing conversation."
|
|
2808
|
-
})
|
|
2809
|
-
], AgentNode.prototype, "thread_id", void 0);
|
|
2810
|
-
__decorate([
|
|
2811
|
-
prop({
|
|
2812
|
-
type: "int",
|
|
2813
|
-
default: 8192,
|
|
2814
|
-
title: "Max Tokens",
|
|
2815
|
-
description: "Upper bound on the number of tokens the model may generate per response (the model's reply, not the prompt). Higher values allow longer answers but cost more and take longer; very low values may cause the model to truncate mid‑sentence. This is also the maxTokenLimit passed to plan and multi-agent executors. Typical values: 1024 for short answers, 4096–8192 for normal use, 16k+ for long‑form generation. Must be within the chosen model's context window.",
|
|
2816
|
-
min: 1,
|
|
2817
|
-
max: 100000
|
|
2818
|
-
})
|
|
2819
|
-
], AgentNode.prototype, "max_tokens", void 0);
|
|
2820
|
-
__decorate([
|
|
2821
|
-
prop({
|
|
2822
|
-
type: "int",
|
|
2823
|
-
default: 100,
|
|
2824
|
-
title: "Max Turns",
|
|
2825
|
-
description: "Upper bound on agentic turns — one turn is a model call plus any tool execution it triggers. Caps both the AgentNode tool-loop iteration count and the provider's internal multi-turn budget (e.g. Claude Agent SDK). Raise for long sandbox sessions; lower to fail fast on runaway loops.",
|
|
2826
|
-
min: 1,
|
|
2827
|
-
max: 1000
|
|
2828
|
-
})
|
|
2829
|
-
], AgentNode.prototype, "max_turns", void 0);
|
|
2830
|
-
__decorate([
|
|
2831
|
-
prop({
|
|
2832
|
-
type: "int",
|
|
2833
|
-
default: 3,
|
|
2834
|
-
title: "Num Agents",
|
|
2835
|
-
description: "Number of sub‑agents to auto‑specialise when Mode is multi-agent. The planner inspects the objective and creates this many distinct roles (e.g. researcher, writer, critic), each with its own skill set, that then collaborate via the Team Strategy. Ignored in loop and plan modes. More agents allow more division of labour but increase token usage and coordination overhead.",
|
|
2836
|
-
min: 2,
|
|
2837
|
-
max: 10
|
|
2838
|
-
})
|
|
2839
|
-
], AgentNode.prototype, "num_agents", void 0);
|
|
2840
|
-
__decorate([
|
|
2841
|
-
prop({
|
|
2842
|
-
type: "enum",
|
|
2843
|
-
default: "coordinator",
|
|
2844
|
-
title: "Team Strategy",
|
|
2845
|
-
description: "How the auto‑specialised sub‑agents collaborate when Mode is multi-agent. Ignored in other modes.\n\n• coordinator: the first agent acts as a project manager — it decomposes the objective into tasks on a shared task board, and the remaining agents claim and execute them. Best for well‑defined goals where you want predictable orchestration.\n• autonomous: every agent sees the full objective and self‑organises, claiming tasks, posting messages, and creating new tasks as needed without a central planner. Best for open‑ended exploration.\n• hybrid: a coordinator seeds an initial plan, but worker agents may also create their own subtasks while executing. Balances structure with flexibility.",
|
|
2846
|
-
values: ["coordinator", "autonomous", "hybrid"]
|
|
2847
|
-
})
|
|
2848
|
-
], AgentNode.prototype, "team_strategy", void 0);
|
|
2849
|
-
export const AGENT_NODES = [
|
|
2850
|
-
SummarizerNode,
|
|
2851
|
-
CreateThreadNode,
|
|
2852
|
-
ExtractorNode,
|
|
2853
|
-
ClassifierNode,
|
|
2854
|
-
AgentNode
|
|
2855
|
-
];
|
|
2856
|
-
//# sourceMappingURL=agents.js.map
|