llmist 15.11.0 → 15.13.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 +1278 -814
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1962 -1623
- package/dist/index.d.ts +1962 -1623
- package/dist/index.js +1276 -814
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -229,7 +229,8 @@ var init_execution_tree = __esm({
|
|
|
229
229
|
response: llmNode.response,
|
|
230
230
|
usage: llmNode.usage,
|
|
231
231
|
finishReason: llmNode.finishReason,
|
|
232
|
-
cost: llmNode.cost
|
|
232
|
+
cost: llmNode.cost,
|
|
233
|
+
thinkingContent: params.thinkingContent
|
|
233
234
|
});
|
|
234
235
|
}
|
|
235
236
|
/**
|
|
@@ -4039,6 +4040,958 @@ var init_registry = __esm({
|
|
|
4039
4040
|
}
|
|
4040
4041
|
});
|
|
4041
4042
|
|
|
4043
|
+
// src/agent/file-logging.ts
|
|
4044
|
+
function formatLlmRequest(messages) {
|
|
4045
|
+
const lines = [];
|
|
4046
|
+
for (const msg of messages) {
|
|
4047
|
+
lines.push(`=== ${msg.role.toUpperCase()} ===`);
|
|
4048
|
+
lines.push(msg.content ? extractMessageText(msg.content) : "");
|
|
4049
|
+
lines.push("");
|
|
4050
|
+
}
|
|
4051
|
+
return lines.join("\n");
|
|
4052
|
+
}
|
|
4053
|
+
function formatCallNumber(n, padding = 4) {
|
|
4054
|
+
return n.toString().padStart(padding, "0");
|
|
4055
|
+
}
|
|
4056
|
+
async function writeLogFile(dir, filename, content) {
|
|
4057
|
+
await (0, import_promises2.mkdir)(dir, { recursive: true });
|
|
4058
|
+
await (0, import_promises2.writeFile)((0, import_node_path3.join)(dir, filename), content, "utf-8");
|
|
4059
|
+
}
|
|
4060
|
+
function createFileLoggingHooks(options) {
|
|
4061
|
+
const {
|
|
4062
|
+
directory,
|
|
4063
|
+
startingCounter = 1,
|
|
4064
|
+
counterPadding = 4,
|
|
4065
|
+
skipSubagents = true,
|
|
4066
|
+
formatRequest = formatLlmRequest,
|
|
4067
|
+
onFileWritten
|
|
4068
|
+
} = options;
|
|
4069
|
+
let callCounter = startingCounter - 1;
|
|
4070
|
+
return {
|
|
4071
|
+
observers: {
|
|
4072
|
+
/**
|
|
4073
|
+
* Write request file when LLM call is ready (messages are finalized).
|
|
4074
|
+
*/
|
|
4075
|
+
onLLMCallReady: async (context) => {
|
|
4076
|
+
if (skipSubagents && context.subagentContext) {
|
|
4077
|
+
return;
|
|
4078
|
+
}
|
|
4079
|
+
callCounter++;
|
|
4080
|
+
const filename = `${formatCallNumber(callCounter, counterPadding)}.request`;
|
|
4081
|
+
const content = formatRequest(context.options.messages);
|
|
4082
|
+
try {
|
|
4083
|
+
await writeLogFile(directory, filename, content);
|
|
4084
|
+
if (onFileWritten) {
|
|
4085
|
+
onFileWritten({
|
|
4086
|
+
filePath: (0, import_node_path3.join)(directory, filename),
|
|
4087
|
+
type: "request",
|
|
4088
|
+
callNumber: callCounter,
|
|
4089
|
+
contentLength: content.length
|
|
4090
|
+
});
|
|
4091
|
+
}
|
|
4092
|
+
} catch (error) {
|
|
4093
|
+
console.warn(`[file-logging] Failed to write ${filename}:`, error);
|
|
4094
|
+
}
|
|
4095
|
+
},
|
|
4096
|
+
/**
|
|
4097
|
+
* Write response file when LLM call completes.
|
|
4098
|
+
*/
|
|
4099
|
+
onLLMCallComplete: async (context) => {
|
|
4100
|
+
if (skipSubagents && context.subagentContext) {
|
|
4101
|
+
return;
|
|
4102
|
+
}
|
|
4103
|
+
const filename = `${formatCallNumber(callCounter, counterPadding)}.response`;
|
|
4104
|
+
const content = context.rawResponse;
|
|
4105
|
+
try {
|
|
4106
|
+
await writeLogFile(directory, filename, content);
|
|
4107
|
+
if (onFileWritten) {
|
|
4108
|
+
onFileWritten({
|
|
4109
|
+
filePath: (0, import_node_path3.join)(directory, filename),
|
|
4110
|
+
type: "response",
|
|
4111
|
+
callNumber: callCounter,
|
|
4112
|
+
contentLength: content.length
|
|
4113
|
+
});
|
|
4114
|
+
}
|
|
4115
|
+
} catch (error) {
|
|
4116
|
+
console.warn(`[file-logging] Failed to write ${filename}:`, error);
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
};
|
|
4121
|
+
}
|
|
4122
|
+
function getEnvFileLoggingHooks() {
|
|
4123
|
+
const directory = process.env[ENV_LOG_RAW_DIRECTORY]?.trim();
|
|
4124
|
+
if (!directory) {
|
|
4125
|
+
return void 0;
|
|
4126
|
+
}
|
|
4127
|
+
return createFileLoggingHooks({ directory });
|
|
4128
|
+
}
|
|
4129
|
+
var import_promises2, import_node_path3, ENV_LOG_RAW_DIRECTORY;
|
|
4130
|
+
var init_file_logging = __esm({
|
|
4131
|
+
"src/agent/file-logging.ts"() {
|
|
4132
|
+
"use strict";
|
|
4133
|
+
import_promises2 = require("fs/promises");
|
|
4134
|
+
import_node_path3 = require("path");
|
|
4135
|
+
init_messages();
|
|
4136
|
+
ENV_LOG_RAW_DIRECTORY = "LLMIST_LOG_RAW_DIRECTORY";
|
|
4137
|
+
}
|
|
4138
|
+
});
|
|
4139
|
+
|
|
4140
|
+
// src/agent/hook-presets.ts
|
|
4141
|
+
var HookPresets;
|
|
4142
|
+
var init_hook_presets = __esm({
|
|
4143
|
+
"src/agent/hook-presets.ts"() {
|
|
4144
|
+
"use strict";
|
|
4145
|
+
init_file_logging();
|
|
4146
|
+
HookPresets = class _HookPresets {
|
|
4147
|
+
/**
|
|
4148
|
+
* Logs LLM calls and gadget execution to console with optional verbosity.
|
|
4149
|
+
*
|
|
4150
|
+
* **Output (basic mode):**
|
|
4151
|
+
* - LLM call start/complete events with iteration numbers
|
|
4152
|
+
* - Gadget execution start/complete with gadget names
|
|
4153
|
+
* - Token counts when available
|
|
4154
|
+
*
|
|
4155
|
+
* **Output (verbose mode):**
|
|
4156
|
+
* - All basic mode output
|
|
4157
|
+
* - Full gadget parameters (formatted JSON)
|
|
4158
|
+
* - Full gadget results
|
|
4159
|
+
* - Complete LLM response text
|
|
4160
|
+
*
|
|
4161
|
+
* **Use cases:**
|
|
4162
|
+
* - Basic development debugging and execution flow visibility
|
|
4163
|
+
* - Understanding agent decision-making and tool usage
|
|
4164
|
+
* - Troubleshooting gadget invocations
|
|
4165
|
+
*
|
|
4166
|
+
* **Performance:** Minimal overhead. Console writes are synchronous but fast.
|
|
4167
|
+
*
|
|
4168
|
+
* @param options - Logging options
|
|
4169
|
+
* @param options.verbose - Include full parameters and results. Default: false
|
|
4170
|
+
* @returns Hook configuration that can be passed to .withHooks()
|
|
4171
|
+
*
|
|
4172
|
+
* @example
|
|
4173
|
+
* ```typescript
|
|
4174
|
+
* // Basic logging
|
|
4175
|
+
* await LLMist.createAgent()
|
|
4176
|
+
* .withHooks(HookPresets.logging())
|
|
4177
|
+
* .ask("Calculate 15 * 23");
|
|
4178
|
+
* // Output: [LLM] Starting call (iteration 0)
|
|
4179
|
+
* // [GADGET] Executing Calculator
|
|
4180
|
+
* // [GADGET] Completed Calculator
|
|
4181
|
+
* // [LLM] Completed (tokens: 245)
|
|
4182
|
+
* ```
|
|
4183
|
+
*
|
|
4184
|
+
* @example
|
|
4185
|
+
* ```typescript
|
|
4186
|
+
* // Verbose logging with full details
|
|
4187
|
+
* await LLMist.createAgent()
|
|
4188
|
+
* .withHooks(HookPresets.logging({ verbose: true }))
|
|
4189
|
+
* .ask("Calculate 15 * 23");
|
|
4190
|
+
* // Output includes: parameters, results, and full responses
|
|
4191
|
+
* ```
|
|
4192
|
+
*
|
|
4193
|
+
* @example
|
|
4194
|
+
* ```typescript
|
|
4195
|
+
* // Environment-based verbosity
|
|
4196
|
+
* const isDev = process.env.NODE_ENV === 'development';
|
|
4197
|
+
* .withHooks(HookPresets.logging({ verbose: isDev }))
|
|
4198
|
+
* ```
|
|
4199
|
+
*
|
|
4200
|
+
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsloggingoptions | Full documentation}
|
|
4201
|
+
*/
|
|
4202
|
+
static logging(options = {}) {
|
|
4203
|
+
return {
|
|
4204
|
+
observers: {
|
|
4205
|
+
onLLMCallStart: async (ctx) => {
|
|
4206
|
+
console.log(`[LLM] Starting call (iteration ${ctx.iteration})`);
|
|
4207
|
+
},
|
|
4208
|
+
onLLMCallComplete: async (ctx) => {
|
|
4209
|
+
const tokens = ctx.usage?.totalTokens ?? "unknown";
|
|
4210
|
+
console.log(`[LLM] Completed (tokens: ${tokens})`);
|
|
4211
|
+
if (options.verbose && ctx.finalMessage) {
|
|
4212
|
+
console.log(`[LLM] Response: ${ctx.finalMessage}`);
|
|
4213
|
+
}
|
|
4214
|
+
},
|
|
4215
|
+
onGadgetExecutionStart: async (ctx) => {
|
|
4216
|
+
console.log(`[GADGET] Executing ${ctx.gadgetName}`);
|
|
4217
|
+
if (options.verbose) {
|
|
4218
|
+
console.log(`[GADGET] Parameters:`, JSON.stringify(ctx.parameters, null, 2));
|
|
4219
|
+
}
|
|
4220
|
+
},
|
|
4221
|
+
onGadgetExecutionComplete: async (ctx) => {
|
|
4222
|
+
console.log(`[GADGET] Completed ${ctx.gadgetName}`);
|
|
4223
|
+
if (options.verbose) {
|
|
4224
|
+
const display = ctx.error ?? ctx.finalResult ?? "(no result)";
|
|
4225
|
+
console.log(`[GADGET] Result: ${display}`);
|
|
4226
|
+
}
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
};
|
|
4230
|
+
}
|
|
4231
|
+
/**
|
|
4232
|
+
* Measures and logs execution time for LLM calls and gadgets.
|
|
4233
|
+
*
|
|
4234
|
+
* **Output:**
|
|
4235
|
+
* - Duration in milliseconds with ⏱️ emoji for each operation
|
|
4236
|
+
* - Separate timing for each LLM iteration
|
|
4237
|
+
* - Separate timing for each gadget execution
|
|
4238
|
+
*
|
|
4239
|
+
* **Use cases:**
|
|
4240
|
+
* - Performance profiling and optimization
|
|
4241
|
+
* - Identifying slow operations (LLM calls vs gadget execution)
|
|
4242
|
+
* - Monitoring response times in production
|
|
4243
|
+
* - Capacity planning and SLA tracking
|
|
4244
|
+
*
|
|
4245
|
+
* **Performance:** Negligible overhead. Uses Date.now() for timing measurements.
|
|
4246
|
+
*
|
|
4247
|
+
* @returns Hook configuration that can be passed to .withHooks()
|
|
4248
|
+
*
|
|
4249
|
+
* @example
|
|
4250
|
+
* ```typescript
|
|
4251
|
+
* // Basic timing
|
|
4252
|
+
* await LLMist.createAgent()
|
|
4253
|
+
* .withHooks(HookPresets.timing())
|
|
4254
|
+
* .withGadgets(Weather, Database)
|
|
4255
|
+
* .ask("What's the weather in NYC?");
|
|
4256
|
+
* // Output: ⏱️ LLM call took 1234ms
|
|
4257
|
+
* // ⏱️ Gadget Weather took 567ms
|
|
4258
|
+
* // ⏱️ LLM call took 890ms
|
|
4259
|
+
* ```
|
|
4260
|
+
*
|
|
4261
|
+
* @example
|
|
4262
|
+
* ```typescript
|
|
4263
|
+
* // Combined with logging for full context
|
|
4264
|
+
* .withHooks(HookPresets.merge(
|
|
4265
|
+
* HookPresets.logging(),
|
|
4266
|
+
* HookPresets.timing()
|
|
4267
|
+
* ))
|
|
4268
|
+
* ```
|
|
4269
|
+
*
|
|
4270
|
+
* @example
|
|
4271
|
+
* ```typescript
|
|
4272
|
+
* // Correlate performance with cost
|
|
4273
|
+
* .withHooks(HookPresets.merge(
|
|
4274
|
+
* HookPresets.timing(),
|
|
4275
|
+
* HookPresets.tokenTracking()
|
|
4276
|
+
* ))
|
|
4277
|
+
* ```
|
|
4278
|
+
*
|
|
4279
|
+
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetstiming | Full documentation}
|
|
4280
|
+
*/
|
|
4281
|
+
static timing() {
|
|
4282
|
+
const timings = /* @__PURE__ */ new Map();
|
|
4283
|
+
return {
|
|
4284
|
+
observers: {
|
|
4285
|
+
onLLMCallStart: async (ctx) => {
|
|
4286
|
+
timings.set(`llm-${ctx.iteration}`, Date.now());
|
|
4287
|
+
},
|
|
4288
|
+
onLLMCallComplete: async (ctx) => {
|
|
4289
|
+
const start = timings.get(`llm-${ctx.iteration}`);
|
|
4290
|
+
if (start) {
|
|
4291
|
+
const duration = Date.now() - start;
|
|
4292
|
+
console.log(`\u23F1\uFE0F LLM call took ${duration}ms`);
|
|
4293
|
+
timings.delete(`llm-${ctx.iteration}`);
|
|
4294
|
+
}
|
|
4295
|
+
},
|
|
4296
|
+
onGadgetExecutionStart: async (ctx) => {
|
|
4297
|
+
const key = `gadget-${ctx.gadgetName}-${Date.now()}`;
|
|
4298
|
+
timings.set(key, Date.now());
|
|
4299
|
+
ctx._timingKey = key;
|
|
4300
|
+
},
|
|
4301
|
+
onGadgetExecutionComplete: async (ctx) => {
|
|
4302
|
+
const key = ctx._timingKey;
|
|
4303
|
+
if (key) {
|
|
4304
|
+
const start = timings.get(key);
|
|
4305
|
+
if (start) {
|
|
4306
|
+
const duration = Date.now() - start;
|
|
4307
|
+
console.log(`\u23F1\uFE0F Gadget ${ctx.gadgetName} took ${duration}ms`);
|
|
4308
|
+
timings.delete(key);
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
};
|
|
4314
|
+
}
|
|
4315
|
+
/**
|
|
4316
|
+
* Tracks cumulative token usage across all LLM calls.
|
|
4317
|
+
*
|
|
4318
|
+
* **Output:**
|
|
4319
|
+
* - Per-call token count with 📊 emoji
|
|
4320
|
+
* - Cumulative total across all calls
|
|
4321
|
+
* - Call count for average calculations
|
|
4322
|
+
*
|
|
4323
|
+
* **Use cases:**
|
|
4324
|
+
* - Cost monitoring and budget tracking
|
|
4325
|
+
* - Optimizing prompts to reduce token usage
|
|
4326
|
+
* - Comparing token efficiency across different approaches
|
|
4327
|
+
* - Real-time cost estimation
|
|
4328
|
+
*
|
|
4329
|
+
* **Performance:** Minimal overhead. Simple counter increments.
|
|
4330
|
+
*
|
|
4331
|
+
* **Note:** Token counts depend on the provider's response. Some providers
|
|
4332
|
+
* may not include usage data, in which case counts won't be logged.
|
|
4333
|
+
*
|
|
4334
|
+
* @returns Hook configuration that can be passed to .withHooks()
|
|
4335
|
+
*
|
|
4336
|
+
* @example
|
|
4337
|
+
* ```typescript
|
|
4338
|
+
* // Basic token tracking
|
|
4339
|
+
* await LLMist.createAgent()
|
|
4340
|
+
* .withHooks(HookPresets.tokenTracking())
|
|
4341
|
+
* .ask("Summarize this document...");
|
|
4342
|
+
* // Output: 📊 Tokens this call: 1,234
|
|
4343
|
+
* // 📊 Total tokens: 1,234 (across 1 calls)
|
|
4344
|
+
* // 📊 Tokens this call: 567
|
|
4345
|
+
* // 📊 Total tokens: 1,801 (across 2 calls)
|
|
4346
|
+
* ```
|
|
4347
|
+
*
|
|
4348
|
+
* @example
|
|
4349
|
+
* ```typescript
|
|
4350
|
+
* // Cost calculation with custom hook
|
|
4351
|
+
* let totalTokens = 0;
|
|
4352
|
+
* .withHooks(HookPresets.merge(
|
|
4353
|
+
* HookPresets.tokenTracking(),
|
|
4354
|
+
* {
|
|
4355
|
+
* observers: {
|
|
4356
|
+
* onLLMCallComplete: async (ctx) => {
|
|
4357
|
+
* totalTokens += ctx.usage?.totalTokens ?? 0;
|
|
4358
|
+
* const cost = (totalTokens / 1_000_000) * 3.0; // $3 per 1M tokens
|
|
4359
|
+
* console.log(`💰 Estimated cost: $${cost.toFixed(4)}`);
|
|
4360
|
+
* },
|
|
4361
|
+
* },
|
|
4362
|
+
* }
|
|
4363
|
+
* ))
|
|
4364
|
+
* ```
|
|
4365
|
+
*
|
|
4366
|
+
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetstokentracking | Full documentation}
|
|
4367
|
+
*/
|
|
4368
|
+
static tokenTracking() {
|
|
4369
|
+
let totalTokens = 0;
|
|
4370
|
+
let totalCalls = 0;
|
|
4371
|
+
return {
|
|
4372
|
+
observers: {
|
|
4373
|
+
onLLMCallComplete: async (ctx) => {
|
|
4374
|
+
totalCalls++;
|
|
4375
|
+
if (ctx.usage?.totalTokens) {
|
|
4376
|
+
totalTokens += ctx.usage.totalTokens;
|
|
4377
|
+
console.log(`\u{1F4CA} Tokens this call: ${ctx.usage.totalTokens}`);
|
|
4378
|
+
console.log(`\u{1F4CA} Total tokens: ${totalTokens} (across ${totalCalls} calls)`);
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
};
|
|
4383
|
+
}
|
|
4384
|
+
/**
|
|
4385
|
+
* Tracks comprehensive progress metrics including iterations, tokens, cost, and timing.
|
|
4386
|
+
*
|
|
4387
|
+
* **This preset showcases llmist's core capabilities by demonstrating:**
|
|
4388
|
+
* - Observer pattern for non-intrusive monitoring
|
|
4389
|
+
* - Integration with ModelRegistry for cost estimation
|
|
4390
|
+
* - Callback-based architecture for flexible UI updates
|
|
4391
|
+
* - Provider-agnostic token and cost tracking
|
|
4392
|
+
*
|
|
4393
|
+
* Unlike `tokenTracking()` which only logs to console, this preset provides
|
|
4394
|
+
* structured data through callbacks, making it perfect for building custom UIs,
|
|
4395
|
+
* dashboards, or progress indicators (like the llmist CLI).
|
|
4396
|
+
*
|
|
4397
|
+
* **Output (when logProgress: true):**
|
|
4398
|
+
* - Iteration number and call count
|
|
4399
|
+
* - Cumulative token usage (input + output)
|
|
4400
|
+
* - Cumulative cost in USD (requires modelRegistry)
|
|
4401
|
+
* - Elapsed time in seconds
|
|
4402
|
+
*
|
|
4403
|
+
* **Use cases:**
|
|
4404
|
+
* - Building CLI progress indicators with live updates
|
|
4405
|
+
* - Creating web dashboards with real-time metrics
|
|
4406
|
+
* - Budget monitoring and cost alerts
|
|
4407
|
+
* - Performance tracking and optimization
|
|
4408
|
+
* - Custom logging to external systems (Datadog, CloudWatch, etc.)
|
|
4409
|
+
*
|
|
4410
|
+
* **Performance:** Minimal overhead. Uses Date.now() for timing and optional
|
|
4411
|
+
* ModelRegistry.estimateCost() which is O(1) lookup. Callback invocation is
|
|
4412
|
+
* synchronous and fast.
|
|
4413
|
+
*
|
|
4414
|
+
* @param options - Progress tracking options
|
|
4415
|
+
* @param options.modelRegistry - ModelRegistry for cost estimation (optional)
|
|
4416
|
+
* @param options.onProgress - Callback invoked after each LLM call (optional)
|
|
4417
|
+
* @param options.logProgress - Log progress to console (default: false)
|
|
4418
|
+
* @returns Hook configuration with progress tracking observers
|
|
4419
|
+
*
|
|
4420
|
+
* @example
|
|
4421
|
+
* ```typescript
|
|
4422
|
+
* // Basic usage with callback (RECOMMENDED - used by llmist CLI)
|
|
4423
|
+
* import { LLMist, HookPresets } from 'llmist';
|
|
4424
|
+
*
|
|
4425
|
+
* const client = LLMist.create();
|
|
4426
|
+
*
|
|
4427
|
+
* await client.agent()
|
|
4428
|
+
* .withHooks(HookPresets.progressTracking({
|
|
4429
|
+
* modelRegistry: client.modelRegistry,
|
|
4430
|
+
* onProgress: (stats) => {
|
|
4431
|
+
* // Update your UI with stats
|
|
4432
|
+
* console.log(`#${stats.currentIteration} | ${stats.totalTokens} tokens | $${stats.totalCost.toFixed(4)}`);
|
|
4433
|
+
* }
|
|
4434
|
+
* }))
|
|
4435
|
+
* .withGadgets(Calculator)
|
|
4436
|
+
* .ask("Calculate 15 * 23");
|
|
4437
|
+
* // Output: #1 | 245 tokens | $0.0012
|
|
4438
|
+
* ```
|
|
4439
|
+
*
|
|
4440
|
+
* @example
|
|
4441
|
+
* ```typescript
|
|
4442
|
+
* // Console logging mode (quick debugging)
|
|
4443
|
+
* await client.agent()
|
|
4444
|
+
* .withHooks(HookPresets.progressTracking({
|
|
4445
|
+
* modelRegistry: client.modelRegistry,
|
|
4446
|
+
* logProgress: true // Simple console output
|
|
4447
|
+
* }))
|
|
4448
|
+
* .ask("Your prompt");
|
|
4449
|
+
* // Output: 📊 Progress: Iteration #1 | 245 tokens | $0.0012 | 1.2s
|
|
4450
|
+
* ```
|
|
4451
|
+
*
|
|
4452
|
+
* @example
|
|
4453
|
+
* ```typescript
|
|
4454
|
+
* // Budget monitoring with alerts
|
|
4455
|
+
* const BUDGET_USD = 0.10;
|
|
4456
|
+
*
|
|
4457
|
+
* await client.agent()
|
|
4458
|
+
* .withHooks(HookPresets.progressTracking({
|
|
4459
|
+
* modelRegistry: client.modelRegistry,
|
|
4460
|
+
* onProgress: (stats) => {
|
|
4461
|
+
* if (stats.totalCost > BUDGET_USD) {
|
|
4462
|
+
* throw new Error(`Budget exceeded: $${stats.totalCost.toFixed(4)}`);
|
|
4463
|
+
* }
|
|
4464
|
+
* }
|
|
4465
|
+
* }))
|
|
4466
|
+
* .ask("Long running task...");
|
|
4467
|
+
* ```
|
|
4468
|
+
*
|
|
4469
|
+
* @example
|
|
4470
|
+
* ```typescript
|
|
4471
|
+
* // Web dashboard integration
|
|
4472
|
+
* let progressBar: HTMLElement;
|
|
4473
|
+
*
|
|
4474
|
+
* await client.agent()
|
|
4475
|
+
* .withHooks(HookPresets.progressTracking({
|
|
4476
|
+
* modelRegistry: client.modelRegistry,
|
|
4477
|
+
* onProgress: (stats) => {
|
|
4478
|
+
* // Update web UI in real-time
|
|
4479
|
+
* progressBar.textContent = `Iteration ${stats.currentIteration}`;
|
|
4480
|
+
* progressBar.dataset.cost = stats.totalCost.toFixed(4);
|
|
4481
|
+
* progressBar.dataset.tokens = stats.totalTokens.toString();
|
|
4482
|
+
* }
|
|
4483
|
+
* }))
|
|
4484
|
+
* .ask("Your prompt");
|
|
4485
|
+
* ```
|
|
4486
|
+
*
|
|
4487
|
+
* @example
|
|
4488
|
+
* ```typescript
|
|
4489
|
+
* // External logging (Datadog, CloudWatch, etc.)
|
|
4490
|
+
* await client.agent()
|
|
4491
|
+
* .withHooks(HookPresets.progressTracking({
|
|
4492
|
+
* modelRegistry: client.modelRegistry,
|
|
4493
|
+
* onProgress: async (stats) => {
|
|
4494
|
+
* await metrics.gauge('llm.iteration', stats.currentIteration);
|
|
4495
|
+
* await metrics.gauge('llm.cost', stats.totalCost);
|
|
4496
|
+
* await metrics.gauge('llm.tokens', stats.totalTokens);
|
|
4497
|
+
* }
|
|
4498
|
+
* }))
|
|
4499
|
+
* .ask("Your prompt");
|
|
4500
|
+
* ```
|
|
4501
|
+
*
|
|
4502
|
+
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsprogresstrackingoptions | Full documentation}
|
|
4503
|
+
* @see {@link ProgressTrackingOptions} for detailed options
|
|
4504
|
+
* @see {@link ProgressStats} for the callback data structure
|
|
4505
|
+
*/
|
|
4506
|
+
static progressTracking(options) {
|
|
4507
|
+
const { modelRegistry, onProgress, logProgress = false } = options ?? {};
|
|
4508
|
+
let totalCalls = 0;
|
|
4509
|
+
let currentIteration = 0;
|
|
4510
|
+
let totalInputTokens = 0;
|
|
4511
|
+
let totalOutputTokens = 0;
|
|
4512
|
+
let totalCost = 0;
|
|
4513
|
+
let totalGadgetCost = 0;
|
|
4514
|
+
const startTime = Date.now();
|
|
4515
|
+
return {
|
|
4516
|
+
observers: {
|
|
4517
|
+
// Track iteration on each LLM call start
|
|
4518
|
+
onLLMCallStart: async (ctx) => {
|
|
4519
|
+
currentIteration++;
|
|
4520
|
+
},
|
|
4521
|
+
// Accumulate metrics and report progress on each LLM call completion
|
|
4522
|
+
onLLMCallComplete: async (ctx) => {
|
|
4523
|
+
totalCalls++;
|
|
4524
|
+
if (ctx.usage) {
|
|
4525
|
+
totalInputTokens += ctx.usage.inputTokens;
|
|
4526
|
+
totalOutputTokens += ctx.usage.outputTokens;
|
|
4527
|
+
if (modelRegistry) {
|
|
4528
|
+
try {
|
|
4529
|
+
const modelName = ctx.options.model.includes(":") ? ctx.options.model.split(":")[1] : ctx.options.model;
|
|
4530
|
+
const costEstimate = modelRegistry.estimateCost(
|
|
4531
|
+
modelName,
|
|
4532
|
+
ctx.usage.inputTokens,
|
|
4533
|
+
ctx.usage.outputTokens,
|
|
4534
|
+
ctx.usage.cachedInputTokens ?? 0,
|
|
4535
|
+
ctx.usage.cacheCreationInputTokens ?? 0,
|
|
4536
|
+
ctx.usage.reasoningTokens ?? 0
|
|
4537
|
+
);
|
|
4538
|
+
if (costEstimate) {
|
|
4539
|
+
totalCost += costEstimate.totalCost;
|
|
4540
|
+
}
|
|
4541
|
+
} catch (error) {
|
|
4542
|
+
if (logProgress) {
|
|
4543
|
+
console.warn(`\u26A0\uFE0F Cost estimation failed:`, error);
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
}
|
|
4548
|
+
const stats = {
|
|
4549
|
+
currentIteration,
|
|
4550
|
+
totalCalls,
|
|
4551
|
+
totalInputTokens,
|
|
4552
|
+
totalOutputTokens,
|
|
4553
|
+
totalTokens: totalInputTokens + totalOutputTokens,
|
|
4554
|
+
totalCost: totalCost + totalGadgetCost,
|
|
4555
|
+
elapsedSeconds: Number(((Date.now() - startTime) / 1e3).toFixed(1))
|
|
4556
|
+
};
|
|
4557
|
+
if (onProgress) {
|
|
4558
|
+
onProgress(stats);
|
|
4559
|
+
}
|
|
4560
|
+
if (logProgress) {
|
|
4561
|
+
const formattedTokens = stats.totalTokens >= 1e3 ? `${(stats.totalTokens / 1e3).toFixed(1)}k` : `${stats.totalTokens}`;
|
|
4562
|
+
const formattedCost = stats.totalCost > 0 ? `$${stats.totalCost.toFixed(4)}` : "$0";
|
|
4563
|
+
console.log(
|
|
4564
|
+
`\u{1F4CA} Progress: Iteration #${stats.currentIteration} | ${formattedTokens} tokens | ${formattedCost} | ${stats.elapsedSeconds}s`
|
|
4565
|
+
);
|
|
4566
|
+
}
|
|
4567
|
+
},
|
|
4568
|
+
// Track gadget execution costs
|
|
4569
|
+
onGadgetExecutionComplete: async (ctx) => {
|
|
4570
|
+
if (ctx.cost && ctx.cost > 0) {
|
|
4571
|
+
totalGadgetCost += ctx.cost;
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
};
|
|
4576
|
+
}
|
|
4577
|
+
/**
|
|
4578
|
+
* Logs detailed error information for debugging and troubleshooting.
|
|
4579
|
+
*
|
|
4580
|
+
* **Output:**
|
|
4581
|
+
* - LLM errors with ❌ emoji, including model and recovery status
|
|
4582
|
+
* - Gadget errors with full context (parameters, error message)
|
|
4583
|
+
* - Separate logging for LLM and gadget failures
|
|
4584
|
+
*
|
|
4585
|
+
* **Use cases:**
|
|
4586
|
+
* - Troubleshooting production issues
|
|
4587
|
+
* - Understanding error patterns and frequency
|
|
4588
|
+
* - Debugging error recovery behavior
|
|
4589
|
+
* - Collecting error metrics for monitoring
|
|
4590
|
+
*
|
|
4591
|
+
* **Performance:** Minimal overhead. Only logs when errors occur.
|
|
4592
|
+
*
|
|
4593
|
+
* @returns Hook configuration that can be passed to .withHooks()
|
|
4594
|
+
*
|
|
4595
|
+
* @example
|
|
4596
|
+
* ```typescript
|
|
4597
|
+
* // Basic error logging
|
|
4598
|
+
* await LLMist.createAgent()
|
|
4599
|
+
* .withHooks(HookPresets.errorLogging())
|
|
4600
|
+
* .withGadgets(Database)
|
|
4601
|
+
* .ask("Fetch user data");
|
|
4602
|
+
* // Output (on LLM error): ❌ LLM Error (iteration 1): Rate limit exceeded
|
|
4603
|
+
* // Model: gpt-5-nano
|
|
4604
|
+
* // Recovered: true
|
|
4605
|
+
* // Output (on gadget error): ❌ Gadget Error: Database
|
|
4606
|
+
* // Error: Connection timeout
|
|
4607
|
+
* // Parameters: {...}
|
|
4608
|
+
* ```
|
|
4609
|
+
*
|
|
4610
|
+
* @example
|
|
4611
|
+
* ```typescript
|
|
4612
|
+
* // Combine with monitoring for full context
|
|
4613
|
+
* .withHooks(HookPresets.merge(
|
|
4614
|
+
* HookPresets.monitoring(), // Includes errorLogging
|
|
4615
|
+
* customErrorAnalytics
|
|
4616
|
+
* ))
|
|
4617
|
+
* ```
|
|
4618
|
+
*
|
|
4619
|
+
* @example
|
|
4620
|
+
* ```typescript
|
|
4621
|
+
* // Error analytics collection
|
|
4622
|
+
* const errors: any[] = [];
|
|
4623
|
+
* .withHooks(HookPresets.merge(
|
|
4624
|
+
* HookPresets.errorLogging(),
|
|
4625
|
+
* {
|
|
4626
|
+
* observers: {
|
|
4627
|
+
* onLLMCallError: async (ctx) => {
|
|
4628
|
+
* errors.push({ type: 'llm', error: ctx.error, recovered: ctx.recovered });
|
|
4629
|
+
* },
|
|
4630
|
+
* },
|
|
4631
|
+
* }
|
|
4632
|
+
* ))
|
|
4633
|
+
* ```
|
|
4634
|
+
*
|
|
4635
|
+
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetserrorlogging | Full documentation}
|
|
4636
|
+
*/
|
|
4637
|
+
static errorLogging() {
|
|
4638
|
+
return {
|
|
4639
|
+
observers: {
|
|
4640
|
+
onLLMCallError: async (ctx) => {
|
|
4641
|
+
console.error(`\u274C LLM Error (iteration ${ctx.iteration}):`, ctx.error.message);
|
|
4642
|
+
console.error(` Model: ${ctx.options.model}`);
|
|
4643
|
+
console.error(` Recovered: ${ctx.recovered}`);
|
|
4644
|
+
},
|
|
4645
|
+
onGadgetExecutionComplete: async (ctx) => {
|
|
4646
|
+
if (ctx.error) {
|
|
4647
|
+
console.error(`\u274C Gadget Error: ${ctx.gadgetName}`);
|
|
4648
|
+
console.error(` Error: ${ctx.error}`);
|
|
4649
|
+
console.error(` Parameters:`, JSON.stringify(ctx.parameters, null, 2));
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
};
|
|
4654
|
+
}
|
|
4655
|
+
/**
|
|
4656
|
+
* Tracks context compaction events.
|
|
4657
|
+
*
|
|
4658
|
+
* **Output:**
|
|
4659
|
+
* - Compaction events with 🗜️ emoji
|
|
4660
|
+
* - Strategy name, tokens before/after, and savings
|
|
4661
|
+
* - Cumulative statistics
|
|
4662
|
+
*
|
|
4663
|
+
* **Use cases:**
|
|
4664
|
+
* - Monitoring long-running conversations
|
|
4665
|
+
* - Understanding when and how compaction occurs
|
|
4666
|
+
* - Debugging context management issues
|
|
4667
|
+
*
|
|
4668
|
+
* **Performance:** Minimal overhead. Simple console output.
|
|
4669
|
+
*
|
|
4670
|
+
* @returns Hook configuration that can be passed to .withHooks()
|
|
4671
|
+
*
|
|
4672
|
+
* @example
|
|
4673
|
+
* ```typescript
|
|
4674
|
+
* await LLMist.createAgent()
|
|
4675
|
+
* .withHooks(HookPresets.compactionTracking())
|
|
4676
|
+
* .ask("Your prompt");
|
|
4677
|
+
* ```
|
|
4678
|
+
*/
|
|
4679
|
+
static compactionTracking() {
|
|
4680
|
+
return {
|
|
4681
|
+
observers: {
|
|
4682
|
+
onCompaction: async (ctx) => {
|
|
4683
|
+
const saved = ctx.event.tokensBefore - ctx.event.tokensAfter;
|
|
4684
|
+
const percent = (saved / ctx.event.tokensBefore * 100).toFixed(1);
|
|
4685
|
+
console.log(
|
|
4686
|
+
`\u{1F5DC}\uFE0F Compaction (${ctx.event.strategy}): ${ctx.event.tokensBefore} \u2192 ${ctx.event.tokensAfter} tokens (saved ${saved}, ${percent}%)`
|
|
4687
|
+
);
|
|
4688
|
+
console.log(` Messages: ${ctx.event.messagesBefore} \u2192 ${ctx.event.messagesAfter}`);
|
|
4689
|
+
if (ctx.stats.totalCompactions > 1) {
|
|
4690
|
+
console.log(
|
|
4691
|
+
` Cumulative: ${ctx.stats.totalCompactions} compactions, ${ctx.stats.totalTokensSaved} tokens saved`
|
|
4692
|
+
);
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
};
|
|
4697
|
+
}
|
|
4698
|
+
/**
|
|
4699
|
+
* Logs LLM requests and responses to files for debugging and audit trails.
|
|
4700
|
+
*
|
|
4701
|
+
* Files are named `{counter}.request` and `{counter}.response` where counter
|
|
4702
|
+
* is a zero-padded number that increments with each LLM call.
|
|
4703
|
+
*
|
|
4704
|
+
* **Output:**
|
|
4705
|
+
* - Request files containing formatted LLM message history
|
|
4706
|
+
* - Response files containing raw LLM output
|
|
4707
|
+
*
|
|
4708
|
+
* **Use cases:**
|
|
4709
|
+
* - Debugging complex agent interactions
|
|
4710
|
+
* - Creating audit trails for compliance
|
|
4711
|
+
* - Analyzing LLM behavior patterns
|
|
4712
|
+
* - Replaying conversations for testing
|
|
4713
|
+
*
|
|
4714
|
+
* **Performance:** Minimal overhead - only file I/O, no synchronous blocking.
|
|
4715
|
+
*
|
|
4716
|
+
* **Note:** Can also be enabled via `LLMIST_LOG_RAW_DIRECTORY` environment
|
|
4717
|
+
* variable for zero-code activation.
|
|
4718
|
+
*
|
|
4719
|
+
* @param options - File logging options
|
|
4720
|
+
* @param options.directory - Directory where log files will be written
|
|
4721
|
+
* @param options.startingCounter - Starting counter (default: 1)
|
|
4722
|
+
* @param options.counterPadding - Number of digits for padding (default: 4)
|
|
4723
|
+
* @param options.skipSubagents - Skip subagent calls (default: true)
|
|
4724
|
+
* @param options.formatRequest - Custom request formatter
|
|
4725
|
+
* @param options.onFileWritten - Callback after each file is written
|
|
4726
|
+
* @returns Hook configuration that can be passed to .withHooks()
|
|
4727
|
+
*
|
|
4728
|
+
* @example
|
|
4729
|
+
* ```typescript
|
|
4730
|
+
* // Basic file logging
|
|
4731
|
+
* await LLMist.createAgent()
|
|
4732
|
+
* .withHooks(HookPresets.fileLogging({
|
|
4733
|
+
* directory: './debug-logs'
|
|
4734
|
+
* }))
|
|
4735
|
+
* .ask("Hello");
|
|
4736
|
+
* // Creates: ./debug-logs/0001.request
|
|
4737
|
+
* // ./debug-logs/0001.response
|
|
4738
|
+
* ```
|
|
4739
|
+
*
|
|
4740
|
+
* @example
|
|
4741
|
+
* ```typescript
|
|
4742
|
+
* // With callback for tracking
|
|
4743
|
+
* await LLMist.createAgent()
|
|
4744
|
+
* .withHooks(HookPresets.fileLogging({
|
|
4745
|
+
* directory: './logs',
|
|
4746
|
+
* onFileWritten: (info) => {
|
|
4747
|
+
* console.log(`Wrote ${info.type}: ${info.filePath}`);
|
|
4748
|
+
* }
|
|
4749
|
+
* }))
|
|
4750
|
+
* .ask("Hello");
|
|
4751
|
+
* ```
|
|
4752
|
+
*
|
|
4753
|
+
* @example
|
|
4754
|
+
* ```typescript
|
|
4755
|
+
* // Combined with other presets
|
|
4756
|
+
* .withHooks(HookPresets.merge(
|
|
4757
|
+
* HookPresets.fileLogging({ directory: logDir }),
|
|
4758
|
+
* HookPresets.progressTracking({ onProgress: updateUI }),
|
|
4759
|
+
* HookPresets.errorLogging()
|
|
4760
|
+
* ))
|
|
4761
|
+
* ```
|
|
4762
|
+
*
|
|
4763
|
+
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsfileloggingoptions | Full documentation}
|
|
4764
|
+
*/
|
|
4765
|
+
static fileLogging(options) {
|
|
4766
|
+
return createFileLoggingHooks(options);
|
|
4767
|
+
}
|
|
4768
|
+
/**
|
|
4769
|
+
* Returns empty hook configuration for clean output without any logging.
|
|
4770
|
+
*
|
|
4771
|
+
* **Output:**
|
|
4772
|
+
* - None. Returns {} (empty object).
|
|
4773
|
+
*
|
|
4774
|
+
* **Use cases:**
|
|
4775
|
+
* - Clean test output without console noise
|
|
4776
|
+
* - Production environments where logging is handled externally
|
|
4777
|
+
* - Baseline for custom hook development
|
|
4778
|
+
* - Temporary disable of all hook output
|
|
4779
|
+
*
|
|
4780
|
+
* **Performance:** Zero overhead. No-op hook configuration.
|
|
4781
|
+
*
|
|
4782
|
+
* @returns Empty hook configuration
|
|
4783
|
+
*
|
|
4784
|
+
* @example
|
|
4785
|
+
* ```typescript
|
|
4786
|
+
* // Clean test output
|
|
4787
|
+
* describe('Agent tests', () => {
|
|
4788
|
+
* it('should calculate correctly', async () => {
|
|
4789
|
+
* const result = await LLMist.createAgent()
|
|
4790
|
+
* .withHooks(HookPresets.silent()) // No console output
|
|
4791
|
+
* .withGadgets(Calculator)
|
|
4792
|
+
* .askAndCollect("What is 15 times 23?");
|
|
4793
|
+
*
|
|
4794
|
+
* expect(result).toContain("345");
|
|
4795
|
+
* });
|
|
4796
|
+
* });
|
|
4797
|
+
* ```
|
|
4798
|
+
*
|
|
4799
|
+
* @example
|
|
4800
|
+
* ```typescript
|
|
4801
|
+
* // Conditional silence based on environment
|
|
4802
|
+
* const isTesting = process.env.NODE_ENV === 'test';
|
|
4803
|
+
* .withHooks(isTesting ? HookPresets.silent() : HookPresets.monitoring())
|
|
4804
|
+
* ```
|
|
4805
|
+
*
|
|
4806
|
+
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetssilent | Full documentation}
|
|
4807
|
+
*/
|
|
4808
|
+
static silent() {
|
|
4809
|
+
return {};
|
|
4810
|
+
}
|
|
4811
|
+
/**
|
|
4812
|
+
* Combines multiple hook configurations into one.
|
|
4813
|
+
*
|
|
4814
|
+
* Merge allows you to compose preset and custom hooks for modular monitoring
|
|
4815
|
+
* configurations. Understanding merge behavior is crucial for proper composition.
|
|
4816
|
+
*
|
|
4817
|
+
* **Merge behavior:**
|
|
4818
|
+
* - **Observers:** Composed - all handlers run sequentially in order
|
|
4819
|
+
* - **Interceptors:** Last one wins - only the last interceptor applies
|
|
4820
|
+
* - **Controllers:** Last one wins - only the last controller applies
|
|
4821
|
+
*
|
|
4822
|
+
* **Why interceptors/controllers don't compose:**
|
|
4823
|
+
* - Interceptors have different signatures per method, making composition impractical
|
|
4824
|
+
* - Controllers return specific actions that can't be meaningfully combined
|
|
4825
|
+
* - Only observers support composition because they're read-only and independent
|
|
4826
|
+
*
|
|
4827
|
+
* **Use cases:**
|
|
4828
|
+
* - Combining multiple presets (logging + timing + tokens)
|
|
4829
|
+
* - Adding custom hooks to presets
|
|
4830
|
+
* - Building modular, reusable monitoring configurations
|
|
4831
|
+
* - Environment-specific hook composition
|
|
4832
|
+
*
|
|
4833
|
+
* **Performance:** Minimal overhead for merging. Runtime performance depends on merged hooks.
|
|
4834
|
+
*
|
|
4835
|
+
* @param hookSets - Variable number of hook configurations to merge
|
|
4836
|
+
* @returns Single merged hook configuration with composed/overridden handlers
|
|
4837
|
+
*
|
|
4838
|
+
* @example
|
|
4839
|
+
* ```typescript
|
|
4840
|
+
* // Combine multiple presets
|
|
4841
|
+
* .withHooks(HookPresets.merge(
|
|
4842
|
+
* HookPresets.logging(),
|
|
4843
|
+
* HookPresets.timing(),
|
|
4844
|
+
* HookPresets.tokenTracking()
|
|
4845
|
+
* ))
|
|
4846
|
+
* // All observers from all three presets will run
|
|
4847
|
+
* ```
|
|
4848
|
+
*
|
|
4849
|
+
* @example
|
|
4850
|
+
* ```typescript
|
|
4851
|
+
* // Add custom observer to preset (both run)
|
|
4852
|
+
* .withHooks(HookPresets.merge(
|
|
4853
|
+
* HookPresets.timing(),
|
|
4854
|
+
* {
|
|
4855
|
+
* observers: {
|
|
4856
|
+
* onLLMCallComplete: async (ctx) => {
|
|
4857
|
+
* await saveMetrics({ tokens: ctx.usage?.totalTokens });
|
|
4858
|
+
* },
|
|
4859
|
+
* },
|
|
4860
|
+
* }
|
|
4861
|
+
* ))
|
|
4862
|
+
* ```
|
|
4863
|
+
*
|
|
4864
|
+
* @example
|
|
4865
|
+
* ```typescript
|
|
4866
|
+
* // Multiple interceptors (last wins!)
|
|
4867
|
+
* .withHooks(HookPresets.merge(
|
|
4868
|
+
* {
|
|
4869
|
+
* interceptors: {
|
|
4870
|
+
* interceptTextChunk: (chunk) => chunk.toUpperCase(), // Ignored
|
|
4871
|
+
* },
|
|
4872
|
+
* },
|
|
4873
|
+
* {
|
|
4874
|
+
* interceptors: {
|
|
4875
|
+
* interceptTextChunk: (chunk) => chunk.toLowerCase(), // This wins
|
|
4876
|
+
* },
|
|
4877
|
+
* }
|
|
4878
|
+
* ))
|
|
4879
|
+
* // Result: text will be lowercase
|
|
4880
|
+
* ```
|
|
4881
|
+
*
|
|
4882
|
+
* @example
|
|
4883
|
+
* ```typescript
|
|
4884
|
+
* // Modular environment-based configuration
|
|
4885
|
+
* const baseHooks = HookPresets.errorLogging();
|
|
4886
|
+
* const devHooks = HookPresets.merge(baseHooks, HookPresets.monitoring({ verbose: true }));
|
|
4887
|
+
* const prodHooks = HookPresets.merge(baseHooks, HookPresets.tokenTracking());
|
|
4888
|
+
*
|
|
4889
|
+
* const hooks = process.env.NODE_ENV === 'production' ? prodHooks : devHooks;
|
|
4890
|
+
* .withHooks(hooks)
|
|
4891
|
+
* ```
|
|
4892
|
+
*
|
|
4893
|
+
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsmergehooksets | Full documentation}
|
|
4894
|
+
*/
|
|
4895
|
+
static merge(...hookSets) {
|
|
4896
|
+
const merged = {
|
|
4897
|
+
observers: {},
|
|
4898
|
+
interceptors: {},
|
|
4899
|
+
controllers: {}
|
|
4900
|
+
};
|
|
4901
|
+
for (const hooks of hookSets) {
|
|
4902
|
+
if (hooks.observers) {
|
|
4903
|
+
for (const [key, handler] of Object.entries(hooks.observers)) {
|
|
4904
|
+
const typedKey = key;
|
|
4905
|
+
if (merged.observers[typedKey]) {
|
|
4906
|
+
const existing = merged.observers[typedKey];
|
|
4907
|
+
merged.observers[typedKey] = async (ctx) => {
|
|
4908
|
+
await existing(ctx);
|
|
4909
|
+
await handler(ctx);
|
|
4910
|
+
};
|
|
4911
|
+
} else {
|
|
4912
|
+
merged.observers[typedKey] = handler;
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
if (hooks.interceptors) {
|
|
4917
|
+
Object.assign(merged.interceptors, hooks.interceptors);
|
|
4918
|
+
}
|
|
4919
|
+
if (hooks.controllers) {
|
|
4920
|
+
Object.assign(merged.controllers, hooks.controllers);
|
|
4921
|
+
}
|
|
4922
|
+
}
|
|
4923
|
+
return merged;
|
|
4924
|
+
}
|
|
4925
|
+
/**
|
|
4926
|
+
* Composite preset combining logging, timing, tokenTracking, and errorLogging.
|
|
4927
|
+
*
|
|
4928
|
+
* This is the recommended preset for development and initial production deployments,
|
|
4929
|
+
* providing comprehensive observability with a single method call.
|
|
4930
|
+
*
|
|
4931
|
+
* **Includes:**
|
|
4932
|
+
* - All output from `logging()` preset (with optional verbosity)
|
|
4933
|
+
* - All output from `timing()` preset (execution times)
|
|
4934
|
+
* - All output from `tokenTracking()` preset (token usage)
|
|
4935
|
+
* - All output from `errorLogging()` preset (error details)
|
|
4936
|
+
*
|
|
4937
|
+
* **Output format:**
|
|
4938
|
+
* - Event logging: [LLM]/[GADGET] messages
|
|
4939
|
+
* - Timing: ⏱️ emoji with milliseconds
|
|
4940
|
+
* - Tokens: 📊 emoji with per-call and cumulative counts
|
|
4941
|
+
* - Errors: ❌ emoji with full error details
|
|
4942
|
+
*
|
|
4943
|
+
* **Use cases:**
|
|
4944
|
+
* - Full observability during development
|
|
4945
|
+
* - Comprehensive monitoring in production
|
|
4946
|
+
* - One-liner for complete agent visibility
|
|
4947
|
+
* - Troubleshooting and debugging with full context
|
|
4948
|
+
*
|
|
4949
|
+
* **Performance:** Combined overhead of all four presets, but still minimal in practice.
|
|
4950
|
+
*
|
|
4951
|
+
* @param options - Monitoring options
|
|
4952
|
+
* @param options.verbose - Passed to logging() preset for detailed output. Default: false
|
|
4953
|
+
* @returns Merged hook configuration combining all monitoring presets
|
|
4954
|
+
*
|
|
4955
|
+
* @example
|
|
4956
|
+
* ```typescript
|
|
4957
|
+
* // Basic monitoring (recommended for development)
|
|
4958
|
+
* await LLMist.createAgent()
|
|
4959
|
+
* .withHooks(HookPresets.monitoring())
|
|
4960
|
+
* .withGadgets(Calculator, Weather)
|
|
4961
|
+
* .ask("What is 15 times 23, and what's the weather in NYC?");
|
|
4962
|
+
* // Output: All events, timing, tokens, and errors in one place
|
|
4963
|
+
* ```
|
|
4964
|
+
*
|
|
4965
|
+
* @example
|
|
4966
|
+
* ```typescript
|
|
4967
|
+
* // Verbose monitoring with full details
|
|
4968
|
+
* await LLMist.createAgent()
|
|
4969
|
+
* .withHooks(HookPresets.monitoring({ verbose: true }))
|
|
4970
|
+
* .ask("Your prompt");
|
|
4971
|
+
* // Output includes: parameters, results, and complete responses
|
|
4972
|
+
* ```
|
|
4973
|
+
*
|
|
4974
|
+
* @example
|
|
4975
|
+
* ```typescript
|
|
4976
|
+
* // Environment-based monitoring
|
|
4977
|
+
* const isDev = process.env.NODE_ENV === 'development';
|
|
4978
|
+
* .withHooks(HookPresets.monitoring({ verbose: isDev }))
|
|
4979
|
+
* ```
|
|
4980
|
+
*
|
|
4981
|
+
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsmonitoringoptions | Full documentation}
|
|
4982
|
+
*/
|
|
4983
|
+
static monitoring(options = {}) {
|
|
4984
|
+
return _HookPresets.merge(
|
|
4985
|
+
_HookPresets.logging(options),
|
|
4986
|
+
_HookPresets.timing(),
|
|
4987
|
+
_HookPresets.tokenTracking(),
|
|
4988
|
+
_HookPresets.errorLogging()
|
|
4989
|
+
);
|
|
4990
|
+
}
|
|
4991
|
+
};
|
|
4992
|
+
}
|
|
4993
|
+
});
|
|
4994
|
+
|
|
4042
4995
|
// src/providers/anthropic-models.ts
|
|
4043
4996
|
var ANTHROPIC_MODELS;
|
|
4044
4997
|
var init_anthropic_models = __esm({
|
|
@@ -4077,10 +5030,10 @@ var init_anthropic_models = __esm({
|
|
|
4077
5030
|
contextWindow: 2e5,
|
|
4078
5031
|
maxOutputTokens: 64e3,
|
|
4079
5032
|
pricing: {
|
|
4080
|
-
input:
|
|
4081
|
-
output:
|
|
4082
|
-
cachedInput: 0.
|
|
4083
|
-
cacheWriteInput: 1
|
|
5033
|
+
input: 1,
|
|
5034
|
+
output: 5,
|
|
5035
|
+
cachedInput: 0.1,
|
|
5036
|
+
cacheWriteInput: 1.25
|
|
4084
5037
|
},
|
|
4085
5038
|
knowledgeCutoff: "2025-02",
|
|
4086
5039
|
features: {
|
|
@@ -4276,10 +5229,10 @@ var init_anthropic_models = __esm({
|
|
|
4276
5229
|
contextWindow: 2e5,
|
|
4277
5230
|
maxOutputTokens: 64e3,
|
|
4278
5231
|
pricing: {
|
|
4279
|
-
input:
|
|
4280
|
-
output:
|
|
4281
|
-
cachedInput: 0.
|
|
4282
|
-
cacheWriteInput: 1
|
|
5232
|
+
input: 1,
|
|
5233
|
+
output: 5,
|
|
5234
|
+
cachedInput: 0.1,
|
|
5235
|
+
cacheWriteInput: 1.25
|
|
4283
5236
|
},
|
|
4284
5237
|
knowledgeCutoff: "2025-02",
|
|
4285
5238
|
features: {
|
|
@@ -4422,10 +5375,15 @@ var init_utils = __esm({
|
|
|
4422
5375
|
});
|
|
4423
5376
|
|
|
4424
5377
|
// src/providers/anthropic.ts
|
|
5378
|
+
function resolveAnthropicThinking(reasoning) {
|
|
5379
|
+
if (!reasoning?.enabled) return void 0;
|
|
5380
|
+
const budget = reasoning.budgetTokens ? Math.max(1024, reasoning.budgetTokens) : ANTHROPIC_EFFORT_BUDGET[reasoning.effort ?? "medium"];
|
|
5381
|
+
return { type: "enabled", budget_tokens: budget };
|
|
5382
|
+
}
|
|
4425
5383
|
function createAnthropicProviderFromEnv() {
|
|
4426
5384
|
return createProviderFromEnv("ANTHROPIC_API_KEY", import_sdk.default, AnthropicMessagesProvider);
|
|
4427
5385
|
}
|
|
4428
|
-
var import_sdk, AnthropicMessagesProvider;
|
|
5386
|
+
var import_sdk, ANTHROPIC_EFFORT_BUDGET, AnthropicMessagesProvider;
|
|
4429
5387
|
var init_anthropic = __esm({
|
|
4430
5388
|
"src/providers/anthropic.ts"() {
|
|
4431
5389
|
"use strict";
|
|
@@ -4435,6 +5393,14 @@ var init_anthropic = __esm({
|
|
|
4435
5393
|
init_base_provider();
|
|
4436
5394
|
init_constants2();
|
|
4437
5395
|
init_utils();
|
|
5396
|
+
ANTHROPIC_EFFORT_BUDGET = {
|
|
5397
|
+
none: 1024,
|
|
5398
|
+
// Minimum allowed by Anthropic
|
|
5399
|
+
low: 2048,
|
|
5400
|
+
medium: 8192,
|
|
5401
|
+
high: 16384,
|
|
5402
|
+
maximum: 32768
|
|
5403
|
+
};
|
|
4438
5404
|
AnthropicMessagesProvider = class extends BaseProviderAdapter {
|
|
4439
5405
|
providerId = "anthropic";
|
|
4440
5406
|
supports(descriptor) {
|
|
@@ -4488,15 +5454,18 @@ var init_anthropic = __esm({
|
|
|
4488
5454
|
)
|
|
4489
5455
|
}));
|
|
4490
5456
|
const defaultMaxTokens = spec?.maxOutputTokens ?? ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS;
|
|
5457
|
+
const thinking = resolveAnthropicThinking(options.reasoning);
|
|
5458
|
+
const temperature = thinking ? void 0 : options.temperature;
|
|
4491
5459
|
const payload = {
|
|
4492
5460
|
model: descriptor.name,
|
|
4493
5461
|
system,
|
|
4494
5462
|
messages: conversation,
|
|
4495
5463
|
max_tokens: options.maxTokens ?? defaultMaxTokens,
|
|
4496
|
-
temperature
|
|
5464
|
+
temperature,
|
|
4497
5465
|
top_p: options.topP,
|
|
4498
5466
|
stop_sequences: options.stopSequences,
|
|
4499
5467
|
stream: true,
|
|
5468
|
+
...thinking ? { thinking } : {},
|
|
4500
5469
|
...options.extra
|
|
4501
5470
|
};
|
|
4502
5471
|
return payload;
|
|
@@ -4576,8 +5545,39 @@ var init_anthropic = __esm({
|
|
|
4576
5545
|
};
|
|
4577
5546
|
continue;
|
|
4578
5547
|
}
|
|
4579
|
-
if (event.type === "
|
|
4580
|
-
|
|
5548
|
+
if (event.type === "content_block_start") {
|
|
5549
|
+
const block = event.content_block;
|
|
5550
|
+
if (block.type === "thinking") {
|
|
5551
|
+
yield { text: "", thinking: { content: "", type: "thinking" }, rawEvent: event };
|
|
5552
|
+
continue;
|
|
5553
|
+
}
|
|
5554
|
+
if (block.type === "redacted_thinking") {
|
|
5555
|
+
yield { text: "", thinking: { content: "", type: "redacted" }, rawEvent: event };
|
|
5556
|
+
continue;
|
|
5557
|
+
}
|
|
5558
|
+
}
|
|
5559
|
+
if (event.type === "content_block_delta") {
|
|
5560
|
+
const delta = event.delta;
|
|
5561
|
+
if (delta.type === "thinking_delta" && delta.thinking) {
|
|
5562
|
+
yield {
|
|
5563
|
+
text: "",
|
|
5564
|
+
thinking: { content: delta.thinking, type: "thinking" },
|
|
5565
|
+
rawEvent: event
|
|
5566
|
+
};
|
|
5567
|
+
continue;
|
|
5568
|
+
}
|
|
5569
|
+
if (delta.type === "signature_delta" && delta.signature) {
|
|
5570
|
+
yield {
|
|
5571
|
+
text: "",
|
|
5572
|
+
thinking: { content: "", type: "thinking", signature: delta.signature },
|
|
5573
|
+
rawEvent: event
|
|
5574
|
+
};
|
|
5575
|
+
continue;
|
|
5576
|
+
}
|
|
5577
|
+
if (delta.type === "text_delta") {
|
|
5578
|
+
yield { text: delta.text ?? "", rawEvent: event };
|
|
5579
|
+
continue;
|
|
5580
|
+
}
|
|
4581
5581
|
continue;
|
|
4582
5582
|
}
|
|
4583
5583
|
if (event.type === "message_delta") {
|
|
@@ -4886,10 +5886,10 @@ var init_gemini_models = __esm({
|
|
|
4886
5886
|
contextWindow: 1048576,
|
|
4887
5887
|
maxOutputTokens: 65536,
|
|
4888
5888
|
pricing: {
|
|
4889
|
-
input: 0.
|
|
4890
|
-
// $0.
|
|
5889
|
+
input: 0.5,
|
|
5890
|
+
// $0.50 for text/image/video
|
|
4891
5891
|
output: 3,
|
|
4892
|
-
cachedInput: 0.
|
|
5892
|
+
cachedInput: 0.05
|
|
4893
5893
|
},
|
|
4894
5894
|
knowledgeCutoff: "2025-01",
|
|
4895
5895
|
features: {
|
|
@@ -5183,6 +6183,23 @@ var init_gemini_speech_models = __esm({
|
|
|
5183
6183
|
});
|
|
5184
6184
|
|
|
5185
6185
|
// src/providers/gemini.ts
|
|
6186
|
+
function resolveGeminiThinkingConfig(reasoning, modelName) {
|
|
6187
|
+
if (!reasoning?.enabled) return void 0;
|
|
6188
|
+
const isGemini3 = modelName.includes("gemini-3");
|
|
6189
|
+
if (isGemini3) {
|
|
6190
|
+
return {
|
|
6191
|
+
thinkingConfig: {
|
|
6192
|
+
thinkingLevel: GEMINI3_THINKING_LEVEL[reasoning.effort ?? "medium"]
|
|
6193
|
+
}
|
|
6194
|
+
};
|
|
6195
|
+
}
|
|
6196
|
+
const budget = reasoning.budgetTokens ?? GEMINI25_THINKING_BUDGET[reasoning.effort ?? "medium"];
|
|
6197
|
+
return {
|
|
6198
|
+
thinkingConfig: {
|
|
6199
|
+
thinkingBudget: budget
|
|
6200
|
+
}
|
|
6201
|
+
};
|
|
6202
|
+
}
|
|
5186
6203
|
function wrapPcmInWav(pcmData, sampleRate, bitsPerSample, numChannels) {
|
|
5187
6204
|
const byteRate = sampleRate * numChannels * bitsPerSample / 8;
|
|
5188
6205
|
const blockAlign = numChannels * bitsPerSample / 8;
|
|
@@ -5211,7 +6228,7 @@ function wrapPcmInWav(pcmData, sampleRate, bitsPerSample, numChannels) {
|
|
|
5211
6228
|
function createGeminiProviderFromEnv() {
|
|
5212
6229
|
return createProviderFromEnv("GEMINI_API_KEY", import_genai.GoogleGenAI, GeminiGenerativeProvider);
|
|
5213
6230
|
}
|
|
5214
|
-
var import_genai, GEMINI_ROLE_MAP, GeminiGenerativeProvider;
|
|
6231
|
+
var import_genai, GEMINI3_THINKING_LEVEL, GEMINI25_THINKING_BUDGET, GEMINI_ROLE_MAP, GeminiGenerativeProvider;
|
|
5215
6232
|
var init_gemini = __esm({
|
|
5216
6233
|
"src/providers/gemini.ts"() {
|
|
5217
6234
|
"use strict";
|
|
@@ -5223,6 +6240,20 @@ var init_gemini = __esm({
|
|
|
5223
6240
|
init_gemini_models();
|
|
5224
6241
|
init_gemini_speech_models();
|
|
5225
6242
|
init_utils();
|
|
6243
|
+
GEMINI3_THINKING_LEVEL = {
|
|
6244
|
+
none: "minimal",
|
|
6245
|
+
low: "low",
|
|
6246
|
+
medium: "medium",
|
|
6247
|
+
high: "high",
|
|
6248
|
+
maximum: "high"
|
|
6249
|
+
};
|
|
6250
|
+
GEMINI25_THINKING_BUDGET = {
|
|
6251
|
+
none: 0,
|
|
6252
|
+
low: 2048,
|
|
6253
|
+
medium: 8192,
|
|
6254
|
+
high: 16384,
|
|
6255
|
+
maximum: 24576
|
|
6256
|
+
};
|
|
5226
6257
|
GEMINI_ROLE_MAP = {
|
|
5227
6258
|
system: "user",
|
|
5228
6259
|
user: "user",
|
|
@@ -5372,6 +6403,7 @@ var init_gemini = __esm({
|
|
|
5372
6403
|
buildApiRequest(options, descriptor, _spec, messages) {
|
|
5373
6404
|
const contents = this.convertMessagesToContents(messages);
|
|
5374
6405
|
const generationConfig = this.buildGenerationConfig(options);
|
|
6406
|
+
const thinkingConfig = resolveGeminiThinkingConfig(options.reasoning, descriptor.name);
|
|
5375
6407
|
const config = {
|
|
5376
6408
|
// Note: systemInstruction removed - it doesn't work with countTokens()
|
|
5377
6409
|
// System messages are now included in contents as user+model exchanges
|
|
@@ -5382,6 +6414,7 @@ var init_gemini = __esm({
|
|
|
5382
6414
|
mode: import_genai.FunctionCallingConfigMode.NONE
|
|
5383
6415
|
}
|
|
5384
6416
|
},
|
|
6417
|
+
...thinkingConfig ?? {},
|
|
5385
6418
|
...options.extra
|
|
5386
6419
|
};
|
|
5387
6420
|
return {
|
|
@@ -5519,7 +6552,18 @@ var init_gemini = __esm({
|
|
|
5519
6552
|
async *normalizeProviderStream(iterable) {
|
|
5520
6553
|
const stream2 = iterable;
|
|
5521
6554
|
for await (const chunk of stream2) {
|
|
5522
|
-
const text3 = this.
|
|
6555
|
+
const { text: text3, thinkingText, thinkingSignature } = this.extractTextAndThinking(chunk);
|
|
6556
|
+
if (thinkingText) {
|
|
6557
|
+
yield {
|
|
6558
|
+
text: "",
|
|
6559
|
+
thinking: {
|
|
6560
|
+
content: thinkingText,
|
|
6561
|
+
type: "thinking",
|
|
6562
|
+
signature: thinkingSignature
|
|
6563
|
+
},
|
|
6564
|
+
rawEvent: chunk
|
|
6565
|
+
};
|
|
6566
|
+
}
|
|
5523
6567
|
if (text3) {
|
|
5524
6568
|
yield { text: text3, rawEvent: chunk };
|
|
5525
6569
|
}
|
|
@@ -5530,11 +6574,30 @@ var init_gemini = __esm({
|
|
|
5530
6574
|
}
|
|
5531
6575
|
}
|
|
5532
6576
|
}
|
|
5533
|
-
|
|
6577
|
+
/**
|
|
6578
|
+
* Extract both regular text and thinking text from a chunk.
|
|
6579
|
+
* Gemini marks thinking parts with `thought: true`.
|
|
6580
|
+
*/
|
|
6581
|
+
extractTextAndThinking(chunk) {
|
|
5534
6582
|
if (!chunk?.candidates) {
|
|
5535
|
-
return "";
|
|
6583
|
+
return { text: "", thinkingText: "" };
|
|
6584
|
+
}
|
|
6585
|
+
let text3 = "";
|
|
6586
|
+
let thinkingText = "";
|
|
6587
|
+
let thinkingSignature;
|
|
6588
|
+
for (const candidate of chunk.candidates) {
|
|
6589
|
+
for (const part of candidate.content?.parts ?? []) {
|
|
6590
|
+
if (part.thought) {
|
|
6591
|
+
thinkingText += part.text ?? "";
|
|
6592
|
+
if (part.thoughtSignature) {
|
|
6593
|
+
thinkingSignature = part.thoughtSignature;
|
|
6594
|
+
}
|
|
6595
|
+
} else {
|
|
6596
|
+
text3 += part.text ?? "";
|
|
6597
|
+
}
|
|
6598
|
+
}
|
|
5536
6599
|
}
|
|
5537
|
-
return
|
|
6600
|
+
return { text: text3, thinkingText, thinkingSignature };
|
|
5538
6601
|
}
|
|
5539
6602
|
extractFinishReason(chunk) {
|
|
5540
6603
|
const candidate = chunk?.candidates?.find((item) => item.finishReason);
|
|
@@ -5550,7 +6613,9 @@ var init_gemini = __esm({
|
|
|
5550
6613
|
outputTokens: usageMetadata.candidatesTokenCount ?? 0,
|
|
5551
6614
|
totalTokens: usageMetadata.totalTokenCount ?? 0,
|
|
5552
6615
|
// Gemini returns cached token count in cachedContentTokenCount
|
|
5553
|
-
cachedInputTokens: usageMetadata.cachedContentTokenCount ?? 0
|
|
6616
|
+
cachedInputTokens: usageMetadata.cachedContentTokenCount ?? 0,
|
|
6617
|
+
// Gemini returns thinking tokens in thoughtsTokenCount
|
|
6618
|
+
reasoningTokens: usageMetadata.thoughtsTokenCount
|
|
5554
6619
|
};
|
|
5555
6620
|
}
|
|
5556
6621
|
/**
|
|
@@ -6571,11 +7636,13 @@ var init_openai_compatible_provider = __esm({
|
|
|
6571
7636
|
yield { text: text3, rawEvent: chunk };
|
|
6572
7637
|
}
|
|
6573
7638
|
const finishReason = chunk.choices.find((choice) => choice.finish_reason)?.finish_reason;
|
|
7639
|
+
const usageDetails = chunk.usage;
|
|
6574
7640
|
const usage = chunk.usage ? {
|
|
6575
7641
|
inputTokens: chunk.usage.prompt_tokens,
|
|
6576
7642
|
outputTokens: chunk.usage.completion_tokens,
|
|
6577
7643
|
totalTokens: chunk.usage.total_tokens,
|
|
6578
|
-
cachedInputTokens: 0
|
|
7644
|
+
cachedInputTokens: 0,
|
|
7645
|
+
reasoningTokens: usageDetails?.completion_tokens_details?.reasoning_tokens
|
|
6579
7646
|
} : void 0;
|
|
6580
7647
|
if (finishReason || usage) {
|
|
6581
7648
|
yield { text: "", finishReason, usage, rawEvent: chunk };
|
|
@@ -6651,6 +7718,21 @@ var init_huggingface = __esm({
|
|
|
6651
7718
|
getModelSpecs() {
|
|
6652
7719
|
return HUGGINGFACE_MODELS;
|
|
6653
7720
|
}
|
|
7721
|
+
/**
|
|
7722
|
+
* Override buildApiRequest to inject DeepSeek-specific thinking parameters.
|
|
7723
|
+
* DeepSeek models use `extra_body: { thinking: { type: "enabled" } }` for reasoning.
|
|
7724
|
+
*/
|
|
7725
|
+
buildApiRequest(options, descriptor, spec, messages) {
|
|
7726
|
+
const request = super.buildApiRequest(options, descriptor, spec, messages);
|
|
7727
|
+
if (options.reasoning?.enabled && descriptor.name.toLowerCase().includes("deepseek")) {
|
|
7728
|
+
const requestObj = request;
|
|
7729
|
+
requestObj.extra_body = {
|
|
7730
|
+
...requestObj.extra_body,
|
|
7731
|
+
thinking: { type: "enabled" }
|
|
7732
|
+
};
|
|
7733
|
+
}
|
|
7734
|
+
return request;
|
|
7735
|
+
}
|
|
6654
7736
|
/**
|
|
6655
7737
|
* Enhance error messages with HuggingFace-specific guidance.
|
|
6656
7738
|
*/
|
|
@@ -7536,7 +8618,7 @@ function sanitizeExtra(extra, allowTemperature) {
|
|
|
7536
8618
|
function createOpenAIProviderFromEnv() {
|
|
7537
8619
|
return createProviderFromEnv("OPENAI_API_KEY", import_openai3.default, OpenAIChatProvider);
|
|
7538
8620
|
}
|
|
7539
|
-
var import_openai3, import_tiktoken, ROLE_MAP2, OpenAIChatProvider;
|
|
8621
|
+
var import_openai3, import_tiktoken, ROLE_MAP2, OPENAI_EFFORT_MAP, OpenAIChatProvider;
|
|
7540
8622
|
var init_openai = __esm({
|
|
7541
8623
|
"src/providers/openai.ts"() {
|
|
7542
8624
|
"use strict";
|
|
@@ -7554,6 +8636,13 @@ var init_openai = __esm({
|
|
|
7554
8636
|
user: "user",
|
|
7555
8637
|
assistant: "assistant"
|
|
7556
8638
|
};
|
|
8639
|
+
OPENAI_EFFORT_MAP = {
|
|
8640
|
+
none: "none",
|
|
8641
|
+
low: "low",
|
|
8642
|
+
medium: "medium",
|
|
8643
|
+
high: "high",
|
|
8644
|
+
maximum: "xhigh"
|
|
8645
|
+
};
|
|
7557
8646
|
OpenAIChatProvider = class extends BaseProviderAdapter {
|
|
7558
8647
|
providerId = "openai";
|
|
7559
8648
|
supports(descriptor) {
|
|
@@ -7644,10 +8733,15 @@ var init_openai = __esm({
|
|
|
7644
8733
|
};
|
|
7645
8734
|
}
|
|
7646
8735
|
buildApiRequest(options, descriptor, spec, messages) {
|
|
7647
|
-
const { maxTokens, temperature, topP, stopSequences, extra } = options;
|
|
8736
|
+
const { maxTokens, temperature, topP, stopSequences, extra, reasoning } = options;
|
|
7648
8737
|
const supportsTemperature = spec?.metadata?.supportsTemperature !== false;
|
|
7649
8738
|
const shouldIncludeTemperature = typeof temperature === "number" && supportsTemperature;
|
|
7650
8739
|
const sanitizedExtra = sanitizeExtra(extra, shouldIncludeTemperature);
|
|
8740
|
+
const reasoningParam = reasoning?.enabled !== void 0 ? {
|
|
8741
|
+
reasoning: {
|
|
8742
|
+
effort: OPENAI_EFFORT_MAP[reasoning.effort ?? "medium"]
|
|
8743
|
+
}
|
|
8744
|
+
} : {};
|
|
7651
8745
|
return {
|
|
7652
8746
|
model: descriptor.name,
|
|
7653
8747
|
messages: messages.map((message) => this.convertToOpenAIMessage(message)),
|
|
@@ -7658,6 +8752,7 @@ var init_openai = __esm({
|
|
|
7658
8752
|
stop: stopSequences,
|
|
7659
8753
|
stream: true,
|
|
7660
8754
|
stream_options: { include_usage: true },
|
|
8755
|
+
...reasoningParam,
|
|
7661
8756
|
...sanitizedExtra ?? {},
|
|
7662
8757
|
...shouldIncludeTemperature ? { temperature } : {}
|
|
7663
8758
|
};
|
|
@@ -7746,11 +8841,13 @@ var init_openai = __esm({
|
|
|
7746
8841
|
yield { text: text3, rawEvent: chunk };
|
|
7747
8842
|
}
|
|
7748
8843
|
const finishReason = chunk.choices.find((choice) => choice.finish_reason)?.finish_reason;
|
|
8844
|
+
const usageDetails = chunk.usage;
|
|
7749
8845
|
const usage = chunk.usage ? {
|
|
7750
8846
|
inputTokens: chunk.usage.prompt_tokens,
|
|
7751
8847
|
outputTokens: chunk.usage.completion_tokens,
|
|
7752
8848
|
totalTokens: chunk.usage.total_tokens,
|
|
7753
|
-
cachedInputTokens:
|
|
8849
|
+
cachedInputTokens: usageDetails?.prompt_tokens_details?.cached_tokens ?? 0,
|
|
8850
|
+
reasoningTokens: usageDetails?.completion_tokens_details?.reasoning_tokens
|
|
7754
8851
|
} : void 0;
|
|
7755
8852
|
if (finishReason || usage) {
|
|
7756
8853
|
yield { text: "", finishReason, usage, rawEvent: chunk };
|
|
@@ -8285,7 +9382,7 @@ function createOpenRouterProviderFromEnv() {
|
|
|
8285
9382
|
});
|
|
8286
9383
|
return new OpenRouterProvider(client, config);
|
|
8287
9384
|
}
|
|
8288
|
-
var import_openai4, OpenRouterProvider;
|
|
9385
|
+
var import_openai4, OPENROUTER_EFFORT_MAP, OpenRouterProvider;
|
|
8289
9386
|
var init_openrouter = __esm({
|
|
8290
9387
|
"src/providers/openrouter.ts"() {
|
|
8291
9388
|
"use strict";
|
|
@@ -8293,6 +9390,13 @@ var init_openrouter = __esm({
|
|
|
8293
9390
|
init_openai_compatible_provider();
|
|
8294
9391
|
init_openrouter_models();
|
|
8295
9392
|
init_utils();
|
|
9393
|
+
OPENROUTER_EFFORT_MAP = {
|
|
9394
|
+
none: "none",
|
|
9395
|
+
low: "low",
|
|
9396
|
+
medium: "medium",
|
|
9397
|
+
high: "high",
|
|
9398
|
+
maximum: "xhigh"
|
|
9399
|
+
};
|
|
8296
9400
|
OpenRouterProvider = class extends OpenAICompatibleProvider {
|
|
8297
9401
|
providerId = "openrouter";
|
|
8298
9402
|
providerAlias = "or";
|
|
@@ -8302,6 +9406,20 @@ var init_openrouter = __esm({
|
|
|
8302
9406
|
getModelSpecs() {
|
|
8303
9407
|
return OPENROUTER_MODELS;
|
|
8304
9408
|
}
|
|
9409
|
+
/**
|
|
9410
|
+
* Override buildApiRequest to inject reasoning parameters.
|
|
9411
|
+
* OpenRouter normalizes reasoning into the standard OpenAI format.
|
|
9412
|
+
*/
|
|
9413
|
+
buildApiRequest(options, descriptor, spec, messages) {
|
|
9414
|
+
const request = super.buildApiRequest(options, descriptor, spec, messages);
|
|
9415
|
+
if (options.reasoning?.enabled !== void 0) {
|
|
9416
|
+
const requestObj = request;
|
|
9417
|
+
requestObj.reasoning = {
|
|
9418
|
+
effort: OPENROUTER_EFFORT_MAP[options.reasoning.effort ?? "medium"]
|
|
9419
|
+
};
|
|
9420
|
+
}
|
|
9421
|
+
return request;
|
|
9422
|
+
}
|
|
8305
9423
|
/**
|
|
8306
9424
|
* Get custom headers for OpenRouter analytics.
|
|
8307
9425
|
*/
|
|
@@ -8539,9 +9657,10 @@ var init_model_registry = __esm({
|
|
|
8539
9657
|
* @param outputTokens - Number of output tokens
|
|
8540
9658
|
* @param cachedInputTokens - Number of cached input tokens (subset of inputTokens)
|
|
8541
9659
|
* @param cacheCreationInputTokens - Number of cache creation tokens (subset of inputTokens, Anthropic only)
|
|
9660
|
+
* @param reasoningTokens - Number of reasoning/thinking tokens (subset of outputTokens)
|
|
8542
9661
|
* @returns CostEstimate if model found, undefined otherwise
|
|
8543
9662
|
*/
|
|
8544
|
-
estimateCost(modelId, inputTokens, outputTokens, cachedInputTokens = 0, cacheCreationInputTokens = 0) {
|
|
9663
|
+
estimateCost(modelId, inputTokens, outputTokens, cachedInputTokens = 0, cacheCreationInputTokens = 0, reasoningTokens = 0) {
|
|
8545
9664
|
const spec = this.getModelSpec(modelId);
|
|
8546
9665
|
if (!spec) return void 0;
|
|
8547
9666
|
const cachedRate = spec.pricing.cachedInput ?? spec.pricing.input;
|
|
@@ -8551,13 +9670,18 @@ var init_model_registry = __esm({
|
|
|
8551
9670
|
const cachedInputCost = cachedInputTokens / 1e6 * cachedRate;
|
|
8552
9671
|
const cacheCreationCost = cacheCreationInputTokens / 1e6 * cacheWriteRate;
|
|
8553
9672
|
const inputCost = uncachedInputCost + cachedInputCost + cacheCreationCost;
|
|
8554
|
-
const
|
|
9673
|
+
const reasoningRate = spec.pricing.reasoningOutput ?? spec.pricing.output;
|
|
9674
|
+
const nonReasoningOutputTokens = outputTokens - reasoningTokens;
|
|
9675
|
+
const reasoningCost = reasoningTokens / 1e6 * reasoningRate;
|
|
9676
|
+
const nonReasoningOutputCost = nonReasoningOutputTokens / 1e6 * spec.pricing.output;
|
|
9677
|
+
const outputCost = nonReasoningOutputCost + reasoningCost;
|
|
8555
9678
|
const totalCost = inputCost + outputCost;
|
|
8556
9679
|
return {
|
|
8557
9680
|
inputCost,
|
|
8558
9681
|
cachedInputCost,
|
|
8559
9682
|
cacheCreationCost,
|
|
8560
9683
|
outputCost,
|
|
9684
|
+
reasoningCost,
|
|
8561
9685
|
totalCost,
|
|
8562
9686
|
currency: "USD"
|
|
8563
9687
|
};
|
|
@@ -9229,6 +10353,8 @@ var init_builder = __esm({
|
|
|
9229
10353
|
init_agent();
|
|
9230
10354
|
init_agent_internal_key();
|
|
9231
10355
|
init_event_handlers();
|
|
10356
|
+
init_file_logging();
|
|
10357
|
+
init_hook_presets();
|
|
9232
10358
|
AgentBuilder = class {
|
|
9233
10359
|
client;
|
|
9234
10360
|
model;
|
|
@@ -9270,6 +10396,7 @@ var init_builder = __esm({
|
|
|
9270
10396
|
// Shared retry config from parent for consistent backoff behavior
|
|
9271
10397
|
// When a gadget calls withParentContext(ctx), this config is shared
|
|
9272
10398
|
sharedRetryConfig;
|
|
10399
|
+
reasoningConfig;
|
|
9273
10400
|
constructor(client) {
|
|
9274
10401
|
this.client = client;
|
|
9275
10402
|
}
|
|
@@ -9855,6 +10982,60 @@ var init_builder = __esm({
|
|
|
9855
10982
|
this.signal = signal;
|
|
9856
10983
|
return this;
|
|
9857
10984
|
}
|
|
10985
|
+
/**
|
|
10986
|
+
* Enable reasoning/thinking mode for reasoning-capable models.
|
|
10987
|
+
*
|
|
10988
|
+
* Can be called with:
|
|
10989
|
+
* - No args: enables reasoning at "medium" effort
|
|
10990
|
+
* - A string effort level: `withReasoning("high")`
|
|
10991
|
+
* - A full config object: `withReasoning({ enabled: true, budgetTokens: 10000 })`
|
|
10992
|
+
*
|
|
10993
|
+
* @param config - Optional effort level or full reasoning config
|
|
10994
|
+
* @returns This builder for chaining
|
|
10995
|
+
*
|
|
10996
|
+
* @example
|
|
10997
|
+
* ```typescript
|
|
10998
|
+
* // Simple — medium effort
|
|
10999
|
+
* LLMist.createAgent()
|
|
11000
|
+
* .withModel("o3")
|
|
11001
|
+
* .withReasoning()
|
|
11002
|
+
* .ask("Solve this logic puzzle...");
|
|
11003
|
+
*
|
|
11004
|
+
* // Explicit effort level
|
|
11005
|
+
* LLMist.createAgent()
|
|
11006
|
+
* .withModel("anthropic:claude-4-opus")
|
|
11007
|
+
* .withReasoning("high")
|
|
11008
|
+
* .ask("Analyze this complex problem");
|
|
11009
|
+
*
|
|
11010
|
+
* // Full config with explicit token budget
|
|
11011
|
+
* LLMist.createAgent()
|
|
11012
|
+
* .withModel("anthropic:claude-4-opus")
|
|
11013
|
+
* .withReasoning({ enabled: true, budgetTokens: 16000 })
|
|
11014
|
+
* .ask("Step through this proof");
|
|
11015
|
+
* ```
|
|
11016
|
+
*/
|
|
11017
|
+
withReasoning(config) {
|
|
11018
|
+
if (typeof config === "string") {
|
|
11019
|
+
this.reasoningConfig = { enabled: true, effort: config };
|
|
11020
|
+
} else if (config === void 0) {
|
|
11021
|
+
this.reasoningConfig = { enabled: true, effort: "medium" };
|
|
11022
|
+
} else {
|
|
11023
|
+
this.reasoningConfig = config;
|
|
11024
|
+
}
|
|
11025
|
+
return this;
|
|
11026
|
+
}
|
|
11027
|
+
/**
|
|
11028
|
+
* Explicitly disable reasoning for this agent, even if the model supports it.
|
|
11029
|
+
*
|
|
11030
|
+
* By default, reasoning is auto-enabled at "medium" effort for models with
|
|
11031
|
+
* `features.reasoning: true`. Use this to opt out.
|
|
11032
|
+
*
|
|
11033
|
+
* @returns This builder for chaining
|
|
11034
|
+
*/
|
|
11035
|
+
withoutReasoning() {
|
|
11036
|
+
this.reasoningConfig = { enabled: false };
|
|
11037
|
+
return this;
|
|
11038
|
+
}
|
|
9858
11039
|
/**
|
|
9859
11040
|
* Set subagent configuration overrides.
|
|
9860
11041
|
*
|
|
@@ -10022,9 +11203,16 @@ ${endPrefix}`
|
|
|
10022
11203
|
* Note: Subagent event visibility is now handled entirely by the ExecutionTree.
|
|
10023
11204
|
* When a subagent uses withParentContext(ctx), it shares the parent's tree,
|
|
10024
11205
|
* and all events are automatically visible to tree subscribers (like the TUI).
|
|
11206
|
+
*
|
|
11207
|
+
* Environment-based file logging (via LLMIST_LOG_RAW_DIRECTORY) is automatically
|
|
11208
|
+
* injected if the env var is set. User-provided hooks take precedence.
|
|
10025
11209
|
*/
|
|
10026
11210
|
composeHooks() {
|
|
10027
|
-
|
|
11211
|
+
let hooks = this.hooks;
|
|
11212
|
+
const envFileLogging = getEnvFileLoggingHooks();
|
|
11213
|
+
if (envFileLogging) {
|
|
11214
|
+
hooks = hooks ? HookPresets.merge(envFileLogging, hooks) : envFileLogging;
|
|
11215
|
+
}
|
|
10028
11216
|
if (!this.trailingMessage) {
|
|
10029
11217
|
return hooks;
|
|
10030
11218
|
}
|
|
@@ -10133,6 +11321,7 @@ ${endPrefix}`
|
|
|
10133
11321
|
retryConfig: this.retryConfig,
|
|
10134
11322
|
rateLimitConfig: this.rateLimitConfig,
|
|
10135
11323
|
signal: this.signal,
|
|
11324
|
+
reasoning: this.reasoningConfig,
|
|
10136
11325
|
subagentConfig: this.subagentConfig,
|
|
10137
11326
|
// Tree context for shared tree model (subagents share parent's tree)
|
|
10138
11327
|
parentTree: this.parentContext?.tree,
|
|
@@ -10320,6 +11509,7 @@ ${endPrefix}`
|
|
|
10320
11509
|
retryConfig: this.retryConfig,
|
|
10321
11510
|
rateLimitConfig: this.rateLimitConfig,
|
|
10322
11511
|
signal: this.signal,
|
|
11512
|
+
reasoning: this.reasoningConfig,
|
|
10323
11513
|
subagentConfig: this.subagentConfig,
|
|
10324
11514
|
// Tree context for shared tree model (subagents share parent's tree)
|
|
10325
11515
|
parentTree: this.parentContext?.tree,
|
|
@@ -10774,6 +11964,7 @@ var init_cost_reporting_client = __esm({
|
|
|
10774
11964
|
let outputTokens = 0;
|
|
10775
11965
|
let cachedInputTokens = 0;
|
|
10776
11966
|
let cacheCreationInputTokens = 0;
|
|
11967
|
+
let reasoningTokens = 0;
|
|
10777
11968
|
const messages = [
|
|
10778
11969
|
...options?.systemPrompt ? [{ role: "system", content: options.systemPrompt }] : [],
|
|
10779
11970
|
{ role: "user", content: prompt }
|
|
@@ -10790,6 +11981,7 @@ var init_cost_reporting_client = __esm({
|
|
|
10790
11981
|
outputTokens = chunk.usage.outputTokens;
|
|
10791
11982
|
cachedInputTokens = chunk.usage.cachedInputTokens ?? 0;
|
|
10792
11983
|
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
11984
|
+
reasoningTokens = chunk.usage.reasoningTokens ?? 0;
|
|
10793
11985
|
}
|
|
10794
11986
|
}
|
|
10795
11987
|
this.reportCostFromUsage(
|
|
@@ -10797,7 +11989,8 @@ var init_cost_reporting_client = __esm({
|
|
|
10797
11989
|
inputTokens,
|
|
10798
11990
|
outputTokens,
|
|
10799
11991
|
cachedInputTokens,
|
|
10800
|
-
cacheCreationInputTokens
|
|
11992
|
+
cacheCreationInputTokens,
|
|
11993
|
+
reasoningTokens
|
|
10801
11994
|
);
|
|
10802
11995
|
return result;
|
|
10803
11996
|
}
|
|
@@ -10816,6 +12009,7 @@ var init_cost_reporting_client = __esm({
|
|
|
10816
12009
|
let outputTokens = 0;
|
|
10817
12010
|
let cachedInputTokens = 0;
|
|
10818
12011
|
let cacheCreationInputTokens = 0;
|
|
12012
|
+
let reasoningTokens = 0;
|
|
10819
12013
|
const messages = [
|
|
10820
12014
|
...options?.systemPrompt ? [{ role: "system", content: options.systemPrompt }] : [],
|
|
10821
12015
|
{ role: "user", content: prompt }
|
|
@@ -10835,6 +12029,7 @@ var init_cost_reporting_client = __esm({
|
|
|
10835
12029
|
outputTokens = chunk.usage.outputTokens;
|
|
10836
12030
|
cachedInputTokens = chunk.usage.cachedInputTokens ?? 0;
|
|
10837
12031
|
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
12032
|
+
reasoningTokens = chunk.usage.reasoningTokens ?? 0;
|
|
10838
12033
|
}
|
|
10839
12034
|
}
|
|
10840
12035
|
} finally {
|
|
@@ -10843,7 +12038,8 @@ var init_cost_reporting_client = __esm({
|
|
|
10843
12038
|
inputTokens,
|
|
10844
12039
|
outputTokens,
|
|
10845
12040
|
cachedInputTokens,
|
|
10846
|
-
cacheCreationInputTokens
|
|
12041
|
+
cacheCreationInputTokens,
|
|
12042
|
+
reasoningTokens
|
|
10847
12043
|
);
|
|
10848
12044
|
}
|
|
10849
12045
|
}
|
|
@@ -10870,6 +12066,7 @@ var init_cost_reporting_client = __esm({
|
|
|
10870
12066
|
let outputTokens = 0;
|
|
10871
12067
|
let cachedInputTokens = 0;
|
|
10872
12068
|
let cacheCreationInputTokens = 0;
|
|
12069
|
+
let reasoningTokens = 0;
|
|
10873
12070
|
try {
|
|
10874
12071
|
for await (const chunk of innerStream) {
|
|
10875
12072
|
if (chunk.usage) {
|
|
@@ -10877,6 +12074,7 @@ var init_cost_reporting_client = __esm({
|
|
|
10877
12074
|
outputTokens = chunk.usage.outputTokens;
|
|
10878
12075
|
cachedInputTokens = chunk.usage.cachedInputTokens ?? 0;
|
|
10879
12076
|
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
12077
|
+
reasoningTokens = chunk.usage.reasoningTokens ?? 0;
|
|
10880
12078
|
}
|
|
10881
12079
|
yield chunk;
|
|
10882
12080
|
}
|
|
@@ -10887,7 +12085,8 @@ var init_cost_reporting_client = __esm({
|
|
|
10887
12085
|
inputTokens,
|
|
10888
12086
|
outputTokens,
|
|
10889
12087
|
cachedInputTokens,
|
|
10890
|
-
cacheCreationInputTokens
|
|
12088
|
+
cacheCreationInputTokens,
|
|
12089
|
+
reasoningTokens
|
|
10891
12090
|
);
|
|
10892
12091
|
}
|
|
10893
12092
|
}
|
|
@@ -10897,14 +12096,15 @@ var init_cost_reporting_client = __esm({
|
|
|
10897
12096
|
/**
|
|
10898
12097
|
* Calculates and reports cost from token usage.
|
|
10899
12098
|
*/
|
|
10900
|
-
reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens = 0, cacheCreationInputTokens = 0) {
|
|
12099
|
+
reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens = 0, cacheCreationInputTokens = 0, reasoningTokens = 0) {
|
|
10901
12100
|
if (inputTokens === 0 && outputTokens === 0) return;
|
|
10902
12101
|
const estimate = this.client.modelRegistry.estimateCost(
|
|
10903
12102
|
model,
|
|
10904
12103
|
inputTokens,
|
|
10905
12104
|
outputTokens,
|
|
10906
12105
|
cachedInputTokens,
|
|
10907
|
-
cacheCreationInputTokens
|
|
12106
|
+
cacheCreationInputTokens,
|
|
12107
|
+
reasoningTokens
|
|
10908
12108
|
);
|
|
10909
12109
|
if (estimate && estimate.totalCost > 0) {
|
|
10910
12110
|
this.reportCost(estimate.totalCost);
|
|
@@ -11996,9 +13196,18 @@ var init_stream_processor = __esm({
|
|
|
11996
13196
|
let usage;
|
|
11997
13197
|
let didExecuteGadgets = false;
|
|
11998
13198
|
let shouldBreakLoop = false;
|
|
13199
|
+
let thinkingContent = "";
|
|
11999
13200
|
for await (const chunk of stream2) {
|
|
12000
13201
|
if (chunk.finishReason) finishReason = chunk.finishReason;
|
|
12001
13202
|
if (chunk.usage) usage = chunk.usage;
|
|
13203
|
+
if (chunk.thinking?.content) {
|
|
13204
|
+
thinkingContent += chunk.thinking.content;
|
|
13205
|
+
yield {
|
|
13206
|
+
type: "thinking",
|
|
13207
|
+
content: chunk.thinking.content,
|
|
13208
|
+
thinkingType: chunk.thinking.type
|
|
13209
|
+
};
|
|
13210
|
+
}
|
|
12002
13211
|
let processedChunk = "";
|
|
12003
13212
|
if (chunk.text) {
|
|
12004
13213
|
processedChunk = chunk.text;
|
|
@@ -12112,7 +13321,8 @@ var init_stream_processor = __esm({
|
|
|
12112
13321
|
finishReason,
|
|
12113
13322
|
usage,
|
|
12114
13323
|
rawResponse: this.responseText,
|
|
12115
|
-
finalMessage
|
|
13324
|
+
finalMessage,
|
|
13325
|
+
thinkingContent: thinkingContent || void 0
|
|
12116
13326
|
};
|
|
12117
13327
|
yield completionEvent;
|
|
12118
13328
|
}
|
|
@@ -12914,6 +14124,7 @@ var init_agent = __esm({
|
|
|
12914
14124
|
mediaStore;
|
|
12915
14125
|
// Cancellation
|
|
12916
14126
|
signal;
|
|
14127
|
+
reasoning;
|
|
12917
14128
|
// Retry configuration
|
|
12918
14129
|
retryConfig;
|
|
12919
14130
|
// Rate limit tracker for proactive throttling
|
|
@@ -13005,6 +14216,7 @@ var init_agent = __esm({
|
|
|
13005
14216
|
);
|
|
13006
14217
|
}
|
|
13007
14218
|
this.signal = options.signal;
|
|
14219
|
+
this.reasoning = options.reasoning;
|
|
13008
14220
|
this.retryConfig = options.sharedRetryConfig ?? resolveRetryConfig(options.retryConfig);
|
|
13009
14221
|
if (options.sharedRateLimitTracker) {
|
|
13010
14222
|
this.rateLimitTracker = options.sharedRateLimitTracker;
|
|
@@ -13407,6 +14619,7 @@ var init_agent = __esm({
|
|
|
13407
14619
|
usage: result.usage,
|
|
13408
14620
|
rawResponse: result.rawResponse,
|
|
13409
14621
|
finalMessage: result.finalMessage,
|
|
14622
|
+
thinkingContent: result.thinkingContent,
|
|
13410
14623
|
logger: this.logger,
|
|
13411
14624
|
subagentContext
|
|
13412
14625
|
};
|
|
@@ -13707,17 +14920,34 @@ var init_agent = __esm({
|
|
|
13707
14920
|
});
|
|
13708
14921
|
return { type: "compaction", event: compactionEvent };
|
|
13709
14922
|
}
|
|
14923
|
+
/**
|
|
14924
|
+
* Resolve reasoning configuration with auto-enable logic.
|
|
14925
|
+
*
|
|
14926
|
+
* Priority: explicit config > auto-enable for reasoning models > undefined
|
|
14927
|
+
* When a model has `features.reasoning: true` and no explicit config is set,
|
|
14928
|
+
* reasoning is automatically enabled at "medium" effort.
|
|
14929
|
+
*/
|
|
14930
|
+
resolveReasoningConfig(spec) {
|
|
14931
|
+
if (this.reasoning !== void 0) return this.reasoning;
|
|
14932
|
+
if (spec?.features?.reasoning) {
|
|
14933
|
+
return { enabled: true, effort: "medium" };
|
|
14934
|
+
}
|
|
14935
|
+
return void 0;
|
|
14936
|
+
}
|
|
13710
14937
|
/**
|
|
13711
14938
|
* Prepare LLM call options, create tree node, and process beforeLLMCall controller.
|
|
13712
14939
|
* @returns options, node ID, and optional skipWithSynthetic response if controller wants to skip
|
|
13713
14940
|
*/
|
|
13714
14941
|
async prepareLLMCall(iteration) {
|
|
14942
|
+
const spec = this.client.modelRegistry?.getModelSpec?.(this.model);
|
|
14943
|
+
const reasoning = this.resolveReasoningConfig(spec);
|
|
13715
14944
|
let llmOptions = {
|
|
13716
14945
|
model: this.model,
|
|
13717
14946
|
messages: this.conversation.getMessages(),
|
|
13718
14947
|
temperature: this.temperature,
|
|
13719
14948
|
maxTokens: this.defaultMaxTokens,
|
|
13720
|
-
signal: this.signal
|
|
14949
|
+
signal: this.signal,
|
|
14950
|
+
reasoning
|
|
13721
14951
|
};
|
|
13722
14952
|
const llmNode = this.tree.addLLMCall({
|
|
13723
14953
|
iteration,
|
|
@@ -13787,13 +15017,15 @@ var init_agent = __esm({
|
|
|
13787
15017
|
inputTokens,
|
|
13788
15018
|
outputTokens,
|
|
13789
15019
|
result.usage?.cachedInputTokens ?? 0,
|
|
13790
|
-
result.usage?.cacheCreationInputTokens ?? 0
|
|
15020
|
+
result.usage?.cacheCreationInputTokens ?? 0,
|
|
15021
|
+
result.usage?.reasoningTokens ?? 0
|
|
13791
15022
|
)?.totalCost;
|
|
13792
15023
|
this.tree.completeLLMCall(nodeId, {
|
|
13793
15024
|
response: result.rawResponse,
|
|
13794
15025
|
usage: result.usage,
|
|
13795
15026
|
finishReason: result.finishReason,
|
|
13796
|
-
cost: llmCost
|
|
15027
|
+
cost: llmCost,
|
|
15028
|
+
thinkingContent: result.thinkingContent
|
|
13797
15029
|
});
|
|
13798
15030
|
}
|
|
13799
15031
|
/**
|
|
@@ -13969,9 +15201,11 @@ __export(index_exports, {
|
|
|
13969
15201
|
filterRootEvents: () => filterRootEvents,
|
|
13970
15202
|
format: () => format,
|
|
13971
15203
|
formatBytes: () => formatBytes,
|
|
15204
|
+
formatCallNumber: () => formatCallNumber,
|
|
13972
15205
|
formatDate: () => formatDate,
|
|
13973
15206
|
formatDuration: () => formatDuration,
|
|
13974
15207
|
formatLLMError: () => formatLLMError,
|
|
15208
|
+
formatLlmRequest: () => formatLlmRequest,
|
|
13975
15209
|
gadgetError: () => gadgetError,
|
|
13976
15210
|
gadgetSuccess: () => gadgetSuccess,
|
|
13977
15211
|
getErrorMessage: () => getErrorMessage,
|
|
@@ -14043,781 +15277,8 @@ var import_zod3 = require("zod");
|
|
|
14043
15277
|
init_agent();
|
|
14044
15278
|
init_builder();
|
|
14045
15279
|
init_event_handlers();
|
|
14046
|
-
|
|
14047
|
-
|
|
14048
|
-
var HookPresets = class _HookPresets {
|
|
14049
|
-
/**
|
|
14050
|
-
* Logs LLM calls and gadget execution to console with optional verbosity.
|
|
14051
|
-
*
|
|
14052
|
-
* **Output (basic mode):**
|
|
14053
|
-
* - LLM call start/complete events with iteration numbers
|
|
14054
|
-
* - Gadget execution start/complete with gadget names
|
|
14055
|
-
* - Token counts when available
|
|
14056
|
-
*
|
|
14057
|
-
* **Output (verbose mode):**
|
|
14058
|
-
* - All basic mode output
|
|
14059
|
-
* - Full gadget parameters (formatted JSON)
|
|
14060
|
-
* - Full gadget results
|
|
14061
|
-
* - Complete LLM response text
|
|
14062
|
-
*
|
|
14063
|
-
* **Use cases:**
|
|
14064
|
-
* - Basic development debugging and execution flow visibility
|
|
14065
|
-
* - Understanding agent decision-making and tool usage
|
|
14066
|
-
* - Troubleshooting gadget invocations
|
|
14067
|
-
*
|
|
14068
|
-
* **Performance:** Minimal overhead. Console writes are synchronous but fast.
|
|
14069
|
-
*
|
|
14070
|
-
* @param options - Logging options
|
|
14071
|
-
* @param options.verbose - Include full parameters and results. Default: false
|
|
14072
|
-
* @returns Hook configuration that can be passed to .withHooks()
|
|
14073
|
-
*
|
|
14074
|
-
* @example
|
|
14075
|
-
* ```typescript
|
|
14076
|
-
* // Basic logging
|
|
14077
|
-
* await LLMist.createAgent()
|
|
14078
|
-
* .withHooks(HookPresets.logging())
|
|
14079
|
-
* .ask("Calculate 15 * 23");
|
|
14080
|
-
* // Output: [LLM] Starting call (iteration 0)
|
|
14081
|
-
* // [GADGET] Executing Calculator
|
|
14082
|
-
* // [GADGET] Completed Calculator
|
|
14083
|
-
* // [LLM] Completed (tokens: 245)
|
|
14084
|
-
* ```
|
|
14085
|
-
*
|
|
14086
|
-
* @example
|
|
14087
|
-
* ```typescript
|
|
14088
|
-
* // Verbose logging with full details
|
|
14089
|
-
* await LLMist.createAgent()
|
|
14090
|
-
* .withHooks(HookPresets.logging({ verbose: true }))
|
|
14091
|
-
* .ask("Calculate 15 * 23");
|
|
14092
|
-
* // Output includes: parameters, results, and full responses
|
|
14093
|
-
* ```
|
|
14094
|
-
*
|
|
14095
|
-
* @example
|
|
14096
|
-
* ```typescript
|
|
14097
|
-
* // Environment-based verbosity
|
|
14098
|
-
* const isDev = process.env.NODE_ENV === 'development';
|
|
14099
|
-
* .withHooks(HookPresets.logging({ verbose: isDev }))
|
|
14100
|
-
* ```
|
|
14101
|
-
*
|
|
14102
|
-
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsloggingoptions | Full documentation}
|
|
14103
|
-
*/
|
|
14104
|
-
static logging(options = {}) {
|
|
14105
|
-
return {
|
|
14106
|
-
observers: {
|
|
14107
|
-
onLLMCallStart: async (ctx) => {
|
|
14108
|
-
console.log(`[LLM] Starting call (iteration ${ctx.iteration})`);
|
|
14109
|
-
},
|
|
14110
|
-
onLLMCallComplete: async (ctx) => {
|
|
14111
|
-
const tokens = ctx.usage?.totalTokens ?? "unknown";
|
|
14112
|
-
console.log(`[LLM] Completed (tokens: ${tokens})`);
|
|
14113
|
-
if (options.verbose && ctx.finalMessage) {
|
|
14114
|
-
console.log(`[LLM] Response: ${ctx.finalMessage}`);
|
|
14115
|
-
}
|
|
14116
|
-
},
|
|
14117
|
-
onGadgetExecutionStart: async (ctx) => {
|
|
14118
|
-
console.log(`[GADGET] Executing ${ctx.gadgetName}`);
|
|
14119
|
-
if (options.verbose) {
|
|
14120
|
-
console.log(`[GADGET] Parameters:`, JSON.stringify(ctx.parameters, null, 2));
|
|
14121
|
-
}
|
|
14122
|
-
},
|
|
14123
|
-
onGadgetExecutionComplete: async (ctx) => {
|
|
14124
|
-
console.log(`[GADGET] Completed ${ctx.gadgetName}`);
|
|
14125
|
-
if (options.verbose) {
|
|
14126
|
-
const display = ctx.error ?? ctx.finalResult ?? "(no result)";
|
|
14127
|
-
console.log(`[GADGET] Result: ${display}`);
|
|
14128
|
-
}
|
|
14129
|
-
}
|
|
14130
|
-
}
|
|
14131
|
-
};
|
|
14132
|
-
}
|
|
14133
|
-
/**
|
|
14134
|
-
* Measures and logs execution time for LLM calls and gadgets.
|
|
14135
|
-
*
|
|
14136
|
-
* **Output:**
|
|
14137
|
-
* - Duration in milliseconds with ⏱️ emoji for each operation
|
|
14138
|
-
* - Separate timing for each LLM iteration
|
|
14139
|
-
* - Separate timing for each gadget execution
|
|
14140
|
-
*
|
|
14141
|
-
* **Use cases:**
|
|
14142
|
-
* - Performance profiling and optimization
|
|
14143
|
-
* - Identifying slow operations (LLM calls vs gadget execution)
|
|
14144
|
-
* - Monitoring response times in production
|
|
14145
|
-
* - Capacity planning and SLA tracking
|
|
14146
|
-
*
|
|
14147
|
-
* **Performance:** Negligible overhead. Uses Date.now() for timing measurements.
|
|
14148
|
-
*
|
|
14149
|
-
* @returns Hook configuration that can be passed to .withHooks()
|
|
14150
|
-
*
|
|
14151
|
-
* @example
|
|
14152
|
-
* ```typescript
|
|
14153
|
-
* // Basic timing
|
|
14154
|
-
* await LLMist.createAgent()
|
|
14155
|
-
* .withHooks(HookPresets.timing())
|
|
14156
|
-
* .withGadgets(Weather, Database)
|
|
14157
|
-
* .ask("What's the weather in NYC?");
|
|
14158
|
-
* // Output: ⏱️ LLM call took 1234ms
|
|
14159
|
-
* // ⏱️ Gadget Weather took 567ms
|
|
14160
|
-
* // ⏱️ LLM call took 890ms
|
|
14161
|
-
* ```
|
|
14162
|
-
*
|
|
14163
|
-
* @example
|
|
14164
|
-
* ```typescript
|
|
14165
|
-
* // Combined with logging for full context
|
|
14166
|
-
* .withHooks(HookPresets.merge(
|
|
14167
|
-
* HookPresets.logging(),
|
|
14168
|
-
* HookPresets.timing()
|
|
14169
|
-
* ))
|
|
14170
|
-
* ```
|
|
14171
|
-
*
|
|
14172
|
-
* @example
|
|
14173
|
-
* ```typescript
|
|
14174
|
-
* // Correlate performance with cost
|
|
14175
|
-
* .withHooks(HookPresets.merge(
|
|
14176
|
-
* HookPresets.timing(),
|
|
14177
|
-
* HookPresets.tokenTracking()
|
|
14178
|
-
* ))
|
|
14179
|
-
* ```
|
|
14180
|
-
*
|
|
14181
|
-
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetstiming | Full documentation}
|
|
14182
|
-
*/
|
|
14183
|
-
static timing() {
|
|
14184
|
-
const timings = /* @__PURE__ */ new Map();
|
|
14185
|
-
return {
|
|
14186
|
-
observers: {
|
|
14187
|
-
onLLMCallStart: async (ctx) => {
|
|
14188
|
-
timings.set(`llm-${ctx.iteration}`, Date.now());
|
|
14189
|
-
},
|
|
14190
|
-
onLLMCallComplete: async (ctx) => {
|
|
14191
|
-
const start = timings.get(`llm-${ctx.iteration}`);
|
|
14192
|
-
if (start) {
|
|
14193
|
-
const duration = Date.now() - start;
|
|
14194
|
-
console.log(`\u23F1\uFE0F LLM call took ${duration}ms`);
|
|
14195
|
-
timings.delete(`llm-${ctx.iteration}`);
|
|
14196
|
-
}
|
|
14197
|
-
},
|
|
14198
|
-
onGadgetExecutionStart: async (ctx) => {
|
|
14199
|
-
const key = `gadget-${ctx.gadgetName}-${Date.now()}`;
|
|
14200
|
-
timings.set(key, Date.now());
|
|
14201
|
-
ctx._timingKey = key;
|
|
14202
|
-
},
|
|
14203
|
-
onGadgetExecutionComplete: async (ctx) => {
|
|
14204
|
-
const key = ctx._timingKey;
|
|
14205
|
-
if (key) {
|
|
14206
|
-
const start = timings.get(key);
|
|
14207
|
-
if (start) {
|
|
14208
|
-
const duration = Date.now() - start;
|
|
14209
|
-
console.log(`\u23F1\uFE0F Gadget ${ctx.gadgetName} took ${duration}ms`);
|
|
14210
|
-
timings.delete(key);
|
|
14211
|
-
}
|
|
14212
|
-
}
|
|
14213
|
-
}
|
|
14214
|
-
}
|
|
14215
|
-
};
|
|
14216
|
-
}
|
|
14217
|
-
/**
|
|
14218
|
-
* Tracks cumulative token usage across all LLM calls.
|
|
14219
|
-
*
|
|
14220
|
-
* **Output:**
|
|
14221
|
-
* - Per-call token count with 📊 emoji
|
|
14222
|
-
* - Cumulative total across all calls
|
|
14223
|
-
* - Call count for average calculations
|
|
14224
|
-
*
|
|
14225
|
-
* **Use cases:**
|
|
14226
|
-
* - Cost monitoring and budget tracking
|
|
14227
|
-
* - Optimizing prompts to reduce token usage
|
|
14228
|
-
* - Comparing token efficiency across different approaches
|
|
14229
|
-
* - Real-time cost estimation
|
|
14230
|
-
*
|
|
14231
|
-
* **Performance:** Minimal overhead. Simple counter increments.
|
|
14232
|
-
*
|
|
14233
|
-
* **Note:** Token counts depend on the provider's response. Some providers
|
|
14234
|
-
* may not include usage data, in which case counts won't be logged.
|
|
14235
|
-
*
|
|
14236
|
-
* @returns Hook configuration that can be passed to .withHooks()
|
|
14237
|
-
*
|
|
14238
|
-
* @example
|
|
14239
|
-
* ```typescript
|
|
14240
|
-
* // Basic token tracking
|
|
14241
|
-
* await LLMist.createAgent()
|
|
14242
|
-
* .withHooks(HookPresets.tokenTracking())
|
|
14243
|
-
* .ask("Summarize this document...");
|
|
14244
|
-
* // Output: 📊 Tokens this call: 1,234
|
|
14245
|
-
* // 📊 Total tokens: 1,234 (across 1 calls)
|
|
14246
|
-
* // 📊 Tokens this call: 567
|
|
14247
|
-
* // 📊 Total tokens: 1,801 (across 2 calls)
|
|
14248
|
-
* ```
|
|
14249
|
-
*
|
|
14250
|
-
* @example
|
|
14251
|
-
* ```typescript
|
|
14252
|
-
* // Cost calculation with custom hook
|
|
14253
|
-
* let totalTokens = 0;
|
|
14254
|
-
* .withHooks(HookPresets.merge(
|
|
14255
|
-
* HookPresets.tokenTracking(),
|
|
14256
|
-
* {
|
|
14257
|
-
* observers: {
|
|
14258
|
-
* onLLMCallComplete: async (ctx) => {
|
|
14259
|
-
* totalTokens += ctx.usage?.totalTokens ?? 0;
|
|
14260
|
-
* const cost = (totalTokens / 1_000_000) * 3.0; // $3 per 1M tokens
|
|
14261
|
-
* console.log(`💰 Estimated cost: $${cost.toFixed(4)}`);
|
|
14262
|
-
* },
|
|
14263
|
-
* },
|
|
14264
|
-
* }
|
|
14265
|
-
* ))
|
|
14266
|
-
* ```
|
|
14267
|
-
*
|
|
14268
|
-
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetstokentracking | Full documentation}
|
|
14269
|
-
*/
|
|
14270
|
-
static tokenTracking() {
|
|
14271
|
-
let totalTokens = 0;
|
|
14272
|
-
let totalCalls = 0;
|
|
14273
|
-
return {
|
|
14274
|
-
observers: {
|
|
14275
|
-
onLLMCallComplete: async (ctx) => {
|
|
14276
|
-
totalCalls++;
|
|
14277
|
-
if (ctx.usage?.totalTokens) {
|
|
14278
|
-
totalTokens += ctx.usage.totalTokens;
|
|
14279
|
-
console.log(`\u{1F4CA} Tokens this call: ${ctx.usage.totalTokens}`);
|
|
14280
|
-
console.log(`\u{1F4CA} Total tokens: ${totalTokens} (across ${totalCalls} calls)`);
|
|
14281
|
-
}
|
|
14282
|
-
}
|
|
14283
|
-
}
|
|
14284
|
-
};
|
|
14285
|
-
}
|
|
14286
|
-
/**
|
|
14287
|
-
* Tracks comprehensive progress metrics including iterations, tokens, cost, and timing.
|
|
14288
|
-
*
|
|
14289
|
-
* **This preset showcases llmist's core capabilities by demonstrating:**
|
|
14290
|
-
* - Observer pattern for non-intrusive monitoring
|
|
14291
|
-
* - Integration with ModelRegistry for cost estimation
|
|
14292
|
-
* - Callback-based architecture for flexible UI updates
|
|
14293
|
-
* - Provider-agnostic token and cost tracking
|
|
14294
|
-
*
|
|
14295
|
-
* Unlike `tokenTracking()` which only logs to console, this preset provides
|
|
14296
|
-
* structured data through callbacks, making it perfect for building custom UIs,
|
|
14297
|
-
* dashboards, or progress indicators (like the llmist CLI).
|
|
14298
|
-
*
|
|
14299
|
-
* **Output (when logProgress: true):**
|
|
14300
|
-
* - Iteration number and call count
|
|
14301
|
-
* - Cumulative token usage (input + output)
|
|
14302
|
-
* - Cumulative cost in USD (requires modelRegistry)
|
|
14303
|
-
* - Elapsed time in seconds
|
|
14304
|
-
*
|
|
14305
|
-
* **Use cases:**
|
|
14306
|
-
* - Building CLI progress indicators with live updates
|
|
14307
|
-
* - Creating web dashboards with real-time metrics
|
|
14308
|
-
* - Budget monitoring and cost alerts
|
|
14309
|
-
* - Performance tracking and optimization
|
|
14310
|
-
* - Custom logging to external systems (Datadog, CloudWatch, etc.)
|
|
14311
|
-
*
|
|
14312
|
-
* **Performance:** Minimal overhead. Uses Date.now() for timing and optional
|
|
14313
|
-
* ModelRegistry.estimateCost() which is O(1) lookup. Callback invocation is
|
|
14314
|
-
* synchronous and fast.
|
|
14315
|
-
*
|
|
14316
|
-
* @param options - Progress tracking options
|
|
14317
|
-
* @param options.modelRegistry - ModelRegistry for cost estimation (optional)
|
|
14318
|
-
* @param options.onProgress - Callback invoked after each LLM call (optional)
|
|
14319
|
-
* @param options.logProgress - Log progress to console (default: false)
|
|
14320
|
-
* @returns Hook configuration with progress tracking observers
|
|
14321
|
-
*
|
|
14322
|
-
* @example
|
|
14323
|
-
* ```typescript
|
|
14324
|
-
* // Basic usage with callback (RECOMMENDED - used by llmist CLI)
|
|
14325
|
-
* import { LLMist, HookPresets } from 'llmist';
|
|
14326
|
-
*
|
|
14327
|
-
* const client = LLMist.create();
|
|
14328
|
-
*
|
|
14329
|
-
* await client.agent()
|
|
14330
|
-
* .withHooks(HookPresets.progressTracking({
|
|
14331
|
-
* modelRegistry: client.modelRegistry,
|
|
14332
|
-
* onProgress: (stats) => {
|
|
14333
|
-
* // Update your UI with stats
|
|
14334
|
-
* console.log(`#${stats.currentIteration} | ${stats.totalTokens} tokens | $${stats.totalCost.toFixed(4)}`);
|
|
14335
|
-
* }
|
|
14336
|
-
* }))
|
|
14337
|
-
* .withGadgets(Calculator)
|
|
14338
|
-
* .ask("Calculate 15 * 23");
|
|
14339
|
-
* // Output: #1 | 245 tokens | $0.0012
|
|
14340
|
-
* ```
|
|
14341
|
-
*
|
|
14342
|
-
* @example
|
|
14343
|
-
* ```typescript
|
|
14344
|
-
* // Console logging mode (quick debugging)
|
|
14345
|
-
* await client.agent()
|
|
14346
|
-
* .withHooks(HookPresets.progressTracking({
|
|
14347
|
-
* modelRegistry: client.modelRegistry,
|
|
14348
|
-
* logProgress: true // Simple console output
|
|
14349
|
-
* }))
|
|
14350
|
-
* .ask("Your prompt");
|
|
14351
|
-
* // Output: 📊 Progress: Iteration #1 | 245 tokens | $0.0012 | 1.2s
|
|
14352
|
-
* ```
|
|
14353
|
-
*
|
|
14354
|
-
* @example
|
|
14355
|
-
* ```typescript
|
|
14356
|
-
* // Budget monitoring with alerts
|
|
14357
|
-
* const BUDGET_USD = 0.10;
|
|
14358
|
-
*
|
|
14359
|
-
* await client.agent()
|
|
14360
|
-
* .withHooks(HookPresets.progressTracking({
|
|
14361
|
-
* modelRegistry: client.modelRegistry,
|
|
14362
|
-
* onProgress: (stats) => {
|
|
14363
|
-
* if (stats.totalCost > BUDGET_USD) {
|
|
14364
|
-
* throw new Error(`Budget exceeded: $${stats.totalCost.toFixed(4)}`);
|
|
14365
|
-
* }
|
|
14366
|
-
* }
|
|
14367
|
-
* }))
|
|
14368
|
-
* .ask("Long running task...");
|
|
14369
|
-
* ```
|
|
14370
|
-
*
|
|
14371
|
-
* @example
|
|
14372
|
-
* ```typescript
|
|
14373
|
-
* // Web dashboard integration
|
|
14374
|
-
* let progressBar: HTMLElement;
|
|
14375
|
-
*
|
|
14376
|
-
* await client.agent()
|
|
14377
|
-
* .withHooks(HookPresets.progressTracking({
|
|
14378
|
-
* modelRegistry: client.modelRegistry,
|
|
14379
|
-
* onProgress: (stats) => {
|
|
14380
|
-
* // Update web UI in real-time
|
|
14381
|
-
* progressBar.textContent = `Iteration ${stats.currentIteration}`;
|
|
14382
|
-
* progressBar.dataset.cost = stats.totalCost.toFixed(4);
|
|
14383
|
-
* progressBar.dataset.tokens = stats.totalTokens.toString();
|
|
14384
|
-
* }
|
|
14385
|
-
* }))
|
|
14386
|
-
* .ask("Your prompt");
|
|
14387
|
-
* ```
|
|
14388
|
-
*
|
|
14389
|
-
* @example
|
|
14390
|
-
* ```typescript
|
|
14391
|
-
* // External logging (Datadog, CloudWatch, etc.)
|
|
14392
|
-
* await client.agent()
|
|
14393
|
-
* .withHooks(HookPresets.progressTracking({
|
|
14394
|
-
* modelRegistry: client.modelRegistry,
|
|
14395
|
-
* onProgress: async (stats) => {
|
|
14396
|
-
* await metrics.gauge('llm.iteration', stats.currentIteration);
|
|
14397
|
-
* await metrics.gauge('llm.cost', stats.totalCost);
|
|
14398
|
-
* await metrics.gauge('llm.tokens', stats.totalTokens);
|
|
14399
|
-
* }
|
|
14400
|
-
* }))
|
|
14401
|
-
* .ask("Your prompt");
|
|
14402
|
-
* ```
|
|
14403
|
-
*
|
|
14404
|
-
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsprogresstrackingoptions | Full documentation}
|
|
14405
|
-
* @see {@link ProgressTrackingOptions} for detailed options
|
|
14406
|
-
* @see {@link ProgressStats} for the callback data structure
|
|
14407
|
-
*/
|
|
14408
|
-
static progressTracking(options) {
|
|
14409
|
-
const { modelRegistry, onProgress, logProgress = false } = options ?? {};
|
|
14410
|
-
let totalCalls = 0;
|
|
14411
|
-
let currentIteration = 0;
|
|
14412
|
-
let totalInputTokens = 0;
|
|
14413
|
-
let totalOutputTokens = 0;
|
|
14414
|
-
let totalCost = 0;
|
|
14415
|
-
let totalGadgetCost = 0;
|
|
14416
|
-
const startTime = Date.now();
|
|
14417
|
-
return {
|
|
14418
|
-
observers: {
|
|
14419
|
-
// Track iteration on each LLM call start
|
|
14420
|
-
onLLMCallStart: async (ctx) => {
|
|
14421
|
-
currentIteration++;
|
|
14422
|
-
},
|
|
14423
|
-
// Accumulate metrics and report progress on each LLM call completion
|
|
14424
|
-
onLLMCallComplete: async (ctx) => {
|
|
14425
|
-
totalCalls++;
|
|
14426
|
-
if (ctx.usage) {
|
|
14427
|
-
totalInputTokens += ctx.usage.inputTokens;
|
|
14428
|
-
totalOutputTokens += ctx.usage.outputTokens;
|
|
14429
|
-
if (modelRegistry) {
|
|
14430
|
-
try {
|
|
14431
|
-
const modelName = ctx.options.model.includes(":") ? ctx.options.model.split(":")[1] : ctx.options.model;
|
|
14432
|
-
const costEstimate = modelRegistry.estimateCost(
|
|
14433
|
-
modelName,
|
|
14434
|
-
ctx.usage.inputTokens,
|
|
14435
|
-
ctx.usage.outputTokens
|
|
14436
|
-
);
|
|
14437
|
-
if (costEstimate) {
|
|
14438
|
-
totalCost += costEstimate.totalCost;
|
|
14439
|
-
}
|
|
14440
|
-
} catch (error) {
|
|
14441
|
-
if (logProgress) {
|
|
14442
|
-
console.warn(`\u26A0\uFE0F Cost estimation failed:`, error);
|
|
14443
|
-
}
|
|
14444
|
-
}
|
|
14445
|
-
}
|
|
14446
|
-
}
|
|
14447
|
-
const stats = {
|
|
14448
|
-
currentIteration,
|
|
14449
|
-
totalCalls,
|
|
14450
|
-
totalInputTokens,
|
|
14451
|
-
totalOutputTokens,
|
|
14452
|
-
totalTokens: totalInputTokens + totalOutputTokens,
|
|
14453
|
-
totalCost: totalCost + totalGadgetCost,
|
|
14454
|
-
elapsedSeconds: Number(((Date.now() - startTime) / 1e3).toFixed(1))
|
|
14455
|
-
};
|
|
14456
|
-
if (onProgress) {
|
|
14457
|
-
onProgress(stats);
|
|
14458
|
-
}
|
|
14459
|
-
if (logProgress) {
|
|
14460
|
-
const formattedTokens = stats.totalTokens >= 1e3 ? `${(stats.totalTokens / 1e3).toFixed(1)}k` : `${stats.totalTokens}`;
|
|
14461
|
-
const formattedCost = stats.totalCost > 0 ? `$${stats.totalCost.toFixed(4)}` : "$0";
|
|
14462
|
-
console.log(
|
|
14463
|
-
`\u{1F4CA} Progress: Iteration #${stats.currentIteration} | ${formattedTokens} tokens | ${formattedCost} | ${stats.elapsedSeconds}s`
|
|
14464
|
-
);
|
|
14465
|
-
}
|
|
14466
|
-
},
|
|
14467
|
-
// Track gadget execution costs
|
|
14468
|
-
onGadgetExecutionComplete: async (ctx) => {
|
|
14469
|
-
if (ctx.cost && ctx.cost > 0) {
|
|
14470
|
-
totalGadgetCost += ctx.cost;
|
|
14471
|
-
}
|
|
14472
|
-
}
|
|
14473
|
-
}
|
|
14474
|
-
};
|
|
14475
|
-
}
|
|
14476
|
-
/**
|
|
14477
|
-
* Logs detailed error information for debugging and troubleshooting.
|
|
14478
|
-
*
|
|
14479
|
-
* **Output:**
|
|
14480
|
-
* - LLM errors with ❌ emoji, including model and recovery status
|
|
14481
|
-
* - Gadget errors with full context (parameters, error message)
|
|
14482
|
-
* - Separate logging for LLM and gadget failures
|
|
14483
|
-
*
|
|
14484
|
-
* **Use cases:**
|
|
14485
|
-
* - Troubleshooting production issues
|
|
14486
|
-
* - Understanding error patterns and frequency
|
|
14487
|
-
* - Debugging error recovery behavior
|
|
14488
|
-
* - Collecting error metrics for monitoring
|
|
14489
|
-
*
|
|
14490
|
-
* **Performance:** Minimal overhead. Only logs when errors occur.
|
|
14491
|
-
*
|
|
14492
|
-
* @returns Hook configuration that can be passed to .withHooks()
|
|
14493
|
-
*
|
|
14494
|
-
* @example
|
|
14495
|
-
* ```typescript
|
|
14496
|
-
* // Basic error logging
|
|
14497
|
-
* await LLMist.createAgent()
|
|
14498
|
-
* .withHooks(HookPresets.errorLogging())
|
|
14499
|
-
* .withGadgets(Database)
|
|
14500
|
-
* .ask("Fetch user data");
|
|
14501
|
-
* // Output (on LLM error): ❌ LLM Error (iteration 1): Rate limit exceeded
|
|
14502
|
-
* // Model: gpt-5-nano
|
|
14503
|
-
* // Recovered: true
|
|
14504
|
-
* // Output (on gadget error): ❌ Gadget Error: Database
|
|
14505
|
-
* // Error: Connection timeout
|
|
14506
|
-
* // Parameters: {...}
|
|
14507
|
-
* ```
|
|
14508
|
-
*
|
|
14509
|
-
* @example
|
|
14510
|
-
* ```typescript
|
|
14511
|
-
* // Combine with monitoring for full context
|
|
14512
|
-
* .withHooks(HookPresets.merge(
|
|
14513
|
-
* HookPresets.monitoring(), // Includes errorLogging
|
|
14514
|
-
* customErrorAnalytics
|
|
14515
|
-
* ))
|
|
14516
|
-
* ```
|
|
14517
|
-
*
|
|
14518
|
-
* @example
|
|
14519
|
-
* ```typescript
|
|
14520
|
-
* // Error analytics collection
|
|
14521
|
-
* const errors: any[] = [];
|
|
14522
|
-
* .withHooks(HookPresets.merge(
|
|
14523
|
-
* HookPresets.errorLogging(),
|
|
14524
|
-
* {
|
|
14525
|
-
* observers: {
|
|
14526
|
-
* onLLMCallError: async (ctx) => {
|
|
14527
|
-
* errors.push({ type: 'llm', error: ctx.error, recovered: ctx.recovered });
|
|
14528
|
-
* },
|
|
14529
|
-
* },
|
|
14530
|
-
* }
|
|
14531
|
-
* ))
|
|
14532
|
-
* ```
|
|
14533
|
-
*
|
|
14534
|
-
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetserrorlogging | Full documentation}
|
|
14535
|
-
*/
|
|
14536
|
-
static errorLogging() {
|
|
14537
|
-
return {
|
|
14538
|
-
observers: {
|
|
14539
|
-
onLLMCallError: async (ctx) => {
|
|
14540
|
-
console.error(`\u274C LLM Error (iteration ${ctx.iteration}):`, ctx.error.message);
|
|
14541
|
-
console.error(` Model: ${ctx.options.model}`);
|
|
14542
|
-
console.error(` Recovered: ${ctx.recovered}`);
|
|
14543
|
-
},
|
|
14544
|
-
onGadgetExecutionComplete: async (ctx) => {
|
|
14545
|
-
if (ctx.error) {
|
|
14546
|
-
console.error(`\u274C Gadget Error: ${ctx.gadgetName}`);
|
|
14547
|
-
console.error(` Error: ${ctx.error}`);
|
|
14548
|
-
console.error(` Parameters:`, JSON.stringify(ctx.parameters, null, 2));
|
|
14549
|
-
}
|
|
14550
|
-
}
|
|
14551
|
-
}
|
|
14552
|
-
};
|
|
14553
|
-
}
|
|
14554
|
-
/**
|
|
14555
|
-
* Tracks context compaction events.
|
|
14556
|
-
*
|
|
14557
|
-
* **Output:**
|
|
14558
|
-
* - Compaction events with 🗜️ emoji
|
|
14559
|
-
* - Strategy name, tokens before/after, and savings
|
|
14560
|
-
* - Cumulative statistics
|
|
14561
|
-
*
|
|
14562
|
-
* **Use cases:**
|
|
14563
|
-
* - Monitoring long-running conversations
|
|
14564
|
-
* - Understanding when and how compaction occurs
|
|
14565
|
-
* - Debugging context management issues
|
|
14566
|
-
*
|
|
14567
|
-
* **Performance:** Minimal overhead. Simple console output.
|
|
14568
|
-
*
|
|
14569
|
-
* @returns Hook configuration that can be passed to .withHooks()
|
|
14570
|
-
*
|
|
14571
|
-
* @example
|
|
14572
|
-
* ```typescript
|
|
14573
|
-
* await LLMist.createAgent()
|
|
14574
|
-
* .withHooks(HookPresets.compactionTracking())
|
|
14575
|
-
* .ask("Your prompt");
|
|
14576
|
-
* ```
|
|
14577
|
-
*/
|
|
14578
|
-
static compactionTracking() {
|
|
14579
|
-
return {
|
|
14580
|
-
observers: {
|
|
14581
|
-
onCompaction: async (ctx) => {
|
|
14582
|
-
const saved = ctx.event.tokensBefore - ctx.event.tokensAfter;
|
|
14583
|
-
const percent = (saved / ctx.event.tokensBefore * 100).toFixed(1);
|
|
14584
|
-
console.log(
|
|
14585
|
-
`\u{1F5DC}\uFE0F Compaction (${ctx.event.strategy}): ${ctx.event.tokensBefore} \u2192 ${ctx.event.tokensAfter} tokens (saved ${saved}, ${percent}%)`
|
|
14586
|
-
);
|
|
14587
|
-
console.log(` Messages: ${ctx.event.messagesBefore} \u2192 ${ctx.event.messagesAfter}`);
|
|
14588
|
-
if (ctx.stats.totalCompactions > 1) {
|
|
14589
|
-
console.log(
|
|
14590
|
-
` Cumulative: ${ctx.stats.totalCompactions} compactions, ${ctx.stats.totalTokensSaved} tokens saved`
|
|
14591
|
-
);
|
|
14592
|
-
}
|
|
14593
|
-
}
|
|
14594
|
-
}
|
|
14595
|
-
};
|
|
14596
|
-
}
|
|
14597
|
-
/**
|
|
14598
|
-
* Returns empty hook configuration for clean output without any logging.
|
|
14599
|
-
*
|
|
14600
|
-
* **Output:**
|
|
14601
|
-
* - None. Returns {} (empty object).
|
|
14602
|
-
*
|
|
14603
|
-
* **Use cases:**
|
|
14604
|
-
* - Clean test output without console noise
|
|
14605
|
-
* - Production environments where logging is handled externally
|
|
14606
|
-
* - Baseline for custom hook development
|
|
14607
|
-
* - Temporary disable of all hook output
|
|
14608
|
-
*
|
|
14609
|
-
* **Performance:** Zero overhead. No-op hook configuration.
|
|
14610
|
-
*
|
|
14611
|
-
* @returns Empty hook configuration
|
|
14612
|
-
*
|
|
14613
|
-
* @example
|
|
14614
|
-
* ```typescript
|
|
14615
|
-
* // Clean test output
|
|
14616
|
-
* describe('Agent tests', () => {
|
|
14617
|
-
* it('should calculate correctly', async () => {
|
|
14618
|
-
* const result = await LLMist.createAgent()
|
|
14619
|
-
* .withHooks(HookPresets.silent()) // No console output
|
|
14620
|
-
* .withGadgets(Calculator)
|
|
14621
|
-
* .askAndCollect("What is 15 times 23?");
|
|
14622
|
-
*
|
|
14623
|
-
* expect(result).toContain("345");
|
|
14624
|
-
* });
|
|
14625
|
-
* });
|
|
14626
|
-
* ```
|
|
14627
|
-
*
|
|
14628
|
-
* @example
|
|
14629
|
-
* ```typescript
|
|
14630
|
-
* // Conditional silence based on environment
|
|
14631
|
-
* const isTesting = process.env.NODE_ENV === 'test';
|
|
14632
|
-
* .withHooks(isTesting ? HookPresets.silent() : HookPresets.monitoring())
|
|
14633
|
-
* ```
|
|
14634
|
-
*
|
|
14635
|
-
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetssilent | Full documentation}
|
|
14636
|
-
*/
|
|
14637
|
-
static silent() {
|
|
14638
|
-
return {};
|
|
14639
|
-
}
|
|
14640
|
-
/**
|
|
14641
|
-
* Combines multiple hook configurations into one.
|
|
14642
|
-
*
|
|
14643
|
-
* Merge allows you to compose preset and custom hooks for modular monitoring
|
|
14644
|
-
* configurations. Understanding merge behavior is crucial for proper composition.
|
|
14645
|
-
*
|
|
14646
|
-
* **Merge behavior:**
|
|
14647
|
-
* - **Observers:** Composed - all handlers run sequentially in order
|
|
14648
|
-
* - **Interceptors:** Last one wins - only the last interceptor applies
|
|
14649
|
-
* - **Controllers:** Last one wins - only the last controller applies
|
|
14650
|
-
*
|
|
14651
|
-
* **Why interceptors/controllers don't compose:**
|
|
14652
|
-
* - Interceptors have different signatures per method, making composition impractical
|
|
14653
|
-
* - Controllers return specific actions that can't be meaningfully combined
|
|
14654
|
-
* - Only observers support composition because they're read-only and independent
|
|
14655
|
-
*
|
|
14656
|
-
* **Use cases:**
|
|
14657
|
-
* - Combining multiple presets (logging + timing + tokens)
|
|
14658
|
-
* - Adding custom hooks to presets
|
|
14659
|
-
* - Building modular, reusable monitoring configurations
|
|
14660
|
-
* - Environment-specific hook composition
|
|
14661
|
-
*
|
|
14662
|
-
* **Performance:** Minimal overhead for merging. Runtime performance depends on merged hooks.
|
|
14663
|
-
*
|
|
14664
|
-
* @param hookSets - Variable number of hook configurations to merge
|
|
14665
|
-
* @returns Single merged hook configuration with composed/overridden handlers
|
|
14666
|
-
*
|
|
14667
|
-
* @example
|
|
14668
|
-
* ```typescript
|
|
14669
|
-
* // Combine multiple presets
|
|
14670
|
-
* .withHooks(HookPresets.merge(
|
|
14671
|
-
* HookPresets.logging(),
|
|
14672
|
-
* HookPresets.timing(),
|
|
14673
|
-
* HookPresets.tokenTracking()
|
|
14674
|
-
* ))
|
|
14675
|
-
* // All observers from all three presets will run
|
|
14676
|
-
* ```
|
|
14677
|
-
*
|
|
14678
|
-
* @example
|
|
14679
|
-
* ```typescript
|
|
14680
|
-
* // Add custom observer to preset (both run)
|
|
14681
|
-
* .withHooks(HookPresets.merge(
|
|
14682
|
-
* HookPresets.timing(),
|
|
14683
|
-
* {
|
|
14684
|
-
* observers: {
|
|
14685
|
-
* onLLMCallComplete: async (ctx) => {
|
|
14686
|
-
* await saveMetrics({ tokens: ctx.usage?.totalTokens });
|
|
14687
|
-
* },
|
|
14688
|
-
* },
|
|
14689
|
-
* }
|
|
14690
|
-
* ))
|
|
14691
|
-
* ```
|
|
14692
|
-
*
|
|
14693
|
-
* @example
|
|
14694
|
-
* ```typescript
|
|
14695
|
-
* // Multiple interceptors (last wins!)
|
|
14696
|
-
* .withHooks(HookPresets.merge(
|
|
14697
|
-
* {
|
|
14698
|
-
* interceptors: {
|
|
14699
|
-
* interceptTextChunk: (chunk) => chunk.toUpperCase(), // Ignored
|
|
14700
|
-
* },
|
|
14701
|
-
* },
|
|
14702
|
-
* {
|
|
14703
|
-
* interceptors: {
|
|
14704
|
-
* interceptTextChunk: (chunk) => chunk.toLowerCase(), // This wins
|
|
14705
|
-
* },
|
|
14706
|
-
* }
|
|
14707
|
-
* ))
|
|
14708
|
-
* // Result: text will be lowercase
|
|
14709
|
-
* ```
|
|
14710
|
-
*
|
|
14711
|
-
* @example
|
|
14712
|
-
* ```typescript
|
|
14713
|
-
* // Modular environment-based configuration
|
|
14714
|
-
* const baseHooks = HookPresets.errorLogging();
|
|
14715
|
-
* const devHooks = HookPresets.merge(baseHooks, HookPresets.monitoring({ verbose: true }));
|
|
14716
|
-
* const prodHooks = HookPresets.merge(baseHooks, HookPresets.tokenTracking());
|
|
14717
|
-
*
|
|
14718
|
-
* const hooks = process.env.NODE_ENV === 'production' ? prodHooks : devHooks;
|
|
14719
|
-
* .withHooks(hooks)
|
|
14720
|
-
* ```
|
|
14721
|
-
*
|
|
14722
|
-
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsmergehooksets | Full documentation}
|
|
14723
|
-
*/
|
|
14724
|
-
static merge(...hookSets) {
|
|
14725
|
-
const merged = {
|
|
14726
|
-
observers: {},
|
|
14727
|
-
interceptors: {},
|
|
14728
|
-
controllers: {}
|
|
14729
|
-
};
|
|
14730
|
-
for (const hooks of hookSets) {
|
|
14731
|
-
if (hooks.observers) {
|
|
14732
|
-
for (const [key, handler] of Object.entries(hooks.observers)) {
|
|
14733
|
-
const typedKey = key;
|
|
14734
|
-
if (merged.observers[typedKey]) {
|
|
14735
|
-
const existing = merged.observers[typedKey];
|
|
14736
|
-
merged.observers[typedKey] = async (ctx) => {
|
|
14737
|
-
await existing(ctx);
|
|
14738
|
-
await handler(ctx);
|
|
14739
|
-
};
|
|
14740
|
-
} else {
|
|
14741
|
-
merged.observers[typedKey] = handler;
|
|
14742
|
-
}
|
|
14743
|
-
}
|
|
14744
|
-
}
|
|
14745
|
-
if (hooks.interceptors) {
|
|
14746
|
-
Object.assign(merged.interceptors, hooks.interceptors);
|
|
14747
|
-
}
|
|
14748
|
-
if (hooks.controllers) {
|
|
14749
|
-
Object.assign(merged.controllers, hooks.controllers);
|
|
14750
|
-
}
|
|
14751
|
-
}
|
|
14752
|
-
return merged;
|
|
14753
|
-
}
|
|
14754
|
-
/**
|
|
14755
|
-
* Composite preset combining logging, timing, tokenTracking, and errorLogging.
|
|
14756
|
-
*
|
|
14757
|
-
* This is the recommended preset for development and initial production deployments,
|
|
14758
|
-
* providing comprehensive observability with a single method call.
|
|
14759
|
-
*
|
|
14760
|
-
* **Includes:**
|
|
14761
|
-
* - All output from `logging()` preset (with optional verbosity)
|
|
14762
|
-
* - All output from `timing()` preset (execution times)
|
|
14763
|
-
* - All output from `tokenTracking()` preset (token usage)
|
|
14764
|
-
* - All output from `errorLogging()` preset (error details)
|
|
14765
|
-
*
|
|
14766
|
-
* **Output format:**
|
|
14767
|
-
* - Event logging: [LLM]/[GADGET] messages
|
|
14768
|
-
* - Timing: ⏱️ emoji with milliseconds
|
|
14769
|
-
* - Tokens: 📊 emoji with per-call and cumulative counts
|
|
14770
|
-
* - Errors: ❌ emoji with full error details
|
|
14771
|
-
*
|
|
14772
|
-
* **Use cases:**
|
|
14773
|
-
* - Full observability during development
|
|
14774
|
-
* - Comprehensive monitoring in production
|
|
14775
|
-
* - One-liner for complete agent visibility
|
|
14776
|
-
* - Troubleshooting and debugging with full context
|
|
14777
|
-
*
|
|
14778
|
-
* **Performance:** Combined overhead of all four presets, but still minimal in practice.
|
|
14779
|
-
*
|
|
14780
|
-
* @param options - Monitoring options
|
|
14781
|
-
* @param options.verbose - Passed to logging() preset for detailed output. Default: false
|
|
14782
|
-
* @returns Merged hook configuration combining all monitoring presets
|
|
14783
|
-
*
|
|
14784
|
-
* @example
|
|
14785
|
-
* ```typescript
|
|
14786
|
-
* // Basic monitoring (recommended for development)
|
|
14787
|
-
* await LLMist.createAgent()
|
|
14788
|
-
* .withHooks(HookPresets.monitoring())
|
|
14789
|
-
* .withGadgets(Calculator, Weather)
|
|
14790
|
-
* .ask("What is 15 times 23, and what's the weather in NYC?");
|
|
14791
|
-
* // Output: All events, timing, tokens, and errors in one place
|
|
14792
|
-
* ```
|
|
14793
|
-
*
|
|
14794
|
-
* @example
|
|
14795
|
-
* ```typescript
|
|
14796
|
-
* // Verbose monitoring with full details
|
|
14797
|
-
* await LLMist.createAgent()
|
|
14798
|
-
* .withHooks(HookPresets.monitoring({ verbose: true }))
|
|
14799
|
-
* .ask("Your prompt");
|
|
14800
|
-
* // Output includes: parameters, results, and complete responses
|
|
14801
|
-
* ```
|
|
14802
|
-
*
|
|
14803
|
-
* @example
|
|
14804
|
-
* ```typescript
|
|
14805
|
-
* // Environment-based monitoring
|
|
14806
|
-
* const isDev = process.env.NODE_ENV === 'development';
|
|
14807
|
-
* .withHooks(HookPresets.monitoring({ verbose: isDev }))
|
|
14808
|
-
* ```
|
|
14809
|
-
*
|
|
14810
|
-
* @see {@link https://github.com/zbigniewsobiecki/llmist/blob/main/docs/HOOKS.md#hookpresetsmonitoringoptions | Full documentation}
|
|
14811
|
-
*/
|
|
14812
|
-
static monitoring(options = {}) {
|
|
14813
|
-
return _HookPresets.merge(
|
|
14814
|
-
_HookPresets.logging(options),
|
|
14815
|
-
_HookPresets.timing(),
|
|
14816
|
-
_HookPresets.tokenTracking(),
|
|
14817
|
-
_HookPresets.errorLogging()
|
|
14818
|
-
);
|
|
14819
|
-
}
|
|
14820
|
-
};
|
|
15280
|
+
init_file_logging();
|
|
15281
|
+
init_hook_presets();
|
|
14821
15282
|
|
|
14822
15283
|
// src/agent/compaction/index.ts
|
|
14823
15284
|
init_config();
|
|
@@ -14830,6 +15291,7 @@ init_gadget_output_store();
|
|
|
14830
15291
|
|
|
14831
15292
|
// src/agent/hints.ts
|
|
14832
15293
|
init_prompt_config();
|
|
15294
|
+
init_hook_presets();
|
|
14833
15295
|
function iterationProgressHint(options) {
|
|
14834
15296
|
const { timing: timing2 = "always", showUrgency = true, template } = options ?? {};
|
|
14835
15297
|
return {
|
|
@@ -15619,9 +16081,11 @@ function getHostExports2(ctx) {
|
|
|
15619
16081
|
filterRootEvents,
|
|
15620
16082
|
format,
|
|
15621
16083
|
formatBytes,
|
|
16084
|
+
formatCallNumber,
|
|
15622
16085
|
formatDate,
|
|
15623
16086
|
formatDuration,
|
|
15624
16087
|
formatLLMError,
|
|
16088
|
+
formatLlmRequest,
|
|
15625
16089
|
gadgetError,
|
|
15626
16090
|
gadgetSuccess,
|
|
15627
16091
|
getErrorMessage,
|