mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-08 22:21:23 +00:00
fix: sanitize reasoning_details IDs to remove invalid characters (#9839)
This commit is contained in:
parent
63f6fecb1a
commit
ae655c5d29
4 changed files with 85 additions and 8 deletions
|
|
@ -29,6 +29,7 @@ import { BaseProvider } from "./base-provider"
|
|||
import type { ApiHandlerCreateMessageMetadata, SingleCompletionHandler } from "../index"
|
||||
import { handleOpenAIError } from "./utils/openai-error-handler"
|
||||
import { generateImageWithProvider, ImageGenerationResult } from "./utils/image-generation"
|
||||
import { sanitizeReasoningDetailId } from "./utils/sanitize-reasoning-id"
|
||||
|
||||
// Add custom interface for OpenRouter params.
|
||||
type OpenRouterChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & {
|
||||
|
|
@ -286,18 +287,18 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
|
|||
if (detail.data !== undefined) {
|
||||
existing.data = (existing.data || "") + detail.data
|
||||
}
|
||||
// Update other fields if provided
|
||||
if (detail.id !== undefined) existing.id = detail.id
|
||||
// Update other fields if provided - sanitize ID to remove invalid characters
|
||||
if (detail.id !== undefined) existing.id = sanitizeReasoningDetailId(detail.id)
|
||||
if (detail.format !== undefined) existing.format = detail.format
|
||||
if (detail.signature !== undefined) existing.signature = detail.signature
|
||||
} else {
|
||||
// Start new reasoning detail accumulation
|
||||
// Start new reasoning detail accumulation - sanitize ID to remove invalid characters
|
||||
reasoningDetailsAccumulator.set(key, {
|
||||
type: detail.type,
|
||||
text: detail.text,
|
||||
summary: detail.summary,
|
||||
data: detail.data,
|
||||
id: detail.id,
|
||||
id: sanitizeReasoningDetailId(detail.id),
|
||||
format: detail.format,
|
||||
signature: detail.signature,
|
||||
index,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import { MODEL_DEFAULTS } from "../providers/fetchers/roo"
|
|||
import { handleOpenAIError } from "./utils/openai-error-handler"
|
||||
import { generateImageWithProvider, generateImageWithImagesApi, ImageGenerationResult } from "./utils/image-generation"
|
||||
import { t } from "../../i18n"
|
||||
import { sanitizeReasoningDetailId } from "./utils/sanitize-reasoning-id"
|
||||
|
||||
// Extend OpenAI's CompletionUsage to include Roo specific fields
|
||||
interface RooUsage extends OpenAI.CompletionUsage {
|
||||
|
|
@ -193,18 +194,18 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<string> {
|
|||
if (detail.data !== undefined) {
|
||||
existing.data = (existing.data || "") + detail.data
|
||||
}
|
||||
// Update other fields if provided
|
||||
if (detail.id !== undefined) existing.id = detail.id
|
||||
// Update other fields if provided - sanitize ID to remove invalid characters
|
||||
if (detail.id !== undefined) existing.id = sanitizeReasoningDetailId(detail.id)
|
||||
if (detail.format !== undefined) existing.format = detail.format
|
||||
if (detail.signature !== undefined) existing.signature = detail.signature
|
||||
} else {
|
||||
// Start new reasoning detail accumulation
|
||||
// Start new reasoning detail accumulation - sanitize ID to remove invalid characters
|
||||
reasoningDetailsAccumulator.set(key, {
|
||||
type: detail.type,
|
||||
text: detail.text,
|
||||
summary: detail.summary,
|
||||
data: detail.data,
|
||||
id: detail.id,
|
||||
id: sanitizeReasoningDetailId(detail.id),
|
||||
format: detail.format,
|
||||
signature: detail.signature,
|
||||
index,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
import { sanitizeReasoningDetailId } from "../sanitize-reasoning-id"
|
||||
|
||||
describe("sanitizeReasoningDetailId", () => {
|
||||
it("should return null for null input", () => {
|
||||
expect(sanitizeReasoningDetailId(null)).toBeNull()
|
||||
})
|
||||
|
||||
it("should return undefined for undefined input", () => {
|
||||
expect(sanitizeReasoningDetailId(undefined)).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should return empty string for empty string input", () => {
|
||||
expect(sanitizeReasoningDetailId("")).toBe("")
|
||||
})
|
||||
|
||||
it("should not modify IDs with only valid characters", () => {
|
||||
expect(sanitizeReasoningDetailId("abc123")).toBe("abc123")
|
||||
expect(sanitizeReasoningDetailId("test_id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test-id")).toBe("test-id")
|
||||
expect(sanitizeReasoningDetailId("ABC_123-test")).toBe("ABC_123-test")
|
||||
})
|
||||
|
||||
it("should replace colons with underscores", () => {
|
||||
expect(sanitizeReasoningDetailId("rs_033ca40017d1ad93016931b1d2bf7481a2969fd5c1835cb1d3:4")).toBe(
|
||||
"rs_033ca40017d1ad93016931b1d2bf7481a2969fd5c1835cb1d3_4",
|
||||
)
|
||||
})
|
||||
|
||||
it("should replace multiple invalid characters", () => {
|
||||
expect(sanitizeReasoningDetailId("test:1:2:3")).toBe("test_1_2_3")
|
||||
})
|
||||
|
||||
it("should replace other special characters with underscores", () => {
|
||||
expect(sanitizeReasoningDetailId("test@id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test.id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test#id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test$id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test%id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test^id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test&id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test*id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test+id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test=id")).toBe("test_id")
|
||||
expect(sanitizeReasoningDetailId("test id")).toBe("test_id")
|
||||
})
|
||||
|
||||
it("should handle mixed valid and invalid characters", () => {
|
||||
expect(sanitizeReasoningDetailId("rs_abc:1@2#3")).toBe("rs_abc_1_2_3")
|
||||
})
|
||||
|
||||
it("should handle IDs starting with valid characters followed by invalid ones", () => {
|
||||
expect(sanitizeReasoningDetailId("valid_start:invalid")).toBe("valid_start_invalid")
|
||||
})
|
||||
|
||||
it("should handle consecutive invalid characters", () => {
|
||||
expect(sanitizeReasoningDetailId("test::id")).toBe("test__id")
|
||||
expect(sanitizeReasoningDetailId("test:::id")).toBe("test___id")
|
||||
})
|
||||
})
|
||||
16
src/api/providers/utils/sanitize-reasoning-id.ts
Normal file
16
src/api/providers/utils/sanitize-reasoning-id.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Sanitizes reasoning detail IDs to only contain allowed characters.
|
||||
* The OpenAI Responses API only allows IDs containing letters, numbers, underscores, or dashes.
|
||||
* This function replaces any invalid characters (like colons from IDs like "rs_xxx:4") with underscores.
|
||||
*
|
||||
* @param id - The original ID that may contain invalid characters
|
||||
* @returns The sanitized ID with only allowed characters, or undefined if input is undefined/null
|
||||
*/
|
||||
export function sanitizeReasoningDetailId(id: string | null | undefined): string | null | undefined {
|
||||
if (id === null || id === undefined) {
|
||||
return id
|
||||
}
|
||||
|
||||
// Replace any character that is not a letter, number, underscore, or dash with an underscore
|
||||
return id.replace(/[^a-zA-Z0-9_-]/g, "_")
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue