feat: preserve reasoning details for OpenRouter models between conversation turns

- Add preserveReasoning flag to ModelInfo for reasoning-capable models
- Set preserveReasoning=true for OpenRouter models that support reasoning
- Ensure reasoning content is included in conversation history for continuity
- Add comprehensive tests for preserveReasoning functionality

Fixes #9125
This commit is contained in:
Roo Code 2025-11-08 12:03:13 +00:00
parent e98f4b9057
commit 894d70e24c
2 changed files with 138 additions and 0 deletions

View file

@ -29,6 +29,7 @@ describe("OpenRouter API", () => {
supportsReasoningBudget: false,
supportsReasoningEffort: false,
supportedParameters: ["max_tokens", "temperature", "reasoning", "include_reasoning"],
preserveReasoning: true,
})
expect(models["anthropic/claude-3.7-sonnet:thinking"]).toEqual({
@ -45,6 +46,7 @@ describe("OpenRouter API", () => {
requiredReasoningBudget: true,
supportsReasoningEffort: true,
supportedParameters: ["max_tokens", "temperature", "reasoning", "include_reasoning"],
preserveReasoning: true,
})
expect(models["google/gemini-2.5-flash-preview-05-20"].maxTokens).toEqual(65535)
@ -97,6 +99,7 @@ describe("OpenRouter API", () => {
description: undefined,
supportsReasoningEffort: undefined,
supportedParameters: undefined,
preserveReasoning: true,
},
"google-ai-studio": {
maxTokens: 65536,
@ -111,6 +114,7 @@ describe("OpenRouter API", () => {
description: undefined,
supportsReasoningEffort: undefined,
supportedParameters: undefined,
preserveReasoning: true,
},
})
@ -236,5 +240,122 @@ describe("OpenRouter API", () => {
expect(textResult.maxTokens).toBe(64000)
expect(imageResult.maxTokens).toBe(64000)
})
describe("preserveReasoning flag", () => {
it("should set preserveReasoning to true for models with reasoning support", () => {
const mockModel = {
name: "Test Reasoning Model",
context_length: 128000,
max_completion_tokens: 8192,
pricing: {
prompt: "0.001",
completion: "0.002",
},
}
const result = parseOpenRouterModel({
id: "test/reasoning-model",
model: mockModel,
inputModality: ["text"],
outputModality: ["text"],
maxTokens: 8192,
supportedParameters: ["reasoning", "temperature"],
})
expect(result.preserveReasoning).toBe(true)
})
it("should set preserveReasoning to true for models with include_reasoning support", () => {
const mockModel = {
name: "Test Include Reasoning Model",
context_length: 128000,
max_completion_tokens: 8192,
pricing: {
prompt: "0.001",
completion: "0.002",
},
}
const result = parseOpenRouterModel({
id: "test/include-reasoning-model",
model: mockModel,
inputModality: ["text"],
outputModality: ["text"],
maxTokens: 8192,
supportedParameters: ["include_reasoning", "temperature"],
})
expect(result.preserveReasoning).toBe(true)
})
it("should set preserveReasoning to false for models without reasoning support", () => {
const mockModel = {
name: "Test Non-Reasoning Model",
context_length: 128000,
max_completion_tokens: 8192,
pricing: {
prompt: "0.001",
completion: "0.002",
},
}
const result = parseOpenRouterModel({
id: "test/non-reasoning-model",
model: mockModel,
inputModality: ["text"],
outputModality: ["text"],
maxTokens: 8192,
supportedParameters: ["temperature", "max_tokens"],
})
expect(result.preserveReasoning).toBe(false)
})
it("should set preserveReasoning to true for claude-3.7-sonnet:thinking", () => {
const mockModel = {
name: "Claude 3.7 Sonnet Thinking",
context_length: 200000,
max_completion_tokens: 8192,
pricing: {
prompt: "0.003",
completion: "0.015",
},
}
const result = parseOpenRouterModel({
id: "anthropic/claude-3.7-sonnet:thinking",
model: mockModel,
inputModality: ["text"],
outputModality: ["text"],
maxTokens: 8192,
supportedParameters: ["reasoning"],
})
expect(result.preserveReasoning).toBe(true)
})
it("should set preserveReasoning to true for claude-haiku-4.5", () => {
const mockModel = {
name: "Claude Haiku 4.5",
context_length: 200000,
max_completion_tokens: 8192,
pricing: {
prompt: "0.001",
completion: "0.005",
},
}
const result = parseOpenRouterModel({
id: "anthropic/claude-haiku-4.5",
model: mockModel,
inputModality: ["text"],
outputModality: ["text"],
maxTokens: 8192,
supportedParameters: ["reasoning"],
})
expect(result.preserveReasoning).toBe(true)
})
})
})
})

View file

@ -205,6 +205,11 @@ export const parseOpenRouterModel = ({
const supportsPromptCache = typeof cacheReadsPrice !== "undefined" // some models support caching but don't charge a cacheWritesPrice, e.g. GPT-5
// Check if the model supports reasoning (either through include_reasoning or reasoning parameters)
const supportsReasoning = supportedParameters
? supportedParameters.includes("reasoning") || supportedParameters.includes("include_reasoning")
: false
const modelInfo: ModelInfo = {
maxTokens: maxTokens || Math.ceil(model.context_length * 0.2),
contextWindow: model.context_length,
@ -217,6 +222,8 @@ export const parseOpenRouterModel = ({
description: model.description,
supportsReasoningEffort: supportedParameters ? supportedParameters.includes("reasoning") : undefined,
supportedParameters: supportedParameters ? supportedParameters.filter(isModelParameter) : undefined,
// Preserve reasoning in conversation history for models that support reasoning
preserveReasoning: supportsReasoning,
}
if (OPEN_ROUTER_REASONING_BUDGET_MODELS.has(id)) {
@ -239,6 +246,8 @@ export const parseOpenRouterModel = ({
if (id === "anthropic/claude-3.7-sonnet:thinking") {
modelInfo.maxTokens = anthropicModels["claude-3-7-sonnet-20250219:thinking"].maxTokens
// Ensure reasoning is preserved for thinking models
modelInfo.preserveReasoning = true
}
// Set claude-opus-4.1 model to use the correct configuration
@ -251,6 +260,14 @@ export const parseOpenRouterModel = ({
if (id === "anthropic/claude-haiku-4.5") {
modelInfo.supportsReasoningBudget = true
modelInfo.supportsReasoningEffort = false
// Preserve reasoning for Claude Haiku 4.5
modelInfo.preserveReasoning = true
}
// Ensure reasoning preservation for models that support reasoning budget
// This includes models like MiniMax-M2 and other reasoning-capable models
if (modelInfo.supportsReasoningBudget || modelInfo.requiredReasoningBudget) {
modelInfo.preserveReasoning = true
}
// Set horizon-alpha model to 32k max tokens