@gt-fe/eap-runtime 0.0.8 → 0.0.10
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 +12 -17
- package/dist/api/handlers/invoke.handler.js +3 -3
- package/dist/api/handlers/messages.handler.js +0 -1
- package/dist/api/handlers/stream.handler.js +3 -3
- package/dist/api/routes/agent.routes.js +2 -2
- package/dist/checkpoint/checkpoint-ref-notify.d.ts +10 -0
- package/dist/checkpoint/checkpoint-ref-notify.js +59 -0
- package/dist/checkpoint/index.d.ts +1 -0
- package/dist/checkpoint/index.js +1 -0
- package/dist/checkpoint/pg-checkpoint-saver.js +25 -2
- package/dist/common/agent-code.d.ts +2 -4
- package/dist/common/agent-code.js +4 -5
- package/dist/contracts/trace-collector.d.ts +15 -1
- package/dist/debug/llm-turn-log.d.ts +100 -0
- package/dist/debug/llm-turn-log.js +130 -0
- package/dist/debug/pg-llm-turn-log.d.ts +22 -0
- package/dist/debug/pg-llm-turn-log.js +57 -0
- package/dist/graph/adapters/langgraph-checkpointer.js +3 -1
- package/dist/graph/adapters/langgraph.adapter.d.ts +1 -0
- package/dist/graph/adapters/langgraph.adapter.js +147 -91
- package/dist/graph/adapters/skill-read.d.ts +12 -0
- package/dist/graph/adapters/skill-read.js +63 -0
- package/dist/graph/adapters/subgraph-compiler.js +5 -0
- package/dist/graph/contracts/executable-graph.d.ts +3 -3
- package/dist/graph/hitl/hitl-handler.d.ts +7 -0
- package/dist/graph/hitl/hitl-handler.js +47 -6
- package/dist/graph/nodes/llm-node.d.ts +14 -0
- package/dist/graph/nodes/skill-bind-node.js +1 -2
- package/dist/graph/nodes/tool-node.d.ts +2 -0
- package/dist/graph/nodes/tool-node.js +24 -9
- package/dist/graph/trace-span.d.ts +22 -0
- package/dist/graph/trace-span.js +69 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/memory/compression/compression.repository.d.ts +1 -15
- package/dist/providers/echo-llm-provider.d.ts +1 -1
- package/dist/providers/echo-llm-provider.js +2 -1
- package/dist/providers/http-llm-provider.d.ts +4 -0
- package/dist/providers/http-llm-provider.js +129 -4
- package/dist/providers/local-llm-provider.d.ts +5 -1
- package/dist/providers/local-llm-provider.js +14 -6
- package/dist/providers/routing-llm-provider.d.ts +4 -0
- package/dist/providers/routing-llm-provider.js +12 -2
- package/dist/registry/agent-models.d.ts +1 -1
- package/dist/registry/agent-models.js +1 -1
- package/dist/registry/agent-registry-client.d.ts +2 -2
- package/dist/registry/deployment-snapshot.d.ts +4 -4
- package/dist/registry/deployment-snapshot.js +50 -28
- package/dist/registry/graph-loader.d.ts +1 -1
- package/dist/registry/graph-loader.js +39 -76
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +1 -1
- package/dist/registry/skill-tree.d.ts +3 -0
- package/dist/registry/skill-tree.js +41 -3
- package/dist/runtime/agent-runtime.d.ts +42 -3
- package/dist/runtime/agent-runtime.js +110 -34
- package/dist/runtime/dev-runtime.d.ts +6 -3
- package/dist/runtime/dev-runtime.js +21 -14
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/storage-profile.d.ts +19 -10
- package/dist/runtime/storage-profile.js +42 -29
- package/dist/runtime/types.d.ts +15 -17
- package/dist/server.js +1 -1
- package/dist/session/session.service.d.ts +5 -0
- package/dist/session/session.service.js +12 -0
- package/dist/sse/events/index.d.ts +5 -3
- package/dist/sse/events/index.js +2 -0
- package/dist/sse/events/skill-events.d.ts +11 -0
- package/dist/sse/trace-collector.d.ts +10 -0
- package/dist/sse/trace-collector.js +1 -0
- package/dist/token/token-budget.service.d.ts +2 -2
- package/dist/token/token-budget.service.js +1 -1
- package/dist/tool/create-tool-executor.js +3 -2
- package/dist/tool/harness-primitives.d.ts +32 -0
- package/dist/tool/harness-primitives.js +385 -0
- package/dist/tool/http-tool-executor.js +1 -1
- package/dist/tool/index.d.ts +2 -1
- package/dist/tool/index.js +2 -1
- package/dist/tool/local-tool-executor.d.ts +3 -3
- package/dist/tool/local-tool-executor.js +5 -7
- package/dist/tool/mcp-tool-executor.js +9 -1
- package/dist/tool/system-prompt-builder.d.ts +61 -25
- package/dist/tool/system-prompt-builder.js +195 -92
- package/dist/tool/tool-execution-router.js +1 -1
- package/dist/tool/tool-executor.interface.d.ts +12 -6
- package/package.json +2 -2
|
@@ -7,16 +7,63 @@
|
|
|
7
7
|
* - tool_calls 解析(同步 & 流式)
|
|
8
8
|
*/
|
|
9
9
|
import { resolveLlmModelName } from './resolve-llm-model.js';
|
|
10
|
+
import { createFileLlmTurnLogFromEnv, maskApiKey } from '../debug/llm-turn-log.js';
|
|
10
11
|
export class HttpLLMProvider {
|
|
11
12
|
apiKey;
|
|
12
13
|
baseUrl;
|
|
13
14
|
defaultModel;
|
|
14
15
|
timeoutMs;
|
|
16
|
+
turnLog;
|
|
15
17
|
constructor(config) {
|
|
16
18
|
this.apiKey = config.apiKey;
|
|
17
19
|
this.baseUrl = (config.baseUrl ?? 'https://api.openai.com/v1').replace(/\/$/, '');
|
|
18
20
|
this.defaultModel = config.defaultModel ?? 'DeepSeek-V4-Pro';
|
|
19
21
|
this.timeoutMs = config.timeoutMs ?? 60000;
|
|
22
|
+
this.turnLog = 'turnLog' in config ? config.turnLog : createFileLlmTurnLogFromEnv();
|
|
23
|
+
}
|
|
24
|
+
async writeTurn(args) {
|
|
25
|
+
if (!this.turnLog)
|
|
26
|
+
return;
|
|
27
|
+
const model = resolveLlmModelName(args.config.model, this.defaultModel);
|
|
28
|
+
try {
|
|
29
|
+
await this.turnLog.record({
|
|
30
|
+
...args.config.turn,
|
|
31
|
+
url: args.url,
|
|
32
|
+
model,
|
|
33
|
+
temperature: args.config.temperature,
|
|
34
|
+
maxTokens: args.config.maxTokens,
|
|
35
|
+
apiKey: maskApiKey(this.apiKey),
|
|
36
|
+
systemPrompt: args.config.systemPrompt,
|
|
37
|
+
systemSections: args.config.systemSections?.map((section) => ({
|
|
38
|
+
id: section.id,
|
|
39
|
+
title: section.title,
|
|
40
|
+
chars: section.body.length,
|
|
41
|
+
body: section.body,
|
|
42
|
+
})),
|
|
43
|
+
tools: args.config.tools?.map((tool) => tool.function.name),
|
|
44
|
+
messages: args.messages.map((message) => ({
|
|
45
|
+
role: message.role,
|
|
46
|
+
content: message.content,
|
|
47
|
+
toolCallId: message.toolCallId,
|
|
48
|
+
toolCalls: message.toolCalls,
|
|
49
|
+
})),
|
|
50
|
+
response: {
|
|
51
|
+
content: args.response.content,
|
|
52
|
+
toolCalls: args.response.toolCalls,
|
|
53
|
+
reasoningContent: args.response.reasoningContent,
|
|
54
|
+
inputTokens: args.response.inputTokens,
|
|
55
|
+
outputTokens: args.response.outputTokens,
|
|
56
|
+
finishReason: args.response.finishReason,
|
|
57
|
+
error: args.response.error,
|
|
58
|
+
},
|
|
59
|
+
latencyMs: Date.now() - args.startedAt,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
if (process.env.EAP_LLM_DEBUG === '1') {
|
|
64
|
+
console.warn('[LLM-TURN] write failed', err);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
20
67
|
}
|
|
21
68
|
async invoke(messages, config) {
|
|
22
69
|
const body = this.buildRequestBody(messages, config, false);
|
|
@@ -52,6 +99,7 @@ export class HttpLLMProvider {
|
|
|
52
99
|
const controller = new AbortController();
|
|
53
100
|
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
54
101
|
const url = `${this.baseUrl}/chat/completions`;
|
|
102
|
+
const startedAt = Date.now();
|
|
55
103
|
try {
|
|
56
104
|
let response;
|
|
57
105
|
try {
|
|
@@ -90,7 +138,7 @@ export class HttpLLMProvider {
|
|
|
90
138
|
usage: data.usage,
|
|
91
139
|
}, null, 2));
|
|
92
140
|
}
|
|
93
|
-
|
|
141
|
+
const result = {
|
|
94
142
|
content,
|
|
95
143
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
96
144
|
...(reasoningContent ? { reasoningContent } : {}),
|
|
@@ -99,6 +147,37 @@ export class HttpLLMProvider {
|
|
|
99
147
|
// model 字段优先级:config(用户显式配置) > data.model(网关返回,可能是路由别名如 "/mnt/fast") > defaultModel
|
|
100
148
|
model: config.model ?? (typeof data.model === 'string' && !data.model.includes('/') ? data.model : undefined) ?? this.defaultModel,
|
|
101
149
|
};
|
|
150
|
+
await this.writeTurn({
|
|
151
|
+
url,
|
|
152
|
+
messages,
|
|
153
|
+
config,
|
|
154
|
+
startedAt,
|
|
155
|
+
response: {
|
|
156
|
+
content: result.content,
|
|
157
|
+
toolCalls: result.toolCalls,
|
|
158
|
+
reasoningContent: result.reasoningContent,
|
|
159
|
+
inputTokens: result.inputTokens,
|
|
160
|
+
outputTokens: result.outputTokens,
|
|
161
|
+
finishReason: choice?.finish_reason,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
await this.writeTurn({
|
|
168
|
+
url,
|
|
169
|
+
messages,
|
|
170
|
+
config,
|
|
171
|
+
startedAt,
|
|
172
|
+
response: {
|
|
173
|
+
content: '',
|
|
174
|
+
inputTokens: 0,
|
|
175
|
+
outputTokens: 0,
|
|
176
|
+
finishReason: 'error',
|
|
177
|
+
error: err instanceof Error ? err.message : String(err),
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
throw err;
|
|
102
181
|
}
|
|
103
182
|
finally {
|
|
104
183
|
clearTimeout(timeout);
|
|
@@ -136,6 +215,13 @@ export class HttpLLMProvider {
|
|
|
136
215
|
const controller = new AbortController();
|
|
137
216
|
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
138
217
|
const url = `${this.baseUrl}/chat/completions`;
|
|
218
|
+
const startedAt = Date.now();
|
|
219
|
+
const contentChunks = [];
|
|
220
|
+
const reasoningChunks = [];
|
|
221
|
+
const toolCallAccumulators = new Map();
|
|
222
|
+
let inputTokens = 0;
|
|
223
|
+
let outputTokens = 0;
|
|
224
|
+
let lastFinishReason;
|
|
139
225
|
try {
|
|
140
226
|
let response;
|
|
141
227
|
try {
|
|
@@ -163,9 +249,6 @@ export class HttpLLMProvider {
|
|
|
163
249
|
const reader = response.body.getReader();
|
|
164
250
|
const decoder = new TextDecoder();
|
|
165
251
|
let buffer = '';
|
|
166
|
-
const toolCallAccumulators = new Map();
|
|
167
|
-
let inputTokens = 0;
|
|
168
|
-
let outputTokens = 0;
|
|
169
252
|
let usageEmitted = false;
|
|
170
253
|
let model = config.model ?? this.defaultModel;
|
|
171
254
|
while (true) {
|
|
@@ -200,6 +283,8 @@ export class HttpLLMProvider {
|
|
|
200
283
|
}
|
|
201
284
|
const delta = chunk.choices?.[0]?.delta;
|
|
202
285
|
const finishReason = chunk.choices?.[0]?.finish_reason;
|
|
286
|
+
if (finishReason)
|
|
287
|
+
lastFinishReason = finishReason;
|
|
203
288
|
// 累积 tool_calls delta
|
|
204
289
|
if (delta?.tool_calls) {
|
|
205
290
|
for (const tc of delta.tool_calls) {
|
|
@@ -223,6 +308,10 @@ export class HttpLLMProvider {
|
|
|
223
308
|
// 产出内容 / reasoning chunk
|
|
224
309
|
const content = delta?.content ?? '';
|
|
225
310
|
const reasoningContent = typeof delta?.reasoning_content === 'string' ? delta.reasoning_content : '';
|
|
311
|
+
if (content)
|
|
312
|
+
contentChunks.push(content);
|
|
313
|
+
if (reasoningContent)
|
|
314
|
+
reasoningChunks.push(reasoningContent);
|
|
226
315
|
const isEnd = finishReason === 'stop' || finishReason === 'tool_calls';
|
|
227
316
|
const hasUsage = Boolean(chunk.usage);
|
|
228
317
|
if (content || reasoningContent || isEnd) {
|
|
@@ -281,6 +370,42 @@ export class HttpLLMProvider {
|
|
|
281
370
|
outputTokens,
|
|
282
371
|
}, null, 2));
|
|
283
372
|
}
|
|
373
|
+
await this.writeTurn({
|
|
374
|
+
url,
|
|
375
|
+
messages,
|
|
376
|
+
config,
|
|
377
|
+
startedAt,
|
|
378
|
+
response: {
|
|
379
|
+
content: contentChunks.join(''),
|
|
380
|
+
toolCalls: toolCallAccumulators.size > 0
|
|
381
|
+
? this.finalizeToolCalls(toolCallAccumulators)
|
|
382
|
+
: undefined,
|
|
383
|
+
reasoningContent: reasoningChunks.length > 0 ? reasoningChunks.join('') : undefined,
|
|
384
|
+
inputTokens,
|
|
385
|
+
outputTokens,
|
|
386
|
+
finishReason: lastFinishReason,
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
catch (err) {
|
|
391
|
+
await this.writeTurn({
|
|
392
|
+
url,
|
|
393
|
+
messages,
|
|
394
|
+
config,
|
|
395
|
+
startedAt,
|
|
396
|
+
response: {
|
|
397
|
+
content: contentChunks.join(''),
|
|
398
|
+
toolCalls: toolCallAccumulators.size > 0
|
|
399
|
+
? this.finalizeToolCalls(toolCallAccumulators)
|
|
400
|
+
: undefined,
|
|
401
|
+
reasoningContent: reasoningChunks.length > 0 ? reasoningChunks.join('') : undefined,
|
|
402
|
+
inputTokens,
|
|
403
|
+
outputTokens,
|
|
404
|
+
finishReason: 'error',
|
|
405
|
+
error: err instanceof Error ? err.message : String(err),
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
throw err;
|
|
284
409
|
}
|
|
285
410
|
finally {
|
|
286
411
|
clearTimeout(timeout);
|
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
* 连接表里只带 apiKeyEnv,密钥从 process.env 读取,不进 JSON。
|
|
4
4
|
*/
|
|
5
5
|
import type { ILLMProvider } from '../graph/nodes/llm-node.js';
|
|
6
|
+
import type { ILlmTurnLog } from '../debug/llm-turn-log.js';
|
|
7
|
+
import { type ModelCallDefaults } from '@gt-fe/eap-contracts';
|
|
6
8
|
export interface LocalLlmConnection {
|
|
7
9
|
baseUrl: string;
|
|
8
10
|
apiKey: string;
|
|
11
|
+
defaults?: ModelCallDefaults;
|
|
9
12
|
}
|
|
10
13
|
export interface LocalLlmConnectionRef {
|
|
11
14
|
baseUrl: string;
|
|
12
15
|
apiKeyEnv: string;
|
|
16
|
+
defaults?: ModelCallDefaults;
|
|
13
17
|
}
|
|
14
18
|
export interface LocalLlmConnectionRefs {
|
|
15
19
|
default?: LocalLlmConnectionRef;
|
|
@@ -24,4 +28,4 @@ export declare function parseLocalLlmConnectionRefs(env?: NodeJS.ProcessEnv): Lo
|
|
|
24
28
|
export declare function hydrateLocalLlmConnections(refs: LocalLlmConnectionRefs, env?: NodeJS.ProcessEnv): LocalLlmConnections;
|
|
25
29
|
export declare function requireLocalLlmConnections(env?: NodeJS.ProcessEnv): LocalLlmConnections;
|
|
26
30
|
export declare function resolveLocalLlmConnection(connections: LocalLlmConnections, model?: string): LocalLlmConnection;
|
|
27
|
-
export declare function createLocalLlmProvider(connections?: LocalLlmConnections): ILLMProvider;
|
|
31
|
+
export declare function createLocalLlmProvider(connections?: LocalLlmConnections, turnLog?: ILlmTurnLog): ILLMProvider;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* 本地多模型 LLM:按节点 model 选网关。仅 dev。
|
|
3
3
|
* 连接表里只带 apiKeyEnv,密钥从 process.env 读取,不进 JSON。
|
|
4
4
|
*/
|
|
5
|
+
import { mergeModelCallParams } from '@gt-fe/eap-contracts';
|
|
5
6
|
import { HttpLLMProvider } from './http-llm-provider.js';
|
|
6
7
|
export const LLM_CONNECTIONS_ENV = 'EAP_LLM_CONNECTIONS';
|
|
7
8
|
export function parseLocalLlmConnectionRefs(env = process.env) {
|
|
@@ -41,7 +42,7 @@ export function hydrateLocalLlmConnections(refs, env = process.env) {
|
|
|
41
42
|
const apiKey = env[ref.apiKeyEnv]?.trim();
|
|
42
43
|
if (!apiKey)
|
|
43
44
|
return undefined;
|
|
44
|
-
return { baseUrl: ref.baseUrl, apiKey };
|
|
45
|
+
return { baseUrl: ref.baseUrl, apiKey, defaults: ref.defaults };
|
|
45
46
|
};
|
|
46
47
|
const byModel = {};
|
|
47
48
|
for (const [name, ref] of Object.entries(refs.byModel ?? {})) {
|
|
@@ -73,20 +74,26 @@ export function resolveLocalLlmConnection(connections, model) {
|
|
|
73
74
|
}
|
|
74
75
|
return connection;
|
|
75
76
|
}
|
|
76
|
-
export function createLocalLlmProvider(connections = requireLocalLlmConnections()) {
|
|
77
|
-
return new LocalLlmProvider(connections);
|
|
77
|
+
export function createLocalLlmProvider(connections = requireLocalLlmConnections(), turnLog) {
|
|
78
|
+
return new LocalLlmProvider(connections, turnLog);
|
|
78
79
|
}
|
|
79
80
|
class LocalLlmProvider {
|
|
80
81
|
connections;
|
|
82
|
+
turnLog;
|
|
81
83
|
cache = new Map();
|
|
82
|
-
constructor(connections) {
|
|
84
|
+
constructor(connections, turnLog) {
|
|
83
85
|
this.connections = connections;
|
|
86
|
+
this.turnLog = turnLog;
|
|
84
87
|
}
|
|
85
88
|
invoke(messages, config) {
|
|
86
|
-
|
|
89
|
+
const connection = resolveLocalLlmConnection(this.connections, config.model);
|
|
90
|
+
const merged = mergeModelCallParams(config, connection.defaults);
|
|
91
|
+
return this.providerFor(config.model).invoke(messages, { ...config, ...merged });
|
|
87
92
|
}
|
|
88
93
|
stream(messages, config) {
|
|
89
|
-
|
|
94
|
+
const connection = resolveLocalLlmConnection(this.connections, config.model);
|
|
95
|
+
const merged = mergeModelCallParams(config, connection.defaults);
|
|
96
|
+
return this.providerFor(config.model).stream(messages, { ...config, ...merged });
|
|
90
97
|
}
|
|
91
98
|
providerFor(model) {
|
|
92
99
|
const connection = resolveLocalLlmConnection(this.connections, model);
|
|
@@ -98,6 +105,7 @@ class LocalLlmProvider {
|
|
|
98
105
|
apiKey: connection.apiKey,
|
|
99
106
|
baseUrl: connection.baseUrl,
|
|
100
107
|
defaultModel: model,
|
|
108
|
+
turnLog: this.turnLog,
|
|
101
109
|
});
|
|
102
110
|
this.cache.set(cacheKey, provider);
|
|
103
111
|
return provider;
|
|
@@ -13,16 +13,20 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { GraphMessage } from '../graph/contracts/executable-graph.js';
|
|
15
15
|
import type { ILLMProvider, LLMNodeConfig, LLMResponse, LLMChunk } from '../graph/nodes/llm-node.js';
|
|
16
|
+
import type { ILlmTurnLog } from '../debug/llm-turn-log.js';
|
|
16
17
|
export interface RoutingLLMProviderConfig {
|
|
17
18
|
foundationUrl: string;
|
|
18
19
|
fallbackProvider?: ILLMProvider;
|
|
20
|
+
turnLog?: ILlmTurnLog;
|
|
19
21
|
}
|
|
20
22
|
export declare class RoutingLLMProvider implements ILLMProvider {
|
|
21
23
|
private readonly foundationUrl;
|
|
22
24
|
private readonly fallback;
|
|
25
|
+
private readonly turnLog?;
|
|
23
26
|
constructor(config: RoutingLLMProviderConfig);
|
|
24
27
|
invoke(messages: GraphMessage[], config: LLMNodeConfig): Promise<LLMResponse>;
|
|
25
28
|
stream(messages: GraphMessage[], config: LLMNodeConfig): AsyncIterableIterator<LLMChunk>;
|
|
29
|
+
private withCallDefaults;
|
|
26
30
|
private selectModel;
|
|
27
31
|
private createProviderForModel;
|
|
28
32
|
}
|
|
@@ -11,24 +11,31 @@
|
|
|
11
11
|
* - 支持任意 OpenAI 兼容 API(OpenAI / DeepSeek / 本地 Ollama 等)
|
|
12
12
|
* - 连接配置由 Foundation 统一管理,Runtime 无感知
|
|
13
13
|
*/
|
|
14
|
+
import { mergeModelCallParams } from '@gt-fe/eap-contracts';
|
|
14
15
|
import { EchoLLMProvider } from './echo-llm-provider.js';
|
|
15
16
|
import { HttpLLMProvider } from './http-llm-provider.js';
|
|
16
17
|
export class RoutingLLMProvider {
|
|
17
18
|
foundationUrl;
|
|
18
19
|
fallback;
|
|
20
|
+
turnLog;
|
|
19
21
|
constructor(config) {
|
|
20
22
|
this.foundationUrl = config.foundationUrl.replace(/\/$/, '');
|
|
21
23
|
this.fallback = config.fallbackProvider ?? new EchoLLMProvider();
|
|
24
|
+
this.turnLog = config.turnLog;
|
|
22
25
|
}
|
|
23
26
|
async invoke(messages, config) {
|
|
24
27
|
const selected = await this.selectModel(config);
|
|
25
28
|
const provider = this.createProviderForModel(selected);
|
|
26
|
-
return provider.invoke(messages,
|
|
29
|
+
return provider.invoke(messages, this.withCallDefaults(config, selected));
|
|
27
30
|
}
|
|
28
31
|
async *stream(messages, config) {
|
|
29
32
|
const selected = await this.selectModel(config);
|
|
30
33
|
const provider = this.createProviderForModel(selected);
|
|
31
|
-
yield* provider.stream(messages,
|
|
34
|
+
yield* provider.stream(messages, this.withCallDefaults(config, selected));
|
|
35
|
+
}
|
|
36
|
+
withCallDefaults(config, selected) {
|
|
37
|
+
const merged = mergeModelCallParams(config, selected.defaults);
|
|
38
|
+
return { ...config, ...merged, model: selected.name };
|
|
32
39
|
}
|
|
33
40
|
async selectModel(config) {
|
|
34
41
|
try {
|
|
@@ -47,6 +54,7 @@ export class RoutingLLMProvider {
|
|
|
47
54
|
name: body.data.name,
|
|
48
55
|
provider: body.data.provider,
|
|
49
56
|
connection: body.data.connection ?? null,
|
|
57
|
+
defaults: body.data.defaults ?? null,
|
|
50
58
|
};
|
|
51
59
|
}
|
|
52
60
|
}
|
|
@@ -64,6 +72,7 @@ export class RoutingLLMProvider {
|
|
|
64
72
|
baseUrl: selected.connection.baseUrl,
|
|
65
73
|
defaultModel: selected.name,
|
|
66
74
|
timeoutMs: selected.connection.timeoutMs,
|
|
75
|
+
turnLog: this.turnLog,
|
|
67
76
|
});
|
|
68
77
|
}
|
|
69
78
|
// Fallback: 兼容旧逻辑(直接读 env)—— 开发环境快速启动用
|
|
@@ -72,6 +81,7 @@ export class RoutingLLMProvider {
|
|
|
72
81
|
apiKey: process.env.OPENAI_API_KEY,
|
|
73
82
|
baseUrl: process.env.OPENAI_BASE_URL,
|
|
74
83
|
defaultModel: selected.name,
|
|
84
|
+
turnLog: this.turnLog,
|
|
75
85
|
});
|
|
76
86
|
}
|
|
77
87
|
return this.fallback;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { resolveDeclaredModels,
|
|
1
|
+
export { resolveDeclaredModels, resolveAgentDefaultModel, resolveAgentModelAllowlist, } from '@gt-fe/eap-contracts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { resolveDeclaredModels,
|
|
1
|
+
export { resolveDeclaredModels, resolveAgentDefaultModel, resolveAgentModelAllowlist, } from '@gt-fe/eap-contracts';
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* {root}/agents/<agentCode>/<version>/users/<userId>/skills/<skillCode>/
|
|
8
8
|
*
|
|
9
9
|
* 命名:agentCode / toolsetCode / skillCode / toolName 均为语义化小写标识;
|
|
10
|
-
* Skill
|
|
10
|
+
* Skill 目录无版本层:包内必含 SKILL.md(Agent Skills)。
|
|
11
11
|
*/
|
|
12
12
|
export interface SnapshotToolRef {
|
|
13
13
|
toolId?: string;
|
|
@@ -17,7 +17,7 @@ export interface SnapshotToolRef {
|
|
|
17
17
|
sha256?: string;
|
|
18
18
|
/**
|
|
19
19
|
* 可选白名单:列出则仅启用所列子工具;省略 = 启用该 toolset 下全部子工具。
|
|
20
|
-
* @see agent.manifest
|
|
20
|
+
* @see agent.manifest toolsets[].enabled
|
|
21
21
|
*/
|
|
22
22
|
enabledTools?: string[];
|
|
23
23
|
/** 免 HITL 确认的子工具名(覆盖子工具 requireConfirmation) */
|
|
@@ -74,12 +74,12 @@ export interface IDeploymentSnapshot {
|
|
|
74
74
|
/** 规范化摘要:去 sha256: 前缀、空白,转小写 hex */
|
|
75
75
|
export declare function normalizeDigest(value: unknown): string | null;
|
|
76
76
|
/**
|
|
77
|
-
*
|
|
77
|
+
* 装配引用 vs 内联完整 ToolManifest。
|
|
78
78
|
* 含 type/execution 的视为内联定义;仅 toolset/name/version/enabledTools 等视为引用。
|
|
79
79
|
*/
|
|
80
80
|
export declare function isInlineToolDeclaration(item: unknown): boolean;
|
|
81
81
|
/**
|
|
82
|
-
* 将 agent.manifest.
|
|
82
|
+
* 将 agent.manifest.toolsets 上的 enabled/autoApprove 叠到 snapshot.tools[](snapshot 未带时)。
|
|
83
83
|
*/
|
|
84
84
|
export declare function overlayToolRefPolicies(snapTools: SnapshotToolRef[], manifest?: Record<string, unknown>): SnapshotToolRef[];
|
|
85
85
|
export declare class FileSystemDeploymentSnapshot implements IDeploymentSnapshot {
|
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
* {root}/agents/<agentCode>/<version>/users/<userId>/skills/<skillCode>/
|
|
8
8
|
*
|
|
9
9
|
* 命名:agentCode / toolsetCode / skillCode / toolName 均为语义化小写标识;
|
|
10
|
-
* Skill
|
|
10
|
+
* Skill 目录无版本层:包内必含 SKILL.md(Agent Skills)。
|
|
11
11
|
*/
|
|
12
12
|
import { readdir, readFile, access } from 'node:fs/promises';
|
|
13
13
|
import { createHash } from 'node:crypto';
|
|
14
14
|
import path from 'node:path';
|
|
15
15
|
import { constants as fsConstants } from 'node:fs';
|
|
16
|
+
import { parseSkillMd } from '@gt-fe/eap-contracts';
|
|
17
|
+
import { findSkillEntryFile } from './skill-tree.js';
|
|
16
18
|
import { preloadMissing, preloadPackageCorrupt, } from './preload-errors.js';
|
|
17
19
|
import { isRequireChecksum, resolvePreloadBehaviorFlags, } from './preload-flags.js';
|
|
18
20
|
async function pathExists(p) {
|
|
@@ -139,7 +141,7 @@ async function pickLatestVersionDir(parent) {
|
|
|
139
141
|
return path.join(parent, dirs[dirs.length - 1]);
|
|
140
142
|
}
|
|
141
143
|
function skillRefsFromManifest(manifest) {
|
|
142
|
-
const raw = manifest.
|
|
144
|
+
const raw = manifest.skills;
|
|
143
145
|
if (!Array.isArray(raw))
|
|
144
146
|
return [];
|
|
145
147
|
const out = [];
|
|
@@ -150,7 +152,7 @@ function skillRefsFromManifest(manifest) {
|
|
|
150
152
|
}
|
|
151
153
|
if (item && typeof item === 'object') {
|
|
152
154
|
const o = item;
|
|
153
|
-
const skillId = String(o.
|
|
155
|
+
const skillId = String(o.code ?? '');
|
|
154
156
|
if (!skillId)
|
|
155
157
|
continue;
|
|
156
158
|
out.push({
|
|
@@ -171,7 +173,7 @@ function parseStringList(value) {
|
|
|
171
173
|
return out.length > 0 ? out : undefined;
|
|
172
174
|
}
|
|
173
175
|
/**
|
|
174
|
-
*
|
|
176
|
+
* 装配引用 vs 内联完整 ToolManifest。
|
|
175
177
|
* 含 type/execution 的视为内联定义;仅 toolset/name/version/enabledTools 等视为引用。
|
|
176
178
|
*/
|
|
177
179
|
export function isInlineToolDeclaration(item) {
|
|
@@ -189,7 +191,7 @@ function parseToolAssemblyRef(item) {
|
|
|
189
191
|
if (isInlineToolDeclaration(item))
|
|
190
192
|
return null;
|
|
191
193
|
const o = item;
|
|
192
|
-
const name = String(o.
|
|
194
|
+
const name = String(o.code ?? o.name ?? '');
|
|
193
195
|
if (!name)
|
|
194
196
|
return null;
|
|
195
197
|
return {
|
|
@@ -197,14 +199,13 @@ function parseToolAssemblyRef(item) {
|
|
|
197
199
|
toolId: o.toolId != null ? String(o.toolId) : undefined,
|
|
198
200
|
version: o.version != null ? String(o.version) : undefined,
|
|
199
201
|
sha256: typeof o.sha256 === 'string' ? o.sha256 : undefined,
|
|
200
|
-
enabledTools: parseStringList(o.
|
|
202
|
+
enabledTools: parseStringList(o.enabled),
|
|
201
203
|
autoApprove: parseStringList(o.autoApprove),
|
|
202
204
|
};
|
|
203
205
|
}
|
|
204
206
|
/**
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
function toolRefsFromManifest(manifest) {
|
|
207
|
+
/** 从 agent.manifest.toolsets 收集工具集装配引用。 */
|
|
208
|
+
function toolsetsFromManifest(manifest) {
|
|
208
209
|
const out = [];
|
|
209
210
|
const byName = new Map();
|
|
210
211
|
const upsert = (ref) => {
|
|
@@ -214,7 +215,6 @@ function toolRefsFromManifest(manifest) {
|
|
|
214
215
|
out.push(ref);
|
|
215
216
|
return;
|
|
216
217
|
}
|
|
217
|
-
// 后写补全白名单等字段,不覆盖已有显式值
|
|
218
218
|
if (!prev.enabledTools && ref.enabledTools)
|
|
219
219
|
prev.enabledTools = ref.enabledTools;
|
|
220
220
|
if (!prev.autoApprove && ref.autoApprove)
|
|
@@ -226,12 +226,7 @@ function toolRefsFromManifest(manifest) {
|
|
|
226
226
|
if (!prev.toolId && ref.toolId)
|
|
227
227
|
prev.toolId = ref.toolId;
|
|
228
228
|
};
|
|
229
|
-
for (const item of Array.isArray(manifest.
|
|
230
|
-
const ref = parseToolAssemblyRef(item);
|
|
231
|
-
if (ref)
|
|
232
|
-
upsert(ref);
|
|
233
|
-
}
|
|
234
|
-
for (const item of Array.isArray(manifest.tools) ? manifest.tools : []) {
|
|
229
|
+
for (const item of Array.isArray(manifest.toolsets) ? manifest.toolsets : []) {
|
|
235
230
|
const ref = parseToolAssemblyRef(item);
|
|
236
231
|
if (ref)
|
|
237
232
|
upsert(ref);
|
|
@@ -239,12 +234,12 @@ function toolRefsFromManifest(manifest) {
|
|
|
239
234
|
return out;
|
|
240
235
|
}
|
|
241
236
|
/**
|
|
242
|
-
* 将 agent.manifest.
|
|
237
|
+
* 将 agent.manifest.toolsets 上的 enabled/autoApprove 叠到 snapshot.tools[](snapshot 未带时)。
|
|
243
238
|
*/
|
|
244
239
|
export function overlayToolRefPolicies(snapTools, manifest) {
|
|
245
240
|
if (!manifest)
|
|
246
241
|
return snapTools;
|
|
247
|
-
const fromManifest =
|
|
242
|
+
const fromManifest = toolsetsFromManifest(manifest);
|
|
248
243
|
if (fromManifest.length === 0)
|
|
249
244
|
return snapTools;
|
|
250
245
|
const byName = new Map(fromManifest.map((r) => [r.name, r]));
|
|
@@ -259,6 +254,12 @@ export function overlayToolRefPolicies(snapTools, manifest) {
|
|
|
259
254
|
};
|
|
260
255
|
});
|
|
261
256
|
}
|
|
257
|
+
function nonemptyRefs(refs) {
|
|
258
|
+
return refs && refs.length > 0 ? refs : undefined;
|
|
259
|
+
}
|
|
260
|
+
function nonemptySkillRefs(refs) {
|
|
261
|
+
return refs && refs.length > 0 ? refs : undefined;
|
|
262
|
+
}
|
|
262
263
|
function normalizeSnapshotToolRefs(raw) {
|
|
263
264
|
if (!Array.isArray(raw))
|
|
264
265
|
return undefined;
|
|
@@ -281,7 +282,7 @@ function normalizeSnapshotSkillRefs(raw) {
|
|
|
281
282
|
}
|
|
282
283
|
if (item && typeof item === 'object') {
|
|
283
284
|
const o = item;
|
|
284
|
-
const skillId = String(o.
|
|
285
|
+
const skillId = String(o.code ?? o.skillId ?? '');
|
|
285
286
|
if (!skillId)
|
|
286
287
|
continue;
|
|
287
288
|
out.push({
|
|
@@ -417,8 +418,8 @@ export class FileSystemDeploymentSnapshot {
|
|
|
417
418
|
return {
|
|
418
419
|
agentCode,
|
|
419
420
|
version: String(snap.version ?? resolvedVersion),
|
|
420
|
-
tools: normalizeSnapshotToolRefs(snap.tools) ??
|
|
421
|
-
skills: normalizeSnapshotSkillRefs(snap.skills) ?? skillRefsFromManifest(manifest ?? {}),
|
|
421
|
+
tools: nonemptyRefs(normalizeSnapshotToolRefs(snap.tools)) ?? toolsetsFromManifest(manifest ?? {}),
|
|
422
|
+
skills: nonemptySkillRefs(normalizeSnapshotSkillRefs(snap.skills)) ?? skillRefsFromManifest(manifest ?? {}),
|
|
422
423
|
graph,
|
|
423
424
|
agentDir,
|
|
424
425
|
manifest: manifest ?? undefined,
|
|
@@ -428,7 +429,7 @@ export class FileSystemDeploymentSnapshot {
|
|
|
428
429
|
return {
|
|
429
430
|
agentCode,
|
|
430
431
|
version: resolvedVersion,
|
|
431
|
-
tools:
|
|
432
|
+
tools: toolsetsFromManifest(manifest ?? {}),
|
|
432
433
|
skills: skillRefsFromManifest(manifest ?? {}),
|
|
433
434
|
graph,
|
|
434
435
|
agentDir,
|
|
@@ -497,10 +498,9 @@ export class FileSystemDeploymentSnapshot {
|
|
|
497
498
|
if (!(await pathExists(bundlePath)))
|
|
498
499
|
continue;
|
|
499
500
|
await this.assertReadyIfRequired(bundlePath, `skill ${ref.skillId}`);
|
|
501
|
+
const entryFile = await findSkillEntryFile(bundlePath);
|
|
500
502
|
const manifestPathCandidates = [
|
|
501
503
|
path.join(bundlePath, 'skill.manifest.json'),
|
|
502
|
-
path.join(bundlePath, 'skill.definition.json'),
|
|
503
|
-
path.join(bundlePath, 'manifest.json'),
|
|
504
504
|
];
|
|
505
505
|
let manifest = null;
|
|
506
506
|
let manifestPath = null;
|
|
@@ -512,22 +512,44 @@ export class FileSystemDeploymentSnapshot {
|
|
|
512
512
|
break;
|
|
513
513
|
}
|
|
514
514
|
}
|
|
515
|
+
if (!manifest && entryFile) {
|
|
516
|
+
const parsed = parseSkillMd(await readFile(entryFile, 'utf8'));
|
|
517
|
+
if (!parsed)
|
|
518
|
+
continue;
|
|
519
|
+
manifest = {
|
|
520
|
+
code: parsed.frontmatter.name,
|
|
521
|
+
name: parsed.frontmatter.name,
|
|
522
|
+
description: parsed.frontmatter.description,
|
|
523
|
+
...(parsed.frontmatter.license ? { license: parsed.frontmatter.license } : {}),
|
|
524
|
+
};
|
|
525
|
+
manifestPath = entryFile;
|
|
526
|
+
}
|
|
515
527
|
if (!manifest || !manifestPath)
|
|
516
528
|
continue;
|
|
517
|
-
|
|
529
|
+
if (!manifest.description && entryFile) {
|
|
530
|
+
const parsed = parseSkillMd(await readFile(entryFile, 'utf8'));
|
|
531
|
+
if (parsed?.frontmatter.description) {
|
|
532
|
+
manifest = { ...manifest, description: parsed.frontmatter.description };
|
|
533
|
+
if (!manifest.name)
|
|
534
|
+
manifest.name = parsed.frontmatter.name;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
518
537
|
const isUserOverridePath = bundlePath.includes('/users/') || bundlePath.includes('\\users\\');
|
|
519
538
|
const isAuthorOverlay = extraRoots.some((root) => root && isPathInside(root, bundlePath));
|
|
520
|
-
if (ref.sha256 && !isUserOverridePath && !isAuthorOverlay) {
|
|
539
|
+
if (ref.sha256 && !isUserOverridePath && !isAuthorOverlay && manifestPath.endsWith('.json')) {
|
|
521
540
|
const actual = await sha256OfFile(manifestPath);
|
|
522
541
|
await assertDigestMatch(ref.sha256, actual, `skill ${ref.skillId}`, { path: manifestPath });
|
|
523
542
|
}
|
|
524
|
-
const skillId = String(manifest.code ??
|
|
543
|
+
const skillId = String(manifest.code ?? ref.skillId);
|
|
525
544
|
return {
|
|
526
545
|
skillId,
|
|
527
546
|
name: String(manifest.name ?? ref.name ?? skillId),
|
|
528
547
|
version: String(manifest.version ?? ref.version ?? path.basename(bundlePath)),
|
|
529
548
|
description: manifest.description ?? ref.description,
|
|
530
|
-
manifest
|
|
549
|
+
manifest: {
|
|
550
|
+
...manifest,
|
|
551
|
+
...(entryFile ? { skillEntryFile: entryFile } : {}),
|
|
552
|
+
},
|
|
531
553
|
bundlePath,
|
|
532
554
|
};
|
|
533
555
|
}
|
|
@@ -111,7 +111,7 @@ export declare class GraphLoader {
|
|
|
111
111
|
loadToolDefinitions(agentCode: string, options?: LoadToolDefinitionsOptions): Promise<LoadedToolDefinition[]>;
|
|
112
112
|
private loadToolDefinitionsFromSource;
|
|
113
113
|
/**
|
|
114
|
-
*
|
|
114
|
+
* 加载已装配 Skill:SKILL.md 目录(Agent Skills)或可选 skill.manifest.json。
|
|
115
115
|
* @param options.userId 用于解析 users/<userId>/skills(优先于 preinstall/skills)
|
|
116
116
|
* @param options.allowMissingSkills 缺失时跳过而非抛错(warm 预热用)
|
|
117
117
|
*/
|