fix: use Fireworks reasoning_effort parameter for DeepSeek V3.1/V3.2

- Changed from supportsReasoningBinary to supportsReasoningEffort for
  DeepSeek V3.1 and V3.2 models on Fireworks
- Added default reasoningEffort: "medium" for both models
- Override createStream in FireworksHandler to use getModelParams
  which passes reasoning_effort parameter (Fireworks API style) instead
  of thinking parameter (Anthropic style)
- Added tests to verify reasoning_effort is passed correctly
This commit is contained in:
Roo Code 2025-12-14 00:34:01 +00:00
parent b138557bda
commit 959d233fd7
3 changed files with 133 additions and 2 deletions

View file

@ -101,6 +101,8 @@ export const fireworksModels = {
supportsImages: false,
supportsPromptCache: false,
supportsNativeTools: true,
supportsReasoningEffort: ["low", "medium", "high"],
reasoningEffort: "medium",
inputPrice: 0.56,
outputPrice: 1.68,
description:
@ -112,7 +114,8 @@ export const fireworksModels = {
supportsImages: false,
supportsPromptCache: false,
supportsNativeTools: true,
supportsReasoningBinary: true,
supportsReasoningEffort: ["low", "medium", "high"],
reasoningEffort: "medium",
inputPrice: 0.56,
outputPrice: 1.68,
description:

View file

@ -214,6 +214,8 @@ describe("FireworksHandler", () => {
contextWindow: 163840,
supportsImages: false,
supportsPromptCache: false,
supportsReasoningEffort: ["low", "medium", "high"],
reasoningEffort: "medium",
inputPrice: 0.56,
outputPrice: 1.68,
description: expect.stringContaining("DeepSeek v3.1 is an improved version"),
@ -235,7 +237,8 @@ describe("FireworksHandler", () => {
contextWindow: 163840,
supportsImages: false,
supportsPromptCache: false,
supportsReasoningBinary: true,
supportsReasoningEffort: ["low", "medium", "high"],
reasoningEffort: "medium",
inputPrice: 0.56,
outputPrice: 1.68,
description: expect.stringContaining("DeepSeek v3.2 is the latest version"),
@ -243,6 +246,70 @@ describe("FireworksHandler", () => {
)
})
it("should include reasoning_effort parameter for DeepSeek V3.2 when reasoning is enabled", async () => {
const testModelId: FireworksModelId = "accounts/fireworks/models/deepseek-v3p2"
const handlerWithModel = new FireworksHandler({
apiModelId: testModelId,
fireworksApiKey: "test-fireworks-api-key",
enableReasoningEffort: true,
reasoningEffort: "high",
})
mockCreate.mockImplementationOnce(() => {
return {
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}
})
const systemPrompt = "Test system prompt"
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Test message" }]
const messageGenerator = handlerWithModel.createMessage(systemPrompt, messages)
await messageGenerator.next()
// Check that reasoning_effort was included in the request
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: testModelId,
reasoning_effort: "high",
}),
undefined,
)
})
it("should not include reasoning_effort parameter when reasoning is disabled", async () => {
const testModelId: FireworksModelId = "accounts/fireworks/models/deepseek-v3p2"
const handlerWithModel = new FireworksHandler({
apiModelId: testModelId,
fireworksApiKey: "test-fireworks-api-key",
enableReasoningEffort: false,
})
mockCreate.mockImplementationOnce(() => {
return {
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}
})
const systemPrompt = "Test system prompt"
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Test message" }]
const messageGenerator = handlerWithModel.createMessage(systemPrompt, messages)
await messageGenerator.next()
// Check that reasoning_effort was NOT included in the request
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs.reasoning_effort).toBeUndefined()
})
it("should return GLM-4.5 model with correct configuration", () => {
const testModelId: FireworksModelId = "accounts/fireworks/models/glm-4p5"
const handlerWithModel = new FireworksHandler({

View file

@ -1,8 +1,16 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { type FireworksModelId, fireworksDefaultModelId, fireworksModels } from "@roo-code/types"
import type { ApiHandlerOptions } from "../../shared/api"
import { getModelParams } from "../transform/model-params"
import { getModelMaxOutputTokens } from "../../shared/api"
import { convertToOpenAiMessages } from "../transform/openai-format"
import type { ApiHandlerCreateMessageMetadata } from "../index"
import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
import { handleOpenAIError } from "./utils/openai-error-handler"
export class FireworksHandler extends BaseOpenAiCompatibleProvider<FireworksModelId> {
constructor(options: ApiHandlerOptions) {
@ -16,4 +24,57 @@ export class FireworksHandler extends BaseOpenAiCompatibleProvider<FireworksMode
defaultTemperature: 0.5,
})
}
override getModel() {
const id =
this.options.apiModelId && this.options.apiModelId in this.providerModels
? (this.options.apiModelId as FireworksModelId)
: this.defaultProviderModelId
const info = this.providerModels[id]
const params = getModelParams({ format: "openai", modelId: id, model: info, settings: this.options })
return { id, info, ...params }
}
protected override createStream(
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
metadata?: ApiHandlerCreateMessageMetadata,
requestOptions?: OpenAI.RequestOptions,
) {
const { id: model, info, reasoning } = this.getModel()
// Centralized cap: clamp to 20% of the context window (unless provider-specific exceptions apply)
const max_tokens =
getModelMaxOutputTokens({
modelId: model,
model: info,
settings: this.options,
format: "openai",
}) ?? undefined
const temperature = this.options.modelTemperature ?? this.defaultTemperature
const params: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = {
model,
max_tokens,
temperature,
messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
stream: true,
stream_options: { include_usage: true },
...(metadata?.tools && { tools: this.convertToolsForOpenAI(metadata.tools) }),
...(metadata?.tool_choice && { tool_choice: metadata.tool_choice }),
...(metadata?.toolProtocol === "native" && {
parallel_tool_calls: metadata.parallelToolCalls ?? false,
}),
// Use Fireworks-style reasoning_effort parameter instead of Anthropic-style "thinking"
...(reasoning && reasoning),
}
try {
return this.client.chat.completions.create(params, requestOptions)
} catch (error) {
throw handleOpenAIError(error, "Fireworks")
}
}
}