llmist 7.0.0 → 8.0.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.
@@ -1,1182 +0,0 @@
1
- import {
2
- DEFAULT_HINTS,
3
- detectAudioMimeType,
4
- detectImageMimeType,
5
- init_anthropic,
6
- init_builder,
7
- init_client,
8
- init_config,
9
- init_conversation_manager,
10
- init_create_gadget,
11
- init_discovery,
12
- init_event_handlers,
13
- init_exceptions,
14
- init_execution_tree,
15
- init_executor,
16
- init_gadget,
17
- init_gadget_output_store,
18
- init_gemini,
19
- init_input_content,
20
- init_logger,
21
- init_manager,
22
- init_media_store,
23
- init_messages,
24
- init_model_registry,
25
- init_model_shortcuts,
26
- init_openai,
27
- init_options,
28
- init_output_viewer,
29
- init_parser,
30
- init_prompt_config,
31
- init_quick_methods,
32
- init_registry,
33
- init_strategies,
34
- init_strategy,
35
- init_stream_processor,
36
- init_typed_gadget,
37
- resolveHintTemplate
38
- } from "./chunk-SFZIL2VR.js";
39
-
40
- // src/index.ts
41
- init_builder();
42
- init_event_handlers();
43
- import { z } from "zod";
44
-
45
- // src/agent/hook-presets.ts
46
- var HookPresets = class _HookPresets {
47
- /**
48
- * Logs LLM calls and gadget execution to console with optional verbosity.
49
- *
50
- * **Output (basic mode):**
51
- * - LLM call start/complete events with iteration numbers
52
- * - Gadget execution start/complete with gadget names
53
- * - Token counts when available
54
- *
55
- * **Output (verbose mode):**
56
- * - All basic mode output
57
- * - Full gadget parameters (formatted JSON)
58
- * - Full gadget results
59
- * - Complete LLM response text
60
- *
61
- * **Use cases:**
62
- * - Basic development debugging and execution flow visibility
63
- * - Understanding agent decision-making and tool usage
64
- * - Troubleshooting gadget invocations
65
- *
66
- * **Performance:** Minimal overhead. Console writes are synchronous but fast.
67
- *
68
- * @param options - Logging options
69
- * @param options.verbose - Include full parameters and results. Default: false
70
- * @returns Hook configuration that can be passed to .withHooks()
71
- *
72
- * @example
73
- * ```typescript
74
- * // Basic logging
75
- * await LLMist.createAgent()
76
- * .withHooks(HookPresets.logging())
77
- * .ask("Calculate 15 * 23");
78
- * // Output: [LLM] Starting call (iteration 0)
79
- * // [GADGET] Executing Calculator
80
- * // [GADGET] Completed Calculator
81
- * // [LLM] Completed (tokens: 245)
82
- * ```
83
- *
84
- * @example
85
- * ```typescript
86
- * // Verbose logging with full details
87
- * await LLMist.createAgent()
88
- * .withHooks(HookPresets.logging({ verbose: true }))
89
- * .ask("Calculate 15 * 23");
90
- * // Output includes: parameters, results, and full responses
91
- * ```
92
- *
93
- * @example
94
- * ```typescript
95
- * // Environment-based verbosity
96
- * const isDev = process.env.NODE_ENV === 'development';
97
- * .withHooks(HookPresets.logging({ verbose: isDev }))
98
- * ```
99
- *
100
- * @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsloggingoptions | Full documentation}
101
- */
102
- static logging(options = {}) {
103
- return {
104
- observers: {
105
- onLLMCallStart: async (ctx) => {
106
- console.log(`[LLM] Starting call (iteration ${ctx.iteration})`);
107
- },
108
- onLLMCallComplete: async (ctx) => {
109
- const tokens = ctx.usage?.totalTokens ?? "unknown";
110
- console.log(`[LLM] Completed (tokens: ${tokens})`);
111
- if (options.verbose && ctx.finalMessage) {
112
- console.log(`[LLM] Response: ${ctx.finalMessage}`);
113
- }
114
- },
115
- onGadgetExecutionStart: async (ctx) => {
116
- console.log(`[GADGET] Executing ${ctx.gadgetName}`);
117
- if (options.verbose) {
118
- console.log(`[GADGET] Parameters:`, JSON.stringify(ctx.parameters, null, 2));
119
- }
120
- },
121
- onGadgetExecutionComplete: async (ctx) => {
122
- console.log(`[GADGET] Completed ${ctx.gadgetName}`);
123
- if (options.verbose) {
124
- const display = ctx.error ?? ctx.finalResult ?? "(no result)";
125
- console.log(`[GADGET] Result: ${display}`);
126
- }
127
- }
128
- }
129
- };
130
- }
131
- /**
132
- * Measures and logs execution time for LLM calls and gadgets.
133
- *
134
- * **Output:**
135
- * - Duration in milliseconds with ⏱️ emoji for each operation
136
- * - Separate timing for each LLM iteration
137
- * - Separate timing for each gadget execution
138
- *
139
- * **Use cases:**
140
- * - Performance profiling and optimization
141
- * - Identifying slow operations (LLM calls vs gadget execution)
142
- * - Monitoring response times in production
143
- * - Capacity planning and SLA tracking
144
- *
145
- * **Performance:** Negligible overhead. Uses Date.now() for timing measurements.
146
- *
147
- * @returns Hook configuration that can be passed to .withHooks()
148
- *
149
- * @example
150
- * ```typescript
151
- * // Basic timing
152
- * await LLMist.createAgent()
153
- * .withHooks(HookPresets.timing())
154
- * .withGadgets(Weather, Database)
155
- * .ask("What's the weather in NYC?");
156
- * // Output: ⏱️ LLM call took 1234ms
157
- * // ⏱️ Gadget Weather took 567ms
158
- * // ⏱️ LLM call took 890ms
159
- * ```
160
- *
161
- * @example
162
- * ```typescript
163
- * // Combined with logging for full context
164
- * .withHooks(HookPresets.merge(
165
- * HookPresets.logging(),
166
- * HookPresets.timing()
167
- * ))
168
- * ```
169
- *
170
- * @example
171
- * ```typescript
172
- * // Correlate performance with cost
173
- * .withHooks(HookPresets.merge(
174
- * HookPresets.timing(),
175
- * HookPresets.tokenTracking()
176
- * ))
177
- * ```
178
- *
179
- * @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetstiming | Full documentation}
180
- */
181
- static timing() {
182
- const timings = /* @__PURE__ */ new Map();
183
- return {
184
- observers: {
185
- onLLMCallStart: async (ctx) => {
186
- timings.set(`llm-${ctx.iteration}`, Date.now());
187
- },
188
- onLLMCallComplete: async (ctx) => {
189
- const start = timings.get(`llm-${ctx.iteration}`);
190
- if (start) {
191
- const duration = Date.now() - start;
192
- console.log(`\u23F1\uFE0F LLM call took ${duration}ms`);
193
- timings.delete(`llm-${ctx.iteration}`);
194
- }
195
- },
196
- onGadgetExecutionStart: async (ctx) => {
197
- const key = `gadget-${ctx.gadgetName}-${Date.now()}`;
198
- timings.set(key, Date.now());
199
- ctx._timingKey = key;
200
- },
201
- onGadgetExecutionComplete: async (ctx) => {
202
- const key = ctx._timingKey;
203
- if (key) {
204
- const start = timings.get(key);
205
- if (start) {
206
- const duration = Date.now() - start;
207
- console.log(`\u23F1\uFE0F Gadget ${ctx.gadgetName} took ${duration}ms`);
208
- timings.delete(key);
209
- }
210
- }
211
- }
212
- }
213
- };
214
- }
215
- /**
216
- * Tracks cumulative token usage across all LLM calls.
217
- *
218
- * **Output:**
219
- * - Per-call token count with 📊 emoji
220
- * - Cumulative total across all calls
221
- * - Call count for average calculations
222
- *
223
- * **Use cases:**
224
- * - Cost monitoring and budget tracking
225
- * - Optimizing prompts to reduce token usage
226
- * - Comparing token efficiency across different approaches
227
- * - Real-time cost estimation
228
- *
229
- * **Performance:** Minimal overhead. Simple counter increments.
230
- *
231
- * **Note:** Token counts depend on the provider's response. Some providers
232
- * may not include usage data, in which case counts won't be logged.
233
- *
234
- * @returns Hook configuration that can be passed to .withHooks()
235
- *
236
- * @example
237
- * ```typescript
238
- * // Basic token tracking
239
- * await LLMist.createAgent()
240
- * .withHooks(HookPresets.tokenTracking())
241
- * .ask("Summarize this document...");
242
- * // Output: 📊 Tokens this call: 1,234
243
- * // 📊 Total tokens: 1,234 (across 1 calls)
244
- * // 📊 Tokens this call: 567
245
- * // 📊 Total tokens: 1,801 (across 2 calls)
246
- * ```
247
- *
248
- * @example
249
- * ```typescript
250
- * // Cost calculation with custom hook
251
- * let totalTokens = 0;
252
- * .withHooks(HookPresets.merge(
253
- * HookPresets.tokenTracking(),
254
- * {
255
- * observers: {
256
- * onLLMCallComplete: async (ctx) => {
257
- * totalTokens += ctx.usage?.totalTokens ?? 0;
258
- * const cost = (totalTokens / 1_000_000) * 3.0; // $3 per 1M tokens
259
- * console.log(`💰 Estimated cost: $${cost.toFixed(4)}`);
260
- * },
261
- * },
262
- * }
263
- * ))
264
- * ```
265
- *
266
- * @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetstokentracking | Full documentation}
267
- */
268
- static tokenTracking() {
269
- let totalTokens = 0;
270
- let totalCalls = 0;
271
- return {
272
- observers: {
273
- onLLMCallComplete: async (ctx) => {
274
- totalCalls++;
275
- if (ctx.usage?.totalTokens) {
276
- totalTokens += ctx.usage.totalTokens;
277
- console.log(`\u{1F4CA} Tokens this call: ${ctx.usage.totalTokens}`);
278
- console.log(`\u{1F4CA} Total tokens: ${totalTokens} (across ${totalCalls} calls)`);
279
- }
280
- }
281
- }
282
- };
283
- }
284
- /**
285
- * Tracks comprehensive progress metrics including iterations, tokens, cost, and timing.
286
- *
287
- * **This preset showcases llmist's core capabilities by demonstrating:**
288
- * - Observer pattern for non-intrusive monitoring
289
- * - Integration with ModelRegistry for cost estimation
290
- * - Callback-based architecture for flexible UI updates
291
- * - Provider-agnostic token and cost tracking
292
- *
293
- * Unlike `tokenTracking()` which only logs to console, this preset provides
294
- * structured data through callbacks, making it perfect for building custom UIs,
295
- * dashboards, or progress indicators (like the llmist CLI).
296
- *
297
- * **Output (when logProgress: true):**
298
- * - Iteration number and call count
299
- * - Cumulative token usage (input + output)
300
- * - Cumulative cost in USD (requires modelRegistry)
301
- * - Elapsed time in seconds
302
- *
303
- * **Use cases:**
304
- * - Building CLI progress indicators with live updates
305
- * - Creating web dashboards with real-time metrics
306
- * - Budget monitoring and cost alerts
307
- * - Performance tracking and optimization
308
- * - Custom logging to external systems (Datadog, CloudWatch, etc.)
309
- *
310
- * **Performance:** Minimal overhead. Uses Date.now() for timing and optional
311
- * ModelRegistry.estimateCost() which is O(1) lookup. Callback invocation is
312
- * synchronous and fast.
313
- *
314
- * @param options - Progress tracking options
315
- * @param options.modelRegistry - ModelRegistry for cost estimation (optional)
316
- * @param options.onProgress - Callback invoked after each LLM call (optional)
317
- * @param options.logProgress - Log progress to console (default: false)
318
- * @returns Hook configuration with progress tracking observers
319
- *
320
- * @example
321
- * ```typescript
322
- * // Basic usage with callback (RECOMMENDED - used by llmist CLI)
323
- * import { LLMist, HookPresets } from 'llmist';
324
- *
325
- * const client = LLMist.create();
326
- *
327
- * await client.agent()
328
- * .withHooks(HookPresets.progressTracking({
329
- * modelRegistry: client.modelRegistry,
330
- * onProgress: (stats) => {
331
- * // Update your UI with stats
332
- * console.log(`#${stats.currentIteration} | ${stats.totalTokens} tokens | $${stats.totalCost.toFixed(4)}`);
333
- * }
334
- * }))
335
- * .withGadgets(Calculator)
336
- * .ask("Calculate 15 * 23");
337
- * // Output: #1 | 245 tokens | $0.0012
338
- * ```
339
- *
340
- * @example
341
- * ```typescript
342
- * // Console logging mode (quick debugging)
343
- * await client.agent()
344
- * .withHooks(HookPresets.progressTracking({
345
- * modelRegistry: client.modelRegistry,
346
- * logProgress: true // Simple console output
347
- * }))
348
- * .ask("Your prompt");
349
- * // Output: 📊 Progress: Iteration #1 | 245 tokens | $0.0012 | 1.2s
350
- * ```
351
- *
352
- * @example
353
- * ```typescript
354
- * // Budget monitoring with alerts
355
- * const BUDGET_USD = 0.10;
356
- *
357
- * await client.agent()
358
- * .withHooks(HookPresets.progressTracking({
359
- * modelRegistry: client.modelRegistry,
360
- * onProgress: (stats) => {
361
- * if (stats.totalCost > BUDGET_USD) {
362
- * throw new Error(`Budget exceeded: $${stats.totalCost.toFixed(4)}`);
363
- * }
364
- * }
365
- * }))
366
- * .ask("Long running task...");
367
- * ```
368
- *
369
- * @example
370
- * ```typescript
371
- * // Web dashboard integration
372
- * let progressBar: HTMLElement;
373
- *
374
- * await client.agent()
375
- * .withHooks(HookPresets.progressTracking({
376
- * modelRegistry: client.modelRegistry,
377
- * onProgress: (stats) => {
378
- * // Update web UI in real-time
379
- * progressBar.textContent = `Iteration ${stats.currentIteration}`;
380
- * progressBar.dataset.cost = stats.totalCost.toFixed(4);
381
- * progressBar.dataset.tokens = stats.totalTokens.toString();
382
- * }
383
- * }))
384
- * .ask("Your prompt");
385
- * ```
386
- *
387
- * @example
388
- * ```typescript
389
- * // External logging (Datadog, CloudWatch, etc.)
390
- * await client.agent()
391
- * .withHooks(HookPresets.progressTracking({
392
- * modelRegistry: client.modelRegistry,
393
- * onProgress: async (stats) => {
394
- * await metrics.gauge('llm.iteration', stats.currentIteration);
395
- * await metrics.gauge('llm.cost', stats.totalCost);
396
- * await metrics.gauge('llm.tokens', stats.totalTokens);
397
- * }
398
- * }))
399
- * .ask("Your prompt");
400
- * ```
401
- *
402
- * @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsprogresstrackingoptions | Full documentation}
403
- * @see {@link ProgressTrackingOptions} for detailed options
404
- * @see {@link ProgressStats} for the callback data structure
405
- */
406
- static progressTracking(options) {
407
- const { modelRegistry, onProgress, logProgress = false } = options ?? {};
408
- let totalCalls = 0;
409
- let currentIteration = 0;
410
- let totalInputTokens = 0;
411
- let totalOutputTokens = 0;
412
- let totalCost = 0;
413
- let totalGadgetCost = 0;
414
- const startTime = Date.now();
415
- return {
416
- observers: {
417
- // Track iteration on each LLM call start
418
- onLLMCallStart: async (ctx) => {
419
- currentIteration++;
420
- },
421
- // Accumulate metrics and report progress on each LLM call completion
422
- onLLMCallComplete: async (ctx) => {
423
- totalCalls++;
424
- if (ctx.usage) {
425
- totalInputTokens += ctx.usage.inputTokens;
426
- totalOutputTokens += ctx.usage.outputTokens;
427
- if (modelRegistry) {
428
- try {
429
- const modelName = ctx.options.model.includes(":") ? ctx.options.model.split(":")[1] : ctx.options.model;
430
- const costEstimate = modelRegistry.estimateCost(
431
- modelName,
432
- ctx.usage.inputTokens,
433
- ctx.usage.outputTokens
434
- );
435
- if (costEstimate) {
436
- totalCost += costEstimate.totalCost;
437
- }
438
- } catch (error) {
439
- if (logProgress) {
440
- console.warn(`\u26A0\uFE0F Cost estimation failed:`, error);
441
- }
442
- }
443
- }
444
- }
445
- const stats = {
446
- currentIteration,
447
- totalCalls,
448
- totalInputTokens,
449
- totalOutputTokens,
450
- totalTokens: totalInputTokens + totalOutputTokens,
451
- totalCost: totalCost + totalGadgetCost,
452
- elapsedSeconds: Number(((Date.now() - startTime) / 1e3).toFixed(1))
453
- };
454
- if (onProgress) {
455
- onProgress(stats);
456
- }
457
- if (logProgress) {
458
- const formattedTokens = stats.totalTokens >= 1e3 ? `${(stats.totalTokens / 1e3).toFixed(1)}k` : `${stats.totalTokens}`;
459
- const formattedCost = stats.totalCost > 0 ? `$${stats.totalCost.toFixed(4)}` : "$0";
460
- console.log(
461
- `\u{1F4CA} Progress: Iteration #${stats.currentIteration} | ${formattedTokens} tokens | ${formattedCost} | ${stats.elapsedSeconds}s`
462
- );
463
- }
464
- },
465
- // Track gadget execution costs
466
- onGadgetExecutionComplete: async (ctx) => {
467
- if (ctx.cost && ctx.cost > 0) {
468
- totalGadgetCost += ctx.cost;
469
- }
470
- }
471
- }
472
- };
473
- }
474
- /**
475
- * Logs detailed error information for debugging and troubleshooting.
476
- *
477
- * **Output:**
478
- * - LLM errors with ❌ emoji, including model and recovery status
479
- * - Gadget errors with full context (parameters, error message)
480
- * - Separate logging for LLM and gadget failures
481
- *
482
- * **Use cases:**
483
- * - Troubleshooting production issues
484
- * - Understanding error patterns and frequency
485
- * - Debugging error recovery behavior
486
- * - Collecting error metrics for monitoring
487
- *
488
- * **Performance:** Minimal overhead. Only logs when errors occur.
489
- *
490
- * @returns Hook configuration that can be passed to .withHooks()
491
- *
492
- * @example
493
- * ```typescript
494
- * // Basic error logging
495
- * await LLMist.createAgent()
496
- * .withHooks(HookPresets.errorLogging())
497
- * .withGadgets(Database)
498
- * .ask("Fetch user data");
499
- * // Output (on LLM error): ❌ LLM Error (iteration 1): Rate limit exceeded
500
- * // Model: gpt-5-nano
501
- * // Recovered: true
502
- * // Output (on gadget error): ❌ Gadget Error: Database
503
- * // Error: Connection timeout
504
- * // Parameters: {...}
505
- * ```
506
- *
507
- * @example
508
- * ```typescript
509
- * // Combine with monitoring for full context
510
- * .withHooks(HookPresets.merge(
511
- * HookPresets.monitoring(), // Includes errorLogging
512
- * customErrorAnalytics
513
- * ))
514
- * ```
515
- *
516
- * @example
517
- * ```typescript
518
- * // Error analytics collection
519
- * const errors: any[] = [];
520
- * .withHooks(HookPresets.merge(
521
- * HookPresets.errorLogging(),
522
- * {
523
- * observers: {
524
- * onLLMCallError: async (ctx) => {
525
- * errors.push({ type: 'llm', error: ctx.error, recovered: ctx.recovered });
526
- * },
527
- * },
528
- * }
529
- * ))
530
- * ```
531
- *
532
- * @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetserrorlogging | Full documentation}
533
- */
534
- static errorLogging() {
535
- return {
536
- observers: {
537
- onLLMCallError: async (ctx) => {
538
- console.error(`\u274C LLM Error (iteration ${ctx.iteration}):`, ctx.error.message);
539
- console.error(` Model: ${ctx.options.model}`);
540
- console.error(` Recovered: ${ctx.recovered}`);
541
- },
542
- onGadgetExecutionComplete: async (ctx) => {
543
- if (ctx.error) {
544
- console.error(`\u274C Gadget Error: ${ctx.gadgetName}`);
545
- console.error(` Error: ${ctx.error}`);
546
- console.error(` Parameters:`, JSON.stringify(ctx.parameters, null, 2));
547
- }
548
- }
549
- }
550
- };
551
- }
552
- /**
553
- * Tracks context compaction events.
554
- *
555
- * **Output:**
556
- * - Compaction events with 🗜️ emoji
557
- * - Strategy name, tokens before/after, and savings
558
- * - Cumulative statistics
559
- *
560
- * **Use cases:**
561
- * - Monitoring long-running conversations
562
- * - Understanding when and how compaction occurs
563
- * - Debugging context management issues
564
- *
565
- * **Performance:** Minimal overhead. Simple console output.
566
- *
567
- * @returns Hook configuration that can be passed to .withHooks()
568
- *
569
- * @example
570
- * ```typescript
571
- * await LLMist.createAgent()
572
- * .withHooks(HookPresets.compactionTracking())
573
- * .ask("Your prompt");
574
- * ```
575
- */
576
- static compactionTracking() {
577
- return {
578
- observers: {
579
- onCompaction: async (ctx) => {
580
- const saved = ctx.event.tokensBefore - ctx.event.tokensAfter;
581
- const percent = (saved / ctx.event.tokensBefore * 100).toFixed(1);
582
- console.log(
583
- `\u{1F5DC}\uFE0F Compaction (${ctx.event.strategy}): ${ctx.event.tokensBefore} \u2192 ${ctx.event.tokensAfter} tokens (saved ${saved}, ${percent}%)`
584
- );
585
- console.log(` Messages: ${ctx.event.messagesBefore} \u2192 ${ctx.event.messagesAfter}`);
586
- if (ctx.stats.totalCompactions > 1) {
587
- console.log(
588
- ` Cumulative: ${ctx.stats.totalCompactions} compactions, ${ctx.stats.totalTokensSaved} tokens saved`
589
- );
590
- }
591
- }
592
- }
593
- };
594
- }
595
- /**
596
- * Returns empty hook configuration for clean output without any logging.
597
- *
598
- * **Output:**
599
- * - None. Returns {} (empty object).
600
- *
601
- * **Use cases:**
602
- * - Clean test output without console noise
603
- * - Production environments where logging is handled externally
604
- * - Baseline for custom hook development
605
- * - Temporary disable of all hook output
606
- *
607
- * **Performance:** Zero overhead. No-op hook configuration.
608
- *
609
- * @returns Empty hook configuration
610
- *
611
- * @example
612
- * ```typescript
613
- * // Clean test output
614
- * describe('Agent tests', () => {
615
- * it('should calculate correctly', async () => {
616
- * const result = await LLMist.createAgent()
617
- * .withHooks(HookPresets.silent()) // No console output
618
- * .withGadgets(Calculator)
619
- * .askAndCollect("What is 15 times 23?");
620
- *
621
- * expect(result).toContain("345");
622
- * });
623
- * });
624
- * ```
625
- *
626
- * @example
627
- * ```typescript
628
- * // Conditional silence based on environment
629
- * const isTesting = process.env.NODE_ENV === 'test';
630
- * .withHooks(isTesting ? HookPresets.silent() : HookPresets.monitoring())
631
- * ```
632
- *
633
- * @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetssilent | Full documentation}
634
- */
635
- static silent() {
636
- return {};
637
- }
638
- /**
639
- * Combines multiple hook configurations into one.
640
- *
641
- * Merge allows you to compose preset and custom hooks for modular monitoring
642
- * configurations. Understanding merge behavior is crucial for proper composition.
643
- *
644
- * **Merge behavior:**
645
- * - **Observers:** Composed - all handlers run sequentially in order
646
- * - **Interceptors:** Last one wins - only the last interceptor applies
647
- * - **Controllers:** Last one wins - only the last controller applies
648
- *
649
- * **Why interceptors/controllers don't compose:**
650
- * - Interceptors have different signatures per method, making composition impractical
651
- * - Controllers return specific actions that can't be meaningfully combined
652
- * - Only observers support composition because they're read-only and independent
653
- *
654
- * **Use cases:**
655
- * - Combining multiple presets (logging + timing + tokens)
656
- * - Adding custom hooks to presets
657
- * - Building modular, reusable monitoring configurations
658
- * - Environment-specific hook composition
659
- *
660
- * **Performance:** Minimal overhead for merging. Runtime performance depends on merged hooks.
661
- *
662
- * @param hookSets - Variable number of hook configurations to merge
663
- * @returns Single merged hook configuration with composed/overridden handlers
664
- *
665
- * @example
666
- * ```typescript
667
- * // Combine multiple presets
668
- * .withHooks(HookPresets.merge(
669
- * HookPresets.logging(),
670
- * HookPresets.timing(),
671
- * HookPresets.tokenTracking()
672
- * ))
673
- * // All observers from all three presets will run
674
- * ```
675
- *
676
- * @example
677
- * ```typescript
678
- * // Add custom observer to preset (both run)
679
- * .withHooks(HookPresets.merge(
680
- * HookPresets.timing(),
681
- * {
682
- * observers: {
683
- * onLLMCallComplete: async (ctx) => {
684
- * await saveMetrics({ tokens: ctx.usage?.totalTokens });
685
- * },
686
- * },
687
- * }
688
- * ))
689
- * ```
690
- *
691
- * @example
692
- * ```typescript
693
- * // Multiple interceptors (last wins!)
694
- * .withHooks(HookPresets.merge(
695
- * {
696
- * interceptors: {
697
- * interceptTextChunk: (chunk) => chunk.toUpperCase(), // Ignored
698
- * },
699
- * },
700
- * {
701
- * interceptors: {
702
- * interceptTextChunk: (chunk) => chunk.toLowerCase(), // This wins
703
- * },
704
- * }
705
- * ))
706
- * // Result: text will be lowercase
707
- * ```
708
- *
709
- * @example
710
- * ```typescript
711
- * // Modular environment-based configuration
712
- * const baseHooks = HookPresets.errorLogging();
713
- * const devHooks = HookPresets.merge(baseHooks, HookPresets.monitoring({ verbose: true }));
714
- * const prodHooks = HookPresets.merge(baseHooks, HookPresets.tokenTracking());
715
- *
716
- * const hooks = process.env.NODE_ENV === 'production' ? prodHooks : devHooks;
717
- * .withHooks(hooks)
718
- * ```
719
- *
720
- * @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsmergehooksets | Full documentation}
721
- */
722
- static merge(...hookSets) {
723
- const merged = {
724
- observers: {},
725
- interceptors: {},
726
- controllers: {}
727
- };
728
- for (const hooks of hookSets) {
729
- if (hooks.observers) {
730
- for (const [key, handler] of Object.entries(hooks.observers)) {
731
- const typedKey = key;
732
- if (merged.observers[typedKey]) {
733
- const existing = merged.observers[typedKey];
734
- merged.observers[typedKey] = async (ctx) => {
735
- await existing(ctx);
736
- await handler(ctx);
737
- };
738
- } else {
739
- merged.observers[typedKey] = handler;
740
- }
741
- }
742
- }
743
- if (hooks.interceptors) {
744
- Object.assign(merged.interceptors, hooks.interceptors);
745
- }
746
- if (hooks.controllers) {
747
- Object.assign(merged.controllers, hooks.controllers);
748
- }
749
- }
750
- return merged;
751
- }
752
- /**
753
- * Composite preset combining logging, timing, tokenTracking, and errorLogging.
754
- *
755
- * This is the recommended preset for development and initial production deployments,
756
- * providing comprehensive observability with a single method call.
757
- *
758
- * **Includes:**
759
- * - All output from `logging()` preset (with optional verbosity)
760
- * - All output from `timing()` preset (execution times)
761
- * - All output from `tokenTracking()` preset (token usage)
762
- * - All output from `errorLogging()` preset (error details)
763
- *
764
- * **Output format:**
765
- * - Event logging: [LLM]/[GADGET] messages
766
- * - Timing: ⏱️ emoji with milliseconds
767
- * - Tokens: 📊 emoji with per-call and cumulative counts
768
- * - Errors: ❌ emoji with full error details
769
- *
770
- * **Use cases:**
771
- * - Full observability during development
772
- * - Comprehensive monitoring in production
773
- * - One-liner for complete agent visibility
774
- * - Troubleshooting and debugging with full context
775
- *
776
- * **Performance:** Combined overhead of all four presets, but still minimal in practice.
777
- *
778
- * @param options - Monitoring options
779
- * @param options.verbose - Passed to logging() preset for detailed output. Default: false
780
- * @returns Merged hook configuration combining all monitoring presets
781
- *
782
- * @example
783
- * ```typescript
784
- * // Basic monitoring (recommended for development)
785
- * await LLMist.createAgent()
786
- * .withHooks(HookPresets.monitoring())
787
- * .withGadgets(Calculator, Weather)
788
- * .ask("What is 15 times 23, and what's the weather in NYC?");
789
- * // Output: All events, timing, tokens, and errors in one place
790
- * ```
791
- *
792
- * @example
793
- * ```typescript
794
- * // Verbose monitoring with full details
795
- * await LLMist.createAgent()
796
- * .withHooks(HookPresets.monitoring({ verbose: true }))
797
- * .ask("Your prompt");
798
- * // Output includes: parameters, results, and complete responses
799
- * ```
800
- *
801
- * @example
802
- * ```typescript
803
- * // Environment-based monitoring
804
- * const isDev = process.env.NODE_ENV === 'development';
805
- * .withHooks(HookPresets.monitoring({ verbose: isDev }))
806
- * ```
807
- *
808
- * @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsmonitoringoptions | Full documentation}
809
- */
810
- static monitoring(options = {}) {
811
- return _HookPresets.merge(
812
- _HookPresets.logging(options),
813
- _HookPresets.timing(),
814
- _HookPresets.tokenTracking(),
815
- _HookPresets.errorLogging()
816
- );
817
- }
818
- };
819
-
820
- // src/agent/compaction/index.ts
821
- init_config();
822
- init_manager();
823
- init_strategies();
824
- init_strategy();
825
-
826
- // src/agent/index.ts
827
- init_conversation_manager();
828
- init_gadget_output_store();
829
-
830
- // src/agent/hints.ts
831
- init_prompt_config();
832
- function iterationProgressHint(options) {
833
- const { timing = "always", showUrgency = true, template } = options ?? {};
834
- return {
835
- controllers: {
836
- beforeLLMCall: async (ctx) => {
837
- const iteration = ctx.iteration + 1;
838
- const maxIterations = ctx.maxIterations;
839
- const progress = iteration / maxIterations;
840
- if (timing === "late" && progress < 0.5) {
841
- return { action: "proceed" };
842
- }
843
- if (timing === "urgent" && progress < 0.8) {
844
- return { action: "proceed" };
845
- }
846
- const remaining = maxIterations - iteration;
847
- const hintContext = {
848
- iteration,
849
- maxIterations,
850
- remaining
851
- };
852
- let hint = resolveHintTemplate(template, DEFAULT_HINTS.iterationProgressHint, hintContext);
853
- if (showUrgency && progress >= 0.8) {
854
- hint += " \u26A0\uFE0F Running low on iterations - focus on completing the task.";
855
- }
856
- const messages = [...ctx.options.messages];
857
- let lastUserIndex = -1;
858
- for (let i = messages.length - 1; i >= 0; i--) {
859
- if (messages[i].role === "user") {
860
- lastUserIndex = i;
861
- break;
862
- }
863
- }
864
- if (lastUserIndex >= 0) {
865
- messages.splice(lastUserIndex + 1, 0, {
866
- role: "user",
867
- content: `[System Hint] ${hint}`
868
- });
869
- } else {
870
- messages.push({
871
- role: "user",
872
- content: `[System Hint] ${hint}`
873
- });
874
- }
875
- return {
876
- action: "proceed",
877
- modifiedOptions: { messages }
878
- };
879
- }
880
- }
881
- };
882
- }
883
- function parallelGadgetHint(options) {
884
- const {
885
- minGadgetsForEfficiency = 2,
886
- message = DEFAULT_HINTS.parallelGadgetsHint,
887
- enabled = true
888
- } = options ?? {};
889
- return {
890
- controllers: {
891
- afterLLMCall: async (ctx) => {
892
- if (!enabled) {
893
- return { action: "continue" };
894
- }
895
- if (ctx.gadgetCallCount > 0 && ctx.gadgetCallCount < minGadgetsForEfficiency) {
896
- return {
897
- action: "append_messages",
898
- messages: [
899
- {
900
- role: "user",
901
- content: `[System Hint] ${message}`
902
- }
903
- ]
904
- };
905
- }
906
- return { action: "continue" };
907
- }
908
- }
909
- };
910
- }
911
- function createHints(config) {
912
- const hooksToMerge = [];
913
- if (config.iterationProgress) {
914
- const options = typeof config.iterationProgress === "boolean" ? {} : config.iterationProgress;
915
- hooksToMerge.push(iterationProgressHint(options));
916
- }
917
- if (config.parallelGadgets) {
918
- const options = typeof config.parallelGadgets === "boolean" ? {} : config.parallelGadgets;
919
- hooksToMerge.push(parallelGadgetHint(options));
920
- }
921
- if (config.custom) {
922
- hooksToMerge.push(...config.custom);
923
- }
924
- return HookPresets.merge(...hooksToMerge);
925
- }
926
-
927
- // src/agent/index.ts
928
- init_stream_processor();
929
-
930
- // src/index.ts
931
- init_client();
932
- init_execution_tree();
933
-
934
- // src/core/execution-events.ts
935
- function isLLMEvent(event) {
936
- return event.type.startsWith("llm_call_");
937
- }
938
- function isGadgetEvent(event) {
939
- return event.type.startsWith("gadget_");
940
- }
941
- function isSubagentEvent(event) {
942
- return event.depth > 0;
943
- }
944
- function isRootEvent(event) {
945
- return event.depth === 0;
946
- }
947
- function filterByDepth(events, depth) {
948
- return events.filter((e) => e.depth === depth);
949
- }
950
- function filterByParent(events, parentId) {
951
- return events.filter((e) => e.parentId === parentId);
952
- }
953
- function filterRootEvents(events) {
954
- return filterByDepth(events, 0);
955
- }
956
- function groupByParent(events) {
957
- const groups = /* @__PURE__ */ new Map();
958
- for (const event of events) {
959
- const parentId = event.parentId;
960
- if (!groups.has(parentId)) {
961
- groups.set(parentId, []);
962
- }
963
- groups.get(parentId)?.push(event);
964
- }
965
- return groups;
966
- }
967
-
968
- // src/index.ts
969
- init_input_content();
970
- init_messages();
971
- init_model_registry();
972
- init_model_shortcuts();
973
- init_options();
974
- init_prompt_config();
975
- init_quick_methods();
976
- init_create_gadget();
977
- init_exceptions();
978
- init_executor();
979
- init_gadget();
980
- init_output_viewer();
981
- init_parser();
982
- init_registry();
983
- init_typed_gadget();
984
-
985
- // src/gadgets/helpers.ts
986
- init_input_content();
987
- function createMediaOutput(kind, data, mimeType, options) {
988
- const buffer = data instanceof Buffer ? data : Buffer.from(data);
989
- return {
990
- kind,
991
- data: buffer.toString("base64"),
992
- mimeType,
993
- description: options?.description,
994
- metadata: options?.metadata,
995
- fileName: options?.fileName
996
- };
997
- }
998
- function resultWithMedia(result, media, cost) {
999
- if (media.length === 0) {
1000
- throw new Error("resultWithMedia: media array cannot be empty");
1001
- }
1002
- return {
1003
- result,
1004
- media,
1005
- cost
1006
- };
1007
- }
1008
- function resultWithImage(result, imageData, options) {
1009
- const buffer = imageData instanceof Buffer ? imageData : Buffer.from(imageData);
1010
- const mimeType = options?.mimeType ?? detectImageMimeType(buffer);
1011
- if (!mimeType) {
1012
- throw new Error(
1013
- "Could not detect image MIME type. Please provide mimeType explicitly in options."
1014
- );
1015
- }
1016
- return {
1017
- result,
1018
- media: [
1019
- {
1020
- kind: "image",
1021
- data: buffer.toString("base64"),
1022
- mimeType,
1023
- description: options?.description,
1024
- metadata: options?.metadata,
1025
- fileName: options?.fileName
1026
- }
1027
- ],
1028
- cost: options?.cost
1029
- };
1030
- }
1031
- function resultWithImages(result, images, cost) {
1032
- if (images.length === 0) {
1033
- throw new Error("resultWithImages: images array cannot be empty");
1034
- }
1035
- const media = images.map((img, index) => {
1036
- const buffer = img.data instanceof Buffer ? img.data : Buffer.from(img.data);
1037
- const mimeType = img.mimeType ?? detectImageMimeType(buffer);
1038
- if (!mimeType) {
1039
- throw new Error(
1040
- `Could not detect MIME type for image at index ${index}. Please provide mimeType explicitly.`
1041
- );
1042
- }
1043
- return {
1044
- kind: "image",
1045
- data: buffer.toString("base64"),
1046
- mimeType,
1047
- description: img.description,
1048
- metadata: img.metadata,
1049
- fileName: img.fileName
1050
- };
1051
- });
1052
- return { result, media, cost };
1053
- }
1054
- function resultWithAudio(result, audioData, options) {
1055
- const buffer = audioData instanceof Buffer ? audioData : Buffer.from(audioData);
1056
- const mimeType = options?.mimeType ?? detectAudioMimeType(buffer);
1057
- if (!mimeType) {
1058
- throw new Error(
1059
- "Could not detect audio MIME type. Please provide mimeType explicitly in options."
1060
- );
1061
- }
1062
- const metadata = options?.durationMs ? { durationMs: options.durationMs } : void 0;
1063
- return {
1064
- result,
1065
- media: [
1066
- {
1067
- kind: "audio",
1068
- data: buffer.toString("base64"),
1069
- mimeType,
1070
- description: options?.description,
1071
- metadata,
1072
- fileName: options?.fileName
1073
- }
1074
- ],
1075
- cost: options?.cost
1076
- };
1077
- }
1078
- function resultWithFile(result, fileData, mimeType, options) {
1079
- const buffer = fileData instanceof Buffer ? fileData : Buffer.from(fileData);
1080
- return {
1081
- result,
1082
- media: [
1083
- {
1084
- kind: "file",
1085
- data: buffer.toString("base64"),
1086
- mimeType,
1087
- description: options?.description,
1088
- fileName: options?.fileName
1089
- }
1090
- ],
1091
- cost: options?.cost
1092
- };
1093
- }
1094
-
1095
- // src/index.ts
1096
- init_media_store();
1097
- init_logger();
1098
-
1099
- // src/utils/config-resolver.ts
1100
- function resolveValue(ctx, gadgetName, options) {
1101
- const { runtime, subagentKey, parentKey, defaultValue, handleInherit } = options;
1102
- if (runtime !== void 0) {
1103
- if (handleInherit && runtime === "inherit") {
1104
- } else {
1105
- return runtime;
1106
- }
1107
- }
1108
- if (subagentKey && ctx.subagentConfig) {
1109
- const subagentCfg = ctx.subagentConfig[gadgetName];
1110
- if (subagentCfg && subagentKey in subagentCfg) {
1111
- const value = subagentCfg[subagentKey];
1112
- if (handleInherit && value === "inherit") {
1113
- } else if (value !== void 0) {
1114
- return value;
1115
- }
1116
- }
1117
- }
1118
- if (parentKey && ctx.agentConfig && parentKey in ctx.agentConfig) {
1119
- const parentValue = ctx.agentConfig[parentKey];
1120
- if (parentValue !== void 0) {
1121
- return parentValue;
1122
- }
1123
- }
1124
- return defaultValue;
1125
- }
1126
- function resolveConfig(ctx, gadgetName, config) {
1127
- const result = {};
1128
- for (const [key, options] of Object.entries(config)) {
1129
- result[key] = resolveValue(ctx, gadgetName, options);
1130
- }
1131
- return result;
1132
- }
1133
- function resolveSubagentModel(ctx, gadgetName, runtimeModel, defaultModel) {
1134
- return resolveValue(ctx, gadgetName, {
1135
- runtime: runtimeModel,
1136
- subagentKey: "model",
1137
- parentKey: "model",
1138
- defaultValue: defaultModel,
1139
- handleInherit: true
1140
- });
1141
- }
1142
-
1143
- // src/index.ts
1144
- init_anthropic();
1145
- init_discovery();
1146
- init_gemini();
1147
- init_openai();
1148
- function getHostExports(ctx) {
1149
- if (!ctx?.hostExports) {
1150
- throw new Error(
1151
- "hostExports not available. Gadgets that create subagents must be run via llmist agent, not standalone. Ensure you are using llmist >= 6.2.0."
1152
- );
1153
- }
1154
- return ctx.hostExports;
1155
- }
1156
-
1157
- export {
1158
- HookPresets,
1159
- iterationProgressHint,
1160
- parallelGadgetHint,
1161
- createHints,
1162
- isLLMEvent,
1163
- isGadgetEvent,
1164
- isSubagentEvent,
1165
- isRootEvent,
1166
- filterByDepth,
1167
- filterByParent,
1168
- filterRootEvents,
1169
- groupByParent,
1170
- createMediaOutput,
1171
- resultWithMedia,
1172
- resultWithImage,
1173
- resultWithImages,
1174
- resultWithAudio,
1175
- resultWithFile,
1176
- resolveValue,
1177
- resolveConfig,
1178
- resolveSubagentModel,
1179
- getHostExports,
1180
- z
1181
- };
1182
- //# sourceMappingURL=chunk-5KEZ7SQX.js.map