@inkeep/agents-run-api 0.0.0-dev-20260105192809 → 0.0.0-dev-20260105201718
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/a2a/handlers.js +2 -1
- package/dist/agents/Agent.d.ts +47 -1
- package/dist/agents/Agent.js +558 -569
- package/dist/agents/generateTaskHandler.js +12 -3
- package/dist/constants/execution-limits/defaults.d.ts +4 -0
- package/dist/constants/execution-limits/defaults.js +5 -1
- package/dist/constants/execution-limits/index.d.ts +2 -2
- package/dist/constants/execution-limits/index.js +8 -3
- package/dist/data/conversations.d.ts +43 -3
- package/dist/data/conversations.js +317 -10
- package/dist/handlers/executionHandler.js +1 -1
- package/dist/services/AgentSession.d.ts +1 -1
- package/dist/services/AgentSession.js +1 -1
- package/dist/services/ArtifactService.js +2 -1
- package/dist/services/BaseCompressor.d.ts +183 -0
- package/dist/services/BaseCompressor.js +504 -0
- package/dist/services/ConversationCompressor.d.ts +32 -0
- package/dist/services/ConversationCompressor.js +91 -0
- package/dist/services/MidGenerationCompressor.d.ts +35 -63
- package/dist/services/MidGenerationCompressor.js +40 -311
- package/dist/tools/distill-conversation-history-tool.d.ts +62 -0
- package/dist/tools/distill-conversation-history-tool.js +206 -0
- package/dist/tools/distill-conversation-tool.js +20 -35
- package/dist/utils/model-context-utils.d.ts +10 -2
- package/dist/utils/model-context-utils.js +79 -47
- package/package.json +2 -2
package/dist/agents/Agent.js
CHANGED
|
@@ -2,18 +2,21 @@ import { getLogger } from "../logger.js";
|
|
|
2
2
|
import dbClient_default from "../data/db/dbClient.js";
|
|
3
3
|
import { AGENT_EXECUTION_MAX_GENERATION_STEPS, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, FUNCTION_TOOL_SANDBOX_VCPUS_DEFAULT, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS, LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS } from "../constants/execution-limits/index.js";
|
|
4
4
|
import { toolSessionManager } from "./ToolSessionManager.js";
|
|
5
|
-
import {
|
|
6
|
-
import { getStreamHelper } from "../utils/stream-registry.js";
|
|
5
|
+
import { getCompressionConfigForModel } from "../utils/model-context-utils.js";
|
|
7
6
|
import { setSpanWithError as setSpanWithError$1, tracer } from "../utils/tracer.js";
|
|
7
|
+
import { getModelAwareCompressionConfig } from "../services/BaseCompressor.js";
|
|
8
|
+
import "../services/ConversationCompressor.js";
|
|
9
|
+
import { createDefaultConversationHistoryConfig, getConversationHistoryWithCompression } from "../data/conversations.js";
|
|
10
|
+
import { getStreamHelper } from "../utils/stream-registry.js";
|
|
8
11
|
import { agentSessionManager } from "../services/AgentSession.js";
|
|
9
12
|
import { IncrementalStreamParser } from "../services/IncrementalStreamParser.js";
|
|
10
|
-
import { MidGenerationCompressor
|
|
13
|
+
import { MidGenerationCompressor } from "../services/MidGenerationCompressor.js";
|
|
11
14
|
import { pendingToolApprovalManager } from "../services/PendingToolApprovalManager.js";
|
|
12
15
|
import { ResponseFormatter } from "../services/ResponseFormatter.js";
|
|
13
16
|
import { generateToolId } from "../utils/agent-operations.js";
|
|
14
17
|
import { jsonSchemaToZod } from "../utils/data-component-schema.js";
|
|
15
18
|
import { ArtifactCreateSchema, ArtifactReferenceSchema } from "../utils/artifact-component-schema.js";
|
|
16
|
-
import {
|
|
19
|
+
import { withJsonPostProcessing } from "../utils/json-postprocessor.js";
|
|
17
20
|
import { calculateBreakdownTotal, estimateTokens } from "../utils/token-estimator.js";
|
|
18
21
|
import { createDelegateToAgentTool, createTransferToAgentTool } from "./relationTools.js";
|
|
19
22
|
import { SystemPromptBuilder } from "./SystemPromptBuilder.js";
|
|
@@ -183,21 +186,6 @@ var Agent = class {
|
|
|
183
186
|
/**
|
|
184
187
|
* Simple compression fallback: drop oldest messages to fit under token limit
|
|
185
188
|
*/
|
|
186
|
-
simpleCompression(messages, targetTokens) {
|
|
187
|
-
if (messages.length === 0) return messages;
|
|
188
|
-
const estimateTokens$1 = (msg) => {
|
|
189
|
-
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
190
|
-
return Math.ceil(content.length / 4);
|
|
191
|
-
};
|
|
192
|
-
let totalTokens = messages.reduce((sum, msg) => sum + estimateTokens$1(msg), 0);
|
|
193
|
-
if (totalTokens <= targetTokens) return messages;
|
|
194
|
-
const result = [...messages];
|
|
195
|
-
while (totalTokens > targetTokens && result.length > 1) {
|
|
196
|
-
const dropped = result.shift();
|
|
197
|
-
if (dropped) totalTokens -= estimateTokens$1(dropped);
|
|
198
|
-
}
|
|
199
|
-
return result;
|
|
200
|
-
}
|
|
201
189
|
/**
|
|
202
190
|
* Set delegation status for this agent instance
|
|
203
191
|
*/
|
|
@@ -931,7 +919,7 @@ var Agent = class {
|
|
|
931
919
|
*/
|
|
932
920
|
async buildPhase2SystemPrompt(runtimeContext) {
|
|
933
921
|
const phase2Config = new Phase2Config();
|
|
934
|
-
const compressionConfig =
|
|
922
|
+
const compressionConfig = getModelAwareCompressionConfig();
|
|
935
923
|
const hasAgentArtifactComponents = await this.hasAgentArtifactComponents() || compressionConfig.enabled;
|
|
936
924
|
const conversationId = runtimeContext?.metadata?.conversationId || runtimeContext?.contextId;
|
|
937
925
|
const resolvedContext = conversationId ? await this.getResolvedContext(conversationId) : null;
|
|
@@ -1036,7 +1024,7 @@ var Agent = class {
|
|
|
1036
1024
|
}, "Failed to process agent prompt with context, using original");
|
|
1037
1025
|
}
|
|
1038
1026
|
const shouldIncludeArtifactComponents = !excludeDataComponents;
|
|
1039
|
-
const compressionConfig =
|
|
1027
|
+
const compressionConfig = getModelAwareCompressionConfig();
|
|
1040
1028
|
const hasAgentArtifactComponents = await this.hasAgentArtifactComponents() || compressionConfig.enabled;
|
|
1041
1029
|
const config = {
|
|
1042
1030
|
corePrompt: processedPrompt,
|
|
@@ -1091,7 +1079,7 @@ var Agent = class {
|
|
|
1091
1079
|
}
|
|
1092
1080
|
async getDefaultTools(streamRequestId) {
|
|
1093
1081
|
const defaultTools = {};
|
|
1094
|
-
const compressionConfig =
|
|
1082
|
+
const compressionConfig = getModelAwareCompressionConfig();
|
|
1095
1083
|
if (await this.agentHasArtifactComponents() || compressionConfig.enabled) defaultTools.get_reference_artifact = this.getArtifactTools();
|
|
1096
1084
|
if (this.config.dataComponents && this.config.dataComponents.length > 0) {
|
|
1097
1085
|
const thinkingCompleteTool = this.createThinkingCompleteTool();
|
|
@@ -1316,80 +1304,13 @@ ${typeof cleanResult === "string" ? cleanResult : JSON.stringify(cleanResult, nu
|
|
|
1316
1304
|
"subAgent.id": this.config.id,
|
|
1317
1305
|
"subAgent.name": this.config.name
|
|
1318
1306
|
} }, async (span) => {
|
|
1319
|
-
const contextId = runtimeContext
|
|
1320
|
-
const taskId = runtimeContext?.metadata?.taskId || "unknown";
|
|
1321
|
-
const streamRequestId = runtimeContext?.metadata?.streamRequestId;
|
|
1322
|
-
const sessionId = streamRequestId || "fallback-session";
|
|
1307
|
+
const { contextId, taskId, streamRequestId, sessionId } = this.setupGenerationContext(runtimeContext);
|
|
1323
1308
|
try {
|
|
1324
|
-
this.streamRequestId
|
|
1325
|
-
this.streamHelper = streamRequestId ? getStreamHelper(streamRequestId) : void 0;
|
|
1309
|
+
const { systemPrompt, thinkingSystemPrompt, sanitizedTools, contextBreakdown: initialContextBreakdown } = await this.loadToolsAndPrompts(sessionId, streamRequestId, runtimeContext);
|
|
1326
1310
|
if (streamRequestId && this.artifactComponents.length > 0) agentSessionManager.updateArtifactComponents(streamRequestId, this.artifactComponents);
|
|
1327
1311
|
const conversationId = runtimeContext?.metadata?.conversationId;
|
|
1328
1312
|
if (conversationId) this.setConversationId(conversationId);
|
|
1329
|
-
const
|
|
1330
|
-
"subAgent.name": this.config.name,
|
|
1331
|
-
"session.id": sessionId || "none"
|
|
1332
|
-
} }, async (childSpan) => {
|
|
1333
|
-
try {
|
|
1334
|
-
const result = await Promise.all([
|
|
1335
|
-
this.getMcpTools(sessionId, streamRequestId),
|
|
1336
|
-
this.buildSystemPrompt(runtimeContext, false),
|
|
1337
|
-
this.buildSystemPrompt(runtimeContext, true),
|
|
1338
|
-
this.getFunctionTools(sessionId, streamRequestId),
|
|
1339
|
-
Promise.resolve(this.getRelationTools(runtimeContext, sessionId)),
|
|
1340
|
-
this.getDefaultTools(streamRequestId)
|
|
1341
|
-
]);
|
|
1342
|
-
childSpan.setStatus({ code: SpanStatusCode.OK });
|
|
1343
|
-
return result;
|
|
1344
|
-
} catch (err) {
|
|
1345
|
-
setSpanWithError$1(childSpan, err instanceof Error ? err : new Error(String(err)));
|
|
1346
|
-
throw err;
|
|
1347
|
-
} finally {
|
|
1348
|
-
childSpan.end();
|
|
1349
|
-
}
|
|
1350
|
-
});
|
|
1351
|
-
const systemPrompt = systemPromptResult.prompt;
|
|
1352
|
-
const thinkingSystemPrompt = thinkingSystemPromptResult.prompt;
|
|
1353
|
-
const contextBreakdown = systemPromptResult.breakdown;
|
|
1354
|
-
const allTools = {
|
|
1355
|
-
...mcpTools,
|
|
1356
|
-
...functionTools,
|
|
1357
|
-
...relationTools,
|
|
1358
|
-
...defaultTools
|
|
1359
|
-
};
|
|
1360
|
-
const sanitizedTools = this.sanitizeToolsForAISDK(allTools);
|
|
1361
|
-
let conversationHistory = "";
|
|
1362
|
-
const historyConfig = this.config.conversationHistoryConfig ?? createDefaultConversationHistoryConfig();
|
|
1363
|
-
if (historyConfig && historyConfig.mode !== "none") {
|
|
1364
|
-
if (historyConfig.mode === "full") {
|
|
1365
|
-
const filters = {
|
|
1366
|
-
delegationId: this.delegationId,
|
|
1367
|
-
isDelegated: this.isDelegatedAgent
|
|
1368
|
-
};
|
|
1369
|
-
conversationHistory = await getFormattedConversationHistory({
|
|
1370
|
-
tenantId: this.config.tenantId,
|
|
1371
|
-
projectId: this.config.projectId,
|
|
1372
|
-
conversationId: contextId,
|
|
1373
|
-
currentMessage: userMessage,
|
|
1374
|
-
options: historyConfig,
|
|
1375
|
-
filters
|
|
1376
|
-
});
|
|
1377
|
-
} else if (historyConfig.mode === "scoped") conversationHistory = await getFormattedConversationHistory({
|
|
1378
|
-
tenantId: this.config.tenantId,
|
|
1379
|
-
projectId: this.config.projectId,
|
|
1380
|
-
conversationId: contextId,
|
|
1381
|
-
currentMessage: userMessage,
|
|
1382
|
-
options: historyConfig,
|
|
1383
|
-
filters: {
|
|
1384
|
-
subAgentId: this.config.id,
|
|
1385
|
-
taskId,
|
|
1386
|
-
delegationId: this.delegationId,
|
|
1387
|
-
isDelegated: this.isDelegatedAgent
|
|
1388
|
-
}
|
|
1389
|
-
});
|
|
1390
|
-
}
|
|
1391
|
-
contextBreakdown.conversationHistory = estimateTokens(conversationHistory);
|
|
1392
|
-
calculateBreakdownTotal(contextBreakdown);
|
|
1313
|
+
const { conversationHistory, contextBreakdown } = await this.buildConversationHistory(contextId, taskId, userMessage, streamRequestId, initialContextBreakdown);
|
|
1393
1314
|
span.setAttributes({
|
|
1394
1315
|
"context.breakdown.system_template_tokens": contextBreakdown.systemPromptTemplate,
|
|
1395
1316
|
"context.breakdown.core_instructions_tokens": contextBreakdown.coreInstructions,
|
|
@@ -1404,289 +1325,24 @@ ${typeof cleanResult === "string" ? cleanResult : JSON.stringify(cleanResult, nu
|
|
|
1404
1325
|
"context.breakdown.conversation_history_tokens": contextBreakdown.conversationHistory,
|
|
1405
1326
|
"context.breakdown.total_tokens": contextBreakdown.total
|
|
1406
1327
|
});
|
|
1407
|
-
const primaryModelSettings = this.
|
|
1408
|
-
const modelSettings = ModelFactory.prepareGenerationConfig(primaryModelSettings);
|
|
1328
|
+
const { primaryModelSettings, modelSettings, hasStructuredOutput, shouldStreamPhase1, timeoutMs } = this.configureModelSettings();
|
|
1409
1329
|
let response;
|
|
1410
1330
|
let textResponse;
|
|
1411
|
-
const
|
|
1412
|
-
const
|
|
1413
|
-
const configuredTimeout = modelSettings.maxDuration ? Math.min(modelSettings.maxDuration * 1e3, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) : shouldStreamPhase1 ? LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING : LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING;
|
|
1414
|
-
const timeoutMs = Math.min(configuredTimeout, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS);
|
|
1415
|
-
if (modelSettings.maxDuration && modelSettings.maxDuration * 1e3 > LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) logger.warn({
|
|
1416
|
-
requestedTimeout: modelSettings.maxDuration * 1e3,
|
|
1417
|
-
appliedTimeout: timeoutMs,
|
|
1418
|
-
maxAllowed: LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS
|
|
1419
|
-
}, "Requested timeout exceeded maximum allowed, capping to 10 minutes");
|
|
1420
|
-
const phase1SystemPrompt = hasStructuredOutput ? thinkingSystemPrompt : systemPrompt;
|
|
1421
|
-
const messages = [];
|
|
1422
|
-
messages.push({
|
|
1423
|
-
role: "system",
|
|
1424
|
-
content: phase1SystemPrompt
|
|
1425
|
-
});
|
|
1426
|
-
if (conversationHistory.trim() !== "") messages.push({
|
|
1427
|
-
role: "user",
|
|
1428
|
-
content: conversationHistory
|
|
1429
|
-
});
|
|
1430
|
-
messages.push({
|
|
1431
|
-
role: "user",
|
|
1432
|
-
content: userMessage
|
|
1433
|
-
});
|
|
1434
|
-
const originalMessageCount = messages.length;
|
|
1435
|
-
const compressionConfigResult = getCompressionConfigForModel(primaryModelSettings);
|
|
1436
|
-
const compressionConfig = {
|
|
1437
|
-
hardLimit: compressionConfigResult.hardLimit,
|
|
1438
|
-
safetyBuffer: compressionConfigResult.safetyBuffer,
|
|
1439
|
-
enabled: compressionConfigResult.enabled
|
|
1440
|
-
};
|
|
1441
|
-
const compressor = compressionConfig.enabled ? new MidGenerationCompressor(sessionId, contextId, this.config.tenantId, this.config.projectId, compressionConfig, this.getSummarizerModel(), primaryModelSettings) : null;
|
|
1442
|
-
this.currentCompressor = compressor;
|
|
1331
|
+
const messages = this.buildInitialMessages(systemPrompt, thinkingSystemPrompt, hasStructuredOutput, conversationHistory, userMessage);
|
|
1332
|
+
const { originalMessageCount, compressor } = this.setupCompression(messages, sessionId, contextId, primaryModelSettings);
|
|
1443
1333
|
if (shouldStreamPhase1) {
|
|
1444
|
-
const
|
|
1334
|
+
const streamConfig = {
|
|
1445
1335
|
...modelSettings,
|
|
1446
|
-
toolChoice: "auto"
|
|
1447
|
-
messages,
|
|
1448
|
-
tools: sanitizedTools,
|
|
1449
|
-
prepareStep: async ({ messages: stepMessages }) => {
|
|
1450
|
-
if (!compressor) return {};
|
|
1451
|
-
if (compressor.isCompressionNeeded(stepMessages)) {
|
|
1452
|
-
logger.info({ compressorState: compressor.getState() }, "Triggering layered mid-generation compression");
|
|
1453
|
-
try {
|
|
1454
|
-
const originalMessages = stepMessages.slice(0, originalMessageCount);
|
|
1455
|
-
const generatedMessages = stepMessages.slice(originalMessageCount);
|
|
1456
|
-
if (generatedMessages.length > 0) {
|
|
1457
|
-
const compressionResult = await compressor.compress(generatedMessages);
|
|
1458
|
-
const finalMessages = [...originalMessages];
|
|
1459
|
-
if (compressionResult.summary.text_messages && compressionResult.summary.text_messages.length > 0) finalMessages.push(...compressionResult.summary.text_messages);
|
|
1460
|
-
const summaryMessage = JSON.stringify({
|
|
1461
|
-
high_level: compressionResult.summary?.summary?.high_level,
|
|
1462
|
-
user_intent: compressionResult.summary?.summary?.user_intent,
|
|
1463
|
-
decisions: compressionResult.summary?.summary?.decisions,
|
|
1464
|
-
open_questions: compressionResult.summary?.summary?.open_questions,
|
|
1465
|
-
next_steps: compressionResult.summary?.summary?.next_steps,
|
|
1466
|
-
related_artifacts: compressionResult?.summary?.summary?.related_artifacts
|
|
1467
|
-
});
|
|
1468
|
-
finalMessages.push({
|
|
1469
|
-
role: "user",
|
|
1470
|
-
content: `Based on your research, here's what you've discovered: ${summaryMessage}
|
|
1471
|
-
|
|
1472
|
-
Now please provide your answer to my original question using this context.`
|
|
1473
|
-
});
|
|
1474
|
-
logger.info({
|
|
1475
|
-
originalTotal: stepMessages.length,
|
|
1476
|
-
compressed: finalMessages.length,
|
|
1477
|
-
originalKept: originalMessages.length,
|
|
1478
|
-
generatedCompressed: generatedMessages.length
|
|
1479
|
-
}, "Generated content compression completed");
|
|
1480
|
-
logger.info({ summaryMessage }, "Summary message");
|
|
1481
|
-
return { messages: finalMessages };
|
|
1482
|
-
}
|
|
1483
|
-
return {};
|
|
1484
|
-
} catch (error) {
|
|
1485
|
-
logger.error({
|
|
1486
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1487
|
-
stack: error instanceof Error ? error.stack : void 0
|
|
1488
|
-
}, "Smart compression failed, falling back to simple compression");
|
|
1489
|
-
try {
|
|
1490
|
-
const targetSize = Math.floor(compressor.getHardLimit() * .5);
|
|
1491
|
-
const fallbackMessages = this.simpleCompression(stepMessages, targetSize);
|
|
1492
|
-
logger.info({
|
|
1493
|
-
originalCount: stepMessages.length,
|
|
1494
|
-
compressedCount: fallbackMessages.length,
|
|
1495
|
-
compressionType: "simple_fallback"
|
|
1496
|
-
}, "Simple compression fallback completed");
|
|
1497
|
-
return { messages: fallbackMessages };
|
|
1498
|
-
} catch (fallbackError) {
|
|
1499
|
-
logger.error({ error: fallbackError instanceof Error ? fallbackError.message : String(fallbackError) }, "Fallback compression also failed, continuing without compression");
|
|
1500
|
-
return {};
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
return {};
|
|
1505
|
-
},
|
|
1506
|
-
stopWhen: async ({ steps }) => {
|
|
1507
|
-
const last = steps.at(-1);
|
|
1508
|
-
if (last && "text" in last && last.text) try {
|
|
1509
|
-
await agentSessionManager.recordEvent(this.getStreamRequestId(), "agent_reasoning", this.config.id, { parts: [{
|
|
1510
|
-
type: "text",
|
|
1511
|
-
content: last.text
|
|
1512
|
-
}] });
|
|
1513
|
-
} catch (error) {
|
|
1514
|
-
logger.debug({ error }, "Failed to track agent reasoning");
|
|
1515
|
-
}
|
|
1516
|
-
if (last && last["content"] && last["content"].length > 0) {
|
|
1517
|
-
const lastContent = last["content"][last["content"].length - 1];
|
|
1518
|
-
if (lastContent["type"] === "tool-error") {
|
|
1519
|
-
const error = lastContent["error"];
|
|
1520
|
-
if (error && typeof error === "object" && "name" in error && error.name === "connection_refused") return true;
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
if (steps.length >= 2) {
|
|
1524
|
-
const previousStep = steps[steps.length - 2];
|
|
1525
|
-
if (previousStep && "toolCalls" in previousStep && previousStep.toolCalls) {
|
|
1526
|
-
if (previousStep.toolCalls.some((tc) => tc.toolName.startsWith("transfer_to_")) && "toolResults" in previousStep && previousStep.toolResults) return true;
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
return steps.length >= this.getMaxGenerationSteps();
|
|
1530
|
-
},
|
|
1531
|
-
experimental_telemetry: {
|
|
1532
|
-
isEnabled: true,
|
|
1533
|
-
functionId: this.config.id,
|
|
1534
|
-
recordInputs: true,
|
|
1535
|
-
recordOutputs: true,
|
|
1536
|
-
metadata: {
|
|
1537
|
-
subAgentId: this.config.id,
|
|
1538
|
-
subAgentName: this.config.name
|
|
1539
|
-
}
|
|
1540
|
-
},
|
|
1541
|
-
abortSignal: AbortSignal.timeout(timeoutMs)
|
|
1542
|
-
});
|
|
1543
|
-
const streamHelper = this.getStreamingHelper();
|
|
1544
|
-
if (!streamHelper) throw new Error("Stream helper is unexpectedly undefined in streaming context");
|
|
1545
|
-
const session = toolSessionManager.getSession(sessionId);
|
|
1546
|
-
const artifactParserOptions = {
|
|
1547
|
-
sessionId,
|
|
1548
|
-
taskId: session?.taskId,
|
|
1549
|
-
projectId: session?.projectId,
|
|
1550
|
-
artifactComponents: this.artifactComponents,
|
|
1551
|
-
streamRequestId: this.getStreamRequestId(),
|
|
1552
|
-
subAgentId: this.config.id
|
|
1336
|
+
toolChoice: "auto"
|
|
1553
1337
|
};
|
|
1554
|
-
const
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
await parser.processTextChunk(event.text);
|
|
1558
|
-
break;
|
|
1559
|
-
case "tool-call":
|
|
1560
|
-
parser.markToolResult();
|
|
1561
|
-
break;
|
|
1562
|
-
case "tool-result":
|
|
1563
|
-
parser.markToolResult();
|
|
1564
|
-
break;
|
|
1565
|
-
case "finish":
|
|
1566
|
-
if (event.finishReason === "tool-calls") parser.markToolResult();
|
|
1567
|
-
break;
|
|
1568
|
-
case "error": {
|
|
1569
|
-
if (event.error instanceof Error) throw event.error;
|
|
1570
|
-
const errorMessage = event.error?.error?.message;
|
|
1571
|
-
throw new Error(errorMessage);
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
1574
|
-
await parser.finalize();
|
|
1338
|
+
const streamResult = streamText(this.buildBaseGenerationConfig(streamConfig, messages, sanitizedTools, compressor, originalMessageCount, timeoutMs, "auto", void 0, false, contextBreakdown.total));
|
|
1339
|
+
const parser = this.setupStreamParser(sessionId, contextId);
|
|
1340
|
+
await this.processStreamEvents(streamResult, parser);
|
|
1575
1341
|
response = await streamResult;
|
|
1576
|
-
|
|
1577
|
-
if (collectedParts.length > 0) response.formattedContent = { parts: collectedParts.map((part) => ({
|
|
1578
|
-
kind: part.kind,
|
|
1579
|
-
...part.kind === "text" && { text: part.text },
|
|
1580
|
-
...part.kind === "data" && { data: part.data }
|
|
1581
|
-
})) };
|
|
1582
|
-
const streamedContent = parser.getAllStreamedContent();
|
|
1583
|
-
if (streamedContent.length > 0) response.streamedContent = { parts: streamedContent.map((part) => ({
|
|
1584
|
-
kind: part.kind,
|
|
1585
|
-
...part.kind === "text" && { text: part.text },
|
|
1586
|
-
...part.kind === "data" && { data: part.data }
|
|
1587
|
-
})) };
|
|
1342
|
+
response = this.formatStreamingResponse(response, parser);
|
|
1588
1343
|
} else {
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
...modelSettings,
|
|
1592
|
-
toolChoice: "required"
|
|
1593
|
-
};
|
|
1594
|
-
else genConfig = {
|
|
1595
|
-
...modelSettings,
|
|
1596
|
-
toolChoice: "auto"
|
|
1597
|
-
};
|
|
1598
|
-
response = await generateText({
|
|
1599
|
-
...genConfig,
|
|
1600
|
-
messages,
|
|
1601
|
-
tools: sanitizedTools,
|
|
1602
|
-
prepareStep: async ({ messages: stepMessages }) => {
|
|
1603
|
-
if (!compressor) return {};
|
|
1604
|
-
if (compressor.isCompressionNeeded(stepMessages)) {
|
|
1605
|
-
logger.info({ compressorState: compressor.getState() }, "Triggering layered mid-generation compression");
|
|
1606
|
-
try {
|
|
1607
|
-
const originalMessages = stepMessages.slice(0, originalMessageCount);
|
|
1608
|
-
const generatedMessages = stepMessages.slice(originalMessageCount);
|
|
1609
|
-
if (generatedMessages.length > 0) {
|
|
1610
|
-
const compressionResult = await compressor.compress(generatedMessages);
|
|
1611
|
-
const finalMessages = [...originalMessages];
|
|
1612
|
-
if (compressionResult.summary.text_messages && compressionResult.summary.text_messages.length > 0) finalMessages.push(...compressionResult.summary.text_messages);
|
|
1613
|
-
const summaryMessage = JSON.stringify({
|
|
1614
|
-
high_level: compressionResult.summary?.summary?.high_level,
|
|
1615
|
-
user_intent: compressionResult.summary?.summary?.user_intent,
|
|
1616
|
-
decisions: compressionResult.summary?.summary?.decisions,
|
|
1617
|
-
open_questions: compressionResult.summary?.summary?.open_questions,
|
|
1618
|
-
next_steps: compressionResult.summary?.summary?.next_steps,
|
|
1619
|
-
related_artifacts: compressionResult?.summary?.summary?.related_artifacts
|
|
1620
|
-
});
|
|
1621
|
-
finalMessages.push({
|
|
1622
|
-
role: "user",
|
|
1623
|
-
content: `Based on your research, here's what you've discovered: ${summaryMessage}
|
|
1624
|
-
|
|
1625
|
-
Now please provide your answer to my original question using this context.`
|
|
1626
|
-
});
|
|
1627
|
-
logger.info({
|
|
1628
|
-
originalTotal: stepMessages.length,
|
|
1629
|
-
compressed: finalMessages.length,
|
|
1630
|
-
originalKept: originalMessages.length,
|
|
1631
|
-
generatedCompressed: generatedMessages.length
|
|
1632
|
-
}, "Generated content compression completed");
|
|
1633
|
-
logger.info({ summaryMessage }, "Summary message");
|
|
1634
|
-
return { messages: finalMessages };
|
|
1635
|
-
}
|
|
1636
|
-
return {};
|
|
1637
|
-
} catch (error) {
|
|
1638
|
-
logger.error({
|
|
1639
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1640
|
-
stack: error instanceof Error ? error.stack : void 0
|
|
1641
|
-
}, "Smart compression failed, falling back to simple compression");
|
|
1642
|
-
try {
|
|
1643
|
-
const targetSize = Math.floor(compressor.getHardLimit() * .5);
|
|
1644
|
-
const fallbackMessages = this.simpleCompression(stepMessages, targetSize);
|
|
1645
|
-
logger.info({
|
|
1646
|
-
originalCount: stepMessages.length,
|
|
1647
|
-
compressedCount: fallbackMessages.length,
|
|
1648
|
-
compressionType: "simple_fallback"
|
|
1649
|
-
}, "Simple compression fallback completed");
|
|
1650
|
-
return { messages: fallbackMessages };
|
|
1651
|
-
} catch (fallbackError) {
|
|
1652
|
-
logger.error({ error: fallbackError instanceof Error ? fallbackError.message : String(fallbackError) }, "Fallback compression also failed, continuing without compression");
|
|
1653
|
-
return {};
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
return {};
|
|
1658
|
-
},
|
|
1659
|
-
stopWhen: async ({ steps }) => {
|
|
1660
|
-
const last = steps.at(-1);
|
|
1661
|
-
if (last && "text" in last && last.text) try {
|
|
1662
|
-
await agentSessionManager.recordEvent(this.getStreamRequestId(), "agent_reasoning", this.config.id, { parts: [{
|
|
1663
|
-
type: "text",
|
|
1664
|
-
content: last.text
|
|
1665
|
-
}] });
|
|
1666
|
-
} catch (error) {
|
|
1667
|
-
logger.debug({ error }, "Failed to track agent reasoning");
|
|
1668
|
-
}
|
|
1669
|
-
if (steps.length >= 2) {
|
|
1670
|
-
const previousStep = steps[steps.length - 2];
|
|
1671
|
-
if (previousStep && "toolCalls" in previousStep && previousStep.toolCalls) {
|
|
1672
|
-
if (previousStep.toolCalls.some((tc) => tc.toolName.startsWith("transfer_to_") || tc.toolName === "thinking_complete") && "toolResults" in previousStep && previousStep.toolResults) return true;
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
return steps.length >= this.getMaxGenerationSteps();
|
|
1676
|
-
},
|
|
1677
|
-
experimental_telemetry: {
|
|
1678
|
-
isEnabled: true,
|
|
1679
|
-
functionId: this.config.id,
|
|
1680
|
-
recordInputs: true,
|
|
1681
|
-
recordOutputs: true,
|
|
1682
|
-
metadata: {
|
|
1683
|
-
phase: "planning",
|
|
1684
|
-
subAgentId: this.config.id,
|
|
1685
|
-
subAgentName: this.config.name
|
|
1686
|
-
}
|
|
1687
|
-
},
|
|
1688
|
-
abortSignal: AbortSignal.timeout(timeoutMs)
|
|
1689
|
-
});
|
|
1344
|
+
const toolChoice = hasStructuredOutput ? "required" : "auto";
|
|
1345
|
+
response = await generateText(this.buildBaseGenerationConfig(modelSettings, messages, sanitizedTools, compressor, originalMessageCount, timeoutMs, toolChoice, "planning", true, contextBreakdown.total));
|
|
1690
1346
|
}
|
|
1691
1347
|
if (response.steps) {
|
|
1692
1348
|
const resolvedSteps = await response.steps;
|
|
@@ -1696,29 +1352,368 @@ Now please provide your answer to my original question using this context.`
|
|
|
1696
1352
|
};
|
|
1697
1353
|
}
|
|
1698
1354
|
if (hasStructuredOutput && !hasToolCallWithPrefix("transfer_to_")(response)) if (response.steps?.flatMap((s) => s.toolCalls || [])?.find((tc) => tc.toolName === "thinking_complete")) {
|
|
1699
|
-
const reasoningFlow =
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1355
|
+
const reasoningFlow = this.buildReasoningFlow(response, sessionId);
|
|
1356
|
+
const dataComponentsSchema = this.buildDataComponentsSchema();
|
|
1357
|
+
const structuredModelSettings = ModelFactory.prepareGenerationConfig(this.getStructuredOutputModel());
|
|
1358
|
+
const phase2TimeoutMs = this.calculatePhase2Timeout(structuredModelSettings);
|
|
1359
|
+
if (this.getStreamingHelper()) {
|
|
1360
|
+
const phase2Messages = await this.buildPhase2Messages(runtimeContext, conversationHistory, userMessage, reasoningFlow);
|
|
1361
|
+
const result = await this.executeStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, sessionId, contextId, response);
|
|
1362
|
+
response = result;
|
|
1363
|
+
textResponse = result.textResponse;
|
|
1364
|
+
} else {
|
|
1365
|
+
const phase2Messages = await this.buildPhase2Messages(runtimeContext, conversationHistory, userMessage, reasoningFlow);
|
|
1366
|
+
const result = await this.executeNonStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, response);
|
|
1367
|
+
response = result;
|
|
1368
|
+
textResponse = result.textResponse;
|
|
1369
|
+
}
|
|
1370
|
+
} else textResponse = response.text || "";
|
|
1371
|
+
else textResponse = response.steps[response.steps.length - 1].text || "";
|
|
1372
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1373
|
+
span.end();
|
|
1374
|
+
const formattedResponse = await this.formatFinalResponse(response, textResponse, sessionId, contextId);
|
|
1375
|
+
if (streamRequestId) {
|
|
1376
|
+
const generationType = response.object ? "object_generation" : "text_generation";
|
|
1377
|
+
agentSessionManager.recordEvent(streamRequestId, "agent_generate", this.config.id, {
|
|
1378
|
+
parts: (formattedResponse.formattedContent?.parts || []).map((part) => ({
|
|
1379
|
+
type: part.kind === "text" ? "text" : part.kind === "data" ? "tool_result" : "text",
|
|
1380
|
+
content: part.text || JSON.stringify(part.data)
|
|
1381
|
+
})),
|
|
1382
|
+
generationType
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
if (compressor) compressor.fullCleanup();
|
|
1386
|
+
this.currentCompressor = null;
|
|
1387
|
+
return formattedResponse;
|
|
1388
|
+
} catch (error) {
|
|
1389
|
+
this.handleGenerationError(error, span);
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Setup generation context and initialize streaming helper
|
|
1395
|
+
*/
|
|
1396
|
+
setupGenerationContext(runtimeContext) {
|
|
1397
|
+
const contextId = runtimeContext?.contextId || "default";
|
|
1398
|
+
const taskId = runtimeContext?.metadata?.taskId || "unknown";
|
|
1399
|
+
const streamRequestId = runtimeContext?.metadata?.streamRequestId;
|
|
1400
|
+
const sessionId = streamRequestId || "fallback-session";
|
|
1401
|
+
this.streamRequestId = streamRequestId;
|
|
1402
|
+
this.streamHelper = streamRequestId ? getStreamHelper(streamRequestId) : void 0;
|
|
1403
|
+
if (streamRequestId && this.artifactComponents.length > 0) agentSessionManager.updateArtifactComponents(streamRequestId, this.artifactComponents);
|
|
1404
|
+
const conversationId = runtimeContext?.metadata?.conversationId;
|
|
1405
|
+
if (conversationId) this.setConversationId(conversationId);
|
|
1406
|
+
return {
|
|
1407
|
+
contextId,
|
|
1408
|
+
taskId,
|
|
1409
|
+
streamRequestId,
|
|
1410
|
+
sessionId
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Load all tools and system prompts in parallel, then combine and sanitize them
|
|
1415
|
+
*/
|
|
1416
|
+
async loadToolsAndPrompts(sessionId, streamRequestId, runtimeContext) {
|
|
1417
|
+
const [mcpTools, systemPromptResult, thinkingSystemPromptResult, functionTools, relationTools, defaultTools] = await tracer.startActiveSpan("agent.load_tools", { attributes: {
|
|
1418
|
+
"subAgent.name": this.config.name,
|
|
1419
|
+
"session.id": sessionId || "none"
|
|
1420
|
+
} }, async (childSpan) => {
|
|
1421
|
+
try {
|
|
1422
|
+
const result = await Promise.all([
|
|
1423
|
+
this.getMcpTools(sessionId, streamRequestId),
|
|
1424
|
+
this.buildSystemPrompt(runtimeContext, false),
|
|
1425
|
+
this.buildSystemPrompt(runtimeContext, true),
|
|
1426
|
+
this.getFunctionTools(sessionId, streamRequestId),
|
|
1427
|
+
Promise.resolve(this.getRelationTools(runtimeContext, sessionId)),
|
|
1428
|
+
this.getDefaultTools(streamRequestId)
|
|
1429
|
+
]);
|
|
1430
|
+
childSpan.setStatus({ code: SpanStatusCode.OK });
|
|
1431
|
+
return result;
|
|
1432
|
+
} catch (err) {
|
|
1433
|
+
setSpanWithError$1(childSpan, err instanceof Error ? err : new Error(String(err)));
|
|
1434
|
+
throw err;
|
|
1435
|
+
} finally {
|
|
1436
|
+
childSpan.end();
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1439
|
+
const systemPrompt = systemPromptResult.prompt;
|
|
1440
|
+
const thinkingSystemPrompt = thinkingSystemPromptResult.prompt;
|
|
1441
|
+
const contextBreakdown = systemPromptResult.breakdown;
|
|
1442
|
+
const allTools = {
|
|
1443
|
+
...mcpTools,
|
|
1444
|
+
...functionTools,
|
|
1445
|
+
...relationTools,
|
|
1446
|
+
...defaultTools
|
|
1447
|
+
};
|
|
1448
|
+
return {
|
|
1449
|
+
systemPrompt,
|
|
1450
|
+
thinkingSystemPrompt,
|
|
1451
|
+
sanitizedTools: this.sanitizeToolsForAISDK(allTools),
|
|
1452
|
+
contextBreakdown
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Build conversation history based on configuration mode and filters
|
|
1457
|
+
*/
|
|
1458
|
+
async buildConversationHistory(contextId, taskId, userMessage, streamRequestId, initialContextBreakdown) {
|
|
1459
|
+
let conversationHistory = "";
|
|
1460
|
+
const historyConfig = this.config.conversationHistoryConfig ?? createDefaultConversationHistoryConfig();
|
|
1461
|
+
if (historyConfig && historyConfig.mode !== "none") {
|
|
1462
|
+
if (historyConfig.mode === "full") {
|
|
1463
|
+
const filters = {
|
|
1464
|
+
delegationId: this.delegationId,
|
|
1465
|
+
isDelegated: this.isDelegatedAgent
|
|
1466
|
+
};
|
|
1467
|
+
conversationHistory = await getConversationHistoryWithCompression({
|
|
1468
|
+
tenantId: this.config.tenantId,
|
|
1469
|
+
projectId: this.config.projectId,
|
|
1470
|
+
conversationId: contextId,
|
|
1471
|
+
currentMessage: userMessage,
|
|
1472
|
+
options: historyConfig,
|
|
1473
|
+
filters,
|
|
1474
|
+
summarizerModel: this.getSummarizerModel(),
|
|
1475
|
+
streamRequestId,
|
|
1476
|
+
fullContextSize: initialContextBreakdown.total
|
|
1477
|
+
});
|
|
1478
|
+
} else if (historyConfig.mode === "scoped") conversationHistory = await getConversationHistoryWithCompression({
|
|
1479
|
+
tenantId: this.config.tenantId,
|
|
1480
|
+
projectId: this.config.projectId,
|
|
1481
|
+
conversationId: contextId,
|
|
1482
|
+
currentMessage: userMessage,
|
|
1483
|
+
options: historyConfig,
|
|
1484
|
+
filters: {
|
|
1485
|
+
subAgentId: this.config.id,
|
|
1486
|
+
taskId,
|
|
1487
|
+
delegationId: this.delegationId,
|
|
1488
|
+
isDelegated: this.isDelegatedAgent
|
|
1489
|
+
},
|
|
1490
|
+
summarizerModel: this.getSummarizerModel(),
|
|
1491
|
+
streamRequestId,
|
|
1492
|
+
fullContextSize: initialContextBreakdown.total
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
const conversationHistoryTokens = estimateTokens(conversationHistory);
|
|
1496
|
+
const updatedContextBreakdown = {
|
|
1497
|
+
...initialContextBreakdown,
|
|
1498
|
+
conversationHistory: conversationHistoryTokens
|
|
1499
|
+
};
|
|
1500
|
+
calculateBreakdownTotal(updatedContextBreakdown);
|
|
1501
|
+
return {
|
|
1502
|
+
conversationHistory,
|
|
1503
|
+
contextBreakdown: updatedContextBreakdown
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Configure model settings, timeouts, and streaming behavior
|
|
1508
|
+
*/
|
|
1509
|
+
configureModelSettings() {
|
|
1510
|
+
const primaryModelSettings = this.getPrimaryModel();
|
|
1511
|
+
const modelSettings = ModelFactory.prepareGenerationConfig(primaryModelSettings);
|
|
1512
|
+
const hasStructuredOutput = Boolean(this.config.dataComponents && this.config.dataComponents.length > 0);
|
|
1513
|
+
const shouldStreamPhase1 = this.getStreamingHelper() && !hasStructuredOutput;
|
|
1514
|
+
const configuredTimeout = modelSettings.maxDuration ? Math.min(modelSettings.maxDuration * 1e3, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) : shouldStreamPhase1 ? LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING : LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING;
|
|
1515
|
+
const timeoutMs = Math.min(configuredTimeout, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS);
|
|
1516
|
+
if (modelSettings.maxDuration && modelSettings.maxDuration * 1e3 > LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) logger.warn({
|
|
1517
|
+
requestedTimeout: modelSettings.maxDuration * 1e3,
|
|
1518
|
+
appliedTimeout: timeoutMs,
|
|
1519
|
+
maxAllowed: LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS
|
|
1520
|
+
}, "Requested timeout exceeded maximum allowed, capping to 10 minutes");
|
|
1521
|
+
return {
|
|
1522
|
+
primaryModelSettings,
|
|
1523
|
+
modelSettings: {
|
|
1524
|
+
...modelSettings,
|
|
1525
|
+
maxDuration: timeoutMs / 1e3
|
|
1526
|
+
},
|
|
1527
|
+
hasStructuredOutput,
|
|
1528
|
+
shouldStreamPhase1,
|
|
1529
|
+
timeoutMs
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
/**
|
|
1533
|
+
* Build initial messages array with system prompt and user content
|
|
1534
|
+
*/
|
|
1535
|
+
buildInitialMessages(systemPrompt, thinkingSystemPrompt, hasStructuredOutput, conversationHistory, userMessage) {
|
|
1536
|
+
const phase1SystemPrompt = hasStructuredOutput ? thinkingSystemPrompt : systemPrompt;
|
|
1537
|
+
const messages = [];
|
|
1538
|
+
messages.push({
|
|
1539
|
+
role: "system",
|
|
1540
|
+
content: phase1SystemPrompt
|
|
1541
|
+
});
|
|
1542
|
+
if (conversationHistory.trim() !== "") messages.push({
|
|
1543
|
+
role: "user",
|
|
1544
|
+
content: conversationHistory
|
|
1545
|
+
});
|
|
1546
|
+
messages.push({
|
|
1547
|
+
role: "user",
|
|
1548
|
+
content: userMessage
|
|
1549
|
+
});
|
|
1550
|
+
return messages;
|
|
1551
|
+
}
|
|
1552
|
+
/**
|
|
1553
|
+
* Setup compression for the current generation
|
|
1554
|
+
*/
|
|
1555
|
+
setupCompression(messages, sessionId, contextId, primaryModelSettings) {
|
|
1556
|
+
const originalMessageCount = messages.length;
|
|
1557
|
+
const compressionConfigResult = getCompressionConfigForModel(primaryModelSettings);
|
|
1558
|
+
const compressionConfig = {
|
|
1559
|
+
hardLimit: compressionConfigResult.hardLimit,
|
|
1560
|
+
safetyBuffer: compressionConfigResult.safetyBuffer,
|
|
1561
|
+
enabled: compressionConfigResult.enabled
|
|
1562
|
+
};
|
|
1563
|
+
const compressor = compressionConfig.enabled ? new MidGenerationCompressor(sessionId, contextId, this.config.tenantId, this.config.projectId, compressionConfig, this.getSummarizerModel(), primaryModelSettings) : null;
|
|
1564
|
+
this.currentCompressor = compressor;
|
|
1565
|
+
return {
|
|
1566
|
+
originalMessageCount,
|
|
1567
|
+
compressor
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Prepare step function for streaming with compression logic
|
|
1572
|
+
*/
|
|
1573
|
+
async handlePrepareStepCompression(stepMessages, compressor, originalMessageCount, fullContextSize) {
|
|
1574
|
+
if (!compressor) return {};
|
|
1575
|
+
if (compressor.isCompressionNeeded(stepMessages)) {
|
|
1576
|
+
logger.info({ compressorState: compressor.getState() }, "Triggering layered mid-generation compression");
|
|
1577
|
+
const originalMessages = stepMessages.slice(0, originalMessageCount);
|
|
1578
|
+
const generatedMessages = stepMessages.slice(originalMessageCount);
|
|
1579
|
+
if (generatedMessages.length > 0) {
|
|
1580
|
+
const compressionResult = await compressor.safeCompress(generatedMessages, fullContextSize);
|
|
1581
|
+
if (Array.isArray(compressionResult.summary)) {
|
|
1582
|
+
const compressedMessages = compressionResult.summary;
|
|
1583
|
+
logger.info({
|
|
1584
|
+
originalTotal: stepMessages.length,
|
|
1585
|
+
compressed: originalMessages.length + compressedMessages.length,
|
|
1586
|
+
originalKept: originalMessages.length,
|
|
1587
|
+
generatedCompressed: compressedMessages.length
|
|
1588
|
+
}, "Simple compression fallback applied");
|
|
1589
|
+
return { messages: [...originalMessages, ...compressedMessages] };
|
|
1590
|
+
}
|
|
1591
|
+
const finalMessages = [...originalMessages];
|
|
1592
|
+
if (compressionResult.summary.text_messages && compressionResult.summary.text_messages.length > 0) finalMessages.push(...compressionResult.summary.text_messages);
|
|
1593
|
+
const summaryData = {
|
|
1594
|
+
high_level: compressionResult.summary?.high_level,
|
|
1595
|
+
user_intent: compressionResult.summary?.user_intent,
|
|
1596
|
+
decisions: compressionResult.summary?.decisions,
|
|
1597
|
+
open_questions: compressionResult.summary?.open_questions,
|
|
1598
|
+
next_steps: compressionResult.summary?.next_steps,
|
|
1599
|
+
related_artifacts: compressionResult.summary?.related_artifacts
|
|
1600
|
+
};
|
|
1601
|
+
if (summaryData.related_artifacts && summaryData.related_artifacts.length > 0) summaryData.related_artifacts = summaryData.related_artifacts.map((artifact) => ({
|
|
1602
|
+
...artifact,
|
|
1603
|
+
artifact_reference: `<artifact:ref id="${artifact.id}" tool="${artifact.tool_call_id}" />`
|
|
1604
|
+
}));
|
|
1605
|
+
const summaryMessage = JSON.stringify(summaryData);
|
|
1606
|
+
finalMessages.push({
|
|
1607
|
+
role: "user",
|
|
1608
|
+
content: `Based on your research, here's what you've discovered: ${summaryMessage}
|
|
1609
|
+
|
|
1610
|
+
**IMPORTANT**: If you have enough information from this compressed research to answer my original question, please provide your answer now. Only continue with additional tool calls if you need critical missing information that wasn't captured in the research above. When referencing any artifacts from the compressed research, you MUST use <artifact:ref id="artifact_id" tool="tool_call_id" /> tags with the exact IDs from the related_artifacts above.`
|
|
1611
|
+
});
|
|
1612
|
+
logger.info({
|
|
1613
|
+
originalTotal: stepMessages.length,
|
|
1614
|
+
compressed: finalMessages.length,
|
|
1615
|
+
originalKept: originalMessages.length,
|
|
1616
|
+
generatedCompressed: generatedMessages.length
|
|
1617
|
+
}, "AI compression completed successfully");
|
|
1618
|
+
return { messages: finalMessages };
|
|
1619
|
+
}
|
|
1620
|
+
return {};
|
|
1621
|
+
}
|
|
1622
|
+
return {};
|
|
1623
|
+
}
|
|
1624
|
+
async handleStopWhenConditions(steps, includeThinkingComplete = false) {
|
|
1625
|
+
const last = steps.at(-1);
|
|
1626
|
+
if (last && "text" in last && last.text) try {
|
|
1627
|
+
await agentSessionManager.recordEvent(this.getStreamRequestId(), "agent_reasoning", this.config.id, { parts: [{
|
|
1628
|
+
type: "text",
|
|
1629
|
+
content: last.text
|
|
1630
|
+
}] });
|
|
1631
|
+
} catch (error) {
|
|
1632
|
+
logger.debug({ error }, "Failed to track agent reasoning");
|
|
1633
|
+
}
|
|
1634
|
+
if (!includeThinkingComplete && last && last["content"] && last["content"].length > 0) {
|
|
1635
|
+
const lastContent = last["content"][last["content"].length - 1];
|
|
1636
|
+
if (lastContent["type"] === "tool-error") {
|
|
1637
|
+
const error = lastContent["error"];
|
|
1638
|
+
if (error && typeof error === "object" && "name" in error && error.name === "connection_refused") return true;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
if (steps.length >= 2) {
|
|
1642
|
+
const previousStep = steps[steps.length - 2];
|
|
1643
|
+
if (previousStep && "toolCalls" in previousStep && previousStep.toolCalls) {
|
|
1644
|
+
const stopToolNames = includeThinkingComplete ? ["transfer_to_", "thinking_complete"] : ["transfer_to_"];
|
|
1645
|
+
if (previousStep.toolCalls.some((tc) => stopToolNames.some((toolName) => toolName.endsWith("_") ? tc.toolName.startsWith(toolName) : tc.toolName === toolName)) && "toolResults" in previousStep && previousStep.toolResults) return true;
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
return steps.length >= this.getMaxGenerationSteps();
|
|
1649
|
+
}
|
|
1650
|
+
setupStreamParser(sessionId, contextId) {
|
|
1651
|
+
const streamHelper = this.getStreamingHelper();
|
|
1652
|
+
if (!streamHelper) throw new Error("Stream helper is unexpectedly undefined in streaming context");
|
|
1653
|
+
const session = toolSessionManager.getSession(sessionId);
|
|
1654
|
+
const artifactParserOptions = {
|
|
1655
|
+
sessionId,
|
|
1656
|
+
taskId: session?.taskId,
|
|
1657
|
+
projectId: session?.projectId,
|
|
1658
|
+
artifactComponents: this.artifactComponents,
|
|
1659
|
+
streamRequestId: this.getStreamRequestId(),
|
|
1660
|
+
subAgentId: this.config.id
|
|
1661
|
+
};
|
|
1662
|
+
return new IncrementalStreamParser(streamHelper, this.config.tenantId, contextId, artifactParserOptions);
|
|
1663
|
+
}
|
|
1664
|
+
buildTelemetryConfig(phase) {
|
|
1665
|
+
return {
|
|
1666
|
+
isEnabled: true,
|
|
1667
|
+
functionId: this.config.id,
|
|
1668
|
+
recordInputs: true,
|
|
1669
|
+
recordOutputs: true,
|
|
1670
|
+
metadata: {
|
|
1671
|
+
...phase && { phase },
|
|
1672
|
+
subAgentId: this.config.id,
|
|
1673
|
+
subAgentName: this.config.name
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
buildBaseGenerationConfig(modelSettings, messages, sanitizedTools, compressor, originalMessageCount, timeoutMs, toolChoice = "auto", phase, includeThinkingComplete = false, fullContextSize) {
|
|
1678
|
+
return {
|
|
1679
|
+
...modelSettings,
|
|
1680
|
+
toolChoice,
|
|
1681
|
+
messages,
|
|
1682
|
+
tools: sanitizedTools,
|
|
1683
|
+
prepareStep: async ({ messages: stepMessages }) => {
|
|
1684
|
+
return await this.handlePrepareStepCompression(stepMessages, compressor, originalMessageCount, fullContextSize);
|
|
1685
|
+
},
|
|
1686
|
+
stopWhen: async ({ steps }) => {
|
|
1687
|
+
return await this.handleStopWhenConditions(steps, includeThinkingComplete);
|
|
1688
|
+
},
|
|
1689
|
+
experimental_telemetry: this.buildTelemetryConfig(phase),
|
|
1690
|
+
abortSignal: AbortSignal.timeout(timeoutMs)
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
buildReasoningFlow(response, sessionId) {
|
|
1694
|
+
const reasoningFlow = [];
|
|
1695
|
+
const compressionSummary = this.currentCompressor?.getCompressionSummary();
|
|
1696
|
+
if (compressionSummary) {
|
|
1697
|
+
const summaryContent = JSON.stringify(compressionSummary, null, 2);
|
|
1698
|
+
reasoningFlow.push({
|
|
1699
|
+
role: "assistant",
|
|
1700
|
+
content: `## Research Summary (Compressed)\n\nBased on tool executions, here's the comprehensive summary:\n\n\`\`\`json\n${summaryContent}\n\`\`\`\n\nThis summary represents all tool execution results in compressed form. Full details are preserved in artifacts.`
|
|
1701
|
+
});
|
|
1702
|
+
} else if (response.steps) response.steps.forEach((step) => {
|
|
1703
|
+
if (step.toolCalls && step.toolResults) step.toolCalls.forEach((call, index) => {
|
|
1704
|
+
const result = step.toolResults[index];
|
|
1705
|
+
if (result) {
|
|
1706
|
+
const storedResult = toolSessionManager.getToolResult(sessionId, result.toolCallId);
|
|
1707
|
+
if ((storedResult?.toolName || call.toolName) === "thinking_complete") return;
|
|
1708
|
+
const actualResult = storedResult?.result || result.result || result;
|
|
1709
|
+
const actualArgs = storedResult?.args || call.args;
|
|
1710
|
+
const cleanResult = actualResult && typeof actualResult === "object" && !Array.isArray(actualResult) ? Object.fromEntries(Object.entries(actualResult).filter(([key]) => key !== "_structureHints")) : actualResult;
|
|
1711
|
+
const input = actualArgs ? JSON.stringify(actualArgs, null, 2) : "No input";
|
|
1712
|
+
const output = typeof cleanResult === "string" ? cleanResult : JSON.stringify(cleanResult, null, 2);
|
|
1713
|
+
let structureHintsFormatted = "";
|
|
1714
|
+
if (actualResult?._structureHints && this.artifactComponents && this.artifactComponents.length > 0) {
|
|
1715
|
+
const hints = actualResult._structureHints;
|
|
1716
|
+
structureHintsFormatted = `
|
|
1722
1717
|
### 📊 Structure Hints for Artifact Creation
|
|
1723
1718
|
|
|
1724
1719
|
**Terminal Field Paths (${hints.terminalPaths?.length || 0} found):**
|
|
@@ -1742,8 +1737,8 @@ ${hints.commonFields?.map((field) => ` • ${field}`).join("\n") || " None det
|
|
|
1742
1737
|
|
|
1743
1738
|
**Forbidden Syntax:** ${hints.forbiddenSyntax || "Use these paths for artifact base selectors."}
|
|
1744
1739
|
`;
|
|
1745
|
-
|
|
1746
|
-
|
|
1740
|
+
}
|
|
1741
|
+
const formattedResult = `## Tool: ${call.toolName}
|
|
1747
1742
|
|
|
1748
1743
|
### 🔧 TOOL_CALL_ID: ${result.toolCallId}
|
|
1749
1744
|
|
|
@@ -1752,185 +1747,179 @@ ${input}
|
|
|
1752
1747
|
|
|
1753
1748
|
### Output
|
|
1754
1749
|
${output}${structureHintsFormatted}`;
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
});
|
|
1759
|
-
}
|
|
1760
|
-
});
|
|
1750
|
+
reasoningFlow.push({
|
|
1751
|
+
role: "assistant",
|
|
1752
|
+
content: formattedResult
|
|
1761
1753
|
});
|
|
1762
|
-
const componentSchemas = [];
|
|
1763
|
-
if (this.config.dataComponents && this.config.dataComponents.length > 0) this.config.dataComponents.forEach((dc) => {
|
|
1764
|
-
const propsSchema = jsonSchemaToZod(dc.props);
|
|
1765
|
-
componentSchemas.push(z.object({
|
|
1766
|
-
id: z.string(),
|
|
1767
|
-
name: z.literal(dc.name),
|
|
1768
|
-
props: propsSchema
|
|
1769
|
-
}));
|
|
1770
|
-
});
|
|
1771
|
-
if (this.artifactComponents.length > 0) {
|
|
1772
|
-
const artifactCreateSchemas = ArtifactCreateSchema.getSchemas(this.artifactComponents);
|
|
1773
|
-
componentSchemas.push(...artifactCreateSchemas);
|
|
1774
|
-
componentSchemas.push(ArtifactReferenceSchema.getSchema());
|
|
1775
|
-
}
|
|
1776
|
-
let dataComponentsSchema;
|
|
1777
|
-
if (componentSchemas.length === 1) dataComponentsSchema = componentSchemas[0];
|
|
1778
|
-
else dataComponentsSchema = z.union(componentSchemas);
|
|
1779
|
-
const structuredModelSettings = ModelFactory.prepareGenerationConfig(this.getStructuredOutputModel());
|
|
1780
|
-
const configuredPhase2Timeout = structuredModelSettings.maxDuration ? Math.min(structuredModelSettings.maxDuration * 1e3, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) : LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS;
|
|
1781
|
-
const phase2TimeoutMs = Math.min(configuredPhase2Timeout, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS);
|
|
1782
|
-
if (structuredModelSettings.maxDuration && structuredModelSettings.maxDuration * 1e3 > LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) logger.warn({
|
|
1783
|
-
requestedTimeout: structuredModelSettings.maxDuration * 1e3,
|
|
1784
|
-
appliedTimeout: phase2TimeoutMs,
|
|
1785
|
-
maxAllowed: LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS,
|
|
1786
|
-
phase: "structured_generation"
|
|
1787
|
-
}, "Phase 2 requested timeout exceeded maximum allowed, capping to 10 minutes");
|
|
1788
|
-
if (this.getStreamingHelper()) {
|
|
1789
|
-
const phase2Messages = [{
|
|
1790
|
-
role: "system",
|
|
1791
|
-
content: await this.buildPhase2SystemPrompt(runtimeContext)
|
|
1792
|
-
}];
|
|
1793
|
-
if (conversationHistory.trim() !== "") phase2Messages.push({
|
|
1794
|
-
role: "user",
|
|
1795
|
-
content: conversationHistory
|
|
1796
|
-
});
|
|
1797
|
-
phase2Messages.push({
|
|
1798
|
-
role: "user",
|
|
1799
|
-
content: userMessage
|
|
1800
|
-
});
|
|
1801
|
-
phase2Messages.push(...reasoningFlow);
|
|
1802
|
-
if (reasoningFlow.length > 0 && reasoningFlow[reasoningFlow.length - 1]?.role === "assistant") phase2Messages.push({
|
|
1803
|
-
role: "user",
|
|
1804
|
-
content: "Continue with the structured response."
|
|
1805
|
-
});
|
|
1806
|
-
const streamResult = streamObject({
|
|
1807
|
-
...structuredModelSettings,
|
|
1808
|
-
messages: phase2Messages,
|
|
1809
|
-
schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
|
|
1810
|
-
experimental_telemetry: {
|
|
1811
|
-
isEnabled: true,
|
|
1812
|
-
functionId: this.config.id,
|
|
1813
|
-
recordInputs: true,
|
|
1814
|
-
recordOutputs: true,
|
|
1815
|
-
metadata: {
|
|
1816
|
-
phase: "structured_generation",
|
|
1817
|
-
subAgentId: this.config.id,
|
|
1818
|
-
subAgentName: this.config.name
|
|
1819
|
-
}
|
|
1820
|
-
},
|
|
1821
|
-
abortSignal: AbortSignal.timeout(phase2TimeoutMs)
|
|
1822
|
-
});
|
|
1823
|
-
const streamHelper = this.getStreamingHelper();
|
|
1824
|
-
if (!streamHelper) throw new Error("Stream helper is unexpectedly undefined in streaming context");
|
|
1825
|
-
const session = toolSessionManager.getSession(sessionId);
|
|
1826
|
-
const artifactParserOptions = {
|
|
1827
|
-
sessionId,
|
|
1828
|
-
taskId: session?.taskId,
|
|
1829
|
-
projectId: session?.projectId,
|
|
1830
|
-
artifactComponents: this.artifactComponents,
|
|
1831
|
-
streamRequestId: this.getStreamRequestId(),
|
|
1832
|
-
subAgentId: this.config.id
|
|
1833
|
-
};
|
|
1834
|
-
const parser = new IncrementalStreamParser(streamHelper, this.config.tenantId, contextId, artifactParserOptions);
|
|
1835
|
-
for await (const delta of streamResult.partialObjectStream) if (delta) await parser.processObjectDelta(delta);
|
|
1836
|
-
await parser.finalize();
|
|
1837
|
-
const structuredResponse = await streamResult;
|
|
1838
|
-
const collectedParts = parser.getCollectedParts();
|
|
1839
|
-
if (collectedParts.length > 0) response.formattedContent = { parts: collectedParts.map((part) => ({
|
|
1840
|
-
kind: part.kind,
|
|
1841
|
-
...part.kind === "text" && { text: part.text },
|
|
1842
|
-
...part.kind === "data" && { data: part.data }
|
|
1843
|
-
})) };
|
|
1844
|
-
response = {
|
|
1845
|
-
...response,
|
|
1846
|
-
object: structuredResponse.object
|
|
1847
|
-
};
|
|
1848
|
-
textResponse = JSON.stringify(structuredResponse.object, null, 2);
|
|
1849
|
-
} else {
|
|
1850
|
-
const { withJsonPostProcessing } = await import("../utils/json-postprocessor.js");
|
|
1851
|
-
const phase2Messages = [{
|
|
1852
|
-
role: "system",
|
|
1853
|
-
content: await this.buildPhase2SystemPrompt(runtimeContext)
|
|
1854
|
-
}];
|
|
1855
|
-
if (conversationHistory.trim() !== "") phase2Messages.push({
|
|
1856
|
-
role: "user",
|
|
1857
|
-
content: conversationHistory
|
|
1858
|
-
});
|
|
1859
|
-
phase2Messages.push({
|
|
1860
|
-
role: "user",
|
|
1861
|
-
content: userMessage
|
|
1862
|
-
});
|
|
1863
|
-
phase2Messages.push(...reasoningFlow);
|
|
1864
|
-
if (reasoningFlow.length > 0 && reasoningFlow[reasoningFlow.length - 1]?.role === "assistant") phase2Messages.push({
|
|
1865
|
-
role: "user",
|
|
1866
|
-
content: "Continue with the structured response."
|
|
1867
|
-
});
|
|
1868
|
-
const structuredResponse = await generateObject(withJsonPostProcessing({
|
|
1869
|
-
...structuredModelSettings,
|
|
1870
|
-
messages: phase2Messages,
|
|
1871
|
-
schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
|
|
1872
|
-
experimental_telemetry: {
|
|
1873
|
-
isEnabled: true,
|
|
1874
|
-
functionId: this.config.id,
|
|
1875
|
-
recordInputs: true,
|
|
1876
|
-
recordOutputs: true,
|
|
1877
|
-
metadata: {
|
|
1878
|
-
phase: "structured_generation",
|
|
1879
|
-
subAgentId: this.config.id,
|
|
1880
|
-
subAgentName: this.config.name
|
|
1881
|
-
}
|
|
1882
|
-
},
|
|
1883
|
-
abortSignal: AbortSignal.timeout(phase2TimeoutMs)
|
|
1884
|
-
}));
|
|
1885
|
-
response = {
|
|
1886
|
-
...response,
|
|
1887
|
-
object: structuredResponse.object
|
|
1888
|
-
};
|
|
1889
|
-
textResponse = JSON.stringify(structuredResponse.object, null, 2);
|
|
1890
|
-
}
|
|
1891
|
-
} else textResponse = response.text || "";
|
|
1892
|
-
else textResponse = response.steps[response.steps.length - 1].text || "";
|
|
1893
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
1894
|
-
span.end();
|
|
1895
|
-
let formattedContent = response.formattedContent || null;
|
|
1896
|
-
if (!formattedContent) {
|
|
1897
|
-
const session = toolSessionManager.getSession(sessionId);
|
|
1898
|
-
const responseFormatter = new ResponseFormatter(this.config.tenantId, {
|
|
1899
|
-
sessionId,
|
|
1900
|
-
taskId: session?.taskId,
|
|
1901
|
-
projectId: session?.projectId,
|
|
1902
|
-
contextId,
|
|
1903
|
-
artifactComponents: this.artifactComponents,
|
|
1904
|
-
streamRequestId: this.getStreamRequestId(),
|
|
1905
|
-
subAgentId: this.config.id
|
|
1906
|
-
});
|
|
1907
|
-
if (response.object) formattedContent = await responseFormatter.formatObjectResponse(response.object, contextId);
|
|
1908
|
-
else if (textResponse) formattedContent = await responseFormatter.formatResponse(textResponse, contextId);
|
|
1909
1754
|
}
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1755
|
+
});
|
|
1756
|
+
});
|
|
1757
|
+
return reasoningFlow;
|
|
1758
|
+
}
|
|
1759
|
+
buildDataComponentsSchema() {
|
|
1760
|
+
const componentSchemas = [];
|
|
1761
|
+
if (this.config.dataComponents && this.config.dataComponents.length > 0) this.config.dataComponents.forEach((dc) => {
|
|
1762
|
+
const propsSchema = jsonSchemaToZod(dc.props);
|
|
1763
|
+
componentSchemas.push(z.object({
|
|
1764
|
+
id: z.string(),
|
|
1765
|
+
name: z.literal(dc.name),
|
|
1766
|
+
props: propsSchema
|
|
1767
|
+
}));
|
|
1768
|
+
});
|
|
1769
|
+
if (this.artifactComponents.length > 0) {
|
|
1770
|
+
const artifactCreateSchemas = ArtifactCreateSchema.getSchemas(this.artifactComponents);
|
|
1771
|
+
componentSchemas.push(...artifactCreateSchemas);
|
|
1772
|
+
componentSchemas.push(ArtifactReferenceSchema.getSchema());
|
|
1773
|
+
}
|
|
1774
|
+
let dataComponentsSchema;
|
|
1775
|
+
if (componentSchemas.length === 1) dataComponentsSchema = componentSchemas[0];
|
|
1776
|
+
else dataComponentsSchema = z.union(componentSchemas);
|
|
1777
|
+
return dataComponentsSchema;
|
|
1778
|
+
}
|
|
1779
|
+
calculatePhase2Timeout(structuredModelSettings) {
|
|
1780
|
+
const configuredPhase2Timeout = structuredModelSettings.maxDuration ? Math.min(structuredModelSettings.maxDuration * 1e3, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) : LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS;
|
|
1781
|
+
const phase2TimeoutMs = Math.min(configuredPhase2Timeout, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS);
|
|
1782
|
+
if (structuredModelSettings.maxDuration && structuredModelSettings.maxDuration * 1e3 > LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) logger.warn({
|
|
1783
|
+
requestedTimeout: structuredModelSettings.maxDuration * 1e3,
|
|
1784
|
+
appliedTimeout: phase2TimeoutMs,
|
|
1785
|
+
maxAllowed: LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS,
|
|
1786
|
+
phase: "structured_generation"
|
|
1787
|
+
}, "Phase 2 requested timeout exceeded maximum allowed, capping to 10 minutes");
|
|
1788
|
+
return phase2TimeoutMs;
|
|
1789
|
+
}
|
|
1790
|
+
async buildPhase2Messages(runtimeContext, conversationHistory, userMessage, reasoningFlow) {
|
|
1791
|
+
const phase2Messages = [{
|
|
1792
|
+
role: "system",
|
|
1793
|
+
content: await this.buildPhase2SystemPrompt(runtimeContext)
|
|
1794
|
+
}];
|
|
1795
|
+
if (conversationHistory.trim() !== "") phase2Messages.push({
|
|
1796
|
+
role: "user",
|
|
1797
|
+
content: conversationHistory
|
|
1798
|
+
});
|
|
1799
|
+
phase2Messages.push({
|
|
1800
|
+
role: "user",
|
|
1801
|
+
content: userMessage
|
|
1933
1802
|
});
|
|
1803
|
+
phase2Messages.push(...reasoningFlow);
|
|
1804
|
+
if (reasoningFlow.length > 0 && reasoningFlow[reasoningFlow.length - 1]?.role === "assistant") phase2Messages.push({
|
|
1805
|
+
role: "user",
|
|
1806
|
+
content: "Continue with the structured response."
|
|
1807
|
+
});
|
|
1808
|
+
return phase2Messages;
|
|
1809
|
+
}
|
|
1810
|
+
async executeStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, sessionId, contextId, response) {
|
|
1811
|
+
const streamResult = streamObject({
|
|
1812
|
+
...structuredModelSettings,
|
|
1813
|
+
messages: phase2Messages,
|
|
1814
|
+
schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
|
|
1815
|
+
experimental_telemetry: this.buildTelemetryConfig("structured_generation"),
|
|
1816
|
+
abortSignal: AbortSignal.timeout(phase2TimeoutMs)
|
|
1817
|
+
});
|
|
1818
|
+
const parser = this.setupStreamParser(sessionId, contextId);
|
|
1819
|
+
for await (const delta of streamResult.partialObjectStream) if (delta) await parser.processObjectDelta(delta);
|
|
1820
|
+
await parser.finalize();
|
|
1821
|
+
const structuredResponse = await streamResult;
|
|
1822
|
+
const collectedParts = parser.getCollectedParts();
|
|
1823
|
+
if (collectedParts.length > 0) response.formattedContent = { parts: collectedParts.map((part) => ({
|
|
1824
|
+
kind: part.kind,
|
|
1825
|
+
...part.kind === "text" && { text: part.text },
|
|
1826
|
+
...part.kind === "data" && { data: part.data }
|
|
1827
|
+
})) };
|
|
1828
|
+
return {
|
|
1829
|
+
...response,
|
|
1830
|
+
object: structuredResponse.object,
|
|
1831
|
+
textResponse: JSON.stringify(structuredResponse.object, null, 2)
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
async executeNonStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, response) {
|
|
1835
|
+
const structuredResponse = await generateObject(withJsonPostProcessing({
|
|
1836
|
+
...structuredModelSettings,
|
|
1837
|
+
messages: phase2Messages,
|
|
1838
|
+
schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
|
|
1839
|
+
experimental_telemetry: this.buildTelemetryConfig("structured_generation"),
|
|
1840
|
+
abortSignal: AbortSignal.timeout(phase2TimeoutMs)
|
|
1841
|
+
}));
|
|
1842
|
+
return {
|
|
1843
|
+
...response,
|
|
1844
|
+
object: structuredResponse.object,
|
|
1845
|
+
textResponse: JSON.stringify(structuredResponse.object, null, 2)
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
async formatFinalResponse(response, textResponse, sessionId, contextId) {
|
|
1849
|
+
let formattedContent = response.formattedContent || null;
|
|
1850
|
+
if (!formattedContent) {
|
|
1851
|
+
const session = toolSessionManager.getSession(sessionId);
|
|
1852
|
+
const responseFormatter = new ResponseFormatter(this.config.tenantId, {
|
|
1853
|
+
sessionId,
|
|
1854
|
+
taskId: session?.taskId,
|
|
1855
|
+
projectId: session?.projectId,
|
|
1856
|
+
contextId,
|
|
1857
|
+
artifactComponents: this.artifactComponents,
|
|
1858
|
+
streamRequestId: this.getStreamRequestId(),
|
|
1859
|
+
subAgentId: this.config.id
|
|
1860
|
+
});
|
|
1861
|
+
if (response.object) formattedContent = await responseFormatter.formatObjectResponse(response.object, contextId);
|
|
1862
|
+
else if (textResponse) formattedContent = await responseFormatter.formatResponse(textResponse, contextId);
|
|
1863
|
+
}
|
|
1864
|
+
return {
|
|
1865
|
+
...response,
|
|
1866
|
+
formattedContent
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
handleGenerationError(error, span) {
|
|
1870
|
+
if (this.currentCompressor) this.currentCompressor.fullCleanup();
|
|
1871
|
+
this.currentCompressor = null;
|
|
1872
|
+
const errorToThrow = error instanceof Error ? error : new Error(String(error));
|
|
1873
|
+
setSpanWithError$1(span, errorToThrow);
|
|
1874
|
+
span.end();
|
|
1875
|
+
throw errorToThrow;
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Public cleanup method for external lifecycle management (e.g., session cleanup)
|
|
1879
|
+
* Performs full cleanup of compression state when agent/session is ending
|
|
1880
|
+
*/
|
|
1881
|
+
cleanupCompression() {
|
|
1882
|
+
if (this.currentCompressor) {
|
|
1883
|
+
this.currentCompressor.fullCleanup();
|
|
1884
|
+
this.currentCompressor = null;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
async processStreamEvents(streamResult, parser) {
|
|
1888
|
+
for await (const event of streamResult.fullStream) switch (event.type) {
|
|
1889
|
+
case "text-delta":
|
|
1890
|
+
await parser.processTextChunk(event.text);
|
|
1891
|
+
break;
|
|
1892
|
+
case "tool-call":
|
|
1893
|
+
parser.markToolResult();
|
|
1894
|
+
break;
|
|
1895
|
+
case "tool-result":
|
|
1896
|
+
parser.markToolResult();
|
|
1897
|
+
break;
|
|
1898
|
+
case "finish":
|
|
1899
|
+
if (event.finishReason === "tool-calls") parser.markToolResult();
|
|
1900
|
+
break;
|
|
1901
|
+
case "error": {
|
|
1902
|
+
if (event.error instanceof Error) throw event.error;
|
|
1903
|
+
const errorMessage = event.error?.error?.message;
|
|
1904
|
+
throw new Error(errorMessage);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
await parser.finalize();
|
|
1908
|
+
}
|
|
1909
|
+
formatStreamingResponse(response, parser) {
|
|
1910
|
+
const collectedParts = parser.getCollectedParts();
|
|
1911
|
+
if (collectedParts.length > 0) response.formattedContent = { parts: collectedParts.map((part) => ({
|
|
1912
|
+
kind: part.kind,
|
|
1913
|
+
...part.kind === "text" && { text: part.text },
|
|
1914
|
+
...part.kind === "data" && { data: part.data }
|
|
1915
|
+
})) };
|
|
1916
|
+
const streamedContent = parser.getAllStreamedContent();
|
|
1917
|
+
if (streamedContent.length > 0) response.streamedContent = { parts: streamedContent.map((part) => ({
|
|
1918
|
+
kind: part.kind,
|
|
1919
|
+
...part.kind === "text" && { text: part.text },
|
|
1920
|
+
...part.kind === "data" && { data: part.data }
|
|
1921
|
+
})) };
|
|
1922
|
+
return response;
|
|
1934
1923
|
}
|
|
1935
1924
|
};
|
|
1936
1925
|
|