UNPKG

assistant-stream

Version:

Streaming utilities for AI assistants

213 lines (192 loc) • 8.68 kB
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", }); }); });