feat: v0.8.3 工作空间切换链路根治 · 思考强度扩档 — 启动TDZ连锁/继承丢数/回显滞后三修复 · xhigh+true档位单源 · 安全防线ready后注册 · 2217 用例全量回归 + 生产模式E2E
CI / 类型检查 + Lint + 单元测试 (push) Failing after 9m37s
CI / 全量测试 (Electron ABI) (push) Failing after 6m1s
CI / 产物编译验证 (push) Successful in 10m54s

This commit is contained in:
2026-09-08 21:14:59 +08:00
parent 4cd6e997b5
commit 9329df68af
18 changed files with 250 additions and 114 deletions
@@ -82,7 +82,9 @@ describe('OpenAIAdapter — 推理模型 reasoning_effort', () => {
['low', 'low'],
['medium', 'medium'],
['high', 'high'],
['xhigh', 'high'], // v0.8.3: OpenAI 档位封顶 highxhigh 就近降档
['max', 'high'], // max 归一 high
['true', 'high'], // v0.8.3: 模型默认档归一 high
] as const)('o3-mini effort=%s → reasoning_effort=%s', async (effort, expected) => {
const adapter = makeAdapter('o3-mini');
mockFetch.mockResolvedValue(okResponse());
@@ -13,7 +13,7 @@
*
* Ollama:
* O1 options 映射(num_predict=numTokens、num_ctx=contextLength、stop、top_p
* O2 think 参数 effort 映射(low→"low"、max→true)与未配置时缺省
* O2 think 参数 effort 映射(low→"low"、xhigh→"xhigh"、max/true→true)与未配置时缺省
* O3 图片归一化(data URI 剥前缀;无 URL 触发下载分支时零网络请求)
*
* Agnes:
@@ -286,7 +286,7 @@ describe('OllamaAdapter — 请求体契约', () => {
expect(options.stop).toEqual(['STOP']);
});
it('O2: think 参数 effort 映射(low→"low"、max→true);未开启思考时缺省', async () => {
it('O2: think 参数 effort 映射(low→"low"、xhigh→"xhigh"、max/true→true);未开启思考时缺省', async () => {
const adapter = makeOllama();
const { bodies } = captureFetch();
@@ -316,12 +316,39 @@ describe('OllamaAdapter — 请求体契约', () => {
);
expect(bodies[1].think).toBe(true);
// v0.8.3: xhigh 原样透传(Qwen3 等思考模板原生档);true → 布尔 true(模型默认档)
await adapter.send(
makeRequest({
params: {
maxTokens: 4096,
temperature: 0,
stream: false,
thinkingEnabled: true,
thinkingEffort: 'xhigh',
},
}),
);
expect(bodies[2].think).toBe('xhigh');
await adapter.send(
makeRequest({
params: {
maxTokens: 4096,
temperature: 0,
stream: false,
thinkingEnabled: true,
thinkingEffort: 'true',
},
}),
);
expect(bodies[3].think).toBe(true);
await adapter.send(
makeRequest({
params: { maxTokens: 4096, temperature: 0, stream: false, thinkingEnabled: false },
}),
);
expect(bodies[2].think).toBeUndefined();
expect(bodies[4].think).toBeUndefined();
});
it('O3: data URI 图片剥前缀转纯 base64 数组(无网络下载路径触发)', async () => {
@@ -516,8 +543,10 @@ describe('AnthropicAdapter — thinking budget 按 effort 映射矩阵', () => {
['low', 1024],
['medium', 4096],
['high', 16384],
['xhigh', 24576], // v0.8.3: 介于 high 与 max 之间
// v0.8.1: max_tokens 不再按模型钳制(100_000 原样透传)→ budget = min(32768, floor(100000/2)) = 32768
['max', 32768],
['true', 16384], // v0.8.3: 模型默认档按 high 同档预算
] as const)('effort=%s → budget 为该档值且 < max_tokens', async (effort, expectBudget) => {
const adapter = makeAdapter();
const { bodies } = captureFetch();
@@ -723,9 +752,11 @@ describe('DeepSeekAdapter — thinking 映射矩阵', () => {
['low', 'high'],
['medium', 'high'],
['high', 'high'],
['xhigh', 'high'], // v0.8.3: DeepSeek 无 xhigh 档,就近映射 high
['max', 'max'],
['true', 'high'], // v0.8.3: 模型默认档映射 high
] as const)(
'effort=%s → reasoning_effort=%slow/medium 归一 high',
'effort=%s → reasoning_effort=%slow/medium/xhigh/true 归一 high',
async (effort, expected) => {
const adapter = makeAdapter();
const { bodies } = captureFetch();
@@ -730,12 +730,16 @@ export class AnthropicAdapter extends BaseAdapter {
}
// Thinking 模式:budget_tokens(必须小于 max_tokens,此处钳制到一半)
// v0.8.3: 新增 xhigh / true 档 —— xhigh 介于 high 与 max 之间取 24576
// true(模型默认档)按 high 同档预算
if (thinkingRequested) {
const budgetMap: Record<string, number> = {
low: 1024,
medium: 4096,
high: 16384,
xhigh: 24576,
max: 32768,
true: 16384,
};
const effortBudget = budgetMap[request.params.thinkingEffort ?? 'high'] ?? 16384;
// thinking 路径 maxTokensForRequest 恒为数字(Math.max(2048, …) 兜底)
@@ -206,13 +206,16 @@ export class DeepSeekAdapter extends OpenAICompatibleAdapter {
body.thinking = { type: 'disabled' };
} else {
body.thinking = { type: 'enabled' };
// v0.8.3: 新增 xhigh / true 档 —— DeepSeek API 仅 high / max 两档,就近映射 high
const effortMap: Record<string, string> = {
low: 'high',
medium: 'high',
high: 'high',
xhigh: 'high',
max: 'max',
true: 'high',
};
// DeepSeek API 仅支持 high / max 两档,low/medium 映射为 high
// DeepSeek API 仅支持 high / max 两档,low/medium/xhigh/true 映射为 high
body.reasoning_effort = effortMap[request.params.thinkingEffort ?? 'high'] ?? 'high';
// 元信息标注不支持思考但用户开启 —— 告知降级兜底路径(不拦截)
if (DeepSeekAdapter.MODEL_INFO[this.config.defaultModel]?.supportsThinking === false) {
@@ -698,11 +698,17 @@ export class OllamaAdapter extends BaseAdapter {
if (request.params.thinkingEnabled) {
const modelThinkingSupported = this.cachedThinkingSupport !== false;
if (modelThinkingSupported) {
// v0.8.3: 新增 xhigh / true 档 —— Qwen3 等新模型思考模板原生支持 xhigh
//(旧版仅下发 low/medium/high 时,模板会 500 "Unexpected reasoning effort");
// true = 不指定档位(think: true),由模型思考模板用自身默认档。
// max 语义为"应用内最高档"Ollama 侧同样以布尔 true 放行给服务端默认。
const effortMap: Record<string, string | boolean> = {
low: 'low',
medium: 'medium',
high: 'high',
xhigh: 'xhigh',
max: true,
true: true,
};
body.think = effortMap[request.params.thinkingEffort ?? 'high'] ?? true;
// v0.8.0 P0-3: 思考占用 num_predict 输出预算 —— 预算过小时显式告警
@@ -149,12 +149,15 @@ export class OpenAIAdapter extends OpenAICompatibleAdapter {
}
// Thinking 模式:推理模型映射 reasoning_effort;非推理模型忽略
// v0.8.3: 新增 xhigh / true 档 —— OpenAI API 档位封顶 high,二者就近映射
if (request.params.thinkingEnabled && isReasoningModel) {
const effortMap: Record<string, string> = {
low: 'low',
medium: 'medium',
high: 'high',
xhigh: 'high',
max: 'high',
true: 'high',
};
body.reasoning_effort = effortMap[request.params.thinkingEffort ?? 'high'] ?? 'high';
// v0.8.0 P0-3: 推理 token 计入 max_completion_tokens —— 用户配置的预算过小时告警