llmist 0.3.0 → 0.4.0

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/index.cjs CHANGED
@@ -4604,6 +4604,189 @@ var HookPresets = class _HookPresets {
4604
4604
  }
4605
4605
  };
4606
4606
  }
4607
+ /**
4608
+ * Tracks comprehensive progress metrics including iterations, tokens, cost, and timing.
4609
+ *
4610
+ * **This preset showcases llmist's core capabilities by demonstrating:**
4611
+ * - Observer pattern for non-intrusive monitoring
4612
+ * - Integration with ModelRegistry for cost estimation
4613
+ * - Callback-based architecture for flexible UI updates
4614
+ * - Provider-agnostic token and cost tracking
4615
+ *
4616
+ * Unlike `tokenTracking()` which only logs to console, this preset provides
4617
+ * structured data through callbacks, making it perfect for building custom UIs,
4618
+ * dashboards, or progress indicators (like the llmist CLI).
4619
+ *
4620
+ * **Output (when logProgress: true):**
4621
+ * - Iteration number and call count
4622
+ * - Cumulative token usage (input + output)
4623
+ * - Cumulative cost in USD (requires modelRegistry)
4624
+ * - Elapsed time in seconds
4625
+ *
4626
+ * **Use cases:**
4627
+ * - Building CLI progress indicators with live updates
4628
+ * - Creating web dashboards with real-time metrics
4629
+ * - Budget monitoring and cost alerts
4630
+ * - Performance tracking and optimization
4631
+ * - Custom logging to external systems (Datadog, CloudWatch, etc.)
4632
+ *
4633
+ * **Performance:** Minimal overhead. Uses Date.now() for timing and optional
4634
+ * ModelRegistry.estimateCost() which is O(1) lookup. Callback invocation is
4635
+ * synchronous and fast.
4636
+ *
4637
+ * @param options - Progress tracking options
4638
+ * @param options.modelRegistry - ModelRegistry for cost estimation (optional)
4639
+ * @param options.onProgress - Callback invoked after each LLM call (optional)
4640
+ * @param options.logProgress - Log progress to console (default: false)
4641
+ * @returns Hook configuration with progress tracking observers
4642
+ *
4643
+ * @example
4644
+ * ```typescript
4645
+ * // Basic usage with callback (RECOMMENDED - used by llmist CLI)
4646
+ * import { LLMist, HookPresets } from 'llmist';
4647
+ *
4648
+ * const client = LLMist.create();
4649
+ *
4650
+ * await client.agent()
4651
+ * .withHooks(HookPresets.progressTracking({
4652
+ * modelRegistry: client.modelRegistry,
4653
+ * onProgress: (stats) => {
4654
+ * // Update your UI with stats
4655
+ * console.log(`#${stats.currentIteration} | ${stats.totalTokens} tokens | $${stats.totalCost.toFixed(4)}`);
4656
+ * }
4657
+ * }))
4658
+ * .withGadgets(Calculator)
4659
+ * .ask("Calculate 15 * 23");
4660
+ * // Output: #1 | 245 tokens | $0.0012
4661
+ * ```
4662
+ *
4663
+ * @example
4664
+ * ```typescript
4665
+ * // Console logging mode (quick debugging)
4666
+ * await client.agent()
4667
+ * .withHooks(HookPresets.progressTracking({
4668
+ * modelRegistry: client.modelRegistry,
4669
+ * logProgress: true // Simple console output
4670
+ * }))
4671
+ * .ask("Your prompt");
4672
+ * // Output: 📊 Progress: Iteration #1 | 245 tokens | $0.0012 | 1.2s
4673
+ * ```
4674
+ *
4675
+ * @example
4676
+ * ```typescript
4677
+ * // Budget monitoring with alerts
4678
+ * const BUDGET_USD = 0.10;
4679
+ *
4680
+ * await client.agent()
4681
+ * .withHooks(HookPresets.progressTracking({
4682
+ * modelRegistry: client.modelRegistry,
4683
+ * onProgress: (stats) => {
4684
+ * if (stats.totalCost > BUDGET_USD) {
4685
+ * throw new Error(`Budget exceeded: $${stats.totalCost.toFixed(4)}`);
4686
+ * }
4687
+ * }
4688
+ * }))
4689
+ * .ask("Long running task...");
4690
+ * ```
4691
+ *
4692
+ * @example
4693
+ * ```typescript
4694
+ * // Web dashboard integration
4695
+ * let progressBar: HTMLElement;
4696
+ *
4697
+ * await client.agent()
4698
+ * .withHooks(HookPresets.progressTracking({
4699
+ * modelRegistry: client.modelRegistry,
4700
+ * onProgress: (stats) => {
4701
+ * // Update web UI in real-time
4702
+ * progressBar.textContent = `Iteration ${stats.currentIteration}`;
4703
+ * progressBar.dataset.cost = stats.totalCost.toFixed(4);
4704
+ * progressBar.dataset.tokens = stats.totalTokens.toString();
4705
+ * }
4706
+ * }))
4707
+ * .ask("Your prompt");
4708
+ * ```
4709
+ *
4710
+ * @example
4711
+ * ```typescript
4712
+ * // External logging (Datadog, CloudWatch, etc.)
4713
+ * await client.agent()
4714
+ * .withHooks(HookPresets.progressTracking({
4715
+ * modelRegistry: client.modelRegistry,
4716
+ * onProgress: async (stats) => {
4717
+ * await metrics.gauge('llm.iteration', stats.currentIteration);
4718
+ * await metrics.gauge('llm.cost', stats.totalCost);
4719
+ * await metrics.gauge('llm.tokens', stats.totalTokens);
4720
+ * }
4721
+ * }))
4722
+ * .ask("Your prompt");
4723
+ * ```
4724
+ *
4725
+ * @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsprogresstrackingoptions | Full documentation}
4726
+ * @see {@link ProgressTrackingOptions} for detailed options
4727
+ * @see {@link ProgressStats} for the callback data structure
4728
+ */
4729
+ static progressTracking(options) {
4730
+ const { modelRegistry, onProgress, logProgress = false } = options ?? {};
4731
+ let totalCalls = 0;
4732
+ let currentIteration = 0;
4733
+ let totalInputTokens = 0;
4734
+ let totalOutputTokens = 0;
4735
+ let totalCost = 0;
4736
+ const startTime = Date.now();
4737
+ return {
4738
+ observers: {
4739
+ // Track iteration on each LLM call start
4740
+ onLLMCallStart: async (ctx) => {
4741
+ currentIteration++;
4742
+ },
4743
+ // Accumulate metrics and report progress on each LLM call completion
4744
+ onLLMCallComplete: async (ctx) => {
4745
+ totalCalls++;
4746
+ if (ctx.usage) {
4747
+ totalInputTokens += ctx.usage.inputTokens;
4748
+ totalOutputTokens += ctx.usage.outputTokens;
4749
+ if (modelRegistry) {
4750
+ try {
4751
+ const modelName = ctx.options.model.includes(":") ? ctx.options.model.split(":")[1] : ctx.options.model;
4752
+ const costEstimate = modelRegistry.estimateCost(
4753
+ modelName,
4754
+ ctx.usage.inputTokens,
4755
+ ctx.usage.outputTokens
4756
+ );
4757
+ if (costEstimate) {
4758
+ totalCost += costEstimate.totalCost;
4759
+ }
4760
+ } catch (error) {
4761
+ if (logProgress) {
4762
+ console.warn(`\u26A0\uFE0F Cost estimation failed:`, error);
4763
+ }
4764
+ }
4765
+ }
4766
+ }
4767
+ const stats = {
4768
+ currentIteration,
4769
+ totalCalls,
4770
+ totalInputTokens,
4771
+ totalOutputTokens,
4772
+ totalTokens: totalInputTokens + totalOutputTokens,
4773
+ totalCost,
4774
+ elapsedSeconds: Number(((Date.now() - startTime) / 1e3).toFixed(1))
4775
+ };
4776
+ if (onProgress) {
4777
+ onProgress(stats);
4778
+ }
4779
+ if (logProgress) {
4780
+ const formattedTokens = stats.totalTokens >= 1e3 ? `${(stats.totalTokens / 1e3).toFixed(1)}k` : `${stats.totalTokens}`;
4781
+ const formattedCost = stats.totalCost > 0 ? `$${stats.totalCost.toFixed(4)}` : "$0";
4782
+ console.log(
4783
+ `\u{1F4CA} Progress: Iteration #${stats.currentIteration} | ${formattedTokens} tokens | ${formattedCost} | ${stats.elapsedSeconds}s`
4784
+ );
4785
+ }
4786
+ }
4787
+ }
4788
+ };
4789
+ }
4607
4790
  /**
4608
4791
  * Logs detailed error information for debugging and troubleshooting.
4609
4792
  *