@gt-fe/eap-runtime 0.0.20 → 0.0.21
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 +3 -2
- package/dist/api/handlers/invoke.handler.d.ts +7 -0
- package/dist/api/handlers/invoke.handler.js +48 -9
- package/dist/api/handlers/stream.handler.d.ts +6 -0
- package/dist/api/handlers/stream.handler.js +56 -39
- package/dist/api/routes/agent.routes.d.ts +8 -1
- package/dist/common/types.d.ts +2 -2
- package/dist/common/types.js +1 -1
- package/dist/execution/index.d.ts +2 -0
- package/dist/execution/index.js +2 -0
- package/dist/execution/interceptors.d.ts +40 -0
- package/dist/execution/interceptors.js +23 -0
- package/dist/execution/ports.d.ts +18 -0
- package/dist/execution/ports.js +13 -0
- package/dist/governance/audit-reporter.d.ts +14 -0
- package/dist/governance/audit-reporter.js +18 -0
- package/dist/governance/index.d.ts +2 -0
- package/dist/governance/index.js +1 -0
- package/dist/governance/permission-guard.d.ts +6 -2
- package/dist/governance/permission-guard.js +38 -5
- package/dist/graph/adapters/graph-nesting.d.ts +12 -0
- package/dist/graph/adapters/graph-nesting.js +29 -0
- package/dist/graph/adapters/index.d.ts +2 -0
- package/dist/graph/adapters/index.js +2 -0
- package/dist/graph/adapters/langgraph.adapter.d.ts +25 -11
- package/dist/graph/adapters/langgraph.adapter.js +142 -82
- package/dist/graph/adapters/skill-isolate.d.ts +17 -0
- package/dist/graph/adapters/skill-isolate.js +50 -0
- package/dist/graph/adapters/subgraph-compiler.d.ts +7 -2
- package/dist/graph/adapters/subgraph-compiler.js +68 -32
- package/dist/graph/contracts/executable-graph.d.ts +27 -5
- package/dist/graph/hitl/hitl-handler.js +2 -0
- package/dist/graph/nodes/index.d.ts +1 -0
- package/dist/graph/nodes/index.js +1 -0
- package/dist/graph/nodes/llm-node.d.ts +3 -1
- package/dist/graph/nodes/llm-node.js +26 -2
- package/dist/graph/nodes/rag/dedup.d.ts +20 -0
- package/dist/graph/nodes/rag/dedup.js +77 -0
- package/dist/graph/nodes/rag/denoise.d.ts +58 -0
- package/dist/graph/nodes/rag/denoise.js +220 -0
- package/dist/graph/nodes/rag/fusion/fuse.d.ts +11 -0
- package/dist/graph/nodes/rag/fusion/fuse.js +25 -0
- package/dist/graph/nodes/rag/fusion/normalize.d.ts +9 -0
- package/dist/graph/nodes/rag/fusion/normalize.js +135 -0
- package/dist/graph/nodes/rag/index.d.ts +18 -0
- package/dist/graph/nodes/rag/index.js +17 -0
- package/dist/graph/nodes/rag/prompts.d.ts +12 -0
- package/dist/graph/nodes/rag/prompts.js +40 -0
- package/dist/graph/nodes/rag/rag-node.d.ts +21 -0
- package/dist/graph/nodes/rag/rag-node.js +135 -0
- package/dist/graph/nodes/rag/rag-search-tool.d.ts +48 -0
- package/dist/graph/nodes/rag/rag-search-tool.js +230 -0
- package/dist/graph/nodes/rag/react-loop.d.ts +57 -0
- package/dist/graph/nodes/rag/react-loop.js +467 -0
- package/dist/graph/nodes/rag/retrievers/http-embedder.d.ts +29 -0
- package/dist/graph/nodes/rag/retrievers/http-embedder.js +66 -0
- package/dist/graph/nodes/rag/retrievers/in-memory-retriever.d.ts +17 -0
- package/dist/graph/nodes/rag/retrievers/in-memory-retriever.js +18 -0
- package/dist/graph/nodes/rag/retrievers/local-sqlite-retriever.d.ts +46 -0
- package/dist/graph/nodes/rag/retrievers/local-sqlite-retriever.js +85 -0
- package/dist/graph/nodes/rag/tools.d.ts +90 -0
- package/dist/graph/nodes/rag/tools.js +260 -0
- package/dist/graph/nodes/rag/types.d.ts +226 -0
- package/dist/graph/nodes/rag/types.js +9 -0
- package/dist/graph/nodes/tool-node.d.ts +19 -1
- package/dist/graph/nodes/tool-node.js +162 -33
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/memory/semantic-retrieval.service.d.ts +1 -1
- package/dist/memory/semantic-retrieval.service.js +13 -23
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/providers/routing-llm-provider.d.ts +5 -0
- package/dist/providers/routing-llm-provider.js +66 -1
- package/dist/runtime/agent-runtime.d.ts +5 -0
- package/dist/runtime/agent-runtime.js +86 -66
- package/dist/runtime/dev-rag-deps.d.ts +21 -0
- package/dist/runtime/dev-rag-deps.js +186 -0
- package/dist/runtime/dev-runtime.d.ts +16 -1
- package/dist/runtime/dev-runtime.js +59 -5
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/types.d.ts +10 -9
- package/dist/session/apply-session-compression.d.ts +81 -0
- package/dist/session/apply-session-compression.js +262 -0
- package/dist/session/apply-session-memory.d.ts +21 -0
- package/dist/session/apply-session-memory.js +48 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/persist-turn-messages.d.ts +51 -0
- package/dist/session/persist-turn-messages.js +140 -3
- package/dist/sse/events/index.d.ts +13 -4
- package/dist/sse/events/index.js +9 -1
- package/dist/sse/events/rag-events.d.ts +63 -0
- package/dist/sse/events/rag-events.js +9 -0
- package/dist/sse/events/session-events.d.ts +38 -0
- package/dist/sse/events/session-events.js +4 -0
- package/dist/token/index.d.ts +2 -1
- package/dist/token/index.js +2 -1
- package/dist/token/record-task-token-usage.d.ts +2 -0
- package/dist/token/record-task-token-usage.js +24 -0
- package/dist/token/token-detail-mode.d.ts +4 -4
- package/dist/token/token-detail-mode.js +4 -4
- package/dist/token/token-detail.d.ts +8 -0
- package/dist/token/token-detail.js +22 -0
- package/dist/token/token-stats.service.d.ts +2 -2
- package/dist/token/token-stats.service.js +1 -1
- package/dist/tool/create-tool-executor.d.ts +6 -0
- package/dist/tool/create-tool-executor.js +4 -3
- package/dist/tool/harness-primitives.d.ts +9 -1
- package/dist/tool/harness-primitives.js +43 -12
- package/dist/tool/index.d.ts +2 -1
- package/dist/tool/index.js +2 -1
- package/dist/tool/tool-executor.interface.d.ts +27 -0
- package/dist/tool/tool-orchestrator.d.ts +3 -1
- package/dist/tool/tool-orchestrator.js +15 -6
- package/dist/tool/tool-retry.d.ts +15 -0
- package/dist/tool/tool-retry.js +69 -0
- package/package.json +2 -2
|
@@ -12,12 +12,15 @@ import { resolveAgentSystemPromptFromLoader } from '../registry/graph-loader.js'
|
|
|
12
12
|
import { toLoadedSkillMap } from '../tool/system-prompt-builder.js';
|
|
13
13
|
import { assembleLoadedTools } from '../tool/harness-primitives.js';
|
|
14
14
|
import { createFileLlmTurnLogFromEnv, } from '../debug/llm-turn-log.js';
|
|
15
|
-
import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, buildTurnTranscriptFromStreamEvents, extractHitlThreadId, extractStreamReasoning,
|
|
15
|
+
import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, buildTurnTranscriptFromStreamEvents, extractHitlThreadId, extractStreamReasoning, applyStreamTextDelta, createStreamAssistantBuckets, visibleStreamAssistantText, streamAssistantTexts, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, resolveCheckpointThreadId, appendTerminalToolResult, } from '../session/persist-turn-messages.js';
|
|
16
|
+
import { iterSessionCompression, } from '../session/apply-session-compression.js';
|
|
17
|
+
import { applySessionMemory } from '../session/apply-session-memory.js';
|
|
18
|
+
import { resolveTokenDetail, shouldEmitTokenSse } from '../token/token-detail.js';
|
|
16
19
|
import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
|
|
17
20
|
import { resolveExecutionContext } from '../governance/governance-client.js';
|
|
18
21
|
import { resolveAgentCode } from '../common/agent-code.js';
|
|
19
22
|
import { resolveTraceId } from '../common/trace-id.js';
|
|
20
|
-
import { collectStreamNodeTokenUsage, recordTaskTokenUsages, } from '../token/record-task-token-usage.js';
|
|
23
|
+
import { collectStreamNodeTokenUsage, recordTaskTokenUsages, tokenUsagesFromLlmChannel, } from '../token/record-task-token-usage.js';
|
|
21
24
|
import { resolveSkillInputFromInvokeInput } from '../registry/skill-io-guard.js';
|
|
22
25
|
const HITL_TERMINAL_STATUSES = [
|
|
23
26
|
'success',
|
|
@@ -208,6 +211,8 @@ export class AgentRuntime {
|
|
|
208
211
|
executionContext: ctx,
|
|
209
212
|
userId,
|
|
210
213
|
traceId: resolveTraceId(params),
|
|
214
|
+
ragScope: params.ragScope,
|
|
215
|
+
ragSources: params.ragSources,
|
|
211
216
|
}, userId);
|
|
212
217
|
return response.data;
|
|
213
218
|
}
|
|
@@ -274,7 +279,31 @@ export class AgentRuntime {
|
|
|
274
279
|
? (await this.deps.graphLoader.resolveAgentDir(agentCode, params.agentVersion)) ?? undefined
|
|
275
280
|
: undefined;
|
|
276
281
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.deps.graphLoader, agentCode, params.agentVersion);
|
|
277
|
-
|
|
282
|
+
let history = messageRecordsToGraphMessages(sessionContext.messages);
|
|
283
|
+
for await (const step of iterSessionCompression({
|
|
284
|
+
history,
|
|
285
|
+
sessionId,
|
|
286
|
+
userId,
|
|
287
|
+
taskId: task.id,
|
|
288
|
+
options: {
|
|
289
|
+
...this.deps.sessionCompression,
|
|
290
|
+
turnLog: this.deps.sessionCompression?.turnLog ?? this.deps.turnLog,
|
|
291
|
+
},
|
|
292
|
+
})) {
|
|
293
|
+
if (step.kind === 'history') {
|
|
294
|
+
history = step.messages;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
yield this.toSSE(step.kind === 'started' ? 'session.compression.started' : 'session.compression.completed', correlationId, task.id, { ...step.payload });
|
|
298
|
+
}
|
|
299
|
+
history = await applySessionMemory({
|
|
300
|
+
history,
|
|
301
|
+
userId,
|
|
302
|
+
agentCode,
|
|
303
|
+
query: typeof params.input === 'string' ? params.input : undefined,
|
|
304
|
+
options: this.deps.sessionMemory,
|
|
305
|
+
});
|
|
306
|
+
const tokenDetail = resolveTokenDetail(params.config?.tokenDetail);
|
|
278
307
|
const messages = await buildTurnGraphMessages({
|
|
279
308
|
history,
|
|
280
309
|
input: params.input,
|
|
@@ -282,7 +311,7 @@ export class AgentRuntime {
|
|
|
282
311
|
threadId: sessionId,
|
|
283
312
|
});
|
|
284
313
|
const startTime = Date.now();
|
|
285
|
-
|
|
314
|
+
const assistantBuckets = createStreamAssistantBuckets();
|
|
286
315
|
let toolCallReasoningContent;
|
|
287
316
|
let assistantReasoningContent;
|
|
288
317
|
const pendingToolCalls = [];
|
|
@@ -300,6 +329,8 @@ export class AgentRuntime {
|
|
|
300
329
|
sessionId,
|
|
301
330
|
executionContext: ctx,
|
|
302
331
|
forceToolServer: params.config?.forceToolServer,
|
|
332
|
+
ragScope: params.ragScope,
|
|
333
|
+
ragSources: params.ragSources,
|
|
303
334
|
loadedTools,
|
|
304
335
|
loadedSkills,
|
|
305
336
|
skillDefinitions,
|
|
@@ -323,32 +354,16 @@ export class AgentRuntime {
|
|
|
323
354
|
toolCallReasoningContent = p.reasoningContent;
|
|
324
355
|
}
|
|
325
356
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (p.toolCallId && p.toolName) {
|
|
329
|
-
toolResults.push({
|
|
330
|
-
toolCallId: p.toolCallId,
|
|
331
|
-
toolName: p.toolName,
|
|
332
|
-
content: p.content,
|
|
333
|
-
success: p.success !== false,
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
}
|
|
357
|
+
// 终态 tool.result / tool.error 回填历史;willRetry 的 error 不进 transcript
|
|
358
|
+
appendTerminalToolResult(runtimeEvent.type, runtimeEvent.payload, toolResults);
|
|
337
359
|
const streamedReasoning = extractStreamReasoning(runtimeEvent.type, runtimeEvent.payload);
|
|
338
360
|
if (streamedReasoning)
|
|
339
361
|
assistantReasoningContent = streamedReasoning;
|
|
340
|
-
|
|
341
|
-
if (delta) {
|
|
342
|
-
if (runtimeEvent.type === 'response.final') {
|
|
343
|
-
if (delta.trim())
|
|
344
|
-
assistantText = delta;
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
assistantText += delta;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
362
|
+
applyStreamTextDelta(assistantBuckets, runtimeEvent.type, runtimeEvent.payload);
|
|
350
363
|
collectStreamNodeTokenUsage(runtimeEvent, nodeTokenUsages);
|
|
351
|
-
|
|
364
|
+
if (shouldEmitTokenSse(tokenDetail, runtimeEvent.type)) {
|
|
365
|
+
yield this.runtimeEventToSSE(runtimeEvent, correlationId, task.id);
|
|
366
|
+
}
|
|
352
367
|
}
|
|
353
368
|
await recordTaskTokenUsages(this.deps.taskService, task.id, nodeTokenUsages);
|
|
354
369
|
await this.deps.taskService.updateStatus(task.id, 'success');
|
|
@@ -359,7 +374,9 @@ export class AgentRuntime {
|
|
|
359
374
|
messages: buildTurnTranscriptFromStreamEvents({
|
|
360
375
|
pendingToolCalls,
|
|
361
376
|
toolResults,
|
|
362
|
-
|
|
377
|
+
assistantTexts: streamAssistantTexts(assistantBuckets),
|
|
378
|
+
assistantText: visibleStreamAssistantText(assistantBuckets),
|
|
379
|
+
finishPoints: graph.finishPoints,
|
|
363
380
|
toolCallReasoningContent,
|
|
364
381
|
assistantReasoningContent,
|
|
365
382
|
}),
|
|
@@ -390,6 +407,7 @@ export class AgentRuntime {
|
|
|
390
407
|
return;
|
|
391
408
|
}
|
|
392
409
|
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
410
|
+
await recordTaskTokenUsages(this.deps.taskService, task.id, nodeTokenUsages);
|
|
393
411
|
await this.deps.taskService.updateStatus(task.id, 'failed', message);
|
|
394
412
|
yield this.toSSE('task.error', correlationId, task.id, {
|
|
395
413
|
code: 'runtime.graph.execution_error',
|
|
@@ -409,7 +427,7 @@ export class AgentRuntime {
|
|
|
409
427
|
let output = '';
|
|
410
428
|
let waiting;
|
|
411
429
|
let sawWait = false;
|
|
412
|
-
|
|
430
|
+
const assistantBuckets = createStreamAssistantBuckets();
|
|
413
431
|
for await (const event of this.streamResumeHitl(taskId, response)) {
|
|
414
432
|
if (event.type === 'hitl.waited') {
|
|
415
433
|
sawWait = true;
|
|
@@ -421,21 +439,12 @@ export class AgentRuntime {
|
|
|
421
439
|
};
|
|
422
440
|
output = waiting.question;
|
|
423
441
|
}
|
|
424
|
-
|
|
425
|
-
if (delta) {
|
|
426
|
-
if (event.type === 'response.final') {
|
|
427
|
-
if (delta.trim())
|
|
428
|
-
assistantText = delta;
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
assistantText += delta;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
442
|
+
applyStreamTextDelta(assistantBuckets, event.type, event.data);
|
|
434
443
|
}
|
|
435
444
|
if (sawWait) {
|
|
436
445
|
return { taskId, status: 'waiting_hitl', output, hitl: waiting };
|
|
437
446
|
}
|
|
438
|
-
return { taskId, status: 'success', output:
|
|
447
|
+
return { taskId, status: 'success', output: visibleStreamAssistantText(assistantBuckets) || output };
|
|
439
448
|
}
|
|
440
449
|
/**
|
|
441
450
|
* HITL 恢复的流式入口:新开一条 SSE(第一条 stream 在 `hitl.waited` 后已结束)。
|
|
@@ -465,6 +474,7 @@ export class AgentRuntime {
|
|
|
465
474
|
if (!claimed) {
|
|
466
475
|
throw new AppError(ErrorCodes.TASK_HITL_INVALID_STATE, `Task ${taskId} is no longer waiting for HITL`, { exposeMessage: true });
|
|
467
476
|
}
|
|
477
|
+
const tokenDetail = resolveTokenDetail(response.tokenDetail);
|
|
468
478
|
yield this.toSSE('hitl.resumed', correlationId, taskId, {
|
|
469
479
|
choice: response.choice,
|
|
470
480
|
userId: response.userId,
|
|
@@ -516,7 +526,7 @@ export class AgentRuntime {
|
|
|
516
526
|
traceCollector: this.deps.traceCollector,
|
|
517
527
|
};
|
|
518
528
|
const startTime = Date.now();
|
|
519
|
-
|
|
529
|
+
const assistantBuckets = createStreamAssistantBuckets();
|
|
520
530
|
let toolCallReasoningContent;
|
|
521
531
|
let assistantReasoningContent;
|
|
522
532
|
const pendingToolCalls = [];
|
|
@@ -537,35 +547,18 @@ export class AgentRuntime {
|
|
|
537
547
|
toolCallReasoningContent = p.reasoningContent;
|
|
538
548
|
}
|
|
539
549
|
}
|
|
540
|
-
|
|
541
|
-
const p = runtimeEvent.payload;
|
|
542
|
-
if (p.toolCallId && p.toolName) {
|
|
543
|
-
toolResults.push({
|
|
544
|
-
toolCallId: p.toolCallId,
|
|
545
|
-
toolName: p.toolName,
|
|
546
|
-
content: p.content ?? p.error,
|
|
547
|
-
success: runtimeEvent.type === 'tool.result' && p.success !== false,
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
}
|
|
550
|
+
appendTerminalToolResult(runtimeEvent.type, runtimeEvent.payload, toolResults);
|
|
551
551
|
const streamedReasoning = extractStreamReasoning(runtimeEvent.type, runtimeEvent.payload);
|
|
552
552
|
if (streamedReasoning)
|
|
553
553
|
assistantReasoningContent = streamedReasoning;
|
|
554
|
-
|
|
555
|
-
if (delta) {
|
|
556
|
-
if (runtimeEvent.type === 'response.final') {
|
|
557
|
-
if (delta.trim())
|
|
558
|
-
assistantText = delta;
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
assistantText += delta;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
554
|
+
applyStreamTextDelta(assistantBuckets, runtimeEvent.type, runtimeEvent.payload);
|
|
564
555
|
collectStreamNodeTokenUsage(runtimeEvent, nodeTokenUsages);
|
|
565
|
-
|
|
556
|
+
if (shouldEmitTokenSse(tokenDetail, runtimeEvent.type)) {
|
|
557
|
+
yield this.runtimeEventToSSE(runtimeEvent, correlationId, taskId);
|
|
558
|
+
}
|
|
566
559
|
}
|
|
567
560
|
await recordTaskTokenUsages(this.deps.taskService, taskId, nodeTokenUsages);
|
|
568
|
-
await this.deps.taskService.updateStatus(taskId, 'success',
|
|
561
|
+
await this.deps.taskService.updateStatus(taskId, 'success', visibleStreamAssistantText(assistantBuckets) || undefined);
|
|
569
562
|
if (sessionId) {
|
|
570
563
|
await persistTurnTranscript(this.deps.messageService, {
|
|
571
564
|
sessionId,
|
|
@@ -574,7 +567,9 @@ export class AgentRuntime {
|
|
|
574
567
|
messages: buildTurnTranscriptFromStreamEvents({
|
|
575
568
|
pendingToolCalls,
|
|
576
569
|
toolResults,
|
|
577
|
-
|
|
570
|
+
assistantTexts: streamAssistantTexts(assistantBuckets),
|
|
571
|
+
assistantText: visibleStreamAssistantText(assistantBuckets),
|
|
572
|
+
finishPoints: graph.finishPoints,
|
|
578
573
|
toolCallReasoningContent,
|
|
579
574
|
assistantReasoningContent,
|
|
580
575
|
}),
|
|
@@ -609,6 +604,7 @@ export class AgentRuntime {
|
|
|
609
604
|
}
|
|
610
605
|
const appErr = err instanceof AppError ? err : AppError.fromUnknown(err);
|
|
611
606
|
const failMsg = appErr.exposeMessage ? appErr.message : '系统内部错误';
|
|
607
|
+
await recordTaskTokenUsages(this.deps.taskService, taskId, nodeTokenUsages);
|
|
612
608
|
await this.deps.taskService.updateStatus(taskId, 'failed', failMsg);
|
|
613
609
|
yield this.toSSE('task.error', correlationId, taskId, {
|
|
614
610
|
code: appErr.code,
|
|
@@ -671,8 +667,14 @@ export class AgentRuntime {
|
|
|
671
667
|
? forkedCheckpoint.state.agentSystemPrompt
|
|
672
668
|
: undefined);
|
|
673
669
|
try {
|
|
670
|
+
const forkState = { ...forkedCheckpoint.state };
|
|
671
|
+
delete forkState.toolSpanId;
|
|
672
|
+
delete forkState.hitlToolCallId;
|
|
674
673
|
const result = await this.deps.graphEngine.invoke(graph, {
|
|
675
|
-
|
|
674
|
+
messages: Array.isArray(forkState.messages)
|
|
675
|
+
? forkState.messages
|
|
676
|
+
: [],
|
|
677
|
+
...forkState,
|
|
676
678
|
agentVersion,
|
|
677
679
|
agentDir,
|
|
678
680
|
agentSystemPrompt,
|
|
@@ -729,6 +731,20 @@ export class AgentRuntime {
|
|
|
729
731
|
}
|
|
730
732
|
catch (err) {
|
|
731
733
|
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
734
|
+
const sessionId = typeof forkedCheckpoint.state.sessionId === 'string'
|
|
735
|
+
? forkedCheckpoint.state.sessionId
|
|
736
|
+
: undefined;
|
|
737
|
+
const threadId = resolveCheckpointThreadId({
|
|
738
|
+
sessionId,
|
|
739
|
+
taskId: task.id,
|
|
740
|
+
});
|
|
741
|
+
const ck = await this.deps.checkpointSaver.load(threadId);
|
|
742
|
+
const baseline = tokenUsagesFromLlmChannel(forkedCheckpoint.state);
|
|
743
|
+
const latest = tokenUsagesFromLlmChannel(ck?.state ?? forkedCheckpoint.state);
|
|
744
|
+
const usages = latest.length >= baseline.length ? latest.slice(baseline.length) : latest;
|
|
745
|
+
await recordTaskTokenUsages(this.deps.taskService, task.id, usages);
|
|
746
|
+
const input_tokens = usages.reduce((sum, row) => sum + row.inputTokens, 0);
|
|
747
|
+
const output_tokens = usages.reduce((sum, row) => sum + row.outputTokens, 0);
|
|
732
748
|
await this.deps.taskService.updateStatus(task.id, 'failed', message);
|
|
733
749
|
return {
|
|
734
750
|
newTraceId: traceId,
|
|
@@ -737,7 +753,11 @@ export class AgentRuntime {
|
|
|
737
753
|
checkpointId,
|
|
738
754
|
status: 'failed',
|
|
739
755
|
output: message,
|
|
740
|
-
tokenUsage: {
|
|
756
|
+
tokenUsage: {
|
|
757
|
+
input_tokens,
|
|
758
|
+
output_tokens,
|
|
759
|
+
total_tokens: input_tokens + output_tokens,
|
|
760
|
+
},
|
|
741
761
|
};
|
|
742
762
|
}
|
|
743
763
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dev 装配的 rag 依赖 — createDevRuntime 检测显式配置后自动注入
|
|
3
|
+
*
|
|
4
|
+
* 启用条件:EAP_EMBED_API_KEY 存在(缺省不注入,行为与无 rag 的 master 一致)。
|
|
5
|
+
* retriever 惰性初始化:vector-engine(better-sqlite3+sqlite-vec,native 依赖)
|
|
6
|
+
* 只在首次检索时动态 import——装配期零 native 加载,服务端部署不受影响。
|
|
7
|
+
* import 目标可用 EAP_VECTOR_ENGINE_PATH 指定绝对路径(默认解析 @eap/vector-engine;
|
|
8
|
+
* eap-cli 发版链等无该包的环境走绝对路径注入)。
|
|
9
|
+
*
|
|
10
|
+
* 环境变量(与 .local 演示脚本同一套):
|
|
11
|
+
* - EAP_EMBED_API_KEY / EAP_EMBED_BASE_URL / EAP_EMBED_MODEL / EAP_EMBED_DIMS:embedding 网关
|
|
12
|
+
* - RAG_DB:向量库路径(缺省 ~/.eap/rag/agent.db)
|
|
13
|
+
*/
|
|
14
|
+
import type { IRetriever, RagNodeDeps } from '../graph/nodes/rag/index.js';
|
|
15
|
+
/**
|
|
16
|
+
* dev rag 装配:EAP_EMBED_API_KEY 显式配置才启用,否则 undefined(不注入)。
|
|
17
|
+
* llmProvider 复用 dev 装配已构造的实例(与 llm 节点同一体系)。
|
|
18
|
+
* EAP_RAG_TRACE=1 时接 console 观测 rag.* 事件流(轮次决策/各源检索/降噪/收束),
|
|
19
|
+
* 供多源融合行为观察;缺省静默。
|
|
20
|
+
*/
|
|
21
|
+
export declare function createDevRagNodeDeps(llmProvider: RagNodeDeps['llmProvider'], loadFn?: () => Promise<IRetriever>): RagNodeDeps | undefined;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dev 装配的 rag 依赖 — createDevRuntime 检测显式配置后自动注入
|
|
3
|
+
*
|
|
4
|
+
* 启用条件:EAP_EMBED_API_KEY 存在(缺省不注入,行为与无 rag 的 master 一致)。
|
|
5
|
+
* retriever 惰性初始化:vector-engine(better-sqlite3+sqlite-vec,native 依赖)
|
|
6
|
+
* 只在首次检索时动态 import——装配期零 native 加载,服务端部署不受影响。
|
|
7
|
+
* import 目标可用 EAP_VECTOR_ENGINE_PATH 指定绝对路径(默认解析 @eap/vector-engine;
|
|
8
|
+
* eap-cli 发版链等无该包的环境走绝对路径注入)。
|
|
9
|
+
*
|
|
10
|
+
* 环境变量(与 .local 演示脚本同一套):
|
|
11
|
+
* - EAP_EMBED_API_KEY / EAP_EMBED_BASE_URL / EAP_EMBED_MODEL / EAP_EMBED_DIMS:embedding 网关
|
|
12
|
+
* - RAG_DB:向量库路径(缺省 ~/.eap/rag/agent.db)
|
|
13
|
+
*/
|
|
14
|
+
import { homedir } from 'node:os';
|
|
15
|
+
import { dirname, join } from 'node:path';
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, watchFile } from 'node:fs';
|
|
17
|
+
import { pathToFileURL } from 'node:url';
|
|
18
|
+
import { appendFileSync } from 'node:fs';
|
|
19
|
+
// 惰性读 env:模块顶层读不到(runtime 经 sdk import 链早于 CLI 灌 .env.local 执行)
|
|
20
|
+
const trace = (line) => {
|
|
21
|
+
console.log(line);
|
|
22
|
+
const f = process.env.EAP_RAG_TRACE_FILE;
|
|
23
|
+
if (f)
|
|
24
|
+
appendFileSync(f, line + '\n', 'utf-8');
|
|
25
|
+
};
|
|
26
|
+
/** 入库日志环形缓冲(globalThis 桥接:Playground 服务同进程读,GET /api/rag-ingest-log) */
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
const ingestLogBuf = (globalThis.__ragIngestLog ??= []);
|
|
29
|
+
const ingestLog = (line) => {
|
|
30
|
+
const ts = new Date().toISOString().slice(11, 19);
|
|
31
|
+
ingestLogBuf.push(`[${ts}] ${line}`);
|
|
32
|
+
if (ingestLogBuf.length > 200)
|
|
33
|
+
ingestLogBuf.splice(0, ingestLogBuf.length - 200);
|
|
34
|
+
};
|
|
35
|
+
import { HttpEmbedder, LocalSqliteRetriever } from '../graph/nodes/rag/index.js';
|
|
36
|
+
/** 惰性 IRetriever:首次调用才加载真实现;加载失败不缓存,允许下轮重试 */
|
|
37
|
+
class LazyLocalRetriever {
|
|
38
|
+
load;
|
|
39
|
+
sourceId = 'local';
|
|
40
|
+
implPromise;
|
|
41
|
+
constructor(load) {
|
|
42
|
+
this.load = load;
|
|
43
|
+
}
|
|
44
|
+
ensure() {
|
|
45
|
+
this.implPromise ??= this.load().catch((err) => {
|
|
46
|
+
this.implPromise = undefined; // 失败不缓存,下次调用重试加载
|
|
47
|
+
throw err;
|
|
48
|
+
});
|
|
49
|
+
return this.implPromise;
|
|
50
|
+
}
|
|
51
|
+
async retrieve(query, opts) {
|
|
52
|
+
return (await this.ensure()).retrieve(query, opts);
|
|
53
|
+
}
|
|
54
|
+
async retrieveByKeyword(query, opts) {
|
|
55
|
+
const impl = await this.ensure();
|
|
56
|
+
if (!impl.retrieveByKeyword)
|
|
57
|
+
return [];
|
|
58
|
+
return impl.retrieveByKeyword(query, opts);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** 默认加载器:HttpEmbedder + vector-engine + LocalSqliteRetriever(单库:RAG_DB) */
|
|
62
|
+
async function loadLocalRetriever() {
|
|
63
|
+
const dims = Number(process.env.EAP_EMBED_DIMS ?? 1024);
|
|
64
|
+
const embedder = new HttpEmbedder({
|
|
65
|
+
baseUrl: String(process.env.EAP_EMBED_BASE_URL ?? 'https://gtaiapi.gtcloud.cn'),
|
|
66
|
+
model: String(process.env.EAP_EMBED_MODEL ?? 'Qwen3-Embedding-8B'),
|
|
67
|
+
apiKey: String(process.env.EAP_EMBED_API_KEY ?? ''),
|
|
68
|
+
dims,
|
|
69
|
+
dimensions: dims,
|
|
70
|
+
});
|
|
71
|
+
const moduleName = process.env.EAP_VECTOR_ENGINE_PATH ?? '@eap/vector-engine';
|
|
72
|
+
// Windows 绝对路径须转 file:// URL(盘符字母否则被解析为 URL scheme)
|
|
73
|
+
const spec = /^[a-zA-Z]:[\\/]/.test(moduleName) ? pathToFileURL(moduleName).href : moduleName;
|
|
74
|
+
const mod = (await import(spec));
|
|
75
|
+
const dbPath = process.env.RAG_DB ?? join(homedir(), '.eap', 'rag', 'agent.db');
|
|
76
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
77
|
+
const engine = await mod.createAsyncEngine({ dbPath, dims });
|
|
78
|
+
// 自动向量化:EAP_RAG_AUTO_INGEST=1 时常驻调度器扫授权目录,文件放入即入库
|
|
79
|
+
// (写入侧归宿主/桌面语义;dev 演示用轮询,生产为授权事件触发)。
|
|
80
|
+
// RAG_DOCS_DIR 支持 `;` 分隔多目录——"授权一个新文件夹" = 追加一个路径。
|
|
81
|
+
//
|
|
82
|
+
// 授权清单热更新(客户端授权动作的 dev 模拟):RAG_AUTHORIZED_DIRS_FILE(json
|
|
83
|
+
// {"dirs":[...]},缺省 <库目录>/authorized-dirs.json)存在则覆盖 RAG_DOCS_DIR,
|
|
84
|
+
// 文件变更 → scheduler.updateDirs 即时生效(新目录入库/移除软删),全程不重启。
|
|
85
|
+
if (process.env.EAP_RAG_AUTO_INGEST) {
|
|
86
|
+
const envDirs = (process.env.RAG_DOCS_DIR ?? join(dirname(dbPath), 'docs'))
|
|
87
|
+
.split(';')
|
|
88
|
+
.map((s) => s.trim())
|
|
89
|
+
.filter(Boolean);
|
|
90
|
+
const authFile = process.env.RAG_AUTHORIZED_DIRS_FILE ?? join(dirname(dbPath), 'authorized-dirs.json');
|
|
91
|
+
const readAuthDirs = () => {
|
|
92
|
+
try {
|
|
93
|
+
const parsed = JSON.parse(readFileSync(authFile, 'utf-8'));
|
|
94
|
+
if (Array.isArray(parsed.dirs) && parsed.dirs.every((d) => typeof d === 'string')) {
|
|
95
|
+
const dirs = parsed.dirs.map((d) => d.trim()).filter(Boolean);
|
|
96
|
+
return dirs.length ? dirs : undefined; // 空清单视为无效,防误清库
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// 文件不存在/解析失败:保持现清单
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
};
|
|
104
|
+
let currentDirs = readAuthDirs() ?? envDirs;
|
|
105
|
+
const scheduler = new mod.IngestScheduler({
|
|
106
|
+
engine,
|
|
107
|
+
embedder,
|
|
108
|
+
userId: process.env.RAG_USER ?? 'anonymous',
|
|
109
|
+
dirs: currentDirs,
|
|
110
|
+
intervalMs: Number(process.env.EAP_RAG_SCAN_INTERVAL_MS ?? 30_000),
|
|
111
|
+
watch: process.env.EAP_RAG_SCAN_MODE !== 'interval',
|
|
112
|
+
onEvent: (e) => {
|
|
113
|
+
if (e.type === 'file.ingested') {
|
|
114
|
+
const line = `[rag-ingest] 📥 ${String(e['filePath']).split('\\').pop()} → ${e['chunks']} 片`;
|
|
115
|
+
console.log(line);
|
|
116
|
+
ingestLog(line);
|
|
117
|
+
}
|
|
118
|
+
else if (e.type === 'file.failed') {
|
|
119
|
+
const line = `[rag-ingest] ✗ ${String(e['filePath']).split('\\').pop()}: ${e['error']}`;
|
|
120
|
+
console.log(line);
|
|
121
|
+
ingestLog(line);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
scheduler.start();
|
|
126
|
+
ingestLog(`🚀 自动向量化已启动:${currentDirs.length} 个授权目录`);
|
|
127
|
+
console.log(`[rag-ingest] 自动向量化已启动:目录 ${currentDirs.join('; ')}(${Number(process.env.EAP_RAG_SCAN_INTERVAL_MS ?? 30_000) / 1000}s 轮询${existsSync(authFile) ? `,授权清单 ${authFile} 热更新` : ''})`);
|
|
128
|
+
// 授权清单文件变更 → updateDirs(编辑器保存/前端写入均触发;stat 轮询最稳)
|
|
129
|
+
watchFile(authFile, { interval: 2000 }, () => {
|
|
130
|
+
const next = readAuthDirs();
|
|
131
|
+
if (next && next.join(';') !== currentDirs.join(';')) {
|
|
132
|
+
currentDirs = next;
|
|
133
|
+
scheduler.updateDirs(next);
|
|
134
|
+
const line = `[rag-ingest] 🔑 授权目录更新(免重启生效):${next.length} 个目录`;
|
|
135
|
+
console.log(line + `(${next.join('; ')})`);
|
|
136
|
+
ingestLog(line);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return new LocalSqliteRetriever({ engine, embedder });
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* dev rag 装配:EAP_EMBED_API_KEY 显式配置才启用,否则 undefined(不注入)。
|
|
144
|
+
* llmProvider 复用 dev 装配已构造的实例(与 llm 节点同一体系)。
|
|
145
|
+
* EAP_RAG_TRACE=1 时接 console 观测 rag.* 事件流(轮次决策/各源检索/降噪/收束),
|
|
146
|
+
* 供多源融合行为观察;缺省静默。
|
|
147
|
+
*/
|
|
148
|
+
export function createDevRagNodeDeps(llmProvider, loadFn = loadLocalRetriever) {
|
|
149
|
+
if (!process.env.EAP_EMBED_API_KEY)
|
|
150
|
+
return undefined;
|
|
151
|
+
const fmt = (v, n = 40) => String(v ?? '').replace(/\s+/g, ' ').slice(0, n);
|
|
152
|
+
const emitEvent = process.env.EAP_RAG_TRACE
|
|
153
|
+
? (type, p) => {
|
|
154
|
+
if (type === 'rag.round') {
|
|
155
|
+
const calls = (p['toolCalls'] ?? [])
|
|
156
|
+
.map((t) => `${t.name}(${JSON.stringify(t.arguments)})`).join(' ');
|
|
157
|
+
trace(`[rag] 第${p['round']}轮: ${calls}`);
|
|
158
|
+
}
|
|
159
|
+
else if (type === 'rag.search') {
|
|
160
|
+
const head = p['kind'] === 'kb' ? `kb:${p['toolName']}` : `local:"${fmt(p['query'], 20)}"`;
|
|
161
|
+
trace(`[rag] search ${head} topK=${p['topK']} → hits=${p['hits']} ${p['durationMs']}ms${p['success'] ? '' : ` ✗${p['error']}`}${p['scope'] ? ` scope=${p['scope']}` : ''}`);
|
|
162
|
+
}
|
|
163
|
+
else if (type === 'rag.denoise') {
|
|
164
|
+
trace(`[rag] 降噪 候选${p['candidates']} → 保留${p['kept']}`);
|
|
165
|
+
for (const f of p['filtered'] ?? [])
|
|
166
|
+
trace(`[rag] 滤 ${fmt(f.rawId, 30)}(${fmt(f.reason, 30)})`);
|
|
167
|
+
}
|
|
168
|
+
else if (type === 'rag.rewrite') {
|
|
169
|
+
trace(`[rag] 空结果重写 "${fmt(p['from'], 20)}" → "${fmt(p['to'], 20)}"`);
|
|
170
|
+
}
|
|
171
|
+
else if (type === 'rag.finish') {
|
|
172
|
+
trace(`[rag] 收束: ${p['rounds']}轮 最终${p['docs']}条${p['empty'] ? '(空)' : ''}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
: undefined;
|
|
176
|
+
// 预热:自动向量化开启时立即后台装配一次——调度器/授权热更新/入库日志不等到首次
|
|
177
|
+
// local_search 才启动(演示"先授权后提问"场景需要授权即生效)。失败静默,检索时重试。
|
|
178
|
+
if (process.env.EAP_RAG_AUTO_INGEST) {
|
|
179
|
+
void Promise.resolve(loadFn()).catch(() => undefined);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
llmProvider,
|
|
183
|
+
retrievers: [new LazyLocalRetriever(loadFn)],
|
|
184
|
+
emitEvent,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
@@ -28,6 +28,19 @@ import { type AgentRuntime } from './agent-runtime.js';
|
|
|
28
28
|
import type { RuntimeStorageProfile } from './storage-profile.js';
|
|
29
29
|
import type { HitlResponse, ResumeHitlResult, SSEEvent } from './types.js';
|
|
30
30
|
import type { ILlmTurnLog } from '../debug/llm-turn-log.js';
|
|
31
|
+
import type { ExecutionInterceptors } from '../execution/interceptors.js';
|
|
32
|
+
import { LongTermMemoryService } from '../memory/long-term-memory.service.js';
|
|
33
|
+
export interface CreateDevRuntimeOptions {
|
|
34
|
+
interceptors?: ExecutionInterceptors;
|
|
35
|
+
compression?: {
|
|
36
|
+
enabled?: boolean;
|
|
37
|
+
persistCompressed?: boolean;
|
|
38
|
+
};
|
|
39
|
+
memory?: {
|
|
40
|
+
enabled?: boolean;
|
|
41
|
+
service?: LongTermMemoryService;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
31
44
|
/** createDevRuntime 返回值(CLI 嵌入用,可选字段后续扩展) */
|
|
32
45
|
export interface DevRuntime {
|
|
33
46
|
invokeOrchestrator: InvokeOrchestrator;
|
|
@@ -63,6 +76,8 @@ export interface DevRuntime {
|
|
|
63
76
|
llmTurnKind: 'file' | 'postgres';
|
|
64
77
|
foundationConfigured: boolean;
|
|
65
78
|
skillHotReload: boolean;
|
|
79
|
+
/** rag 节点依赖已注入(EAP_EMBED_API_KEY 显式配置时 true) */
|
|
80
|
+
ragEnabled: boolean;
|
|
66
81
|
};
|
|
67
82
|
}
|
|
68
83
|
/**
|
|
@@ -71,4 +86,4 @@ export interface DevRuntime {
|
|
|
71
86
|
* @returns DevRuntime(含 invokeOrchestrator / streamOrchestrator / 诊断摘要)
|
|
72
87
|
* @throws EAP_LLM_MODE=local 但解不出任何密钥时抛错,避免静默 Echo
|
|
73
88
|
*/
|
|
74
|
-
export declare function createDevRuntime(): DevRuntime;
|
|
89
|
+
export declare function createDevRuntime(options?: CreateDevRuntimeOptions): DevRuntime;
|