@nodetool-ai/base-nodes 0.7.0-rc.23 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/README.md +21 -0
  2. package/dist/.nodetool-build-stamp +1 -0
  3. package/dist/builtin-packs.d.ts +17 -0
  4. package/dist/builtin-packs.d.ts.map +1 -0
  5. package/dist/builtin-packs.js +95 -0
  6. package/dist/builtin-packs.js.map +1 -0
  7. package/dist/index.d.ts +63 -67
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +186 -173
  10. package/dist/index.js.map +1 -1
  11. package/dist/platforms/browser.d.ts +19 -0
  12. package/dist/platforms/browser.d.ts.map +1 -0
  13. package/dist/platforms/browser.js +29 -0
  14. package/dist/platforms/browser.js.map +1 -0
  15. package/package.json +72 -65
  16. package/dist/lib/audio-wav.d.ts +0 -66
  17. package/dist/lib/audio-wav.d.ts.map +0 -1
  18. package/dist/lib/audio-wav.js +0 -215
  19. package/dist/lib/audio-wav.js.map +0 -1
  20. package/dist/nodes/agent-tool-hydration.d.ts +0 -10
  21. package/dist/nodes/agent-tool-hydration.d.ts.map +0 -1
  22. package/dist/nodes/agent-tool-hydration.js +0 -43
  23. package/dist/nodes/agent-tool-hydration.js.map +0 -1
  24. package/dist/nodes/agents.d.ts +0 -262
  25. package/dist/nodes/agents.d.ts.map +0 -1
  26. package/dist/nodes/agents.js +0 -2856
  27. package/dist/nodes/agents.js.map +0 -1
  28. package/dist/nodes/anthropic.d.ts +0 -26
  29. package/dist/nodes/anthropic.d.ts.map +0 -1
  30. package/dist/nodes/anthropic.js +0 -133
  31. package/dist/nodes/anthropic.js.map +0 -1
  32. package/dist/nodes/apify.d.ts +0 -120
  33. package/dist/nodes/apify.d.ts.map +0 -1
  34. package/dist/nodes/apify.js +0 -618
  35. package/dist/nodes/apify.js.map +0 -1
  36. package/dist/nodes/audio.d.ts +0 -301
  37. package/dist/nodes/audio.d.ts.map +0 -1
  38. package/dist/nodes/audio.js +0 -1274
  39. package/dist/nodes/audio.js.map +0 -1
  40. package/dist/nodes/code-node.d.ts +0 -34
  41. package/dist/nodes/code-node.d.ts.map +0 -1
  42. package/dist/nodes/code-node.js +0 -343
  43. package/dist/nodes/code-node.js.map +0 -1
  44. package/dist/nodes/code.d.ts +0 -301
  45. package/dist/nodes/code.d.ts.map +0 -1
  46. package/dist/nodes/code.js +0 -850
  47. package/dist/nodes/code.js.map +0 -1
  48. package/dist/nodes/compare.d.ts +0 -18
  49. package/dist/nodes/compare.d.ts.map +0 -1
  50. package/dist/nodes/compare.js +0 -122
  51. package/dist/nodes/compare.js.map +0 -1
  52. package/dist/nodes/constant.d.ts +0 -315
  53. package/dist/nodes/constant.d.ts.map +0 -1
  54. package/dist/nodes/constant.js +0 -715
  55. package/dist/nodes/constant.js.map +0 -1
  56. package/dist/nodes/control.d.ts +0 -83
  57. package/dist/nodes/control.d.ts.map +0 -1
  58. package/dist/nodes/control.js +0 -212
  59. package/dist/nodes/control.js.map +0 -1
  60. package/dist/nodes/data.d.ts +0 -369
  61. package/dist/nodes/data.d.ts.map +0 -1
  62. package/dist/nodes/data.js +0 -1590
  63. package/dist/nodes/data.js.map +0 -1
  64. package/dist/nodes/document.d.ts +0 -134
  65. package/dist/nodes/document.d.ts.map +0 -1
  66. package/dist/nodes/document.js +0 -984
  67. package/dist/nodes/document.js.map +0 -1
  68. package/dist/nodes/extended-placeholders.d.ts +0 -6
  69. package/dist/nodes/extended-placeholders.d.ts.map +0 -1
  70. package/dist/nodes/extended-placeholders.js +0 -19
  71. package/dist/nodes/extended-placeholders.js.map +0 -1
  72. package/dist/nodes/gemini.d.ts +0 -110
  73. package/dist/nodes/gemini.d.ts.map +0 -1
  74. package/dist/nodes/gemini.js +0 -871
  75. package/dist/nodes/gemini.js.map +0 -1
  76. package/dist/nodes/generators.d.ts +0 -84
  77. package/dist/nodes/generators.d.ts.map +0 -1
  78. package/dist/nodes/generators.js +0 -968
  79. package/dist/nodes/generators.js.map +0 -1
  80. package/dist/nodes/image.d.ts +0 -232
  81. package/dist/nodes/image.d.ts.map +0 -1
  82. package/dist/nodes/image.js +0 -1395
  83. package/dist/nodes/image.js.map +0 -1
  84. package/dist/nodes/input.d.ts +0 -445
  85. package/dist/nodes/input.d.ts.map +0 -1
  86. package/dist/nodes/input.js +0 -1459
  87. package/dist/nodes/input.js.map +0 -1
  88. package/dist/nodes/kie-dynamic.d.ts +0 -31
  89. package/dist/nodes/kie-dynamic.d.ts.map +0 -1
  90. package/dist/nodes/kie-dynamic.js +0 -270
  91. package/dist/nodes/kie-dynamic.js.map +0 -1
  92. package/dist/nodes/lib-audio-dsp.d.ts +0 -85
  93. package/dist/nodes/lib-audio-dsp.d.ts.map +0 -1
  94. package/dist/nodes/lib-audio-dsp.js +0 -450
  95. package/dist/nodes/lib-audio-dsp.js.map +0 -1
  96. package/dist/nodes/lib-audio-effects.d.ts +0 -116
  97. package/dist/nodes/lib-audio-effects.d.ts.map +0 -1
  98. package/dist/nodes/lib-audio-effects.js +0 -905
  99. package/dist/nodes/lib-audio-effects.js.map +0 -1
  100. package/dist/nodes/lib-browser.d.ts +0 -99
  101. package/dist/nodes/lib-browser.d.ts.map +0 -1
  102. package/dist/nodes/lib-browser.js +0 -692
  103. package/dist/nodes/lib-browser.js.map +0 -1
  104. package/dist/nodes/lib-charts.d.ts +0 -20
  105. package/dist/nodes/lib-charts.d.ts.map +0 -1
  106. package/dist/nodes/lib-charts.js +0 -245
  107. package/dist/nodes/lib-charts.js.map +0 -1
  108. package/dist/nodes/lib-datetime.d.ts +0 -108
  109. package/dist/nodes/lib-datetime.d.ts.map +0 -1
  110. package/dist/nodes/lib-datetime.js +0 -421
  111. package/dist/nodes/lib-datetime.js.map +0 -1
  112. package/dist/nodes/lib-doc-convert.d.ts +0 -19
  113. package/dist/nodes/lib-doc-convert.d.ts.map +0 -1
  114. package/dist/nodes/lib-doc-convert.js +0 -257
  115. package/dist/nodes/lib-doc-convert.js.map +0 -1
  116. package/dist/nodes/lib-doc-transform.d.ts +0 -39
  117. package/dist/nodes/lib-doc-transform.d.ts.map +0 -1
  118. package/dist/nodes/lib-doc-transform.js +0 -335
  119. package/dist/nodes/lib-doc-transform.js.map +0 -1
  120. package/dist/nodes/lib-docx.d.ts +0 -106
  121. package/dist/nodes/lib-docx.d.ts.map +0 -1
  122. package/dist/nodes/lib-docx.js +0 -595
  123. package/dist/nodes/lib-docx.js.map +0 -1
  124. package/dist/nodes/lib-epub.d.ts +0 -55
  125. package/dist/nodes/lib-epub.d.ts.map +0 -1
  126. package/dist/nodes/lib-epub.js +0 -220
  127. package/dist/nodes/lib-epub.js.map +0 -1
  128. package/dist/nodes/lib-excel.d.ts +0 -74
  129. package/dist/nodes/lib-excel.d.ts.map +0 -1
  130. package/dist/nodes/lib-excel.js +0 -448
  131. package/dist/nodes/lib-excel.js.map +0 -1
  132. package/dist/nodes/lib-graphql.d.ts +0 -66
  133. package/dist/nodes/lib-graphql.d.ts.map +0 -1
  134. package/dist/nodes/lib-graphql.js +0 -333
  135. package/dist/nodes/lib-graphql.js.map +0 -1
  136. package/dist/nodes/lib-grid.d.ts +0 -27
  137. package/dist/nodes/lib-grid.d.ts.map +0 -1
  138. package/dist/nodes/lib-grid.js +0 -172
  139. package/dist/nodes/lib-grid.js.map +0 -1
  140. package/dist/nodes/lib-html-parse.d.ts +0 -108
  141. package/dist/nodes/lib-html-parse.d.ts.map +0 -1
  142. package/dist/nodes/lib-html-parse.js +0 -365
  143. package/dist/nodes/lib-html-parse.js.map +0 -1
  144. package/dist/nodes/lib-http.d.ts +0 -98
  145. package/dist/nodes/lib-http.d.ts.map +0 -1
  146. package/dist/nodes/lib-http.js +0 -267
  147. package/dist/nodes/lib-http.js.map +0 -1
  148. package/dist/nodes/lib-image-color-grading.d.ts +0 -3
  149. package/dist/nodes/lib-image-color-grading.d.ts.map +0 -1
  150. package/dist/nodes/lib-image-color-grading.js +0 -1386
  151. package/dist/nodes/lib-image-color-grading.js.map +0 -1
  152. package/dist/nodes/lib-image-draw.d.ts +0 -3
  153. package/dist/nodes/lib-image-draw.d.ts.map +0 -1
  154. package/dist/nodes/lib-image-draw.js +0 -470
  155. package/dist/nodes/lib-image-draw.js.map +0 -1
  156. package/dist/nodes/lib-image-enhance.d.ts +0 -3
  157. package/dist/nodes/lib-image-enhance.d.ts.map +0 -1
  158. package/dist/nodes/lib-image-enhance.js +0 -731
  159. package/dist/nodes/lib-image-enhance.js.map +0 -1
  160. package/dist/nodes/lib-image-filter.d.ts +0 -3
  161. package/dist/nodes/lib-image-filter.d.ts.map +0 -1
  162. package/dist/nodes/lib-image-filter.js +0 -684
  163. package/dist/nodes/lib-image-filter.js.map +0 -1
  164. package/dist/nodes/lib-image-utils.d.ts +0 -24
  165. package/dist/nodes/lib-image-utils.d.ts.map +0 -1
  166. package/dist/nodes/lib-image-utils.js +0 -150
  167. package/dist/nodes/lib-image-utils.js.map +0 -1
  168. package/dist/nodes/lib-mail.d.ts +0 -74
  169. package/dist/nodes/lib-mail.d.ts.map +0 -1
  170. package/dist/nodes/lib-mail.js +0 -452
  171. package/dist/nodes/lib-mail.js.map +0 -1
  172. package/dist/nodes/lib-markdown.d.ts +0 -70
  173. package/dist/nodes/lib-markdown.d.ts.map +0 -1
  174. package/dist/nodes/lib-markdown.js +0 -236
  175. package/dist/nodes/lib-markdown.js.map +0 -1
  176. package/dist/nodes/lib-nlp.d.ts +0 -98
  177. package/dist/nodes/lib-nlp.d.ts.map +0 -1
  178. package/dist/nodes/lib-nlp.js +0 -374
  179. package/dist/nodes/lib-nlp.js.map +0 -1
  180. package/dist/nodes/lib-notion.d.ts +0 -99
  181. package/dist/nodes/lib-notion.d.ts.map +0 -1
  182. package/dist/nodes/lib-notion.js +0 -601
  183. package/dist/nodes/lib-notion.js.map +0 -1
  184. package/dist/nodes/lib-ocr.d.ts +0 -31
  185. package/dist/nodes/lib-ocr.d.ts.map +0 -1
  186. package/dist/nodes/lib-ocr.js +0 -143
  187. package/dist/nodes/lib-ocr.js.map +0 -1
  188. package/dist/nodes/lib-os.d.ts +0 -312
  189. package/dist/nodes/lib-os.d.ts.map +0 -1
  190. package/dist/nodes/lib-os.js +0 -909
  191. package/dist/nodes/lib-os.js.map +0 -1
  192. package/dist/nodes/lib-pdf.d.ts +0 -142
  193. package/dist/nodes/lib-pdf.d.ts.map +0 -1
  194. package/dist/nodes/lib-pdf.js +0 -871
  195. package/dist/nodes/lib-pdf.js.map +0 -1
  196. package/dist/nodes/lib-pptx.d.ts +0 -29
  197. package/dist/nodes/lib-pptx.d.ts.map +0 -1
  198. package/dist/nodes/lib-pptx.js +0 -122
  199. package/dist/nodes/lib-pptx.js.map +0 -1
  200. package/dist/nodes/lib-rss.d.ts +0 -31
  201. package/dist/nodes/lib-rss.d.ts.map +0 -1
  202. package/dist/nodes/lib-rss.js +0 -163
  203. package/dist/nodes/lib-rss.js.map +0 -1
  204. package/dist/nodes/lib-s3.d.ts +0 -112
  205. package/dist/nodes/lib-s3.d.ts.map +0 -1
  206. package/dist/nodes/lib-s3.js +0 -521
  207. package/dist/nodes/lib-s3.js.map +0 -1
  208. package/dist/nodes/lib-secret.d.ts +0 -15
  209. package/dist/nodes/lib-secret.d.ts.map +0 -1
  210. package/dist/nodes/lib-secret.js +0 -42
  211. package/dist/nodes/lib-secret.js.map +0 -1
  212. package/dist/nodes/lib-sqlite.d.ts +0 -102
  213. package/dist/nodes/lib-sqlite.d.ts.map +0 -1
  214. package/dist/nodes/lib-sqlite.js +0 -532
  215. package/dist/nodes/lib-sqlite.js.map +0 -1
  216. package/dist/nodes/lib-supabase.d.ts +0 -86
  217. package/dist/nodes/lib-supabase.d.ts.map +0 -1
  218. package/dist/nodes/lib-supabase.js +0 -425
  219. package/dist/nodes/lib-supabase.js.map +0 -1
  220. package/dist/nodes/lib-svg.d.ts +0 -199
  221. package/dist/nodes/lib-svg.d.ts.map +0 -1
  222. package/dist/nodes/lib-svg.js +0 -1027
  223. package/dist/nodes/lib-svg.js.map +0 -1
  224. package/dist/nodes/lib-tensorflow.d.ts +0 -71
  225. package/dist/nodes/lib-tensorflow.d.ts.map +0 -1
  226. package/dist/nodes/lib-tensorflow.js +0 -351
  227. package/dist/nodes/lib-tensorflow.js.map +0 -1
  228. package/dist/nodes/lib-twilio.d.ts +0 -71
  229. package/dist/nodes/lib-twilio.d.ts.map +0 -1
  230. package/dist/nodes/lib-twilio.js +0 -469
  231. package/dist/nodes/lib-twilio.js.map +0 -1
  232. package/dist/nodes/lib-validate.d.ts +0 -84
  233. package/dist/nodes/lib-validate.d.ts.map +0 -1
  234. package/dist/nodes/lib-validate.js +0 -215
  235. package/dist/nodes/lib-validate.js.map +0 -1
  236. package/dist/nodes/lib-video-download.d.ts +0 -26
  237. package/dist/nodes/lib-video-download.d.ts.map +0 -1
  238. package/dist/nodes/lib-video-download.js +0 -299
  239. package/dist/nodes/lib-video-download.js.map +0 -1
  240. package/dist/nodes/messaging.d.ts +0 -88
  241. package/dist/nodes/messaging.d.ts.map +0 -1
  242. package/dist/nodes/messaging.js +0 -418
  243. package/dist/nodes/messaging.js.map +0 -1
  244. package/dist/nodes/mistral.d.ts +0 -83
  245. package/dist/nodes/mistral.d.ts.map +0 -1
  246. package/dist/nodes/mistral.js +0 -450
  247. package/dist/nodes/mistral.js.map +0 -1
  248. package/dist/nodes/model3d/analysis.d.ts +0 -13
  249. package/dist/nodes/model3d/analysis.d.ts.map +0 -1
  250. package/dist/nodes/model3d/analysis.js +0 -36
  251. package/dist/nodes/model3d/analysis.js.map +0 -1
  252. package/dist/nodes/model3d/base.d.ts +0 -11
  253. package/dist/nodes/model3d/base.d.ts.map +0 -1
  254. package/dist/nodes/model3d/base.js +0 -20
  255. package/dist/nodes/model3d/base.js.map +0 -1
  256. package/dist/nodes/model3d/boolean-ops.d.ts +0 -2
  257. package/dist/nodes/model3d/boolean-ops.d.ts.map +0 -1
  258. package/dist/nodes/model3d/boolean-ops.js +0 -172
  259. package/dist/nodes/model3d/boolean-ops.js.map +0 -1
  260. package/dist/nodes/model3d/cleanup.d.ts +0 -47
  261. package/dist/nodes/model3d/cleanup.d.ts.map +0 -1
  262. package/dist/nodes/model3d/cleanup.js +0 -136
  263. package/dist/nodes/model3d/cleanup.js.map +0 -1
  264. package/dist/nodes/model3d/convert.d.ts +0 -14
  265. package/dist/nodes/model3d/convert.d.ts.map +0 -1
  266. package/dist/nodes/model3d/convert.js +0 -64
  267. package/dist/nodes/model3d/convert.js.map +0 -1
  268. package/dist/nodes/model3d/defaults.d.ts +0 -34
  269. package/dist/nodes/model3d/defaults.d.ts.map +0 -1
  270. package/dist/nodes/model3d/defaults.js +0 -28
  271. package/dist/nodes/model3d/defaults.js.map +0 -1
  272. package/dist/nodes/model3d/document-ops.d.ts +0 -4
  273. package/dist/nodes/model3d/document-ops.d.ts.map +0 -1
  274. package/dist/nodes/model3d/document-ops.js +0 -40
  275. package/dist/nodes/model3d/document-ops.js.map +0 -1
  276. package/dist/nodes/model3d/generation.d.ts +0 -39
  277. package/dist/nodes/model3d/generation.d.ts.map +0 -1
  278. package/dist/nodes/model3d/generation.js +0 -179
  279. package/dist/nodes/model3d/generation.js.map +0 -1
  280. package/dist/nodes/model3d/glb.d.ts +0 -29
  281. package/dist/nodes/model3d/glb.d.ts.map +0 -1
  282. package/dist/nodes/model3d/glb.js +0 -255
  283. package/dist/nodes/model3d/glb.js.map +0 -1
  284. package/dist/nodes/model3d/index.d.ts +0 -20
  285. package/dist/nodes/model3d/index.d.ts.map +0 -1
  286. package/dist/nodes/model3d/index.js +0 -38
  287. package/dist/nodes/model3d/index.js.map +0 -1
  288. package/dist/nodes/model3d/io.d.ts +0 -38
  289. package/dist/nodes/model3d/io.d.ts.map +0 -1
  290. package/dist/nodes/model3d/io.js +0 -162
  291. package/dist/nodes/model3d/io.js.map +0 -1
  292. package/dist/nodes/model3d/mesh-ops.d.ts +0 -40
  293. package/dist/nodes/model3d/mesh-ops.d.ts.map +0 -1
  294. package/dist/nodes/model3d/mesh-ops.js +0 -775
  295. package/dist/nodes/model3d/mesh-ops.js.map +0 -1
  296. package/dist/nodes/model3d/nodes.d.ts +0 -2
  297. package/dist/nodes/model3d/nodes.d.ts.map +0 -1
  298. package/dist/nodes/model3d/nodes.js +0 -2
  299. package/dist/nodes/model3d/nodes.js.map +0 -1
  300. package/dist/nodes/model3d/ops.d.ts +0 -37
  301. package/dist/nodes/model3d/ops.d.ts.map +0 -1
  302. package/dist/nodes/model3d/ops.js +0 -137
  303. package/dist/nodes/model3d/ops.js.map +0 -1
  304. package/dist/nodes/model3d/transforms.d.ts +0 -49
  305. package/dist/nodes/model3d/transforms.d.ts.map +0 -1
  306. package/dist/nodes/model3d/transforms.js +0 -142
  307. package/dist/nodes/model3d/transforms.js.map +0 -1
  308. package/dist/nodes/model3d/types.d.ts +0 -81
  309. package/dist/nodes/model3d/types.d.ts.map +0 -1
  310. package/dist/nodes/model3d/types.js +0 -2
  311. package/dist/nodes/model3d/types.js.map +0 -1
  312. package/dist/nodes/model3d/utils.d.ts +0 -34
  313. package/dist/nodes/model3d/utils.d.ts.map +0 -1
  314. package/dist/nodes/model3d/utils.js +0 -185
  315. package/dist/nodes/model3d/utils.js.map +0 -1
  316. package/dist/nodes/model3d.d.ts +0 -2
  317. package/dist/nodes/model3d.d.ts.map +0 -1
  318. package/dist/nodes/model3d.js +0 -2
  319. package/dist/nodes/model3d.js.map +0 -1
  320. package/dist/nodes/openai.d.ts +0 -172
  321. package/dist/nodes/openai.d.ts.map +0 -1
  322. package/dist/nodes/openai.js +0 -1294
  323. package/dist/nodes/openai.js.map +0 -1
  324. package/dist/nodes/output.d.ts +0 -29
  325. package/dist/nodes/output.d.ts.map +0 -1
  326. package/dist/nodes/output.js +0 -87
  327. package/dist/nodes/output.js.map +0 -1
  328. package/dist/nodes/sandbox.d.ts +0 -63
  329. package/dist/nodes/sandbox.d.ts.map +0 -1
  330. package/dist/nodes/sandbox.js +0 -633
  331. package/dist/nodes/sandbox.js.map +0 -1
  332. package/dist/nodes/search.d.ts +0 -117
  333. package/dist/nodes/search.d.ts.map +0 -1
  334. package/dist/nodes/search.js +0 -607
  335. package/dist/nodes/search.js.map +0 -1
  336. package/dist/nodes/team.d.ts +0 -74
  337. package/dist/nodes/team.d.ts.map +0 -1
  338. package/dist/nodes/team.js +0 -309
  339. package/dist/nodes/team.js.map +0 -1
  340. package/dist/nodes/text-extra.d.ts +0 -633
  341. package/dist/nodes/text-extra.d.ts.map +0 -1
  342. package/dist/nodes/text-extra.js +0 -2226
  343. package/dist/nodes/text-extra.js.map +0 -1
  344. package/dist/nodes/tool-agents.d.ts +0 -334
  345. package/dist/nodes/tool-agents.d.ts.map +0 -1
  346. package/dist/nodes/tool-agents.js +0 -1861
  347. package/dist/nodes/tool-agents.js.map +0 -1
  348. package/dist/nodes/triggers.d.ts +0 -107
  349. package/dist/nodes/triggers.d.ts.map +0 -1
  350. package/dist/nodes/triggers.js +0 -706
  351. package/dist/nodes/triggers.js.map +0 -1
  352. package/dist/nodes/vector.d.ts +0 -168
  353. package/dist/nodes/vector.d.ts.map +0 -1
  354. package/dist/nodes/vector.js +0 -1121
  355. package/dist/nodes/vector.js.map +0 -1
  356. package/dist/nodes/video.d.ts +0 -387
  357. package/dist/nodes/video.d.ts.map +0 -1
  358. package/dist/nodes/video.js +0 -2383
  359. package/dist/nodes/video.js.map +0 -1
  360. package/dist/nodes/workflow.d.ts +0 -24
  361. package/dist/nodes/workflow.d.ts.map +0 -1
  362. package/dist/nodes/workflow.js +0 -146
  363. package/dist/nodes/workflow.js.map +0 -1
  364. package/dist/nodes/workspace.d.ts +0 -203
  365. package/dist/nodes/workspace.d.ts.map +0 -1
  366. package/dist/nodes/workspace.js +0 -795
  367. package/dist/nodes/workspace.js.map +0 -1
@@ -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