@bubblelab/bubble-core 0.1.10 → 0.1.11

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 (298) hide show
  1. package/dist/bubble-bundle.d.ts +759 -350
  2. package/dist/bubble-factory.d.ts.map +1 -1
  3. package/dist/bubble-factory.js +125 -35
  4. package/dist/bubble-factory.js.map +1 -1
  5. package/dist/bubble-flow/bubble-flow-class.d.ts +5 -0
  6. package/dist/bubble-flow/bubble-flow-class.d.ts.map +1 -1
  7. package/dist/bubble-flow/bubble-flow-class.js +20 -0
  8. package/dist/bubble-flow/bubble-flow-class.js.map +1 -1
  9. package/dist/bubble-flow/sample/simplified-data-analysis.flow.d.ts.map +1 -1
  10. package/dist/bubble-flow/sample/simplified-data-analysis.flow.js +6 -3
  11. package/dist/bubble-flow/sample/simplified-data-analysis.flow.js.map +1 -1
  12. package/dist/bubbles/service-bubble/agi-inc.d.ts +1121 -0
  13. package/dist/bubbles/service-bubble/agi-inc.d.ts.map +1 -0
  14. package/dist/bubbles/service-bubble/agi-inc.js +730 -0
  15. package/dist/bubbles/service-bubble/agi-inc.js.map +1 -0
  16. package/dist/bubbles/service-bubble/ai-agent.d.ts +277 -65
  17. package/dist/bubbles/service-bubble/ai-agent.d.ts.map +1 -1
  18. package/dist/bubbles/service-bubble/ai-agent.js +533 -399
  19. package/dist/bubbles/service-bubble/ai-agent.js.map +1 -1
  20. package/dist/bubbles/service-bubble/airtable.d.ts +1753 -0
  21. package/dist/bubbles/service-bubble/airtable.d.ts.map +1 -0
  22. package/dist/bubbles/service-bubble/airtable.js +1173 -0
  23. package/dist/bubbles/service-bubble/airtable.js.map +1 -0
  24. package/dist/bubbles/service-bubble/apify/actors/google-maps-scraper.d.ts +240 -0
  25. package/dist/bubbles/service-bubble/apify/actors/google-maps-scraper.d.ts.map +1 -0
  26. package/dist/bubbles/service-bubble/apify/actors/google-maps-scraper.js +119 -0
  27. package/dist/bubbles/service-bubble/apify/actors/google-maps-scraper.js.map +1 -0
  28. package/dist/bubbles/service-bubble/apify/actors/instagram-hashtag-scraper.d.ts +4 -4
  29. package/dist/bubbles/service-bubble/apify/actors/instagram-scraper.d.ts +14 -14
  30. package/dist/bubbles/service-bubble/apify/actors/linkedin-jobs-scraper.d.ts +137 -0
  31. package/dist/bubbles/service-bubble/apify/actors/linkedin-jobs-scraper.d.ts.map +1 -0
  32. package/dist/bubbles/service-bubble/apify/actors/linkedin-jobs-scraper.js +81 -0
  33. package/dist/bubbles/service-bubble/apify/actors/linkedin-jobs-scraper.js.map +1 -0
  34. package/dist/bubbles/service-bubble/apify/actors/linkedin-posts-search.d.ts +6 -6
  35. package/dist/bubbles/service-bubble/apify/actors/linkedin-profile-posts.d.ts +32 -32
  36. package/dist/bubbles/service-bubble/apify/actors/tiktok-scraper.d.ts +488 -0
  37. package/dist/bubbles/service-bubble/apify/actors/tiktok-scraper.d.ts.map +1 -0
  38. package/dist/bubbles/service-bubble/apify/actors/tiktok-scraper.js +463 -0
  39. package/dist/bubbles/service-bubble/apify/actors/tiktok-scraper.js.map +1 -0
  40. package/dist/bubbles/service-bubble/apify/actors/twitter-scraper.d.ts +262 -0
  41. package/dist/bubbles/service-bubble/apify/actors/twitter-scraper.d.ts.map +1 -0
  42. package/dist/bubbles/service-bubble/apify/actors/twitter-scraper.js +291 -0
  43. package/dist/bubbles/service-bubble/apify/actors/twitter-scraper.js.map +1 -0
  44. package/dist/bubbles/service-bubble/apify/actors/youtube-scraper.d.ts +12 -12
  45. package/dist/bubbles/service-bubble/apify/apify-scraper.schema.d.ts +1301 -170
  46. package/dist/bubbles/service-bubble/apify/apify-scraper.schema.d.ts.map +1 -1
  47. package/dist/bubbles/service-bubble/apify/apify-scraper.schema.js +32 -0
  48. package/dist/bubbles/service-bubble/apify/apify-scraper.schema.js.map +1 -1
  49. package/dist/bubbles/service-bubble/apify/apify.d.ts +162 -17
  50. package/dist/bubbles/service-bubble/apify/apify.d.ts.map +1 -1
  51. package/dist/bubbles/service-bubble/apify/apify.js +205 -32
  52. package/dist/bubbles/service-bubble/apify/apify.js.map +1 -1
  53. package/dist/bubbles/service-bubble/eleven-labs.d.ts +421 -0
  54. package/dist/bubbles/service-bubble/eleven-labs.d.ts.map +1 -0
  55. package/dist/bubbles/service-bubble/eleven-labs.js +479 -0
  56. package/dist/bubbles/service-bubble/eleven-labs.js.map +1 -0
  57. package/dist/bubbles/service-bubble/firecrawl.d.ts +37748 -0
  58. package/dist/bubbles/service-bubble/firecrawl.d.ts.map +1 -0
  59. package/dist/bubbles/service-bubble/firecrawl.js +1489 -0
  60. package/dist/bubbles/service-bubble/firecrawl.js.map +1 -0
  61. package/dist/bubbles/service-bubble/followupboss.d.ts +6822 -0
  62. package/dist/bubbles/service-bubble/followupboss.d.ts.map +1 -0
  63. package/dist/bubbles/service-bubble/followupboss.js +1394 -0
  64. package/dist/bubbles/service-bubble/followupboss.js.map +1 -0
  65. package/dist/bubbles/service-bubble/github.d.ts +2399 -0
  66. package/dist/bubbles/service-bubble/github.d.ts.map +1 -0
  67. package/dist/bubbles/service-bubble/github.js +1052 -0
  68. package/dist/bubbles/service-bubble/github.js.map +1 -0
  69. package/dist/bubbles/service-bubble/gmail.d.ts +180 -180
  70. package/dist/bubbles/service-bubble/google-calendar.d.ts +60 -60
  71. package/dist/bubbles/service-bubble/google-drive.d.ts +37 -36
  72. package/dist/bubbles/service-bubble/google-drive.d.ts.map +1 -1
  73. package/dist/bubbles/service-bubble/google-drive.js +35 -3
  74. package/dist/bubbles/service-bubble/google-drive.js.map +1 -1
  75. package/dist/bubbles/service-bubble/google-sheets/google-sheets.d.ts +943 -0
  76. package/dist/bubbles/service-bubble/google-sheets/google-sheets.d.ts.map +1 -0
  77. package/dist/bubbles/service-bubble/google-sheets/google-sheets.integration.flow.d.ts +31 -0
  78. package/dist/bubbles/service-bubble/google-sheets/google-sheets.integration.flow.d.ts.map +1 -0
  79. package/dist/bubbles/service-bubble/google-sheets/google-sheets.integration.flow.js +184 -0
  80. package/dist/bubbles/service-bubble/google-sheets/google-sheets.integration.flow.js.map +1 -0
  81. package/dist/bubbles/service-bubble/google-sheets/google-sheets.js +401 -0
  82. package/dist/bubbles/service-bubble/google-sheets/google-sheets.js.map +1 -0
  83. package/dist/bubbles/service-bubble/google-sheets/google-sheets.schema.d.ts +1024 -0
  84. package/dist/bubbles/service-bubble/google-sheets/google-sheets.schema.d.ts.map +1 -0
  85. package/dist/bubbles/service-bubble/{google-sheets.js → google-sheets/google-sheets.schema.js} +45 -409
  86. package/dist/bubbles/service-bubble/google-sheets/google-sheets.schema.js.map +1 -0
  87. package/dist/bubbles/service-bubble/google-sheets/google-sheets.utils.d.ts +38 -0
  88. package/dist/bubbles/service-bubble/google-sheets/google-sheets.utils.d.ts.map +1 -0
  89. package/dist/bubbles/service-bubble/google-sheets/google-sheets.utils.js +183 -0
  90. package/dist/bubbles/service-bubble/google-sheets/google-sheets.utils.js.map +1 -0
  91. package/dist/bubbles/service-bubble/google-sheets/index.d.ts +4 -0
  92. package/dist/bubbles/service-bubble/google-sheets/index.d.ts.map +1 -0
  93. package/dist/bubbles/service-bubble/google-sheets/index.js +4 -0
  94. package/dist/bubbles/service-bubble/google-sheets/index.js.map +1 -0
  95. package/dist/bubbles/service-bubble/hello-world.d.ts +4 -4
  96. package/dist/bubbles/service-bubble/http.d.ts +4 -4
  97. package/dist/bubbles/service-bubble/http.d.ts.map +1 -1
  98. package/dist/bubbles/service-bubble/http.js +7 -1
  99. package/dist/bubbles/service-bubble/http.js.map +1 -1
  100. package/dist/bubbles/service-bubble/insforge-db.d.ts +140 -0
  101. package/dist/bubbles/service-bubble/insforge-db.d.ts.map +1 -0
  102. package/dist/bubbles/service-bubble/insforge-db.js +260 -0
  103. package/dist/bubbles/service-bubble/insforge-db.js.map +1 -0
  104. package/dist/bubbles/service-bubble/notion/index.d.ts +3 -0
  105. package/dist/bubbles/service-bubble/notion/index.d.ts.map +1 -0
  106. package/dist/bubbles/service-bubble/notion/index.js +3 -0
  107. package/dist/bubbles/service-bubble/notion/index.js.map +1 -0
  108. package/dist/bubbles/service-bubble/notion/notion.d.ts +35405 -0
  109. package/dist/bubbles/service-bubble/notion/notion.d.ts.map +1 -0
  110. package/dist/bubbles/service-bubble/notion/notion.js +1492 -0
  111. package/dist/bubbles/service-bubble/notion/notion.js.map +1 -0
  112. package/dist/bubbles/service-bubble/notion/property-schemas.d.ts +1148 -0
  113. package/dist/bubbles/service-bubble/notion/property-schemas.d.ts.map +1 -0
  114. package/dist/bubbles/service-bubble/notion/property-schemas.js +341 -0
  115. package/dist/bubbles/service-bubble/notion/property-schemas.js.map +1 -0
  116. package/dist/bubbles/service-bubble/postgresql.d.ts +12 -12
  117. package/dist/bubbles/service-bubble/resend.d.ts +34 -13
  118. package/dist/bubbles/service-bubble/resend.d.ts.map +1 -1
  119. package/dist/bubbles/service-bubble/resend.js +133 -2
  120. package/dist/bubbles/service-bubble/resend.js.map +1 -1
  121. package/dist/bubbles/service-bubble/slack.d.ts +241 -241
  122. package/dist/bubbles/service-bubble/slack.d.ts.map +1 -1
  123. package/dist/bubbles/service-bubble/slack.js +2 -2
  124. package/dist/bubbles/service-bubble/slack.js.map +1 -1
  125. package/dist/bubbles/service-bubble/storage.d.ts +25 -21
  126. package/dist/bubbles/service-bubble/storage.d.ts.map +1 -1
  127. package/dist/bubbles/service-bubble/storage.js +43 -2
  128. package/dist/bubbles/service-bubble/storage.js.map +1 -1
  129. package/dist/bubbles/service-bubble/telegram.d.ts +7742 -0
  130. package/dist/bubbles/service-bubble/telegram.d.ts.map +1 -0
  131. package/dist/bubbles/service-bubble/telegram.js +1132 -0
  132. package/dist/bubbles/service-bubble/telegram.js.map +1 -0
  133. package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.d.ts +76 -20
  134. package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.d.ts.map +1 -1
  135. package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.js +12 -0
  136. package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.js.map +1 -1
  137. package/dist/bubbles/tool-bubble/chart-js-tool.d.ts +14 -14
  138. package/dist/bubbles/tool-bubble/code-edit-tool.d.ts +188 -0
  139. package/dist/bubbles/tool-bubble/code-edit-tool.d.ts.map +1 -0
  140. package/dist/bubbles/tool-bubble/code-edit-tool.js +321 -0
  141. package/dist/bubbles/tool-bubble/code-edit-tool.js.map +1 -0
  142. package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts +8 -4
  143. package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts.map +1 -1
  144. package/dist/bubbles/tool-bubble/get-bubble-details-tool.js +115 -10
  145. package/dist/bubbles/tool-bubble/get-bubble-details-tool.js.map +1 -1
  146. package/dist/bubbles/tool-bubble/google-maps-tool.d.ts +455 -0
  147. package/dist/bubbles/tool-bubble/google-maps-tool.d.ts.map +1 -0
  148. package/dist/bubbles/tool-bubble/google-maps-tool.js +205 -0
  149. package/dist/bubbles/tool-bubble/google-maps-tool.js.map +1 -0
  150. package/dist/bubbles/tool-bubble/instagram-tool.d.ts +36 -36
  151. package/dist/bubbles/tool-bubble/instagram-tool.d.ts.map +1 -1
  152. package/dist/bubbles/tool-bubble/instagram-tool.js +2 -2
  153. package/dist/bubbles/tool-bubble/instagram-tool.js.map +1 -1
  154. package/dist/bubbles/tool-bubble/linkedin-tool.d.ts +824 -447
  155. package/dist/bubbles/tool-bubble/linkedin-tool.d.ts.map +1 -1
  156. package/dist/bubbles/tool-bubble/linkedin-tool.js +232 -12
  157. package/dist/bubbles/tool-bubble/linkedin-tool.js.map +1 -1
  158. package/dist/bubbles/tool-bubble/list-bubbles-tool.d.ts +4 -4
  159. package/dist/bubbles/tool-bubble/reddit-scrape-tool.d.ts +66 -66
  160. package/dist/bubbles/tool-bubble/research-agent-tool.d.ts +17 -16
  161. package/dist/bubbles/tool-bubble/research-agent-tool.d.ts.map +1 -1
  162. package/dist/bubbles/tool-bubble/research-agent-tool.js +26 -16
  163. package/dist/bubbles/tool-bubble/research-agent-tool.js.map +1 -1
  164. package/dist/bubbles/tool-bubble/sql-query-tool.d.ts +8 -8
  165. package/dist/bubbles/tool-bubble/tiktok-tool.d.ts +485 -0
  166. package/dist/bubbles/tool-bubble/tiktok-tool.d.ts.map +1 -0
  167. package/dist/bubbles/tool-bubble/tiktok-tool.js +226 -0
  168. package/dist/bubbles/tool-bubble/tiktok-tool.js.map +1 -0
  169. package/dist/bubbles/tool-bubble/tool-template.d.ts +8 -8
  170. package/dist/bubbles/tool-bubble/twitter-tool.d.ts +947 -0
  171. package/dist/bubbles/tool-bubble/twitter-tool.d.ts.map +1 -0
  172. package/dist/bubbles/tool-bubble/twitter-tool.js +494 -0
  173. package/dist/bubbles/tool-bubble/twitter-tool.js.map +1 -0
  174. package/dist/bubbles/tool-bubble/web-crawl-tool.d.ts +22 -16
  175. package/dist/bubbles/tool-bubble/web-crawl-tool.d.ts.map +1 -1
  176. package/dist/bubbles/tool-bubble/web-crawl-tool.js +58 -59
  177. package/dist/bubbles/tool-bubble/web-crawl-tool.js.map +1 -1
  178. package/dist/bubbles/tool-bubble/web-extract-tool.d.ts +8 -8
  179. package/dist/bubbles/tool-bubble/web-extract-tool.d.ts.map +1 -1
  180. package/dist/bubbles/tool-bubble/web-extract-tool.js +17 -17
  181. package/dist/bubbles/tool-bubble/web-extract-tool.js.map +1 -1
  182. package/dist/bubbles/tool-bubble/web-scrape-tool.d.ts +15 -107
  183. package/dist/bubbles/tool-bubble/web-scrape-tool.d.ts.map +1 -1
  184. package/dist/bubbles/tool-bubble/web-scrape-tool.js +51 -72
  185. package/dist/bubbles/tool-bubble/web-scrape-tool.js.map +1 -1
  186. package/dist/bubbles/tool-bubble/web-search-tool.d.ts +20 -9
  187. package/dist/bubbles/tool-bubble/web-search-tool.d.ts.map +1 -1
  188. package/dist/bubbles/tool-bubble/web-search-tool.js +45 -35
  189. package/dist/bubbles/tool-bubble/web-search-tool.js.map +1 -1
  190. package/dist/bubbles/tool-bubble/youtube-tool.d.ts +25 -25
  191. package/dist/bubbles/tool-bubble/youtube-tool.d.ts.map +1 -1
  192. package/dist/bubbles/tool-bubble/youtube-tool.js +5 -5
  193. package/dist/bubbles/tool-bubble/youtube-tool.js.map +1 -1
  194. package/dist/bubbles/workflow-bubble/database-analyzer.workflow.d.ts +4 -4
  195. package/dist/bubbles/workflow-bubble/generate-document.workflow.d.ts +30 -30
  196. package/dist/bubbles/workflow-bubble/generate-document.workflow.js +1 -1
  197. package/dist/bubbles/workflow-bubble/parse-document.workflow.d.ts +22 -22
  198. package/dist/bubbles/workflow-bubble/parse-document.workflow.js +1 -1
  199. package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.d.ts +54 -54
  200. package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.d.ts.map +1 -1
  201. package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.js +4 -4
  202. package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.js.map +1 -1
  203. package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.d.ts +36 -36
  204. package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.js +1 -1
  205. package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts +14 -14
  206. package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts.map +1 -1
  207. package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.js +6 -6
  208. package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.js.map +1 -1
  209. package/dist/bubbles/workflow-bubble/slack-formatter-agent.d.ts +26 -26
  210. package/dist/bubbles/workflow-bubble/slack-formatter-agent.d.ts.map +1 -1
  211. package/dist/bubbles/workflow-bubble/slack-formatter-agent.js +24 -4
  212. package/dist/bubbles/workflow-bubble/slack-formatter-agent.js.map +1 -1
  213. package/dist/bubbles/workflow-bubble/slack-notifier.workflow.d.ts +19 -19
  214. package/dist/bubbles/workflow-bubble/slack-notifier.workflow.d.ts.map +1 -1
  215. package/dist/bubbles/workflow-bubble/slack-notifier.workflow.js +8 -8
  216. package/dist/bubbles/workflow-bubble/slack-notifier.workflow.js.map +1 -1
  217. package/dist/bubbles.json +220 -72
  218. package/dist/index.d.ts +23 -4
  219. package/dist/index.d.ts.map +1 -1
  220. package/dist/index.js +16 -3
  221. package/dist/index.js.map +1 -1
  222. package/dist/logging/BubbleLogger.d.ts +45 -16
  223. package/dist/logging/BubbleLogger.d.ts.map +1 -1
  224. package/dist/logging/BubbleLogger.js +178 -77
  225. package/dist/logging/BubbleLogger.js.map +1 -1
  226. package/dist/logging/StreamingBubbleLogger.d.ts +13 -1
  227. package/dist/logging/StreamingBubbleLogger.d.ts.map +1 -1
  228. package/dist/logging/StreamingBubbleLogger.js +51 -8
  229. package/dist/logging/StreamingBubbleLogger.js.map +1 -1
  230. package/dist/logging/WebhookStreamLogger.d.ts +66 -0
  231. package/dist/logging/WebhookStreamLogger.d.ts.map +1 -0
  232. package/dist/logging/WebhookStreamLogger.js +291 -0
  233. package/dist/logging/WebhookStreamLogger.js.map +1 -0
  234. package/dist/types/available-tools.d.ts +1 -1
  235. package/dist/types/available-tools.d.ts.map +1 -1
  236. package/dist/types/available-tools.js +1 -0
  237. package/dist/types/available-tools.js.map +1 -1
  238. package/dist/types/base-bubble-class.d.ts +6 -4
  239. package/dist/types/base-bubble-class.d.ts.map +1 -1
  240. package/dist/types/base-bubble-class.js +35 -20
  241. package/dist/types/base-bubble-class.js.map +1 -1
  242. package/dist/types/bubble.d.ts +2 -0
  243. package/dist/types/bubble.d.ts.map +1 -1
  244. package/dist/types/service-bubble-class.d.ts +1 -1
  245. package/dist/types/service-bubble-class.d.ts.map +1 -1
  246. package/dist/types/service-bubble-class.js +2 -2
  247. package/dist/types/service-bubble-class.js.map +1 -1
  248. package/dist/types/tool-bubble-class.d.ts +1 -1
  249. package/dist/types/tool-bubble-class.d.ts.map +1 -1
  250. package/dist/types/tool-bubble-class.js +2 -2
  251. package/dist/types/tool-bubble-class.js.map +1 -1
  252. package/dist/types/workflow-bubble-class.d.ts +1 -1
  253. package/dist/types/workflow-bubble-class.d.ts.map +1 -1
  254. package/dist/types/workflow-bubble-class.js +2 -2
  255. package/dist/types/workflow-bubble-class.js.map +1 -1
  256. package/dist/utils/agent-formatter.d.ts +14 -2
  257. package/dist/utils/agent-formatter.d.ts.map +1 -1
  258. package/dist/utils/agent-formatter.js +174 -26
  259. package/dist/utils/agent-formatter.js.map +1 -1
  260. package/dist/utils/bubbleflow-validation.d.ts +7 -0
  261. package/dist/utils/bubbleflow-validation.d.ts.map +1 -1
  262. package/dist/utils/bubbleflow-validation.js +171 -6
  263. package/dist/utils/bubbleflow-validation.js.map +1 -1
  264. package/dist/utils/json-parsing.d.ts.map +1 -1
  265. package/dist/utils/json-parsing.js +146 -0
  266. package/dist/utils/json-parsing.js.map +1 -1
  267. package/dist/utils/safe-gemini-chat.d.ts +31 -0
  268. package/dist/utils/safe-gemini-chat.d.ts.map +1 -0
  269. package/dist/utils/safe-gemini-chat.js +93 -0
  270. package/dist/utils/safe-gemini-chat.js.map +1 -0
  271. package/dist/utils/schema-comparison.d.ts +92 -0
  272. package/dist/utils/schema-comparison.d.ts.map +1 -0
  273. package/dist/utils/schema-comparison.js +716 -0
  274. package/dist/utils/schema-comparison.js.map +1 -0
  275. package/dist/utils/zod-schema.d.ts +24 -0
  276. package/dist/utils/zod-schema.d.ts.map +1 -0
  277. package/dist/utils/zod-schema.js +56 -0
  278. package/dist/utils/zod-schema.js.map +1 -0
  279. package/package.json +5 -4
  280. package/dist/bubbles/service-bubble/google-sheets.d.ts +0 -1811
  281. package/dist/bubbles/service-bubble/google-sheets.d.ts.map +0 -1
  282. package/dist/bubbles/service-bubble/google-sheets.js.map +0 -1
  283. package/dist/bubbles/service-bubble/x-twitter.d.ts +0 -814
  284. package/dist/bubbles/service-bubble/x-twitter.d.ts.map +0 -1
  285. package/dist/bubbles/service-bubble/x-twitter.js +0 -445
  286. package/dist/bubbles/service-bubble/x-twitter.js.map +0 -1
  287. package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.d.ts +0 -125
  288. package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.d.ts.map +0 -1
  289. package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.js +0 -808
  290. package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.js.map +0 -1
  291. package/dist/test-gm.d.ts +0 -10
  292. package/dist/test-gm.d.ts.map +0 -1
  293. package/dist/test-gm.js +0 -95
  294. package/dist/test-gm.js.map +0 -1
  295. package/dist/utils/param-helper.d.ts +0 -2
  296. package/dist/utils/param-helper.d.ts.map +0 -1
  297. package/dist/utils/param-helper.js +0 -5
  298. package/dist/utils/param-helper.js.map +0 -1
@@ -1,20 +1,49 @@
1
1
  import { z } from 'zod';
2
2
  import { ServiceBubble } from '../../types/service-bubble-class.js';
3
- import { CredentialType, BUBBLE_CREDENTIAL_OPTIONS, } from '@bubblelab/shared-schemas';
3
+ import { CredentialType, BUBBLE_CREDENTIAL_OPTIONS, RECOMMENDED_MODELS, } from '@bubblelab/shared-schemas';
4
4
  import { StateGraph, MessagesAnnotation } from '@langchain/langgraph';
5
5
  import { ChatOpenAI } from '@langchain/openai';
6
6
  import { ChatAnthropic } from '@langchain/anthropic';
7
- import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
8
7
  import { HumanMessage, AIMessage, ToolMessage, AIMessageChunk, } from '@langchain/core/messages';
9
8
  import { DynamicStructuredTool } from '@langchain/core/tools';
10
9
  import { AvailableModels } from '@bubblelab/shared-schemas';
11
10
  import { AvailableTools, } from '../../types/available-tools.js';
12
11
  import { BubbleFactory } from '../../bubble-factory.js';
13
- import { extractAndStreamThinkingTokens, formatFinalResponse, } from '../../utils/agent-formatter.js';
12
+ import { ConversationMessageSchema } from '@bubblelab/shared-schemas';
13
+ import { extractAndStreamThinkingTokens, formatFinalResponse, generationsToMessageContent, } from '../../utils/agent-formatter.js';
14
14
  import { isAIMessage, isAIMessageChunk } from '@langchain/core/messages';
15
+ import { HarmBlockThreshold, HarmCategory } from '@google/generative-ai';
16
+ import { SafeGeminiChat } from '../../utils/safe-gemini-chat.js';
17
+ import { zodSchemaToJsonString, buildJsonSchemaInstruction, } from '../../utils/zod-schema.js';
18
+ // Define backup model configuration schema
19
+ const BackupModelConfigSchema = z.object({
20
+ model: AvailableModels.describe('Backup AI model to use if the primary model fails (format: provider/model-name).'),
21
+ temperature: z
22
+ .number()
23
+ .min(0)
24
+ .max(2)
25
+ .optional()
26
+ .describe('Temperature for backup model. If not specified, uses primary model temperature.'),
27
+ maxTokens: z
28
+ .number()
29
+ .positive()
30
+ .optional()
31
+ .describe('Max tokens for backup model. If not specified, uses primary model maxTokens.'),
32
+ reasoningEffort: z
33
+ .enum(['low', 'medium', 'high'])
34
+ .optional()
35
+ .describe('Reasoning effort for backup model. If not specified, uses primary model reasoningEffort.'),
36
+ maxRetries: z
37
+ .number()
38
+ .int()
39
+ .min(0)
40
+ .max(10)
41
+ .optional()
42
+ .describe('Max retries for backup model. If not specified, uses primary model maxRetries.'),
43
+ });
15
44
  // Define model configuration
16
45
  const ModelConfigSchema = z.object({
17
- model: AvailableModels.default('google/gemini-2.5-flash').describe('AI model to use (format: provider/model-name).'),
46
+ model: AvailableModels.describe('AI model to use (format: provider/model-name).'),
18
47
  temperature: z
19
48
  .number()
20
49
  .min(0)
@@ -27,10 +56,30 @@ const ModelConfigSchema = z.object({
27
56
  .optional()
28
57
  .default(12800)
29
58
  .describe('Maximum number of tokens to generate in response, keep at default of 40000 unless the response is expected to be certain length'),
59
+ reasoningEffort: z
60
+ .enum(['low', 'medium', 'high'])
61
+ .optional()
62
+ .describe('Reasoning effort for model. If not specified, uses primary model reasoningEffort.'),
63
+ maxRetries: z
64
+ .number()
65
+ .int()
66
+ .min(0)
67
+ .max(10)
68
+ .default(3)
69
+ .describe('Maximum number of retries for API calls (default: 3). Useful for handling transient errors like 503 Service Unavailable.'),
70
+ provider: z
71
+ .array(z.string())
72
+ .optional()
73
+ .describe('Providers for ai agent (open router only).'),
30
74
  jsonMode: z
31
75
  .boolean()
32
76
  .default(false)
33
- .describe('When true, strips markdown formatting and returns clean JSON response'),
77
+ .describe('When true, returns clean JSON response, you must provide the exact JSON schema in the system prompt'),
78
+ backupModel: BackupModelConfigSchema.default({
79
+ model: RECOMMENDED_MODELS.FAST,
80
+ })
81
+ .optional()
82
+ .describe('Backup model configuration to use if the primary model fails.'),
34
83
  });
35
84
  // Define tool configuration for pre-registered tools
36
85
  const ToolConfigSchema = z.object({
@@ -56,8 +105,11 @@ const CustomToolSchema = z.object({
56
105
  .min(1)
57
106
  .describe('Description of what the tool does - helps the AI know when to use it'),
58
107
  schema: z
59
- .record(z.string(), z.unknown())
60
- .describe('Zod schema object defining the tool parameters. Example: { amount: z.number().describe("Amount to calculate tax on"), rate: z.number().describe("Tax rate") }'),
108
+ .union([
109
+ z.record(z.string(), z.unknown()),
110
+ z.custom((val) => val && typeof val === 'object' && '_def' in val),
111
+ ])
112
+ .describe('Zod schema object defining the tool parameters. Can be either a plain object (e.g., { amount: z.number() }) or a Zod object directly (e.g., z.object({ amount: z.number() })).'),
61
113
  func: z
62
114
  .function()
63
115
  .args(z.record(z.string(), z.unknown()))
@@ -91,6 +143,11 @@ const ImageInputSchema = z.discriminatedUnion('type', [
91
143
  Base64ImageSchema,
92
144
  UrlImageSchema,
93
145
  ]);
146
+ // Schema for the expected JSON output structure - accepts either a Zod schema or a JSON schema string
147
+ const ExpectedOutputSchema = z.union([
148
+ z.custom((val) => val?._def !== undefined),
149
+ z.string(),
150
+ ]);
94
151
  // Define the parameters schema for the AI Agent bubble
95
152
  const AIAgentParamsSchema = z.object({
96
153
  message: z
@@ -101,6 +158,10 @@ const AIAgentParamsSchema = z.object({
101
158
  .array(ImageInputSchema)
102
159
  .default([])
103
160
  .describe('Array of base64 encoded images to include with the message (for multimodal AI models). Example: [{type: "base64", data: "base64...", mimeType: "image/png", description: "A beautiful image of a cat"}] or [{type: "url", url: "https://example.com/image.png", description: "A beautiful image of a cat"}]'),
161
+ conversationHistory: z
162
+ .array(ConversationMessageSchema)
163
+ .optional()
164
+ .describe('Previous conversation messages for multi-turn conversations. When provided, messages are sent as separate turns to enable KV cache optimization. Format: [{role: "user", content: "..."}, {role: "assistant", content: "..."}, ...]'),
104
165
  systemPrompt: z
105
166
  .string()
106
167
  .default('You are a helpful AI assistant')
@@ -111,21 +172,15 @@ const AIAgentParamsSchema = z.object({
111
172
  .optional()
112
173
  .describe('A friendly name for the AI agent'),
113
174
  model: ModelConfigSchema.default({
114
- model: 'google/gemini-2.5-flash',
115
- temperature: 0.7,
175
+ model: RECOMMENDED_MODELS.FAST,
176
+ temperature: 1,
116
177
  maxTokens: 50000,
178
+ maxRetries: 3,
117
179
  jsonMode: false,
118
- }).describe('AI model configuration including provider, temperature, and tokens. For model unless otherwise specified, use google/gemini-2.5-flash as default. Use google/gemini-2.5-flash-image-preview to edit and generate images.'),
180
+ }).describe('AI model configuration including provider, temperature, and tokens, retries, and json mode. Always include this.'),
119
181
  tools: z
120
182
  .array(ToolConfigSchema)
121
- .default([
122
- {
123
- name: 'web-search-tool',
124
- config: {
125
- maxResults: 5,
126
- },
127
- },
128
- ])
183
+ .default([])
129
184
  .describe('Array of pre-registered tools the AI agent can use. Can be tool types (web-search-tool, web-scrape-tool, web-crawl-tool, web-extract-tool, instagram-tool). If using image models, set the tools to []'),
130
185
  customTools: z
131
186
  .array(CustomToolSchema)
@@ -135,9 +190,9 @@ const AIAgentParamsSchema = z.object({
135
190
  maxIterations: z
136
191
  .number()
137
192
  .positive()
138
- .min(2)
139
- .default(10)
140
- .describe('Maximum number of iterations for the agent workflow, 2 iterations per turn of conversation'),
193
+ .min(4)
194
+ .default(40)
195
+ .describe('Maximum number of iterations for the agent workflow, 5 iterations per turn of conversation'),
141
196
  credentials: z
142
197
  .record(z.nativeEnum(CredentialType), z.string())
143
198
  .optional()
@@ -146,13 +201,14 @@ const AIAgentParamsSchema = z.object({
146
201
  .boolean()
147
202
  .default(false)
148
203
  .describe('Enable real-time streaming of tokens, tool calls, and iteration progress'),
204
+ expectedOutputSchema: ExpectedOutputSchema.optional().describe('Zod schema or JSON schema string that defines the expected structure of the AI response. When provided, automatically enables JSON mode and instructs the AI to output in the exact format. Example: z.object({ summary: z.string(), items: z.array(z.object({ name: z.string(), score: z.number() })) })'),
149
205
  // Note: beforeToolCall and afterToolCall are function hooks added via TypeScript interface
150
206
  // They cannot be part of the Zod schema but are available in the params
151
207
  });
152
208
  const AIAgentResultSchema = z.object({
153
209
  response: z
154
210
  .string()
155
- .describe('The AI agents final response to the user message. For text responses, returns plain text or JSON string. For image generation models (like gemini-2.5-flash-image-preview), returns base64-encoded image data with data URI format (data:image/png;base64,...)'),
211
+ .describe('The AI agents final response to the user message. For text responses, returns plain text. If JSON mode is enabled, returns a JSON string. For image generation models (like gemini-2.5-flash-image-preview), returns base64-encoded image data with data URI format (data:image/png;base64,...)'),
156
212
  toolCalls: z
157
213
  .array(z.object({
158
214
  tool: z.string().describe('Name of the tool that was called'),
@@ -190,15 +246,19 @@ export class AIAgentBubble extends ServiceBubble {
190
246
  factory;
191
247
  beforeToolCallHook;
192
248
  afterToolCallHook;
249
+ afterLLMCallHook;
193
250
  streamingCallback;
194
251
  shouldStopAfterTools = false;
252
+ shouldContinueToAgent = false;
195
253
  constructor(params = {
196
254
  message: 'Hello, how are you?',
197
255
  systemPrompt: 'You are a helpful AI assistant',
198
- }, context) {
199
- super(params, context);
256
+ model: { model: RECOMMENDED_MODELS.FAST },
257
+ }, context, instanceId) {
258
+ super(params, context, instanceId);
200
259
  this.beforeToolCallHook = params.beforeToolCall;
201
260
  this.afterToolCallHook = params.afterToolCall;
261
+ this.afterLLMCallHook = params.afterLLMCall;
202
262
  this.streamingCallback = params.streamingCallback;
203
263
  this.factory = new BubbleFactory();
204
264
  }
@@ -211,97 +271,89 @@ export class AIAgentBubble extends ServiceBubble {
211
271
  }
212
272
  return false;
213
273
  }
274
+ /**
275
+ * Build effective model config from primary and optional backup settings
276
+ */
277
+ buildModelConfig(primaryConfig, backupConfig) {
278
+ if (!backupConfig) {
279
+ return primaryConfig;
280
+ }
281
+ return {
282
+ model: backupConfig.model,
283
+ temperature: backupConfig.temperature ?? primaryConfig.temperature,
284
+ maxTokens: backupConfig.maxTokens ?? primaryConfig.maxTokens,
285
+ maxRetries: backupConfig.maxRetries ?? primaryConfig.maxRetries,
286
+ provider: primaryConfig.provider,
287
+ jsonMode: primaryConfig.jsonMode,
288
+ backupModel: undefined, // Don't chain backup models
289
+ };
290
+ }
291
+ /**
292
+ * Core execution logic for running the agent with a given model config
293
+ */
294
+ async executeWithModel(modelConfig) {
295
+ const { message, images, systemPrompt, tools, customTools, maxIterations, conversationHistory, } = this.params;
296
+ // Initialize the language model
297
+ const llm = this.initializeModel(modelConfig);
298
+ // Initialize tools (both pre-registered and custom)
299
+ const agentTools = await this.initializeTools(tools, customTools);
300
+ // Create the agent graph
301
+ const graph = await this.createAgentGraph(llm, agentTools, systemPrompt);
302
+ // Execute the agent
303
+ return this.executeAgent(graph, message, images, maxIterations, modelConfig, conversationHistory);
304
+ }
305
+ /**
306
+ * Modify params before execution - centralizes all param transformations
307
+ */
308
+ beforeAction() {
309
+ // Auto-enable JSON mode when expectedOutputSchema is provided
310
+ if (this.params.expectedOutputSchema) {
311
+ this.params.model.jsonMode = true;
312
+ // Enhance system prompt with JSON schema instructions
313
+ const schemaString = zodSchemaToJsonString(this.params.expectedOutputSchema);
314
+ this.params.systemPrompt = `${this.params.systemPrompt}\n\n${buildJsonSchemaInstruction(schemaString)}`;
315
+ }
316
+ }
214
317
  async performAction(context) {
215
318
  // Context is available but not currently used in this implementation
216
319
  void context;
217
- const { message, images, systemPrompt, model, tools, customTools, maxIterations, } = this.params;
320
+ // Apply param transformations before execution
321
+ this.beforeAction();
218
322
  try {
219
- // Initialize the language model
220
- const llm = this.initializeModel(model);
221
- // Initialize tools (both pre-registered and custom)
222
- const agentTools = await this.initializeTools(tools, customTools);
223
- // Create the agent graph
224
- const graph = await this.createAgentGraph(llm, agentTools, systemPrompt);
225
- // Execute the agent
226
- const result = await this.executeAgent(graph, message, images, maxIterations, model.jsonMode);
227
- return result;
323
+ return await this.executeWithModel(this.params.model);
228
324
  }
229
325
  catch (error) {
230
- // Return error information but mark as recoverable
231
326
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
232
- console.warn('[AIAgent] Execution error (continuing):', errorMessage);
327
+ console.warn('[AIAgent] Execution error:', errorMessage);
328
+ // Return error information but mark as recoverable
233
329
  return {
234
330
  response: `Error: ${errorMessage}`,
235
- success: false, // Still false but execution can continue
331
+ success: false,
236
332
  toolCalls: [],
237
333
  error: errorMessage,
238
334
  iterations: 0,
239
335
  };
240
336
  }
241
337
  }
338
+ getCredentialType() {
339
+ return this.getCredentialTypeForModel(this.params.model.model);
340
+ }
242
341
  /**
243
- * Execute the AI agent with streaming support for real-time feedback
342
+ * Get credential type for a specific model string
244
343
  */
245
- async actionWithStreaming(streamingCallback, context) {
246
- // Context is available but not currently used in this implementation
247
- void context;
248
- const { message, images, systemPrompt, model, tools, customTools, maxIterations, } = this.params;
249
- const startTime = Date.now();
250
- // Send start event
251
- await streamingCallback({
252
- type: 'start',
253
- data: {
254
- message: `Analyzing with ${this.params.name || 'AI Agent'}`,
255
- maxIterations,
256
- timestamp: new Date().toISOString(),
257
- },
258
- });
259
- try {
260
- // Send LLM start event
261
- await streamingCallback({
262
- type: 'llm_start',
263
- data: {
264
- model: model.model,
265
- temperature: model.temperature,
266
- },
267
- });
268
- // Initialize the language model
269
- const llm = this.initializeModel(model);
270
- // Initialize tools (both pre-registered and custom)
271
- const agentTools = await this.initializeTools(tools, customTools);
272
- // Create the agent graph
273
- const graph = await this.createAgentGraph(llm, agentTools, systemPrompt);
274
- // Execute the agent with streaming
275
- const result = await this.executeAgentWithStreaming(graph, message, images, maxIterations, model.jsonMode, streamingCallback);
276
- const totalDuration = Date.now() - startTime;
277
- // Send completion event
278
- await streamingCallback({
279
- type: 'complete',
280
- data: {
281
- result,
282
- totalDuration,
283
- },
284
- });
285
- return result;
286
- }
287
- catch (error) {
288
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
289
- // Send error event as recoverable
290
- await streamingCallback({
291
- type: 'error',
292
- data: {
293
- error: errorMessage,
294
- recoverable: true, // Mark as recoverable to continue execution
295
- },
296
- });
297
- console.warn('[AIAgent] Streaming execution error (continuing):', errorMessage);
298
- return {
299
- response: `Error: ${errorMessage}`,
300
- success: false, // Still false but execution can continue
301
- toolCalls: [],
302
- error: errorMessage,
303
- iterations: 0,
304
- };
344
+ getCredentialTypeForModel(model) {
345
+ const [provider] = model.split('/');
346
+ switch (provider) {
347
+ case 'openai':
348
+ return CredentialType.OPENAI_CRED;
349
+ case 'google':
350
+ return CredentialType.GOOGLE_GEMINI_CRED;
351
+ case 'anthropic':
352
+ return CredentialType.ANTHROPIC_CRED;
353
+ case 'openrouter':
354
+ return CredentialType.OPENROUTER_CRED;
355
+ default:
356
+ throw new Error(`Unsupported model provider: ${provider}`);
305
357
  }
306
358
  }
307
359
  chooseCredential() {
@@ -327,15 +379,40 @@ export class AIAgentBubble extends ServiceBubble {
327
379
  }
328
380
  }
329
381
  initializeModel(modelConfig) {
330
- const { model, temperature, maxTokens } = modelConfig;
382
+ const { model, temperature, maxTokens, maxRetries } = modelConfig;
331
383
  const slashIndex = model.indexOf('/');
332
384
  const provider = model.substring(0, slashIndex);
333
385
  const modelName = model.substring(slashIndex + 1);
334
- // Use chooseCredential to get the appropriate credential
335
- // This will throw immediately if credentials are missing
336
- const apiKey = this.chooseCredential();
386
+ const reasoningEffort = modelConfig.reasoningEffort;
387
+ // Get credential based on the modelConfig's provider (not this.params.model)
388
+ const credentials = this.params.credentials;
389
+ if (!credentials || typeof credentials !== 'object') {
390
+ throw new Error(`No ${provider.toUpperCase()} credentials provided`);
391
+ }
392
+ let apiKey;
393
+ switch (provider) {
394
+ case 'openai':
395
+ apiKey = credentials[CredentialType.OPENAI_CRED];
396
+ break;
397
+ case 'google':
398
+ apiKey = credentials[CredentialType.GOOGLE_GEMINI_CRED];
399
+ break;
400
+ case 'anthropic':
401
+ apiKey = credentials[CredentialType.ANTHROPIC_CRED];
402
+ break;
403
+ case 'openrouter':
404
+ apiKey = credentials[CredentialType.OPENROUTER_CRED];
405
+ break;
406
+ default:
407
+ throw new Error(`Unsupported model provider: ${provider}`);
408
+ }
409
+ if (!apiKey) {
410
+ throw new Error(`No credential found for provider: ${provider}`);
411
+ }
337
412
  // Enable streaming if streamingCallback is provided
338
413
  const enableStreaming = !!this.streamingCallback;
414
+ // Default to 3 retries if not specified
415
+ const retries = maxRetries ?? 3;
339
416
  switch (provider) {
340
417
  case 'openai':
341
418
  return new ChatOpenAI({
@@ -343,17 +420,70 @@ export class AIAgentBubble extends ServiceBubble {
343
420
  temperature,
344
421
  maxTokens,
345
422
  apiKey,
423
+ ...(reasoningEffort && {
424
+ reasoning: {
425
+ effort: reasoningEffort,
426
+ summary: 'auto',
427
+ },
428
+ }),
346
429
  streaming: enableStreaming,
430
+ maxRetries: retries,
347
431
  });
348
- case 'google':
349
- return new ChatGoogleGenerativeAI({
432
+ case 'google': {
433
+ const thinkingConfig = reasoningEffort
434
+ ? {
435
+ includeThoughts: reasoningEffort ? true : false,
436
+ thinkingBudget: reasoningEffort === 'low'
437
+ ? 1025
438
+ : reasoningEffort === 'medium'
439
+ ? 5000
440
+ : 10000,
441
+ }
442
+ : undefined;
443
+ return new SafeGeminiChat({
350
444
  model: modelName,
351
445
  temperature,
352
446
  maxOutputTokens: maxTokens,
447
+ ...(thinkingConfig && { thinkingConfig }),
353
448
  apiKey,
354
- streaming: enableStreaming,
449
+ // 3.0 pro preview does breaks with streaming, disabled temporarily until fixed
450
+ streaming: false,
451
+ maxRetries: retries,
452
+ // Disable all safety filters to prevent candidateContent.parts.reduce errors
453
+ // when Gemini blocks content and returns candidates without content field
454
+ safetySettings: [
455
+ {
456
+ category: HarmCategory.HARM_CATEGORY_HARASSMENT,
457
+ threshold: HarmBlockThreshold.BLOCK_NONE,
458
+ },
459
+ {
460
+ category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
461
+ threshold: HarmBlockThreshold.BLOCK_NONE,
462
+ },
463
+ {
464
+ category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
465
+ threshold: HarmBlockThreshold.BLOCK_NONE,
466
+ },
467
+ {
468
+ category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
469
+ threshold: HarmBlockThreshold.BLOCK_NONE,
470
+ },
471
+ ],
355
472
  });
356
- case 'anthropic':
473
+ }
474
+ case 'anthropic': {
475
+ // Configure Anthropic "thinking" only when reasoning is enabled.
476
+ // Anthropic's API does not allow `budget_tokens` when thinking is disabled.
477
+ const thinkingConfig = reasoningEffort != null
478
+ ? {
479
+ type: 'enabled',
480
+ budget_tokens: reasoningEffort === 'low'
481
+ ? 1025
482
+ : reasoningEffort === 'medium'
483
+ ? 5000
484
+ : 10000,
485
+ }
486
+ : undefined;
357
487
  return new ChatAnthropic({
358
488
  model: modelName,
359
489
  temperature,
@@ -361,7 +491,10 @@ export class AIAgentBubble extends ServiceBubble {
361
491
  maxTokens,
362
492
  streaming: enableStreaming,
363
493
  apiKey,
494
+ ...(thinkingConfig && { thinking: thinkingConfig }),
495
+ maxRetries: retries,
364
496
  });
497
+ }
365
498
  case 'openrouter':
366
499
  console.log('openrouter', modelName);
367
500
  return new ChatOpenAI({
@@ -371,12 +504,16 @@ export class AIAgentBubble extends ServiceBubble {
371
504
  maxTokens,
372
505
  apiKey,
373
506
  streaming: enableStreaming,
507
+ maxRetries: retries,
374
508
  configuration: {
375
509
  baseURL: 'https://openrouter.ai/api/v1',
376
510
  },
377
511
  modelKwargs: {
512
+ provider: {
513
+ order: this.params.model.provider,
514
+ },
378
515
  reasoning: {
379
- effort: 'medium',
516
+ effort: reasoningEffort ?? 'medium',
380
517
  exclude: false,
381
518
  },
382
519
  },
@@ -392,10 +529,22 @@ export class AIAgentBubble extends ServiceBubble {
392
529
  for (const customTool of customToolConfigs) {
393
530
  try {
394
531
  console.log(`🛠️ [AIAgent] Initializing custom tool: ${customTool.name}`);
532
+ // Handle both plain object and Zod object schemas
533
+ let schema;
534
+ if (customTool.schema &&
535
+ typeof customTool.schema === 'object' &&
536
+ '_def' in customTool.schema) {
537
+ // Already a Zod schema object, use it directly
538
+ schema = customTool.schema;
539
+ }
540
+ else {
541
+ // Plain object, convert to Zod object
542
+ schema = z.object(customTool.schema);
543
+ }
395
544
  const dynamicTool = new DynamicStructuredTool({
396
545
  name: customTool.name,
397
546
  description: customTool.description,
398
- schema: z.object(customTool.schema),
547
+ schema: schema,
399
548
  func: customTool.func,
400
549
  });
401
550
  tools.push(dynamicTool);
@@ -475,12 +624,35 @@ export class AIAgentBubble extends ServiceBubble {
475
624
  const tool = tools.find((t) => t.name === toolCall.name);
476
625
  if (!tool) {
477
626
  console.warn(`Tool ${toolCall.name} not found`);
627
+ const errorContent = `Error: Tool ${toolCall.name} not found`;
628
+ const startTime = Date.now();
629
+ // Send tool_start event
630
+ this.streamingCallback?.({
631
+ type: 'tool_start',
632
+ data: {
633
+ tool: toolCall.name,
634
+ input: toolCall.args,
635
+ callId: toolCall.id,
636
+ },
637
+ });
638
+ // Send tool_complete event with error
639
+ this.streamingCallback?.({
640
+ type: 'tool_complete',
641
+ data: {
642
+ callId: toolCall.id,
643
+ input: toolCall.args,
644
+ tool: toolCall.name,
645
+ output: { error: errorContent },
646
+ duration: Date.now() - startTime,
647
+ },
648
+ });
478
649
  toolMessages.push(new ToolMessage({
479
- content: `Error: Tool ${toolCall.name} not found`,
650
+ content: errorContent,
480
651
  tool_call_id: toolCall.id,
481
652
  }));
482
653
  continue;
483
654
  }
655
+ const startTime = Date.now();
484
656
  try {
485
657
  // Call beforeToolCall hook if provided
486
658
  const hookResult_before = await this.beforeToolCallHook?.({
@@ -488,7 +660,6 @@ export class AIAgentBubble extends ServiceBubble {
488
660
  toolInput: toolCall.args,
489
661
  messages: currentMessages,
490
662
  });
491
- const startTime = Date.now();
492
663
  this.streamingCallback?.({
493
664
  type: 'tool_start',
494
665
  data: {
@@ -545,17 +716,30 @@ export class AIAgentBubble extends ServiceBubble {
545
716
  }
546
717
  catch (error) {
547
718
  console.error(`Error executing tool ${toolCall.name}:`, error);
719
+ const errorContent = `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
548
720
  const errorMessage = new ToolMessage({
549
- content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
721
+ content: errorContent,
550
722
  tool_call_id: toolCall.id,
551
723
  });
552
724
  toolMessages.push(errorMessage);
553
725
  currentMessages = [...currentMessages, errorMessage];
726
+ // Send tool_complete event even on failure so frontend can track it properly
727
+ this.streamingCallback?.({
728
+ type: 'tool_complete',
729
+ data: {
730
+ callId: toolCall.id,
731
+ input: toolCall.args,
732
+ tool: toolCall.name,
733
+ output: { error: errorContent },
734
+ duration: Date.now() - startTime,
735
+ },
736
+ });
554
737
  }
555
738
  }
556
739
  // Return the updated messages
557
740
  // If hooks modified messages, use those; otherwise use the original messages + tool messages
558
741
  if (currentMessages.length !== messages.length + toolMessages.length) {
742
+ console.error('[AIAgent] Current messages length does not match expected length', currentMessages.length, messages.length, toolMessages.length);
559
743
  return { messages: currentMessages };
560
744
  }
561
745
  return { messages: toolMessages };
@@ -563,63 +747,180 @@ export class AIAgentBubble extends ServiceBubble {
563
747
  async createAgentGraph(llm, tools, systemPrompt) {
564
748
  // Define the agent node
565
749
  const agentNode = async ({ messages }) => {
566
- // Enhance system prompt for JSON mode
750
+ // systemPrompt is already enhanced by beforeAction() if expectedOutputSchema was provided
567
751
  const systemMessage = new HumanMessage(systemPrompt);
568
752
  const allMessages = [systemMessage, ...messages];
569
- // If we have tools, bind them to the LLM
570
- const modelWithTools = tools.length > 0 ? llm.bindTools(tools) : llm;
571
- // Use streaming if streamingCallback is provided
572
- if (this.streamingCallback) {
573
- const messageId = `msg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
574
- // Use invoke with callbacks for streaming
575
- const response = await modelWithTools.invoke(allMessages, {
576
- callbacks: [
577
- {
578
- handleLLMStart: async () => {
579
- await this.streamingCallback?.({
580
- type: 'llm_start',
581
- data: {
582
- model: this.params.model.model,
583
- temperature: this.params.model.temperature,
584
- },
585
- });
586
- },
587
- handleLLMEnd: async (output) => {
588
- // Extract thinking tokens from different model providers
589
- const thinking = extractAndStreamThinkingTokens(output);
590
- if (thinking) {
753
+ // Helper function for exponential backoff with jitter
754
+ const exponentialBackoff = (attemptNumber) => {
755
+ // Base delay: 1 second, exponentially increases (1s, 2s, 4s, 8s, ...)
756
+ const baseDelay = 1000;
757
+ const maxDelay = 32000; // Cap at 32 seconds
758
+ const delay = Math.min(baseDelay * Math.pow(2, attemptNumber - 1), maxDelay);
759
+ // Add jitter (random ±25% variation) to prevent thundering herd
760
+ const jitter = delay * 0.25 * (Math.random() - 0.5);
761
+ const finalDelay = delay + jitter;
762
+ return new Promise((resolve) => setTimeout(resolve, finalDelay));
763
+ };
764
+ // Shared onFailedAttempt callback to avoid duplication
765
+ const onFailedAttempt = async (error) => {
766
+ const attemptNumber = error.attemptNumber;
767
+ const retriesLeft = error.retriesLeft;
768
+ // Check if this is a candidateContent error
769
+ const errorMessage = error.message || String(error);
770
+ if (errorMessage.includes('candidateContent') ||
771
+ errorMessage.includes('parts.reduce') ||
772
+ errorMessage.includes('undefined is not an object')) {
773
+ this.context?.logger?.error(`[AIAgent] Gemini candidateContent error detected (attempt ${attemptNumber}). This indicates blocked/empty content from Gemini API.`);
774
+ }
775
+ this.context?.logger?.warn(`[AIAgent] LLM call failed (attempt ${attemptNumber}/${this.params.model.maxRetries}). Retries left: ${retriesLeft}. Error: ${error.message}`);
776
+ // Optionally emit streaming event for retry
777
+ if (this.streamingCallback) {
778
+ await this.streamingCallback({
779
+ type: 'error',
780
+ data: {
781
+ error: `Retry attempt ${attemptNumber}/${this.params.model.maxRetries}: ${error.message}`,
782
+ recoverable: retriesLeft > 0,
783
+ },
784
+ });
785
+ }
786
+ // Wait with exponential backoff before retrying
787
+ if (retriesLeft > 0) {
788
+ await exponentialBackoff(attemptNumber);
789
+ }
790
+ };
791
+ // If we have tools, bind them to the LLM, then add retry logic
792
+ // IMPORTANT: Must bind tools FIRST, then add retry - not the other way around
793
+ const modelWithTools = tools.length > 0
794
+ ? llm.bindTools(tools).withRetry({
795
+ stopAfterAttempt: this.params.model.maxRetries,
796
+ onFailedAttempt,
797
+ })
798
+ : llm.withRetry({
799
+ stopAfterAttempt: this.params.model.maxRetries,
800
+ onFailedAttempt,
801
+ });
802
+ try {
803
+ // Use streaming if streamingCallback is provided
804
+ if (this.streamingCallback) {
805
+ const messageId = `msg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
806
+ // Use invoke with callbacks for streaming
807
+ const response = await modelWithTools.invoke(allMessages, {
808
+ callbacks: [
809
+ {
810
+ handleLLMStart: async () => {
811
+ await this.streamingCallback?.({
812
+ type: 'llm_start',
813
+ data: {
814
+ model: this.params.model.model,
815
+ temperature: this.params.model.temperature,
816
+ },
817
+ });
818
+ },
819
+ handleLLMEnd: async (output) => {
820
+ // Extract thinking tokens from different model providers
821
+ const thinking = extractAndStreamThinkingTokens(output);
822
+ if (thinking) {
823
+ await this.streamingCallback?.({
824
+ type: 'think',
825
+ data: {
826
+ content: thinking,
827
+ messageId,
828
+ },
829
+ });
830
+ }
831
+ const content = formatFinalResponse(generationsToMessageContent(output.generations.flat()), this.params.model.model).response;
591
832
  await this.streamingCallback?.({
592
- type: 'think',
833
+ type: 'llm_complete',
593
834
  data: {
594
- content: thinking,
595
835
  messageId,
836
+ content: content,
837
+ totalTokens: output.llmOutput?.usage_metadata?.total_tokens,
596
838
  },
597
839
  });
598
- }
599
- await this.streamingCallback?.({
600
- type: 'llm_complete',
601
- data: {
602
- messageId,
603
- totalTokens: output.llmOutput?.usage_metadata?.total_tokens,
604
- },
605
- });
840
+ },
606
841
  },
607
- },
608
- ],
609
- });
610
- return { messages: [response] };
842
+ ],
843
+ });
844
+ return { messages: [response] };
845
+ }
846
+ else {
847
+ // Non-streaming fallback
848
+ const response = await modelWithTools.invoke(allMessages);
849
+ return { messages: [response] };
850
+ }
611
851
  }
612
- else {
613
- // Non-streaming fallback
614
- const response = await modelWithTools.invoke(allMessages);
615
- return { messages: [response] };
852
+ catch (error) {
853
+ // Catch candidateContent errors that slip through SafeGeminiChat
854
+ const errorMessage = error instanceof Error ? error.message : String(error);
855
+ if (errorMessage.includes('candidateContent') ||
856
+ errorMessage.includes('parts.reduce') ||
857
+ errorMessage.includes('undefined is not an object')) {
858
+ console.error('[AIAgent] Caught candidateContent error in agentNode:', errorMessage);
859
+ // Return error as AIMessage instead of crashing
860
+ return {
861
+ messages: [
862
+ new AIMessage({
863
+ content: `[Gemini Error] Unable to generate response due to content filtering. Error: ${errorMessage}`,
864
+ additional_kwargs: {
865
+ finishReason: 'ERROR',
866
+ error: errorMessage,
867
+ },
868
+ }),
869
+ ],
870
+ };
871
+ }
872
+ // Rethrow other errors
873
+ throw error;
616
874
  }
617
875
  };
618
- // Define conditional edge function
619
- const shouldContinue = ({ messages }) => {
876
+ // Node that runs after agent to check afterLLMCall hook before routing
877
+ const afterLLMCheckNode = async ({ messages, }) => {
878
+ // Reset the flag at the start
879
+ this.shouldContinueToAgent = false;
880
+ // Get the last AI message
620
881
  const lastMessage = messages[messages.length - 1];
621
- // Check if the last message has tool calls
622
- if (lastMessage.tool_calls && lastMessage.tool_calls.length > 0) {
882
+ const hasToolCalls = !!(lastMessage.tool_calls && lastMessage.tool_calls.length > 0);
883
+ // Only call hook if we're about to end (no tool calls) and hook is provided
884
+ if (!hasToolCalls && this.afterLLMCallHook) {
885
+ console.log('[AIAgent] No tool calls detected, calling afterLLMCall hook');
886
+ const hookResult = await this.afterLLMCallHook({
887
+ messages,
888
+ lastAIMessage: lastMessage,
889
+ hasToolCalls,
890
+ });
891
+ // If hook wants to continue to agent, set flag and return modified messages
892
+ if (hookResult.continueToAgent) {
893
+ console.log('[AIAgent] afterLLMCall hook requested retry to agent');
894
+ this.shouldContinueToAgent = true;
895
+ // Return the modified messages from the hook
896
+ // We need to return only the new messages to append
897
+ const newMessages = hookResult.messages.slice(messages.length);
898
+ return { messages: newMessages };
899
+ }
900
+ }
901
+ // No modifications needed
902
+ return { messages: [] };
903
+ };
904
+ // Define conditional edge function after LLM check
905
+ const shouldContinueAfterLLMCheck = ({ messages, }) => {
906
+ // First check if afterLLMCall hook requested continuing to agent
907
+ if (this.shouldContinueToAgent) {
908
+ return 'agent';
909
+ }
910
+ // Find the last AI message (could be followed by human messages from hook)
911
+ const aiMessages = [];
912
+ for (const msg of messages) {
913
+ if (isAIMessage(msg)) {
914
+ aiMessages.push(msg);
915
+ }
916
+ else if ('tool_calls' in msg &&
917
+ msg.constructor?.name === 'AIMessageChunk') {
918
+ aiMessages.push(msg);
919
+ }
920
+ }
921
+ const lastAIMessage = aiMessages[aiMessages.length - 1];
922
+ // Check if the last AI message has tool calls
923
+ if (lastAIMessage?.tool_calls && lastAIMessage.tool_calls.length > 0) {
623
924
  return 'tools';
624
925
  }
625
926
  return '__end__';
@@ -642,23 +943,57 @@ export class AIAgentBubble extends ServiceBubble {
642
943
  };
643
944
  graph
644
945
  .addNode('tools', toolNode)
946
+ .addNode('afterLLMCheck', afterLLMCheckNode)
645
947
  .addEdge('__start__', 'agent')
646
- .addConditionalEdges('agent', shouldContinue)
948
+ .addEdge('agent', 'afterLLMCheck')
949
+ .addConditionalEdges('afterLLMCheck', shouldContinueAfterLLMCheck)
647
950
  .addConditionalEdges('tools', shouldContinueAfterTools);
648
951
  }
649
952
  else {
650
- graph.addEdge('__start__', 'agent').addEdge('agent', '__end__');
953
+ // Even without tools, add the afterLLMCheck node for hook support
954
+ graph
955
+ .addNode('afterLLMCheck', afterLLMCheckNode)
956
+ .addEdge('__start__', 'agent')
957
+ .addEdge('agent', 'afterLLMCheck')
958
+ .addConditionalEdges('afterLLMCheck', shouldContinueAfterLLMCheck);
651
959
  }
652
960
  return graph.compile();
653
961
  }
654
- async executeAgent(graph, message, images, maxIterations, jsonMode) {
962
+ async executeAgent(graph, message, images, maxIterations, modelConfig, conversationHistory) {
963
+ const jsonMode = modelConfig.jsonMode;
655
964
  const toolCalls = [];
656
965
  let iterations = 0;
657
966
  console.log('[AIAgent] Starting execution with message:', message.substring(0, 100) + '...');
658
967
  console.log('[AIAgent] Max iterations:', maxIterations);
659
968
  try {
660
969
  console.log('[AIAgent] Invoking graph...');
661
- // Create human message with text and optional images
970
+ // Build messages array starting with conversation history (for KV cache optimization)
971
+ const initialMessages = [];
972
+ // Convert conversation history to LangChain messages if provided
973
+ // This enables KV cache optimization by keeping previous turns as separate messages
974
+ if (conversationHistory && conversationHistory.length > 0) {
975
+ for (const historyMsg of conversationHistory) {
976
+ switch (historyMsg.role) {
977
+ case 'user':
978
+ initialMessages.push(new HumanMessage(historyMsg.content));
979
+ break;
980
+ case 'assistant':
981
+ initialMessages.push(new AIMessage(historyMsg.content));
982
+ break;
983
+ case 'tool':
984
+ // Tool messages require a tool_call_id
985
+ if (historyMsg.toolCallId) {
986
+ initialMessages.push(new ToolMessage({
987
+ content: historyMsg.content,
988
+ tool_call_id: historyMsg.toolCallId,
989
+ name: historyMsg.name,
990
+ }));
991
+ }
992
+ break;
993
+ }
994
+ }
995
+ }
996
+ // Create the current human message with text and optional images
662
997
  let humanMessage;
663
998
  if (images && images.length > 0) {
664
999
  console.log('[AIAgent] Creating multimodal message with', images.length, 'images');
@@ -709,7 +1044,9 @@ export class AIAgentBubble extends ServiceBubble {
709
1044
  // Text-only message
710
1045
  humanMessage = new HumanMessage(message);
711
1046
  }
712
- const result = await graph.invoke({ messages: [humanMessage] }, { recursionLimit: maxIterations });
1047
+ // Add the current message to the conversation
1048
+ initialMessages.push(humanMessage);
1049
+ const result = await graph.invoke({ messages: initialMessages }, { recursionLimit: maxIterations });
713
1050
  console.log('[AIAgent] Graph execution completed');
714
1051
  console.log('[AIAgent] Total messages:', result.messages.length);
715
1052
  iterations = result.messages.length;
@@ -760,6 +1097,9 @@ export class AIAgentBubble extends ServiceBubble {
760
1097
  const aiMessages = result.messages.filter((msg) => isAIMessage(msg) || isAIMessageChunk(msg));
761
1098
  console.log('[AIAgent] Found', aiMessages.length, 'AI messages');
762
1099
  const finalMessage = aiMessages[aiMessages.length - 1];
1100
+ if (finalMessage?.additional_kwargs?.finishReason === 'SAFETY_BLOCKED') {
1101
+ throw new Error(`[Gemini Error] Unable to generate a response. Please increase maxTokens in model configuration or try again with a different model.`);
1102
+ }
763
1103
  // Check for MAX_TOKENS finish reason
764
1104
  if (finalMessage?.additional_kwargs?.finishReason === 'MAX_TOKENS') {
765
1105
  throw new Error('Response was truncated due to max tokens limit. Please increase maxTokens in model configuration.');
@@ -785,19 +1125,29 @@ export class AIAgentBubble extends ServiceBubble {
785
1125
  }
786
1126
  if (totalTokensSum > 0 && this.context && this.context.logger) {
787
1127
  this.context.logger.logTokenUsage({
788
- inputTokens: totalInputTokens,
789
- outputTokens: totalOutputTokens,
790
- totalTokens: totalTokensSum,
791
- modelName: this.params.model.model,
792
- }, `LLM completion: ${totalInputTokens} input + ${totalOutputTokens} output = ${totalTokensSum} total tokens`, {
1128
+ usage: totalInputTokens,
1129
+ service: this.getCredentialTypeForModel(modelConfig.model),
1130
+ unit: 'input_tokens',
1131
+ subService: modelConfig.model,
1132
+ }, `LLM completion: ${totalInputTokens} input`, {
1133
+ bubbleName: 'ai-agent',
1134
+ variableId: this.context?.variableId,
1135
+ operationType: 'bubble_execution',
1136
+ });
1137
+ this.context.logger.logTokenUsage({
1138
+ usage: totalOutputTokens,
1139
+ service: this.getCredentialTypeForModel(modelConfig.model),
1140
+ unit: 'output_tokens',
1141
+ subService: modelConfig.model,
1142
+ }, `LLM completion: ${totalOutputTokens} output`, {
793
1143
  bubbleName: 'ai-agent',
794
1144
  variableId: this.context?.variableId,
795
1145
  operationType: 'bubble_execution',
796
1146
  });
797
1147
  }
798
- const response = finalMessage?.content || 'No response generated';
1148
+ const response = finalMessage?.content || '';
799
1149
  // Use shared formatting method
800
- const formattedResult = await formatFinalResponse(response, this.params.model.model, jsonMode);
1150
+ const formattedResult = formatFinalResponse(response, modelConfig.model, jsonMode);
801
1151
  // If there's an error from formatting (e.g., invalid JSON), return early
802
1152
  if (formattedResult.error) {
803
1153
  return {
@@ -828,6 +1178,21 @@ export class AIAgentBubble extends ServiceBubble {
828
1178
  console.warn('[AIAgent] Execution error (continuing):', error);
829
1179
  console.log('[AIAgent] Tool calls before error:', toolCalls.length);
830
1180
  console.log('[AIAgent] Iterations before error:', iterations);
1181
+ // Model fallback logic - only retry if this config has a backup model
1182
+ if (modelConfig.backupModel) {
1183
+ console.log(`[AIAgent] Retrying with backup model: ${modelConfig.backupModel.model}`);
1184
+ this.context?.logger?.warn(`Primary model ${modelConfig.model} failed: ${error instanceof Error ? error.message : 'Unknown error'}. Retrying with backup model... ${modelConfig.backupModel.model}`);
1185
+ this.streamingCallback?.({
1186
+ type: 'error',
1187
+ data: {
1188
+ error: `Primary model ${modelConfig.model} failed: ${error instanceof Error ? error.message : 'Unknown error'}. Retrying with backup model... ${modelConfig.backupModel.model}`,
1189
+ recoverable: true,
1190
+ },
1191
+ });
1192
+ const backupModelConfig = this.buildModelConfig(modelConfig, modelConfig.backupModel);
1193
+ const backupResult = await this.executeWithModel(backupModelConfig);
1194
+ return backupResult;
1195
+ }
831
1196
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
832
1197
  // Return partial results to allow execution to continue
833
1198
  // Include any tool calls that were completed before the error
@@ -840,236 +1205,5 @@ export class AIAgentBubble extends ServiceBubble {
840
1205
  };
841
1206
  }
842
1207
  }
843
- /**
844
- * Execute agent with streaming support using LangGraph streamEvents
845
- */
846
- async executeAgentWithStreaming(graph, message, images, maxIterations, jsonMode, streamingCallback) {
847
- const toolCalls = [];
848
- let iterations = 0;
849
- let currentMessageId = '';
850
- console.log('[AIAgent] Starting streaming execution with message:', message.substring(0, 100) + '...');
851
- try {
852
- // Create human message with text and optional images
853
- let humanMessage;
854
- if (images && images.length > 0) {
855
- console.log('[AIAgent] Creating multimodal message with', images.length, 'images');
856
- // Create multimodal content array
857
- const content = [{ type: 'text', text: message }];
858
- // Add images to content
859
- for (const image of images) {
860
- let imageUrl;
861
- if (image.type === 'base64') {
862
- // Base64 encoded image
863
- imageUrl = `data:${image.mimeType};base64,${image.data}`;
864
- }
865
- else {
866
- // URL image - fetch and convert to base64 for Google Gemini compatibility
867
- try {
868
- console.log('[AIAgent] Fetching image from URL:', image.url);
869
- const response = await fetch(image.url);
870
- if (!response.ok) {
871
- throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
872
- }
873
- const arrayBuffer = await response.arrayBuffer();
874
- const base64Data = Buffer.from(arrayBuffer).toString('base64');
875
- // Detect MIME type from response or default to PNG
876
- const contentType = response.headers.get('content-type') || 'image/png';
877
- imageUrl = `data:${contentType};base64,${base64Data}`;
878
- console.log('[AIAgent] Successfully converted URL image to base64');
879
- }
880
- catch (error) {
881
- console.error('[AIAgent] Error fetching image from URL:', error);
882
- throw new Error(`Failed to load image from URL ${image.url}: ${error instanceof Error ? error.message : 'Unknown error'}`);
883
- }
884
- }
885
- content.push({
886
- type: 'image_url',
887
- image_url: { url: imageUrl },
888
- });
889
- // Add image description if provided
890
- if (image.description) {
891
- content.push({
892
- type: 'text',
893
- text: `Image description: ${image.description}`,
894
- });
895
- }
896
- }
897
- humanMessage = new HumanMessage({ content });
898
- }
899
- else {
900
- // Text-only message
901
- humanMessage = new HumanMessage(message);
902
- }
903
- // Stream events from the graph
904
- const eventStream = graph.streamEvents({ messages: [humanMessage] }, {
905
- version: 'v2',
906
- recursionLimit: maxIterations,
907
- });
908
- let currentIteration = 0;
909
- const toolCallMap = new Map();
910
- let accumulatedContent = '';
911
- // Track processed events to prevent duplicates
912
- const processedIterationEvents = new Set();
913
- for await (const event of eventStream) {
914
- if (!event || typeof event !== 'object')
915
- continue;
916
- // Handle different types of streaming events
917
- switch (event.event) {
918
- case 'on_chat_model_start':
919
- currentIteration++;
920
- currentMessageId = `msg-${Date.now()}-${currentIteration}`;
921
- if (streamingCallback) {
922
- await streamingCallback({
923
- type: 'iteration_start',
924
- data: { iteration: currentIteration },
925
- });
926
- }
927
- break;
928
- case 'on_chat_model_stream':
929
- // Stream individual tokens
930
- if (event.data?.chunk?.content && streamingCallback) {
931
- const content = event.data.chunk.content;
932
- accumulatedContent += content;
933
- await streamingCallback({
934
- type: 'token',
935
- data: {
936
- content,
937
- messageId: currentMessageId,
938
- },
939
- });
940
- }
941
- break;
942
- case 'on_chat_model_end':
943
- if (streamingCallback) {
944
- const usageMetadata = event.data?.output?.usage_metadata;
945
- const totalTokens = usageMetadata?.total_tokens;
946
- // Track token usage if available
947
- if (usageMetadata &&
948
- this.context != null &&
949
- this.context.logger != null) {
950
- const tokenUsage = {
951
- inputTokens: usageMetadata.input_tokens || 0,
952
- outputTokens: usageMetadata.output_tokens || 0,
953
- totalTokens: totalTokens || 0,
954
- modelName: this.params.model.model,
955
- };
956
- this.context.logger.logTokenUsage(tokenUsage, `LLM completion: ${tokenUsage.inputTokens} input + ${tokenUsage.outputTokens} output = ${tokenUsage.totalTokens} total tokens`, {
957
- bubbleName: 'ai-agent',
958
- variableId: this.context?.variableId,
959
- operationType: 'bubble_execution',
960
- });
961
- }
962
- await streamingCallback({
963
- type: 'llm_complete',
964
- data: {
965
- messageId: currentMessageId,
966
- totalTokens,
967
- },
968
- });
969
- }
970
- break;
971
- case 'on_tool_start':
972
- if (event.name && event.data?.input && streamingCallback) {
973
- const callId = `tool-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
974
- toolCallMap.set(callId, {
975
- name: event.name,
976
- args: event.data.input,
977
- startTime: Date.now(),
978
- });
979
- await streamingCallback({
980
- type: 'tool_start',
981
- data: {
982
- tool: event.name,
983
- input: event.data.input,
984
- callId,
985
- },
986
- });
987
- }
988
- break;
989
- case 'on_tool_end':
990
- if (event.name && event.data?.output && streamingCallback) {
991
- // Find matching tool call
992
- const matchingCall = Array.from(toolCallMap.entries()).find(([, callData]) => callData.name === event.name);
993
- if (matchingCall) {
994
- const [callId, callData] = matchingCall;
995
- const duration = Date.now() - callData.startTime;
996
- toolCalls.push({
997
- tool: callData.name,
998
- input: callData.args,
999
- output: event.data.output,
1000
- });
1001
- await streamingCallback({
1002
- type: 'tool_complete',
1003
- data: {
1004
- callId,
1005
- input: callData.args,
1006
- tool: callData.name,
1007
- output: event.data.output,
1008
- duration,
1009
- },
1010
- });
1011
- toolCallMap.delete(callId);
1012
- }
1013
- }
1014
- break;
1015
- case 'on_chain_end':
1016
- // This indicates the completion of the entire graph
1017
- if (event.data?.output) {
1018
- iterations = currentIteration;
1019
- // Prevent duplicate iteration_complete events
1020
- const iterationKey = `iteration_${currentIteration}`;
1021
- if (streamingCallback &&
1022
- !processedIterationEvents.has(iterationKey)) {
1023
- processedIterationEvents.add(iterationKey);
1024
- await streamingCallback({
1025
- type: 'iteration_complete',
1026
- data: {
1027
- iteration: currentIteration,
1028
- hasToolCalls: toolCalls.length > 0,
1029
- },
1030
- });
1031
- }
1032
- }
1033
- break;
1034
- }
1035
- }
1036
- // Process final result
1037
- const accumulatedResponse = accumulatedContent || 'No response generated';
1038
- // Use shared formatting method
1039
- const formattedResult = await formatFinalResponse(accumulatedResponse, this.params.model.model, jsonMode);
1040
- // If there's an error from formatting (e.g., invalid JSON), return early with consistent behavior
1041
- if (formattedResult.error) {
1042
- return {
1043
- response: formattedResult.response,
1044
- toolCalls: toolCalls.length > 0 ? toolCalls : [],
1045
- iterations,
1046
- error: formattedResult.error,
1047
- success: false,
1048
- };
1049
- }
1050
- const finalResponse = formattedResult.response;
1051
- console.log('[AIAgent] Streaming execution completed with', iterations, 'iterations and', toolCalls.length, 'tool calls');
1052
- return {
1053
- response: typeof finalResponse === 'string'
1054
- ? finalResponse
1055
- : JSON.stringify(finalResponse),
1056
- toolCalls: toolCalls.length > 0 ? toolCalls : [],
1057
- iterations,
1058
- error: '',
1059
- success: true,
1060
- };
1061
- }
1062
- catch (error) {
1063
- console.warn('[AIAgent] Streaming execution error (continuing):', error);
1064
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1065
- return {
1066
- response: `Execution error: ${errorMessage}`,
1067
- success: false, // Still false but don't completely halt execution
1068
- iterations,
1069
- toolCalls: toolCalls.length > 0 ? toolCalls : [], // Preserve completed tool calls
1070
- error: errorMessage,
1071
- };
1072
- }
1073
- }
1074
1208
  }
1075
1209
  //# sourceMappingURL=ai-agent.js.map