diff --git a/index.ts b/index.ts index 7d6f0b6..91a8ffd 100644 --- a/index.ts +++ b/index.ts @@ -2,7 +2,7 @@ import { parseArgs } from "node:util"; import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; import { streamText } from "ai"; -import { loadConfig, saveConfig } from "./src/config"; +import { initConfig, loadConfig } from "./src/config"; import { Conversation } from "./src/conversation"; import Renderer from "./src/render"; @@ -13,8 +13,8 @@ const { values: args, positionals } = parseArgs({ m: { type: "string" }, new: { type: "boolean" }, n: { type: "boolean" }, - save: { type: "boolean" }, - s: { type: "boolean" }, + config: { type: "boolean" }, + c: { type: "boolean" }, plain: { type: "boolean" }, plainOut: { type: "boolean" }, plainErr: { type: "boolean" }, @@ -24,26 +24,18 @@ const { values: args, positionals } = parseArgs({ }); async function main() { - const config = await loadConfig(); - const newConvo = args.new ?? args.n; const plainOut = args.plainOut ?? args.plain ?? !process.stdout.isTTY; const plainErr = args.plainErr ?? args.plain ?? !process.stderr.isTTY; const modelFlag = args.model ?? args.m; - const saveConfigFlag = args.save ?? args.s; + const configFlag = args.config ?? args.c; + + const config = await loadConfig(); const model = modelFlag ?? config.model; - if (!model) { - console.error( - "Please run 'lmc --model --save' to set a default model.", - ); - process.exit(1); - } - - if (saveConfigFlag) { - config.model = modelFlag ?? config.model; - await saveConfig(config); + if (configFlag) { + await initConfig(config); return; } @@ -52,7 +44,7 @@ async function main() { const provider = createOpenAICompatible({ name: "llama.cpp", apiKey: "local", - baseURL: "http://127.0.0.1:8080/v1", + baseURL: config.baseURL, includeUsage: true, // Include usage information in streaming responses }); diff --git a/src/config/index.ts b/src/config/index.ts index 8281bf7..40664c9 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,11 +1,55 @@ import os from "node:os"; +import { autocomplete, intro, isCancel, outro, text } from "@clack/prompts"; import chalk from "chalk"; const home = os.homedir(); const configFile = Bun.file(`${home}/.lmc_config.json`); export interface AppConfig { - model?: string; + baseURL: string; + model: string; +} + +export async function initConfig(old?: AppConfig): Promise { + intro(chalk.blue("Welcome to LMC. Let's make a new config!")); + const baseURL = await text({ + message: "What's your base URL:", + initialValue: old?.baseURL, + defaultValue: "http://127.0.0.1:8080/v1", + placeholder: "http://127.0.0.1:8080/v1", + }); + if (isCancel(baseURL)) process.exit(1); + + const models = await getModels(baseURL); + const model = await autocomplete({ + message: "Choose a default model:", + initialValue: old?.model, + placeholder: old?.model, + options: models, + }); + if (isCancel(model)) process.exit(1); + + const config = { baseURL, model } satisfies AppConfig; + + await configFile.write(JSON.stringify(config, null, " ")); + outro(chalk.green("Configuration saved!")); + + return config; +} + +type Models = { + data: { + created: number; + id: string; + object: "model"; + owned_by: string; + }[]; +}; + +async function getModels(url: string) { + const response = await fetch(`${url}/models`); + const models = (await response.json()) as Models; + return models.data.map((model) => ({ value: model.id })); } export async function loadConfig(): Promise { @@ -13,12 +57,7 @@ export async function loadConfig(): Promise { try { config = await configFile.json(); } catch { - config = {}; + config = await initConfig(); } return config; } - -export async function saveConfig(config: AppConfig): Promise { - await configFile.write(JSON.stringify(config, null, " ")); - console.log(chalk.green("Configuration saved!")); -} diff --git a/src/render/index.ts b/src/render/index.ts index 4956b7d..012458c 100644 --- a/src/render/index.ts +++ b/src/render/index.ts @@ -130,6 +130,9 @@ export default class Renderer { break; } case "text-start": { + if (!this.isPlain) { + spin.clear(); + } this.write("\n"); break; } diff --git a/src/render/markdown.ts b/src/render/markdown.ts index 258cdfe..d576ffd 100644 --- a/src/render/markdown.ts +++ b/src/render/markdown.ts @@ -112,5 +112,6 @@ export async function renderMarkdown( html: () => "", }); - return rendered.replace(/\n\n$/, "\n"); + const replaced = rendered.replace(/\n\n$/, "\n"); + return Bun.wrapAnsi(replaced, columns, { trim: false }); }