illuma-agents 1.0.51 → 1.0.53

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "illuma-agents",
3
- "version": "1.0.51",
3
+ "version": "1.0.53",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -737,27 +737,30 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
737
737
  } = structuredOutputConfig;
738
738
 
739
739
  // Determine the method based on mode and provider
740
- // - 'tool': Use tool calling (works well with OpenAI)
741
- // - 'json_mode': Use JSON mode (works well with Bedrock/Anthropic)
740
+ // - 'tool' / 'functionCalling': Use tool calling (works with OpenAI, Bedrock)
741
+ // - 'provider' / 'jsonMode': Use native JSON mode (OpenAI, Anthropic direct - NOT Bedrock)
742
742
  // - 'auto': Auto-detect based on provider
743
+ // NOTE: ChatBedrockConverse does NOT support 'jsonMode' - it only supports tool-based structured output
743
744
  let method: 'functionCalling' | 'jsonMode' | 'jsonSchema' | undefined;
744
745
 
745
746
  if (mode === 'tool') {
746
747
  method = 'functionCalling';
747
748
  } else if (mode === 'provider') {
748
- // Use native JSON mode - best for Anthropic/Bedrock
749
- method = 'jsonMode';
750
- } else {
751
- // Auto mode: choose based on provider
752
- // Bedrock and Anthropic work better with JSON mode
753
- // OpenAI, Azure work better with function calling
754
- if (provider === Providers.BEDROCK || provider === Providers.ANTHROPIC) {
755
- method = 'jsonMode';
756
- } else if (provider === Providers.GOOGLE || provider === Providers.VERTEXAI) {
757
- // Gemini supports native JSON mode
749
+ // Use native JSON mode - but NOT for Bedrock which doesn't support it
750
+ if (provider === Providers.BEDROCK) {
751
+ // Bedrock only supports function calling for structured output
752
+ method = 'functionCalling';
753
+ } else {
758
754
  method = 'jsonMode';
759
755
  }
760
- // For OpenAI/Azure, leave undefined to use default (function calling)
756
+ } else {
757
+ // Auto mode: use function calling for all providers
758
+ // This is the most compatible approach
759
+ // Bedrock: only supports functionCalling
760
+ // Anthropic direct: supports both but functionCalling is more reliable
761
+ // OpenAI/Azure: supports both, functionCalling is default
762
+ // Google/Vertex: supports jsonMode but functionCalling works too
763
+ method = undefined; // Let LangChain choose the default
761
764
  }
762
765
 
763
766
  // Use withStructuredOutput to bind the schema
@@ -1125,18 +1128,27 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1125
1128
  // Also disable thinking mode - Anthropic/Bedrock doesn't allow tool_choice with thinking enabled
1126
1129
  const structuredClientOptions = { ...agentContext.clientOptions } as t.ClientOptions;
1127
1130
 
1131
+ // CRITICAL: Disable streaming for structured output
1132
+ // Structured output uses model.invoke() with tool binding, not streaming
1133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1134
+ (structuredClientOptions as any).streaming = false;
1135
+
1128
1136
  // Remove thinking configuration for Bedrock
1137
+ // Bedrock uses additionalModelRequestFields.thinking for extended thinking
1129
1138
  if (agentContext.provider === Providers.BEDROCK) {
1130
1139
  const bedrockOpts = structuredClientOptions as t.BedrockAnthropicClientOptions;
1131
- if (bedrockOpts.additionalModelRequestFields?.['thinking']) {
1140
+ if (bedrockOpts.additionalModelRequestFields) {
1132
1141
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1133
1142
  const additionalFields = Object.assign({}, bedrockOpts.additionalModelRequestFields) as any;
1143
+ // Remove thinking configuration
1134
1144
  delete additionalFields.thinking;
1145
+ // Remove budget tokens which is also related to thinking
1146
+ delete additionalFields.budgetTokens;
1135
1147
  bedrockOpts.additionalModelRequestFields = additionalFields;
1136
1148
  }
1137
1149
  }
1138
1150
 
1139
- // Remove thinking configuration for Anthropic
1151
+ // Remove thinking configuration for Anthropic direct API
1140
1152
  if (agentContext.provider === Providers.ANTHROPIC) {
1141
1153
  const anthropicOpts = structuredClientOptions as t.AnthropicClientOptions;
1142
1154
  if (anthropicOpts.thinking) {
@@ -315,7 +315,7 @@ describe('CustomChatBedrockConverse', () => {
315
315
  expect(model.removeContentBlockIndex(undefined)).toBeUndefined();
316
316
  });
317
317
 
318
- test('cleanChunk should remove contentBlockIndex from AIMessageChunk response_metadata', () => {
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.cleanChunk(chunkWithIndex);
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('cleanChunk should pass through chunks without contentBlockIndex unchanged', () => {
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.cleanChunk(chunkWithoutIndex);
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('cleanChunk should handle deeply nested contentBlockIndex in response_metadata', () => {
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.cleanChunk(chunkWithNestedIndex);
384
+ const cleaned = model.processChunk(chunkWithNestedIndex);
385
385
 
386
386
  expect(cleaned.message.response_metadata).toEqual({
387
387
  amazon: {
@@ -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 10 browser tools', () => {
25
- expect(Object.keys(EBrowserTools).length).toBe(10);
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 10 browser tools', () => {
86
- expect(tools.length).toBe(10);
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', () => {