Skip to content

Commit 459f2af

Browse files
committed
fix patch retry with updated html
1 parent 0546ce4 commit 459f2af

3 files changed

Lines changed: 43 additions & 4 deletions

File tree

apps/backend/src/services/generate/prompts.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,24 @@ export const buildSystemPrompt = (deps?: PackageInfo[]): string =>
104104
export const buildCorrectivePrompt = (
105105
error: GenerationError,
106106
successfulPatches: readonly Patch[] = [],
107+
currentHtml?: string,
107108
): string => {
108109
const applied =
109110
successfulPatches.length > 0
110111
? `\nAPPLIED: ${JSON.stringify(successfulPatches)}\nContinue from here.`
111112
: "";
112113

114+
const pageState = currentHtml
115+
? `\nCURRENT PAGE STATE:\n${currentHtml}\n`
116+
: "";
117+
113118
if (error._tag === "JsonParseError") {
114-
return `JSON ERROR: ${error.message}
119+
return `${pageState}JSON ERROR: ${error.message}
115120
Bad: ${error.line.slice(0, 100)}
116121
Fix: valid JSONL, one JSON/line, single quotes in HTML attrs${applied}`;
117122
}
118123

119-
return `PATCH ERROR "${error.patch.selector}": ${error.reason}
124+
return `${pageState}PATCH ERROR "${error.patch.selector}": ${error.reason}
120125
Fix: selector must exist, use #id only${applied}`;
121126
};
122127

apps/backend/src/services/generate/service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { streamText, type TextStreamPart } from "ai";
33
import type { LanguageModelConfig } from "@cuttlekit/common/server";
44
import { MemoryService, type MemorySearchResult } from "../memory/index.js";
55
import { accumulateLinesWithFlush } from "../../stream/utils.js";
6-
import { PatchValidator, renderCETree, type Patch, type ValidationContext } from "../vdom/index.js";
6+
import { PatchValidator, renderCETree, getCompactHtmlFromCtx, type Patch, type ValidationContext } from "../vdom/index.js";
77
import { ModelRegistry } from "../model-registry.js";
88
import {
99
PatchSchema,
@@ -305,6 +305,7 @@ export class GenerateService extends Effect.Service<GenerateService>()(
305305
Effect.gen(function* () {
306306
const successfulPatches = yield* Ref.get(patchesRef);
307307
yield* Ref.set(patchesRef, []); // Reset for next attempt
308+
const compactHtml = yield* getCompactHtmlFromCtx(validationCtx);
308309
yield* Effect.log(
309310
`[Attempt ${attempt}] ${genError._tag}, retrying...`,
310311
{
@@ -323,6 +324,7 @@ export class GenerateService extends Effect.Service<GenerateService>()(
323324
content: buildCorrectivePrompt(
324325
genError,
325326
successfulPatches,
327+
compactHtml,
326328
),
327329
},
328330
],

apps/backend/src/services/vdom/patch-validator.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Effect, Data, pipe } from "effect";
2-
import { Window } from "happy-dom";
2+
import { Window, HTMLElement as HappyHTMLElement } from "happy-dom";
33
import { applyPatch, type Patch } from "@cuttlekit/common/client";
44
import {
55
makeCEShell,
@@ -203,3 +203,35 @@ export class PatchValidator extends Effect.Service<PatchValidator>()(
203203
}),
204204
},
205205
) {}
206+
207+
/**
208+
* Extract compact HTML from a ValidationContext.
209+
* Clones the DOM and strips CE template content, keeping only data-children content.
210+
*/
211+
export const getCompactHtmlFromCtx = (ctx: ValidationContext) =>
212+
Effect.gen(function* () {
213+
if (ctx.registry.size === 0) return ctx.window.document.body.innerHTML;
214+
215+
// Clone to avoid mutating real VDOM
216+
const clone = yield* Effect.sync(() =>
217+
ctx.window.document.body.cloneNode(true) as unknown as HappyHTMLElement
218+
);
219+
220+
// Strip rendered CE template content, keeping only data-children content
221+
yield* pipe(
222+
[...ctx.registry.keys()],
223+
Effect.forEach((tag) =>
224+
pipe(
225+
[...clone.querySelectorAll(tag)],
226+
Effect.forEach((el) =>
227+
Effect.sync(() => {
228+
const childrenContainer = el.querySelector("[data-children]");
229+
el.innerHTML = childrenContainer ? childrenContainer.innerHTML : "";
230+
}),
231+
),
232+
),
233+
),
234+
);
235+
236+
return clone.innerHTML;
237+
});

0 commit comments

Comments
 (0)