fix: edit_diagram streaming and JSON repair improvements (#271)

- Add shared editDiagramOriginalXmlRef between streaming preview and tool handler
  to avoid conflicts when applying operations (fixes "cell already exists" errors)
- Add JSON repair preprocessing to fix LLM-generated malformed JSON like `:=`
- Filter out tool calls with invalid/undefined inputs from interrupted streaming
- Remove perf console logs
This commit is contained in:
Dayuan Jiang
2025-12-15 21:28:31 +09:00
committed by GitHub
parent c527ce1520
commit cd76fa615e
5 changed files with 364 additions and 91 deletions

View File

@@ -743,8 +743,6 @@ function checkNestedMxCells(xml: string): string | null {
* @returns null if valid, error message string if invalid
*/
export function validateMxCellStructure(xml: string): string | null {
console.time("perf:validateMxCellStructure")
console.log(`perf:validateMxCellStructure XML size: ${xml.length} bytes`)
// Size check for performance
if (xml.length > MAX_XML_SIZE) {
console.warn(
@@ -754,18 +752,10 @@ export function validateMxCellStructure(xml: string): string | null {
// 0. First use DOM parser to catch syntax errors (most accurate)
try {
console.time("perf:validate-DOMParser")
const parser = new DOMParser()
const doc = parser.parseFromString(xml, "text/xml")
console.timeEnd("perf:validate-DOMParser")
const parseError = doc.querySelector("parsererror")
if (parseError) {
const actualError = parseError.textContent || "Unknown parse error"
console.log(
"[validateMxCellStructure] DOMParser error:",
actualError,
)
console.timeEnd("perf:validateMxCellStructure")
return `Invalid XML: The XML contains syntax errors (likely unescaped special characters like <, >, & in attribute values). Please escape special characters: use &lt; for <, &gt; for >, &amp; for &, &quot; for ". Regenerate the diagram with properly escaped values.`
}
@@ -774,7 +764,6 @@ export function validateMxCellStructure(xml: string): string | null {
for (const cell of allCells) {
if (cell.parentElement?.tagName === "mxCell") {
const id = cell.getAttribute("id") || "unknown"
console.timeEnd("perf:validateMxCellStructure")
return `Invalid XML: Found nested mxCell (id="${id}"). Cells should be siblings, not nested inside other mxCell elements.`
}
}
@@ -788,16 +777,12 @@ export function validateMxCellStructure(xml: string): string | null {
// 1. Check for CDATA wrapper (invalid at document root)
if (/^\s*<!\[CDATA\[/.test(xml)) {
console.timeEnd("perf:validateMxCellStructure")
return "Invalid XML: XML is wrapped in CDATA section - remove <![CDATA[ from start and ]]> from end"
}
// 2. Check for duplicate structural attributes
console.time("perf:checkDuplicateAttributes")
const dupAttrError = checkDuplicateAttributes(xml)
console.timeEnd("perf:checkDuplicateAttributes")
if (dupAttrError) {
console.timeEnd("perf:validateMxCellStructure")
return dupAttrError
}
@@ -807,33 +792,25 @@ export function validateMxCellStructure(xml: string): string | null {
while ((attrValMatch = attrValuePattern.exec(xml)) !== null) {
const value = attrValMatch[1]
if (/</.test(value) && !/&lt;/.test(value)) {
console.timeEnd("perf:validateMxCellStructure")
return "Invalid XML: Unescaped < character in attribute values. Replace < with &lt;"
}
}
// 4. Check for duplicate IDs
console.time("perf:checkDuplicateIds")
const dupIdError = checkDuplicateIds(xml)
console.timeEnd("perf:checkDuplicateIds")
if (dupIdError) {
console.timeEnd("perf:validateMxCellStructure")
return dupIdError
}
// 5. Check for tag mismatches
console.time("perf:checkTagMismatches")
const tagMismatchError = checkTagMismatches(xml)
console.timeEnd("perf:checkTagMismatches")
if (tagMismatchError) {
console.timeEnd("perf:validateMxCellStructure")
return tagMismatchError
}
// 6. Check invalid character references
const charRefError = checkCharacterReferences(xml)
if (charRefError) {
console.timeEnd("perf:validateMxCellStructure")
return charRefError
}
@@ -842,7 +819,6 @@ export function validateMxCellStructure(xml: string): string | null {
let commentMatch
while ((commentMatch = commentPattern.exec(xml)) !== null) {
if (/--/.test(commentMatch[1])) {
console.timeEnd("perf:validateMxCellStructure")
return "Invalid XML: Comment contains -- (double hyphen) which is not allowed"
}
}
@@ -850,24 +826,20 @@ export function validateMxCellStructure(xml: string): string | null {
// 8. Check for unescaped entity references and invalid entity names
const entityError = checkEntityReferences(xml)
if (entityError) {
console.timeEnd("perf:validateMxCellStructure")
return entityError
}
// 9. Check for empty id attributes on mxCell
if (/<mxCell[^>]*\sid\s*=\s*["']\s*["'][^>]*>/g.test(xml)) {
console.timeEnd("perf:validateMxCellStructure")
return "Invalid XML: Found mxCell element(s) with empty id attribute"
}
// 10. Check for nested mxCell tags
const nestedCellError = checkNestedMxCells(xml)
if (nestedCellError) {
console.timeEnd("perf:validateMxCellStructure")
return nestedCellError
}
console.timeEnd("perf:validateMxCellStructure")
return null
}