@gt-fe/eap-runtime 0.0.8
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 +323 -0
- package/dist/api/handlers/index.d.ts +3 -0
- package/dist/api/handlers/index.js +4 -0
- package/dist/api/handlers/invoke.handler.d.ts +77 -0
- package/dist/api/handlers/invoke.handler.js +411 -0
- package/dist/api/handlers/messages.handler.d.ts +53 -0
- package/dist/api/handlers/messages.handler.js +80 -0
- package/dist/api/handlers/stream.handler.d.ts +92 -0
- package/dist/api/handlers/stream.handler.js +432 -0
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +4 -0
- package/dist/api/middleware/auth.middleware.d.ts +96 -0
- package/dist/api/middleware/auth.middleware.js +158 -0
- package/dist/api/middleware/index.d.ts +1 -0
- package/dist/api/middleware/index.js +2 -0
- package/dist/api/routes/agent.routes.d.ts +44 -0
- package/dist/api/routes/agent.routes.js +204 -0
- package/dist/api/routes/index.d.ts +3 -0
- package/dist/api/routes/index.js +4 -0
- package/dist/api/routes/session.routes.d.ts +82 -0
- package/dist/api/routes/session.routes.js +169 -0
- package/dist/api/routes/task.routes.d.ts +75 -0
- package/dist/api/routes/task.routes.js +108 -0
- package/dist/checkpoint/checkpoint-serializer.d.ts +58 -0
- package/dist/checkpoint/checkpoint-serializer.js +81 -0
- package/dist/checkpoint/composite-checkpoint-saver.d.ts +42 -0
- package/dist/checkpoint/composite-checkpoint-saver.js +118 -0
- package/dist/checkpoint/contracts/checkpoint-saver.contract.d.ts +65 -0
- package/dist/checkpoint/contracts/checkpoint-saver.contract.js +12 -0
- package/dist/checkpoint/contracts/index.d.ts +1 -0
- package/dist/checkpoint/contracts/index.js +1 -0
- package/dist/checkpoint/file-checkpoint-saver.d.ts +19 -0
- package/dist/checkpoint/file-checkpoint-saver.js +98 -0
- package/dist/checkpoint/hybrid-checkpoint-saver.d.ts +130 -0
- package/dist/checkpoint/hybrid-checkpoint-saver.js +211 -0
- package/dist/checkpoint/in-memory-checkpoint-saver.d.ts +14 -0
- package/dist/checkpoint/in-memory-checkpoint-saver.js +46 -0
- package/dist/checkpoint/index.d.ts +16 -0
- package/dist/checkpoint/index.js +13 -0
- package/dist/checkpoint/mongo-checkpoint-saver.d.ts +38 -0
- package/dist/checkpoint/mongo-checkpoint-saver.js +56 -0
- package/dist/checkpoint/pg-checkpoint-saver.d.ts +59 -0
- package/dist/checkpoint/pg-checkpoint-saver.js +140 -0
- package/dist/checkpoint/redis-checkpoint-saver.d.ts +68 -0
- package/dist/checkpoint/redis-checkpoint-saver.js +173 -0
- package/dist/checkpoint/restore-checkpoint.d.ts +33 -0
- package/dist/checkpoint/restore-checkpoint.js +33 -0
- package/dist/common/agent-code.d.ts +10 -0
- package/dist/common/agent-code.js +12 -0
- package/dist/common/concurrency.d.ts +79 -0
- package/dist/common/concurrency.js +121 -0
- package/dist/common/health.d.ts +40 -0
- package/dist/common/health.js +37 -0
- package/dist/common/index.d.ts +5 -0
- package/dist/common/index.js +5 -0
- package/dist/common/logger.d.ts +34 -0
- package/dist/common/logger.js +38 -0
- package/dist/common/queue-consumer.d.ts +125 -0
- package/dist/common/queue-consumer.js +159 -0
- package/dist/common/trace-id.d.ts +2 -0
- package/dist/common/trace-id.js +8 -0
- package/dist/common/types.d.ts +104 -0
- package/dist/common/types.js +14 -0
- package/dist/contracts/index.d.ts +11 -0
- package/dist/contracts/index.js +10 -0
- package/dist/contracts/permission-checker.d.ts +10 -0
- package/dist/contracts/permission-checker.js +4 -0
- package/dist/contracts/trace-collector.d.ts +211 -0
- package/dist/contracts/trace-collector.js +13 -0
- package/dist/governance/governance-client.d.ts +38 -0
- package/dist/governance/governance-client.js +95 -0
- package/dist/governance/index.d.ts +7 -0
- package/dist/governance/index.js +5 -0
- package/dist/governance/permission-guard.d.ts +19 -0
- package/dist/governance/permission-guard.js +59 -0
- package/dist/graph/adapters/index.d.ts +3 -0
- package/dist/graph/adapters/index.js +3 -0
- package/dist/graph/adapters/langgraph-checkpointer.d.ts +40 -0
- package/dist/graph/adapters/langgraph-checkpointer.js +211 -0
- package/dist/graph/adapters/langgraph.adapter.d.ts +190 -0
- package/dist/graph/adapters/langgraph.adapter.js +1303 -0
- package/dist/graph/adapters/subgraph-compiler.d.ts +39 -0
- package/dist/graph/adapters/subgraph-compiler.js +248 -0
- package/dist/graph/condition-evaluator.d.ts +57 -0
- package/dist/graph/condition-evaluator.js +162 -0
- package/dist/graph/contracts/executable-graph.d.ts +340 -0
- package/dist/graph/contracts/executable-graph.js +10 -0
- package/dist/graph/contracts/index.d.ts +2 -0
- package/dist/graph/contracts/index.js +2 -0
- package/dist/graph/contracts/state-graph.contract.d.ts +76 -0
- package/dist/graph/contracts/state-graph.contract.js +11 -0
- package/dist/graph/hitl/hitl-handler.d.ts +199 -0
- package/dist/graph/hitl/hitl-handler.js +480 -0
- package/dist/graph/hitl/index.d.ts +1 -0
- package/dist/graph/hitl/index.js +1 -0
- package/dist/graph/index.d.ts +7 -0
- package/dist/graph/index.js +7 -0
- package/dist/graph/nodes/index.d.ts +2 -0
- package/dist/graph/nodes/index.js +2 -0
- package/dist/graph/nodes/llm-node.d.ts +182 -0
- package/dist/graph/nodes/llm-node.js +281 -0
- package/dist/graph/nodes/skill-bind-node.d.ts +32 -0
- package/dist/graph/nodes/skill-bind-node.js +116 -0
- package/dist/graph/nodes/tool-node.d.ts +75 -0
- package/dist/graph/nodes/tool-node.js +324 -0
- package/dist/graph/resolve-graph-prompt.d.ts +5 -0
- package/dist/graph/resolve-graph-prompt.js +75 -0
- package/dist/graph/retry/budget-aware-retry.d.ts +91 -0
- package/dist/graph/retry/budget-aware-retry.js +126 -0
- package/dist/graph/retry/index.d.ts +9 -0
- package/dist/graph/retry/index.js +8 -0
- package/dist/graph/retry/node-retry.d.ts +123 -0
- package/dist/graph/retry/node-retry.js +235 -0
- package/dist/graph/skill-audit-recorder.d.ts +126 -0
- package/dist/graph/skill-audit-recorder.js +114 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +32 -0
- package/dist/memory/compression/compression-stats.d.ts +109 -0
- package/dist/memory/compression/compression-stats.js +144 -0
- package/dist/memory/compression/compression.repository.d.ts +62 -0
- package/dist/memory/compression/compression.repository.js +71 -0
- package/dist/memory/compression/index.d.ts +10 -0
- package/dist/memory/compression/index.js +15 -0
- package/dist/memory/compression/llm-summarize.d.ts +58 -0
- package/dist/memory/compression/llm-summarize.js +192 -0
- package/dist/memory/compression/sliding-window.compression.d.ts +60 -0
- package/dist/memory/compression/sliding-window.compression.js +175 -0
- package/dist/memory/index.d.ts +4 -0
- package/dist/memory/index.js +7 -0
- package/dist/memory/long-term-memory.service.d.ts +94 -0
- package/dist/memory/long-term-memory.service.js +120 -0
- package/dist/memory/message-compression.service.d.ts +128 -0
- package/dist/memory/message-compression.service.js +69 -0
- package/dist/memory/pg-memory.repository.d.ts +44 -0
- package/dist/memory/pg-memory.repository.js +108 -0
- package/dist/memory/semantic-retrieval.service.d.ts +148 -0
- package/dist/memory/semantic-retrieval.service.js +238 -0
- package/dist/providers/echo-llm-provider.d.ts +10 -0
- package/dist/providers/echo-llm-provider.js +87 -0
- package/dist/providers/http-llm-provider.d.ts +35 -0
- package/dist/providers/http-llm-provider.js +404 -0
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.js +5 -0
- package/dist/providers/local-llm-provider.d.ts +27 -0
- package/dist/providers/local-llm-provider.js +105 -0
- package/dist/providers/resolve-llm-model.d.ts +13 -0
- package/dist/providers/resolve-llm-model.js +30 -0
- package/dist/providers/routing-llm-provider.d.ts +28 -0
- package/dist/providers/routing-llm-provider.js +79 -0
- package/dist/registry/agent-models.d.ts +1 -0
- package/dist/registry/agent-models.js +1 -0
- package/dist/registry/agent-registry-client.d.ts +56 -0
- package/dist/registry/agent-registry-client.js +36 -0
- package/dist/registry/definition-cache.d.ts +37 -0
- package/dist/registry/definition-cache.js +82 -0
- package/dist/registry/deployment-snapshot.d.ts +99 -0
- package/dist/registry/deployment-snapshot.js +542 -0
- package/dist/registry/graph-loader.d.ts +134 -0
- package/dist/registry/graph-loader.js +985 -0
- package/dist/registry/index.d.ts +9 -0
- package/dist/registry/index.js +9 -0
- package/dist/registry/preload-errors.d.ts +13 -0
- package/dist/registry/preload-errors.js +35 -0
- package/dist/registry/preload-flags.d.ts +22 -0
- package/dist/registry/preload-flags.js +44 -0
- package/dist/registry/skill-io-guard.d.ts +25 -0
- package/dist/registry/skill-io-guard.js +98 -0
- package/dist/registry/skill-tree.d.ts +27 -0
- package/dist/registry/skill-tree.js +183 -0
- package/dist/runtime/agent-runtime.d.ts +106 -0
- package/dist/runtime/agent-runtime.js +777 -0
- package/dist/runtime/dev-runtime.d.ts +69 -0
- package/dist/runtime/dev-runtime.js +199 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/storage-profile.d.ts +50 -0
- package/dist/runtime/storage-profile.js +138 -0
- package/dist/runtime/types.d.ts +120 -0
- package/dist/runtime/types.js +9 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.js +161 -0
- package/dist/session/compression-middleware.d.ts +126 -0
- package/dist/session/compression-middleware.js +230 -0
- package/dist/session/file-message.repository.d.ts +25 -0
- package/dist/session/file-message.repository.js +78 -0
- package/dist/session/file-session.repository.d.ts +72 -0
- package/dist/session/file-session.repository.js +212 -0
- package/dist/session/index.d.ts +13 -0
- package/dist/session/index.js +12 -0
- package/dist/session/message.service.d.ts +80 -0
- package/dist/session/message.service.js +128 -0
- package/dist/session/persist-turn-messages.d.ts +145 -0
- package/dist/session/persist-turn-messages.js +325 -0
- package/dist/session/pg-message.repository.d.ts +49 -0
- package/dist/session/pg-message.repository.js +149 -0
- package/dist/session/pg-session.repository.d.ts +46 -0
- package/dist/session/pg-session.repository.js +117 -0
- package/dist/session/session-context.service.d.ts +49 -0
- package/dist/session/session-context.service.js +66 -0
- package/dist/session/session.service.d.ts +194 -0
- package/dist/session/session.service.js +166 -0
- package/dist/sse/connection-event-logger.d.ts +21 -0
- package/dist/sse/connection-event-logger.js +33 -0
- package/dist/sse/contracts/index.d.ts +1 -0
- package/dist/sse/contracts/index.js +1 -0
- package/dist/sse/contracts/sse-emitter.contract.d.ts +74 -0
- package/dist/sse/contracts/sse-emitter.contract.js +11 -0
- package/dist/sse/event-buffer.d.ts +20 -0
- package/dist/sse/event-buffer.js +45 -0
- package/dist/sse/events/code-events.d.ts +19 -0
- package/dist/sse/events/code-events.js +9 -0
- package/dist/sse/events/collaboration-events.d.ts +37 -0
- package/dist/sse/events/collaboration-events.js +11 -0
- package/dist/sse/events/connection-events.d.ts +13 -0
- package/dist/sse/events/connection-events.js +9 -0
- package/dist/sse/events/file-events.d.ts +29 -0
- package/dist/sse/events/file-events.js +10 -0
- package/dist/sse/events/hitl-events.d.ts +33 -0
- package/dist/sse/events/hitl-events.js +10 -0
- package/dist/sse/events/index.d.ts +107 -0
- package/dist/sse/events/index.js +96 -0
- package/dist/sse/events/plan-events.d.ts +38 -0
- package/dist/sse/events/plan-events.js +11 -0
- package/dist/sse/events/reasoning-events.d.ts +35 -0
- package/dist/sse/events/reasoning-events.js +13 -0
- package/dist/sse/events/response-events.d.ts +25 -0
- package/dist/sse/events/response-events.js +12 -0
- package/dist/sse/events/session-events.d.ts +43 -0
- package/dist/sse/events/session-events.js +12 -0
- package/dist/sse/events/skill-events.d.ts +48 -0
- package/dist/sse/events/skill-events.js +11 -0
- package/dist/sse/events/stats-events.d.ts +44 -0
- package/dist/sse/events/stats-events.js +12 -0
- package/dist/sse/events/tool-events.d.ts +68 -0
- package/dist/sse/events/tool-events.js +14 -0
- package/dist/sse/index.d.ts +8 -0
- package/dist/sse/index.js +8 -0
- package/dist/sse/sse-connection-manager.d.ts +116 -0
- package/dist/sse/sse-connection-manager.js +185 -0
- package/dist/sse/sse-emitter.d.ts +108 -0
- package/dist/sse/sse-emitter.js +210 -0
- package/dist/sse/sse-heartbeat.d.ts +58 -0
- package/dist/sse/sse-heartbeat.js +76 -0
- package/dist/sse/trace-collector.d.ts +60 -0
- package/dist/sse/trace-collector.js +107 -0
- package/dist/task/contracts/index.d.ts +2 -0
- package/dist/task/contracts/index.js +1 -0
- package/dist/task/contracts/task-manager.contract.d.ts +160 -0
- package/dist/task/contracts/task-manager.contract.js +10 -0
- package/dist/task/index.d.ts +7 -0
- package/dist/task/index.js +5 -0
- package/dist/task/pg-task.repository.d.ts +47 -0
- package/dist/task/pg-task.repository.js +131 -0
- package/dist/task/task-progress-tracker.d.ts +67 -0
- package/dist/task/task-progress-tracker.js +80 -0
- package/dist/task/task-timeout.d.ts +70 -0
- package/dist/task/task-timeout.js +116 -0
- package/dist/task/task.service.d.ts +84 -0
- package/dist/task/task.service.js +233 -0
- package/dist/token/compression-budget-integration.d.ts +145 -0
- package/dist/token/compression-budget-integration.js +185 -0
- package/dist/token/context-window-budget.d.ts +113 -0
- package/dist/token/context-window-budget.js +134 -0
- package/dist/token/contracts/index.d.ts +1 -0
- package/dist/token/contracts/index.js +1 -0
- package/dist/token/contracts/token-budget.contract.d.ts +77 -0
- package/dist/token/contracts/token-budget.contract.js +11 -0
- package/dist/token/index.d.ts +12 -0
- package/dist/token/index.js +10 -0
- package/dist/token/pg-token-budget.repository.d.ts +39 -0
- package/dist/token/pg-token-budget.repository.js +116 -0
- package/dist/token/session-budget-checker.d.ts +54 -0
- package/dist/token/session-budget-checker.js +72 -0
- package/dist/token/token-budget-guard.d.ts +66 -0
- package/dist/token/token-budget-guard.js +97 -0
- package/dist/token/token-budget.service.d.ts +86 -0
- package/dist/token/token-budget.service.js +130 -0
- package/dist/token/token-detail-mode.d.ts +73 -0
- package/dist/token/token-detail-mode.js +107 -0
- package/dist/token/token-estimator.d.ts +80 -0
- package/dist/token/token-estimator.js +134 -0
- package/dist/token/token-stats.service.d.ts +88 -0
- package/dist/token/token-stats.service.js +94 -0
- package/dist/tool/create-tool-executor.d.ts +33 -0
- package/dist/tool/create-tool-executor.js +60 -0
- package/dist/tool/dev-http-executor.d.ts +18 -0
- package/dist/tool/dev-http-executor.js +96 -0
- package/dist/tool/dev-mcp-executor.d.ts +29 -0
- package/dist/tool/dev-mcp-executor.js +84 -0
- package/dist/tool/dev-tool-executor.d.ts +23 -0
- package/dist/tool/dev-tool-executor.js +55 -0
- package/dist/tool/file-artifact.d.ts +11 -0
- package/dist/tool/file-artifact.js +90 -0
- package/dist/tool/http-tool-executor.d.ts +17 -0
- package/dist/tool/http-tool-executor.js +158 -0
- package/dist/tool/index.d.ts +18 -0
- package/dist/tool/index.js +17 -0
- package/dist/tool/loaded-tool-definition.d.ts +35 -0
- package/dist/tool/loaded-tool-definition.js +9 -0
- package/dist/tool/local-mcp-client.d.ts +23 -0
- package/dist/tool/local-mcp-client.js +225 -0
- package/dist/tool/local-tool-executor.d.ts +37 -0
- package/dist/tool/local-tool-executor.js +249 -0
- package/dist/tool/mcp-tool-executor.d.ts +23 -0
- package/dist/tool/mcp-tool-executor.js +160 -0
- package/dist/tool/mock-tool-executor.d.ts +28 -0
- package/dist/tool/mock-tool-executor.js +65 -0
- package/dist/tool/system-prompt-builder.d.ts +114 -0
- package/dist/tool/system-prompt-builder.js +213 -0
- package/dist/tool/tool-confirmation.d.ts +29 -0
- package/dist/tool/tool-confirmation.js +81 -0
- package/dist/tool/tool-event-emitter.d.ts +58 -0
- package/dist/tool/tool-event-emitter.js +154 -0
- package/dist/tool/tool-execution-router.d.ts +25 -0
- package/dist/tool/tool-execution-router.js +50 -0
- package/dist/tool/tool-executor.interface.d.ts +85 -0
- package/dist/tool/tool-executor.interface.js +7 -0
- package/dist/tool/tool-orchestrator.d.ts +22 -0
- package/dist/tool/tool-orchestrator.js +104 -0
- package/dist/tool/tool-result-injector.d.ts +64 -0
- package/dist/tool/tool-result-injector.js +65 -0
- package/dist/utils/format-error.d.ts +4 -0
- package/dist/utils/format-error.js +4 -0
- package/dist/utils/http-error.d.ts +11 -0
- package/dist/utils/http-error.js +24 -0
- package/package.json +56 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InvokeOrchestrator — 同步调用编排器
|
|
3
|
+
*
|
|
4
|
+
* 实现 POST /api/v1/agents/:agentCode/invoke 的核心编排逻辑:
|
|
5
|
+
* 1. 检查 Session 日预算(SessionBudgetChecker)
|
|
6
|
+
* 2. 解析/创建 Session 上下文(SessionContextService)
|
|
7
|
+
* 3. 创建 Task(TaskService)
|
|
8
|
+
* 4. 启动 Task(pending → running)
|
|
9
|
+
* 5. 构建/执行 Graph(IStateGraphEngine)
|
|
10
|
+
* 6. 记录 Token 消耗(TokenStatsService + TaskService)
|
|
11
|
+
* 7. 完成 Task(running → success)
|
|
12
|
+
* 8. 返回 InvokeResponse
|
|
13
|
+
*
|
|
14
|
+
* @see design.md §1.5 /stream 内部执行逻辑
|
|
15
|
+
* @see requirements.md Requirement 11 AC1-AC2
|
|
16
|
+
*/
|
|
17
|
+
import { randomUUID } from 'node:crypto';
|
|
18
|
+
import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, } from '../../session/persist-turn-messages.js';
|
|
19
|
+
import { SessionBudgetExhaustedError } from '../../token/session-budget-checker.js';
|
|
20
|
+
import { TokenBudgetExhaustedError } from '../../token/token-budget-guard.js';
|
|
21
|
+
import { resolveExecutionContext } from '../../governance/governance-client.js';
|
|
22
|
+
import { resolveSkillInputFromInvokeInput } from '../../registry/skill-io-guard.js';
|
|
23
|
+
import { toLoadedToolMap } from '../../tool/loaded-tool-definition.js';
|
|
24
|
+
import { toLoadedSkillMap, mergeSkillScriptTools } from '../../tool/system-prompt-builder.js';
|
|
25
|
+
import { resolveAgentCode } from '../../common/agent-code.js';
|
|
26
|
+
import { unwrapHitlPaused } from '../../graph/hitl/hitl-handler.js';
|
|
27
|
+
import { resolveTraceId } from '../../common/trace-id.js';
|
|
28
|
+
// ─────────────────────────────────────────────
|
|
29
|
+
// Default Graph DSL fallback
|
|
30
|
+
// ─────────────────────────────────────────────
|
|
31
|
+
const DEFAULT_GRAPH_DSL = {
|
|
32
|
+
name: 'default-graph',
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
state: { messages: 'array' },
|
|
35
|
+
nodes: [
|
|
36
|
+
{ id: 'llm', type: 'llm', config: {} },
|
|
37
|
+
],
|
|
38
|
+
edges: [
|
|
39
|
+
{ from: '__start__', to: 'llm' },
|
|
40
|
+
{ from: 'llm', to: '__end__' },
|
|
41
|
+
],
|
|
42
|
+
config: { timeout: 60000 },
|
|
43
|
+
};
|
|
44
|
+
// ─────────────────────────────────────────────
|
|
45
|
+
// InvokeOrchestrator
|
|
46
|
+
// ─────────────────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* InvokeOrchestrator — 同步调用全流程编排
|
|
49
|
+
*
|
|
50
|
+
* 协调 SessionContextService、TaskService、IStateGraphEngine、TokenStatsService
|
|
51
|
+
* 等服务完成一次同步 Agent 调用的完整生命周期。
|
|
52
|
+
*/
|
|
53
|
+
export class InvokeOrchestrator {
|
|
54
|
+
sessionContextService;
|
|
55
|
+
taskService;
|
|
56
|
+
graphEngine;
|
|
57
|
+
tokenStatsService;
|
|
58
|
+
tokenBudgetGuard;
|
|
59
|
+
sessionBudgetChecker;
|
|
60
|
+
messageService;
|
|
61
|
+
graphLoader;
|
|
62
|
+
checkpointSaver;
|
|
63
|
+
traceCollector;
|
|
64
|
+
constructor(deps) {
|
|
65
|
+
this.sessionContextService = deps.sessionContextService;
|
|
66
|
+
this.taskService = deps.taskService;
|
|
67
|
+
this.graphEngine = deps.graphEngine;
|
|
68
|
+
this.tokenStatsService = deps.tokenStatsService;
|
|
69
|
+
this.tokenBudgetGuard = deps.tokenBudgetGuard;
|
|
70
|
+
this.sessionBudgetChecker = deps.sessionBudgetChecker;
|
|
71
|
+
this.messageService = deps.messageService;
|
|
72
|
+
this.graphLoader = deps.graphLoader;
|
|
73
|
+
this.checkpointSaver = deps.checkpointSaver;
|
|
74
|
+
this.traceCollector = deps.traceCollector;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 执行同步调用编排
|
|
78
|
+
*
|
|
79
|
+
* @param req - 解析后的调用请求
|
|
80
|
+
* @param userId - 经认证的用户 ID
|
|
81
|
+
* @returns 完整的 InvokeResponse(含 taskId, sessionId, output, tokenUsage, duration, status)
|
|
82
|
+
*/
|
|
83
|
+
async execute(req, userId) {
|
|
84
|
+
const startTime = Date.now();
|
|
85
|
+
const agentCode = resolveAgentCode(req);
|
|
86
|
+
const ctx = resolveExecutionContext({ ...req, agentCode }, userId);
|
|
87
|
+
const correlationId = ctx.correlationId;
|
|
88
|
+
const traceId = randomUUID();
|
|
89
|
+
const effectiveUserId = ctx.userId ?? userId;
|
|
90
|
+
// ─── Step 1: 检查 Session 日预算 ───
|
|
91
|
+
// 若已有 sessionId,先检查是否预算耗尽
|
|
92
|
+
if (req.sessionId) {
|
|
93
|
+
try {
|
|
94
|
+
await this.sessionBudgetChecker.assertBudgetAvailable(req.sessionId);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (error instanceof SessionBudgetExhaustedError) {
|
|
98
|
+
return this.buildErrorResponse(traceId, {
|
|
99
|
+
taskId: '',
|
|
100
|
+
sessionId: req.sessionId,
|
|
101
|
+
output: error.message,
|
|
102
|
+
tokenUsage: { input: 0, output: 0, total: 0 },
|
|
103
|
+
duration: Date.now() - startTime,
|
|
104
|
+
status: 'failed',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// ─── Step 2: 解析/创建 Session 上下文 ───
|
|
111
|
+
const sessionContext = await this.sessionContextService.resolveSessionContext({
|
|
112
|
+
sessionId: req.sessionId,
|
|
113
|
+
userId: effectiveUserId,
|
|
114
|
+
agentId: agentCode,
|
|
115
|
+
agentVersion: req.agentVersion,
|
|
116
|
+
});
|
|
117
|
+
const sessionId = sessionContext.session.id;
|
|
118
|
+
// 新创建的 Session 也需要检查预算(边界情况:用户全局日预算已耗尽)
|
|
119
|
+
if (!req.sessionId) {
|
|
120
|
+
try {
|
|
121
|
+
await this.sessionBudgetChecker.assertBudgetAvailable(sessionId);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (error instanceof SessionBudgetExhaustedError) {
|
|
125
|
+
return this.buildErrorResponse(traceId, {
|
|
126
|
+
taskId: '',
|
|
127
|
+
sessionId,
|
|
128
|
+
output: error.message,
|
|
129
|
+
tokenUsage: { input: 0, output: 0, total: 0 },
|
|
130
|
+
duration: Date.now() - startTime,
|
|
131
|
+
status: 'failed',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// ─── Step 3: 创建 Task(Hosting 传入 taskId 时复用)───
|
|
138
|
+
const timeout = req.config?.timeout ?? 60000;
|
|
139
|
+
const task = await this.taskService.create({
|
|
140
|
+
id: req.taskId,
|
|
141
|
+
agentId: agentCode,
|
|
142
|
+
sessionId,
|
|
143
|
+
userId: effectiveUserId,
|
|
144
|
+
correlationId,
|
|
145
|
+
input: req.input,
|
|
146
|
+
timeout,
|
|
147
|
+
});
|
|
148
|
+
// ─── Step 4: 启动 Task (pending → running) ───
|
|
149
|
+
await this.taskService.updateStatus(task.id, 'running');
|
|
150
|
+
// ─── Step 4b: 持久化本轮 user(多轮短期记忆)───
|
|
151
|
+
await persistUserTurnMessage(this.messageService, {
|
|
152
|
+
sessionId,
|
|
153
|
+
taskId: task.id,
|
|
154
|
+
correlationId,
|
|
155
|
+
content: req.input,
|
|
156
|
+
});
|
|
157
|
+
// ─── Step 5: 构建/执行 Graph ───
|
|
158
|
+
let graphResult;
|
|
159
|
+
try {
|
|
160
|
+
// 构建图 — 每次请求创建独立 StateGraph 实例,确保请求间无共享可变状态
|
|
161
|
+
const loadOpts = { agentVersion: req.agentVersion };
|
|
162
|
+
const graphDsl = this.graphLoader
|
|
163
|
+
? await this.graphLoader.loadGraph(agentCode, loadOpts)
|
|
164
|
+
: DEFAULT_GRAPH_DSL;
|
|
165
|
+
const graph = await this.graphEngine.buildGraph(graphDsl);
|
|
166
|
+
const loadedToolsBase = this.graphLoader
|
|
167
|
+
? toLoadedToolMap(await this.graphLoader.loadToolDefinitions(agentCode, loadOpts))
|
|
168
|
+
: undefined;
|
|
169
|
+
const loadedSkills = this.graphLoader
|
|
170
|
+
? await this.graphLoader.loadSkillDefinitions(agentCode, {
|
|
171
|
+
...loadOpts,
|
|
172
|
+
userId: effectiveUserId,
|
|
173
|
+
})
|
|
174
|
+
: undefined;
|
|
175
|
+
const skillDefinitions = toLoadedSkillMap(loadedSkills);
|
|
176
|
+
// Skill scripts 虚拟 Tool 并入 loadedTools(同名不覆盖平台 Tool)
|
|
177
|
+
const loadedTools = mergeSkillScriptTools(loadedToolsBase, loadedSkills);
|
|
178
|
+
const promptRoots = this.graphLoader
|
|
179
|
+
? await this.graphLoader.resolvePromptRoots(agentCode, req.agentVersion)
|
|
180
|
+
: [];
|
|
181
|
+
// 组装 GraphState:有 Checkpoint 历史则只追加本轮 user,避免双重 append
|
|
182
|
+
const history = messageRecordsToGraphMessages(sessionContext.messages);
|
|
183
|
+
const messages = await buildTurnGraphMessages({
|
|
184
|
+
history,
|
|
185
|
+
input: req.input,
|
|
186
|
+
checkpointSaver: this.checkpointSaver,
|
|
187
|
+
threadId: sessionId,
|
|
188
|
+
});
|
|
189
|
+
const skillInput = resolveSkillInputFromInvokeInput(req.input);
|
|
190
|
+
// 同步执行图
|
|
191
|
+
graphResult = await this.graphEngine.invoke(graph, {
|
|
192
|
+
messages,
|
|
193
|
+
userId: effectiveUserId,
|
|
194
|
+
delegationToken: ctx.delegationToken,
|
|
195
|
+
agentCode,
|
|
196
|
+
agentVersion: req.agentVersion,
|
|
197
|
+
correlationId,
|
|
198
|
+
taskId: task.id,
|
|
199
|
+
sessionId,
|
|
200
|
+
executionContext: ctx,
|
|
201
|
+
forceToolServer: req.config?.forceToolServer,
|
|
202
|
+
loadedTools,
|
|
203
|
+
loadedSkills,
|
|
204
|
+
skillDefinitions,
|
|
205
|
+
skillInput,
|
|
206
|
+
promptRoots,
|
|
207
|
+
traceId: resolveTraceId(req),
|
|
208
|
+
traceCollector: this.traceCollector,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
const hitlPaused = unwrapHitlPaused(error);
|
|
213
|
+
if (hitlPaused) {
|
|
214
|
+
await this.taskService.updateStatus(task.id, 'waiting_hitl', buildHitlWaitingTaskOutput({
|
|
215
|
+
threadId: hitlPaused.threadId,
|
|
216
|
+
question: hitlPaused.question,
|
|
217
|
+
options: hitlPaused.options,
|
|
218
|
+
}));
|
|
219
|
+
await persistHitlWaitingPlaceholder(this.messageService, {
|
|
220
|
+
sessionId,
|
|
221
|
+
taskId: task.id,
|
|
222
|
+
correlationId,
|
|
223
|
+
question: hitlPaused.question,
|
|
224
|
+
options: hitlPaused.options,
|
|
225
|
+
threadId: hitlPaused.threadId,
|
|
226
|
+
});
|
|
227
|
+
return this.buildResponse(traceId, {
|
|
228
|
+
taskId: task.id,
|
|
229
|
+
sessionId,
|
|
230
|
+
output: hitlPaused.question,
|
|
231
|
+
tokenUsage: { input: 0, output: 0, total: 0 },
|
|
232
|
+
duration: Date.now() - startTime,
|
|
233
|
+
status: 'waiting_hitl',
|
|
234
|
+
hitl: {
|
|
235
|
+
question: hitlPaused.question,
|
|
236
|
+
options: hitlPaused.options,
|
|
237
|
+
threadId: hitlPaused.threadId,
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
// ─── 错误处理:TokenBudgetExhausted ───
|
|
242
|
+
if (error instanceof TokenBudgetExhaustedError) {
|
|
243
|
+
await this.taskService.updateStatus(task.id, 'token_exhausted', error.message);
|
|
244
|
+
return this.buildResponse(traceId, {
|
|
245
|
+
taskId: task.id,
|
|
246
|
+
sessionId,
|
|
247
|
+
output: error.message,
|
|
248
|
+
tokenUsage: { input: 0, output: 0, total: 0 },
|
|
249
|
+
duration: Date.now() - startTime,
|
|
250
|
+
status: 'token_exhausted',
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
// ─── 错误处理:Timeout ───
|
|
254
|
+
if (this.isTimeoutError(error)) {
|
|
255
|
+
await this.taskService.updateStatus(task.id, 'timeout');
|
|
256
|
+
return this.buildResponse(traceId, {
|
|
257
|
+
taskId: task.id,
|
|
258
|
+
sessionId,
|
|
259
|
+
output: 'Task execution timed out',
|
|
260
|
+
tokenUsage: { input: 0, output: 0, total: 0 },
|
|
261
|
+
duration: Date.now() - startTime,
|
|
262
|
+
status: 'timeout',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// ─── 错误处理:通用错误 ───
|
|
266
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
267
|
+
await this.taskService.updateStatus(task.id, 'failed', errorMessage);
|
|
268
|
+
return this.buildErrorResponse(traceId, {
|
|
269
|
+
taskId: task.id,
|
|
270
|
+
sessionId,
|
|
271
|
+
output: errorMessage,
|
|
272
|
+
tokenUsage: { input: 0, output: 0, total: 0 },
|
|
273
|
+
duration: Date.now() - startTime,
|
|
274
|
+
status: 'failed',
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// ─── Step 6: 记录 Token 消耗 ───
|
|
278
|
+
//
|
|
279
|
+
// 合并两个来源:
|
|
280
|
+
// 1. tokenStatsService:按 task 累计的 token(可能多节点累加)
|
|
281
|
+
// 2. graphResult.tokenUsage:图执行返回的 token(含 perNode 明细 + total 累计)
|
|
282
|
+
//
|
|
283
|
+
// 优先使用 graphResult(它已含多轮 LLM 累加后的 total + perNode 明细)。
|
|
284
|
+
// 注:早期 `||` 在 0 值时会错误回退,此处改为 `??` 并优先 graphResult。
|
|
285
|
+
const tokenStats = this.tokenStatsService.getTaskStats(task.id);
|
|
286
|
+
const graphUsage = graphResult.tokenUsage;
|
|
287
|
+
const tokenUsage = {
|
|
288
|
+
input: graphUsage.input > 0 ? graphUsage.input : (tokenStats.total.input ?? 0),
|
|
289
|
+
output: graphUsage.output > 0 ? graphUsage.output : (tokenStats.total.output ?? 0),
|
|
290
|
+
total: graphUsage.total > 0 ? graphUsage.total : (tokenStats.total.total ?? 0),
|
|
291
|
+
perNode: graphUsage.perNode,
|
|
292
|
+
// 会话累计:当前请求前历史消息 tokenCount 之和 + 本次请求消耗
|
|
293
|
+
// 注:历史 tokenCount 是输入/输出未区分的粗粒度值,故 conversationTotal.input/output 是近似值
|
|
294
|
+
conversationTotal: (() => {
|
|
295
|
+
const historyTokens = sessionContext.messages.reduce((sum, m) => sum + (m.tokenCount ?? 0), 0);
|
|
296
|
+
const total = historyTokens + (graphUsage.total || tokenStats.total.total || 0);
|
|
297
|
+
return {
|
|
298
|
+
input: historyTokens + graphUsage.input,
|
|
299
|
+
output: graphUsage.output,
|
|
300
|
+
total,
|
|
301
|
+
};
|
|
302
|
+
})(),
|
|
303
|
+
};
|
|
304
|
+
// 将 Graph 层面的 token 消耗记录到 Task(按节点明细逐个记录,便于审计)
|
|
305
|
+
if (graphUsage.perNode && graphUsage.perNode.length > 0) {
|
|
306
|
+
for (const u of graphUsage.perNode) {
|
|
307
|
+
await this.taskService.recordTokenUsage(task.id, {
|
|
308
|
+
nodeId: u.nodeId,
|
|
309
|
+
model: u.model,
|
|
310
|
+
inputTokens: u.inputTokens,
|
|
311
|
+
outputTokens: u.outputTokens,
|
|
312
|
+
costEstimate: 0,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else if (graphUsage.total > 0) {
|
|
317
|
+
await this.taskService.recordTokenUsage(task.id, {
|
|
318
|
+
nodeId: 'graph',
|
|
319
|
+
model: 'unknown',
|
|
320
|
+
inputTokens: graphUsage.input,
|
|
321
|
+
outputTokens: graphUsage.output,
|
|
322
|
+
costEstimate: 0,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
// ─── Step 7: 完成 Task (running → success) ───
|
|
326
|
+
await this.taskService.updateStatus(task.id, 'success', graphResult.output);
|
|
327
|
+
// ─── Step 7b: 持久化本轮 transcript(统一顺序:assistant tool_calls → tool → assistant 文本)───
|
|
328
|
+
const initialMessagesCount = sessionContext.messages.length + 1; // history + 本轮 user
|
|
329
|
+
const allMessages = Array.isArray(graphResult.finalState?.messages)
|
|
330
|
+
? graphResult.finalState.messages
|
|
331
|
+
: [];
|
|
332
|
+
const newMessages = allMessages.slice(initialMessagesCount);
|
|
333
|
+
await persistTurnTranscript(this.messageService, {
|
|
334
|
+
sessionId,
|
|
335
|
+
taskId: task.id,
|
|
336
|
+
correlationId,
|
|
337
|
+
messages: newMessages,
|
|
338
|
+
fallbackAssistantContent: graphResult.output ?? '',
|
|
339
|
+
assistantTokenCount: tokenUsage.output,
|
|
340
|
+
});
|
|
341
|
+
// ─── Step 8: 返回 InvokeResponse ───
|
|
342
|
+
const duration = Date.now() - startTime;
|
|
343
|
+
// 从本轮 Graph 执行结果中提取 tool 事件明细(供 CLI / UI 渲染 ReAct 循环)
|
|
344
|
+
const toolEvents = extractToolEvents(newMessages);
|
|
345
|
+
return this.buildResponse(traceId, {
|
|
346
|
+
taskId: task.id,
|
|
347
|
+
sessionId,
|
|
348
|
+
output: graphResult.output,
|
|
349
|
+
tokenUsage,
|
|
350
|
+
duration,
|
|
351
|
+
status: 'success',
|
|
352
|
+
toolEvents: toolEvents.length > 0 ? toolEvents : undefined,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
// ─────────────────────────────────────────────
|
|
356
|
+
// Private Helpers
|
|
357
|
+
// ─────────────────────────────────────────────
|
|
358
|
+
buildResponse(traceId, data) {
|
|
359
|
+
return { code: 'ok', data, traceId };
|
|
360
|
+
}
|
|
361
|
+
buildErrorResponse(traceId, data) {
|
|
362
|
+
return { code: data.status === 'token_exhausted' ? 'runtime.budget.task_exhausted' : 'runtime.graph.execution_error', data, traceId };
|
|
363
|
+
}
|
|
364
|
+
isTimeoutError(error) {
|
|
365
|
+
if (error instanceof Error) {
|
|
366
|
+
const msg = error.message.toLowerCase();
|
|
367
|
+
return msg.includes('timeout') || msg.includes('timed out');
|
|
368
|
+
}
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// ─────────────────────────────────────────────
|
|
373
|
+
// Module-level Helpers
|
|
374
|
+
// ─────────────────────────────────────────────
|
|
375
|
+
/**
|
|
376
|
+
* 从本轮新增的 GraphMessage 中提取 tool.called / tool.result 事件
|
|
377
|
+
*
|
|
378
|
+
* 扫描顺序:按 messages 数组原始顺序(即 Graph 执行的时间顺序)。
|
|
379
|
+
* - assistant 消息含 toolCalls → 生成一条或多条 kind='calling' 事件
|
|
380
|
+
* - tool 角色消息 → 生成 kind='result' 事件,content 截断到 500 字符
|
|
381
|
+
*/
|
|
382
|
+
const TOOL_RESULT_PREVIEW_MAX = 500;
|
|
383
|
+
function extractToolEvents(newMessages) {
|
|
384
|
+
const events = [];
|
|
385
|
+
for (const msg of newMessages) {
|
|
386
|
+
if (msg.role === 'assistant' && Array.isArray(msg.toolCalls) && msg.toolCalls.length > 0) {
|
|
387
|
+
for (const tc of msg.toolCalls) {
|
|
388
|
+
events.push({
|
|
389
|
+
kind: 'calling',
|
|
390
|
+
toolName: tc.name ?? 'unknown',
|
|
391
|
+
arguments: (tc.arguments ?? {}),
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
else if (msg.role === 'tool') {
|
|
396
|
+
const rawContent = typeof msg.content === 'string' ? msg.content : '';
|
|
397
|
+
const truncated = rawContent.length > TOOL_RESULT_PREVIEW_MAX
|
|
398
|
+
? rawContent.slice(0, TOOL_RESULT_PREVIEW_MAX) + '…'
|
|
399
|
+
: rawContent;
|
|
400
|
+
events.push({
|
|
401
|
+
kind: 'result',
|
|
402
|
+
toolName: msg.toolName ?? 'tool',
|
|
403
|
+
content: truncated,
|
|
404
|
+
success: msg.success !== false,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return events;
|
|
409
|
+
}
|
|
410
|
+
/** @deprecated 请直接从 `graph/hitl/hitl-handler` 导入;此处保留 re-export 兼容旧测试引用 */
|
|
411
|
+
export { unwrapHitlPaused } from '../../graph/hitl/hitl-handler.js';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MessagesOrchestrator — 会话消息编排器
|
|
3
|
+
*
|
|
4
|
+
* 实现多轮对话核心逻辑:
|
|
5
|
+
* - POST /api/v1/sessions/:sessionId/messages: 在已有会话中发送新消息,触发 Agent 执行
|
|
6
|
+
* - GET /api/v1/sessions/:sessionId/messages: 获取会话消息历史
|
|
7
|
+
*
|
|
8
|
+
* 编排步骤(postMessage):
|
|
9
|
+
* 1. 验证 Session 存在(SessionService.getSession)
|
|
10
|
+
* 2. 委托 InvokeOrchestrator.execute()(主路径已写回 user/assistant Message)
|
|
11
|
+
* 3. 返回 InvokeResponse
|
|
12
|
+
*
|
|
13
|
+
* @see design.md §1.5 多轮对话
|
|
14
|
+
* @see requirements.md Requirement 11 AC3, AC5, AC10
|
|
15
|
+
*/
|
|
16
|
+
import type { SessionService } from '../../session/session.service.js';
|
|
17
|
+
import type { MessageService } from '../../session/message.service.js';
|
|
18
|
+
import type { InvokeOrchestrator } from './invoke.handler.js';
|
|
19
|
+
import type { PostMessageRequest, PostMessageResponse, GetMessagesRequest, GetMessagesResponse } from '../routes/session.routes.js';
|
|
20
|
+
/**
|
|
21
|
+
* MessagesOrchestrator 构造器依赖
|
|
22
|
+
*/
|
|
23
|
+
export interface MessagesOrchestratorDeps {
|
|
24
|
+
sessionService: SessionService;
|
|
25
|
+
messageService: MessageService;
|
|
26
|
+
invokeOrchestrator: InvokeOrchestrator;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* MessagesOrchestrator — 会话消息编排
|
|
30
|
+
*
|
|
31
|
+
* 将 messages 端点与 invoke 编排逻辑关联,确保会话连续性。
|
|
32
|
+
*/
|
|
33
|
+
export declare class MessagesOrchestrator {
|
|
34
|
+
private readonly sessionService;
|
|
35
|
+
private readonly messageService;
|
|
36
|
+
private readonly invokeOrchestrator;
|
|
37
|
+
constructor(deps: MessagesOrchestratorDeps);
|
|
38
|
+
/**
|
|
39
|
+
* 在已有会话中发送新消息并触发 Agent 执行
|
|
40
|
+
*
|
|
41
|
+
* @param req - 发送消息请求(含 sessionId, input, config)
|
|
42
|
+
* @param userId - 经认证的用户 ID
|
|
43
|
+
* @returns 与 InvokeResponse 相同的响应
|
|
44
|
+
*/
|
|
45
|
+
postMessage(req: PostMessageRequest, userId: string): Promise<PostMessageResponse>;
|
|
46
|
+
/**
|
|
47
|
+
* 获取会话消息历史(支持分页)
|
|
48
|
+
*
|
|
49
|
+
* @param req - 获取消息请求(含 sessionId, limit, offset)
|
|
50
|
+
* @returns 消息列表响应
|
|
51
|
+
*/
|
|
52
|
+
getMessages(req: GetMessagesRequest): Promise<GetMessagesResponse>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MessagesOrchestrator — 会话消息编排器
|
|
3
|
+
*
|
|
4
|
+
* 实现多轮对话核心逻辑:
|
|
5
|
+
* - POST /api/v1/sessions/:sessionId/messages: 在已有会话中发送新消息,触发 Agent 执行
|
|
6
|
+
* - GET /api/v1/sessions/:sessionId/messages: 获取会话消息历史
|
|
7
|
+
*
|
|
8
|
+
* 编排步骤(postMessage):
|
|
9
|
+
* 1. 验证 Session 存在(SessionService.getSession)
|
|
10
|
+
* 2. 委托 InvokeOrchestrator.execute()(主路径已写回 user/assistant Message)
|
|
11
|
+
* 3. 返回 InvokeResponse
|
|
12
|
+
*
|
|
13
|
+
* @see design.md §1.5 多轮对话
|
|
14
|
+
* @see requirements.md Requirement 11 AC3, AC5, AC10
|
|
15
|
+
*/
|
|
16
|
+
// ─────────────────────────────────────────────
|
|
17
|
+
// MessagesOrchestrator
|
|
18
|
+
// ─────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* MessagesOrchestrator — 会话消息编排
|
|
21
|
+
*
|
|
22
|
+
* 将 messages 端点与 invoke 编排逻辑关联,确保会话连续性。
|
|
23
|
+
*/
|
|
24
|
+
export class MessagesOrchestrator {
|
|
25
|
+
sessionService;
|
|
26
|
+
messageService;
|
|
27
|
+
invokeOrchestrator;
|
|
28
|
+
constructor(deps) {
|
|
29
|
+
this.sessionService = deps.sessionService;
|
|
30
|
+
this.messageService = deps.messageService;
|
|
31
|
+
this.invokeOrchestrator = deps.invokeOrchestrator;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 在已有会话中发送新消息并触发 Agent 执行
|
|
35
|
+
*
|
|
36
|
+
* @param req - 发送消息请求(含 sessionId, input, config)
|
|
37
|
+
* @param userId - 经认证的用户 ID
|
|
38
|
+
* @returns 与 InvokeResponse 相同的响应
|
|
39
|
+
*/
|
|
40
|
+
async postMessage(req, userId) {
|
|
41
|
+
const { sessionId, input, config } = req;
|
|
42
|
+
// ─── Step 1: 验证 Session 存在 ───
|
|
43
|
+
const session = await this.sessionService.getSession(sessionId);
|
|
44
|
+
const metaVersion = typeof session.metadata?.agentVersion === 'string'
|
|
45
|
+
? session.metadata.agentVersion
|
|
46
|
+
: undefined;
|
|
47
|
+
const agentVersion = req.agentVersion ?? metaVersion;
|
|
48
|
+
// ─── Step 2: 委托 InvokeOrchestrator(user/assistant Message 由 invoke 主路径写回)───
|
|
49
|
+
const invokeReq = {
|
|
50
|
+
agentId: session.agentId,
|
|
51
|
+
agentCode: session.agentId,
|
|
52
|
+
input,
|
|
53
|
+
sessionId,
|
|
54
|
+
config,
|
|
55
|
+
agentVersion,
|
|
56
|
+
};
|
|
57
|
+
return this.invokeOrchestrator.execute(invokeReq, userId);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 获取会话消息历史(支持分页)
|
|
61
|
+
*
|
|
62
|
+
* @param req - 获取消息请求(含 sessionId, limit, offset)
|
|
63
|
+
* @returns 消息列表响应
|
|
64
|
+
*/
|
|
65
|
+
async getMessages(req) {
|
|
66
|
+
const { sessionId, limit, offset } = req;
|
|
67
|
+
// ─── Step 1: 验证 Session 存在 ───
|
|
68
|
+
await this.sessionService.getSession(sessionId);
|
|
69
|
+
// ─── Step 2: 查询消息列表(分页) ───
|
|
70
|
+
const messages = await this.messageService.getMessages(sessionId, { limit, offset });
|
|
71
|
+
// ─── Step 3: 返回消息列表 ───
|
|
72
|
+
return {
|
|
73
|
+
code: 'ok',
|
|
74
|
+
data: {
|
|
75
|
+
messages,
|
|
76
|
+
total: messages.length,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StreamOrchestrator — 流式调用编排器
|
|
3
|
+
*
|
|
4
|
+
* 实现 POST /api/v1/agents/:agentCode/stream 的核心编排逻辑:
|
|
5
|
+
* 1. 检查 Session 日预算(SessionBudgetChecker)
|
|
6
|
+
* 2. 解析/创建 Session 上下文(SessionContextService),sessionId 缺失时自动创建
|
|
7
|
+
* 3. 创建 Task(TaskService)
|
|
8
|
+
* 4. 启动 Task(pending → running)
|
|
9
|
+
* 5. 通过 SSEEmitter 推送事件流
|
|
10
|
+
* 6. 构建/流式执行 Graph(IStateGraphEngine.stream())
|
|
11
|
+
* 7. 将 RuntimeEvent 转为 SSE 事件推送
|
|
12
|
+
* 8. 记录 Token 消耗 + 完成 Task + 关闭 SSE 连接
|
|
13
|
+
*
|
|
14
|
+
* 与 InvokeOrchestrator 区别:
|
|
15
|
+
* - 使用 Graph.stream() 而非 invoke()
|
|
16
|
+
* - 通过 SSEEmitter 实时推送事件,而非等待完整结果
|
|
17
|
+
* - 在流式过程中逐步 emit 各类事件(session.created, task.started, response.chunk 等)
|
|
18
|
+
*
|
|
19
|
+
* @see design.md §1.5 /stream 内部执行逻辑
|
|
20
|
+
* @see requirements.md Requirement 11 AC2
|
|
21
|
+
*/
|
|
22
|
+
import type { ServerResponse } from 'node:http';
|
|
23
|
+
import type { SessionContextService } from '../../session/session-context.service.js';
|
|
24
|
+
import type { MessageService } from '../../session/message.service.js';
|
|
25
|
+
import type { TaskService } from '../../task/task.service.js';
|
|
26
|
+
import type { IStateGraphEngine } from '../../graph/contracts/state-graph.contract.js';
|
|
27
|
+
import type { TokenStatsService } from '../../token/token-stats.service.js';
|
|
28
|
+
import type { TokenBudgetGuard } from '../../token/token-budget-guard.js';
|
|
29
|
+
import type { SessionBudgetChecker } from '../../token/session-budget-checker.js';
|
|
30
|
+
import type { StreamRequest } from '../routes/agent.routes.js';
|
|
31
|
+
import type { GraphLoader } from '../../registry/graph-loader.js';
|
|
32
|
+
import type { ICheckpointSaver } from '../../checkpoint/contracts/checkpoint-saver.contract.js';
|
|
33
|
+
import type { ITraceCollector } from '../../contracts/trace-collector.js';
|
|
34
|
+
/**
|
|
35
|
+
* StreamOrchestrator 构造器依赖
|
|
36
|
+
*/
|
|
37
|
+
export interface StreamOrchestratorDeps {
|
|
38
|
+
sessionContextService: SessionContextService;
|
|
39
|
+
taskService: TaskService;
|
|
40
|
+
graphEngine: IStateGraphEngine;
|
|
41
|
+
tokenStatsService: TokenStatsService;
|
|
42
|
+
tokenBudgetGuard: TokenBudgetGuard;
|
|
43
|
+
sessionBudgetChecker: SessionBudgetChecker;
|
|
44
|
+
/** 多轮短期记忆写回;未注入时跳过持久化 */
|
|
45
|
+
messageService?: MessageService;
|
|
46
|
+
graphLoader?: GraphLoader;
|
|
47
|
+
/** 用于判断 session Checkpoint 是否已有历史,避免双重 append */
|
|
48
|
+
checkpointSaver?: ICheckpointSaver;
|
|
49
|
+
/** Hosting 注入的 Trace 采集器;与 traceId 同时存在时创建 Skill/Tool span */
|
|
50
|
+
traceCollector?: ITraceCollector;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* StreamOrchestrator — 流式调用全流程编排
|
|
54
|
+
*
|
|
55
|
+
* 协调 SessionContextService、TaskService、IStateGraphEngine、SSEEmitter、TokenStatsService
|
|
56
|
+
* 等服务完成一次 SSE 流式 Agent 调用的完整生命周期。
|
|
57
|
+
*/
|
|
58
|
+
export declare class StreamOrchestrator {
|
|
59
|
+
private readonly sessionContextService;
|
|
60
|
+
private readonly taskService;
|
|
61
|
+
private readonly graphEngine;
|
|
62
|
+
private readonly tokenStatsService;
|
|
63
|
+
private readonly tokenBudgetGuard;
|
|
64
|
+
private readonly sessionBudgetChecker;
|
|
65
|
+
private readonly messageService?;
|
|
66
|
+
private readonly graphLoader?;
|
|
67
|
+
private readonly checkpointSaver?;
|
|
68
|
+
private readonly traceCollector?;
|
|
69
|
+
constructor(deps: StreamOrchestratorDeps);
|
|
70
|
+
/**
|
|
71
|
+
* 执行流式调用编排
|
|
72
|
+
*
|
|
73
|
+
* @param req - 解析后的调用请求(与 InvokeRequest 相同)
|
|
74
|
+
* @param res - Node.js HTTP ServerResponse,用于写入 SSE 事件
|
|
75
|
+
* @param userId - 经认证的用户 ID
|
|
76
|
+
*/
|
|
77
|
+
execute(req: StreamRequest, res: ServerResponse, userId: string): Promise<void>;
|
|
78
|
+
private executeInternal;
|
|
79
|
+
/**
|
|
80
|
+
* 将 RuntimeEvent 转为 SSEEvent
|
|
81
|
+
*/
|
|
82
|
+
private runtimeEventToSSE;
|
|
83
|
+
/**
|
|
84
|
+
* 构建标准 SSEEvent
|
|
85
|
+
*/
|
|
86
|
+
private buildSSEEvent;
|
|
87
|
+
/**
|
|
88
|
+
* 发射错误事件并关闭连接
|
|
89
|
+
*/
|
|
90
|
+
private emitErrorAndClose;
|
|
91
|
+
private isTimeoutError;
|
|
92
|
+
}
|