@usagetap/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,398 @@
1
+ import OpenAI from 'openai';
2
+
3
+ type ReasoningLevel = "NONE" | "LOW" | "MEDIUM" | "HIGH";
4
+ type LimitType = "NONE" | "BLOCK" | "DOWNGRADE";
5
+ interface RequestedEntitlements {
6
+ standard?: boolean;
7
+ premium?: boolean;
8
+ audio?: boolean;
9
+ image?: boolean;
10
+ search?: boolean;
11
+ reasoningLevel?: ReasoningLevel;
12
+ }
13
+ type AllowedEntitlements = Required<RequestedEntitlements>;
14
+ type ModelTier = "premium" | "standard" | "none";
15
+ interface EntitlementDowngradeHint {
16
+ reason: string;
17
+ fallbackTier?: ModelTier;
18
+ }
19
+ interface EntitlementHints {
20
+ suggestedModelTier: ModelTier;
21
+ reasoningLevel: ReasoningLevel;
22
+ policy: LimitType;
23
+ downgrade?: EntitlementDowngradeHint;
24
+ }
25
+ interface MeterSummary {
26
+ remaining: number | null;
27
+ limit: number | null;
28
+ used: number | null;
29
+ unlimited: boolean;
30
+ ratio: number | null;
31
+ }
32
+ type MeterSnapshot = Record<string, MeterSummary>;
33
+ type RemainingRatios = Record<string, number | null | undefined>;
34
+ interface SubscriptionSnapshot {
35
+ id: string | null;
36
+ usagePlanVersionId: string | null;
37
+ planName: string | null;
38
+ planVersion: string | null;
39
+ limitType: LimitType;
40
+ reasoningLevel: ReasoningLevel;
41
+ lastReplenishedAt: string | null;
42
+ nextReplenishAt: string | null;
43
+ subscriptionVersion: number | null;
44
+ customerFriendlyName?: string | null;
45
+ customerEmail?: string | null;
46
+ pending?: {
47
+ usagePlanVersionId: string | null;
48
+ strategy: string | null;
49
+ effectiveAt: string | null;
50
+ };
51
+ stripeCustomerId?: string | null;
52
+ }
53
+ type ModelHints = Record<string, string[]>;
54
+ interface IdempotencyMetadata {
55
+ key: string;
56
+ source: "explicit" | "derived";
57
+ }
58
+ interface VendorHints {
59
+ preferredModel?: string;
60
+ reasoning?: ReasoningLevel;
61
+ maxInputTokens?: number;
62
+ maxResponseTokens?: number;
63
+ }
64
+ interface BeginCallRequest {
65
+ customerId: string;
66
+ requested?: RequestedEntitlements;
67
+ feature?: string;
68
+ tags?: string[];
69
+ idempotency?: string;
70
+ customerName?: string;
71
+ customerEmail?: string;
72
+ stripeCustomerId?: string;
73
+ }
74
+ interface BalanceSummary {
75
+ standardCallsRemaining?: number;
76
+ premiumCallsRemaining?: number;
77
+ tokensRemaining?: number;
78
+ searchesRemaining?: number;
79
+ audioSecondsRemaining?: number;
80
+ }
81
+ interface PlanSummary {
82
+ id: string | null;
83
+ name: string | null;
84
+ version: string | null;
85
+ }
86
+ interface BeginCallResponseBody {
87
+ callId: string;
88
+ startTime: string;
89
+ feature?: string;
90
+ tags?: string[];
91
+ newCustomer: boolean;
92
+ canceled: boolean;
93
+ policy: LimitType;
94
+ allowed: AllowedEntitlements;
95
+ entitlementHints: EntitlementHints;
96
+ meters: MeterSnapshot;
97
+ remainingRatios: RemainingRatios;
98
+ subscription: SubscriptionSnapshot;
99
+ models?: ModelHints;
100
+ idempotency?: IdempotencyMetadata;
101
+ vendorHints?: VendorHints;
102
+ plan?: PlanSummary;
103
+ balances?: BalanceSummary;
104
+ stripeCustomerId?: string | null;
105
+ }
106
+ interface EndCallRequest {
107
+ callId: string;
108
+ modelUsed?: string;
109
+ inputTokens?: number;
110
+ responseTokens?: number;
111
+ cachedTokens?: number;
112
+ reasoningTokens?: number;
113
+ searches?: number;
114
+ audio?: number;
115
+ audioSeconds?: number;
116
+ error?: {
117
+ code: string;
118
+ message: string;
119
+ };
120
+ stripeCustomerId?: string;
121
+ }
122
+ interface MeteredUsage {
123
+ calls?: number;
124
+ tokens?: number;
125
+ reasoningTokens?: number;
126
+ searches?: number;
127
+ audio?: number;
128
+ audioSeconds?: number;
129
+ }
130
+ interface EndCallResponseBody {
131
+ callId: string;
132
+ costUSD: number;
133
+ metered?: MeteredUsage;
134
+ balances?: BalanceSummary;
135
+ stripeCustomerId?: string | null;
136
+ }
137
+ type UsageTapResultStatus = "ACCEPTED" | "ERROR";
138
+ interface UsageTapResultEnvelope {
139
+ status: UsageTapResultStatus;
140
+ code?: string;
141
+ message?: string;
142
+ timestamp?: string;
143
+ }
144
+ interface UsageTapErrorPayload {
145
+ code: string;
146
+ message: string;
147
+ details?: Record<string, unknown>;
148
+ }
149
+ interface UsageTapSuccessResponse<TData> {
150
+ result: UsageTapResultEnvelope;
151
+ data: TData;
152
+ correlationId: string;
153
+ }
154
+ interface UsageTapErrorResponse {
155
+ result: UsageTapResultEnvelope;
156
+ error: UsageTapErrorPayload;
157
+ correlationId: string;
158
+ }
159
+ interface RetryOptions {
160
+ /**
161
+ * Maximum number of attempts including the initial try.
162
+ * @default 3
163
+ */
164
+ maxAttempts?: number;
165
+ /**
166
+ * Base backoff delay in milliseconds.
167
+ * @default 250
168
+ */
169
+ baseDelayMs?: number;
170
+ /**
171
+ * Maximum backoff delay in milliseconds.
172
+ * @default 5000
173
+ */
174
+ maxDelayMs?: number;
175
+ /**
176
+ * Jitter ratio between 0 and 1 applied to the computed delay.
177
+ * @default 0.2
178
+ */
179
+ jitterRatio?: number;
180
+ }
181
+ interface UsageTapLogEntry {
182
+ event: "request:start" | "request:success" | "request:error" | "retry:scheduled" | "retry:exhausted";
183
+ path: string;
184
+ attempt: number;
185
+ elapsedMs?: number;
186
+ idempotencyKey?: string;
187
+ correlationId?: string;
188
+ error?: unknown;
189
+ }
190
+ interface UsageTapClientOptions {
191
+ apiKey: string;
192
+ baseUrl: string;
193
+ defaultFeature?: string;
194
+ defaultTags?: string[];
195
+ fetchImpl?: typeof fetch;
196
+ headers?: Record<string, string>;
197
+ retries?: RetryOptions;
198
+ idempotencyGenerator?: () => string;
199
+ /**
200
+ * When true (default), the client auto-generates an idempotency key when one is not provided.
201
+ * Set to false to rely on the backend's deterministic fallback.
202
+ */
203
+ autoIdempotency?: boolean;
204
+ onLog?: (entry: UsageTapLogEntry) => void;
205
+ /**
206
+ * Override the default Authorization header with x-api-key when true.
207
+ */
208
+ useApiKeyHeader?: boolean;
209
+ /**
210
+ * Allow constructing the client in browser-like environments for testing.
211
+ */
212
+ allowBrowser?: boolean;
213
+ }
214
+ interface RequestOptions {
215
+ signal?: AbortSignal;
216
+ headers?: Record<string, string>;
217
+ retries?: RetryOptions;
218
+ }
219
+ interface BeginCallOptions extends RequestOptions {
220
+ correlationId?: string;
221
+ }
222
+ interface EndCallOptions extends RequestOptions {
223
+ correlationId?: string;
224
+ }
225
+ interface WithUsageContext {
226
+ begin: UsageTapSuccessResponse<BeginCallResponseBody>;
227
+ setUsage: (usage: Partial<Omit<EndCallRequest, "callId" | "error">>) => void;
228
+ setError: (error: EndCallRequest["error"]) => void;
229
+ }
230
+ interface WithUsageOptions {
231
+ signal?: AbortSignal;
232
+ headers?: Record<string, string>;
233
+ retries?: RetryOptions;
234
+ correlationId?: string;
235
+ /**
236
+ * Default error payload applied when the handler throws and setError was never invoked.
237
+ */
238
+ defaultErrorCode?: string;
239
+ }
240
+
241
+ declare class UsageTapClient {
242
+ private readonly apiKey;
243
+ private readonly baseUrl;
244
+ private readonly fetchImpl;
245
+ private readonly defaultFeature?;
246
+ private readonly defaultTags?;
247
+ private readonly defaultHeaders;
248
+ private readonly retryDefaults;
249
+ private readonly idempotencyGenerator;
250
+ private readonly logFn?;
251
+ private readonly authHeader;
252
+ private readonly autoIdempotency;
253
+ constructor(options: UsageTapClientOptions);
254
+ beginCall(request: BeginCallRequest, options?: BeginCallOptions): Promise<UsageTapSuccessResponse<BeginCallResponseBody>>;
255
+ endCall(request: EndCallRequest, options?: EndCallOptions): Promise<UsageTapSuccessResponse<EndCallResponseBody>>;
256
+ withUsage<T>(beginRequest: BeginCallRequest, handler: (context: WithUsageContext) => Promise<T>, options?: WithUsageOptions): Promise<T>;
257
+ private request;
258
+ private performFetch;
259
+ private composeHeaders;
260
+ private log;
261
+ private mergeTags;
262
+ private shouldRetry;
263
+ private toHttpError;
264
+ private toApiError;
265
+ }
266
+
267
+ interface OpenAIAdapterInit {
268
+ client: OpenAI;
269
+ usageTap: UsageTapClient;
270
+ }
271
+ interface OpenAIRequestContext {
272
+ hints?: VendorHints;
273
+ begin: UsageTapSuccessResponse<BeginCallResponseBody>;
274
+ }
275
+ interface OpenAIInvokeParams<TResponse> {
276
+ begin: BeginCallRequest;
277
+ call: (client: OpenAI, ctx: OpenAIRequestContext) => Promise<TResponse>;
278
+ extractUsage?: (response: TResponse) => Partial<Omit<EndCallRequest, "callId" | "error">> | void;
279
+ withUsageOptions?: WithUsageOptions;
280
+ }
281
+ interface OpenAIInvokeResult<TResponse> {
282
+ data: TResponse;
283
+ begin: UsageTapSuccessResponse<BeginCallResponseBody>;
284
+ }
285
+ interface OpenAIStreamCallResult<TStream> {
286
+ stream: AsyncIterable<TStream>;
287
+ onComplete?: () => Promise<Partial<Omit<EndCallRequest, "callId" | "error">> | void> | Partial<Omit<EndCallRequest, "callId" | "error">> | void;
288
+ }
289
+ interface OpenAIStreamParams<TStream> {
290
+ begin: BeginCallRequest;
291
+ call: (client: OpenAI, ctx: OpenAIRequestContext) => Promise<OpenAIStreamCallResult<TStream>>;
292
+ withUsageOptions?: WithUsageOptions;
293
+ }
294
+ interface OpenAIStreamResult<TStream> {
295
+ stream: AsyncIterable<TStream> & {
296
+ __usageTapFinalize?: () => Promise<void>;
297
+ };
298
+ begin: UsageTapSuccessResponse<BeginCallResponseBody>;
299
+ finalize: () => Promise<void>;
300
+ }
301
+ interface OpenAIAdapter {
302
+ invoke<TResponse>(params: OpenAIInvokeParams<TResponse>): Promise<OpenAIInvokeResult<TResponse>>;
303
+ invokeStream<TStream>(params: OpenAIStreamParams<TStream>): Promise<OpenAIStreamResult<TStream>>;
304
+ }
305
+ type ReplaceProperty<T, K extends keyof T, V> = Omit<T, K> & Record<K, V>;
306
+ type WrapOpenAIContext = BeginCallRequest;
307
+ interface WrapOpenAIOptions {
308
+ defaultContext?: Partial<WrapOpenAIContext>;
309
+ applyVendorHints?: boolean;
310
+ }
311
+ type ChatCompletionsResource = OpenAI["chat"]["completions"];
312
+ type ChatCompletionCreate = ChatCompletionsResource["create"];
313
+ type ChatCompletionCreateParams = Parameters<ChatCompletionCreate>[0];
314
+ type ChatCompletionCreateOptions = Parameters<ChatCompletionCreate>[1];
315
+ type ChatCompletionCreateReturn = ReturnType<ChatCompletionCreate>;
316
+ type WrapOpenAICallOptions = (ChatCompletionCreateOptions extends undefined ? {
317
+ usageTap?: Partial<WrapOpenAIContext>;
318
+ withUsage?: WithUsageOptions;
319
+ } : ChatCompletionCreateOptions & {
320
+ usageTap?: Partial<WrapOpenAIContext>;
321
+ withUsage?: WithUsageOptions;
322
+ });
323
+ interface WrappedChatCompletions extends Omit<ChatCompletionsResource, "create"> {
324
+ create: (params: ChatCompletionCreateParams, options?: WrapOpenAICallOptions) => ChatCompletionCreateReturn;
325
+ }
326
+ type ResponsesResource = OpenAI extends {
327
+ responses: infer R;
328
+ } ? R : never;
329
+ type ResponsesCreate = ResponsesResource extends {
330
+ create: infer T;
331
+ } ? T : never;
332
+ type ResponsesCreateParams = ResponsesCreate extends (...args: infer P) => unknown ? P[0] : never;
333
+ type ResponsesCreateOptions = ResponsesCreate extends (...args: infer P) => unknown ? P[1] : never;
334
+ type ResponsesCreateReturn = ResponsesCreate extends (...args: unknown[]) => infer R ? R : never;
335
+ type WrapOpenAIResponseCallOptions = (ResponsesCreateOptions extends undefined ? {
336
+ usageTap?: Partial<WrapOpenAIContext>;
337
+ withUsage?: WithUsageOptions;
338
+ } : ResponsesCreateOptions & {
339
+ usageTap?: Partial<WrapOpenAIContext>;
340
+ withUsage?: WithUsageOptions;
341
+ });
342
+ type WrappedResponses = ResponsesResource extends undefined ? undefined : Omit<NonNullable<ResponsesResource>, "create"> & {
343
+ create: (params: ResponsesCreateParams, options?: WrapOpenAIResponseCallOptions) => ResponsesCreateReturn;
344
+ };
345
+ type WrappedOpenAI = OpenAI & {
346
+ chat: ReplaceProperty<OpenAI["chat"], "completions", WrappedChatCompletions>;
347
+ } & (ResponsesResource extends undefined ? {
348
+ responses?: undefined;
349
+ } : {
350
+ responses: WrappedResponses;
351
+ }) & {
352
+ toNextResponse: typeof toNextResponse;
353
+ pipeToResponse: typeof pipeToResponse;
354
+ unwrap: () => OpenAI;
355
+ };
356
+ interface StreamOpenAIRouteOptions {
357
+ getRequest: (req: Request) => Promise<{
358
+ params: ChatCompletionCreateParams;
359
+ usageTap?: Partial<WrapOpenAIContext>;
360
+ withUsage?: WithUsageOptions;
361
+ }>;
362
+ wrapOptions?: WrapOpenAIOptions;
363
+ defaultContext?: Partial<WrapOpenAIContext>;
364
+ stream?: {
365
+ mode?: StreamMode;
366
+ headers?: Record<string, string>;
367
+ responseInit?: ResponseInit;
368
+ };
369
+ }
370
+ type StreamMode = "text" | "sse";
371
+ interface StreamToResponseOptions {
372
+ mode?: StreamMode;
373
+ headers?: Record<string, string>;
374
+ contentType?: string;
375
+ sse?: {
376
+ event?: string;
377
+ retry?: number;
378
+ };
379
+ }
380
+ declare function createOpenAIAdapter(init: OpenAIAdapterInit): OpenAIAdapter;
381
+ type UsageTapStream<T> = AsyncIterable<T> & {
382
+ __usageTapFinalize?: () => Promise<void>;
383
+ };
384
+ declare function toNextResponse<T>(stream: UsageTapStream<T>, options?: StreamToResponseOptions): Response;
385
+ declare function pipeToResponse<T>(stream: UsageTapStream<T>, res: NodeResponseLike, options?: StreamToResponseOptions): Promise<void>;
386
+ declare function wrapOpenAI(client: OpenAI, usageTap: UsageTapClient, options?: WrapOpenAIOptions): WrappedOpenAI;
387
+ declare function streamOpenAIRoute(usageTap: UsageTapClient, openai: OpenAI, options: StreamOpenAIRouteOptions): (req: Request) => Promise<Response>;
388
+ interface NodeResponseLike {
389
+ write(chunk: string | Uint8Array | Buffer): unknown;
390
+ end(chunk?: string | Uint8Array | Buffer): unknown;
391
+ setHeader?(name: string, value: string): void;
392
+ headersSent?: boolean;
393
+ statusCode?: number;
394
+ status?(code: number): void;
395
+ flush?(): void;
396
+ }
397
+
398
+ export { type AllowedEntitlements as A, type BeginCallRequest as B, type UsageTapClientOptions as C, type UsageTapSuccessResponse as D, type EndCallOptions as E, type UsageTapErrorResponse as F, type UsageTapResultEnvelope as G, type UsageTapResultStatus as H, type UsageTapLogEntry as I, type WithUsageContext as J, type WithUsageOptions as K, type LimitType as L, type MeterSummary as M, type NodeResponseLike as N, type OpenAIAdapter as O, type PlanSummary as P, type SubscriptionSnapshot as Q, type RemainingRatios as R, type StreamMode as S, type ModelHints as T, UsageTapClient as U, type VendorHints as V, type WrapOpenAIContext as W, type IdempotencyMetadata as X, type OpenAIRequestContext as Y, type OpenAIStreamCallResult as Z, type UsageTapStream as _, type WrapOpenAIOptions as a, type OpenAIAdapterInit as b, createOpenAIAdapter as c, type OpenAIInvokeParams as d, type OpenAIInvokeResult as e, type OpenAIStreamParams as f, type OpenAIStreamResult as g, type StreamToResponseOptions as h, type WrapOpenAICallOptions as i, type WrapOpenAIResponseCallOptions as j, type WrappedOpenAI as k, type StreamOpenAIRouteOptions as l, type BeginCallOptions as m, type BeginCallResponseBody as n, type EndCallRequest as o, pipeToResponse as p, type EndCallResponseBody as q, type BalanceSummary as r, streamOpenAIRoute as s, toNextResponse as t, type EntitlementHints as u, type MeterSnapshot as v, wrapOpenAI as w, type MeteredUsage as x, type RequestedEntitlements as y, type RetryOptions as z };