History

Fix OpenCode raw text delta assembly (#2526)

Kiri110K committed 6 months ago

Commit 271d65e1 parent aa8b9f22 files · +87 −17Browse files

Unified · Split

2 files
  1. apps/server/src/provider/Layers/OpenCodeAdapter.test.ts +85 −4
  2. apps/server/src/provider/Layers/OpenCodeAdapter.ts +2 −13
apps/server/src/provider/Layers/OpenCodeAdapter.test.ts+85−4
@@ -559,17 +559,98 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { · 558 unmodified lines
559559 }),
560560 );
561561
562− it.effect("deduplicates overlapping assistant text deltas after part updates", () =>
562+ it.effect("appends raw assistant text deltas and reconciles part update snapshots", () =>
563563 Effect.sync(() => {
564564 const firstUpdate = mergeOpenCodeAssistantText(undefined, "Hello");
565565 const overlapDelta = appendOpenCodeAssistantTextDelta(firstUpdate.latestText, "lo world");
566− const secondUpdate = mergeOpenCodeAssistantText(overlapDelta.nextText, "Hello world!");
566+ const secondUpdate = mergeOpenCodeAssistantText(overlapDelta.nextText, "Hellolo world");
567567
568568 assert.deepEqual(
569569 [firstUpdate.deltaToEmit, overlapDelta.deltaToEmit, secondUpdate.deltaToEmit],
570− ["Hello", " world", "!"],
570+ ["Hello", "lo world", ""],
571571 );
572− assert.equal(secondUpdate.latestText, "Hello world!");
572+ assert.equal(secondUpdate.latestText, "Hellolo world");
573+ }),
574+ );
575+
576+ it.effect("does not strip coincidental prefix overlap from OpenCode part deltas", () =>
577+ Effect.gen(function* () {
578+ const adapter = yield* OpenCodeAdapter;
579+ const threadId = asThreadId("thread-opencode-raw-delta");
580+ const part = {
581+ id: "part-raw-delta",
582+ sessionID: "http://127.0.0.1:9999/session",
583+ messageID: "msg-raw-delta",
584+ type: "text",
585+ text: "A B",
586+ time: { start: 1 },
587+ };
588+ runtimeMock.state.subscribedEvents = [
589+ {
590+ type: "message.updated",
591+ properties: {
592+ sessionID: "http://127.0.0.1:9999/session",
593+ info: {
594+ id: "msg-raw-delta",
595+ role: "assistant",
596+ },
597+ },
598+ },
599+ {
600+ type: "message.part.updated",
601+ properties: {
602+ sessionID: "http://127.0.0.1:9999/session",
603+ part,
604+ time: 1,
605+ },
606+ },
607+ {
608+ type: "message.part.delta",
609+ properties: {
610+ sessionID: "http://127.0.0.1:9999/session",
611+ messageID: "msg-raw-delta",
612+ partID: "part-raw-delta",
613+ field: "text",
614+ delta: "Bonus",
615+ },
616+ },
617+ {
618+ type: "message.part.updated",
619+ properties: {
620+ sessionID: "http://127.0.0.1:9999/session",
621+ part: {
622+ ...part,
623+ text: "A BBonus",
624+ time: { start: 1, end: 2 },
625+ },
626+ time: 2,
627+ },
628+ },
629+ ];
630+ const eventsFiber = yield* adapter.streamEvents.pipe(
631+ Stream.filter((event) => event.threadId === threadId),
632+ Stream.take(5),
633+ Stream.runCollect,
634+ Effect.forkChild,
635+ );
636+
637+ yield* adapter.startSession({
638+ provider: ProviderDriverKind.make("opencode"),
639+ threadId,
640+ runtimeMode: "full-access",
641+ });
642+
643+ const events = Array.from(yield* Fiber.join(eventsFiber).pipe(Effect.timeout("1 second")));
644+ const deltas = events.filter((event) => event.type === "content.delta");
645+ assert.deepEqual(
646+ deltas.map((event) => (event.type === "content.delta" ? event.payload.delta : "")),
647+ ["A B", "Bonus"],
648+ );
649+ assert.equal(events.at(-1)?.type, "item.completed");
650+ const completed = events.at(-1);
651+ if (completed?.type === "item.completed") {
652+ assert.equal(completed.payload.detail, "A BBonus");
653+ }
573654 }),
574655 );
575656
apps/server/src/provider/Layers/OpenCodeAdapter.ts+2−13
@@ -311,16 +311,6 @@ function commonPrefixLength(left: string, right: string): number { · 310 unmodified lines
311311 return index;
312312 }
313313
314−function suffixPrefixOverlap(text: string, delta: string): number {
315− const maxLength = Math.min(text.length, delta.length);
316− for (let length = maxLength; length > 0; length -= 1) {
317− if (text.endsWith(delta.slice(0, length))) {
318− return length;
319− }
320− }
321− return 0;
322−}
323−
324314 function resolveLatestAssistantText(previousText: string | undefined, nextText: string): string {
325315 if (previousText && previousText.length > nextText.length && previousText.startsWith(nextText)) {
326316 return previousText;
@@ -349,10 +339,9 @@ export function appendOpenCodeAssistantTextDelta( · 22 unmodified lines
349339 readonly nextText: string;
350340 readonly deltaToEmit: string;
351341 } {
352− const deltaToEmit = delta.slice(suffixPrefixOverlap(previousText, delta));
353342 return {
354− nextText: previousText + deltaToEmit,
355− deltaToEmit,
343+ nextText: previousText + delta,
344+ deltaToEmit: delta,
356345 };
357346 }
358347

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t