@illuma-ai/agents 1.1.21 → 1.1.22
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 +12 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +85 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/run.cjs +20 -9
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +12 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +85 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/run.mjs +20 -9
- package/dist/esm/run.mjs.map +1 -1
- package/dist/types/graphs/MultiAgentGraph.d.ts +17 -0
- package/package.json +1 -1
- package/src/graphs/Graph.ts +12 -1
- package/src/graphs/MultiAgentGraph.ts +105 -1
- package/src/graphs/__tests__/multi-agent-delegate.test.ts +191 -0
- package/src/run.ts +20 -11
- package/src/scripts/test-bedrock-handoff-autonomous.ts +231 -0
- package/src/agents/AgentContext.js +0 -782
- package/src/agents/AgentContext.test.js +0 -421
- package/src/agents/__tests__/AgentContext.test.js +0 -678
- package/src/agents/__tests__/resolveStructuredOutputMode.test.js +0 -117
- package/src/common/enum.js +0 -192
- package/src/common/index.js +0 -3
- package/src/events.js +0 -166
- package/src/graphs/Graph.js +0 -1857
- package/src/graphs/MultiAgentGraph.js +0 -1092
- package/src/graphs/__tests__/structured-output.integration.test.js +0 -624
- package/src/graphs/__tests__/structured-output.test.js +0 -144
- package/src/graphs/contextManagement.e2e.test.js +0 -718
- package/src/graphs/contextManagement.test.js +0 -485
- package/src/graphs/handoffValidation.test.js +0 -276
- package/src/graphs/index.js +0 -3
- package/src/index.js +0 -28
- package/src/instrumentation.js +0 -21
- package/src/llm/anthropic/index.js +0 -319
- package/src/llm/anthropic/types.js +0 -46
- package/src/llm/anthropic/utils/message_inputs.js +0 -627
- package/src/llm/anthropic/utils/message_outputs.js +0 -290
- package/src/llm/anthropic/utils/output_parsers.js +0 -89
- package/src/llm/anthropic/utils/tools.js +0 -25
- package/src/llm/bedrock/__tests__/bedrock-caching.test.js +0 -392
- package/src/llm/bedrock/index.js +0 -303
- package/src/llm/bedrock/types.js +0 -2
- package/src/llm/bedrock/utils/index.js +0 -6
- package/src/llm/bedrock/utils/message_inputs.js +0 -463
- package/src/llm/bedrock/utils/message_outputs.js +0 -269
- package/src/llm/fake.js +0 -92
- package/src/llm/google/index.js +0 -215
- package/src/llm/google/types.js +0 -12
- package/src/llm/google/utils/common.js +0 -670
- package/src/llm/google/utils/tools.js +0 -111
- package/src/llm/google/utils/zod_to_genai_parameters.js +0 -47
- package/src/llm/openai/index.js +0 -1033
- package/src/llm/openai/types.js +0 -2
- package/src/llm/openai/utils/index.js +0 -756
- package/src/llm/openai/utils/isReasoningModel.test.js +0 -79
- package/src/llm/openrouter/index.js +0 -261
- package/src/llm/openrouter/reasoning.test.js +0 -181
- package/src/llm/providers.js +0 -36
- package/src/llm/text.js +0 -65
- package/src/llm/vertexai/index.js +0 -402
- package/src/messages/__tests__/tools.test.js +0 -392
- package/src/messages/cache.js +0 -404
- package/src/messages/cache.test.js +0 -1167
- package/src/messages/content.js +0 -48
- package/src/messages/content.test.js +0 -314
- package/src/messages/core.js +0 -359
- package/src/messages/ensureThinkingBlock.test.js +0 -997
- package/src/messages/format.js +0 -973
- package/src/messages/formatAgentMessages.test.js +0 -2278
- package/src/messages/formatAgentMessages.tools.test.js +0 -362
- package/src/messages/formatMessage.test.js +0 -608
- package/src/messages/ids.js +0 -18
- package/src/messages/index.js +0 -9
- package/src/messages/labelContentByAgent.test.js +0 -725
- package/src/messages/prune.js +0 -438
- package/src/messages/reducer.js +0 -60
- package/src/messages/shiftIndexTokenCountMap.test.js +0 -63
- package/src/messages/summarize.js +0 -146
- package/src/messages/summarize.test.js +0 -332
- package/src/messages/tools.js +0 -90
- package/src/mockStream.js +0 -81
- package/src/prompts/collab.js +0 -7
- package/src/prompts/index.js +0 -3
- package/src/prompts/taskmanager.js +0 -58
- package/src/run.js +0 -427
- package/src/schemas/index.js +0 -3
- package/src/schemas/schema-preparation.test.js +0 -370
- package/src/schemas/validate.js +0 -314
- package/src/schemas/validate.test.js +0 -264
- package/src/scripts/abort.js +0 -127
- package/src/scripts/ant_web_search.js +0 -130
- package/src/scripts/ant_web_search_edge_case.js +0 -133
- package/src/scripts/ant_web_search_error_edge_case.js +0 -119
- package/src/scripts/args.js +0 -41
- package/src/scripts/bedrock-cache-debug.js +0 -186
- package/src/scripts/bedrock-content-aggregation-test.js +0 -195
- package/src/scripts/bedrock-merge-test.js +0 -80
- package/src/scripts/bedrock-parallel-tools-test.js +0 -150
- package/src/scripts/caching.js +0 -106
- package/src/scripts/cli.js +0 -152
- package/src/scripts/cli2.js +0 -119
- package/src/scripts/cli3.js +0 -163
- package/src/scripts/cli4.js +0 -165
- package/src/scripts/cli5.js +0 -165
- package/src/scripts/code_exec.js +0 -171
- package/src/scripts/code_exec_files.js +0 -180
- package/src/scripts/code_exec_multi_session.js +0 -185
- package/src/scripts/code_exec_ptc.js +0 -265
- package/src/scripts/code_exec_session.js +0 -217
- package/src/scripts/code_exec_simple.js +0 -120
- package/src/scripts/content.js +0 -111
- package/src/scripts/empty_input.js +0 -125
- package/src/scripts/handoff-test.js +0 -96
- package/src/scripts/image.js +0 -138
- package/src/scripts/memory.js +0 -83
- package/src/scripts/multi-agent-chain.js +0 -271
- package/src/scripts/multi-agent-conditional.js +0 -185
- package/src/scripts/multi-agent-document-review-chain.js +0 -171
- package/src/scripts/multi-agent-hybrid-flow.js +0 -264
- package/src/scripts/multi-agent-parallel-start.js +0 -214
- package/src/scripts/multi-agent-parallel.js +0 -346
- package/src/scripts/multi-agent-sequence.js +0 -184
- package/src/scripts/multi-agent-supervisor.js +0 -324
- package/src/scripts/multi-agent-test.js +0 -147
- package/src/scripts/parallel-asymmetric-tools-test.js +0 -202
- package/src/scripts/parallel-full-metadata-test.js +0 -176
- package/src/scripts/parallel-tools-test.js +0 -256
- package/src/scripts/programmatic_exec.js +0 -277
- package/src/scripts/programmatic_exec_agent.js +0 -168
- package/src/scripts/search.js +0 -118
- package/src/scripts/sequential-full-metadata-test.js +0 -143
- package/src/scripts/simple.js +0 -174
- package/src/scripts/single-agent-metadata-test.js +0 -152
- package/src/scripts/stream.js +0 -113
- package/src/scripts/test-custom-prompt-key.js +0 -132
- package/src/scripts/test-handoff-input.js +0 -143
- package/src/scripts/test-handoff-preamble.js +0 -227
- package/src/scripts/test-handoff-steering.js +0 -353
- package/src/scripts/test-multi-agent-list-handoff.js +0 -318
- package/src/scripts/test-parallel-agent-labeling.js +0 -253
- package/src/scripts/test-parallel-handoffs.js +0 -229
- package/src/scripts/test-thinking-handoff-bedrock.js +0 -132
- package/src/scripts/test-thinking-handoff.js +0 -132
- package/src/scripts/test-thinking-to-thinking-handoff-bedrock.js +0 -140
- package/src/scripts/test-tool-before-handoff-role-order.js +0 -223
- package/src/scripts/test-tools-before-handoff.js +0 -187
- package/src/scripts/test_code_api.js +0 -263
- package/src/scripts/thinking-bedrock.js +0 -128
- package/src/scripts/thinking-vertexai.js +0 -130
- package/src/scripts/thinking.js +0 -134
- package/src/scripts/tool_search.js +0 -114
- package/src/scripts/tools.js +0 -125
- package/src/specs/agent-handoffs-bedrock.integration.test.js +0 -280
- package/src/specs/agent-handoffs.test.js +0 -924
- package/src/specs/anthropic.simple.test.js +0 -287
- package/src/specs/azure.simple.test.js +0 -381
- package/src/specs/cache.simple.test.js +0 -282
- package/src/specs/custom-event-await.test.js +0 -148
- package/src/specs/deepseek.simple.test.js +0 -189
- package/src/specs/emergency-prune.test.js +0 -308
- package/src/specs/moonshot.simple.test.js +0 -237
- package/src/specs/observability.integration.test.js +0 -1337
- package/src/specs/openai.simple.test.js +0 -233
- package/src/specs/openrouter.simple.test.js +0 -202
- package/src/specs/prune.test.js +0 -733
- package/src/specs/reasoning.test.js +0 -144
- package/src/specs/spec.utils.js +0 -4
- package/src/specs/thinking-handoff.test.js +0 -486
- package/src/specs/thinking-prune.test.js +0 -600
- package/src/specs/token-distribution-edge-case.test.js +0 -246
- package/src/specs/token-memoization.test.js +0 -32
- package/src/specs/tokens.test.js +0 -49
- package/src/specs/tool-error.test.js +0 -139
- package/src/splitStream.js +0 -204
- package/src/splitStream.test.js +0 -504
- package/src/stream.js +0 -650
- package/src/stream.test.js +0 -225
- package/src/test/mockTools.js +0 -340
- package/src/tools/BrowserTools.js +0 -245
- package/src/tools/Calculator.js +0 -38
- package/src/tools/Calculator.test.js +0 -225
- package/src/tools/CodeExecutor.js +0 -233
- package/src/tools/ProgrammaticToolCalling.js +0 -602
- package/src/tools/StreamingToolCallBuffer.js +0 -179
- package/src/tools/ToolNode.js +0 -930
- package/src/tools/ToolSearch.js +0 -904
- package/src/tools/__tests__/BrowserTools.test.js +0 -306
- package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.js +0 -276
- package/src/tools/__tests__/ProgrammaticToolCalling.test.js +0 -807
- package/src/tools/__tests__/StreamingToolCallBuffer.test.js +0 -175
- package/src/tools/__tests__/ToolApproval.test.js +0 -675
- package/src/tools/__tests__/ToolNode.recovery.test.js +0 -200
- package/src/tools/__tests__/ToolNode.session.test.js +0 -319
- package/src/tools/__tests__/ToolSearch.integration.test.js +0 -125
- package/src/tools/__tests__/ToolSearch.test.js +0 -812
- package/src/tools/__tests__/handlers.test.js +0 -799
- package/src/tools/__tests__/truncation-recovery.integration.test.js +0 -362
- package/src/tools/handlers.js +0 -306
- package/src/tools/schema.js +0 -25
- package/src/tools/search/anthropic.js +0 -34
- package/src/tools/search/content.js +0 -116
- package/src/tools/search/content.test.js +0 -133
- package/src/tools/search/firecrawl.js +0 -173
- package/src/tools/search/format.js +0 -198
- package/src/tools/search/highlights.js +0 -241
- package/src/tools/search/index.js +0 -3
- package/src/tools/search/jina-reranker.test.js +0 -106
- package/src/tools/search/rerankers.js +0 -165
- package/src/tools/search/schema.js +0 -102
- package/src/tools/search/search.js +0 -561
- package/src/tools/search/serper-scraper.js +0 -126
- package/src/tools/search/test.js +0 -129
- package/src/tools/search/tool.js +0 -453
- package/src/tools/search/types.js +0 -2
- package/src/tools/search/utils.js +0 -59
- package/src/types/graph.js +0 -24
- package/src/types/graph.test.js +0 -192
- package/src/types/index.js +0 -7
- package/src/types/llm.js +0 -2
- package/src/types/messages.js +0 -2
- package/src/types/run.js +0 -2
- package/src/types/stream.js +0 -2
- package/src/types/tools.js +0 -2
- package/src/utils/contextAnalytics.js +0 -79
- package/src/utils/contextAnalytics.test.js +0 -166
- package/src/utils/events.js +0 -26
- package/src/utils/graph.js +0 -11
- package/src/utils/handlers.js +0 -65
- package/src/utils/index.js +0 -10
- package/src/utils/llm.js +0 -21
- package/src/utils/llmConfig.js +0 -205
- package/src/utils/logging.js +0 -37
- package/src/utils/misc.js +0 -51
- package/src/utils/run.js +0 -69
- package/src/utils/schema.js +0 -21
- package/src/utils/title.js +0 -119
- package/src/utils/tokens.js +0 -92
- package/src/utils/toonFormat.js +0 -379
package/src/types/graph.test.js
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type-level tests to ensure StructuredOutputConfig interface is correctly defined.
|
|
3
|
-
* These tests don't run at runtime but will fail at compile time if types are wrong.
|
|
4
|
-
*/
|
|
5
|
-
describe('StructuredOutputConfig type', () => {
|
|
6
|
-
describe('type compatibility', () => {
|
|
7
|
-
it('should accept minimal configuration', () => {
|
|
8
|
-
const config = {
|
|
9
|
-
schema: { type: 'object' },
|
|
10
|
-
};
|
|
11
|
-
expect(config.schema).toBeDefined();
|
|
12
|
-
});
|
|
13
|
-
it('should accept full configuration', () => {
|
|
14
|
-
const config = {
|
|
15
|
-
schema: {
|
|
16
|
-
type: 'object',
|
|
17
|
-
properties: {
|
|
18
|
-
name: { type: 'string' },
|
|
19
|
-
value: { type: 'number' },
|
|
20
|
-
},
|
|
21
|
-
required: ['name'],
|
|
22
|
-
},
|
|
23
|
-
name: 'TestSchema',
|
|
24
|
-
description: 'A test schema for structured output',
|
|
25
|
-
mode: 'auto',
|
|
26
|
-
strict: true,
|
|
27
|
-
handleErrors: true,
|
|
28
|
-
maxRetries: 3,
|
|
29
|
-
includeRaw: false,
|
|
30
|
-
};
|
|
31
|
-
expect(config.schema).toBeDefined();
|
|
32
|
-
expect(config.name).toBe('TestSchema');
|
|
33
|
-
expect(config.description).toBe('A test schema for structured output');
|
|
34
|
-
expect(config.mode).toBe('auto');
|
|
35
|
-
expect(config.strict).toBe(true);
|
|
36
|
-
expect(config.handleErrors).toBe(true);
|
|
37
|
-
expect(config.maxRetries).toBe(3);
|
|
38
|
-
expect(config.includeRaw).toBe(false);
|
|
39
|
-
});
|
|
40
|
-
it('should accept all valid mode values', () => {
|
|
41
|
-
const modes = ['auto', 'tool', 'provider'];
|
|
42
|
-
for (const mode of modes) {
|
|
43
|
-
const config = {
|
|
44
|
-
schema: { type: 'string' },
|
|
45
|
-
mode,
|
|
46
|
-
};
|
|
47
|
-
expect(config.mode).toBe(mode);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
it('should make all fields except schema optional', () => {
|
|
51
|
-
const minimalConfig = {
|
|
52
|
-
schema: { type: 'boolean' },
|
|
53
|
-
};
|
|
54
|
-
// These should all be undefined for minimal config
|
|
55
|
-
expect(minimalConfig.name).toBeUndefined();
|
|
56
|
-
expect(minimalConfig.description).toBeUndefined();
|
|
57
|
-
expect(minimalConfig.mode).toBeUndefined();
|
|
58
|
-
expect(minimalConfig.strict).toBeUndefined();
|
|
59
|
-
expect(minimalConfig.handleErrors).toBeUndefined();
|
|
60
|
-
expect(minimalConfig.maxRetries).toBeUndefined();
|
|
61
|
-
expect(minimalConfig.includeRaw).toBeUndefined();
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
describe('schema property types', () => {
|
|
65
|
-
it('should accept object schema', () => {
|
|
66
|
-
const config = {
|
|
67
|
-
schema: {
|
|
68
|
-
type: 'object',
|
|
69
|
-
properties: {
|
|
70
|
-
nested: {
|
|
71
|
-
type: 'object',
|
|
72
|
-
properties: {
|
|
73
|
-
value: { type: 'string' },
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
expect(config.schema.type).toBe('object');
|
|
80
|
-
});
|
|
81
|
-
it('should accept array schema', () => {
|
|
82
|
-
const config = {
|
|
83
|
-
schema: {
|
|
84
|
-
type: 'array',
|
|
85
|
-
items: {
|
|
86
|
-
type: 'object',
|
|
87
|
-
properties: {
|
|
88
|
-
id: { type: 'number' },
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
expect(config.schema.type).toBe('array');
|
|
94
|
-
});
|
|
95
|
-
it('should accept schema with enum', () => {
|
|
96
|
-
const config = {
|
|
97
|
-
schema: {
|
|
98
|
-
type: 'string',
|
|
99
|
-
enum: ['option1', 'option2', 'option3'],
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
expect(config.schema.enum).toEqual(['option1', 'option2', 'option3']);
|
|
103
|
-
});
|
|
104
|
-
it('should accept schema with $ref', () => {
|
|
105
|
-
const config = {
|
|
106
|
-
schema: {
|
|
107
|
-
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
108
|
-
definitions: {
|
|
109
|
-
Address: {
|
|
110
|
-
type: 'object',
|
|
111
|
-
properties: {
|
|
112
|
-
street: { type: 'string' },
|
|
113
|
-
city: { type: 'string' },
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
type: 'object',
|
|
118
|
-
properties: {
|
|
119
|
-
address: { $ref: '#/definitions/Address' },
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
expect(config.schema.$schema).toBeDefined();
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
describe('AgentInputs with structuredOutput', () => {
|
|
128
|
-
it('should accept structuredOutput as optional field', () => {
|
|
129
|
-
const inputsWithout = {
|
|
130
|
-
agentId: 'agent-1',
|
|
131
|
-
provider: 'openai',
|
|
132
|
-
};
|
|
133
|
-
expect(inputsWithout.structuredOutput).toBeUndefined();
|
|
134
|
-
const inputsWith = {
|
|
135
|
-
agentId: 'agent-2',
|
|
136
|
-
provider: 'anthropic',
|
|
137
|
-
structuredOutput: {
|
|
138
|
-
schema: { type: 'object' },
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
expect(inputsWith.structuredOutput).toBeDefined();
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
describe('BaseGraphState with structuredResponse', () => {
|
|
145
|
-
it('should support structuredResponse field', () => {
|
|
146
|
-
const state = {
|
|
147
|
-
messages: [],
|
|
148
|
-
structuredResponse: { result: 'success', data: [1, 2, 3] },
|
|
149
|
-
};
|
|
150
|
-
expect(state.structuredResponse).toEqual({
|
|
151
|
-
result: 'success',
|
|
152
|
-
data: [1, 2, 3],
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
it('should allow structuredResponse to be undefined', () => {
|
|
156
|
-
const state = {
|
|
157
|
-
messages: [],
|
|
158
|
-
};
|
|
159
|
-
expect(state.structuredResponse).toBeUndefined();
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
describe('AgentInputs with summarizeCallback', () => {
|
|
163
|
-
it('should accept summarizeCallback as optional field', () => {
|
|
164
|
-
const inputsWithout = {
|
|
165
|
-
agentId: 'agent-1',
|
|
166
|
-
provider: 'openai',
|
|
167
|
-
};
|
|
168
|
-
expect(inputsWithout.summarizeCallback).toBeUndefined();
|
|
169
|
-
const inputsWith = {
|
|
170
|
-
agentId: 'agent-2',
|
|
171
|
-
provider: 'anthropic',
|
|
172
|
-
summarizeCallback: async (_messages) => 'summary',
|
|
173
|
-
};
|
|
174
|
-
expect(inputsWith.summarizeCallback).toBeDefined();
|
|
175
|
-
});
|
|
176
|
-
it('should accept a valid callback function type', () => {
|
|
177
|
-
const mockCallback = async (messages) => {
|
|
178
|
-
if (messages.length === 0) {
|
|
179
|
-
return undefined;
|
|
180
|
-
}
|
|
181
|
-
return `Summary of ${messages.length} messages`;
|
|
182
|
-
};
|
|
183
|
-
const inputs = {
|
|
184
|
-
agentId: 'agent-summarize',
|
|
185
|
-
provider: 'openai',
|
|
186
|
-
summarizeCallback: mockCallback,
|
|
187
|
-
};
|
|
188
|
-
expect(inputs.summarizeCallback).toBe(mockCallback);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
export {};
|
|
192
|
-
//# sourceMappingURL=graph.test.js.map
|
package/src/types/index.js
DELETED
package/src/types/llm.js
DELETED
package/src/types/messages.js
DELETED
package/src/types/run.js
DELETED
package/src/types/stream.js
DELETED
package/src/types/tools.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
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
|
-
* Resolve the message type string from a BaseMessage or message-like object.
|
|
9
|
-
*
|
|
10
|
-
* Uses the same duck-typing contract as LangChain's `isBaseMessage`:
|
|
11
|
-
* prefer `_getType()` (internal), fallback to `getType()` (public).
|
|
12
|
-
* This handles cross-package BaseMessage instances where `instanceof`
|
|
13
|
-
* checks may fail but prototype methods are still present.
|
|
14
|
-
*/
|
|
15
|
-
function resolveMessageType(msg) {
|
|
16
|
-
if (typeof msg._getType === 'function') {
|
|
17
|
-
return msg._getType();
|
|
18
|
-
}
|
|
19
|
-
if (typeof msg.getType === 'function') {
|
|
20
|
-
return msg.getType();
|
|
21
|
-
}
|
|
22
|
-
// Last resort: infer from known LangChain serialization fields
|
|
23
|
-
return msg.type ?? 'unknown';
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Build context analytics for traces (no logging).
|
|
27
|
-
*
|
|
28
|
-
* @param messages - Array of BaseMessage (may originate from different @langchain/core installations)
|
|
29
|
-
* @param options - Token counting and context budget info
|
|
30
|
-
*/
|
|
31
|
-
export function buildContextAnalytics(messages, options) {
|
|
32
|
-
const { tokenCounter, maxContextTokens, instructionTokens, indexTokenCountMap, } = options;
|
|
33
|
-
let totalTokens = 0;
|
|
34
|
-
const breakdown = {};
|
|
35
|
-
for (let i = 0; i < messages.length; i++) {
|
|
36
|
-
const msg = messages[i];
|
|
37
|
-
const type = resolveMessageType(msg);
|
|
38
|
-
let tokens = 0;
|
|
39
|
-
if (indexTokenCountMap && indexTokenCountMap[i] != null) {
|
|
40
|
-
tokens = indexTokenCountMap[i];
|
|
41
|
-
}
|
|
42
|
-
else if (tokenCounter) {
|
|
43
|
-
try {
|
|
44
|
-
tokens = tokenCounter(msg);
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
const content = typeof msg.content === 'string'
|
|
48
|
-
? msg.content
|
|
49
|
-
: JSON.stringify(msg.content);
|
|
50
|
-
tokens = Math.ceil(content.length / 4);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
totalTokens += tokens;
|
|
54
|
-
if (!breakdown[type]) {
|
|
55
|
-
breakdown[type] = { tokens: 0, percent: 0 };
|
|
56
|
-
}
|
|
57
|
-
breakdown[type].tokens += tokens;
|
|
58
|
-
}
|
|
59
|
-
for (const type of Object.keys(breakdown)) {
|
|
60
|
-
breakdown[type].percent =
|
|
61
|
-
totalTokens > 0
|
|
62
|
-
? Math.round((breakdown[type].tokens / totalTokens) * 1000) / 10
|
|
63
|
-
: 0;
|
|
64
|
-
}
|
|
65
|
-
let utilizationPercent;
|
|
66
|
-
if (maxContextTokens && maxContextTokens > 0) {
|
|
67
|
-
utilizationPercent =
|
|
68
|
-
Math.round((totalTokens / maxContextTokens) * 1000) / 10;
|
|
69
|
-
}
|
|
70
|
-
return {
|
|
71
|
-
messageCount: messages.length,
|
|
72
|
-
totalTokens,
|
|
73
|
-
maxContextTokens,
|
|
74
|
-
instructionTokens,
|
|
75
|
-
utilizationPercent,
|
|
76
|
-
breakdown,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
//# sourceMappingURL=contextAnalytics.js.map
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { HumanMessage, AIMessage, SystemMessage, ToolMessage, } from '@langchain/core/messages';
|
|
2
|
-
import { buildContextAnalytics } from './contextAnalytics';
|
|
3
|
-
describe('buildContextAnalytics', () => {
|
|
4
|
-
describe('with proper BaseMessage instances', () => {
|
|
5
|
-
it('should count messages and compute breakdown by type', () => {
|
|
6
|
-
const messages = [
|
|
7
|
-
new SystemMessage('You are helpful'),
|
|
8
|
-
new HumanMessage('Hello'),
|
|
9
|
-
new AIMessage('Hi there'),
|
|
10
|
-
];
|
|
11
|
-
const result = buildContextAnalytics(messages, {});
|
|
12
|
-
expect(result.messageCount).toBe(3);
|
|
13
|
-
expect(result.breakdown).toBeDefined();
|
|
14
|
-
expect(result.breakdown['system']).toBeDefined();
|
|
15
|
-
expect(result.breakdown['human']).toBeDefined();
|
|
16
|
-
expect(result.breakdown['ai']).toBeDefined();
|
|
17
|
-
});
|
|
18
|
-
it('should use tokenCounter when provided', () => {
|
|
19
|
-
const messages = [
|
|
20
|
-
new HumanMessage('Hello world'),
|
|
21
|
-
new AIMessage('Response'),
|
|
22
|
-
];
|
|
23
|
-
const tokenCounter = jest.fn().mockReturnValue(10);
|
|
24
|
-
const result = buildContextAnalytics(messages, { tokenCounter });
|
|
25
|
-
expect(tokenCounter).toHaveBeenCalledTimes(2);
|
|
26
|
-
expect(result.totalTokens).toBe(20);
|
|
27
|
-
});
|
|
28
|
-
it('should prefer indexTokenCountMap over tokenCounter', () => {
|
|
29
|
-
const messages = [new HumanMessage('Hello')];
|
|
30
|
-
const tokenCounter = jest.fn().mockReturnValue(999);
|
|
31
|
-
const result = buildContextAnalytics(messages, {
|
|
32
|
-
tokenCounter,
|
|
33
|
-
indexTokenCountMap: { '0': 42 },
|
|
34
|
-
});
|
|
35
|
-
expect(tokenCounter).not.toHaveBeenCalled();
|
|
36
|
-
expect(result.totalTokens).toBe(42);
|
|
37
|
-
});
|
|
38
|
-
it('should calculate utilization percentage', () => {
|
|
39
|
-
const messages = [new HumanMessage('Hello')];
|
|
40
|
-
const result = buildContextAnalytics(messages, {
|
|
41
|
-
tokenCounter: () => 500,
|
|
42
|
-
maxContextTokens: 1000,
|
|
43
|
-
});
|
|
44
|
-
expect(result.utilizationPercent).toBe(50);
|
|
45
|
-
});
|
|
46
|
-
it('should handle ToolMessage instances', () => {
|
|
47
|
-
const messages = [
|
|
48
|
-
new HumanMessage('Do something'),
|
|
49
|
-
new AIMessage({
|
|
50
|
-
content: '',
|
|
51
|
-
tool_calls: [{ id: 'tc1', name: 'test', args: {} }],
|
|
52
|
-
}),
|
|
53
|
-
new ToolMessage({ content: 'result', tool_call_id: 'tc1' }),
|
|
54
|
-
];
|
|
55
|
-
const result = buildContextAnalytics(messages, {});
|
|
56
|
-
expect(result.messageCount).toBe(3);
|
|
57
|
-
expect(result.breakdown['tool']).toBeDefined();
|
|
58
|
-
});
|
|
59
|
-
it('should calculate correct percentage breakdown', () => {
|
|
60
|
-
const messages = [
|
|
61
|
-
new HumanMessage('Hello'),
|
|
62
|
-
new AIMessage('World'),
|
|
63
|
-
];
|
|
64
|
-
const result = buildContextAnalytics(messages, {
|
|
65
|
-
tokenCounter: () => 50,
|
|
66
|
-
maxContextTokens: 1000,
|
|
67
|
-
});
|
|
68
|
-
expect(result.breakdown['human'].percent).toBe(50);
|
|
69
|
-
expect(result.breakdown['ai'].percent).toBe(50);
|
|
70
|
-
expect(result.utilizationPercent).toBe(10);
|
|
71
|
-
});
|
|
72
|
-
it('should pass through maxContextTokens and instructionTokens', () => {
|
|
73
|
-
const result = buildContextAnalytics([], {
|
|
74
|
-
maxContextTokens: 4096,
|
|
75
|
-
instructionTokens: 200,
|
|
76
|
-
});
|
|
77
|
-
expect(result.maxContextTokens).toBe(4096);
|
|
78
|
-
expect(result.instructionTokens).toBe(200);
|
|
79
|
-
expect(result.messageCount).toBe(0);
|
|
80
|
-
expect(result.totalTokens).toBe(0);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
describe('with cross-package or plain objects (defensive handling)', () => {
|
|
84
|
-
it('should resolve type via _getType when getType is missing', () => {
|
|
85
|
-
// Simulates a message from a different @langchain/core installation
|
|
86
|
-
// where instanceof fails but _getType still works
|
|
87
|
-
const fakeMsg = {
|
|
88
|
-
content: 'hello',
|
|
89
|
-
_getType: () => 'human',
|
|
90
|
-
// Deliberately no getType
|
|
91
|
-
};
|
|
92
|
-
const result = buildContextAnalytics([fakeMsg], { tokenCounter: () => 5 });
|
|
93
|
-
expect(result.messageCount).toBe(1);
|
|
94
|
-
expect(result.breakdown['human']).toBeDefined();
|
|
95
|
-
expect(result.breakdown['human'].tokens).toBe(5);
|
|
96
|
-
});
|
|
97
|
-
it('should fall back to type field when both getType and _getType are missing', () => {
|
|
98
|
-
// Simulates a fully serialized message (e.g., from JSON deserialization)
|
|
99
|
-
const plainObj = {
|
|
100
|
-
content: 'test message',
|
|
101
|
-
type: 'ai',
|
|
102
|
-
};
|
|
103
|
-
const result = buildContextAnalytics([plainObj], { tokenCounter: () => 10 });
|
|
104
|
-
expect(result.breakdown['ai']).toBeDefined();
|
|
105
|
-
});
|
|
106
|
-
it('should label as unknown when no type information is available', () => {
|
|
107
|
-
const bareObj = { content: 'orphan' };
|
|
108
|
-
const result = buildContextAnalytics([bareObj], { tokenCounter: () => 3 });
|
|
109
|
-
expect(result.breakdown['unknown']).toBeDefined();
|
|
110
|
-
expect(result.totalTokens).toBe(3);
|
|
111
|
-
});
|
|
112
|
-
it('should estimate tokens from content when tokenCounter throws', () => {
|
|
113
|
-
const messages = [new HumanMessage('twelve chars')]; // 12 chars -> ceil(12/4) = 3
|
|
114
|
-
const tokenCounter = jest.fn(() => {
|
|
115
|
-
throw new Error('counter failed');
|
|
116
|
-
});
|
|
117
|
-
const result = buildContextAnalytics(messages, { tokenCounter });
|
|
118
|
-
expect(result.totalTokens).toBe(Math.ceil('twelve chars'.length / 4));
|
|
119
|
-
});
|
|
120
|
-
it('should estimate tokens from JSON-stringified content for structured content', () => {
|
|
121
|
-
const msg = new HumanMessage({
|
|
122
|
-
content: [{ type: 'text', text: 'hi' }],
|
|
123
|
-
});
|
|
124
|
-
const tokenCounter = jest.fn(() => {
|
|
125
|
-
throw new Error('counter failed');
|
|
126
|
-
});
|
|
127
|
-
const result = buildContextAnalytics([msg], { tokenCounter });
|
|
128
|
-
const expectedContent = JSON.stringify([{ type: 'text', text: 'hi' }]);
|
|
129
|
-
expect(result.totalTokens).toBe(Math.ceil(expectedContent.length / 4));
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
describe('edge cases', () => {
|
|
133
|
-
it('should handle empty messages array', () => {
|
|
134
|
-
const result = buildContextAnalytics([], {});
|
|
135
|
-
expect(result.messageCount).toBe(0);
|
|
136
|
-
expect(result.totalTokens).toBe(0);
|
|
137
|
-
expect(result.breakdown).toEqual({});
|
|
138
|
-
});
|
|
139
|
-
it('should handle zero maxContextTokens without dividing by zero', () => {
|
|
140
|
-
const result = buildContextAnalytics([new HumanMessage('x')], {
|
|
141
|
-
tokenCounter: () => 100,
|
|
142
|
-
maxContextTokens: 0,
|
|
143
|
-
});
|
|
144
|
-
expect(result.utilizationPercent).toBeUndefined();
|
|
145
|
-
});
|
|
146
|
-
it('should handle undefined maxContextTokens', () => {
|
|
147
|
-
const result = buildContextAnalytics([new HumanMessage('x')], {
|
|
148
|
-
tokenCounter: () => 100,
|
|
149
|
-
});
|
|
150
|
-
expect(result.utilizationPercent).toBeUndefined();
|
|
151
|
-
});
|
|
152
|
-
it('should handle large message arrays efficiently', () => {
|
|
153
|
-
const messages = Array.from({ length: 1000 }, (_, i) => i % 2 === 0 ? new HumanMessage(`msg ${i}`) : new AIMessage(`reply ${i}`));
|
|
154
|
-
const start = Date.now();
|
|
155
|
-
const result = buildContextAnalytics(messages, {
|
|
156
|
-
tokenCounter: () => 10,
|
|
157
|
-
});
|
|
158
|
-
const elapsed = Date.now() - start;
|
|
159
|
-
expect(result.messageCount).toBe(1000);
|
|
160
|
-
expect(result.totalTokens).toBe(10000);
|
|
161
|
-
// Should complete in well under 1 second
|
|
162
|
-
expect(elapsed).toBeLessThan(1000);
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
//# sourceMappingURL=contextAnalytics.test.js.map
|
package/src/utils/events.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
// src/utils/events.ts
|
|
3
|
-
import { dispatchCustomEvent } from '@langchain/core/callbacks/dispatch';
|
|
4
|
-
/**
|
|
5
|
-
* Safely dispatches a custom event and properly awaits it to avoid
|
|
6
|
-
* race conditions where events are dispatched after run cleanup.
|
|
7
|
-
*/
|
|
8
|
-
export async function safeDispatchCustomEvent(event, payload, config) {
|
|
9
|
-
try {
|
|
10
|
-
await dispatchCustomEvent(event, payload, config);
|
|
11
|
-
}
|
|
12
|
-
catch (e) {
|
|
13
|
-
// Check if this is the known EventStreamCallbackHandler error
|
|
14
|
-
if (e instanceof Error &&
|
|
15
|
-
e.message.includes('handleCustomEvent: Run ID') &&
|
|
16
|
-
e.message.includes('not found in run map')) {
|
|
17
|
-
// Suppress this specific error - it's expected during parallel execution
|
|
18
|
-
// when EventStreamCallbackHandler loses track of run IDs
|
|
19
|
-
// console.debug('Suppressed error dispatching custom event:', e);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
// Log other errors
|
|
23
|
-
console.error('Error dispatching custom event:', e);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=events.js.map
|
package/src/utils/graph.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export const resetIfNotEmpty = (value, resetValue) => {
|
|
2
|
-
if (Array.isArray(value)) {
|
|
3
|
-
return value.length > 0 ? resetValue : value;
|
|
4
|
-
}
|
|
5
|
-
if (value instanceof Set || value instanceof Map) {
|
|
6
|
-
return value.size > 0 ? resetValue : value;
|
|
7
|
-
}
|
|
8
|
-
return value !== undefined ? resetValue : value;
|
|
9
|
-
};
|
|
10
|
-
export const joinKeys = (args) => args.join('_');
|
|
11
|
-
//# sourceMappingURL=graph.js.map
|
package/src/utils/handlers.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Multi-Agent Handler Utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides a simple helper to create handlers with content aggregation for multi-agent scripts.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* ```typescript
|
|
8
|
-
* const { contentParts, aggregateContent, handlers } = createHandlers();
|
|
9
|
-
*
|
|
10
|
-
* // With callbacks
|
|
11
|
-
* const { contentParts, aggregateContent, handlers } = createHandlers({
|
|
12
|
-
* onRunStep: (event, data) => console.log('Step:', data),
|
|
13
|
-
* onRunStepCompleted: (event, data) => console.log('Completed:', data)
|
|
14
|
-
* });
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
import { GraphEvents } from '@/common';
|
|
18
|
-
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
19
|
-
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
20
|
-
/**
|
|
21
|
-
* Creates handlers with content aggregation for multi-agent scripts
|
|
22
|
-
*/
|
|
23
|
-
export function createHandlers(callbacks) {
|
|
24
|
-
// Set up content aggregator
|
|
25
|
-
const { contentParts, aggregateContent } = createContentAggregator();
|
|
26
|
-
// Create the handlers object
|
|
27
|
-
const handlers = {
|
|
28
|
-
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
29
|
-
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
|
|
30
|
-
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
31
|
-
[GraphEvents.ON_RUN_STEP]: {
|
|
32
|
-
handle: (event, data) => {
|
|
33
|
-
aggregateContent({ event, data: data });
|
|
34
|
-
callbacks?.onRunStep?.(event, data);
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
38
|
-
handle: (event, data) => {
|
|
39
|
-
aggregateContent({
|
|
40
|
-
event,
|
|
41
|
-
data: data,
|
|
42
|
-
});
|
|
43
|
-
callbacks?.onRunStepCompleted?.(event, data);
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
47
|
-
handle: (event, data) => {
|
|
48
|
-
aggregateContent({ event, data: data });
|
|
49
|
-
callbacks?.onRunStepDelta?.(event, data);
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
53
|
-
handle: (event, data) => {
|
|
54
|
-
aggregateContent({ event, data: data });
|
|
55
|
-
callbacks?.onMessageDelta?.(event, data);
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
return {
|
|
60
|
-
contentParts,
|
|
61
|
-
aggregateContent,
|
|
62
|
-
handlers,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
//# sourceMappingURL=handlers.js.map
|
package/src/utils/index.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export * from './graph';
|
|
2
|
-
export * from './llm';
|
|
3
|
-
export * from './misc';
|
|
4
|
-
export * from './handlers';
|
|
5
|
-
export * from './run';
|
|
6
|
-
export * from './tokens';
|
|
7
|
-
export * from './toonFormat';
|
|
8
|
-
export * from './contextAnalytics';
|
|
9
|
-
export * from './schema';
|
|
10
|
-
//# sourceMappingURL=index.js.map
|
package/src/utils/llm.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// src/utils/llm.ts
|
|
2
|
-
import { Providers } from '@/common';
|
|
3
|
-
export function isOpenAILike(provider) {
|
|
4
|
-
if (provider == null) {
|
|
5
|
-
return false;
|
|
6
|
-
}
|
|
7
|
-
return [
|
|
8
|
-
Providers.OPENAI,
|
|
9
|
-
Providers.AZURE,
|
|
10
|
-
Providers.OPENROUTER,
|
|
11
|
-
Providers.XAI,
|
|
12
|
-
Providers.DEEPSEEK,
|
|
13
|
-
].includes(provider);
|
|
14
|
-
}
|
|
15
|
-
export function isGoogleLike(provider) {
|
|
16
|
-
if (provider == null) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
return [Providers.GOOGLE, Providers.VERTEXAI].includes(provider);
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=llm.js.map
|