llm-burn 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,2 +1,390 @@
1
1
  # llm-burn
2
- > LLM cost tracking interceptor for NestJS — coming soon.
2
+
3
+ [![npm version](https://img.shields.io/npm/v/llm-burn.svg)](https://www.npmjs.com/package/llm-burn)
4
+ [![license](https://img.shields.io/npm/l/llm-burn.svg)](LICENSE)
5
+ [![tests](https://img.shields.io/badge/tests-60%20passed-brightgreen)](https://github.com/julioxavier/llm-burn)
6
+
7
+ **Cost tracking and budget enforcement for LLM calls in NestJS — with a single decorator.**
8
+
9
+ Every OpenAI and Anthropic call burns money. `llm-burn` tells you exactly how much, per method, per model, in real time — and optionally blocks requests once a spending limit is reached. Zero changes to your business logic required.
10
+
11
+ ---
12
+
13
+ ## Features
14
+
15
+ - **One decorator** — `@TrackLLMBurn()` is all you need to start tracking a method
16
+ - **Auto-detection** — parses OpenAI, Anthropic, flat, and LangChain response shapes out of the box
17
+ - **Budget enforcement** — `BudgetGuard` throws HTTP 403 before the LLM call is made when a cap is exceeded
18
+ - **Per-method and global budgets** — granular control over individual methods or the entire application
19
+ - **Built-in pricing** — ships with up-to-date prices for all major GPT and Claude models
20
+ - **Extensible** — override prices, add custom models, or write a custom extractor for any SDK
21
+
22
+ ---
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install llm-burn
28
+ ```
29
+
30
+ **Peer dependencies** (already present in any NestJS project):
31
+
32
+ ```bash
33
+ npm install @nestjs/common @nestjs/core reflect-metadata rxjs
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Quick Start
39
+
40
+ **1. Register the module in `AppModule`:**
41
+
42
+ ```typescript
43
+ import { LLMBurnModule } from 'llm-burn';
44
+
45
+ @Module({
46
+ imports: [
47
+ LLMBurnModule.forRoot({
48
+ globalBudget: 10.00, // block all LLM calls after $10 spent
49
+ enableLogging: true,
50
+ }),
51
+ ],
52
+ })
53
+ export class AppModule {}
54
+ ```
55
+
56
+ **2. Decorate the method that calls your LLM:**
57
+
58
+ ```typescript
59
+ import { TrackLLMBurn } from 'llm-burn';
60
+
61
+ @Injectable()
62
+ export class AiService {
63
+ @TrackLLMBurn({ model: 'gpt-4o', budget: 2.00 })
64
+ async summarize(text: string) {
65
+ return this.openai.chat.completions.create({
66
+ model: 'gpt-4o',
67
+ messages: [{ role: 'user', content: text }],
68
+ });
69
+ }
70
+ }
71
+ ```
72
+
73
+ **3. Query costs anywhere in your application:**
74
+
75
+ ```typescript
76
+ @Injectable()
77
+ export class DashboardService {
78
+ constructor(private readonly llmBurn: LLMBurnService) {}
79
+
80
+ getReport() {
81
+ return this.llmBurn.getStats();
82
+ // { totalCost, totalCalls, byMethod, byModel, records }
83
+ }
84
+ }
85
+ ```
86
+
87
+ That's it. Token usage is extracted automatically from the response — no wrappers, no interceptors to wire up manually.
88
+
89
+ ---
90
+
91
+ ## Module Registration
92
+
93
+ ### Synchronous
94
+
95
+ ```typescript
96
+ LLMBurnModule.forRoot({
97
+ globalBudget: 10.00,
98
+ enableLogging: true,
99
+ })
100
+ ```
101
+
102
+ ### Async (with ConfigService)
103
+
104
+ ```typescript
105
+ LLMBurnModule.forRootAsync({
106
+ imports: [ConfigModule],
107
+ inject: [ConfigService],
108
+ useFactory: (cfg: ConfigService) => ({
109
+ globalBudget: cfg.get<number>('LLM_BUDGET'),
110
+ enableLogging: cfg.get<boolean>('LLM_LOGGING'),
111
+ }),
112
+ })
113
+ ```
114
+
115
+ ### With Global Interceptor
116
+
117
+ Registers `LLMBurnInterceptor` as an `APP_INTERCEPTOR` so every route in your application is automatically intercepted. Combine with `@TrackLLMBurn()` on specific methods to control what gets tracked.
118
+
119
+ ```typescript
120
+ LLMBurnModule.forRootWithGlobalInterceptor({
121
+ globalBudget: 5.00,
122
+ enableLogging: true,
123
+ })
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Decorator: `@TrackLLMBurn`
129
+
130
+ Marks a method for LLM cost tracking. Automatically attaches the interceptor to that method — no need to wire up `UseInterceptors` manually.
131
+
132
+ ```typescript
133
+ @TrackLLMBurn({ model: 'claude-3-5-sonnet-20241022', budget: 1.50 })
134
+ async generateReport(prompt: string) {
135
+ return this.anthropic.messages.create({
136
+ model: 'claude-3-5-sonnet-20241022',
137
+ max_tokens: 1024,
138
+ messages: [{ role: 'user', content: prompt }],
139
+ });
140
+ }
141
+ ```
142
+
143
+ | Option | Type | Description |
144
+ |---|---|---|
145
+ | `model` | `string` | Model name used as fallback when the response doesn't include one. |
146
+ | `provider` | `string` | Provider hint (`"openai"`, `"anthropic"`, or custom). Auto-detected from model name when omitted. |
147
+ | `budget` | `number` | Per-method USD cap. `BudgetGuard` blocks calls once this is reached. |
148
+ | `extractUsage` | `(result: unknown) => ExtractedUsage \| null` | Custom extractor for non-standard response shapes. |
149
+
150
+ ---
151
+
152
+ ## Budget Guard
153
+
154
+ `BudgetGuard` runs **before** the route handler. It checks two thresholds in order:
155
+
156
+ 1. **Per-method budget** — reads `budget` from `@TrackLLMBurn({ budget: N })` and compares it against the cumulative cost of all previous calls to that method.
157
+ 2. **Global budget** — checks if `totalCost >= globalBudget` across all tracked calls.
158
+
159
+ If either threshold is exceeded, it throws `ForbiddenException` (HTTP 403) and the LLM call is never made.
160
+
161
+ > The guard checks cost accumulated from **previous calls**. The current call's cost is recorded after it completes (in the interceptor). This is by design — the guard acts as a spending limiter, not a per-call price check.
162
+
163
+ ### Applying the guard
164
+
165
+ **Globally:**
166
+
167
+ ```typescript
168
+ // main.ts
169
+ async function bootstrap() {
170
+ const app = await NestFactory.create(AppModule);
171
+ app.useGlobalGuards(app.get(BudgetGuard));
172
+ await app.listen(3000);
173
+ }
174
+ ```
175
+
176
+ **Per controller:**
177
+
178
+ ```typescript
179
+ @UseGuards(BudgetGuard)
180
+ @Controller('ai')
181
+ export class AiController {}
182
+ ```
183
+
184
+ **Per route:**
185
+
186
+ ```typescript
187
+ @UseGuards(BudgetGuard)
188
+ @Post('summarize')
189
+ @TrackLLMBurn({ model: 'gpt-4o', budget: 0.50 })
190
+ async summarize(@Body() dto: SummarizeDto) { ... }
191
+ ```
192
+
193
+ > When `BudgetGuard` is not registered, no request is ever blocked. Cost tracking via the interceptor still works normally.
194
+
195
+ ---
196
+
197
+ ## LLMBurnService
198
+
199
+ Injectable service available anywhere after importing `LLMBurnModule`.
200
+
201
+ | Method | Return | Description |
202
+ |---|---|---|
203
+ | `getStats()` | `LLMStats` | Full breakdown: totals, per-method, per-model, raw records |
204
+ | `getTotalCost()` | `number` | Total USD spent across all calls |
205
+ | `getMethodCost(method)` | `number` | Cumulative USD cost for a specific method |
206
+ | `getBudgetStatus()` | `BudgetStatus` | Global budget usage (remaining, exceeded, % used) |
207
+ | `getGlobalBudget()` | `number \| undefined` | Configured global budget cap |
208
+ | `calculateCost(model, in, out, cached?)` | `number` | Calculate USD cost for a given token count |
209
+ | `getPricing(model)` | `ModelPricing \| undefined` | Retrieve pricing for a model (supports prefix matching) |
210
+ | `listKnownModels()` | `string[]` | All model names from built-in + custom prices |
211
+ | `record(method, model, provider, in, out)` | `LLMCallRecord` | Manually record a call |
212
+ | `reset()` | `void` | Clear all recorded usage |
213
+
214
+ **Example — cost dashboard endpoint:**
215
+
216
+ ```typescript
217
+ @Get('cost-report')
218
+ getCostReport() {
219
+ return {
220
+ stats: this.llmBurn.getStats(),
221
+ budget: this.llmBurn.getBudgetStatus(),
222
+ };
223
+ }
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Supported Response Formats
229
+
230
+ The interceptor auto-detects four response shapes out of the box:
231
+
232
+ | Format | Shape |
233
+ |---|---|
234
+ | **OpenAI SDK** | `{ usage: { prompt_tokens, completion_tokens }, model }` |
235
+ | **Anthropic SDK** | `{ usage: { input_tokens, output_tokens }, model }` |
236
+ | **Flat** | `{ inputTokens, outputTokens, model? }` |
237
+ | **LangChain** | `{ llmOutput: { tokenUsage: { promptTokens, completionTokens } } }` |
238
+
239
+ ### Custom extractor
240
+
241
+ For non-standard SDKs or response wrappers, provide an `extractUsage` function:
242
+
243
+ ```typescript
244
+ @TrackLLMBurn({
245
+ model: 'my-custom-model',
246
+ extractUsage: (result: unknown) => {
247
+ const r = result as MyCustomResponse;
248
+ if (!r?.meta?.tokens) return null;
249
+ return {
250
+ inputTokens: r.meta.tokens.input,
251
+ outputTokens: r.meta.tokens.output,
252
+ model: r.meta.model, // optional — overrides decorator model
253
+ provider: 'my-provider', // optional — overrides auto-detection
254
+ };
255
+ },
256
+ })
257
+ async callCustomLLM(prompt: string) { ... }
258
+ ```
259
+
260
+ Return `null` to skip recording for a specific call — the interceptor will log a warning.
261
+
262
+ ---
263
+
264
+ ## Supported Models & Pricing
265
+
266
+ Prices are in USD per 1 million tokens (updated March 2026).
267
+
268
+ ### OpenAI
269
+
270
+ | Model | Input / M | Output / M | Cached Input / M |
271
+ |---|---|---|---|
272
+ | `gpt-4o` | $2.50 | $10.00 | $1.25 |
273
+ | `gpt-4o-mini` | $0.15 | $0.60 | $0.075 |
274
+ | `gpt-4-turbo` | $10.00 | $30.00 | — |
275
+ | `gpt-4` | $30.00 | $60.00 | — |
276
+ | `gpt-3.5-turbo` | $0.50 | $1.50 | — |
277
+ | `o1` | $15.00 | $60.00 | $7.50 |
278
+ | `o1-mini` | $3.00 | $12.00 | $1.50 |
279
+ | `o3` | $10.00 | $40.00 | $2.50 |
280
+ | `o3-mini` | $1.10 | $4.40 | $0.55 |
281
+ | `gpt-4.5-preview` | $75.00 | $150.00 | $37.50 |
282
+
283
+ ### Anthropic
284
+
285
+ | Model | Input / M | Output / M |
286
+ |---|---|---|
287
+ | `claude-opus-4-6` | $15.00 | $75.00 |
288
+ | `claude-sonnet-4-6` | $3.00 | $15.00 |
289
+ | `claude-haiku-4-5` | $0.80 | $4.00 |
290
+ | `claude-3-5-sonnet-20241022` | $3.00 | $15.00 |
291
+ | `claude-3-5-haiku-20241022` | $0.80 | $4.00 |
292
+ | `claude-3-opus-20240229` | $15.00 | $75.00 |
293
+ | `claude-3-haiku-20240307` | $0.25 | $1.25 |
294
+ | `claude-2.1` | $8.00 | $24.00 |
295
+
296
+ > **Prefix matching:** dated model variants like `gpt-4o-2024-11-20` are matched automatically — if no exact match exists, the interceptor falls back to the nearest prefix entry (`gpt-4o`).
297
+
298
+ ### Custom Prices
299
+
300
+ **Inline** — add or override any model price at module registration:
301
+
302
+ ```typescript
303
+ LLMBurnModule.forRoot({
304
+ customPrices: {
305
+ 'my-fine-tuned-gpt4': {
306
+ inputPricePerMillion: 5.00,
307
+ outputPricePerMillion: 20.00,
308
+ },
309
+ 'local-llama': {
310
+ inputPricePerMillion: 0,
311
+ outputPricePerMillion: 0,
312
+ },
313
+ },
314
+ })
315
+ ```
316
+
317
+ **External file** — point to your own JSON file to manage prices independently of the package version:
318
+
319
+ ```typescript
320
+ LLMBurnModule.forRoot({
321
+ pricesPath: './prices.json',
322
+ })
323
+ ```
324
+
325
+ The file can be flat or nested (same shape as the built-in `prices.json`):
326
+
327
+ ```json
328
+ // flat
329
+ {
330
+ "gpt-4o": { "inputPricePerMillion": 2.50, "outputPricePerMillion": 10.00 },
331
+ "claude-sonnet-4-6": { "inputPricePerMillion": 3.00, "outputPricePerMillion": 15.00 }
332
+ }
333
+
334
+ // nested (grouped by provider)
335
+ {
336
+ "openai": {
337
+ "gpt-4o": { "inputPricePerMillion": 2.50, "outputPricePerMillion": 10.00 }
338
+ },
339
+ "anthropic": {
340
+ "claude-sonnet-4-6": { "inputPricePerMillion": 3.00, "outputPricePerMillion": 15.00 }
341
+ }
342
+ }
343
+ ```
344
+
345
+ **Priority order:** `customPrices` > `pricesPath` > built-in prices.
346
+
347
+ ---
348
+
349
+ ## API Reference
350
+
351
+ ### LLMBurnModuleOptions
352
+
353
+ | Option | Type | Default | Description |
354
+ |---|---|---|---|
355
+ | `globalBudget` | `number` | — | USD cap for total spend. `BudgetGuard` blocks when exceeded. |
356
+ | `enableLogging` | `boolean` | `false` | Log each tracked call via NestJS Logger. |
357
+ | `customPrices` | `Record<string, ModelPricing>` | — | Inline price overrides. Takes precedence over everything. |
358
+ | `pricesPath` | `string` | — | Path to an external JSON prices file. Takes precedence over built-in prices. |
359
+
360
+ ### LLMStats
361
+
362
+ ```typescript
363
+ interface LLMStats {
364
+ totalCost: number;
365
+ totalInputTokens: number;
366
+ totalOutputTokens: number;
367
+ totalCalls: number;
368
+ byMethod: Record<string, MethodStats>; // breakdown per decorated method
369
+ byModel: Record<string, ModelStats>; // breakdown per model name
370
+ records: LLMCallRecord[]; // all raw records
371
+ }
372
+ ```
373
+
374
+ ### BudgetStatus
375
+
376
+ ```typescript
377
+ interface BudgetStatus {
378
+ globalBudget?: number; // configured cap (undefined if not set)
379
+ totalCost: number; // total USD spent
380
+ remaining?: number; // USD left before cap (0 when exceeded)
381
+ isExceeded: boolean;
382
+ percentUsed?: number; // 0–100+
383
+ }
384
+ ```
385
+
386
+ ---
387
+
388
+ ## License
389
+
390
+ MIT
@@ -0,0 +1,2 @@
1
+ export declare const LLM_BURN_OPTIONS = "LLM_BURN_OPTIONS";
2
+ export declare const LLM_BURN_METADATA = "llm_burn:track";
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LLM_BURN_METADATA = exports.LLM_BURN_OPTIONS = void 0;
4
+ exports.LLM_BURN_OPTIONS = 'LLM_BURN_OPTIONS';
5
+ exports.LLM_BURN_METADATA = 'llm_burn:track';
6
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,gBAAgB,GAAG,kBAAkB,CAAC;AACtC,QAAA,iBAAiB,GAAG,gBAAgB,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface TrackLLMBurnOptions {
2
+ model?: string;
3
+ provider?: string;
4
+ budget?: number;
5
+ extractUsage?: (result: unknown) => ExtractedUsage | null;
6
+ }
7
+ export interface ExtractedUsage {
8
+ inputTokens: number;
9
+ outputTokens: number;
10
+ model?: string;
11
+ provider?: string;
12
+ }
13
+ export declare function TrackLLMBurn(options?: TrackLLMBurnOptions): MethodDecorator;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TrackLLMBurn = TrackLLMBurn;
4
+ const common_1 = require("@nestjs/common");
5
+ const constants_1 = require("../constants");
6
+ const llm_burn_interceptor_1 = require("../llm-burn.interceptor");
7
+ function TrackLLMBurn(options = {}) {
8
+ return (0, common_1.applyDecorators)((0, common_1.SetMetadata)(constants_1.LLM_BURN_METADATA, options), (0, common_1.UseInterceptors)(llm_burn_interceptor_1.LLMBurnInterceptor));
9
+ }
10
+ //# sourceMappingURL=track-llm-burn.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-llm-burn.decorator.js","sourceRoot":"","sources":["../../src/decorators/track-llm-burn.decorator.ts"],"names":[],"mappings":";;AAoDA,oCAKC;AAzDD,2CAA+E;AAC/E,4CAAiD;AACjD,kEAA6D;AAkD7D,SAAgB,YAAY,CAAC,UAA+B,EAAE;IAC5D,OAAO,IAAA,wBAAe,EACpB,IAAA,oBAAW,EAAC,6BAAiB,EAAE,OAAO,CAAC,EACvC,IAAA,wBAAe,EAAC,yCAAkB,CAAC,CACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { CanActivate, ExecutionContext } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { LLMBurnService } from '../llm-burn.service';
4
+ export declare class BudgetGuard implements CanActivate {
5
+ private readonly llmBurnService;
6
+ private readonly reflector;
7
+ private readonly logger;
8
+ constructor(llmBurnService: LLMBurnService, reflector: Reflector);
9
+ canActivate(context: ExecutionContext): boolean;
10
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var BudgetGuard_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.BudgetGuard = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const core_1 = require("@nestjs/core");
16
+ const constants_1 = require("../constants");
17
+ const llm_burn_service_1 = require("../llm-burn.service");
18
+ let BudgetGuard = BudgetGuard_1 = class BudgetGuard {
19
+ constructor(llmBurnService, reflector) {
20
+ this.llmBurnService = llmBurnService;
21
+ this.reflector = reflector;
22
+ this.logger = new common_1.Logger(BudgetGuard_1.name);
23
+ }
24
+ canActivate(context) {
25
+ const options = this.reflector.getAllAndOverride(constants_1.LLM_BURN_METADATA, [
26
+ context.getHandler(),
27
+ context.getClass(),
28
+ ]);
29
+ if (options?.budget != null) {
30
+ const methodName = context.getHandler().name;
31
+ const methodCost = this.llmBurnService.getMethodCost(methodName);
32
+ if (methodCost >= options.budget) {
33
+ const msg = `Method "${methodName}" has exceeded its budget cap of $${options.budget.toFixed(4)} ` +
34
+ `(current: $${methodCost.toFixed(4)}).`;
35
+ this.logger.warn(msg);
36
+ throw new common_1.ForbiddenException(msg);
37
+ }
38
+ }
39
+ const status = this.llmBurnService.getBudgetStatus();
40
+ if (status.isExceeded) {
41
+ const msg = `Global LLM budget of $${status.globalBudget.toFixed(4)} exceeded ` +
42
+ `(spent: $${status.totalCost.toFixed(4)}).`;
43
+ this.logger.warn(msg);
44
+ throw new common_1.ForbiddenException(msg);
45
+ }
46
+ return true;
47
+ }
48
+ };
49
+ exports.BudgetGuard = BudgetGuard;
50
+ exports.BudgetGuard = BudgetGuard = BudgetGuard_1 = __decorate([
51
+ (0, common_1.Injectable)(),
52
+ __metadata("design:paramtypes", [llm_burn_service_1.LLMBurnService,
53
+ core_1.Reflector])
54
+ ], BudgetGuard);
55
+ //# sourceMappingURL=budget.guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.guard.js","sourceRoot":"","sources":["../../src/guards/budget.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAMwB;AACxB,uCAAyC;AACzC,4CAAiD;AAEjD,0DAAqD;AAqB9C,IAAM,WAAW,mBAAjB,MAAM,WAAW;IAGtB,YACmB,cAA8B,EAC9B,SAAoB;QADpB,mBAAc,GAAd,cAAc,CAAgB;QAC9B,cAAS,GAAT,SAAS,CAAW;QAJtB,WAAM,GAAG,IAAI,eAAM,CAAC,aAAW,CAAC,IAAI,CAAC,CAAC;IAKpD,CAAC;IAEJ,WAAW,CAAC,OAAyB;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAsB,6BAAiB,EAAE;YACvF,OAAO,CAAC,UAAU,EAAE;YACpB,OAAO,CAAC,QAAQ,EAAE;SACnB,CAAC,CAAC;QAGH,IAAI,OAAO,EAAE,MAAM,IAAI,IAAI,EAAE,CAAC;YAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC;YAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAEjE,IAAI,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACjC,MAAM,GAAG,GACP,WAAW,UAAU,qCAAqC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;oBACtF,cAAc,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC1C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACtB,MAAM,IAAI,2BAAkB,CAAC,GAAG,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QAGD,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC;QACrD,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,MAAM,GAAG,GACP,yBAAyB,MAAM,CAAC,YAAa,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY;gBACpE,YAAY,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtB,MAAM,IAAI,2BAAkB,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAxCY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;qCAKwB,iCAAc;QACnB,gBAAS;GAL5B,WAAW,CAwCvB"}
@@ -0,0 +1,9 @@
1
+ export { LLMBurnModule } from './llm-burn.module';
2
+ export { LLMBurnService } from './llm-burn.service';
3
+ export { LLMBurnInterceptor } from './llm-burn.interceptor';
4
+ export { BudgetGuard } from './guards/budget.guard';
5
+ export { TrackLLMBurn } from './decorators/track-llm-burn.decorator';
6
+ export type { TrackLLMBurnOptions, ExtractedUsage } from './decorators/track-llm-burn.decorator';
7
+ export type { LLMBurnModuleOptions, LLMBurnModuleAsyncOptions, ModelPricing, } from './interfaces/llm-burn-module-options.interface';
8
+ export type { LLMCallRecord, LLMStats, MethodStats, ModelStats, BudgetStatus, } from './interfaces/llm-usage.interface';
9
+ export { LLM_BURN_OPTIONS, LLM_BURN_METADATA } from './constants';
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LLM_BURN_METADATA = exports.LLM_BURN_OPTIONS = exports.TrackLLMBurn = exports.BudgetGuard = exports.LLMBurnInterceptor = exports.LLMBurnService = exports.LLMBurnModule = void 0;
4
+ var llm_burn_module_1 = require("./llm-burn.module");
5
+ Object.defineProperty(exports, "LLMBurnModule", { enumerable: true, get: function () { return llm_burn_module_1.LLMBurnModule; } });
6
+ var llm_burn_service_1 = require("./llm-burn.service");
7
+ Object.defineProperty(exports, "LLMBurnService", { enumerable: true, get: function () { return llm_burn_service_1.LLMBurnService; } });
8
+ var llm_burn_interceptor_1 = require("./llm-burn.interceptor");
9
+ Object.defineProperty(exports, "LLMBurnInterceptor", { enumerable: true, get: function () { return llm_burn_interceptor_1.LLMBurnInterceptor; } });
10
+ var budget_guard_1 = require("./guards/budget.guard");
11
+ Object.defineProperty(exports, "BudgetGuard", { enumerable: true, get: function () { return budget_guard_1.BudgetGuard; } });
12
+ var track_llm_burn_decorator_1 = require("./decorators/track-llm-burn.decorator");
13
+ Object.defineProperty(exports, "TrackLLMBurn", { enumerable: true, get: function () { return track_llm_burn_decorator_1.TrackLLMBurn; } });
14
+ var constants_1 = require("./constants");
15
+ Object.defineProperty(exports, "LLM_BURN_OPTIONS", { enumerable: true, get: function () { return constants_1.LLM_BURN_OPTIONS; } });
16
+ Object.defineProperty(exports, "LLM_BURN_METADATA", { enumerable: true, get: function () { return constants_1.LLM_BURN_METADATA; } });
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,qDAAkD;AAAzC,gHAAA,aAAa,OAAA;AAGtB,uDAAoD;AAA3C,kHAAA,cAAc,OAAA;AAGvB,+DAA4D;AAAnD,0HAAA,kBAAkB,OAAA;AAG3B,sDAAoD;AAA3C,2GAAA,WAAW,OAAA;AAGpB,kFAAqE;AAA5D,wHAAA,YAAY,OAAA;AAmBrB,yCAAkE;AAAzD,6GAAA,gBAAgB,OAAA;AAAE,8GAAA,iBAAiB,OAAA"}
@@ -0,0 +1,17 @@
1
+ export interface ModelPricing {
2
+ inputPricePerMillion: number;
3
+ outputPricePerMillion: number;
4
+ cachedInputPricePerMillion?: number;
5
+ }
6
+ export interface LLMBurnModuleOptions {
7
+ globalBudget?: number;
8
+ throwOnBudgetExceeded?: boolean;
9
+ customPrices?: Record<string, ModelPricing>;
10
+ pricesPath?: string;
11
+ enableLogging?: boolean;
12
+ }
13
+ export interface LLMBurnModuleAsyncOptions {
14
+ imports?: any[];
15
+ useFactory: (...args: any[]) => LLMBurnModuleOptions | Promise<LLMBurnModuleOptions>;
16
+ inject?: any[];
17
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=llm-burn-module-options.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-burn-module-options.interface.js","sourceRoot":"","sources":["../../src/interfaces/llm-burn-module-options.interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ export interface LLMCallRecord {
2
+ method: string;
3
+ model: string;
4
+ provider: string;
5
+ inputTokens: number;
6
+ outputTokens: number;
7
+ cost: number;
8
+ timestamp: Date;
9
+ }
10
+ export interface MethodStats {
11
+ totalCost: number;
12
+ totalCalls: number;
13
+ totalInputTokens: number;
14
+ totalOutputTokens: number;
15
+ lastCalledAt?: Date;
16
+ }
17
+ export interface ModelStats {
18
+ totalCost: number;
19
+ totalCalls: number;
20
+ totalInputTokens: number;
21
+ totalOutputTokens: number;
22
+ }
23
+ export interface LLMStats {
24
+ totalCost: number;
25
+ totalInputTokens: number;
26
+ totalOutputTokens: number;
27
+ totalCalls: number;
28
+ byMethod: Record<string, MethodStats>;
29
+ byModel: Record<string, ModelStats>;
30
+ records: LLMCallRecord[];
31
+ }
32
+ export interface BudgetStatus {
33
+ globalBudget?: number;
34
+ totalCost: number;
35
+ remaining?: number;
36
+ isExceeded: boolean;
37
+ percentUsed?: number;
38
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=llm-usage.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-usage.interface.js","sourceRoot":"","sources":["../../src/interfaces/llm-usage.interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { Observable } from 'rxjs';
4
+ import { LLMBurnService } from './llm-burn.service';
5
+ export declare class LLMBurnInterceptor implements NestInterceptor {
6
+ private readonly llmBurnService;
7
+ private readonly reflector;
8
+ private readonly logger;
9
+ constructor(llmBurnService: LLMBurnService, reflector: Reflector);
10
+ intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
11
+ private extractUsage;
12
+ private detectProvider;
13
+ }