Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 85 additions & 4 deletions apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,17 +565,98 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
}),
);

it.effect("deduplicates overlapping assistant text deltas after part updates", () =>
it.effect("appends raw assistant text deltas and reconciles part update snapshots", () =>
Effect.sync(() => {
const firstUpdate = mergeOpenCodeAssistantText(undefined, "Hello");
const overlapDelta = appendOpenCodeAssistantTextDelta(firstUpdate.latestText, "lo world");
const secondUpdate = mergeOpenCodeAssistantText(overlapDelta.nextText, "Hello world!");
const secondUpdate = mergeOpenCodeAssistantText(overlapDelta.nextText, "Hellolo world");

assert.deepEqual(
[firstUpdate.deltaToEmit, overlapDelta.deltaToEmit, secondUpdate.deltaToEmit],
["Hello", " world", "!"],
["Hello", "lo world", ""],
);
assert.equal(secondUpdate.latestText, "Hello world!");
assert.equal(secondUpdate.latestText, "Hellolo world");
}),
);

it.effect("does not strip coincidental prefix overlap from OpenCode part deltas", () =>
Effect.gen(function* () {
const adapter = yield* OpenCodeAdapter;
const threadId = asThreadId("thread-opencode-raw-delta");
const part = {
id: "part-raw-delta",
sessionID: "http://127.0.0.1:9999/session",
messageID: "msg-raw-delta",
type: "text",
text: "A B",
time: { start: 1 },
};
runtimeMock.state.subscribedEvents = [
{
type: "message.updated",
properties: {
sessionID: "http://127.0.0.1:9999/session",
info: {
id: "msg-raw-delta",
role: "assistant",
},
},
},
{
type: "message.part.updated",
properties: {
sessionID: "http://127.0.0.1:9999/session",
part,
time: 1,
},
},
{
type: "message.part.delta",
properties: {
sessionID: "http://127.0.0.1:9999/session",
messageID: "msg-raw-delta",
partID: "part-raw-delta",
field: "text",
delta: "Bonus",
},
},
{
type: "message.part.updated",
properties: {
sessionID: "http://127.0.0.1:9999/session",
part: {
...part,
text: "A BBonus",
time: { start: 1, end: 2 },
},
time: 2,
},
},
];
const eventsFiber = yield* adapter.streamEvents.pipe(
Stream.filter((event) => event.threadId === threadId),
Stream.take(5),
Stream.runCollect,
Effect.forkChild,
);

yield* adapter.startSession({
provider: ProviderDriverKind.make("opencode"),
threadId,
runtimeMode: "full-access",
});

const events = Array.from(yield* Fiber.join(eventsFiber).pipe(Effect.timeout("1 second")));
const deltas = events.filter((event) => event.type === "content.delta");
assert.deepEqual(
deltas.map((event) => (event.type === "content.delta" ? event.payload.delta : "")),
["A B", "Bonus"],
);
assert.equal(events.at(-1)?.type, "item.completed");
const completed = events.at(-1);
if (completed?.type === "item.completed") {
assert.equal(completed.payload.detail, "A BBonus");
}
}),
);

Expand Down
15 changes: 2 additions & 13 deletions apps/server/src/provider/Layers/OpenCodeAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,16 +302,6 @@ function commonPrefixLength(left: string, right: string): number {
return index;
}

function suffixPrefixOverlap(text: string, delta: string): number {
const maxLength = Math.min(text.length, delta.length);
for (let length = maxLength; length > 0; length -= 1) {
if (text.endsWith(delta.slice(0, length))) {
return length;
}
}
return 0;
}

function resolveLatestAssistantText(previousText: string | undefined, nextText: string): string {
if (previousText && previousText.length > nextText.length && previousText.startsWith(nextText)) {
return previousText;
Expand Down Expand Up @@ -340,10 +330,9 @@ export function appendOpenCodeAssistantTextDelta(
readonly nextText: string;
readonly deltaToEmit: string;
} {
const deltaToEmit = delta.slice(suffixPrefixOverlap(previousText, delta));
return {
nextText: previousText + deltaToEmit,
deltaToEmit,
nextText: previousText + delta,
deltaToEmit: delta,
};
}

Expand Down
Loading