llmist 0.1.3 → 0.1.5

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