feat: v0.8.3 工作空间切换链路根治 · 思考强度扩档 — 启动TDZ连锁/继承丢数/回显滞后三修复 · xhigh+true档位单源 · 安全防线ready后注册 · 2217 用例全量回归 + 生产模式E2E
This commit is contained in:
@@ -82,7 +82,9 @@ describe('OpenAIAdapter — 推理模型 reasoning_effort', () => {
|
||||
['low', 'low'],
|
||||
['medium', 'medium'],
|
||||
['high', 'high'],
|
||||
['xhigh', 'high'], // v0.8.3: OpenAI 档位封顶 high,xhigh 就近降档
|
||||
['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=%s(low/medium 归一 high)',
|
||||
'effort=%s → reasoning_effort=%s(low/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 —— 用户配置的预算过小时告警
|
||||
|
||||
Reference in New Issue
Block a user