mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
fix: improve HTML entity unescaping for Gemini and other models
- Add HTML entity unescaping to applyDiffTool for non-Claude models - Enhance unescapeHtmlEntities function to handle more entity types - Add support for alternative encodings like ', /, \, ` - Add comprehensive tests for new entity types - Improve comments to clarify the purpose of unescaping Fixes #7890
This commit is contained in:
parent
8fee3127ff
commit
6477cad443
4 changed files with 71 additions and 3 deletions
|
|
@ -25,6 +25,8 @@ export async function applyDiffToolLegacy(
|
|||
const relPath: string | undefined = block.params.path
|
||||
let diffContent: string | undefined = block.params.diff
|
||||
|
||||
// Unescape HTML entities for non-Claude models (e.g., Gemini, DeepSeek, Llama)
|
||||
// These models may return content with escaped characters that need to be unescaped
|
||||
if (diffContent && !cline.api.getModel().id.includes("claude")) {
|
||||
diffContent = unescapeHtmlEntities(diffContent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,9 +73,11 @@ export async function writeToFileTool(
|
|||
cline.diffViewProvider.editType = fileExists ? "modify" : "create"
|
||||
}
|
||||
|
||||
// pre-processing newContent for cases where weaker models might add artifacts like markdown codeblock markers (deepseek/llama) or extra escape characters (gemini)
|
||||
// Pre-processing newContent for cases where models might add artifacts
|
||||
// Some models (DeepSeek/Llama) add markdown codeblock markers
|
||||
// Others (Gemini) return content with HTML-escaped characters
|
||||
if (newContent.startsWith("```")) {
|
||||
// cline handles cases where it includes language specifiers like ```python ```js
|
||||
// Handle cases where it includes language specifiers like ```python ```js
|
||||
newContent = newContent.split("\n").slice(1).join("\n")
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +85,8 @@ export async function writeToFileTool(
|
|||
newContent = newContent.split("\n").slice(0, -1).join("\n")
|
||||
}
|
||||
|
||||
// Unescape HTML entities for non-Claude models (e.g., Gemini, DeepSeek, Llama)
|
||||
// These models may return content with escaped characters that need to be unescaped
|
||||
if (!cline.api.getModel().id.includes("claude")) {
|
||||
newContent = unescapeHtmlEntities(newContent)
|
||||
}
|
||||
|
|
|
|||
57
src/utils/__tests__/text-normalization-extended.spec.ts
Normal file
57
src/utils/__tests__/text-normalization-extended.spec.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { describe, it, expect } from "vitest"
|
||||
import { unescapeHtmlEntities } from "../text-normalization"
|
||||
|
||||
describe("Extended HTML entity unescaping", () => {
|
||||
describe("unescapeHtmlEntities", () => {
|
||||
it("unescapes alternative apostrophe encoding", () => {
|
||||
const input = "It's working"
|
||||
const expected = "It's working"
|
||||
expect(unescapeHtmlEntities(input)).toBe(expected)
|
||||
})
|
||||
|
||||
it("unescapes forward slash", () => {
|
||||
const input = "path/to/file"
|
||||
const expected = "path/to/file"
|
||||
expect(unescapeHtmlEntities(input)).toBe(expected)
|
||||
})
|
||||
|
||||
it("unescapes backslash", () => {
|
||||
const input = "C:\Users\file"
|
||||
const expected = "C:\\Users\\file"
|
||||
expect(unescapeHtmlEntities(input)).toBe(expected)
|
||||
})
|
||||
|
||||
it("unescapes backtick", () => {
|
||||
const input = "`code`"
|
||||
const expected = "`code`"
|
||||
expect(unescapeHtmlEntities(input)).toBe(expected)
|
||||
})
|
||||
|
||||
it("unescapes non-breaking space", () => {
|
||||
const input = "Hello World"
|
||||
const expected = "Hello World"
|
||||
expect(unescapeHtmlEntities(input)).toBe(expected)
|
||||
})
|
||||
|
||||
it("handles complex mixed content with all entity types", () => {
|
||||
const input =
|
||||
"<div class="test">It's a test/path\file with `code` & more</div>"
|
||||
const expected = '<div class="test">It\'s a test/path\\file with `code` & more</div>'
|
||||
expect(unescapeHtmlEntities(input)).toBe(expected)
|
||||
})
|
||||
|
||||
it("handles Gemini-style escaped markdown content", () => {
|
||||
const input =
|
||||
"```python\n<search>\ndef old_function():\n return 'old'\n</search>\n<replace>\ndef new_function():\n return 'new'\n</replace>\n```"
|
||||
const expected =
|
||||
"```python\n<search>\ndef old_function():\n return 'old'\n</search>\n<replace>\ndef new_function():\n return 'new'\n</replace>\n```"
|
||||
expect(unescapeHtmlEntities(input)).toBe(expected)
|
||||
})
|
||||
|
||||
it("correctly orders ampersand unescaping to avoid double-unescaping", () => {
|
||||
const input = "&lt;&gt;&amp;"
|
||||
const expected = "<>&"
|
||||
expect(unescapeHtmlEntities(input)).toBe(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -91,5 +91,10 @@ export function unescapeHtmlEntities(text: string): string {
|
|||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/'/g, "'")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/'/g, "'") // Alternative apostrophe encoding
|
||||
.replace(///g, "/") // Forward slash
|
||||
.replace(/\/g, "\\") // Backslash
|
||||
.replace(/`/g, "`") // Backtick
|
||||
.replace(/ /g, " ") // Non-breaking space
|
||||
.replace(/&/g, "&") // Must be last to avoid double-unescaping
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue