illuma-agents 1.0.16 → 1.0.18

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.
Files changed (114) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +3 -1
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +18 -0
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +79 -32
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/llm/bedrock/index.cjs +5 -3
  8. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  9. package/dist/cjs/llm/openai/index.cjs +1 -0
  10. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  11. package/dist/cjs/llm/openrouter/index.cjs +10 -1
  12. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  13. package/dist/cjs/llm/vertexai/index.cjs +7 -8
  14. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  15. package/dist/cjs/main.cjs +15 -0
  16. package/dist/cjs/main.cjs.map +1 -1
  17. package/dist/cjs/messages/cache.cjs +11 -6
  18. package/dist/cjs/messages/cache.cjs.map +1 -1
  19. package/dist/cjs/messages/core.cjs +16 -8
  20. package/dist/cjs/messages/core.cjs.map +1 -1
  21. package/dist/cjs/messages/format.cjs +9 -2
  22. package/dist/cjs/messages/format.cjs.map +1 -1
  23. package/dist/cjs/messages/tools.cjs +17 -10
  24. package/dist/cjs/messages/tools.cjs.map +1 -1
  25. package/dist/cjs/stream.cjs +30 -16
  26. package/dist/cjs/stream.cjs.map +1 -1
  27. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +209 -47
  28. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  29. package/dist/cjs/tools/ToolNode.cjs +73 -3
  30. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  31. package/dist/cjs/tools/handlers.cjs +1 -0
  32. package/dist/cjs/tools/handlers.cjs.map +1 -1
  33. package/dist/cjs/tools/search/search.cjs.map +1 -1
  34. package/dist/cjs/tools/search/tool.cjs +3 -1
  35. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  36. package/dist/cjs/utils/contextAnalytics.cjs +66 -0
  37. package/dist/cjs/utils/contextAnalytics.cjs.map +1 -0
  38. package/dist/cjs/utils/run.cjs.map +1 -1
  39. package/dist/cjs/utils/toonFormat.cjs +388 -0
  40. package/dist/cjs/utils/toonFormat.cjs.map +1 -0
  41. package/dist/esm/agents/AgentContext.mjs +3 -1
  42. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  43. package/dist/esm/common/enum.mjs +19 -1
  44. package/dist/esm/common/enum.mjs.map +1 -1
  45. package/dist/esm/graphs/Graph.mjs +81 -34
  46. package/dist/esm/graphs/Graph.mjs.map +1 -1
  47. package/dist/esm/llm/bedrock/index.mjs +5 -3
  48. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  49. package/dist/esm/llm/openai/index.mjs +1 -0
  50. package/dist/esm/llm/openai/index.mjs.map +1 -1
  51. package/dist/esm/llm/openrouter/index.mjs +10 -1
  52. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  53. package/dist/esm/llm/vertexai/index.mjs +7 -8
  54. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  55. package/dist/esm/main.mjs +4 -2
  56. package/dist/esm/main.mjs.map +1 -1
  57. package/dist/esm/messages/cache.mjs +11 -6
  58. package/dist/esm/messages/cache.mjs.map +1 -1
  59. package/dist/esm/messages/core.mjs +18 -10
  60. package/dist/esm/messages/core.mjs.map +1 -1
  61. package/dist/esm/messages/format.mjs +10 -3
  62. package/dist/esm/messages/format.mjs.map +1 -1
  63. package/dist/esm/messages/tools.mjs +19 -12
  64. package/dist/esm/messages/tools.mjs.map +1 -1
  65. package/dist/esm/stream.mjs +30 -16
  66. package/dist/esm/stream.mjs.map +1 -1
  67. package/dist/esm/tools/ProgrammaticToolCalling.mjs +208 -48
  68. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  69. package/dist/esm/tools/ToolNode.mjs +73 -3
  70. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  71. package/dist/esm/tools/handlers.mjs +1 -0
  72. package/dist/esm/tools/handlers.mjs.map +1 -1
  73. package/dist/esm/tools/search/search.mjs.map +1 -1
  74. package/dist/esm/tools/search/tool.mjs +3 -1
  75. package/dist/esm/tools/search/tool.mjs.map +1 -1
  76. package/dist/esm/utils/contextAnalytics.mjs +64 -0
  77. package/dist/esm/utils/contextAnalytics.mjs.map +1 -0
  78. package/dist/esm/utils/run.mjs.map +1 -1
  79. package/dist/esm/utils/toonFormat.mjs +381 -0
  80. package/dist/esm/utils/toonFormat.mjs.map +1 -0
  81. package/dist/types/common/enum.d.ts +17 -0
  82. package/dist/types/graphs/Graph.d.ts +8 -0
  83. package/dist/types/tools/ProgrammaticToolCalling.d.ts +19 -0
  84. package/dist/types/types/tools.d.ts +3 -1
  85. package/dist/types/utils/contextAnalytics.d.ts +37 -0
  86. package/dist/types/utils/index.d.ts +2 -0
  87. package/dist/types/utils/toonFormat.d.ts +111 -0
  88. package/package.json +3 -2
  89. package/src/agents/AgentContext.ts +28 -20
  90. package/src/common/enum.ts +18 -0
  91. package/src/graphs/Graph.ts +152 -62
  92. package/src/llm/bedrock/__tests__/bedrock-caching.test.ts +495 -473
  93. package/src/llm/bedrock/index.ts +47 -35
  94. package/src/llm/openrouter/index.ts +11 -1
  95. package/src/llm/vertexai/index.ts +9 -10
  96. package/src/messages/cache.ts +104 -55
  97. package/src/messages/core.ts +29 -19
  98. package/src/messages/format.ts +14 -3
  99. package/src/messages/tools.ts +20 -13
  100. package/src/scripts/simple.ts +1 -1
  101. package/src/specs/emergency-prune.test.ts +407 -355
  102. package/src/stream.ts +28 -20
  103. package/src/tools/ProgrammaticToolCalling.ts +246 -52
  104. package/src/tools/ToolNode.ts +78 -5
  105. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +155 -0
  106. package/src/tools/search/jina-reranker.test.ts +32 -28
  107. package/src/tools/search/search.ts +3 -1
  108. package/src/tools/search/tool.ts +16 -7
  109. package/src/types/tools.ts +3 -1
  110. package/src/utils/contextAnalytics.ts +103 -0
  111. package/src/utils/index.ts +2 -0
  112. package/src/utils/llmConfig.ts +8 -1
  113. package/src/utils/run.ts +5 -4
  114. package/src/utils/toonFormat.ts +475 -0
@@ -12,6 +12,7 @@ import {
12
12
  filterToolsByUsage,
13
13
  executeTools,
14
14
  normalizeToPythonIdentifier,
15
+ unwrapToolResponse,
15
16
  } from '../ProgrammaticToolCalling';
16
17
  import {
17
18
  createProgrammaticToolRegistry,
@@ -228,6 +229,160 @@ describe('ProgrammaticToolCalling', () => {
228
229
  });
229
230
  });
230
231
 
232
+ describe('unwrapToolResponse', () => {
233
+ describe('non-MCP tools', () => {
234
+ it('returns result as-is for non-MCP tools', () => {
235
+ const result = { temperature: 65, condition: 'Foggy' };
236
+ expect(unwrapToolResponse(result, false)).toEqual(result);
237
+ });
238
+
239
+ it('returns string as-is for non-MCP tools', () => {
240
+ expect(unwrapToolResponse('plain string', false)).toBe('plain string');
241
+ });
242
+
243
+ it('returns array as-is for non-MCP tools', () => {
244
+ const result = [1, 2, 3];
245
+ expect(unwrapToolResponse(result, false)).toEqual(result);
246
+ });
247
+ });
248
+
249
+ describe('MCP tools - tuple format [content, artifacts]', () => {
250
+ it('extracts string content from tuple', () => {
251
+ const result = ['Hello world', { artifacts: [] }];
252
+ expect(unwrapToolResponse(result, true)).toBe('Hello world');
253
+ });
254
+
255
+ it('parses JSON string content from tuple', () => {
256
+ const result = ['{"temperature": 65}', { artifacts: [] }];
257
+ expect(unwrapToolResponse(result, true)).toEqual({ temperature: 65 });
258
+ });
259
+
260
+ it('parses JSON array string content from tuple', () => {
261
+ const result = ['[1, 2, 3]', { artifacts: [] }];
262
+ expect(unwrapToolResponse(result, true)).toEqual([1, 2, 3]);
263
+ });
264
+
265
+ it('extracts text from single content block in tuple', () => {
266
+ const result = [{ type: 'text', text: 'Spreadsheet info here' }, {}];
267
+ expect(unwrapToolResponse(result, true)).toBe('Spreadsheet info here');
268
+ });
269
+
270
+ it('extracts and parses JSON from single content block in tuple', () => {
271
+ const result = [
272
+ { type: 'text', text: '{"id": "123", "name": "Test"}' },
273
+ {},
274
+ ];
275
+ expect(unwrapToolResponse(result, true)).toEqual({
276
+ id: '123',
277
+ name: 'Test',
278
+ });
279
+ });
280
+
281
+ it('extracts text from array of content blocks in tuple', () => {
282
+ const result = [
283
+ [
284
+ { type: 'text', text: 'Line 1' },
285
+ { type: 'text', text: 'Line 2' },
286
+ ],
287
+ {},
288
+ ];
289
+ expect(unwrapToolResponse(result, true)).toBe('Line 1\nLine 2');
290
+ });
291
+
292
+ it('returns object content as-is when not a text block', () => {
293
+ const result = [{ temperature: 65, condition: 'Foggy' }, {}];
294
+ expect(unwrapToolResponse(result, true)).toEqual({
295
+ temperature: 65,
296
+ condition: 'Foggy',
297
+ });
298
+ });
299
+ });
300
+
301
+ describe('MCP tools - single content block (not in tuple)', () => {
302
+ it('extracts text from single content block object', () => {
303
+ const result = { type: 'text', text: 'No data found in range' };
304
+ expect(unwrapToolResponse(result, true)).toBe('No data found in range');
305
+ });
306
+
307
+ it('extracts and parses JSON from single content block object', () => {
308
+ const result = {
309
+ type: 'text',
310
+ text: '{"sheets": [{"name": "raw_data"}]}',
311
+ };
312
+ expect(unwrapToolResponse(result, true)).toEqual({
313
+ sheets: [{ name: 'raw_data' }],
314
+ });
315
+ });
316
+
317
+ it('handles real-world MCP spreadsheet response', () => {
318
+ const result = {
319
+ type: 'text',
320
+ text: 'Spreadsheet: "NYC Taxi - Top Pickup Neighborhoods" (ID: abc123)\nSheets (2):\n - "raw_data" (ID: 123) | Size: 1000x26',
321
+ };
322
+ expect(unwrapToolResponse(result, true)).toBe(
323
+ 'Spreadsheet: "NYC Taxi - Top Pickup Neighborhoods" (ID: abc123)\nSheets (2):\n - "raw_data" (ID: 123) | Size: 1000x26'
324
+ );
325
+ });
326
+
327
+ it('handles real-world MCP no data response', () => {
328
+ const result = {
329
+ type: 'text',
330
+ text: 'No data found in range \'raw_data!A1:D25\' for user@example.com.',
331
+ };
332
+ expect(unwrapToolResponse(result, true)).toBe(
333
+ 'No data found in range \'raw_data!A1:D25\' for user@example.com.'
334
+ );
335
+ });
336
+ });
337
+
338
+ describe('MCP tools - array of content blocks (not in tuple)', () => {
339
+ it('extracts text from array of content blocks', () => {
340
+ const result = [
341
+ { type: 'text', text: 'First block' },
342
+ { type: 'text', text: 'Second block' },
343
+ ];
344
+ expect(unwrapToolResponse(result, true)).toBe(
345
+ 'First block\nSecond block'
346
+ );
347
+ });
348
+
349
+ it('filters out non-text blocks', () => {
350
+ const result = [
351
+ { type: 'text', text: 'Text content' },
352
+ { type: 'image', data: 'base64...' },
353
+ { type: 'text', text: 'More text' },
354
+ ];
355
+ expect(unwrapToolResponse(result, true)).toBe(
356
+ 'Text content\nMore text'
357
+ );
358
+ });
359
+ });
360
+
361
+ describe('edge cases', () => {
362
+ it('returns non-text block object as-is', () => {
363
+ const result = { type: 'image', data: 'base64...' };
364
+ expect(unwrapToolResponse(result, true)).toEqual(result);
365
+ });
366
+
367
+ it('handles empty array', () => {
368
+ expect(unwrapToolResponse([], true)).toEqual([]);
369
+ });
370
+
371
+ it('handles malformed JSON in text block gracefully', () => {
372
+ const result = { type: 'text', text: '{ invalid json }' };
373
+ expect(unwrapToolResponse(result, true)).toBe('{ invalid json }');
374
+ });
375
+
376
+ it('handles null', () => {
377
+ expect(unwrapToolResponse(null, true)).toBe(null);
378
+ });
379
+
380
+ it('handles undefined', () => {
381
+ expect(unwrapToolResponse(undefined, true)).toBe(undefined);
382
+ });
383
+ });
384
+ });
385
+
231
386
  describe('extractUsedToolNames', () => {
232
387
  const createToolMap = (names: string[]): Map<string, string> => {
233
388
  const map = new Map<string, string>();
@@ -1,18 +1,22 @@
1
- import { JinaReranker } from './rerankers';
1
+ import { JinaReranker, createReranker } from './rerankers';
2
2
  import { createDefaultLogger } from './utils';
3
3
 
4
+ // Helper to access private apiUrl property for testing
5
+ const getApiUrl = (reranker: JinaReranker): string =>
6
+ (reranker as unknown as { apiUrl: string }).apiUrl;
7
+
4
8
  describe('JinaReranker', () => {
5
9
  const mockLogger = createDefaultLogger();
6
-
10
+
7
11
  describe('constructor', () => {
8
12
  it('should use default API URL when no apiUrl is provided', () => {
9
13
  const reranker = new JinaReranker({
10
14
  apiKey: 'test-key',
11
15
  logger: mockLogger,
12
16
  });
13
-
17
+
14
18
  // Access private property for testing
15
- const apiUrl = (reranker as any).apiUrl;
19
+ const apiUrl = getApiUrl(reranker);
16
20
  expect(apiUrl).toBe('https://api.jina.ai/v1/rerank');
17
21
  });
18
22
 
@@ -23,25 +27,25 @@ describe('JinaReranker', () => {
23
27
  apiUrl: customUrl,
24
28
  logger: mockLogger,
25
29
  });
26
-
27
- const apiUrl = (reranker as any).apiUrl;
30
+
31
+ const apiUrl = getApiUrl(reranker);
28
32
  expect(apiUrl).toBe(customUrl);
29
33
  });
30
34
 
31
35
  it('should use environment variable JINA_API_URL when available', () => {
32
36
  const originalEnv = process.env.JINA_API_URL;
33
37
  process.env.JINA_API_URL = 'https://env-jina-endpoint.com/v1/rerank';
34
-
38
+
35
39
  const reranker = new JinaReranker({
36
40
  apiKey: 'test-key',
37
41
  logger: mockLogger,
38
42
  });
39
-
40
- const apiUrl = (reranker as any).apiUrl;
43
+
44
+ const apiUrl = getApiUrl(reranker);
41
45
  expect(apiUrl).toBe('https://env-jina-endpoint.com/v1/rerank');
42
-
46
+
43
47
  // Restore original environment
44
- if (originalEnv) {
48
+ if (originalEnv != null && originalEnv !== '') {
45
49
  process.env.JINA_API_URL = originalEnv;
46
50
  } else {
47
51
  delete process.env.JINA_API_URL;
@@ -51,19 +55,19 @@ describe('JinaReranker', () => {
51
55
  it('should prioritize explicit apiUrl over environment variable', () => {
52
56
  const originalEnv = process.env.JINA_API_URL;
53
57
  process.env.JINA_API_URL = 'https://env-jina-endpoint.com/v1/rerank';
54
-
58
+
55
59
  const customUrl = 'https://explicit-jina-endpoint.com/v1/rerank';
56
60
  const reranker = new JinaReranker({
57
61
  apiKey: 'test-key',
58
62
  apiUrl: customUrl,
59
63
  logger: mockLogger,
60
64
  });
61
-
62
- const apiUrl = (reranker as any).apiUrl;
65
+
66
+ const apiUrl = getApiUrl(reranker);
63
67
  expect(apiUrl).toBe(customUrl);
64
-
68
+
65
69
  // Restore original environment
66
- if (originalEnv) {
70
+ if (originalEnv != null && originalEnv !== '') {
67
71
  process.env.JINA_API_URL = originalEnv;
68
72
  } else {
69
73
  delete process.env.JINA_API_URL;
@@ -79,27 +83,27 @@ describe('JinaReranker', () => {
79
83
  apiUrl: customUrl,
80
84
  logger: mockLogger,
81
85
  });
82
-
86
+
83
87
  const logSpy = jest.spyOn(mockLogger, 'debug');
84
-
88
+
85
89
  try {
86
90
  await reranker.rerank('test query', ['document1', 'document2'], 2);
87
- } catch (error) {
91
+ } catch (_error) {
88
92
  // Expected to fail due to missing API key, but we can check the log
89
93
  }
90
-
94
+
91
95
  expect(logSpy).toHaveBeenCalledWith(
92
- expect.stringContaining(`Reranking 2 chunks with Jina using API URL: ${customUrl}`)
96
+ expect.stringContaining(
97
+ `Reranking 2 chunks with Jina using API URL: ${customUrl}`
98
+ )
93
99
  );
94
-
100
+
95
101
  logSpy.mockRestore();
96
102
  });
97
103
  });
98
104
  });
99
105
 
100
106
  describe('createReranker', () => {
101
- const { createReranker } = require('./rerankers');
102
-
103
107
  it('should create JinaReranker with jinaApiUrl when provided', () => {
104
108
  const customUrl = 'https://custom-jina-endpoint.com/v1/rerank';
105
109
  const reranker = createReranker({
@@ -107,9 +111,9 @@ describe('createReranker', () => {
107
111
  jinaApiKey: 'test-key',
108
112
  jinaApiUrl: customUrl,
109
113
  });
110
-
114
+
111
115
  expect(reranker).toBeInstanceOf(JinaReranker);
112
- const apiUrl = (reranker as any).apiUrl;
116
+ const apiUrl = getApiUrl(reranker as JinaReranker);
113
117
  expect(apiUrl).toBe(customUrl);
114
118
  });
115
119
 
@@ -118,9 +122,9 @@ describe('createReranker', () => {
118
122
  rerankerType: 'jina',
119
123
  jinaApiKey: 'test-key',
120
124
  });
121
-
125
+
122
126
  expect(reranker).toBeInstanceOf(JinaReranker);
123
- const apiUrl = (reranker as any).apiUrl;
127
+ const apiUrl = getApiUrl(reranker as JinaReranker);
124
128
  expect(apiUrl).toBe('https://api.jina.ai/v1/rerank');
125
129
  });
126
130
  });
@@ -603,7 +603,9 @@ export const createSourceProcessor = (
603
603
 
604
604
  // If content was already extracted directly (e.g., direct URL extraction), skip scraping
605
605
  if (skipScraping) {
606
- logger_.debug('Skipping additional scraping - content already extracted');
606
+ logger_.debug(
607
+ 'Skipping additional scraping - content already extracted'
608
+ );
607
609
  return result.data;
608
610
  }
609
611
 
@@ -98,15 +98,21 @@ async function extractDirectUrlContent({
98
98
 
99
99
  results.push({
100
100
  position: results.length + 1,
101
- title: getString(metadata.title) ?? getString(metadata.ogTitle) ?? url,
101
+ title:
102
+ getString(metadata.title) ?? getString(metadata.ogTitle) ?? url,
102
103
  link: url,
103
- snippet: getString(metadata.description) ?? getString(metadata.ogDescription) ?? '',
104
+ snippet:
105
+ getString(metadata.description) ??
106
+ getString(metadata.ogDescription) ??
107
+ '',
104
108
  content: content,
105
109
  references: references,
106
110
  processed: true,
107
111
  });
108
112
  } else {
109
- logger.warn(`Failed to extract content from ${url}: ${response.error}`);
113
+ logger.warn(
114
+ `Failed to extract content from ${url}: ${response.error}`
115
+ );
110
116
  // Still add the URL as a result, but without content
111
117
  results.push({
112
118
  position: results.length + 1,
@@ -424,15 +430,17 @@ function createTool({
424
430
  async (params, runnableConfig) => {
425
431
  const { query, date, country: _c, images, videos, news } = params;
426
432
  const country = typeof _c === 'string' && _c ? _c : undefined;
427
-
433
+
428
434
  // Log the incoming query for debugging URL detection
429
435
  const toolLogger = createDefaultLogger();
430
436
  toolLogger.debug(`[web_search] Received query: "${query}"`);
431
437
  const detectedUrls = extractUrlsFromQuery(query);
432
438
  if (detectedUrls.length > 0) {
433
- toolLogger.debug(`[web_search] Detected URLs in query: ${detectedUrls.join(', ')}`);
439
+ toolLogger.debug(
440
+ `[web_search] Detected URLs in query: ${detectedUrls.join(', ')}`
441
+ );
434
442
  }
435
-
443
+
436
444
  const searchResult = await search({
437
445
  query,
438
446
  date,
@@ -452,7 +460,8 @@ function createTool({
452
460
  },
453
461
  {
454
462
  name: Constants.WEB_SEARCH,
455
- description: `Real-time web search and direct URL content extraction. Results have required citation anchors.
463
+ description:
464
+ `Real-time web search and direct URL content extraction. Results have required citation anchors.
456
465
 
457
466
  **CAPABILITIES:**
458
467
  - Search: Query the web for information on any topic
@@ -15,7 +15,9 @@ export type CustomToolCall = {
15
15
  output?: string;
16
16
  };
17
17
 
18
- export type GenericTool = StructuredToolInterface | RunnableToolLike;
18
+ export type GenericTool = (StructuredToolInterface | RunnableToolLike) & {
19
+ mcp?: boolean;
20
+ };
19
21
 
20
22
  export type ToolMap = Map<string, GenericTool>;
21
23
  export type ToolRefs = {
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Context Analytics Utility
3
+ *
4
+ * Provides context analytics data for observability/traces.
5
+ * No console logging - just data structures for event emission.
6
+ */
7
+
8
+ import type { BaseMessage } from '@langchain/core/messages';
9
+ import type { TokenCounter } from '@/types/run';
10
+
11
+ /**
12
+ * Context analytics data for traces
13
+ */
14
+ export interface ContextAnalytics {
15
+ /** Total messages in context */
16
+ messageCount: number;
17
+ /** Total tokens in context */
18
+ totalTokens: number;
19
+ /** Maximum allowed context tokens */
20
+ maxContextTokens?: number;
21
+ /** Instruction/system tokens */
22
+ instructionTokens?: number;
23
+ /** Context utilization percentage (0-100) */
24
+ utilizationPercent?: number;
25
+ /** Breakdown by message type */
26
+ breakdown?: Record<string, { tokens: number; percent: number }>;
27
+ }
28
+
29
+ /**
30
+ * Build context analytics for traces (no logging)
31
+ */
32
+ export function buildContextAnalytics(
33
+ messages: BaseMessage[],
34
+ options: {
35
+ tokenCounter?: TokenCounter;
36
+ maxContextTokens?: number;
37
+ instructionTokens?: number;
38
+ indexTokenCountMap?: Record<string, number | undefined>;
39
+ }
40
+ ): ContextAnalytics {
41
+ const {
42
+ tokenCounter,
43
+ maxContextTokens,
44
+ instructionTokens,
45
+ indexTokenCountMap,
46
+ } = options;
47
+
48
+ // Calculate total tokens
49
+ let totalTokens = 0;
50
+ const breakdown: Record<string, { tokens: number; percent: number }> = {};
51
+
52
+ for (let i = 0; i < messages.length; i++) {
53
+ const msg = messages[i];
54
+ const type = msg.getType();
55
+
56
+ let tokens = 0;
57
+ if (indexTokenCountMap && indexTokenCountMap[i] != null) {
58
+ tokens = indexTokenCountMap[i]!;
59
+ } else if (tokenCounter) {
60
+ try {
61
+ tokens = tokenCounter(msg);
62
+ } catch {
63
+ // Estimate from content length
64
+ const content =
65
+ typeof msg.content === 'string'
66
+ ? msg.content
67
+ : JSON.stringify(msg.content);
68
+ tokens = Math.ceil(content.length / 4);
69
+ }
70
+ }
71
+
72
+ totalTokens += tokens;
73
+
74
+ if (!breakdown[type]) {
75
+ breakdown[type] = { tokens: 0, percent: 0 };
76
+ }
77
+ breakdown[type].tokens += tokens;
78
+ }
79
+
80
+ // Calculate percentages
81
+ for (const type of Object.keys(breakdown)) {
82
+ breakdown[type].percent =
83
+ totalTokens > 0
84
+ ? Math.round((breakdown[type].tokens / totalTokens) * 1000) / 10
85
+ : 0;
86
+ }
87
+
88
+ // Calculate utilization
89
+ let utilizationPercent: number | undefined;
90
+ if (maxContextTokens && maxContextTokens > 0) {
91
+ utilizationPercent =
92
+ Math.round((totalTokens / maxContextTokens) * 1000) / 10;
93
+ }
94
+
95
+ return {
96
+ messageCount: messages.length,
97
+ totalTokens,
98
+ maxContextTokens,
99
+ instructionTokens,
100
+ utilizationPercent,
101
+ breakdown,
102
+ };
103
+ }
@@ -4,3 +4,5 @@ export * from './misc';
4
4
  export * from './handlers';
5
5
  export * from './run';
6
6
  export * from './tokens';
7
+ export * from './toonFormat';
8
+ export * from './contextAnalytics';
@@ -56,7 +56,9 @@ export const llmConfigs: Record<string, t.LLMConfig | undefined> = {
56
56
  provider: Providers.OPENROUTER,
57
57
  streaming: true,
58
58
  streamUsage: true,
59
- model: 'anthropic/claude-sonnet-4',
59
+ // model: 'anthropic/claude-sonnet-4',
60
+ // model: 'moonshotai/kimi-k2-thinking',
61
+ model: 'google/gemini-3-pro-preview',
60
62
  apiKey: process.env.OPENROUTER_API_KEY,
61
63
  configuration: {
62
64
  baseURL: process.env.OPENROUTER_BASE_URL,
@@ -145,9 +147,14 @@ export const llmConfigs: Record<string, t.LLMConfig | undefined> = {
145
147
  [Providers.VERTEXAI]: {
146
148
  provider: Providers.VERTEXAI,
147
149
  model: 'gemini-2.5-flash',
150
+ // model: 'gemini-2.5-pro',
148
151
  streaming: true,
149
152
  streamUsage: true,
150
153
  keyFile: process.env.VERTEXAI_KEY_FILE,
154
+ // maxRetries: 2,
155
+ // location: 'global',
156
+ // thinkingBudget: -1,
157
+ // includeThoughts: true,
151
158
  } as t.VertexAIClientOptions & t.LLMConfig,
152
159
  [Providers.GOOGLE]: {
153
160
  provider: Providers.GOOGLE,
package/src/utils/run.ts CHANGED
@@ -57,9 +57,10 @@ export class RunnableCallable<I = unknown, O = unknown> extends Runnable<I, O> {
57
57
  ): Promise<O> {
58
58
  return new Promise<O>((resolve, reject) => {
59
59
  // Defensive check: ensure runManager has getChild method before calling
60
- const childCallbacks = typeof runManager?.getChild === 'function'
61
- ? runManager.getChild()
62
- : undefined;
60
+ const childCallbacks =
61
+ typeof runManager?.getChild === 'function'
62
+ ? runManager.getChild()
63
+ : undefined;
63
64
  const childConfig = patchConfig(config, {
64
65
  callbacks: childCallbacks,
65
66
  });
@@ -102,4 +103,4 @@ export class RunnableCallable<I = unknown, O = unknown> extends Runnable<I, O> {
102
103
 
103
104
  return returnValue;
104
105
  }
105
- }
106
+ }