@librechat/agents 3.0.54 → 3.0.55
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 +11 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +7 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +22 -1
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/stream.cjs +19 -19
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +11 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +7 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +22 -1
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/stream.mjs +19 -19
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/types/graphs/Graph.d.ts +6 -0
- package/dist/types/graphs/MultiAgentGraph.d.ts +5 -0
- package/dist/types/messages/format.d.ts +8 -2
- package/dist/types/types/stream.d.ts +11 -2
- package/package.json +1 -1
- package/src/graphs/Graph.ts +12 -2
- package/src/graphs/MultiAgentGraph.ts +8 -0
- package/src/messages/format.ts +33 -3
- package/src/messages/formatAgentMessages.test.ts +168 -0
- package/src/scripts/multi-agent-parallel-start.ts +5 -3
- package/src/scripts/multi-agent-sequence.ts +6 -1
- package/src/scripts/single-agent-metadata-test.ts +7 -1
- package/src/stream.ts +19 -22
- package/src/types/stream.ts +12 -4
|
@@ -203,6 +203,14 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
203
203
|
return this.agentParallelGroups.get(agentId);
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Override to indicate this is a multi-agent graph.
|
|
208
|
+
* Enables agentId to be included in RunStep for frontend agent labeling.
|
|
209
|
+
*/
|
|
210
|
+
protected override isMultiAgentGraph(): boolean {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
|
|
206
214
|
/**
|
|
207
215
|
* Override base class method to provide parallel group IDs for run steps.
|
|
208
216
|
*/
|
package/src/messages/format.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
ExtendedMessageContent,
|
|
15
15
|
MessageContentComplex,
|
|
16
16
|
ReasoningContentText,
|
|
17
|
+
ContentMetadata,
|
|
17
18
|
ToolCallContent,
|
|
18
19
|
ToolCallPart,
|
|
19
20
|
TPayload,
|
|
@@ -626,23 +627,31 @@ export const labelContentByAgent = (
|
|
|
626
627
|
* @param payload - The array of messages to format.
|
|
627
628
|
* @param indexTokenCountMap - Optional map of message indices to token counts.
|
|
628
629
|
* @param tools - Optional set of tool names that are allowed in the request.
|
|
630
|
+
* @param options - Optional configuration for agent filtering.
|
|
631
|
+
* @param options.targetAgentId - If provided, only content parts from this agent will be included.
|
|
632
|
+
* @param options.contentMetadataMap - Map of content index to metadata (required when targetAgentId is provided).
|
|
629
633
|
* @returns - Object containing formatted messages and updated indexTokenCountMap if provided.
|
|
630
634
|
*/
|
|
631
635
|
export const formatAgentMessages = (
|
|
632
636
|
payload: TPayload,
|
|
633
|
-
indexTokenCountMap?: Record<number, number>,
|
|
634
|
-
tools?: Set<string
|
|
637
|
+
indexTokenCountMap?: Record<number, number | undefined>,
|
|
638
|
+
tools?: Set<string>,
|
|
639
|
+
options?: {
|
|
640
|
+
targetAgentId?: string;
|
|
641
|
+
contentMetadataMap?: Map<number, ContentMetadata>;
|
|
642
|
+
}
|
|
635
643
|
): {
|
|
636
644
|
messages: Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>;
|
|
637
645
|
indexTokenCountMap?: Record<number, number>;
|
|
638
646
|
} => {
|
|
647
|
+
const { targetAgentId, contentMetadataMap } = options ?? {};
|
|
639
648
|
const messages: Array<
|
|
640
649
|
HumanMessage | AIMessage | SystemMessage | ToolMessage
|
|
641
650
|
> = [];
|
|
642
651
|
// If indexTokenCountMap is provided, create a new map to track the updated indices
|
|
643
652
|
const updatedIndexTokenCountMap: Record<number, number> = {};
|
|
644
653
|
// Keep track of the mapping from original payload indices to result indices
|
|
645
|
-
const indexMapping: Record<number, number[]> = {};
|
|
654
|
+
const indexMapping: Record<number, number[] | undefined> = {};
|
|
646
655
|
|
|
647
656
|
// Process messages with tool conversion if tools set is provided
|
|
648
657
|
for (let i = 0; i < payload.length; i++) {
|
|
@@ -654,6 +663,27 @@ export const formatAgentMessages = (
|
|
|
654
663
|
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: message.content },
|
|
655
664
|
];
|
|
656
665
|
}
|
|
666
|
+
|
|
667
|
+
// Filter content parts by targetAgentId if provided (only for assistant messages with array content)
|
|
668
|
+
if (
|
|
669
|
+
targetAgentId != null &&
|
|
670
|
+
targetAgentId !== '' &&
|
|
671
|
+
contentMetadataMap != null &&
|
|
672
|
+
message.role === 'assistant' &&
|
|
673
|
+
Array.isArray(message.content)
|
|
674
|
+
) {
|
|
675
|
+
const filteredContent = message.content.filter((_, partIndex) => {
|
|
676
|
+
const metadata = contentMetadataMap.get(partIndex);
|
|
677
|
+
return metadata?.agentId === targetAgentId;
|
|
678
|
+
});
|
|
679
|
+
// Skip this message entirely if no content parts match the target agent
|
|
680
|
+
if (filteredContent.length === 0) {
|
|
681
|
+
indexMapping[i] = [];
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
message.content = filteredContent;
|
|
685
|
+
}
|
|
686
|
+
|
|
657
687
|
if (message.role !== 'assistant') {
|
|
658
688
|
messages.push(
|
|
659
689
|
formatMessage({
|
|
@@ -1141,4 +1141,172 @@ describe('formatAgentMessages', () => {
|
|
|
1141
1141
|
expect(result.messages[1].name).toBe('search');
|
|
1142
1142
|
expect(result.messages[1].content).toBe('');
|
|
1143
1143
|
});
|
|
1144
|
+
|
|
1145
|
+
describe('targetAgentId filtering', () => {
|
|
1146
|
+
it('should filter content parts to only include those from targetAgentId', () => {
|
|
1147
|
+
const payload: TPayload = [
|
|
1148
|
+
{ role: 'user', content: 'Hello' },
|
|
1149
|
+
{
|
|
1150
|
+
role: 'assistant',
|
|
1151
|
+
content: [
|
|
1152
|
+
{ type: ContentTypes.TEXT, text: 'Response from agent_a' },
|
|
1153
|
+
{ type: ContentTypes.TEXT, text: 'Response from agent_b' },
|
|
1154
|
+
{ type: ContentTypes.TEXT, text: 'Another from agent_a' },
|
|
1155
|
+
],
|
|
1156
|
+
},
|
|
1157
|
+
];
|
|
1158
|
+
|
|
1159
|
+
const contentMetadataMap = new Map([
|
|
1160
|
+
[0, { agentId: 'agent_a' }],
|
|
1161
|
+
[1, { agentId: 'agent_b' }],
|
|
1162
|
+
[2, { agentId: 'agent_a' }],
|
|
1163
|
+
]);
|
|
1164
|
+
|
|
1165
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1166
|
+
targetAgentId: 'agent_a',
|
|
1167
|
+
contentMetadataMap,
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
// Should have user message + filtered assistant message
|
|
1171
|
+
expect(result.messages).toHaveLength(2);
|
|
1172
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
1173
|
+
expect(result.messages[1]).toBeInstanceOf(AIMessage);
|
|
1174
|
+
|
|
1175
|
+
// The AIMessage should only have agent_a's content parts
|
|
1176
|
+
const aiMessage = result.messages[1] as AIMessage;
|
|
1177
|
+
expect(Array.isArray(aiMessage.content)).toBe(true);
|
|
1178
|
+
expect(aiMessage.content as Array<{ text: string }>).toHaveLength(2);
|
|
1179
|
+
expect((aiMessage.content as Array<{ text: string }>)[0].text).toBe(
|
|
1180
|
+
'Response from agent_a'
|
|
1181
|
+
);
|
|
1182
|
+
expect((aiMessage.content as Array<{ text: string }>)[1].text).toBe(
|
|
1183
|
+
'Another from agent_a'
|
|
1184
|
+
);
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
it('should skip assistant message entirely if no content parts match targetAgentId', () => {
|
|
1188
|
+
const payload: TPayload = [
|
|
1189
|
+
{ role: 'user', content: 'Hello' },
|
|
1190
|
+
{
|
|
1191
|
+
role: 'assistant',
|
|
1192
|
+
content: [{ type: ContentTypes.TEXT, text: 'Response from agent_b' }],
|
|
1193
|
+
},
|
|
1194
|
+
];
|
|
1195
|
+
|
|
1196
|
+
const contentMetadataMap = new Map([[0, { agentId: 'agent_b' }]]);
|
|
1197
|
+
|
|
1198
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1199
|
+
targetAgentId: 'agent_a',
|
|
1200
|
+
contentMetadataMap,
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
// Should only have the user message, assistant message skipped
|
|
1204
|
+
expect(result.messages).toHaveLength(1);
|
|
1205
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
it('should not filter when targetAgentId is not provided', () => {
|
|
1209
|
+
const payload: TPayload = [
|
|
1210
|
+
{
|
|
1211
|
+
role: 'assistant',
|
|
1212
|
+
content: [
|
|
1213
|
+
{ type: ContentTypes.TEXT, text: 'Response from agent_a' },
|
|
1214
|
+
{ type: ContentTypes.TEXT, text: 'Response from agent_b' },
|
|
1215
|
+
],
|
|
1216
|
+
},
|
|
1217
|
+
];
|
|
1218
|
+
|
|
1219
|
+
const contentMetadataMap = new Map([
|
|
1220
|
+
[0, { agentId: 'agent_a' }],
|
|
1221
|
+
[1, { agentId: 'agent_b' }],
|
|
1222
|
+
]);
|
|
1223
|
+
|
|
1224
|
+
// No targetAgentId provided - should include all content
|
|
1225
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1226
|
+
contentMetadataMap,
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
expect(result.messages).toHaveLength(1);
|
|
1230
|
+
const aiMessage = result.messages[0] as AIMessage;
|
|
1231
|
+
expect(Array.isArray(aiMessage.content)).toBe(true);
|
|
1232
|
+
expect(aiMessage.content as Array<{ text: string }>).toHaveLength(2);
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
it('should not filter when contentMetadataMap is not provided', () => {
|
|
1236
|
+
const payload: TPayload = [
|
|
1237
|
+
{
|
|
1238
|
+
role: 'assistant',
|
|
1239
|
+
content: [
|
|
1240
|
+
{ type: ContentTypes.TEXT, text: 'Response 1' },
|
|
1241
|
+
{ type: ContentTypes.TEXT, text: 'Response 2' },
|
|
1242
|
+
],
|
|
1243
|
+
},
|
|
1244
|
+
];
|
|
1245
|
+
|
|
1246
|
+
// targetAgentId provided but no contentMetadataMap - should include all content
|
|
1247
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1248
|
+
targetAgentId: 'agent_a',
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
expect(result.messages).toHaveLength(1);
|
|
1252
|
+
const aiMessage = result.messages[0] as AIMessage;
|
|
1253
|
+
expect(Array.isArray(aiMessage.content)).toBe(true);
|
|
1254
|
+
expect(aiMessage.content as Array<{ text: string }>).toHaveLength(2);
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
it('should filter content with groupId metadata (parallel execution)', () => {
|
|
1258
|
+
const payload: TPayload = [
|
|
1259
|
+
{ role: 'user', content: 'Analyze this' },
|
|
1260
|
+
{
|
|
1261
|
+
role: 'assistant',
|
|
1262
|
+
content: [
|
|
1263
|
+
{ type: ContentTypes.TEXT, text: 'Creative analysis' },
|
|
1264
|
+
{ type: ContentTypes.TEXT, text: 'Practical analysis' },
|
|
1265
|
+
],
|
|
1266
|
+
},
|
|
1267
|
+
];
|
|
1268
|
+
|
|
1269
|
+
const contentMetadataMap = new Map([
|
|
1270
|
+
[0, { agentId: 'creative_analyst', groupId: 1 }],
|
|
1271
|
+
[1, { agentId: 'practical_analyst', groupId: 1 }],
|
|
1272
|
+
]);
|
|
1273
|
+
|
|
1274
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1275
|
+
targetAgentId: 'creative_analyst',
|
|
1276
|
+
contentMetadataMap,
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
expect(result.messages).toHaveLength(2);
|
|
1280
|
+
const aiMessage = result.messages[1] as AIMessage;
|
|
1281
|
+
expect(Array.isArray(aiMessage.content)).toBe(true);
|
|
1282
|
+
expect(aiMessage.content as Array<{ text: string }>).toHaveLength(1);
|
|
1283
|
+
expect((aiMessage.content as Array<{ text: string }>)[0].text).toBe(
|
|
1284
|
+
'Creative analysis'
|
|
1285
|
+
);
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
it('should not affect non-assistant messages when filtering', () => {
|
|
1289
|
+
const payload: TPayload = [
|
|
1290
|
+
{ role: 'user', content: 'Hello from user' },
|
|
1291
|
+
{ role: 'system', content: 'System message' },
|
|
1292
|
+
{
|
|
1293
|
+
role: 'assistant',
|
|
1294
|
+
content: [{ type: ContentTypes.TEXT, text: 'From agent_a' }],
|
|
1295
|
+
},
|
|
1296
|
+
];
|
|
1297
|
+
|
|
1298
|
+
const contentMetadataMap = new Map([[0, { agentId: 'agent_a' }]]);
|
|
1299
|
+
|
|
1300
|
+
const result = formatAgentMessages(payload, undefined, undefined, {
|
|
1301
|
+
targetAgentId: 'agent_a',
|
|
1302
|
+
contentMetadataMap,
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
// All three messages should be present
|
|
1306
|
+
expect(result.messages).toHaveLength(3);
|
|
1307
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
1308
|
+
expect(result.messages[1]).toBeInstanceOf(SystemMessage);
|
|
1309
|
+
expect(result.messages[2]).toBeInstanceOf(AIMessage);
|
|
1310
|
+
});
|
|
1311
|
+
});
|
|
1144
1312
|
});
|
|
@@ -25,7 +25,8 @@ async function testParallelFromStart() {
|
|
|
25
25
|
console.log('Testing Parallel From Start Multi-Agent System...\n');
|
|
26
26
|
|
|
27
27
|
// Set up content aggregator
|
|
28
|
-
const { contentParts, aggregateContent } =
|
|
28
|
+
const { contentParts, aggregateContent, contentMetadataMap } =
|
|
29
|
+
createContentAggregator();
|
|
29
30
|
|
|
30
31
|
// Define two agents - both have NO incoming edges, so they run in parallel from the start
|
|
31
32
|
const agents: t.AgentInputs[] = [
|
|
@@ -249,9 +250,10 @@ async function testParallelFromStart() {
|
|
|
249
250
|
console.log('====================================\n');
|
|
250
251
|
|
|
251
252
|
console.log('Final content parts:', contentParts.length, 'parts');
|
|
253
|
+
console.log('\n=== Content Parts (clean, no metadata) ===');
|
|
252
254
|
console.dir(contentParts, { depth: null });
|
|
253
|
-
|
|
254
|
-
|
|
255
|
+
console.log('\n=== Content Metadata Map (separate from content) ===');
|
|
256
|
+
console.dir(Object.fromEntries(contentMetadataMap), { depth: null });
|
|
255
257
|
|
|
256
258
|
await sleep(3000);
|
|
257
259
|
} catch (error) {
|
|
@@ -22,7 +22,8 @@ async function testSequentialMultiAgent() {
|
|
|
22
22
|
console.log('Testing Sequential Multi-Agent System (A → B → C)...\n');
|
|
23
23
|
|
|
24
24
|
// Set up content aggregator
|
|
25
|
-
const { contentParts, aggregateContent } =
|
|
25
|
+
const { contentParts, aggregateContent, contentMetadataMap } =
|
|
26
|
+
createContentAggregator();
|
|
26
27
|
|
|
27
28
|
// Define three simple agents
|
|
28
29
|
const agents: t.AgentInputs[] = [
|
|
@@ -194,6 +195,10 @@ async function testSequentialMultiAgent() {
|
|
|
194
195
|
console.log('\n\n=== Final Output ===');
|
|
195
196
|
console.log('Sequential flow completed successfully!');
|
|
196
197
|
console.log(`Total content parts: ${contentParts.length}`);
|
|
198
|
+
console.log('\n=== Content Parts (clean, no metadata) ===');
|
|
199
|
+
console.dir(contentParts, { depth: null });
|
|
200
|
+
console.log('\n=== Content Metadata Map (separate from content) ===');
|
|
201
|
+
console.dir(Object.fromEntries(contentMetadataMap), { depth: null });
|
|
197
202
|
|
|
198
203
|
// Display the sequential responses
|
|
199
204
|
const aiMessages = conversationHistory.filter(
|
|
@@ -20,7 +20,8 @@ async function testSingleAgent() {
|
|
|
20
20
|
console.log('Testing Single Agent with Metadata Logging...\n');
|
|
21
21
|
|
|
22
22
|
// Set up content aggregator
|
|
23
|
-
const { contentParts, aggregateContent } =
|
|
23
|
+
const { contentParts, aggregateContent, contentMetadataMap } =
|
|
24
|
+
createContentAggregator();
|
|
24
25
|
|
|
25
26
|
const startTime = Date.now();
|
|
26
27
|
|
|
@@ -179,7 +180,12 @@ async function testSingleAgent() {
|
|
|
179
180
|
|
|
180
181
|
console.log('\n\n========== SUMMARY ==========');
|
|
181
182
|
console.log('Final content parts:', contentParts.length, 'parts');
|
|
183
|
+
console.log('\n=== Content Parts (clean, no metadata) ===');
|
|
182
184
|
console.dir(contentParts, { depth: null });
|
|
185
|
+
console.log(
|
|
186
|
+
'\n=== Content Metadata Map (should be empty for single-agent) ==='
|
|
187
|
+
);
|
|
188
|
+
console.dir(Object.fromEntries(contentMetadataMap), { depth: null });
|
|
183
189
|
console.log('====================================\n');
|
|
184
190
|
|
|
185
191
|
await sleep(3000);
|
package/src/stream.ts
CHANGED
|
@@ -583,15 +583,6 @@ export function createContentAggregator(): t.ContentAggregatorResult {
|
|
|
583
583
|
tool_call: newToolCall,
|
|
584
584
|
};
|
|
585
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
|
-
}
|
|
595
586
|
};
|
|
596
587
|
|
|
597
588
|
const aggregateContent = ({
|
|
@@ -610,18 +601,19 @@ export function createContentAggregator(): t.ContentAggregatorResult {
|
|
|
610
601
|
const runStep = data as t.RunStep;
|
|
611
602
|
stepMap.set(runStep.id, runStep);
|
|
612
603
|
|
|
613
|
-
// Track agentId and groupId
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
if (
|
|
619
|
-
existingMeta
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
604
|
+
// Track agentId (MultiAgentGraph) and groupId (parallel execution) separately
|
|
605
|
+
// - agentId: present for all MultiAgentGraph runs (enables agent labels in UI)
|
|
606
|
+
// - groupId: present only for parallel execution (enables column rendering)
|
|
607
|
+
const hasAgentId = runStep.agentId != null && runStep.agentId !== '';
|
|
608
|
+
const hasGroupId = runStep.groupId != null;
|
|
609
|
+
if (hasAgentId || hasGroupId) {
|
|
610
|
+
const existingMeta = contentMetaMap.get(runStep.index) ?? {};
|
|
611
|
+
if (hasAgentId) {
|
|
612
|
+
existingMeta.agentId = runStep.agentId;
|
|
613
|
+
}
|
|
614
|
+
if (hasGroupId) {
|
|
615
|
+
existingMeta.groupId = runStep.groupId;
|
|
616
|
+
}
|
|
625
617
|
contentMetaMap.set(runStep.index, existingMeta);
|
|
626
618
|
}
|
|
627
619
|
|
|
@@ -735,5 +727,10 @@ export function createContentAggregator(): t.ContentAggregatorResult {
|
|
|
735
727
|
}
|
|
736
728
|
};
|
|
737
729
|
|
|
738
|
-
return {
|
|
730
|
+
return {
|
|
731
|
+
contentParts,
|
|
732
|
+
aggregateContent,
|
|
733
|
+
stepMap,
|
|
734
|
+
contentMetadataMap: contentMetaMap,
|
|
735
|
+
};
|
|
739
736
|
}
|
package/src/types/stream.ts
CHANGED
|
@@ -343,10 +343,6 @@ export type MessageContentComplex = (
|
|
|
343
343
|
})
|
|
344
344
|
) & {
|
|
345
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;
|
|
350
346
|
};
|
|
351
347
|
|
|
352
348
|
export interface TMessage {
|
|
@@ -410,8 +406,20 @@ export type ContentAggregator = ({
|
|
|
410
406
|
result: ToolEndEvent;
|
|
411
407
|
};
|
|
412
408
|
}) => void;
|
|
409
|
+
/**
|
|
410
|
+
* Metadata for content parts in multi-agent runs.
|
|
411
|
+
* - agentId: present for all MultiAgentGraph runs (enables agent labels in UI)
|
|
412
|
+
* - groupId: present only for parallel execution (enables column rendering)
|
|
413
|
+
*/
|
|
414
|
+
export type ContentMetadata = {
|
|
415
|
+
agentId?: string;
|
|
416
|
+
groupId?: number;
|
|
417
|
+
};
|
|
418
|
+
|
|
413
419
|
export type ContentAggregatorResult = {
|
|
414
420
|
stepMap: Map<string, RunStep | undefined>;
|
|
415
421
|
contentParts: Array<MessageContentComplex | undefined>;
|
|
422
|
+
/** Map of content index to metadata (agentId, groupId). Only populated for MultiAgentGraph runs. */
|
|
423
|
+
contentMetadataMap: Map<number, ContentMetadata>;
|
|
416
424
|
aggregateContent: ContentAggregator;
|
|
417
425
|
};
|