@faapi/agent 6.31.0 → 6.32.0
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/dist/index.d.ts +58 -17
- package/dist/index.js +178 -76
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { LlmConfig, AgentToolDescriptor, FaapiContext, AgentCore, AgentMetadata, ToolMetadata, ToolModule, FaapiPlugin } from '@faapi/faapi';
|
|
1
|
+
import { LlmConfig, LlmComplete, AgentToolDescriptor, FaapiContext, AgentCore, AgentMetadata, ToolMetadata, ToolModule, FaapiPlugin } from '@faapi/faapi';
|
|
2
|
+
export { LlmComplete, LlmCompleteOptions } from '@faapi/faapi';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* LLM Provider 错误
|
|
@@ -11,12 +12,31 @@ declare class LLMProviderError extends Error {
|
|
|
11
12
|
readonly status?: number;
|
|
12
13
|
/** 响应体摘要(前 500 字符,便于诊断) */
|
|
13
14
|
readonly body?: string;
|
|
15
|
+
/**
|
|
16
|
+
* 实际发起的 HTTP 尝试次数(≥1,含失败尝试)
|
|
17
|
+
*
|
|
18
|
+
* 重试耗尽抛出时由重试循环回填(构造时未知,故非 readonly);
|
|
19
|
+
* 首次尝试即失败的确定性错误(4xx 直抛)为 1,未回填时视为 1。
|
|
20
|
+
*/
|
|
21
|
+
attempts?: number;
|
|
14
22
|
constructor(message: string, options?: {
|
|
15
23
|
status?: number;
|
|
16
24
|
body?: string;
|
|
17
25
|
cause?: unknown;
|
|
18
26
|
});
|
|
19
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* LLM 请求超时
|
|
30
|
+
*
|
|
31
|
+
* {@link LLMProviderError} 的超时子类——`instanceof LLMTimeoutError` 可编程区分
|
|
32
|
+
* 「超时」与「网络错误」(两者 status 均为 undefined,仅靠 message 字符串无法区分)。
|
|
33
|
+
* 超时计入重试(与 429/5xx/网络错误同策略)。
|
|
34
|
+
*/
|
|
35
|
+
declare class LLMTimeoutError extends LLMProviderError {
|
|
36
|
+
constructor(message: string, options?: {
|
|
37
|
+
cause?: unknown;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
20
40
|
/**
|
|
21
41
|
* 创建 OpenAI 兼容 LLMProvider
|
|
22
42
|
*
|
|
@@ -121,11 +141,23 @@ interface LLMCompleteRequest {
|
|
|
121
141
|
temperature?: number;
|
|
122
142
|
/** 最大生成 token 数 */
|
|
123
143
|
maxTokens?: number;
|
|
144
|
+
/**
|
|
145
|
+
* 本次请求的超时(毫秒,调用级覆盖)
|
|
146
|
+
*
|
|
147
|
+
* 缺省回落 `LlmConfig.timeoutMs`,两者皆未设置时无超时。
|
|
148
|
+
* 超时触发抛 `LLMTimeoutError`(计入重试,与 429/5xx/网络错误同策略)。
|
|
149
|
+
*/
|
|
150
|
+
timeoutMs?: number;
|
|
151
|
+
/**
|
|
152
|
+
* 本次请求的重试上限(调用级覆盖)
|
|
153
|
+
*
|
|
154
|
+
* 缺省回落 `LlmConfig.maxRetries`(默认 2)。仅 429/5xx/网络错误/超时计入重试。
|
|
155
|
+
*/
|
|
156
|
+
maxRetries?: number;
|
|
124
157
|
/**
|
|
125
158
|
* 取消信号(透传到底层 HTTP 请求)
|
|
126
159
|
*
|
|
127
|
-
* abort 时请求中断并抛 `AgentAbortError
|
|
128
|
-
* 超时信号组合生效(任一触发即中断)。
|
|
160
|
+
* abort 时请求中断并抛 `AgentAbortError`;与超时信号组合生效(任一触发即中断)。
|
|
129
161
|
*/
|
|
130
162
|
signal?: AbortSignal;
|
|
131
163
|
}
|
|
@@ -168,6 +200,12 @@ interface LLMResponse {
|
|
|
168
200
|
stopReason: LLMStopReason;
|
|
169
201
|
/** token 用量(部分 provider 不返回) */
|
|
170
202
|
usage?: LLMUsage;
|
|
203
|
+
/**
|
|
204
|
+
* 实际发起的 HTTP 尝试次数(≥1,含失败尝试)
|
|
205
|
+
*
|
|
206
|
+
* 供失败钩子/日志观测重试消耗;provider 未提供时视为 1。
|
|
207
|
+
*/
|
|
208
|
+
attempts?: number;
|
|
171
209
|
}
|
|
172
210
|
/**
|
|
173
211
|
* Token 用量(OpenAI chat completions 规范形)
|
|
@@ -228,6 +266,20 @@ interface LLMProvider {
|
|
|
228
266
|
*/
|
|
229
267
|
declare function createProvider(config: LlmConfig): LLMProvider;
|
|
230
268
|
|
|
269
|
+
/**
|
|
270
|
+
* 创建轻量补全通道
|
|
271
|
+
*
|
|
272
|
+
* @param deps.llms provider 配置映射(来自 `config.agent.llms`,同源零新增配置)
|
|
273
|
+
* @param deps.providers 已构建的 provider 实例映射(插件传入——与 agent 循环共享
|
|
274
|
+
* 同一单例);缺省时按 `llms` 逐项 `createProvider` 现场构建
|
|
275
|
+
* (隔离 worker 重建 / 测试形态)
|
|
276
|
+
* @returns 补全通道(`complete` 方法)
|
|
277
|
+
*/
|
|
278
|
+
declare function createLightComplete(deps: {
|
|
279
|
+
llms: Record<string, LlmConfig>;
|
|
280
|
+
providers?: Map<string, LLMProvider>;
|
|
281
|
+
}): LlmComplete;
|
|
282
|
+
|
|
231
283
|
/**
|
|
232
284
|
* 单次 agent.run() / agent.stream() 的结构化调用明细
|
|
233
285
|
*
|
|
@@ -1096,19 +1148,8 @@ declare class Agent {
|
|
|
1096
1148
|
*/
|
|
1097
1149
|
private resolveExternalProvider;
|
|
1098
1150
|
/**
|
|
1099
|
-
* 解析 `options.model` 字符串 key → provider + model
|
|
1100
|
-
*
|
|
1101
|
-
* 规则见 [agentHandle.md](./agentHandle.md) 的「`options.model` 字符串 key 解析规则」。
|
|
1102
|
-
* 无默认 provider——`key` 未传时用 agent 元数据 `config.model` 作为缺省 key;
|
|
1103
|
-
* 两者皆无抛 `AgentError`(要求调用方传 `options.model` 或 `options.provider`)。
|
|
1104
|
-
* 1. 精确匹配 `deps.providers` 的 key → 该 provider + 其 `models` 第一个 key
|
|
1105
|
-
* (该 provider 未声明 `models` 时回落 `meta.model`)
|
|
1106
|
-
* 2. 含 `/` → `provider/model` 形式,`deps.providers.get(provider)` + 该 model
|
|
1107
|
-
* (要求该 model 在 `deps.llms[provider].models` 里)
|
|
1108
|
-
* 3. 不含 `/` 且非 provider key → 在所有 provider 的 `models` 里按 model 名查找
|
|
1109
|
-
* - 唯一 → 该 provider + 该 model
|
|
1110
|
-
* - 多个 → 抛 `AgentError`(要求用 `provider/model` 消歧)
|
|
1111
|
-
* - 无 → 抛 `AgentError`
|
|
1151
|
+
* 解析 `options.model` 字符串 key → provider + model(模块级 [resolveModelKey] 的
|
|
1152
|
+
* Agent 方法包装——meta.model 作缺省 key,与 [lightComplete](./lightComplete.md) 共用同一解析实现)
|
|
1112
1153
|
*
|
|
1113
1154
|
* @throws {AgentError} key 与 `meta.model` 均缺省;key 解析失败(provider/model
|
|
1114
1155
|
* 不存在或歧义)
|
|
@@ -1294,4 +1335,4 @@ declare function createToolSchemaResolver(options?: {
|
|
|
1294
1335
|
*/
|
|
1295
1336
|
declare const agentPlugin: FaapiPlugin;
|
|
1296
1337
|
|
|
1297
|
-
export { Agent, AgentAbortError, type AgentDeps, AgentError, type AgentHandle, AgentRecursionError, type AgentRunOptions, type AgentRuntimeConfig, AgentToolTimeoutError, type AgentTrace, type AgentTraceEvent, type LLMCompleteRequest, type LLMMessage, type LLMProvider, LLMProviderError, type LLMResponse, type LLMStopReason, type LLMStreamChunk, type LLMToolCall, type LLMToolDefinition, type LLMUsage, type LlmCallEvent, type ReactLoopConfig, ReactLoopError, type ReactLoopResult, type ReactLoopStreamChunk, type SchemaSourceRef, type SubAgentCallEvent, type SubAgentDelta, type SubAgentDeltaEmitter, type SubAgentToolResult, type ToolCallEvent, type ToolExecutor, type ToolSchemaResolution, type TracingToolResult, createOpenAIProvider, createProvider, createToolSchemaResolver, agentPlugin as default, isSubAgentToolResult, isTracingToolResult, reactLoop, reactLoopStream };
|
|
1338
|
+
export { Agent, AgentAbortError, type AgentDeps, AgentError, type AgentHandle, AgentRecursionError, type AgentRunOptions, type AgentRuntimeConfig, AgentToolTimeoutError, type AgentTrace, type AgentTraceEvent, type LLMCompleteRequest, type LLMMessage, type LLMProvider, LLMProviderError, type LLMResponse, type LLMStopReason, type LLMStreamChunk, LLMTimeoutError, type LLMToolCall, type LLMToolDefinition, type LLMUsage, type LlmCallEvent, type ReactLoopConfig, ReactLoopError, type ReactLoopResult, type ReactLoopStreamChunk, type SchemaSourceRef, type SubAgentCallEvent, type SubAgentDelta, type SubAgentDeltaEmitter, type SubAgentToolResult, type ToolCallEvent, type ToolExecutor, type ToolSchemaResolution, type TracingToolResult, createLightComplete, createOpenAIProvider, createProvider, createToolSchemaResolver, agentPlugin as default, isSubAgentToolResult, isTracingToolResult, reactLoop, reactLoopStream };
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,13 @@ var LLMProviderError = class extends Error {
|
|
|
14
14
|
status;
|
|
15
15
|
/** 响应体摘要(前 500 字符,便于诊断) */
|
|
16
16
|
body;
|
|
17
|
+
/**
|
|
18
|
+
* 实际发起的 HTTP 尝试次数(≥1,含失败尝试)
|
|
19
|
+
*
|
|
20
|
+
* 重试耗尽抛出时由重试循环回填(构造时未知,故非 readonly);
|
|
21
|
+
* 首次尝试即失败的确定性错误(4xx 直抛)为 1,未回填时视为 1。
|
|
22
|
+
*/
|
|
23
|
+
attempts;
|
|
17
24
|
constructor(message, options) {
|
|
18
25
|
super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
19
26
|
this.name = "LLMProviderError";
|
|
@@ -21,6 +28,12 @@ var LLMProviderError = class extends Error {
|
|
|
21
28
|
this.body = options?.body;
|
|
22
29
|
}
|
|
23
30
|
};
|
|
31
|
+
var LLMTimeoutError = class extends LLMProviderError {
|
|
32
|
+
constructor(message, options) {
|
|
33
|
+
super(message, options);
|
|
34
|
+
this.name = "LLMTimeoutError";
|
|
35
|
+
}
|
|
36
|
+
};
|
|
24
37
|
function createOpenAIProvider(config) {
|
|
25
38
|
const baseURL = (config.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
26
39
|
const apiKey = config.apiKey;
|
|
@@ -69,13 +82,19 @@ function createOpenAIProvider(config) {
|
|
|
69
82
|
if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
|
|
70
83
|
return headers;
|
|
71
84
|
}
|
|
72
|
-
function
|
|
85
|
+
function resolveTimeoutMs(request) {
|
|
86
|
+
if (typeof request.timeoutMs === "number") return request.timeoutMs;
|
|
87
|
+
return typeof config.timeoutMs === "number" ? config.timeoutMs : void 0;
|
|
88
|
+
}
|
|
89
|
+
function classifyAbortError(err, externalSignal, timeoutMs) {
|
|
73
90
|
if (externalSignal?.aborted) {
|
|
74
91
|
return new AgentAbortError();
|
|
75
92
|
}
|
|
76
93
|
const errName = err?.name;
|
|
77
94
|
if (errName === "AbortError" || errName === "TimeoutError") {
|
|
78
|
-
return new
|
|
95
|
+
return new LLMTimeoutError(
|
|
96
|
+
timeoutMs !== void 0 ? `LLM request timed out after ${timeoutMs}ms` : "LLM request timed out"
|
|
97
|
+
);
|
|
79
98
|
}
|
|
80
99
|
const reason = err instanceof Error ? err.message : String(err);
|
|
81
100
|
return new LLMProviderError(`Network error: ${reason}`, { cause: err });
|
|
@@ -84,11 +103,11 @@ function createOpenAIProvider(config) {
|
|
|
84
103
|
const name = err?.name;
|
|
85
104
|
return name === "AbortError" || name === "TimeoutError";
|
|
86
105
|
}
|
|
87
|
-
async function safeFetch(url, init, externalSignal) {
|
|
106
|
+
async function safeFetch(url, init, externalSignal, timeoutMs) {
|
|
88
107
|
try {
|
|
89
108
|
return await fetch(url, init);
|
|
90
109
|
} catch (err) {
|
|
91
|
-
throw classifyAbortError(err, externalSignal);
|
|
110
|
+
throw classifyAbortError(err, externalSignal, timeoutMs);
|
|
92
111
|
}
|
|
93
112
|
}
|
|
94
113
|
function isRetryable(err) {
|
|
@@ -106,17 +125,23 @@ function createOpenAIProvider(config) {
|
|
|
106
125
|
}
|
|
107
126
|
function effectiveSignal(request) {
|
|
108
127
|
const { signal } = request;
|
|
109
|
-
const timeoutMs =
|
|
128
|
+
const timeoutMs = resolveTimeoutMs(request);
|
|
110
129
|
if (!signal && !timeoutMs) return void 0;
|
|
111
130
|
const signals = [];
|
|
112
131
|
if (signal) signals.push(signal);
|
|
113
132
|
if (timeoutMs) signals.push(AbortSignal.timeout(timeoutMs));
|
|
114
133
|
return signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
115
134
|
}
|
|
116
|
-
|
|
135
|
+
function resolveMaxRetries(request) {
|
|
136
|
+
if (typeof request.maxRetries === "number") return Math.max(0, request.maxRetries);
|
|
137
|
+
return maxRetries;
|
|
138
|
+
}
|
|
139
|
+
async function fetchOkWithRetry(url, baseInit, request) {
|
|
140
|
+
const maxAttemptsRetries = resolveMaxRetries(request);
|
|
141
|
+
const timeoutMs = resolveTimeoutMs(request);
|
|
117
142
|
let lastErr;
|
|
118
143
|
let lastResponse;
|
|
119
|
-
for (let attempt = 0; attempt <=
|
|
144
|
+
for (let attempt = 0; attempt <= maxAttemptsRetries; attempt++) {
|
|
120
145
|
if (request.signal?.aborted) {
|
|
121
146
|
throw new AgentAbortError();
|
|
122
147
|
}
|
|
@@ -126,13 +151,13 @@ function createOpenAIProvider(config) {
|
|
|
126
151
|
const init = { ...baseInit, signal: effectiveSignal(request) };
|
|
127
152
|
let response;
|
|
128
153
|
try {
|
|
129
|
-
response = await safeFetch(url, init, request.signal);
|
|
154
|
+
response = await safeFetch(url, init, request.signal, timeoutMs);
|
|
130
155
|
} catch (err2) {
|
|
131
156
|
if (!isRetryable(err2)) throw err2;
|
|
132
157
|
lastErr = err2;
|
|
133
158
|
continue;
|
|
134
159
|
}
|
|
135
|
-
if (response.ok) return response;
|
|
160
|
+
if (response.ok) return { response, attempts: attempt + 1 };
|
|
136
161
|
let excerpt = "<body unreadable>";
|
|
137
162
|
try {
|
|
138
163
|
excerpt = (await response.text()).slice(0, 500);
|
|
@@ -146,6 +171,9 @@ function createOpenAIProvider(config) {
|
|
|
146
171
|
lastErr = err;
|
|
147
172
|
lastResponse = response;
|
|
148
173
|
}
|
|
174
|
+
if (lastErr instanceof LLMProviderError) {
|
|
175
|
+
lastErr.attempts = maxAttemptsRetries + 1;
|
|
176
|
+
}
|
|
149
177
|
throw lastErr;
|
|
150
178
|
}
|
|
151
179
|
async function complete(request) {
|
|
@@ -156,13 +184,14 @@ function createOpenAIProvider(config) {
|
|
|
156
184
|
headers: buildHeaders(),
|
|
157
185
|
body: JSON.stringify(body)
|
|
158
186
|
};
|
|
159
|
-
const
|
|
187
|
+
const timeoutMs = resolveTimeoutMs(request);
|
|
188
|
+
const { response, attempts } = await fetchOkWithRetry(url, baseInit, request);
|
|
160
189
|
let bodyText;
|
|
161
190
|
try {
|
|
162
191
|
bodyText = await response.text();
|
|
163
192
|
} catch (err) {
|
|
164
193
|
if (!isAbortLike(err)) throw err;
|
|
165
|
-
throw classifyAbortError(err, request.signal);
|
|
194
|
+
throw classifyAbortError(err, request.signal, timeoutMs);
|
|
166
195
|
}
|
|
167
196
|
let json;
|
|
168
197
|
try {
|
|
@@ -192,7 +221,8 @@ function createOpenAIProvider(config) {
|
|
|
192
221
|
return {
|
|
193
222
|
message,
|
|
194
223
|
stopReason: mapStopReason(choice.finish_reason ?? void 0),
|
|
195
|
-
usage: mapUsage(json.usage)
|
|
224
|
+
usage: mapUsage(json.usage),
|
|
225
|
+
attempts
|
|
196
226
|
};
|
|
197
227
|
}
|
|
198
228
|
async function* stream(request) {
|
|
@@ -204,7 +234,8 @@ function createOpenAIProvider(config) {
|
|
|
204
234
|
headers: buildHeaders(),
|
|
205
235
|
body: JSON.stringify(body)
|
|
206
236
|
};
|
|
207
|
-
const
|
|
237
|
+
const timeoutMs = resolveTimeoutMs(request);
|
|
238
|
+
const { response } = await fetchOkWithRetry(url, baseInit, request);
|
|
208
239
|
if (!response.body) {
|
|
209
240
|
throw new LLMProviderError("Response body is null (streaming unsupported)", {
|
|
210
241
|
status: response.status
|
|
@@ -281,7 +312,7 @@ function createOpenAIProvider(config) {
|
|
|
281
312
|
yield finalizeStreamChunk(accumulators, finishReason, usage);
|
|
282
313
|
} catch (err) {
|
|
283
314
|
if (!isAbortLike(err)) throw err;
|
|
284
|
-
throw classifyAbortError(err, request.signal);
|
|
315
|
+
throw classifyAbortError(err, request.signal, timeoutMs);
|
|
285
316
|
} finally {
|
|
286
317
|
try {
|
|
287
318
|
await reader.cancel();
|
|
@@ -433,6 +464,9 @@ function createProvider(config) {
|
|
|
433
464
|
}
|
|
434
465
|
}
|
|
435
466
|
|
|
467
|
+
// src/agent.ts
|
|
468
|
+
import { readResource, subAgentToolName } from "@faapi/faapi";
|
|
469
|
+
|
|
436
470
|
// src/trace.ts
|
|
437
471
|
function isTracingToolResult(value) {
|
|
438
472
|
return typeof value === "object" && value !== null && value.__trace === true;
|
|
@@ -958,7 +992,6 @@ async function* reactLoopStream(input, config) {
|
|
|
958
992
|
}
|
|
959
993
|
|
|
960
994
|
// src/agent.ts
|
|
961
|
-
import { readResource, subAgentToolName } from "@faapi/faapi";
|
|
962
995
|
var DEFAULT_MAX_AGENT_DEPTH = 3;
|
|
963
996
|
var DEFAULT_SUBAGENT_INPUT_DESCRIPTION = "\u6D3E\u53D1\u7ED9\u8BE5 agent \u7684\u4EFB\u52A1\u4EA4\u63A5\u5355\uFF08\u81EA\u7136\u8BED\u8A00\uFF09\uFF1A\u5199\u6E05\u4EFB\u52A1\u76EE\u6807\u3001\u5FC5\u8981\u7684\u4E0A\u4E0B\u6587\u4FE1\u606F\u4E0E\u5BF9\u4EA7\u51FA\u7ED3\u679C\u7684\u8981\u6C42";
|
|
964
997
|
function extractSubAgentUserInput(args) {
|
|
@@ -984,6 +1017,64 @@ function materializeProvider(external) {
|
|
|
984
1017
|
'options.provider must be an LlmConfig object (with a string "provider" field) or an LLMProvider instance (with complete/stream methods)'
|
|
985
1018
|
);
|
|
986
1019
|
}
|
|
1020
|
+
function resolveModelKey(key, deps, fallbackKey) {
|
|
1021
|
+
const effectiveKey = key ?? fallbackKey;
|
|
1022
|
+
if (effectiveKey === void 0) {
|
|
1023
|
+
throw new AgentError(
|
|
1024
|
+
'No LLM provider resolved: pass options.model (a provider key / "provider/model" / model name from config.agent.llms), options.provider (external provider), or declare model in the agent config'
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
const byProviderKey = deps.providers.get(effectiveKey);
|
|
1028
|
+
if (byProviderKey) {
|
|
1029
|
+
const llmConfig = deps.llms[effectiveKey];
|
|
1030
|
+
const firstModel = llmConfig ? Object.keys(llmConfig.models)[0] : void 0;
|
|
1031
|
+
return {
|
|
1032
|
+
provider: byProviderKey,
|
|
1033
|
+
providerName: effectiveKey,
|
|
1034
|
+
model: firstModel ?? fallbackKey
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
if (effectiveKey.includes("/")) {
|
|
1038
|
+
const slashIdx = effectiveKey.indexOf("/");
|
|
1039
|
+
const providerName = effectiveKey.slice(0, slashIdx);
|
|
1040
|
+
const modelName = effectiveKey.slice(slashIdx + 1);
|
|
1041
|
+
const provider = deps.providers.get(providerName);
|
|
1042
|
+
if (!provider) {
|
|
1043
|
+
throw new AgentError(
|
|
1044
|
+
`Unknown provider "${providerName}" in model key "${effectiveKey}". Declare it in config.agent.llms, or pass options.provider to use an external provider.`
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
const llmConfig = deps.llms[providerName];
|
|
1048
|
+
if (!llmConfig || !llmConfig.models[modelName]) {
|
|
1049
|
+
throw new AgentError(
|
|
1050
|
+
`Model "${modelName}" not found in provider "${providerName}". Declare it in config.agent.llms.${providerName}.models.`
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
return { provider, providerName, model: modelName };
|
|
1054
|
+
}
|
|
1055
|
+
const matches = [];
|
|
1056
|
+
for (const [providerName, provider] of deps.providers) {
|
|
1057
|
+
const llmConfig = deps.llms[providerName];
|
|
1058
|
+
if (llmConfig && llmConfig.models[effectiveKey]) {
|
|
1059
|
+
matches.push({ provider, providerName });
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
if (matches.length === 1) {
|
|
1063
|
+
return {
|
|
1064
|
+
provider: matches[0].provider,
|
|
1065
|
+
providerName: matches[0].providerName,
|
|
1066
|
+
model: effectiveKey
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
if (matches.length > 1) {
|
|
1070
|
+
throw new AgentError(
|
|
1071
|
+
`Model "${effectiveKey}" is ambiguous (found in providers: ${matches.map((m) => m.providerName).join(", ")}). Use "provider/model" to disambiguate.`
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
throw new AgentError(
|
|
1075
|
+
`Model "${effectiveKey}" not found in any provider. Declare it in config.agent.llms.*.models, or pass options.provider to use an external provider.`
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
987
1078
|
function validateResumeHistory(messages) {
|
|
988
1079
|
messages.forEach((message, index) => {
|
|
989
1080
|
if (!VALID_MESSAGE_ROLES.has(message.role)) {
|
|
@@ -1286,72 +1377,14 @@ var Agent = class _Agent {
|
|
|
1286
1377
|
return { provider, model };
|
|
1287
1378
|
}
|
|
1288
1379
|
/**
|
|
1289
|
-
* 解析 `options.model` 字符串 key → provider + model
|
|
1290
|
-
*
|
|
1291
|
-
* 规则见 [agentHandle.md](./agentHandle.md) 的「`options.model` 字符串 key 解析规则」。
|
|
1292
|
-
* 无默认 provider——`key` 未传时用 agent 元数据 `config.model` 作为缺省 key;
|
|
1293
|
-
* 两者皆无抛 `AgentError`(要求调用方传 `options.model` 或 `options.provider`)。
|
|
1294
|
-
* 1. 精确匹配 `deps.providers` 的 key → 该 provider + 其 `models` 第一个 key
|
|
1295
|
-
* (该 provider 未声明 `models` 时回落 `meta.model`)
|
|
1296
|
-
* 2. 含 `/` → `provider/model` 形式,`deps.providers.get(provider)` + 该 model
|
|
1297
|
-
* (要求该 model 在 `deps.llms[provider].models` 里)
|
|
1298
|
-
* 3. 不含 `/` 且非 provider key → 在所有 provider 的 `models` 里按 model 名查找
|
|
1299
|
-
* - 唯一 → 该 provider + 该 model
|
|
1300
|
-
* - 多个 → 抛 `AgentError`(要求用 `provider/model` 消歧)
|
|
1301
|
-
* - 无 → 抛 `AgentError`
|
|
1380
|
+
* 解析 `options.model` 字符串 key → provider + model(模块级 [resolveModelKey] 的
|
|
1381
|
+
* Agent 方法包装——meta.model 作缺省 key,与 [lightComplete](./lightComplete.md) 共用同一解析实现)
|
|
1302
1382
|
*
|
|
1303
1383
|
* @throws {AgentError} key 与 `meta.model` 均缺省;key 解析失败(provider/model
|
|
1304
1384
|
* 不存在或歧义)
|
|
1305
1385
|
*/
|
|
1306
1386
|
resolveModelKey(key, meta) {
|
|
1307
|
-
|
|
1308
|
-
if (effectiveKey === void 0) {
|
|
1309
|
-
throw new AgentError(
|
|
1310
|
-
'No LLM provider resolved: pass options.model (a provider key / "provider/model" / model name from config.agent.llms), options.provider (external provider), or declare model in the agent config'
|
|
1311
|
-
);
|
|
1312
|
-
}
|
|
1313
|
-
const byProviderKey = this.deps.providers.get(effectiveKey);
|
|
1314
|
-
if (byProviderKey) {
|
|
1315
|
-
const llmConfig = this.deps.llms[effectiveKey];
|
|
1316
|
-
const firstModel = llmConfig ? Object.keys(llmConfig.models)[0] : void 0;
|
|
1317
|
-
return { provider: byProviderKey, model: firstModel ?? meta.model };
|
|
1318
|
-
}
|
|
1319
|
-
if (effectiveKey.includes("/")) {
|
|
1320
|
-
const slashIdx = effectiveKey.indexOf("/");
|
|
1321
|
-
const providerName = effectiveKey.slice(0, slashIdx);
|
|
1322
|
-
const modelName = effectiveKey.slice(slashIdx + 1);
|
|
1323
|
-
const provider = this.deps.providers.get(providerName);
|
|
1324
|
-
if (!provider) {
|
|
1325
|
-
throw new AgentError(
|
|
1326
|
-
`Unknown provider "${providerName}" in model key "${effectiveKey}". Declare it in config.agent.llms, or pass options.provider to use an external provider.`
|
|
1327
|
-
);
|
|
1328
|
-
}
|
|
1329
|
-
const llmConfig = this.deps.llms[providerName];
|
|
1330
|
-
if (!llmConfig || !llmConfig.models[modelName]) {
|
|
1331
|
-
throw new AgentError(
|
|
1332
|
-
`Model "${modelName}" not found in provider "${providerName}". Declare it in config.agent.llms.${providerName}.models.`
|
|
1333
|
-
);
|
|
1334
|
-
}
|
|
1335
|
-
return { provider, model: modelName };
|
|
1336
|
-
}
|
|
1337
|
-
const matches = [];
|
|
1338
|
-
for (const [providerName, provider] of this.deps.providers) {
|
|
1339
|
-
const llmConfig = this.deps.llms[providerName];
|
|
1340
|
-
if (llmConfig && llmConfig.models[effectiveKey]) {
|
|
1341
|
-
matches.push({ provider, providerName });
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
if (matches.length === 1) {
|
|
1345
|
-
return { provider: matches[0].provider, model: effectiveKey };
|
|
1346
|
-
}
|
|
1347
|
-
if (matches.length > 1) {
|
|
1348
|
-
throw new AgentError(
|
|
1349
|
-
`Model "${effectiveKey}" is ambiguous (found in providers: ${matches.map((m) => m.providerName).join(", ")}). Use "provider/model" to disambiguate.`
|
|
1350
|
-
);
|
|
1351
|
-
}
|
|
1352
|
-
throw new AgentError(
|
|
1353
|
-
`Model "${effectiveKey}" not found in any provider. Declare it in config.agent.llms.*.models, or pass options.provider to use an external provider.`
|
|
1354
|
-
);
|
|
1387
|
+
return resolveModelKey(key, this.deps, meta.model);
|
|
1355
1388
|
}
|
|
1356
1389
|
/**
|
|
1357
1390
|
* 组装 LLM 可见 tool 列表(OpenAI chat completions 规范形)
|
|
@@ -1623,6 +1656,72 @@ var Agent = class _Agent {
|
|
|
1623
1656
|
}
|
|
1624
1657
|
};
|
|
1625
1658
|
|
|
1659
|
+
// src/lightComplete.ts
|
|
1660
|
+
var DEFAULT_LIGHT_TIMEOUT_MS = 6e4;
|
|
1661
|
+
function createLightComplete(deps) {
|
|
1662
|
+
const llms = deps.llms;
|
|
1663
|
+
const providers = deps.providers ?? buildProviders(llms);
|
|
1664
|
+
return {
|
|
1665
|
+
async complete(input, options) {
|
|
1666
|
+
const defaultKey = Object.keys(llms)[0];
|
|
1667
|
+
const { provider, providerName, model } = resolveModelKey(
|
|
1668
|
+
options?.model,
|
|
1669
|
+
{ providers, llms },
|
|
1670
|
+
defaultKey
|
|
1671
|
+
);
|
|
1672
|
+
const messages = options?.system ? [
|
|
1673
|
+
{ role: "system", content: options.system },
|
|
1674
|
+
{ role: "user", content: input }
|
|
1675
|
+
] : [{ role: "user", content: input }];
|
|
1676
|
+
const llmConfig = llms[providerName];
|
|
1677
|
+
const configTimeout = typeof llmConfig?.timeoutMs === "number" ? llmConfig.timeoutMs : void 0;
|
|
1678
|
+
const timeoutMs = options?.timeoutMs ?? configTimeout ?? DEFAULT_LIGHT_TIMEOUT_MS;
|
|
1679
|
+
try {
|
|
1680
|
+
const response = await provider.complete({
|
|
1681
|
+
messages,
|
|
1682
|
+
model,
|
|
1683
|
+
...options?.temperature !== void 0 ? { temperature: options.temperature } : {},
|
|
1684
|
+
...options?.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
|
|
1685
|
+
...options?.signal !== void 0 ? { signal: options.signal } : {},
|
|
1686
|
+
timeoutMs,
|
|
1687
|
+
...options?.maxRetries !== void 0 ? { maxRetries: options.maxRetries } : {}
|
|
1688
|
+
});
|
|
1689
|
+
return response.message.content ?? "";
|
|
1690
|
+
} catch (err) {
|
|
1691
|
+
if (err instanceof AgentAbortError) throw err;
|
|
1692
|
+
if (!(err instanceof LLMProviderError)) throw err;
|
|
1693
|
+
return handleFailure(err, options);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
function buildProviders(llms) {
|
|
1699
|
+
const providers = /* @__PURE__ */ new Map();
|
|
1700
|
+
for (const [name, llmConfig] of Object.entries(llms)) {
|
|
1701
|
+
providers.set(name, createProvider(llmConfig));
|
|
1702
|
+
}
|
|
1703
|
+
return providers;
|
|
1704
|
+
}
|
|
1705
|
+
function handleFailure(err, options) {
|
|
1706
|
+
const { onFailure, fallback } = options ?? {};
|
|
1707
|
+
const attempts = err.attempts ?? 1;
|
|
1708
|
+
if (onFailure) {
|
|
1709
|
+
try {
|
|
1710
|
+
onFailure(err, { attempts });
|
|
1711
|
+
} catch {
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
if (fallback !== void 0) {
|
|
1715
|
+
if (!onFailure) {
|
|
1716
|
+
console.warn(
|
|
1717
|
+
`[faapi/agent] llm.complete fallback engaged after ${attempts} attempt(s): ${err.message}`
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
return Promise.resolve(fallback);
|
|
1721
|
+
}
|
|
1722
|
+
return Promise.reject(err);
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1626
1725
|
// src/toolSchemaResolver.ts
|
|
1627
1726
|
import { statSync } from "fs";
|
|
1628
1727
|
import { z } from "zod";
|
|
@@ -1692,6 +1791,7 @@ var agentPlugin = {
|
|
|
1692
1791
|
}
|
|
1693
1792
|
providers.set(name, createProvider(llmConfig));
|
|
1694
1793
|
}
|
|
1794
|
+
ctx.registries.llm.register(createLightComplete({ llms, providers }));
|
|
1695
1795
|
const runtimeConfig = {
|
|
1696
1796
|
maxTurns: agentConfig?.maxTurns,
|
|
1697
1797
|
maxAgentDepth: agentConfig?.maxAgentDepth,
|
|
@@ -1739,7 +1839,9 @@ export {
|
|
|
1739
1839
|
AgentRecursionError,
|
|
1740
1840
|
AgentToolTimeoutError,
|
|
1741
1841
|
LLMProviderError,
|
|
1842
|
+
LLMTimeoutError,
|
|
1742
1843
|
ReactLoopError,
|
|
1844
|
+
createLightComplete,
|
|
1743
1845
|
createOpenAIProvider,
|
|
1744
1846
|
createProvider,
|
|
1745
1847
|
createToolSchemaResolver,
|