@bubblelab/bubble-core 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bubble-bundle.d.ts +333 -1438
- package/dist/bubble-factory.d.ts.map +1 -1
- package/dist/bubble-factory.js +64 -28
- package/dist/bubble-factory.js.map +1 -1
- package/dist/bubble-flow/bubble-flow-class.d.ts +17 -1
- package/dist/bubble-flow/bubble-flow-class.d.ts.map +1 -1
- package/dist/bubble-flow/bubble-flow-class.js +16 -0
- package/dist/bubble-flow/bubble-flow-class.js.map +1 -1
- package/dist/bubble-flow/sample/data-analyst-flow.d.ts +1 -1
- package/dist/bubble-flow/sample/data-analyst-flow.d.ts.map +1 -1
- package/dist/bubble-flow/sample/error-ts.d.ts +1 -1
- package/dist/bubble-flow/sample/error-ts.d.ts.map +1 -1
- package/dist/bubble-flow/sample/sanitytest.d.ts +1 -1
- package/dist/bubble-flow/sample/sanitytest.d.ts.map +1 -1
- package/dist/bubble-flow/sample/simple-webhook-2.d.ts +1 -1
- package/dist/bubble-flow/sample/simple-webhook-2.d.ts.map +1 -1
- package/dist/bubble-flow/sample/simple-webhook.d.ts +1 -1
- package/dist/bubble-flow/sample/simple-webhook.d.ts.map +1 -1
- package/dist/bubble-flow/sample/simplified-data-analysis.flow.d.ts +1 -1
- package/dist/bubble-flow/sample/simplified-data-analysis.flow.d.ts.map +1 -1
- package/dist/bubble-flow/sample/slack-v0.1.d.ts +1 -1
- package/dist/bubble-flow/sample/slack-v0.1.d.ts.map +1 -1
- package/dist/bubble-flow/sample/slackagenttest.d.ts +1 -1
- package/dist/bubble-flow/sample/slackagenttest.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/ai-agent.d.ts +115 -97
- package/dist/bubbles/service-bubble/ai-agent.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/ai-agent.js +276 -96
- package/dist/bubbles/service-bubble/ai-agent.js.map +1 -1
- package/dist/bubbles/service-bubble/apify/actors/instagram-hashtag-scraper.d.ts +805 -0
- package/dist/bubbles/service-bubble/apify/actors/instagram-hashtag-scraper.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/instagram-hashtag-scraper.js +131 -0
- package/dist/bubbles/service-bubble/apify/actors/instagram-hashtag-scraper.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/instagram-scraper.d.ts +485 -0
- package/dist/bubbles/service-bubble/apify/actors/instagram-scraper.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/instagram-scraper.js +176 -0
- package/dist/bubbles/service-bubble/apify/actors/instagram-scraper.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-posts-search.d.ts +302 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-posts-search.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-posts-search.js +138 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-posts-search.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-profile-posts.d.ts +642 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-profile-posts.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-profile-posts.js +123 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-profile-posts.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/youtube-scraper.d.ts +184 -0
- package/dist/bubbles/service-bubble/apify/actors/youtube-scraper.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/youtube-scraper.js +145 -0
- package/dist/bubbles/service-bubble/apify/actors/youtube-scraper.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/youtube-transcript-scraper.d.ts +52 -0
- package/dist/bubbles/service-bubble/apify/actors/youtube-transcript-scraper.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/youtube-transcript-scraper.js +29 -0
- package/dist/bubbles/service-bubble/apify/actors/youtube-transcript-scraper.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/apify-scraper.schema.d.ts +1999 -0
- package/dist/bubbles/service-bubble/apify/apify-scraper.schema.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/apify-scraper.schema.js +54 -0
- package/dist/bubbles/service-bubble/apify/apify-scraper.schema.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/apify.d.ts +143 -0
- package/dist/bubbles/service-bubble/apify/apify.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/apify.js +276 -0
- package/dist/bubbles/service-bubble/apify/apify.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/index.d.ts +6 -0
- package/dist/bubbles/service-bubble/apify/index.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/index.js +6 -0
- package/dist/bubbles/service-bubble/apify/index.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/types.d.ts +7 -0
- package/dist/bubbles/service-bubble/apify/types.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/types.js +5 -0
- package/dist/bubbles/service-bubble/apify/types.js.map +1 -0
- package/dist/bubbles/service-bubble/gmail.d.ts +626 -132
- package/dist/bubbles/service-bubble/gmail.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/gmail.js +435 -7
- package/dist/bubbles/service-bubble/gmail.js.map +1 -1
- package/dist/bubbles/service-bubble/google-calendar.d.ts +36 -36
- package/dist/bubbles/service-bubble/google-drive.d.ts +233 -4
- package/dist/bubbles/service-bubble/google-drive.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/google-drive.js +65 -75
- package/dist/bubbles/service-bubble/google-drive.js.map +1 -1
- package/dist/bubbles/service-bubble/google-sheets.d.ts +52 -52
- package/dist/bubbles/service-bubble/hello-world.js +2 -2
- package/dist/bubbles/service-bubble/hello-world.js.map +1 -1
- package/dist/bubbles/service-bubble/http.d.ts +6 -6
- package/dist/bubbles/service-bubble/postgresql.d.ts +4 -4
- package/dist/bubbles/service-bubble/resend.d.ts +5 -5
- package/dist/bubbles/service-bubble/resend.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/resend.js +16 -5
- package/dist/bubbles/service-bubble/resend.js.map +1 -1
- package/dist/bubbles/service-bubble/slack.d.ts +18 -18
- package/dist/bubbles/service-bubble/storage.d.ts +4 -4
- package/dist/bubbles/service-bubble/storage.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/storage.js +16 -5
- package/dist/bubbles/service-bubble/storage.js.map +1 -1
- package/dist/bubbles/service-bubble/x-twitter.d.ts +814 -0
- package/dist/bubbles/service-bubble/x-twitter.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/x-twitter.js +445 -0
- package/dist/bubbles/service-bubble/x-twitter.js.map +1 -0
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.d.ts +20 -20
- package/dist/bubbles/tool-bubble/chart-js-tool.d.ts +16 -16
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts +14 -1
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.js +101 -47
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/instagram-tool.d.ts +435 -0
- package/dist/bubbles/tool-bubble/instagram-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/instagram-tool.js +474 -0
- package/dist/bubbles/tool-bubble/instagram-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/linkedin-tool.d.ts +2136 -0
- package/dist/bubbles/tool-bubble/linkedin-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/linkedin-tool.js +608 -0
- package/dist/bubbles/tool-bubble/linkedin-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.d.ts +69 -64
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.js +97 -22
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/research-agent-tool.d.ts +6 -6
- package/dist/bubbles/tool-bubble/research-agent-tool.js +5 -5
- package/dist/bubbles/tool-bubble/research-agent-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/tool-template.d.ts +8 -8
- package/dist/bubbles/tool-bubble/web-crawl-tool.d.ts +14 -14
- package/dist/bubbles/tool-bubble/web-extract-tool.d.ts +4 -4
- package/dist/bubbles/tool-bubble/web-scrape-tool.d.ts +28 -28
- package/dist/bubbles/tool-bubble/web-scrape-tool.js +1 -1
- package/dist/bubbles/tool-bubble/web-scrape-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/web-search-tool.d.ts +5 -4
- package/dist/bubbles/tool-bubble/web-search-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/web-search-tool.js +6 -2
- package/dist/bubbles/tool-bubble/web-search-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/youtube-tool.d.ts +394 -0
- package/dist/bubbles/tool-bubble/youtube-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/youtube-tool.js +352 -0
- package/dist/bubbles/tool-bubble/youtube-tool.js.map +1 -0
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.d.ts +47 -36
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.d.ts.map +1 -1
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.js +96 -65
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.js.map +1 -1
- package/dist/bubbles/workflow-bubble/generate-document.workflow.d.ts +38 -38
- package/dist/bubbles/workflow-bubble/generate-document.workflow.js +1 -1
- package/dist/bubbles/workflow-bubble/generate-document.workflow.js.map +1 -1
- package/dist/bubbles/workflow-bubble/parse-document.workflow.d.ts +42 -42
- package/dist/bubbles/workflow-bubble/parse-document.workflow.js +1 -1
- package/dist/bubbles/workflow-bubble/parse-document.workflow.js.map +1 -1
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.d.ts +22 -22
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.d.ts.map +1 -1
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.js +1 -4
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.js.map +1 -1
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.d.ts +60 -60
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.d.ts.map +1 -1
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.js +2 -2
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.js.map +1 -1
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts +20 -20
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.js +1 -1
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.js.map +1 -1
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.d.ts +66 -66
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.js +1 -1
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.js.map +1 -1
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.d.ts +18 -18
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.d.ts.map +1 -1
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.js +1 -2
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.js.map +1 -1
- package/dist/bubbles.json +489 -0
- package/dist/index.d.ts +16 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/logging/BubbleLogger.d.ts +11 -0
- package/dist/logging/BubbleLogger.d.ts.map +1 -1
- package/dist/logging/BubbleLogger.js +87 -33
- package/dist/logging/BubbleLogger.js.map +1 -1
- package/dist/logging/StreamingBubbleLogger.d.ts.map +1 -1
- package/dist/logging/StreamingBubbleLogger.js +23 -16
- package/dist/logging/StreamingBubbleLogger.js.map +1 -1
- package/dist/test-gm.d.ts +10 -0
- package/dist/test-gm.d.ts.map +1 -0
- package/dist/test-gm.js +95 -0
- package/dist/test-gm.js.map +1 -0
- package/dist/types/available-tools.d.ts +1 -1
- package/dist/types/available-tools.d.ts.map +1 -1
- package/dist/types/available-tools.js +2 -0
- package/dist/types/available-tools.js.map +1 -1
- package/dist/types/base-bubble-class.d.ts +6 -1
- package/dist/types/base-bubble-class.d.ts.map +1 -1
- package/dist/types/base-bubble-class.js +47 -24
- package/dist/types/base-bubble-class.js.map +1 -1
- package/dist/types/bubble.d.ts +3 -13
- package/dist/types/bubble.d.ts.map +1 -1
- package/dist/types/bubble.js +1 -1
- package/dist/types/bubble.js.map +1 -1
- package/dist/types/service-bubble-class.d.ts +5 -5
- package/dist/types/service-bubble-class.d.ts.map +1 -1
- package/dist/types/service-bubble-class.js +6 -7
- package/dist/types/service-bubble-class.js.map +1 -1
- package/dist/types/tool-bubble-class.d.ts +1 -1
- package/dist/types/tool-bubble-class.d.ts.map +1 -1
- package/dist/types/tool-bubble-class.js +9 -3
- package/dist/types/tool-bubble-class.js.map +1 -1
- package/dist/types/workflow-bubble-class.d.ts +1 -1
- package/dist/types/workflow-bubble-class.d.ts.map +1 -1
- package/dist/utils/agent-formatter.d.ts +17 -0
- package/dist/utils/agent-formatter.d.ts.map +1 -0
- package/dist/utils/agent-formatter.js +139 -0
- package/dist/utils/agent-formatter.js.map +1 -0
- package/dist/utils/bubbleflow-validation.d.ts.map +1 -1
- package/dist/utils/bubbleflow-validation.js +89 -32
- package/dist/utils/bubbleflow-validation.js.map +1 -1
- package/dist/utils/error-sanitizer.d.ts +12 -0
- package/dist/utils/error-sanitizer.d.ts.map +1 -0
- package/dist/utils/error-sanitizer.js +77 -0
- package/dist/utils/error-sanitizer.js.map +1 -0
- package/dist/utils/json-parsing.d.ts +1 -0
- package/dist/utils/json-parsing.d.ts.map +1 -1
- package/dist/utils/json-parsing.js +205 -32
- package/dist/utils/json-parsing.js.map +1 -1
- package/package.json +6 -5
- package/dist/bubble-trigger/index.d.ts +0 -2
- package/dist/bubble-trigger/index.d.ts.map +0 -1
- package/dist/bubble-trigger/index.js +0 -2
- package/dist/bubble-trigger/index.js.map +0 -1
- package/dist/bubble-trigger/types.d.ts +0 -87
- package/dist/bubble-trigger/types.d.ts.map +0 -1
- package/dist/bubble-trigger/types.js +0 -14
- package/dist/bubble-trigger/types.js.map +0 -1
- package/dist/types/ai-models.d.ts +0 -4
- package/dist/types/ai-models.d.ts.map +0 -1
- package/dist/types/ai-models.js +0 -16
- package/dist/types/ai-models.js.map +0 -1
|
@@ -2,16 +2,16 @@ import { z } from 'zod';
|
|
|
2
2
|
import { ServiceBubble } from '../../types/service-bubble-class.js';
|
|
3
3
|
import { CredentialType, BUBBLE_CREDENTIAL_OPTIONS, } from '@bubblelab/shared-schemas';
|
|
4
4
|
import { StateGraph, MessagesAnnotation } from '@langchain/langgraph';
|
|
5
|
-
import { ToolNode } from '@langchain/langgraph/prebuilt';
|
|
6
5
|
import { ChatOpenAI } from '@langchain/openai';
|
|
7
6
|
import { ChatAnthropic } from '@langchain/anthropic';
|
|
8
7
|
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
|
|
9
|
-
import { HumanMessage, AIMessage, ToolMessage } from '@langchain/core/messages';
|
|
8
|
+
import { HumanMessage, AIMessage, ToolMessage, AIMessageChunk, } from '@langchain/core/messages';
|
|
10
9
|
import { DynamicStructuredTool } from '@langchain/core/tools';
|
|
11
|
-
import { AvailableModels } from '
|
|
12
|
-
import { AvailableTools } from '../../types/available-tools.js';
|
|
10
|
+
import { AvailableModels } from '@bubblelab/shared-schemas';
|
|
11
|
+
import { AvailableTools, } from '../../types/available-tools.js';
|
|
13
12
|
import { BubbleFactory } from '../../bubble-factory.js';
|
|
14
|
-
import {
|
|
13
|
+
import { extractAndStreamThinkingTokens, formatFinalResponse, } from '../../utils/agent-formatter.js';
|
|
14
|
+
import { isAIMessage, isAIMessageChunk } from '@langchain/core/messages';
|
|
15
15
|
// Define model configuration
|
|
16
16
|
const ModelConfigSchema = z.object({
|
|
17
17
|
model: AvailableModels.default('google/gemini-2.5-flash').describe('AI model to use (format: provider/model-name).'),
|
|
@@ -19,20 +19,20 @@ const ModelConfigSchema = z.object({
|
|
|
19
19
|
.number()
|
|
20
20
|
.min(0)
|
|
21
21
|
.max(2)
|
|
22
|
-
.default(
|
|
22
|
+
.default(1)
|
|
23
23
|
.describe('Temperature for response randomness (0 = deterministic, 2 = very random)'),
|
|
24
24
|
maxTokens: z
|
|
25
25
|
.number()
|
|
26
26
|
.positive()
|
|
27
27
|
.optional()
|
|
28
|
-
.default(
|
|
29
|
-
.describe('Maximum number of tokens to generate in response'),
|
|
28
|
+
.default(12800)
|
|
29
|
+
.describe('Maximum number of tokens to generate in response, keep at default of 40000 unless the response is expected to be certain length'),
|
|
30
30
|
jsonMode: z
|
|
31
31
|
.boolean()
|
|
32
32
|
.default(false)
|
|
33
33
|
.describe('When true, strips markdown formatting and returns clean JSON response'),
|
|
34
34
|
});
|
|
35
|
-
// Define tool configuration
|
|
35
|
+
// Define tool configuration for pre-registered tools
|
|
36
36
|
const ToolConfigSchema = z.object({
|
|
37
37
|
name: AvailableTools.describe('Name of the tool type or tool bubble to enable for the AI agent'),
|
|
38
38
|
credentials: z
|
|
@@ -45,6 +45,25 @@ const ToolConfigSchema = z.object({
|
|
|
45
45
|
.optional()
|
|
46
46
|
.describe('Configuration for the tool or tool bubble'),
|
|
47
47
|
});
|
|
48
|
+
// Define custom tool schema for runtime-defined tools
|
|
49
|
+
const CustomToolSchema = z.object({
|
|
50
|
+
name: z
|
|
51
|
+
.string()
|
|
52
|
+
.min(1)
|
|
53
|
+
.describe('Unique name for your custom tool (e.g., "calculate-tax")'),
|
|
54
|
+
description: z
|
|
55
|
+
.string()
|
|
56
|
+
.min(1)
|
|
57
|
+
.describe('Description of what the tool does - helps the AI know when to use it'),
|
|
58
|
+
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") }'),
|
|
61
|
+
func: z
|
|
62
|
+
.function()
|
|
63
|
+
.args(z.record(z.string(), z.unknown()))
|
|
64
|
+
.returns(z.promise(z.unknown()))
|
|
65
|
+
.describe('Async function that executes the tool logic. Receives params matching the schema and returns a result.'),
|
|
66
|
+
});
|
|
48
67
|
// Define image input schemas - supports both base64 data and URLs
|
|
49
68
|
const Base64ImageSchema = z.object({
|
|
50
69
|
type: z.literal('base64').default('base64'),
|
|
@@ -107,12 +126,18 @@ const AIAgentParamsSchema = z.object({
|
|
|
107
126
|
},
|
|
108
127
|
},
|
|
109
128
|
])
|
|
110
|
-
.describe('Array of tools the AI agent can use. Can be tool types (web-search-tool, web-scrape-tool, web-crawl-tool, web-extract-tool). If using image models, set the tools to []'),
|
|
129
|
+
.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
|
+
customTools: z
|
|
131
|
+
.array(CustomToolSchema)
|
|
132
|
+
.default([])
|
|
133
|
+
.optional()
|
|
134
|
+
.describe('Array of custom runtime-defined tools with their own schemas and functions. Use this to add domain-specific tools without pre-registration. Example: [{ name: "calculate-tax", description: "Calculates sales tax", schema: { amount: z.number() }, func: async (input) => {...} }]'),
|
|
111
135
|
maxIterations: z
|
|
112
136
|
.number()
|
|
113
137
|
.positive()
|
|
138
|
+
.min(2)
|
|
114
139
|
.default(10)
|
|
115
|
-
.describe('Maximum number of iterations for the agent workflow'),
|
|
140
|
+
.describe('Maximum number of iterations for the agent workflow, 2 iterations per turn of conversation'),
|
|
116
141
|
credentials: z
|
|
117
142
|
.record(z.nativeEnum(CredentialType), z.string())
|
|
118
143
|
.optional()
|
|
@@ -121,6 +146,8 @@ const AIAgentParamsSchema = z.object({
|
|
|
121
146
|
.boolean()
|
|
122
147
|
.default(false)
|
|
123
148
|
.describe('Enable real-time streaming of tokens, tool calls, and iteration progress'),
|
|
149
|
+
// Note: beforeToolCall and afterToolCall are function hooks added via TypeScript interface
|
|
150
|
+
// They cannot be part of the Zod schema but are available in the params
|
|
124
151
|
});
|
|
125
152
|
const AIAgentResultSchema = z.object({
|
|
126
153
|
response: z
|
|
@@ -161,11 +188,18 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
161
188
|
`;
|
|
162
189
|
static alias = 'agent';
|
|
163
190
|
factory;
|
|
191
|
+
beforeToolCallHook;
|
|
192
|
+
afterToolCallHook;
|
|
193
|
+
streamingCallback;
|
|
194
|
+
shouldStopAfterTools = false;
|
|
164
195
|
constructor(params = {
|
|
165
196
|
message: 'Hello, how are you?',
|
|
166
197
|
systemPrompt: 'You are a helpful AI assistant',
|
|
167
198
|
}, context) {
|
|
168
199
|
super(params, context);
|
|
200
|
+
this.beforeToolCallHook = params.beforeToolCall;
|
|
201
|
+
this.afterToolCallHook = params.afterToolCall;
|
|
202
|
+
this.streamingCallback = params.streamingCallback;
|
|
169
203
|
this.factory = new BubbleFactory();
|
|
170
204
|
}
|
|
171
205
|
async testCredential() {
|
|
@@ -180,12 +214,12 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
180
214
|
async performAction(context) {
|
|
181
215
|
// Context is available but not currently used in this implementation
|
|
182
216
|
void context;
|
|
183
|
-
const { message, images, systemPrompt, model, tools, maxIterations } = this.params;
|
|
217
|
+
const { message, images, systemPrompt, model, tools, customTools, maxIterations, } = this.params;
|
|
184
218
|
try {
|
|
185
219
|
// Initialize the language model
|
|
186
220
|
const llm = this.initializeModel(model);
|
|
187
|
-
// Initialize tools
|
|
188
|
-
const agentTools = await this.initializeTools(tools);
|
|
221
|
+
// Initialize tools (both pre-registered and custom)
|
|
222
|
+
const agentTools = await this.initializeTools(tools, customTools);
|
|
189
223
|
// Create the agent graph
|
|
190
224
|
const graph = await this.createAgentGraph(llm, agentTools, systemPrompt);
|
|
191
225
|
// Execute the agent
|
|
@@ -211,7 +245,7 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
211
245
|
async actionWithStreaming(streamingCallback, context) {
|
|
212
246
|
// Context is available but not currently used in this implementation
|
|
213
247
|
void context;
|
|
214
|
-
const { message, images, systemPrompt, model, tools, maxIterations } = this.params;
|
|
248
|
+
const { message, images, systemPrompt, model, tools, customTools, maxIterations, } = this.params;
|
|
215
249
|
const startTime = Date.now();
|
|
216
250
|
// Send start event
|
|
217
251
|
await streamingCallback({
|
|
@@ -233,8 +267,8 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
233
267
|
});
|
|
234
268
|
// Initialize the language model
|
|
235
269
|
const llm = this.initializeModel(model);
|
|
236
|
-
// Initialize tools
|
|
237
|
-
const agentTools = await this.initializeTools(tools);
|
|
270
|
+
// Initialize tools (both pre-registered and custom)
|
|
271
|
+
const agentTools = await this.initializeTools(tools, customTools);
|
|
238
272
|
// Create the agent graph
|
|
239
273
|
const graph = await this.createAgentGraph(llm, agentTools, systemPrompt);
|
|
240
274
|
// Execute the agent with streaming
|
|
@@ -292,70 +326,16 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
292
326
|
throw new Error(`Unsupported model provider: ${provider}`);
|
|
293
327
|
}
|
|
294
328
|
}
|
|
295
|
-
/**
|
|
296
|
-
* Format final response with special handling for Gemini image models and JSON mode
|
|
297
|
-
*/
|
|
298
|
-
async formatFinalResponse(response, modelConfig, jsonMode) {
|
|
299
|
-
let finalResponse = typeof response === 'string' ? response : JSON.stringify(response);
|
|
300
|
-
// Special handling for Gemini image models that return images in inlineData format
|
|
301
|
-
if (modelConfig.model.includes('gemini') &&
|
|
302
|
-
modelConfig.model.includes('image')) {
|
|
303
|
-
finalResponse = this.formatGeminiImageResponse(finalResponse);
|
|
304
|
-
}
|
|
305
|
-
else if (jsonMode && typeof finalResponse === 'string') {
|
|
306
|
-
// Handle JSON mode: use the improved utility function
|
|
307
|
-
const result = parseJsonWithFallbacks(finalResponse);
|
|
308
|
-
if (!result.success) {
|
|
309
|
-
return {
|
|
310
|
-
response: result.response,
|
|
311
|
-
error: `${this.params.name || 'AI Agent'} failed to generate valid JSON. Post-processing attempted but JSON is still malformed. Original response: ${finalResponse}`,
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
return { response: result.response };
|
|
315
|
-
}
|
|
316
|
-
return { response: finalResponse };
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Convert Gemini's inlineData format to LangChain-compatible data URI format
|
|
320
|
-
*/
|
|
321
|
-
formatGeminiImageResponse(response) {
|
|
322
|
-
if (typeof response !== 'string') {
|
|
323
|
-
return String(response);
|
|
324
|
-
}
|
|
325
|
-
try {
|
|
326
|
-
console.log('[AIAgent] Formatting Gemini image response...');
|
|
327
|
-
// Look for Gemini's inlineData format in the response
|
|
328
|
-
const inlineDataRegex = /\{\s*"inlineData"\s*:\s*\{\s*"mimeType"\s*:\s*"([^"]+)"\s*,\s*"data"\s*:\s*"([^"]+)"\s*\}\s*\}/;
|
|
329
|
-
const match = response.match(inlineDataRegex);
|
|
330
|
-
if (match) {
|
|
331
|
-
const [, mimeType, data] = match;
|
|
332
|
-
const dataUri = `data:${mimeType};base64,${data}`;
|
|
333
|
-
console.log(`[AIAgent] Extracted first data URI from Gemini inlineData: ${mimeType}`);
|
|
334
|
-
return dataUri;
|
|
335
|
-
}
|
|
336
|
-
// Also check for the more complex format with text
|
|
337
|
-
const complexInlineDataRegex = /\{\s*"inlineData"\s*:\s*\{\s*"mimeType"\s*:\s*"([^"]+)"\s*,\s*"data"\s*:\s*"([^"]+)"/;
|
|
338
|
-
const complexMatch = response.match(complexInlineDataRegex);
|
|
339
|
-
if (complexMatch) {
|
|
340
|
-
const [, mimeType, data] = complexMatch;
|
|
341
|
-
const dataUri = `data:${mimeType};base64,${data}`;
|
|
342
|
-
console.log(`[AIAgent] Extracted first data URI from complex Gemini inlineData: ${mimeType}`);
|
|
343
|
-
return dataUri;
|
|
344
|
-
}
|
|
345
|
-
// If no inlineData found, return original response
|
|
346
|
-
return response;
|
|
347
|
-
}
|
|
348
|
-
catch (error) {
|
|
349
|
-
console.warn('[AIAgent] Error formatting Gemini image response:', error);
|
|
350
|
-
return response;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
329
|
initializeModel(modelConfig) {
|
|
354
330
|
const { model, temperature, maxTokens } = modelConfig;
|
|
355
|
-
const
|
|
331
|
+
const slashIndex = model.indexOf('/');
|
|
332
|
+
const provider = model.substring(0, slashIndex);
|
|
333
|
+
const modelName = model.substring(slashIndex + 1);
|
|
356
334
|
// Use chooseCredential to get the appropriate credential
|
|
357
335
|
// This will throw immediately if credentials are missing
|
|
358
336
|
const apiKey = this.chooseCredential();
|
|
337
|
+
// Enable streaming if streamingCallback is provided
|
|
338
|
+
const enableStreaming = !!this.streamingCallback;
|
|
359
339
|
switch (provider) {
|
|
360
340
|
case 'openai':
|
|
361
341
|
return new ChatOpenAI({
|
|
@@ -363,6 +343,7 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
363
343
|
temperature,
|
|
364
344
|
maxTokens,
|
|
365
345
|
apiKey,
|
|
346
|
+
streaming: enableStreaming,
|
|
366
347
|
});
|
|
367
348
|
case 'google':
|
|
368
349
|
return new ChatGoogleGenerativeAI({
|
|
@@ -370,39 +351,70 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
370
351
|
temperature,
|
|
371
352
|
maxOutputTokens: maxTokens,
|
|
372
353
|
apiKey,
|
|
354
|
+
streaming: enableStreaming,
|
|
373
355
|
});
|
|
374
356
|
case 'anthropic':
|
|
375
357
|
return new ChatAnthropic({
|
|
376
358
|
model: modelName,
|
|
377
359
|
temperature,
|
|
378
360
|
anthropicApiKey: apiKey,
|
|
379
|
-
maxTokens
|
|
380
|
-
streaming:
|
|
361
|
+
maxTokens,
|
|
362
|
+
streaming: enableStreaming,
|
|
381
363
|
apiKey,
|
|
382
364
|
});
|
|
383
365
|
case 'openrouter':
|
|
366
|
+
console.log('openrouter', modelName);
|
|
384
367
|
return new ChatOpenAI({
|
|
385
368
|
model: modelName,
|
|
369
|
+
__includeRawResponse: true,
|
|
386
370
|
temperature,
|
|
387
371
|
maxTokens,
|
|
388
372
|
apiKey,
|
|
373
|
+
streaming: enableStreaming,
|
|
389
374
|
configuration: {
|
|
390
375
|
baseURL: 'https://openrouter.ai/api/v1',
|
|
391
376
|
},
|
|
377
|
+
modelKwargs: {
|
|
378
|
+
reasoning: {
|
|
379
|
+
effort: 'medium',
|
|
380
|
+
exclude: false,
|
|
381
|
+
},
|
|
382
|
+
},
|
|
392
383
|
});
|
|
393
384
|
default:
|
|
394
385
|
throw new Error(`Unsupported model provider: ${provider}`);
|
|
395
386
|
}
|
|
396
387
|
}
|
|
397
|
-
async initializeTools(toolConfigs) {
|
|
388
|
+
async initializeTools(toolConfigs, customToolConfigs = []) {
|
|
398
389
|
const tools = [];
|
|
399
390
|
await this.factory.registerDefaults();
|
|
391
|
+
// First, initialize custom tools
|
|
392
|
+
for (const customTool of customToolConfigs) {
|
|
393
|
+
try {
|
|
394
|
+
console.log(`🛠️ [AIAgent] Initializing custom tool: ${customTool.name}`);
|
|
395
|
+
const dynamicTool = new DynamicStructuredTool({
|
|
396
|
+
name: customTool.name,
|
|
397
|
+
description: customTool.description,
|
|
398
|
+
schema: z.object(customTool.schema),
|
|
399
|
+
func: customTool.func,
|
|
400
|
+
});
|
|
401
|
+
tools.push(dynamicTool);
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
console.error(`Error initializing custom tool '${customTool.name}':`, error);
|
|
405
|
+
// Continue with other tools even if one fails
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// Then, initialize pre-registered tools from factory
|
|
400
410
|
for (const toolConfig of toolConfigs) {
|
|
401
411
|
try {
|
|
402
|
-
// Get the tool bubble class from the factory
|
|
403
412
|
const ToolBubbleClass = this.factory.get(toolConfig.name);
|
|
404
413
|
if (!ToolBubbleClass) {
|
|
405
|
-
|
|
414
|
+
if (this.context && this.context.logger) {
|
|
415
|
+
this.context.logger.warn(`Tool bubble '${toolConfig.name}' not found in factory. This tool will not be used.`);
|
|
416
|
+
}
|
|
417
|
+
console.warn(`Tool bubble '${toolConfig.name}' not found in factory. This tool will not be used.`);
|
|
406
418
|
continue;
|
|
407
419
|
}
|
|
408
420
|
// Check if it's a tool bubble (has toAgentTool method)
|
|
@@ -447,6 +459,107 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
447
459
|
}
|
|
448
460
|
return tools;
|
|
449
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Custom tool execution node that supports hooks
|
|
464
|
+
*/
|
|
465
|
+
async executeToolsWithHooks(state, tools) {
|
|
466
|
+
const { messages } = state;
|
|
467
|
+
const lastMessage = messages[messages.length - 1];
|
|
468
|
+
const toolCalls = lastMessage.tool_calls || [];
|
|
469
|
+
const toolMessages = [];
|
|
470
|
+
let currentMessages = [...messages];
|
|
471
|
+
// Reset stop flag at the start of tool execution
|
|
472
|
+
this.shouldStopAfterTools = false;
|
|
473
|
+
// Execute each tool call
|
|
474
|
+
for (const toolCall of toolCalls) {
|
|
475
|
+
const tool = tools.find((t) => t.name === toolCall.name);
|
|
476
|
+
if (!tool) {
|
|
477
|
+
console.warn(`Tool ${toolCall.name} not found`);
|
|
478
|
+
toolMessages.push(new ToolMessage({
|
|
479
|
+
content: `Error: Tool ${toolCall.name} not found`,
|
|
480
|
+
tool_call_id: toolCall.id,
|
|
481
|
+
}));
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
try {
|
|
485
|
+
// Call beforeToolCall hook if provided
|
|
486
|
+
const hookResult_before = await this.beforeToolCallHook?.({
|
|
487
|
+
toolName: toolCall.name,
|
|
488
|
+
toolInput: toolCall.args,
|
|
489
|
+
messages: currentMessages,
|
|
490
|
+
});
|
|
491
|
+
const startTime = Date.now();
|
|
492
|
+
this.streamingCallback?.({
|
|
493
|
+
type: 'tool_start',
|
|
494
|
+
data: {
|
|
495
|
+
tool: toolCall.name,
|
|
496
|
+
input: toolCall.args,
|
|
497
|
+
callId: toolCall.id,
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
// If hook returns modified messages/toolInput, apply them
|
|
501
|
+
if (hookResult_before) {
|
|
502
|
+
if (hookResult_before.messages) {
|
|
503
|
+
currentMessages = hookResult_before.messages;
|
|
504
|
+
}
|
|
505
|
+
toolCall.args = hookResult_before.toolInput;
|
|
506
|
+
}
|
|
507
|
+
// Execute the tool
|
|
508
|
+
const toolOutput = await tool.invoke(toolCall.args);
|
|
509
|
+
// Create tool message
|
|
510
|
+
const toolMessage = new ToolMessage({
|
|
511
|
+
content: typeof toolOutput === 'string'
|
|
512
|
+
? toolOutput
|
|
513
|
+
: JSON.stringify(toolOutput),
|
|
514
|
+
tool_call_id: toolCall.id,
|
|
515
|
+
});
|
|
516
|
+
toolMessages.push(toolMessage);
|
|
517
|
+
currentMessages = [...currentMessages, toolMessage];
|
|
518
|
+
// Call afterToolCall hook if provided
|
|
519
|
+
const hookResult_after = await this.afterToolCallHook?.({
|
|
520
|
+
toolName: toolCall.name,
|
|
521
|
+
toolInput: toolCall.args,
|
|
522
|
+
toolOutput,
|
|
523
|
+
messages: currentMessages,
|
|
524
|
+
});
|
|
525
|
+
// If hook returns modified messages, update current messages
|
|
526
|
+
if (hookResult_after) {
|
|
527
|
+
if (hookResult_after.messages) {
|
|
528
|
+
currentMessages = hookResult_after.messages;
|
|
529
|
+
}
|
|
530
|
+
// Check if hook wants to stop execution
|
|
531
|
+
if (hookResult_after.shouldStop === true) {
|
|
532
|
+
this.shouldStopAfterTools = true;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
this.streamingCallback?.({
|
|
536
|
+
type: 'tool_complete',
|
|
537
|
+
data: {
|
|
538
|
+
callId: toolCall.id,
|
|
539
|
+
input: toolCall.args,
|
|
540
|
+
tool: toolCall.name,
|
|
541
|
+
output: toolOutput,
|
|
542
|
+
duration: Date.now() - startTime,
|
|
543
|
+
},
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
catch (error) {
|
|
547
|
+
console.error(`Error executing tool ${toolCall.name}:`, error);
|
|
548
|
+
const errorMessage = new ToolMessage({
|
|
549
|
+
content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
550
|
+
tool_call_id: toolCall.id,
|
|
551
|
+
});
|
|
552
|
+
toolMessages.push(errorMessage);
|
|
553
|
+
currentMessages = [...currentMessages, errorMessage];
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
// Return the updated messages
|
|
557
|
+
// If hooks modified messages, use those; otherwise use the original messages + tool messages
|
|
558
|
+
if (currentMessages.length !== messages.length + toolMessages.length) {
|
|
559
|
+
return { messages: currentMessages };
|
|
560
|
+
}
|
|
561
|
+
return { messages: toolMessages };
|
|
562
|
+
}
|
|
450
563
|
async createAgentGraph(llm, tools, systemPrompt) {
|
|
451
564
|
// Define the agent node
|
|
452
565
|
const agentNode = async ({ messages }) => {
|
|
@@ -455,8 +568,52 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
455
568
|
const allMessages = [systemMessage, ...messages];
|
|
456
569
|
// If we have tools, bind them to the LLM
|
|
457
570
|
const modelWithTools = tools.length > 0 ? llm.bindTools(tools) : llm;
|
|
458
|
-
|
|
459
|
-
|
|
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) {
|
|
591
|
+
await this.streamingCallback?.({
|
|
592
|
+
type: 'think',
|
|
593
|
+
data: {
|
|
594
|
+
content: thinking,
|
|
595
|
+
messageId,
|
|
596
|
+
},
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
await this.streamingCallback?.({
|
|
600
|
+
type: 'llm_complete',
|
|
601
|
+
data: {
|
|
602
|
+
messageId,
|
|
603
|
+
totalTokens: output.llmOutput?.usage_metadata?.total_tokens,
|
|
604
|
+
},
|
|
605
|
+
});
|
|
606
|
+
},
|
|
607
|
+
},
|
|
608
|
+
],
|
|
609
|
+
});
|
|
610
|
+
return { messages: [response] };
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
// Non-streaming fallback
|
|
614
|
+
const response = await modelWithTools.invoke(allMessages);
|
|
615
|
+
return { messages: [response] };
|
|
616
|
+
}
|
|
460
617
|
};
|
|
461
618
|
// Define conditional edge function
|
|
462
619
|
const shouldContinue = ({ messages }) => {
|
|
@@ -467,16 +624,27 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
467
624
|
}
|
|
468
625
|
return '__end__';
|
|
469
626
|
};
|
|
627
|
+
// Define conditional edge after tools to check if we should stop
|
|
628
|
+
const shouldContinueAfterTools = () => {
|
|
629
|
+
// Check if the afterToolCall hook requested stopping
|
|
630
|
+
if (this.shouldStopAfterTools) {
|
|
631
|
+
return '__end__';
|
|
632
|
+
}
|
|
633
|
+
// Otherwise continue back to agent
|
|
634
|
+
return 'agent';
|
|
635
|
+
};
|
|
470
636
|
// Build the graph
|
|
471
637
|
const graph = new StateGraph(MessagesAnnotation).addNode('agent', agentNode);
|
|
472
638
|
if (tools.length > 0) {
|
|
473
|
-
// Use
|
|
474
|
-
const toolNode =
|
|
639
|
+
// Use custom tool node with hooks support
|
|
640
|
+
const toolNode = async (state) => {
|
|
641
|
+
return await this.executeToolsWithHooks(state, tools);
|
|
642
|
+
};
|
|
475
643
|
graph
|
|
476
644
|
.addNode('tools', toolNode)
|
|
477
645
|
.addEdge('__start__', 'agent')
|
|
478
646
|
.addConditionalEdges('agent', shouldContinue)
|
|
479
|
-
.
|
|
647
|
+
.addConditionalEdges('tools', shouldContinueAfterTools);
|
|
480
648
|
}
|
|
481
649
|
else {
|
|
482
650
|
graph.addEdge('__start__', 'agent').addEdge('agent', '__end__');
|
|
@@ -545,15 +713,16 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
545
713
|
console.log('[AIAgent] Graph execution completed');
|
|
546
714
|
console.log('[AIAgent] Total messages:', result.messages.length);
|
|
547
715
|
iterations = result.messages.length;
|
|
548
|
-
// Extract tool calls from messages
|
|
716
|
+
// Extract tool calls from messages and track individual LLM calls
|
|
549
717
|
// Store tool calls temporarily to match with their responses
|
|
550
718
|
const toolCallMap = new Map();
|
|
551
719
|
for (let i = 0; i < result.messages.length; i++) {
|
|
552
720
|
const msg = result.messages[i];
|
|
553
|
-
if (msg instanceof AIMessage
|
|
721
|
+
if (msg instanceof AIMessage ||
|
|
722
|
+
(msg instanceof AIMessageChunk && msg.tool_calls)) {
|
|
554
723
|
const typedToolCalls = msg.tool_calls;
|
|
555
724
|
// Log and track tool calls
|
|
556
|
-
for (const toolCall of typedToolCalls) {
|
|
725
|
+
for (const toolCall of typedToolCalls || []) {
|
|
557
726
|
toolCallMap.set(toolCall.id, {
|
|
558
727
|
name: toolCall.name,
|
|
559
728
|
args: toolCall.args,
|
|
@@ -588,7 +757,7 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
588
757
|
}
|
|
589
758
|
// Get the final AI message response
|
|
590
759
|
console.log('[AIAgent] Filtering AI messages...');
|
|
591
|
-
const aiMessages = result.messages.filter((msg) => msg
|
|
760
|
+
const aiMessages = result.messages.filter((msg) => isAIMessage(msg) || isAIMessageChunk(msg));
|
|
592
761
|
console.log('[AIAgent] Found', aiMessages.length, 'AI messages');
|
|
593
762
|
const finalMessage = aiMessages[aiMessages.length - 1];
|
|
594
763
|
// Check for MAX_TOKENS finish reason
|
|
@@ -601,10 +770,17 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
601
770
|
let totalOutputTokens = 0;
|
|
602
771
|
let totalTokensSum = 0;
|
|
603
772
|
for (const msg of result.messages) {
|
|
604
|
-
if (msg instanceof AIMessage
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
773
|
+
if (msg instanceof AIMessage ||
|
|
774
|
+
(msg instanceof AIMessageChunk && msg.usage_metadata)) {
|
|
775
|
+
totalInputTokens +=
|
|
776
|
+
msg.usage_metadata?.input_tokens ||
|
|
777
|
+
0;
|
|
778
|
+
totalOutputTokens +=
|
|
779
|
+
msg.usage_metadata?.output_tokens ||
|
|
780
|
+
0;
|
|
781
|
+
totalTokensSum +=
|
|
782
|
+
msg.usage_metadata?.total_tokens ||
|
|
783
|
+
0;
|
|
608
784
|
}
|
|
609
785
|
}
|
|
610
786
|
if (totalTokensSum > 0 && this.context && this.context.logger) {
|
|
@@ -615,12 +791,13 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
615
791
|
modelName: this.params.model.model,
|
|
616
792
|
}, `LLM completion: ${totalInputTokens} input + ${totalOutputTokens} output = ${totalTokensSum} total tokens`, {
|
|
617
793
|
bubbleName: 'ai-agent',
|
|
794
|
+
variableId: this.context?.variableId,
|
|
618
795
|
operationType: 'bubble_execution',
|
|
619
796
|
});
|
|
620
797
|
}
|
|
621
798
|
const response = finalMessage?.content || 'No response generated';
|
|
622
799
|
// Use shared formatting method
|
|
623
|
-
const formattedResult = await
|
|
800
|
+
const formattedResult = await formatFinalResponse(response, this.params.model.model, jsonMode);
|
|
624
801
|
// If there's an error from formatting (e.g., invalid JSON), return early
|
|
625
802
|
if (formattedResult.error) {
|
|
626
803
|
return {
|
|
@@ -778,6 +955,7 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
778
955
|
};
|
|
779
956
|
this.context.logger.logTokenUsage(tokenUsage, `LLM completion: ${tokenUsage.inputTokens} input + ${tokenUsage.outputTokens} output = ${tokenUsage.totalTokens} total tokens`, {
|
|
780
957
|
bubbleName: 'ai-agent',
|
|
958
|
+
variableId: this.context?.variableId,
|
|
781
959
|
operationType: 'bubble_execution',
|
|
782
960
|
});
|
|
783
961
|
}
|
|
@@ -824,6 +1002,8 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
824
1002
|
type: 'tool_complete',
|
|
825
1003
|
data: {
|
|
826
1004
|
callId,
|
|
1005
|
+
input: callData.args,
|
|
1006
|
+
tool: callData.name,
|
|
827
1007
|
output: event.data.output,
|
|
828
1008
|
duration,
|
|
829
1009
|
},
|
|
@@ -856,7 +1036,7 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
856
1036
|
// Process final result
|
|
857
1037
|
const accumulatedResponse = accumulatedContent || 'No response generated';
|
|
858
1038
|
// Use shared formatting method
|
|
859
|
-
const formattedResult = await
|
|
1039
|
+
const formattedResult = await formatFinalResponse(accumulatedResponse, this.params.model.model, jsonMode);
|
|
860
1040
|
// If there's an error from formatting (e.g., invalid JSON), return early with consistent behavior
|
|
861
1041
|
if (formattedResult.error) {
|
|
862
1042
|
return {
|