Merge PR #7561: fix(catalog): honor disabled DeepSeek thinking (@roboomp)
This commit is contained in:
@@ -25,16 +25,22 @@ function abortedSignal(): AbortSignal {
|
||||
return controller.signal;
|
||||
}
|
||||
|
||||
interface CaptureOptions {
|
||||
tools?: Tool[];
|
||||
reasoning?: Effort;
|
||||
disableReasoning?: boolean;
|
||||
}
|
||||
|
||||
async function capturePayload(
|
||||
model: Model<"openai-completions">,
|
||||
tools?: Tool[],
|
||||
reasoning: "high" | "max" = "high",
|
||||
options: CaptureOptions = {},
|
||||
): Promise<Record<string, unknown>> {
|
||||
const { promise, resolve } = Promise.withResolvers<unknown>();
|
||||
streamOpenAICompletions(model, contextWithTools(tools), {
|
||||
streamOpenAICompletions(model, contextWithTools(options.tools), {
|
||||
apiKey: "test-key",
|
||||
signal: abortedSignal(),
|
||||
reasoning,
|
||||
reasoning: options.disableReasoning ? undefined : (options.reasoning ?? Effort.High),
|
||||
disableReasoning: options.disableReasoning,
|
||||
toolChoice: "auto",
|
||||
maxTokens: 123,
|
||||
onPayload: payload => resolve(payload),
|
||||
@@ -42,7 +48,7 @@ async function capturePayload(
|
||||
return (await promise) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function customDeepseekFlash(): Model<"openai-completions"> {
|
||||
function customDeepseekFlash(legacyThinkingExtraBody = false): Model<"openai-completions"> {
|
||||
return buildModel({
|
||||
...getBundledModel("openai", "gpt-4o-mini"),
|
||||
api: "openai-completions",
|
||||
@@ -54,6 +60,7 @@ function customDeepseekFlash(): Model<"openai-completions"> {
|
||||
compat: {
|
||||
supportsReasoningEffort: true,
|
||||
reasoningEffortMap: { xhigh: "max" },
|
||||
...(legacyThinkingExtraBody ? { extraBody: { thinking: { type: "enabled" } } } : {}),
|
||||
},
|
||||
} as ModelSpec<"openai-completions">);
|
||||
}
|
||||
@@ -65,7 +72,9 @@ describe("issue #1207 — DeepSeek V4 keeps reasoning with tools", () => {
|
||||
|
||||
expect(compat.supportsToolChoice).toBe(false);
|
||||
expect(compat.maxTokensField).toBe("max_tokens");
|
||||
expect(compat.extraBody).toEqual({ thinking: { type: "enabled" } });
|
||||
expect(compat.extraBody).toBeUndefined();
|
||||
expect(compat.reasoningDisableMode).toBe("zai-thinking-disabled");
|
||||
expect(compat.whenThinking?.extraBody).toEqual({ thinking: { type: "enabled" } });
|
||||
// DeepSeek's reasoning_effort is the honest wire-exact high/max pair;
|
||||
// no synthetic lower tiers, no alias map.
|
||||
expect(model.thinking?.efforts).toEqual([Effort.High, Effort.Max]);
|
||||
@@ -93,6 +102,18 @@ describe("issue #1207 — DeepSeek V4 keeps reasoning with tools", () => {
|
||||
expect(body.max_completion_tokens).toBeUndefined();
|
||||
});
|
||||
|
||||
it("disables thinking for bundled and legacy cached model definitions", async () => {
|
||||
const bundled = getBundledModel("deepseek", "deepseek-v4-flash") as Model<"openai-completions">;
|
||||
const legacyCached = customDeepseekFlash(true);
|
||||
|
||||
for (const model of [bundled, legacyCached]) {
|
||||
const body = await capturePayload(model, { disableReasoning: true });
|
||||
expect(model.compat.extraBody).toBeUndefined();
|
||||
expect(body.reasoning_effort).toBeUndefined();
|
||||
expect(body.thinking).toEqual({ type: "disabled" });
|
||||
}
|
||||
});
|
||||
|
||||
it("does not mix Fireworks DeepSeek effort with the native thinking toggle", async () => {
|
||||
const model = getBundledModel("fireworks", "deepseek-v4-pro") as Model<"openai-completions">;
|
||||
const compat = model.compat;
|
||||
@@ -127,7 +148,7 @@ describe("issue #1207 — DeepSeek V4 keeps reasoning with tools", () => {
|
||||
paths: "(string | string[])?",
|
||||
}),
|
||||
};
|
||||
const body = await capturePayload(model, [unionTool]);
|
||||
const body = await capturePayload(model, { tools: [unionTool] });
|
||||
const tools = body.tools as Array<{ function: { parameters: Record<string, unknown> } }>;
|
||||
const properties = tools[0].function.parameters.properties as Record<string, Record<string, unknown>>;
|
||||
const branches = properties.paths.anyOf as Array<Record<string, unknown>>;
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
- Replaced arktype with `@oh-my-pi/omptype` for discovery payload schemas (same definition DSL, lazy JIT validation).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `thinking-level: off` still enabling reasoning on direct DeepSeek V4 requests by emitting the provider's disabled thinking toggle and migrating stale cached model metadata ([#7559](https://github.com/can1357/oh-my-pi/issues/7559)).
|
||||
|
||||
## [17.2.6] - 2026-08-03
|
||||
|
||||
### Added
|
||||
|
||||
@@ -583,7 +583,7 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
|
||||
wireModelIdMode,
|
||||
isVercelGatewayHost: isVercelGateway,
|
||||
supportsStrictMode: detectStrictModeSupport(provider, baseUrl),
|
||||
extraBody: isDirectDeepseekReasoning ? { thinking: { type: "enabled" } } : undefined,
|
||||
extraBody: undefined,
|
||||
toolStrictMode: isCerebras ? "all_strict" : "mixed",
|
||||
// Kimi-family ids trigger MFJS on any host, not just native base URLs:
|
||||
// proxies (OpenRouter, custom gateways) forward `tools.function.parameters`
|
||||
@@ -604,10 +604,24 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
|
||||
};
|
||||
|
||||
applyCompatOverrides(compat, spec.compat);
|
||||
const deepseekThinking = compat.extraBody?.thinking;
|
||||
if (
|
||||
isDirectDeepseekReasoning &&
|
||||
typeof deepseekThinking === "object" &&
|
||||
deepseekThinking !== null &&
|
||||
"type" in deepseekThinking &&
|
||||
deepseekThinking.type === "enabled"
|
||||
) {
|
||||
const extraBody = { ...compat.extraBody };
|
||||
delete extraBody.thinking;
|
||||
compat.extraBody = Object.keys(extraBody).length > 0 ? extraBody : undefined;
|
||||
}
|
||||
if (spec.compat?.reasoningDisableMode === undefined) {
|
||||
compat.reasoningDisableMode = requiresEnabledThinking
|
||||
? "omit"
|
||||
: resolveReasoningDisableMode(compat.thinkingFormat);
|
||||
: isDirectDeepseekReasoning
|
||||
? "zai-thinking-disabled"
|
||||
: resolveReasoningDisableMode(compat.thinkingFormat);
|
||||
}
|
||||
if (spec.compat?.omitReasoningEffort === undefined && !compat.supportsReasoningEffort) {
|
||||
compat.omitReasoningEffort = true;
|
||||
@@ -615,7 +629,12 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
|
||||
mergeModelReasoningEffortMap(compat, spec.id, isMimoReasoningEffortModel);
|
||||
|
||||
const whenThinkingPolicy =
|
||||
spec.compat?.whenThinking ?? (isOpenCodeProvider && spec.reasoning ? OPENCODE_WHEN_THINKING : undefined);
|
||||
spec.compat?.whenThinking ??
|
||||
(isDirectDeepseekReasoning
|
||||
? { extraBody: { ...compat.extraBody, thinking: { type: "enabled" } } }
|
||||
: isOpenCodeProvider && spec.reasoning
|
||||
? OPENCODE_WHEN_THINKING
|
||||
: undefined);
|
||||
if (whenThinkingPolicy) {
|
||||
const variant: ResolvedOpenAICompat = { ...compat };
|
||||
applyCompatOverrides(variant, whenThinkingPolicy);
|
||||
|
||||
Reference in New Issue
Block a user