diff --git a/index.ts b/index.ts index 6a9fecd..c7fb1fe 100644 --- a/index.ts +++ b/index.ts @@ -2,9 +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 chalk from "chalk"; -import StreamRenderer from "./src/render"; +import Renderer from "./src/render"; const { values: args, positionals } = parseArgs({ args: Bun.argv, @@ -15,6 +14,8 @@ const { values: args, positionals } = parseArgs({ n: { type: "boolean" }, save: { type: "boolean" }, s: { type: "boolean" }, + plain: { type: "boolean" }, + p: { type: "boolean" }, }, strict: true, allowPositionals: true, @@ -26,6 +27,7 @@ const configFile = Bun.file(`${home}/.aicl_config.json`); async function main() { const newConvo = args.new ?? args.n; + const plain = args.plain ?? args.p ?? !process.stdout.isTTY; let saveConfig = args.save ?? args.s; let config: { model?: string }; @@ -47,6 +49,8 @@ async function main() { await configFile.write(JSON.stringify(config, null, " ")); } + const renderer = new Renderer(plain); + const provider = createOpenAICompatible({ name: "llama.cpp", apiKey: "local", @@ -78,28 +82,22 @@ async function main() { }); const start = Date.now(); - await new StreamRenderer().renderStream(fullStream); + await renderer.renderStream(fullStream); const stop = Date.now(); convo.messages.push({ role: "assistant", content: await text }); await convoFile.write(JSON.stringify(convo)); - const [width] = process.stdout.getWindowSize(); - await Bun.spawn({ - cmd: ["glow", "-w", `${width}`, "-"], - stdin: textStream, - stdout: "inherit", - }).exited; + if (!plain) { + const [width] = process.stdout.getWindowSize(); + await Bun.spawn({ + cmd: ["glow", "-w", `${width}`, "-"], + stdin: textStream, + stdout: "inherit", + }).exited; + } - const { inputTokens, outputTokens } = await totalUsage; - const context = (inputTokens ?? 0) / 1000; - const output = (outputTokens ?? 0) / 1000; - const time = (stop - start) / 1000; - process.stderr.write( - chalk.dim( - `Context: ${context.toFixed(1)}k, Output: ${output.toFixed(1)}k, Time: ${time.toFixed(2)}s\n`, - ), - ); + renderer.renderStats(await totalUsage, start, stop); } await main(); diff --git a/src/render/index.ts b/src/render/index.ts index 58b964e..b474955 100644 --- a/src/render/index.ts +++ b/src/render/index.ts @@ -1,19 +1,24 @@ import os from "node:os"; import type { WriteStream } from "node:tty"; -import type { AsyncIterableStream, TextStreamPart, ToolSet } from "ai"; +import type { + AsyncIterableStream, + LanguageModelUsage, + TextStreamPart, + ToolSet, +} from "ai"; import styles from "ansi-styles"; -import { supportsColorStderr } from "chalk"; +import { chalkStderr, supportsColorStderr } from "chalk"; const home = os.homedir(); const reasoningFile = Bun.file(`${home}/.aicl_reasoning.md`); const reasoningWriter = reasoningFile.writer(); -export default class StreamRenderer { +export default class Renderer { private lineCount = 0; private charsSinceNL = 0; private memOk = true; - constructor() { + constructor(private isPlain = false) { process.stdout.addListener("drain", () => { this.memOk = true; }); @@ -50,7 +55,7 @@ export default class StreamRenderer { this.charsSinceNL = this.charsSinceNL - columns; } - if (this.lineCount >= Math.floor(rows * 0.75)) { + if (this.lineCount >= rows - 4) { this.moveCursor(-this.lineCount, pipe); this.clearScreenDown(pipe); this.resetCounts(); @@ -96,13 +101,17 @@ export default class StreamRenderer { } case "text-delta": { this.write(part.text); - this.checkContainment(part.text); + if (!this.isPlain) this.checkContainment(part.text); break; } case "text-end": { - this.moveCursor(-this.lineCount - 1); - this.clearScreenDown(process.stderr); - this.resetCounts(); + if (!this.isPlain) { + this.moveCursor(-this.lineCount - 1); + this.clearScreenDown(process.stderr); + this.resetCounts(); + } else { + this.write("\n\n"); + } break; } } @@ -110,4 +119,13 @@ export default class StreamRenderer { while (!this.memOk) await Bun.sleep(10); } } + + renderStats(usage: LanguageModelUsage, start: number, stop: number) { + const { inputTokens, outputTokens } = usage; + const context = (inputTokens ?? 0) / 1000; + const output = (outputTokens ?? 0) / 1000; + const time = (stop - start) / 1000; + const template = `Context: ${context.toFixed(1)}k, Output: ${output.toFixed(1)}k, Time: ${time.toFixed(2)}s\n`; + process.stderr.write(chalkStderr.dim(template)); + } }