assistant-stream
Version:
Streaming utilities for AI assistants
97 lines (96 loc) • 3.02 kB
JavaScript
import { assertSafePathSegment } from "./changeTree.js";
// src/core/gorp/GorpStreamAccumulator.ts
var GorpStreamAccumulator = class {
_state;
_strict;
_logged = /* @__PURE__ */ new Set();
_warnedClamp = false;
constructor(initialValue = null, options = {}) {
this._state = initialValue;
this._strict = options.strict ?? true;
}
get state() {
return this._state;
}
logOnce(key, log) {
if (this._logged.has(key) || this._logged.size >= 20) return;
this._logged.add(key);
log();
}
append(ops) {
this._state = ops.reduce((state, op) => {
if (this._strict) return this.apply(state, op);
try {
return this.apply(state, op);
} catch (error) {
this.logOnce(`skip:${String(error)}`, () => console.error(`Skipped unappliable gorp operation: ${String(error)}`, op));
return state;
}
}, this._state);
}
apply(state, op) {
const type = op.type;
switch (type) {
case "set": return this.updatePath(state, op.path, () => op.value);
case "append-text": return this.updatePath(state, op.path, (current) => {
if (typeof current !== "string") throw new Error(`Expected string at path [${op.path.join(", ")}]`);
return current + op.value;
});
default: throw new Error(`Invalid operation type: ${type}`);
}
}
updatePath(state, path, updater) {
if (path.length === 0) return updater(state);
const frames = [];
let current = state;
for (let depth = 0; depth < path.length; depth++) {
current ??= {};
if (typeof current !== "object") throw new Error(`Invalid path: [${path.slice(depth).join(", ")}]`);
const key = path[depth];
assertSafePathSegment(key);
if (Array.isArray(current)) {
let index = Number(key);
if (!Number.isInteger(index) || String(index) !== String(key)) throw new Error(`Expected array index at [${path.slice(depth).join(", ")}]`);
if (index < 0) throw new Error(`Insert array index out of bounds`);
if (index > current.length) {
if (this._strict) throw new Error(`Insert array index out of bounds`);
if (!this._warnedClamp) {
this._warnedClamp = true;
console.warn(`Clamped out-of-bounds gorp array index ${index} to ${current.length}`);
}
index = current.length;
}
frames.push({
kind: "array",
state: current,
key: index
});
current = Object.hasOwn(current, index) ? current[index] : void 0;
} else {
const object = current;
frames.push({
kind: "object",
state: object,
key
});
current = Object.hasOwn(object, key) ? object[key] : void 0;
}
}
let updated = updater(current);
for (let index = frames.length - 1; index >= 0; index--) {
const frame = frames[index];
if (frame.kind === "array") {
const next = [...frame.state];
next[frame.key] = updated;
updated = next;
} else {
const next = { ...frame.state };
next[frame.key] = updated;
updated = next;
}
}
return updated;
}
};
export { GorpStreamAccumulator };
//# sourceMappingURL=GorpStreamAccumulator.js.map