illuma-agents 1.0.59 → 1.0.61

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.
@@ -785,14 +785,11 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
785
785
  let lastError: Error | undefined;
786
786
  let attempts = 0;
787
787
 
788
- // Create a silent config for structured output - we don't want tool call events
789
- // to be emitted for the synthetic "response" tool used by withStructuredOutput()
790
- // This prevents the UI from showing a fake tool call
791
- const silentConfig = config ? { ...config, callbacks: undefined } : undefined;
792
-
793
788
  while (attempts <= maxRetries) {
794
789
  try {
795
- const result = await structuredModel.invoke(finalMessages, silentConfig);
790
+ // Note: We pass the original config here. The stream aggregator will filter out
791
+ // the synthetic "response" tool call events from withStructuredOutput()
792
+ const result = await structuredModel.invoke(finalMessages, config);
796
793
 
797
794
  // Debug: log what we got back
798
795
  console.log('[Graph] Structured output raw result type:', typeof result);
@@ -1139,10 +1136,17 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1139
1136
  );
1140
1137
 
1141
1138
  // Check if structured output mode is enabled
1142
- if (
1139
+ // IMPORTANT: If tools are available, we need to let the model use them first.
1140
+ // Only use structured output when:
1141
+ // 1. No tools are configured, OR
1142
+ // 2. The model has already used tools and is ready to give final response
1143
+ const hasTools = agentContext.tools && agentContext.tools.length > 0;
1144
+ const shouldUseStructuredOutputNow =
1143
1145
  agentContext.isStructuredOutputMode &&
1144
- agentContext.structuredOutput
1145
- ) {
1146
+ agentContext.structuredOutput &&
1147
+ !hasTools; // Only use structured output immediately if no tools
1148
+
1149
+ if (shouldUseStructuredOutputNow) {
1146
1150
  const schema = agentContext.getStructuredOutputSchema();
1147
1151
  if (!schema) {
1148
1152
  throw new Error('Structured output schema is not configured');
@@ -1193,7 +1197,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1193
1197
  currentModel: structuredModel,
1194
1198
  finalMessages,
1195
1199
  schema,
1196
- structuredOutputConfig: agentContext.structuredOutput,
1200
+ structuredOutputConfig: agentContext.structuredOutput!,
1197
1201
  provider: agentContext.provider,
1198
1202
  },
1199
1203
  config
@@ -1466,6 +1470,107 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
1466
1470
  if (!result) {
1467
1471
  throw new Error('No result after model invocation');
1468
1472
  }
1473
+
1474
+ // Check if we need to apply structured output for the final response
1475
+ // This handles the case where tools were used, and now the model is giving its final answer
1476
+ const resultMessage = result.messages?.[0] as AIMessageChunk | undefined;
1477
+ const hasToolCalls = (resultMessage?.tool_calls?.length ?? 0) > 0;
1478
+
1479
+ if (
1480
+ agentContext.isStructuredOutputMode &&
1481
+ agentContext.structuredOutput &&
1482
+ !hasToolCalls && // Model is giving final response (no tool calls)
1483
+ hasTools // We skipped structured output earlier because tools were available
1484
+ ) {
1485
+ const schema = agentContext.getStructuredOutputSchema();
1486
+ if (schema) {
1487
+ try {
1488
+ console.log('[Graph] Applying structured output for final response after tool execution');
1489
+
1490
+ // Get a fresh model for structured output
1491
+ const structuredClientOptions = { ...agentContext.clientOptions } as t.ClientOptions;
1492
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1493
+ (structuredClientOptions as any).streaming = false;
1494
+
1495
+ // Remove thinking configuration
1496
+ if (agentContext.provider === Providers.BEDROCK) {
1497
+ const bedrockOpts = structuredClientOptions as t.BedrockAnthropicClientOptions;
1498
+ if (bedrockOpts.additionalModelRequestFields) {
1499
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1500
+ const additionalFields = Object.assign({}, bedrockOpts.additionalModelRequestFields) as any;
1501
+ delete additionalFields.thinking;
1502
+ delete additionalFields.budgetTokens;
1503
+ bedrockOpts.additionalModelRequestFields = additionalFields;
1504
+ }
1505
+ }
1506
+ if (agentContext.provider === Providers.ANTHROPIC) {
1507
+ const anthropicOpts = structuredClientOptions as t.AnthropicClientOptions;
1508
+ if (anthropicOpts.thinking) {
1509
+ delete anthropicOpts.thinking;
1510
+ }
1511
+ }
1512
+
1513
+ const structuredModel = this.getNewModel({
1514
+ provider: agentContext.provider,
1515
+ clientOptions: structuredClientOptions,
1516
+ });
1517
+
1518
+ // Include the current result in the messages so the model knows what it just said
1519
+ const messagesWithResult = [...finalMessages];
1520
+ if (resultMessage) {
1521
+ messagesWithResult.push(resultMessage);
1522
+ }
1523
+
1524
+ const { structuredResponse, rawMessage } =
1525
+ await this.attemptStructuredInvoke(
1526
+ {
1527
+ currentModel: structuredModel,
1528
+ finalMessages: messagesWithResult,
1529
+ schema,
1530
+ structuredOutputConfig: agentContext.structuredOutput!,
1531
+ provider: agentContext.provider,
1532
+ },
1533
+ config
1534
+ );
1535
+
1536
+ // Emit structured output event
1537
+ await safeDispatchCustomEvent(
1538
+ GraphEvents.ON_STRUCTURED_OUTPUT,
1539
+ {
1540
+ structuredResponse,
1541
+ schema,
1542
+ raw: rawMessage,
1543
+ },
1544
+ config
1545
+ );
1546
+
1547
+ agentContext.currentUsage = rawMessage
1548
+ ? this.getUsageMetadata(rawMessage)
1549
+ : undefined;
1550
+ this.cleanupSignalListener();
1551
+
1552
+ // Return clean message without tool_calls
1553
+ let cleanMessage: AIMessageChunk | undefined;
1554
+ if (rawMessage) {
1555
+ cleanMessage = new AIMessageChunk({
1556
+ content: JSON.stringify(structuredResponse, null, 2),
1557
+ id: rawMessage.id,
1558
+ response_metadata: rawMessage.response_metadata,
1559
+ usage_metadata: rawMessage.usage_metadata,
1560
+ });
1561
+ }
1562
+
1563
+ return {
1564
+ messages: cleanMessage ? [cleanMessage] : [],
1565
+ structuredResponse,
1566
+ };
1567
+ } catch (structuredError) {
1568
+ console.error('[Graph] Structured output failed after tool execution:', structuredError);
1569
+ // Fall through to return normal result
1570
+ }
1571
+ }
1572
+ }
1573
+
1469
1574
  agentContext.currentUsage = this.getUsageMetadata(result.messages?.[0]);
1470
1575
  this.cleanupSignalListener();
1471
1576
  return result;