@bubblelab/bubble-core 0.1.6 → 0.1.9
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 +628 -299
- package/dist/bubble-factory.d.ts.map +1 -1
- package/dist/bubble-factory.js +13 -9
- package/dist/bubble-factory.js.map +1 -1
- package/dist/bubbles/service-bubble/ai-agent.d.ts +129 -111
- package/dist/bubbles/service-bubble/ai-agent.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/ai-agent.js +273 -95
- 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/api-scraper.schema.d.ts +370 -0
- package/dist/bubbles/service-bubble/apify/api-scraper.schema.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/api-scraper.schema.js +14 -0
- package/dist/bubbles/service-bubble/apify/api-scraper.schema.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/apify-scraper.schema.d.ts +1770 -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 +38 -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 +4 -0
- package/dist/bubbles/service-bubble/apify/index.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/index.js +3 -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 +6 -0
- package/dist/bubbles/service-bubble/apify/types.js.map +1 -0
- package/dist/bubbles/service-bubble/apify.d.ts +136 -0
- package/dist/bubbles/service-bubble/apify.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify.js +282 -0
- package/dist/bubbles/service-bubble/apify.js.map +1 -0
- package/dist/bubbles/service-bubble/gmail.d.ts +376 -376
- package/dist/bubbles/service-bubble/google-calendar.d.ts +24 -24
- package/dist/bubbles/service-bubble/google-drive.d.ts +68 -68
- package/dist/bubbles/service-bubble/google-sheets.d.ts +64 -64
- package/dist/bubbles/service-bubble/hello-world.d.ts +4 -4
- package/dist/bubbles/service-bubble/http.d.ts +16 -16
- package/dist/bubbles/service-bubble/postgresql.d.ts +12 -12
- package/dist/bubbles/service-bubble/resend.d.ts +17 -17
- 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 +470 -470
- package/dist/bubbles/service-bubble/storage.d.ts +32 -32
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.d.ts +4 -4
- package/dist/bubbles/tool-bubble/chart-js-tool.d.ts +12 -12
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts +18 -5
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.js +85 -39
- 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/list-bubbles-tool.d.ts +4 -4
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.d.ts +14 -14
- package/dist/bubbles/tool-bubble/research-agent-tool.d.ts +14 -14
- package/dist/bubbles/tool-bubble/research-agent-tool.js +1 -1
- package/dist/bubbles/tool-bubble/research-agent-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/sql-query-tool.d.ts +8 -8
- package/dist/bubbles/tool-bubble/tool-template.d.ts +4 -4
- package/dist/bubbles/tool-bubble/web-crawl-tool.d.ts +4 -4
- package/dist/bubbles/tool-bubble/web-extract-tool.d.ts +8 -8
- package/dist/bubbles/tool-bubble/web-scrape-tool.d.ts +4 -4
- 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 +10 -10
- package/dist/bubbles/tool-bubble/web-search-tool.js +1 -1
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.d.ts +8 -8
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.d.ts.map +1 -1
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.js +5 -0
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.js.map +1 -1
- package/dist/bubbles/workflow-bubble/database-analyzer.workflow.d.ts +4 -4
- package/dist/bubbles/workflow-bubble/generate-document.workflow.d.ts +14 -14
- 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 +14 -14
- 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 +106 -106
- 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 +28 -28
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.js +1 -1
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.js.map +1 -1
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts +12 -12
- 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 +32 -32
- 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 +14 -14
- 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 +474 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- 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 +69 -22
- package/dist/logging/BubbleLogger.js.map +1 -1
- package/dist/logging/StreamingBubbleLogger.d.ts.map +1 -1
- package/dist/logging/StreamingBubbleLogger.js +18 -11
- package/dist/logging/StreamingBubbleLogger.js.map +1 -1
- package/dist/types/ai-models.d.ts +1 -1
- package/dist/types/ai-models.d.ts.map +1 -1
- package/dist/types/ai-models.js +4 -0
- package/dist/types/ai-models.js.map +1 -1
- package/dist/types/api-scraper.schema.d.ts +453 -0
- package/dist/types/api-scraper.schema.d.ts.map +1 -0
- package/dist/types/api-scraper.schema.js +160 -0
- package/dist/types/api-scraper.schema.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 +5 -0
- package/dist/types/base-bubble-class.d.ts.map +1 -1
- package/dist/types/base-bubble-class.js +18 -3
- package/dist/types/base-bubble-class.js.map +1 -1
- package/dist/types/bubble.d.ts +2 -3
- package/dist/types/bubble.d.ts.map +1 -1
- package/dist/types/service-bubble-class.d.ts +4 -4
- 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.map +1 -1
- package/dist/types/tool-bubble-class.js +9 -1
- package/dist/types/tool-bubble-class.js.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/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 +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-agent.d.ts","sourceRoot":"","sources":["../../../src/bubbles/service-bubble/ai-agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EACL,cAAc,EAEf,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"ai-agent.d.ts","sourceRoot":"","sources":["../../../src/bubbles/service-bubble/ai-agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
28
|
.default(40000)
|
|
29
|
-
.describe('Maximum number of tokens to generate in response'),
|
|
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,106 @@ 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
|
+
tool: toolCall.name,
|
|
540
|
+
output: toolOutput,
|
|
541
|
+
duration: Date.now() - startTime,
|
|
542
|
+
},
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
catch (error) {
|
|
546
|
+
console.error(`Error executing tool ${toolCall.name}:`, error);
|
|
547
|
+
const errorMessage = new ToolMessage({
|
|
548
|
+
content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
549
|
+
tool_call_id: toolCall.id,
|
|
550
|
+
});
|
|
551
|
+
toolMessages.push(errorMessage);
|
|
552
|
+
currentMessages = [...currentMessages, errorMessage];
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
// Return the updated messages
|
|
556
|
+
// If hooks modified messages, use those; otherwise use the original messages + tool messages
|
|
557
|
+
if (currentMessages.length !== messages.length + toolMessages.length) {
|
|
558
|
+
return { messages: currentMessages };
|
|
559
|
+
}
|
|
560
|
+
return { messages: toolMessages };
|
|
561
|
+
}
|
|
450
562
|
async createAgentGraph(llm, tools, systemPrompt) {
|
|
451
563
|
// Define the agent node
|
|
452
564
|
const agentNode = async ({ messages }) => {
|
|
@@ -455,8 +567,52 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
455
567
|
const allMessages = [systemMessage, ...messages];
|
|
456
568
|
// If we have tools, bind them to the LLM
|
|
457
569
|
const modelWithTools = tools.length > 0 ? llm.bindTools(tools) : llm;
|
|
458
|
-
|
|
459
|
-
|
|
570
|
+
// Use streaming if streamingCallback is provided
|
|
571
|
+
if (this.streamingCallback) {
|
|
572
|
+
const messageId = `msg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
573
|
+
// Use invoke with callbacks for streaming
|
|
574
|
+
const response = await modelWithTools.invoke(allMessages, {
|
|
575
|
+
callbacks: [
|
|
576
|
+
{
|
|
577
|
+
handleLLMStart: async () => {
|
|
578
|
+
await this.streamingCallback?.({
|
|
579
|
+
type: 'llm_start',
|
|
580
|
+
data: {
|
|
581
|
+
model: this.params.model.model,
|
|
582
|
+
temperature: this.params.model.temperature,
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
},
|
|
586
|
+
handleLLMEnd: async (output) => {
|
|
587
|
+
// Extract thinking tokens from different model providers
|
|
588
|
+
const thinking = extractAndStreamThinkingTokens(output);
|
|
589
|
+
if (thinking) {
|
|
590
|
+
await this.streamingCallback?.({
|
|
591
|
+
type: 'think',
|
|
592
|
+
data: {
|
|
593
|
+
content: thinking,
|
|
594
|
+
messageId,
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
await this.streamingCallback?.({
|
|
599
|
+
type: 'llm_complete',
|
|
600
|
+
data: {
|
|
601
|
+
messageId,
|
|
602
|
+
totalTokens: output.llmOutput?.usage_metadata?.total_tokens,
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
},
|
|
606
|
+
},
|
|
607
|
+
],
|
|
608
|
+
});
|
|
609
|
+
return { messages: [response] };
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
// Non-streaming fallback
|
|
613
|
+
const response = await modelWithTools.invoke(allMessages);
|
|
614
|
+
return { messages: [response] };
|
|
615
|
+
}
|
|
460
616
|
};
|
|
461
617
|
// Define conditional edge function
|
|
462
618
|
const shouldContinue = ({ messages }) => {
|
|
@@ -467,16 +623,27 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
467
623
|
}
|
|
468
624
|
return '__end__';
|
|
469
625
|
};
|
|
626
|
+
// Define conditional edge after tools to check if we should stop
|
|
627
|
+
const shouldContinueAfterTools = () => {
|
|
628
|
+
// Check if the afterToolCall hook requested stopping
|
|
629
|
+
if (this.shouldStopAfterTools) {
|
|
630
|
+
return '__end__';
|
|
631
|
+
}
|
|
632
|
+
// Otherwise continue back to agent
|
|
633
|
+
return 'agent';
|
|
634
|
+
};
|
|
470
635
|
// Build the graph
|
|
471
636
|
const graph = new StateGraph(MessagesAnnotation).addNode('agent', agentNode);
|
|
472
637
|
if (tools.length > 0) {
|
|
473
|
-
// Use
|
|
474
|
-
const toolNode =
|
|
638
|
+
// Use custom tool node with hooks support
|
|
639
|
+
const toolNode = async (state) => {
|
|
640
|
+
return await this.executeToolsWithHooks(state, tools);
|
|
641
|
+
};
|
|
475
642
|
graph
|
|
476
643
|
.addNode('tools', toolNode)
|
|
477
644
|
.addEdge('__start__', 'agent')
|
|
478
645
|
.addConditionalEdges('agent', shouldContinue)
|
|
479
|
-
.
|
|
646
|
+
.addConditionalEdges('tools', shouldContinueAfterTools);
|
|
480
647
|
}
|
|
481
648
|
else {
|
|
482
649
|
graph.addEdge('__start__', 'agent').addEdge('agent', '__end__');
|
|
@@ -545,15 +712,16 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
545
712
|
console.log('[AIAgent] Graph execution completed');
|
|
546
713
|
console.log('[AIAgent] Total messages:', result.messages.length);
|
|
547
714
|
iterations = result.messages.length;
|
|
548
|
-
// Extract tool calls from messages
|
|
715
|
+
// Extract tool calls from messages and track individual LLM calls
|
|
549
716
|
// Store tool calls temporarily to match with their responses
|
|
550
717
|
const toolCallMap = new Map();
|
|
551
718
|
for (let i = 0; i < result.messages.length; i++) {
|
|
552
719
|
const msg = result.messages[i];
|
|
553
|
-
if (msg instanceof AIMessage
|
|
720
|
+
if (msg instanceof AIMessage ||
|
|
721
|
+
(msg instanceof AIMessageChunk && msg.tool_calls)) {
|
|
554
722
|
const typedToolCalls = msg.tool_calls;
|
|
555
723
|
// Log and track tool calls
|
|
556
|
-
for (const toolCall of typedToolCalls) {
|
|
724
|
+
for (const toolCall of typedToolCalls || []) {
|
|
557
725
|
toolCallMap.set(toolCall.id, {
|
|
558
726
|
name: toolCall.name,
|
|
559
727
|
args: toolCall.args,
|
|
@@ -588,7 +756,7 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
588
756
|
}
|
|
589
757
|
// Get the final AI message response
|
|
590
758
|
console.log('[AIAgent] Filtering AI messages...');
|
|
591
|
-
const aiMessages = result.messages.filter((msg) => msg
|
|
759
|
+
const aiMessages = result.messages.filter((msg) => isAIMessage(msg) || isAIMessageChunk(msg));
|
|
592
760
|
console.log('[AIAgent] Found', aiMessages.length, 'AI messages');
|
|
593
761
|
const finalMessage = aiMessages[aiMessages.length - 1];
|
|
594
762
|
// Check for MAX_TOKENS finish reason
|
|
@@ -601,10 +769,17 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
601
769
|
let totalOutputTokens = 0;
|
|
602
770
|
let totalTokensSum = 0;
|
|
603
771
|
for (const msg of result.messages) {
|
|
604
|
-
if (msg instanceof AIMessage
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
772
|
+
if (msg instanceof AIMessage ||
|
|
773
|
+
(msg instanceof AIMessageChunk && msg.usage_metadata)) {
|
|
774
|
+
totalInputTokens +=
|
|
775
|
+
msg.usage_metadata?.input_tokens ||
|
|
776
|
+
0;
|
|
777
|
+
totalOutputTokens +=
|
|
778
|
+
msg.usage_metadata?.output_tokens ||
|
|
779
|
+
0;
|
|
780
|
+
totalTokensSum +=
|
|
781
|
+
msg.usage_metadata?.total_tokens ||
|
|
782
|
+
0;
|
|
608
783
|
}
|
|
609
784
|
}
|
|
610
785
|
if (totalTokensSum > 0 && this.context && this.context.logger) {
|
|
@@ -615,12 +790,13 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
615
790
|
modelName: this.params.model.model,
|
|
616
791
|
}, `LLM completion: ${totalInputTokens} input + ${totalOutputTokens} output = ${totalTokensSum} total tokens`, {
|
|
617
792
|
bubbleName: 'ai-agent',
|
|
793
|
+
variableId: this.context?.variableId,
|
|
618
794
|
operationType: 'bubble_execution',
|
|
619
795
|
});
|
|
620
796
|
}
|
|
621
797
|
const response = finalMessage?.content || 'No response generated';
|
|
622
798
|
// Use shared formatting method
|
|
623
|
-
const formattedResult = await
|
|
799
|
+
const formattedResult = await formatFinalResponse(response, this.params.model.model, jsonMode);
|
|
624
800
|
// If there's an error from formatting (e.g., invalid JSON), return early
|
|
625
801
|
if (formattedResult.error) {
|
|
626
802
|
return {
|
|
@@ -778,6 +954,7 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
778
954
|
};
|
|
779
955
|
this.context.logger.logTokenUsage(tokenUsage, `LLM completion: ${tokenUsage.inputTokens} input + ${tokenUsage.outputTokens} output = ${tokenUsage.totalTokens} total tokens`, {
|
|
780
956
|
bubbleName: 'ai-agent',
|
|
957
|
+
variableId: this.context?.variableId,
|
|
781
958
|
operationType: 'bubble_execution',
|
|
782
959
|
});
|
|
783
960
|
}
|
|
@@ -824,6 +1001,7 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
824
1001
|
type: 'tool_complete',
|
|
825
1002
|
data: {
|
|
826
1003
|
callId,
|
|
1004
|
+
tool: callData.name,
|
|
827
1005
|
output: event.data.output,
|
|
828
1006
|
duration,
|
|
829
1007
|
},
|
|
@@ -856,7 +1034,7 @@ export class AIAgentBubble extends ServiceBubble {
|
|
|
856
1034
|
// Process final result
|
|
857
1035
|
const accumulatedResponse = accumulatedContent || 'No response generated';
|
|
858
1036
|
// Use shared formatting method
|
|
859
|
-
const formattedResult = await
|
|
1037
|
+
const formattedResult = await formatFinalResponse(accumulatedResponse, this.params.model.model, jsonMode);
|
|
860
1038
|
// If there's an error from formatting (e.g., invalid JSON), return early with consistent behavior
|
|
861
1039
|
if (formattedResult.error) {
|
|
862
1040
|
return {
|