mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
fix: conditionally include encrypted reasoning only for models that support it
- gpt-5-chat-latest does not support encrypted reasoning - Only include reasoning.encrypted_content for models with supportsReasoningEffort !== false - Add tests to verify gpt-5-chat-latest works without encrypted reasoning - Fixes #9225
This commit is contained in:
parent
4e6cdad052
commit
2cd9f1311b
2 changed files with 165 additions and 3 deletions
|
|
@ -1316,5 +1316,159 @@ describe("GPT-5 streaming event coverage (additional)", () => {
|
|||
expect(bodyStr).not.toContain('"verbosity"')
|
||||
})
|
||||
})
|
||||
|
||||
describe("gpt-5-chat-latest encrypted reasoning fix", () => {
|
||||
it("should NOT include encrypted reasoning for gpt-5-chat-latest", async () => {
|
||||
const mockFetch = vitest.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
body: new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode(
|
||||
'data: {"type":"response.output_item.added","item":{"type":"text","text":"Response without encrypted reasoning"}}\n\n',
|
||||
),
|
||||
)
|
||||
controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"))
|
||||
controller.close()
|
||||
},
|
||||
}),
|
||||
})
|
||||
global.fetch = mockFetch as any
|
||||
|
||||
// Mock SDK to fail so it uses fetch
|
||||
mockResponsesCreate.mockRejectedValue(new Error("SDK not available"))
|
||||
|
||||
const handler = new OpenAiNativeHandler({
|
||||
apiModelId: "gpt-5-chat-latest",
|
||||
openAiNativeApiKey: "test-api-key",
|
||||
})
|
||||
|
||||
const systemPrompt = "You are a helpful assistant."
|
||||
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello!" }]
|
||||
|
||||
const stream = handler.createMessage(systemPrompt, messages)
|
||||
const chunks: any[] = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
// Verify the request was made
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
"https://api.openai.com/v1/responses",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
headers: expect.objectContaining({
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer test-api-key",
|
||||
Accept: "text/event-stream",
|
||||
}),
|
||||
body: expect.any(String),
|
||||
}),
|
||||
)
|
||||
|
||||
const requestBody = JSON.parse(mockFetch.mock.calls[0][1].body)
|
||||
|
||||
// CRITICAL: gpt-5-chat-latest should NOT have encrypted reasoning
|
||||
expect(requestBody.include).toBeUndefined()
|
||||
expect(requestBody).not.toHaveProperty("include")
|
||||
|
||||
// Should still have other expected properties
|
||||
expect(requestBody.model).toBe("gpt-5-chat-latest")
|
||||
expect(requestBody.instructions).toBe("You are a helpful assistant.")
|
||||
expect(requestBody.stream).toBe(true)
|
||||
expect(requestBody.store).toBe(false)
|
||||
|
||||
// gpt-5-chat-latest supports verbosity
|
||||
expect(requestBody.text?.verbosity).toBe("medium")
|
||||
|
||||
// gpt-5-chat-latest does NOT support reasoning effort
|
||||
expect(requestBody.reasoning).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should still include encrypted reasoning for models that support it (gpt-5-2025-08-07)", async () => {
|
||||
const mockFetch = vitest.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
body: new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode(
|
||||
'data: {"type":"response.output_item.added","item":{"type":"text","text":"Response with encrypted reasoning"}}\n\n',
|
||||
),
|
||||
)
|
||||
controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"))
|
||||
controller.close()
|
||||
},
|
||||
}),
|
||||
})
|
||||
global.fetch = mockFetch as any
|
||||
|
||||
// Mock SDK to fail so it uses fetch
|
||||
mockResponsesCreate.mockRejectedValue(new Error("SDK not available"))
|
||||
|
||||
const handler = new OpenAiNativeHandler({
|
||||
apiModelId: "gpt-5-2025-08-07",
|
||||
openAiNativeApiKey: "test-api-key",
|
||||
})
|
||||
|
||||
const systemPrompt = "You are a helpful assistant."
|
||||
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello!" }]
|
||||
|
||||
const stream = handler.createMessage(systemPrompt, messages)
|
||||
const chunks: any[] = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
const requestBody = JSON.parse(mockFetch.mock.calls[0][1].body)
|
||||
|
||||
// gpt-5-2025-08-07 SHOULD have encrypted reasoning
|
||||
expect(requestBody.include).toEqual(["reasoning.encrypted_content"])
|
||||
|
||||
// Should also have reasoning effort
|
||||
expect(requestBody.reasoning?.effort).toBe("medium")
|
||||
})
|
||||
|
||||
it("should NOT include encrypted reasoning for gpt-5-chat-latest in completePrompt", async () => {
|
||||
// Clear the mock before this test
|
||||
mockResponsesCreate.mockClear()
|
||||
|
||||
const handler = new OpenAiNativeHandler({
|
||||
apiModelId: "gpt-5-chat-latest",
|
||||
openAiNativeApiKey: "test-api-key",
|
||||
})
|
||||
|
||||
// Mock the responses.create method
|
||||
mockResponsesCreate.mockResolvedValue({
|
||||
output: [
|
||||
{
|
||||
type: "message",
|
||||
content: [
|
||||
{
|
||||
type: "output_text",
|
||||
text: "Completion without encrypted reasoning",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const result = await handler.completePrompt("Test prompt")
|
||||
|
||||
expect(result).toBe("Completion without encrypted reasoning")
|
||||
expect(mockResponsesCreate).toHaveBeenCalledTimes(1)
|
||||
expect(mockResponsesCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: "gpt-5-chat-latest",
|
||||
stream: false,
|
||||
store: false,
|
||||
}),
|
||||
)
|
||||
|
||||
// Check that include is NOT in the request
|
||||
const callArg = mockResponsesCreate.mock.calls[0][0]
|
||||
expect(callArg.include).toBeUndefined()
|
||||
expect(callArg).not.toHaveProperty("include")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -206,17 +206,21 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
|
|||
const requestedTier = (this.options.openAiNativeServiceTier as ServiceTier | undefined) || undefined
|
||||
const allowedTierNames = new Set(model.info.tiers?.map((t) => t.name).filter(Boolean) || [])
|
||||
|
||||
// Check if the model supports reasoning (and thus encrypted reasoning)
|
||||
const supportsReasoning = model.info.supportsReasoningEffort !== false || reasoningEffort !== undefined
|
||||
|
||||
const body: Gpt5RequestBody = {
|
||||
model: model.id,
|
||||
input: formattedInput,
|
||||
stream: true,
|
||||
// Always use stateless operation with encrypted reasoning
|
||||
// Always use stateless operation
|
||||
store: false,
|
||||
// Always include instructions (system prompt) for Responses API.
|
||||
// Unlike Chat Completions, system/developer roles in input have no special semantics here.
|
||||
// The official way to set system behavior is the top-level `instructions` field.
|
||||
instructions: systemPrompt,
|
||||
include: ["reasoning.encrypted_content"],
|
||||
// Only include encrypted reasoning for models that support reasoning
|
||||
...(supportsReasoning ? { include: ["reasoning.encrypted_content"] } : {}),
|
||||
...(reasoningEffort
|
||||
? {
|
||||
reasoning: {
|
||||
|
|
@ -1087,6 +1091,9 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
|
|||
// Resolve reasoning effort for models that support it
|
||||
const reasoningEffort = this.getReasoningEffort(model)
|
||||
|
||||
// Check if the model supports reasoning (and thus encrypted reasoning)
|
||||
const supportsReasoning = model.info.supportsReasoningEffort !== false || reasoningEffort !== undefined
|
||||
|
||||
// Build request body for Responses API
|
||||
const requestBody: any = {
|
||||
model: model.id,
|
||||
|
|
@ -1098,7 +1105,8 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
|
|||
],
|
||||
stream: false, // Non-streaming for completePrompt
|
||||
store: false, // Don't store prompt completions
|
||||
include: ["reasoning.encrypted_content"],
|
||||
// Only include encrypted reasoning for models that support reasoning
|
||||
...(supportsReasoning ? { include: ["reasoning.encrypted_content"] } : {}),
|
||||
}
|
||||
|
||||
// Include service tier if selected and supported
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue