llmist 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -18,7 +18,9 @@ import {
18
18
  init_exceptions,
19
19
  init_logger,
20
20
  init_messages,
21
- init_registry
21
+ init_model_shortcuts,
22
+ init_registry,
23
+ resolveModel
22
24
  } from "./chunk-J3NCIWMY.js";
23
25
 
24
26
  // src/cli/constants.ts
@@ -63,7 +65,7 @@ import { Command, InvalidArgumentError as InvalidArgumentError3 } from "commande
63
65
  // package.json
64
66
  var package_default = {
65
67
  name: "llmist",
66
- version: "0.1.4",
68
+ version: "0.1.6",
67
69
  description: "Universal TypeScript LLM client with streaming-first agent framework. Works with any model - no structured outputs or native tool calling required. Implements its own flexible grammar for function calling.",
68
70
  type: "module",
69
71
  main: "dist/index.cjs",
@@ -169,11 +171,10 @@ init_builder();
169
171
  init_registry();
170
172
  init_constants();
171
173
  import { createInterface } from "node:readline/promises";
172
- import chalk3 from "chalk";
174
+ import chalk2 from "chalk";
173
175
  import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
174
176
 
175
177
  // src/cli/builtin-gadgets.ts
176
- import chalk from "chalk";
177
178
  import { z } from "zod";
178
179
  init_exceptions();
179
180
  var askUser = createGadget({
@@ -195,17 +196,17 @@ var tellUser = createGadget({
195
196
  type: z.enum(["info", "success", "warning", "error"]).default("info").describe("Message type: info, success, warning, or error")
196
197
  }),
197
198
  execute: ({ message, done, type }) => {
198
- const formatters = {
199
- info: (msg) => chalk.blue(`\u2139\uFE0F ${msg}`),
200
- success: (msg) => chalk.green(`\u2705 ${msg}`),
201
- warning: (msg) => chalk.yellow(`\u26A0\uFE0F ${msg}`),
202
- error: (msg) => chalk.red(`\u274C ${msg}`)
199
+ const prefixes = {
200
+ info: "\u2139\uFE0F ",
201
+ success: "\u2705 ",
202
+ warning: "\u26A0\uFE0F ",
203
+ error: "\u274C "
203
204
  };
204
- const formatted = formatters[type](message);
205
+ const plainResult = prefixes[type] + message;
205
206
  if (done) {
206
- throw new BreakLoopException(formatted);
207
+ throw new BreakLoopException(plainResult);
207
208
  }
208
- return formatted;
209
+ return plainResult;
209
210
  }
210
211
  });
211
212
  var builtinGadgets = [askUser, tellUser];
@@ -308,7 +309,7 @@ async function loadGadgets(specifiers, cwd, importer = (specifier) => import(spe
308
309
 
309
310
  // src/cli/utils.ts
310
311
  init_constants();
311
- import chalk2 from "chalk";
312
+ import chalk from "chalk";
312
313
  import { InvalidArgumentError } from "commander";
313
314
  function createNumericParser({
314
315
  label,
@@ -366,9 +367,10 @@ function isInteractive(stream) {
366
367
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
367
368
  var SPINNER_DELAY_MS = 500;
368
369
  var StreamProgress = class {
369
- constructor(target, isTTY) {
370
+ constructor(target, isTTY, modelRegistry) {
370
371
  this.target = target;
371
372
  this.isTTY = isTTY;
373
+ this.modelRegistry = modelRegistry;
372
374
  }
373
375
  // Animation state
374
376
  frameIndex = 0;
@@ -389,6 +391,7 @@ var StreamProgress = class {
389
391
  // Cumulative stats (cumulative mode)
390
392
  totalStartTime = Date.now();
391
393
  totalTokens = 0;
394
+ totalCost = 0;
392
395
  iterations = 0;
393
396
  /**
394
397
  * Starts a new LLM call. Switches to streaming mode.
@@ -415,6 +418,20 @@ var StreamProgress = class {
415
418
  this.iterations++;
416
419
  if (usage) {
417
420
  this.totalTokens += usage.totalTokens;
421
+ if (this.modelRegistry && this.model) {
422
+ try {
423
+ const modelName = this.model.includes(":") ? this.model.split(":")[1] : this.model;
424
+ const cost = this.modelRegistry.estimateCost(
425
+ modelName,
426
+ usage.inputTokens,
427
+ usage.outputTokens
428
+ );
429
+ if (cost) {
430
+ this.totalCost += cost.totalCost;
431
+ }
432
+ } catch {
433
+ }
434
+ }
418
435
  }
419
436
  this.pause();
420
437
  this.mode = "cumulative";
@@ -478,33 +495,39 @@ var StreamProgress = class {
478
495
  const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
479
496
  const parts = [];
480
497
  if (this.model) {
481
- parts.push(chalk2.cyan(this.model));
498
+ parts.push(chalk.cyan(this.model));
482
499
  }
483
500
  if (this.callInputTokens > 0) {
484
501
  const prefix = this.callInputTokensEstimated ? "~" : "";
485
- parts.push(chalk2.dim("out:") + chalk2.yellow(` ${prefix}${this.callInputTokens}`));
502
+ parts.push(chalk.dim("out:") + chalk.yellow(` ${prefix}${this.callInputTokens}`));
486
503
  }
487
504
  if (this.isStreaming || outTokens > 0) {
488
505
  const prefix = this.callOutputTokensEstimated ? "~" : "";
489
- parts.push(chalk2.dim("in:") + chalk2.green(` ${prefix}${outTokens}`));
506
+ parts.push(chalk.dim("in:") + chalk.green(` ${prefix}${outTokens}`));
507
+ }
508
+ if (this.totalCost > 0) {
509
+ parts.push(chalk.dim("cost:") + chalk.cyan(` $${this.formatCost(this.totalCost)}`));
490
510
  }
491
- parts.push(chalk2.dim(`${elapsed}s`));
492
- this.target.write(`\r${chalk2.cyan(spinner)} ${parts.join(chalk2.dim(" | "))}`);
511
+ parts.push(chalk.dim(`${elapsed}s`));
512
+ this.target.write(`\r${chalk.cyan(spinner)} ${parts.join(chalk.dim(" | "))}`);
493
513
  }
494
514
  renderCumulativeMode(spinner) {
495
515
  const elapsed = ((Date.now() - this.totalStartTime) / 1e3).toFixed(1);
496
516
  const parts = [];
497
517
  if (this.model) {
498
- parts.push(chalk2.cyan(this.model));
518
+ parts.push(chalk.cyan(this.model));
499
519
  }
500
520
  if (this.totalTokens > 0) {
501
- parts.push(chalk2.dim("total:") + chalk2.magenta(` ${this.totalTokens}`));
521
+ parts.push(chalk.dim("total:") + chalk.magenta(` ${this.totalTokens}`));
502
522
  }
503
523
  if (this.iterations > 0) {
504
- parts.push(chalk2.dim("iter:") + chalk2.blue(` ${this.iterations}`));
524
+ parts.push(chalk.dim("iter:") + chalk.blue(` ${this.iterations}`));
505
525
  }
506
- parts.push(chalk2.dim(`${elapsed}s`));
507
- this.target.write(`\r${chalk2.cyan(spinner)} ${parts.join(chalk2.dim(" | "))}`);
526
+ if (this.totalCost > 0) {
527
+ parts.push(chalk.dim("cost:") + chalk.cyan(` $${this.formatCost(this.totalCost)}`));
528
+ }
529
+ parts.push(chalk.dim(`${elapsed}s`));
530
+ this.target.write(`\r${chalk.cyan(spinner)} ${parts.join(chalk.dim(" | "))}`);
508
531
  }
509
532
  /**
510
533
  * Pauses the progress indicator and clears the line.
@@ -532,6 +555,12 @@ var StreamProgress = class {
532
555
  complete() {
533
556
  this.pause();
534
557
  }
558
+ /**
559
+ * Returns the total accumulated cost across all calls.
560
+ */
561
+ getTotalCost() {
562
+ return this.totalCost;
563
+ }
535
564
  /**
536
565
  * Returns a formatted prompt string with stats (like bash PS1).
537
566
  * Shows current call stats during streaming, cumulative stats otherwise.
@@ -546,25 +575,28 @@ var StreamProgress = class {
546
575
  if (this.callInputTokens > 0) {
547
576
  const prefix = this.callInputTokensEstimated ? "~" : "";
548
577
  parts.push(
549
- chalk2.dim("out:") + chalk2.yellow(` ${prefix}${this.formatTokens(this.callInputTokens)}`)
578
+ chalk.dim("out:") + chalk.yellow(` ${prefix}${this.formatTokens(this.callInputTokens)}`)
550
579
  );
551
580
  }
552
581
  if (outTokens > 0) {
553
582
  const prefix = outEstimated ? "~" : "";
554
- parts.push(chalk2.dim("in:") + chalk2.green(` ${prefix}${this.formatTokens(outTokens)}`));
583
+ parts.push(chalk.dim("in:") + chalk.green(` ${prefix}${this.formatTokens(outTokens)}`));
555
584
  }
556
- parts.push(chalk2.dim(`${elapsed}s`));
585
+ parts.push(chalk.dim(`${elapsed}s`));
557
586
  } else {
558
587
  const elapsed = Math.round((Date.now() - this.totalStartTime) / 1e3);
559
588
  if (this.totalTokens > 0) {
560
- parts.push(chalk2.magenta(this.formatTokens(this.totalTokens)));
589
+ parts.push(chalk.magenta(this.formatTokens(this.totalTokens)));
561
590
  }
562
591
  if (this.iterations > 0) {
563
- parts.push(chalk2.blue(`i${this.iterations}`));
592
+ parts.push(chalk.blue(`i${this.iterations}`));
593
+ }
594
+ if (this.totalCost > 0) {
595
+ parts.push(chalk.cyan(`$${this.formatCost(this.totalCost)}`));
564
596
  }
565
- parts.push(chalk2.dim(`${elapsed}s`));
597
+ parts.push(chalk.dim(`${elapsed}s`));
566
598
  }
567
- return `${parts.join(chalk2.dim(" \u2502 "))} ${chalk2.green(">")} `;
599
+ return `${parts.join(chalk.dim(" \u2502 "))} ${chalk.green(">")} `;
568
600
  }
569
601
  /**
570
602
  * Formats token count compactly (3625 -> "3.6k").
@@ -572,6 +604,21 @@ var StreamProgress = class {
572
604
  formatTokens(tokens) {
573
605
  return tokens >= 1e3 ? `${(tokens / 1e3).toFixed(1)}k` : `${tokens}`;
574
606
  }
607
+ /**
608
+ * Formats cost compactly (0.0001234 -> "0.00012", 0.1234 -> "0.12", 1.234 -> "1.23").
609
+ */
610
+ formatCost(cost) {
611
+ if (cost < 1e-3) {
612
+ return cost.toFixed(5);
613
+ }
614
+ if (cost < 0.01) {
615
+ return cost.toFixed(4);
616
+ }
617
+ if (cost < 1) {
618
+ return cost.toFixed(3);
619
+ }
620
+ return cost.toFixed(2);
621
+ }
575
622
  };
576
623
  async function readStream(stream) {
577
624
  const chunks = [];
@@ -603,29 +650,42 @@ async function resolvePrompt(promptArg, env) {
603
650
  function renderSummary(metadata) {
604
651
  const parts = [];
605
652
  if (metadata.iterations !== void 0) {
606
- parts.push(chalk2.dim(`iterations: ${metadata.iterations}`));
653
+ parts.push(chalk.dim(`iterations: ${metadata.iterations}`));
607
654
  }
608
655
  if (metadata.finishReason) {
609
- parts.push(chalk2.dim(`finish: ${metadata.finishReason}`));
656
+ parts.push(chalk.dim(`finish: ${metadata.finishReason}`));
610
657
  }
611
658
  if (metadata.usage) {
612
659
  const { inputTokens, outputTokens, totalTokens } = metadata.usage;
613
660
  parts.push(
614
- chalk2.dim(`tokens: `) + chalk2.cyan(`${totalTokens}`) + chalk2.dim(` (in: ${inputTokens}, out: ${outputTokens})`)
661
+ chalk.dim(`tokens: `) + chalk.cyan(`${totalTokens}`) + chalk.dim(` (in: ${inputTokens}, out: ${outputTokens})`)
615
662
  );
616
663
  }
664
+ if (metadata.cost !== void 0 && metadata.cost > 0) {
665
+ let formattedCost;
666
+ if (metadata.cost < 1e-3) {
667
+ formattedCost = metadata.cost.toFixed(5);
668
+ } else if (metadata.cost < 0.01) {
669
+ formattedCost = metadata.cost.toFixed(4);
670
+ } else if (metadata.cost < 1) {
671
+ formattedCost = metadata.cost.toFixed(3);
672
+ } else {
673
+ formattedCost = metadata.cost.toFixed(2);
674
+ }
675
+ parts.push(chalk.dim(`cost: `) + chalk.cyan(`$${formattedCost}`));
676
+ }
617
677
  if (parts.length === 0) {
618
678
  return null;
619
679
  }
620
- return `${chalk2.dim("\u2500".repeat(40))}
621
- ${parts.join(chalk2.dim(" \u2502 "))}`;
680
+ return `${chalk.dim("\u2500".repeat(40))}
681
+ ${parts.join(chalk.dim(" \u2502 "))}`;
622
682
  }
623
683
  async function executeAction(action, env) {
624
684
  try {
625
685
  await action();
626
686
  } catch (error) {
627
687
  const message = error instanceof Error ? error.message : String(error);
628
- env.stderr.write(`${chalk2.red.bold("Error:")} ${message}
688
+ env.stderr.write(`${chalk.red.bold("Error:")} ${message}
629
689
  `);
630
690
  env.setExitCode(1);
631
691
  }
@@ -669,17 +729,18 @@ ${statsPrompt}` : statsPrompt;
669
729
  };
670
730
  }
671
731
  function formatGadgetSummary(result) {
672
- const gadgetLabel = chalk3.magenta.bold(result.gadgetName);
673
- const timeLabel = chalk3.dim(`${Math.round(result.executionTimeMs)}ms`);
732
+ const gadgetLabel = chalk2.magenta.bold(result.gadgetName);
733
+ const timeLabel = chalk2.dim(`${Math.round(result.executionTimeMs)}ms`);
674
734
  if (result.error) {
675
- return `${chalk3.red("\u2717")} ${gadgetLabel} ${chalk3.red("error:")} ${result.error} ${timeLabel}`;
735
+ return `${chalk2.red("\u2717")} ${gadgetLabel} ${chalk2.red("error:")} ${result.error} ${timeLabel}`;
676
736
  }
677
737
  if (result.breaksLoop) {
678
- return `${chalk3.yellow("\u23F9")} ${gadgetLabel} ${chalk3.yellow("finished:")} ${result.result} ${timeLabel}`;
738
+ return `${chalk2.yellow("\u23F9")} ${gadgetLabel} ${chalk2.yellow("finished:")} ${result.result} ${timeLabel}`;
679
739
  }
680
740
  const maxLen = 80;
681
- const resultText = result.result ? result.result.length > maxLen ? `${result.result.slice(0, maxLen)}...` : result.result : "";
682
- return `${chalk3.green("\u2713")} ${gadgetLabel} ${chalk3.dim("\u2192")} ${resultText} ${timeLabel}`;
741
+ const shouldTruncate = result.gadgetName !== "TellUser";
742
+ const resultText = result.result ? shouldTruncate && result.result.length > maxLen ? `${result.result.slice(0, maxLen)}...` : result.result : "";
743
+ return `${chalk2.green("\u2713")} ${gadgetLabel} ${chalk2.dim("\u2192")} ${resultText} ${timeLabel}`;
683
744
  }
684
745
  async function handleAgentCommand(promptArg, options, env) {
685
746
  const prompt = await resolvePrompt(promptArg, env);
@@ -699,7 +760,7 @@ async function handleAgentCommand(promptArg, options, env) {
699
760
  }
700
761
  const printer = new StreamPrinter(env.stdout);
701
762
  const stderrTTY = env.stderr.isTTY === true;
702
- const progress = new StreamProgress(env.stderr, stderrTTY);
763
+ const progress = new StreamProgress(env.stderr, stderrTTY, client.modelRegistry);
703
764
  let finishReason;
704
765
  let usage;
705
766
  let iterations = 0;
@@ -756,16 +817,25 @@ async function handleAgentCommand(promptArg, options, env) {
756
817
  printer.write(event.content);
757
818
  } else if (event.type === "gadget_result") {
758
819
  progress.pause();
759
- env.stderr.write(`${formatGadgetSummary(event.result)}
820
+ if (stderrTTY) {
821
+ env.stderr.write(`${formatGadgetSummary(event.result)}
760
822
  `);
823
+ }
761
824
  }
762
825
  }
763
826
  progress.complete();
764
827
  printer.ensureNewline();
765
- const summary = renderSummary({ finishReason, usage, iterations });
766
- if (summary) {
767
- env.stderr.write(`${summary}
828
+ if (stderrTTY) {
829
+ const summary = renderSummary({
830
+ finishReason,
831
+ usage,
832
+ iterations,
833
+ cost: progress.getTotalCost()
834
+ });
835
+ if (summary) {
836
+ env.stderr.write(`${summary}
768
837
  `);
838
+ }
769
839
  }
770
840
  }
771
841
  function registerAgentCommand(program, env) {
@@ -794,26 +864,28 @@ function registerAgentCommand(program, env) {
794
864
 
795
865
  // src/cli/complete-command.ts
796
866
  init_messages();
867
+ init_model_shortcuts();
797
868
  init_constants();
798
869
  async function handleCompleteCommand(promptArg, options, env) {
799
870
  const prompt = await resolvePrompt(promptArg, env);
800
871
  const client = env.createClient();
872
+ const model = resolveModel(options.model);
801
873
  const builder = new LLMMessageBuilder();
802
874
  if (options.system) {
803
875
  builder.addSystem(options.system);
804
876
  }
805
877
  builder.addUser(prompt);
806
878
  const stream = client.stream({
807
- model: options.model,
879
+ model,
808
880
  messages: builder.build(),
809
881
  temperature: options.temperature,
810
882
  maxTokens: options.maxTokens
811
883
  });
812
884
  const printer = new StreamPrinter(env.stdout);
813
885
  const stderrTTY = env.stderr.isTTY === true;
814
- const progress = new StreamProgress(env.stderr, stderrTTY);
886
+ const progress = new StreamProgress(env.stderr, stderrTTY, client.modelRegistry);
815
887
  const estimatedInputTokens = Math.round(prompt.length / FALLBACK_CHARS_PER_TOKEN);
816
- progress.startCall(options.model, estimatedInputTokens);
888
+ progress.startCall(model, estimatedInputTokens);
817
889
  let finishReason;
818
890
  let usage;
819
891
  let totalChars = 0;
@@ -837,12 +909,15 @@ async function handleCompleteCommand(promptArg, options, env) {
837
909
  finishReason = chunk.finishReason;
838
910
  }
839
911
  }
912
+ progress.endCall(usage);
840
913
  progress.complete();
841
914
  printer.ensureNewline();
842
- const summary = renderSummary({ finishReason, usage });
843
- if (summary) {
844
- env.stderr.write(`${summary}
915
+ if (stderrTTY) {
916
+ const summary = renderSummary({ finishReason, usage, cost: progress.getTotalCost() });
917
+ if (summary) {
918
+ env.stderr.write(`${summary}
845
919
  `);
920
+ }
846
921
  }
847
922
  }
848
923
  function registerCompleteCommand(program, env) {
@@ -866,7 +941,7 @@ function registerCompleteCommand(program, env) {
866
941
  init_client();
867
942
  init_logger();
868
943
  import readline from "node:readline";
869
- import chalk4 from "chalk";
944
+ import chalk3 from "chalk";
870
945
  var LOG_LEVEL_MAP = {
871
946
  silly: 0,
872
947
  trace: 1,
@@ -910,14 +985,14 @@ function createPromptFunction(stdin, stdout) {
910
985
  output: stdout
911
986
  });
912
987
  stdout.write("\n");
913
- stdout.write(`${chalk4.cyan("\u2500".repeat(60))}
988
+ stdout.write(`${chalk3.cyan("\u2500".repeat(60))}
914
989
  `);
915
- stdout.write(chalk4.cyan.bold("\u{1F916} Agent asks:\n"));
990
+ stdout.write(chalk3.cyan.bold("\u{1F916} Agent asks:\n"));
916
991
  stdout.write(`${question}
917
992
  `);
918
- stdout.write(`${chalk4.cyan("\u2500".repeat(60))}
993
+ stdout.write(`${chalk3.cyan("\u2500".repeat(60))}
919
994
  `);
920
- rl.question(chalk4.green.bold("You: "), (answer) => {
995
+ rl.question(chalk3.green.bold("You: "), (answer) => {
921
996
  rl.close();
922
997
  resolve(answer);
923
998
  });