@librechat/agents 3.0.52 → 3.0.54
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 +17 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +101 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/stream.cjs +22 -0
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +17 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +101 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/stream.mjs +22 -0
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/types/graphs/Graph.d.ts +8 -0
- package/dist/types/graphs/MultiAgentGraph.d.ts +36 -0
- package/dist/types/types/stream.d.ts +13 -0
- package/package.json +4 -2
- package/src/graphs/Graph.ts +19 -1
- package/src/graphs/MultiAgentGraph.ts +111 -0
- package/src/scripts/multi-agent-chain.ts +59 -6
- package/src/scripts/multi-agent-parallel-start.ts +263 -0
- package/src/scripts/multi-agent-parallel.ts +61 -10
- package/src/scripts/parallel-asymmetric-tools-test.ts +274 -0
- package/src/scripts/parallel-full-metadata-test.ts +240 -0
- package/src/scripts/parallel-tools-test.ts +340 -0
- package/src/scripts/sequential-full-metadata-test.ts +197 -0
- package/src/scripts/single-agent-metadata-test.ts +192 -0
- package/src/scripts/test-thinking-handoff.ts +8 -0
- package/src/scripts/tools.ts +31 -11
- package/src/stream.ts +29 -0
- package/src/types/stream.ts +15 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { config } from 'dotenv';
|
|
2
|
+
config();
|
|
3
|
+
|
|
4
|
+
import { HumanMessage, BaseMessage } from '@langchain/core/messages';
|
|
5
|
+
import type * as t from '@/types';
|
|
6
|
+
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
7
|
+
import { Providers, GraphEvents } from '@/common';
|
|
8
|
+
import { sleep } from '@/utils/run';
|
|
9
|
+
import { Run } from '@/run';
|
|
10
|
+
|
|
11
|
+
const conversationHistory: BaseMessage[] = [];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Dump ALL metadata for SEQUENTIAL execution to compare with parallel
|
|
15
|
+
*/
|
|
16
|
+
async function testSequentialMetadata() {
|
|
17
|
+
console.log('Dumping FULL metadata for SEQUENTIAL execution...\n');
|
|
18
|
+
|
|
19
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
20
|
+
|
|
21
|
+
const allMetadata: Array<{
|
|
22
|
+
event: string;
|
|
23
|
+
timestamp: number;
|
|
24
|
+
metadata: Record<string, unknown>;
|
|
25
|
+
}> = [];
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
|
|
28
|
+
// Sequential chain: agent_a -> agent_b
|
|
29
|
+
const agents: t.AgentInputs[] = [
|
|
30
|
+
{
|
|
31
|
+
agentId: 'agent_a',
|
|
32
|
+
provider: Providers.ANTHROPIC,
|
|
33
|
+
clientOptions: {
|
|
34
|
+
modelName: 'claude-haiku-4-5',
|
|
35
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
36
|
+
},
|
|
37
|
+
instructions: `You are Agent A. Just say "Hello from A" in one sentence.`,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
agentId: 'agent_b',
|
|
41
|
+
provider: Providers.ANTHROPIC,
|
|
42
|
+
clientOptions: {
|
|
43
|
+
modelName: 'claude-haiku-4-5',
|
|
44
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
45
|
+
},
|
|
46
|
+
instructions: `You are Agent B. Just say "Hello from B" in one sentence.`,
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// Sequential edge: A -> B (using edgeType not type)
|
|
51
|
+
const edges: t.GraphEdge[] = [
|
|
52
|
+
{ from: 'agent_a', to: 'agent_b', edgeType: 'direct' },
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const captureMetadata = (
|
|
56
|
+
eventName: string,
|
|
57
|
+
metadata?: Record<string, unknown>
|
|
58
|
+
) => {
|
|
59
|
+
if (metadata) {
|
|
60
|
+
allMetadata.push({
|
|
61
|
+
event: eventName,
|
|
62
|
+
timestamp: Date.now() - startTime,
|
|
63
|
+
metadata: { ...metadata },
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const customHandlers = {
|
|
69
|
+
[GraphEvents.CHAT_MODEL_END]: {
|
|
70
|
+
handle: (
|
|
71
|
+
_event: string,
|
|
72
|
+
_data: t.StreamEventData,
|
|
73
|
+
metadata?: Record<string, unknown>
|
|
74
|
+
): void => {
|
|
75
|
+
captureMetadata('CHAT_MODEL_END', metadata);
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
[GraphEvents.CHAT_MODEL_START]: {
|
|
79
|
+
handle: (
|
|
80
|
+
_event: string,
|
|
81
|
+
_data: t.StreamEventData,
|
|
82
|
+
metadata?: Record<string, unknown>
|
|
83
|
+
): void => {
|
|
84
|
+
captureMetadata('CHAT_MODEL_START', metadata);
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
88
|
+
[GraphEvents.ON_RUN_STEP]: {
|
|
89
|
+
handle: (
|
|
90
|
+
event: GraphEvents.ON_RUN_STEP,
|
|
91
|
+
data: t.StreamEventData,
|
|
92
|
+
metadata?: Record<string, unknown>
|
|
93
|
+
): void => {
|
|
94
|
+
captureMetadata('ON_RUN_STEP', metadata);
|
|
95
|
+
aggregateContent({ event, data: data as t.RunStep });
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
99
|
+
handle: (
|
|
100
|
+
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
101
|
+
data: t.StreamEventData,
|
|
102
|
+
metadata?: Record<string, unknown>
|
|
103
|
+
): void => {
|
|
104
|
+
aggregateContent({ event, data: data as t.RunStepDeltaEvent });
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
108
|
+
handle: (
|
|
109
|
+
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
110
|
+
data: t.StreamEventData,
|
|
111
|
+
metadata?: Record<string, unknown>
|
|
112
|
+
): void => {
|
|
113
|
+
aggregateContent({ event, data: data as t.MessageDeltaEvent });
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const runConfig: t.RunConfig = {
|
|
119
|
+
runId: `sequential-metadata-${Date.now()}`,
|
|
120
|
+
graphConfig: {
|
|
121
|
+
type: 'multi-agent',
|
|
122
|
+
agents,
|
|
123
|
+
edges,
|
|
124
|
+
},
|
|
125
|
+
customHandlers,
|
|
126
|
+
returnContent: true,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const run = await Run.create(runConfig);
|
|
131
|
+
|
|
132
|
+
const userMessage = `Hi`;
|
|
133
|
+
conversationHistory.push(new HumanMessage(userMessage));
|
|
134
|
+
|
|
135
|
+
const config = {
|
|
136
|
+
configurable: {
|
|
137
|
+
thread_id: 'sequential-metadata-test-1',
|
|
138
|
+
},
|
|
139
|
+
streamMode: 'values',
|
|
140
|
+
version: 'v2' as const,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
await run.processStream({ messages: conversationHistory }, config);
|
|
144
|
+
|
|
145
|
+
// Print ALL CHAT_MODEL_START metadata (don't dedupe)
|
|
146
|
+
console.log(
|
|
147
|
+
'\n\n========== ALL CHAT_MODEL_START EVENTS (SEQUENTIAL) ==========\n'
|
|
148
|
+
);
|
|
149
|
+
for (const entry of allMetadata) {
|
|
150
|
+
if (entry.event === 'CHAT_MODEL_START') {
|
|
151
|
+
const node = entry.metadata.langgraph_node as string;
|
|
152
|
+
console.log(`\n--- ${node} (at ${entry.timestamp}ms) ---`);
|
|
153
|
+
console.dir(entry.metadata, { depth: null });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.log('\n\n========== ALL EVENTS ==========\n');
|
|
158
|
+
for (const entry of allMetadata) {
|
|
159
|
+
console.log(
|
|
160
|
+
`[${entry.timestamp}ms] ${entry.event}: ${entry.metadata.langgraph_node}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Key comparison
|
|
165
|
+
console.log(
|
|
166
|
+
'\n\n========== KEY FIELDS COMPARISON (SEQUENTIAL) ==========\n'
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const agentMetadataMap = new Map<string, Record<string, unknown>>();
|
|
170
|
+
for (const entry of allMetadata) {
|
|
171
|
+
if (entry.event === 'CHAT_MODEL_START') {
|
|
172
|
+
const node = entry.metadata.langgraph_node as string;
|
|
173
|
+
if (!agentMetadataMap.has(node)) {
|
|
174
|
+
agentMetadataMap.set(node, entry.metadata);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const [node, meta] of agentMetadataMap) {
|
|
180
|
+
console.log(`${node}:`);
|
|
181
|
+
console.log(` langgraph_step: ${meta.langgraph_step}`);
|
|
182
|
+
console.log(
|
|
183
|
+
` langgraph_triggers: ${JSON.stringify(meta.langgraph_triggers)}`
|
|
184
|
+
);
|
|
185
|
+
console.log(` checkpoint_ns: ${meta.checkpoint_ns}`);
|
|
186
|
+
console.log(` __pregel_task_id: ${meta.__pregel_task_id}`);
|
|
187
|
+
console.log(` langgraph_path: ${JSON.stringify(meta.langgraph_path)}`);
|
|
188
|
+
console.log();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
await sleep(1000);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error('Error:', error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
testSequentialMetadata();
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { config } from 'dotenv';
|
|
2
|
+
config();
|
|
3
|
+
|
|
4
|
+
import { HumanMessage, BaseMessage } from '@langchain/core/messages';
|
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
import type * as t from '@/types';
|
|
7
|
+
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
8
|
+
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
9
|
+
import { Providers, GraphEvents } from '@/common';
|
|
10
|
+
import { sleep } from '@/utils/run';
|
|
11
|
+
import { Run } from '@/run';
|
|
12
|
+
|
|
13
|
+
const conversationHistory: BaseMessage[] = [];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Single agent test with extensive metadata logging
|
|
17
|
+
* Compare with multi-agent-parallel-start.ts to see metadata differences
|
|
18
|
+
*/
|
|
19
|
+
async function testSingleAgent() {
|
|
20
|
+
console.log('Testing Single Agent with Metadata Logging...\n');
|
|
21
|
+
|
|
22
|
+
// Set up content aggregator
|
|
23
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
24
|
+
|
|
25
|
+
const startTime = Date.now();
|
|
26
|
+
|
|
27
|
+
// Create custom handlers with extensive metadata logging
|
|
28
|
+
const customHandlers = {
|
|
29
|
+
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
30
|
+
[GraphEvents.CHAT_MODEL_END]: {
|
|
31
|
+
handle: (
|
|
32
|
+
_event: string,
|
|
33
|
+
_data: t.StreamEventData,
|
|
34
|
+
metadata?: Record<string, unknown>
|
|
35
|
+
): void => {
|
|
36
|
+
console.log('\n====== CHAT_MODEL_END METADATA ======');
|
|
37
|
+
console.dir(metadata, { depth: null });
|
|
38
|
+
const elapsed = Date.now() - startTime;
|
|
39
|
+
console.log(`⏱️ COMPLETED at ${elapsed}ms`);
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
[GraphEvents.CHAT_MODEL_START]: {
|
|
43
|
+
handle: (
|
|
44
|
+
_event: string,
|
|
45
|
+
_data: t.StreamEventData,
|
|
46
|
+
metadata?: Record<string, unknown>
|
|
47
|
+
): void => {
|
|
48
|
+
console.log('\n====== CHAT_MODEL_START METADATA ======');
|
|
49
|
+
console.dir(metadata, { depth: null });
|
|
50
|
+
const elapsed = Date.now() - startTime;
|
|
51
|
+
console.log(`⏱️ STARTED at ${elapsed}ms`);
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
55
|
+
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
56
|
+
handle: (
|
|
57
|
+
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
58
|
+
data: t.StreamEventData,
|
|
59
|
+
metadata?: Record<string, unknown>
|
|
60
|
+
): void => {
|
|
61
|
+
console.log('\n====== ON_RUN_STEP_COMPLETED ======');
|
|
62
|
+
console.log('DATA:');
|
|
63
|
+
console.dir(data, { depth: null });
|
|
64
|
+
console.log('METADATA:');
|
|
65
|
+
console.dir(metadata, { depth: null });
|
|
66
|
+
aggregateContent({
|
|
67
|
+
event,
|
|
68
|
+
data: data as unknown as { result: t.ToolEndEvent },
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
[GraphEvents.ON_RUN_STEP]: {
|
|
73
|
+
handle: (
|
|
74
|
+
event: GraphEvents.ON_RUN_STEP,
|
|
75
|
+
data: t.StreamEventData,
|
|
76
|
+
metadata?: Record<string, unknown>
|
|
77
|
+
): void => {
|
|
78
|
+
console.log('\n====== ON_RUN_STEP ======');
|
|
79
|
+
console.log('DATA:');
|
|
80
|
+
console.dir(data, { depth: null });
|
|
81
|
+
console.log('METADATA:');
|
|
82
|
+
console.dir(metadata, { depth: null });
|
|
83
|
+
aggregateContent({ event, data: data as t.RunStep });
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
87
|
+
handle: (
|
|
88
|
+
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
89
|
+
data: t.StreamEventData,
|
|
90
|
+
metadata?: Record<string, unknown>
|
|
91
|
+
): void => {
|
|
92
|
+
console.log('\n====== ON_RUN_STEP_DELTA ======');
|
|
93
|
+
console.log('DATA:');
|
|
94
|
+
console.dir(data, { depth: null });
|
|
95
|
+
console.log('METADATA:');
|
|
96
|
+
console.dir(metadata, { depth: null });
|
|
97
|
+
aggregateContent({ event, data: data as t.RunStepDeltaEvent });
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
101
|
+
handle: (
|
|
102
|
+
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
103
|
+
data: t.StreamEventData,
|
|
104
|
+
metadata?: Record<string, unknown>
|
|
105
|
+
): void => {
|
|
106
|
+
console.log('\n====== ON_MESSAGE_DELTA ======');
|
|
107
|
+
console.log('DATA:');
|
|
108
|
+
console.dir(data, { depth: null });
|
|
109
|
+
console.log('METADATA:');
|
|
110
|
+
console.dir(metadata, { depth: null });
|
|
111
|
+
aggregateContent({ event, data: data as t.MessageDeltaEvent });
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Create single-agent run configuration (standard graph, not multi-agent)
|
|
117
|
+
const runConfig: t.RunConfig = {
|
|
118
|
+
runId: `single-agent-${Date.now()}`,
|
|
119
|
+
graphConfig: {
|
|
120
|
+
type: 'standard',
|
|
121
|
+
llmConfig: {
|
|
122
|
+
provider: Providers.ANTHROPIC,
|
|
123
|
+
modelName: 'claude-haiku-4-5',
|
|
124
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
125
|
+
},
|
|
126
|
+
instructions: `You are a helpful AI assistant. Keep your response concise (50-100 words).`,
|
|
127
|
+
},
|
|
128
|
+
customHandlers,
|
|
129
|
+
returnContent: true,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
// Create and execute the run
|
|
134
|
+
const run = await Run.create(runConfig);
|
|
135
|
+
|
|
136
|
+
// Debug: Log the graph structure
|
|
137
|
+
console.log('=== DEBUG: Graph Structure ===');
|
|
138
|
+
const graph = (run as any).Graph;
|
|
139
|
+
console.log('Graph exists:', !!graph);
|
|
140
|
+
if (graph) {
|
|
141
|
+
console.log('Graph type:', graph.constructor.name);
|
|
142
|
+
console.log('AgentContexts exists:', !!graph.agentContexts);
|
|
143
|
+
if (graph.agentContexts) {
|
|
144
|
+
console.log('AgentContexts size:', graph.agentContexts.size);
|
|
145
|
+
for (const [agentId, context] of graph.agentContexts) {
|
|
146
|
+
console.log(`\nAgent: ${agentId}`);
|
|
147
|
+
console.log(
|
|
148
|
+
`Tools: ${context.tools?.map((t: any) => t.name || 'unnamed').join(', ') || 'none'}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
console.log('=== END DEBUG ===\n');
|
|
154
|
+
|
|
155
|
+
const userMessage = `What are the best approaches to learning a new programming language?`;
|
|
156
|
+
conversationHistory.push(new HumanMessage(userMessage));
|
|
157
|
+
|
|
158
|
+
console.log('Invoking single-agent graph...\n');
|
|
159
|
+
|
|
160
|
+
const config = {
|
|
161
|
+
configurable: {
|
|
162
|
+
thread_id: 'single-agent-conversation-1',
|
|
163
|
+
},
|
|
164
|
+
streamMode: 'values',
|
|
165
|
+
version: 'v2' as const,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Process with streaming
|
|
169
|
+
const inputs = {
|
|
170
|
+
messages: conversationHistory,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const finalContentParts = await run.processStream(inputs, config);
|
|
174
|
+
const finalMessages = run.getRunMessages();
|
|
175
|
+
|
|
176
|
+
if (finalMessages) {
|
|
177
|
+
conversationHistory.push(...finalMessages);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log('\n\n========== SUMMARY ==========');
|
|
181
|
+
console.log('Final content parts:', contentParts.length, 'parts');
|
|
182
|
+
console.dir(contentParts, { depth: null });
|
|
183
|
+
console.log('====================================\n');
|
|
184
|
+
|
|
185
|
+
await sleep(3000);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error('Error in single-agent test:', error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Run the test
|
|
192
|
+
testSingleAgent();
|
|
@@ -32,6 +32,14 @@ async function testThinkingHandoff() {
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
},
|
|
35
|
+
[GraphEvents.ON_RUN_STEP]: {
|
|
36
|
+
handle: (_event: string, data: t.StreamEventData): void => {
|
|
37
|
+
const runStep = data as t.RunStep;
|
|
38
|
+
console.log(
|
|
39
|
+
`\n📍 ON_RUN_STEP: agentId=${runStep.agentId}, groupId=${runStep.groupId}`
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
},
|
|
35
43
|
};
|
|
36
44
|
|
|
37
45
|
// Create the graph configuration
|
package/src/scripts/tools.ts
CHANGED
|
@@ -21,15 +21,36 @@ async function testStandardStreaming(): Promise<void> {
|
|
|
21
21
|
[GraphEvents.TOOL_END]: new ToolEndHandler(undefined, (name?: string) => {
|
|
22
22
|
return true;
|
|
23
23
|
}),
|
|
24
|
-
[GraphEvents.CHAT_MODEL_END]:
|
|
24
|
+
[GraphEvents.CHAT_MODEL_END]: {
|
|
25
|
+
handle: (
|
|
26
|
+
_event: string,
|
|
27
|
+
_data: t.StreamEventData,
|
|
28
|
+
metadata?: Record<string, unknown>
|
|
29
|
+
): void => {
|
|
30
|
+
console.log('\n====== CHAT_MODEL_END METADATA ======');
|
|
31
|
+
console.dir(metadata, { depth: null });
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
[GraphEvents.CHAT_MODEL_START]: {
|
|
35
|
+
handle: (
|
|
36
|
+
_event: string,
|
|
37
|
+
_data: t.StreamEventData,
|
|
38
|
+
metadata?: Record<string, unknown>
|
|
39
|
+
): void => {
|
|
40
|
+
console.log('\n====== CHAT_MODEL_START METADATA ======');
|
|
41
|
+
console.dir(metadata, { depth: null });
|
|
42
|
+
},
|
|
43
|
+
},
|
|
25
44
|
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
26
45
|
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
27
46
|
handle: (
|
|
28
47
|
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
29
|
-
data: t.StreamEventData
|
|
48
|
+
data: t.StreamEventData,
|
|
49
|
+
metadata?: Record<string, unknown>
|
|
30
50
|
): void => {
|
|
31
51
|
console.log('====== ON_RUN_STEP_COMPLETED ======');
|
|
32
|
-
console.
|
|
52
|
+
console.log('METADATA:');
|
|
53
|
+
console.dir(metadata, { depth: null });
|
|
33
54
|
aggregateContent({
|
|
34
55
|
event,
|
|
35
56
|
data: data as unknown as { result: t.ToolEndEvent },
|
|
@@ -39,10 +60,14 @@ async function testStandardStreaming(): Promise<void> {
|
|
|
39
60
|
[GraphEvents.ON_RUN_STEP]: {
|
|
40
61
|
handle: (
|
|
41
62
|
event: GraphEvents.ON_RUN_STEP,
|
|
42
|
-
data: t.StreamEventData
|
|
63
|
+
data: t.StreamEventData,
|
|
64
|
+
metadata?: Record<string, unknown>
|
|
43
65
|
): void => {
|
|
44
66
|
console.log('====== ON_RUN_STEP ======');
|
|
67
|
+
console.log('DATA:');
|
|
45
68
|
console.dir(data, { depth: null });
|
|
69
|
+
console.log('METADATA:');
|
|
70
|
+
console.dir(metadata, { depth: null });
|
|
46
71
|
aggregateContent({ event, data: data as t.RunStep });
|
|
47
72
|
},
|
|
48
73
|
},
|
|
@@ -51,8 +76,6 @@ async function testStandardStreaming(): Promise<void> {
|
|
|
51
76
|
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
52
77
|
data: t.StreamEventData
|
|
53
78
|
): void => {
|
|
54
|
-
console.log('====== ON_RUN_STEP_DELTA ======');
|
|
55
|
-
console.dir(data, { depth: null });
|
|
56
79
|
aggregateContent({ event, data: data as t.RunStepDeltaEvent });
|
|
57
80
|
},
|
|
58
81
|
},
|
|
@@ -61,8 +84,6 @@ async function testStandardStreaming(): Promise<void> {
|
|
|
61
84
|
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
62
85
|
data: t.StreamEventData
|
|
63
86
|
): void => {
|
|
64
|
-
console.log('====== ON_MESSAGE_DELTA ======');
|
|
65
|
-
console.dir(data, { depth: null });
|
|
66
87
|
aggregateContent({ event, data: data as t.MessageDeltaEvent });
|
|
67
88
|
},
|
|
68
89
|
},
|
|
@@ -71,8 +92,6 @@ async function testStandardStreaming(): Promise<void> {
|
|
|
71
92
|
event: GraphEvents.ON_REASONING_DELTA,
|
|
72
93
|
data: t.StreamEventData
|
|
73
94
|
) => {
|
|
74
|
-
console.log('====== ON_REASONING_DELTA ======');
|
|
75
|
-
console.dir(data, { depth: null });
|
|
76
95
|
aggregateContent({ event, data: data as t.ReasoningDeltaEvent });
|
|
77
96
|
},
|
|
78
97
|
},
|
|
@@ -83,7 +102,8 @@ async function testStandardStreaming(): Promise<void> {
|
|
|
83
102
|
metadata?: Record<string, unknown>
|
|
84
103
|
): void => {
|
|
85
104
|
console.log('====== TOOL_START ======');
|
|
86
|
-
console.
|
|
105
|
+
console.log('METADATA:');
|
|
106
|
+
console.dir(metadata, { depth: null });
|
|
87
107
|
},
|
|
88
108
|
},
|
|
89
109
|
};
|
package/src/stream.ts
CHANGED
|
@@ -442,6 +442,11 @@ export function createContentAggregator(): t.ContentAggregatorResult {
|
|
|
442
442
|
const contentParts: Array<t.MessageContentComplex | undefined> = [];
|
|
443
443
|
const stepMap = new Map<string, t.RunStep>();
|
|
444
444
|
const toolCallIdMap = new Map<string, string>();
|
|
445
|
+
// Track agentId and groupId for each content index (applied to content parts)
|
|
446
|
+
const contentMetaMap = new Map<
|
|
447
|
+
number,
|
|
448
|
+
{ agentId?: string; groupId?: number }
|
|
449
|
+
>();
|
|
445
450
|
|
|
446
451
|
const updateContent = (
|
|
447
452
|
index: number,
|
|
@@ -578,6 +583,15 @@ export function createContentAggregator(): t.ContentAggregatorResult {
|
|
|
578
583
|
tool_call: newToolCall,
|
|
579
584
|
};
|
|
580
585
|
}
|
|
586
|
+
|
|
587
|
+
// Apply agentId and groupId to content part for parallel execution attribution
|
|
588
|
+
const meta = contentMetaMap.get(index);
|
|
589
|
+
if (meta?.agentId !== undefined) {
|
|
590
|
+
(contentParts[index] as t.MessageContentComplex).agentId = meta.agentId;
|
|
591
|
+
}
|
|
592
|
+
if (meta?.groupId !== undefined) {
|
|
593
|
+
(contentParts[index] as t.MessageContentComplex).groupId = meta.groupId;
|
|
594
|
+
}
|
|
581
595
|
};
|
|
582
596
|
|
|
583
597
|
const aggregateContent = ({
|
|
@@ -596,6 +610,21 @@ export function createContentAggregator(): t.ContentAggregatorResult {
|
|
|
596
610
|
const runStep = data as t.RunStep;
|
|
597
611
|
stepMap.set(runStep.id, runStep);
|
|
598
612
|
|
|
613
|
+
// Track agentId and groupId for this content index
|
|
614
|
+
const existingMeta = contentMetaMap.get(runStep.index) ?? {};
|
|
615
|
+
if (runStep.agentId != null && runStep.agentId !== '') {
|
|
616
|
+
existingMeta.agentId = runStep.agentId;
|
|
617
|
+
}
|
|
618
|
+
if (runStep.groupId != null) {
|
|
619
|
+
existingMeta.groupId = runStep.groupId;
|
|
620
|
+
}
|
|
621
|
+
if (
|
|
622
|
+
(existingMeta.agentId != null && existingMeta.agentId !== '') ||
|
|
623
|
+
existingMeta.groupId != null
|
|
624
|
+
) {
|
|
625
|
+
contentMetaMap.set(runStep.index, existingMeta);
|
|
626
|
+
}
|
|
627
|
+
|
|
599
628
|
// Store tool call IDs if present
|
|
600
629
|
if (
|
|
601
630
|
runStep.stepDetails.type === StepTypes.TOOL_CALLS &&
|
package/src/types/stream.ts
CHANGED
|
@@ -66,6 +66,17 @@ export type RunStep = {
|
|
|
66
66
|
id: string; // #new
|
|
67
67
|
runId?: string; // #new
|
|
68
68
|
agentId?: string; // #new - tracks which agent this step belongs to
|
|
69
|
+
/**
|
|
70
|
+
* Group ID - incrementing number (1, 2, 3...) reflecting execution order.
|
|
71
|
+
* Agents with the same groupId run in parallel and should be rendered together.
|
|
72
|
+
* undefined means the agent runs sequentially (not part of any parallel group).
|
|
73
|
+
*
|
|
74
|
+
* Example for: researcher -> [analyst1, analyst2, analyst3] -> summarizer
|
|
75
|
+
* - researcher: undefined (sequential)
|
|
76
|
+
* - analyst1, analyst2, analyst3: 1 (first parallel group)
|
|
77
|
+
* - summarizer: undefined (sequential)
|
|
78
|
+
*/
|
|
79
|
+
groupId?: number; // #new
|
|
69
80
|
index: number; // #new
|
|
70
81
|
stepIndex?: number; // #new
|
|
71
82
|
stepDetails: StepDetails;
|
|
@@ -332,6 +343,10 @@ export type MessageContentComplex = (
|
|
|
332
343
|
})
|
|
333
344
|
) & {
|
|
334
345
|
tool_call_ids?: string[];
|
|
346
|
+
// Optional agentId for parallel execution attribution
|
|
347
|
+
agentId?: string;
|
|
348
|
+
// Optional groupId for parallel group attribution
|
|
349
|
+
groupId?: number;
|
|
335
350
|
};
|
|
336
351
|
|
|
337
352
|
export interface TMessage {
|