@scrawn/core 0.0.6 → 0.0.7

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.
@@ -7,7 +7,7 @@
7
7
  * @example
8
8
  * ```typescript
9
9
  * try {
10
- * await scrawn.sdkCallEventConsumer({ ... });
10
+ * await scrawn.basicUsageEventConsumer({ ... });
11
11
  * } catch (error) {
12
12
  * if (error instanceof ScrawnAuthenticationError) {
13
13
  * console.error('Auth failed:', error.message);
@@ -7,7 +7,7 @@
7
7
  * @example
8
8
  * ```typescript
9
9
  * try {
10
- * await scrawn.sdkCallEventConsumer({ ... });
10
+ * await scrawn.basicUsageEventConsumer({ ... });
11
11
  * } catch (error) {
12
12
  * if (error instanceof ScrawnAuthenticationError) {
13
13
  * console.error('Auth failed:', error.message);
@@ -15,7 +15,7 @@
15
15
  * const exprString = serializeExpr(expr);
16
16
  *
17
17
  * // Use in event payload
18
- * await scrawn.sdkCallEventConsumer({
18
+ * await scrawn.basicUsageEventConsumer({
19
19
  * userId: 'u123',
20
20
  * debitExpr: expr
21
21
  * });
@@ -15,7 +15,7 @@
15
15
  * const exprString = serializeExpr(expr);
16
16
  *
17
17
  * // Use in event payload
18
- * await scrawn.sdkCallEventConsumer({
18
+ * await scrawn.basicUsageEventConsumer({
19
19
  * userId: 'u123',
20
20
  * debitExpr: expr
21
21
  * });
@@ -22,8 +22,8 @@ import type { StreamEventResponse } from "../gen/event/v1/event_pb.js";
22
22
  * });
23
23
  *
24
24
  * // Tags are compile-time checked
25
- * biller.sdkCallEventConsumer({ userId: 'u123', debitTag: 'PREMIUM_FEATURE' });
26
- * // biller.sdkCallEventConsumer({ userId: 'u123', debitTag: 'UNKNOWN' }); // Type error!
25
+ * biller.basicUsageEventConsumer({ userId: 'u123', debitTag: 'PREMIUM_FEATURE' });
26
+ * // biller.basicUsageEventConsumer({ userId: 'u123', debitTag: 'UNKNOWN' }); // Type error!
27
27
  * ```
28
28
  */
29
29
  export declare class Scrawn<TTags extends string = string, TExprs extends string = string> {
@@ -38,10 +38,14 @@ export declare class Scrawn<TTags extends string = string, TExprs extends string
38
38
  private apiKey;
39
39
  /** gRPC client for making type-safe API calls */
40
40
  private grpcClient;
41
+ /** Number of auto-retry attempts on retryable errors before falling back to onError */
42
+ private retryCount;
41
43
  /** Public access to the gRPC client for use by other packages (e.g. @scrawn/analytics) */
42
44
  get grpc(): GrpcClient;
43
45
  /** API key used for authorizing gRPC calls */
44
46
  get apikey(): string;
47
+ private sleep;
48
+ private backoffMs;
45
49
  private notifyEventConsumerError;
46
50
  private notifyValidationError;
47
51
  /**
@@ -65,6 +69,7 @@ export declare class Scrawn<TTags extends string = string, TExprs extends string
65
69
  baseURL: string;
66
70
  secure?: boolean;
67
71
  credentials?: import("@grpc/grpc-js").ChannelCredentials;
72
+ retryCount?: number;
68
73
  });
69
74
  private parseURLToTarget;
70
75
  /**
@@ -99,13 +104,13 @@ export declare class Scrawn<TTags extends string = string, TExprs extends string
99
104
  * @example
100
105
  * ```typescript
101
106
  * // Reference a persisted expression
102
- * biller.sdkCallEventConsumer({
107
+ * biller.basicUsageEventConsumer({
103
108
  * userId: 'u123',
104
109
  * debitExpr: biller.expr("MY_EXPR"),
105
110
  * });
106
111
  *
107
112
  * // Inline expression passthrough
108
- * biller.sdkCallEventConsumer({
113
+ * biller.basicUsageEventConsumer({
109
114
  * userId: 'u123',
110
115
  * debitExpr: biller.expr(mul(biller.tag("PREMIUM_CALL"), 3)),
111
116
  * });
@@ -147,17 +152,19 @@ export declare class Scrawn<TTags extends string = string, TExprs extends string
147
152
  */
148
153
  private getCredsFor;
149
154
  /**
150
- * Track an SDK call event.
155
+ * Track a basic usage event.
151
156
  *
152
- * Records SDK usage to the Scrawn backend for billing tracking.
157
+ * Records basic usage to the Scrawn backend for billing tracking.
153
158
  * The event is authenticated using the API key provided during SDK initialization.
154
159
  *
155
- * @param payload - The SDK call data to track
160
+ * @param payload - The usage data to track
156
161
  * @param payload.userId - Unique identifier of the user making the call
157
162
  * @param payload.debitAmount - (Optional) Direct amount in cents to debit from the user's account
158
163
  * @param payload.debitTag - (Optional) Named price tag for backend-managed pricing
159
164
  * @param payload.debitExpr - (Optional) Pricing expression for complex calculations
165
+ * @param payload.metadata - (Optional) Arbitrary metadata to associate with the event
160
166
  * @param options - Optional configuration
167
+ * @param options.eventId - (Optional) Override the auto-generated event ID
161
168
  * @param options.onError - Optional callback for handling validation or gRPC errors
162
169
  * @returns A promise that resolves when the event is tracked or returns early on error
163
170
  *
@@ -166,25 +173,26 @@ export declare class Scrawn<TTags extends string = string, TExprs extends string
166
173
  * import { add, mul, tag } from '@scrawn/core';
167
174
  *
168
175
  * // Using direct amount (500 cents = $5.00)
169
- * await scrawn.sdkCallEventConsumer({
176
+ * await scrawn.basicUsageEventConsumer({
170
177
  * userId: 'user_abc123',
171
178
  * debitAmount: 500
172
179
  * });
173
180
  *
174
181
  * // Using price tag
175
- * await scrawn.sdkCallEventConsumer({
182
+ * await scrawn.basicUsageEventConsumer({
176
183
  * userId: 'user_abc123',
177
184
  * debitTag: 'PREMIUM_FEATURE'
178
185
  * });
179
186
  *
180
187
  * // Using pricing expression: (PREMIUM_CALL * 3) + EXTRA_FEE + 250 cents
181
- * await scrawn.sdkCallEventConsumer({
188
+ * await scrawn.basicUsageEventConsumer({
182
189
  * userId: 'user_abc123',
183
190
  * debitExpr: add(mul(tag('PREMIUM_CALL'), 3), tag('EXTRA_FEE'), 250)
184
191
  * });
185
192
  * ```
186
193
  */
187
- sdkCallEventConsumer(payload: EventPayload<TTags>, options?: {
194
+ basicUsageEventConsumer(payload: EventPayload<TTags>, options?: {
195
+ eventId?: string;
188
196
  onError?: EventConsumerErrorCallback;
189
197
  }): Promise<void>;
190
198
  /**
@@ -271,6 +279,8 @@ export declare class Scrawn<TTags extends string = string, TExprs extends string
271
279
  * @param payload - Event payload data
272
280
  * @param authMethodName - Name of the auth method to use (must be in AuthRegistry)
273
281
  * @param eventType - Type of event for categorization (RAW or MIDDLEWARE_CALL)
282
+ * @param eventId - Stable event ID (generated by caller, reused across retries)
283
+ * @param idempotencyKey - Stable idempotency key (generated by caller, reused across retries)
274
284
  * @returns A promise that resolves when the event is processed
275
285
  * @throws Error if auth method is not registered or gRPC call fails
276
286
  *
@@ -361,6 +371,12 @@ export interface ScrawnInitConfig {
361
371
  credentials?: import("@grpc/grpc-js").ChannelCredentials;
362
372
  tags?: readonly string[];
363
373
  expressions?: readonly string[];
374
+ /**
375
+ * Number of automatic retry attempts on transient network errors
376
+ * (UNAVAILABLE, DEADLINE_EXCEEDED). Defaults to 2. Set to 0 to disable.
377
+ * Each event also gets a manual `.retry()` context in the onError callback.
378
+ */
379
+ retryCount?: number;
364
380
  }
365
381
  /**
366
382
  * Create a type-safe Scrawn billing instance.
@@ -380,11 +396,11 @@ export interface ScrawnInitConfig {
380
396
  * expressions: ["MY_EXPR"] as const,
381
397
  * });
382
398
  *
383
- * biller.sdkCallEventConsumer({
399
+ * biller.basicUsageEventConsumer({
384
400
  * userId: 'u123',
385
401
  * debitExpr: biller.expr("MY_EXPR"), // persisted expression
386
402
  * });
387
- * biller.sdkCallEventConsumer({
403
+ * biller.basicUsageEventConsumer({
388
404
  * userId: 'u123',
389
405
  * debitExpr: mul(biller.tag("PREMIUM_CALL"), 3), // inline
390
406
  * });
@@ -1 +1 @@
1
- {"version":3,"file":"scrawn.d.ts","sourceRoot":"","sources":["../../src/core/scrawn.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scrawn.d.ts","sourceRoot":"","sources":["../../src/core/scrawn.ts"],"names":[],"mappings":"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"}
@@ -5,12 +5,13 @@ import { forkAsyncIterable } from "../utils/forkAsyncIterable.js";
5
5
  import { EventPayloadSchema, AITokenUsagePayloadSchema, } from "./types/event.js";
6
6
  import { GrpcClient } from "./grpc/index.js";
7
7
  import { EventServiceClient } from "../gen/event/v1/event_grpc_pb.js";
8
- import { RegisterEventRequest, StreamEventRequest, EventType, SDKCallType, SDKCall, AITokenUsage, } from "../gen/event/v1/event_pb.js";
8
+ import { RegisterEventRequest, StreamEventRequest, EventType, BasicUsageType, BasicUsage, AITokenUsage, } from "../gen/event/v1/event_pb.js";
9
9
  import { PaymentServiceClient } from "../gen/payment/v1/payment_grpc_pb.js";
10
10
  import { CreateCheckoutLinkRequest, } from "../gen/payment/v1/payment_pb.js";
11
- import { ScrawnConfigError, ScrawnValidationError, convertGrpcError, isScrawnError, } from "./errors/index.js";
11
+ import { ScrawnConfigError, ScrawnValidationError, convertGrpcError, isScrawnError, isRetryableError, } from "./errors/index.js";
12
12
  import { serializeExpr, resolveTokens, prettyPrintExpr, tag as _tag } from "./pricing/index.js";
13
13
  import { ScrawnConfig } from "../config.js";
14
+ import { randomUUID } from "node:crypto";
14
15
  const log = new ScrawnLogger("Scrawn");
15
16
  /**
16
17
  * Main SDK class for Scrawn billing infrastructure.
@@ -31,8 +32,8 @@ const log = new ScrawnLogger("Scrawn");
31
32
  * });
32
33
  *
33
34
  * // Tags are compile-time checked
34
- * biller.sdkCallEventConsumer({ userId: 'u123', debitTag: 'PREMIUM_FEATURE' });
35
- * // biller.sdkCallEventConsumer({ userId: 'u123', debitTag: 'UNKNOWN' }); // Type error!
35
+ * biller.basicUsageEventConsumer({ userId: 'u123', debitTag: 'PREMIUM_FEATURE' });
36
+ * // biller.basicUsageEventConsumer({ userId: 'u123', debitTag: 'UNKNOWN' }); // Type error!
36
37
  * ```
37
38
  */
38
39
  export class Scrawn {
@@ -44,6 +45,12 @@ export class Scrawn {
44
45
  get apikey() {
45
46
  return this.apiKey;
46
47
  }
48
+ sleep(ms) {
49
+ return new Promise((resolve) => setTimeout(resolve, ms));
50
+ }
51
+ backoffMs(attempt) {
52
+ return Math.min(1000 * Math.pow(2, attempt), 8000);
53
+ }
47
54
  notifyEventConsumerError(error, onError) {
48
55
  const converted = isScrawnError(error) ? error : convertGrpcError(error);
49
56
  onError?.(converted);
@@ -90,6 +97,7 @@ export class Scrawn {
90
97
  });
91
98
  }
92
99
  this.apiKey = config.apiKey;
100
+ this.retryCount = config.retryCount ?? 2;
93
101
  this.grpcClient = new GrpcClient(this.parseURLToTarget(config.baseURL), { secure: config.secure ?? true, credentials: config.credentials });
94
102
  this.registerAuthMethod("api", new ApiKeyAuth(this.apiKey));
95
103
  }
@@ -183,17 +191,19 @@ export class Scrawn {
183
191
  return creds;
184
192
  }
185
193
  /**
186
- * Track an SDK call event.
194
+ * Track a basic usage event.
187
195
  *
188
- * Records SDK usage to the Scrawn backend for billing tracking.
196
+ * Records basic usage to the Scrawn backend for billing tracking.
189
197
  * The event is authenticated using the API key provided during SDK initialization.
190
198
  *
191
- * @param payload - The SDK call data to track
199
+ * @param payload - The usage data to track
192
200
  * @param payload.userId - Unique identifier of the user making the call
193
201
  * @param payload.debitAmount - (Optional) Direct amount in cents to debit from the user's account
194
202
  * @param payload.debitTag - (Optional) Named price tag for backend-managed pricing
195
203
  * @param payload.debitExpr - (Optional) Pricing expression for complex calculations
204
+ * @param payload.metadata - (Optional) Arbitrary metadata to associate with the event
196
205
  * @param options - Optional configuration
206
+ * @param options.eventId - (Optional) Override the auto-generated event ID
197
207
  * @param options.onError - Optional callback for handling validation or gRPC errors
198
208
  * @returns A promise that resolves when the event is tracked or returns early on error
199
209
  *
@@ -202,37 +212,38 @@ export class Scrawn {
202
212
  * import { add, mul, tag } from '@scrawn/core';
203
213
  *
204
214
  * // Using direct amount (500 cents = $5.00)
205
- * await scrawn.sdkCallEventConsumer({
215
+ * await scrawn.basicUsageEventConsumer({
206
216
  * userId: 'user_abc123',
207
217
  * debitAmount: 500
208
218
  * });
209
219
  *
210
220
  * // Using price tag
211
- * await scrawn.sdkCallEventConsumer({
221
+ * await scrawn.basicUsageEventConsumer({
212
222
  * userId: 'user_abc123',
213
223
  * debitTag: 'PREMIUM_FEATURE'
214
224
  * });
215
225
  *
216
226
  * // Using pricing expression: (PREMIUM_CALL * 3) + EXTRA_FEE + 250 cents
217
- * await scrawn.sdkCallEventConsumer({
227
+ * await scrawn.basicUsageEventConsumer({
218
228
  * userId: 'user_abc123',
219
229
  * debitExpr: add(mul(tag('PREMIUM_CALL'), 3), tag('EXTRA_FEE'), 250)
220
230
  * });
221
231
  * ```
222
232
  */
223
- async sdkCallEventConsumer(payload, options) {
233
+ async basicUsageEventConsumer(payload, options) {
224
234
  const rawPayload = {
225
235
  userId: payload.userId,
226
236
  debitAmount: payload.debitAmount,
227
237
  debitTag: payload.debitTag,
228
238
  debitExpr: payload.debitExpr?._expr,
239
+ metadata: payload.metadata,
229
240
  };
230
241
  const validationResult = EventPayloadSchema.safeParse(rawPayload);
231
242
  if (!validationResult.success) {
232
243
  const errors = validationResult.error.issues
233
244
  .map((e) => `${e.path.join(".")}: ${e.message}`)
234
245
  .join(", ");
235
- log.error(`Invalid payload for sdkCallEventConsumer: ${errors}`);
246
+ log.error(`Invalid payload for basicUsageEventConsumer: ${errors}`);
236
247
  const error = new ScrawnValidationError("Payload validation failed", {
237
248
  details: {
238
249
  errors: validationResult.error.issues.map((e) => ({
@@ -244,12 +255,34 @@ export class Scrawn {
244
255
  this.notifyValidationError(error, options?.onError);
245
256
  return;
246
257
  }
258
+ // Fixed identity for this event — survives retries
259
+ const eventId = options?.eventId ?? randomUUID();
260
+ const idempotencyKey = randomUUID();
261
+ const attempt = () => this.consumeEvent(validationResult.data, "api", "RAW", eventId, idempotencyKey);
247
262
  try {
248
- await this.consumeEvent(validationResult.data, "api", "SDK_CALL");
263
+ await attempt();
249
264
  }
250
265
  catch (error) {
251
- log.error(`Failed to track sdkCallEventConsumer event: ${error instanceof Error ? error.message : "Unknown error"}`);
252
- this.notifyEventConsumerError(error, options?.onError);
266
+ log.error(`Failed to track basicUsageEventConsumer event: ${error instanceof Error ? error.message : "Unknown error"}`);
267
+ if (options?.onError) {
268
+ const converted = isScrawnError(error)
269
+ ? error
270
+ : convertGrpcError(error);
271
+ const retryContext = {
272
+ retry: async () => {
273
+ try {
274
+ await attempt();
275
+ }
276
+ catch (retryError) {
277
+ const convertedRetry = isScrawnError(retryError)
278
+ ? retryError
279
+ : convertGrpcError(retryError);
280
+ options.onError(convertedRetry, retryContext);
281
+ }
282
+ },
283
+ };
284
+ options.onError(converted, retryContext);
285
+ }
253
286
  return;
254
287
  }
255
288
  }
@@ -335,6 +368,7 @@ export class Scrawn {
335
368
  debitAmount: extractedPayload.debitAmount,
336
369
  debitTag: extractedPayload.debitTag,
337
370
  debitExpr: extractedPayload.debitExpr?._expr,
371
+ metadata: extractedPayload.metadata,
338
372
  };
339
373
  const validationResult = EventPayloadSchema.safeParse(rawPayload);
340
374
  if (!validationResult.success) {
@@ -353,7 +387,9 @@ export class Scrawn {
353
387
  this.notifyValidationError(error, config.onError);
354
388
  return next();
355
389
  }
356
- this.consumeEvent(validationResult.data, "api", "MIDDLEWARE_CALL").catch((error) => {
390
+ const eventId = randomUUID();
391
+ const idempotencyKey = randomUUID();
392
+ this.consumeEvent(validationResult.data, "api", "MIDDLEWARE_CALL", eventId, idempotencyKey).catch((error) => {
357
393
  log.error(`Failed to track middleware event: ${error.message}`);
358
394
  this.notifyEventConsumerError(error, config.onError);
359
395
  });
@@ -422,12 +458,14 @@ export class Scrawn {
422
458
  * @param payload - Event payload data
423
459
  * @param authMethodName - Name of the auth method to use (must be in AuthRegistry)
424
460
  * @param eventType - Type of event for categorization (RAW or MIDDLEWARE_CALL)
461
+ * @param eventId - Stable event ID (generated by caller, reused across retries)
462
+ * @param idempotencyKey - Stable idempotency key (generated by caller, reused across retries)
425
463
  * @returns A promise that resolves when the event is processed
426
464
  * @throws Error if auth method is not registered or gRPC call fails
427
465
  *
428
466
  * @internal
429
467
  */
430
- async consumeEvent(payload, authMethodName, eventType) {
468
+ async consumeEvent(payload, authMethodName, eventType, eventId, idempotencyKey) {
431
469
  const auth = this.authMethods.get(authMethodName);
432
470
  if (!auth) {
433
471
  throw new ScrawnConfigError(`No auth registered for type ${authMethodName}`, {
@@ -439,52 +477,67 @@ export class Scrawn {
439
477
  await auth.preRun();
440
478
  // Get creds (from cache or fresh)
441
479
  const creds = await this.getCredsFor(authMethodName);
442
- // Map event type to SDKCallType
443
- const sdkCallType = eventType === "SDK_CALL" ? SDKCallType.RAW : SDKCallType.MIDDLEWARE_CALL;
444
- try {
445
- log.info(`Ingesting event (type: ${eventType}) with creds: ${JSON.stringify(creds)}, payload: ${JSON.stringify(payload)}`);
446
- // Build debit field based on which debit option is provided
447
- let debitField;
448
- if (payload.debitAmount !== undefined) {
449
- debitField = { case: "amount", value: payload.debitAmount };
450
- }
451
- else if (payload.debitTag !== undefined) {
452
- debitField = { case: "tag", value: payload.debitTag };
453
- }
454
- else {
455
- // debitExpr is defined (validated by schema)
456
- const serialized = serializeExpr(payload.debitExpr);
457
- log.debug(`Serialized pricing expression: ${serialized}\n${prettyPrintExpr(payload.debitExpr)}`);
458
- debitField = {
459
- case: "expr",
460
- value: serialized,
461
- };
462
- }
463
- const sdkCall = new SDKCall();
464
- sdkCall.setSdkcalltype(sdkCallType);
465
- if (debitField.case === "amount") {
466
- sdkCall.setAmount(debitField.value);
467
- }
468
- else if (debitField.case === "tag") {
469
- sdkCall.setTag(debitField.value);
480
+ // Map event type to BasicUsageType
481
+ const basicUsageType = eventType === "RAW" ? BasicUsageType.RAW : BasicUsageType.MIDDLEWARE_CALL;
482
+ // Build debit field based on which debit option is provided
483
+ let debitField;
484
+ if (payload.debitAmount !== undefined) {
485
+ debitField = { case: "amount", value: payload.debitAmount };
486
+ }
487
+ else if (payload.debitTag !== undefined) {
488
+ debitField = { case: "tag", value: payload.debitTag };
489
+ }
490
+ else {
491
+ const serialized = serializeExpr(payload.debitExpr);
492
+ log.debug(`Serialized pricing expression: ${serialized}\n${prettyPrintExpr(payload.debitExpr)}`);
493
+ debitField = {
494
+ case: "expr",
495
+ value: serialized,
496
+ };
497
+ }
498
+ // Retry loop for retryable failures
499
+ for (let attempt = 0;; attempt++) {
500
+ try {
501
+ log.info(`Ingesting event (type: ${eventType}) — attempt ${attempt + 1}`);
502
+ const basicUsage = new BasicUsage();
503
+ basicUsage.setBasicusagetype(basicUsageType);
504
+ if (debitField.case === "amount") {
505
+ basicUsage.setAmount(debitField.value);
506
+ }
507
+ else if (debitField.case === "tag") {
508
+ basicUsage.setTag(debitField.value);
509
+ }
510
+ else {
511
+ basicUsage.setExpr(debitField.value);
512
+ }
513
+ if (payload.metadata) {
514
+ basicUsage.setMetadata(JSON.stringify(payload.metadata));
515
+ }
516
+ const request = new RegisterEventRequest();
517
+ request.setType(EventType.BASIC_USAGE);
518
+ request.setUserid(payload.userId);
519
+ request.setEventid(eventId);
520
+ request.setIdempotencykey(idempotencyKey);
521
+ request.setBasicusage(basicUsage);
522
+ const response = await this.grpcClient
523
+ .newCall(EventServiceClient, "registerEvent")
524
+ .addMetadata("authorization", `Bearer ${creds.apiKey}`)
525
+ .addPayload(request)
526
+ .request();
527
+ log.info(`Event registered successfully: ${JSON.stringify(response)}`);
528
+ break;
470
529
  }
471
- else {
472
- sdkCall.setExpr(debitField.value);
530
+ catch (error) {
531
+ const converted = convertGrpcError(error);
532
+ if (attempt < this.retryCount && isRetryableError(converted)) {
533
+ const delay = this.backoffMs(attempt);
534
+ log.warn(`Retryable error on attempt ${attempt + 1}, retrying in ${delay}ms: ${converted.message}`);
535
+ await this.sleep(delay);
536
+ continue;
537
+ }
538
+ log.error(`Failed to register event: ${converted.message}`);
539
+ throw converted;
473
540
  }
474
- const request = new RegisterEventRequest();
475
- request.setType(EventType.SDK_CALL);
476
- request.setUserid(payload.userId);
477
- request.setSdkcall(sdkCall);
478
- const response = await this.grpcClient
479
- .newCall(EventServiceClient, "registerEvent")
480
- .addMetadata("authorization", `Bearer ${creds.apiKey}`)
481
- .addPayload(request)
482
- .request();
483
- log.info(`Event registered successfully: ${JSON.stringify(response)}`);
484
- }
485
- catch (error) {
486
- log.error(`Failed to register event: ${error instanceof Error ? error.message : "Unknown error"}`);
487
- throw convertGrpcError(error);
488
541
  }
489
542
  if (auth.postRun)
490
543
  await auth.postRun();
@@ -520,8 +573,8 @@ export class Scrawn {
520
573
  * model: 'gpt-4',
521
574
  * inputTokens: 100,
522
575
  * outputTokens: 50,
523
- * inputDebit: { amount: 0.003 },
524
- * outputDebit: { amount: 0.006 }
576
+ * inputDebit: { amount: 1 },
577
+ * outputDebit: { amount: 1 }
525
578
  * };
526
579
  * }
527
580
  *
@@ -617,6 +670,16 @@ export class Scrawn {
617
670
  tag: payload.outputDebit.tag,
618
671
  expr: payload.outputDebit.expr?._expr,
619
672
  },
673
+ metadata: payload.metadata,
674
+ provider: payload.provider,
675
+ inputCacheTokens: payload.inputCacheTokens,
676
+ inputCacheDebit: payload.inputCacheDebit
677
+ ? {
678
+ amount: payload.inputCacheDebit.amount,
679
+ tag: payload.inputCacheDebit.tag,
680
+ expr: payload.inputCacheDebit.expr?._expr,
681
+ }
682
+ : undefined,
620
683
  };
621
684
  // Validate each payload
622
685
  const validationResult = AITokenUsagePayloadSchema.safeParse(rawPayload);
@@ -710,9 +773,38 @@ export class Scrawn {
710
773
  else {
711
774
  aiTokenUsage.setOutputexpr(outputDebit.value);
712
775
  }
776
+ // Set metadata on AITokenUsage if provided
777
+ if (validated.metadata) {
778
+ aiTokenUsage.setMetadata(JSON.stringify(validated.metadata));
779
+ }
780
+ // Set provider if provided
781
+ if (validated.provider) {
782
+ aiTokenUsage.setProvider(validated.provider);
783
+ }
784
+ // Set input cache tokens if provided
785
+ if (validated.inputCacheTokens !== undefined) {
786
+ aiTokenUsage.setInputcachetokens(validated.inputCacheTokens);
787
+ }
788
+ // Set input cache debit if provided
789
+ if (validated.inputCacheDebit) {
790
+ if (validated.inputCacheDebit.amount !== undefined) {
791
+ aiTokenUsage.setInputcacheamount(validated.inputCacheDebit.amount);
792
+ }
793
+ else if (validated.inputCacheDebit.tag !== undefined) {
794
+ aiTokenUsage.setInputcachetag(validated.inputCacheDebit.tag);
795
+ }
796
+ else if (validated.inputCacheDebit.expr) {
797
+ const resolved = resolveTokens(validated.inputCacheDebit.expr, tokenContext);
798
+ aiTokenUsage.setInputcacheexpr(serializeExpr(resolved));
799
+ }
800
+ }
801
+ const eventId = randomUUID();
802
+ const idempotencyKey = randomUUID();
713
803
  const request = new StreamEventRequest();
714
804
  request.setType(EventType.AI_TOKEN_USAGE);
715
805
  request.setUserid(validated.userId);
806
+ request.setEventid(eventId);
807
+ request.setIdempotencykey(idempotencyKey);
716
808
  request.setAitokenusage(aiTokenUsage);
717
809
  yield request;
718
810
  }
@@ -724,6 +816,7 @@ export function createScrawn(config) {
724
816
  baseURL: config.baseURL,
725
817
  secure: config.secure,
726
818
  credentials: config.credentials,
819
+ retryCount: config.retryCount,
727
820
  });
728
821
  }
729
822
  //# sourceMappingURL=scrawn.js.map