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