mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: disable reasoning by default for DeepSeek V3.1 Terminus on OpenRouter
- Added explicit reasoning exclusion for deepseek/deepseek-v3.1-terminus when reasoning is not enabled - Ensures consistent behavior with user expectations where reasoning should be opt-in - Added comprehensive tests to verify the fix works correctly - Fixes #8270
This commit is contained in:
parent
fc4ee93585
commit
389c96a63f
2 changed files with 233 additions and 0 deletions
214
src/api/providers/__tests__/openrouter-deepseek-terminus.spec.ts
Normal file
214
src/api/providers/__tests__/openrouter-deepseek-terminus.spec.ts
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest"
|
||||
import { OpenRouterHandler } from "../openrouter"
|
||||
import type { ApiHandlerOptions } from "../../../shared/api"
|
||||
|
||||
// Mock the fetchers
|
||||
vi.mock("../fetchers/modelCache", () => ({
|
||||
getModels: vi.fn().mockResolvedValue({
|
||||
"deepseek/deepseek-v3.1-terminus": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 128000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
supportsReasoningEffort: true,
|
||||
inputPrice: 0.5,
|
||||
outputPrice: 2.0,
|
||||
description: "DeepSeek V3.1 Terminus model",
|
||||
},
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock("../fetchers/modelEndpointCache", () => ({
|
||||
getModelEndpoints: vi.fn().mockResolvedValue({}),
|
||||
}))
|
||||
|
||||
// Mock OpenAI client
|
||||
vi.mock("openai", () => {
|
||||
const mockStream = {
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
yield {
|
||||
choices: [{ delta: { content: "Test response" } }],
|
||||
usage: {
|
||||
prompt_tokens: 10,
|
||||
completion_tokens: 5,
|
||||
total_tokens: 15,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
default: vi.fn().mockImplementation(() => ({
|
||||
chat: {
|
||||
completions: {
|
||||
create: vi.fn().mockResolvedValue(mockStream),
|
||||
},
|
||||
},
|
||||
})),
|
||||
}
|
||||
})
|
||||
|
||||
describe("OpenRouterHandler - DeepSeek V3.1 Terminus", () => {
|
||||
let handler: OpenRouterHandler
|
||||
let mockCreate: any
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it("should exclude reasoning for DeepSeek V3.1 Terminus when reasoning is not enabled", async () => {
|
||||
const options: ApiHandlerOptions = {
|
||||
openRouterApiKey: "test-key",
|
||||
openRouterModelId: "deepseek/deepseek-v3.1-terminus",
|
||||
enableReasoningEffort: false,
|
||||
}
|
||||
|
||||
handler = new OpenRouterHandler(options)
|
||||
|
||||
// Spy on the OpenAI client's create method
|
||||
mockCreate = vi.fn().mockResolvedValue({
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
yield {
|
||||
choices: [{ delta: { content: "Test" } }],
|
||||
usage: { prompt_tokens: 10, completion_tokens: 5 },
|
||||
}
|
||||
},
|
||||
})
|
||||
;(handler as any).client.chat.completions.create = mockCreate
|
||||
|
||||
// Create a message
|
||||
const generator = handler.createMessage("System prompt", [{ role: "user", content: "Test message" }])
|
||||
|
||||
// Consume the generator
|
||||
const results = []
|
||||
for await (const chunk of generator) {
|
||||
results.push(chunk)
|
||||
}
|
||||
|
||||
// Check that the create method was called with reasoning excluded
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: "deepseek/deepseek-v3.1-terminus",
|
||||
reasoning: { exclude: true },
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should not exclude reasoning for DeepSeek V3.1 Terminus when reasoning is enabled", async () => {
|
||||
const options: ApiHandlerOptions = {
|
||||
openRouterApiKey: "test-key",
|
||||
openRouterModelId: "deepseek/deepseek-v3.1-terminus",
|
||||
enableReasoningEffort: true,
|
||||
reasoningEffort: "medium",
|
||||
}
|
||||
|
||||
handler = new OpenRouterHandler(options)
|
||||
|
||||
// Spy on the OpenAI client's create method
|
||||
mockCreate = vi.fn().mockResolvedValue({
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
yield {
|
||||
choices: [{ delta: { content: "Test" } }],
|
||||
usage: { prompt_tokens: 10, completion_tokens: 5 },
|
||||
}
|
||||
},
|
||||
})
|
||||
;(handler as any).client.chat.completions.create = mockCreate
|
||||
|
||||
// Create a message
|
||||
const generator = handler.createMessage("System prompt", [{ role: "user", content: "Test message" }])
|
||||
|
||||
// Consume the generator
|
||||
const results = []
|
||||
for await (const chunk of generator) {
|
||||
results.push(chunk)
|
||||
}
|
||||
|
||||
// Check that the create method was called with reasoning effort
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: "deepseek/deepseek-v3.1-terminus",
|
||||
reasoning: { effort: "medium" },
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should not affect other models", async () => {
|
||||
const options: ApiHandlerOptions = {
|
||||
openRouterApiKey: "test-key",
|
||||
openRouterModelId: "anthropic/claude-3-sonnet",
|
||||
enableReasoningEffort: false,
|
||||
}
|
||||
|
||||
// Mock a different model
|
||||
const { getModels } = await import("../fetchers/modelCache")
|
||||
vi.mocked(getModels).mockResolvedValue({
|
||||
"anthropic/claude-3-sonnet": {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 200000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3.0,
|
||||
outputPrice: 15.0,
|
||||
description: "Claude 3 Sonnet",
|
||||
},
|
||||
})
|
||||
|
||||
handler = new OpenRouterHandler(options)
|
||||
|
||||
// Spy on the OpenAI client's create method
|
||||
mockCreate = vi.fn().mockResolvedValue({
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
yield {
|
||||
choices: [{ delta: { content: "Test" } }],
|
||||
usage: { prompt_tokens: 10, completion_tokens: 5 },
|
||||
}
|
||||
},
|
||||
})
|
||||
;(handler as any).client.chat.completions.create = mockCreate
|
||||
|
||||
// Create a message
|
||||
const generator = handler.createMessage("System prompt", [{ role: "user", content: "Test message" }])
|
||||
|
||||
// Consume the generator
|
||||
const results = []
|
||||
for await (const chunk of generator) {
|
||||
results.push(chunk)
|
||||
}
|
||||
|
||||
// Check that reasoning was not excluded for other models
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.not.objectContaining({
|
||||
reasoning: { exclude: true },
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should exclude reasoning in completePrompt for DeepSeek V3.1 Terminus", async () => {
|
||||
const options: ApiHandlerOptions = {
|
||||
openRouterApiKey: "test-key",
|
||||
openRouterModelId: "deepseek/deepseek-v3.1-terminus",
|
||||
enableReasoningEffort: false,
|
||||
}
|
||||
|
||||
handler = new OpenRouterHandler(options)
|
||||
|
||||
// Mock the non-streaming response
|
||||
mockCreate = vi.fn().mockResolvedValue({
|
||||
choices: [{ message: { content: "Test response" } }],
|
||||
})
|
||||
;(handler as any).client.chat.completions.create = mockCreate
|
||||
|
||||
// Call completePrompt
|
||||
await handler.completePrompt("Test prompt")
|
||||
|
||||
// Check that the create method was called with reasoning excluded
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: "deepseek/deepseek-v3.1-terminus",
|
||||
reasoning: { exclude: true },
|
||||
stream: false,
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
@ -118,6 +118,16 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
|
|||
reasoning = { exclude: true }
|
||||
}
|
||||
|
||||
// DeepSeek V3.1 Terminus also has reasoning enabled by default on OpenRouter
|
||||
// We need to explicitly disable it when the user hasn't enabled reasoning
|
||||
if (
|
||||
modelId === "deepseek/deepseek-v3.1-terminus" &&
|
||||
typeof reasoning === "undefined" &&
|
||||
!this.options.enableReasoningEffort
|
||||
) {
|
||||
reasoning = { exclude: true }
|
||||
}
|
||||
|
||||
// Convert Anthropic messages to OpenAI format.
|
||||
let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
|
|
@ -248,6 +258,15 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
|
|||
async completePrompt(prompt: string) {
|
||||
let { id: modelId, maxTokens, temperature, reasoning } = await this.fetchModel()
|
||||
|
||||
// Apply the same reasoning exclusion logic for DeepSeek V3.1 Terminus
|
||||
if (
|
||||
modelId === "deepseek/deepseek-v3.1-terminus" &&
|
||||
typeof reasoning === "undefined" &&
|
||||
!this.options.enableReasoningEffort
|
||||
) {
|
||||
reasoning = { exclude: true }
|
||||
}
|
||||
|
||||
const completionParams: OpenRouterChatCompletionParams = {
|
||||
model: modelId,
|
||||
max_tokens: maxTokens,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue