mirror of
https://github.com/cline/cline.git
synced 2026-09-15 21:04:27 +08:00
Fix DeepSeek reasoning error.
This commit is contained in:
committed by
Max Paulus 🥪
parent
693bd3f7da
commit
82badf29ae
@@ -564,8 +564,9 @@ Uncomment each and confirm the call site compiles against the contract. If anyth
|
||||
- Read `src/core/api/providers/deepseek.ts`. Confirm `getModel()` reads `modelId`/`modelInfo` from passed-in config, not a static map.
|
||||
- If a static-map lookup exists, replace with read from passed-in config. Add test: `getModel().info` equals the passed-in info.
|
||||
- Verify `cline-session-factory.ts` resolves provider config via `ProviderConfigStore` and snapshots at task start.
|
||||
- Audit every auxiliary field the legacy provider panel could set, not just `modelId`/`modelInfo`. For DeepSeek this includes reasoning/thinking state (`enabled`, `effort`, and budget tokens). The migrated runtime must either carry each auxiliary field through the SDK provider settings/session config coherently, or explicitly document that field as out of scope for the picker migration. In particular, disabled thinking must clear any orphaned reasoning effort before inference; otherwise provider switches can tear configuration across providers.
|
||||
|
||||
**Exit:** Runtime test passes. `cline-session-factory` does not import `deepSeekModels`.
|
||||
**Exit:** Runtime test passes. `cline-session-factory` does not import `deepSeekModels`. Auxiliary-field tests prove DeepSeek does not send contradictory thinking/reasoning options after switching from a provider with reasoning enabled.
|
||||
|
||||
### Step 6.3 — End-to-end live test
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
buildStartSessionInput,
|
||||
createHistoryItemFromSession,
|
||||
getHistoryItemById,
|
||||
normalizeProviderReasoningSettings,
|
||||
updateHistoryItem,
|
||||
} from "./cline-session-factory"
|
||||
|
||||
@@ -123,6 +124,36 @@ describe("buildResumeSessionInput", () => {
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// normalizeProviderReasoningSettings
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("normalizeProviderReasoningSettings", () => {
|
||||
it("does not emit reasoningEffort when thinking is disabled", () => {
|
||||
const result = normalizeProviderReasoningSettings({ enabled: false, effort: "medium" })
|
||||
|
||||
expect(result).toEqual({ thinking: false })
|
||||
})
|
||||
|
||||
it("treats effort none as disabled thinking", () => {
|
||||
const result = normalizeProviderReasoningSettings({ effort: "none" })
|
||||
|
||||
expect(result).toEqual({ thinking: false })
|
||||
})
|
||||
|
||||
it("passes enabled reasoning with a concrete effort", () => {
|
||||
const result = normalizeProviderReasoningSettings({ enabled: true, effort: "high" })
|
||||
|
||||
expect(result).toEqual({ thinking: true, reasoningEffort: "high" })
|
||||
})
|
||||
|
||||
it("leaves explicit effort-only settings enabled by SDK/provider defaults", () => {
|
||||
const result = normalizeProviderReasoningSettings({ effort: "medium" })
|
||||
|
||||
expect(result).toEqual({ reasoningEffort: "medium" })
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// createHistoryItemFromSession
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -8,7 +8,12 @@
|
||||
//
|
||||
// The factory does NOT handle UI concerns — that's the SdkController's job.
|
||||
|
||||
import { type ClineCoreStartInput, type CoreSessionConfig, type StartSessionResult } from "@cline/core"
|
||||
import {
|
||||
type ClineCoreStartInput,
|
||||
type CoreSessionConfig,
|
||||
type ProviderSettings,
|
||||
type StartSessionResult,
|
||||
} from "@cline/core"
|
||||
import { buildClineSystemPrompt } from "@cline/shared"
|
||||
import type { ApiConfiguration } from "@shared/api"
|
||||
import type { HistoryItem } from "@shared/HistoryItem"
|
||||
@@ -106,6 +111,67 @@ function resolveWorkspaceName(workspacePath: string): string {
|
||||
return name || "workspace"
|
||||
}
|
||||
|
||||
type ReasoningEffort = NonNullable<CoreSessionConfig["reasoningEffort"]>
|
||||
type ProviderReasoningSettings = NonNullable<ProviderSettings["reasoning"]>
|
||||
type SessionReasoningConfig = Pick<CoreSessionConfig, "thinking" | "reasoningEffort">
|
||||
|
||||
function isReasoningEffort(value: unknown): value is ReasoningEffort {
|
||||
return value === "low" || value === "medium" || value === "high" || value === "xhigh"
|
||||
}
|
||||
|
||||
function hasStaleDisabledReasoningFields(reasoning: ProviderReasoningSettings | undefined): boolean {
|
||||
return reasoning?.enabled === false && (reasoning.effort !== undefined || reasoning.budgetTokens !== undefined)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert SDK provider-level reasoning settings into the SDK session fields that
|
||||
* are actually forwarded as model options. Keep `thinking` and
|
||||
* `reasoningEffort` coherent: a disabled/none state must never carry an effort.
|
||||
*/
|
||||
export function normalizeProviderReasoningSettings(reasoning: ProviderReasoningSettings | undefined): SessionReasoningConfig {
|
||||
if (!reasoning) {
|
||||
return {}
|
||||
}
|
||||
|
||||
if (reasoning.enabled === false || reasoning.effort === "none") {
|
||||
return { thinking: false }
|
||||
}
|
||||
|
||||
if (reasoning.enabled === true) {
|
||||
return {
|
||||
thinking: true,
|
||||
...(isReasoningEffort(reasoning.effort) ? { reasoningEffort: reasoning.effort } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
return isReasoningEffort(reasoning.effort) ? { reasoningEffort: reasoning.effort } : {}
|
||||
}
|
||||
|
||||
function resolveProviderReasoningConfig(providerId: string): SessionReasoningConfig {
|
||||
try {
|
||||
const manager = getProviderSettingsManager(resolveDataDir())
|
||||
const settings = manager.getProviderSettings(providerId)
|
||||
if (!settings) {
|
||||
return {}
|
||||
}
|
||||
|
||||
if (hasStaleDisabledReasoningFields(settings.reasoning)) {
|
||||
const sanitizedSettings: ProviderSettings = {
|
||||
...settings,
|
||||
reasoning: { enabled: false },
|
||||
}
|
||||
manager.saveProviderSettings(sanitizedSettings, { setLastUsed: false })
|
||||
Logger.warn(`[SessionFactory] Cleared stale disabled reasoning fields for provider=${providerId}`)
|
||||
return normalizeProviderReasoningSettings(sanitizedSettings.reasoning)
|
||||
}
|
||||
|
||||
return normalizeProviderReasoningSettings(settings.reasoning)
|
||||
} catch (error) {
|
||||
Logger.warn("[SessionFactory] Provider reasoning resolution failed:", error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Provider → API key field mapping
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -365,6 +431,7 @@ export async function buildSessionConfig(input: SessionConfigInput): Promise<Cor
|
||||
providerId = providerId ?? "cline"
|
||||
modelId = modelId ?? "openai/gpt-5.4"
|
||||
apiKey = apiKey ?? ""
|
||||
const reasoningConfig = resolveProviderReasoningConfig(providerId)
|
||||
|
||||
// Build the system prompt using the shared prompt builder. Core still
|
||||
// expects callers to provide a concrete systemPrompt, but the prompt builder
|
||||
@@ -423,7 +490,7 @@ export async function buildSessionConfig(input: SessionConfigInput): Promise<Cor
|
||||
enableAgentTeams: false,
|
||||
disableMcpSettingsTools: true,
|
||||
mode: mode === "plan" ? "plan" : "act",
|
||||
thinking: false,
|
||||
...reasoningConfig,
|
||||
maxIterations: undefined,
|
||||
logger: sdkLogger,
|
||||
extensionContext: {
|
||||
|
||||
Reference in New Issue
Block a user