refactor: convert render module to StreamRenderer class
This commit is contained in:
parent
bcd9771cd5
commit
2ec00018bd
2 changed files with 71 additions and 69 deletions
9
index.ts
9
index.ts
|
|
@ -2,10 +2,8 @@ import os from "node:os";
|
|||
import { parseArgs } from "node:util";
|
||||
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
||||
import { type ModelMessage, streamText } from "ai";
|
||||
import { renderStream } from "./src/render";
|
||||
|
||||
const home = os.homedir();
|
||||
const convoFile = Bun.file(`${home}/.aicl_convo.json`);
|
||||
import StreamRenderer from "./src/render";
|
||||
|
||||
const { values: args, positionals } = parseArgs({
|
||||
args: Bun.argv,
|
||||
|
|
@ -21,6 +19,9 @@ const { values: args, positionals } = parseArgs({
|
|||
allowPositionals: true,
|
||||
});
|
||||
|
||||
const home = os.homedir();
|
||||
const convoFile = Bun.file(`${home}/.aicl_convo.json`);
|
||||
|
||||
async function main() {
|
||||
const provider = createOpenAICompatible({
|
||||
name: "llama.cpp",
|
||||
|
|
@ -48,7 +49,7 @@ async function main() {
|
|||
|
||||
const start = Date.now();
|
||||
|
||||
const streamRender = renderStream(fullStream);
|
||||
const streamRender = new StreamRenderer().renderStream(fullStream);
|
||||
|
||||
const [width] = process.stdout.getWindowSize();
|
||||
const glow = Bun.spawn({
|
||||
|
|
|
|||
|
|
@ -4,80 +4,81 @@ import type { AsyncIterableStream, TextStreamPart, ToolSet } from "ai";
|
|||
const home = os.homedir();
|
||||
const reasoningWriter = Bun.file(`${home}/.aicl_reasoning.md`).writer();
|
||||
|
||||
let lineCount = 0;
|
||||
let charsSinceNL = 0;
|
||||
export default class StreamRenderer {
|
||||
private lineCount = 0;
|
||||
private charsSinceNL = 0;
|
||||
private memOk = true;
|
||||
|
||||
function resetCounts() {
|
||||
lineCount = 0;
|
||||
charsSinceNL = 0;
|
||||
}
|
||||
constructor() {
|
||||
process.stdout.addListener("drain", () => {
|
||||
this.memOk = true;
|
||||
});
|
||||
}
|
||||
|
||||
let memOk = true;
|
||||
private write(text: string) {
|
||||
this.memOk = process.stdout.write(text);
|
||||
}
|
||||
|
||||
process.stdout.addListener("drain", () => {
|
||||
memOk = true;
|
||||
});
|
||||
private moveCursor(x: number, y: number) {
|
||||
this.memOk = process.stdout.moveCursor(x, y);
|
||||
}
|
||||
|
||||
function write(text: string) {
|
||||
memOk = process.stdout.write(text);
|
||||
}
|
||||
private clearScreenDown() {
|
||||
this.memOk = process.stdout.clearScreenDown();
|
||||
}
|
||||
|
||||
function moveCursor(x: number, y: number) {
|
||||
memOk = process.stdout.moveCursor(x, y);
|
||||
}
|
||||
private resetCounts() {
|
||||
this.lineCount = 0;
|
||||
this.charsSinceNL = 0;
|
||||
}
|
||||
|
||||
function clearScreenDown() {
|
||||
memOk = process.stdout.clearScreenDown();
|
||||
}
|
||||
private checkContainment(text: string) {
|
||||
const [columns, rows] = process.stdout.getWindowSize();
|
||||
this.charsSinceNL += text.length;
|
||||
if (text.includes("\n")) {
|
||||
this.lineCount += text.match(/\n/g)?.length ?? 1;
|
||||
this.charsSinceNL = text.length - text.lastIndexOf("\n") - 1;
|
||||
} else if (this.charsSinceNL >= columns) {
|
||||
this.lineCount++;
|
||||
this.charsSinceNL = this.charsSinceNL - columns;
|
||||
}
|
||||
|
||||
export async function renderStream(
|
||||
stream: AsyncIterableStream<TextStreamPart<ToolSet>>,
|
||||
) {
|
||||
resetCounts();
|
||||
if (this.lineCount >= rows / 2) {
|
||||
this.moveCursor(0, -this.lineCount);
|
||||
this.clearScreenDown();
|
||||
this.lineCount = 0;
|
||||
this.charsSinceNL = 0;
|
||||
}
|
||||
}
|
||||
|
||||
async renderStream(stream: AsyncIterableStream<TextStreamPart<ToolSet>>) {
|
||||
this.resetCounts();
|
||||
|
||||
for await (const part of stream) {
|
||||
if (part.type === "reasoning-start") {
|
||||
write("\u001b[2m\u001b[3m\n");
|
||||
this.write("\u001b[2m\u001b[3m\n");
|
||||
} else if (part.type === "reasoning-delta") {
|
||||
write(part.text);
|
||||
this.write(part.text);
|
||||
reasoningWriter.write(part.text);
|
||||
checkContainment(part.text);
|
||||
this.checkContainment(part.text);
|
||||
} else if (part.type === "reasoning-end") {
|
||||
write("\n\u001b[22m\u001b[23m");
|
||||
moveCursor(0, -lineCount - 2);
|
||||
clearScreenDown();
|
||||
resetCounts();
|
||||
this.write("\n\u001b[22m\u001b[23m");
|
||||
this.moveCursor(0, -this.lineCount - 2);
|
||||
this.clearScreenDown();
|
||||
this.resetCounts();
|
||||
reasoningWriter.end();
|
||||
} else if (part.type === "text-start") {
|
||||
write("\n");
|
||||
this.write("\n");
|
||||
} else if (part.type === "text-delta") {
|
||||
write(part.text);
|
||||
checkContainment(part.text);
|
||||
this.write(part.text);
|
||||
this.checkContainment(part.text);
|
||||
} else if (part.type === "text-end") {
|
||||
moveCursor(0, -lineCount - 1);
|
||||
clearScreenDown();
|
||||
resetCounts();
|
||||
this.moveCursor(0, -this.lineCount - 1);
|
||||
this.clearScreenDown();
|
||||
this.resetCounts();
|
||||
}
|
||||
|
||||
while (!memOk) await Bun.sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
function checkContainment(text: string) {
|
||||
const [columns, rows] = process.stdout.getWindowSize();
|
||||
charsSinceNL += text.length;
|
||||
if (text.includes("\n")) {
|
||||
lineCount += text.match(/\n/g)?.length ?? 1;
|
||||
charsSinceNL = text.length - text.lastIndexOf("\n") - 1;
|
||||
} else if (charsSinceNL >= columns) {
|
||||
lineCount++;
|
||||
charsSinceNL = charsSinceNL - columns;
|
||||
}
|
||||
|
||||
if (lineCount >= rows / 2) {
|
||||
moveCursor(0, -lineCount);
|
||||
clearScreenDown();
|
||||
lineCount = 0;
|
||||
charsSinceNL = 0;
|
||||
while (!this.memOk) await Bun.sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue