Merge pull request #3927 from acamq/feat/support-dsv4-thinking
fix(model-settings): support DeepSeek reasoning effort
This commit is contained in:
@@ -6,6 +6,7 @@ export type HeuristicModelFamilyDefinition = {
|
|||||||
pattern?: RegExp
|
pattern?: RegExp
|
||||||
variants?: string[]
|
variants?: string[]
|
||||||
reasoningEfforts?: string[]
|
reasoningEfforts?: string[]
|
||||||
|
reasoningEffortAliases?: Record<string, string>
|
||||||
supportsThinking?: boolean
|
supportsThinking?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +73,12 @@ export const HEURISTIC_MODEL_FAMILY_REGISTRY: ReadonlyArray<HeuristicModelFamily
|
|||||||
family: "deepseek",
|
family: "deepseek",
|
||||||
includes: ["deepseek"],
|
includes: ["deepseek"],
|
||||||
variants: ["low", "medium", "high"],
|
variants: ["low", "medium", "high"],
|
||||||
|
reasoningEfforts: ["high", "max"],
|
||||||
|
reasoningEffortAliases: {
|
||||||
|
low: "high",
|
||||||
|
medium: "high",
|
||||||
|
xhigh: "max",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
family: "mistral",
|
family: "mistral",
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ describe("resolveCompatibleModelSettings", () => {
|
|||||||
{ name: "Kimi (k2)", modelID: "k2-v2", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
{ name: "Kimi (k2)", modelID: "k2-v2", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
||||||
{ name: "GLM", modelID: "glm-5", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
{ name: "GLM", modelID: "glm-5", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
||||||
{ name: "Minimax", modelID: "minimax-m2.5", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
{ name: "Minimax", modelID: "minimax-m2.5", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
||||||
{ name: "DeepSeek", modelID: "deepseek-r2", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
{ name: "DeepSeek", modelID: "deepseek-r2", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: true },
|
||||||
{ name: "Mistral", modelID: "mistral-large-next", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
{ name: "Mistral", modelID: "mistral-large-next", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
||||||
{ name: "Codestral → Mistral", modelID: "codestral-2506", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
{ name: "Codestral → Mistral", modelID: "codestral-2506", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
||||||
{ name: "Llama", modelID: "llama-4-maverick", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
{ name: "Llama", modelID: "llama-4-maverick", expectedVariants: ["low", "medium", "high"], hasReasoningEffort: false },
|
||||||
@@ -320,6 +320,68 @@ describe("resolveCompatibleModelSettings", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("DeepSeek keeps canonical high and max reasoningEffort values", () => {
|
||||||
|
for (const reasoningEffort of ["high", "max"]) {
|
||||||
|
const result = resolveCompatibleModelSettings({
|
||||||
|
providerID: "openai-compatible",
|
||||||
|
modelID: "deepseek-v4-pro",
|
||||||
|
desired: { reasoningEffort },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.reasoningEffort).toBe(reasoningEffort)
|
||||||
|
expect(result.changes).toEqual([])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("DeepSeek maps generic reasoningEffort levels to canonical API values", () => {
|
||||||
|
const cases = [
|
||||||
|
{ requested: "low", expected: "high" },
|
||||||
|
{ requested: "medium", expected: "high" },
|
||||||
|
{ requested: "xhigh", expected: "max" },
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const { requested, expected } of cases) {
|
||||||
|
const result = resolveCompatibleModelSettings({
|
||||||
|
providerID: "openai-compatible",
|
||||||
|
modelID: "deepseek-v4-pro",
|
||||||
|
desired: { reasoningEffort: requested },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.reasoningEffort).toBe(expected)
|
||||||
|
expect(result.changes).toEqual([
|
||||||
|
{
|
||||||
|
field: "reasoningEffort",
|
||||||
|
from: requested,
|
||||||
|
to: expected,
|
||||||
|
reason: "unsupported-by-model-family",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("DeepSeek maps generic reasoningEffort levels when capabilities come from heuristics", () => {
|
||||||
|
const capabilities = getModelCapabilities({
|
||||||
|
providerID: "openai-compatible",
|
||||||
|
modelID: "deepseek-v4-pro",
|
||||||
|
})
|
||||||
|
const result = resolveCompatibleModelSettings({
|
||||||
|
providerID: "openai-compatible",
|
||||||
|
modelID: "deepseek-v4-pro",
|
||||||
|
desired: { reasoningEffort: "xhigh" },
|
||||||
|
capabilities,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.reasoningEffort).toBe("max")
|
||||||
|
expect(result.changes).toEqual([
|
||||||
|
{
|
||||||
|
field: "reasoningEffort",
|
||||||
|
from: "xhigh",
|
||||||
|
to: "max",
|
||||||
|
reason: "unsupported-by-model-family",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
test("GPT-5 downgrades unsupported max variant to xhigh", () => {
|
test("GPT-5 downgrades unsupported max variant to xhigh", () => {
|
||||||
const result = resolveCompatibleModelSettings({
|
const result = resolveCompatibleModelSettings({
|
||||||
providerID: "openai",
|
providerID: "openai",
|
||||||
|
|||||||
@@ -86,7 +86,13 @@ function resolveField(
|
|||||||
ladder: string[],
|
ladder: string[],
|
||||||
familyKnown: boolean,
|
familyKnown: boolean,
|
||||||
metadataOverride?: string[],
|
metadataOverride?: string[],
|
||||||
|
familyAliases?: Record<string, string>,
|
||||||
): FieldResolution {
|
): FieldResolution {
|
||||||
|
const aliased = familyAliases?.[normalized]
|
||||||
|
if (aliased && (metadataOverride?.includes(aliased) || familyCaps?.includes(aliased))) {
|
||||||
|
return { value: aliased, reason: "unsupported-by-model-family" }
|
||||||
|
}
|
||||||
|
|
||||||
if (metadataOverride) {
|
if (metadataOverride) {
|
||||||
if (metadataOverride.includes(normalized)) return { value: normalized }
|
if (metadataOverride.includes(normalized)) return { value: normalized }
|
||||||
return {
|
return {
|
||||||
@@ -132,7 +138,14 @@ export function resolveCompatibleModelSettings(
|
|||||||
let reasoningEffort = input.desired.reasoningEffort
|
let reasoningEffort = input.desired.reasoningEffort
|
||||||
if (reasoningEffort !== undefined) {
|
if (reasoningEffort !== undefined) {
|
||||||
const normalized = reasoningEffort.toLowerCase()
|
const normalized = reasoningEffort.toLowerCase()
|
||||||
const resolved = resolveField(normalized, family?.reasoningEfforts, REASONING_LADDER, familyKnown, metadataReasoningEfforts)
|
const resolved = resolveField(
|
||||||
|
normalized,
|
||||||
|
family?.reasoningEfforts,
|
||||||
|
REASONING_LADDER,
|
||||||
|
familyKnown,
|
||||||
|
metadataReasoningEfforts,
|
||||||
|
family?.reasoningEffortAliases,
|
||||||
|
)
|
||||||
if (resolved.value !== normalized && resolved.reason) {
|
if (resolved.value !== normalized && resolved.reason) {
|
||||||
changes.push({ field: "reasoningEffort", from: reasoningEffort, to: resolved.value, reason: resolved.reason })
|
changes.push({ field: "reasoningEffort", from: reasoningEffort, to: resolved.value, reason: resolved.reason })
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user