@gt-fe/eap-runtime 0.0.19 → 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 +57 -24
- package/dist/api/handlers/stream.handler.d.ts +6 -0
- package/dist/api/handlers/stream.handler.js +59 -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/contracts/index.d.ts +1 -1
- package/dist/contracts/trace-collector.d.ts +16 -8
- 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 +26 -11
- package/dist/graph/adapters/langgraph.adapter.js +178 -83
- package/dist/graph/adapters/skill-entry-span.d.ts +37 -0
- package/dist/graph/adapters/skill-entry-span.js +78 -0
- package/dist/graph/adapters/skill-isolate.d.ts +17 -0
- package/dist/graph/adapters/skill-isolate.js +50 -0
- package/dist/graph/adapters/skill-read.d.ts +10 -0
- package/dist/graph/adapters/skill-read.js +33 -0
- package/dist/graph/adapters/subgraph-compiler.d.ts +7 -2
- package/dist/graph/adapters/subgraph-compiler.js +81 -35
- 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 +25 -1
- package/dist/graph/nodes/tool-node.js +193 -35
- package/dist/graph/skill-audit-recorder.d.ts +4 -4
- package/dist/graph/skill-audit-recorder.js +3 -3
- package/dist/graph/trace-span.d.ts +2 -9
- package/dist/graph/trace-span.js +1 -0
- package/dist/index.d.ts +3 -1
- 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 +105 -63
- 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/sse/trace-collector.d.ts +2 -9
- package/dist/token/index.d.ts +3 -0
- package/dist/token/index.js +2 -0
- package/dist/token/record-task-token-usage.d.ts +23 -0
- package/dist/token/record-task-token-usage.js +76 -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 +4 -2
- package/dist/token/token-stats.service.js +5 -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 +3 -2
- package/dist/tool/index.js +3 -2
- package/dist/tool/mcp-list.d.ts +12 -0
- package/dist/tool/mcp-list.js +15 -0
- 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
|
@@ -2,6 +2,16 @@ export interface SkillDirRef {
|
|
|
2
2
|
skillId: string;
|
|
3
3
|
bundlePath: string;
|
|
4
4
|
}
|
|
5
|
+
/** 技能包落点:Agent 预装 / 用户装配 / 开发态 overlay。 */
|
|
6
|
+
export type SkillSpace = 'agent' | 'user' | 'local';
|
|
7
|
+
/**
|
|
8
|
+
* 按实际 bundle 路径判定空间。有 agentDir 时相对预装根与 users/ 判定;
|
|
9
|
+
* 否则看路径段 `preinstall/skills` / `users/<id>/skills`。
|
|
10
|
+
*/
|
|
11
|
+
export declare function classifySkillSpace(bundleOrFilePath: string, opts?: {
|
|
12
|
+
agentDir?: string;
|
|
13
|
+
userId?: string;
|
|
14
|
+
}): SkillSpace | undefined;
|
|
5
15
|
/**
|
|
6
16
|
* 判断 harness 读操作是否命中技能包,并用 bundlePath 最长前缀解析 skillId。
|
|
7
17
|
*/
|
|
@@ -33,6 +33,39 @@ function skillIdFromSkillsSegment(norm) {
|
|
|
33
33
|
const match = norm.match(/(?:^|\/)(?:preinstall\/)?skills\/([^/]+)(?:\/|$)/i);
|
|
34
34
|
return match?.[1] ?? '';
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* 按实际 bundle 路径判定空间。有 agentDir 时相对预装根与 users/ 判定;
|
|
38
|
+
* 否则看路径段 `preinstall/skills` / `users/<id>/skills`。
|
|
39
|
+
*/
|
|
40
|
+
export function classifySkillSpace(bundleOrFilePath, opts) {
|
|
41
|
+
if (!bundleOrFilePath)
|
|
42
|
+
return undefined;
|
|
43
|
+
const target = normalizeForMatch(bundleOrFilePath);
|
|
44
|
+
const agentDir = opts?.agentDir?.trim();
|
|
45
|
+
if (agentDir) {
|
|
46
|
+
const root = normalizeForMatch(agentDir);
|
|
47
|
+
const pre = `${root}/preinstall/skills`;
|
|
48
|
+
if (target === pre || target.startsWith(`${pre}/`))
|
|
49
|
+
return 'agent';
|
|
50
|
+
const uid = opts?.userId?.trim();
|
|
51
|
+
if (uid) {
|
|
52
|
+
const user = `${root}/users/${uid}/skills`;
|
|
53
|
+
if (target === user || target.startsWith(`${user}/`))
|
|
54
|
+
return 'user';
|
|
55
|
+
}
|
|
56
|
+
if (new RegExp(`^${escapeRegExp(root)}/users/[^/]+/skills(/|$)`).test(target))
|
|
57
|
+
return 'user';
|
|
58
|
+
return 'local';
|
|
59
|
+
}
|
|
60
|
+
if (/(^|\/)preinstall\/skills(\/|$)/i.test(target))
|
|
61
|
+
return 'agent';
|
|
62
|
+
if (/(^|\/)users\/[^/]+\/skills(\/|$)/i.test(target))
|
|
63
|
+
return 'user';
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
function escapeRegExp(value) {
|
|
67
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
68
|
+
}
|
|
36
69
|
/**
|
|
37
70
|
* 判断 harness 读操作是否命中技能包,并用 bundlePath 最长前缀解析 skillId。
|
|
38
71
|
*/
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 将 Skill DSL 编译为子图节点函数,嵌入父 Graph 执行。
|
|
5
5
|
* 子图作为独立 StateGraph 编译,父图通过节点函数调用子图并合并输出。
|
|
6
|
+
* Skill 子图不得再含 type=subgraph(G-3 / B14)。
|
|
6
7
|
*
|
|
7
8
|
* @see design.md §1.4 子图嵌套: Skill作为子图嵌入主Graph
|
|
8
9
|
* @see requirements.md Requirement 1 AC2 — 子图嵌套(Skill作为子图)
|
|
@@ -24,16 +25,20 @@ type LangGraphState = Record<string, unknown>;
|
|
|
24
25
|
* 4. 执行时,子图内部运行完整的 graph invoke 流程,输出合并回父图 state
|
|
25
26
|
*/
|
|
26
27
|
export declare class SubGraphCompiler {
|
|
28
|
+
private readonly auditReporter?;
|
|
27
29
|
private readonly adapter;
|
|
28
30
|
private readonly governanceClient?;
|
|
29
31
|
private readonly skillAuditRecorder?;
|
|
30
|
-
constructor(adapter?: LangGraphAdapter, governanceClient?: IGovernanceClient, skillAuditRecorder?: SkillAuditRecorder);
|
|
32
|
+
constructor(adapter?: LangGraphAdapter, governanceClient?: IGovernanceClient, skillAuditRecorder?: SkillAuditRecorder, auditReporter?: import("../../governance/audit-reporter.js").IAuditReporter | undefined);
|
|
31
33
|
/**
|
|
32
34
|
* 将 Skill DSL 编译为可嵌入父图的 NodeFunction
|
|
33
35
|
*
|
|
34
36
|
* @param dsl - Skill 的 GraphDSL 定义
|
|
35
37
|
* @param skillId - Skill 资源 ID(用于 checkPermission skill:use)
|
|
36
38
|
*/
|
|
37
|
-
compileSubGraph(dsl: GraphDSL, skillId?: string
|
|
39
|
+
compileSubGraph(dsl: GraphDSL, skillId?: string, options?: {
|
|
40
|
+
isolateContext?: boolean;
|
|
41
|
+
parentNodeId?: string;
|
|
42
|
+
}): NodeFunction<LangGraphState>;
|
|
38
43
|
}
|
|
39
44
|
export {};
|
|
@@ -3,17 +3,22 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 将 Skill DSL 编译为子图节点函数,嵌入父 Graph 执行。
|
|
5
5
|
* 子图作为独立 StateGraph 编译,父图通过节点函数调用子图并合并输出。
|
|
6
|
+
* Skill 子图不得再含 type=subgraph(G-3 / B14)。
|
|
6
7
|
*
|
|
7
8
|
* @see design.md §1.4 子图嵌套: Skill作为子图嵌入主Graph
|
|
8
9
|
* @see requirements.md Requirement 1 AC2 — 子图嵌套(Skill作为子图)
|
|
9
10
|
*/
|
|
11
|
+
import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
|
|
10
12
|
import { assertSkillPermission } from '../../governance/permission-guard.js';
|
|
11
13
|
import { LangGraphAdapter } from './langgraph.adapter.js';
|
|
14
|
+
import { assertSkillGraphHasNoSubgraphNodes } from './graph-nesting.js';
|
|
12
15
|
import { resolveExecutionTrace, warnTraceSpanFailure, } from '../execution-trace-context.js';
|
|
16
|
+
import { skillSpanFields, skillTraceIdentity } from './skill-entry-span.js';
|
|
13
17
|
import { filterLoadedToolsForSkill, } from '../../tool/system-prompt-builder.js';
|
|
14
18
|
import { unwrapHitlPaused } from '../hitl/hitl-handler.js';
|
|
15
19
|
import { pickContextFields } from '../../token/context-window.js';
|
|
16
20
|
import { assertSkillIoIfEnabled, coerceSkillInputFromMessages, } from '../../registry/skill-io-guard.js';
|
|
21
|
+
import { isolatedSkillMessages, isolatedSkillOutput, SKILL_ISOLATE_HITL_MESSAGE, } from './skill-isolate.js';
|
|
17
22
|
/**
|
|
18
23
|
* SubGraphCompiler — 子图编译器
|
|
19
24
|
*
|
|
@@ -24,10 +29,12 @@ import { assertSkillIoIfEnabled, coerceSkillInputFromMessages, } from '../../reg
|
|
|
24
29
|
* 4. 执行时,子图内部运行完整的 graph invoke 流程,输出合并回父图 state
|
|
25
30
|
*/
|
|
26
31
|
export class SubGraphCompiler {
|
|
32
|
+
auditReporter;
|
|
27
33
|
adapter;
|
|
28
34
|
governanceClient;
|
|
29
35
|
skillAuditRecorder;
|
|
30
|
-
constructor(adapter, governanceClient, skillAuditRecorder) {
|
|
36
|
+
constructor(adapter, governanceClient, skillAuditRecorder, auditReporter) {
|
|
37
|
+
this.auditReporter = auditReporter;
|
|
31
38
|
this.adapter = adapter ?? new LangGraphAdapter();
|
|
32
39
|
this.governanceClient = governanceClient;
|
|
33
40
|
this.skillAuditRecorder = skillAuditRecorder;
|
|
@@ -38,14 +45,23 @@ export class SubGraphCompiler {
|
|
|
38
45
|
* @param dsl - Skill 的 GraphDSL 定义
|
|
39
46
|
* @param skillId - Skill 资源 ID(用于 checkPermission skill:use)
|
|
40
47
|
*/
|
|
41
|
-
compileSubGraph(dsl, skillId) {
|
|
48
|
+
compileSubGraph(dsl, skillId, options) {
|
|
49
|
+
assertSkillGraphHasNoSubgraphNodes(dsl, skillId ?? dsl.name);
|
|
42
50
|
const adapter = this.adapter;
|
|
51
|
+
const isolateContext = options?.isolateContext === true;
|
|
43
52
|
const resolvedSkillId = skillId ?? dsl.name ?? '';
|
|
44
53
|
const subGraphNodeFn = async (state) => {
|
|
45
|
-
const executionContext = state['executionContext']
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
const executionContext = state['executionContext'] ?? {
|
|
55
|
+
userId: String(state['userId'] ?? ''),
|
|
56
|
+
callerType: 'user',
|
|
57
|
+
roles: [],
|
|
58
|
+
permissions: [],
|
|
59
|
+
delegationToken: String(state['delegationToken'] ?? ''),
|
|
60
|
+
correlationId: String(state['correlationId'] ?? ''),
|
|
61
|
+
};
|
|
62
|
+
await assertSkillPermission(this.governanceClient, executionContext, resolvedSkillId, {
|
|
63
|
+
auditReporter: this.auditReporter,
|
|
64
|
+
});
|
|
49
65
|
const existingSpanId = typeof state['skillSpanId'] === 'string' && state['skillSpanId']
|
|
50
66
|
? state['skillSpanId']
|
|
51
67
|
: '';
|
|
@@ -63,6 +79,7 @@ export class SubGraphCompiler {
|
|
|
63
79
|
// 用父图这次调用上的 collector;内层 invoke 只补 collector/traceId,沿用外层流式袋。
|
|
64
80
|
const { collector: traceCollector, traceId } = resolveExecutionTrace(state);
|
|
65
81
|
const skillName = dsl.name || resolvedSkillId;
|
|
82
|
+
const identity = skillTraceIdentity(state, resolvedSkillId);
|
|
66
83
|
if (!resuming) {
|
|
67
84
|
adapter.emitStreamEvent({
|
|
68
85
|
type: 'skill.started',
|
|
@@ -71,19 +88,27 @@ export class SubGraphCompiler {
|
|
|
71
88
|
timestamp: new Date().toISOString(),
|
|
72
89
|
payload: {
|
|
73
90
|
skillId: resolvedSkillId,
|
|
74
|
-
skillName,
|
|
91
|
+
skillName: identity.skillName || skillName,
|
|
92
|
+
...(identity.skillSpace ? { skillSpace: identity.skillSpace } : {}),
|
|
93
|
+
...(identity.skillVersion ? { skillVersion: identity.skillVersion } : {}),
|
|
75
94
|
},
|
|
76
95
|
});
|
|
77
96
|
}
|
|
78
97
|
let skillSpanId = existingSpanId || null;
|
|
79
98
|
if (!resuming && traceCollector && traceId) {
|
|
80
99
|
try {
|
|
100
|
+
const fields = skillSpanFields(identity, { source: 'subgraph' });
|
|
81
101
|
skillSpanId = await traceCollector.createNode({
|
|
82
102
|
traceId,
|
|
83
103
|
name: resolvedSkillId || dsl.name || 'skill',
|
|
84
104
|
type: 'skill',
|
|
85
105
|
agentCode: state['agentCode'] ?? state['agentId'],
|
|
86
|
-
|
|
106
|
+
...(fields.version ? { version: fields.version } : {}),
|
|
107
|
+
input: fields.input,
|
|
108
|
+
metadata: {
|
|
109
|
+
taskId: state['taskId'],
|
|
110
|
+
correlationId: state['correlationId'],
|
|
111
|
+
},
|
|
87
112
|
});
|
|
88
113
|
}
|
|
89
114
|
catch (err) {
|
|
@@ -109,26 +134,32 @@ export class SubGraphCompiler {
|
|
|
109
134
|
if (skillInfo) {
|
|
110
135
|
assertSkillIoIfEnabled(skillInfo, skillInput);
|
|
111
136
|
}
|
|
137
|
+
const parentMessages = messages.map((msg) => ({
|
|
138
|
+
role: msg['role'] ?? 'user',
|
|
139
|
+
content: msg['content'] ?? '',
|
|
140
|
+
toolCalls: msg['tool_calls'] ?? msg['toolCalls'],
|
|
141
|
+
...(typeof msg['toolCallId'] === 'string' ? { toolCallId: msg['toolCallId'] } : {}),
|
|
142
|
+
...(typeof msg['toolName'] === 'string' ? { toolName: msg['toolName'] } : {}),
|
|
143
|
+
...(typeof msg['success'] === 'boolean' ? { success: msg['success'] } : {}),
|
|
144
|
+
...(typeof msg['reasoningContent'] === 'string'
|
|
145
|
+
? { reasoningContent: msg['reasoningContent'] }
|
|
146
|
+
: {}),
|
|
147
|
+
}));
|
|
148
|
+
const subMessagesIn = isolateContext
|
|
149
|
+
? isolatedSkillMessages(parentMessages, skillInput)
|
|
150
|
+
: parentMessages;
|
|
112
151
|
const subGraphState = {
|
|
113
|
-
messages:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
toolCalls: msg['tool_calls'] ?? msg['toolCalls'],
|
|
117
|
-
...(typeof msg['toolCallId'] === 'string' ? { toolCallId: msg['toolCallId'] } : {}),
|
|
118
|
-
...(typeof msg['toolName'] === 'string' ? { toolName: msg['toolName'] } : {}),
|
|
119
|
-
...(typeof msg['success'] === 'boolean' ? { success: msg['success'] } : {}),
|
|
120
|
-
...(typeof msg['reasoningContent'] === 'string'
|
|
121
|
-
? { reasoningContent: msg['reasoningContent'] }
|
|
122
|
-
: {}),
|
|
123
|
-
})),
|
|
124
|
-
// 子图与父图共用 session thread;入口必须 __set,否则 checkpointer 会把父图 messages 再 append 一遍
|
|
125
|
-
replaceCheckpointMessages: true,
|
|
152
|
+
messages: subMessagesIn,
|
|
153
|
+
// 默认同父 session thread;隔离时不写父 Checkpoint,避免短历史覆盖父对话
|
|
154
|
+
replaceCheckpointMessages: !isolateContext,
|
|
126
155
|
userId,
|
|
127
156
|
delegationToken,
|
|
128
157
|
agentCode,
|
|
129
158
|
taskId: state['taskId'],
|
|
130
159
|
correlationId: state['correlationId'],
|
|
131
|
-
sessionId: state['sessionId'],
|
|
160
|
+
sessionId: isolateContext ? undefined : state['sessionId'],
|
|
161
|
+
// ToolNode 用此标志在 interrupt 前拒绝 HITL
|
|
162
|
+
skillIsolateContext: isolateContext || undefined,
|
|
132
163
|
executionContext: state['executionContext'],
|
|
133
164
|
loadedTools: scopedTools,
|
|
134
165
|
loadedSkills: state['loadedSkills'],
|
|
@@ -146,7 +177,7 @@ export class SubGraphCompiler {
|
|
|
146
177
|
hitlRespondedBy: state['hitlRespondedBy'],
|
|
147
178
|
};
|
|
148
179
|
const result = await adapter.runIsolatedLlmStreamFlags(() => adapter.invoke(subGraph, subGraphState));
|
|
149
|
-
//
|
|
180
|
+
// 进程内调试记录;生产记录是上面的 skill Span
|
|
150
181
|
await auditCtx?.finish({
|
|
151
182
|
status: 'success',
|
|
152
183
|
tokenUsage: result.tokenUsage,
|
|
@@ -182,19 +213,26 @@ export class SubGraphCompiler {
|
|
|
182
213
|
...(reasoningContent ? { reasoningContent } : {}),
|
|
183
214
|
},
|
|
184
215
|
});
|
|
185
|
-
const added = subMessages.slice(
|
|
186
|
-
const outputMessages =
|
|
187
|
-
? added
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
216
|
+
const added = subMessages.slice(subMessagesIn.length);
|
|
217
|
+
const outputMessages = isolateContext
|
|
218
|
+
? isolatedSkillOutput(added.length > 0
|
|
219
|
+
? added
|
|
220
|
+
: result.output
|
|
221
|
+
? [{ role: 'assistant', content: result.output }]
|
|
222
|
+
: [])
|
|
223
|
+
: added.length > 0
|
|
224
|
+
? added
|
|
225
|
+
: result.output
|
|
226
|
+
? [{ role: 'assistant', content: result.output }]
|
|
227
|
+
: [];
|
|
191
228
|
const perNode = result.tokenUsage?.perNode;
|
|
192
229
|
const nestedLastSpanId = (typeof result.finalState?.lastSpanId === 'string' && result.finalState.lastSpanId) ||
|
|
193
230
|
skillSpanId ||
|
|
194
231
|
undefined;
|
|
195
232
|
return {
|
|
196
233
|
messages: outputMessages,
|
|
197
|
-
currentNode:
|
|
234
|
+
currentNode: options?.parentNodeId
|
|
235
|
+
|| (dsl.name ? `subgraph:${dsl.name}` : 'subgraph'),
|
|
198
236
|
...(nestedLastSpanId ? { lastSpanId: nestedLastSpanId } : {}),
|
|
199
237
|
...(Array.isArray(perNode) && perNode.length > 0
|
|
200
238
|
? {
|
|
@@ -211,18 +249,26 @@ export class SubGraphCompiler {
|
|
|
211
249
|
}
|
|
212
250
|
catch (err) {
|
|
213
251
|
const hitlPaused = unwrapHitlPaused(err);
|
|
214
|
-
|
|
252
|
+
// 未隔离:HITL 暂停原样上抛。隔离:转 GRAPH_EXECUTION_ERROR,走 skill.failed,不当 waiting_hitl。
|
|
253
|
+
if (hitlPaused && !isolateContext) {
|
|
215
254
|
throw err;
|
|
216
255
|
}
|
|
256
|
+
const failErr = hitlPaused && isolateContext
|
|
257
|
+
? new AppError(ErrorCodes.GRAPH_EXECUTION_ERROR, SKILL_ISOLATE_HITL_MESSAGE, {
|
|
258
|
+
exposeMessage: true,
|
|
259
|
+
cause: err,
|
|
260
|
+
})
|
|
261
|
+
: err;
|
|
262
|
+
const failMessage = failErr instanceof Error ? failErr.message : String(failErr);
|
|
217
263
|
await auditCtx?.finish({
|
|
218
264
|
status: 'failed',
|
|
219
|
-
errorMessage:
|
|
265
|
+
errorMessage: failMessage,
|
|
220
266
|
});
|
|
221
267
|
if (traceCollector && skillSpanId) {
|
|
222
268
|
try {
|
|
223
269
|
await traceCollector.completeNode(skillSpanId, {
|
|
224
270
|
status: 'failed',
|
|
225
|
-
error:
|
|
271
|
+
error: failMessage,
|
|
226
272
|
});
|
|
227
273
|
}
|
|
228
274
|
catch (completeErr) {
|
|
@@ -237,10 +283,10 @@ export class SubGraphCompiler {
|
|
|
237
283
|
payload: {
|
|
238
284
|
skillId: resolvedSkillId,
|
|
239
285
|
skillName,
|
|
240
|
-
error:
|
|
286
|
+
error: failMessage,
|
|
241
287
|
},
|
|
242
288
|
});
|
|
243
|
-
throw
|
|
289
|
+
throw failErr;
|
|
244
290
|
}
|
|
245
291
|
};
|
|
246
292
|
return subGraphNodeFn;
|
|
@@ -7,8 +7,14 @@
|
|
|
7
7
|
* @see design.md §1.4 StateGraph Contract
|
|
8
8
|
* @see requirements.md Requirement 1 AC1-AC2
|
|
9
9
|
*/
|
|
10
|
-
import type { GraphDSL as BaseGraphDSL, GraphNode as BaseGraphNode, GraphEdge as BaseGraphEdge, GraphNodeType, GraphConfig } from '@gt-fe/eap-contracts';
|
|
11
|
-
export type {
|
|
10
|
+
import type { GraphDSL as BaseGraphDSL, GraphNode as BaseGraphNode, GraphEdge as BaseGraphEdge, GraphNodeType as ContractsGraphNodeType, GraphConfig } from '@gt-fe/eap-contracts';
|
|
11
|
+
export type { GraphConfig };
|
|
12
|
+
/**
|
|
13
|
+
* GraphNodeType — runtime 侧扩展联合:contracts 官方包的 'rag' 类型尚未合入
|
|
14
|
+
* (客户 MR 跟进中),在此扩展使官方 contracts 零 patch 可用;
|
|
15
|
+
* contracts 合入后此扩展退化为无害超集,可直接删除回归 re-export。
|
|
16
|
+
*/
|
|
17
|
+
export type GraphNodeType = ContractsGraphNodeType | 'rag';
|
|
12
18
|
/**
|
|
13
19
|
* RuntimeGraphConfig — Runtime 层扩展的 Graph 配置
|
|
14
20
|
*
|
|
@@ -26,7 +32,9 @@ export interface RuntimeGraphConfig extends GraphConfig {
|
|
|
26
32
|
* - HITL 标记(interrupt_before / interrupt_after)
|
|
27
33
|
* - 描述信息(调试/Trace 用)
|
|
28
34
|
*/
|
|
29
|
-
export interface GraphNode extends BaseGraphNode {
|
|
35
|
+
export interface GraphNode extends Omit<BaseGraphNode, 'type'> {
|
|
36
|
+
/** 节点类型(contracts 联合 + runtime 扩展 'rag',见 GraphNodeType) */
|
|
37
|
+
type: GraphNodeType;
|
|
30
38
|
/** 节点显示名称(调试/Trace 用) */
|
|
31
39
|
label?: string;
|
|
32
40
|
/** 节点描述 */
|
|
@@ -37,8 +45,6 @@ export interface GraphNode extends BaseGraphNode {
|
|
|
37
45
|
interrupt?: NodeInterruptConfig;
|
|
38
46
|
/** 子图引用 ID(type=subgraph 时必填) */
|
|
39
47
|
subgraphId?: string;
|
|
40
|
-
/** 并行分支定义(type=parallel 时使用) */
|
|
41
|
-
branches?: string[];
|
|
42
48
|
}
|
|
43
49
|
/**
|
|
44
50
|
* GraphEdge — 基础边定义
|
|
@@ -183,6 +189,10 @@ export interface GraphState {
|
|
|
183
189
|
executionContext?: PlatformExecutionContext;
|
|
184
190
|
/** ExecuteConfig.forceToolServer — 单次 invoke 强制走 Gateway 的 toolName */
|
|
185
191
|
forceToolServer?: string[];
|
|
192
|
+
/** 请求级检索范围(前端圈定 fileIds/dirPaths;rag 节点读取,仅本地检索源生效) */
|
|
193
|
+
ragScope?: import('../../graph/nodes/rag/types.js').RagScope;
|
|
194
|
+
/** 会话级数据源勾选(数据源 id 列表;undefined = 全部启用) */
|
|
195
|
+
ragSources?: string[];
|
|
186
196
|
/** GraphLoader 注入:toolName → 运行期 manifest 引用 */
|
|
187
197
|
loadedTools?: import('../../tool/loaded-tool-definition.js').LoadedToolMap;
|
|
188
198
|
/** GraphLoader 注入:已装配 Skill 列表 */
|
|
@@ -212,8 +222,20 @@ export interface GraphState {
|
|
|
212
222
|
* 不入 Checkpoint。
|
|
213
223
|
*/
|
|
214
224
|
traceCollector?: import('../../contracts/trace-collector.js').ITraceCollector;
|
|
225
|
+
/**
|
|
226
|
+
* 当前节点跑在 Skill 隔离子图内。为 true 时禁止 HITL interrupt
|
|
227
|
+
*(隔离不写父 session Checkpoint,确认态会丢)。
|
|
228
|
+
*/
|
|
229
|
+
skillIsolateContext?: boolean;
|
|
215
230
|
/** 当前 Skill 子图 createNode 返回的 span id;子图内 Tool span 的 parentNodeId */
|
|
216
231
|
skillSpanId?: string;
|
|
232
|
+
/**
|
|
233
|
+
* HITL 续跑:暂停时已 create 的 tool Span id。
|
|
234
|
+
* 按 hitlToolCallId 复用,禁止再 createNode。
|
|
235
|
+
*/
|
|
236
|
+
toolSpanId?: string;
|
|
237
|
+
/** 与 toolSpanId 配对的 toolCallId(同一 assistant 多 tool 时只复用这一次调用) */
|
|
238
|
+
hitlToolCallId?: string;
|
|
217
239
|
/** 最近一次 createNode 的 span id;Checkpoint 引用挂在此 Span 上 */
|
|
218
240
|
lastSpanId?: string;
|
|
219
241
|
/**
|
|
@@ -384,6 +384,8 @@ export class HitlHandler {
|
|
|
384
384
|
...cleaned,
|
|
385
385
|
hitlChoice: choice,
|
|
386
386
|
hitlRespondedBy: userId,
|
|
387
|
+
...(waiting.toolSpanId ? { toolSpanId: waiting.toolSpanId } : {}),
|
|
388
|
+
...(waiting.toolCallId ? { hitlToolCallId: waiting.toolCallId } : {}),
|
|
387
389
|
};
|
|
388
390
|
const now = new Date().toISOString();
|
|
389
391
|
await this.checkpointSaver.save(waiting.threadId, stripCheckpointCollectors({
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* @see requirements.md Requirement 1 AC6, Requirement 9
|
|
11
11
|
*/
|
|
12
12
|
import type { NodeRetryConfig, GraphMessage } from '../contracts/executable-graph.js';
|
|
13
|
+
import { type ExecutionInterceptors } from '../../execution/interceptors.js';
|
|
13
14
|
/**
|
|
14
15
|
* LLMNodeConfig — LLM 节点配置
|
|
15
16
|
*/
|
|
@@ -132,6 +133,7 @@ type LangGraphState = Record<string, unknown>;
|
|
|
132
133
|
* 5. 将 LLM 响应追加到 state.messages 并返回
|
|
133
134
|
*/
|
|
134
135
|
export declare class LLMNode {
|
|
136
|
+
private readonly interceptors?;
|
|
135
137
|
private readonly config;
|
|
136
138
|
private readonly provider;
|
|
137
139
|
private readonly retryConfig;
|
|
@@ -145,7 +147,7 @@ export declare class LLMNode {
|
|
|
145
147
|
* @param streaming - 是否启用流式输出(默认 false)
|
|
146
148
|
* @param onChunk - 流式模式下每个文本片段回调(用于 SSE response.chunk)
|
|
147
149
|
*/
|
|
148
|
-
constructor(nodeId: string, config: LLMNodeConfig, provider: ILLMProvider, streaming?: boolean, onChunk?: (chunk: string) => void);
|
|
150
|
+
constructor(nodeId: string, config: LLMNodeConfig, provider: ILLMProvider, streaming?: boolean, onChunk?: (chunk: string) => void, interceptors?: Pick<ExecutionInterceptors, "beforeModel" | "afterModel"> | undefined);
|
|
149
151
|
/**
|
|
150
152
|
* execute — StateGraph 节点函数
|
|
151
153
|
*
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
import { formatUnknownError } from '../../utils/format-error.js';
|
|
13
13
|
import { buildContextUsage, turnSectionChars } from '../../token/context-window.js';
|
|
14
14
|
import { isAbortError } from '../../tool/timeout.js';
|
|
15
|
+
import { emitRuntimeLog } from '../../common/logger.js';
|
|
16
|
+
import { decideBeforeModel, } from '../../execution/interceptors.js';
|
|
15
17
|
// ─────────────────────────────────────────────
|
|
16
18
|
// Default retry config
|
|
17
19
|
// ─────────────────────────────────────────────
|
|
@@ -38,6 +40,7 @@ const DEFAULT_RETRY_CONFIG = {
|
|
|
38
40
|
* 5. 将 LLM 响应追加到 state.messages 并返回
|
|
39
41
|
*/
|
|
40
42
|
export class LLMNode {
|
|
43
|
+
interceptors;
|
|
41
44
|
config;
|
|
42
45
|
provider;
|
|
43
46
|
retryConfig;
|
|
@@ -51,7 +54,8 @@ export class LLMNode {
|
|
|
51
54
|
* @param streaming - 是否启用流式输出(默认 false)
|
|
52
55
|
* @param onChunk - 流式模式下每个文本片段回调(用于 SSE response.chunk)
|
|
53
56
|
*/
|
|
54
|
-
constructor(nodeId, config, provider, streaming = false, onChunk) {
|
|
57
|
+
constructor(nodeId, config, provider, streaming = false, onChunk, interceptors) {
|
|
58
|
+
this.interceptors = interceptors;
|
|
55
59
|
this.nodeId = nodeId;
|
|
56
60
|
this.config = config;
|
|
57
61
|
this.provider = provider;
|
|
@@ -71,10 +75,30 @@ export class LLMNode {
|
|
|
71
75
|
const messages = this.extractMessages(state);
|
|
72
76
|
// 注入 system prompt
|
|
73
77
|
const messagesWithSystem = this.prependSystemPrompt(messages);
|
|
78
|
+
const modelInput = {
|
|
79
|
+
messages: messagesWithSystem,
|
|
80
|
+
config: this.config,
|
|
81
|
+
nodeId: this.nodeId,
|
|
82
|
+
};
|
|
83
|
+
const gate = await decideBeforeModel(this.interceptors?.beforeModel, modelInput);
|
|
84
|
+
if (gate.action === 'deny') {
|
|
85
|
+
throw new Error(gate.reason);
|
|
86
|
+
}
|
|
87
|
+
const messagesForModel = gate.messages ?? messagesWithSystem;
|
|
74
88
|
const startedAt = Date.now();
|
|
75
89
|
// 带重试的 LLM 调用
|
|
76
|
-
const response = await this.invokeWithRetry(
|
|
90
|
+
const response = await this.invokeWithRetry(messagesForModel);
|
|
77
91
|
const durationMs = Date.now() - startedAt;
|
|
92
|
+
try {
|
|
93
|
+
await this.interceptors?.afterModel?.({ ...modelInput, messages: messagesForModel }, response);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
emitRuntimeLog('warn', 'runtime.intercept', 'afterModel failed', {
|
|
97
|
+
event: 'runtime.intercept.after_model_failed',
|
|
98
|
+
nodeId: this.nodeId,
|
|
99
|
+
error: err instanceof Error ? err.message : String(err),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
78
102
|
// 构建 assistant message(保留 reasoningContent 供后续 HTTP 回传)
|
|
79
103
|
const assistantMessage = {
|
|
80
104
|
role: 'assistant',
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dedup — 确定性去重(纯函数,不经 LLM)
|
|
3
|
+
*
|
|
4
|
+
* 精确去重(源内、fuse 前):sourceId:rawId + 归一化 content hash。
|
|
5
|
+
* 约束:入参须先经收集侧(react-loop pushDocs)消除"同 id 不同 content"撞号
|
|
6
|
+
* (重编号 `${rawId}#r${n}`)——本函数 id 命中即弃仅对真重复成立;
|
|
7
|
+
* fuse 的跨源键 sourceId:rawId 同样依赖该唯一性。
|
|
8
|
+
* 近重去重(融合后、降噪判定前,M4):字符 3-gram Jaccard 超 nearDupThreshold
|
|
9
|
+
* 判近重,保留序列靠前者(design-v2 §6.3 ①;跨源候选集几乎不重叠,属低频兜底)。
|
|
10
|
+
*/
|
|
11
|
+
import type { RetrievalDoc } from './types.js';
|
|
12
|
+
export declare function dedupExact(docs: RetrievalDoc[]): RetrievalDoc[];
|
|
13
|
+
/**
|
|
14
|
+
* 归一化内容相等(与 dedupExact 内容去重同口径):"重复"判定单一出处——
|
|
15
|
+
* 收集侧(react-loop pushDocs)跳过同 id 真重复时必须与融合前去重用同一口径,
|
|
16
|
+
* 两处口径分叉会导致同 id 同排版差异的文档一处跳过一处保留。
|
|
17
|
+
*/
|
|
18
|
+
export declare function sameContent(a: string, b: string): boolean;
|
|
19
|
+
/** 近重去重:字符 n-gram Jaccard 超阈值即判近重,保留序列靠前者 */
|
|
20
|
+
export declare function dedupNearDup(docs: RetrievalDoc[], threshold?: number): RetrievalDoc[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dedup — 确定性去重(纯函数,不经 LLM)
|
|
3
|
+
*
|
|
4
|
+
* 精确去重(源内、fuse 前):sourceId:rawId + 归一化 content hash。
|
|
5
|
+
* 约束:入参须先经收集侧(react-loop pushDocs)消除"同 id 不同 content"撞号
|
|
6
|
+
* (重编号 `${rawId}#r${n}`)——本函数 id 命中即弃仅对真重复成立;
|
|
7
|
+
* fuse 的跨源键 sourceId:rawId 同样依赖该唯一性。
|
|
8
|
+
* 近重去重(融合后、降噪判定前,M4):字符 3-gram Jaccard 超 nearDupThreshold
|
|
9
|
+
* 判近重,保留序列靠前者(design-v2 §6.3 ①;跨源候选集几乎不重叠,属低频兜底)。
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
12
|
+
export function dedupExact(docs) {
|
|
13
|
+
const seenIds = new Set();
|
|
14
|
+
const seenContent = new Set();
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const d of docs) {
|
|
17
|
+
const idKey = `${d.sourceId}:${d.rawId}`;
|
|
18
|
+
const contentKey = hashContent(d.content);
|
|
19
|
+
if (seenIds.has(idKey) || seenContent.has(contentKey))
|
|
20
|
+
continue;
|
|
21
|
+
seenIds.add(idKey);
|
|
22
|
+
seenContent.add(contentKey);
|
|
23
|
+
out.push(d);
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
function hashContent(content) {
|
|
28
|
+
// 归一化:压空白后取 hash,挡"内容相同仅排版不同"
|
|
29
|
+
const normalized = content.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
30
|
+
return createHash('sha1').update(normalized).digest('hex');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 归一化内容相等(与 dedupExact 内容去重同口径):"重复"判定单一出处——
|
|
34
|
+
* 收集侧(react-loop pushDocs)跳过同 id 真重复时必须与融合前去重用同一口径,
|
|
35
|
+
* 两处口径分叉会导致同 id 同排版差异的文档一处跳过一处保留。
|
|
36
|
+
*/
|
|
37
|
+
export function sameContent(a, b) {
|
|
38
|
+
return hashContent(a) === hashContent(b);
|
|
39
|
+
}
|
|
40
|
+
/** 近重去重:字符 n-gram Jaccard 超阈值即判近重,保留序列靠前者 */
|
|
41
|
+
export function dedupNearDup(docs, threshold = 0.8) {
|
|
42
|
+
const kept = [];
|
|
43
|
+
const keptGrams = [];
|
|
44
|
+
for (const d of docs) {
|
|
45
|
+
const grams = charGrams(d.content);
|
|
46
|
+
let near = false;
|
|
47
|
+
for (const g of keptGrams) {
|
|
48
|
+
if (jaccard(grams, g) > threshold) {
|
|
49
|
+
near = true;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (near)
|
|
54
|
+
continue;
|
|
55
|
+
kept.push(d);
|
|
56
|
+
keptGrams.push(grams);
|
|
57
|
+
}
|
|
58
|
+
return kept;
|
|
59
|
+
}
|
|
60
|
+
const NGRAM_SIZE = 3;
|
|
61
|
+
function charGrams(content) {
|
|
62
|
+
const norm = content.replace(/\s+/g, ' ').trim();
|
|
63
|
+
const grams = new Set();
|
|
64
|
+
for (let i = 0; i + NGRAM_SIZE <= norm.length; i++) {
|
|
65
|
+
grams.add(norm.slice(i, i + NGRAM_SIZE));
|
|
66
|
+
}
|
|
67
|
+
return grams;
|
|
68
|
+
}
|
|
69
|
+
function jaccard(a, b) {
|
|
70
|
+
if (a.size === 0 || b.size === 0)
|
|
71
|
+
return 0;
|
|
72
|
+
let inter = 0;
|
|
73
|
+
for (const x of a)
|
|
74
|
+
if (b.has(x))
|
|
75
|
+
inter++;
|
|
76
|
+
return inter / (a.size + b.size - inter);
|
|
77
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* denoise — 降噪管线(design-v2 §6.3,M4)
|
|
3
|
+
*
|
|
4
|
+
* 数万 token 候选压成数百 token,LLM 只做判定与选择、绝不产出正文:
|
|
5
|
+
* ① 近重去重(字符 n-gram Jaccard,纯函数,确定性优先)
|
|
6
|
+
* ② 一次结构化 LLM 判定(主力模型,temp 0)→ DenoiseVerdict[]
|
|
7
|
+
* { rawId, relevant, reason, keepSentences }——rawId 为复合键 sourceId:rawId
|
|
8
|
+
* (rawId 仅源内唯一;payload 标签、verdict 匹配、filtered 项三处必须同用复合键,
|
|
9
|
+
* 裸 id 跨源碰撞会让两份文档共享同一 verdict:错滤另一源、按别家句子编号截正文)
|
|
10
|
+
* ③ 代码按句子编号拼装原文(零改写:返回文本逐字出自原文句子)
|
|
11
|
+
*
|
|
12
|
+
* 防御策略(§6.5 误滤宁保守):
|
|
13
|
+
* verdict 调用或解析失败 / 文档缺 verdict 项 / keepSentences 无有效编号 → 整篇保留;
|
|
14
|
+
* 判定存活集为空 → react-loop 走 QUERY_REWRITE 重写兜底(本模块只报 verdictEmpty)。
|
|
15
|
+
*/
|
|
16
|
+
import type { ILLMProvider } from '../llm-node.js';
|
|
17
|
+
import type { DenoiseVerdict, RagTokenUsage, RetrievalDoc } from './types.js';
|
|
18
|
+
export declare const RAG_EMPTY_MARKER = "[rag] \u672A\u627E\u5230\u9AD8\u76F8\u5173\u7ED3\u679C";
|
|
19
|
+
/** 格式化(降噪后的)文档集为最终上下文 */
|
|
20
|
+
export declare function formatContext(query: string, docs: RetrievalDoc[]): string;
|
|
21
|
+
export interface DenoiseInput {
|
|
22
|
+
query: string;
|
|
23
|
+
/** 融合后的候选文档集(可能数万 token) */
|
|
24
|
+
docs: RetrievalDoc[];
|
|
25
|
+
llmProvider: ILLMProvider;
|
|
26
|
+
/** 主力模型(与决策同款,不分档) */
|
|
27
|
+
model: string;
|
|
28
|
+
nodeId: string;
|
|
29
|
+
/** 近重判定阈值,缺省 0.8 */
|
|
30
|
+
nearDupThreshold?: number;
|
|
31
|
+
tokenUsage: RagTokenUsage[];
|
|
32
|
+
}
|
|
33
|
+
export interface DenoiseResult {
|
|
34
|
+
/** 存活文档(正文已按 keepSentences 截取) */
|
|
35
|
+
docs: RetrievalDoc[];
|
|
36
|
+
contextText: string;
|
|
37
|
+
/** 判定后存活集是否为空(react-loop 据此触发重写兜底) */
|
|
38
|
+
verdictEmpty: boolean;
|
|
39
|
+
/** 被滤文档与原因(空结果重写定向 / rag.denoise 事件用);rawId 为复合键 sourceId:rawId(rewrite 归因整键匹配用) */
|
|
40
|
+
filtered: Array<{
|
|
41
|
+
rawId: string;
|
|
42
|
+
reason: string;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
export declare function denoise(input: DenoiseInput): Promise<DenoiseResult>;
|
|
46
|
+
export declare function rewriteQuery(input: {
|
|
47
|
+
query: string;
|
|
48
|
+
reasons: string[];
|
|
49
|
+
llmProvider: ILLMProvider;
|
|
50
|
+
model: string;
|
|
51
|
+
nodeId: string;
|
|
52
|
+
tokenUsage: RagTokenUsage[];
|
|
53
|
+
}): Promise<string | undefined>;
|
|
54
|
+
/** 确定性分句:按中英文句终符与换行切,句符随句保留(零改写:句子为原文子串) */
|
|
55
|
+
export declare function splitSentences(content: string): string[];
|
|
56
|
+
/** 解析判定输出:剥代码围栏、截取首尾方括号;解析失败返回 undefined(保守放行全部)。
|
|
57
|
+
* rawId 不校验复合键形态——LLM 抄错(裸 id/截断)时 find 不中,该文档无 verdict 即整篇保留,仍走保守路径 */
|
|
58
|
+
export declare function parseVerdicts(text: string): DenoiseVerdict[] | undefined;
|