@gt-fe/eap-runtime 0.0.23 → 0.0.25
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/README.md +1 -0
- package/dist/api/handlers/invoke.handler.d.ts +4 -0
- package/dist/api/handlers/invoke.handler.js +69 -37
- package/dist/api/handlers/stream.handler.d.ts +4 -0
- package/dist/api/handlers/stream.handler.js +40 -21
- package/dist/api/routes/agent.routes.js +6 -5
- package/dist/checkpoint/checkpoint-ref-notify.js +1 -1
- package/dist/common/types.d.ts +6 -2
- package/dist/common/types.js +3 -1
- package/dist/graph/adapters/langgraph.adapter.d.ts +6 -4
- package/dist/graph/adapters/langgraph.adapter.js +103 -18
- package/dist/graph/contracts/executable-graph.d.ts +11 -2
- package/dist/graph/execution-trace-context.d.ts +4 -0
- package/dist/graph/execution-trace-context.js +3 -1
- package/dist/graph/nodes/rag/types.d.ts +4 -1
- package/dist/providers/http-llm-provider.js +25 -0
- package/dist/registry/graph-loader.js +5 -4
- package/dist/runtime/agent-runtime.d.ts +4 -1
- package/dist/runtime/agent-runtime.js +110 -20
- package/dist/runtime/dev-runtime.d.ts +1 -0
- package/dist/runtime/dev-runtime.js +9 -2
- package/dist/runtime/types.d.ts +1 -1
- package/dist/session/apply-session-compression.d.ts +6 -4
- package/dist/session/apply-session-compression.js +24 -11
- package/dist/session/index.d.ts +1 -1
- package/dist/session/index.js +1 -1
- package/dist/session/persist-turn-messages.d.ts +50 -9
- package/dist/session/persist-turn-messages.js +133 -15
- package/dist/sse/events/response-events.d.ts +2 -2
- package/dist/sse/events/response-events.js +1 -1
- package/dist/task/contracts/task-manager.contract.d.ts +2 -0
- package/dist/task/index.d.ts +1 -0
- package/dist/task/index.js +1 -0
- package/dist/task/resolve-task-timeout.d.ts +6 -0
- package/dist/task/resolve-task-timeout.js +16 -0
- package/dist/task/task-timeout.d.ts +1 -1
- package/dist/task/task-timeout.js +3 -2
- package/dist/task/task.service.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Enterprise Agent Platform **执行引擎**(LangGraph.js):Agent Graph 编
|
|
|
8
8
|
|
|
9
9
|
| 日期 | 摘要 |
|
|
10
10
|
|------|------|
|
|
11
|
+
| 2026-09-14 | 会话滑动窗口压缩默认开;`EAP_RUNTIME_COMPRESSION=0` 关闭 |
|
|
11
12
|
| 2026-09-08 | [`docs/TODO.md`](./docs/TODO.md) 改为主路径状态(已落地 / 不在本包),不是排期 |
|
|
12
13
|
| 2026-08-29 | 待办收口:[`docs/TODO.md`](./docs/TODO.md)。此后压缩已挂主路径(默认关);窗口展示不在 Runtime;Gateway 须 `tools/list` |
|
|
13
14
|
| 2026-08-24 | 0.0.10:官方 Skill / Harness、`ForkParams`、对话详情、存储两档。依赖 `@eap/contracts@0.0.9`。详见 [CHANGELOG.md](./CHANGELOG.md) |
|
|
@@ -22,6 +22,7 @@ import type { TokenStatsService } from '../../token/token-stats.service.js';
|
|
|
22
22
|
import type { TokenBudgetGuard } from '../../token/token-budget-guard.js';
|
|
23
23
|
import type { SessionBudgetChecker } from '../../token/session-budget-checker.js';
|
|
24
24
|
import type { InvokeRequest, InvokeResponse } from '../routes/agent.routes.js';
|
|
25
|
+
import type { TaskTimeoutManager } from '../../task/task-timeout.js';
|
|
25
26
|
import { type GraphLoader } from '../../registry/graph-loader.js';
|
|
26
27
|
import type { ICheckpointSaver } from '../../checkpoint/contracts/checkpoint-saver.contract.js';
|
|
27
28
|
import type { ITraceCollector } from '../../contracts/trace-collector.js';
|
|
@@ -46,6 +47,8 @@ export interface InvokeOrchestratorDeps {
|
|
|
46
47
|
traceCollector?: ITraceCollector;
|
|
47
48
|
sessionCompression?: SessionCompressionOptions;
|
|
48
49
|
sessionMemory?: SessionMemoryOptions;
|
|
50
|
+
/** 未注入时不启动整图超时(测试桩) */
|
|
51
|
+
timeoutManager?: TaskTimeoutManager;
|
|
49
52
|
}
|
|
50
53
|
/**
|
|
51
54
|
* InvokeOrchestrator — 同步调用全流程编排
|
|
@@ -66,6 +69,7 @@ export declare class InvokeOrchestrator {
|
|
|
66
69
|
private readonly traceCollector?;
|
|
67
70
|
private readonly sessionCompression?;
|
|
68
71
|
private readonly sessionMemory?;
|
|
72
|
+
private readonly timeoutManager?;
|
|
69
73
|
constructor(deps: InvokeOrchestratorDeps);
|
|
70
74
|
/**
|
|
71
75
|
* 执行同步调用编排
|
|
@@ -15,12 +15,17 @@
|
|
|
15
15
|
* @see requirements.md Requirement 11 AC1-AC2
|
|
16
16
|
*/
|
|
17
17
|
import { randomUUID } from 'node:crypto';
|
|
18
|
-
import { buildHitlWaitingTaskOutput,
|
|
18
|
+
import { buildHitlWaitingTaskOutput, loadModelFacingHistory, messageRecordsToGraphMessages, persistGraphMessageBaseline, turnGraphMessagesAfterCompression, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, resolveCheckpointThreadId, sessionVisibleGraphMessages, } from '../../session/persist-turn-messages.js';
|
|
19
19
|
import { SessionBudgetExhaustedError } from '../../token/session-budget-checker.js';
|
|
20
20
|
import { TokenBudgetExhaustedError } from '../../token/token-budget-guard.js';
|
|
21
21
|
import { recordTaskTokenUsages, tokenUsagesFromLlmChannel, } from '../../token/record-task-token-usage.js';
|
|
22
22
|
import { resolveExecutionContext } from '../../governance/governance-client.js';
|
|
23
23
|
import { resolveSkillInputFromInvokeInput } from '../../registry/skill-io-guard.js';
|
|
24
|
+
import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
|
|
25
|
+
import { classifyRuntimeError } from '../../utils/http-error.js';
|
|
26
|
+
import { DEFAULT_GRAPH_TIMEOUT_MS } from '../../common/types.js';
|
|
27
|
+
import { resolveTaskTimeoutMs } from '../../task/resolve-task-timeout.js';
|
|
28
|
+
import { runWithExecutionTrace } from '../../graph/execution-trace-context.js';
|
|
24
29
|
import { resolveAgentSystemPromptFromLoader } from '../../registry/graph-loader.js';
|
|
25
30
|
import { toLoadedToolMap } from '../../tool/loaded-tool-definition.js';
|
|
26
31
|
import { toLoadedSkillMap } from '../../tool/system-prompt-builder.js';
|
|
@@ -28,7 +33,7 @@ import { assembleLoadedTools } from '../../tool/harness-primitives.js';
|
|
|
28
33
|
import { resolveAgentCode } from '../../common/agent-code.js';
|
|
29
34
|
import { unwrapHitlPaused } from '../../graph/hitl/hitl-handler.js';
|
|
30
35
|
import { resolveTraceId } from '../../common/trace-id.js';
|
|
31
|
-
import {
|
|
36
|
+
import { runSessionCompression, withCompressionSummaryModel, } from '../../session/apply-session-compression.js';
|
|
32
37
|
import { applySessionMemory, } from '../../session/apply-session-memory.js';
|
|
33
38
|
// ─────────────────────────────────────────────
|
|
34
39
|
// Default Graph DSL fallback
|
|
@@ -44,7 +49,7 @@ const DEFAULT_GRAPH_DSL = {
|
|
|
44
49
|
{ from: '__start__', to: 'llm' },
|
|
45
50
|
{ from: 'llm', to: '__end__' },
|
|
46
51
|
],
|
|
47
|
-
config: { timeout:
|
|
52
|
+
config: { timeout: DEFAULT_GRAPH_TIMEOUT_MS },
|
|
48
53
|
};
|
|
49
54
|
// ─────────────────────────────────────────────
|
|
50
55
|
// InvokeOrchestrator
|
|
@@ -68,6 +73,7 @@ export class InvokeOrchestrator {
|
|
|
68
73
|
traceCollector;
|
|
69
74
|
sessionCompression;
|
|
70
75
|
sessionMemory;
|
|
76
|
+
timeoutManager;
|
|
71
77
|
constructor(deps) {
|
|
72
78
|
this.sessionContextService = deps.sessionContextService;
|
|
73
79
|
this.taskService = deps.taskService;
|
|
@@ -81,6 +87,7 @@ export class InvokeOrchestrator {
|
|
|
81
87
|
this.traceCollector = deps.traceCollector;
|
|
82
88
|
this.sessionCompression = deps.sessionCompression;
|
|
83
89
|
this.sessionMemory = deps.sessionMemory;
|
|
90
|
+
this.timeoutManager = deps.timeoutManager;
|
|
84
91
|
}
|
|
85
92
|
/**
|
|
86
93
|
* 执行同步调用编排
|
|
@@ -143,8 +150,12 @@ export class InvokeOrchestrator {
|
|
|
143
150
|
throw error;
|
|
144
151
|
}
|
|
145
152
|
}
|
|
146
|
-
// ─── Step 3:
|
|
147
|
-
const
|
|
153
|
+
// ─── Step 3: 加载图并创建 Task(Hosting 传入 taskId 时复用)───
|
|
154
|
+
const loadOpts = { agentVersion: req.agentVersion };
|
|
155
|
+
const graphDsl = this.graphLoader
|
|
156
|
+
? await this.graphLoader.loadGraph(agentCode, loadOpts)
|
|
157
|
+
: DEFAULT_GRAPH_DSL;
|
|
158
|
+
const timeout = resolveTaskTimeoutMs(req.config?.timeout, graphDsl.config?.timeout);
|
|
148
159
|
const task = await this.taskService.create({
|
|
149
160
|
id: req.taskId,
|
|
150
161
|
agentId: agentCode,
|
|
@@ -156,6 +167,9 @@ export class InvokeOrchestrator {
|
|
|
156
167
|
});
|
|
157
168
|
// ─── Step 4: 启动 Task (pending → running) ───
|
|
158
169
|
await this.taskService.updateStatus(task.id, 'running');
|
|
170
|
+
const abortSignal = this.timeoutManager?.startTimer(task.id, timeout, async () => {
|
|
171
|
+
await this.taskService.tryUpdateStatus(task.id, 'running', 'timeout', 'Task execution timed out');
|
|
172
|
+
});
|
|
159
173
|
// ─── Step 4b: 持久化本轮 user(多轮短期记忆)───
|
|
160
174
|
await persistUserTurnMessage(this.messageService, {
|
|
161
175
|
sessionId,
|
|
@@ -166,12 +180,9 @@ export class InvokeOrchestrator {
|
|
|
166
180
|
// ─── Step 5: 构建/执行 Graph ───
|
|
167
181
|
let graphResult;
|
|
168
182
|
let finishPoints;
|
|
183
|
+
let persistBaseline = 0;
|
|
169
184
|
try {
|
|
170
185
|
// 构建图 — 每次请求创建独立 StateGraph 实例,确保请求间无共享可变状态
|
|
171
|
-
const loadOpts = { agentVersion: req.agentVersion };
|
|
172
|
-
const graphDsl = this.graphLoader
|
|
173
|
-
? await this.graphLoader.loadGraph(agentCode, loadOpts)
|
|
174
|
-
: DEFAULT_GRAPH_DSL;
|
|
175
186
|
const graph = await this.graphEngine.buildGraph(graphDsl);
|
|
176
187
|
finishPoints = graph.finishPoints;
|
|
177
188
|
const loadedToolsBase = this.graphLoader
|
|
@@ -192,32 +203,45 @@ export class InvokeOrchestrator {
|
|
|
192
203
|
? (await this.graphLoader.resolveAgentDir(agentCode, req.agentVersion)) ?? undefined
|
|
193
204
|
: undefined;
|
|
194
205
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.graphLoader, agentCode, req.agentVersion);
|
|
195
|
-
//
|
|
206
|
+
// 模型上下文:Checkpoint 已有 messages 则压它;压成功用 __set 写回最新 Checkpoint。Message 库不改。
|
|
207
|
+
const facing = await loadModelFacingHistory({
|
|
208
|
+
sessionHistory: messageRecordsToGraphMessages(sessionContext.messages),
|
|
209
|
+
checkpointSaver: this.checkpointSaver,
|
|
210
|
+
threadId: sessionId,
|
|
211
|
+
});
|
|
212
|
+
const compression = await runSessionCompression({
|
|
213
|
+
history: facing.messages,
|
|
214
|
+
sessionId,
|
|
215
|
+
userId: effectiveUserId,
|
|
216
|
+
taskId: task.id,
|
|
217
|
+
options: await withCompressionSummaryModel(this.sessionCompression, () => this.graphLoader
|
|
218
|
+
? this.graphLoader.resolveDefaultModel(agentCode, req.agentVersion)
|
|
219
|
+
: Promise.resolve(undefined)),
|
|
220
|
+
});
|
|
196
221
|
const history = await applySessionMemory({
|
|
197
|
-
history:
|
|
198
|
-
history: messageRecordsToGraphMessages(sessionContext.messages),
|
|
199
|
-
sessionId,
|
|
200
|
-
userId: effectiveUserId,
|
|
201
|
-
taskId: task.id,
|
|
202
|
-
options: await withCompressionSummaryModel(this.sessionCompression, () => this.graphLoader
|
|
203
|
-
? this.graphLoader.resolveDefaultModel(agentCode, req.agentVersion)
|
|
204
|
-
: Promise.resolve(undefined)),
|
|
205
|
-
}),
|
|
222
|
+
history: compression.messages,
|
|
206
223
|
userId: effectiveUserId,
|
|
207
224
|
agentCode,
|
|
208
225
|
query: typeof req.input === 'string' ? req.input : undefined,
|
|
209
226
|
options: this.sessionMemory,
|
|
210
227
|
});
|
|
211
|
-
const messages =
|
|
228
|
+
const { messages, replaceCheckpointMessages } = turnGraphMessagesAfterCompression({
|
|
212
229
|
history,
|
|
213
230
|
input: req.input,
|
|
214
|
-
|
|
215
|
-
|
|
231
|
+
fromCheckpoint: facing.fromCheckpoint,
|
|
232
|
+
compressed: compression.compressed,
|
|
233
|
+
});
|
|
234
|
+
persistBaseline = persistGraphMessageBaseline({
|
|
235
|
+
fromCheckpoint: facing.fromCheckpoint,
|
|
236
|
+
replaceCheckpointMessages,
|
|
237
|
+
graphInputCount: messages.length,
|
|
238
|
+
modelFacingCount: facing.messages.length,
|
|
216
239
|
});
|
|
217
240
|
const skillInput = resolveSkillInputFromInvokeInput(req.input);
|
|
218
241
|
// 同步执行图
|
|
219
|
-
graphResult = await this.graphEngine.invoke(graph, {
|
|
242
|
+
graphResult = await runWithExecutionTrace({ abortSignal }, () => this.graphEngine.invoke(graph, {
|
|
220
243
|
messages,
|
|
244
|
+
replaceCheckpointMessages,
|
|
221
245
|
userId: effectiveUserId,
|
|
222
246
|
delegationToken: ctx.delegationToken,
|
|
223
247
|
agentCode,
|
|
@@ -238,7 +262,9 @@ export class InvokeOrchestrator {
|
|
|
238
262
|
agentSystemPrompt,
|
|
239
263
|
traceId: resolveTraceId(req),
|
|
240
264
|
traceCollector: this.traceCollector,
|
|
241
|
-
|
|
265
|
+
abortSignal,
|
|
266
|
+
abortDeadlineMs: Date.now() + timeout,
|
|
267
|
+
}));
|
|
242
268
|
}
|
|
243
269
|
catch (error) {
|
|
244
270
|
const usages = await this.persistInvokeUsages(task.id, sessionId);
|
|
@@ -286,7 +312,7 @@ export class InvokeOrchestrator {
|
|
|
286
312
|
}
|
|
287
313
|
// ─── 错误处理:Timeout ───
|
|
288
314
|
if (this.isTimeoutError(error)) {
|
|
289
|
-
await this.taskService.
|
|
315
|
+
await this.taskService.tryUpdateStatus(task.id, 'running', 'timeout');
|
|
290
316
|
return this.buildResponse(traceId, {
|
|
291
317
|
taskId: task.id,
|
|
292
318
|
sessionId,
|
|
@@ -297,7 +323,9 @@ export class InvokeOrchestrator {
|
|
|
297
323
|
});
|
|
298
324
|
}
|
|
299
325
|
// ─── 错误处理:通用错误 ───
|
|
300
|
-
const
|
|
326
|
+
const classified = classifyRuntimeError(error);
|
|
327
|
+
const fuse = classified.code === ErrorCodes.GRAPH_MAX_ITERATIONS ? classified : undefined;
|
|
328
|
+
const errorMessage = fuse?.message ?? (error instanceof Error ? error.message : 'Unknown error');
|
|
301
329
|
await this.taskService.updateStatus(task.id, 'failed', errorMessage);
|
|
302
330
|
return this.buildErrorResponse(traceId, {
|
|
303
331
|
taskId: task.id,
|
|
@@ -306,7 +334,10 @@ export class InvokeOrchestrator {
|
|
|
306
334
|
tokenUsage,
|
|
307
335
|
duration: Date.now() - startTime,
|
|
308
336
|
status: 'failed',
|
|
309
|
-
});
|
|
337
|
+
}, fuse ?? error);
|
|
338
|
+
}
|
|
339
|
+
finally {
|
|
340
|
+
this.timeoutManager?.clearTimer(task.id);
|
|
310
341
|
}
|
|
311
342
|
// ─── Step 6: 记录 Token 消耗 ───
|
|
312
343
|
//
|
|
@@ -352,12 +383,11 @@ export class InvokeOrchestrator {
|
|
|
352
383
|
// ─── Step 7: 完成 Task (running → success) ───
|
|
353
384
|
await this.taskService.updateStatus(task.id, 'success', graphResult.output);
|
|
354
385
|
// ─── Step 7b: 持久化本轮 transcript(统一顺序:assistant tool_calls → tool → assistant 文本)───
|
|
355
|
-
//
|
|
356
|
-
const initialMessagesCount = sessionContext.messages.length + 1;
|
|
386
|
+
// 切点是入图 messages 基线(压缩 `__set` 后 Graph 短于 Session 全文)。user 已在 4b 写入。
|
|
357
387
|
const allMessages = Array.isArray(graphResult.finalState?.messages)
|
|
358
388
|
? graphResult.finalState.messages
|
|
359
389
|
: [];
|
|
360
|
-
const newMessages = sessionVisibleGraphMessages(allMessages.slice(
|
|
390
|
+
const newMessages = sessionVisibleGraphMessages(allMessages.slice(persistBaseline), finishPoints);
|
|
361
391
|
const duration = Date.now() - startTime;
|
|
362
392
|
await persistTurnTranscript(this.messageService, {
|
|
363
393
|
sessionId,
|
|
@@ -387,15 +417,17 @@ export class InvokeOrchestrator {
|
|
|
387
417
|
buildResponse(traceId, data) {
|
|
388
418
|
return { code: 'ok', data, traceId };
|
|
389
419
|
}
|
|
390
|
-
buildErrorResponse(traceId, data) {
|
|
391
|
-
|
|
420
|
+
buildErrorResponse(traceId, data, error) {
|
|
421
|
+
if (data.status === 'token_exhausted') {
|
|
422
|
+
return { code: 'runtime.budget.task_exhausted', data, traceId };
|
|
423
|
+
}
|
|
424
|
+
if (error instanceof AppError) {
|
|
425
|
+
return { code: error.code, data, traceId };
|
|
426
|
+
}
|
|
427
|
+
return { code: 'runtime.graph.execution_error', data, traceId };
|
|
392
428
|
}
|
|
393
429
|
isTimeoutError(error) {
|
|
394
|
-
|
|
395
|
-
const msg = error.message.toLowerCase();
|
|
396
|
-
return msg.includes('timeout') || msg.includes('timed out');
|
|
397
|
-
}
|
|
398
|
-
return false;
|
|
430
|
+
return error instanceof Error && /timed out/i.test(error.message);
|
|
399
431
|
}
|
|
400
432
|
async persistInvokeUsages(taskId, sessionId) {
|
|
401
433
|
if (!this.checkpointSaver)
|
|
@@ -33,6 +33,7 @@ import type { StreamRequest } from '../routes/agent.routes.js';
|
|
|
33
33
|
import { type GraphLoader } from '../../registry/graph-loader.js';
|
|
34
34
|
import type { ICheckpointSaver } from '../../checkpoint/contracts/checkpoint-saver.contract.js';
|
|
35
35
|
import type { ITraceCollector } from '../../contracts/trace-collector.js';
|
|
36
|
+
import type { TaskTimeoutManager } from '../../task/task-timeout.js';
|
|
36
37
|
/**
|
|
37
38
|
* StreamOrchestrator 构造器依赖
|
|
38
39
|
*/
|
|
@@ -52,6 +53,8 @@ export interface StreamOrchestratorDeps {
|
|
|
52
53
|
traceCollector?: ITraceCollector;
|
|
53
54
|
sessionCompression?: SessionCompressionOptions;
|
|
54
55
|
sessionMemory?: SessionMemoryOptions;
|
|
56
|
+
/** 未注入时不启动整图超时(测试桩) */
|
|
57
|
+
timeoutManager?: TaskTimeoutManager;
|
|
55
58
|
}
|
|
56
59
|
/**
|
|
57
60
|
* StreamOrchestrator — 流式调用全流程编排
|
|
@@ -72,6 +75,7 @@ export declare class StreamOrchestrator {
|
|
|
72
75
|
private readonly traceCollector?;
|
|
73
76
|
private readonly sessionCompression?;
|
|
74
77
|
private readonly sessionMemory?;
|
|
78
|
+
private readonly timeoutManager?;
|
|
75
79
|
constructor(deps: StreamOrchestratorDeps);
|
|
76
80
|
/**
|
|
77
81
|
* 执行流式调用编排
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* @see design.md §1.5 /stream 内部执行逻辑
|
|
20
20
|
* @see requirements.md Requirement 11 AC2
|
|
21
21
|
*/
|
|
22
|
-
import { buildHitlWaitingTaskOutput,
|
|
22
|
+
import { buildHitlWaitingTaskOutput, buildTurnTranscriptFromStreamEvents, loadModelFacingHistory, turnGraphMessagesAfterCompression, extractStreamReasoning, applyStreamTextDelta, createStreamAssistantBuckets, visibleStreamAssistantText, streamAssistantTexts, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, appendTerminalToolResult, } from '../../session/persist-turn-messages.js';
|
|
23
23
|
import { SessionBudgetExhaustedError } from '../../token/session-budget-checker.js';
|
|
24
24
|
import { TokenBudgetExhaustedError } from '../../token/token-budget-guard.js';
|
|
25
25
|
import { recordTaskTokenUsages } from '../../token/record-task-token-usage.js';
|
|
@@ -39,6 +39,9 @@ import { resolveAgentCode } from '../../common/agent-code.js';
|
|
|
39
39
|
import { unwrapHitlPaused } from './invoke.handler.js';
|
|
40
40
|
import { buildHitlWaitedPayload } from '../../graph/hitl/hitl-handler.js';
|
|
41
41
|
import { resolveTraceId } from '../../common/trace-id.js';
|
|
42
|
+
import { DEFAULT_GRAPH_TIMEOUT_MS } from '../../common/types.js';
|
|
43
|
+
import { resolveTaskTimeoutMs } from '../../task/resolve-task-timeout.js';
|
|
44
|
+
import { runWithExecutionTrace } from '../../graph/execution-trace-context.js';
|
|
42
45
|
// ─────────────────────────────────────────────
|
|
43
46
|
// Placeholder Graph DSL (Agent Registry 接入前)
|
|
44
47
|
// ─────────────────────────────────────────────
|
|
@@ -53,7 +56,7 @@ const DEFAULT_GRAPH_DSL = {
|
|
|
53
56
|
{ from: '__start__', to: 'llm' },
|
|
54
57
|
{ from: 'llm', to: '__end__' },
|
|
55
58
|
],
|
|
56
|
-
config: { timeout:
|
|
59
|
+
config: { timeout: DEFAULT_GRAPH_TIMEOUT_MS },
|
|
57
60
|
};
|
|
58
61
|
// ─────────────────────────────────────────────
|
|
59
62
|
// StreamOrchestrator
|
|
@@ -77,6 +80,7 @@ export class StreamOrchestrator {
|
|
|
77
80
|
traceCollector;
|
|
78
81
|
sessionCompression;
|
|
79
82
|
sessionMemory;
|
|
83
|
+
timeoutManager;
|
|
80
84
|
constructor(deps) {
|
|
81
85
|
this.sessionContextService = deps.sessionContextService;
|
|
82
86
|
this.taskService = deps.taskService;
|
|
@@ -90,6 +94,7 @@ export class StreamOrchestrator {
|
|
|
90
94
|
this.traceCollector = deps.traceCollector;
|
|
91
95
|
this.sessionCompression = deps.sessionCompression;
|
|
92
96
|
this.sessionMemory = deps.sessionMemory;
|
|
97
|
+
this.timeoutManager = deps.timeoutManager;
|
|
93
98
|
}
|
|
94
99
|
/**
|
|
95
100
|
* 执行流式调用编排
|
|
@@ -166,8 +171,12 @@ export class StreamOrchestrator {
|
|
|
166
171
|
userId,
|
|
167
172
|
}));
|
|
168
173
|
}
|
|
169
|
-
// ─── Step 3:
|
|
170
|
-
const
|
|
174
|
+
// ─── Step 3: 加载图并创建 Task(Hosting 传入 taskId 时复用)───
|
|
175
|
+
const loadOpts = { agentVersion: req.agentVersion };
|
|
176
|
+
const graphDsl = this.graphLoader
|
|
177
|
+
? await this.graphLoader.loadGraph(agentCode, loadOpts)
|
|
178
|
+
: DEFAULT_GRAPH_DSL;
|
|
179
|
+
const timeout = resolveTaskTimeoutMs(req.config?.timeout, graphDsl.config?.timeout);
|
|
171
180
|
const task = await this.taskService.create({
|
|
172
181
|
id: req.taskId,
|
|
173
182
|
agentId: agentCode,
|
|
@@ -180,6 +189,9 @@ export class StreamOrchestrator {
|
|
|
180
189
|
const taskId = task.id;
|
|
181
190
|
// ─── Step 4: 启动 Task (pending → running) ───
|
|
182
191
|
await this.taskService.updateStatus(taskId, 'running');
|
|
192
|
+
const abortSignal = this.timeoutManager?.startTimer(taskId, timeout, async () => {
|
|
193
|
+
await this.taskService.tryUpdateStatus(taskId, 'running', 'timeout', 'Task execution timed out');
|
|
194
|
+
});
|
|
183
195
|
// ─── Step 4b: 持久化本轮 user(多轮短期记忆)───
|
|
184
196
|
await persistUserTurnMessage(this.messageService, {
|
|
185
197
|
sessionId,
|
|
@@ -193,10 +205,6 @@ export class StreamOrchestrator {
|
|
|
193
205
|
sessionId,
|
|
194
206
|
}));
|
|
195
207
|
// ─── Step 5: 构建 Graph — 每次请求创建独立 StateGraph 实例,确保请求间无共享可变状态 ───
|
|
196
|
-
const loadOpts = { agentVersion: req.agentVersion };
|
|
197
|
-
const graphDsl = this.graphLoader
|
|
198
|
-
? await this.graphLoader.loadGraph(agentCode, loadOpts)
|
|
199
|
-
: DEFAULT_GRAPH_DSL;
|
|
200
208
|
const graph = await this.graphEngine.buildGraph(graphDsl);
|
|
201
209
|
const loadedToolsBase = this.graphLoader
|
|
202
210
|
? toLoadedToolMap(await this.graphLoader.loadToolDefinitions(agentCode, loadOpts))
|
|
@@ -213,8 +221,14 @@ export class StreamOrchestrator {
|
|
|
213
221
|
? (await this.graphLoader.resolveAgentDir(agentCode, req.agentVersion)) ?? undefined
|
|
214
222
|
: undefined;
|
|
215
223
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.graphLoader, agentCode, req.agentVersion);
|
|
216
|
-
//
|
|
217
|
-
|
|
224
|
+
// 模型上下文:Checkpoint 已有 messages 则压它;压成功用 __set 写回最新 Checkpoint。Message 库不改。
|
|
225
|
+
const facing = await loadModelFacingHistory({
|
|
226
|
+
sessionHistory: messageRecordsToGraphMessages(sessionContext.messages),
|
|
227
|
+
checkpointSaver: this.checkpointSaver,
|
|
228
|
+
threadId: sessionId,
|
|
229
|
+
});
|
|
230
|
+
let history = facing.messages;
|
|
231
|
+
let compressed = false;
|
|
218
232
|
const compressionOptions = await withCompressionSummaryModel(this.sessionCompression, () => this.graphLoader
|
|
219
233
|
? this.graphLoader.resolveDefaultModel(agentCode, req.agentVersion)
|
|
220
234
|
: Promise.resolve(undefined));
|
|
@@ -229,6 +243,9 @@ export class StreamOrchestrator {
|
|
|
229
243
|
history = step.messages;
|
|
230
244
|
continue;
|
|
231
245
|
}
|
|
246
|
+
if (step.kind === 'completed') {
|
|
247
|
+
compressed = step.payload.success === true;
|
|
248
|
+
}
|
|
232
249
|
emitter.emit(this.buildSSEEvent(step.kind === 'started' ? 'session.compression.started' : 'session.compression.completed', correlationId, taskId, { ...step.payload }));
|
|
233
250
|
}
|
|
234
251
|
history = await applySessionMemory({
|
|
@@ -239,11 +256,11 @@ export class StreamOrchestrator {
|
|
|
239
256
|
options: this.sessionMemory,
|
|
240
257
|
});
|
|
241
258
|
const tokenDetail = resolveTokenDetail(req.config?.tokenDetail);
|
|
242
|
-
const messages =
|
|
259
|
+
const { messages, replaceCheckpointMessages } = turnGraphMessagesAfterCompression({
|
|
243
260
|
history,
|
|
244
261
|
input: req.input,
|
|
245
|
-
|
|
246
|
-
|
|
262
|
+
fromCheckpoint: facing.fromCheckpoint,
|
|
263
|
+
compressed,
|
|
247
264
|
});
|
|
248
265
|
const skillInput = resolveSkillInputFromInvokeInput(req.input);
|
|
249
266
|
const assistantBuckets = createStreamAssistantBuckets();
|
|
@@ -253,8 +270,9 @@ export class StreamOrchestrator {
|
|
|
253
270
|
const toolResults = [];
|
|
254
271
|
// ─── Step 6: Graph.stream() 流式执行 ───
|
|
255
272
|
try {
|
|
256
|
-
const eventStream = this.graphEngine.stream(graph, {
|
|
273
|
+
const eventStream = runWithExecutionTrace({ abortSignal }, () => this.graphEngine.stream(graph, {
|
|
257
274
|
messages,
|
|
275
|
+
replaceCheckpointMessages,
|
|
258
276
|
userId,
|
|
259
277
|
delegationToken: executionContext.delegationToken,
|
|
260
278
|
agentCode,
|
|
@@ -275,7 +293,9 @@ export class StreamOrchestrator {
|
|
|
275
293
|
agentSystemPrompt,
|
|
276
294
|
traceId: resolveTraceId(req),
|
|
277
295
|
traceCollector: this.traceCollector,
|
|
278
|
-
|
|
296
|
+
abortSignal,
|
|
297
|
+
abortDeadlineMs: Date.now() + timeout,
|
|
298
|
+
}));
|
|
279
299
|
// 逐事件转发到 SSE;顺带把节点 token 记入 TokenStatsService(stream 原先未 record → 结尾全 0)
|
|
280
300
|
for await (const runtimeEvent of eventStream) {
|
|
281
301
|
// 连接已断开则提前退出
|
|
@@ -347,7 +367,7 @@ export class StreamOrchestrator {
|
|
|
347
367
|
// ─── 错误处理:Timeout ───
|
|
348
368
|
if (this.isTimeoutError(error)) {
|
|
349
369
|
await recordTaskTokenUsages(this.taskService, taskId, this.tokenStatsService.list(taskId));
|
|
350
|
-
await this.taskService.
|
|
370
|
+
await this.taskService.tryUpdateStatus(taskId, 'running', 'timeout');
|
|
351
371
|
emitter.emit(this.buildSSEEvent('task.error', correlationId, taskId, {
|
|
352
372
|
code: ErrorCodes.TASK_TIMEOUT,
|
|
353
373
|
message: 'Task execution timed out',
|
|
@@ -363,6 +383,9 @@ export class StreamOrchestrator {
|
|
|
363
383
|
this.emitErrorAndClose(emitter, correlationId, taskId, error);
|
|
364
384
|
return;
|
|
365
385
|
}
|
|
386
|
+
finally {
|
|
387
|
+
this.timeoutManager?.clearTimer(taskId);
|
|
388
|
+
}
|
|
366
389
|
// ─── Step 7: 发送结束事件序列 ───
|
|
367
390
|
// Emit: response.final(带完整 assistant 文本,供客户端兜底)
|
|
368
391
|
const tokenStats = this.tokenStatsService.getTaskStats(taskId);
|
|
@@ -454,10 +477,6 @@ export class StreamOrchestrator {
|
|
|
454
477
|
emitter.close();
|
|
455
478
|
}
|
|
456
479
|
isTimeoutError(error) {
|
|
457
|
-
|
|
458
|
-
const msg = error.message.toLowerCase();
|
|
459
|
-
return msg.includes('timeout') || msg.includes('timed out');
|
|
460
|
-
}
|
|
461
|
-
return false;
|
|
480
|
+
return error instanceof Error && /timed out/i.test(error.message);
|
|
462
481
|
}
|
|
463
482
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 生产环境由 Hosting 提供对外 HTTP;本模块供 `EAP_DEV_HTTP=1` 本地调试。
|
|
5
5
|
*/
|
|
6
|
+
import { classifyRuntimeError } from '../../utils/http-error.js';
|
|
6
7
|
// ─────────────────────────────────────────────
|
|
7
8
|
// Route Handlers
|
|
8
9
|
// ─────────────────────────────────────────────
|
|
@@ -85,18 +86,18 @@ export function createAgentRouteHandlers(deps) {
|
|
|
85
86
|
};
|
|
86
87
|
}
|
|
87
88
|
catch (error) {
|
|
88
|
-
// 错误处理
|
|
89
89
|
const duration = Date.now() - startTime;
|
|
90
|
-
const
|
|
90
|
+
const classified = classifyRuntimeError(error);
|
|
91
|
+
const output = classified.exposeMessage ? classified.message : '系统内部错误';
|
|
91
92
|
return {
|
|
92
|
-
code:
|
|
93
|
+
code: classified.code,
|
|
93
94
|
data: {
|
|
94
95
|
taskId: '',
|
|
95
96
|
sessionId: sessionId ?? '',
|
|
96
|
-
output
|
|
97
|
+
output,
|
|
97
98
|
tokenUsage: { input: 0, output: 0, total: 0 },
|
|
98
99
|
duration,
|
|
99
|
-
status: 'failed',
|
|
100
|
+
status: classified.code === 'runtime.task.timeout' ? 'timeout' : 'failed',
|
|
100
101
|
},
|
|
101
102
|
traceId: '',
|
|
102
103
|
};
|
|
@@ -12,7 +12,7 @@ function pickString(...values) {
|
|
|
12
12
|
return undefined;
|
|
13
13
|
}
|
|
14
14
|
function omitTraceCollector(record) {
|
|
15
|
-
const { traceCollector: _drop, ...rest } = record;
|
|
15
|
+
const { traceCollector: _drop, abortSignal: _abort, abortDeadlineMs: _deadline, ...rest } = record;
|
|
16
16
|
return rest;
|
|
17
17
|
}
|
|
18
18
|
/** 去掉不可序列化的 collector,再交给 Saver。 */
|
package/dist/common/types.d.ts
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* - detail: 每次 LLM 调用输出 stats.node_token_usage + 结束时汇总
|
|
13
13
|
*/
|
|
14
14
|
export type TokenDetailLevel = 'none' | 'summary' | 'detail';
|
|
15
|
+
/** 未写请求/图 timeout 时的整图缺省(毫秒)。20 分钟,须大于单次 LLM HTTP 缺省 180s。 */
|
|
16
|
+
export declare const DEFAULT_GRAPH_TIMEOUT_MS = 1200000;
|
|
15
17
|
/**
|
|
16
18
|
* Runtime 执行引擎配置
|
|
17
19
|
*
|
|
@@ -20,9 +22,11 @@ export type TokenDetailLevel = 'none' | 'summary' | 'detail';
|
|
|
20
22
|
export interface RuntimeConfig {
|
|
21
23
|
/** Token 统计输出粒度 */
|
|
22
24
|
tokenDetail: TokenDetailLevel;
|
|
23
|
-
/** Task
|
|
25
|
+
/** Task 执行超时时间(毫秒)。未写请求/图 timeout 时为 {@link DEFAULT_GRAPH_TIMEOUT_MS} */
|
|
24
26
|
timeout: number;
|
|
25
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated Runtime 忽略。图级 / 请求级步数不是限长手段。整图限长用 timeout。
|
|
29
|
+
*/
|
|
26
30
|
maxIterations: number;
|
|
27
31
|
/** SSE 心跳间隔(毫秒),默认 30000 */
|
|
28
32
|
heartbeatInterval: number;
|
package/dist/common/types.js
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
* - @gt-fe/eap-contracts 定义跨模块的公共协议(如 Task 实体、Session 实体、Agent Manifest)
|
|
6
6
|
* - 本文件定义 Runtime 执行引擎内部流转的配置、上下文、枚举等
|
|
7
7
|
*/
|
|
8
|
+
/** 未写请求/图 timeout 时的整图缺省(毫秒)。20 分钟,须大于单次 LLM HTTP 缺省 180s。 */
|
|
9
|
+
export const DEFAULT_GRAPH_TIMEOUT_MS = 1_200_000;
|
|
8
10
|
/** RuntimeConfig 默认值 */
|
|
9
11
|
export const DEFAULT_RUNTIME_CONFIG = {
|
|
10
12
|
tokenDetail: 'detail',
|
|
11
|
-
timeout:
|
|
13
|
+
timeout: DEFAULT_GRAPH_TIMEOUT_MS,
|
|
12
14
|
maxIterations: 10,
|
|
13
15
|
heartbeatInterval: 30000,
|
|
14
16
|
};
|
|
@@ -17,12 +17,14 @@ import type { IToolExecutor } from '../../tool/tool-executor.interface.js';
|
|
|
17
17
|
import type { IGovernanceClient } from '../../governance/governance-client.js';
|
|
18
18
|
import { type ConditionEvaluatorFn } from '../condition-evaluator.js';
|
|
19
19
|
import type { ExecutionInterceptors } from '../../execution/interceptors.js';
|
|
20
|
-
/** 图未写 `config.maxIterations` 时的 recursionLimit。`agent ⇄ tools` 一圈约 2,50 ≈ 24 次工具往返。 */
|
|
21
|
-
export declare const DEFAULT_GRAPH_RECURSION_LIMIT = 50;
|
|
22
20
|
/**
|
|
23
|
-
* LangGraph `recursionLimit
|
|
24
|
-
*
|
|
21
|
+
* 传给 LangGraph 的 `recursionLimit`:一轮调度(superstep)+1。
|
|
22
|
+
* 同一步里并行的多个节点只 +1。ReAct 的 llm 再 tool 大约 +2。
|
|
23
|
+
* 不读图级 / 请求级 `maxIterations`。整图运行时长用 `timeout`。
|
|
24
|
+
* 配了 Token 配额才会因预算停止。
|
|
25
25
|
*/
|
|
26
|
+
export declare const DEFAULT_GRAPH_RECURSION_LIMIT = 5000;
|
|
27
|
+
export declare function isGraphRecursionError(error: unknown): boolean;
|
|
26
28
|
/** 并行节点各自返回 [id] 时合并进同一路径;末项相同不重复。 */
|
|
27
29
|
export declare function mergeHistoryPath(current: unknown, update: unknown): string[];
|
|
28
30
|
/**
|