@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
package/dist/server.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @eap/runtime HTTP 服务入口
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 独立部署模式:包装 InvokeOrchestrator / StreamOrchestrator 为 REST API。
|
|
5
|
+
* Phase 2+ 推荐由 hosting-service 内嵌 SDK,本服务可选。
|
|
6
|
+
*
|
|
7
|
+
* TODO: Phase 2 将本文件迁移至 dev-tools/cli(作为 `eap dev` 命令实现),
|
|
8
|
+
* runtime 包不再包含 HTTP 服务代码,仅作为纯库导出。
|
|
9
|
+
*/
|
|
10
|
+
import { ErrorCodes } from '@gt-fe/eap-contracts';
|
|
11
|
+
import { createServer } from 'node:http';
|
|
12
|
+
import { URL } from 'node:url';
|
|
13
|
+
import { resolveExecutionContext } from './governance/index.js';
|
|
14
|
+
import { createTaskRouteHandlers, createDevRuntime, } from './index.js';
|
|
15
|
+
import { sendErrorJson } from './utils/http-error.js';
|
|
16
|
+
const port = parseInt(process.env.EAP_RUNTIME_PORT ?? process.env.PORT ?? '3000', 10);
|
|
17
|
+
const host = process.env.EAP_RUNTIME_HOST ?? process.env.HOST ?? '0.0.0.0';
|
|
18
|
+
function readBody(req) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const chunks = [];
|
|
21
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
22
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
23
|
+
req.on('error', reject);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function sendJson(res, status, body) {
|
|
27
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
28
|
+
res.end(JSON.stringify(body));
|
|
29
|
+
}
|
|
30
|
+
// 组装运行时依赖(全部委托给 createDevRuntime 工厂,保证 CLI 嵌入模式与 HTTP 模式 DI 一致)
|
|
31
|
+
const devRuntime = createDevRuntime();
|
|
32
|
+
const { invokeOrchestrator, streamOrchestrator, taskService } = devRuntime;
|
|
33
|
+
const { llmProviderKind, governanceKind, sessionStoreKind, messageStoreKind, checkpointKind, storageProfile, foundationConfigured, deploymentContext, toolExecutionMode, } = devRuntime.diagnostics;
|
|
34
|
+
const taskHandlers = createTaskRouteHandlers(taskService);
|
|
35
|
+
const devHttpEnabled = process.env.EAP_DEV_HTTP === '1' || process.env.EAP_DEV_HTTP === 'true';
|
|
36
|
+
const server = createServer(async (req, res) => {
|
|
37
|
+
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
38
|
+
if (req.method === 'GET' && url.pathname === '/health') {
|
|
39
|
+
sendJson(res, 200, { status: 'ok', timestamp: new Date().toISOString(), devHttp: devHttpEnabled });
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!devHttpEnabled) {
|
|
43
|
+
sendJson(res, 404, {
|
|
44
|
+
code: ErrorCodes.PLATFORM_ROUTE_NOT_FOUND,
|
|
45
|
+
message: 'Runtime HTTP is dev-only. Set EAP_DEV_HTTP=1 for local invoke/stream, or use Hosting.',
|
|
46
|
+
});
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (req.method === 'GET' && url.pathname === '/metrics') {
|
|
50
|
+
sendJson(res, 200, {
|
|
51
|
+
mode: 'standalone',
|
|
52
|
+
checkpointDir: process.env.EAP_CHECKPOINT_DIR ?? '/tmp/eap-checkpoints',
|
|
53
|
+
foundationUrl: process.env.FOUNDATION_SERVICE_URL ?? 'http://localhost:3100',
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const invokeMatch = url.pathname.match(/^\/api\/v1\/agents\/([^/]+)\/invoke$/);
|
|
58
|
+
if (req.method === 'POST' && invokeMatch) {
|
|
59
|
+
try {
|
|
60
|
+
const body = JSON.parse(await readBody(req));
|
|
61
|
+
const authHeader = req.headers.authorization;
|
|
62
|
+
const delegationToken = authHeader?.startsWith('Bearer ') ? authHeader.slice(7).trim() : undefined;
|
|
63
|
+
const userId = req.headers['x-user-id'] ?? body.userId ?? 'anonymous';
|
|
64
|
+
const agentCode = invokeMatch[1];
|
|
65
|
+
const executionContext = body.executionContext ??
|
|
66
|
+
resolveExecutionContext({ agentCode, sessionId: body.sessionId, userId }, userId, { delegationToken });
|
|
67
|
+
const result = await invokeOrchestrator.execute({
|
|
68
|
+
agentCode,
|
|
69
|
+
input: body.input ?? '',
|
|
70
|
+
sessionId: body.sessionId,
|
|
71
|
+
userId,
|
|
72
|
+
config: body.config,
|
|
73
|
+
executionContext,
|
|
74
|
+
}, userId);
|
|
75
|
+
sendJson(res, result.code === 'ok' ? 200 : 500, result);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
sendErrorJson(res, error);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const streamMatch = url.pathname.match(/^\/api\/v1\/agents\/([^/]+)\/stream$/);
|
|
83
|
+
if (req.method === 'POST' && streamMatch) {
|
|
84
|
+
try {
|
|
85
|
+
const body = JSON.parse(await readBody(req));
|
|
86
|
+
const authHeader = req.headers.authorization;
|
|
87
|
+
const delegationToken = authHeader?.startsWith('Bearer ') ? authHeader.slice(7).trim() : undefined;
|
|
88
|
+
const userId = req.headers['x-user-id'] ?? body.userId ?? 'anonymous';
|
|
89
|
+
const agentCode = streamMatch[1];
|
|
90
|
+
const executionContext = body.executionContext ??
|
|
91
|
+
resolveExecutionContext({ agentCode, sessionId: body.sessionId, userId }, userId, { delegationToken });
|
|
92
|
+
await streamOrchestrator.execute({
|
|
93
|
+
agentCode,
|
|
94
|
+
input: body.input ?? '',
|
|
95
|
+
sessionId: body.sessionId,
|
|
96
|
+
userId,
|
|
97
|
+
executionContext,
|
|
98
|
+
}, res, userId);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
sendErrorJson(res, error);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const taskStatusMatch = url.pathname.match(/^\/api\/v1\/tasks\/([^/]+)\/status$/);
|
|
106
|
+
if (req.method === 'GET' && taskStatusMatch) {
|
|
107
|
+
const result = await taskHandlers.getTaskStatusHandler({ taskId: taskStatusMatch[1] });
|
|
108
|
+
sendJson(res, result.code === 'ok' ? 200 : result.code === 'runtime.task.not_found' ? 404 : 500, result);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const hitlMatch = url.pathname.match(/^\/api\/v1\/tasks\/([^/]+)\/hitl$/);
|
|
112
|
+
if (req.method === 'POST' && hitlMatch) {
|
|
113
|
+
const body = JSON.parse(await readBody(req));
|
|
114
|
+
const taskId = hitlMatch[1];
|
|
115
|
+
const choice = body.choice ?? 'confirm';
|
|
116
|
+
const userId = body.userId ?? 'anonymous';
|
|
117
|
+
try {
|
|
118
|
+
const result = await devRuntime.resumeHitl(taskId, { choice, userId });
|
|
119
|
+
const statusCode = result.status === 'waiting_hitl' ? 202 : 200;
|
|
120
|
+
sendJson(res, statusCode, {
|
|
121
|
+
code: 'ok',
|
|
122
|
+
data: {
|
|
123
|
+
acknowledged: true,
|
|
124
|
+
taskId,
|
|
125
|
+
status: result.status,
|
|
126
|
+
output: result.output,
|
|
127
|
+
hitl: result.hitl,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
sendErrorJson(res, error);
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
sendJson(res, 404, {
|
|
137
|
+
code: ErrorCodes.PLATFORM_ROUTE_NOT_FOUND,
|
|
138
|
+
message: `Route not found: ${req.method} ${url.pathname}`,
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
server.listen(port, host, () => {
|
|
142
|
+
console.log(`@eap/runtime HTTP server on ${host}:${port}`);
|
|
143
|
+
console.log(`Health: http://${host}:${port}/health`);
|
|
144
|
+
if (devHttpEnabled) {
|
|
145
|
+
console.log(`Dev HTTP enabled — Invoke: POST http://${host}:${port}/api/v1/agents/{agentCode}/invoke`);
|
|
146
|
+
// 本地闭环模式自检摘要,方便开发者快速定位环境问题
|
|
147
|
+
console.log(` mode: ${foundationConfigured ? 'connected' : 'local-closed-loop'}`);
|
|
148
|
+
console.log(` deploymentContext: ${deploymentContext}`);
|
|
149
|
+
console.log(` toolExecutionMode: ${toolExecutionMode}`);
|
|
150
|
+
console.log(` llmProvider: ${llmProviderKind}`);
|
|
151
|
+
console.log(` governance: ${governanceKind}`);
|
|
152
|
+
console.log(` storageProfile: ${storageProfile}`);
|
|
153
|
+
console.log(` sessionStore: ${sessionStoreKind}`);
|
|
154
|
+
console.log(` messageStore: ${messageStoreKind}`);
|
|
155
|
+
console.log(` checkpoint: ${checkpointKind}`);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
console.log('Dev HTTP disabled — only /health exposed (set EAP_DEV_HTTP=1 for local API)');
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
export { server };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CompressionMiddleware — 压缩中间件
|
|
3
|
+
*
|
|
4
|
+
* 在 Session 加载历史消息时自动触发压缩。
|
|
5
|
+
* 集成到 SessionContextService 或独立使用。
|
|
6
|
+
*
|
|
7
|
+
* @see message-compression.service.ts 压缩接口
|
|
8
|
+
* @see session-context.service.ts 会话上下文
|
|
9
|
+
*/
|
|
10
|
+
import type { MessageRecord, IMessageRepository } from './session.service.js';
|
|
11
|
+
import type { ILLMProvider } from '../graph/nodes/llm-node.js';
|
|
12
|
+
import type { CompressionConfig, IMessageCompressionService } from '../memory/message-compression.service.js';
|
|
13
|
+
import type { ICompressionRepository } from '../memory/compression/compression.repository.js';
|
|
14
|
+
/**
|
|
15
|
+
* 压缩中间件配置
|
|
16
|
+
*/
|
|
17
|
+
export interface CompressionMiddlewareConfig {
|
|
18
|
+
/** 是否启用自动压缩(默认 true) */
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
/** 压缩配置 */
|
|
21
|
+
compressionConfig?: Partial<CompressionConfig>;
|
|
22
|
+
/** 是否保存压缩记录(默认 true) */
|
|
23
|
+
saveRecord?: boolean;
|
|
24
|
+
/** 是否持久化压缩后的消息(默认 true) */
|
|
25
|
+
persistCompressed?: boolean;
|
|
26
|
+
/** 是否标记原始消息为已压缩(默认 true) */
|
|
27
|
+
markOriginals?: boolean;
|
|
28
|
+
/** LLM 配置(用于摘要生成) */
|
|
29
|
+
llmConfig?: {
|
|
30
|
+
/** LLM 提供者 */
|
|
31
|
+
provider?: ILLMProvider;
|
|
32
|
+
/** 模型名称 */
|
|
33
|
+
model?: string;
|
|
34
|
+
/** 温度参数 */
|
|
35
|
+
temperature?: number;
|
|
36
|
+
/** 最大输出 Token 数 */
|
|
37
|
+
maxTokens?: number;
|
|
38
|
+
/** 是否使用精简 Prompt */
|
|
39
|
+
concise?: boolean;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 默认配置
|
|
44
|
+
*/
|
|
45
|
+
export declare const DEFAULT_MIDDLEWARE_CONFIG: CompressionMiddlewareConfig;
|
|
46
|
+
/**
|
|
47
|
+
* CompressionMiddleware — 压缩中间件
|
|
48
|
+
*
|
|
49
|
+
* 在加载历史消息时自动检查并执行压缩。
|
|
50
|
+
*/
|
|
51
|
+
export declare class CompressionMiddleware {
|
|
52
|
+
private readonly compression;
|
|
53
|
+
private readonly repository?;
|
|
54
|
+
private readonly messageRepository?;
|
|
55
|
+
private readonly config;
|
|
56
|
+
constructor(config?: Partial<CompressionMiddlewareConfig>, compression?: IMessageCompressionService, repository?: ICompressionRepository, messageRepository?: IMessageRepository);
|
|
57
|
+
/**
|
|
58
|
+
* 处理消息列表(自动压缩)
|
|
59
|
+
*
|
|
60
|
+
* @param messages - 原始消息列表
|
|
61
|
+
* @param context - 上下文信息(用于审计)
|
|
62
|
+
* @returns 处理后的消息列表
|
|
63
|
+
*/
|
|
64
|
+
processMessages(messages: MessageRecord[], context: {
|
|
65
|
+
sessionId: string;
|
|
66
|
+
taskId?: string;
|
|
67
|
+
userId: string;
|
|
68
|
+
}): Promise<MessageRecord[]>;
|
|
69
|
+
/**
|
|
70
|
+
* 检查是否需要压缩(不执行)
|
|
71
|
+
*/
|
|
72
|
+
checkCompression(messages: MessageRecord[]): {
|
|
73
|
+
needsCompression: boolean;
|
|
74
|
+
currentTokens: number;
|
|
75
|
+
currentRounds: number;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* 获取配置
|
|
79
|
+
*/
|
|
80
|
+
getConfig(): CompressionMiddlewareConfig;
|
|
81
|
+
/**
|
|
82
|
+
* MessageRecord 转 GraphMessage
|
|
83
|
+
*/
|
|
84
|
+
private toGraphMessages;
|
|
85
|
+
/**
|
|
86
|
+
* GraphMessage 转 MessageRecord
|
|
87
|
+
*/
|
|
88
|
+
private toMessageRecords;
|
|
89
|
+
/**
|
|
90
|
+
* 提取摘要内容
|
|
91
|
+
*/
|
|
92
|
+
private extractSummary;
|
|
93
|
+
/**
|
|
94
|
+
* 保存压缩记录
|
|
95
|
+
*/
|
|
96
|
+
private saveCompressionRecord;
|
|
97
|
+
/**
|
|
98
|
+
* 持久化压缩后的消息
|
|
99
|
+
*/
|
|
100
|
+
private persistCompressedMessages;
|
|
101
|
+
/**
|
|
102
|
+
* 标记原始消息为已压缩
|
|
103
|
+
*
|
|
104
|
+
* 通过在 metadata 中添加 compressed 标记,避免重复压缩。
|
|
105
|
+
*/
|
|
106
|
+
private markOriginalMessages;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 创建压缩中间件
|
|
110
|
+
*
|
|
111
|
+
* @param config - 配置
|
|
112
|
+
* @param repository - 压缩记录存储(可选)
|
|
113
|
+
* @param messageRepository - 消息存储(可选,用于持久化压缩后的消息)
|
|
114
|
+
* @returns CompressionMiddleware 实例
|
|
115
|
+
*/
|
|
116
|
+
export declare function createCompressionMiddleware(config?: Partial<CompressionMiddlewareConfig>, repository?: ICompressionRepository, messageRepository?: IMessageRepository): CompressionMiddleware;
|
|
117
|
+
/**
|
|
118
|
+
* 创建带 LLM 摘要能力的压缩中间件
|
|
119
|
+
*
|
|
120
|
+
* @param provider - ILLMProvider 实例
|
|
121
|
+
* @param config - 配置
|
|
122
|
+
* @param repository - 压缩记录存储(可选)
|
|
123
|
+
* @param messageRepository - 消息存储(可选)
|
|
124
|
+
* @returns CompressionMiddleware 实例
|
|
125
|
+
*/
|
|
126
|
+
export declare function createCompressionMiddlewareWithLLM(provider: ILLMProvider, config?: Partial<CompressionMiddlewareConfig>, repository?: ICompressionRepository, messageRepository?: IMessageRepository): CompressionMiddleware;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CompressionMiddleware — 压缩中间件
|
|
3
|
+
*
|
|
4
|
+
* 在 Session 加载历史消息时自动触发压缩。
|
|
5
|
+
* 集成到 SessionContextService 或独立使用。
|
|
6
|
+
*
|
|
7
|
+
* @see message-compression.service.ts 压缩接口
|
|
8
|
+
* @see session-context.service.ts 会话上下文
|
|
9
|
+
*/
|
|
10
|
+
import { SlidingWindowCompression } from '../memory/compression/sliding-window.compression.js';
|
|
11
|
+
import { createSummarize } from '../memory/compression/llm-summarize.js';
|
|
12
|
+
/**
|
|
13
|
+
* 默认配置
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_MIDDLEWARE_CONFIG = {
|
|
16
|
+
enabled: true,
|
|
17
|
+
saveRecord: true,
|
|
18
|
+
persistCompressed: true,
|
|
19
|
+
markOriginals: true,
|
|
20
|
+
};
|
|
21
|
+
// ─────────────────────────────────────────────
|
|
22
|
+
// CompressionMiddleware
|
|
23
|
+
// ─────────────────────────────────────────────
|
|
24
|
+
/**
|
|
25
|
+
* CompressionMiddleware — 压缩中间件
|
|
26
|
+
*
|
|
27
|
+
* 在加载历史消息时自动检查并执行压缩。
|
|
28
|
+
*/
|
|
29
|
+
export class CompressionMiddleware {
|
|
30
|
+
compression;
|
|
31
|
+
repository;
|
|
32
|
+
messageRepository;
|
|
33
|
+
config;
|
|
34
|
+
constructor(config, compression, repository, messageRepository) {
|
|
35
|
+
this.config = { ...DEFAULT_MIDDLEWARE_CONFIG, ...config };
|
|
36
|
+
// 如果未提供 compression 实例,创建默认的
|
|
37
|
+
if (compression) {
|
|
38
|
+
this.compression = compression;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// 创建摘要函数(优先使用 ILLMProvider)
|
|
42
|
+
const summarize = createSummarize({
|
|
43
|
+
provider: this.config.llmConfig?.provider,
|
|
44
|
+
model: this.config.llmConfig?.model,
|
|
45
|
+
temperature: this.config.llmConfig?.temperature,
|
|
46
|
+
maxTokens: this.config.llmConfig?.maxTokens,
|
|
47
|
+
concise: this.config.llmConfig?.concise,
|
|
48
|
+
});
|
|
49
|
+
this.compression = new SlidingWindowCompression(this.config.compressionConfig, summarize);
|
|
50
|
+
}
|
|
51
|
+
this.repository = repository;
|
|
52
|
+
this.messageRepository = messageRepository;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 处理消息列表(自动压缩)
|
|
56
|
+
*
|
|
57
|
+
* @param messages - 原始消息列表
|
|
58
|
+
* @param context - 上下文信息(用于审计)
|
|
59
|
+
* @returns 处理后的消息列表
|
|
60
|
+
*/
|
|
61
|
+
async processMessages(messages, context) {
|
|
62
|
+
if (!this.config.enabled) {
|
|
63
|
+
return messages;
|
|
64
|
+
}
|
|
65
|
+
// 转换为 GraphMessage 格式进行检查
|
|
66
|
+
const graphMessages = this.toGraphMessages(messages);
|
|
67
|
+
// 检查是否需要压缩
|
|
68
|
+
const checkResult = this.compression.checkCompression(graphMessages);
|
|
69
|
+
if (!checkResult.needsCompression) {
|
|
70
|
+
return messages;
|
|
71
|
+
}
|
|
72
|
+
// 执行压缩
|
|
73
|
+
const startTime = Date.now();
|
|
74
|
+
const compressedMessages = await this.compression.compress(graphMessages);
|
|
75
|
+
const duration = Date.now() - startTime;
|
|
76
|
+
// 转换回 MessageRecord 格式
|
|
77
|
+
const compressedRecords = this.toMessageRecords(compressedMessages, messages);
|
|
78
|
+
// 持久化压缩后的消息
|
|
79
|
+
if (this.config.persistCompressed && this.messageRepository) {
|
|
80
|
+
await this.persistCompressedMessages(compressedRecords, context);
|
|
81
|
+
}
|
|
82
|
+
// 标记原始消息为已压缩
|
|
83
|
+
if (this.config.markOriginals && this.messageRepository) {
|
|
84
|
+
await this.markOriginalMessages(messages, context.userId);
|
|
85
|
+
}
|
|
86
|
+
// 保存压缩记录
|
|
87
|
+
if (this.config.saveRecord && this.repository) {
|
|
88
|
+
await this.saveCompressionRecord({
|
|
89
|
+
sessionId: context.sessionId,
|
|
90
|
+
taskId: context.taskId ?? null,
|
|
91
|
+
originalMessageCount: messages.length,
|
|
92
|
+
compressedMessageCount: compressedRecords.length,
|
|
93
|
+
originalTokens: checkResult.currentTokens,
|
|
94
|
+
compressedTokens: this.compression.estimateTokens(compressedMessages),
|
|
95
|
+
strategy: 'sliding-window',
|
|
96
|
+
summaryContent: this.extractSummary(compressedMessages),
|
|
97
|
+
createdBy: context.userId,
|
|
98
|
+
createdAt: new Date().toISOString(),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
console.log(`[CompressionMiddleware] Compressed ${messages.length} → ${compressedRecords.length} messages ` +
|
|
102
|
+
`(${checkResult.currentTokens} → ${this.compression.estimateTokens(compressedMessages)} tokens, ` +
|
|
103
|
+
`${duration}ms)`);
|
|
104
|
+
return compressedRecords;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 检查是否需要压缩(不执行)
|
|
108
|
+
*/
|
|
109
|
+
checkCompression(messages) {
|
|
110
|
+
const graphMessages = this.toGraphMessages(messages);
|
|
111
|
+
return this.compression.checkCompression(graphMessages);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 获取配置
|
|
115
|
+
*/
|
|
116
|
+
getConfig() {
|
|
117
|
+
return { ...this.config };
|
|
118
|
+
}
|
|
119
|
+
// ─────────────────────────────────────────────
|
|
120
|
+
// Private
|
|
121
|
+
// ─────────────────────────────────────────────
|
|
122
|
+
/**
|
|
123
|
+
* MessageRecord 转 GraphMessage
|
|
124
|
+
*/
|
|
125
|
+
toGraphMessages(messages) {
|
|
126
|
+
return messages.map((m) => ({
|
|
127
|
+
role: m.role,
|
|
128
|
+
content: m.content,
|
|
129
|
+
toolCalls: m.toolCalls,
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* GraphMessage 转 MessageRecord
|
|
134
|
+
*/
|
|
135
|
+
toMessageRecords(compressed, original) {
|
|
136
|
+
const now = new Date().toISOString();
|
|
137
|
+
const firstOriginal = original[0];
|
|
138
|
+
return compressed.map((m, index) => ({
|
|
139
|
+
id: `compressed-${Date.now()}-${index}`,
|
|
140
|
+
sessionId: firstOriginal?.sessionId ?? '',
|
|
141
|
+
taskId: firstOriginal?.taskId ?? null,
|
|
142
|
+
correlationId: firstOriginal?.correlationId ?? null,
|
|
143
|
+
role: m.role,
|
|
144
|
+
content: m.content,
|
|
145
|
+
toolCalls: m.toolCalls ?? null,
|
|
146
|
+
tokenCount: 0, // 会在后续计算
|
|
147
|
+
createdBy: 'system',
|
|
148
|
+
createdAt: now,
|
|
149
|
+
updatedBy: 'system',
|
|
150
|
+
updatedAt: now,
|
|
151
|
+
isDeleted: false,
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 提取摘要内容
|
|
156
|
+
*/
|
|
157
|
+
extractSummary(messages) {
|
|
158
|
+
const summaryMessage = messages.find((m) => m.role === 'system' && m.content.includes('对话历史摘要'));
|
|
159
|
+
return summaryMessage?.content ?? '';
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 保存压缩记录
|
|
163
|
+
*/
|
|
164
|
+
async saveCompressionRecord(record) {
|
|
165
|
+
try {
|
|
166
|
+
await this.repository?.save(record);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.error('[CompressionMiddleware] Failed to save compression record:', error);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 持久化压缩后的消息
|
|
174
|
+
*/
|
|
175
|
+
async persistCompressedMessages(messages, context) {
|
|
176
|
+
if (!this.messageRepository)
|
|
177
|
+
return;
|
|
178
|
+
try {
|
|
179
|
+
for (const message of messages) {
|
|
180
|
+
await this.messageRepository.save(message);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
console.error('[CompressionMiddleware] Failed to persist compressed messages:', error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 标记原始消息为已压缩
|
|
189
|
+
*
|
|
190
|
+
* 通过在 metadata 中添加 compressed 标记,避免重复压缩。
|
|
191
|
+
*/
|
|
192
|
+
async markOriginalMessages(messages, userId) {
|
|
193
|
+
// 注意:当前 IMessageRepository 接口没有 update 方法
|
|
194
|
+
// 这里只是记录日志,实际实现需要扩展接口
|
|
195
|
+
console.log(`[CompressionMiddleware] Would mark ${messages.length} messages as compressed (not implemented)`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// ─────────────────────────────────────────────
|
|
199
|
+
// 工厂函数
|
|
200
|
+
// ─────────────────────────────────────────────
|
|
201
|
+
/**
|
|
202
|
+
* 创建压缩中间件
|
|
203
|
+
*
|
|
204
|
+
* @param config - 配置
|
|
205
|
+
* @param repository - 压缩记录存储(可选)
|
|
206
|
+
* @param messageRepository - 消息存储(可选,用于持久化压缩后的消息)
|
|
207
|
+
* @returns CompressionMiddleware 实例
|
|
208
|
+
*/
|
|
209
|
+
export function createCompressionMiddleware(config, repository, messageRepository) {
|
|
210
|
+
return new CompressionMiddleware(config, undefined, repository, messageRepository);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* 创建带 LLM 摘要能力的压缩中间件
|
|
214
|
+
*
|
|
215
|
+
* @param provider - ILLMProvider 实例
|
|
216
|
+
* @param config - 配置
|
|
217
|
+
* @param repository - 压缩记录存储(可选)
|
|
218
|
+
* @param messageRepository - 消息存储(可选)
|
|
219
|
+
* @returns CompressionMiddleware 实例
|
|
220
|
+
*/
|
|
221
|
+
export function createCompressionMiddlewareWithLLM(provider, config, repository, messageRepository) {
|
|
222
|
+
const llmConfig = {
|
|
223
|
+
provider,
|
|
224
|
+
model: config?.llmConfig?.model ?? 'DeepSeek-V4-Pro',
|
|
225
|
+
temperature: config?.llmConfig?.temperature ?? 0.3,
|
|
226
|
+
maxTokens: config?.llmConfig?.maxTokens ?? 1024,
|
|
227
|
+
concise: config?.llmConfig?.concise ?? false,
|
|
228
|
+
};
|
|
229
|
+
return new CompressionMiddleware({ ...config, llmConfig }, undefined, repository, messageRepository);
|
|
230
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileMessageRepository — Message 文件持久化(dev profile)
|
|
3
|
+
*
|
|
4
|
+
* 每个 session 一个 JSON 数组文件:{baseDir}/{sessionId}.messages.json
|
|
5
|
+
* 与 FileSessionRepository 配套,支持 eap chat 进程重启后恢复多轮上下文。
|
|
6
|
+
*/
|
|
7
|
+
import type { MessageRecord } from './session.service.js';
|
|
8
|
+
import type { IFullMessageRepository } from './message.service.js';
|
|
9
|
+
export interface FileMessageRepositoryOptions {
|
|
10
|
+
/** 存储目录;建议与 Session 同根或旁路子目录 */
|
|
11
|
+
baseDir: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class FileMessageRepository implements IFullMessageRepository {
|
|
14
|
+
private readonly baseDir;
|
|
15
|
+
constructor(options: FileMessageRepositoryOptions);
|
|
16
|
+
save(message: MessageRecord): Promise<void>;
|
|
17
|
+
saveMany(messages: MessageRecord[]): Promise<void>;
|
|
18
|
+
findBySessionId(sessionId: string, limit?: number, offset?: number): Promise<MessageRecord[]>;
|
|
19
|
+
findByTaskId(taskId: string): Promise<MessageRecord[]>;
|
|
20
|
+
markAsCompressed(messageIds: string[], compressedBy: string, compressedAt: string): Promise<void>;
|
|
21
|
+
private getFilePath;
|
|
22
|
+
private ensureDir;
|
|
23
|
+
private readAll;
|
|
24
|
+
private writeAll;
|
|
25
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileMessageRepository — Message 文件持久化(dev profile)
|
|
3
|
+
*
|
|
4
|
+
* 每个 session 一个 JSON 数组文件:{baseDir}/{sessionId}.messages.json
|
|
5
|
+
* 与 FileSessionRepository 配套,支持 eap chat 进程重启后恢复多轮上下文。
|
|
6
|
+
*/
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { readFile, writeFile, mkdir, access } from 'node:fs/promises';
|
|
9
|
+
export class FileMessageRepository {
|
|
10
|
+
baseDir;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.baseDir = options.baseDir;
|
|
13
|
+
}
|
|
14
|
+
async save(message) {
|
|
15
|
+
await this.ensureDir();
|
|
16
|
+
const messages = await this.readAll(message.sessionId);
|
|
17
|
+
const idx = messages.findIndex((m) => m.id === message.id);
|
|
18
|
+
if (idx >= 0) {
|
|
19
|
+
messages[idx] = { ...message };
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
messages.push({ ...message });
|
|
23
|
+
}
|
|
24
|
+
messages.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
25
|
+
await this.writeAll(message.sessionId, messages);
|
|
26
|
+
}
|
|
27
|
+
async saveMany(messages) {
|
|
28
|
+
for (const message of messages) {
|
|
29
|
+
await this.save(message);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async findBySessionId(sessionId, limit, offset) {
|
|
33
|
+
let messages = (await this.readAll(sessionId)).filter((m) => !m.isDeleted);
|
|
34
|
+
const start = offset ?? 0;
|
|
35
|
+
if (limit !== undefined) {
|
|
36
|
+
messages = messages.slice(start, start + limit);
|
|
37
|
+
}
|
|
38
|
+
else if (start > 0) {
|
|
39
|
+
messages = messages.slice(start);
|
|
40
|
+
}
|
|
41
|
+
return messages.map((m) => ({ ...m }));
|
|
42
|
+
}
|
|
43
|
+
async findByTaskId(taskId) {
|
|
44
|
+
// 文件布局按 session;扫描成本高,dev 下按需线性扫描目录不值得——此处返回空并由调用方用 session 查询
|
|
45
|
+
void taskId;
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
async markAsCompressed(messageIds, compressedBy, compressedAt) {
|
|
49
|
+
// 文件实现:在匹配消息上写入 updatedBy/updatedAt(无独立 compressed 字段)
|
|
50
|
+
const idSet = new Set(messageIds);
|
|
51
|
+
// 无全局索引:无法枚举全部 session;压缩中间件在 memory/pg 场景更常用
|
|
52
|
+
void idSet;
|
|
53
|
+
void compressedBy;
|
|
54
|
+
void compressedAt;
|
|
55
|
+
}
|
|
56
|
+
getFilePath(sessionId) {
|
|
57
|
+
return join(this.baseDir, `${sessionId}.messages.json`);
|
|
58
|
+
}
|
|
59
|
+
async ensureDir() {
|
|
60
|
+
await mkdir(this.baseDir, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
async readAll(sessionId) {
|
|
63
|
+
const filePath = this.getFilePath(sessionId);
|
|
64
|
+
try {
|
|
65
|
+
await access(filePath);
|
|
66
|
+
const content = await readFile(filePath, 'utf-8');
|
|
67
|
+
const parsed = JSON.parse(content);
|
|
68
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async writeAll(sessionId, messages) {
|
|
75
|
+
await this.ensureDir();
|
|
76
|
+
await writeFile(this.getFilePath(sessionId), JSON.stringify(messages, null, 2), 'utf-8');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileSessionRepository — Session 持久化文件存储实现
|
|
3
|
+
*
|
|
4
|
+
* 基于本地 JSON 文件实现 ISessionRepository 接口。
|
|
5
|
+
* 适用于生产环境中的会话信息存储,支持审计字段和软删除。
|
|
6
|
+
*
|
|
7
|
+
* 文件结构:
|
|
8
|
+
* - 每个 Session 存储为独立的 JSON 文件
|
|
9
|
+
* - 文件路径:{baseDir}/{sessionId}.json
|
|
10
|
+
* - 支持索引文件用于快速查询
|
|
11
|
+
*
|
|
12
|
+
* @see design.md §1.3 数据模型 — sessions 表
|
|
13
|
+
* @see session.service.ts ISessionRepository 接口定义
|
|
14
|
+
*/
|
|
15
|
+
import type { SessionRecord, ISessionRepository } from './session.service.js';
|
|
16
|
+
/**
|
|
17
|
+
* FileSessionRepository 配置选项
|
|
18
|
+
*/
|
|
19
|
+
export interface FileSessionRepositoryOptions {
|
|
20
|
+
/** 存储目录路径 */
|
|
21
|
+
baseDir: string;
|
|
22
|
+
/** 是否启用索引文件(默认 true) */
|
|
23
|
+
enableIndex?: boolean;
|
|
24
|
+
/** 文件扩展名(默认 '.json') */
|
|
25
|
+
fileExtension?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare class FileSessionRepository implements ISessionRepository {
|
|
28
|
+
private readonly baseDir;
|
|
29
|
+
private readonly enableIndex;
|
|
30
|
+
private readonly fileExtension;
|
|
31
|
+
private readonly indexFile;
|
|
32
|
+
constructor(options: FileSessionRepositoryOptions);
|
|
33
|
+
/**
|
|
34
|
+
* 保存 Session 到 JSON 文件
|
|
35
|
+
*/
|
|
36
|
+
save(session: SessionRecord): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* 从 JSON 文件加载 Session
|
|
39
|
+
* Excludes soft-deleted records
|
|
40
|
+
*/
|
|
41
|
+
findById(id: string): Promise<SessionRecord | null>;
|
|
42
|
+
/**
|
|
43
|
+
* 更新 Session 文件
|
|
44
|
+
*/
|
|
45
|
+
update(session: SessionRecord): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* 软删除 Session
|
|
48
|
+
*/
|
|
49
|
+
softDelete(id: string, deletedBy: string): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* 列出所有 Session(从索引文件)
|
|
52
|
+
* Excludes soft-deleted records
|
|
53
|
+
*/
|
|
54
|
+
listAll(): Promise<SessionRecord[]>;
|
|
55
|
+
/**
|
|
56
|
+
* 按 userId 查询 Session
|
|
57
|
+
* Excludes soft-deleted records
|
|
58
|
+
*/
|
|
59
|
+
findByUserId(userId: string): Promise<SessionRecord[]>;
|
|
60
|
+
/**
|
|
61
|
+
* 获取 Session 文件路径
|
|
62
|
+
*/
|
|
63
|
+
private getFilePath;
|
|
64
|
+
/**
|
|
65
|
+
* 确保目录存在
|
|
66
|
+
*/
|
|
67
|
+
private ensureDir;
|
|
68
|
+
/**
|
|
69
|
+
* 更新索引文件
|
|
70
|
+
*/
|
|
71
|
+
private updateIndex;
|
|
72
|
+
}
|