@gt-fe/eap-runtime 0.0.13 → 0.0.15
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 -1
- package/dist/api/handlers/invoke.handler.d.ts +1 -1
- package/dist/api/handlers/invoke.handler.js +3 -0
- package/dist/api/handlers/stream.handler.d.ts +1 -1
- package/dist/api/handlers/stream.handler.js +3 -0
- package/dist/common/index.d.ts +1 -0
- package/dist/common/index.js +1 -0
- package/dist/common/logger.d.ts +16 -4
- package/dist/common/logger.js +82 -16
- package/dist/common/runtime-package-version.d.ts +2 -0
- package/dist/common/runtime-package-version.js +5 -0
- package/dist/debug/llm-turn-log.d.ts +12 -0
- package/dist/debug/llm-turn-log.js +1 -0
- package/dist/graph/adapters/langgraph.adapter.d.ts +3 -1
- package/dist/graph/adapters/langgraph.adapter.js +66 -13
- package/dist/graph/adapters/subgraph-compiler.js +5 -2
- package/dist/graph/contracts/executable-graph.d.ts +13 -2
- package/dist/graph/nodes/llm-node.d.ts +3 -0
- package/dist/graph/nodes/llm-node.js +8 -0
- package/dist/graph/nodes/skill-bind-node.d.ts +3 -4
- package/dist/graph/nodes/skill-bind-node.js +1 -20
- package/dist/graph/nodes/tool-node.d.ts +2 -0
- package/dist/graph/nodes/tool-node.js +20 -14
- package/dist/memory/compression/index.d.ts +1 -1
- package/dist/memory/compression/index.js +1 -1
- package/dist/memory/compression/sliding-window.compression.js +7 -7
- package/dist/memory/index.d.ts +1 -1
- package/dist/memory/index.js +1 -1
- package/dist/memory/message-compression.service.d.ts +12 -1
- package/dist/memory/message-compression.service.js +29 -1
- package/dist/providers/http-llm-provider.d.ts +1 -0
- package/dist/providers/http-llm-provider.js +4 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/providers/local-llm-provider.d.ts +3 -0
- package/dist/providers/local-llm-provider.js +14 -2
- package/dist/providers/routing-llm-provider.js +12 -3
- package/dist/registry/deployment-snapshot.js +18 -16
- package/dist/registry/graph-loader.d.ts +10 -0
- package/dist/registry/graph-loader.js +115 -66
- package/dist/runtime/agent-runtime.d.ts +3 -3
- package/dist/runtime/agent-runtime.js +13 -3
- package/dist/runtime/dev-runtime.d.ts +2 -0
- package/dist/runtime/dev-runtime.js +2 -0
- package/dist/runtime/types.d.ts +2 -2
- package/dist/session/compression-middleware.d.ts +2 -1
- package/dist/session/compression-middleware.js +7 -4
- package/dist/sse/events/stats-events.d.ts +9 -0
- package/dist/token/compression-budget-integration.d.ts +1 -1
- package/dist/token/compression-budget-integration.js +18 -10
- package/dist/token/context-window.d.ts +42 -0
- package/dist/token/context-window.js +73 -0
- package/dist/token/index.d.ts +1 -0
- package/dist/token/index.js +1 -0
- package/dist/tool/dev-http-executor.d.ts +1 -0
- package/dist/tool/dev-http-executor.js +12 -0
- package/dist/tool/dev-mcp-executor.d.ts +11 -1
- package/dist/tool/dev-mcp-executor.js +26 -0
- package/dist/tool/dev-tool-executor.d.ts +2 -1
- package/dist/tool/dev-tool-executor.js +6 -0
- package/dist/tool/harness-command.js +1 -1
- package/dist/tool/harness-primitives.d.ts +7 -0
- package/dist/tool/harness-primitives.js +56 -5
- package/dist/tool/index.d.ts +2 -1
- package/dist/tool/index.js +1 -0
- package/dist/tool/local-mcp-client.d.ts +4 -1
- package/dist/tool/local-mcp-client.js +130 -3
- package/dist/tool/local-tool-executor.d.ts +2 -1
- package/dist/tool/local-tool-executor.js +15 -0
- package/dist/tool/mcp-list.d.ts +53 -0
- package/dist/tool/mcp-list.js +199 -0
- package/dist/tool/mcp-tool-executor.d.ts +6 -1
- package/dist/tool/mcp-tool-executor.js +52 -0
- package/dist/tool/skill-dir-infer.d.ts +17 -0
- package/dist/tool/skill-dir-infer.js +81 -0
- package/dist/tool/skill-script-refs.d.ts +1 -8
- package/dist/tool/skill-script-refs.js +32 -20
- package/dist/tool/system-prompt-builder.d.ts +35 -16
- package/dist/tool/system-prompt-builder.js +30 -58
- package/dist/tool/timeout.js +4 -1
- package/dist/tool/tool-execution-router.d.ts +2 -1
- package/dist/tool/tool-execution-router.js +7 -0
- package/dist/tool/tool-executor.interface.d.ts +11 -0
- package/package.json +6 -33
- package/dist/registry/mcp-stdio-cwd.d.ts +0 -5
- package/dist/registry/mcp-stdio-cwd.js +0 -13
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* @see requirements.md Requirement 1 AC6, Requirement 9
|
|
11
11
|
*/
|
|
12
12
|
import { formatUnknownError } from '../../utils/format-error.js';
|
|
13
|
+
import { buildContextUsage, turnSectionChars } from '../../token/context-window.js';
|
|
14
|
+
import { isAbortError } from '../../tool/timeout.js';
|
|
13
15
|
// ─────────────────────────────────────────────
|
|
14
16
|
// Default retry config
|
|
15
17
|
// ─────────────────────────────────────────────
|
|
@@ -94,6 +96,7 @@ export class LLMNode {
|
|
|
94
96
|
// (即 user 消息重复的根因)。
|
|
95
97
|
//
|
|
96
98
|
// __llmTokenUsage channel 同样是 **数组累加器 reducer**,每次追加一条使用明细。
|
|
99
|
+
const context = buildContextUsage(response.inputTokens, this.config.maxContextTokens, turnSectionChars(this.config, messages));
|
|
97
100
|
return {
|
|
98
101
|
messages: [assistantMessage],
|
|
99
102
|
currentNode: this.nodeId,
|
|
@@ -104,6 +107,7 @@ export class LLMNode {
|
|
|
104
107
|
inputTokens: response.inputTokens,
|
|
105
108
|
outputTokens: response.outputTokens,
|
|
106
109
|
durationMs,
|
|
110
|
+
...(context ?? {}),
|
|
107
111
|
},
|
|
108
112
|
],
|
|
109
113
|
};
|
|
@@ -183,6 +187,7 @@ export class LLMNode {
|
|
|
183
187
|
*
|
|
184
188
|
* 根据 streaming 配置选择 invoke 或 stream 模式。
|
|
185
189
|
* 失败时按 backoff 策略重试,最多重试 maxRetries 次。
|
|
190
|
+
* AbortError / 「This operation was aborted」(含 LLM HTTP 超时包装)立即失败,不重试。
|
|
186
191
|
*/
|
|
187
192
|
async invokeWithRetry(messages) {
|
|
188
193
|
let lastError;
|
|
@@ -195,6 +200,9 @@ export class LLMNode {
|
|
|
195
200
|
}
|
|
196
201
|
catch (error) {
|
|
197
202
|
lastError = error instanceof Error ? error : new Error(formatUnknownError(error));
|
|
203
|
+
if (isAbortError(error) || isAbortError(lastError)) {
|
|
204
|
+
throw lastError;
|
|
205
|
+
}
|
|
198
206
|
// 最后一次尝试不需要等待
|
|
199
207
|
if (attempt < this.retryConfig.maxRetries) {
|
|
200
208
|
const delay = this.calculateBackoffDelay(attempt);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 可选图胶水:把 silent LLM 的结构化输出写成
|
|
3
|
-
* 不是 Graph
|
|
2
|
+
* 可选图胶水:把 silent LLM 的结构化输出写成 skillRoute / activatedSkill。
|
|
3
|
+
* 不是 Graph 节点类型。技能正文走短目录 + read_file,不灌 system。
|
|
4
4
|
*/
|
|
5
5
|
import type { LoadedSkillInfo, LoadedSkillMap } from '../../tool/system-prompt-builder.js';
|
|
6
6
|
export type SkillRoute = 'skill' | 'clarify' | 'none';
|
|
@@ -9,7 +9,6 @@ export interface SkillBindPatch {
|
|
|
9
9
|
activatedSkill: string | null;
|
|
10
10
|
/** 展示名;未选中时为空 */
|
|
11
11
|
skillName?: string;
|
|
12
|
-
activeSkillPrompt?: string;
|
|
13
12
|
skillInput?: Record<string, unknown>;
|
|
14
13
|
messages: Array<Record<string, unknown>>;
|
|
15
14
|
/** 用户写了 skill 名但清单里没有,才算 unmatched */
|
|
@@ -25,7 +24,7 @@ interface SkillSelectPayload {
|
|
|
25
24
|
export declare function extractJsonObject(text: string): SkillSelectPayload | null;
|
|
26
25
|
export declare function resolveSkillDefinition(defs: LoadedSkillMap | undefined, rawName: string): LoadedSkillInfo | undefined;
|
|
27
26
|
/**
|
|
28
|
-
* 消费选择节点刚写出的 assistant JSON
|
|
27
|
+
* 消费选择节点刚写出的 assistant JSON,改写为路由。
|
|
29
28
|
* 返回的 messages 是**整段替换**(调用方须覆盖 messages channel 时改用 replace 语义)。
|
|
30
29
|
*/
|
|
31
30
|
export declare function bindSelectedSkill(state: Record<string, unknown>): SkillBindPatch;
|
|
@@ -38,26 +38,8 @@ export function resolveSkillDefinition(defs, rawName) {
|
|
|
38
38
|
(skill.name && skill.name.toLowerCase() === needle));
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
-
function formatParams(params) {
|
|
42
|
-
return Object.entries(params)
|
|
43
|
-
.map(([key, value]) => `- ${key}: ${JSON.stringify(value)}`)
|
|
44
|
-
.join('\n');
|
|
45
|
-
}
|
|
46
|
-
function buildSkillPrompt(skill, params) {
|
|
47
|
-
const parts = [`# 技能:${skill.name || skill.skillId}`];
|
|
48
|
-
if (skill.description) {
|
|
49
|
-
parts.push(skill.description);
|
|
50
|
-
}
|
|
51
|
-
if (params && Object.keys(params).length > 0) {
|
|
52
|
-
parts.push(`# 提取的参数\n${formatParams(params)}`);
|
|
53
|
-
}
|
|
54
|
-
if (skill.prompt?.trim()) {
|
|
55
|
-
parts.push(skill.prompt.trim());
|
|
56
|
-
}
|
|
57
|
-
return parts.join('\n\n');
|
|
58
|
-
}
|
|
59
41
|
/**
|
|
60
|
-
* 消费选择节点刚写出的 assistant JSON
|
|
42
|
+
* 消费选择节点刚写出的 assistant JSON,改写为路由。
|
|
61
43
|
* 返回的 messages 是**整段替换**(调用方须覆盖 messages channel 时改用 replace 语义)。
|
|
62
44
|
*/
|
|
63
45
|
export function bindSelectedSkill(state) {
|
|
@@ -108,7 +90,6 @@ export function bindSelectedSkill(state) {
|
|
|
108
90
|
skillRoute: 'skill',
|
|
109
91
|
activatedSkill: skill.skillId,
|
|
110
92
|
skillName: skill.name || skill.skillId,
|
|
111
|
-
activeSkillPrompt: buildSkillPrompt(skill, params),
|
|
112
93
|
skillInput: params,
|
|
113
94
|
messages: nextMessages(),
|
|
114
95
|
};
|
|
@@ -33,6 +33,8 @@ type LangGraphState = Record<string, unknown>;
|
|
|
33
33
|
export interface ToolNodeProgress {
|
|
34
34
|
toolCallId: string;
|
|
35
35
|
toolName: string;
|
|
36
|
+
/** 工具集编码;与 toolName 一起给 UI 显示 `toolset/toolName` */
|
|
37
|
+
toolset?: string;
|
|
36
38
|
status: 'started' | 'completed' | 'error';
|
|
37
39
|
arguments?: Record<string, unknown>;
|
|
38
40
|
content?: string;
|
|
@@ -10,29 +10,18 @@
|
|
|
10
10
|
* @see requirements.md Requirement 8 AC1, AC3, AC5
|
|
11
11
|
*/
|
|
12
12
|
import { resolveJailFromState } from '../../tool/harness-primitives.js';
|
|
13
|
+
import { inferHarnessSkillDir } from '../../tool/skill-dir-infer.js';
|
|
13
14
|
import { isHitlDecision, unwrapHitlPaused } from '../hitl/hitl-handler.js';
|
|
14
15
|
import { ToolConfirmationInterceptor } from '../../tool/tool-confirmation.js';
|
|
15
16
|
import { resolveCheckpointThreadId } from '../../session/persist-turn-messages.js';
|
|
16
17
|
import { resolveTraceId } from '../../common/trace-id.js';
|
|
18
|
+
import { toolsetCodeFromLoaded } from '../../tool/mcp-list.js';
|
|
17
19
|
function skillDirsFromState(state) {
|
|
18
20
|
const defs = state['skillDefinitions'];
|
|
19
21
|
return Object.values(defs ?? {})
|
|
20
22
|
.filter((skill) => Boolean(skill.bundlePath))
|
|
21
23
|
.map((skill) => ({ skillId: skill.skillId, bundlePath: skill.bundlePath }));
|
|
22
24
|
}
|
|
23
|
-
function resolveActiveSkillDir(state) {
|
|
24
|
-
const defs = state['skillDefinitions'];
|
|
25
|
-
if (!defs)
|
|
26
|
-
return undefined;
|
|
27
|
-
const activated = state['activatedSkill'];
|
|
28
|
-
if (typeof activated === 'string' && defs[activated]?.bundlePath) {
|
|
29
|
-
return defs[activated].bundlePath;
|
|
30
|
-
}
|
|
31
|
-
const paths = Object.values(defs)
|
|
32
|
-
.map((skill) => skill.bundlePath)
|
|
33
|
-
.filter((p) => Boolean(p));
|
|
34
|
-
return paths.length === 1 ? paths[0] : undefined;
|
|
35
|
-
}
|
|
36
25
|
export class ToolNode {
|
|
37
26
|
orchestrator;
|
|
38
27
|
injector;
|
|
@@ -75,12 +64,24 @@ export class ToolNode {
|
|
|
75
64
|
skillDefinitions: state['skillDefinitions'],
|
|
76
65
|
});
|
|
77
66
|
const jailExtraRoots = jail.extraRoots ?? [];
|
|
78
|
-
const
|
|
67
|
+
const batchCalls = lastAssistantMessage.toolCalls.map((tc) => ({
|
|
68
|
+
name: tc.name,
|
|
69
|
+
arguments: tc.arguments,
|
|
70
|
+
}));
|
|
71
|
+
const boundSkill = typeof state['activatedSkill'] === 'string' ? state['activatedSkill'] : undefined;
|
|
79
72
|
// 4. 转换为 ToolCallRequest[](附带 manifest 路由字段)
|
|
80
73
|
const requests = lastAssistantMessage.toolCalls.map((tc) => {
|
|
81
74
|
const loaded = loadedTools?.[tc.name];
|
|
82
75
|
const correlationId = String(state['correlationId'] ?? tc.id);
|
|
83
76
|
const taskId = String(state['taskId'] ?? state['correlationId'] ?? tc.id);
|
|
77
|
+
const command = typeof tc.arguments?.command === 'string' ? tc.arguments.command : undefined;
|
|
78
|
+
const skillDir = inferHarnessSkillDir({
|
|
79
|
+
command,
|
|
80
|
+
toolCalls: batchCalls,
|
|
81
|
+
messages,
|
|
82
|
+
skillDirs,
|
|
83
|
+
activatedSkill: boundSkill,
|
|
84
|
+
});
|
|
84
85
|
return {
|
|
85
86
|
toolCallId: tc.id,
|
|
86
87
|
toolName: tc.name,
|
|
@@ -153,11 +154,13 @@ export class ToolNode {
|
|
|
153
154
|
const needsHitl = Boolean(this.hitlHandler) &&
|
|
154
155
|
requests.some((req) => confirmationTools.has(req.toolName));
|
|
155
156
|
const runOne = async (req, i) => {
|
|
157
|
+
const toolset = toolsetCodeFromLoaded(loadedTools?.[req.toolName]);
|
|
156
158
|
const resumingHitl = isHitlDecision(state['hitlChoice']);
|
|
157
159
|
if (!resumingHitl) {
|
|
158
160
|
emitProgress({
|
|
159
161
|
toolCallId: req.toolCallId,
|
|
160
162
|
toolName: req.toolName,
|
|
163
|
+
...(toolset ? { toolset } : {}),
|
|
161
164
|
status: 'started',
|
|
162
165
|
arguments: req.arguments,
|
|
163
166
|
});
|
|
@@ -197,6 +200,7 @@ export class ToolNode {
|
|
|
197
200
|
emitProgress({
|
|
198
201
|
toolCallId: result.toolCallId,
|
|
199
202
|
toolName: result.toolName,
|
|
203
|
+
...(toolset ? { toolset } : {}),
|
|
200
204
|
status: 'completed',
|
|
201
205
|
arguments: req.arguments,
|
|
202
206
|
content: result.content,
|
|
@@ -207,6 +211,7 @@ export class ToolNode {
|
|
|
207
211
|
emitProgress({
|
|
208
212
|
toolCallId: result.toolCallId,
|
|
209
213
|
toolName: result.toolName,
|
|
214
|
+
...(toolset ? { toolset } : {}),
|
|
210
215
|
status: 'error',
|
|
211
216
|
arguments: req.arguments,
|
|
212
217
|
error: result.error ?? 'unknown error',
|
|
@@ -243,6 +248,7 @@ export class ToolNode {
|
|
|
243
248
|
emitProgress({
|
|
244
249
|
toolCallId: req.toolCallId,
|
|
245
250
|
toolName: req.toolName,
|
|
251
|
+
...(toolset ? { toolset } : {}),
|
|
246
252
|
status: 'error',
|
|
247
253
|
arguments: req.arguments,
|
|
248
254
|
error: message,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 提供多种压缩策略用于处理长对话历史。
|
|
5
5
|
*/
|
|
6
|
-
export { type CompressionConfig, type CompressionResult, type CompressionRecord, type CompressionStrategy, type CompressionReason, type IMessageCompressionService, DEFAULT_COMPRESSION_CONFIG, estimateTokenCount, estimateMessagesTokens, countRounds, } from '../message-compression.service.js';
|
|
6
|
+
export { type CompressionConfig, type CompressionResult, type CompressionRecord, type CompressionStrategy, type CompressionReason, type IMessageCompressionService, DEFAULT_COMPRESSION_CONFIG, COMPRESSION_WINDOW_FACTOR, COMPRESSION_MAX_TOKENS_FALLBACK, resolveCompressionMaxTokens, mergeCompressionConfig, estimateTokenCount, estimateMessagesTokens, countRounds, } from '../message-compression.service.js';
|
|
7
7
|
export { SlidingWindowCompression, TruncateCompression, type SummarizeFunction, } from './sliding-window.compression.js';
|
|
8
8
|
export { createLLMSummarize, createSimpleSummarize, type LLMCallFunction, } from './llm-summarize.js';
|
|
9
9
|
export { PostgresCompressionRepository, InMemoryCompressionRepository, type ICompressionRepository, } from './compression.repository.js';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 提供多种压缩策略用于处理长对话历史。
|
|
5
5
|
*/
|
|
6
6
|
// 压缩接口和工具函数
|
|
7
|
-
export { DEFAULT_COMPRESSION_CONFIG, estimateTokenCount, estimateMessagesTokens, countRounds, } from '../message-compression.service.js';
|
|
7
|
+
export { DEFAULT_COMPRESSION_CONFIG, COMPRESSION_WINDOW_FACTOR, COMPRESSION_MAX_TOKENS_FALLBACK, resolveCompressionMaxTokens, mergeCompressionConfig, estimateTokenCount, estimateMessagesTokens, countRounds, } from '../message-compression.service.js';
|
|
8
8
|
// 滑动窗口压缩策略
|
|
9
9
|
export { SlidingWindowCompression, TruncateCompression, } from './sliding-window.compression.js';
|
|
10
10
|
// LLM 摘要函数
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @see message-compression.service.ts 压缩接口定义
|
|
10
10
|
* @see requirements.md Requirement 4.3
|
|
11
11
|
*/
|
|
12
|
-
import { DEFAULT_COMPRESSION_CONFIG, estimateMessagesTokens, countRounds, } from '../message-compression.service.js';
|
|
12
|
+
import { DEFAULT_COMPRESSION_CONFIG, estimateMessagesTokens, countRounds, mergeCompressionConfig, } from '../message-compression.service.js';
|
|
13
13
|
/**
|
|
14
14
|
* 默认摘要函数(占位)
|
|
15
15
|
*
|
|
@@ -30,14 +30,14 @@ export class SlidingWindowCompression {
|
|
|
30
30
|
config;
|
|
31
31
|
summarize;
|
|
32
32
|
constructor(config, summarize) {
|
|
33
|
-
this.config =
|
|
33
|
+
this.config = mergeCompressionConfig(DEFAULT_COMPRESSION_CONFIG, config);
|
|
34
34
|
this.summarize = summarize ?? defaultSummarize;
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
37
|
* 检查是否需要压缩
|
|
38
38
|
*/
|
|
39
39
|
checkCompression(messages, config) {
|
|
40
|
-
const mergedConfig =
|
|
40
|
+
const mergedConfig = mergeCompressionConfig(this.config, config);
|
|
41
41
|
const currentTokens = this.estimateTokens(messages);
|
|
42
42
|
const currentRounds = countRounds(messages);
|
|
43
43
|
// 检查 Token 数是否超限
|
|
@@ -73,7 +73,7 @@ export class SlidingWindowCompression {
|
|
|
73
73
|
* 3. 用摘要 + 最近 N 轮替换原始消息
|
|
74
74
|
*/
|
|
75
75
|
async compress(messages, config) {
|
|
76
|
-
const mergedConfig =
|
|
76
|
+
const mergedConfig = mergeCompressionConfig(this.config, config);
|
|
77
77
|
// 1. 找到最近 N 轮的起始位置
|
|
78
78
|
const splitIndex = this.findSplitIndex(messages, mergedConfig.preserveRecentRounds);
|
|
79
79
|
// 如果消息太少,不需要压缩
|
|
@@ -134,10 +134,10 @@ export class SlidingWindowCompression {
|
|
|
134
134
|
export class TruncateCompression {
|
|
135
135
|
config;
|
|
136
136
|
constructor(config) {
|
|
137
|
-
this.config =
|
|
137
|
+
this.config = mergeCompressionConfig(DEFAULT_COMPRESSION_CONFIG, config);
|
|
138
138
|
}
|
|
139
139
|
checkCompression(messages, config) {
|
|
140
|
-
const mergedConfig =
|
|
140
|
+
const mergedConfig = mergeCompressionConfig(this.config, config);
|
|
141
141
|
const currentTokens = this.estimateTokens(messages);
|
|
142
142
|
const currentRounds = countRounds(messages);
|
|
143
143
|
if (currentTokens > mergedConfig.maxTokens || currentRounds > mergedConfig.maxRounds) {
|
|
@@ -155,7 +155,7 @@ export class TruncateCompression {
|
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
157
|
async compress(messages, config) {
|
|
158
|
-
const mergedConfig =
|
|
158
|
+
const mergedConfig = mergeCompressionConfig(this.config, config);
|
|
159
159
|
const preserveCount = mergedConfig.preserveRecentRounds * 2; // 每轮 2 条消息
|
|
160
160
|
if (messages.length <= preserveCount) {
|
|
161
161
|
return messages;
|
package/dist/memory/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { LongTermMemoryService, InMemoryMemoryRepository, type IMemoryRepository, type MemoryRecord, type MemoryType, type CompressionResult, } from './long-term-memory.service.js';
|
|
2
2
|
export { PostgresMemoryRepository } from './pg-memory.repository.js';
|
|
3
|
-
export { SlidingWindowCompression, TruncateCompression, type IMessageCompressionService, type CompressionConfig, type SummarizeFunction, DEFAULT_COMPRESSION_CONFIG, estimateMessagesTokens, countRounds, } from './compression/index.js';
|
|
3
|
+
export { SlidingWindowCompression, TruncateCompression, type IMessageCompressionService, type CompressionConfig, type SummarizeFunction, DEFAULT_COMPRESSION_CONFIG, COMPRESSION_WINDOW_FACTOR, resolveCompressionMaxTokens, estimateMessagesTokens, countRounds, } from './compression/index.js';
|
|
4
4
|
export { SemanticRetrievalService, InMemoryVectorStore, type RetrievalConfig, type RetrievalStrategy, type RetrievalResult, type IVectorStore, type VectorResult, type EmbeddingFunction, DEFAULT_RETRIEVAL_CONFIG, } from './semantic-retrieval.service.js';
|
package/dist/memory/index.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
export { LongTermMemoryService, InMemoryMemoryRepository, } from './long-term-memory.service.js';
|
|
3
3
|
export { PostgresMemoryRepository } from './pg-memory.repository.js';
|
|
4
4
|
// 消息压缩模块
|
|
5
|
-
export { SlidingWindowCompression, TruncateCompression, DEFAULT_COMPRESSION_CONFIG, estimateMessagesTokens, countRounds, } from './compression/index.js';
|
|
5
|
+
export { SlidingWindowCompression, TruncateCompression, DEFAULT_COMPRESSION_CONFIG, COMPRESSION_WINDOW_FACTOR, resolveCompressionMaxTokens, estimateMessagesTokens, countRounds, } from './compression/index.js';
|
|
6
6
|
// 语义检索模块
|
|
7
7
|
export { SemanticRetrievalService, InMemoryVectorStore, DEFAULT_RETRIEVAL_CONFIG, } from './semantic-retrieval.service.js';
|
|
@@ -18,8 +18,10 @@ export type CompressionStrategy = 'sliding-window' | 'truncate' | 'headroom';
|
|
|
18
18
|
export interface CompressionConfig {
|
|
19
19
|
/** 压缩策略(默认 sliding-window) */
|
|
20
20
|
strategy: CompressionStrategy;
|
|
21
|
-
/** 触发压缩的 Token
|
|
21
|
+
/** 触发压缩的 Token 上限。有 maxContextTokens 且未显式传 maxTokens 时按 window×系数 */
|
|
22
22
|
maxTokens: number;
|
|
23
|
+
/** 模型上下文窗。有则 maxTokens = floor(window × COMPRESSION_WINDOW_FACTOR) */
|
|
24
|
+
maxContextTokens?: number;
|
|
23
25
|
/** 备用:按轮次触发(默认 20 轮) */
|
|
24
26
|
maxRounds: number;
|
|
25
27
|
/** 压缩后保留的最近轮次(默认 5 轮) */
|
|
@@ -29,6 +31,15 @@ export interface CompressionConfig {
|
|
|
29
31
|
/** 用于摘要的模型(可选,默认使用当前 Agent 的模型) */
|
|
30
32
|
summaryModel?: string;
|
|
31
33
|
}
|
|
34
|
+
/** 触发阈值 = 模型窗口 × 此系数(与原先 100000/128000 一致) */
|
|
35
|
+
export declare const COMPRESSION_WINDOW_FACTOR = 0.7;
|
|
36
|
+
/** 无模型窗口时的触发阈值(旧默认,按 128k×0.7) */
|
|
37
|
+
export declare const COMPRESSION_MAX_TOKENS_FALLBACK = 100000;
|
|
38
|
+
export declare function resolveCompressionMaxTokens(window?: number, factor?: number): number;
|
|
39
|
+
/**
|
|
40
|
+
* 合并压缩配置。未显式传 maxTokens 且有 maxContextTokens 时,按窗口×系数。
|
|
41
|
+
*/
|
|
42
|
+
export declare function mergeCompressionConfig(base: CompressionConfig, override?: Partial<CompressionConfig>): CompressionConfig;
|
|
32
43
|
/**
|
|
33
44
|
* 默认压缩配置
|
|
34
45
|
*/
|
|
@@ -7,12 +7,40 @@
|
|
|
7
7
|
* @see design.md §1.1 记忆管理
|
|
8
8
|
* @see requirements.md Requirement 4.3
|
|
9
9
|
*/
|
|
10
|
+
/** 触发阈值 = 模型窗口 × 此系数(与原先 100000/128000 一致) */
|
|
11
|
+
export const COMPRESSION_WINDOW_FACTOR = 0.7;
|
|
12
|
+
/** 无模型窗口时的触发阈值(旧默认,按 128k×0.7) */
|
|
13
|
+
export const COMPRESSION_MAX_TOKENS_FALLBACK = 100000;
|
|
14
|
+
export function resolveCompressionMaxTokens(window, factor = COMPRESSION_WINDOW_FACTOR) {
|
|
15
|
+
if (typeof window === 'number' && Number.isFinite(window) && window > 0) {
|
|
16
|
+
const f = typeof factor === 'number' && Number.isFinite(factor) && factor > 0 && factor <= 1
|
|
17
|
+
? factor
|
|
18
|
+
: COMPRESSION_WINDOW_FACTOR;
|
|
19
|
+
return Math.max(1, Math.floor(window * f));
|
|
20
|
+
}
|
|
21
|
+
return COMPRESSION_MAX_TOKENS_FALLBACK;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 合并压缩配置。未显式传 maxTokens 且有 maxContextTokens 时,按窗口×系数。
|
|
25
|
+
*/
|
|
26
|
+
export function mergeCompressionConfig(base, override) {
|
|
27
|
+
if (!override)
|
|
28
|
+
return { ...base };
|
|
29
|
+
const merged = { ...base, ...override };
|
|
30
|
+
if (Object.prototype.hasOwnProperty.call(override, 'maxTokens')) {
|
|
31
|
+
return merged;
|
|
32
|
+
}
|
|
33
|
+
if (override.maxContextTokens) {
|
|
34
|
+
merged.maxTokens = resolveCompressionMaxTokens(override.maxContextTokens);
|
|
35
|
+
}
|
|
36
|
+
return merged;
|
|
37
|
+
}
|
|
10
38
|
/**
|
|
11
39
|
* 默认压缩配置
|
|
12
40
|
*/
|
|
13
41
|
export const DEFAULT_COMPRESSION_CONFIG = {
|
|
14
42
|
strategy: 'sliding-window',
|
|
15
|
-
maxTokens:
|
|
43
|
+
maxTokens: COMPRESSION_MAX_TOKENS_FALLBACK,
|
|
16
44
|
maxRounds: 20,
|
|
17
45
|
preserveRecentRounds: 5,
|
|
18
46
|
compressionRatio: 0.3,
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import type { GraphMessage } from '../graph/contracts/executable-graph.js';
|
|
10
10
|
import type { ILLMProvider, LLMNodeConfig, LLMResponse, LLMChunk } from '../graph/nodes/llm-node.js';
|
|
11
11
|
import { type ILlmTurnLog } from '../debug/llm-turn-log.js';
|
|
12
|
+
export declare const DEFAULT_LLM_HTTP_TIMEOUT_MS = 180000;
|
|
12
13
|
export interface HttpLLMProviderConfig {
|
|
13
14
|
apiKey: string;
|
|
14
15
|
baseUrl?: string;
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { resolveLlmModelName } from './resolve-llm-model.js';
|
|
10
10
|
import { createFileLlmTurnLogFromEnv, maskApiKey } from '../debug/llm-turn-log.js';
|
|
11
|
+
import { buildContextUsage, turnSectionChars } from '../token/context-window.js';
|
|
12
|
+
export const DEFAULT_LLM_HTTP_TIMEOUT_MS = 180_000;
|
|
11
13
|
export class HttpLLMProvider {
|
|
12
14
|
apiKey;
|
|
13
15
|
baseUrl;
|
|
@@ -18,7 +20,7 @@ export class HttpLLMProvider {
|
|
|
18
20
|
this.apiKey = config.apiKey;
|
|
19
21
|
this.baseUrl = (config.baseUrl ?? 'https://api.openai.com/v1').replace(/\/$/, '');
|
|
20
22
|
this.defaultModel = config.defaultModel ?? 'DeepSeek-V4-Pro';
|
|
21
|
-
this.timeoutMs = config.timeoutMs ??
|
|
23
|
+
this.timeoutMs = config.timeoutMs ?? DEFAULT_LLM_HTTP_TIMEOUT_MS;
|
|
22
24
|
this.turnLog = 'turnLog' in config ? config.turnLog : createFileLlmTurnLogFromEnv();
|
|
23
25
|
}
|
|
24
26
|
async writeTurn(args) {
|
|
@@ -57,6 +59,7 @@ export class HttpLLMProvider {
|
|
|
57
59
|
error: args.response.error,
|
|
58
60
|
},
|
|
59
61
|
latencyMs: Date.now() - args.startedAt,
|
|
62
|
+
context: buildContextUsage(args.response.inputTokens, args.config.maxContextTokens, turnSectionChars(args.config, args.messages)),
|
|
60
63
|
});
|
|
61
64
|
}
|
|
62
65
|
catch (err) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { EchoLLMProvider, createDefaultLLMProvider } from './echo-llm-provider.js';
|
|
2
|
-
export { HttpLLMProvider, type HttpLLMProviderConfig } from './http-llm-provider.js';
|
|
2
|
+
export { HttpLLMProvider, DEFAULT_LLM_HTTP_TIMEOUT_MS, type HttpLLMProviderConfig } from './http-llm-provider.js';
|
|
3
3
|
export { RoutingLLMProvider, type RoutingLLMProviderConfig } from './routing-llm-provider.js';
|
|
4
4
|
export { resolveLlmModelName, canOverrideGraphModel } from './resolve-llm-model.js';
|
|
5
5
|
export { createLocalLlmProvider, requireLocalLlmConnections, resolveLocalLlmConnection, parseLocalLlmConnectionRefs, hydrateLocalLlmConnections, LLM_CONNECTIONS_ENV, } from './local-llm-provider.js';
|
package/dist/providers/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { EchoLLMProvider, createDefaultLLMProvider } from './echo-llm-provider.js';
|
|
2
|
-
export { HttpLLMProvider } from './http-llm-provider.js';
|
|
2
|
+
export { HttpLLMProvider, DEFAULT_LLM_HTTP_TIMEOUT_MS } from './http-llm-provider.js';
|
|
3
3
|
export { RoutingLLMProvider } from './routing-llm-provider.js';
|
|
4
4
|
export { resolveLlmModelName, canOverrideGraphModel } from './resolve-llm-model.js';
|
|
5
5
|
export { createLocalLlmProvider, requireLocalLlmConnections, resolveLocalLlmConnection, parseLocalLlmConnectionRefs, hydrateLocalLlmConnections, LLM_CONNECTIONS_ENV, } from './local-llm-provider.js';
|
|
@@ -9,11 +9,14 @@ export interface LocalLlmConnection {
|
|
|
9
9
|
baseUrl: string;
|
|
10
10
|
apiKey: string;
|
|
11
11
|
defaults?: ModelCallDefaults;
|
|
12
|
+
/** HttpLLMProvider 超时(毫秒);缺省用 DEFAULT_LLM_HTTP_TIMEOUT_MS */
|
|
13
|
+
timeoutMs?: number;
|
|
12
14
|
}
|
|
13
15
|
export interface LocalLlmConnectionRef {
|
|
14
16
|
baseUrl: string;
|
|
15
17
|
apiKeyEnv: string;
|
|
16
18
|
defaults?: ModelCallDefaults;
|
|
19
|
+
timeoutMs?: number;
|
|
17
20
|
}
|
|
18
21
|
export interface LocalLlmConnectionRefs {
|
|
19
22
|
default?: LocalLlmConnectionRef;
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { mergeModelCallParams } from '@gt-fe/eap-contracts';
|
|
6
6
|
import { HttpLLMProvider } from './http-llm-provider.js';
|
|
7
|
+
function parseTimeoutMs(value) {
|
|
8
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 1000)
|
|
9
|
+
return undefined;
|
|
10
|
+
return Math.min(3_600_000, Math.trunc(value));
|
|
11
|
+
}
|
|
7
12
|
export const LLM_CONNECTIONS_ENV = 'EAP_LLM_CONNECTIONS';
|
|
8
13
|
export function parseLocalLlmConnectionRefs(env = process.env) {
|
|
9
14
|
const raw = env[LLM_CONNECTIONS_ENV]?.trim();
|
|
@@ -42,7 +47,13 @@ export function hydrateLocalLlmConnections(refs, env = process.env) {
|
|
|
42
47
|
const apiKey = env[ref.apiKeyEnv]?.trim();
|
|
43
48
|
if (!apiKey)
|
|
44
49
|
return undefined;
|
|
45
|
-
|
|
50
|
+
const timeoutMs = parseTimeoutMs(ref.timeoutMs);
|
|
51
|
+
return {
|
|
52
|
+
baseUrl: ref.baseUrl,
|
|
53
|
+
apiKey,
|
|
54
|
+
defaults: ref.defaults,
|
|
55
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
56
|
+
};
|
|
46
57
|
};
|
|
47
58
|
const byModel = {};
|
|
48
59
|
for (const [name, ref] of Object.entries(refs.byModel ?? {})) {
|
|
@@ -97,7 +108,7 @@ class LocalLlmProvider {
|
|
|
97
108
|
}
|
|
98
109
|
providerFor(model) {
|
|
99
110
|
const connection = resolveLocalLlmConnection(this.connections, model);
|
|
100
|
-
const cacheKey = `${connection.baseUrl}\0${connection.apiKey}\0${model ?? ''}`;
|
|
111
|
+
const cacheKey = `${connection.baseUrl}\0${connection.apiKey}\0${model ?? ''}\0${connection.timeoutMs ?? ''}`;
|
|
101
112
|
const cached = this.cache.get(cacheKey);
|
|
102
113
|
if (cached)
|
|
103
114
|
return cached;
|
|
@@ -105,6 +116,7 @@ class LocalLlmProvider {
|
|
|
105
116
|
apiKey: connection.apiKey,
|
|
106
117
|
baseUrl: connection.baseUrl,
|
|
107
118
|
defaultModel: model,
|
|
119
|
+
timeoutMs: connection.timeoutMs,
|
|
108
120
|
turnLog: this.turnLog,
|
|
109
121
|
});
|
|
110
122
|
this.cache.set(cacheKey, provider);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { mergeModelCallParams } from '@gt-fe/eap-contracts';
|
|
15
15
|
import { EchoLLMProvider } from './echo-llm-provider.js';
|
|
16
|
-
import { HttpLLMProvider } from './http-llm-provider.js';
|
|
16
|
+
import { DEFAULT_LLM_HTTP_TIMEOUT_MS, HttpLLMProvider } from './http-llm-provider.js';
|
|
17
17
|
export class RoutingLLMProvider {
|
|
18
18
|
foundationUrl;
|
|
19
19
|
fallback;
|
|
@@ -35,7 +35,12 @@ export class RoutingLLMProvider {
|
|
|
35
35
|
}
|
|
36
36
|
withCallDefaults(config, selected) {
|
|
37
37
|
const merged = mergeModelCallParams(config, selected.defaults);
|
|
38
|
-
return {
|
|
38
|
+
return {
|
|
39
|
+
...config,
|
|
40
|
+
...merged,
|
|
41
|
+
model: selected.name,
|
|
42
|
+
maxContextTokens: selected.maxContextTokens ?? config.maxContextTokens,
|
|
43
|
+
};
|
|
39
44
|
}
|
|
40
45
|
async selectModel(config) {
|
|
41
46
|
try {
|
|
@@ -50,11 +55,14 @@ export class RoutingLLMProvider {
|
|
|
50
55
|
if (res.ok) {
|
|
51
56
|
const body = await res.json();
|
|
52
57
|
if (body.data?.name) {
|
|
58
|
+
const window = body.data.maxContextTokens
|
|
59
|
+
?? body.data.capabilities?.maxContextTokens;
|
|
53
60
|
return {
|
|
54
61
|
name: body.data.name,
|
|
55
62
|
provider: body.data.provider,
|
|
56
63
|
connection: body.data.connection ?? null,
|
|
57
64
|
defaults: body.data.defaults ?? null,
|
|
65
|
+
...(typeof window === 'number' && window > 0 ? { maxContextTokens: window } : {}),
|
|
58
66
|
};
|
|
59
67
|
}
|
|
60
68
|
}
|
|
@@ -71,7 +79,7 @@ export class RoutingLLMProvider {
|
|
|
71
79
|
apiKey: selected.connection.apiKey ?? '',
|
|
72
80
|
baseUrl: selected.connection.baseUrl,
|
|
73
81
|
defaultModel: selected.name,
|
|
74
|
-
timeoutMs: selected.connection.timeoutMs,
|
|
82
|
+
timeoutMs: selected.connection.timeoutMs ?? DEFAULT_LLM_HTTP_TIMEOUT_MS,
|
|
75
83
|
turnLog: this.turnLog,
|
|
76
84
|
});
|
|
77
85
|
}
|
|
@@ -81,6 +89,7 @@ export class RoutingLLMProvider {
|
|
|
81
89
|
apiKey: process.env.OPENAI_API_KEY,
|
|
82
90
|
baseUrl: process.env.OPENAI_BASE_URL,
|
|
83
91
|
defaultModel: selected.name,
|
|
92
|
+
timeoutMs: DEFAULT_LLM_HTTP_TIMEOUT_MS,
|
|
84
93
|
turnLog: this.turnLog,
|
|
85
94
|
});
|
|
86
95
|
}
|
|
@@ -538,35 +538,37 @@ export class FileSystemDeploymentSnapshot {
|
|
|
538
538
|
break;
|
|
539
539
|
}
|
|
540
540
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
541
|
+
const parsedMd = entryFile
|
|
542
|
+
? parseSkillMd(await readFile(entryFile, 'utf8'))
|
|
543
|
+
: null;
|
|
544
|
+
if (!manifest) {
|
|
545
|
+
if (!parsedMd)
|
|
544
546
|
continue;
|
|
545
547
|
manifest = {
|
|
546
|
-
code:
|
|
547
|
-
name:
|
|
548
|
-
description:
|
|
549
|
-
...(
|
|
548
|
+
code: ref.skillId,
|
|
549
|
+
name: parsedMd.frontmatter.name,
|
|
550
|
+
description: parsedMd.frontmatter.description,
|
|
551
|
+
...(parsedMd.frontmatter.license ? { license: parsedMd.frontmatter.license } : {}),
|
|
550
552
|
};
|
|
551
553
|
manifestPath = entryFile;
|
|
552
554
|
}
|
|
555
|
+
else if (parsedMd) {
|
|
556
|
+
manifest = {
|
|
557
|
+
...manifest,
|
|
558
|
+
name: parsedMd.frontmatter.name,
|
|
559
|
+
description: parsedMd.frontmatter.description,
|
|
560
|
+
...(parsedMd.frontmatter.license ? { license: parsedMd.frontmatter.license } : {}),
|
|
561
|
+
};
|
|
562
|
+
}
|
|
553
563
|
if (!manifest || !manifestPath)
|
|
554
564
|
continue;
|
|
555
|
-
if (!manifest.description && entryFile) {
|
|
556
|
-
const parsed = parseSkillMd(await readFile(entryFile, 'utf8'));
|
|
557
|
-
if (parsed?.frontmatter.description) {
|
|
558
|
-
manifest = { ...manifest, description: parsed.frontmatter.description };
|
|
559
|
-
if (!manifest.name)
|
|
560
|
-
manifest.name = parsed.frontmatter.name;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
565
|
const isUserOverridePath = bundlePath.includes('/users/') || bundlePath.includes('\\users\\');
|
|
564
566
|
const isAuthorOverlay = extraRoots.some((root) => root && isPathInside(root, bundlePath));
|
|
565
567
|
if (ref.sha256 && !isUserOverridePath && !isAuthorOverlay && manifestPath.endsWith('.json')) {
|
|
566
568
|
const actual = await sha256OfFile(manifestPath);
|
|
567
569
|
await assertDigestMatch(ref.sha256, actual, `skill ${ref.skillId}`, { path: manifestPath });
|
|
568
570
|
}
|
|
569
|
-
const skillId = String(
|
|
571
|
+
const skillId = String(ref.skillId);
|
|
570
572
|
return {
|
|
571
573
|
skillId,
|
|
572
574
|
name: String(manifest.name ?? ref.name ?? skillId),
|
|
@@ -17,6 +17,11 @@ export interface LoadAgentOptions {
|
|
|
17
17
|
/** 跳过读写进程内定义缓存 */
|
|
18
18
|
bypassCache?: boolean;
|
|
19
19
|
}
|
|
20
|
+
export declare function resolveAgentSystemPromptFromLoader(loader: {
|
|
21
|
+
resolveAgentSystemPrompt?: (agentCode: string, version?: string) => Promise<string | undefined>;
|
|
22
|
+
} | undefined, agentCode: string, version?: string): Promise<string | undefined>;
|
|
23
|
+
/** manifest.prompts.system;空白视为缺省。 */
|
|
24
|
+
export declare function extractAgentSystemPrompt(manifest: unknown): string | undefined;
|
|
20
25
|
export interface GraphLoaderConfig {
|
|
21
26
|
registryClient?: AgentRegistryClient;
|
|
22
27
|
deploymentContext?: DeploymentContext;
|
|
@@ -104,6 +109,11 @@ export declare class GraphLoader {
|
|
|
104
109
|
private cloneCached;
|
|
105
110
|
loadGraph(agentCode: string, options?: LoadAgentOptions): Promise<GraphDSL>;
|
|
106
111
|
private loadGraphFromSource;
|
|
112
|
+
/**
|
|
113
|
+
* Agent 基线系统提示词(manifest.prompts.system)。
|
|
114
|
+
* 每轮 execute/stream 注入 GraphState.agentSystemPrompt;不写进节点 config。
|
|
115
|
+
*/
|
|
116
|
+
resolveAgentSystemPrompt(agentCode: string, version?: string): Promise<string | undefined>;
|
|
107
117
|
/**
|
|
108
118
|
* 解析 agent tools[] + graph tool 节点 union;
|
|
109
119
|
* server+预装:以预装 manifest 为准;desktop 合并 ILocalToolInstallStore。
|