@ai-zen/agents-sdk 1.0.0-alpha.13 → 1.0.0-alpha.14
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/CHANGELOG.md +32 -0
- package/dist/config/ConfigManager.d.ts +22 -1
- package/dist/config/ConfigManager.js +67 -0
- package/dist/config/constants.js +11 -17
- package/dist/config/reasoning-presets.d.ts +29 -0
- package/dist/config/reasoning-presets.js +56 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -2
- package/dist/runtime/createModel.d.ts +2 -1
- package/dist/runtime/createModel.js +3 -2
- package/dist/scope/Scope.d.ts +9 -1
- package/dist/scope/Scope.js +8 -0
- package/dist/shared/reasoning.d.ts +37 -39
- package/dist/shared/reasoning.js +116 -108
- package/dist/types/index.d.ts +32 -28
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0-alpha.14] - 2026-10-22
|
|
4
|
+
|
|
5
|
+
### 🔧 行为变更(不兼容)
|
|
6
|
+
|
|
7
|
+
- **思考能力声明从「模型字段」改为「策略表」** —— alpha.13 那版用 `Model.reasoning: { efforts, disable? }` 表达厂商差异,结果三处结构性缺陷:`disable` 一词双义(既指关闭方式又指能力)、`off` 靠拼接而非声明(`disable ? ["off", ...efforts] : efforts`)、用全局 `ReasoningEffort` 枚举假装各家档位等价。现在改为**档位 → 请求参数补丁**的声明式表:
|
|
8
|
+
- 新增 `<basePath>/reasoning-presets.json`(与 `config.json` 同级),出厂内容见 `config/reasoning-presets.ts`;
|
|
9
|
+
- `Model.reasoning?` → **`Model.reasoningPreset?: string`**(引用策略名;缺省 = 不可调档);
|
|
10
|
+
- `AgentDefinition.reasoningEffort` 类型 `ReasoningEffort` → **`string`**(档位名由策略表定义,不再用假枚举假装统一);
|
|
11
|
+
- 删除 `ReasoningEffort` / `ReasoningCapability` 类型与 `capabilityOf` / `modelEfforts` / `modelAllowedEfforts` / `isReasoningEffort` 函数;
|
|
12
|
+
- 函数签名统一以 `(model, presets, …)` 开头:`readReasoningEffort(model, presets, params?)`、`applyReasoningEffort(model, presets, level, params?)`、`resolveReasoningEffort(model, presets, definition?)`、`resolveModelParams(model, presets, definition?)`;
|
|
13
|
+
- `Scope` 新增 `reasoningPresets`(与 `config` 平级注入,不做全局单例)。
|
|
14
|
+
|
|
15
|
+
换来三个结构性保证,全部来自表的形状、代码零分支:① 可选档位 = `Object.keys(levels)`;② 能否关闭思考 = 表里有没有 `off` 键;③ 「哪些参数键属于思考」= 整张表出现过的键的并集(写入前按此剔除旧值,`max` → `off` 不再残留 `reasoning_effort`,模型换策略也不会反刍上一套参数)。
|
|
16
|
+
|
|
17
|
+
- **`applyReasoningEffort` 的档位校验更严** —— 策略里没有该档位直接抛错(旧版对未声明能力的模型给的是「不支持档位」文案),错误信息带上策略名与可选档位列表。
|
|
18
|
+
|
|
19
|
+
### 🚀 新功能
|
|
20
|
+
|
|
21
|
+
- **`ConfigManager.readReasoningPresets()` / `ensureReasoningPresets()`** —— 策略表的读写与出厂同步。同步粒度**到单个策略**,规则与出厂模型同步同构:文件缺失即写种子;`custom: true` 的策略冻结;未标的用出厂版覆盖;用户自建的非出厂策略永远保留。`bootstrap()` 会自动调用。文件缺失或损坏时 `readReasoningPresets()` 回落到出厂种子,不让上层拿到空表。
|
|
22
|
+
- **`presetOf(model, presets)` / `modelLevels(model, presets)`** —— 策略查找与可选档位读取(`modelLevels` 取代旧的 `modelEfforts` + `modelAllowedEfforts` 两个函数:`off` 现在只是表里的一行,没有「档位」与「关闭」的二分)。
|
|
23
|
+
- **包入口补导出 `TokenThresholds` / `ReasoningPatch` / `ReasoningPreset` / `ReasoningPresets`** —— alpha.13 漏了这几个类型的导出(原 `7b10196` 补丁并入本版本)。
|
|
24
|
+
|
|
25
|
+
### ✅ 测试
|
|
26
|
+
|
|
27
|
+
- `src/shared/reasoning.test.ts` 重写(26 例):出厂表协议事实(OpenAI 表无 `thinking` 键、`zhipu-5.3` 表无 `off`、DeepSeek 用 `thinking.type` 关闭)、可调档位 = 表的键、反查取最具体匹配、写入剔除旧键、往返一致、Agent 声明非法回退模型默认、原型链污染名字(`constructor`)不生效。
|
|
28
|
+
- `src/config/reasoning-presets.test.ts` 新增(11 例):落盘位置、首次释放、幂等不写盘、未标 `custom` 被覆盖、标了 `custom` 冻结、用户自建策略保留、损坏/数组内容回落种子、`bootstrap()` 顺带释放。
|
|
29
|
+
|
|
30
|
+
### 📄 文档
|
|
31
|
+
|
|
32
|
+
- `docs/{zh,en}/sdk.md`:「思考强度」章节重写为「思考预设(reasoning presets)」,含策略表结构、出厂策略对照表、三个结构性保证、新 API 表与 `Scope` 注入示例。
|
|
33
|
+
- 顺手修掉 alpha.13 之后就过期的示例:`ContextGuardPlugin({ maxTokens, isSuspended })` → `{ guardTokens }`、`AutoMigratePlugin({ maxTokens })` → `{ handoffTokens }`、「交接回合护栏放宽 × 2」的旧描述。
|
|
34
|
+
|
|
3
35
|
## [1.0.0-alpha.13] - 2026-10-22
|
|
4
36
|
|
|
5
37
|
### 💥 Breaking Changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AppConfig, AgentDefinition } from "../types/index.js";
|
|
1
|
+
import type { AppConfig, AgentDefinition, ReasoningPresets } from "../types/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* 配置管理器 — 负责配置文件的读写、目录初始化、默认实体创建。
|
|
4
4
|
*/
|
|
@@ -58,6 +58,27 @@ export declare class ConfigManager {
|
|
|
58
58
|
agent: AgentDefinition | null;
|
|
59
59
|
subAgent: AgentDefinition | null;
|
|
60
60
|
}>;
|
|
61
|
+
/** 策略表文件路径(与 config.json 同级) */
|
|
62
|
+
get reasoningPresetsPath(): string;
|
|
63
|
+
/**
|
|
64
|
+
* 读取思考策略表。
|
|
65
|
+
*
|
|
66
|
+
* 文件缺失或无法解析时返回出厂种子(内存值,不写盘)——保证调用方永远拿到可用的表:
|
|
67
|
+
* 用户误删文件后仍能正常选档位,下次 `ensureReasoningPresets()` 会重新释放。
|
|
68
|
+
*/
|
|
69
|
+
readReasoningPresets(): Promise<ReasoningPresets>;
|
|
70
|
+
/**
|
|
71
|
+
* 释放 / 同步出厂思考策略。
|
|
72
|
+
*
|
|
73
|
+
* 覆盖策略与 `syncManagedModels` 同构,**按策略逐个判断**(粒度到单个策略):
|
|
74
|
+
* - 文件不存在 → 写入出厂种子;
|
|
75
|
+
* - 某策略标了 `custom: true` → 用户自有,原样保留;
|
|
76
|
+
* - 某策略未标 → 用出厂版覆盖该策略(内容相同则不写盘);
|
|
77
|
+
* - 用户自建的非出厂策略 → 始终保留。
|
|
78
|
+
*
|
|
79
|
+
* @returns 实际写入的策略名列表(未变化 / 用户自有 均不计入)
|
|
80
|
+
*/
|
|
81
|
+
ensureReasoningPresets(): Promise<string[]>;
|
|
61
82
|
/**
|
|
62
83
|
* 释放 / 同步出厂 Skill。
|
|
63
84
|
*
|
|
@@ -2,6 +2,7 @@ import { promises as fs } from "node:fs";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { DEFAULT_AGENT_ID, DEFAULT_AGENT_DEFINITION, DEFAULT_SUBAGENT_ID, DEFAULT_SUBAGENT_DEFINITION, DEFAULT_APP_CONFIG, DEFAULT_MCP_CONFIG, CONFIG_SUB_DIRS, } from "./constants.js";
|
|
4
4
|
import { FACTORY_SKILLS, isCustomSkill } from "./factory-skills.js";
|
|
5
|
+
import { FACTORY_REASONING_PRESETS, REASONING_PRESETS_FILE, } from "./reasoning-presets.js";
|
|
5
6
|
import { migrateConfig } from "./migrate.js";
|
|
6
7
|
/**
|
|
7
8
|
* 配置管理器 — 负责配置文件的读写、目录初始化、默认实体创建。
|
|
@@ -188,9 +189,75 @@ export class ConfigManager {
|
|
|
188
189
|
const subAgent = await this.ensureDefaultSubAgent();
|
|
189
190
|
await this.ensureDefaultMcpConfig();
|
|
190
191
|
await this.ensureFactorySkills();
|
|
192
|
+
await this.ensureReasoningPresets();
|
|
191
193
|
return { config, agent, subAgent };
|
|
192
194
|
}
|
|
193
195
|
// -----------------------------------------------------------------------
|
|
196
|
+
// 思考策略表({basePath}/reasoning-presets.json)
|
|
197
|
+
// -----------------------------------------------------------------------
|
|
198
|
+
/** 策略表文件路径(与 config.json 同级) */
|
|
199
|
+
get reasoningPresetsPath() {
|
|
200
|
+
return join(this.basePath, REASONING_PRESETS_FILE);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* 读取思考策略表。
|
|
204
|
+
*
|
|
205
|
+
* 文件缺失或无法解析时返回出厂种子(内存值,不写盘)——保证调用方永远拿到可用的表:
|
|
206
|
+
* 用户误删文件后仍能正常选档位,下次 `ensureReasoningPresets()` 会重新释放。
|
|
207
|
+
*/
|
|
208
|
+
async readReasoningPresets() {
|
|
209
|
+
try {
|
|
210
|
+
const raw = await fs.readFile(this.reasoningPresetsPath, "utf-8");
|
|
211
|
+
const parsed = JSON.parse(raw);
|
|
212
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
213
|
+
return parsed;
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// 缺失或损坏 → 回落出厂种子
|
|
217
|
+
}
|
|
218
|
+
return { ...FACTORY_REASONING_PRESETS };
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* 释放 / 同步出厂思考策略。
|
|
222
|
+
*
|
|
223
|
+
* 覆盖策略与 `syncManagedModels` 同构,**按策略逐个判断**(粒度到单个策略):
|
|
224
|
+
* - 文件不存在 → 写入出厂种子;
|
|
225
|
+
* - 某策略标了 `custom: true` → 用户自有,原样保留;
|
|
226
|
+
* - 某策略未标 → 用出厂版覆盖该策略(内容相同则不写盘);
|
|
227
|
+
* - 用户自建的非出厂策略 → 始终保留。
|
|
228
|
+
*
|
|
229
|
+
* @returns 实际写入的策略名列表(未变化 / 用户自有 均不计入)
|
|
230
|
+
*/
|
|
231
|
+
async ensureReasoningPresets() {
|
|
232
|
+
let onDisk = null;
|
|
233
|
+
try {
|
|
234
|
+
onDisk = JSON.parse(await fs.readFile(this.reasoningPresetsPath, "utf-8"));
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
onDisk = null;
|
|
238
|
+
}
|
|
239
|
+
if (!onDisk || typeof onDisk !== "object" || Array.isArray(onDisk)) {
|
|
240
|
+
await fs.mkdir(this.basePath, { recursive: true });
|
|
241
|
+
await fs.writeFile(this.reasoningPresetsPath, JSON.stringify(FACTORY_REASONING_PRESETS, null, 2), "utf-8");
|
|
242
|
+
return Object.keys(FACTORY_REASONING_PRESETS);
|
|
243
|
+
}
|
|
244
|
+
const next = { ...onDisk };
|
|
245
|
+
const written = [];
|
|
246
|
+
for (const [name, preset] of Object.entries(FACTORY_REASONING_PRESETS)) {
|
|
247
|
+
const existing = onDisk[name];
|
|
248
|
+
if (existing?.custom === true)
|
|
249
|
+
continue; // 用户改过 → 冻结
|
|
250
|
+
if (JSON.stringify(existing) === JSON.stringify(preset))
|
|
251
|
+
continue; // 已是最新
|
|
252
|
+
next[name] = preset;
|
|
253
|
+
written.push(name);
|
|
254
|
+
}
|
|
255
|
+
if (written.length > 0) {
|
|
256
|
+
await fs.writeFile(this.reasoningPresetsPath, JSON.stringify(next, null, 2), "utf-8");
|
|
257
|
+
}
|
|
258
|
+
return written;
|
|
259
|
+
}
|
|
260
|
+
// -----------------------------------------------------------------------
|
|
194
261
|
// 出厂 Skill({basePath}/skills/<id>/SKILL.md)
|
|
195
262
|
// -----------------------------------------------------------------------
|
|
196
263
|
/**
|
package/dist/config/constants.js
CHANGED
|
@@ -103,15 +103,10 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
103
103
|
// 出厂取值:窗口 ≥1M 的模型统一 handoff=250K / guard=375K(约窗口的 25% / 37.5%),
|
|
104
104
|
// glm-4.7-flash 窗口仅 200K,取 handoff=100K / guard=150K。
|
|
105
105
|
//
|
|
106
|
-
// reasoning
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
// 关闭用 thinking.type:"disabled";默认开思考且默认 effort=high
|
|
111
|
-
// 智谱 → docs.bigmodel.cn/cn/guide/capabilities/thinking:GLM-5.3 系**只认**
|
|
112
|
-
// low/high/max(其余输入报错),且不再支持关闭思考(传 disabled 会报错);
|
|
113
|
-
// GLM-4.7 强制思考且无 reasoning_effort 参数(仅 5.2+ 支持)
|
|
114
|
-
// 未声明 reasoning 的模型 = 不可调档,`modelAllowedEfforts()` 返回空数组。
|
|
106
|
+
// reasoningPreset:思考策略名,引用 `<basePath>/reasoning-presets.json` 里的键
|
|
107
|
+
// (出厂内容见 `config/reasoning-presets.ts`,档位的协议差异全部写在表里)。
|
|
108
|
+
// 不写这个字段 = 不可调档(如 GLM-4.7 强制思考且无 reasoning_effort 参数)。
|
|
109
|
+
// 新接入端点时先在策略表里加一张表,再在这里引用它的名字。
|
|
115
110
|
models: [
|
|
116
111
|
// ---------- OpenAI ----------
|
|
117
112
|
{
|
|
@@ -122,7 +117,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
122
117
|
tokenThresholds: { handoff: 250_000, guard: 375_000 },
|
|
123
118
|
defaultParams: {},
|
|
124
119
|
vision: true,
|
|
125
|
-
|
|
120
|
+
reasoningPreset: "openai",
|
|
126
121
|
description: "OpenAI 最强旗舰(2026-09):高级分析、软件工程、深度研究与科学工作",
|
|
127
122
|
},
|
|
128
123
|
{
|
|
@@ -133,7 +128,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
133
128
|
tokenThresholds: { handoff: 250_000, guard: 375_000 },
|
|
134
129
|
defaultParams: {},
|
|
135
130
|
vision: true,
|
|
136
|
-
|
|
131
|
+
reasoningPreset: "openai",
|
|
137
132
|
description: "OpenAI 最新旗舰(2026-09-29):Agentic Coding、计算机操作与文档密集型专业任务",
|
|
138
133
|
},
|
|
139
134
|
{
|
|
@@ -144,7 +139,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
144
139
|
tokenThresholds: { handoff: 250_000, guard: 375_000 },
|
|
145
140
|
defaultParams: {},
|
|
146
141
|
vision: true,
|
|
147
|
-
|
|
142
|
+
reasoningPreset: "openai",
|
|
148
143
|
description: "OpenAI GPT-6 系列快速低成本模型:适合高吞吐、低延迟场景",
|
|
149
144
|
},
|
|
150
145
|
// ---------- 智谱 BigModel ----------
|
|
@@ -155,7 +150,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
155
150
|
modelName: "glm-5.3",
|
|
156
151
|
tokenThresholds: { handoff: 250_000, guard: 375_000 },
|
|
157
152
|
defaultParams: { thinking: { type: "enabled" }, reasoning_effort: "max" },
|
|
158
|
-
|
|
153
|
+
reasoningPreset: "zhipu-5.3",
|
|
159
154
|
description: "智谱最新旗舰:复杂软件工程与长程 Agent 任务(强制开启思考,1M 上下文)",
|
|
160
155
|
},
|
|
161
156
|
{
|
|
@@ -166,7 +161,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
166
161
|
tokenThresholds: { handoff: 250_000, guard: 375_000 },
|
|
167
162
|
defaultParams: { thinking: { type: "enabled" }, reasoning_effort: "max" },
|
|
168
163
|
vision: true,
|
|
169
|
-
|
|
164
|
+
reasoningPreset: "zhipu-5.3",
|
|
170
165
|
description: "智谱原生多模态模型(图片/视频/文件):视觉 Coding,1M 上下文",
|
|
171
166
|
},
|
|
172
167
|
{
|
|
@@ -177,7 +172,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
177
172
|
tokenThresholds: { handoff: 250_000, guard: 375_000 },
|
|
178
173
|
defaultParams: { thinking: { type: "enabled" }, reasoning_effort: "max" },
|
|
179
174
|
vision: true,
|
|
180
|
-
|
|
175
|
+
reasoningPreset: "zhipu-5.3",
|
|
181
176
|
description: "GLM-5.3-Flash 高速版(200 tokens/s),原生多模态",
|
|
182
177
|
},
|
|
183
178
|
{
|
|
@@ -188,7 +183,6 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
188
183
|
// 该模型真实窗口仅 200K,阈值下调至 100K(约窗口的 50%),留足 response 空间
|
|
189
184
|
tokenThresholds: { handoff: 100_000, guard: 150_000 },
|
|
190
185
|
defaultParams: {},
|
|
191
|
-
reasoning: { efforts: [] },
|
|
192
186
|
description: "智谱免费文本模型:200K 上下文,适合轻量任务",
|
|
193
187
|
},
|
|
194
188
|
// ---------- DeepSeek ----------
|
|
@@ -200,7 +194,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
200
194
|
tokenThresholds: { handoff: 250_000, guard: 375_000 },
|
|
201
195
|
defaultParams: { thinking: { type: "enabled" } },
|
|
202
196
|
vision: true,
|
|
203
|
-
|
|
197
|
+
reasoningPreset: "deepseek",
|
|
204
198
|
description: "DeepSeek 旗舰(2026-09-10):552B MoE 非对称架构,原生多模态,1M 上下文",
|
|
205
199
|
},
|
|
206
200
|
],
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 思考预设 —— 出厂种子 + 策略表文件名
|
|
3
|
+
*
|
|
4
|
+
* 这里的内容只是**初始值**:`ConfigManager.ensureReasoningPresets()` 在首次运行时把它
|
|
5
|
+
* 写到 `<basePath>/reasoning-presets.json`,之后以磁盘文件为准(用户可改、可加)。
|
|
6
|
+
* 与 `DEFAULT_APP_CONFIG` 写进 `config.json` 完全同一个套路。
|
|
7
|
+
*
|
|
8
|
+
* 为什么档位细节不留在代码里:档位与厂商协议绑定,属于「数据」而非「逻辑」。
|
|
9
|
+
* 放进用户目录后,厂商新增档位或接入新端点时用户能自己加,不必等 SDK 发版。
|
|
10
|
+
*
|
|
11
|
+
* ## 为什么每张表长这样(**全部来自官方文档,不要凭印象改**)
|
|
12
|
+
*
|
|
13
|
+
* | 端点 | 官方依据 | 若写错会怎样 |
|
|
14
|
+
* |------|---------|-------------|
|
|
15
|
+
* | OpenAI | `openai-node` 的 `Shared.ReasoningEffort`(`none/minimal/low/medium/high/xhigh/max`) | 无 `thinking` 参数,写了会被当未知参数拒绝 → 表里干脆不出现该键 |
|
|
16
|
+
* | DeepSeek | `api-docs.deepseek.com/guides/thinking_mode` | 可调 `low/high/max`;关闭用 `thinking.type = "disabled"` |
|
|
17
|
+
* | 智谱 GLM-5.3 系 | `docs.bigmodel.cn/cn/guide/capabilities/thinking` | **只认** `low/high/max`,其余输入报错;**不再支持关闭**(传 disabled 也报错)→ 表里没有 `off` |
|
|
18
|
+
* | 智谱 GLM-4.7 | 同上 | 强制思考且无 `reasoning_effort` 参数 → 不给它挂策略 |
|
|
19
|
+
*
|
|
20
|
+
* 三个结构性保证,都是「表的形状」带来的,不需要任何代码分支:
|
|
21
|
+
* 1. 可选档位 = `Object.keys(levels)`,不会有「菜单里有、发出去报错」的档位;
|
|
22
|
+
* 2. 能否关闭思考 = 表里有没有 `off` 键;
|
|
23
|
+
* 3. 某个键属于思考参数 = 它在表里出现过 —— 写入时按此剔除旧值,不需要硬编码键名。
|
|
24
|
+
*/
|
|
25
|
+
import type { ReasoningPresets } from "../types/index.js";
|
|
26
|
+
/** 策略表文件名(位于 `<basePath>/` 下,与 `config.json` 同级) */
|
|
27
|
+
export declare const REASONING_PRESETS_FILE = "reasoning-presets.json";
|
|
28
|
+
/** 出厂策略表 */
|
|
29
|
+
export declare const FACTORY_REASONING_PRESETS: ReasoningPresets;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 思考预设 —— 出厂种子 + 策略表文件名
|
|
3
|
+
*
|
|
4
|
+
* 这里的内容只是**初始值**:`ConfigManager.ensureReasoningPresets()` 在首次运行时把它
|
|
5
|
+
* 写到 `<basePath>/reasoning-presets.json`,之后以磁盘文件为准(用户可改、可加)。
|
|
6
|
+
* 与 `DEFAULT_APP_CONFIG` 写进 `config.json` 完全同一个套路。
|
|
7
|
+
*
|
|
8
|
+
* 为什么档位细节不留在代码里:档位与厂商协议绑定,属于「数据」而非「逻辑」。
|
|
9
|
+
* 放进用户目录后,厂商新增档位或接入新端点时用户能自己加,不必等 SDK 发版。
|
|
10
|
+
*
|
|
11
|
+
* ## 为什么每张表长这样(**全部来自官方文档,不要凭印象改**)
|
|
12
|
+
*
|
|
13
|
+
* | 端点 | 官方依据 | 若写错会怎样 |
|
|
14
|
+
* |------|---------|-------------|
|
|
15
|
+
* | OpenAI | `openai-node` 的 `Shared.ReasoningEffort`(`none/minimal/low/medium/high/xhigh/max`) | 无 `thinking` 参数,写了会被当未知参数拒绝 → 表里干脆不出现该键 |
|
|
16
|
+
* | DeepSeek | `api-docs.deepseek.com/guides/thinking_mode` | 可调 `low/high/max`;关闭用 `thinking.type = "disabled"` |
|
|
17
|
+
* | 智谱 GLM-5.3 系 | `docs.bigmodel.cn/cn/guide/capabilities/thinking` | **只认** `low/high/max`,其余输入报错;**不再支持关闭**(传 disabled 也报错)→ 表里没有 `off` |
|
|
18
|
+
* | 智谱 GLM-4.7 | 同上 | 强制思考且无 `reasoning_effort` 参数 → 不给它挂策略 |
|
|
19
|
+
*
|
|
20
|
+
* 三个结构性保证,都是「表的形状」带来的,不需要任何代码分支:
|
|
21
|
+
* 1. 可选档位 = `Object.keys(levels)`,不会有「菜单里有、发出去报错」的档位;
|
|
22
|
+
* 2. 能否关闭思考 = 表里有没有 `off` 键;
|
|
23
|
+
* 3. 某个键属于思考参数 = 它在表里出现过 —— 写入时按此剔除旧值,不需要硬编码键名。
|
|
24
|
+
*/
|
|
25
|
+
/** 策略表文件名(位于 `<basePath>/` 下,与 `config.json` 同级) */
|
|
26
|
+
export const REASONING_PRESETS_FILE = "reasoning-presets.json";
|
|
27
|
+
/** 出厂策略表 */
|
|
28
|
+
export const FACTORY_REASONING_PRESETS = {
|
|
29
|
+
/** OpenAI 系:只认 `reasoning_effort`;关闭思考 = `"none"` */
|
|
30
|
+
openai: {
|
|
31
|
+
levels: {
|
|
32
|
+
minimal: { reasoning_effort: "minimal" },
|
|
33
|
+
low: { reasoning_effort: "low" },
|
|
34
|
+
medium: { reasoning_effort: "medium" },
|
|
35
|
+
high: { reasoning_effort: "high" },
|
|
36
|
+
off: { reasoning_effort: "none" },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
/** 智谱 GLM-5.3 / 5.3-Flash / 5.3-FlashX:只认三档,且**不能关闭思考** */
|
|
40
|
+
"zhipu-5.3": {
|
|
41
|
+
levels: {
|
|
42
|
+
low: { thinking: { type: "enabled" }, reasoning_effort: "low" },
|
|
43
|
+
high: { thinking: { type: "enabled" }, reasoning_effort: "high" },
|
|
44
|
+
max: { thinking: { type: "enabled" }, reasoning_effort: "max" },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
/** DeepSeek:`thinking` 开关 + `reasoning_effort`,可关闭 */
|
|
48
|
+
deepseek: {
|
|
49
|
+
levels: {
|
|
50
|
+
low: { thinking: { type: "enabled" }, reasoning_effort: "low" },
|
|
51
|
+
high: { thinking: { type: "enabled" }, reasoning_effort: "high" },
|
|
52
|
+
max: { thinking: { type: "enabled" }, reasoning_effort: "max" },
|
|
53
|
+
off: { thinking: { type: "disabled" } },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { PermissionPolicy, AgentPermissions, Endpoint, EndpointCapabilities, Model, ImageModel, AgentDefinition, AppConfig, ToolEnv, McpIcon, McpAnnotations, McpToolDef, McpResourceDef, McpPromptDef, McpServerManifest, McpServerConfig, McpConnectionState, } from "./types/index.js";
|
|
1
|
+
export type { PermissionPolicy, AgentPermissions, Endpoint, EndpointCapabilities, Model, TokenThresholds, ReasoningPatch, ReasoningPreset, ReasoningPresets, ImageModel, AgentDefinition, AppConfig, ToolEnv, McpIcon, McpAnnotations, McpToolDef, McpResourceDef, McpPromptDef, McpServerManifest, McpServerConfig, McpConnectionState, } from "./types/index.js";
|
|
2
2
|
export { PermissionEvaluator } from "./scope/PermissionEvaluator.js";
|
|
3
3
|
export type { CandidateSets } from "./scope/PermissionEvaluator.js";
|
|
4
4
|
export { createDisclosureParam } from "./scope/disclosure.js";
|
|
@@ -21,11 +21,12 @@ export { allInOne } from "./scope/plugins/allInOne.js";
|
|
|
21
21
|
export type { AllInOneOptions } from "./scope/plugins/allInOne.js";
|
|
22
22
|
export { ConfigManager, DEFAULT_AGENT_ID, DEFAULT_AGENT_DEFINITION, DEFAULT_SUBAGENT_ID, DEFAULT_SUBAGENT_DEFINITION, DEFAULT_APP_CONFIG, DEFAULT_MCP_CONFIG, CONFIG_SUB_DIRS, } from "./config/ConfigManager.js";
|
|
23
23
|
export { FACTORY_SKILLS, HANDOFF_SKILL_ID, FALLBACK_HANDOFF_TEMPLATE, isCustomSkill, } from "./config/factory-skills.js";
|
|
24
|
+
export { FACTORY_REASONING_PRESETS, REASONING_PRESETS_FILE, } from "./config/reasoning-presets.js";
|
|
24
25
|
export { EntityRepository } from "./shared/EntityRepository.js";
|
|
25
26
|
export { AgentRepository } from "./crud/AgentRepository.js";
|
|
26
27
|
export { fileToContentSection, resolveModelAndEndpoint, supportsFilesApi, mimeOf, toDataUrl, base64ByteLength, contentHash, clearUploadCache, MIME_BY_EXT, DEFAULT_MAX_INLINE_BYTES, } from "./shared/content-blocks.js";
|
|
27
28
|
export type { FileSource, FileToSectionInput, FileToSectionResult, ContentTransport, UploadFn, } from "./shared/content-blocks.js";
|
|
28
|
-
export { applyReasoningEffort,
|
|
29
|
+
export { applyReasoningEffort, modelLevels, presetOf, readReasoningEffort, resolveModelParams, resolveReasoningEffort, } from "./shared/reasoning.js";
|
|
29
30
|
export { createModel } from "./runtime/createModel.js";
|
|
30
31
|
export { SdkAgent } from "./runtime/SdkAgent.js";
|
|
31
32
|
export type { AgentPlugin, SendContext } from "./runtime/SdkAgent.js";
|
package/dist/index.js
CHANGED
|
@@ -19,13 +19,15 @@ export { allInOne } from "./scope/plugins/allInOne.js";
|
|
|
19
19
|
export { ConfigManager, DEFAULT_AGENT_ID, DEFAULT_AGENT_DEFINITION, DEFAULT_SUBAGENT_ID, DEFAULT_SUBAGENT_DEFINITION, DEFAULT_APP_CONFIG, DEFAULT_MCP_CONFIG, CONFIG_SUB_DIRS, } from "./config/ConfigManager.js";
|
|
20
20
|
// 出厂资产 — 出厂 Skill(随版本释放到 <basePath>/skills/)
|
|
21
21
|
export { FACTORY_SKILLS, HANDOFF_SKILL_ID, FALLBACK_HANDOFF_TEMPLATE, isCustomSkill, } from "./config/factory-skills.js";
|
|
22
|
+
// 出厂资产 — 出厂思考策略(随版本释放到 <basePath>/reasoning-presets.json)
|
|
23
|
+
export { FACTORY_REASONING_PRESETS, REASONING_PRESETS_FILE, } from "./config/reasoning-presets.js";
|
|
22
24
|
// CRUD — 实体仓储(仅 Agent 定义等能力实体)
|
|
23
25
|
export { EntityRepository } from "./shared/EntityRepository.js";
|
|
24
26
|
export { AgentRepository } from "./crud/AgentRepository.js";
|
|
25
27
|
// 文件 → 内容块(统一入口)— 上层应用构造媒体消息时请走这里,不要自己判断上传还是内联
|
|
26
28
|
export { fileToContentSection, resolveModelAndEndpoint, supportsFilesApi, mimeOf, toDataUrl, base64ByteLength, contentHash, clearUploadCache, MIME_BY_EXT, DEFAULT_MAX_INLINE_BYTES, } from "./shared/content-blocks.js";
|
|
27
|
-
//
|
|
28
|
-
export { applyReasoningEffort,
|
|
29
|
+
// 思考预设(reasoning presets)— 档位读取、校验与参数写入
|
|
30
|
+
export { applyReasoningEffort, modelLevels, presetOf, readReasoningEffort, resolveModelParams, resolveReasoningEffort, } from "./shared/reasoning.js";
|
|
29
31
|
// 运行时
|
|
30
32
|
export { createModel } from "./runtime/createModel.js";
|
|
31
33
|
export { SdkAgent } from "./runtime/SdkAgent.js";
|
|
@@ -21,6 +21,7 @@ export interface AgentModel {
|
|
|
21
21
|
*
|
|
22
22
|
* `overrides.reasoningEffort` 是 **Agent 级覆盖**(`AgentDefinition.reasoningEffort`):
|
|
23
23
|
* 同一个模型可以给不同 Agent 配不同思考强度,而模型自身的 `defaultParams` 不变。
|
|
24
|
-
*
|
|
24
|
+
* 档位如何翻译成请求参数,由 `scope.reasoningPresets` 里该模型引用的策略表决定
|
|
25
|
+
* (见 `shared/reasoning.ts`)。
|
|
25
26
|
*/
|
|
26
27
|
export declare function createModel(scope: Scope, modelId: string, overrides?: Pick<AgentDefinition, "reasoningEffort">): AgentModel;
|
|
@@ -8,7 +8,8 @@ import { resolveModelParams } from "../shared/reasoning.js";
|
|
|
8
8
|
*
|
|
9
9
|
* `overrides.reasoningEffort` 是 **Agent 级覆盖**(`AgentDefinition.reasoningEffort`):
|
|
10
10
|
* 同一个模型可以给不同 Agent 配不同思考强度,而模型自身的 `defaultParams` 不变。
|
|
11
|
-
*
|
|
11
|
+
* 档位如何翻译成请求参数,由 `scope.reasoningPresets` 里该模型引用的策略表决定
|
|
12
|
+
* (见 `shared/reasoning.ts`)。
|
|
12
13
|
*/
|
|
13
14
|
export function createModel(scope, modelId, overrides) {
|
|
14
15
|
const config = scope.config;
|
|
@@ -31,6 +32,6 @@ export function createModel(scope, modelId, overrides) {
|
|
|
31
32
|
client,
|
|
32
33
|
model: apiModelName,
|
|
33
34
|
// 档位覆盖在这里生效:未声明 reasoningEffort 时原样返回模型 defaultParams
|
|
34
|
-
modelConfig: resolveModelParams(modelConfig, overrides),
|
|
35
|
+
modelConfig: resolveModelParams(modelConfig, scope.reasoningPresets, overrides),
|
|
35
36
|
};
|
|
36
37
|
}
|
package/dist/scope/Scope.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Tool } from "@ai-zen/agents-core";
|
|
2
|
-
import type { AppConfig, AgentDefinition, ToolEnv } from "../types/index.js";
|
|
2
|
+
import type { AppConfig, AgentDefinition, ReasoningPresets, ToolEnv } from "../types/index.js";
|
|
3
3
|
import type { ScopePlugin } from "./ScopePlugin.js";
|
|
4
4
|
/**
|
|
5
5
|
* 能力过滤系统的公共类型。
|
|
@@ -56,6 +56,13 @@ export declare class Scope {
|
|
|
56
56
|
readonly env: ToolEnv;
|
|
57
57
|
/** Agent 定义目录(createAgent 用) */
|
|
58
58
|
readonly agentsDir: string;
|
|
59
|
+
/**
|
|
60
|
+
* 思考策略表(`<basePath>/reasoning-presets.json`)。
|
|
61
|
+
*
|
|
62
|
+
* 与 `config` 平级、同样由调用方加载后注入:`createModel` 靠它把 Agent 的
|
|
63
|
+
* `reasoningEffort` 翻译成具体请求参数。不传则视为「无任何可选档位」。
|
|
64
|
+
*/
|
|
65
|
+
readonly reasoningPresets: ReasoningPresets;
|
|
59
66
|
/** 已注册的插件 */
|
|
60
67
|
private _plugins;
|
|
61
68
|
/** 是否已 init(init 之后禁止再 use) */
|
|
@@ -64,6 +71,7 @@ export declare class Scope {
|
|
|
64
71
|
config: AppConfig;
|
|
65
72
|
agentsDir: string;
|
|
66
73
|
cwd?: string;
|
|
74
|
+
reasoningPresets?: ReasoningPresets;
|
|
67
75
|
});
|
|
68
76
|
/** 已注册插件(只读视图) */
|
|
69
77
|
get plugins(): readonly ScopePlugin[];
|
package/dist/scope/Scope.js
CHANGED
|
@@ -29,12 +29,20 @@ export class Scope {
|
|
|
29
29
|
env;
|
|
30
30
|
/** Agent 定义目录(createAgent 用) */
|
|
31
31
|
agentsDir;
|
|
32
|
+
/**
|
|
33
|
+
* 思考策略表(`<basePath>/reasoning-presets.json`)。
|
|
34
|
+
*
|
|
35
|
+
* 与 `config` 平级、同样由调用方加载后注入:`createModel` 靠它把 Agent 的
|
|
36
|
+
* `reasoningEffort` 翻译成具体请求参数。不传则视为「无任何可选档位」。
|
|
37
|
+
*/
|
|
38
|
+
reasoningPresets;
|
|
32
39
|
/** 已注册的插件 */
|
|
33
40
|
_plugins = [];
|
|
34
41
|
/** 是否已 init(init 之后禁止再 use) */
|
|
35
42
|
_initialized = false;
|
|
36
43
|
constructor(options) {
|
|
37
44
|
this.config = options.config;
|
|
45
|
+
this.reasoningPresets = options.reasoningPresets ?? {};
|
|
38
46
|
this.cwd = options.cwd ?? process.cwd();
|
|
39
47
|
this.env = { cwd: this.cwd, config: this.config };
|
|
40
48
|
this.agentsDir = options.agentsDir;
|
|
@@ -1,64 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 思考预设(reasoning presets)—— 档位读取、校验与参数写入的唯一实现
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 同一家不同型号也不同(智谱文档原文):
|
|
4
|
+
* ## 为什么是「表」而不是「字段」
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* | 智谱 GLM-5.3 系 | `/cn/guide/capabilities/thinking` | `low / high / max`(**其余报错**) | **不能**,传 disabled 直接报错 |
|
|
11
|
-
* | 智谱 GLM-5.2 | 同上 | `none / minimal / low / medium / high / xhigh / max` | 能(`none` 或 `disabled`) |
|
|
12
|
-
* | OpenAI | `openai-node` 的 `Shared.ReasoningEffort` | `minimal / low / medium / high`(枚举含 none/xhigh/max) | 能,`reasoning_effort = "none"` |
|
|
6
|
+
* 档位不是全局统一枚举:各端点接受的值域互不相同,同一家不同型号也不同
|
|
7
|
+
* (见 `config/reasoning-presets.ts` 的文档表)。所以本模块不判断「厂商是谁」,
|
|
8
|
+
* 只做一件事:**在模型关联的那张策略表里查档位、写补丁**。
|
|
13
9
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* 于是三个问题都变成查表,不需要任何 if/else 分支:
|
|
11
|
+
* 1. 可选档位有哪些? → `Object.keys(preset.levels)`
|
|
12
|
+
* 2. 能不能关闭思考? → 表里有没有 `off` 键
|
|
13
|
+
* 3. 哪些参数键属于思考? → 所有补丁里出现过的键的并集(写入前先剔除)
|
|
14
|
+
*
|
|
15
|
+
* 第 3 条是关键:有了它,从 `max` 切到 `off` 不会残留上一档的参数,而代码里
|
|
16
|
+
* 一个键名都不必硬编码(`thinking` / `reasoning_effort` 只出现在策略表里)。
|
|
16
17
|
*/
|
|
17
|
-
import type { AgentDefinition, Model,
|
|
18
|
-
/** 是否为合法档位值 */
|
|
19
|
-
export declare function isReasoningEffort(value: unknown): value is ReasoningEffort;
|
|
20
|
-
/** 模型的能力声明(缺省 = 不可调档) */
|
|
21
|
-
export declare function capabilityOf(model?: Model | null): ReasoningCapability | undefined;
|
|
18
|
+
import type { AgentDefinition, Model, ReasoningPreset, ReasoningPresets } from "../types/index.js";
|
|
22
19
|
/**
|
|
23
|
-
*
|
|
20
|
+
* 模型关联的策略(未声明 `reasoningPreset`、或名字在表里不存在 → `undefined`)。
|
|
24
21
|
*
|
|
25
|
-
*
|
|
22
|
+
* 名字不存在时**不猜、不兜底**:宁可视作不可调档,也不把来路不明的参数发给服务端。
|
|
26
23
|
*/
|
|
27
|
-
export declare function
|
|
24
|
+
export declare function presetOf(model?: Model | null, presets?: ReasoningPresets | null): ReasoningPreset | undefined;
|
|
28
25
|
/**
|
|
29
|
-
*
|
|
26
|
+
* 模型可选的全部档位(顺序 = 策略表声明顺序)。
|
|
30
27
|
*
|
|
31
|
-
*
|
|
32
|
-
* 本身就不该出现在菜单里。
|
|
28
|
+
* 不再区分「档位」与「关闭」:表里的键就是菜单里的项。未关联策略 → 空数组。
|
|
33
29
|
*/
|
|
34
|
-
export declare function
|
|
30
|
+
export declare function modelLevels(model?: Model | null, presets?: ReasoningPresets | null): string[];
|
|
35
31
|
/**
|
|
36
|
-
*
|
|
32
|
+
* 反查当前档位:参数里命中哪个补丁的键值,就是哪一档。
|
|
33
|
+
*
|
|
34
|
+
* 与 `applyReasoningEffort()` 互为逆运算,两边共用同一张表。
|
|
35
|
+
* 多张补丁同时命中时取**最具体**的那个(键最多的),键数相同取先声明的。
|
|
37
36
|
*
|
|
38
|
-
* 返回 `undefined`
|
|
39
|
-
*
|
|
40
|
-
* 展示层要区分这两种情况,请配合 `capabilityOf()` 判断。
|
|
37
|
+
* 返回 `undefined` = 参数不完整匹配任何档位(例如 DeepSeek 出厂只写了
|
|
38
|
+
* `thinking: { type: "enabled" }` 而没写 effort),展示层应视为「跟随模型默认」。
|
|
41
39
|
*/
|
|
42
|
-
export declare function readReasoningEffort(params?: Record<string, unknown> | null):
|
|
40
|
+
export declare function readReasoningEffort(model?: Model | null, presets?: ReasoningPresets | null, params?: Record<string, unknown> | null): string | undefined;
|
|
43
41
|
/**
|
|
44
|
-
*
|
|
42
|
+
* 把档位写成请求参数(不改入参)。
|
|
45
43
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* 对它写 `thinking` 会被当作未知参数拒绝)
|
|
50
|
-
* - 未声明 `disable`(如 GLM-5.3 系)→ 只写 `reasoning_effort`,传 `off` 直接抛错
|
|
44
|
+
* 写入方式 = **先剔除思考参数键的旧值,再合并新补丁**。这样 `max` → `off` 不会残留
|
|
45
|
+
* 上一档的 `reasoning_effort`,模型换策略也不会反刍旧策略的参数,而「哪些键属于思考」
|
|
46
|
+
* 由策略表自己声明,代码零硬编码。
|
|
51
47
|
*
|
|
52
|
-
* @throws
|
|
48
|
+
* @throws 模型未关联策略、或策略里没有该档位(绝不静默降级:档位写错必须当场发现)
|
|
53
49
|
*/
|
|
54
|
-
export declare function applyReasoningEffort(
|
|
50
|
+
export declare function applyReasoningEffort(model: Model | null | undefined, presets: ReasoningPresets | null | undefined, level: string, params?: Record<string, unknown> | null): Record<string, unknown>;
|
|
55
51
|
/**
|
|
56
|
-
* 解析某 Agent 的实际档位:Agent
|
|
52
|
+
* 解析某 Agent 的实际档位:Agent 声明优先,否则反查模型默认参数。
|
|
53
|
+
*
|
|
54
|
+
* 声明了但策略里没有这一档 → 退化为模型默认(宁可忽略设置,也不发非法参数)。
|
|
57
55
|
*/
|
|
58
|
-
export declare function resolveReasoningEffort(model?: Model | null, definition?: Pick<AgentDefinition, "reasoningEffort"> | null):
|
|
56
|
+
export declare function resolveReasoningEffort(model?: Model | null, presets?: ReasoningPresets | null, definition?: Pick<AgentDefinition, "reasoningEffort"> | null): string | undefined;
|
|
59
57
|
/**
|
|
60
58
|
* 把 Agent 的档位覆盖应用到模型参数上(`createModel` 用)。
|
|
61
59
|
*
|
|
62
60
|
* 未声明 `reasoningEffort` 时原样返回模型参数,保证既有行为不变。
|
|
63
61
|
*/
|
|
64
|
-
export declare function resolveModelParams(model?: Model | null, definition?: Pick<AgentDefinition, "reasoningEffort"> | null): Record<string, unknown> | undefined;
|
|
62
|
+
export declare function resolveModelParams(model?: Model | null, presets?: ReasoningPresets | null, definition?: Pick<AgentDefinition, "reasoningEffort"> | null): Record<string, unknown> | undefined;
|
package/dist/shared/reasoning.js
CHANGED
|
@@ -1,151 +1,159 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 思考预设(reasoning presets)—— 档位读取、校验与参数写入的唯一实现
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 同一家不同型号也不同(智谱文档原文):
|
|
4
|
+
* ## 为什么是「表」而不是「字段」
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* | 智谱 GLM-5.3 系 | `/cn/guide/capabilities/thinking` | `low / high / max`(**其余报错**) | **不能**,传 disabled 直接报错 |
|
|
11
|
-
* | 智谱 GLM-5.2 | 同上 | `none / minimal / low / medium / high / xhigh / max` | 能(`none` 或 `disabled`) |
|
|
12
|
-
* | OpenAI | `openai-node` 的 `Shared.ReasoningEffort` | `minimal / low / medium / high`(枚举含 none/xhigh/max) | 能,`reasoning_effort = "none"` |
|
|
6
|
+
* 档位不是全局统一枚举:各端点接受的值域互不相同,同一家不同型号也不同
|
|
7
|
+
* (见 `config/reasoning-presets.ts` 的文档表)。所以本模块不判断「厂商是谁」,
|
|
8
|
+
* 只做一件事:**在模型关联的那张策略表里查档位、写补丁**。
|
|
13
9
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* 于是三个问题都变成查表,不需要任何 if/else 分支:
|
|
11
|
+
* 1. 可选档位有哪些? → `Object.keys(preset.levels)`
|
|
12
|
+
* 2. 能不能关闭思考? → 表里有没有 `off` 键
|
|
13
|
+
* 3. 哪些参数键属于思考? → 所有补丁里出现过的键的并集(写入前先剔除)
|
|
14
|
+
*
|
|
15
|
+
* 第 3 条是关键:有了它,从 `max` 切到 `off` 不会残留上一档的参数,而代码里
|
|
16
|
+
* 一个键名都不必硬编码(`thinking` / `reasoning_effort` 只出现在策略表里)。
|
|
16
17
|
*/
|
|
17
|
-
/** `
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"off",
|
|
23
|
-
"none",
|
|
24
|
-
"minimal",
|
|
25
|
-
"low",
|
|
26
|
-
"medium",
|
|
27
|
-
"high",
|
|
28
|
-
"xhigh",
|
|
29
|
-
"max",
|
|
30
|
-
];
|
|
31
|
-
/** 是否为合法档位值 */
|
|
32
|
-
export function isReasoningEffort(value) {
|
|
33
|
-
return typeof value === "string" && KNOWN_EFFORTS.includes(value);
|
|
18
|
+
/** 安全取自有属性(策略名源自用户配置,可能是 `constructor` / `__proto__` 等) */
|
|
19
|
+
function own(bag, key) {
|
|
20
|
+
if (!bag || !Object.prototype.hasOwnProperty.call(bag, key))
|
|
21
|
+
return undefined;
|
|
22
|
+
return bag[key];
|
|
34
23
|
}
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
24
|
+
/** 结构化相等 —— 补丁是纯 JSON,逐层比对即可 */
|
|
25
|
+
function sameValue(a, b) {
|
|
26
|
+
if (a === b)
|
|
27
|
+
return true;
|
|
28
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null)
|
|
29
|
+
return false;
|
|
30
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
31
|
+
return false;
|
|
32
|
+
const left = a;
|
|
33
|
+
const right = b;
|
|
34
|
+
const leftKeys = Object.keys(left);
|
|
35
|
+
if (leftKeys.length !== Object.keys(right).length)
|
|
36
|
+
return false;
|
|
37
|
+
return leftKeys.every((k) => Object.prototype.hasOwnProperty.call(right, k) && sameValue(left[k], right[k]));
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
40
|
+
* 模型关联的策略(未声明 `reasoningPreset`、或名字在表里不存在 → `undefined`)。
|
|
41
41
|
*
|
|
42
|
-
*
|
|
42
|
+
* 名字不存在时**不猜、不兜底**:宁可视作不可调档,也不把来路不明的参数发给服务端。
|
|
43
43
|
*/
|
|
44
|
-
export function
|
|
45
|
-
|
|
44
|
+
export function presetOf(model, presets) {
|
|
45
|
+
const name = model?.reasoningPreset;
|
|
46
|
+
if (!name)
|
|
47
|
+
return undefined;
|
|
48
|
+
return own(presets ?? undefined, name);
|
|
46
49
|
}
|
|
47
50
|
/**
|
|
48
|
-
*
|
|
51
|
+
* 模型可选的全部档位(顺序 = 策略表声明顺序)。
|
|
49
52
|
*
|
|
50
|
-
*
|
|
51
|
-
* 本身就不该出现在菜单里。
|
|
53
|
+
* 不再区分「档位」与「关闭」:表里的键就是菜单里的项。未关联策略 → 空数组。
|
|
52
54
|
*/
|
|
53
|
-
export function
|
|
54
|
-
const
|
|
55
|
-
if (!
|
|
55
|
+
export function modelLevels(model, presets) {
|
|
56
|
+
const preset = presetOf(model, presets);
|
|
57
|
+
if (!preset)
|
|
56
58
|
return [];
|
|
57
|
-
return
|
|
59
|
+
return Object.keys(preset.levels ?? {});
|
|
58
60
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
/**
|
|
62
|
+
* 「思考参数键」的全集 —— **整张策略表**里任何补丁出现过的键。
|
|
63
|
+
*
|
|
64
|
+
* 用整张表而非单个策略,是为了让换策略时不留残渣:模型从 `deepseek` 改挂 `openai`
|
|
65
|
+
* 时,旧策略写进去的 `thinking` 也必须被清掉(OpenAI 不认这个参数,带着就 400)。
|
|
66
|
+
* 好处依旧:键名一个都不用在代码里硬编码,全部由表声明。
|
|
67
|
+
*/
|
|
68
|
+
function ownedKeys(presets) {
|
|
69
|
+
const keys = new Set();
|
|
70
|
+
for (const preset of Object.values(presets ?? {})) {
|
|
71
|
+
for (const patch of Object.values(preset?.levels ?? {})) {
|
|
72
|
+
for (const key of Object.keys(patch ?? {}))
|
|
73
|
+
keys.add(key);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return keys;
|
|
65
77
|
}
|
|
66
78
|
/**
|
|
67
|
-
*
|
|
79
|
+
* 反查当前档位:参数里命中哪个补丁的键值,就是哪一档。
|
|
68
80
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
81
|
+
* 与 `applyReasoningEffort()` 互为逆运算,两边共用同一张表。
|
|
82
|
+
* 多张补丁同时命中时取**最具体**的那个(键最多的),键数相同取先声明的。
|
|
83
|
+
*
|
|
84
|
+
* 返回 `undefined` = 参数不完整匹配任何档位(例如 DeepSeek 出厂只写了
|
|
85
|
+
* `thinking: { type: "enabled" }` 而没写 effort),展示层应视为「跟随模型默认」。
|
|
72
86
|
*/
|
|
73
|
-
export function readReasoningEffort(params) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
export function readReasoningEffort(model, presets, params) {
|
|
88
|
+
const preset = presetOf(model, presets);
|
|
89
|
+
if (!preset)
|
|
90
|
+
return undefined;
|
|
91
|
+
// 显式传 null = 「没有参数」,不回落模型默认;不传才读模型默认
|
|
92
|
+
const actual = params === undefined ? model?.defaultParams : params;
|
|
93
|
+
if (!actual)
|
|
78
94
|
return undefined;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
let best;
|
|
96
|
+
let bestSize = 0;
|
|
97
|
+
for (const [level, patch] of Object.entries(preset.levels ?? {})) {
|
|
98
|
+
const keys = Object.keys(patch ?? {});
|
|
99
|
+
if (keys.length === 0 || keys.length <= bestSize)
|
|
100
|
+
continue;
|
|
101
|
+
if (!keys.every((k) => sameValue(actual[k], patch[k])))
|
|
102
|
+
continue;
|
|
103
|
+
best = level;
|
|
104
|
+
bestSize = keys.length;
|
|
105
|
+
}
|
|
106
|
+
return best;
|
|
83
107
|
}
|
|
84
108
|
/**
|
|
85
|
-
*
|
|
109
|
+
* 把档位写成请求参数(不改入参)。
|
|
86
110
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* 对它写 `thinking` 会被当作未知参数拒绝)
|
|
91
|
-
* - 未声明 `disable`(如 GLM-5.3 系)→ 只写 `reasoning_effort`,传 `off` 直接抛错
|
|
111
|
+
* 写入方式 = **先剔除思考参数键的旧值,再合并新补丁**。这样 `max` → `off` 不会残留
|
|
112
|
+
* 上一档的 `reasoning_effort`,模型换策略也不会反刍旧策略的参数,而「哪些键属于思考」
|
|
113
|
+
* 由策略表自己声明,代码零硬编码。
|
|
92
114
|
*
|
|
93
|
-
* @throws
|
|
115
|
+
* @throws 模型未关联策略、或策略里没有该档位(绝不静默降级:档位写错必须当场发现)
|
|
94
116
|
*/
|
|
95
|
-
export function applyReasoningEffort(
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
if (capability?.disable === "effort-none") {
|
|
105
|
-
next[EFFORT_KEY] = "none";
|
|
106
|
-
// 不在 OpenAI 的请求里塞 thinking:那是未知参数
|
|
107
|
-
delete next[THINKING_KEY];
|
|
108
|
-
return next;
|
|
109
|
-
}
|
|
110
|
-
throw new Error(`模型 "${model?.id ?? "(未知)"}" 不支持关闭思考`);
|
|
117
|
+
export function applyReasoningEffort(model, presets, level, params) {
|
|
118
|
+
const id = model?.id ?? "(未知)";
|
|
119
|
+
const name = model?.reasoningPreset;
|
|
120
|
+
const preset = presetOf(model, presets);
|
|
121
|
+
if (!preset) {
|
|
122
|
+
throw new Error(name
|
|
123
|
+
? `模型 "${id}" 关联的思考策略 "${name}" 不存在(检查 reasoning-presets.json)`
|
|
124
|
+
: `模型 "${id}" 未关联思考策略(reasoningPreset),无法设置档位`);
|
|
111
125
|
}
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
const patch = own(preset.levels, level);
|
|
127
|
+
if (!patch) {
|
|
128
|
+
throw new Error(`策略 "${name}" 没有档位 "${level}"(可选:${Object.keys(preset.levels).join(" / ")})`);
|
|
114
129
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
next[THINKING_KEY] = { type: "enabled" };
|
|
120
|
-
}
|
|
121
|
-
next[EFFORT_KEY] = effort;
|
|
122
|
-
return next;
|
|
130
|
+
const owned = ownedKeys(presets);
|
|
131
|
+
const base = params === undefined ? model?.defaultParams : params;
|
|
132
|
+
const kept = Object.entries(base ?? {}).filter(([key]) => !owned.has(key));
|
|
133
|
+
return { ...Object.fromEntries(kept), ...patch };
|
|
123
134
|
}
|
|
124
135
|
/**
|
|
125
|
-
* 解析某 Agent 的实际档位:Agent
|
|
136
|
+
* 解析某 Agent 的实际档位:Agent 声明优先,否则反查模型默认参数。
|
|
137
|
+
*
|
|
138
|
+
* 声明了但策略里没有这一档 → 退化为模型默认(宁可忽略设置,也不发非法参数)。
|
|
126
139
|
*/
|
|
127
|
-
export function resolveReasoningEffort(model, definition) {
|
|
140
|
+
export function resolveReasoningEffort(model, presets, definition) {
|
|
128
141
|
const declared = definition?.reasoningEffort;
|
|
129
|
-
if (declared)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (allowed.length === 0 || allowed.includes(declared))
|
|
133
|
-
return declared;
|
|
134
|
-
}
|
|
135
|
-
return readReasoningEffort(model?.defaultParams);
|
|
142
|
+
if (declared && modelLevels(model, presets).includes(declared))
|
|
143
|
+
return declared;
|
|
144
|
+
return readReasoningEffort(model, presets);
|
|
136
145
|
}
|
|
137
146
|
/**
|
|
138
147
|
* 把 Agent 的档位覆盖应用到模型参数上(`createModel` 用)。
|
|
139
148
|
*
|
|
140
149
|
* 未声明 `reasoningEffort` 时原样返回模型参数,保证既有行为不变。
|
|
141
150
|
*/
|
|
142
|
-
export function resolveModelParams(model, definition) {
|
|
151
|
+
export function resolveModelParams(model, presets, definition) {
|
|
143
152
|
const declared = definition?.reasoningEffort;
|
|
144
153
|
if (!declared)
|
|
145
154
|
return model?.defaultParams;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (allowed.length === 0 || !allowed.includes(declared))
|
|
155
|
+
// 模型没关联策略、或策略里没这一档:保持模型默认,绝不发非法参数
|
|
156
|
+
if (!modelLevels(model, presets).includes(declared))
|
|
149
157
|
return model?.defaultParams;
|
|
150
|
-
return applyReasoningEffort(model
|
|
158
|
+
return applyReasoningEffort(model, presets, declared);
|
|
151
159
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -26,34 +26,34 @@ export interface EndpointCapabilities {
|
|
|
26
26
|
filesApi?: boolean;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* 单个档位的请求参数补丁 —— 原样合并进模型请求参数。
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* `off` 是本 SDK 的语义值(「关闭思考」),对接时按 `ReasoningCapability.disable` 选择表达方式。
|
|
37
|
-
* 它与 `none` 不重复:`none` 是端点自己认的档位值,`off` 是「没有档位、彻底不思考」。
|
|
31
|
+
* 各家协议差异全部关在补丁里,SDK 不解释、不硬编码任何键名:
|
|
32
|
+
* OpenAI → `{ reasoning_effort: "high" }`(没有 `thinking` 参数)
|
|
33
|
+
* DeepSeek → `{ thinking: { type: "enabled" }, reasoning_effort: "high" }`
|
|
34
|
+
* 关闭 → `{ reasoning_effort: "none" }` 或 `{ thinking: { type: "disabled" } }`
|
|
38
35
|
*/
|
|
39
|
-
export type
|
|
36
|
+
export type ReasoningPatch = Record<string, unknown>;
|
|
40
37
|
/**
|
|
41
|
-
*
|
|
38
|
+
* 一个具名思考策略:档位名 → 参数补丁。
|
|
42
39
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
40
|
+
* `levels` 的键集合**就是**该策略的全部可选档位 —— 没有单独的「能否关闭思考」字段:
|
|
41
|
+
* 表里有 `off` 键就能关,没有就不能(如智谱 GLM-5.3 系,官方原文「其余输入将报错」)。
|
|
45
42
|
*/
|
|
46
|
-
export interface
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
* - `thinking` → `thinking: { type: "disabled" }`(DeepSeek / GLM-5.2)
|
|
52
|
-
* - `effort-none` → `reasoning_effort: "none"`(OpenAI / GLM-5.2)
|
|
53
|
-
* - 缺省 → 不支持关闭(如 GLM-5.3 系,传 disabled 会报错)
|
|
54
|
-
*/
|
|
55
|
-
disable?: "thinking" | "effort-none";
|
|
43
|
+
export interface ReasoningPreset {
|
|
44
|
+
/** 用户改过 → SDK 初始化不再覆盖该策略(与 `Model.custom` 同构) */
|
|
45
|
+
custom?: boolean;
|
|
46
|
+
/** 档位名 → 参数补丁 */
|
|
47
|
+
levels: Record<string, ReasoningPatch>;
|
|
56
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* 思考策略表 —— `<basePath>/reasoning-presets.json` 的结构,出厂内容见
|
|
51
|
+
* `config/reasoning-presets.ts`。
|
|
52
|
+
*
|
|
53
|
+
* 键是策略名,由 `Model.reasoningPreset` 引用。**策略名一旦发布即视为稳定标识**
|
|
54
|
+
* (用户配置文件里写着它),改名等同破坏性变更。
|
|
55
|
+
*/
|
|
56
|
+
export type ReasoningPresets = Record<string, ReasoningPreset>;
|
|
57
57
|
/** API 端点 */
|
|
58
58
|
export interface Endpoint {
|
|
59
59
|
id: string;
|
|
@@ -96,12 +96,13 @@ export interface Model {
|
|
|
96
96
|
tokenThresholds: TokenThresholds;
|
|
97
97
|
defaultParams?: Record<string, unknown>;
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
99
|
+
* 思考策略名,引用 `reasoning-presets.json` 里的键。缺省视为不可调档。
|
|
100
100
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
101
|
+
* 为什么只存名字不存整张表:档位细节属于**厂商协议知识**,跟 SDK 版本一起演进;
|
|
102
|
+
* 配置只需要记录「这个型号套用哪套协议」。名字指向同目录下的策略表文件,
|
|
103
|
+
* 用户看得见、改得动,不必等 SDK 发版。
|
|
103
104
|
*/
|
|
104
|
-
|
|
105
|
+
reasoningPreset?: string;
|
|
105
106
|
/** 是否支持图片输入(视觉模型)。看图工具(view_image)仅对视觉模型启用;generate_image 是否返回图片内容块也据此判断 */
|
|
106
107
|
vision?: boolean;
|
|
107
108
|
description?: string;
|
|
@@ -122,12 +123,15 @@ export interface AgentDefinition {
|
|
|
122
123
|
messages: AgentNS.Message[];
|
|
123
124
|
modelId?: string;
|
|
124
125
|
/**
|
|
125
|
-
* 该 Agent
|
|
126
|
+
* 该 Agent 的思考档位(可选),必须是该模型策略表 `levels` 里的键。
|
|
126
127
|
*
|
|
127
128
|
* 有了它,同一个模型可以给不同 Agent 配不同强度(编程 Agent `max`、闲聊 Agent `low`),
|
|
128
129
|
* 而模型本身的默认参数保持不变。未声明时沿用模型默认。
|
|
130
|
+
*
|
|
131
|
+
* 类型是 `string` 而非联合类型:档位名由策略表定义,各家本就不同名,
|
|
132
|
+
* 用一个全局枚举假装统一只会把厂商差异藏进字段里。合法性在运行时按表校验。
|
|
129
133
|
*/
|
|
130
|
-
reasoningEffort?:
|
|
134
|
+
reasoningEffort?: string;
|
|
131
135
|
permissions?: AgentPermissions;
|
|
132
136
|
function?: AgentNS.FunctionDefine;
|
|
133
137
|
createdAt: string;
|