aden-ts 0.1.1
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/LICENSE +21 -0
- package/README.md +1241 -0
- package/dist/index.d.mts +2478 -0
- package/dist/index.d.ts +2478 -0
- package/dist/index.js +6744 -0
- package/dist/index.mjs +6626 -0
- package/package.json +99 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,2478 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Control Types - Types for the Control Agent
|
|
5
|
+
*
|
|
6
|
+
* Defines control actions, events, and policies for bidirectional
|
|
7
|
+
* communication with the control server.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Control actions that can be applied to requests
|
|
12
|
+
* - allow: Request proceeds normally
|
|
13
|
+
* - block: Request is rejected
|
|
14
|
+
* - throttle: Request is delayed before proceeding
|
|
15
|
+
* - degrade: Request uses a cheaper/fallback model
|
|
16
|
+
* - alert: Request proceeds but triggers an alert notification
|
|
17
|
+
*/
|
|
18
|
+
type ControlAction = "allow" | "block" | "throttle" | "degrade" | "alert";
|
|
19
|
+
/**
|
|
20
|
+
* Control decision - what action to take for a request
|
|
21
|
+
*/
|
|
22
|
+
interface ControlDecision {
|
|
23
|
+
/** The action to take */
|
|
24
|
+
action: ControlAction;
|
|
25
|
+
/** Human-readable reason for the decision */
|
|
26
|
+
reason?: string;
|
|
27
|
+
/** If action is "degrade", switch to this model */
|
|
28
|
+
degradeToModel?: string;
|
|
29
|
+
/** If action is "throttle", delay by this many milliseconds */
|
|
30
|
+
throttleDelayMs?: number;
|
|
31
|
+
/** If action is "alert", the severity level */
|
|
32
|
+
alertLevel?: "info" | "warning" | "critical";
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Base event structure with common fields
|
|
36
|
+
*/
|
|
37
|
+
interface BaseEvent {
|
|
38
|
+
/** Event type discriminator */
|
|
39
|
+
event_type: string;
|
|
40
|
+
/** ISO timestamp of the event */
|
|
41
|
+
timestamp: string;
|
|
42
|
+
/** SDK instance ID for tracking */
|
|
43
|
+
sdk_instance_id: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Control event - emitted when a control action is taken
|
|
47
|
+
*/
|
|
48
|
+
interface ControlEvent extends BaseEvent {
|
|
49
|
+
event_type: "control";
|
|
50
|
+
/** Trace ID for correlation */
|
|
51
|
+
trace_id: string;
|
|
52
|
+
/** Span ID of the affected request */
|
|
53
|
+
span_id: string;
|
|
54
|
+
/** Context ID (user, session, deal, etc.) */
|
|
55
|
+
context_id?: string;
|
|
56
|
+
/** Provider (openai, anthropic, gemini) */
|
|
57
|
+
provider: string;
|
|
58
|
+
/** Original model that was requested */
|
|
59
|
+
original_model: string;
|
|
60
|
+
/** Action that was taken */
|
|
61
|
+
action: ControlAction;
|
|
62
|
+
/** Reason for the action */
|
|
63
|
+
reason?: string;
|
|
64
|
+
/** If degraded, what model was used instead */
|
|
65
|
+
degraded_to?: string;
|
|
66
|
+
/** If throttled, how long was the delay in ms */
|
|
67
|
+
throttle_delay_ms?: number;
|
|
68
|
+
/** Estimated cost that triggered the decision */
|
|
69
|
+
estimated_cost?: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Heartbeat event - periodic health check
|
|
73
|
+
*/
|
|
74
|
+
interface HeartbeatEvent extends BaseEvent {
|
|
75
|
+
event_type: "heartbeat";
|
|
76
|
+
/** Connection status */
|
|
77
|
+
status: "healthy" | "degraded" | "reconnecting";
|
|
78
|
+
/** Requests processed since last heartbeat */
|
|
79
|
+
requests_since_last: number;
|
|
80
|
+
/** Errors since last heartbeat */
|
|
81
|
+
errors_since_last: number;
|
|
82
|
+
/** Current policy cache age in seconds */
|
|
83
|
+
policy_cache_age_seconds: number;
|
|
84
|
+
/** Whether WebSocket is connected */
|
|
85
|
+
websocket_connected: boolean;
|
|
86
|
+
/** SDK version */
|
|
87
|
+
sdk_version: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Budget type - what scope the budget applies to
|
|
91
|
+
*/
|
|
92
|
+
type BudgetType = "global" | "agent" | "tenant" | "customer" | "feature" | "tag";
|
|
93
|
+
/**
|
|
94
|
+
* Limit action - what to do when budget is exceeded
|
|
95
|
+
*/
|
|
96
|
+
type LimitAction = "kill" | "throttle" | "degrade";
|
|
97
|
+
/**
|
|
98
|
+
* Budget alert configuration
|
|
99
|
+
*/
|
|
100
|
+
interface BudgetAlert {
|
|
101
|
+
/** Threshold percentage (0-100) */
|
|
102
|
+
threshold: number;
|
|
103
|
+
/** Whether this alert is enabled */
|
|
104
|
+
enabled: boolean;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Budget notification settings
|
|
108
|
+
*/
|
|
109
|
+
interface BudgetNotifications {
|
|
110
|
+
/** Show in-app notifications */
|
|
111
|
+
inApp: boolean;
|
|
112
|
+
/** Send email notifications */
|
|
113
|
+
email: boolean;
|
|
114
|
+
/** Email recipients */
|
|
115
|
+
emailRecipients: string[];
|
|
116
|
+
/** Send webhook notifications */
|
|
117
|
+
webhook: boolean;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Budget rule - limits spend per context
|
|
121
|
+
*/
|
|
122
|
+
interface BudgetRule {
|
|
123
|
+
/** Unique identifier for this budget */
|
|
124
|
+
id: string;
|
|
125
|
+
/** Human-readable name */
|
|
126
|
+
name: string;
|
|
127
|
+
/** Budget type/scope */
|
|
128
|
+
type: BudgetType;
|
|
129
|
+
/** Tags for tag-based budgets (required when type is 'tag') */
|
|
130
|
+
tags?: string[];
|
|
131
|
+
/** Budget limit in USD */
|
|
132
|
+
limit: number;
|
|
133
|
+
/** Current spend in USD */
|
|
134
|
+
spent: number;
|
|
135
|
+
/** Action to take when budget is exceeded */
|
|
136
|
+
limitAction: LimitAction;
|
|
137
|
+
/** If limitAction is "degrade", switch to this model */
|
|
138
|
+
degradeToModel?: string;
|
|
139
|
+
/** Alert thresholds */
|
|
140
|
+
alerts: BudgetAlert[];
|
|
141
|
+
/** Notification settings */
|
|
142
|
+
notifications: BudgetNotifications;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Throttle rule - rate limiting
|
|
146
|
+
*/
|
|
147
|
+
interface ThrottleRule {
|
|
148
|
+
/** Context ID this rule applies to (omit for global) */
|
|
149
|
+
context_id?: string;
|
|
150
|
+
/** Provider this rule applies to (omit for all) */
|
|
151
|
+
provider?: string;
|
|
152
|
+
/** Maximum requests per minute */
|
|
153
|
+
requests_per_minute?: number;
|
|
154
|
+
/** Fixed delay to apply to each request (ms) */
|
|
155
|
+
delay_ms?: number;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Block rule - hard block on certain requests
|
|
159
|
+
*/
|
|
160
|
+
interface BlockRule {
|
|
161
|
+
/** Context ID to block (omit for pattern match) */
|
|
162
|
+
context_id?: string;
|
|
163
|
+
/** Provider to block (omit for all) */
|
|
164
|
+
provider?: string;
|
|
165
|
+
/** Model pattern to block (e.g., "gpt-4*") */
|
|
166
|
+
model_pattern?: string;
|
|
167
|
+
/** Reason shown to caller */
|
|
168
|
+
reason: string;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Degrade rule - automatic model downgrade
|
|
172
|
+
*/
|
|
173
|
+
interface DegradeRule {
|
|
174
|
+
/** Model to downgrade from */
|
|
175
|
+
from_model: string;
|
|
176
|
+
/** Model to downgrade to */
|
|
177
|
+
to_model: string;
|
|
178
|
+
/** When to trigger the downgrade */
|
|
179
|
+
trigger: "budget_threshold" | "rate_limit" | "always";
|
|
180
|
+
/** For budget_threshold: percentage at which to trigger (0-100) */
|
|
181
|
+
threshold_percent?: number;
|
|
182
|
+
/** Context ID this rule applies to (omit for all) */
|
|
183
|
+
context_id?: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Alert rule - trigger notifications without blocking
|
|
187
|
+
*/
|
|
188
|
+
interface AlertRule {
|
|
189
|
+
/** Context ID this rule applies to (omit for global) */
|
|
190
|
+
context_id?: string;
|
|
191
|
+
/** Provider this rule applies to (omit for all) */
|
|
192
|
+
provider?: string;
|
|
193
|
+
/** Model pattern to alert on (e.g., "gpt-4*" for expensive models) */
|
|
194
|
+
model_pattern?: string;
|
|
195
|
+
/** When to trigger the alert */
|
|
196
|
+
trigger: "budget_threshold" | "model_usage" | "always";
|
|
197
|
+
/** For budget_threshold: percentage at which to trigger (0-100) */
|
|
198
|
+
threshold_percent?: number;
|
|
199
|
+
/** Alert severity level */
|
|
200
|
+
level: "info" | "warning" | "critical";
|
|
201
|
+
/** Message to include in the alert */
|
|
202
|
+
message: string;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Complete control policy from server
|
|
206
|
+
*/
|
|
207
|
+
interface ControlPolicy {
|
|
208
|
+
/** Policy version for cache invalidation */
|
|
209
|
+
version: string;
|
|
210
|
+
/** When this policy was last updated */
|
|
211
|
+
updated_at: string;
|
|
212
|
+
/** Budget rules */
|
|
213
|
+
budgets?: BudgetRule[];
|
|
214
|
+
/** Throttle rules */
|
|
215
|
+
throttles?: ThrottleRule[];
|
|
216
|
+
/** Block rules */
|
|
217
|
+
blocks?: BlockRule[];
|
|
218
|
+
/** Degrade rules */
|
|
219
|
+
degradations?: DegradeRule[];
|
|
220
|
+
/** Alert rules */
|
|
221
|
+
alerts?: AlertRule[];
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Request context for getting a control decision
|
|
225
|
+
*/
|
|
226
|
+
interface ControlRequest {
|
|
227
|
+
/** Context ID (user, session, deal, etc.) */
|
|
228
|
+
context_id?: string;
|
|
229
|
+
/** Provider being called */
|
|
230
|
+
provider: string;
|
|
231
|
+
/** Model being requested */
|
|
232
|
+
model: string;
|
|
233
|
+
/** Estimated cost of this request in USD */
|
|
234
|
+
estimated_cost?: number;
|
|
235
|
+
/** Estimated input tokens */
|
|
236
|
+
estimated_input_tokens?: number;
|
|
237
|
+
/** Custom metadata */
|
|
238
|
+
metadata?: Record<string, unknown>;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Alert event passed to onAlert callback
|
|
242
|
+
*/
|
|
243
|
+
interface AlertEvent {
|
|
244
|
+
/** Alert severity level */
|
|
245
|
+
level: "info" | "warning" | "critical";
|
|
246
|
+
/** Alert message */
|
|
247
|
+
message: string;
|
|
248
|
+
/** Reason the alert was triggered */
|
|
249
|
+
reason: string;
|
|
250
|
+
/** Context ID that triggered the alert */
|
|
251
|
+
contextId?: string;
|
|
252
|
+
/** Provider that triggered the alert */
|
|
253
|
+
provider: string;
|
|
254
|
+
/** Model that triggered the alert */
|
|
255
|
+
model: string;
|
|
256
|
+
/** Timestamp of the alert */
|
|
257
|
+
timestamp: Date;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Options for creating a control agent
|
|
261
|
+
*/
|
|
262
|
+
interface ControlAgentOptions {
|
|
263
|
+
/** Server URL (wss:// for WebSocket, https:// for HTTP-only) */
|
|
264
|
+
serverUrl: string;
|
|
265
|
+
/** API key for authentication */
|
|
266
|
+
apiKey: string;
|
|
267
|
+
/** Polling interval for HTTP fallback (ms), default: 30000 */
|
|
268
|
+
pollingIntervalMs?: number;
|
|
269
|
+
/** Heartbeat interval (ms), default: 10000 */
|
|
270
|
+
heartbeatIntervalMs?: number;
|
|
271
|
+
/** Request timeout (ms), default: 5000 */
|
|
272
|
+
timeoutMs?: number;
|
|
273
|
+
/** Fail open (allow) if server is unreachable, default: true */
|
|
274
|
+
failOpen?: boolean;
|
|
275
|
+
/** Custom context ID extractor */
|
|
276
|
+
getContextId?: () => string | undefined;
|
|
277
|
+
/** SDK instance identifier (auto-generated if not provided) */
|
|
278
|
+
instanceId?: string;
|
|
279
|
+
/**
|
|
280
|
+
* Callback invoked when an alert is triggered.
|
|
281
|
+
* Alerts do NOT block requests - they are notifications only.
|
|
282
|
+
* Use this for logging, notifications, or monitoring.
|
|
283
|
+
*/
|
|
284
|
+
onAlert?: (alert: AlertEvent) => void | Promise<void>;
|
|
285
|
+
/**
|
|
286
|
+
* Enable hybrid enforcement (local + server-side validation).
|
|
287
|
+
* When enabled, budgets above the threshold are validated with the server.
|
|
288
|
+
* Default: false
|
|
289
|
+
*/
|
|
290
|
+
enableHybridEnforcement?: boolean;
|
|
291
|
+
/**
|
|
292
|
+
* Budget usage threshold (percentage) at which to start server validation.
|
|
293
|
+
* Requests below this threshold use local-only enforcement.
|
|
294
|
+
* Default: 80
|
|
295
|
+
*/
|
|
296
|
+
serverValidationThreshold?: number;
|
|
297
|
+
/**
|
|
298
|
+
* Timeout for server validation requests (ms).
|
|
299
|
+
* Default: 2000
|
|
300
|
+
*/
|
|
301
|
+
serverValidationTimeoutMs?: number;
|
|
302
|
+
/**
|
|
303
|
+
* Enable adaptive threshold adjustment based on remaining budget.
|
|
304
|
+
* When enabled, force validation when remaining budget is critically low.
|
|
305
|
+
* Default: true
|
|
306
|
+
*/
|
|
307
|
+
adaptiveThresholdEnabled?: boolean;
|
|
308
|
+
/**
|
|
309
|
+
* Minimum remaining budget (USD) that triggers forced server validation.
|
|
310
|
+
* Only applies when adaptiveThresholdEnabled is true.
|
|
311
|
+
* Default: 1.0
|
|
312
|
+
*/
|
|
313
|
+
adaptiveMinRemainingUsd?: number;
|
|
314
|
+
/**
|
|
315
|
+
* Enable probabilistic sampling for server validation.
|
|
316
|
+
* Reduces latency impact by validating a fraction of requests.
|
|
317
|
+
* Default: true
|
|
318
|
+
*/
|
|
319
|
+
samplingEnabled?: boolean;
|
|
320
|
+
/**
|
|
321
|
+
* Base sampling rate at the threshold (0-1).
|
|
322
|
+
* This is the minimum rate used at serverValidationThreshold.
|
|
323
|
+
* Default: 0.1 (10%)
|
|
324
|
+
*/
|
|
325
|
+
samplingBaseRate?: number;
|
|
326
|
+
/**
|
|
327
|
+
* Budget usage percentage at which to validate all requests.
|
|
328
|
+
* Between threshold and this value, sampling rate interpolates to 1.0.
|
|
329
|
+
* Default: 95
|
|
330
|
+
*/
|
|
331
|
+
samplingFullValidationPercent?: number;
|
|
332
|
+
/**
|
|
333
|
+
* Maximum expected overspend percentage beyond the soft limit.
|
|
334
|
+
* Acts as a hard limit safety net to prevent runaway spending.
|
|
335
|
+
* Default: 10 (allowing up to 110% of budget)
|
|
336
|
+
*/
|
|
337
|
+
maxExpectedOverspendPercent?: number;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Budget validation request sent to server
|
|
341
|
+
*/
|
|
342
|
+
interface BudgetValidationRequest {
|
|
343
|
+
/** Budget ID to validate */
|
|
344
|
+
budgetId: string;
|
|
345
|
+
/** Estimated cost of the pending request */
|
|
346
|
+
estimatedCost: number;
|
|
347
|
+
/** Local spend tracking (server uses max of local vs server) */
|
|
348
|
+
localSpend?: number;
|
|
349
|
+
/** Budget context */
|
|
350
|
+
context?: {
|
|
351
|
+
type?: string;
|
|
352
|
+
value?: string;
|
|
353
|
+
tags?: string[];
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Budget validation response from server
|
|
358
|
+
*/
|
|
359
|
+
interface BudgetValidationResponse {
|
|
360
|
+
/** Whether the request is allowed */
|
|
361
|
+
allowed: boolean;
|
|
362
|
+
/** Action to take */
|
|
363
|
+
action: "allow" | "block" | "degrade" | "throttle";
|
|
364
|
+
/** Authoritative spend from server (TSDB) */
|
|
365
|
+
authoritativeSpend: number;
|
|
366
|
+
/** Budget limit */
|
|
367
|
+
budgetLimit: number;
|
|
368
|
+
/** Current usage percentage */
|
|
369
|
+
usagePercent: number;
|
|
370
|
+
/** Policy version */
|
|
371
|
+
policyVersion: string;
|
|
372
|
+
/** Updated spend after this request */
|
|
373
|
+
updatedSpend: number;
|
|
374
|
+
/** Reason for the decision */
|
|
375
|
+
reason?: string;
|
|
376
|
+
/** Projected usage percentage */
|
|
377
|
+
projectedPercent?: number;
|
|
378
|
+
/** Model to degrade to (if action is "degrade") */
|
|
379
|
+
degradeToModel?: string;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Control Agent interface - the public API
|
|
383
|
+
*/
|
|
384
|
+
interface IControlAgent {
|
|
385
|
+
/**
|
|
386
|
+
* Connect to the control server
|
|
387
|
+
*/
|
|
388
|
+
connect(): Promise<void>;
|
|
389
|
+
/**
|
|
390
|
+
* Disconnect from the control server
|
|
391
|
+
*/
|
|
392
|
+
disconnect(): Promise<void>;
|
|
393
|
+
/**
|
|
394
|
+
* Get a control decision for a request
|
|
395
|
+
*/
|
|
396
|
+
getDecision(request: ControlRequest): Promise<ControlDecision>;
|
|
397
|
+
/**
|
|
398
|
+
* Report a metric event to the server
|
|
399
|
+
*/
|
|
400
|
+
reportMetric(event: MetricEvent): Promise<void>;
|
|
401
|
+
/**
|
|
402
|
+
* Report a control event to the server
|
|
403
|
+
*/
|
|
404
|
+
reportControlEvent(event: Omit<ControlEvent, "event_type" | "timestamp" | "sdk_instance_id">): Promise<void>;
|
|
405
|
+
/**
|
|
406
|
+
* Check if connected to server
|
|
407
|
+
*/
|
|
408
|
+
isConnected(): boolean;
|
|
409
|
+
/**
|
|
410
|
+
* Get current cached policy
|
|
411
|
+
*/
|
|
412
|
+
getPolicy(): ControlPolicy | null;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Normalized usage metrics that work across both API response shapes
|
|
417
|
+
* (Responses API vs Chat Completions API)
|
|
418
|
+
*/
|
|
419
|
+
interface NormalizedUsage {
|
|
420
|
+
/** Input/prompt tokens consumed */
|
|
421
|
+
input_tokens: number;
|
|
422
|
+
/** Output/completion tokens consumed */
|
|
423
|
+
output_tokens: number;
|
|
424
|
+
/** Total tokens (input + output) */
|
|
425
|
+
total_tokens: number;
|
|
426
|
+
/** Reasoning tokens used (for o1/o3 models) */
|
|
427
|
+
reasoning_tokens: number;
|
|
428
|
+
/** Tokens served from prompt cache (reduces cost) */
|
|
429
|
+
cached_tokens: number;
|
|
430
|
+
/** Prediction tokens that were accepted */
|
|
431
|
+
accepted_prediction_tokens: number;
|
|
432
|
+
/** Prediction tokens that were rejected */
|
|
433
|
+
rejected_prediction_tokens: number;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Request metadata that affects billing/cost
|
|
437
|
+
*/
|
|
438
|
+
interface RequestMetadata {
|
|
439
|
+
/** Unique trace ID for this request */
|
|
440
|
+
traceId: string;
|
|
441
|
+
/** Model used for the request */
|
|
442
|
+
model: string;
|
|
443
|
+
/** Service tier (affects pricing/performance) */
|
|
444
|
+
service_tier?: "auto" | "default" | "flex" | "priority" | string;
|
|
445
|
+
/** Maximum output tokens cap */
|
|
446
|
+
max_output_tokens?: number;
|
|
447
|
+
/** Maximum tool calls allowed */
|
|
448
|
+
max_tool_calls?: number;
|
|
449
|
+
/** Prompt cache key for improved cache hits */
|
|
450
|
+
prompt_cache_key?: string;
|
|
451
|
+
/** Prompt cache retention policy */
|
|
452
|
+
prompt_cache_retention?: "in_memory" | "24h" | string;
|
|
453
|
+
/** Whether streaming was enabled */
|
|
454
|
+
stream: boolean;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Information about where an LLM call originated in the code
|
|
458
|
+
*/
|
|
459
|
+
interface CallSite {
|
|
460
|
+
/** File path where the call originated */
|
|
461
|
+
file: string;
|
|
462
|
+
/** Line number */
|
|
463
|
+
line: number;
|
|
464
|
+
/** Column number */
|
|
465
|
+
column: number;
|
|
466
|
+
/** Function name (if available) */
|
|
467
|
+
function?: string;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Complete metric event emitted after each API call.
|
|
471
|
+
* All fields are flat (not nested) for consistent cross-provider analytics.
|
|
472
|
+
* Uses OpenTelemetry-compatible naming: trace_id groups operations, span_id identifies each operation.
|
|
473
|
+
*/
|
|
474
|
+
interface MetricEvent {
|
|
475
|
+
/** Trace ID grouping related operations (OTel standard) */
|
|
476
|
+
trace_id: string;
|
|
477
|
+
/** Unique span ID for this specific operation (OTel standard) */
|
|
478
|
+
span_id: string;
|
|
479
|
+
/** Parent span ID for nested/hierarchical calls (OTel standard) */
|
|
480
|
+
parent_span_id?: string;
|
|
481
|
+
/** Provider-specific request ID (if available) */
|
|
482
|
+
request_id: string | null;
|
|
483
|
+
/** LLM provider: openai, gemini, anthropic */
|
|
484
|
+
provider: "openai" | "gemini" | "anthropic";
|
|
485
|
+
/** Model used for the request */
|
|
486
|
+
model: string;
|
|
487
|
+
/** Whether streaming was enabled */
|
|
488
|
+
stream: boolean;
|
|
489
|
+
/** ISO timestamp when the request started */
|
|
490
|
+
timestamp: string;
|
|
491
|
+
/** Request latency in milliseconds */
|
|
492
|
+
latency_ms: number;
|
|
493
|
+
/** HTTP status code (if available) */
|
|
494
|
+
status_code?: number;
|
|
495
|
+
/** Error message if request failed */
|
|
496
|
+
error?: string;
|
|
497
|
+
/** Input/prompt tokens consumed */
|
|
498
|
+
input_tokens: number;
|
|
499
|
+
/** Output/completion tokens consumed */
|
|
500
|
+
output_tokens: number;
|
|
501
|
+
/** Total tokens (input + output) */
|
|
502
|
+
total_tokens: number;
|
|
503
|
+
/** Tokens served from cache (reduces cost) */
|
|
504
|
+
cached_tokens: number;
|
|
505
|
+
/** Reasoning tokens used (for o1/o3 models) */
|
|
506
|
+
reasoning_tokens: number;
|
|
507
|
+
/** Remaining requests in current window */
|
|
508
|
+
rate_limit_remaining_requests?: number;
|
|
509
|
+
/** Remaining tokens in current window */
|
|
510
|
+
rate_limit_remaining_tokens?: number;
|
|
511
|
+
/** Time until request limit resets (seconds) */
|
|
512
|
+
rate_limit_reset_requests?: number;
|
|
513
|
+
/** Time until token limit resets (seconds) */
|
|
514
|
+
rate_limit_reset_tokens?: number;
|
|
515
|
+
/** Sequence number within the trace */
|
|
516
|
+
call_sequence?: number;
|
|
517
|
+
/** Stack of agent/handler names leading to this call */
|
|
518
|
+
agent_stack?: string[];
|
|
519
|
+
/** File path where the call originated (immediate caller) */
|
|
520
|
+
call_site_file?: string;
|
|
521
|
+
/** Line number where the call originated */
|
|
522
|
+
call_site_line?: number;
|
|
523
|
+
/** Column number where the call originated */
|
|
524
|
+
call_site_column?: number;
|
|
525
|
+
/** Function name where the call originated */
|
|
526
|
+
call_site_function?: string;
|
|
527
|
+
/** Full call stack for detailed tracing (file:line:function) */
|
|
528
|
+
call_stack?: string[];
|
|
529
|
+
/** Number of tool calls made */
|
|
530
|
+
tool_call_count?: number;
|
|
531
|
+
/** Tool names that were called (comma-separated) */
|
|
532
|
+
tool_names?: string;
|
|
533
|
+
/** Service tier (OpenAI: auto, default, flex, priority) */
|
|
534
|
+
service_tier?: string;
|
|
535
|
+
/** Custom metadata attached to the request */
|
|
536
|
+
metadata?: Record<string, string>;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Rate limit information from response headers
|
|
540
|
+
*/
|
|
541
|
+
interface RateLimitInfo {
|
|
542
|
+
/** Remaining requests in current window */
|
|
543
|
+
remaining_requests?: number;
|
|
544
|
+
/** Remaining tokens in current window */
|
|
545
|
+
remaining_tokens?: number;
|
|
546
|
+
/** Time until request limit resets (seconds) */
|
|
547
|
+
reset_requests?: number;
|
|
548
|
+
/** Time until token limit resets (seconds) */
|
|
549
|
+
reset_tokens?: number;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Metric for individual tool calls
|
|
553
|
+
*/
|
|
554
|
+
interface ToolCallMetric {
|
|
555
|
+
/** Tool type (function, web_search, code_interpreter, etc.) */
|
|
556
|
+
type: string;
|
|
557
|
+
/** Tool/function name */
|
|
558
|
+
name?: string;
|
|
559
|
+
/** Duration of tool execution in ms (if available) */
|
|
560
|
+
duration_ms?: number;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Callback function for emitting metrics
|
|
564
|
+
*/
|
|
565
|
+
type MetricEmitter = (event: MetricEvent) => void | Promise<void>;
|
|
566
|
+
/**
|
|
567
|
+
* Context passed to the beforeRequest hook
|
|
568
|
+
*/
|
|
569
|
+
interface BeforeRequestContext {
|
|
570
|
+
/** The model being used for this request */
|
|
571
|
+
model: string;
|
|
572
|
+
/** Whether this is a streaming request */
|
|
573
|
+
stream: boolean;
|
|
574
|
+
/** Generated span ID for this request (OTel standard) */
|
|
575
|
+
spanId: string;
|
|
576
|
+
/** Trace ID grouping related operations (OTel standard) */
|
|
577
|
+
traceId: string;
|
|
578
|
+
/** Timestamp when the request was initiated */
|
|
579
|
+
timestamp: Date;
|
|
580
|
+
/** Custom metadata that can be passed through */
|
|
581
|
+
metadata?: Record<string, unknown>;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Result from the beforeRequest hook
|
|
585
|
+
*/
|
|
586
|
+
type BeforeRequestResult = {
|
|
587
|
+
action: "proceed";
|
|
588
|
+
} | {
|
|
589
|
+
action: "throttle";
|
|
590
|
+
delayMs: number;
|
|
591
|
+
} | {
|
|
592
|
+
action: "cancel";
|
|
593
|
+
reason: string;
|
|
594
|
+
} | {
|
|
595
|
+
action: "degrade";
|
|
596
|
+
toModel: string;
|
|
597
|
+
reason?: string;
|
|
598
|
+
delayMs?: number;
|
|
599
|
+
} | {
|
|
600
|
+
action: "alert";
|
|
601
|
+
level: "info" | "warning" | "critical";
|
|
602
|
+
message: string;
|
|
603
|
+
delayMs?: number;
|
|
604
|
+
};
|
|
605
|
+
/**
|
|
606
|
+
* Hook called before each API request, allowing user-defined rate limiting
|
|
607
|
+
*
|
|
608
|
+
* @example
|
|
609
|
+
* ```ts
|
|
610
|
+
* beforeRequest: async (params, context) => {
|
|
611
|
+
* const remaining = await checkQuota(context.metadata?.tenantId);
|
|
612
|
+
* if (remaining <= 0) {
|
|
613
|
+
* return { action: 'cancel', reason: 'Quota exceeded' };
|
|
614
|
+
* }
|
|
615
|
+
* if (remaining < 10) {
|
|
616
|
+
* return { action: 'throttle', delayMs: 1000 };
|
|
617
|
+
* }
|
|
618
|
+
* return { action: 'proceed' };
|
|
619
|
+
* }
|
|
620
|
+
* ```
|
|
621
|
+
*/
|
|
622
|
+
type BeforeRequestHook = (params: Record<string, unknown>, context: BeforeRequestContext) => BeforeRequestResult | Promise<BeforeRequestResult>;
|
|
623
|
+
/**
|
|
624
|
+
* Error thrown when a request is cancelled by the beforeRequest hook
|
|
625
|
+
*/
|
|
626
|
+
declare class RequestCancelledError extends Error {
|
|
627
|
+
readonly reason: string;
|
|
628
|
+
readonly context: BeforeRequestContext;
|
|
629
|
+
constructor(reason: string, context: BeforeRequestContext);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Options for the metered OpenAI client
|
|
633
|
+
*/
|
|
634
|
+
/**
|
|
635
|
+
* SDK classes that can be passed for instrumentation.
|
|
636
|
+
* Use this when you have multiple copies of SDK packages in your node_modules
|
|
637
|
+
* (e.g., when using file: dependencies or monorepos).
|
|
638
|
+
*/
|
|
639
|
+
interface SDKClasses {
|
|
640
|
+
/** GoogleGenerativeAI class from @google/generative-ai */
|
|
641
|
+
GoogleGenerativeAI?: any;
|
|
642
|
+
/** OpenAI class from openai */
|
|
643
|
+
OpenAI?: any;
|
|
644
|
+
/** Anthropic class from @anthropic-ai/sdk */
|
|
645
|
+
Anthropic?: any;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Default control server URL
|
|
649
|
+
*/
|
|
650
|
+
declare const DEFAULT_CONTROL_SERVER = "https://kube.acho.io";
|
|
651
|
+
/**
|
|
652
|
+
* Get the control server URL with priority:
|
|
653
|
+
* 1. Explicit serverUrl option
|
|
654
|
+
* 2. ADEN_API_URL environment variable
|
|
655
|
+
* 3. DEFAULT_CONTROL_SERVER constant
|
|
656
|
+
*/
|
|
657
|
+
declare function getControlServerUrl(serverUrl?: string): string;
|
|
658
|
+
interface MeterOptions {
|
|
659
|
+
/**
|
|
660
|
+
* API key for the control server.
|
|
661
|
+
* When provided, automatically creates a control agent and emitter.
|
|
662
|
+
* This is the simplest way to enable metering with remote control.
|
|
663
|
+
*
|
|
664
|
+
* If not provided, checks ADEN_API_KEY environment variable.
|
|
665
|
+
*
|
|
666
|
+
* @example
|
|
667
|
+
* ```typescript
|
|
668
|
+
* // Simplest setup - just provide API key
|
|
669
|
+
* await instrument({
|
|
670
|
+
* apiKey: process.env.ADEN_API_KEY,
|
|
671
|
+
* sdks: { OpenAI },
|
|
672
|
+
* });
|
|
673
|
+
* ```
|
|
674
|
+
*/
|
|
675
|
+
apiKey?: string;
|
|
676
|
+
/**
|
|
677
|
+
* Control server URL.
|
|
678
|
+
* Priority: serverUrl option > ADEN_API_URL env var > https://kube.acho.io
|
|
679
|
+
* Only used when apiKey is provided.
|
|
680
|
+
*/
|
|
681
|
+
serverUrl?: string;
|
|
682
|
+
/**
|
|
683
|
+
* Whether to allow requests when control server is unreachable.
|
|
684
|
+
* Default: true (fail open - requests proceed if server is down)
|
|
685
|
+
* Set to false for strict control (fail closed - block if server unreachable)
|
|
686
|
+
*/
|
|
687
|
+
failOpen?: boolean;
|
|
688
|
+
/**
|
|
689
|
+
* Custom metric emitter function.
|
|
690
|
+
* When apiKey is provided, this is optional - metrics go to control server.
|
|
691
|
+
* When apiKey is NOT provided, this is required.
|
|
692
|
+
*
|
|
693
|
+
* You can combine with apiKey to emit to multiple destinations:
|
|
694
|
+
* @example
|
|
695
|
+
* ```typescript
|
|
696
|
+
* await instrument({
|
|
697
|
+
* apiKey: process.env.ADEN_API_KEY,
|
|
698
|
+
* emitMetric: createConsoleEmitter({ pretty: true }), // Also log locally
|
|
699
|
+
* });
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
702
|
+
emitMetric?: MetricEmitter;
|
|
703
|
+
/** Whether to include tool call metrics (default: true) */
|
|
704
|
+
trackToolCalls?: boolean;
|
|
705
|
+
/** Custom span ID generator (default: crypto.randomUUID) */
|
|
706
|
+
generateSpanId?: () => string;
|
|
707
|
+
/**
|
|
708
|
+
* Hook called before each request for user-defined rate limiting.
|
|
709
|
+
* Can cancel requests, throttle them with a delay, or allow them to proceed.
|
|
710
|
+
*/
|
|
711
|
+
beforeRequest?: BeforeRequestHook;
|
|
712
|
+
/** Custom metadata to pass to beforeRequest hook */
|
|
713
|
+
requestMetadata?: Record<string, unknown>;
|
|
714
|
+
/**
|
|
715
|
+
* Whether to automatically track call relationships using AsyncLocalStorage.
|
|
716
|
+
* When enabled, related LLM calls are grouped by session, with parent/child
|
|
717
|
+
* relationships, agent stacks, and call sites automatically detected.
|
|
718
|
+
* Default: true
|
|
719
|
+
*/
|
|
720
|
+
trackCallRelationships?: boolean;
|
|
721
|
+
/**
|
|
722
|
+
* SDK classes to instrument. Pass these when you have multiple copies of
|
|
723
|
+
* SDK packages in your node_modules (common in monorepos or with file: dependencies).
|
|
724
|
+
* If not provided, Aden will try to import the SDKs from its own node_modules,
|
|
725
|
+
* which may not be the same instance your application uses.
|
|
726
|
+
*
|
|
727
|
+
* @example
|
|
728
|
+
* ```typescript
|
|
729
|
+
* import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
730
|
+
*
|
|
731
|
+
* instrument({
|
|
732
|
+
* apiKey: process.env.ADEN_API_KEY,
|
|
733
|
+
* sdks: { GoogleGenerativeAI },
|
|
734
|
+
* });
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
sdks?: SDKClasses;
|
|
738
|
+
/**
|
|
739
|
+
* Function to get the current context ID (user ID, session ID, etc.)
|
|
740
|
+
* Used for budget tracking and policy enforcement per context.
|
|
741
|
+
*
|
|
742
|
+
* @example
|
|
743
|
+
* ```typescript
|
|
744
|
+
* instrument({
|
|
745
|
+
* apiKey: process.env.ADEN_API_KEY,
|
|
746
|
+
* getContextId: () => getCurrentUserId(),
|
|
747
|
+
* });
|
|
748
|
+
* ```
|
|
749
|
+
*/
|
|
750
|
+
getContextId?: () => string | undefined;
|
|
751
|
+
/**
|
|
752
|
+
* Pre-configured control agent instance.
|
|
753
|
+
* Use this for advanced control agent configuration.
|
|
754
|
+
* When apiKey is provided, a control agent is created automatically.
|
|
755
|
+
*/
|
|
756
|
+
controlAgent?: IControlAgent;
|
|
757
|
+
/**
|
|
758
|
+
* Callback invoked when an alert is triggered by the control agent.
|
|
759
|
+
* Alerts do NOT block requests - they are notifications only.
|
|
760
|
+
* Use this for logging, notifications, or monitoring.
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* ```typescript
|
|
764
|
+
* instrument({
|
|
765
|
+
* apiKey: process.env.ADEN_API_KEY,
|
|
766
|
+
* onAlert: (alert) => {
|
|
767
|
+
* console.warn(`[${alert.level}] ${alert.message}`);
|
|
768
|
+
* // Send to Slack, PagerDuty, etc.
|
|
769
|
+
* },
|
|
770
|
+
* });
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
onAlert?: (alert: {
|
|
774
|
+
level: "info" | "warning" | "critical";
|
|
775
|
+
message: string;
|
|
776
|
+
reason: string;
|
|
777
|
+
contextId?: string;
|
|
778
|
+
provider: string;
|
|
779
|
+
model: string;
|
|
780
|
+
timestamp: Date;
|
|
781
|
+
}) => void | Promise<void>;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Budget configuration for guardrails
|
|
785
|
+
*/
|
|
786
|
+
interface BudgetConfig {
|
|
787
|
+
/** Maximum input tokens allowed per request */
|
|
788
|
+
maxInputTokens?: number;
|
|
789
|
+
/** Maximum total tokens allowed per request */
|
|
790
|
+
maxTotalTokens?: number;
|
|
791
|
+
/** Action to take when budget is exceeded */
|
|
792
|
+
onExceeded?: "throw" | "truncate" | "warn";
|
|
793
|
+
/** Custom handler when budget is exceeded */
|
|
794
|
+
onExceededHandler?: (info: BudgetExceededInfo) => void | Promise<void>;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Information about a budget violation
|
|
798
|
+
*/
|
|
799
|
+
interface BudgetExceededInfo {
|
|
800
|
+
/** Estimated input tokens */
|
|
801
|
+
estimatedInputTokens: number;
|
|
802
|
+
/** Configured maximum */
|
|
803
|
+
maxInputTokens: number;
|
|
804
|
+
/** Model being used */
|
|
805
|
+
model: string;
|
|
806
|
+
/** Original input that exceeded budget */
|
|
807
|
+
input: unknown;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Streaming event types we care about for metrics
|
|
811
|
+
*/
|
|
812
|
+
type StreamingEventType = "response.created" | "response.in_progress" | "response.completed" | "response.failed" | "response.incomplete" | "response.output_item.added" | "response.content_part.added" | "response.content_part.done" | "response.output_item.done" | "response.function_call_arguments.delta" | "response.function_call_arguments.done";
|
|
813
|
+
/**
|
|
814
|
+
* Extended OpenAI client with metering capabilities
|
|
815
|
+
*/
|
|
816
|
+
type MeteredOpenAI = OpenAI & {
|
|
817
|
+
__metered: true;
|
|
818
|
+
__meterOptions: MeterOptions;
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Wraps an OpenAI client with metering capabilities.
|
|
823
|
+
*
|
|
824
|
+
* This function injects metering into the client without modifying the SDK,
|
|
825
|
+
* allowing you to track usage metrics, billing data, and request metadata
|
|
826
|
+
* for every API call.
|
|
827
|
+
*
|
|
828
|
+
* @param client - The OpenAI client instance to wrap
|
|
829
|
+
* @param options - Metering options including the metric emitter
|
|
830
|
+
* @returns The same client with metering injected
|
|
831
|
+
*
|
|
832
|
+
* @example
|
|
833
|
+
* ```ts
|
|
834
|
+
* import OpenAI from "openai";
|
|
835
|
+
* import { makeMeteredOpenAI } from "openai-meter";
|
|
836
|
+
*
|
|
837
|
+
* const client = new OpenAI();
|
|
838
|
+
* const metered = makeMeteredOpenAI(client, {
|
|
839
|
+
* emitMetric: (event) => {
|
|
840
|
+
* console.log("Usage:", event.usage);
|
|
841
|
+
* // Send to your metrics backend
|
|
842
|
+
* },
|
|
843
|
+
* });
|
|
844
|
+
*
|
|
845
|
+
* // Use normally - metrics are collected automatically
|
|
846
|
+
* const response = await metered.responses.create({
|
|
847
|
+
* model: "gpt-4.1",
|
|
848
|
+
* input: "Hello!",
|
|
849
|
+
* });
|
|
850
|
+
* ```
|
|
851
|
+
*/
|
|
852
|
+
declare function makeMeteredOpenAI(client: OpenAI, options: MeterOptions): MeteredOpenAI;
|
|
853
|
+
/**
|
|
854
|
+
* Check if a client has already been wrapped with metering
|
|
855
|
+
*/
|
|
856
|
+
declare function isMetered(client: OpenAI): client is MeteredOpenAI;
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* OpenAI SDK instrumentation.
|
|
860
|
+
*
|
|
861
|
+
* Call `instrumentOpenAI()` to instrument the OpenAI SDK.
|
|
862
|
+
* This is called automatically by the main `instrument()` function.
|
|
863
|
+
*/
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Instrument OpenAI SDK globally.
|
|
867
|
+
*
|
|
868
|
+
* Call once at application startup. All OpenAI client instances
|
|
869
|
+
* will automatically be metered.
|
|
870
|
+
*
|
|
871
|
+
* @returns true if instrumentation was successful, false if SDK not found
|
|
872
|
+
*/
|
|
873
|
+
declare function instrumentOpenAI(options: MeterOptions): Promise<boolean>;
|
|
874
|
+
/**
|
|
875
|
+
* Remove OpenAI instrumentation.
|
|
876
|
+
*
|
|
877
|
+
* Restores original behavior for all clients.
|
|
878
|
+
*/
|
|
879
|
+
declare function uninstrumentOpenAI(): Promise<void>;
|
|
880
|
+
/**
|
|
881
|
+
* Check if OpenAI is currently instrumented
|
|
882
|
+
*/
|
|
883
|
+
declare function isOpenAIInstrumented(): boolean;
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Global instrumentation for Google Generative AI (Gemini) clients.
|
|
887
|
+
*
|
|
888
|
+
* Call `instrumentGemini()` once at startup, and all Gemini client instances
|
|
889
|
+
* (existing and future) are automatically metered.
|
|
890
|
+
*/
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Instrument Google Generative AI (Gemini) globally.
|
|
894
|
+
*
|
|
895
|
+
* Call once at application startup. All Gemini GenerativeModel instances
|
|
896
|
+
* will automatically be metered.
|
|
897
|
+
*
|
|
898
|
+
* @returns true if instrumentation was successful, false if SDK not found
|
|
899
|
+
*/
|
|
900
|
+
declare function instrumentGemini(options: MeterOptions): Promise<boolean>;
|
|
901
|
+
/**
|
|
902
|
+
* Remove Gemini instrumentation.
|
|
903
|
+
*
|
|
904
|
+
* Restores original behavior for all clients.
|
|
905
|
+
*/
|
|
906
|
+
declare function uninstrumentGemini(): Promise<void>;
|
|
907
|
+
/**
|
|
908
|
+
* Check if Gemini is currently instrumented
|
|
909
|
+
*/
|
|
910
|
+
declare function isGeminiInstrumented(): boolean;
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Global instrumentation for Anthropic (Claude) SDK clients.
|
|
914
|
+
*
|
|
915
|
+
* Call `instrumentAnthropic()` once at startup, and all Anthropic client instances
|
|
916
|
+
* (existing and future) are automatically metered.
|
|
917
|
+
*/
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Instrument Anthropic (Claude) SDK globally.
|
|
921
|
+
*
|
|
922
|
+
* Call once at application startup. All Anthropic client instances
|
|
923
|
+
* will automatically be metered.
|
|
924
|
+
*
|
|
925
|
+
* @returns true if instrumentation was successful, false if SDK not found
|
|
926
|
+
*/
|
|
927
|
+
declare function instrumentAnthropic(options: MeterOptions): Promise<boolean>;
|
|
928
|
+
/**
|
|
929
|
+
* Remove Anthropic instrumentation.
|
|
930
|
+
*
|
|
931
|
+
* Restores original behavior for all clients.
|
|
932
|
+
*/
|
|
933
|
+
declare function uninstrumentAnthropic(): Promise<void>;
|
|
934
|
+
/**
|
|
935
|
+
* Check if Anthropic is currently instrumented
|
|
936
|
+
*/
|
|
937
|
+
declare function isAnthropicInstrumented(): boolean;
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Fetch-based instrumentation for frameworks that bypass SDK classes
|
|
941
|
+
*
|
|
942
|
+
* Works with: Vercel AI SDK, LangChain, Mastra, and any framework
|
|
943
|
+
* that makes direct HTTP calls to LLM APIs.
|
|
944
|
+
*/
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Instrument global fetch to capture LLM API calls
|
|
948
|
+
*/
|
|
949
|
+
declare function instrumentFetch(options: MeterOptions): Promise<boolean>;
|
|
950
|
+
/**
|
|
951
|
+
* Remove fetch instrumentation
|
|
952
|
+
*/
|
|
953
|
+
declare function uninstrumentFetch(): Promise<void>;
|
|
954
|
+
/**
|
|
955
|
+
* Check if fetch is instrumented
|
|
956
|
+
*/
|
|
957
|
+
declare function isFetchInstrumented(): boolean;
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Unified instrumentation for LLM SDKs.
|
|
961
|
+
*
|
|
962
|
+
* Call `instrument()` once at startup, and all available LLM client instances
|
|
963
|
+
* (OpenAI, Gemini, Anthropic) are automatically detected and metered.
|
|
964
|
+
*/
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* Result of instrumentation showing which SDKs were instrumented
|
|
968
|
+
*/
|
|
969
|
+
interface InstrumentationResult {
|
|
970
|
+
openai: boolean;
|
|
971
|
+
gemini: boolean;
|
|
972
|
+
anthropic: boolean;
|
|
973
|
+
controlAgent: IControlAgent | null;
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Instrument all available LLM SDKs globally.
|
|
977
|
+
*
|
|
978
|
+
* Call once at application startup. All detected LLM client instances
|
|
979
|
+
* (OpenAI, Gemini, Anthropic) will automatically be metered.
|
|
980
|
+
*
|
|
981
|
+
* The function auto-detects which SDKs are installed and instruments them.
|
|
982
|
+
* SDKs that aren't installed are silently skipped.
|
|
983
|
+
*
|
|
984
|
+
* @example
|
|
985
|
+
* ```typescript
|
|
986
|
+
* import { instrument } from "aden";
|
|
987
|
+
* import OpenAI from "openai";
|
|
988
|
+
*
|
|
989
|
+
* // Simplest setup - just provide API key
|
|
990
|
+
* await instrument({
|
|
991
|
+
* apiKey: process.env.ADEN_API_KEY,
|
|
992
|
+
* sdks: { OpenAI },
|
|
993
|
+
* });
|
|
994
|
+
*
|
|
995
|
+
* // Or use ADEN_API_KEY environment variable
|
|
996
|
+
* await instrument({ sdks: { OpenAI } });
|
|
997
|
+
*
|
|
998
|
+
* // Use any LLM SDK normally - metrics collected automatically
|
|
999
|
+
* const openai = new OpenAI();
|
|
1000
|
+
* ```
|
|
1001
|
+
*/
|
|
1002
|
+
declare function instrument(options: MeterOptions): Promise<InstrumentationResult>;
|
|
1003
|
+
/**
|
|
1004
|
+
* Remove instrumentation from all LLM SDKs.
|
|
1005
|
+
*
|
|
1006
|
+
* Restores original behavior for all clients.
|
|
1007
|
+
*/
|
|
1008
|
+
declare function uninstrument(): Promise<void>;
|
|
1009
|
+
/**
|
|
1010
|
+
* Check which SDKs are currently instrumented
|
|
1011
|
+
*/
|
|
1012
|
+
declare function getInstrumentedSDKs(): InstrumentationResult;
|
|
1013
|
+
/**
|
|
1014
|
+
* Check if any SDK is currently instrumented
|
|
1015
|
+
*/
|
|
1016
|
+
declare function isInstrumented(): boolean;
|
|
1017
|
+
/**
|
|
1018
|
+
* Get the current instrumentation options
|
|
1019
|
+
*/
|
|
1020
|
+
declare function getInstrumentationOptions(): MeterOptions | null;
|
|
1021
|
+
/**
|
|
1022
|
+
* Update instrumentation options without re-instrumenting.
|
|
1023
|
+
*
|
|
1024
|
+
* Useful for changing emitters or settings at runtime.
|
|
1025
|
+
*/
|
|
1026
|
+
declare function updateInstrumentationOptions(updates: Partial<MeterOptions>): void;
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Google GenAI SDK instrumentation (new SDK).
|
|
1030
|
+
*
|
|
1031
|
+
* This module provides global instrumentation for the new Google GenAI SDK
|
|
1032
|
+
* (@google/genai package) used by Google ADK and other modern Google AI tools.
|
|
1033
|
+
*
|
|
1034
|
+
* The new SDK uses:
|
|
1035
|
+
* import { GoogleGenAI } from "@google/genai";
|
|
1036
|
+
* const client = new GoogleGenAI({ apiKey: "..." });
|
|
1037
|
+
* await client.models.generateContent({ model: "...", contents: "..." });
|
|
1038
|
+
*/
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Instrument the Google GenAI SDK (google-genai package).
|
|
1042
|
+
*
|
|
1043
|
+
* This is the new SDK used by Google ADK and replaces google-generativeai.
|
|
1044
|
+
*
|
|
1045
|
+
* @param options - Metering options including the metric emitter
|
|
1046
|
+
* @returns true if instrumentation succeeded, false if SDK not available
|
|
1047
|
+
*/
|
|
1048
|
+
declare function instrumentGenai(options: MeterOptions): Promise<boolean>;
|
|
1049
|
+
/**
|
|
1050
|
+
* Remove Google GenAI SDK instrumentation.
|
|
1051
|
+
*/
|
|
1052
|
+
declare function uninstrumentGenai(): Promise<void>;
|
|
1053
|
+
/**
|
|
1054
|
+
* Check if Google GenAI SDK is currently instrumented.
|
|
1055
|
+
*/
|
|
1056
|
+
declare function isGenaiInstrumented(): boolean;
|
|
1057
|
+
/**
|
|
1058
|
+
* Get current GenAI instrumentation options.
|
|
1059
|
+
*/
|
|
1060
|
+
declare function getGenaiOptions(): MeterOptions | null;
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* Context for tracking related LLM calls within a trace (OTel-compatible)
|
|
1064
|
+
*/
|
|
1065
|
+
interface MeterContext {
|
|
1066
|
+
/** Trace ID grouping related operations (OTel standard) */
|
|
1067
|
+
traceId: string;
|
|
1068
|
+
/** Current call sequence number within trace */
|
|
1069
|
+
callSequence: number;
|
|
1070
|
+
/** Stack of agent/function names for nested calls */
|
|
1071
|
+
agentStack: string[];
|
|
1072
|
+
/** Parent span ID for hierarchical tracking (OTel standard) */
|
|
1073
|
+
parentSpanId?: string;
|
|
1074
|
+
/** Custom metadata attached to this context */
|
|
1075
|
+
metadata?: Record<string, unknown>;
|
|
1076
|
+
/** Stack fingerprint for heuristic grouping */
|
|
1077
|
+
stackFingerprint?: string;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Extended metric event with call relationship tracking (OTel-compatible)
|
|
1081
|
+
*/
|
|
1082
|
+
interface CallRelationship {
|
|
1083
|
+
/** Trace ID grouping related operations (OTel standard) */
|
|
1084
|
+
traceId: string;
|
|
1085
|
+
/** Parent span ID if this is a nested call (OTel standard) */
|
|
1086
|
+
parentSpanId?: string;
|
|
1087
|
+
/** Sequence number within the trace */
|
|
1088
|
+
callSequence: number;
|
|
1089
|
+
/** Stack of agent names leading to this call */
|
|
1090
|
+
agentStack: string[];
|
|
1091
|
+
/** Where in the code this call originated (immediate caller) */
|
|
1092
|
+
callSite?: CallSite;
|
|
1093
|
+
/** Full call stack for detailed tracing */
|
|
1094
|
+
callStack?: string[];
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Extract the first user-code call site from the current stack
|
|
1098
|
+
*/
|
|
1099
|
+
declare function extractCallSite(): CallSite | undefined;
|
|
1100
|
+
/**
|
|
1101
|
+
* Extract agent names from the current call stack
|
|
1102
|
+
*/
|
|
1103
|
+
declare function extractAgentStack(): string[];
|
|
1104
|
+
/**
|
|
1105
|
+
* Generate a fingerprint from the call stack for grouping related calls
|
|
1106
|
+
*/
|
|
1107
|
+
declare function generateStackFingerprint(): string;
|
|
1108
|
+
/**
|
|
1109
|
+
* Create a new meter context
|
|
1110
|
+
*/
|
|
1111
|
+
declare function createMeterContext(options?: {
|
|
1112
|
+
traceId?: string;
|
|
1113
|
+
metadata?: Record<string, unknown>;
|
|
1114
|
+
}): MeterContext;
|
|
1115
|
+
/**
|
|
1116
|
+
* Get the current meter context, creating one automatically if needed
|
|
1117
|
+
*
|
|
1118
|
+
* This enables zero-wrapper usage - the first LLM call will auto-create
|
|
1119
|
+
* a session context that subsequent calls will inherit.
|
|
1120
|
+
*/
|
|
1121
|
+
declare function getCurrentContext(): MeterContext;
|
|
1122
|
+
/**
|
|
1123
|
+
* Check if we're currently inside a meter context
|
|
1124
|
+
*/
|
|
1125
|
+
declare function hasContext(): boolean;
|
|
1126
|
+
/**
|
|
1127
|
+
* Run a function within a new meter context (trace)
|
|
1128
|
+
*
|
|
1129
|
+
* @example
|
|
1130
|
+
* ```ts
|
|
1131
|
+
* await withMeterContext(async () => {
|
|
1132
|
+
* // All LLM calls here share the same trace
|
|
1133
|
+
* await client.responses.create({ ... });
|
|
1134
|
+
* await client.responses.create({ ... });
|
|
1135
|
+
* }, { metadata: { userId: "123" } });
|
|
1136
|
+
* ```
|
|
1137
|
+
*/
|
|
1138
|
+
declare function withMeterContext<T>(fn: () => T, options?: {
|
|
1139
|
+
traceId?: string;
|
|
1140
|
+
metadata?: Record<string, unknown>;
|
|
1141
|
+
}): T;
|
|
1142
|
+
/**
|
|
1143
|
+
* Run an async function within a new meter context (trace)
|
|
1144
|
+
*/
|
|
1145
|
+
declare function withMeterContextAsync<T>(fn: () => Promise<T>, options?: {
|
|
1146
|
+
traceId?: string;
|
|
1147
|
+
metadata?: Record<string, unknown>;
|
|
1148
|
+
}): Promise<T>;
|
|
1149
|
+
/**
|
|
1150
|
+
* Enter a meter context that persists across awaits without explicit wrapping
|
|
1151
|
+
*
|
|
1152
|
+
* This is the zero-wrapper approach - call once at the start of your
|
|
1153
|
+
* request/session handler, and all subsequent LLM calls will be tracked.
|
|
1154
|
+
*
|
|
1155
|
+
* @example
|
|
1156
|
+
* ```ts
|
|
1157
|
+
* app.post('/chat', async (req, res) => {
|
|
1158
|
+
* enterMeterContext({ metadata: { userId: req.userId } });
|
|
1159
|
+
*
|
|
1160
|
+
* // All LLM calls in this request are now tracked together
|
|
1161
|
+
* const response = await client.responses.create({ ... });
|
|
1162
|
+
* res.json(response);
|
|
1163
|
+
* });
|
|
1164
|
+
* ```
|
|
1165
|
+
*/
|
|
1166
|
+
declare function enterMeterContext(options?: {
|
|
1167
|
+
traceId?: string;
|
|
1168
|
+
metadata?: Record<string, unknown>;
|
|
1169
|
+
}): MeterContext;
|
|
1170
|
+
/**
|
|
1171
|
+
* Push an agent name onto the current context's agent stack
|
|
1172
|
+
*
|
|
1173
|
+
* Use this when entering a named agent/handler to track nesting.
|
|
1174
|
+
*/
|
|
1175
|
+
declare function pushAgent(name: string): void;
|
|
1176
|
+
/**
|
|
1177
|
+
* Pop an agent name from the current context's agent stack
|
|
1178
|
+
*/
|
|
1179
|
+
declare function popAgent(): string | undefined;
|
|
1180
|
+
/**
|
|
1181
|
+
* Run a function within a named agent context
|
|
1182
|
+
*
|
|
1183
|
+
* @example
|
|
1184
|
+
* ```ts
|
|
1185
|
+
* await withAgent("ResearchAgent", async () => {
|
|
1186
|
+
* // LLM calls here will have "ResearchAgent" in their agentStack
|
|
1187
|
+
* await client.responses.create({ ... });
|
|
1188
|
+
* });
|
|
1189
|
+
* ```
|
|
1190
|
+
*/
|
|
1191
|
+
declare function withAgent<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
1192
|
+
/**
|
|
1193
|
+
* Reset the global session (useful for testing)
|
|
1194
|
+
*/
|
|
1195
|
+
declare function resetGlobalSession(): void;
|
|
1196
|
+
/**
|
|
1197
|
+
* Set custom metadata on the current context
|
|
1198
|
+
*/
|
|
1199
|
+
declare function setContextMetadata(key: string, value: unknown): void;
|
|
1200
|
+
/**
|
|
1201
|
+
* Get custom metadata from the current context
|
|
1202
|
+
*/
|
|
1203
|
+
declare function getContextMetadata(key: string): unknown;
|
|
1204
|
+
/**
|
|
1205
|
+
* Merge stack-detected agents with explicit agent stack
|
|
1206
|
+
*/
|
|
1207
|
+
declare function getFullAgentStack(): string[];
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Normalizes usage data from OpenAI API responses.
|
|
1211
|
+
*
|
|
1212
|
+
* Handles both API shapes:
|
|
1213
|
+
* - Responses API: uses `input_tokens` / `output_tokens`
|
|
1214
|
+
* - Chat Completions API: uses `prompt_tokens` / `completion_tokens`
|
|
1215
|
+
*
|
|
1216
|
+
* @param usage - Raw usage object from OpenAI API response
|
|
1217
|
+
* @returns Normalized usage metrics, or null if no usage data provided
|
|
1218
|
+
*/
|
|
1219
|
+
declare function normalizeOpenAIUsage(usage: unknown): NormalizedUsage | null;
|
|
1220
|
+
/**
|
|
1221
|
+
* Normalizes usage data from Anthropic API responses.
|
|
1222
|
+
*
|
|
1223
|
+
* Anthropic Messages API usage fields:
|
|
1224
|
+
* - input_tokens: Input tokens consumed
|
|
1225
|
+
* - output_tokens: Output tokens generated
|
|
1226
|
+
* - cache_read_input_tokens: Tokens served from cache (optional)
|
|
1227
|
+
* - cache_creation_input_tokens: Tokens used to create cache (optional)
|
|
1228
|
+
*
|
|
1229
|
+
* @param usage - Raw usage object from Anthropic API response
|
|
1230
|
+
* @returns Normalized usage metrics, or null if no usage data provided
|
|
1231
|
+
*/
|
|
1232
|
+
declare function normalizeAnthropicUsage(usage: unknown): NormalizedUsage | null;
|
|
1233
|
+
/**
|
|
1234
|
+
* Normalizes usage data from Google Gemini API responses.
|
|
1235
|
+
*
|
|
1236
|
+
* Gemini GenerateContent usage_metadata fields:
|
|
1237
|
+
* - promptTokenCount: Input tokens
|
|
1238
|
+
* - candidatesTokenCount: Output tokens
|
|
1239
|
+
* - totalTokenCount: Total tokens
|
|
1240
|
+
* - cachedContentTokenCount: Cached tokens (optional)
|
|
1241
|
+
*
|
|
1242
|
+
* @param usageMetadata - Raw usage_metadata from Gemini API response
|
|
1243
|
+
* @returns Normalized usage metrics, or null if no usage data provided
|
|
1244
|
+
*/
|
|
1245
|
+
declare function normalizeGeminiUsage(usageMetadata: unknown): NormalizedUsage | null;
|
|
1246
|
+
/**
|
|
1247
|
+
* Normalizes usage data from any supported LLM provider.
|
|
1248
|
+
*
|
|
1249
|
+
* @param usage - Raw usage object from API response
|
|
1250
|
+
* @param provider - The provider the usage came from (default: "openai")
|
|
1251
|
+
* @returns Normalized usage metrics, or null if no usage data provided
|
|
1252
|
+
*
|
|
1253
|
+
* @example
|
|
1254
|
+
* ```typescript
|
|
1255
|
+
* // OpenAI
|
|
1256
|
+
* const response = await openai.chat.completions.create({ ... });
|
|
1257
|
+
* const normalized = normalizeUsage(response.usage, "openai");
|
|
1258
|
+
*
|
|
1259
|
+
* // Anthropic
|
|
1260
|
+
* const response = await anthropic.messages.create({ ... });
|
|
1261
|
+
* const normalized = normalizeUsage(response.usage, "anthropic");
|
|
1262
|
+
*
|
|
1263
|
+
* // Gemini
|
|
1264
|
+
* const response = await model.generateContent({ ... });
|
|
1265
|
+
* const normalized = normalizeUsage(response.usageMetadata, "gemini");
|
|
1266
|
+
* ```
|
|
1267
|
+
*/
|
|
1268
|
+
declare function normalizeUsage(usage: unknown, provider?: "openai" | "anthropic" | "gemini"): NormalizedUsage | null;
|
|
1269
|
+
/**
|
|
1270
|
+
* Creates an empty/zero usage object
|
|
1271
|
+
*/
|
|
1272
|
+
declare function emptyUsage(): NormalizedUsage;
|
|
1273
|
+
/**
|
|
1274
|
+
* Merges two usage objects (useful for accumulating streaming deltas)
|
|
1275
|
+
*/
|
|
1276
|
+
declare function mergeUsage(a: NormalizedUsage, b: NormalizedUsage): NormalizedUsage;
|
|
1277
|
+
|
|
1278
|
+
/**
|
|
1279
|
+
* Error thrown when a request exceeds the configured budget
|
|
1280
|
+
*/
|
|
1281
|
+
declare class BudgetExceededError extends Error {
|
|
1282
|
+
readonly estimatedInputTokens: number;
|
|
1283
|
+
readonly maxInputTokens: number;
|
|
1284
|
+
readonly model: string;
|
|
1285
|
+
constructor(info: BudgetExceededInfo);
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Counts input tokens using OpenAI's input token counting endpoint.
|
|
1289
|
+
*
|
|
1290
|
+
* Uses POST /v1/responses/input_tokens to get exact token counts
|
|
1291
|
+
* before making the actual API call.
|
|
1292
|
+
*
|
|
1293
|
+
* @param client - OpenAI client instance
|
|
1294
|
+
* @param model - Model to count tokens for
|
|
1295
|
+
* @param input - The input to count tokens for
|
|
1296
|
+
* @returns The estimated input token count
|
|
1297
|
+
*
|
|
1298
|
+
* @example
|
|
1299
|
+
* ```ts
|
|
1300
|
+
* const tokens = await countInputTokens(client, "gpt-4.1-mini", "Hello world");
|
|
1301
|
+
* console.log(`This prompt will use ${tokens} input tokens`);
|
|
1302
|
+
* ```
|
|
1303
|
+
*/
|
|
1304
|
+
declare function countInputTokens(client: OpenAI, model: string, input: unknown): Promise<number>;
|
|
1305
|
+
/**
|
|
1306
|
+
* Creates a budget-enforced wrapper around the OpenAI client.
|
|
1307
|
+
*
|
|
1308
|
+
* This wrapper checks input token counts before making API calls and
|
|
1309
|
+
* can throw, warn, or truncate based on your configuration.
|
|
1310
|
+
*
|
|
1311
|
+
* @param client - OpenAI client instance (can be metered or not)
|
|
1312
|
+
* @param config - Budget configuration
|
|
1313
|
+
* @returns The client with budget enforcement
|
|
1314
|
+
*
|
|
1315
|
+
* @example
|
|
1316
|
+
* ```ts
|
|
1317
|
+
* import OpenAI from "openai";
|
|
1318
|
+
* import { withBudgetGuardrails } from "openai-meter";
|
|
1319
|
+
*
|
|
1320
|
+
* const client = new OpenAI();
|
|
1321
|
+
* const budgeted = withBudgetGuardrails(client, {
|
|
1322
|
+
* maxInputTokens: 4000,
|
|
1323
|
+
* onExceeded: "throw",
|
|
1324
|
+
* });
|
|
1325
|
+
*
|
|
1326
|
+
* // This will throw if input exceeds 4000 tokens
|
|
1327
|
+
* await budgeted.responses.create({
|
|
1328
|
+
* model: "gpt-4.1",
|
|
1329
|
+
* input: veryLongPrompt,
|
|
1330
|
+
* });
|
|
1331
|
+
* ```
|
|
1332
|
+
*/
|
|
1333
|
+
declare function withBudgetGuardrails<T extends OpenAI>(client: T, config: BudgetConfig): T;
|
|
1334
|
+
/**
|
|
1335
|
+
* Convenience function to create a fully metered and budgeted client
|
|
1336
|
+
*/
|
|
1337
|
+
declare function createBudgetedMeteredClient(client: MeteredOpenAI, budgetConfig: BudgetConfig): MeteredOpenAI;
|
|
1338
|
+
|
|
1339
|
+
/**
|
|
1340
|
+
* A simple console emitter for development/debugging
|
|
1341
|
+
*/
|
|
1342
|
+
declare function createConsoleEmitter(options?: {
|
|
1343
|
+
/** Log level: "info" logs all events, "warn" logs only errors */
|
|
1344
|
+
level?: "info" | "warn";
|
|
1345
|
+
/** Whether to pretty-print the output */
|
|
1346
|
+
pretty?: boolean;
|
|
1347
|
+
}): MetricEmitter;
|
|
1348
|
+
/**
|
|
1349
|
+
* Creates an emitter that batches metrics and flushes periodically
|
|
1350
|
+
*/
|
|
1351
|
+
declare function createBatchEmitter(flush: (events: MetricEvent[]) => void | Promise<void>, options?: {
|
|
1352
|
+
/** Maximum batch size before auto-flush */
|
|
1353
|
+
maxBatchSize?: number;
|
|
1354
|
+
/** Maximum time (ms) to wait before flushing */
|
|
1355
|
+
flushInterval?: number;
|
|
1356
|
+
}): MetricEmitter & {
|
|
1357
|
+
flush: () => Promise<void>;
|
|
1358
|
+
stop: () => void;
|
|
1359
|
+
};
|
|
1360
|
+
/**
|
|
1361
|
+
* Creates an emitter that writes to multiple destinations
|
|
1362
|
+
*/
|
|
1363
|
+
declare function createMultiEmitter(emitters: MetricEmitter[]): MetricEmitter;
|
|
1364
|
+
/**
|
|
1365
|
+
* Creates an emitter that filters events before passing to another emitter
|
|
1366
|
+
*/
|
|
1367
|
+
declare function createFilteredEmitter(emitter: MetricEmitter, filter: (event: MetricEvent) => boolean): MetricEmitter;
|
|
1368
|
+
/**
|
|
1369
|
+
* Creates an emitter that transforms events before passing to another emitter
|
|
1370
|
+
*/
|
|
1371
|
+
declare function createTransformEmitter<T>(emitter: (transformed: T) => void | Promise<void>, transform: (event: MetricEvent) => T): MetricEmitter;
|
|
1372
|
+
/**
|
|
1373
|
+
* Creates a no-op emitter (useful for testing or disabling metrics)
|
|
1374
|
+
*/
|
|
1375
|
+
declare function createNoopEmitter(): MetricEmitter;
|
|
1376
|
+
/**
|
|
1377
|
+
* Helper to collect metrics in memory (useful for testing)
|
|
1378
|
+
*/
|
|
1379
|
+
declare function createMemoryEmitter(): MetricEmitter & {
|
|
1380
|
+
events: MetricEvent[];
|
|
1381
|
+
clear: () => void;
|
|
1382
|
+
};
|
|
1383
|
+
/**
|
|
1384
|
+
* Options for the JSON file emitter
|
|
1385
|
+
*/
|
|
1386
|
+
interface JsonFileEmitterOptions {
|
|
1387
|
+
/** File path to write metrics to */
|
|
1388
|
+
filePath: string;
|
|
1389
|
+
/** Format: "jsonl" for JSON Lines (one event per line), "json" for array */
|
|
1390
|
+
format?: "jsonl" | "json";
|
|
1391
|
+
/** Whether to use async writes (default: true for better performance) */
|
|
1392
|
+
async?: boolean;
|
|
1393
|
+
/** Whether to pretty-print JSON (default: false) */
|
|
1394
|
+
pretty?: boolean;
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Creates an emitter that writes metrics to a local JSON/JSONL file.
|
|
1398
|
+
*
|
|
1399
|
+
* JSONL format (default) is recommended - one JSON object per line:
|
|
1400
|
+
* - Efficient for appending
|
|
1401
|
+
* - Easy to stream/parse line by line
|
|
1402
|
+
* - Works well with tools like jq
|
|
1403
|
+
*
|
|
1404
|
+
* @example
|
|
1405
|
+
* ```typescript
|
|
1406
|
+
* // JSONL format (recommended)
|
|
1407
|
+
* const emitter = createJsonFileEmitter({
|
|
1408
|
+
* filePath: "./metrics.jsonl",
|
|
1409
|
+
* });
|
|
1410
|
+
*
|
|
1411
|
+
* // JSON array format
|
|
1412
|
+
* const emitter = createJsonFileEmitter({
|
|
1413
|
+
* filePath: "./metrics.json",
|
|
1414
|
+
* format: "json",
|
|
1415
|
+
* pretty: true,
|
|
1416
|
+
* });
|
|
1417
|
+
*
|
|
1418
|
+
* instrument({ emitMetric: emitter });
|
|
1419
|
+
* ```
|
|
1420
|
+
*/
|
|
1421
|
+
declare function createJsonFileEmitter(options: JsonFileEmitterOptions): MetricEmitter & {
|
|
1422
|
+
flush: () => Promise<void>;
|
|
1423
|
+
};
|
|
1424
|
+
|
|
1425
|
+
/**
|
|
1426
|
+
* File-based metric logging for local storage and analysis.
|
|
1427
|
+
*
|
|
1428
|
+
* Writes raw metric data to JSONL files organized by date and session,
|
|
1429
|
+
* enabling offline analysis, debugging, and compliance auditing.
|
|
1430
|
+
*/
|
|
1431
|
+
|
|
1432
|
+
/**
|
|
1433
|
+
* Options for the MetricFileLogger
|
|
1434
|
+
*/
|
|
1435
|
+
interface MetricFileLoggerOptions {
|
|
1436
|
+
/** Base directory for log files. Default: ./meter_logs or METER_LOG_DIR env var */
|
|
1437
|
+
logDir?: string;
|
|
1438
|
+
/** Whether to use async writes (default: true for better performance) */
|
|
1439
|
+
async?: boolean;
|
|
1440
|
+
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Writes raw metric data to local JSONL files for analysis.
|
|
1443
|
+
*
|
|
1444
|
+
* Files are organized by date and session:
|
|
1445
|
+
* meter_logs/
|
|
1446
|
+
* 2024-01-15/
|
|
1447
|
+
* session_abc123.jsonl
|
|
1448
|
+
* session_def456.jsonl
|
|
1449
|
+
* 2024-01-16/
|
|
1450
|
+
* ...
|
|
1451
|
+
*
|
|
1452
|
+
* Each line in the JSONL file is a complete JSON object representing
|
|
1453
|
+
* one metric event (LLM request, TTS synthesis, STT transcription).
|
|
1454
|
+
*
|
|
1455
|
+
* @example
|
|
1456
|
+
* ```typescript
|
|
1457
|
+
* import { MetricFileLogger } from "aden";
|
|
1458
|
+
*
|
|
1459
|
+
* const logger = new MetricFileLogger({ logDir: "./my_logs" });
|
|
1460
|
+
* logger.writeLLMEvent({
|
|
1461
|
+
* sessionId: "session_123",
|
|
1462
|
+
* inputTokens: 100,
|
|
1463
|
+
* outputTokens: 50,
|
|
1464
|
+
* model: "gpt-4o-mini",
|
|
1465
|
+
* });
|
|
1466
|
+
* ```
|
|
1467
|
+
*/
|
|
1468
|
+
declare class MetricFileLogger {
|
|
1469
|
+
private logDir;
|
|
1470
|
+
private useAsync;
|
|
1471
|
+
constructor(options?: MetricFileLoggerOptions);
|
|
1472
|
+
/**
|
|
1473
|
+
* Create the log directory if it doesn't exist.
|
|
1474
|
+
*/
|
|
1475
|
+
private ensureDirExists;
|
|
1476
|
+
/**
|
|
1477
|
+
* Get the log file path for a session.
|
|
1478
|
+
*/
|
|
1479
|
+
private getSessionFile;
|
|
1480
|
+
/**
|
|
1481
|
+
* Write a metric event to the session's log file.
|
|
1482
|
+
*/
|
|
1483
|
+
writeEvent(sessionId: string, eventType: string, data: Record<string, unknown>): Promise<void>;
|
|
1484
|
+
/**
|
|
1485
|
+
* Write a MetricEvent to the log file.
|
|
1486
|
+
*/
|
|
1487
|
+
writeMetricEvent(event: MetricEvent): Promise<void>;
|
|
1488
|
+
/**
|
|
1489
|
+
* Write an LLM metric event.
|
|
1490
|
+
*/
|
|
1491
|
+
writeLLMEvent(options: {
|
|
1492
|
+
sessionId: string;
|
|
1493
|
+
inputTokens: number;
|
|
1494
|
+
outputTokens: number;
|
|
1495
|
+
model: string;
|
|
1496
|
+
latencyMs?: number;
|
|
1497
|
+
metadata?: Record<string, unknown>;
|
|
1498
|
+
}): Promise<void>;
|
|
1499
|
+
/**
|
|
1500
|
+
* Write a TTS metric event.
|
|
1501
|
+
*/
|
|
1502
|
+
writeTTSEvent(options: {
|
|
1503
|
+
sessionId: string;
|
|
1504
|
+
characters: number;
|
|
1505
|
+
model: string;
|
|
1506
|
+
metadata?: Record<string, unknown>;
|
|
1507
|
+
}): Promise<void>;
|
|
1508
|
+
/**
|
|
1509
|
+
* Write an STT metric event.
|
|
1510
|
+
*/
|
|
1511
|
+
writeSTTEvent(options: {
|
|
1512
|
+
sessionId: string;
|
|
1513
|
+
audioSeconds: number;
|
|
1514
|
+
model: string;
|
|
1515
|
+
metadata?: Record<string, unknown>;
|
|
1516
|
+
}): Promise<void>;
|
|
1517
|
+
/**
|
|
1518
|
+
* Write session start event.
|
|
1519
|
+
*/
|
|
1520
|
+
writeSessionStart(options: {
|
|
1521
|
+
sessionId: string;
|
|
1522
|
+
roomName: string;
|
|
1523
|
+
metadata?: Record<string, unknown>;
|
|
1524
|
+
}): Promise<void>;
|
|
1525
|
+
/**
|
|
1526
|
+
* Write session end event with final summary.
|
|
1527
|
+
*/
|
|
1528
|
+
writeSessionEnd(options: {
|
|
1529
|
+
sessionId: string;
|
|
1530
|
+
summary: Record<string, unknown>;
|
|
1531
|
+
}): Promise<void>;
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Create a file-based metric emitter.
|
|
1535
|
+
*
|
|
1536
|
+
* This creates a MetricEmitter that writes to session-organized JSONL files.
|
|
1537
|
+
*
|
|
1538
|
+
* @example
|
|
1539
|
+
* ```typescript
|
|
1540
|
+
* import { instrument, createFileEmitter } from "aden";
|
|
1541
|
+
*
|
|
1542
|
+
* instrument({
|
|
1543
|
+
* emitMetric: createFileEmitter({ logDir: "./my_logs" }),
|
|
1544
|
+
* });
|
|
1545
|
+
* ```
|
|
1546
|
+
*
|
|
1547
|
+
* @param options - Options for the file logger
|
|
1548
|
+
* @returns A MetricEmitter function
|
|
1549
|
+
*/
|
|
1550
|
+
declare function createFileEmitter(options?: MetricFileLoggerOptions): MetricEmitter;
|
|
1551
|
+
|
|
1552
|
+
/**
|
|
1553
|
+
* HTTP transport for sending metrics to a central API endpoint.
|
|
1554
|
+
*
|
|
1555
|
+
* This is the recommended approach for production - clients send metrics
|
|
1556
|
+
* to your API, which handles storage, aggregation, and multi-tenancy.
|
|
1557
|
+
*/
|
|
1558
|
+
|
|
1559
|
+
/**
|
|
1560
|
+
* Callback when metrics are dropped due to queue overflow
|
|
1561
|
+
*/
|
|
1562
|
+
type QueueOverflowHandler = (droppedCount: number) => void;
|
|
1563
|
+
/**
|
|
1564
|
+
* Callback when a batch send fails after all retries
|
|
1565
|
+
*/
|
|
1566
|
+
type SendErrorHandler = (error: Error, batch: MetricEvent[], stats: TransportStats) => void;
|
|
1567
|
+
/**
|
|
1568
|
+
* Transport statistics for observability
|
|
1569
|
+
*/
|
|
1570
|
+
interface TransportStats {
|
|
1571
|
+
/** Total metrics successfully sent */
|
|
1572
|
+
sent: number;
|
|
1573
|
+
/** Total metrics dropped due to queue overflow */
|
|
1574
|
+
dropped: number;
|
|
1575
|
+
/** Total metrics that failed to send after retries */
|
|
1576
|
+
errors: number;
|
|
1577
|
+
/** Current queue size */
|
|
1578
|
+
queued: number;
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Options for the HTTP transport
|
|
1582
|
+
*/
|
|
1583
|
+
interface HttpTransportOptions {
|
|
1584
|
+
/** API endpoint URL */
|
|
1585
|
+
apiUrl: string;
|
|
1586
|
+
/** API key for authentication (sent as Bearer token) */
|
|
1587
|
+
apiKey?: string;
|
|
1588
|
+
/** Number of events to batch before sending (default: 50) */
|
|
1589
|
+
batchSize?: number;
|
|
1590
|
+
/** Milliseconds between automatic flushes (default: 5000) */
|
|
1591
|
+
flushInterval?: number;
|
|
1592
|
+
/** Request timeout in milliseconds (default: 10000) */
|
|
1593
|
+
timeout?: number;
|
|
1594
|
+
/** Maximum retry attempts (default: 3) */
|
|
1595
|
+
maxRetries?: number;
|
|
1596
|
+
/** Maximum queue size before dropping events (default: 10000) */
|
|
1597
|
+
maxQueueSize?: number;
|
|
1598
|
+
/** Additional headers to send with requests */
|
|
1599
|
+
headers?: Record<string, string>;
|
|
1600
|
+
/** Callback when events are dropped due to queue overflow */
|
|
1601
|
+
onQueueOverflow?: QueueOverflowHandler;
|
|
1602
|
+
/** Callback when a batch fails to send */
|
|
1603
|
+
onSendError?: SendErrorHandler;
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* HTTP transport that batches and sends metrics to an API endpoint.
|
|
1607
|
+
*
|
|
1608
|
+
* Features:
|
|
1609
|
+
* - Batched sending for efficiency
|
|
1610
|
+
* - Automatic periodic flushing
|
|
1611
|
+
* - Retry with exponential backoff
|
|
1612
|
+
* - Queue overflow protection
|
|
1613
|
+
* - Observability via stats
|
|
1614
|
+
*
|
|
1615
|
+
* @example
|
|
1616
|
+
* ```typescript
|
|
1617
|
+
* const transport = createHttpTransport({
|
|
1618
|
+
* apiUrl: "https://api.example.com/v1/metrics",
|
|
1619
|
+
* apiKey: "your-api-key",
|
|
1620
|
+
* });
|
|
1621
|
+
*
|
|
1622
|
+
* const metered = makeMeteredOpenAI(client, {
|
|
1623
|
+
* emitMetric: transport,
|
|
1624
|
+
* });
|
|
1625
|
+
*
|
|
1626
|
+
* // On shutdown
|
|
1627
|
+
* await transport.flush();
|
|
1628
|
+
* transport.stop();
|
|
1629
|
+
* ```
|
|
1630
|
+
*/
|
|
1631
|
+
declare class HttpTransport {
|
|
1632
|
+
private readonly apiUrl;
|
|
1633
|
+
private readonly apiKey?;
|
|
1634
|
+
private readonly batchSize;
|
|
1635
|
+
private readonly flushInterval;
|
|
1636
|
+
private readonly timeout;
|
|
1637
|
+
private readonly maxRetries;
|
|
1638
|
+
private readonly maxQueueSize;
|
|
1639
|
+
private readonly extraHeaders;
|
|
1640
|
+
private readonly onQueueOverflow?;
|
|
1641
|
+
private readonly onSendError?;
|
|
1642
|
+
private queue;
|
|
1643
|
+
private flushTimer;
|
|
1644
|
+
private isStopped;
|
|
1645
|
+
private sentCount;
|
|
1646
|
+
private droppedCount;
|
|
1647
|
+
private errorCount;
|
|
1648
|
+
constructor(options: HttpTransportOptions);
|
|
1649
|
+
private startFlushTimer;
|
|
1650
|
+
/**
|
|
1651
|
+
* Add an event to the send queue.
|
|
1652
|
+
* This is the MetricEmitter interface.
|
|
1653
|
+
*/
|
|
1654
|
+
emit: MetricEmitter;
|
|
1655
|
+
/**
|
|
1656
|
+
* Flush pending events (async version)
|
|
1657
|
+
*/
|
|
1658
|
+
flushAsync(): Promise<void>;
|
|
1659
|
+
/**
|
|
1660
|
+
* Flush pending events (sync-friendly, returns promise)
|
|
1661
|
+
*/
|
|
1662
|
+
flush(): Promise<void>;
|
|
1663
|
+
/**
|
|
1664
|
+
* Flush all pending events (may require multiple batches)
|
|
1665
|
+
*/
|
|
1666
|
+
flushAll(): Promise<void>;
|
|
1667
|
+
private sendBatch;
|
|
1668
|
+
private sleep;
|
|
1669
|
+
/**
|
|
1670
|
+
* Get transport statistics
|
|
1671
|
+
*/
|
|
1672
|
+
get stats(): TransportStats;
|
|
1673
|
+
/**
|
|
1674
|
+
* Stop the transport and flush remaining events
|
|
1675
|
+
*/
|
|
1676
|
+
stop(): Promise<void>;
|
|
1677
|
+
/**
|
|
1678
|
+
* Check if the transport is stopped
|
|
1679
|
+
*/
|
|
1680
|
+
get stopped(): boolean;
|
|
1681
|
+
}
|
|
1682
|
+
/**
|
|
1683
|
+
* Create an HTTP transport for sending metrics to an API.
|
|
1684
|
+
*
|
|
1685
|
+
* @example
|
|
1686
|
+
* ```typescript
|
|
1687
|
+
* // Using options
|
|
1688
|
+
* const transport = createHttpTransport({
|
|
1689
|
+
* apiUrl: "https://api.example.com/v1/metrics",
|
|
1690
|
+
* apiKey: "your-api-key",
|
|
1691
|
+
* batchSize: 100,
|
|
1692
|
+
* });
|
|
1693
|
+
*
|
|
1694
|
+
* // Using environment variables
|
|
1695
|
+
* // Set METER_API_URL and optionally METER_API_KEY
|
|
1696
|
+
* const transport = createHttpTransport();
|
|
1697
|
+
* ```
|
|
1698
|
+
*/
|
|
1699
|
+
declare function createHttpTransport(options?: Partial<HttpTransportOptions>): HttpTransport;
|
|
1700
|
+
/**
|
|
1701
|
+
* Create an HTTP transport emitter function.
|
|
1702
|
+
*
|
|
1703
|
+
* This is a convenience function that returns just the emit function,
|
|
1704
|
+
* suitable for use directly as a MetricEmitter.
|
|
1705
|
+
*
|
|
1706
|
+
* Note: The returned emitter cannot be stopped or flushed. For production use,
|
|
1707
|
+
* prefer createHttpTransport() which gives you access to stop() and flush().
|
|
1708
|
+
*
|
|
1709
|
+
* @example
|
|
1710
|
+
* ```typescript
|
|
1711
|
+
* const metered = makeMeteredOpenAI(client, {
|
|
1712
|
+
* emitMetric: createHttpEmitter({
|
|
1713
|
+
* apiUrl: "https://api.example.com/v1/metrics",
|
|
1714
|
+
* }),
|
|
1715
|
+
* });
|
|
1716
|
+
* ```
|
|
1717
|
+
*/
|
|
1718
|
+
declare function createHttpEmitter(options?: Partial<HttpTransportOptions>): MetricEmitter;
|
|
1719
|
+
|
|
1720
|
+
/**
|
|
1721
|
+
* Time-series data point for trend analysis
|
|
1722
|
+
*/
|
|
1723
|
+
interface TimeSeriesPoint {
|
|
1724
|
+
timestamp: Date;
|
|
1725
|
+
value: number;
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* Comprehensive analytics for CTO-level reporting
|
|
1729
|
+
*/
|
|
1730
|
+
interface AnalyticsReport {
|
|
1731
|
+
costs: {
|
|
1732
|
+
total: number;
|
|
1733
|
+
byModel: Record<string, number>;
|
|
1734
|
+
byHour: TimeSeriesPoint[];
|
|
1735
|
+
projectedMonthly: number;
|
|
1736
|
+
cacheSavings: number;
|
|
1737
|
+
avgCostPerRequest: number;
|
|
1738
|
+
avgCostPer1kTokens: number;
|
|
1739
|
+
};
|
|
1740
|
+
performance: {
|
|
1741
|
+
avgLatency: number;
|
|
1742
|
+
p50Latency: number;
|
|
1743
|
+
p95Latency: number;
|
|
1744
|
+
p99Latency: number;
|
|
1745
|
+
requestsPerMinute: number;
|
|
1746
|
+
tokensPerSecond: number;
|
|
1747
|
+
};
|
|
1748
|
+
efficiency: {
|
|
1749
|
+
cacheHitRate: number;
|
|
1750
|
+
avgInputTokens: number;
|
|
1751
|
+
avgOutputTokens: number;
|
|
1752
|
+
inputOutputRatio: number;
|
|
1753
|
+
reasoningOverhead: number;
|
|
1754
|
+
};
|
|
1755
|
+
reliability: {
|
|
1756
|
+
successRate: number;
|
|
1757
|
+
errorRate: number;
|
|
1758
|
+
errorsByType: Record<string, number>;
|
|
1759
|
+
avgRetriesPerRequest: number;
|
|
1760
|
+
};
|
|
1761
|
+
usage: {
|
|
1762
|
+
totalRequests: number;
|
|
1763
|
+
totalTokens: number;
|
|
1764
|
+
peakRequestsPerMinute: number;
|
|
1765
|
+
peakHour: string;
|
|
1766
|
+
modelDistribution: Record<string, number>;
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1770
|
+
* Analytics engine for collecting and analyzing metrics
|
|
1771
|
+
*/
|
|
1772
|
+
declare class AnalyticsEngine {
|
|
1773
|
+
private events;
|
|
1774
|
+
private pricing;
|
|
1775
|
+
private startTime;
|
|
1776
|
+
constructor(customPricing?: Record<string, {
|
|
1777
|
+
input: number;
|
|
1778
|
+
output: number;
|
|
1779
|
+
cached: number;
|
|
1780
|
+
}>);
|
|
1781
|
+
/**
|
|
1782
|
+
* Record a metric event
|
|
1783
|
+
*/
|
|
1784
|
+
record(event: MetricEvent): void;
|
|
1785
|
+
/**
|
|
1786
|
+
* Get the pricing for a model (with fallback to base model)
|
|
1787
|
+
*/
|
|
1788
|
+
private getModelPricing;
|
|
1789
|
+
/**
|
|
1790
|
+
* Calculate cost for a single event
|
|
1791
|
+
*/
|
|
1792
|
+
private calculateEventCost;
|
|
1793
|
+
/**
|
|
1794
|
+
* Calculate percentile from sorted array
|
|
1795
|
+
*/
|
|
1796
|
+
private percentile;
|
|
1797
|
+
/**
|
|
1798
|
+
* Generate comprehensive analytics report
|
|
1799
|
+
*/
|
|
1800
|
+
generateReport(): AnalyticsReport;
|
|
1801
|
+
/**
|
|
1802
|
+
* Classify error type
|
|
1803
|
+
*/
|
|
1804
|
+
private classifyError;
|
|
1805
|
+
/**
|
|
1806
|
+
* Format report as markdown for CTO presentation
|
|
1807
|
+
*/
|
|
1808
|
+
formatMarkdown(report: AnalyticsReport): string;
|
|
1809
|
+
/**
|
|
1810
|
+
* Generate actionable recommendations based on metrics
|
|
1811
|
+
*/
|
|
1812
|
+
private generateRecommendations;
|
|
1813
|
+
/**
|
|
1814
|
+
* Reset analytics data
|
|
1815
|
+
*/
|
|
1816
|
+
reset(): void;
|
|
1817
|
+
/**
|
|
1818
|
+
* Get raw events
|
|
1819
|
+
*/
|
|
1820
|
+
getEvents(): MetricEvent[];
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* Create an emitter that feeds into the analytics engine
|
|
1824
|
+
*/
|
|
1825
|
+
declare function createAnalyticsEmitter(engine: AnalyticsEngine): (event: MetricEvent) => void;
|
|
1826
|
+
|
|
1827
|
+
/**
|
|
1828
|
+
* JSON Schema types for comprehensive analytics reporting
|
|
1829
|
+
*/
|
|
1830
|
+
/**
|
|
1831
|
+
* Individual request/event within a pattern
|
|
1832
|
+
*/
|
|
1833
|
+
interface PatternEvent {
|
|
1834
|
+
id: string;
|
|
1835
|
+
timestamp: string;
|
|
1836
|
+
model: string;
|
|
1837
|
+
latency_ms: number;
|
|
1838
|
+
input_tokens: number;
|
|
1839
|
+
output_tokens: number;
|
|
1840
|
+
cached_tokens: number;
|
|
1841
|
+
reasoning_tokens: number;
|
|
1842
|
+
cost: number;
|
|
1843
|
+
error?: string;
|
|
1844
|
+
metadata?: Record<string, unknown>;
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Multi-turn conversation pattern
|
|
1848
|
+
*/
|
|
1849
|
+
interface ConversationPattern {
|
|
1850
|
+
type: "conversation";
|
|
1851
|
+
conversation_id: string;
|
|
1852
|
+
user_id?: string;
|
|
1853
|
+
started_at: string;
|
|
1854
|
+
ended_at?: string;
|
|
1855
|
+
turns: Array<{
|
|
1856
|
+
turn_number: number;
|
|
1857
|
+
role: "user" | "assistant";
|
|
1858
|
+
event: PatternEvent;
|
|
1859
|
+
context_tokens: number;
|
|
1860
|
+
context_utilization: number;
|
|
1861
|
+
cumulative_cost: number;
|
|
1862
|
+
cache_efficiency: number;
|
|
1863
|
+
}>;
|
|
1864
|
+
summary: {
|
|
1865
|
+
total_turns: number;
|
|
1866
|
+
total_tokens: number;
|
|
1867
|
+
total_cost: number;
|
|
1868
|
+
avg_latency: number;
|
|
1869
|
+
avg_tokens_per_turn: number;
|
|
1870
|
+
context_growth_rate: number;
|
|
1871
|
+
cache_savings: number;
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Tenant in multi-tenant pattern
|
|
1876
|
+
*/
|
|
1877
|
+
interface TenantUsage {
|
|
1878
|
+
tenant_id: string;
|
|
1879
|
+
tenant_name?: string;
|
|
1880
|
+
tier: "free" | "pro" | "enterprise" | string;
|
|
1881
|
+
users: Array<{
|
|
1882
|
+
user_id: string;
|
|
1883
|
+
request_count: number;
|
|
1884
|
+
total_tokens: number;
|
|
1885
|
+
total_cost: number;
|
|
1886
|
+
}>;
|
|
1887
|
+
events: PatternEvent[];
|
|
1888
|
+
summary: {
|
|
1889
|
+
total_requests: number;
|
|
1890
|
+
total_tokens: number;
|
|
1891
|
+
total_cost: number;
|
|
1892
|
+
quota_used: number;
|
|
1893
|
+
quota_limit: number;
|
|
1894
|
+
avg_cost_per_request: number;
|
|
1895
|
+
peak_requests_per_minute: number;
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Multi-tenant agent fleet pattern
|
|
1900
|
+
*/
|
|
1901
|
+
interface MultiTenantPattern {
|
|
1902
|
+
type: "multi_tenant";
|
|
1903
|
+
period_start: string;
|
|
1904
|
+
period_end: string;
|
|
1905
|
+
tenants: TenantUsage[];
|
|
1906
|
+
summary: {
|
|
1907
|
+
total_tenants: number;
|
|
1908
|
+
total_requests: number;
|
|
1909
|
+
total_tokens: number;
|
|
1910
|
+
total_revenue?: number;
|
|
1911
|
+
total_cost: number;
|
|
1912
|
+
gross_margin?: number;
|
|
1913
|
+
top_tenant_by_usage: string;
|
|
1914
|
+
top_tenant_by_cost: string;
|
|
1915
|
+
tier_distribution: Record<string, number>;
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Tool call within an agent
|
|
1920
|
+
*/
|
|
1921
|
+
interface ToolCall {
|
|
1922
|
+
tool_name: string;
|
|
1923
|
+
tool_id: string;
|
|
1924
|
+
input: Record<string, unknown>;
|
|
1925
|
+
output?: unknown;
|
|
1926
|
+
duration_ms: number;
|
|
1927
|
+
success: boolean;
|
|
1928
|
+
error?: string;
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Agent execution within cascaded pattern
|
|
1932
|
+
*/
|
|
1933
|
+
interface AgentExecution {
|
|
1934
|
+
agent_id: string;
|
|
1935
|
+
agent_name: string;
|
|
1936
|
+
agent_type: "orchestrator" | "worker" | "specialist";
|
|
1937
|
+
parent_agent_id?: string;
|
|
1938
|
+
depth: number;
|
|
1939
|
+
started_at: string;
|
|
1940
|
+
ended_at: string;
|
|
1941
|
+
model: string;
|
|
1942
|
+
events: PatternEvent[];
|
|
1943
|
+
tool_calls: ToolCall[];
|
|
1944
|
+
subagents: AgentExecution[];
|
|
1945
|
+
summary: {
|
|
1946
|
+
total_llm_calls: number;
|
|
1947
|
+
total_tool_calls: number;
|
|
1948
|
+
total_tokens: number;
|
|
1949
|
+
total_cost: number;
|
|
1950
|
+
total_duration_ms: number;
|
|
1951
|
+
success: boolean;
|
|
1952
|
+
error?: string;
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Multi-agent cascaded pattern
|
|
1957
|
+
*/
|
|
1958
|
+
interface CascadedAgentPattern {
|
|
1959
|
+
type: "cascaded_agents";
|
|
1960
|
+
task_id: string;
|
|
1961
|
+
task_description?: string;
|
|
1962
|
+
started_at: string;
|
|
1963
|
+
ended_at: string;
|
|
1964
|
+
root_agent: AgentExecution;
|
|
1965
|
+
summary: {
|
|
1966
|
+
total_agents: number;
|
|
1967
|
+
max_depth: number;
|
|
1968
|
+
total_llm_calls: number;
|
|
1969
|
+
total_tool_calls: number;
|
|
1970
|
+
total_tokens: number;
|
|
1971
|
+
total_cost: number;
|
|
1972
|
+
total_duration_ms: number;
|
|
1973
|
+
parallelism_factor: number;
|
|
1974
|
+
cost_by_agent_type: Record<string, number>;
|
|
1975
|
+
tokens_by_depth: Record<number, number>;
|
|
1976
|
+
tool_usage: Record<string, {
|
|
1977
|
+
count: number;
|
|
1978
|
+
avg_duration_ms: number;
|
|
1979
|
+
success_rate: number;
|
|
1980
|
+
}>;
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Complete analytics report with all patterns
|
|
1985
|
+
*/
|
|
1986
|
+
interface AnalyticsJSON {
|
|
1987
|
+
generated_at: string;
|
|
1988
|
+
period: {
|
|
1989
|
+
start: string;
|
|
1990
|
+
end: string;
|
|
1991
|
+
duration_hours: number;
|
|
1992
|
+
};
|
|
1993
|
+
summary: {
|
|
1994
|
+
total_requests: number;
|
|
1995
|
+
total_tokens: number;
|
|
1996
|
+
total_cost: number;
|
|
1997
|
+
projected_monthly_cost: number;
|
|
1998
|
+
cache_savings: number;
|
|
1999
|
+
success_rate: number;
|
|
2000
|
+
avg_latency_ms: number;
|
|
2001
|
+
p50_latency_ms: number;
|
|
2002
|
+
p95_latency_ms: number;
|
|
2003
|
+
p99_latency_ms: number;
|
|
2004
|
+
};
|
|
2005
|
+
costs: {
|
|
2006
|
+
by_model: Record<string, {
|
|
2007
|
+
requests: number;
|
|
2008
|
+
tokens: number;
|
|
2009
|
+
cost: number;
|
|
2010
|
+
}>;
|
|
2011
|
+
by_hour: Array<{
|
|
2012
|
+
hour: string;
|
|
2013
|
+
cost: number;
|
|
2014
|
+
requests: number;
|
|
2015
|
+
}>;
|
|
2016
|
+
by_pattern_type: Record<string, number>;
|
|
2017
|
+
};
|
|
2018
|
+
patterns: {
|
|
2019
|
+
conversations: ConversationPattern[];
|
|
2020
|
+
multi_tenant: MultiTenantPattern | null;
|
|
2021
|
+
cascaded_agents: CascadedAgentPattern[];
|
|
2022
|
+
};
|
|
2023
|
+
recommendations: Array<{
|
|
2024
|
+
category: "cost" | "performance" | "efficiency" | "reliability";
|
|
2025
|
+
severity: "info" | "warning" | "critical";
|
|
2026
|
+
title: string;
|
|
2027
|
+
description: string;
|
|
2028
|
+
potential_savings?: number;
|
|
2029
|
+
action: string;
|
|
2030
|
+
}>;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
/**
|
|
2034
|
+
* Report Builder
|
|
2035
|
+
* Combines real analytics data with pattern simulations to generate comprehensive reports
|
|
2036
|
+
*/
|
|
2037
|
+
|
|
2038
|
+
interface ReportBuilderOptions {
|
|
2039
|
+
/**
|
|
2040
|
+
* Include simulated conversation patterns
|
|
2041
|
+
*/
|
|
2042
|
+
simulateConversations?: number;
|
|
2043
|
+
/**
|
|
2044
|
+
* Include simulated multi-tenant data
|
|
2045
|
+
*/
|
|
2046
|
+
simulateMultiTenant?: {
|
|
2047
|
+
tenants: number;
|
|
2048
|
+
requestsPerTenant?: {
|
|
2049
|
+
min: number;
|
|
2050
|
+
max: number;
|
|
2051
|
+
};
|
|
2052
|
+
};
|
|
2053
|
+
/**
|
|
2054
|
+
* Include simulated cascaded agent executions
|
|
2055
|
+
*/
|
|
2056
|
+
simulateCascadedAgents?: number;
|
|
2057
|
+
/**
|
|
2058
|
+
* Custom model pricing
|
|
2059
|
+
*/
|
|
2060
|
+
pricing?: Record<string, {
|
|
2061
|
+
input: number;
|
|
2062
|
+
output: number;
|
|
2063
|
+
cached: number;
|
|
2064
|
+
}>;
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Report Builder for generating comprehensive analytics reports
|
|
2068
|
+
*/
|
|
2069
|
+
declare class ReportBuilder {
|
|
2070
|
+
private events;
|
|
2071
|
+
private conversations;
|
|
2072
|
+
private multiTenant;
|
|
2073
|
+
private cascadedAgents;
|
|
2074
|
+
private startTime;
|
|
2075
|
+
private pricing;
|
|
2076
|
+
constructor(options?: {
|
|
2077
|
+
pricing?: Record<string, {
|
|
2078
|
+
input: number;
|
|
2079
|
+
output: number;
|
|
2080
|
+
cached: number;
|
|
2081
|
+
}>;
|
|
2082
|
+
});
|
|
2083
|
+
/**
|
|
2084
|
+
* Record a metric event from real API calls
|
|
2085
|
+
*/
|
|
2086
|
+
recordEvent(event: MetricEvent): void;
|
|
2087
|
+
/**
|
|
2088
|
+
* Add a conversation pattern (real or simulated)
|
|
2089
|
+
*/
|
|
2090
|
+
addConversation(conversation: ConversationPattern): void;
|
|
2091
|
+
/**
|
|
2092
|
+
* Set multi-tenant data (real or simulated)
|
|
2093
|
+
*/
|
|
2094
|
+
setMultiTenant(data: MultiTenantPattern): void;
|
|
2095
|
+
/**
|
|
2096
|
+
* Add a cascaded agent execution (real or simulated)
|
|
2097
|
+
*/
|
|
2098
|
+
addCascadedAgent(execution: CascadedAgentPattern): void;
|
|
2099
|
+
/**
|
|
2100
|
+
* Simulate and add patterns based on options
|
|
2101
|
+
*/
|
|
2102
|
+
addSimulatedPatterns(options: ReportBuilderOptions): void;
|
|
2103
|
+
/**
|
|
2104
|
+
* Calculate cost for MetricEvent
|
|
2105
|
+
*/
|
|
2106
|
+
private calculateEventCost;
|
|
2107
|
+
/**
|
|
2108
|
+
* Calculate cost for UnifiedEvent
|
|
2109
|
+
*/
|
|
2110
|
+
private calculateUnifiedEventCost;
|
|
2111
|
+
/**
|
|
2112
|
+
* Generate the complete analytics JSON
|
|
2113
|
+
*/
|
|
2114
|
+
buildJSON(): AnalyticsJSON;
|
|
2115
|
+
/**
|
|
2116
|
+
* Generate recommendations based on metrics
|
|
2117
|
+
*/
|
|
2118
|
+
private generateRecommendations;
|
|
2119
|
+
/**
|
|
2120
|
+
* Build and return HTML report
|
|
2121
|
+
*/
|
|
2122
|
+
buildHTML(): string;
|
|
2123
|
+
/**
|
|
2124
|
+
* Reset the builder
|
|
2125
|
+
*/
|
|
2126
|
+
reset(): void;
|
|
2127
|
+
}
|
|
2128
|
+
/**
|
|
2129
|
+
* Create a metric emitter that feeds into the report builder
|
|
2130
|
+
*/
|
|
2131
|
+
declare function createReportEmitter(builder: ReportBuilder): (event: MetricEvent) => void;
|
|
2132
|
+
|
|
2133
|
+
/**
|
|
2134
|
+
* HTML Report Generator
|
|
2135
|
+
* Creates interactive HTML reports with charts for CTO-level analytics
|
|
2136
|
+
*/
|
|
2137
|
+
|
|
2138
|
+
/**
|
|
2139
|
+
* Generate complete HTML report from analytics JSON
|
|
2140
|
+
*/
|
|
2141
|
+
declare function generateHTMLReport(data: AnalyticsJSON): string;
|
|
2142
|
+
|
|
2143
|
+
/**
|
|
2144
|
+
* Pattern Simulator for generating realistic analytics data
|
|
2145
|
+
* Simulates multi-turn conversations, multi-tenant fleets, and cascaded agents
|
|
2146
|
+
*/
|
|
2147
|
+
|
|
2148
|
+
/**
|
|
2149
|
+
* Simulate a multi-turn conversation
|
|
2150
|
+
*/
|
|
2151
|
+
declare function simulateConversation(options: {
|
|
2152
|
+
turns?: number;
|
|
2153
|
+
model?: string;
|
|
2154
|
+
userId?: string;
|
|
2155
|
+
enableCaching?: boolean;
|
|
2156
|
+
}): ConversationPattern;
|
|
2157
|
+
/**
|
|
2158
|
+
* Simulate multi-tenant usage
|
|
2159
|
+
*/
|
|
2160
|
+
declare function simulateMultiTenant(options: {
|
|
2161
|
+
tenantCount?: number;
|
|
2162
|
+
requestsPerTenant?: {
|
|
2163
|
+
min: number;
|
|
2164
|
+
max: number;
|
|
2165
|
+
};
|
|
2166
|
+
tiers?: Array<{
|
|
2167
|
+
name: string;
|
|
2168
|
+
quota: number;
|
|
2169
|
+
weight: number;
|
|
2170
|
+
}>;
|
|
2171
|
+
}): MultiTenantPattern;
|
|
2172
|
+
/**
|
|
2173
|
+
* Simulate cascaded multi-agent execution
|
|
2174
|
+
*/
|
|
2175
|
+
declare function simulateCascadedAgents(options: {
|
|
2176
|
+
taskDescription?: string;
|
|
2177
|
+
maxDepth?: number;
|
|
2178
|
+
avgSubagentsPerLevel?: number;
|
|
2179
|
+
toolsAvailable?: string[];
|
|
2180
|
+
}): CascadedAgentPattern;
|
|
2181
|
+
|
|
2182
|
+
/**
|
|
2183
|
+
* Logging configuration for the aden SDK.
|
|
2184
|
+
*
|
|
2185
|
+
* Provides a simple logging abstraction that can be configured via
|
|
2186
|
+
* environment variables or programmatically.
|
|
2187
|
+
*/
|
|
2188
|
+
/**
|
|
2189
|
+
* Log levels in order of severity
|
|
2190
|
+
*/
|
|
2191
|
+
type LogLevel = "debug" | "info" | "warn" | "error" | "silent";
|
|
2192
|
+
/**
|
|
2193
|
+
* Logging configuration options
|
|
2194
|
+
*/
|
|
2195
|
+
interface LoggingConfig {
|
|
2196
|
+
/** Global log level for the SDK. Default: "info" or ADEN_LOG_LEVEL env var */
|
|
2197
|
+
level?: LogLevel;
|
|
2198
|
+
/** Log level for metrics-specific logs. Default: same as level */
|
|
2199
|
+
metricsLevel?: LogLevel;
|
|
2200
|
+
/** Custom log handler. Default: console */
|
|
2201
|
+
handler?: LogHandler;
|
|
2202
|
+
}
|
|
2203
|
+
/**
|
|
2204
|
+
* Log handler interface
|
|
2205
|
+
*/
|
|
2206
|
+
interface LogHandler {
|
|
2207
|
+
debug: (message: string, ...args: unknown[]) => void;
|
|
2208
|
+
info: (message: string, ...args: unknown[]) => void;
|
|
2209
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
2210
|
+
error: (message: string, ...args: unknown[]) => void;
|
|
2211
|
+
}
|
|
2212
|
+
/**
|
|
2213
|
+
* Configure logging for the aden SDK.
|
|
2214
|
+
*
|
|
2215
|
+
* @example
|
|
2216
|
+
* ```typescript
|
|
2217
|
+
* import { configureLogging } from "aden";
|
|
2218
|
+
*
|
|
2219
|
+
* // Set log level to debug
|
|
2220
|
+
* configureLogging({ level: "debug" });
|
|
2221
|
+
*
|
|
2222
|
+
* // Quiet metrics logs but keep other logs
|
|
2223
|
+
* configureLogging({ level: "info", metricsLevel: "warn" });
|
|
2224
|
+
*
|
|
2225
|
+
* // Use a custom log handler
|
|
2226
|
+
* configureLogging({
|
|
2227
|
+
* handler: {
|
|
2228
|
+
* debug: (msg, ...args) => myLogger.debug(msg, ...args),
|
|
2229
|
+
* info: (msg, ...args) => myLogger.info(msg, ...args),
|
|
2230
|
+
* warn: (msg, ...args) => myLogger.warn(msg, ...args),
|
|
2231
|
+
* error: (msg, ...args) => myLogger.error(msg, ...args),
|
|
2232
|
+
* },
|
|
2233
|
+
* });
|
|
2234
|
+
* ```
|
|
2235
|
+
*/
|
|
2236
|
+
declare function configureLogging(config: LoggingConfig): void;
|
|
2237
|
+
/**
|
|
2238
|
+
* Get the current logging configuration
|
|
2239
|
+
*/
|
|
2240
|
+
declare function getLoggingConfig(): Readonly<Required<LoggingConfig>>;
|
|
2241
|
+
/**
|
|
2242
|
+
* Reset logging configuration to defaults
|
|
2243
|
+
*/
|
|
2244
|
+
declare function resetLoggingConfig(): void;
|
|
2245
|
+
/**
|
|
2246
|
+
* Logger for the aden SDK
|
|
2247
|
+
*/
|
|
2248
|
+
declare const logger: {
|
|
2249
|
+
debug(message: string, ...args: unknown[]): void;
|
|
2250
|
+
info(message: string, ...args: unknown[]): void;
|
|
2251
|
+
warn(message: string, ...args: unknown[]): void;
|
|
2252
|
+
error(message: string, ...args: unknown[]): void;
|
|
2253
|
+
};
|
|
2254
|
+
/**
|
|
2255
|
+
* Logger for metrics-specific logs
|
|
2256
|
+
*/
|
|
2257
|
+
declare const metricsLogger: {
|
|
2258
|
+
debug(message: string, ...args: unknown[]): void;
|
|
2259
|
+
info(message: string, ...args: unknown[]): void;
|
|
2260
|
+
warn(message: string, ...args: unknown[]): void;
|
|
2261
|
+
error(message: string, ...args: unknown[]): void;
|
|
2262
|
+
};
|
|
2263
|
+
|
|
2264
|
+
/**
|
|
2265
|
+
* Control Agent - Bidirectional communication with control server
|
|
2266
|
+
*
|
|
2267
|
+
* Emits: metrics, control events, heartbeat
|
|
2268
|
+
* Receives: control policies (budgets, throttle, block, degrade)
|
|
2269
|
+
*
|
|
2270
|
+
* Uses WebSocket for real-time communication with HTTP polling fallback.
|
|
2271
|
+
*/
|
|
2272
|
+
|
|
2273
|
+
/**
|
|
2274
|
+
* Control Agent implementation
|
|
2275
|
+
*/
|
|
2276
|
+
declare class ControlAgent implements IControlAgent {
|
|
2277
|
+
private options;
|
|
2278
|
+
private ws;
|
|
2279
|
+
private cachedPolicy;
|
|
2280
|
+
private lastPolicyFetch;
|
|
2281
|
+
private pollingTimer;
|
|
2282
|
+
private heartbeatTimer;
|
|
2283
|
+
private reconnectTimer;
|
|
2284
|
+
private connected;
|
|
2285
|
+
private reconnectAttempts;
|
|
2286
|
+
private maxReconnectAttempts;
|
|
2287
|
+
private eventQueue;
|
|
2288
|
+
private maxQueueSize;
|
|
2289
|
+
private requestsSinceLastHeartbeat;
|
|
2290
|
+
private errorsSinceLastHeartbeat;
|
|
2291
|
+
private requestCounts;
|
|
2292
|
+
constructor(options: ControlAgentOptions);
|
|
2293
|
+
/**
|
|
2294
|
+
* Connect to the control server
|
|
2295
|
+
*/
|
|
2296
|
+
connect(): Promise<void>;
|
|
2297
|
+
/**
|
|
2298
|
+
* Connect via WebSocket
|
|
2299
|
+
*/
|
|
2300
|
+
private connectWebSocket;
|
|
2301
|
+
/**
|
|
2302
|
+
* Schedule WebSocket reconnection
|
|
2303
|
+
*/
|
|
2304
|
+
private scheduleReconnect;
|
|
2305
|
+
/**
|
|
2306
|
+
* Handle incoming WebSocket message
|
|
2307
|
+
*/
|
|
2308
|
+
private handleMessage;
|
|
2309
|
+
/**
|
|
2310
|
+
* Start HTTP polling for policy updates
|
|
2311
|
+
* Returns a promise that resolves when the first policy fetch completes
|
|
2312
|
+
*/
|
|
2313
|
+
private startPolling;
|
|
2314
|
+
/**
|
|
2315
|
+
* Stop HTTP polling
|
|
2316
|
+
*/
|
|
2317
|
+
private stopPolling;
|
|
2318
|
+
/**
|
|
2319
|
+
* Fetch policy via HTTP
|
|
2320
|
+
*/
|
|
2321
|
+
private fetchPolicy;
|
|
2322
|
+
/**
|
|
2323
|
+
* Start heartbeat timer
|
|
2324
|
+
*/
|
|
2325
|
+
private startHeartbeat;
|
|
2326
|
+
/**
|
|
2327
|
+
* Stop heartbeat timer
|
|
2328
|
+
*/
|
|
2329
|
+
private stopHeartbeat;
|
|
2330
|
+
/**
|
|
2331
|
+
* Send heartbeat event
|
|
2332
|
+
*/
|
|
2333
|
+
private sendHeartbeat;
|
|
2334
|
+
/**
|
|
2335
|
+
* Disconnect from the control server
|
|
2336
|
+
*/
|
|
2337
|
+
disconnect(): Promise<void>;
|
|
2338
|
+
/**
|
|
2339
|
+
* Get a control decision for a request
|
|
2340
|
+
*/
|
|
2341
|
+
getDecision(request: ControlRequest): Promise<ControlDecision>;
|
|
2342
|
+
/**
|
|
2343
|
+
* Evaluate policy rules against a request
|
|
2344
|
+
* Priority order: block > budget/degrade > throttle > alert > allow
|
|
2345
|
+
* Note: throttle adds delay but doesn't skip other checks
|
|
2346
|
+
*/
|
|
2347
|
+
private evaluatePolicy;
|
|
2348
|
+
/**
|
|
2349
|
+
* Check if request matches an alert rule
|
|
2350
|
+
*/
|
|
2351
|
+
private matchesAlertRule;
|
|
2352
|
+
/**
|
|
2353
|
+
* Check if request matches a block rule
|
|
2354
|
+
*/
|
|
2355
|
+
private matchesBlockRule;
|
|
2356
|
+
/**
|
|
2357
|
+
* Find budgets that apply to the given request based on budget type
|
|
2358
|
+
*/
|
|
2359
|
+
private findApplicableBudgets;
|
|
2360
|
+
/**
|
|
2361
|
+
* Check rate limit for a key
|
|
2362
|
+
*/
|
|
2363
|
+
private checkRateLimit;
|
|
2364
|
+
/**
|
|
2365
|
+
* Report a metric event to the server
|
|
2366
|
+
*/
|
|
2367
|
+
reportMetric(event: MetricEvent): Promise<void>;
|
|
2368
|
+
/**
|
|
2369
|
+
* Estimate cost from a metric event
|
|
2370
|
+
* Uses gpt-4o pricing as default: $2.50/1M input, $10/1M output
|
|
2371
|
+
*/
|
|
2372
|
+
private estimateCost;
|
|
2373
|
+
/**
|
|
2374
|
+
* Calculate the sampling rate for server validation based on usage percentage.
|
|
2375
|
+
*
|
|
2376
|
+
* The rate interpolates from samplingBaseRate at threshold to 1.0 at
|
|
2377
|
+
* samplingFullValidationPercent.
|
|
2378
|
+
*
|
|
2379
|
+
* Example with defaults (threshold=80%, base_rate=0.1, full=95%):
|
|
2380
|
+
* - At 80%: 10% of requests validated
|
|
2381
|
+
* - At 87.5%: 55% of requests validated
|
|
2382
|
+
* - At 95%+: 100% of requests validated
|
|
2383
|
+
*/
|
|
2384
|
+
private calculateSamplingRate;
|
|
2385
|
+
/**
|
|
2386
|
+
* Determine if we should validate this request with the server.
|
|
2387
|
+
*
|
|
2388
|
+
* Uses adaptive thresholds and probabilistic sampling to minimize
|
|
2389
|
+
* latency impact while maintaining enforcement accuracy.
|
|
2390
|
+
*
|
|
2391
|
+
* Returns true if:
|
|
2392
|
+
* 1. Hybrid enforcement is enabled
|
|
2393
|
+
* 2. Budget usage is at or above the validation threshold
|
|
2394
|
+
* 3. Either:
|
|
2395
|
+
* a. Remaining budget is below adaptiveMinRemainingUsd (always validate)
|
|
2396
|
+
* b. Sampling dice roll succeeds based on current usage level
|
|
2397
|
+
*/
|
|
2398
|
+
private shouldValidateWithServer;
|
|
2399
|
+
/**
|
|
2400
|
+
* Check if request exceeds the hard limit (soft limit + max overspend buffer).
|
|
2401
|
+
*
|
|
2402
|
+
* This provides a safety net to prevent runaway spending even under
|
|
2403
|
+
* concurrency race conditions.
|
|
2404
|
+
*
|
|
2405
|
+
* Returns a BLOCK decision if hard limit exceeded, null otherwise.
|
|
2406
|
+
*/
|
|
2407
|
+
private checkHardLimit;
|
|
2408
|
+
/**
|
|
2409
|
+
* Validate budget with server synchronously.
|
|
2410
|
+
*
|
|
2411
|
+
* Returns BudgetValidationResponse if successful, null if validation failed.
|
|
2412
|
+
* On failure, caller should fall back to local enforcement based on failOpen.
|
|
2413
|
+
*/
|
|
2414
|
+
private validateBudgetWithServer;
|
|
2415
|
+
/**
|
|
2416
|
+
* Convert server validation response to a ControlDecision
|
|
2417
|
+
*/
|
|
2418
|
+
private applyServerValidationResult;
|
|
2419
|
+
/**
|
|
2420
|
+
* Evaluate a single budget with hybrid enforcement
|
|
2421
|
+
*/
|
|
2422
|
+
private evaluateBudgetWithHybridEnforcement;
|
|
2423
|
+
/**
|
|
2424
|
+
* Report a control event to the server
|
|
2425
|
+
*/
|
|
2426
|
+
reportControlEvent(event: Omit<ControlEvent, "event_type" | "timestamp" | "sdk_instance_id">): Promise<void>;
|
|
2427
|
+
/**
|
|
2428
|
+
* Report an error event
|
|
2429
|
+
*/
|
|
2430
|
+
reportError(message: string, error?: Error, traceId?: string): Promise<void>;
|
|
2431
|
+
/**
|
|
2432
|
+
* Send an event to the server
|
|
2433
|
+
*/
|
|
2434
|
+
private sendEvent;
|
|
2435
|
+
/**
|
|
2436
|
+
* Queue an event for later sending
|
|
2437
|
+
*/
|
|
2438
|
+
private queueEvent;
|
|
2439
|
+
/**
|
|
2440
|
+
* Flush queued events
|
|
2441
|
+
*/
|
|
2442
|
+
private flushEventQueue;
|
|
2443
|
+
/**
|
|
2444
|
+
* Make HTTP request to server
|
|
2445
|
+
*/
|
|
2446
|
+
private httpRequest;
|
|
2447
|
+
/**
|
|
2448
|
+
* Check if connected to server (WebSocket)
|
|
2449
|
+
*/
|
|
2450
|
+
isConnected(): boolean;
|
|
2451
|
+
/**
|
|
2452
|
+
* Get current cached policy
|
|
2453
|
+
*/
|
|
2454
|
+
getPolicy(): ControlPolicy | null;
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* Create a control agent
|
|
2458
|
+
*/
|
|
2459
|
+
declare function createControlAgent(options: ControlAgentOptions): ControlAgent;
|
|
2460
|
+
/**
|
|
2461
|
+
* Create a metric emitter that sends to the control agent
|
|
2462
|
+
*
|
|
2463
|
+
* This allows the control agent to work alongside other emitters:
|
|
2464
|
+
* ```typescript
|
|
2465
|
+
* const agent = createControlAgent({ ... });
|
|
2466
|
+
*
|
|
2467
|
+
* await instrument({
|
|
2468
|
+
* emitMetric: createMultiEmitter([
|
|
2469
|
+
* createConsoleEmitter({ pretty: true }),
|
|
2470
|
+
* createControlAgentEmitter(agent),
|
|
2471
|
+
* ]),
|
|
2472
|
+
* controlAgent: agent,
|
|
2473
|
+
* });
|
|
2474
|
+
* ```
|
|
2475
|
+
*/
|
|
2476
|
+
declare function createControlAgentEmitter(agent: IControlAgent): (event: MetricEvent) => Promise<void>;
|
|
2477
|
+
|
|
2478
|
+
export { type AgentExecution, type AlertEvent, type AlertRule, AnalyticsEngine, type AnalyticsJSON, type AnalyticsReport, type BeforeRequestContext, type BeforeRequestHook, type BeforeRequestResult, type BlockRule, type BudgetConfig, BudgetExceededError, type BudgetExceededInfo, type BudgetRule, type BudgetValidationRequest, type BudgetValidationResponse, type CallRelationship, type CallSite, type CascadedAgentPattern, type ControlAction, ControlAgent, type ControlAgentOptions, type ControlDecision, type ControlEvent, type ControlPolicy, type ControlRequest, type ConversationPattern, DEFAULT_CONTROL_SERVER, type DegradeRule, type HeartbeatEvent, HttpTransport, type HttpTransportOptions, type IControlAgent, type InstrumentationResult, type JsonFileEmitterOptions, type LogHandler, type LogLevel, type LoggingConfig, type MeterContext, type MeterOptions, type MeteredOpenAI, type MetricEmitter, type MetricEvent, MetricFileLogger, type MetricFileLoggerOptions, type MultiTenantPattern, type NormalizedUsage, type PatternEvent, type QueueOverflowHandler, type RateLimitInfo, ReportBuilder, RequestCancelledError, type RequestMetadata, type SDKClasses, type SendErrorHandler, type StreamingEventType, type TenantUsage, type ThrottleRule, type ToolCall, type ToolCallMetric, type TransportStats, configureLogging, countInputTokens, createAnalyticsEmitter, createBatchEmitter, createBudgetedMeteredClient, createConsoleEmitter, createControlAgent, createControlAgentEmitter, createFileEmitter, createFilteredEmitter, createHttpEmitter, createHttpTransport, createJsonFileEmitter, createMemoryEmitter, createMeterContext, createMultiEmitter, createNoopEmitter, createReportEmitter, createTransformEmitter, emptyUsage, enterMeterContext, extractAgentStack, extractCallSite, generateHTMLReport, generateStackFingerprint, getContextMetadata, getControlServerUrl, getCurrentContext, getFullAgentStack, getGenaiOptions, getInstrumentationOptions, getInstrumentedSDKs, getLoggingConfig, hasContext, instrument, instrumentAnthropic, instrumentFetch, instrumentGemini, instrumentGenai, instrumentOpenAI, isAnthropicInstrumented, isFetchInstrumented, isGeminiInstrumented, isGenaiInstrumented, isInstrumented, isMetered, isOpenAIInstrumented, logger, makeMeteredOpenAI, mergeUsage, metricsLogger, normalizeAnthropicUsage, normalizeGeminiUsage, normalizeOpenAIUsage, normalizeUsage, popAgent, pushAgent, resetGlobalSession, resetLoggingConfig, setContextMetadata, simulateCascadedAgents, simulateConversation, simulateMultiTenant, uninstrument, uninstrumentAnthropic, uninstrumentFetch, uninstrumentGemini, uninstrumentGenai, uninstrumentOpenAI, updateInstrumentationOptions, withAgent, withBudgetGuardrails, withMeterContext, withMeterContextAsync };
|