fix: Robust patch generation and safety timeouts

- Condensed Canvas Isolation Architecture to streamline AI focus
- Added robust JSON extraction (brace-finding) to handle conversational AI output
- Implemented 90s safety timeout for patch generation to prevent infinite 'Generating' state
- Throttled status updates to improve UI performance during streaming
This commit is contained in:
Gemini AI
2025-12-20 15:03:13 +04:00
Unverified
parent 0c76288d01
commit 7785feed1d

View File

@@ -24,34 +24,11 @@ export const setActiveModel = (model: string): void => {
}; };
export const CANVAS_ISOLATION_ARCHITECTURE = ` export const CANVAS_ISOLATION_ARCHITECTURE = `
# CANVAS ISOLATION ARCHITECTURE ### CANVAS ISOLATION ARCHITECTURE
1. **Base Canvas**: Existing code is immutable. Avoid touching unrelated blocks.
## 1. LAYERED CANVAS SYSTEM 2. **Isolation Chamber**: Extract ONLY specific sections mentioned.
- **Base Canvas**: The complete, immutable original codebase 3. **Precision Merge**: Merge back using surgical anchors. 100% preservation.
- **Change Canvas**: Isolated layer containing ONLY the requested modifications 4. **Boundary Mapping**: Identify exact coordinates. No cascading changes.
- **Merge Canvas**: The precise intersection where changes are applied
- **Validation Canvas**: Verification layer ensuring no unintended modifications
## 2. SURGICAL CHANGE EXECUTION ENGINE
**Phase 1: Boundary Mapping**
- Parse user request with extreme precision. Create coordinates of exact change locations.
- Establish quarantine zones around unrelated code.
**Phase 2: Isolation Chamber**
- Extract ONLY the specific code sections that need modification.
- Maintain isolation. Apply atomic operations that cannot affect surrounding code.
**Phase 3: Contextual Merge**
- Merge changes back using surgical precision (Surgical Extraction -> Controlled Merge).
- Verify zero impact on unrelated code.
## 3. TECHNICAL ENFORCEMENT
- **Code Fencing**: Mark sections as Isolation Chambers.
- **Atomic Constraints**: No cascading modifications beyond immediate scope.
- **Preservation Rate**: 100% of unrelated code must remain unchanged.
Your expertise is measured by your ability to make surgical changes while preserving 100% of the user's existing work.
`; `;
// --- GEMINI 3 PRO / VIBE CODING TEMPLATE --- // --- GEMINI 3 PRO / VIBE CODING TEMPLATE ---
@@ -180,7 +157,7 @@ OUTPUT ONLY JSON:
const onChunk = (c: string) => buf += c; const onChunk = (c: string) => buf += c;
const onComplete = (c: string) => { const onComplete = (c: string) => {
electron.removeChatListeners(); electron.removeChatListeners();
const final = (c && c.length > buf.length ? c : buf).replace(/```json/g, '').replace(/```/g, '').trim(); const final = (c && c.length > buf.length ? c : buf).replace(/```json/gi, '').replace(/```/g, '').trim();
try { try {
const res = JSON.parse(final); const res = JSON.parse(final);
resolve(res); resolve(res);
@@ -725,15 +702,30 @@ If the request requires a FULL REDESIGN (changing >50% of layout or colors), set
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let fullResponse = ''; let fullResponse = '';
const timeout = setTimeout(() => {
cleanup();
reject(new Error("Patch Generation Timeout (90s). The model took too long to respond."));
}, 90000);
const onChunkHandler = (c: string) => { const onChunkHandler = (c: string) => {
fullResponse += c; fullResponse += c;
if (onChunk) onChunk(JSON.stringify({ status: "Generating Patch...", size: fullResponse.length })); // Throttled status updates
if (onChunk && fullResponse.length % 50 === 0) {
onChunk(JSON.stringify({ status: "Generating Patch...", size: fullResponse.length }));
}
}; };
const onCompleteHandler = async (c: string) => { const onCompleteHandler = async (c: string) => {
clearTimeout(timeout);
cleanup(); cleanup();
const rawJson = (c.length > fullResponse.length ? c : fullResponse).trim(); let rawJson = (c.length > fullResponse.length ? c : fullResponse).trim();
// Robust extraction: find first { and last }
const firstBrace = rawJson.indexOf('{');
const lastBrace = rawJson.lastIndexOf('}');
if (firstBrace !== -1 && lastBrace > firstBrace) {
rawJson = rawJson.substring(firstBrace, lastBrace + 1);
}
// Parse JSON // Parse JSON
let patchPlan: PatchPlan | null = null; let patchPlan: PatchPlan | null = null;
@@ -741,13 +733,11 @@ If the request requires a FULL REDESIGN (changing >50% of layout or colors), set
const jsonStr = rawJson.replace(/```json/gi, '').replace(/```/g, '').trim(); const jsonStr = rawJson.replace(/```json/gi, '').replace(/```/g, '').trim();
patchPlan = JSON.parse(jsonStr); patchPlan = JSON.parse(jsonStr);
} catch (e) { } catch (e) {
console.error("Failed to parse patch JSON", e); console.error("Failed to parse patch JSON", e, rawJson.substring(0, 300));
// Retry or Fail?
if (retryCount < 1) { if (retryCount < 1) {
console.log("Retrying patch generation...");
return resolve(applyPlanToExistingHtml(plan, currentHtml, onChunk, retryCount + 1, projectId)); return resolve(applyPlanToExistingHtml(plan, currentHtml, onChunk, retryCount + 1, projectId));
} }
reject(new Error("Failed to parse patch plan")); reject(new Error("Failed to parse patch plan: " + (e as Error).message));
return; return;
} }