@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
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* 执行期 Tool 权限守卫
|
|
3
3
|
*/
|
|
4
4
|
import type { ExecutionContext, PermissionDenyReason } from '@gt-fe/eap-contracts';
|
|
5
|
-
import type
|
|
5
|
+
import { type IGovernanceClient } from './governance-client.js';
|
|
6
|
+
import { type IAuditReporter } from './audit-reporter.js';
|
|
6
7
|
export declare class GovernanceUnavailableError extends Error {
|
|
7
8
|
readonly reason: PermissionDenyReason;
|
|
8
9
|
constructor(message?: string);
|
|
@@ -15,6 +16,9 @@ export declare function permissionMessage(kind: 'tool' | 'skill', reason: Permis
|
|
|
15
16
|
export declare function assertToolPermission(client: IGovernanceClient | undefined, ctx: ExecutionContext, toolId: string, options?: {
|
|
16
17
|
dataScope?: 'user-bound' | 'department-bound' | 'global';
|
|
17
18
|
resourceOwnerId?: string;
|
|
19
|
+
auditReporter?: IAuditReporter;
|
|
18
20
|
}): Promise<void>;
|
|
19
21
|
/** Skill 激活守卫(04 R15.3) */
|
|
20
|
-
export declare function assertSkillPermission(client: IGovernanceClient | undefined, ctx: ExecutionContext, skillId: string
|
|
22
|
+
export declare function assertSkillPermission(client: IGovernanceClient | undefined, ctx: ExecutionContext, skillId: string, options?: {
|
|
23
|
+
auditReporter?: IAuditReporter;
|
|
24
|
+
}): Promise<void>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 执行期 Tool 权限守卫
|
|
3
3
|
*/
|
|
4
|
+
import { AllowAllGovernanceClient } from './governance-client.js';
|
|
5
|
+
import { reportPermissionDenial } from './audit-reporter.js';
|
|
4
6
|
export class GovernanceUnavailableError extends Error {
|
|
5
7
|
reason = 'GOVERNANCE_UNAVAILABLE';
|
|
6
8
|
constructor(message = '治理服务不可用') {
|
|
@@ -27,8 +29,17 @@ export async function assertToolPermission(client, ctx, toolId, options) {
|
|
|
27
29
|
if (!client) {
|
|
28
30
|
return;
|
|
29
31
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
// 会向治理服务校验的客户端无 token 即拒绝。AllowAll 不校验,不要求 token。
|
|
33
|
+
if (!ctx.delegationToken && !(client instanceof AllowAllGovernanceClient)) {
|
|
34
|
+
const err = new PermissionDeniedError('NO_PERMISSION', permissionMessage('tool', 'NO_PERMISSION'));
|
|
35
|
+
await reportPermissionDenial(options?.auditReporter, {
|
|
36
|
+
kind: 'tool',
|
|
37
|
+
resourceId: toolId,
|
|
38
|
+
reason: 'NO_PERMISSION',
|
|
39
|
+
userId: ctx.userId,
|
|
40
|
+
correlationId: ctx.correlationId,
|
|
41
|
+
});
|
|
42
|
+
throw err;
|
|
32
43
|
}
|
|
33
44
|
const result = await client.checkPermission(ctx, {
|
|
34
45
|
resourceType: 'tool',
|
|
@@ -42,16 +53,31 @@ export async function assertToolPermission(client, ctx, toolId, options) {
|
|
|
42
53
|
}
|
|
43
54
|
if (!result.allowed) {
|
|
44
55
|
const reason = result.reason ?? 'NO_PERMISSION';
|
|
56
|
+
await reportPermissionDenial(options?.auditReporter, {
|
|
57
|
+
kind: 'tool',
|
|
58
|
+
resourceId: toolId,
|
|
59
|
+
reason,
|
|
60
|
+
userId: ctx.userId,
|
|
61
|
+
correlationId: ctx.correlationId,
|
|
62
|
+
});
|
|
45
63
|
throw new PermissionDeniedError(reason, permissionMessage('tool', reason));
|
|
46
64
|
}
|
|
47
65
|
}
|
|
48
66
|
/** Skill 激活守卫(04 R15.3) */
|
|
49
|
-
export async function assertSkillPermission(client, ctx, skillId) {
|
|
67
|
+
export async function assertSkillPermission(client, ctx, skillId, options) {
|
|
50
68
|
if (!client || !skillId) {
|
|
51
69
|
return;
|
|
52
70
|
}
|
|
53
|
-
if (!ctx.delegationToken &&
|
|
54
|
-
|
|
71
|
+
if (!ctx.delegationToken && !(client instanceof AllowAllGovernanceClient)) {
|
|
72
|
+
const err = new PermissionDeniedError('NO_PERMISSION', permissionMessage('skill', 'NO_PERMISSION'));
|
|
73
|
+
await reportPermissionDenial(options?.auditReporter, {
|
|
74
|
+
kind: 'skill',
|
|
75
|
+
resourceId: skillId,
|
|
76
|
+
reason: 'NO_PERMISSION',
|
|
77
|
+
userId: ctx.userId,
|
|
78
|
+
correlationId: ctx.correlationId,
|
|
79
|
+
});
|
|
80
|
+
throw err;
|
|
55
81
|
}
|
|
56
82
|
const result = await client.checkPermission(ctx, {
|
|
57
83
|
resourceType: 'skill',
|
|
@@ -63,6 +89,13 @@ export async function assertSkillPermission(client, ctx, skillId) {
|
|
|
63
89
|
}
|
|
64
90
|
if (!result.allowed) {
|
|
65
91
|
const reason = result.reason ?? 'NO_PERMISSION';
|
|
92
|
+
await reportPermissionDenial(options?.auditReporter, {
|
|
93
|
+
kind: 'skill',
|
|
94
|
+
resourceId: skillId,
|
|
95
|
+
reason,
|
|
96
|
+
userId: ctx.userId,
|
|
97
|
+
correlationId: ctx.correlationId,
|
|
98
|
+
});
|
|
66
99
|
throw new PermissionDeniedError(reason, permissionMessage('skill', reason));
|
|
67
100
|
}
|
|
68
101
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill 子图只允许一层:Agent 图可以有 type=subgraph,子图 DSL 不能再有。
|
|
3
|
+
* Checkpoint / HITL / Span 未按嵌套设计(B14)。
|
|
4
|
+
*/
|
|
5
|
+
import type { GraphDSL, GraphNode } from '../contracts/executable-graph.js';
|
|
6
|
+
export declare function subgraphNodesOf(dsl: GraphDSL | undefined): GraphNode[];
|
|
7
|
+
/**
|
|
8
|
+
* Skill 子图(compileSubGraph 入参、父节点内嵌 subgraphDsl、运行时 workflow)不得含 type=subgraph。
|
|
9
|
+
*/
|
|
10
|
+
export declare function assertSkillGraphHasNoSubgraphNodes(dsl: GraphDSL, label?: string): void;
|
|
11
|
+
/** 父图 buildGraph:内嵌 subgraphDsl 在编译父图时就拒绝,不等第一次 invoke。 */
|
|
12
|
+
export declare function assertEmbeddedSubgraphsAreFlat(dsl: GraphDSL): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill 子图只允许一层:Agent 图可以有 type=subgraph,子图 DSL 不能再有。
|
|
3
|
+
* Checkpoint / HITL / Span 未按嵌套设计(B14)。
|
|
4
|
+
*/
|
|
5
|
+
import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
|
|
6
|
+
export function subgraphNodesOf(dsl) {
|
|
7
|
+
return (dsl?.nodes ?? []).filter((node) => node.type === 'subgraph');
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Skill 子图(compileSubGraph 入参、父节点内嵌 subgraphDsl、运行时 workflow)不得含 type=subgraph。
|
|
11
|
+
*/
|
|
12
|
+
export function assertSkillGraphHasNoSubgraphNodes(dsl, label) {
|
|
13
|
+
const nested = subgraphNodesOf(dsl);
|
|
14
|
+
if (nested.length === 0)
|
|
15
|
+
return;
|
|
16
|
+
const where = label ?? dsl.name ?? 'unnamed';
|
|
17
|
+
throw new AppError(ErrorCodes.GRAPH_BUILD_FAILED, `Skill 子图不允许再包含 type=subgraph(不支持子图套子图): ${where} → ${nested.map((n) => n.id).join(', ')}`, { exposeMessage: true });
|
|
18
|
+
}
|
|
19
|
+
/** 父图 buildGraph:内嵌 subgraphDsl 在编译父图时就拒绝,不等第一次 invoke。 */
|
|
20
|
+
export function assertEmbeddedSubgraphsAreFlat(dsl) {
|
|
21
|
+
for (const node of dsl.nodes ?? []) {
|
|
22
|
+
if (node.type !== 'subgraph')
|
|
23
|
+
continue;
|
|
24
|
+
const nested = node.config?.['subgraphDsl'];
|
|
25
|
+
if (nested?.nodes?.length) {
|
|
26
|
+
assertSkillGraphHasNoSubgraphNodes(nested, node.id);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { LangGraphAdapter } from './langgraph.adapter.js';
|
|
2
2
|
export { LangGraphCheckpointer } from './langgraph-checkpointer.js';
|
|
3
3
|
export { SubGraphCompiler } from './subgraph-compiler.js';
|
|
4
|
+
export { assertSkillGraphHasNoSubgraphNodes, assertEmbeddedSubgraphsAreFlat, subgraphNodesOf, } from './graph-nesting.js';
|
|
5
|
+
export { isSkillContextIsolated, isolatedSkillMessages, isolatedSkillOutput, } from './skill-isolate.js';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { LangGraphAdapter } from './langgraph.adapter.js';
|
|
2
2
|
export { LangGraphCheckpointer } from './langgraph-checkpointer.js';
|
|
3
3
|
export { SubGraphCompiler } from './subgraph-compiler.js';
|
|
4
|
+
export { assertSkillGraphHasNoSubgraphNodes, assertEmbeddedSubgraphsAreFlat, subgraphNodesOf, } from './graph-nesting.js';
|
|
5
|
+
export { isSkillContextIsolated, isolatedSkillMessages, isolatedSkillOutput, } from './skill-isolate.js';
|
|
@@ -12,9 +12,11 @@ import type { ExecutableGraph, GraphDSL, GraphState, RuntimeEvent, CheckpointDat
|
|
|
12
12
|
import type { ICheckpointSaver } from '../../checkpoint/contracts/checkpoint-saver.contract.js';
|
|
13
13
|
import type { HitlHandler } from '../hitl/hitl-handler.js';
|
|
14
14
|
import { type ILLMProvider } from '../nodes/llm-node.js';
|
|
15
|
+
import { type RagNodeDeps } from '../nodes/rag/index.js';
|
|
15
16
|
import type { IToolExecutor } from '../../tool/tool-executor.interface.js';
|
|
16
17
|
import type { IGovernanceClient } from '../../governance/governance-client.js';
|
|
17
18
|
import { type ConditionEvaluatorFn } from '../condition-evaluator.js';
|
|
19
|
+
import type { ExecutionInterceptors } from '../../execution/interceptors.js';
|
|
18
20
|
/** 图未写 `config.maxIterations` 时的 recursionLimit。`agent ⇄ tools` 一圈约 2,50 ≈ 24 次工具往返。 */
|
|
19
21
|
export declare const DEFAULT_GRAPH_RECURSION_LIMIT = 50;
|
|
20
22
|
/**
|
|
@@ -26,6 +28,7 @@ export declare const DEFAULT_GRAPH_RECURSION_LIMIT = 50;
|
|
|
26
28
|
* 3. 提供 Checkpoint 提取与恢复能力
|
|
27
29
|
*/
|
|
28
30
|
export declare class LangGraphAdapter implements IStateGraphEngine {
|
|
31
|
+
private readonly auditReporter?;
|
|
29
32
|
private readonly checkpointer;
|
|
30
33
|
private readonly subGraphCompiler;
|
|
31
34
|
private readonly llmProvider;
|
|
@@ -42,11 +45,17 @@ export declare class LangGraphAdapter implements IStateGraphEngine {
|
|
|
42
45
|
*/
|
|
43
46
|
private readonly governanceClient?;
|
|
44
47
|
private hitlHandler?;
|
|
45
|
-
|
|
48
|
+
private readonly ragNodeDeps?;
|
|
49
|
+
private readonly interceptors?;
|
|
50
|
+
/** 开发 overlay 根;createDevRuntime 在恢复 process.env 之前钉死 */
|
|
51
|
+
private overlayExtraRoots;
|
|
52
|
+
constructor(checkpointSaver?: ICheckpointSaver, llmProvider?: ILLMProvider, toolExecutor?: IToolExecutor, governanceClient?: IGovernanceClient, hitlHandler?: HitlHandler, ragNodeDeps?: RagNodeDeps, interceptors?: ExecutionInterceptors, auditReporter?: import("../../governance/audit-reporter.js").IAuditReporter | undefined);
|
|
46
53
|
/**
|
|
47
54
|
* 注册自定义条件评估函数
|
|
48
55
|
*/
|
|
49
56
|
registerConditionEvaluator(name: string, fn: ConditionEvaluatorFn): void;
|
|
57
|
+
/** SDK 恢复 env 之后仍要把 overlay 算进 LLM jail 根,不能再读 process.env。 */
|
|
58
|
+
bindOverlayExtraRoots(roots: string[] | undefined): void;
|
|
50
59
|
/**
|
|
51
60
|
* 节点旁路推送 RuntimeEvent(子图 Skill 生命周期、Tool 执行)。
|
|
52
61
|
* 无本次执行流式袋时为空操作(纯 invoke)。
|
|
@@ -111,21 +120,15 @@ export declare class LangGraphAdapter implements IStateGraphEngine {
|
|
|
111
120
|
private buildSkillCatalog;
|
|
112
121
|
private createToolNodeFunction;
|
|
113
122
|
private createLLMNodeFunction;
|
|
123
|
+
/**
|
|
124
|
+
* RAG 节点:编译期 fail-fast(deps 缺失),请求级状态经 execute(state) 流转
|
|
125
|
+
*/
|
|
126
|
+
private createRagNodeFunction;
|
|
114
127
|
/**
|
|
115
128
|
* 子图节点:优先 config.subgraphDsl;否则按 skillId 从 state.skillDefinitions.workflow 解析。
|
|
116
129
|
* 技能正文用短目录 + read_file,不灌 system。
|
|
117
130
|
*/
|
|
118
131
|
private createSubGraphNodeFunction;
|
|
119
|
-
/**
|
|
120
|
-
* 并行节点:并行执行多个子任务分支
|
|
121
|
-
*
|
|
122
|
-
* 支持两种并行模式:
|
|
123
|
-
* 1. branches 模式:node.branches 列出要并行执行的节点 ID,每个节点的执行函数由 DSL 定义
|
|
124
|
-
* 2. tasks 模式:node.config.tasks 列出要并行执行的异步任务函数
|
|
125
|
-
*
|
|
126
|
-
* 执行结果合并到 state.__parallelResults 中。
|
|
127
|
-
*/
|
|
128
|
-
private createParallelNodeFunction;
|
|
129
132
|
/**
|
|
130
133
|
* 代码执行节点:在受限沙箱中执行用户代码
|
|
131
134
|
*
|
|
@@ -173,6 +176,17 @@ export declare class LangGraphAdapter implements IStateGraphEngine {
|
|
|
173
176
|
* 否则多轮回放时 DeepSeek thinking 会因缺少 reasoning_content 而 400,
|
|
174
177
|
* tool 消息也会丢掉 tool_call_id。
|
|
175
178
|
*/
|
|
179
|
+
/**
|
|
180
|
+
* 工具形态检索面白名单(图声明即真相):tool 节点 config.retrievalSources 声明的
|
|
181
|
+
* 数据源目录 → 本图工具白名单(声明源解析出的工具 + rag_search)。未声明的检索类
|
|
182
|
+
* 工具对主 LLM 工具表与执行链整体不可见/不可用;图未声明 = 不收敓名单(通用形态)。
|
|
183
|
+
*
|
|
184
|
+
* 约束——单检索面假设:find 只取全图首个带 retrievalSources 的 tool 节点、按它过滤
|
|
185
|
+
* 全图 loadedTools。多 tool 节点各声明检索面属未支持场景:第二面的工具会被当成
|
|
186
|
+
* 未声明而过滤,无法各自收敛;如需支持须改为 per-node 收敛(在节点边界分别过滤
|
|
187
|
+
* loadedTools,而非此处全图一次性收敛)。
|
|
188
|
+
*/
|
|
189
|
+
private applyDeclaredSearchFace;
|
|
176
190
|
private graphStateToLangGraphInput;
|
|
177
191
|
/**
|
|
178
192
|
* 将 LangGraph updates 模式输出转为平台 RuntimeEvent 序列
|
|
@@ -11,8 +11,12 @@ import { StateGraph, START, END, } from '@langchain/langgraph';
|
|
|
11
11
|
import { resolveCheckpointThreadId } from '../../session/persist-turn-messages.js';
|
|
12
12
|
import { LangGraphCheckpointer } from './langgraph-checkpointer.js';
|
|
13
13
|
import { SubGraphCompiler } from './subgraph-compiler.js';
|
|
14
|
+
import { assertEmbeddedSubgraphsAreFlat } from './graph-nesting.js';
|
|
15
|
+
import { isSkillContextIsolated } from './skill-isolate.js';
|
|
14
16
|
import { LLMNode } from '../nodes/llm-node.js';
|
|
15
17
|
import { ToolNode } from '../nodes/tool-node.js';
|
|
18
|
+
import { RagNode, RAG_SEARCH_TOOL_NAME, RagSearchToolExecutor } from '../nodes/rag/index.js';
|
|
19
|
+
import { resolveRagSources, appendSourceCatalog } from '../nodes/rag/tools.js';
|
|
16
20
|
import { ToolOrchestrator } from '../../tool/tool-orchestrator.js';
|
|
17
21
|
import { ToolResultInjector } from '../../tool/tool-result-injector.js';
|
|
18
22
|
import { fileArtifactFromToolOutput } from '../../tool/file-artifact.js';
|
|
@@ -33,6 +37,7 @@ import { classifySkillRead } from './skill-read.js';
|
|
|
33
37
|
import { skillEntryInvocationFromTool } from './skill-entry-span.js';
|
|
34
38
|
import { withGraphSpan } from '../trace-span.js';
|
|
35
39
|
import { executionTraceFromState, getExecutionStream, runWithExecutionTrace, } from '../execution-trace-context.js';
|
|
40
|
+
import { isRetryableToolFailure, resolveToolRetryConfig, sleepToolRetry, toolRetryDelayMs, } from '../../tool/tool-retry.js';
|
|
36
41
|
/** 图未写 `config.maxIterations` 时的 recursionLimit。`agent ⇄ tools` 一圈约 2,50 ≈ 24 次工具往返。 */
|
|
37
42
|
export const DEFAULT_GRAPH_RECURSION_LIMIT = 50;
|
|
38
43
|
/**
|
|
@@ -61,6 +66,7 @@ function langGraphRunOptions(dsl, extra) {
|
|
|
61
66
|
* 3. 提供 Checkpoint 提取与恢复能力
|
|
62
67
|
*/
|
|
63
68
|
export class LangGraphAdapter {
|
|
69
|
+
auditReporter;
|
|
64
70
|
checkpointer;
|
|
65
71
|
subGraphCompiler;
|
|
66
72
|
llmProvider;
|
|
@@ -77,17 +83,29 @@ export class LangGraphAdapter {
|
|
|
77
83
|
*/
|
|
78
84
|
governanceClient;
|
|
79
85
|
hitlHandler;
|
|
80
|
-
|
|
86
|
+
ragNodeDeps;
|
|
87
|
+
interceptors;
|
|
88
|
+
/** 开发 overlay 根;createDevRuntime 在恢复 process.env 之前钉死 */
|
|
89
|
+
overlayExtraRoots = [];
|
|
90
|
+
constructor(checkpointSaver, llmProvider, toolExecutor, governanceClient, hitlHandler, ragNodeDeps, interceptors, auditReporter) {
|
|
91
|
+
this.auditReporter = auditReporter;
|
|
81
92
|
this.governanceClient = governanceClient;
|
|
82
93
|
this.hitlHandler = hitlHandler;
|
|
94
|
+
this.ragNodeDeps = ragNodeDeps;
|
|
95
|
+
this.interceptors = interceptors;
|
|
83
96
|
this.checkpointer = checkpointSaver
|
|
84
97
|
? new LangGraphCheckpointer(checkpointSaver)
|
|
85
98
|
: undefined;
|
|
86
99
|
this.llmProvider = llmProvider ?? new EchoLLMProvider();
|
|
87
100
|
this.toolExecutor = toolExecutor;
|
|
88
101
|
this.toolOrchestrator = toolExecutor
|
|
89
|
-
? new ToolOrchestrator(toolExecutor, governanceClient)
|
|
102
|
+
? new ToolOrchestrator(toolExecutor, governanceClient, this.auditReporter)
|
|
90
103
|
: undefined;
|
|
104
|
+
// rag_search 装饰器注入同款编排器(内层 kb 派发与 tool 节点同一链路;
|
|
105
|
+
// 装饰器 deps 已显式提供 orchestrator 时不覆盖)
|
|
106
|
+
if (toolExecutor instanceof RagSearchToolExecutor && this.toolOrchestrator) {
|
|
107
|
+
toolExecutor.attachToolOrchestrator(this.toolOrchestrator);
|
|
108
|
+
}
|
|
91
109
|
this.toolResultInjector = new ToolResultInjector();
|
|
92
110
|
this.conditionEvaluator = new ConditionEvaluator();
|
|
93
111
|
this.systemPromptBuilder = new SystemPromptBuilder();
|
|
@@ -125,7 +143,7 @@ export class LangGraphAdapter {
|
|
|
125
143
|
return 'none';
|
|
126
144
|
});
|
|
127
145
|
// 必须在其他依赖初始化后再创建 SubGraphCompiler(它持有 adapter 引用)
|
|
128
|
-
this.subGraphCompiler = new SubGraphCompiler(this, governanceClient, this.skillAuditRecorder);
|
|
146
|
+
this.subGraphCompiler = new SubGraphCompiler(this, governanceClient, this.skillAuditRecorder, this.auditReporter);
|
|
129
147
|
}
|
|
130
148
|
/**
|
|
131
149
|
* 注册自定义条件评估函数
|
|
@@ -133,6 +151,10 @@ export class LangGraphAdapter {
|
|
|
133
151
|
registerConditionEvaluator(name, fn) {
|
|
134
152
|
this.conditionEvaluator.register(name, fn);
|
|
135
153
|
}
|
|
154
|
+
/** SDK 恢复 env 之后仍要把 overlay 算进 LLM jail 根,不能再读 process.env。 */
|
|
155
|
+
bindOverlayExtraRoots(roots) {
|
|
156
|
+
this.overlayExtraRoots = (roots ?? []).filter((root) => root.trim());
|
|
157
|
+
}
|
|
136
158
|
/**
|
|
137
159
|
* 节点旁路推送 RuntimeEvent(子图 Skill 生命周期、Tool 执行)。
|
|
138
160
|
* 无本次执行流式袋时为空操作(纯 invoke)。
|
|
@@ -290,7 +312,7 @@ export class LangGraphAdapter {
|
|
|
290
312
|
toolName: progress.toolName,
|
|
291
313
|
...(progress.toolset ? { toolset: progress.toolset } : {}),
|
|
292
314
|
error: progress.error ?? 'Unknown error',
|
|
293
|
-
willRetry:
|
|
315
|
+
willRetry: progress.willRetry === true,
|
|
294
316
|
...loc,
|
|
295
317
|
},
|
|
296
318
|
});
|
|
@@ -312,6 +334,7 @@ export class LangGraphAdapter {
|
|
|
312
334
|
*/
|
|
313
335
|
async buildGraph(dsl) {
|
|
314
336
|
this.assertShouldContinueTargetsTool(dsl);
|
|
337
|
+
assertEmbeddedSubgraphsAreFlat(dsl);
|
|
315
338
|
const silentNodeIds = new Set(dsl.nodes
|
|
316
339
|
.filter((node) => node.type === 'llm' && node.config?.['silent'] === true)
|
|
317
340
|
.map((node) => node.id));
|
|
@@ -352,6 +375,7 @@ export class LangGraphAdapter {
|
|
|
352
375
|
correlationId: ephemeral(''),
|
|
353
376
|
taskId: ephemeral(''),
|
|
354
377
|
sessionId: ephemeral(''),
|
|
378
|
+
skillIsolateContext: ephemeral(undefined),
|
|
355
379
|
loadedTools: ephemeral(undefined),
|
|
356
380
|
loadedSkills: ephemeral(undefined),
|
|
357
381
|
skillDefinitions: ephemeral(undefined),
|
|
@@ -364,9 +388,15 @@ export class LangGraphAdapter {
|
|
|
364
388
|
agentDir: ephemeral(undefined),
|
|
365
389
|
executionContext: ephemeral(undefined),
|
|
366
390
|
forceToolServer: ephemeral(undefined),
|
|
391
|
+
// 请求级检索范围(前端圈定 fileIds/dirPaths;rag 节点读取,仅本地检索源生效)
|
|
392
|
+
ragScope: ephemeral(undefined),
|
|
393
|
+
// 会话级数据源勾选(数据源 id 列表,undefined=全部启用;rag 节点读取)
|
|
394
|
+
ragSources: ephemeral(undefined),
|
|
367
395
|
traceCollector: ephemeral(undefined),
|
|
368
396
|
traceId: ephemeral(undefined),
|
|
369
397
|
skillSpanId: ephemeral(undefined),
|
|
398
|
+
toolSpanId: ephemeral(undefined),
|
|
399
|
+
hitlToolCallId: ephemeral(undefined),
|
|
370
400
|
lastSpanId: ephemeral(undefined),
|
|
371
401
|
hitlChoice: ephemeral(undefined),
|
|
372
402
|
// ── LLM Token 使用量:数组累加器 reducer,每次 LLM 节点追加一条使用明细
|
|
@@ -378,7 +408,6 @@ export class LangGraphAdapter {
|
|
|
378
408
|
},
|
|
379
409
|
default: () => [],
|
|
380
410
|
},
|
|
381
|
-
__parallelResults: ephemeral(undefined),
|
|
382
411
|
__codeResult: ephemeral(undefined),
|
|
383
412
|
__codeError: ephemeral(undefined),
|
|
384
413
|
__conditionResult: ephemeral(undefined),
|
|
@@ -450,7 +479,7 @@ export class LangGraphAdapter {
|
|
|
450
479
|
*/
|
|
451
480
|
async *stream(graph, state) {
|
|
452
481
|
const ref = this.getCompiledRef(graph);
|
|
453
|
-
const input = this.graphStateToLangGraphInput(state);
|
|
482
|
+
const input = this.graphStateToLangGraphInput(this.applyDeclaredSearchFace(state, ref.dsl));
|
|
454
483
|
const queue = [];
|
|
455
484
|
const waiter = { fn: null };
|
|
456
485
|
let finished = false;
|
|
@@ -523,7 +552,7 @@ export class LangGraphAdapter {
|
|
|
523
552
|
*/
|
|
524
553
|
async invoke(graph, state) {
|
|
525
554
|
const ref = this.getCompiledRef(graph);
|
|
526
|
-
const input = this.graphStateToLangGraphInput(state);
|
|
555
|
+
const input = this.graphStateToLangGraphInput(this.applyDeclaredSearchFace(state, ref.dsl));
|
|
527
556
|
const startTime = Date.now();
|
|
528
557
|
const traceCtx = executionTraceFromState(state);
|
|
529
558
|
// 只补 collector/traceId。子图套娃时外层 stream 袋经 ?? 保留。
|
|
@@ -651,23 +680,26 @@ export class LangGraphAdapter {
|
|
|
651
680
|
case 'subgraph':
|
|
652
681
|
return this.createSubGraphNodeFunction(node);
|
|
653
682
|
case 'human':
|
|
654
|
-
|
|
655
|
-
return { currentNode: node.id };
|
|
656
|
-
};
|
|
657
|
-
case 'parallel':
|
|
658
|
-
return this.createParallelNodeFunction(node);
|
|
683
|
+
throw new AppError(ErrorCodes.GRAPH_BUILD_FAILED, `Graph 节点 "${node.id}" type=human 未实现;人工确认请用 require_confirmation`, { exposeMessage: true });
|
|
659
684
|
case 'code':
|
|
660
685
|
if (node.config?.['bindSkill'] === true) {
|
|
661
686
|
return this.createSkillBindNodeFunction(node);
|
|
662
687
|
}
|
|
663
688
|
return this.createCodeNodeFunction(node);
|
|
664
|
-
|
|
689
|
+
case 'rag':
|
|
690
|
+
return this.createRagNodeFunction(node);
|
|
691
|
+
default: {
|
|
665
692
|
if (node.config?.['bindSkill'] === true) {
|
|
666
693
|
return this.createSkillBindNodeFunction(node);
|
|
667
694
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
695
|
+
const type = String(node.type);
|
|
696
|
+
if (type === 'start' || type === 'end') {
|
|
697
|
+
return async (state) => {
|
|
698
|
+
return { ...state, currentNode: node.id };
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
throw new AppError(ErrorCodes.GRAPH_BUILD_FAILED, `Graph 节点 "${node.id}" type=${type} 不是合法类型`, { exposeMessage: true });
|
|
702
|
+
}
|
|
671
703
|
}
|
|
672
704
|
}
|
|
673
705
|
createSkillBindNodeFunction(node) {
|
|
@@ -744,7 +776,7 @@ export class LangGraphAdapter {
|
|
|
744
776
|
userId: 'system',
|
|
745
777
|
delegationToken: '',
|
|
746
778
|
agentCode: '',
|
|
747
|
-
}, (progress) => this.emitToolExecutionEvents(progress), this.hitlHandler, node.id);
|
|
779
|
+
}, (progress) => this.emitToolExecutionEvents(progress), this.hitlHandler, node.id, this.interceptors, node.config?.['retrievalSources'], node.config?.['totalK'], node.retry);
|
|
748
780
|
return (state) => toolNode.execute(state);
|
|
749
781
|
}
|
|
750
782
|
if (this.toolExecutor) {
|
|
@@ -767,7 +799,7 @@ export class LangGraphAdapter {
|
|
|
767
799
|
const jailExtraRoots = Object.values(skillDefs ?? {})
|
|
768
800
|
.map((skill) => skill.bundlePath)
|
|
769
801
|
.filter((p) => Boolean(p));
|
|
770
|
-
const
|
|
802
|
+
const request = {
|
|
771
803
|
toolCallId,
|
|
772
804
|
toolName,
|
|
773
805
|
arguments: defaultArgs,
|
|
@@ -775,7 +807,25 @@ export class LangGraphAdapter {
|
|
|
775
807
|
correlationId,
|
|
776
808
|
taskId,
|
|
777
809
|
...(jailExtraRoots.length > 0 ? { jailExtraRoots } : {}),
|
|
778
|
-
}
|
|
810
|
+
};
|
|
811
|
+
const retry = resolveToolRetryConfig(node.retry);
|
|
812
|
+
const maxRetries = retry?.maxRetries ?? 0;
|
|
813
|
+
let result = await this.toolExecutor.execute(request);
|
|
814
|
+
for (let attempt = 0; !result.success && attempt < maxRetries; attempt++) {
|
|
815
|
+
if (!isRetryableToolFailure(result))
|
|
816
|
+
break;
|
|
817
|
+
this.emitToolExecutionEvents({
|
|
818
|
+
toolCallId: result.toolCallId,
|
|
819
|
+
toolName: result.toolName,
|
|
820
|
+
status: 'error',
|
|
821
|
+
arguments: defaultArgs,
|
|
822
|
+
error: result.error,
|
|
823
|
+
willRetry: true,
|
|
824
|
+
node: node.id,
|
|
825
|
+
});
|
|
826
|
+
await sleepToolRetry(toolRetryDelayMs(attempt, retry.backoff));
|
|
827
|
+
result = await this.toolExecutor.execute(request);
|
|
828
|
+
}
|
|
779
829
|
const executionTime = Date.now() - startedAt;
|
|
780
830
|
const skillDirs = Object.values(skillDefs ?? {})
|
|
781
831
|
.filter((skill) => Boolean(skill.bundlePath))
|
|
@@ -787,6 +837,7 @@ export class LangGraphAdapter {
|
|
|
787
837
|
arguments: defaultArgs,
|
|
788
838
|
content: result.content,
|
|
789
839
|
error: result.error,
|
|
840
|
+
willRetry: false,
|
|
790
841
|
executionTime,
|
|
791
842
|
node: node.id,
|
|
792
843
|
...(skillDirs.length > 0 ? { skillDirs } : {}),
|
|
@@ -891,6 +942,9 @@ export class LangGraphAdapter {
|
|
|
891
942
|
loadedTools,
|
|
892
943
|
skillDefinitions: skillDefs,
|
|
893
944
|
});
|
|
945
|
+
if (this.overlayExtraRoots.length > 0) {
|
|
946
|
+
jail.extraRoots = [...(jail.extraRoots ?? []), ...this.overlayExtraRoots];
|
|
947
|
+
}
|
|
894
948
|
const workingDirectory = jail.workdir;
|
|
895
949
|
const jailRoots = resolveJailRoots(jail);
|
|
896
950
|
const skillDirs = Object.values(skillDefs ?? {})
|
|
@@ -972,7 +1026,7 @@ export class LangGraphAdapter {
|
|
|
972
1026
|
},
|
|
973
1027
|
});
|
|
974
1028
|
}
|
|
975
|
-
: undefined);
|
|
1029
|
+
: undefined, this.interceptors);
|
|
976
1030
|
const next = await llmNode.execute(state);
|
|
977
1031
|
const hydratedUpdate = loadedTools && loadedTools !== loadedToolsBase ? { loadedTools } : {};
|
|
978
1032
|
if (!silent)
|
|
@@ -990,6 +1044,31 @@ export class LangGraphAdapter {
|
|
|
990
1044
|
});
|
|
991
1045
|
};
|
|
992
1046
|
}
|
|
1047
|
+
/**
|
|
1048
|
+
* RAG 节点:编译期 fail-fast(deps 缺失),请求级状态经 execute(state) 流转
|
|
1049
|
+
*/
|
|
1050
|
+
createRagNodeFunction(node) {
|
|
1051
|
+
if (!this.ragNodeDeps) {
|
|
1052
|
+
throw new Error(`图使用了 type:rag 节点 "${node.id}",但宿主未注入 ragNodeDeps(LangGraphAdapter 第 6 参)`);
|
|
1053
|
+
}
|
|
1054
|
+
const ragNode = new RagNode(node.id, (node.config ?? {}), {
|
|
1055
|
+
...this.ragNodeDeps,
|
|
1056
|
+
// 未显式提供时复用 adapter 已组装的 orchestrator(§9.3:toolExecutor+governance 透传)
|
|
1057
|
+
toolOrchestrator: this.ragNodeDeps.toolOrchestrator ?? this.toolOrchestrator,
|
|
1058
|
+
// rag.* 事件接 SSE 旁路(请求级执行流存活期间有效;宿主/测试注入的 emitEvent 优先)
|
|
1059
|
+
emitEvent: this.ragNodeDeps.emitEvent ??
|
|
1060
|
+
((type, payload) => {
|
|
1061
|
+
this.emitStreamEvent({
|
|
1062
|
+
type,
|
|
1063
|
+
taskId: undefined,
|
|
1064
|
+
correlationId: undefined,
|
|
1065
|
+
timestamp: new Date().toISOString(),
|
|
1066
|
+
payload,
|
|
1067
|
+
});
|
|
1068
|
+
}),
|
|
1069
|
+
});
|
|
1070
|
+
return (state) => ragNode.execute(state);
|
|
1071
|
+
}
|
|
993
1072
|
/**
|
|
994
1073
|
* 子图节点:优先 config.subgraphDsl;否则按 skillId 从 state.skillDefinitions.workflow 解析。
|
|
995
1074
|
* 技能正文用短目录 + read_file,不灌 system。
|
|
@@ -1000,7 +1079,10 @@ export class LangGraphAdapter {
|
|
|
1000
1079
|
node.config?.['skillId'] ??
|
|
1001
1080
|
embeddedDsl?.name;
|
|
1002
1081
|
if (embeddedDsl) {
|
|
1003
|
-
return this.subGraphCompiler.compileSubGraph(embeddedDsl, configuredSkillId
|
|
1082
|
+
return this.subGraphCompiler.compileSubGraph(embeddedDsl, configuredSkillId, {
|
|
1083
|
+
isolateContext: isSkillContextIsolated(node),
|
|
1084
|
+
parentNodeId: node.id,
|
|
1085
|
+
});
|
|
1004
1086
|
}
|
|
1005
1087
|
return async (state) => {
|
|
1006
1088
|
const skillId = configuredSkillId ??
|
|
@@ -1011,70 +1093,13 @@ export class LangGraphAdapter {
|
|
|
1011
1093
|
if (!workflow?.nodes?.length) {
|
|
1012
1094
|
throw preloadSkillNotFound(`子图节点无法解析 Skill workflow: node=${node.id}, skillId=${skillId ?? '(none)'}`, { nodeId: node.id, skillId });
|
|
1013
1095
|
}
|
|
1014
|
-
const fn = this.subGraphCompiler.compileSubGraph(workflow, skillId ?? def?.skillId
|
|
1096
|
+
const fn = this.subGraphCompiler.compileSubGraph(workflow, skillId ?? def?.skillId, {
|
|
1097
|
+
isolateContext: isSkillContextIsolated(node),
|
|
1098
|
+
parentNodeId: node.id,
|
|
1099
|
+
});
|
|
1015
1100
|
return fn(state);
|
|
1016
1101
|
};
|
|
1017
1102
|
}
|
|
1018
|
-
/**
|
|
1019
|
-
* 并行节点:并行执行多个子任务分支
|
|
1020
|
-
*
|
|
1021
|
-
* 支持两种并行模式:
|
|
1022
|
-
* 1. branches 模式:node.branches 列出要并行执行的节点 ID,每个节点的执行函数由 DSL 定义
|
|
1023
|
-
* 2. tasks 模式:node.config.tasks 列出要并行执行的异步任务函数
|
|
1024
|
-
*
|
|
1025
|
-
* 执行结果合并到 state.__parallelResults 中。
|
|
1026
|
-
*/
|
|
1027
|
-
createParallelNodeFunction(node) {
|
|
1028
|
-
return async (state) => {
|
|
1029
|
-
const branchNodeIds = node.branches ?? node.config?.['branches'] ?? [];
|
|
1030
|
-
const tasks = node.config?.['tasks'];
|
|
1031
|
-
const results = [];
|
|
1032
|
-
if (tasks?.length) {
|
|
1033
|
-
// tasks 模式:并行执行配置的任务
|
|
1034
|
-
const promises = tasks.map(async (task) => {
|
|
1035
|
-
try {
|
|
1036
|
-
// 任务结果由外部注入的 task executor 处理
|
|
1037
|
-
// 此处仅记录任务 ID,实际执行依赖外部编排
|
|
1038
|
-
return { branchId: task.id, success: true, result: { taskId: task.id, status: 'dispatched' } };
|
|
1039
|
-
}
|
|
1040
|
-
catch (err) {
|
|
1041
|
-
return {
|
|
1042
|
-
branchId: task.id,
|
|
1043
|
-
success: false,
|
|
1044
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
});
|
|
1048
|
-
results.push(...await Promise.all(promises));
|
|
1049
|
-
}
|
|
1050
|
-
else if (branchNodeIds.length) {
|
|
1051
|
-
// branches 模式:并行执行分支节点
|
|
1052
|
-
const promises = branchNodeIds.map(async (branchId) => {
|
|
1053
|
-
try {
|
|
1054
|
-
// 每个分支节点的实际执行由 LangGraph 编排层驱动
|
|
1055
|
-
// 并行节点仅负责触发并汇总状态
|
|
1056
|
-
return { branchId, success: true, result: { dispatched: true } };
|
|
1057
|
-
}
|
|
1058
|
-
catch (err) {
|
|
1059
|
-
return {
|
|
1060
|
-
branchId,
|
|
1061
|
-
success: false,
|
|
1062
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
});
|
|
1066
|
-
results.push(...await Promise.all(promises));
|
|
1067
|
-
}
|
|
1068
|
-
const allSuccess = results.every((r) => r.success);
|
|
1069
|
-
return {
|
|
1070
|
-
...state,
|
|
1071
|
-
currentNode: node.id,
|
|
1072
|
-
__parallelResults: results,
|
|
1073
|
-
__parallelAllSuccess: allSuccess,
|
|
1074
|
-
__conditionResult: allSuccess ? 'success' : 'partial_failure',
|
|
1075
|
-
};
|
|
1076
|
-
};
|
|
1077
|
-
}
|
|
1078
1103
|
/**
|
|
1079
1104
|
* 代码执行节点:在受限沙箱中执行用户代码
|
|
1080
1105
|
*
|
|
@@ -1250,6 +1275,41 @@ export class LangGraphAdapter {
|
|
|
1250
1275
|
* 否则多轮回放时 DeepSeek thinking 会因缺少 reasoning_content 而 400,
|
|
1251
1276
|
* tool 消息也会丢掉 tool_call_id。
|
|
1252
1277
|
*/
|
|
1278
|
+
/**
|
|
1279
|
+
* 工具形态检索面白名单(图声明即真相):tool 节点 config.retrievalSources 声明的
|
|
1280
|
+
* 数据源目录 → 本图工具白名单(声明源解析出的工具 + rag_search)。未声明的检索类
|
|
1281
|
+
* 工具对主 LLM 工具表与执行链整体不可见/不可用;图未声明 = 不收敓名单(通用形态)。
|
|
1282
|
+
*
|
|
1283
|
+
* 约束——单检索面假设:find 只取全图首个带 retrievalSources 的 tool 节点、按它过滤
|
|
1284
|
+
* 全图 loadedTools。多 tool 节点各声明检索面属未支持场景:第二面的工具会被当成
|
|
1285
|
+
* 未声明而过滤,无法各自收敛;如需支持须改为 per-node 收敛(在节点边界分别过滤
|
|
1286
|
+
* loadedTools,而非此处全图一次性收敛)。
|
|
1287
|
+
*/
|
|
1288
|
+
applyDeclaredSearchFace(state, dsl) {
|
|
1289
|
+
const face = dsl?.nodes?.find((n) => n.type === 'tool' && n.config?.['retrievalSources']);
|
|
1290
|
+
const declaration = face?.config?.['retrievalSources'];
|
|
1291
|
+
const loaded = state.loadedTools;
|
|
1292
|
+
if (!declaration || !loaded || typeof loaded !== 'object')
|
|
1293
|
+
return state;
|
|
1294
|
+
const resolved = resolveRagSources({ retrievalSources: declaration });
|
|
1295
|
+
const allowed = new Set([...(resolved.toolScope ?? []), RAG_SEARCH_TOOL_NAME]);
|
|
1296
|
+
const filtered = {};
|
|
1297
|
+
for (const [name, def] of Object.entries(loaded)) {
|
|
1298
|
+
if (!allowed.has(name))
|
|
1299
|
+
continue;
|
|
1300
|
+
// 数据源目录写进保留工具的 description(幂等)——主 LLM 直调时与 rag 内核
|
|
1301
|
+
// 同一份目录,杜绝瞎猜 datasetCode;内核组表侧 appendSourceCatalog 幂等不重复
|
|
1302
|
+
const catalog = resolved.sourceCatalog[name];
|
|
1303
|
+
if (catalog?.length) {
|
|
1304
|
+
const desc = appendSourceCatalog(def.manifest.description, catalog);
|
|
1305
|
+
filtered[name] = { ...def, manifest: { ...def.manifest, description: desc } };
|
|
1306
|
+
}
|
|
1307
|
+
else {
|
|
1308
|
+
filtered[name] = def;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
return { ...state, loadedTools: filtered };
|
|
1312
|
+
}
|
|
1253
1313
|
graphStateToLangGraphInput(state) {
|
|
1254
1314
|
const mapped = (state.messages ?? []).map((msg) => ({
|
|
1255
1315
|
role: msg.role,
|