@wingman-ai/gateway 0.5.0 → 0.5.2
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/cli/core/agentInvoker.cjs +351 -12
- package/dist/cli/core/agentInvoker.d.ts +18 -1
- package/dist/cli/core/agentInvoker.js +319 -4
- package/dist/cli/core/outputManager.cjs +22 -1
- package/dist/cli/core/outputManager.d.ts +17 -1
- package/dist/cli/core/outputManager.js +22 -1
- package/dist/cli/types.d.ts +18 -1
- package/dist/cli/ui/App.cjs +2 -0
- package/dist/cli/ui/App.js +2 -0
- package/dist/gateway/server.cjs +1 -0
- package/dist/gateway/server.js +1 -0
- package/dist/tests/agentInvokerSummarization.test.cjs +139 -0
- package/dist/tests/agentInvokerSummarization.test.js +140 -1
- package/dist/tests/agentInvokerTokenUsage.test.cjs +124 -0
- package/dist/tests/agentInvokerTokenUsage.test.d.ts +1 -0
- package/dist/tests/agentInvokerTokenUsage.test.js +118 -0
- package/dist/tests/gateway-http-security.test.cjs +20 -0
- package/dist/tests/gateway-http-security.test.js +20 -0
- package/dist/tests/integration/summarization-e2e.integration.test.cjs +127 -0
- package/dist/tests/integration/summarization-e2e.integration.test.d.ts +1 -0
- package/dist/tests/integration/summarization-e2e.integration.test.js +121 -0
- package/dist/tests/outputManagerContextSummarized.test.cjs +43 -0
- package/dist/tests/outputManagerContextSummarized.test.d.ts +1 -0
- package/dist/tests/outputManagerContextSummarized.test.js +37 -0
- package/dist/webui/assets/index-D07GBGp0.js +215 -0
- package/dist/webui/assets/index-DV8IYeOw.css +11 -0
- package/dist/webui/index.html +2 -2
- package/package.json +2 -1
- package/dist/webui/assets/index-_GQBoNDx.js +0 -215
- package/dist/webui/assets/index-tPN3uQMb.css +0 -11
|
@@ -124,6 +124,13 @@ const configureDeepAgentSummarizationMiddleware = (agent, settings, model)=>{
|
|
|
124
124
|
}
|
|
125
125
|
});
|
|
126
126
|
};
|
|
127
|
+
const recompileDeepAgentWithMiddlewareOverrides = (agent)=>{
|
|
128
|
+
if (agent && "object" == typeof agent) {
|
|
129
|
+
const maybeWithConfig = agent.withConfig;
|
|
130
|
+
if ("function" == typeof maybeWithConfig) return maybeWithConfig.call(agent, {});
|
|
131
|
+
}
|
|
132
|
+
return agent;
|
|
133
|
+
};
|
|
127
134
|
const detectToolEventContext = (chunk)=>{
|
|
128
135
|
if (!chunk || "object" != typeof chunk || Array.isArray(chunk)) return null;
|
|
129
136
|
const eventChunk = chunk;
|
|
@@ -134,6 +141,85 @@ const detectToolEventContext = (chunk)=>{
|
|
|
134
141
|
toolName
|
|
135
142
|
};
|
|
136
143
|
};
|
|
144
|
+
const chunkHasBuiltInSummarizationSignal = (chunk)=>{
|
|
145
|
+
if (!chunk || "object" != typeof chunk || Array.isArray(chunk)) return false;
|
|
146
|
+
const eventChunk = chunk;
|
|
147
|
+
if ("on_chain_end" !== eventChunk.event || "SummarizationMiddleware.before_model" !== eventChunk.name) return false;
|
|
148
|
+
const data = eventChunk.data && "object" == typeof eventChunk.data && !Array.isArray(eventChunk.data) ? eventChunk.data : null;
|
|
149
|
+
const output = data?.output && "object" == typeof data.output && !Array.isArray(data.output) ? data.output : null;
|
|
150
|
+
const outputMessages = Array.isArray(output?.messages) ? output.messages : [];
|
|
151
|
+
return outputMessages.some((message)=>{
|
|
152
|
+
if (!message || "object" != typeof message || Array.isArray(message)) return false;
|
|
153
|
+
const messageRecord = message;
|
|
154
|
+
const additionalKwargs = messageRecord.additional_kwargs && "object" == typeof messageRecord.additional_kwargs && !Array.isArray(messageRecord.additional_kwargs) ? messageRecord.additional_kwargs : null;
|
|
155
|
+
return additionalKwargs?.lc_source === "summarization";
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
const SUMMARIZATION_MIDDLEWARE_NODE = "summarizationmiddleware.before_model";
|
|
159
|
+
const normalizeNodeMarker = (value)=>{
|
|
160
|
+
if ("string" != typeof value) return null;
|
|
161
|
+
const normalized = value.trim().toLowerCase();
|
|
162
|
+
return normalized.length > 0 ? normalized : null;
|
|
163
|
+
};
|
|
164
|
+
const extractSummarizationNodeCandidate = (value)=>{
|
|
165
|
+
if (!value || "object" != typeof value || Array.isArray(value)) return null;
|
|
166
|
+
const record = value;
|
|
167
|
+
const directCandidates = [
|
|
168
|
+
record.langgraph_node,
|
|
169
|
+
record.langgraphNode,
|
|
170
|
+
record.node,
|
|
171
|
+
record.node_id,
|
|
172
|
+
record.nodeId
|
|
173
|
+
];
|
|
174
|
+
for (const candidate of directCandidates){
|
|
175
|
+
const normalized = normalizeNodeMarker(candidate);
|
|
176
|
+
if (normalized) return normalized;
|
|
177
|
+
}
|
|
178
|
+
const tagCandidates = [
|
|
179
|
+
record.tags,
|
|
180
|
+
record.ls_tags
|
|
181
|
+
];
|
|
182
|
+
for (const tags of tagCandidates)if (Array.isArray(tags)) for (const tag of tags){
|
|
183
|
+
if ("string" != typeof tag) continue;
|
|
184
|
+
const normalizedTag = tag.trim().toLowerCase();
|
|
185
|
+
if (normalizedTag) {
|
|
186
|
+
if (normalizedTag === `langgraph_node:${SUMMARIZATION_MIDDLEWARE_NODE}`) return SUMMARIZATION_MIDDLEWARE_NODE;
|
|
187
|
+
if (normalizedTag === `langgraph_node=${SUMMARIZATION_MIDDLEWARE_NODE}`) return SUMMARIZATION_MIDDLEWARE_NODE;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
};
|
|
192
|
+
const chunkBelongsToSummarizationMiddleware = (chunk)=>{
|
|
193
|
+
if (!chunk || "object" != typeof chunk || Array.isArray(chunk)) return false;
|
|
194
|
+
const eventChunk = chunk;
|
|
195
|
+
const nameNode = normalizeNodeMarker(eventChunk.name);
|
|
196
|
+
if (nameNode === SUMMARIZATION_MIDDLEWARE_NODE) return true;
|
|
197
|
+
const metadataCandidates = [
|
|
198
|
+
eventChunk.metadata,
|
|
199
|
+
eventChunk.data?.metadata,
|
|
200
|
+
eventChunk.data?.chunk,
|
|
201
|
+
eventChunk.data?.message
|
|
202
|
+
];
|
|
203
|
+
for (const candidate of metadataCandidates){
|
|
204
|
+
const node = extractSummarizationNodeCandidate(candidate);
|
|
205
|
+
if (node === SUMMARIZATION_MIDDLEWARE_NODE) return true;
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
};
|
|
209
|
+
const SUMMARIZATION_ACTIVE_EVENTS = new Set([
|
|
210
|
+
"on_chat_model_start",
|
|
211
|
+
"on_chat_model_stream",
|
|
212
|
+
"on_chat_model_end",
|
|
213
|
+
"on_llm_start",
|
|
214
|
+
"on_llm_stream",
|
|
215
|
+
"on_llm_end"
|
|
216
|
+
]);
|
|
217
|
+
const chunkSignalsActiveSummarization = (chunk)=>{
|
|
218
|
+
if (!chunkBelongsToSummarizationMiddleware(chunk)) return false;
|
|
219
|
+
if (!chunk || "object" != typeof chunk || Array.isArray(chunk)) return false;
|
|
220
|
+
const eventName = chunk.event;
|
|
221
|
+
return "string" == typeof eventName && SUMMARIZATION_ACTIVE_EVENTS.has(eventName);
|
|
222
|
+
};
|
|
137
223
|
const chunkHasAssistantText = (chunk)=>{
|
|
138
224
|
if (!chunk || "object" != typeof chunk || Array.isArray(chunk)) return false;
|
|
139
225
|
const eventChunk = chunk;
|
|
@@ -195,6 +281,202 @@ const detectStreamErrorMessage = (chunk)=>{
|
|
|
195
281
|
if (null != errorPayload) return String(errorPayload);
|
|
196
282
|
return eventName;
|
|
197
283
|
};
|
|
284
|
+
const getFiniteTokenNumber = (value)=>"number" == typeof value && Number.isFinite(value) ? value : 0;
|
|
285
|
+
const asRecord = (value)=>value && "object" == typeof value && !Array.isArray(value) ? value : null;
|
|
286
|
+
const collectTokenUsageSnapshot = (target, payload, visited, depth)=>{
|
|
287
|
+
if (depth > 8 || !payload || "object" != typeof payload) return;
|
|
288
|
+
if (visited.has(payload)) return;
|
|
289
|
+
visited.add(payload);
|
|
290
|
+
const record = payload;
|
|
291
|
+
const directInput = getFiniteTokenNumber(record.input_tokens) || getFiniteTokenNumber(record.inputTokens) || getFiniteTokenNumber(record.prompt_tokens) || getFiniteTokenNumber(record.promptTokens);
|
|
292
|
+
const directOutput = getFiniteTokenNumber(record.output_tokens) || getFiniteTokenNumber(record.outputTokens) || getFiniteTokenNumber(record.completion_tokens) || getFiniteTokenNumber(record.completionTokens);
|
|
293
|
+
const directTotal = getFiniteTokenNumber(record.total_tokens) || getFiniteTokenNumber(record.totalTokens);
|
|
294
|
+
if (directInput > 0) target.inputTokens = Math.max(target.inputTokens, directInput);
|
|
295
|
+
if (directOutput > 0) target.outputTokens = Math.max(target.outputTokens, directOutput);
|
|
296
|
+
if (directTotal > 0) target.totalTokens = Math.max(target.totalTokens, directTotal);
|
|
297
|
+
const nestedCandidates = [
|
|
298
|
+
record.usage,
|
|
299
|
+
record.usage_metadata,
|
|
300
|
+
record.usageMetadata,
|
|
301
|
+
record.tokenUsage,
|
|
302
|
+
record.response_metadata,
|
|
303
|
+
record.responseMetadata,
|
|
304
|
+
record.additional_kwargs,
|
|
305
|
+
record.additionalKwargs,
|
|
306
|
+
record.metadata,
|
|
307
|
+
record.data,
|
|
308
|
+
record.output,
|
|
309
|
+
record.message,
|
|
310
|
+
record.chunk
|
|
311
|
+
];
|
|
312
|
+
for (const nested of nestedCandidates)collectTokenUsageSnapshot(target, nested, visited, depth + 1);
|
|
313
|
+
};
|
|
314
|
+
const extractTokenUsageSnapshot = (payload)=>{
|
|
315
|
+
const snapshot = {
|
|
316
|
+
inputTokens: 0,
|
|
317
|
+
outputTokens: 0,
|
|
318
|
+
totalTokens: 0
|
|
319
|
+
};
|
|
320
|
+
const visited = new WeakSet();
|
|
321
|
+
collectTokenUsageSnapshot(snapshot, payload, visited, 0);
|
|
322
|
+
if (0 === snapshot.totalTokens) snapshot.totalTokens = snapshot.inputTokens + snapshot.outputTokens;
|
|
323
|
+
if (snapshot.inputTokens <= 0 && snapshot.outputTokens <= 0 && snapshot.totalTokens <= 0) return null;
|
|
324
|
+
return snapshot;
|
|
325
|
+
};
|
|
326
|
+
const getMessageClassName = (message)=>{
|
|
327
|
+
const id = message.id;
|
|
328
|
+
if (Array.isArray(id) && id.length > 0) {
|
|
329
|
+
const tail = id[id.length - 1];
|
|
330
|
+
if ("string" == typeof tail) return tail.trim().toLowerCase();
|
|
331
|
+
}
|
|
332
|
+
const type = "string" == typeof message.type ? message.type : "";
|
|
333
|
+
return type.trim().toLowerCase();
|
|
334
|
+
};
|
|
335
|
+
const getMessageRole = (message)=>{
|
|
336
|
+
const kwargs = asRecord(message.kwargs);
|
|
337
|
+
const additionalKwargs = asRecord(message.additional_kwargs);
|
|
338
|
+
const additionalKwargsCamel = asRecord(message.additionalKwargs);
|
|
339
|
+
const candidates = [
|
|
340
|
+
message.role,
|
|
341
|
+
kwargs?.role,
|
|
342
|
+
additionalKwargs?.role,
|
|
343
|
+
additionalKwargsCamel?.role
|
|
344
|
+
];
|
|
345
|
+
for (const candidate of candidates)if ("string" == typeof candidate && candidate.trim()) return candidate.trim().toLowerCase();
|
|
346
|
+
return "";
|
|
347
|
+
};
|
|
348
|
+
const isMessageLikeRecord = (value)=>{
|
|
349
|
+
const record = asRecord(value);
|
|
350
|
+
if (!record) return false;
|
|
351
|
+
const role = getMessageRole(record);
|
|
352
|
+
if ("user" === role || "human" === role || "assistant" === role || "ai" === role || "system" === role || "tool" === role) return true;
|
|
353
|
+
const className = getMessageClassName(record);
|
|
354
|
+
if (className.includes("humanmessage") || className.includes("aimessage") || className.includes("toolmessage") || className.includes("systemmessage")) return true;
|
|
355
|
+
return "human" === className || "user" === className || "assistant" === className || "ai" === className || "system" === className || "tool" === className;
|
|
356
|
+
};
|
|
357
|
+
const extractTextFromContent = (content)=>{
|
|
358
|
+
if ("string" == typeof content) return content;
|
|
359
|
+
if (!Array.isArray(content)) return "";
|
|
360
|
+
return content.map((item)=>{
|
|
361
|
+
if ("string" == typeof item) return item;
|
|
362
|
+
const record = asRecord(item);
|
|
363
|
+
if (!record) return "";
|
|
364
|
+
if ("text" === record.type && "string" == typeof record.text) return record.text;
|
|
365
|
+
return "string" == typeof record.text ? record.text : "";
|
|
366
|
+
}).join("");
|
|
367
|
+
};
|
|
368
|
+
const extractMessageContent = (message)=>{
|
|
369
|
+
const kwargs = asRecord(message.kwargs);
|
|
370
|
+
const additionalKwargs = asRecord(message.additional_kwargs);
|
|
371
|
+
const additionalKwargsCamel = asRecord(message.additionalKwargs);
|
|
372
|
+
const candidates = [
|
|
373
|
+
message.content,
|
|
374
|
+
kwargs?.content,
|
|
375
|
+
additionalKwargs?.content,
|
|
376
|
+
additionalKwargsCamel?.content
|
|
377
|
+
];
|
|
378
|
+
for (const candidate of candidates){
|
|
379
|
+
const extracted = extractTextFromContent(candidate);
|
|
380
|
+
if (extracted.length > 0) return extracted;
|
|
381
|
+
}
|
|
382
|
+
return "";
|
|
383
|
+
};
|
|
384
|
+
const extractToolCalls = (message)=>{
|
|
385
|
+
const kwargs = asRecord(message.kwargs);
|
|
386
|
+
const candidates = [
|
|
387
|
+
message.tool_calls,
|
|
388
|
+
message.toolCalls,
|
|
389
|
+
kwargs?.tool_calls,
|
|
390
|
+
kwargs?.toolCalls
|
|
391
|
+
];
|
|
392
|
+
for (const candidate of candidates)if (Array.isArray(candidate) && candidate.length > 0) return candidate;
|
|
393
|
+
return [];
|
|
394
|
+
};
|
|
395
|
+
const extractToolCallId = (message)=>{
|
|
396
|
+
const kwargs = asRecord(message.kwargs);
|
|
397
|
+
const candidates = [
|
|
398
|
+
message.tool_call_id,
|
|
399
|
+
message.toolCallId,
|
|
400
|
+
kwargs?.tool_call_id,
|
|
401
|
+
kwargs?.toolCallId
|
|
402
|
+
];
|
|
403
|
+
for (const candidate of candidates)if ("string" == typeof candidate && candidate.trim()) return candidate.trim();
|
|
404
|
+
return "";
|
|
405
|
+
};
|
|
406
|
+
const isAiMessageRecord = (message)=>{
|
|
407
|
+
const role = getMessageRole(message);
|
|
408
|
+
if ("assistant" === role || "ai" === role) return true;
|
|
409
|
+
const className = getMessageClassName(message);
|
|
410
|
+
return "ai" === className || "assistant" === className || className.includes("aimessage");
|
|
411
|
+
};
|
|
412
|
+
const isToolMessageRecord = (message)=>{
|
|
413
|
+
const role = getMessageRole(message);
|
|
414
|
+
if ("tool" === role) return true;
|
|
415
|
+
const className = getMessageClassName(message);
|
|
416
|
+
return "tool" === className || className.includes("toolmessage");
|
|
417
|
+
};
|
|
418
|
+
const estimateTokensForMessageArray = (messages)=>{
|
|
419
|
+
if (0 === messages.length) return 0;
|
|
420
|
+
let totalChars = 0;
|
|
421
|
+
for (const message of messages){
|
|
422
|
+
let textContent = extractMessageContent(message);
|
|
423
|
+
if (isAiMessageRecord(message)) {
|
|
424
|
+
const toolCalls = extractToolCalls(message);
|
|
425
|
+
if (toolCalls.length > 0) textContent += JSON.stringify(toolCalls);
|
|
426
|
+
}
|
|
427
|
+
if (isToolMessageRecord(message)) textContent += extractToolCallId(message);
|
|
428
|
+
totalChars += textContent.length;
|
|
429
|
+
}
|
|
430
|
+
return Math.ceil(totalChars / 4);
|
|
431
|
+
};
|
|
432
|
+
const collectMessageArraysFromPayload = (target, payload, visited, depth)=>{
|
|
433
|
+
if (depth > 7 || !payload || "object" != typeof payload) return;
|
|
434
|
+
if (visited.has(payload)) return;
|
|
435
|
+
visited.add(payload);
|
|
436
|
+
if (Array.isArray(payload)) {
|
|
437
|
+
const messageRecords = payload.filter(isMessageLikeRecord);
|
|
438
|
+
if (messageRecords.length > 0) target.push(messageRecords);
|
|
439
|
+
for (const item of payload)collectMessageArraysFromPayload(target, item, visited, depth + 1);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const record = payload;
|
|
443
|
+
const directMessages = record.messages;
|
|
444
|
+
if (Array.isArray(directMessages)) {
|
|
445
|
+
const messageRecords = directMessages.filter(isMessageLikeRecord);
|
|
446
|
+
if (messageRecords.length > 0) target.push(messageRecords);
|
|
447
|
+
}
|
|
448
|
+
for (const nested of Object.values(record))if (nested && "object" == typeof nested) collectMessageArraysFromPayload(target, nested, visited, depth + 1);
|
|
449
|
+
};
|
|
450
|
+
const estimateContextTokensFromChunk = (chunk)=>{
|
|
451
|
+
const candidates = [];
|
|
452
|
+
collectMessageArraysFromPayload(candidates, chunk, new WeakSet(), 0);
|
|
453
|
+
if (0 === candidates.length) return null;
|
|
454
|
+
let estimate = 0;
|
|
455
|
+
for (const candidate of candidates){
|
|
456
|
+
const tokens = estimateTokensForMessageArray(candidate);
|
|
457
|
+
if (tokens > estimate) estimate = tokens;
|
|
458
|
+
}
|
|
459
|
+
return estimate > 0 ? estimate : null;
|
|
460
|
+
};
|
|
461
|
+
const detectContextSummarizationTransition = ({ thresholdTokens, peakInputTokens, currentInputTokens })=>{
|
|
462
|
+
if (!Number.isFinite(thresholdTokens) || !Number.isFinite(peakInputTokens) || !Number.isFinite(currentInputTokens)) return false;
|
|
463
|
+
if (thresholdTokens <= 0 || peakInputTokens <= 0 || currentInputTokens <= 0) return false;
|
|
464
|
+
if (peakInputTokens < 0.9 * thresholdTokens) return false;
|
|
465
|
+
if (currentInputTokens > 0.65 * thresholdTokens) return false;
|
|
466
|
+
if (currentInputTokens > 0.75 * peakInputTokens) return false;
|
|
467
|
+
return true;
|
|
468
|
+
};
|
|
469
|
+
const mergeTokenUsageSnapshots = (current, next)=>{
|
|
470
|
+
if (!next) return current;
|
|
471
|
+
if (!current) return next;
|
|
472
|
+
const merged = {
|
|
473
|
+
inputTokens: Math.max(current.inputTokens, next.inputTokens),
|
|
474
|
+
outputTokens: Math.max(current.outputTokens, next.outputTokens),
|
|
475
|
+
totalTokens: Math.max(current.totalTokens, next.totalTokens)
|
|
476
|
+
};
|
|
477
|
+
if (0 === merged.totalTokens) merged.totalTokens = merged.inputTokens + merged.outputTokens;
|
|
478
|
+
return merged;
|
|
479
|
+
};
|
|
198
480
|
const extractStreamEventRecord = (chunk)=>{
|
|
199
481
|
if (!chunk || "object" != typeof chunk || Array.isArray(chunk)) return null;
|
|
200
482
|
const record = chunk;
|
|
@@ -434,7 +716,7 @@ class AgentInvoker {
|
|
|
434
716
|
rootDir: outputMount.absolutePath,
|
|
435
717
|
virtualMode: true
|
|
436
718
|
});
|
|
437
|
-
|
|
719
|
+
let standaloneAgent = createDeepAgent({
|
|
438
720
|
systemPrompt: targetAgent.systemPrompt,
|
|
439
721
|
tools: targetAgent.tools,
|
|
440
722
|
model: targetAgent.model,
|
|
@@ -450,10 +732,15 @@ class AgentInvoker {
|
|
|
450
732
|
checkpointer: checkpointer
|
|
451
733
|
});
|
|
452
734
|
configureDeepAgentSummarizationMiddleware(standaloneAgent, summarizationSettings, targetAgent.model);
|
|
735
|
+
standaloneAgent = recompileDeepAgentWithMiddlewareOverrides(standaloneAgent);
|
|
453
736
|
this.logger.debug("Agent created, sending message");
|
|
454
737
|
const userContent = buildUserContent(prompt, attachments, targetAgent.model);
|
|
455
738
|
if (this.sessionManager && sessionId) {
|
|
456
739
|
this.logger.debug(`Using streaming with session: ${sessionId}`);
|
|
740
|
+
let streamTokenUsage = null;
|
|
741
|
+
let streamEstimatedContextTokens = 0;
|
|
742
|
+
let contextSummarizationStarted = false;
|
|
743
|
+
let contextSummarizationEmitted = false;
|
|
457
744
|
const stream = await standaloneAgent.streamEvents({
|
|
458
745
|
messages: [
|
|
459
746
|
{
|
|
@@ -486,7 +773,32 @@ class AgentInvoker {
|
|
|
486
773
|
cancelled: true
|
|
487
774
|
};
|
|
488
775
|
}
|
|
489
|
-
|
|
776
|
+
const chunkTokenUsage = extractTokenUsageSnapshot(chunk);
|
|
777
|
+
streamTokenUsage = mergeTokenUsageSnapshots(streamTokenUsage, chunkTokenUsage);
|
|
778
|
+
const isSummarizationChunk = chunkBelongsToSummarizationMiddleware(chunk);
|
|
779
|
+
const isActiveSummarizationChunk = chunkSignalsActiveSummarization(chunk);
|
|
780
|
+
if (isActiveSummarizationChunk && !contextSummarizationStarted) {
|
|
781
|
+
contextSummarizationStarted = true;
|
|
782
|
+
this.outputManager.emitContextSummarizing();
|
|
783
|
+
}
|
|
784
|
+
if (!isSummarizationChunk) {
|
|
785
|
+
const chunkEstimatedContextTokens = estimateContextTokensFromChunk(chunk);
|
|
786
|
+
if ("number" == typeof chunkEstimatedContextTokens && Number.isFinite(chunkEstimatedContextTokens) && chunkEstimatedContextTokens > streamEstimatedContextTokens) streamEstimatedContextTokens = chunkEstimatedContextTokens;
|
|
787
|
+
this.outputManager.emitAgentStream(chunk, chunkTokenUsage || void 0, streamEstimatedContextTokens > 0 ? streamEstimatedContextTokens : void 0);
|
|
788
|
+
}
|
|
789
|
+
if (!contextSummarizationEmitted && summarizationSettings && chunkHasBuiltInSummarizationSignal(chunk)) {
|
|
790
|
+
if (!contextSummarizationStarted) {
|
|
791
|
+
contextSummarizationStarted = true;
|
|
792
|
+
this.outputManager.emitContextSummarizing();
|
|
793
|
+
}
|
|
794
|
+
contextSummarizationEmitted = true;
|
|
795
|
+
const observedInputTokens = chunkTokenUsage?.inputTokens || 0;
|
|
796
|
+
this.outputManager.emitContextSummarized({
|
|
797
|
+
inputTokens: observedInputTokens,
|
|
798
|
+
peakInputTokens: observedInputTokens,
|
|
799
|
+
thresholdTokens: summarizationSettings.maxTokensBeforeSummary
|
|
800
|
+
});
|
|
801
|
+
}
|
|
490
802
|
if (isRootLangGraphTerminalEvent(chunk, rootLangGraphRunId)) {
|
|
491
803
|
this.logger.debug("Detected root LangGraph on_chain_end event; finalizing stream without waiting for iterator shutdown");
|
|
492
804
|
break;
|
|
@@ -501,7 +813,10 @@ class AgentInvoker {
|
|
|
501
813
|
};
|
|
502
814
|
}
|
|
503
815
|
this.logger.info("Agent streaming completed successfully");
|
|
504
|
-
const completionPayload = {
|
|
816
|
+
const completionPayload = streamTokenUsage ? {
|
|
817
|
+
streaming: true,
|
|
818
|
+
tokenUsage: streamTokenUsage
|
|
819
|
+
} : {
|
|
505
820
|
streaming: true
|
|
506
821
|
};
|
|
507
822
|
emitCompletionAndContinuePostProcessing({
|
|
@@ -815,4 +1130,4 @@ function buildAttachmentPreview(attachments) {
|
|
|
815
1130
|
if (hasImage) return "[image]";
|
|
816
1131
|
return "";
|
|
817
1132
|
}
|
|
818
|
-
export { AGENTS_MEMORY_VIRTUAL_PATHS, AgentInvoker, OUTPUT_VIRTUAL_PATH, WORKDIR_VIRTUAL_PATH, buildUserContent, chunkHasAssistantText, configureDeepAgentSummarizationMiddleware, detectStreamErrorMessage, detectToolEventContext, emitCompletionAndContinuePostProcessing, isRootLangGraphTerminalEvent, resolveAgentExecutionWorkspace, resolveAgentMemorySources, resolveExecutionWorkspace, resolveExternalOutputMount, resolveHumanInTheLoopSettings, resolveModelRetryMiddlewareSettings, resolveSummarizationMiddlewareSettings, resolveToolRetryMiddlewareSettings, selectStreamingFallbackText, toWorkspaceAliasVirtualPath, trackRootLangGraphRunId };
|
|
1133
|
+
export { AGENTS_MEMORY_VIRTUAL_PATHS, AgentInvoker, OUTPUT_VIRTUAL_PATH, WORKDIR_VIRTUAL_PATH, buildUserContent, chunkBelongsToSummarizationMiddleware, chunkHasAssistantText, chunkHasBuiltInSummarizationSignal, chunkSignalsActiveSummarization, configureDeepAgentSummarizationMiddleware, detectContextSummarizationTransition, detectStreamErrorMessage, detectToolEventContext, emitCompletionAndContinuePostProcessing, estimateContextTokensFromChunk, extractTokenUsageSnapshot, isRootLangGraphTerminalEvent, mergeTokenUsageSnapshots, recompileDeepAgentWithMiddlewareOverrides, resolveAgentExecutionWorkspace, resolveAgentMemorySources, resolveExecutionWorkspace, resolveExternalOutputMount, resolveHumanInTheLoopSettings, resolveModelRetryMiddlewareSettings, resolveSummarizationMiddlewareSettings, resolveToolRetryMiddlewareSettings, selectStreamingFallbackText, toWorkspaceAliasVirtualPath, trackRootLangGraphRunId };
|
|
@@ -66,10 +66,31 @@ class OutputManager extends external_node_events_namespaceObject.EventEmitter {
|
|
|
66
66
|
timestamp: new Date().toISOString()
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
-
emitAgentStream(chunk) {
|
|
69
|
+
emitAgentStream(chunk, tokenUsage, estimatedContextTokens) {
|
|
70
70
|
this.emitEvent({
|
|
71
71
|
type: "agent-stream",
|
|
72
72
|
chunk,
|
|
73
|
+
...tokenUsage ? {
|
|
74
|
+
tokenUsage
|
|
75
|
+
} : {},
|
|
76
|
+
..."number" == typeof estimatedContextTokens && Number.isFinite(estimatedContextTokens) && estimatedContextTokens > 0 ? {
|
|
77
|
+
estimatedContextTokens: Math.round(estimatedContextTokens)
|
|
78
|
+
} : {},
|
|
79
|
+
timestamp: new Date().toISOString()
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
emitContextSummarizing() {
|
|
83
|
+
this.emitEvent({
|
|
84
|
+
type: "context-summarizing",
|
|
85
|
+
timestamp: new Date().toISOString()
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
emitContextSummarized(payload) {
|
|
89
|
+
this.emitEvent({
|
|
90
|
+
type: "context-summarized",
|
|
91
|
+
inputTokens: payload.inputTokens,
|
|
92
|
+
peakInputTokens: payload.peakInputTokens,
|
|
93
|
+
thresholdTokens: payload.thresholdTokens,
|
|
73
94
|
timestamp: new Date().toISOString()
|
|
74
95
|
});
|
|
75
96
|
}
|
|
@@ -31,7 +31,23 @@ export declare class OutputManager extends EventEmitter {
|
|
|
31
31
|
* Emit agent stream chunk
|
|
32
32
|
* Forwards raw chunks from deepagents/LangGraph for client-side interpretation
|
|
33
33
|
*/
|
|
34
|
-
emitAgentStream(chunk: any
|
|
34
|
+
emitAgentStream(chunk: any, tokenUsage?: {
|
|
35
|
+
inputTokens: number;
|
|
36
|
+
outputTokens: number;
|
|
37
|
+
totalTokens: number;
|
|
38
|
+
}, estimatedContextTokens?: number): void;
|
|
39
|
+
/**
|
|
40
|
+
* Emit explicit context summarization start signal
|
|
41
|
+
*/
|
|
42
|
+
emitContextSummarizing(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Emit explicit context summarization signal
|
|
45
|
+
*/
|
|
46
|
+
emitContextSummarized(payload: {
|
|
47
|
+
inputTokens: number;
|
|
48
|
+
peakInputTokens: number;
|
|
49
|
+
thresholdTokens: number;
|
|
50
|
+
}): void;
|
|
35
51
|
/**
|
|
36
52
|
* Emit agent completion
|
|
37
53
|
*/
|
|
@@ -38,10 +38,31 @@ class OutputManager extends EventEmitter {
|
|
|
38
38
|
timestamp: new Date().toISOString()
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
-
emitAgentStream(chunk) {
|
|
41
|
+
emitAgentStream(chunk, tokenUsage, estimatedContextTokens) {
|
|
42
42
|
this.emitEvent({
|
|
43
43
|
type: "agent-stream",
|
|
44
44
|
chunk,
|
|
45
|
+
...tokenUsage ? {
|
|
46
|
+
tokenUsage
|
|
47
|
+
} : {},
|
|
48
|
+
..."number" == typeof estimatedContextTokens && Number.isFinite(estimatedContextTokens) && estimatedContextTokens > 0 ? {
|
|
49
|
+
estimatedContextTokens: Math.round(estimatedContextTokens)
|
|
50
|
+
} : {},
|
|
51
|
+
timestamp: new Date().toISOString()
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
emitContextSummarizing() {
|
|
55
|
+
this.emitEvent({
|
|
56
|
+
type: "context-summarizing",
|
|
57
|
+
timestamp: new Date().toISOString()
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
emitContextSummarized(payload) {
|
|
61
|
+
this.emitEvent({
|
|
62
|
+
type: "context-summarized",
|
|
63
|
+
inputTokens: payload.inputTokens,
|
|
64
|
+
peakInputTokens: payload.peakInputTokens,
|
|
65
|
+
thresholdTokens: payload.thresholdTokens,
|
|
45
66
|
timestamp: new Date().toISOString()
|
|
46
67
|
});
|
|
47
68
|
}
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -130,6 +130,23 @@ export interface AgentStartEvent {
|
|
|
130
130
|
export interface AgentStreamEvent {
|
|
131
131
|
type: "agent-stream";
|
|
132
132
|
chunk: any;
|
|
133
|
+
tokenUsage?: {
|
|
134
|
+
inputTokens: number;
|
|
135
|
+
outputTokens: number;
|
|
136
|
+
totalTokens: number;
|
|
137
|
+
};
|
|
138
|
+
estimatedContextTokens?: number;
|
|
139
|
+
timestamp: string;
|
|
140
|
+
}
|
|
141
|
+
export interface AgentContextSummarizingEvent {
|
|
142
|
+
type: "context-summarizing";
|
|
143
|
+
timestamp: string;
|
|
144
|
+
}
|
|
145
|
+
export interface AgentContextSummarizedEvent {
|
|
146
|
+
type: "context-summarized";
|
|
147
|
+
inputTokens: number;
|
|
148
|
+
peakInputTokens: number;
|
|
149
|
+
thresholdTokens: number;
|
|
133
150
|
timestamp: string;
|
|
134
151
|
}
|
|
135
152
|
export interface AgentCompleteEvent {
|
|
@@ -178,7 +195,7 @@ export interface SkillRemoveEvent {
|
|
|
178
195
|
skill: string;
|
|
179
196
|
timestamp: string;
|
|
180
197
|
}
|
|
181
|
-
export type OutputEvent = LogEvent | AgentStartEvent | AgentStreamEvent | AgentCompleteEvent | AgentErrorEvent | SkillBrowseEvent | SkillInstallProgressEvent | SkillInstallCompleteEvent | SkillListEvent | SkillRemoveEvent;
|
|
198
|
+
export type OutputEvent = LogEvent | AgentStartEvent | AgentStreamEvent | AgentContextSummarizingEvent | AgentContextSummarizedEvent | AgentCompleteEvent | AgentErrorEvent | SkillBrowseEvent | SkillInstallProgressEvent | SkillInstallCompleteEvent | SkillListEvent | SkillRemoveEvent;
|
|
182
199
|
export interface TextBlock {
|
|
183
200
|
content: string;
|
|
184
201
|
isStreaming: boolean;
|
package/dist/cli/ui/App.cjs
CHANGED
package/dist/cli/ui/App.js
CHANGED
package/dist/gateway/server.cjs
CHANGED
|
@@ -1652,6 +1652,7 @@ class GatewayServer {
|
|
|
1652
1652
|
defaultAgentId,
|
|
1653
1653
|
outputRoot: this.resolveOutputRoot(),
|
|
1654
1654
|
dynamicUiEnabled: this.wingmanConfig.gateway?.dynamicUiEnabled !== false,
|
|
1655
|
+
summarization: this.wingmanConfig.summarization,
|
|
1655
1656
|
voice: this.wingmanConfig.voice,
|
|
1656
1657
|
agents
|
|
1657
1658
|
}, null, 2), {
|
package/dist/gateway/server.js
CHANGED
|
@@ -1617,6 +1617,7 @@ class GatewayServer {
|
|
|
1617
1617
|
defaultAgentId,
|
|
1618
1618
|
outputRoot: this.resolveOutputRoot(),
|
|
1619
1619
|
dynamicUiEnabled: this.wingmanConfig.gateway?.dynamicUiEnabled !== false,
|
|
1620
|
+
summarization: this.wingmanConfig.summarization,
|
|
1620
1621
|
voice: this.wingmanConfig.voice,
|
|
1621
1622
|
agents
|
|
1622
1623
|
}, null, 2), {
|