illuma-agents 1.0.52 → 1.0.54
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/cjs/graphs/Graph.cjs +27 -5
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +15 -0
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/messages/tools.cjs +4 -0
- package/dist/cjs/messages/tools.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +27 -5
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +15 -0
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/messages/tools.mjs +4 -0
- package/dist/esm/messages/tools.mjs.map +1 -1
- package/package.json +1 -1
- package/src/graphs/Graph.ts +30 -6
- package/src/llm/bedrock/index.ts +18 -0
- package/src/llm/bedrock/llm.spec.ts +6 -6
- package/src/messages/tools.ts +2 -0
- package/src/tools/__tests__/BrowserTools.test.ts +6 -4
|
@@ -14,6 +14,8 @@ import { findLastIndex } from './core.mjs';
|
|
|
14
14
|
* @returns Array of discovered tool names (empty if no new discoveries)
|
|
15
15
|
*/
|
|
16
16
|
function extractToolDiscoveries(messages) {
|
|
17
|
+
if (messages.length === 0)
|
|
18
|
+
return [];
|
|
17
19
|
const lastMessage = messages[messages.length - 1];
|
|
18
20
|
// Use getType() instead of instanceof to avoid module mismatch issues
|
|
19
21
|
if (lastMessage.getType() !== MessageTypes.TOOL)
|
|
@@ -58,6 +60,8 @@ function extractToolDiscoveries(messages) {
|
|
|
58
60
|
* Quick check to avoid full extraction when not needed.
|
|
59
61
|
*/
|
|
60
62
|
function hasToolSearchInCurrentTurn(messages) {
|
|
63
|
+
if (messages.length === 0)
|
|
64
|
+
return false;
|
|
61
65
|
const lastMessage = messages[messages.length - 1];
|
|
62
66
|
// Use getType() instead of instanceof to avoid module mismatch issues
|
|
63
67
|
if (lastMessage.getType() !== MessageTypes.TOOL)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.mjs","sources":["../../../src/messages/tools.ts"],"sourcesContent":["// src/messages/toolDiscovery.ts\nimport { AIMessageChunk, ToolMessage } from '@langchain/core/messages';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { Constants, MessageTypes } from '@/common';\nimport { findLastIndex } from './core';\n\ntype ToolSearchArtifact = {\n tool_references?: Array<{ tool_name: string }>;\n};\n\n/**\n * Extracts discovered tool names from tool search results in the current turn.\n * Only processes tool search messages after the latest AI message with tool calls.\n *\n * Similar pattern to formatArtifactPayload - finds relevant messages efficiently\n * by identifying the latest AI parent and only processing subsequent tool messages.\n *\n * @param messages - All messages in the conversation\n * @returns Array of discovered tool names (empty if no new discoveries)\n */\nexport function extractToolDiscoveries(messages: BaseMessage[]): string[] {\n const lastMessage = messages[messages.length - 1];\n // Use getType() instead of instanceof to avoid module mismatch issues\n if (lastMessage.getType() !== MessageTypes.TOOL) return [];\n const lastToolMessage = lastMessage as ToolMessage;\n\n // Find the latest AIMessage with tool_calls that this tool message belongs to\n const latestAIParentIndex = findLastIndex(\n messages,\n (msg) =>\n (msg instanceof AIMessageChunk &&\n (msg.tool_calls?.length ?? 0) > 0 &&\n msg.tool_calls?.some((tc) => tc.id === lastToolMessage.tool_call_id)) ??\n false\n );\n\n if (latestAIParentIndex === -1) return [];\n\n // Collect tool_call_ids from the AI message\n const aiMessage = messages[latestAIParentIndex] as AIMessageChunk;\n const toolCallIds = new Set(aiMessage.tool_calls?.map((tc) => tc.id) ?? []);\n\n // Only process tool search results after the AI message that belong to this turn\n const discoveredNames: string[] = [];\n for (let i = latestAIParentIndex + 1; i < messages.length; i++) {\n const msg = messages[i];\n // Use getType() instead of instanceof to avoid module mismatch issues\n if (msg.getType() !== MessageTypes.TOOL) continue;\n const toolMsg = msg as ToolMessage;\n if (toolMsg.name !== Constants.TOOL_SEARCH) continue;\n if (!toolCallIds.has(toolMsg.tool_call_id)) continue;\n\n // This is a tool search result from the current turn\n if (typeof toolMsg.artifact === 'object' && toolMsg.artifact != null) {\n const artifact = toolMsg.artifact as ToolSearchArtifact;\n if (artifact.tool_references && artifact.tool_references.length > 0) {\n for (const ref of artifact.tool_references) {\n discoveredNames.push(ref.tool_name);\n }\n }\n }\n }\n\n return discoveredNames;\n}\n\n/**\n * Checks if the current turn has any tool search results.\n * Quick check to avoid full extraction when not needed.\n */\nexport function hasToolSearchInCurrentTurn(messages: BaseMessage[]): boolean {\n const lastMessage = messages[messages.length - 1];\n // Use getType() instead of instanceof to avoid module mismatch issues\n if (lastMessage.getType() !== MessageTypes.TOOL) return false;\n const lastToolMessage = lastMessage as ToolMessage;\n\n // Find the latest AIMessage with tool_calls\n const latestAIParentIndex = findLastIndex(\n messages,\n (msg) =>\n (msg instanceof AIMessageChunk &&\n (msg.tool_calls?.length ?? 0) > 0 &&\n msg.tool_calls?.some((tc) => tc.id === lastToolMessage.tool_call_id)) ??\n false\n );\n\n if (latestAIParentIndex === -1) return false;\n\n const aiMessage = messages[latestAIParentIndex] as AIMessageChunk;\n const toolCallIds = new Set(aiMessage.tool_calls?.map((tc) => tc.id) ?? []);\n\n // Check if any tool search results exist after the AI message\n // Use getType() instead of instanceof to avoid module mismatch issues\n for (let i = latestAIParentIndex + 1; i < messages.length; i++) {\n const msg = messages[i];\n if (\n msg.getType() === MessageTypes.TOOL &&\n (msg as ToolMessage).name === Constants.TOOL_SEARCH &&\n toolCallIds.has((msg as ToolMessage).tool_call_id)\n ) {\n return true;\n }\n }\n\n return false;\n}\n"],"names":[],"mappings":";;;;AAAA;AAUA;;;;;;;;;AASG;AACG,SAAU,sBAAsB,CAAC,QAAuB,EAAA;
|
|
1
|
+
{"version":3,"file":"tools.mjs","sources":["../../../src/messages/tools.ts"],"sourcesContent":["// src/messages/toolDiscovery.ts\nimport { AIMessageChunk, ToolMessage } from '@langchain/core/messages';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { Constants, MessageTypes } from '@/common';\nimport { findLastIndex } from './core';\n\ntype ToolSearchArtifact = {\n tool_references?: Array<{ tool_name: string }>;\n};\n\n/**\n * Extracts discovered tool names from tool search results in the current turn.\n * Only processes tool search messages after the latest AI message with tool calls.\n *\n * Similar pattern to formatArtifactPayload - finds relevant messages efficiently\n * by identifying the latest AI parent and only processing subsequent tool messages.\n *\n * @param messages - All messages in the conversation\n * @returns Array of discovered tool names (empty if no new discoveries)\n */\nexport function extractToolDiscoveries(messages: BaseMessage[]): string[] {\n if (messages.length === 0) return [];\n const lastMessage = messages[messages.length - 1];\n // Use getType() instead of instanceof to avoid module mismatch issues\n if (lastMessage.getType() !== MessageTypes.TOOL) return [];\n const lastToolMessage = lastMessage as ToolMessage;\n\n // Find the latest AIMessage with tool_calls that this tool message belongs to\n const latestAIParentIndex = findLastIndex(\n messages,\n (msg) =>\n (msg instanceof AIMessageChunk &&\n (msg.tool_calls?.length ?? 0) > 0 &&\n msg.tool_calls?.some((tc) => tc.id === lastToolMessage.tool_call_id)) ??\n false\n );\n\n if (latestAIParentIndex === -1) return [];\n\n // Collect tool_call_ids from the AI message\n const aiMessage = messages[latestAIParentIndex] as AIMessageChunk;\n const toolCallIds = new Set(aiMessage.tool_calls?.map((tc) => tc.id) ?? []);\n\n // Only process tool search results after the AI message that belong to this turn\n const discoveredNames: string[] = [];\n for (let i = latestAIParentIndex + 1; i < messages.length; i++) {\n const msg = messages[i];\n // Use getType() instead of instanceof to avoid module mismatch issues\n if (msg.getType() !== MessageTypes.TOOL) continue;\n const toolMsg = msg as ToolMessage;\n if (toolMsg.name !== Constants.TOOL_SEARCH) continue;\n if (!toolCallIds.has(toolMsg.tool_call_id)) continue;\n\n // This is a tool search result from the current turn\n if (typeof toolMsg.artifact === 'object' && toolMsg.artifact != null) {\n const artifact = toolMsg.artifact as ToolSearchArtifact;\n if (artifact.tool_references && artifact.tool_references.length > 0) {\n for (const ref of artifact.tool_references) {\n discoveredNames.push(ref.tool_name);\n }\n }\n }\n }\n\n return discoveredNames;\n}\n\n/**\n * Checks if the current turn has any tool search results.\n * Quick check to avoid full extraction when not needed.\n */\nexport function hasToolSearchInCurrentTurn(messages: BaseMessage[]): boolean {\n if (messages.length === 0) return false;\n const lastMessage = messages[messages.length - 1];\n // Use getType() instead of instanceof to avoid module mismatch issues\n if (lastMessage.getType() !== MessageTypes.TOOL) return false;\n const lastToolMessage = lastMessage as ToolMessage;\n\n // Find the latest AIMessage with tool_calls\n const latestAIParentIndex = findLastIndex(\n messages,\n (msg) =>\n (msg instanceof AIMessageChunk &&\n (msg.tool_calls?.length ?? 0) > 0 &&\n msg.tool_calls?.some((tc) => tc.id === lastToolMessage.tool_call_id)) ??\n false\n );\n\n if (latestAIParentIndex === -1) return false;\n\n const aiMessage = messages[latestAIParentIndex] as AIMessageChunk;\n const toolCallIds = new Set(aiMessage.tool_calls?.map((tc) => tc.id) ?? []);\n\n // Check if any tool search results exist after the AI message\n // Use getType() instead of instanceof to avoid module mismatch issues\n for (let i = latestAIParentIndex + 1; i < messages.length; i++) {\n const msg = messages[i];\n if (\n msg.getType() === MessageTypes.TOOL &&\n (msg as ToolMessage).name === Constants.TOOL_SEARCH &&\n toolCallIds.has((msg as ToolMessage).tool_call_id)\n ) {\n return true;\n }\n }\n\n return false;\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/graphs/Graph.ts
CHANGED
|
@@ -764,30 +764,51 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
764
764
|
}
|
|
765
765
|
|
|
766
766
|
// Use withStructuredOutput to bind the schema
|
|
767
|
+
// Always use includeRaw: true internally so we can debug what's returned
|
|
767
768
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
768
769
|
const structuredModel = (model as any).withStructuredOutput(schema, {
|
|
769
770
|
name,
|
|
770
771
|
method,
|
|
771
|
-
includeRaw,
|
|
772
|
+
includeRaw: true, // Always true internally for debugging
|
|
772
773
|
strict: structuredOutputConfig.strict !== false,
|
|
773
774
|
});
|
|
774
775
|
|
|
776
|
+
console.log('[Graph] Structured output config:', {
|
|
777
|
+
name,
|
|
778
|
+
method,
|
|
779
|
+
provider,
|
|
780
|
+
schemaKeys: Object.keys(schema),
|
|
781
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
782
|
+
modelName: (model as any).model || (model as any).modelId || 'unknown',
|
|
783
|
+
});
|
|
784
|
+
|
|
775
785
|
let lastError: Error | undefined;
|
|
776
786
|
let attempts = 0;
|
|
777
787
|
|
|
778
788
|
while (attempts <= maxRetries) {
|
|
779
789
|
try {
|
|
780
790
|
const result = await structuredModel.invoke(finalMessages, config);
|
|
791
|
+
|
|
792
|
+
// Debug: log what we got back
|
|
793
|
+
console.log('[Graph] Structured output raw result type:', typeof result);
|
|
794
|
+
if (result?.raw) {
|
|
795
|
+
const rawMsg = result.raw;
|
|
796
|
+
console.log('[Graph] Raw message content type:', typeof rawMsg?.content);
|
|
797
|
+
console.log('[Graph] Raw message tool_calls:', rawMsg?.tool_calls?.length ?? 0);
|
|
798
|
+
if (rawMsg?.content && typeof rawMsg.content === 'string' && rawMsg.content.length > 0) {
|
|
799
|
+
console.log('[Graph] Raw message text content (first 200):', rawMsg.content.substring(0, 200));
|
|
800
|
+
}
|
|
801
|
+
}
|
|
781
802
|
|
|
782
|
-
// Handle includeRaw
|
|
783
|
-
if (
|
|
803
|
+
// Handle response - we always use includeRaw internally
|
|
804
|
+
if (result?.raw && result?.parsed !== undefined) {
|
|
784
805
|
return {
|
|
785
806
|
structuredResponse: result.parsed as Record<string, unknown>,
|
|
786
807
|
rawMessage: result.raw as AIMessageChunk,
|
|
787
808
|
};
|
|
788
809
|
}
|
|
789
810
|
|
|
790
|
-
//
|
|
811
|
+
// Fallback for models that don't support includeRaw
|
|
791
812
|
return {
|
|
792
813
|
structuredResponse: result as Record<string, unknown>,
|
|
793
814
|
};
|
|
@@ -1128,6 +1149,11 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1128
1149
|
// Also disable thinking mode - Anthropic/Bedrock doesn't allow tool_choice with thinking enabled
|
|
1129
1150
|
const structuredClientOptions = { ...agentContext.clientOptions } as t.ClientOptions;
|
|
1130
1151
|
|
|
1152
|
+
// CRITICAL: Disable streaming for structured output
|
|
1153
|
+
// Structured output uses model.invoke() with tool binding, not streaming
|
|
1154
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1155
|
+
(structuredClientOptions as any).streaming = false;
|
|
1156
|
+
|
|
1131
1157
|
// Remove thinking configuration for Bedrock
|
|
1132
1158
|
// Bedrock uses additionalModelRequestFields.thinking for extended thinking
|
|
1133
1159
|
if (agentContext.provider === Providers.BEDROCK) {
|
|
@@ -1151,8 +1177,6 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1151
1177
|
}
|
|
1152
1178
|
}
|
|
1153
1179
|
|
|
1154
|
-
console.log('[Graph] Creating structured model for provider:', agentContext.provider, 'with options:', JSON.stringify(structuredClientOptions, null, 2));
|
|
1155
|
-
|
|
1156
1180
|
const structuredModel = this.getNewModel({
|
|
1157
1181
|
provider: agentContext.provider,
|
|
1158
1182
|
clientOptions: structuredClientOptions,
|
package/src/llm/bedrock/index.ts
CHANGED
|
@@ -101,6 +101,24 @@ export class CustomChatBedrockConverse extends ChatBedrockConverse {
|
|
|
101
101
|
this.promptCache = fields?.promptCache ?? false;
|
|
102
102
|
this.applicationInferenceProfile = fields?.applicationInferenceProfile;
|
|
103
103
|
this.serviceTier = fields?.serviceTier;
|
|
104
|
+
|
|
105
|
+
// Fix: Force supportsToolChoiceValues for Claude models
|
|
106
|
+
// The parent constructor checks `model.includes('claude-3')` but this fails when:
|
|
107
|
+
// 1. Using applicationInferenceProfile ARNs (arn:aws:bedrock:...)
|
|
108
|
+
// 2. Using different naming conventions (claude-4, claude-opus-4, etc.)
|
|
109
|
+
// We need to ensure tool_choice is properly set for withStructuredOutput to work
|
|
110
|
+
const modelName = (fields?.model ?? '').toLowerCase();
|
|
111
|
+
const profileName = (fields?.applicationInferenceProfile ?? '').toLowerCase();
|
|
112
|
+
const isClaudeModel =
|
|
113
|
+
modelName.includes('claude') ||
|
|
114
|
+
modelName.includes('anthropic') ||
|
|
115
|
+
profileName.includes('claude') ||
|
|
116
|
+
profileName.includes('anthropic');
|
|
117
|
+
|
|
118
|
+
if (isClaudeModel && !this.supportsToolChoiceValues?.length) {
|
|
119
|
+
// Claude models support all tool choice values
|
|
120
|
+
this.supportsToolChoiceValues = ['auto', 'any', 'tool'];
|
|
121
|
+
}
|
|
104
122
|
}
|
|
105
123
|
|
|
106
124
|
static lc_name(): string {
|
|
@@ -315,7 +315,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
315
315
|
expect(model.removeContentBlockIndex(undefined)).toBeUndefined();
|
|
316
316
|
});
|
|
317
317
|
|
|
318
|
-
test('
|
|
318
|
+
test('processChunk should remove contentBlockIndex from AIMessageChunk response_metadata', () => {
|
|
319
319
|
const model = getModelWithCleanMethods();
|
|
320
320
|
|
|
321
321
|
const chunkWithIndex = new ChatGenerationChunk({
|
|
@@ -329,7 +329,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
329
329
|
}),
|
|
330
330
|
});
|
|
331
331
|
|
|
332
|
-
const cleaned = model.
|
|
332
|
+
const cleaned = model.processChunk(chunkWithIndex);
|
|
333
333
|
|
|
334
334
|
expect(cleaned.message.response_metadata).toEqual({
|
|
335
335
|
stopReason: null,
|
|
@@ -340,7 +340,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
340
340
|
expect(cleaned.text).toBe('Hello');
|
|
341
341
|
});
|
|
342
342
|
|
|
343
|
-
test('
|
|
343
|
+
test('processChunk should pass through chunks without contentBlockIndex unchanged', () => {
|
|
344
344
|
const model = getModelWithCleanMethods();
|
|
345
345
|
|
|
346
346
|
const chunkWithoutIndex = new ChatGenerationChunk({
|
|
@@ -354,7 +354,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
354
354
|
}),
|
|
355
355
|
});
|
|
356
356
|
|
|
357
|
-
const cleaned = model.
|
|
357
|
+
const cleaned = model.processChunk(chunkWithoutIndex);
|
|
358
358
|
|
|
359
359
|
expect(cleaned.message.response_metadata).toEqual({
|
|
360
360
|
stopReason: 'end_turn',
|
|
@@ -362,7 +362,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
362
362
|
});
|
|
363
363
|
});
|
|
364
364
|
|
|
365
|
-
test('
|
|
365
|
+
test('processChunk should handle deeply nested contentBlockIndex in response_metadata', () => {
|
|
366
366
|
const model = getModelWithCleanMethods();
|
|
367
367
|
|
|
368
368
|
const chunkWithNestedIndex = new ChatGenerationChunk({
|
|
@@ -381,7 +381,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
381
381
|
}),
|
|
382
382
|
});
|
|
383
383
|
|
|
384
|
-
const cleaned = model.
|
|
384
|
+
const cleaned = model.processChunk(chunkWithNestedIndex);
|
|
385
385
|
|
|
386
386
|
expect(cleaned.message.response_metadata).toEqual({
|
|
387
387
|
amazon: {
|
package/src/messages/tools.ts
CHANGED
|
@@ -19,6 +19,7 @@ type ToolSearchArtifact = {
|
|
|
19
19
|
* @returns Array of discovered tool names (empty if no new discoveries)
|
|
20
20
|
*/
|
|
21
21
|
export function extractToolDiscoveries(messages: BaseMessage[]): string[] {
|
|
22
|
+
if (messages.length === 0) return [];
|
|
22
23
|
const lastMessage = messages[messages.length - 1];
|
|
23
24
|
// Use getType() instead of instanceof to avoid module mismatch issues
|
|
24
25
|
if (lastMessage.getType() !== MessageTypes.TOOL) return [];
|
|
@@ -69,6 +70,7 @@ export function extractToolDiscoveries(messages: BaseMessage[]): string[] {
|
|
|
69
70
|
* Quick check to avoid full extraction when not needed.
|
|
70
71
|
*/
|
|
71
72
|
export function hasToolSearchInCurrentTurn(messages: BaseMessage[]): boolean {
|
|
73
|
+
if (messages.length === 0) return false;
|
|
72
74
|
const lastMessage = messages[messages.length - 1];
|
|
73
75
|
// Use getType() instead of instanceof to avoid module mismatch issues
|
|
74
76
|
if (lastMessage.getType() !== MessageTypes.TOOL) return false;
|
|
@@ -19,10 +19,12 @@ describe('BrowserTools', () => {
|
|
|
19
19
|
expect(EBrowserTools.BACK).toBe('browser_back');
|
|
20
20
|
expect(EBrowserTools.SCREENSHOT).toBe('browser_screenshot');
|
|
21
21
|
expect(EBrowserTools.GET_PAGE_STATE).toBe('browser_get_page_state');
|
|
22
|
+
expect(EBrowserTools.KEYPRESS).toBe('browser_keypress');
|
|
23
|
+
expect(EBrowserTools.SWITCH_TAB).toBe('browser_switch_tab');
|
|
22
24
|
});
|
|
23
25
|
|
|
24
|
-
it('should have exactly
|
|
25
|
-
expect(Object.keys(EBrowserTools).length).toBe(
|
|
26
|
+
it('should have exactly 12 browser tools', () => {
|
|
27
|
+
expect(Object.keys(EBrowserTools).length).toBe(12);
|
|
26
28
|
});
|
|
27
29
|
});
|
|
28
30
|
|
|
@@ -82,8 +84,8 @@ describe('BrowserTools', () => {
|
|
|
82
84
|
tools = createBrowserTools();
|
|
83
85
|
});
|
|
84
86
|
|
|
85
|
-
it('should create exactly
|
|
86
|
-
expect(tools.length).toBe(
|
|
87
|
+
it('should create exactly 12 browser tools', () => {
|
|
88
|
+
expect(tools.length).toBe(12);
|
|
87
89
|
});
|
|
88
90
|
|
|
89
91
|
it('should create tools with correct names', () => {
|