assistant-stream
Version:
Streaming utilities for AI assistants
213 lines (192 loc) • 8.68 kB
text/typescript
import { describe, expect, it } from "vitest";
import { GorpStreamDeltaTracker } from "./GorpStreamDeltaTracker";
import { createGorpStream } from "./createGorpStream";
import type { ReadonlyJSONObject } from "../../utils";
describe("GorpStreamDeltaTracker frames", () => {
it("reports ops of the current frame in getChangedKeys", () => {
const tracker = new GorpStreamDeltaTracker({ items: {} });
tracker.append([
{ type: "set", path: ["items", "a"], value: { name: "alpha" } },
]);
expect(tracker.getChangedKeys(["items"])).toEqual(["a"]);
expect((tracker.state as ReadonlyJSONObject).items).toEqual({
a: { name: "alpha" },
});
});
it("opens a separate frame per append", () => {
const tracker = new GorpStreamDeltaTracker({ items: {} });
tracker.append([
{ type: "set", path: ["items", "a"], value: { name: "alpha" } },
]);
tracker.append([
{ type: "set", path: ["items", "b"], value: { name: "beta" } },
]);
expect(tracker.getChangedKeys(["items"])).toEqual(["b"]);
expect(tracker.isChangedAt(["items", "a"])).toBe(false);
});
it("reports an empty change set before the first append and after an empty append", () => {
const tracker = new GorpStreamDeltaTracker({ count: 0 });
expect(tracker.isChangedAt([])).toBe(false);
expect(tracker.isChangedAt(["count"])).toBe(false);
expect(tracker.getChangedKeys([])).toEqual([]);
tracker.append([{ type: "set", path: ["count"], value: 1 }]);
expect(tracker.isChangedAt([])).toBe(true);
tracker.append([]);
expect(tracker.isChangedAt([])).toBe(false);
expect(tracker.isChangedAt(["count"])).toBe(false);
expect(tracker.getChangedKeys([])).toEqual([]);
});
});
describe("GorpStreamDeltaTracker isChangedAt", () => {
it("returns truthy for any path inside a marked subtree", () => {
const tracker = new GorpStreamDeltaTracker();
tracker.append([{ type: "set", path: [], value: { items: {} } }]);
expect(tracker.isChangedAt([])).toBe(true);
expect(tracker.isChangedAt(["items"])).toBe(true);
expect(tracker.isChangedAt(["items", "anything", "deep"])).toBe(true);
});
it("returns true for ancestors of a changed path and false for siblings", () => {
const tracker = new GorpStreamDeltaTracker({ a: { b: "x" }, c: "y" });
tracker.append([{ type: "set", path: ["a", "b"], value: "z" }]);
expect(tracker.isChangedAt([])).toBe(true);
expect(tracker.isChangedAt(["a"])).toBe(true);
expect(tracker.isChangedAt(["a", "b"])).toBe(true);
expect(tracker.isChangedAt(["c"])).toBe(false);
});
it("collapses a subtree to fully-changed when an ancestor is set in the same frame", () => {
const tracker = new GorpStreamDeltaTracker({ a: { b: 1, c: 2 } });
tracker.append([
{ type: "set", path: ["a", "b"], value: 10 },
{ type: "set", path: ["a"], value: { d: 3 } },
]);
expect(tracker.isChangedAt(["a", "anything", "deep"])).toBe(true);
expect(tracker.getChangedKeys(["a"])).toEqual(["d", "c", "b"]);
});
it("keeps a subtree fully-changed when a descendant is set after its ancestor in the same frame", () => {
const tracker = new GorpStreamDeltaTracker({ a: { b: 1, c: 2 } });
tracker.append([
{ type: "set", path: ["a"], value: { d: 3 } },
{ type: "set", path: ["a", "d"], value: 4 },
]);
expect(tracker.isChangedAt(["a", "anything", "deep"])).toBe(true);
expect(tracker.getChangedKeys(["a"])).toEqual(["d", "c", "b"]);
});
it("reports a leaf set twice in one frame as changed once", () => {
const tracker = new GorpStreamDeltaTracker({ a: { b: 1 } });
tracker.append([
{ type: "set", path: ["a", "b"], value: 2 },
{ type: "set", path: ["a", "b"], value: 3 },
]);
expect(tracker.getChangedKeys(["a"])).toEqual(["b"]);
expect((tracker.state as ReadonlyJSONObject).a).toEqual({ b: 3 });
});
it("marks append-text targets as changed", () => {
const tracker = new GorpStreamDeltaTracker({ message: "Hello" });
tracker.append([{ type: "append-text", path: ["message"], value: "!" }]);
expect(tracker.isChangedAt(["message"])).toBe(true);
expect(tracker.getChangedKeys([])).toEqual(["message"]);
expect((tracker.state as ReadonlyJSONObject).message).toBe("Hello!");
});
});
describe("GorpStreamDeltaTracker getChangedKeys", () => {
it("diffs keys against the previous frame when a subtree is fully replaced", () => {
const tracker = new GorpStreamDeltaTracker({ count: 0, items: {} });
tracker.append([
{ type: "set", path: ["items", "a"], value: { name: "alpha" } },
]);
tracker.append([
{ type: "set", path: ["items", "b"], value: { name: "beta" } },
]);
tracker.append([
{
type: "set",
path: [],
value: {
count: 99,
items: { b: { name: "beta-new" }, c: { name: "gamma" } },
},
},
]);
expect(tracker.getChangedKeys(["items"])).toEqual(["b", "c", "a"]);
});
it("returns interior node keys for a partially changed subtree", () => {
const tracker = new GorpStreamDeltaTracker({ items: { a: 1, b: 2 } });
tracker.append([
{ type: "set", path: ["items", "a"], value: 10 },
{ type: "set", path: ["items", "c"], value: 3 },
]);
expect(tracker.getChangedKeys(["items"])).toEqual(["a", "c"]);
expect(tracker.getChangedKeys([])).toEqual(["items"]);
expect(tracker.getChangedKeys(["other"])).toEqual([]);
});
it("diffs keys when the previous value at a replaced path was not an object", () => {
const tracker = new GorpStreamDeltaTracker({ value: "initial" });
tracker.append([{ type: "set", path: ["value"], value: { nested: true } }]);
expect(tracker.getChangedKeys(["value"])).toEqual(["nested"]);
});
});
describe("GorpStreamDeltaTracker inherited object members", () => {
it("does not treat inherited members as changes on a fresh tracker", () => {
const tracker = new GorpStreamDeltaTracker({ items: {} });
expect(tracker.isChangedAt(["toString"])).toBe(false);
expect(tracker.isChangedAt(["hasOwnProperty"])).toBe(false);
expect(tracker.getChangedKeys(["toString"])).toEqual([]);
});
it("does not treat inherited members as changes on a populated tree", () => {
const tracker = new GorpStreamDeltaTracker({ items: {} });
tracker.append([{ type: "set", path: ["items", "a"], value: 1 }]);
expect(tracker.isChangedAt(["toString"])).toBe(false);
expect(tracker.isChangedAt(["items", "toString"])).toBe(false);
expect(tracker.isChangedAt(["items", "valueOf", "deep"])).toBe(false);
expect(tracker.getChangedKeys([])).toEqual(["items"]);
expect(tracker.getChangedKeys(["items"])).toEqual(["a"]);
});
});
describe("GorpStreamDeltaTracker failed append", () => {
it("leaves state and the change frame untouched when an operation throws", () => {
const tracker = new GorpStreamDeltaTracker({ count: 5, message: "hi" });
tracker.append([{ type: "set", path: ["message"], value: "hello" }]);
const stateBefore = tracker.state;
expect(() =>
tracker.append([
{ type: "set", path: ["other"], value: 1 },
{ type: "append-text", path: ["count"], value: "!" },
]),
).toThrow(/Expected string/);
expect(tracker.state).toBe(stateBefore);
expect(tracker.isChangedAt(["message"])).toBe(true);
expect(tracker.isChangedAt(["other"])).toBe(false);
expect(tracker.isChangedAt(["count"])).toBe(false);
expect(tracker.getChangedKeys([])).toEqual(["message"]);
});
});
describe("GorpStreamDeltaTracker streamed accumulation", () => {
it("tracks one frame per streamed chunk", async () => {
const stream = createGorpStream({
execute: (controller) => {
controller.enqueue([
{ type: "set", path: ["user"], value: { name: "Initial" } },
]);
controller.enqueue([
{ type: "set", path: ["user", "name"], value: "Updated" },
{ type: "set", path: ["user", "email"], value: "user@example.com" },
]);
controller.enqueue([
{ type: "set", path: ["status"], value: "complete" },
]);
},
});
const tracker = new GorpStreamDeltaTracker();
const frames: string[][] = [];
for await (const chunk of stream) {
tracker.append(chunk.operations);
frames.push(tracker.getChangedKeys(["user"]));
expect(tracker.state).toEqual(chunk.snapshot);
}
expect(frames).toEqual([["name"], ["name", "email"], []]);
expect(tracker.state).toEqual({
user: { name: "Updated", email: "user@example.com" },
status: "complete",
});
});
});