assistant-stream
Version:
Streaming utilities for AI assistants
83 lines (77 loc) • 2.28 kB
text/typescript
import type {
AssistantStreamChunk,
PartInit,
} from "../../AssistantStreamChunk";
/**
* For chunk types that are associated with a part,
* we require a non‑nullable meta field.
*/
export type AssistantMetaStreamChunk =
| (AssistantStreamChunk & {
type: "text-delta" | "part-finish";
meta: PartInit;
})
| (AssistantStreamChunk & {
type: "result" | "tool-call-args-text-finish" | "tool-call-approval";
meta: PartInit & { type: "tool-call" };
})
| (AssistantStreamChunk & {
type: Exclude<
AssistantStreamChunk["type"],
| "text-delta"
| "result"
| "tool-call-args-text-finish"
| "tool-call-approval"
| "part-finish"
>;
});
export class AssistantMetaTransformStream extends TransformStream<
AssistantStreamChunk,
AssistantMetaStreamChunk
> {
constructor() {
// We use an array to record parts as they are introduced.
const parts: PartInit[] = [];
super({
transform(chunk, controller) {
// For chunks that introduce a new part.
if (chunk.type === "part-start") {
if (chunk.path.length !== 0) {
controller.error(new Error("Nested parts are not supported"));
return;
}
parts.push(chunk.part);
controller.enqueue(chunk);
return;
}
// For chunks that expect an associated part.
if (
chunk.type === "text-delta" ||
chunk.type === "result" ||
chunk.type === "part-finish" ||
chunk.type === "tool-call-args-text-finish" ||
chunk.type === "tool-call-approval"
) {
if (chunk.path.length !== 1) {
controller.error(
new Error(`${chunk.type} chunks must have a path of length 1`),
);
return;
}
const idx = chunk.path[0]!;
if (idx < 0 || idx >= parts.length) {
controller.error(new Error(`Invalid path index: ${idx}`));
return;
}
const part = parts[idx]!;
controller.enqueue({
...chunk,
meta: part as any, // TODO
});
return;
}
controller.enqueue(chunk);
},
});
}
}