@illuma-ai/agents 1.1.20 → 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/llm/bedrock/index.cjs +14 -0
- package/dist/cjs/llm/bedrock/index.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/llm/bedrock/index.mjs +14 -0
- package/dist/esm/llm/bedrock/index.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/llm/bedrock/index.ts +17 -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
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
// src/scripts/cli.test.ts
|
|
3
|
-
import { config } from 'dotenv';
|
|
4
|
-
config();
|
|
5
|
-
import { HumanMessage, } from '@langchain/core/messages';
|
|
6
|
-
import { capitalizeFirstLetter } from './spec.utils';
|
|
7
|
-
import { createContentAggregator } from '@/stream';
|
|
8
|
-
import { GraphEvents, Providers } from '@/common';
|
|
9
|
-
import { getLLMConfig } from '@/utils/llmConfig';
|
|
10
|
-
import { getArgs } from '@/scripts/args';
|
|
11
|
-
import { Run } from '@/run';
|
|
12
|
-
const reasoningText = `<think>
|
|
13
|
-
Okay, the user is Jo from New York. I should start by greeting them by name. Let's keep it friendly and open-ended. Maybe mention the weather in New York to make it personal. Then offer help with something specific like plans or questions. Need to keep it concise and welcoming. Check for any typos. Alright, that should work.
|
|
14
|
-
</think>
|
|
15
|
-
Hi Jo! 🌆 How's everything in New York today? Whether you need recommendations for the city, help with a task, or just want to chat, I'm here for it. What's on your mind? 😊`;
|
|
16
|
-
const provider = 'Reasoning LLM';
|
|
17
|
-
describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
18
|
-
jest.setTimeout(30000);
|
|
19
|
-
let run;
|
|
20
|
-
let contentParts;
|
|
21
|
-
let conversationHistory;
|
|
22
|
-
let aggregateContent;
|
|
23
|
-
let runSteps;
|
|
24
|
-
const config = {
|
|
25
|
-
configurable: {
|
|
26
|
-
thread_id: 'conversation-num-1',
|
|
27
|
-
},
|
|
28
|
-
streamMode: 'values',
|
|
29
|
-
version: 'v2',
|
|
30
|
-
callbacks: [
|
|
31
|
-
{
|
|
32
|
-
async handleCustomEvent(event, data, metadata) {
|
|
33
|
-
if (event !== GraphEvents.ON_MESSAGE_DELTA) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
const messageDeltaData = data;
|
|
37
|
-
// Wait until we see the run step (with timeout for safety)
|
|
38
|
-
const maxAttempts = 50; // 5 seconds total
|
|
39
|
-
let attempts = 0;
|
|
40
|
-
while (!runSteps.has(messageDeltaData.id) && attempts < maxAttempts) {
|
|
41
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
42
|
-
attempts++;
|
|
43
|
-
}
|
|
44
|
-
if (!runSteps.has(messageDeltaData.id)) {
|
|
45
|
-
console.warn(`Timeout waiting for run step: ${messageDeltaData.id}`);
|
|
46
|
-
}
|
|
47
|
-
onMessageDeltaSpy(event, data, metadata, run.Graph);
|
|
48
|
-
aggregateContent({ event, data: messageDeltaData });
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
};
|
|
53
|
-
beforeEach(async () => {
|
|
54
|
-
conversationHistory = [];
|
|
55
|
-
const { contentParts: parts, aggregateContent: ac } = createContentAggregator();
|
|
56
|
-
aggregateContent = ac;
|
|
57
|
-
runSteps = new Set();
|
|
58
|
-
contentParts = parts;
|
|
59
|
-
});
|
|
60
|
-
afterEach(() => {
|
|
61
|
-
runSteps.clear();
|
|
62
|
-
});
|
|
63
|
-
const onReasoningDeltaSpy = jest.fn();
|
|
64
|
-
const onMessageDeltaSpy = jest.fn();
|
|
65
|
-
const onRunStepSpy = jest.fn();
|
|
66
|
-
afterAll(() => {
|
|
67
|
-
onReasoningDeltaSpy.mockReset();
|
|
68
|
-
onMessageDeltaSpy.mockReset();
|
|
69
|
-
onRunStepSpy.mockReset();
|
|
70
|
-
});
|
|
71
|
-
const setupCustomHandlers = () => ({
|
|
72
|
-
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
73
|
-
handle: (event, data) => {
|
|
74
|
-
aggregateContent({
|
|
75
|
-
event,
|
|
76
|
-
data: data,
|
|
77
|
-
});
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
[GraphEvents.ON_RUN_STEP]: {
|
|
81
|
-
handle: (event, data, metadata, graph) => {
|
|
82
|
-
const runStepData = data;
|
|
83
|
-
runSteps.add(runStepData.id);
|
|
84
|
-
onRunStepSpy(event, runStepData, metadata, graph);
|
|
85
|
-
aggregateContent({ event, data: runStepData });
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
89
|
-
handle: (event, data) => {
|
|
90
|
-
aggregateContent({ event, data: data });
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
[GraphEvents.ON_REASONING_DELTA]: {
|
|
94
|
-
handle: (event, data, metadata, graph) => {
|
|
95
|
-
onReasoningDeltaSpy(event, data, metadata, graph);
|
|
96
|
-
aggregateContent({ event, data: data });
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
test(`${capitalizeFirstLetter(provider)}: should process a simple reasoning message`, async () => {
|
|
101
|
-
const { userName, location } = await getArgs();
|
|
102
|
-
const llmConfig = getLLMConfig(Providers.OPENAI);
|
|
103
|
-
const customHandlers = setupCustomHandlers();
|
|
104
|
-
run = await Run.create({
|
|
105
|
-
runId: 'test-run-id',
|
|
106
|
-
graphConfig: {
|
|
107
|
-
type: 'standard',
|
|
108
|
-
llmConfig,
|
|
109
|
-
instructions: 'You are a friendly AI assistant. Always address the user by their name.',
|
|
110
|
-
additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
|
|
111
|
-
},
|
|
112
|
-
returnContent: true,
|
|
113
|
-
skipCleanup: true,
|
|
114
|
-
customHandlers,
|
|
115
|
-
});
|
|
116
|
-
run.Graph?.overrideTestModel([reasoningText], 2);
|
|
117
|
-
const userMessage = 'hi';
|
|
118
|
-
conversationHistory.push(new HumanMessage(userMessage));
|
|
119
|
-
const inputs = {
|
|
120
|
-
messages: conversationHistory,
|
|
121
|
-
};
|
|
122
|
-
await run.processStream(inputs, config);
|
|
123
|
-
expect(contentParts).toBeDefined();
|
|
124
|
-
expect(contentParts.length).toBe(2);
|
|
125
|
-
const reasoningContent = reasoningText.match(/<think>(.*)<\/think>/s)?.[0];
|
|
126
|
-
const content = reasoningText.split(/<\/think>/)[1];
|
|
127
|
-
expect(contentParts[0].think).toBe(reasoningContent);
|
|
128
|
-
expect(contentParts[1].text).toBe(content);
|
|
129
|
-
const finalMessages = run.getRunMessages();
|
|
130
|
-
expect(finalMessages).toBeDefined();
|
|
131
|
-
conversationHistory.push(...(finalMessages ?? []));
|
|
132
|
-
expect(conversationHistory.length).toBeGreaterThan(1);
|
|
133
|
-
expect(onMessageDeltaSpy).toHaveBeenCalled();
|
|
134
|
-
expect(onMessageDeltaSpy.mock.calls.length).toBeGreaterThan(1);
|
|
135
|
-
expect(onMessageDeltaSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
|
|
136
|
-
expect(onReasoningDeltaSpy).toHaveBeenCalled();
|
|
137
|
-
expect(onReasoningDeltaSpy.mock.calls.length).toBeGreaterThan(1);
|
|
138
|
-
expect(onReasoningDeltaSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
|
|
139
|
-
expect(onRunStepSpy).toHaveBeenCalled();
|
|
140
|
-
expect(onRunStepSpy.mock.calls.length).toBeGreaterThan(0);
|
|
141
|
-
expect(onRunStepSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
//# sourceMappingURL=reasoning.test.js.map
|
package/src/specs/spec.utils.js
DELETED
|
@@ -1,486 +0,0 @@
|
|
|
1
|
-
// src/specs/thinking-handoff.test.ts
|
|
2
|
-
import { HumanMessage } from '@langchain/core/messages';
|
|
3
|
-
import { Providers, Constants } from '@/common';
|
|
4
|
-
import { Run } from '@/run';
|
|
5
|
-
/**
|
|
6
|
-
* Test suite for Thinking-Enabled Agent Handoff Edge Case
|
|
7
|
-
*
|
|
8
|
-
* Tests the specific edge case where:
|
|
9
|
-
* - An agent without thinking blocks (e.g., OpenAI) makes a tool call
|
|
10
|
-
* - Control is handed off to an agent with thinking enabled (e.g., Anthropic/Bedrock)
|
|
11
|
-
* - The system should handle the transition without errors
|
|
12
|
-
*
|
|
13
|
-
* Background:
|
|
14
|
-
* When Anthropic's extended thinking is enabled, the API requires that any assistant
|
|
15
|
-
* message with tool_use content must start with a thinking or redacted_thinking block.
|
|
16
|
-
* When switching from a non-thinking agent to a thinking-enabled agent, previous
|
|
17
|
-
* messages may not have these blocks, causing API errors.
|
|
18
|
-
*
|
|
19
|
-
* Solution:
|
|
20
|
-
* The ensureThinkingBlockInMessages() function converts AI messages with tool calls
|
|
21
|
-
* (that lack thinking blocks) into HumanMessages with buffer strings, avoiding the
|
|
22
|
-
* thinking block requirement while preserving context.
|
|
23
|
-
*/
|
|
24
|
-
describe('Thinking-Enabled Agent Handoff Tests', () => {
|
|
25
|
-
jest.setTimeout(30000);
|
|
26
|
-
const createTestConfig = (agents, edges) => ({
|
|
27
|
-
runId: `thinking-handoff-test-${Date.now()}-${Math.random()}`,
|
|
28
|
-
graphConfig: {
|
|
29
|
-
type: 'multi-agent',
|
|
30
|
-
agents,
|
|
31
|
-
edges,
|
|
32
|
-
},
|
|
33
|
-
returnContent: true,
|
|
34
|
-
skipCleanup: true,
|
|
35
|
-
});
|
|
36
|
-
describe('OpenAI to Anthropic with Thinking', () => {
|
|
37
|
-
it('should successfully handoff from OpenAI to Anthropic with thinking enabled', async () => {
|
|
38
|
-
const agents = [
|
|
39
|
-
{
|
|
40
|
-
agentId: 'supervisor',
|
|
41
|
-
provider: Providers.OPENAI,
|
|
42
|
-
clientOptions: {
|
|
43
|
-
modelName: 'gpt-4o-mini',
|
|
44
|
-
apiKey: 'test-key',
|
|
45
|
-
},
|
|
46
|
-
instructions: 'You are a supervisor. Use transfer_to_specialist when asked.',
|
|
47
|
-
maxContextTokens: 8000,
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
agentId: 'specialist',
|
|
51
|
-
provider: Providers.ANTHROPIC,
|
|
52
|
-
clientOptions: {
|
|
53
|
-
modelName: 'claude-3-7-sonnet-20250219',
|
|
54
|
-
apiKey: 'test-key',
|
|
55
|
-
thinking: {
|
|
56
|
-
type: 'enabled',
|
|
57
|
-
budget_tokens: 2000,
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
instructions: 'You are a specialist. Provide detailed answers.',
|
|
61
|
-
maxContextTokens: 8000,
|
|
62
|
-
},
|
|
63
|
-
];
|
|
64
|
-
const edges = [
|
|
65
|
-
{
|
|
66
|
-
from: 'supervisor',
|
|
67
|
-
to: 'specialist',
|
|
68
|
-
edgeType: 'handoff',
|
|
69
|
-
description: 'Transfer to specialist for detailed analysis',
|
|
70
|
-
},
|
|
71
|
-
];
|
|
72
|
-
const run = await Run.create(createTestConfig(agents, edges));
|
|
73
|
-
// Simulate supervisor using handoff tool
|
|
74
|
-
run.Graph?.overrideTestModel([
|
|
75
|
-
'Let me transfer you to our specialist',
|
|
76
|
-
'As a specialist, let me analyze this carefully...',
|
|
77
|
-
], 10, [
|
|
78
|
-
{
|
|
79
|
-
id: 'tool_call_1',
|
|
80
|
-
name: `${Constants.LC_TRANSFER_TO_}specialist`,
|
|
81
|
-
args: {},
|
|
82
|
-
},
|
|
83
|
-
]);
|
|
84
|
-
const messages = [new HumanMessage('I need expert analysis')];
|
|
85
|
-
const config = {
|
|
86
|
-
configurable: {
|
|
87
|
-
thread_id: 'test-thinking-handoff-thread',
|
|
88
|
-
},
|
|
89
|
-
streamMode: 'values',
|
|
90
|
-
version: 'v2',
|
|
91
|
-
};
|
|
92
|
-
// Should not throw despite thinking requirement
|
|
93
|
-
await expect(run.processStream({ messages }, config)).resolves.not.toThrow();
|
|
94
|
-
const finalMessages = run.getRunMessages();
|
|
95
|
-
expect(finalMessages).toBeDefined();
|
|
96
|
-
expect(finalMessages.length).toBeGreaterThan(1);
|
|
97
|
-
// Should have successful handoff
|
|
98
|
-
const toolMessages = finalMessages.filter((msg) => msg.getType() === 'tool');
|
|
99
|
-
const handoffMessage = toolMessages.find((msg) => msg.name === `${Constants.LC_TRANSFER_TO_}specialist`);
|
|
100
|
-
expect(handoffMessage).toBeDefined();
|
|
101
|
-
expect(handoffMessage?.content).toContain('transferred to specialist');
|
|
102
|
-
});
|
|
103
|
-
it('should convert tool sequence to HumanMessage for thinking-enabled agent', async () => {
|
|
104
|
-
const agents = [
|
|
105
|
-
{
|
|
106
|
-
agentId: 'agent_a',
|
|
107
|
-
provider: Providers.OPENAI,
|
|
108
|
-
clientOptions: {
|
|
109
|
-
modelName: 'gpt-4o-mini',
|
|
110
|
-
apiKey: 'test-key',
|
|
111
|
-
},
|
|
112
|
-
instructions: 'You are agent A',
|
|
113
|
-
maxContextTokens: 8000,
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
agentId: 'agent_b',
|
|
117
|
-
provider: Providers.ANTHROPIC,
|
|
118
|
-
clientOptions: {
|
|
119
|
-
modelName: 'claude-3-7-sonnet-20250219',
|
|
120
|
-
apiKey: 'test-key',
|
|
121
|
-
thinking: {
|
|
122
|
-
type: 'enabled',
|
|
123
|
-
budget_tokens: 2000,
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
instructions: 'You are agent B with thinking enabled',
|
|
127
|
-
maxContextTokens: 8000,
|
|
128
|
-
},
|
|
129
|
-
];
|
|
130
|
-
const edges = [
|
|
131
|
-
{
|
|
132
|
-
from: 'agent_a',
|
|
133
|
-
to: 'agent_b',
|
|
134
|
-
edgeType: 'handoff',
|
|
135
|
-
},
|
|
136
|
-
];
|
|
137
|
-
const run = await Run.create(createTestConfig(agents, edges));
|
|
138
|
-
// Check that agent B's context is set up correctly
|
|
139
|
-
const agentBContext = run.Graph.agentContexts.get('agent_b');
|
|
140
|
-
expect(agentBContext).toBeDefined();
|
|
141
|
-
// Verify thinking is enabled
|
|
142
|
-
const thinkingConfig = (agentBContext?.clientOptions).thinking;
|
|
143
|
-
expect(thinkingConfig).toBeDefined();
|
|
144
|
-
expect(thinkingConfig?.type).toBe('enabled');
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
describe('Bedrock with Thinking', () => {
|
|
148
|
-
it('should handle handoff from Bedrock without thinking to Bedrock with thinking', async () => {
|
|
149
|
-
const agents = [
|
|
150
|
-
{
|
|
151
|
-
agentId: 'coordinator',
|
|
152
|
-
provider: Providers.BEDROCK,
|
|
153
|
-
clientOptions: {
|
|
154
|
-
region: 'us-east-1',
|
|
155
|
-
model: 'anthropic.claude-3-5-haiku-20241022-v1:0',
|
|
156
|
-
// No thinking config
|
|
157
|
-
},
|
|
158
|
-
instructions: 'You are a coordinator',
|
|
159
|
-
maxContextTokens: 8000,
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
agentId: 'analyst',
|
|
163
|
-
provider: Providers.BEDROCK,
|
|
164
|
-
clientOptions: {
|
|
165
|
-
region: 'us-east-1',
|
|
166
|
-
model: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
|
|
167
|
-
additionalModelRequestFields: {
|
|
168
|
-
thinking: {
|
|
169
|
-
type: 'enabled',
|
|
170
|
-
budget_tokens: 2000,
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
instructions: 'You are an analyst with extended thinking',
|
|
175
|
-
maxContextTokens: 8000,
|
|
176
|
-
},
|
|
177
|
-
];
|
|
178
|
-
const edges = [
|
|
179
|
-
{
|
|
180
|
-
from: 'coordinator',
|
|
181
|
-
to: 'analyst',
|
|
182
|
-
edgeType: 'handoff',
|
|
183
|
-
description: 'Transfer to analyst for deep analysis',
|
|
184
|
-
},
|
|
185
|
-
];
|
|
186
|
-
const run = await Run.create(createTestConfig(agents, edges));
|
|
187
|
-
run.Graph?.overrideTestModel(['Transferring to analyst', 'Deep analysis results...'], 10, [
|
|
188
|
-
{
|
|
189
|
-
id: 'tool_call_1',
|
|
190
|
-
name: `${Constants.LC_TRANSFER_TO_}analyst`,
|
|
191
|
-
args: {},
|
|
192
|
-
},
|
|
193
|
-
]);
|
|
194
|
-
const messages = [new HumanMessage('Analyze this data')];
|
|
195
|
-
const config = {
|
|
196
|
-
configurable: {
|
|
197
|
-
thread_id: 'test-bedrock-thinking-thread',
|
|
198
|
-
},
|
|
199
|
-
streamMode: 'values',
|
|
200
|
-
version: 'v2',
|
|
201
|
-
};
|
|
202
|
-
await expect(run.processStream({ messages }, config)).resolves.not.toThrow();
|
|
203
|
-
const finalMessages = run.getRunMessages();
|
|
204
|
-
expect(finalMessages).toBeDefined();
|
|
205
|
-
});
|
|
206
|
-
it('should verify Bedrock thinking configuration is properly detected', async () => {
|
|
207
|
-
const agents = [
|
|
208
|
-
{
|
|
209
|
-
agentId: 'agent_a',
|
|
210
|
-
provider: Providers.OPENAI,
|
|
211
|
-
clientOptions: {
|
|
212
|
-
modelName: 'gpt-4o-mini',
|
|
213
|
-
apiKey: 'test-key',
|
|
214
|
-
},
|
|
215
|
-
instructions: 'You are agent A',
|
|
216
|
-
maxContextTokens: 8000,
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
agentId: 'agent_b',
|
|
220
|
-
provider: Providers.BEDROCK,
|
|
221
|
-
clientOptions: {
|
|
222
|
-
region: 'us-east-1',
|
|
223
|
-
model: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
|
|
224
|
-
additionalModelRequestFields: {
|
|
225
|
-
thinking: {
|
|
226
|
-
type: 'enabled',
|
|
227
|
-
budget_tokens: 3000,
|
|
228
|
-
},
|
|
229
|
-
},
|
|
230
|
-
},
|
|
231
|
-
instructions: 'You are agent B with Bedrock thinking',
|
|
232
|
-
maxContextTokens: 8000,
|
|
233
|
-
},
|
|
234
|
-
];
|
|
235
|
-
const edges = [
|
|
236
|
-
{
|
|
237
|
-
from: 'agent_a',
|
|
238
|
-
to: 'agent_b',
|
|
239
|
-
edgeType: 'handoff',
|
|
240
|
-
},
|
|
241
|
-
];
|
|
242
|
-
const run = await Run.create(createTestConfig(agents, edges));
|
|
243
|
-
const agentBContext = run.Graph.agentContexts.get('agent_b');
|
|
244
|
-
expect(agentBContext).toBeDefined();
|
|
245
|
-
expect(agentBContext?.provider).toBe(Providers.BEDROCK);
|
|
246
|
-
// Verify thinking configuration in additionalModelRequestFields
|
|
247
|
-
const bedrockOptions = agentBContext?.clientOptions;
|
|
248
|
-
expect(bedrockOptions.additionalModelRequestFields).toBeDefined();
|
|
249
|
-
expect(bedrockOptions.additionalModelRequestFields?.thinking).toBeDefined();
|
|
250
|
-
const thinkingConfig = bedrockOptions.additionalModelRequestFields
|
|
251
|
-
?.thinking;
|
|
252
|
-
expect(thinkingConfig.type).toBe('enabled');
|
|
253
|
-
expect(thinkingConfig.budget_tokens).toBe(3000);
|
|
254
|
-
});
|
|
255
|
-
it('should handle OpenAI to Bedrock with thinking handoff', async () => {
|
|
256
|
-
const agents = [
|
|
257
|
-
{
|
|
258
|
-
agentId: 'supervisor',
|
|
259
|
-
provider: Providers.OPENAI,
|
|
260
|
-
clientOptions: {
|
|
261
|
-
modelName: 'gpt-4o-mini',
|
|
262
|
-
apiKey: 'test-key',
|
|
263
|
-
},
|
|
264
|
-
instructions: 'You are a supervisor',
|
|
265
|
-
maxContextTokens: 8000,
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
agentId: 'bedrock_specialist',
|
|
269
|
-
provider: Providers.BEDROCK,
|
|
270
|
-
clientOptions: {
|
|
271
|
-
region: 'us-east-1',
|
|
272
|
-
model: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
|
|
273
|
-
additionalModelRequestFields: {
|
|
274
|
-
thinking: {
|
|
275
|
-
type: 'enabled',
|
|
276
|
-
budget_tokens: 2000,
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
instructions: 'You are a Bedrock specialist with thinking',
|
|
281
|
-
maxContextTokens: 8000,
|
|
282
|
-
},
|
|
283
|
-
];
|
|
284
|
-
const edges = [
|
|
285
|
-
{
|
|
286
|
-
from: 'supervisor',
|
|
287
|
-
to: 'bedrock_specialist',
|
|
288
|
-
edgeType: 'handoff',
|
|
289
|
-
description: 'Transfer to Bedrock specialist',
|
|
290
|
-
},
|
|
291
|
-
];
|
|
292
|
-
const run = await Run.create(createTestConfig(agents, edges));
|
|
293
|
-
run.Graph?.overrideTestModel(['Transferring', 'Analysis complete'], 10, [
|
|
294
|
-
{
|
|
295
|
-
id: 'tool_call_1',
|
|
296
|
-
name: `${Constants.LC_TRANSFER_TO_}bedrock_specialist`,
|
|
297
|
-
args: {},
|
|
298
|
-
},
|
|
299
|
-
]);
|
|
300
|
-
const messages = [new HumanMessage('Analyze this')];
|
|
301
|
-
const config = {
|
|
302
|
-
configurable: {
|
|
303
|
-
thread_id: 'test-openai-bedrock-thread',
|
|
304
|
-
},
|
|
305
|
-
streamMode: 'values',
|
|
306
|
-
version: 'v2',
|
|
307
|
-
};
|
|
308
|
-
await expect(run.processStream({ messages }, config)).resolves.not.toThrow();
|
|
309
|
-
const finalMessages = run.getRunMessages();
|
|
310
|
-
expect(finalMessages).toBeDefined();
|
|
311
|
-
const toolMessages = finalMessages.filter((msg) => msg.getType() === 'tool');
|
|
312
|
-
const handoffMessage = toolMessages.find((msg) => msg.name === `${Constants.LC_TRANSFER_TO_}bedrock_specialist`);
|
|
313
|
-
expect(handoffMessage).toBeDefined();
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
describe('Multiple Handoffs with Mixed Thinking Configurations', () => {
|
|
317
|
-
it('should handle chain of handoffs with varying thinking configurations', async () => {
|
|
318
|
-
const agents = [
|
|
319
|
-
{
|
|
320
|
-
agentId: 'router',
|
|
321
|
-
provider: Providers.OPENAI,
|
|
322
|
-
clientOptions: {
|
|
323
|
-
modelName: 'gpt-4o-mini',
|
|
324
|
-
apiKey: 'test-key',
|
|
325
|
-
},
|
|
326
|
-
instructions: 'You route requests',
|
|
327
|
-
maxContextTokens: 8000,
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
agentId: 'processor',
|
|
331
|
-
provider: Providers.ANTHROPIC,
|
|
332
|
-
clientOptions: {
|
|
333
|
-
modelName: 'claude-haiku-4-5',
|
|
334
|
-
apiKey: 'test-key',
|
|
335
|
-
// No thinking
|
|
336
|
-
},
|
|
337
|
-
instructions: 'You process requests',
|
|
338
|
-
maxContextTokens: 8000,
|
|
339
|
-
},
|
|
340
|
-
{
|
|
341
|
-
agentId: 'reviewer',
|
|
342
|
-
provider: Providers.ANTHROPIC,
|
|
343
|
-
clientOptions: {
|
|
344
|
-
modelName: 'claude-3-7-sonnet-20250219',
|
|
345
|
-
apiKey: 'test-key',
|
|
346
|
-
thinking: {
|
|
347
|
-
type: 'enabled',
|
|
348
|
-
budget_tokens: 2000,
|
|
349
|
-
},
|
|
350
|
-
},
|
|
351
|
-
instructions: 'You review with deep thinking',
|
|
352
|
-
maxContextTokens: 8000,
|
|
353
|
-
},
|
|
354
|
-
];
|
|
355
|
-
const edges = [
|
|
356
|
-
{
|
|
357
|
-
from: 'router',
|
|
358
|
-
to: 'processor',
|
|
359
|
-
edgeType: 'handoff',
|
|
360
|
-
},
|
|
361
|
-
{
|
|
362
|
-
from: 'processor',
|
|
363
|
-
to: 'reviewer',
|
|
364
|
-
edgeType: 'handoff',
|
|
365
|
-
},
|
|
366
|
-
];
|
|
367
|
-
const run = await Run.create(createTestConfig(agents, edges));
|
|
368
|
-
// Verify all agents are created with correct configurations
|
|
369
|
-
const routerContext = run.Graph.agentContexts.get('router');
|
|
370
|
-
const processorContext = run.Graph.agentContexts.get('processor');
|
|
371
|
-
const reviewerContext = run.Graph.agentContexts.get('reviewer');
|
|
372
|
-
expect(routerContext).toBeDefined();
|
|
373
|
-
expect(processorContext).toBeDefined();
|
|
374
|
-
expect(reviewerContext).toBeDefined();
|
|
375
|
-
// Verify thinking configuration on reviewer
|
|
376
|
-
const reviewerThinking = (reviewerContext?.clientOptions).thinking;
|
|
377
|
-
expect(reviewerThinking).toBeDefined();
|
|
378
|
-
expect(reviewerThinking?.type).toBe('enabled');
|
|
379
|
-
// Verify handoff tools exist
|
|
380
|
-
expect(routerContext?.graphTools?.find((tool) => tool.name ===
|
|
381
|
-
`${Constants.LC_TRANSFER_TO_}processor`)).toBeDefined();
|
|
382
|
-
expect(processorContext?.graphTools?.find((tool) => tool.name ===
|
|
383
|
-
`${Constants.LC_TRANSFER_TO_}reviewer`)).toBeDefined();
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
describe('Edge Cases', () => {
|
|
387
|
-
it('should not modify messages when agent already uses thinking', async () => {
|
|
388
|
-
const agents = [
|
|
389
|
-
{
|
|
390
|
-
agentId: 'agent_a',
|
|
391
|
-
provider: Providers.ANTHROPIC,
|
|
392
|
-
clientOptions: {
|
|
393
|
-
modelName: 'claude-3-7-sonnet-20250219',
|
|
394
|
-
apiKey: 'test-key',
|
|
395
|
-
thinking: {
|
|
396
|
-
type: 'enabled',
|
|
397
|
-
budget_tokens: 2000,
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
|
-
instructions: 'You are agent A with thinking',
|
|
401
|
-
maxContextTokens: 8000,
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
agentId: 'agent_b',
|
|
405
|
-
provider: Providers.ANTHROPIC,
|
|
406
|
-
clientOptions: {
|
|
407
|
-
modelName: 'claude-3-7-sonnet-20250219',
|
|
408
|
-
apiKey: 'test-key',
|
|
409
|
-
thinking: {
|
|
410
|
-
type: 'enabled',
|
|
411
|
-
budget_tokens: 2000,
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
|
-
instructions: 'You are agent B with thinking',
|
|
415
|
-
maxContextTokens: 8000,
|
|
416
|
-
},
|
|
417
|
-
];
|
|
418
|
-
const edges = [
|
|
419
|
-
{
|
|
420
|
-
from: 'agent_a',
|
|
421
|
-
to: 'agent_b',
|
|
422
|
-
edgeType: 'handoff',
|
|
423
|
-
},
|
|
424
|
-
];
|
|
425
|
-
const run = await Run.create(createTestConfig(agents, edges));
|
|
426
|
-
run.Graph?.overrideTestModel(['Transferring', 'Received handoff'], 10, [
|
|
427
|
-
{
|
|
428
|
-
id: 'tool_call_1',
|
|
429
|
-
name: `${Constants.LC_TRANSFER_TO_}agent_b`,
|
|
430
|
-
args: {},
|
|
431
|
-
},
|
|
432
|
-
]);
|
|
433
|
-
const messages = [new HumanMessage('Test message')];
|
|
434
|
-
const config = {
|
|
435
|
-
configurable: {
|
|
436
|
-
thread_id: 'test-both-thinking-thread',
|
|
437
|
-
},
|
|
438
|
-
streamMode: 'values',
|
|
439
|
-
version: 'v2',
|
|
440
|
-
};
|
|
441
|
-
// Should work fine when both agents use thinking
|
|
442
|
-
await expect(run.processStream({ messages }, config)).resolves.not.toThrow();
|
|
443
|
-
});
|
|
444
|
-
it('should handle empty conversation history', async () => {
|
|
445
|
-
const agents = [
|
|
446
|
-
{
|
|
447
|
-
agentId: 'agent_a',
|
|
448
|
-
provider: Providers.OPENAI,
|
|
449
|
-
clientOptions: {
|
|
450
|
-
modelName: 'gpt-4o-mini',
|
|
451
|
-
apiKey: 'test-key',
|
|
452
|
-
},
|
|
453
|
-
instructions: 'You are agent A',
|
|
454
|
-
maxContextTokens: 8000,
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
agentId: 'agent_b',
|
|
458
|
-
provider: Providers.ANTHROPIC,
|
|
459
|
-
clientOptions: {
|
|
460
|
-
modelName: 'claude-3-7-sonnet-20250219',
|
|
461
|
-
apiKey: 'test-key',
|
|
462
|
-
thinking: {
|
|
463
|
-
type: 'enabled',
|
|
464
|
-
budget_tokens: 2000,
|
|
465
|
-
},
|
|
466
|
-
},
|
|
467
|
-
instructions: 'You are agent B',
|
|
468
|
-
maxContextTokens: 8000,
|
|
469
|
-
},
|
|
470
|
-
];
|
|
471
|
-
const edges = [
|
|
472
|
-
{
|
|
473
|
-
from: 'agent_a',
|
|
474
|
-
to: 'agent_b',
|
|
475
|
-
edgeType: 'handoff',
|
|
476
|
-
},
|
|
477
|
-
];
|
|
478
|
-
const run = await Run.create(createTestConfig(agents, edges));
|
|
479
|
-
expect(run.Graph).toBeDefined();
|
|
480
|
-
// Just verify the graph was created correctly
|
|
481
|
-
const agentBContext = run.Graph.agentContexts.get('agent_b');
|
|
482
|
-
expect(agentBContext).toBeDefined();
|
|
483
|
-
});
|
|
484
|
-
});
|
|
485
|
-
});
|
|
486
|
-
//# sourceMappingURL=thinking-handoff.test.js.map
|