@usebillow/sdk 0.8.0 → 0.10.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/{billing-B-VyZCXV.d.cts → billing-8ug1Gw05.d.cts} +14 -10
  3. package/dist/{billing-CgjcWmvb.d.ts → billing-Bs10EcjR.d.ts} +14 -10
  4. package/dist/{billing-status-BZQN_gm7.d.cts → billing-status-DZkB0VPK.d.cts} +5 -5
  5. package/dist/{billing-status-BZQN_gm7.d.ts → billing-status-DZkB0VPK.d.ts} +5 -5
  6. package/dist/{chunk-CCG4F5FK.js → chunk-47J7FYM5.js} +2 -2
  7. package/dist/chunk-47J7FYM5.js.map +1 -0
  8. package/dist/{chunk-J6JIZGWB.js → chunk-XKN7ZRAR.js} +81 -22
  9. package/dist/chunk-XKN7ZRAR.js.map +1 -0
  10. package/dist/config.cjs +3 -3
  11. package/dist/config.cjs.map +1 -1
  12. package/dist/config.d.cts +10 -10
  13. package/dist/config.d.ts +10 -10
  14. package/dist/config.js +3 -3
  15. package/dist/config.js.map +1 -1
  16. package/dist/{hosted-domains-BbuyxnuH.d.cts → hosted-domains-Ci6jeNAe.d.cts} +124 -17
  17. package/dist/{hosted-domains-BP91tg9P.d.ts → hosted-domains-DSs5Iw9U.d.ts} +124 -17
  18. package/dist/index.cjs +79 -20
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +89 -55
  21. package/dist/index.d.ts +89 -55
  22. package/dist/index.js +2 -2
  23. package/dist/ingestion.cjs.map +1 -1
  24. package/dist/ingestion.d.cts +19 -19
  25. package/dist/ingestion.d.ts +19 -19
  26. package/dist/ingestion.js.map +1 -1
  27. package/dist/react.cjs +28 -5
  28. package/dist/react.cjs.map +1 -1
  29. package/dist/react.d.cts +7 -7
  30. package/dist/react.d.ts +7 -7
  31. package/dist/react.js +2 -2
  32. package/dist/react.js.map +1 -1
  33. package/dist/server.cjs.map +1 -1
  34. package/dist/server.d.cts +12 -12
  35. package/dist/server.d.ts +12 -12
  36. package/dist/server.js.map +1 -1
  37. package/dist/status.cjs.map +1 -1
  38. package/dist/status.d.cts +1 -1
  39. package/dist/status.d.ts +1 -1
  40. package/dist/status.js +1 -1
  41. package/dist/webhooks.cjs.map +1 -1
  42. package/dist/webhooks.d.cts +27 -14
  43. package/dist/webhooks.d.ts +27 -14
  44. package/dist/webhooks.js.map +1 -1
  45. package/package.json +1 -1
  46. package/dist/chunk-CCG4F5FK.js.map +0 -1
  47. package/dist/chunk-J6JIZGWB.js.map +0 -1
  48. package/dist/{index.d-CKQAhQfJ.d.ts → index.d-DSEYhV2c.d.cts} +2 -2
  49. package/dist/{index.d-CKQAhQfJ.d.cts → index.d-DSEYhV2c.d.ts} +2 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ingestion.ts"],"names":[],"mappings":"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","file":"ingestion.cjs","sourcesContent":["/**\n * Auto-metering (PRD-16): ingestion strategies that call billow's `track()` for you,\n * so you never hand-instrument usage.\n *\n * Two layers:\n * 1. A generic primitive — {@link createUsageMeter} + {@link meterFunction} — a\n * non-blocking, buffered, idempotent dispatcher you can point at ANY usage\n * source (an S3 upload, a job run, a DB write).\n * 2. LLM strategies built on it — {@link meterOpenAI} (OpenAI-compatible clients)\n * and {@link meterAIResult} (Vercel AI SDK) that auto-count prompt+completion\n * tokens from the provider's own response (never re-tokenizing).\n *\n * Posture (every strategy): SERVER-SIDE ONLY — a browser can't be trusted to\n * self-report usage — and NON-BLOCKING: metering is a side effect that can never\n * delay or break the wrapped call. A failed `track()` buffers and retries under an\n * idempotency key; if it ultimately fails it is dropped (via `onError`), never thrown.\n *\n * import { Billow } from \"@usebillow/sdk\";\n * import { createUsageMeter, meterOpenAI } from \"@usebillow/sdk/ingestion\";\n *\n * const billow = new Billow(process.env.BILLOW_SECRET_KEY!);\n * const meter = createUsageMeter(billow);\n * const openai = meterOpenAI(new OpenAI(), {\n * meter,\n * feature: \"tokens\",\n * customer: (params) => String(params.user), // your billow customer id\n * });\n * // use `openai` exactly as before — every completion auto-meters its tokens.\n */\n\nimport type { Billow } from \"./index\";\n\n/** One usage event handed to `track()`. Mirrors {@link Billow.track}'s input. */\nexport interface UsageRecord {\n customerId: string;\n featureId: string;\n /** Units to record (the engine defaults to 1). */\n value?: number;\n /** Event properties for meter filters / property-based aggregations. */\n properties?: Record<string, string | number>;\n /**\n * Dedup key. Auto-generated per record when omitted, so a retried dispatch is a\n * no-op — set it yourself to dedupe across process restarts.\n */\n idempotencyKey?: string;\n}\n\n/** The minimal client the meter needs — the real {@link Billow} satisfies it. */\nexport type TrackClient = Pick<Billow, \"track\">;\n\nexport interface MeterOptions {\n /** Max dispatch attempts for one record before it's dropped (default 5). */\n maxAttempts?: number;\n /** Base retry backoff in ms; doubles each attempt (default 250). */\n retryBaseMs?: number;\n /** Called once when a record is permanently dropped after exhausting retries. */\n onError?: (error: unknown, record: UsageRecord) => void;\n}\n\n/**\n * A buffered, non-blocking dispatcher to `track()`. `record()` returns immediately\n * and never throws; delivery (with retries under an idempotency key) happens in the\n * background. Call {@link UsageMeter.flush} before a serverless handler returns so\n * pending records aren't lost when the process freezes.\n */\nexport interface UsageMeter {\n /** Enqueue a usage event. Returns immediately, never throws. */\n record(record: UsageRecord): void;\n /**\n * Enqueue a usage event whose record resolves LATER (e.g. a streamed response\n * whose token totals arrive as a promise). Registers the pending work immediately\n * so {@link UsageMeter.flush} awaits it — a `null` resolution or a rejected source\n * is dropped. Returns immediately, never throws.\n */\n recordAsync(source: Promise<UsageRecord | null>): void;\n /** Resolve once every buffered dispatch has settled (delivered or dropped). */\n flush(): Promise<void>;\n /** Records still in flight (queued or retrying). */\n readonly pending: number;\n}\n\nconst DEFAULT_MAX_ATTEMPTS = 5;\nconst DEFAULT_RETRY_BASE_MS = 250;\n\nfunction newIdempotencyKey(): string {\n return crypto.randomUUID();\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Build a {@link UsageMeter} bound to a billow client (or any `{ track }`). */\nexport function createUsageMeter(client: TrackClient, options: MeterOptions = {}): UsageMeter {\n const maxAttempts = Math.max(1, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS);\n const retryBaseMs = Math.max(0, options.retryBaseMs ?? DEFAULT_RETRY_BASE_MS);\n const inFlight = new Set<Promise<void>>();\n\n async function dispatch(record: UsageRecord): Promise<void> {\n // Freeze one idempotency key for the record so every retry is the SAME call.\n const input: UsageRecord = {\n ...record,\n idempotencyKey: record.idempotencyKey ?? newIdempotencyKey(),\n };\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await client.track(input);\n return;\n } catch (error) {\n lastError = error;\n if (attempt < maxAttempts) await sleep(retryBaseMs * 2 ** (attempt - 1));\n }\n }\n try {\n options.onError?.(lastError, input);\n } catch {\n // A throwing onError must not reject the tracked dispatch (which would make\n // flush() reject) — the meter's contract is that it never throws.\n }\n }\n\n /** Register a unit of metering work so `flush()`/`pending` account for it. */\n function track(work: Promise<void>): void {\n const p = work.finally(() => {\n inFlight.delete(p);\n });\n inFlight.add(p);\n }\n\n return {\n record(record) {\n track(dispatch(record));\n },\n recordAsync(source) {\n // Chain resolve-then-dispatch into ONE tracked unit so flush() covers work\n // whose record isn't known yet. A null record or a rejected source is dropped.\n track(\n source.then(\n (record) => (record ? dispatch(record) : undefined),\n () => undefined,\n ),\n );\n },\n flush() {\n return Promise.all([...inFlight]).then(() => undefined);\n },\n get pending() {\n return inFlight.size;\n },\n };\n}\n\n/**\n * Derive a usage record from a completed call. Return `null` to skip metering it\n * (e.g. a cache hit with no billable usage). Receives the resolved result and the\n * original call arguments.\n */\nexport type UsageExtractor<A extends unknown[], R> = (result: R, args: A) => UsageRecord | null;\n\n/**\n * Wrap any async function so each successful call auto-records usage via `meter` —\n * the generic ingestion primitive. Point it at any source by supplying an `extract`.\n * Metering runs AFTER the call resolves and never blocks it; an extractor that\n * throws is swallowed. If the wrapped call itself rejects, nothing is recorded and\n * the error propagates unchanged.\n */\nexport function meterFunction<A extends unknown[], R>(\n fn: (...args: A) => Promise<R>,\n meter: UsageMeter,\n extract: UsageExtractor<A, R>,\n): (...args: A) => Promise<R> {\n return async (...args: A) => {\n const result = await fn(...args);\n try {\n const record = extract(result, args);\n if (record) meter.record(record);\n } catch {\n // Metering must never break the wrapped call — drop extractor errors.\n }\n return result;\n };\n}\n\n/**\n * Yield every value of `source` straight through to the consumer while calling\n * `onValue` for each and `onDone` when iteration finishes (including an early\n * `break`/`return` — it runs in a `finally`). Taps a stream WITHOUT consuming it:\n * the caller still receives every chunk. Tap callbacks that throw are swallowed so\n * they can't break the stream.\n */\nexport async function* tapAsyncIterable<T>(\n source: AsyncIterable<T>,\n taps: { onValue?: (value: T) => void; onDone?: () => void },\n): AsyncGenerator<T> {\n try {\n for await (const value of source) {\n try {\n taps.onValue?.(value);\n } catch {\n // never let a tap break the passthrough\n }\n yield value;\n }\n } finally {\n try {\n taps.onDone?.();\n } catch {\n // never let a tap break the passthrough\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// LLM token metering\n// ---------------------------------------------------------------------------\n\n/** Normalized token counts, whatever the provider's field naming. */\nexport interface TokenCounts {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n}\n\n/** Which token count a meter records as its `value` (default `\"total\"`). */\nexport type TokenCount = \"total\" | \"prompt\" | \"completion\";\n\nfunction firstNumber(...values: unknown[]): number | null {\n for (const v of values) if (typeof v === \"number\" && Number.isFinite(v)) return v;\n return null;\n}\n\n/**\n * Read token counts from a provider usage object, accepting OpenAI (`prompt_tokens`\n * / `completion_tokens` / `total_tokens`), Vercel AI SDK v4 (`promptTokens` / …) and\n * v5 (`inputTokens` / `outputTokens`) shapes. Returns `null` when no token fields\n * are present. `totalTokens` is derived from prompt+completion when not supplied.\n */\nexport function normalizeTokenUsage(usage: unknown): TokenCounts | null {\n if (typeof usage !== \"object\" || usage === null) return null;\n const u = usage as {\n prompt_tokens?: unknown;\n promptTokens?: unknown;\n inputTokens?: unknown;\n completion_tokens?: unknown;\n completionTokens?: unknown;\n outputTokens?: unknown;\n total_tokens?: unknown;\n totalTokens?: unknown;\n };\n const prompt = firstNumber(u.prompt_tokens, u.promptTokens, u.inputTokens);\n const completion = firstNumber(u.completion_tokens, u.completionTokens, u.outputTokens);\n const total = firstNumber(u.total_tokens, u.totalTokens);\n if (prompt === null && completion === null && total === null) return null;\n const promptTokens = prompt ?? 0;\n const completionTokens = completion ?? 0;\n return {\n promptTokens,\n completionTokens,\n totalTokens: total ?? promptTokens + completionTokens,\n };\n}\n\nfunction pickCount(counts: TokenCounts, which: TokenCount): number {\n if (which === \"prompt\") return counts.promptTokens;\n if (which === \"completion\") return counts.completionTokens;\n return counts.totalTokens;\n}\n\n/** A value supplied directly, or resolved from a call's params. */\ntype Resolvable<T, P> = T | ((params: P) => T);\n\nfunction resolveValue<T, P>(r: Resolvable<T, P>, params: P): T {\n return typeof r === \"function\" ? (r as (p: P) => T)(params) : r;\n}\n\nexport interface MeterLLMOptions<P = unknown> {\n /** The buffered meter that dispatches to `track()`. */\n meter: UsageMeter;\n /** The metered feature (token meter) id — static, or resolved per call. */\n feature: Resolvable<string, P>;\n /**\n * The billow customer to bill. Static for single-tenant, or resolved per call\n * (e.g. from the OpenAI `user` param) for multi-tenant. Returning `null`/`undefined`\n * skips metering that call — there's no customer to attribute it to.\n */\n customer: Resolvable<string | null | undefined, P>;\n /** Which token count to record as the meter value (default `\"total\"`). */\n count?: TokenCount;\n /** Extra event properties merged onto each record (`model` is added automatically). */\n properties?: (params: P, tokens: TokenCounts) => Record<string, string | number>;\n}\n\n/**\n * Build the usage record for one LLM call from its usage + model, or `null` to skip.\n * TOTAL by contract — a throwing `customer`/`feature`/`properties` resolver (e.g. an\n * unexpected params shape) drops the record rather than surfacing in the wrapped\n * call, so every caller (sync or async) inherits the non-blocking guarantee here.\n */\nfunction buildTokenRecord<P>(\n options: MeterLLMOptions<P>,\n params: P,\n usage: unknown,\n model: unknown,\n): UsageRecord | null {\n const counts = normalizeTokenUsage(usage);\n if (!counts) return null;\n try {\n const customerId = resolveValue(options.customer, params);\n if (!customerId) return null;\n const properties: Record<string, string | number> = {\n tokens_prompt: counts.promptTokens,\n tokens_completion: counts.completionTokens,\n tokens_total: counts.totalTokens,\n ...(typeof model === \"string\" ? { model } : {}),\n ...options.properties?.(params, counts),\n };\n return {\n customerId,\n featureId: resolveValue(options.feature, params),\n value: pickCount(counts, options.count ?? \"total\"),\n properties,\n };\n } catch {\n return null; // a resolver mistake must never break the wrapped call\n }\n}\n\nfunction isPromiseLike(v: unknown): v is Promise<unknown> {\n return (\n (typeof v === \"object\" || typeof v === \"function\") &&\n v !== null &&\n typeof (v as { then?: unknown }).then === \"function\"\n );\n}\n\n/** The two params slots an OpenAI-style `create(body, requestOptions?)` takes. */\ntype CreateArgs = [params: Record<string, unknown>, ...rest: unknown[]];\n\nfunction wrapOpenAICreate(\n create: (...args: unknown[]) => unknown,\n thisArg: unknown,\n options: MeterLLMOptions<Record<string, unknown>>,\n): (...args: CreateArgs) => unknown {\n return (params, ...rest) => {\n const returned = Reflect.apply(create, thisArg, [params, ...rest]);\n // openai-node returns a thenable (APIPromise) for BOTH streaming and not.\n if (!isPromiseLike(returned)) return returned;\n\n if ((params as { stream?: unknown }).stream === true) {\n // Streaming: TAP iteration to read the usage the final chunk carries (present\n // when the caller sets `stream_options.include_usage`) WITHOUT dropping the\n // OpenAI return contract — the APIPromise keeps its helpers and the resolved\n // Stream keeps its methods; we only override `Symbol.asyncIterator`.\n return proxyResolved(returned, (stream) =>\n typeof stream === \"object\" && stream !== null\n ? proxyAsyncIterable(stream, () =>\n tapOpenAIStream(stream as AsyncIterable<unknown>, params, options),\n )\n : stream,\n );\n }\n // Non-streaming: record from the resolved completion's `.usage`, tracked by the\n // meter so `flush()` awaits it. The caller receives the ORIGINAL promise\n // untouched (APIPromise helpers intact); a failed call resolves to no record.\n options.meter.recordAsync(returned.then((resp) => completionRecord(resp, params, options)));\n return returned;\n };\n}\n\nfunction completionRecord(\n resp: unknown,\n params: Record<string, unknown>,\n options: MeterLLMOptions<Record<string, unknown>>,\n): UsageRecord | null {\n if (typeof resp !== \"object\" || resp === null) return null;\n const r = resp as { usage?: unknown; model?: unknown };\n return buildTokenRecord(options, params, r.usage, r.model);\n}\n\n/**\n * Proxy a thenable so awaiting it yields `map(resolvedValue)`, while every other\n * property/method (an APIPromise's `.withResponse()`/`.asResponse()`, …) passes\n * straight through. Lets us tap the resolved value without dropping the return\n * contract.\n */\nfunction proxyResolved(\n source: Promise<unknown>,\n map: (value: unknown) => unknown,\n): Promise<unknown> {\n const mappedThen = (onF?: (v: unknown) => unknown, onR?: (e: unknown) => unknown) =>\n source.then((v) => (onF ? onF(map(v)) : map(v)), onR);\n return new Proxy(source, {\n get(target, prop) {\n // Route `then`/`catch`/`finally` through the mapping so awaiting via ANY of\n // them yields the tapped value — binding the native methods to `target` would\n // resolve the raw (un-tapped) stream and silently skip metering.\n if (prop === \"then\") return mappedThen;\n if (prop === \"catch\") return (onR: (e: unknown) => unknown) => mappedThen(undefined, onR);\n if (prop === \"finally\") {\n return (cb?: () => void) =>\n mappedThen(\n (v) => {\n cb?.();\n return v;\n },\n (e) => {\n cb?.();\n throw e;\n },\n );\n }\n const value = Reflect.get(target, prop);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n }) as Promise<unknown>;\n}\n\n/**\n * Proxy an async-iterable object so `for await` runs `makeIterator()` (our tap),\n * while every other property/method (a Stream's `.tee()`, `.toReadableStream()`, …)\n * passes straight through. Taps iteration WITHOUT replacing the object.\n */\nfunction proxyAsyncIterable(source: object, makeIterator: () => AsyncIterator<unknown>): object {\n return new Proxy(source, {\n get(target, prop) {\n if (prop === Symbol.asyncIterator) return makeIterator;\n const value = Reflect.get(target, prop);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n}\n\n/** Usage + model folded out of a streamed response, ready for {@link buildTokenRecord}. */\ninterface StreamUsage {\n usage?: unknown;\n model?: unknown;\n}\n\n/**\n * Meter a streamed response: tap the stream (yielding every chunk untouched), fold\n * each chunk into an accumulator via `reduce`, and record ONE usage event when\n * iteration finishes. This is the reusable middle between the generic\n * {@link tapAsyncIterable} and a specific provider's chunk shape — a new streaming\n * source (Anthropic SSE, etc.) plugs in by supplying only its `reduce`.\n */\nfunction meterStream<C, P>(\n stream: AsyncIterable<C>,\n params: P,\n options: MeterLLMOptions<P>,\n reduce: (chunk: C, acc: StreamUsage) => void,\n): AsyncGenerator<C> {\n const acc: StreamUsage = {};\n return tapAsyncIterable(stream, {\n onValue: (chunk) => reduce(chunk, acc),\n onDone: () => {\n const record = buildTokenRecord(options, params, acc.usage, acc.model);\n if (record) options.meter.record(record);\n },\n });\n}\n\nfunction tapOpenAIStream(\n stream: AsyncIterable<unknown>,\n params: Record<string, unknown>,\n options: MeterLLMOptions<Record<string, unknown>>,\n): AsyncGenerator<unknown> {\n return meterStream(stream, params, options, (chunk, acc) => {\n if (typeof chunk !== \"object\" || chunk === null) return;\n const c = chunk as { usage?: unknown; model?: unknown };\n if (c.usage) acc.usage = c.usage; // the final chunk carries usage (include_usage)\n if (c.model && acc.model === undefined) acc.model = c.model;\n });\n}\n\n/**\n * Wrap an OpenAI-compatible client so every `chat.completions.create` call\n * auto-meters its token usage. Returns a proxy of the SAME type — use it exactly as\n * before; every other property/method passes straight through untouched.\n *\n * Reads the token count from the provider's own response (no re-tokenizing), for\n * both blocking and streaming calls. For a STREAMED call, pass\n * `stream_options: { include_usage: true }` so the final chunk carries usage —\n * otherwise a streamed call can't be metered (billow never mutates your request).\n *\n * A streamed response is metered when consumed the standard way: `await` the call,\n * then `for await` the stream. The `Stream` helpers (`.tee()`, `.toReadableStream()`)\n * and the un-awaited `APIPromise` helpers (`.withResponse()`, `.asResponse()`) are\n * PRESERVED (they keep working), but consuming through them bypasses the iteration\n * tap, so those calls are not token-metered.\n *\n * Works with any client exposing `chat.completions.create` returning a `.usage`\n * shape: OpenAI, Azure OpenAI, Together, Groq, OpenRouter, and other compatibles.\n *\n * const openai = meterOpenAI(new OpenAI(), { meter, feature: \"tokens\", customer });\n * const res = await openai.chat.completions.create({ model, messages }); // metered\n */\nexport function meterOpenAI<T extends object>(\n client: T,\n options: MeterLLMOptions<Record<string, unknown>>,\n): T {\n const root = client as { chat?: { completions?: { create?: unknown } } };\n const completions = root.chat?.completions;\n const create = completions?.create;\n if (typeof create !== \"function\" || !completions) {\n throw new TypeError(\"meterOpenAI: expected client.chat.completions.create to be a function\");\n }\n const wrapped = wrapOpenAICreate(create as (...args: unknown[]) => unknown, completions, options);\n return replaceCreate(client, wrapped);\n}\n\n/**\n * Return a non-mutating proxy of `client` that swaps ONLY `chat.completions.create`\n * for `wrapped`, passing every other access straight through. Untouched methods keep\n * their real receiver, so `this` stays correct.\n */\nfunction replaceCreate<T extends object>(client: T, wrapped: (...args: CreateArgs) => unknown): T {\n const root = client as { chat: { completions: object } };\n const completionsProxy = new Proxy(root.chat.completions, {\n get: (t, p, r) => (p === \"create\" ? wrapped : Reflect.get(t, p, r)),\n });\n const chatProxy = new Proxy(root.chat, {\n get: (t, p, r) => (p === \"completions\" ? completionsProxy : Reflect.get(t, p, r)),\n });\n return new Proxy(client, {\n get: (t, p, r) => (p === \"chat\" ? chatProxy : Reflect.get(t, p, r)),\n });\n}\n\n/**\n * Meter a Vercel AI SDK result (the object `generateText`/`streamText` resolves to).\n * Reads `result.usage` — an object (`generateText`) or a Promise (`streamText`) — and\n * records the token count, non-blocking. Never touches `result.textStream`, so a\n * streamed response is delivered to your consumer untouched. Accepts AI SDK v4\n * (`promptTokens`/`completionTokens`) and v5 (`inputTokens`/`outputTokens`) shapes.\n *\n * const result = streamText({ model, prompt });\n * meterAIResult(result, { meter, feature: \"tokens\", customer: customerId });\n * return result.toDataStreamResponse();\n */\nexport function meterAIResult(\n result: { usage?: unknown; response?: unknown },\n options: MeterLLMOptions<void>,\n): void {\n const model = extractAIModel(result.response);\n const usage = result.usage;\n if (isPromiseLike(usage)) {\n // `streamText` resolves usage LATE — hand the meter the whole resolve→record\n // chain so `flush()` (the README's serverless guidance) awaits it instead of\n // snapshotting an empty in-flight set and dropping the usage.\n options.meter.recordAsync(usage.then((u) => buildTokenRecord(options, undefined, u, model)));\n return;\n }\n const record = buildTokenRecord(options, undefined, usage, model);\n if (record) options.meter.record(record);\n}\n\n/** Best-effort model id from an AI SDK result's `response` (skipped if it's a promise). */\nfunction extractAIModel(response: unknown): unknown {\n if (typeof response !== \"object\" || response === null) return undefined;\n const m = (response as { modelId?: unknown }).modelId;\n return typeof m === \"string\" ? m : undefined;\n}\n"]}
1
+ {"version":3,"sources":["../src/ingestion.ts"],"names":[],"mappings":"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","file":"ingestion.cjs","sourcesContent":["/**\n * Auto-metering (PRD-16): ingestion strategies that call billow's `track()` for you,\n * so you never hand-instrument usage.\n *\n * Two layers:\n * 1. A generic primitive - {@link createUsageMeter} + {@link meterFunction} - a\n * non-blocking, buffered, idempotent dispatcher you can point at ANY usage\n * source (an S3 upload, a job run, a DB write).\n * 2. LLM strategies built on it - {@link meterOpenAI} (OpenAI-compatible clients)\n * and {@link meterAIResult} (Vercel AI SDK) that auto-count prompt+completion\n * tokens from the provider's own response (never re-tokenizing).\n *\n * Posture (every strategy): SERVER-SIDE ONLY - a browser can't be trusted to\n * self-report usage - and NON-BLOCKING: metering is a side effect that can never\n * delay or break the wrapped call. A failed `track()` buffers and retries under an\n * idempotency key; if it ultimately fails it is dropped (via `onError`), never thrown.\n *\n * import { Billow } from \"@usebillow/sdk\";\n * import { createUsageMeter, meterOpenAI } from \"@usebillow/sdk/ingestion\";\n *\n * const billow = new Billow(process.env.BILLOW_SECRET_KEY!);\n * const meter = createUsageMeter(billow);\n * const openai = meterOpenAI(new OpenAI(), {\n * meter,\n * feature: \"tokens\",\n * customer: (params) => String(params.user), // your billow customer id\n * });\n * // use `openai` exactly as before - every completion auto-meters its tokens.\n */\n\nimport type { Billow } from \"./index\";\n\n/** One usage event handed to `track()`. Mirrors {@link Billow.track}'s input. */\nexport interface UsageRecord {\n customerId: string;\n featureId: string;\n /** Units to record (the engine defaults to 1). */\n value?: number;\n /** Event properties for meter filters / property-based aggregations. */\n properties?: Record<string, string | number>;\n /**\n * Dedup key. Auto-generated per record when omitted, so a retried dispatch is a\n * no-op - set it yourself to dedupe across process restarts.\n */\n idempotencyKey?: string;\n}\n\n/** The minimal client the meter needs - the real {@link Billow} satisfies it. */\nexport type TrackClient = Pick<Billow, \"track\">;\n\nexport interface MeterOptions {\n /** Max dispatch attempts for one record before it's dropped (default 5). */\n maxAttempts?: number;\n /** Base retry backoff in ms; doubles each attempt (default 250). */\n retryBaseMs?: number;\n /** Called once when a record is permanently dropped after exhausting retries. */\n onError?: (error: unknown, record: UsageRecord) => void;\n}\n\n/**\n * A buffered, non-blocking dispatcher to `track()`. `record()` returns immediately\n * and never throws; delivery (with retries under an idempotency key) happens in the\n * background. Call {@link UsageMeter.flush} before a serverless handler returns so\n * pending records aren't lost when the process freezes.\n */\nexport interface UsageMeter {\n /** Enqueue a usage event. Returns immediately, never throws. */\n record(record: UsageRecord): void;\n /**\n * Enqueue a usage event whose record resolves LATER (e.g. a streamed response\n * whose token totals arrive as a promise). Registers the pending work immediately\n * so {@link UsageMeter.flush} awaits it - a `null` resolution or a rejected source\n * is dropped. Returns immediately, never throws.\n */\n recordAsync(source: Promise<UsageRecord | null>): void;\n /** Resolve once every buffered dispatch has settled (delivered or dropped). */\n flush(): Promise<void>;\n /** Records still in flight (queued or retrying). */\n readonly pending: number;\n}\n\nconst DEFAULT_MAX_ATTEMPTS = 5;\nconst DEFAULT_RETRY_BASE_MS = 250;\n\nfunction newIdempotencyKey(): string {\n return crypto.randomUUID();\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Build a {@link UsageMeter} bound to a billow client (or any `{ track }`). */\nexport function createUsageMeter(client: TrackClient, options: MeterOptions = {}): UsageMeter {\n const maxAttempts = Math.max(1, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS);\n const retryBaseMs = Math.max(0, options.retryBaseMs ?? DEFAULT_RETRY_BASE_MS);\n const inFlight = new Set<Promise<void>>();\n\n async function dispatch(record: UsageRecord): Promise<void> {\n // Freeze one idempotency key for the record so every retry is the SAME call.\n const input: UsageRecord = {\n ...record,\n idempotencyKey: record.idempotencyKey ?? newIdempotencyKey(),\n };\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await client.track(input);\n return;\n } catch (error) {\n lastError = error;\n if (attempt < maxAttempts) await sleep(retryBaseMs * 2 ** (attempt - 1));\n }\n }\n try {\n options.onError?.(lastError, input);\n } catch {\n // A throwing onError must not reject the tracked dispatch (which would make\n // flush() reject) - the meter's contract is that it never throws.\n }\n }\n\n /** Register a unit of metering work so `flush()`/`pending` account for it. */\n function track(work: Promise<void>): void {\n const p = work.finally(() => {\n inFlight.delete(p);\n });\n inFlight.add(p);\n }\n\n return {\n record(record) {\n track(dispatch(record));\n },\n recordAsync(source) {\n // Chain resolve-then-dispatch into ONE tracked unit so flush() covers work\n // whose record isn't known yet. A null record or a rejected source is dropped.\n track(\n source.then(\n (record) => (record ? dispatch(record) : undefined),\n () => undefined,\n ),\n );\n },\n flush() {\n return Promise.all([...inFlight]).then(() => undefined);\n },\n get pending() {\n return inFlight.size;\n },\n };\n}\n\n/**\n * Derive a usage record from a completed call. Return `null` to skip metering it\n * (e.g. a cache hit with no billable usage). Receives the resolved result and the\n * original call arguments.\n */\nexport type UsageExtractor<A extends unknown[], R> = (result: R, args: A) => UsageRecord | null;\n\n/**\n * Wrap any async function so each successful call auto-records usage via `meter` -\n * the generic ingestion primitive. Point it at any source by supplying an `extract`.\n * Metering runs AFTER the call resolves and never blocks it; an extractor that\n * throws is swallowed. If the wrapped call itself rejects, nothing is recorded and\n * the error propagates unchanged.\n */\nexport function meterFunction<A extends unknown[], R>(\n fn: (...args: A) => Promise<R>,\n meter: UsageMeter,\n extract: UsageExtractor<A, R>,\n): (...args: A) => Promise<R> {\n return async (...args: A) => {\n const result = await fn(...args);\n try {\n const record = extract(result, args);\n if (record) meter.record(record);\n } catch {\n // Metering must never break the wrapped call - drop extractor errors.\n }\n return result;\n };\n}\n\n/**\n * Yield every value of `source` straight through to the consumer while calling\n * `onValue` for each and `onDone` when iteration finishes (including an early\n * `break`/`return` - it runs in a `finally`). Taps a stream WITHOUT consuming it:\n * the caller still receives every chunk. Tap callbacks that throw are swallowed so\n * they can't break the stream.\n */\nexport async function* tapAsyncIterable<T>(\n source: AsyncIterable<T>,\n taps: { onValue?: (value: T) => void; onDone?: () => void },\n): AsyncGenerator<T> {\n try {\n for await (const value of source) {\n try {\n taps.onValue?.(value);\n } catch {\n // never let a tap break the passthrough\n }\n yield value;\n }\n } finally {\n try {\n taps.onDone?.();\n } catch {\n // never let a tap break the passthrough\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// LLM token metering\n// ---------------------------------------------------------------------------\n\n/** Normalized token counts, whatever the provider's field naming. */\nexport interface TokenCounts {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n}\n\n/** Which token count a meter records as its `value` (default `\"total\"`). */\nexport type TokenCount = \"total\" | \"prompt\" | \"completion\";\n\nfunction firstNumber(...values: unknown[]): number | null {\n for (const v of values) if (typeof v === \"number\" && Number.isFinite(v)) return v;\n return null;\n}\n\n/**\n * Read token counts from a provider usage object, accepting OpenAI (`prompt_tokens`\n * / `completion_tokens` / `total_tokens`), Vercel AI SDK v4 (`promptTokens` / …) and\n * v5 (`inputTokens` / `outputTokens`) shapes. Returns `null` when no token fields\n * are present. `totalTokens` is derived from prompt+completion when not supplied.\n */\nexport function normalizeTokenUsage(usage: unknown): TokenCounts | null {\n if (typeof usage !== \"object\" || usage === null) return null;\n const u = usage as {\n prompt_tokens?: unknown;\n promptTokens?: unknown;\n inputTokens?: unknown;\n completion_tokens?: unknown;\n completionTokens?: unknown;\n outputTokens?: unknown;\n total_tokens?: unknown;\n totalTokens?: unknown;\n };\n const prompt = firstNumber(u.prompt_tokens, u.promptTokens, u.inputTokens);\n const completion = firstNumber(u.completion_tokens, u.completionTokens, u.outputTokens);\n const total = firstNumber(u.total_tokens, u.totalTokens);\n if (prompt === null && completion === null && total === null) return null;\n const promptTokens = prompt ?? 0;\n const completionTokens = completion ?? 0;\n return {\n promptTokens,\n completionTokens,\n totalTokens: total ?? promptTokens + completionTokens,\n };\n}\n\nfunction pickCount(counts: TokenCounts, which: TokenCount): number {\n if (which === \"prompt\") return counts.promptTokens;\n if (which === \"completion\") return counts.completionTokens;\n return counts.totalTokens;\n}\n\n/** A value supplied directly, or resolved from a call's params. */\ntype Resolvable<T, P> = T | ((params: P) => T);\n\nfunction resolveValue<T, P>(r: Resolvable<T, P>, params: P): T {\n return typeof r === \"function\" ? (r as (p: P) => T)(params) : r;\n}\n\nexport interface MeterLLMOptions<P = unknown> {\n /** The buffered meter that dispatches to `track()`. */\n meter: UsageMeter;\n /** The metered feature (token meter) id - static, or resolved per call. */\n feature: Resolvable<string, P>;\n /**\n * The billow customer to bill. Static for single-tenant, or resolved per call\n * (e.g. from the OpenAI `user` param) for multi-tenant. Returning `null`/`undefined`\n * skips metering that call - there's no customer to attribute it to.\n */\n customer: Resolvable<string | null | undefined, P>;\n /** Which token count to record as the meter value (default `\"total\"`). */\n count?: TokenCount;\n /** Extra event properties merged onto each record (`model` is added automatically). */\n properties?: (params: P, tokens: TokenCounts) => Record<string, string | number>;\n}\n\n/**\n * Build the usage record for one LLM call from its usage + model, or `null` to skip.\n * TOTAL by contract - a throwing `customer`/`feature`/`properties` resolver (e.g. an\n * unexpected params shape) drops the record rather than surfacing in the wrapped\n * call, so every caller (sync or async) inherits the non-blocking guarantee here.\n */\nfunction buildTokenRecord<P>(\n options: MeterLLMOptions<P>,\n params: P,\n usage: unknown,\n model: unknown,\n): UsageRecord | null {\n const counts = normalizeTokenUsage(usage);\n if (!counts) return null;\n try {\n const customerId = resolveValue(options.customer, params);\n if (!customerId) return null;\n const properties: Record<string, string | number> = {\n tokens_prompt: counts.promptTokens,\n tokens_completion: counts.completionTokens,\n tokens_total: counts.totalTokens,\n ...(typeof model === \"string\" ? { model } : {}),\n ...options.properties?.(params, counts),\n };\n return {\n customerId,\n featureId: resolveValue(options.feature, params),\n value: pickCount(counts, options.count ?? \"total\"),\n properties,\n };\n } catch {\n return null; // a resolver mistake must never break the wrapped call\n }\n}\n\nfunction isPromiseLike(v: unknown): v is Promise<unknown> {\n return (\n (typeof v === \"object\" || typeof v === \"function\") &&\n v !== null &&\n typeof (v as { then?: unknown }).then === \"function\"\n );\n}\n\n/** The two params slots an OpenAI-style `create(body, requestOptions?)` takes. */\ntype CreateArgs = [params: Record<string, unknown>, ...rest: unknown[]];\n\nfunction wrapOpenAICreate(\n create: (...args: unknown[]) => unknown,\n thisArg: unknown,\n options: MeterLLMOptions<Record<string, unknown>>,\n): (...args: CreateArgs) => unknown {\n return (params, ...rest) => {\n const returned = Reflect.apply(create, thisArg, [params, ...rest]);\n // openai-node returns a thenable (APIPromise) for BOTH streaming and not.\n if (!isPromiseLike(returned)) return returned;\n\n if ((params as { stream?: unknown }).stream === true) {\n // Streaming: TAP iteration to read the usage the final chunk carries (present\n // when the caller sets `stream_options.include_usage`) WITHOUT dropping the\n // OpenAI return contract - the APIPromise keeps its helpers and the resolved\n // Stream keeps its methods; we only override `Symbol.asyncIterator`.\n return proxyResolved(returned, (stream) =>\n typeof stream === \"object\" && stream !== null\n ? proxyAsyncIterable(stream, () =>\n tapOpenAIStream(stream as AsyncIterable<unknown>, params, options),\n )\n : stream,\n );\n }\n // Non-streaming: record from the resolved completion's `.usage`, tracked by the\n // meter so `flush()` awaits it. The caller receives the ORIGINAL promise\n // untouched (APIPromise helpers intact); a failed call resolves to no record.\n options.meter.recordAsync(returned.then((resp) => completionRecord(resp, params, options)));\n return returned;\n };\n}\n\nfunction completionRecord(\n resp: unknown,\n params: Record<string, unknown>,\n options: MeterLLMOptions<Record<string, unknown>>,\n): UsageRecord | null {\n if (typeof resp !== \"object\" || resp === null) return null;\n const r = resp as { usage?: unknown; model?: unknown };\n return buildTokenRecord(options, params, r.usage, r.model);\n}\n\n/**\n * Proxy a thenable so awaiting it yields `map(resolvedValue)`, while every other\n * property/method (an APIPromise's `.withResponse()`/`.asResponse()`, …) passes\n * straight through. Lets us tap the resolved value without dropping the return\n * contract.\n */\nfunction proxyResolved(\n source: Promise<unknown>,\n map: (value: unknown) => unknown,\n): Promise<unknown> {\n const mappedThen = (onF?: (v: unknown) => unknown, onR?: (e: unknown) => unknown) =>\n source.then((v) => (onF ? onF(map(v)) : map(v)), onR);\n return new Proxy(source, {\n get(target, prop) {\n // Route `then`/`catch`/`finally` through the mapping so awaiting via ANY of\n // them yields the tapped value - binding the native methods to `target` would\n // resolve the raw (un-tapped) stream and silently skip metering.\n if (prop === \"then\") return mappedThen;\n if (prop === \"catch\") return (onR: (e: unknown) => unknown) => mappedThen(undefined, onR);\n if (prop === \"finally\") {\n return (cb?: () => void) =>\n mappedThen(\n (v) => {\n cb?.();\n return v;\n },\n (e) => {\n cb?.();\n throw e;\n },\n );\n }\n const value = Reflect.get(target, prop);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n }) as Promise<unknown>;\n}\n\n/**\n * Proxy an async-iterable object so `for await` runs `makeIterator()` (our tap),\n * while every other property/method (a Stream's `.tee()`, `.toReadableStream()`, …)\n * passes straight through. Taps iteration WITHOUT replacing the object.\n */\nfunction proxyAsyncIterable(source: object, makeIterator: () => AsyncIterator<unknown>): object {\n return new Proxy(source, {\n get(target, prop) {\n if (prop === Symbol.asyncIterator) return makeIterator;\n const value = Reflect.get(target, prop);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n}\n\n/** Usage + model folded out of a streamed response, ready for {@link buildTokenRecord}. */\ninterface StreamUsage {\n usage?: unknown;\n model?: unknown;\n}\n\n/**\n * Meter a streamed response: tap the stream (yielding every chunk untouched), fold\n * each chunk into an accumulator via `reduce`, and record ONE usage event when\n * iteration finishes. This is the reusable middle between the generic\n * {@link tapAsyncIterable} and a specific provider's chunk shape - a new streaming\n * source (Anthropic SSE, etc.) plugs in by supplying only its `reduce`.\n */\nfunction meterStream<C, P>(\n stream: AsyncIterable<C>,\n params: P,\n options: MeterLLMOptions<P>,\n reduce: (chunk: C, acc: StreamUsage) => void,\n): AsyncGenerator<C> {\n const acc: StreamUsage = {};\n return tapAsyncIterable(stream, {\n onValue: (chunk) => reduce(chunk, acc),\n onDone: () => {\n const record = buildTokenRecord(options, params, acc.usage, acc.model);\n if (record) options.meter.record(record);\n },\n });\n}\n\nfunction tapOpenAIStream(\n stream: AsyncIterable<unknown>,\n params: Record<string, unknown>,\n options: MeterLLMOptions<Record<string, unknown>>,\n): AsyncGenerator<unknown> {\n return meterStream(stream, params, options, (chunk, acc) => {\n if (typeof chunk !== \"object\" || chunk === null) return;\n const c = chunk as { usage?: unknown; model?: unknown };\n if (c.usage) acc.usage = c.usage; // the final chunk carries usage (include_usage)\n if (c.model && acc.model === undefined) acc.model = c.model;\n });\n}\n\n/**\n * Wrap an OpenAI-compatible client so every `chat.completions.create` call\n * auto-meters its token usage. Returns a proxy of the SAME type - use it exactly as\n * before; every other property/method passes straight through untouched.\n *\n * Reads the token count from the provider's own response (no re-tokenizing), for\n * both blocking and streaming calls. For a STREAMED call, pass\n * `stream_options: { include_usage: true }` so the final chunk carries usage -\n * otherwise a streamed call can't be metered (billow never mutates your request).\n *\n * A streamed response is metered when consumed the standard way: `await` the call,\n * then `for await` the stream. The `Stream` helpers (`.tee()`, `.toReadableStream()`)\n * and the un-awaited `APIPromise` helpers (`.withResponse()`, `.asResponse()`) are\n * PRESERVED (they keep working), but consuming through them bypasses the iteration\n * tap, so those calls are not token-metered.\n *\n * Works with any client exposing `chat.completions.create` returning a `.usage`\n * shape: OpenAI, Azure OpenAI, Together, Groq, OpenRouter, and other compatibles.\n *\n * const openai = meterOpenAI(new OpenAI(), { meter, feature: \"tokens\", customer });\n * const res = await openai.chat.completions.create({ model, messages }); // metered\n */\nexport function meterOpenAI<T extends object>(\n client: T,\n options: MeterLLMOptions<Record<string, unknown>>,\n): T {\n const root = client as { chat?: { completions?: { create?: unknown } } };\n const completions = root.chat?.completions;\n const create = completions?.create;\n if (typeof create !== \"function\" || !completions) {\n throw new TypeError(\"meterOpenAI: expected client.chat.completions.create to be a function\");\n }\n const wrapped = wrapOpenAICreate(create as (...args: unknown[]) => unknown, completions, options);\n return replaceCreate(client, wrapped);\n}\n\n/**\n * Return a non-mutating proxy of `client` that swaps ONLY `chat.completions.create`\n * for `wrapped`, passing every other access straight through. Untouched methods keep\n * their real receiver, so `this` stays correct.\n */\nfunction replaceCreate<T extends object>(client: T, wrapped: (...args: CreateArgs) => unknown): T {\n const root = client as { chat: { completions: object } };\n const completionsProxy = new Proxy(root.chat.completions, {\n get: (t, p, r) => (p === \"create\" ? wrapped : Reflect.get(t, p, r)),\n });\n const chatProxy = new Proxy(root.chat, {\n get: (t, p, r) => (p === \"completions\" ? completionsProxy : Reflect.get(t, p, r)),\n });\n return new Proxy(client, {\n get: (t, p, r) => (p === \"chat\" ? chatProxy : Reflect.get(t, p, r)),\n });\n}\n\n/**\n * Meter a Vercel AI SDK result (the object `generateText`/`streamText` resolves to).\n * Reads `result.usage` - an object (`generateText`) or a Promise (`streamText`) - and\n * records the token count, non-blocking. Never touches `result.textStream`, so a\n * streamed response is delivered to your consumer untouched. Accepts AI SDK v4\n * (`promptTokens`/`completionTokens`) and v5 (`inputTokens`/`outputTokens`) shapes.\n *\n * const result = streamText({ model, prompt });\n * meterAIResult(result, { meter, feature: \"tokens\", customer: customerId });\n * return result.toDataStreamResponse();\n */\nexport function meterAIResult(\n result: { usage?: unknown; response?: unknown },\n options: MeterLLMOptions<void>,\n): void {\n const model = extractAIModel(result.response);\n const usage = result.usage;\n if (isPromiseLike(usage)) {\n // `streamText` resolves usage LATE - hand the meter the whole resolve→record\n // chain so `flush()` (the README's serverless guidance) awaits it instead of\n // snapshotting an empty in-flight set and dropping the usage.\n options.meter.recordAsync(usage.then((u) => buildTokenRecord(options, undefined, u, model)));\n return;\n }\n const record = buildTokenRecord(options, undefined, usage, model);\n if (record) options.meter.record(record);\n}\n\n/** Best-effort model id from an AI SDK result's `response` (skipped if it's a promise). */\nfunction extractAIModel(response: unknown): unknown {\n if (typeof response !== \"object\" || response === null) return undefined;\n const m = (response as { modelId?: unknown }).modelId;\n return typeof m === \"string\" ? m : undefined;\n}\n"]}
@@ -1,25 +1,25 @@
1
1
  import { Billow } from './index.cjs';
2
- import './index.d-CKQAhQfJ.cjs';
2
+ import './index.d-DSEYhV2c.cjs';
3
3
  import 'zod';
4
- import './billing-status-BZQN_gm7.cjs';
4
+ import './billing-status-DZkB0VPK.cjs';
5
5
  import './status.cjs';
6
- import './billing-B-VyZCXV.cjs';
7
- import './hosted-domains-BbuyxnuH.cjs';
6
+ import './billing-8ug1Gw05.cjs';
7
+ import './hosted-domains-Ci6jeNAe.cjs';
8
8
 
9
9
  /**
10
10
  * Auto-metering (PRD-16): ingestion strategies that call billow's `track()` for you,
11
11
  * so you never hand-instrument usage.
12
12
  *
13
13
  * Two layers:
14
- * 1. A generic primitive — {@link createUsageMeter} + {@link meterFunction} — a
14
+ * 1. A generic primitive - {@link createUsageMeter} + {@link meterFunction} - a
15
15
  * non-blocking, buffered, idempotent dispatcher you can point at ANY usage
16
16
  * source (an S3 upload, a job run, a DB write).
17
- * 2. LLM strategies built on it — {@link meterOpenAI} (OpenAI-compatible clients)
17
+ * 2. LLM strategies built on it - {@link meterOpenAI} (OpenAI-compatible clients)
18
18
  * and {@link meterAIResult} (Vercel AI SDK) that auto-count prompt+completion
19
19
  * tokens from the provider's own response (never re-tokenizing).
20
20
  *
21
- * Posture (every strategy): SERVER-SIDE ONLY — a browser can't be trusted to
22
- * self-report usage — and NON-BLOCKING: metering is a side effect that can never
21
+ * Posture (every strategy): SERVER-SIDE ONLY - a browser can't be trusted to
22
+ * self-report usage - and NON-BLOCKING: metering is a side effect that can never
23
23
  * delay or break the wrapped call. A failed `track()` buffers and retries under an
24
24
  * idempotency key; if it ultimately fails it is dropped (via `onError`), never thrown.
25
25
  *
@@ -33,7 +33,7 @@ import './hosted-domains-BbuyxnuH.cjs';
33
33
  * feature: "tokens",
34
34
  * customer: (params) => String(params.user), // your billow customer id
35
35
  * });
36
- * // use `openai` exactly as before — every completion auto-meters its tokens.
36
+ * // use `openai` exactly as before - every completion auto-meters its tokens.
37
37
  */
38
38
 
39
39
  /** One usage event handed to `track()`. Mirrors {@link Billow.track}'s input. */
@@ -46,11 +46,11 @@ interface UsageRecord {
46
46
  properties?: Record<string, string | number>;
47
47
  /**
48
48
  * Dedup key. Auto-generated per record when omitted, so a retried dispatch is a
49
- * no-op — set it yourself to dedupe across process restarts.
49
+ * no-op - set it yourself to dedupe across process restarts.
50
50
  */
51
51
  idempotencyKey?: string;
52
52
  }
53
- /** The minimal client the meter needs — the real {@link Billow} satisfies it. */
53
+ /** The minimal client the meter needs - the real {@link Billow} satisfies it. */
54
54
  type TrackClient = Pick<Billow, "track">;
55
55
  interface MeterOptions {
56
56
  /** Max dispatch attempts for one record before it's dropped (default 5). */
@@ -72,7 +72,7 @@ interface UsageMeter {
72
72
  /**
73
73
  * Enqueue a usage event whose record resolves LATER (e.g. a streamed response
74
74
  * whose token totals arrive as a promise). Registers the pending work immediately
75
- * so {@link UsageMeter.flush} awaits it — a `null` resolution or a rejected source
75
+ * so {@link UsageMeter.flush} awaits it - a `null` resolution or a rejected source
76
76
  * is dropped. Returns immediately, never throws.
77
77
  */
78
78
  recordAsync(source: Promise<UsageRecord | null>): void;
@@ -90,7 +90,7 @@ declare function createUsageMeter(client: TrackClient, options?: MeterOptions):
90
90
  */
91
91
  type UsageExtractor<A extends unknown[], R> = (result: R, args: A) => UsageRecord | null;
92
92
  /**
93
- * Wrap any async function so each successful call auto-records usage via `meter` —
93
+ * Wrap any async function so each successful call auto-records usage via `meter` -
94
94
  * the generic ingestion primitive. Point it at any source by supplying an `extract`.
95
95
  * Metering runs AFTER the call resolves and never blocks it; an extractor that
96
96
  * throws is swallowed. If the wrapped call itself rejects, nothing is recorded and
@@ -100,7 +100,7 @@ declare function meterFunction<A extends unknown[], R>(fn: (...args: A) => Promi
100
100
  /**
101
101
  * Yield every value of `source` straight through to the consumer while calling
102
102
  * `onValue` for each and `onDone` when iteration finishes (including an early
103
- * `break`/`return` — it runs in a `finally`). Taps a stream WITHOUT consuming it:
103
+ * `break`/`return` - it runs in a `finally`). Taps a stream WITHOUT consuming it:
104
104
  * the caller still receives every chunk. Tap callbacks that throw are swallowed so
105
105
  * they can't break the stream.
106
106
  */
@@ -128,12 +128,12 @@ type Resolvable<T, P> = T | ((params: P) => T);
128
128
  interface MeterLLMOptions<P = unknown> {
129
129
  /** The buffered meter that dispatches to `track()`. */
130
130
  meter: UsageMeter;
131
- /** The metered feature (token meter) id — static, or resolved per call. */
131
+ /** The metered feature (token meter) id - static, or resolved per call. */
132
132
  feature: Resolvable<string, P>;
133
133
  /**
134
134
  * The billow customer to bill. Static for single-tenant, or resolved per call
135
135
  * (e.g. from the OpenAI `user` param) for multi-tenant. Returning `null`/`undefined`
136
- * skips metering that call — there's no customer to attribute it to.
136
+ * skips metering that call - there's no customer to attribute it to.
137
137
  */
138
138
  customer: Resolvable<string | null | undefined, P>;
139
139
  /** Which token count to record as the meter value (default `"total"`). */
@@ -143,12 +143,12 @@ interface MeterLLMOptions<P = unknown> {
143
143
  }
144
144
  /**
145
145
  * Wrap an OpenAI-compatible client so every `chat.completions.create` call
146
- * auto-meters its token usage. Returns a proxy of the SAME type — use it exactly as
146
+ * auto-meters its token usage. Returns a proxy of the SAME type - use it exactly as
147
147
  * before; every other property/method passes straight through untouched.
148
148
  *
149
149
  * Reads the token count from the provider's own response (no re-tokenizing), for
150
150
  * both blocking and streaming calls. For a STREAMED call, pass
151
- * `stream_options: { include_usage: true }` so the final chunk carries usage —
151
+ * `stream_options: { include_usage: true }` so the final chunk carries usage -
152
152
  * otherwise a streamed call can't be metered (billow never mutates your request).
153
153
  *
154
154
  * A streamed response is metered when consumed the standard way: `await` the call,
@@ -166,7 +166,7 @@ interface MeterLLMOptions<P = unknown> {
166
166
  declare function meterOpenAI<T extends object>(client: T, options: MeterLLMOptions<Record<string, unknown>>): T;
167
167
  /**
168
168
  * Meter a Vercel AI SDK result (the object `generateText`/`streamText` resolves to).
169
- * Reads `result.usage` — an object (`generateText`) or a Promise (`streamText`) — and
169
+ * Reads `result.usage` - an object (`generateText`) or a Promise (`streamText`) - and
170
170
  * records the token count, non-blocking. Never touches `result.textStream`, so a
171
171
  * streamed response is delivered to your consumer untouched. Accepts AI SDK v4
172
172
  * (`promptTokens`/`completionTokens`) and v5 (`inputTokens`/`outputTokens`) shapes.
@@ -1,25 +1,25 @@
1
1
  import { Billow } from './index.js';
2
- import './index.d-CKQAhQfJ.js';
2
+ import './index.d-DSEYhV2c.js';
3
3
  import 'zod';
4
- import './billing-status-BZQN_gm7.js';
4
+ import './billing-status-DZkB0VPK.js';
5
5
  import './status.js';
6
- import './billing-CgjcWmvb.js';
7
- import './hosted-domains-BP91tg9P.js';
6
+ import './billing-Bs10EcjR.js';
7
+ import './hosted-domains-DSs5Iw9U.js';
8
8
 
9
9
  /**
10
10
  * Auto-metering (PRD-16): ingestion strategies that call billow's `track()` for you,
11
11
  * so you never hand-instrument usage.
12
12
  *
13
13
  * Two layers:
14
- * 1. A generic primitive — {@link createUsageMeter} + {@link meterFunction} — a
14
+ * 1. A generic primitive - {@link createUsageMeter} + {@link meterFunction} - a
15
15
  * non-blocking, buffered, idempotent dispatcher you can point at ANY usage
16
16
  * source (an S3 upload, a job run, a DB write).
17
- * 2. LLM strategies built on it — {@link meterOpenAI} (OpenAI-compatible clients)
17
+ * 2. LLM strategies built on it - {@link meterOpenAI} (OpenAI-compatible clients)
18
18
  * and {@link meterAIResult} (Vercel AI SDK) that auto-count prompt+completion
19
19
  * tokens from the provider's own response (never re-tokenizing).
20
20
  *
21
- * Posture (every strategy): SERVER-SIDE ONLY — a browser can't be trusted to
22
- * self-report usage — and NON-BLOCKING: metering is a side effect that can never
21
+ * Posture (every strategy): SERVER-SIDE ONLY - a browser can't be trusted to
22
+ * self-report usage - and NON-BLOCKING: metering is a side effect that can never
23
23
  * delay or break the wrapped call. A failed `track()` buffers and retries under an
24
24
  * idempotency key; if it ultimately fails it is dropped (via `onError`), never thrown.
25
25
  *
@@ -33,7 +33,7 @@ import './hosted-domains-BP91tg9P.js';
33
33
  * feature: "tokens",
34
34
  * customer: (params) => String(params.user), // your billow customer id
35
35
  * });
36
- * // use `openai` exactly as before — every completion auto-meters its tokens.
36
+ * // use `openai` exactly as before - every completion auto-meters its tokens.
37
37
  */
38
38
 
39
39
  /** One usage event handed to `track()`. Mirrors {@link Billow.track}'s input. */
@@ -46,11 +46,11 @@ interface UsageRecord {
46
46
  properties?: Record<string, string | number>;
47
47
  /**
48
48
  * Dedup key. Auto-generated per record when omitted, so a retried dispatch is a
49
- * no-op — set it yourself to dedupe across process restarts.
49
+ * no-op - set it yourself to dedupe across process restarts.
50
50
  */
51
51
  idempotencyKey?: string;
52
52
  }
53
- /** The minimal client the meter needs — the real {@link Billow} satisfies it. */
53
+ /** The minimal client the meter needs - the real {@link Billow} satisfies it. */
54
54
  type TrackClient = Pick<Billow, "track">;
55
55
  interface MeterOptions {
56
56
  /** Max dispatch attempts for one record before it's dropped (default 5). */
@@ -72,7 +72,7 @@ interface UsageMeter {
72
72
  /**
73
73
  * Enqueue a usage event whose record resolves LATER (e.g. a streamed response
74
74
  * whose token totals arrive as a promise). Registers the pending work immediately
75
- * so {@link UsageMeter.flush} awaits it — a `null` resolution or a rejected source
75
+ * so {@link UsageMeter.flush} awaits it - a `null` resolution or a rejected source
76
76
  * is dropped. Returns immediately, never throws.
77
77
  */
78
78
  recordAsync(source: Promise<UsageRecord | null>): void;
@@ -90,7 +90,7 @@ declare function createUsageMeter(client: TrackClient, options?: MeterOptions):
90
90
  */
91
91
  type UsageExtractor<A extends unknown[], R> = (result: R, args: A) => UsageRecord | null;
92
92
  /**
93
- * Wrap any async function so each successful call auto-records usage via `meter` —
93
+ * Wrap any async function so each successful call auto-records usage via `meter` -
94
94
  * the generic ingestion primitive. Point it at any source by supplying an `extract`.
95
95
  * Metering runs AFTER the call resolves and never blocks it; an extractor that
96
96
  * throws is swallowed. If the wrapped call itself rejects, nothing is recorded and
@@ -100,7 +100,7 @@ declare function meterFunction<A extends unknown[], R>(fn: (...args: A) => Promi
100
100
  /**
101
101
  * Yield every value of `source` straight through to the consumer while calling
102
102
  * `onValue` for each and `onDone` when iteration finishes (including an early
103
- * `break`/`return` — it runs in a `finally`). Taps a stream WITHOUT consuming it:
103
+ * `break`/`return` - it runs in a `finally`). Taps a stream WITHOUT consuming it:
104
104
  * the caller still receives every chunk. Tap callbacks that throw are swallowed so
105
105
  * they can't break the stream.
106
106
  */
@@ -128,12 +128,12 @@ type Resolvable<T, P> = T | ((params: P) => T);
128
128
  interface MeterLLMOptions<P = unknown> {
129
129
  /** The buffered meter that dispatches to `track()`. */
130
130
  meter: UsageMeter;
131
- /** The metered feature (token meter) id — static, or resolved per call. */
131
+ /** The metered feature (token meter) id - static, or resolved per call. */
132
132
  feature: Resolvable<string, P>;
133
133
  /**
134
134
  * The billow customer to bill. Static for single-tenant, or resolved per call
135
135
  * (e.g. from the OpenAI `user` param) for multi-tenant. Returning `null`/`undefined`
136
- * skips metering that call — there's no customer to attribute it to.
136
+ * skips metering that call - there's no customer to attribute it to.
137
137
  */
138
138
  customer: Resolvable<string | null | undefined, P>;
139
139
  /** Which token count to record as the meter value (default `"total"`). */
@@ -143,12 +143,12 @@ interface MeterLLMOptions<P = unknown> {
143
143
  }
144
144
  /**
145
145
  * Wrap an OpenAI-compatible client so every `chat.completions.create` call
146
- * auto-meters its token usage. Returns a proxy of the SAME type — use it exactly as
146
+ * auto-meters its token usage. Returns a proxy of the SAME type - use it exactly as
147
147
  * before; every other property/method passes straight through untouched.
148
148
  *
149
149
  * Reads the token count from the provider's own response (no re-tokenizing), for
150
150
  * both blocking and streaming calls. For a STREAMED call, pass
151
- * `stream_options: { include_usage: true }` so the final chunk carries usage —
151
+ * `stream_options: { include_usage: true }` so the final chunk carries usage -
152
152
  * otherwise a streamed call can't be metered (billow never mutates your request).
153
153
  *
154
154
  * A streamed response is metered when consumed the standard way: `await` the call,
@@ -166,7 +166,7 @@ interface MeterLLMOptions<P = unknown> {
166
166
  declare function meterOpenAI<T extends object>(client: T, options: MeterLLMOptions<Record<string, unknown>>): T;
167
167
  /**
168
168
  * Meter a Vercel AI SDK result (the object `generateText`/`streamText` resolves to).
169
- * Reads `result.usage` — an object (`generateText`) or a Promise (`streamText`) — and
169
+ * Reads `result.usage` - an object (`generateText`) or a Promise (`streamText`) - and
170
170
  * records the token count, non-blocking. Never touches `result.textStream`, so a
171
171
  * streamed response is delivered to your consumer untouched. Accepts AI SDK v4
172
172
  * (`promptTokens`/`completionTokens`) and v5 (`inputTokens`/`outputTokens`) shapes.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ingestion.ts"],"names":[],"mappings":"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","file":"ingestion.js","sourcesContent":["/**\n * Auto-metering (PRD-16): ingestion strategies that call billow's `track()` for you,\n * so you never hand-instrument usage.\n *\n * Two layers:\n * 1. A generic primitive — {@link createUsageMeter} + {@link meterFunction} — a\n * non-blocking, buffered, idempotent dispatcher you can point at ANY usage\n * source (an S3 upload, a job run, a DB write).\n * 2. LLM strategies built on it — {@link meterOpenAI} (OpenAI-compatible clients)\n * and {@link meterAIResult} (Vercel AI SDK) that auto-count prompt+completion\n * tokens from the provider's own response (never re-tokenizing).\n *\n * Posture (every strategy): SERVER-SIDE ONLY — a browser can't be trusted to\n * self-report usage — and NON-BLOCKING: metering is a side effect that can never\n * delay or break the wrapped call. A failed `track()` buffers and retries under an\n * idempotency key; if it ultimately fails it is dropped (via `onError`), never thrown.\n *\n * import { Billow } from \"@usebillow/sdk\";\n * import { createUsageMeter, meterOpenAI } from \"@usebillow/sdk/ingestion\";\n *\n * const billow = new Billow(process.env.BILLOW_SECRET_KEY!);\n * const meter = createUsageMeter(billow);\n * const openai = meterOpenAI(new OpenAI(), {\n * meter,\n * feature: \"tokens\",\n * customer: (params) => String(params.user), // your billow customer id\n * });\n * // use `openai` exactly as before — every completion auto-meters its tokens.\n */\n\nimport type { Billow } from \"./index\";\n\n/** One usage event handed to `track()`. Mirrors {@link Billow.track}'s input. */\nexport interface UsageRecord {\n customerId: string;\n featureId: string;\n /** Units to record (the engine defaults to 1). */\n value?: number;\n /** Event properties for meter filters / property-based aggregations. */\n properties?: Record<string, string | number>;\n /**\n * Dedup key. Auto-generated per record when omitted, so a retried dispatch is a\n * no-op — set it yourself to dedupe across process restarts.\n */\n idempotencyKey?: string;\n}\n\n/** The minimal client the meter needs — the real {@link Billow} satisfies it. */\nexport type TrackClient = Pick<Billow, \"track\">;\n\nexport interface MeterOptions {\n /** Max dispatch attempts for one record before it's dropped (default 5). */\n maxAttempts?: number;\n /** Base retry backoff in ms; doubles each attempt (default 250). */\n retryBaseMs?: number;\n /** Called once when a record is permanently dropped after exhausting retries. */\n onError?: (error: unknown, record: UsageRecord) => void;\n}\n\n/**\n * A buffered, non-blocking dispatcher to `track()`. `record()` returns immediately\n * and never throws; delivery (with retries under an idempotency key) happens in the\n * background. Call {@link UsageMeter.flush} before a serverless handler returns so\n * pending records aren't lost when the process freezes.\n */\nexport interface UsageMeter {\n /** Enqueue a usage event. Returns immediately, never throws. */\n record(record: UsageRecord): void;\n /**\n * Enqueue a usage event whose record resolves LATER (e.g. a streamed response\n * whose token totals arrive as a promise). Registers the pending work immediately\n * so {@link UsageMeter.flush} awaits it — a `null` resolution or a rejected source\n * is dropped. Returns immediately, never throws.\n */\n recordAsync(source: Promise<UsageRecord | null>): void;\n /** Resolve once every buffered dispatch has settled (delivered or dropped). */\n flush(): Promise<void>;\n /** Records still in flight (queued or retrying). */\n readonly pending: number;\n}\n\nconst DEFAULT_MAX_ATTEMPTS = 5;\nconst DEFAULT_RETRY_BASE_MS = 250;\n\nfunction newIdempotencyKey(): string {\n return crypto.randomUUID();\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Build a {@link UsageMeter} bound to a billow client (or any `{ track }`). */\nexport function createUsageMeter(client: TrackClient, options: MeterOptions = {}): UsageMeter {\n const maxAttempts = Math.max(1, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS);\n const retryBaseMs = Math.max(0, options.retryBaseMs ?? DEFAULT_RETRY_BASE_MS);\n const inFlight = new Set<Promise<void>>();\n\n async function dispatch(record: UsageRecord): Promise<void> {\n // Freeze one idempotency key for the record so every retry is the SAME call.\n const input: UsageRecord = {\n ...record,\n idempotencyKey: record.idempotencyKey ?? newIdempotencyKey(),\n };\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await client.track(input);\n return;\n } catch (error) {\n lastError = error;\n if (attempt < maxAttempts) await sleep(retryBaseMs * 2 ** (attempt - 1));\n }\n }\n try {\n options.onError?.(lastError, input);\n } catch {\n // A throwing onError must not reject the tracked dispatch (which would make\n // flush() reject) — the meter's contract is that it never throws.\n }\n }\n\n /** Register a unit of metering work so `flush()`/`pending` account for it. */\n function track(work: Promise<void>): void {\n const p = work.finally(() => {\n inFlight.delete(p);\n });\n inFlight.add(p);\n }\n\n return {\n record(record) {\n track(dispatch(record));\n },\n recordAsync(source) {\n // Chain resolve-then-dispatch into ONE tracked unit so flush() covers work\n // whose record isn't known yet. A null record or a rejected source is dropped.\n track(\n source.then(\n (record) => (record ? dispatch(record) : undefined),\n () => undefined,\n ),\n );\n },\n flush() {\n return Promise.all([...inFlight]).then(() => undefined);\n },\n get pending() {\n return inFlight.size;\n },\n };\n}\n\n/**\n * Derive a usage record from a completed call. Return `null` to skip metering it\n * (e.g. a cache hit with no billable usage). Receives the resolved result and the\n * original call arguments.\n */\nexport type UsageExtractor<A extends unknown[], R> = (result: R, args: A) => UsageRecord | null;\n\n/**\n * Wrap any async function so each successful call auto-records usage via `meter` —\n * the generic ingestion primitive. Point it at any source by supplying an `extract`.\n * Metering runs AFTER the call resolves and never blocks it; an extractor that\n * throws is swallowed. If the wrapped call itself rejects, nothing is recorded and\n * the error propagates unchanged.\n */\nexport function meterFunction<A extends unknown[], R>(\n fn: (...args: A) => Promise<R>,\n meter: UsageMeter,\n extract: UsageExtractor<A, R>,\n): (...args: A) => Promise<R> {\n return async (...args: A) => {\n const result = await fn(...args);\n try {\n const record = extract(result, args);\n if (record) meter.record(record);\n } catch {\n // Metering must never break the wrapped call — drop extractor errors.\n }\n return result;\n };\n}\n\n/**\n * Yield every value of `source` straight through to the consumer while calling\n * `onValue` for each and `onDone` when iteration finishes (including an early\n * `break`/`return` — it runs in a `finally`). Taps a stream WITHOUT consuming it:\n * the caller still receives every chunk. Tap callbacks that throw are swallowed so\n * they can't break the stream.\n */\nexport async function* tapAsyncIterable<T>(\n source: AsyncIterable<T>,\n taps: { onValue?: (value: T) => void; onDone?: () => void },\n): AsyncGenerator<T> {\n try {\n for await (const value of source) {\n try {\n taps.onValue?.(value);\n } catch {\n // never let a tap break the passthrough\n }\n yield value;\n }\n } finally {\n try {\n taps.onDone?.();\n } catch {\n // never let a tap break the passthrough\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// LLM token metering\n// ---------------------------------------------------------------------------\n\n/** Normalized token counts, whatever the provider's field naming. */\nexport interface TokenCounts {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n}\n\n/** Which token count a meter records as its `value` (default `\"total\"`). */\nexport type TokenCount = \"total\" | \"prompt\" | \"completion\";\n\nfunction firstNumber(...values: unknown[]): number | null {\n for (const v of values) if (typeof v === \"number\" && Number.isFinite(v)) return v;\n return null;\n}\n\n/**\n * Read token counts from a provider usage object, accepting OpenAI (`prompt_tokens`\n * / `completion_tokens` / `total_tokens`), Vercel AI SDK v4 (`promptTokens` / …) and\n * v5 (`inputTokens` / `outputTokens`) shapes. Returns `null` when no token fields\n * are present. `totalTokens` is derived from prompt+completion when not supplied.\n */\nexport function normalizeTokenUsage(usage: unknown): TokenCounts | null {\n if (typeof usage !== \"object\" || usage === null) return null;\n const u = usage as {\n prompt_tokens?: unknown;\n promptTokens?: unknown;\n inputTokens?: unknown;\n completion_tokens?: unknown;\n completionTokens?: unknown;\n outputTokens?: unknown;\n total_tokens?: unknown;\n totalTokens?: unknown;\n };\n const prompt = firstNumber(u.prompt_tokens, u.promptTokens, u.inputTokens);\n const completion = firstNumber(u.completion_tokens, u.completionTokens, u.outputTokens);\n const total = firstNumber(u.total_tokens, u.totalTokens);\n if (prompt === null && completion === null && total === null) return null;\n const promptTokens = prompt ?? 0;\n const completionTokens = completion ?? 0;\n return {\n promptTokens,\n completionTokens,\n totalTokens: total ?? promptTokens + completionTokens,\n };\n}\n\nfunction pickCount(counts: TokenCounts, which: TokenCount): number {\n if (which === \"prompt\") return counts.promptTokens;\n if (which === \"completion\") return counts.completionTokens;\n return counts.totalTokens;\n}\n\n/** A value supplied directly, or resolved from a call's params. */\ntype Resolvable<T, P> = T | ((params: P) => T);\n\nfunction resolveValue<T, P>(r: Resolvable<T, P>, params: P): T {\n return typeof r === \"function\" ? (r as (p: P) => T)(params) : r;\n}\n\nexport interface MeterLLMOptions<P = unknown> {\n /** The buffered meter that dispatches to `track()`. */\n meter: UsageMeter;\n /** The metered feature (token meter) id — static, or resolved per call. */\n feature: Resolvable<string, P>;\n /**\n * The billow customer to bill. Static for single-tenant, or resolved per call\n * (e.g. from the OpenAI `user` param) for multi-tenant. Returning `null`/`undefined`\n * skips metering that call — there's no customer to attribute it to.\n */\n customer: Resolvable<string | null | undefined, P>;\n /** Which token count to record as the meter value (default `\"total\"`). */\n count?: TokenCount;\n /** Extra event properties merged onto each record (`model` is added automatically). */\n properties?: (params: P, tokens: TokenCounts) => Record<string, string | number>;\n}\n\n/**\n * Build the usage record for one LLM call from its usage + model, or `null` to skip.\n * TOTAL by contract — a throwing `customer`/`feature`/`properties` resolver (e.g. an\n * unexpected params shape) drops the record rather than surfacing in the wrapped\n * call, so every caller (sync or async) inherits the non-blocking guarantee here.\n */\nfunction buildTokenRecord<P>(\n options: MeterLLMOptions<P>,\n params: P,\n usage: unknown,\n model: unknown,\n): UsageRecord | null {\n const counts = normalizeTokenUsage(usage);\n if (!counts) return null;\n try {\n const customerId = resolveValue(options.customer, params);\n if (!customerId) return null;\n const properties: Record<string, string | number> = {\n tokens_prompt: counts.promptTokens,\n tokens_completion: counts.completionTokens,\n tokens_total: counts.totalTokens,\n ...(typeof model === \"string\" ? { model } : {}),\n ...options.properties?.(params, counts),\n };\n return {\n customerId,\n featureId: resolveValue(options.feature, params),\n value: pickCount(counts, options.count ?? \"total\"),\n properties,\n };\n } catch {\n return null; // a resolver mistake must never break the wrapped call\n }\n}\n\nfunction isPromiseLike(v: unknown): v is Promise<unknown> {\n return (\n (typeof v === \"object\" || typeof v === \"function\") &&\n v !== null &&\n typeof (v as { then?: unknown }).then === \"function\"\n );\n}\n\n/** The two params slots an OpenAI-style `create(body, requestOptions?)` takes. */\ntype CreateArgs = [params: Record<string, unknown>, ...rest: unknown[]];\n\nfunction wrapOpenAICreate(\n create: (...args: unknown[]) => unknown,\n thisArg: unknown,\n options: MeterLLMOptions<Record<string, unknown>>,\n): (...args: CreateArgs) => unknown {\n return (params, ...rest) => {\n const returned = Reflect.apply(create, thisArg, [params, ...rest]);\n // openai-node returns a thenable (APIPromise) for BOTH streaming and not.\n if (!isPromiseLike(returned)) return returned;\n\n if ((params as { stream?: unknown }).stream === true) {\n // Streaming: TAP iteration to read the usage the final chunk carries (present\n // when the caller sets `stream_options.include_usage`) WITHOUT dropping the\n // OpenAI return contract — the APIPromise keeps its helpers and the resolved\n // Stream keeps its methods; we only override `Symbol.asyncIterator`.\n return proxyResolved(returned, (stream) =>\n typeof stream === \"object\" && stream !== null\n ? proxyAsyncIterable(stream, () =>\n tapOpenAIStream(stream as AsyncIterable<unknown>, params, options),\n )\n : stream,\n );\n }\n // Non-streaming: record from the resolved completion's `.usage`, tracked by the\n // meter so `flush()` awaits it. The caller receives the ORIGINAL promise\n // untouched (APIPromise helpers intact); a failed call resolves to no record.\n options.meter.recordAsync(returned.then((resp) => completionRecord(resp, params, options)));\n return returned;\n };\n}\n\nfunction completionRecord(\n resp: unknown,\n params: Record<string, unknown>,\n options: MeterLLMOptions<Record<string, unknown>>,\n): UsageRecord | null {\n if (typeof resp !== \"object\" || resp === null) return null;\n const r = resp as { usage?: unknown; model?: unknown };\n return buildTokenRecord(options, params, r.usage, r.model);\n}\n\n/**\n * Proxy a thenable so awaiting it yields `map(resolvedValue)`, while every other\n * property/method (an APIPromise's `.withResponse()`/`.asResponse()`, …) passes\n * straight through. Lets us tap the resolved value without dropping the return\n * contract.\n */\nfunction proxyResolved(\n source: Promise<unknown>,\n map: (value: unknown) => unknown,\n): Promise<unknown> {\n const mappedThen = (onF?: (v: unknown) => unknown, onR?: (e: unknown) => unknown) =>\n source.then((v) => (onF ? onF(map(v)) : map(v)), onR);\n return new Proxy(source, {\n get(target, prop) {\n // Route `then`/`catch`/`finally` through the mapping so awaiting via ANY of\n // them yields the tapped value — binding the native methods to `target` would\n // resolve the raw (un-tapped) stream and silently skip metering.\n if (prop === \"then\") return mappedThen;\n if (prop === \"catch\") return (onR: (e: unknown) => unknown) => mappedThen(undefined, onR);\n if (prop === \"finally\") {\n return (cb?: () => void) =>\n mappedThen(\n (v) => {\n cb?.();\n return v;\n },\n (e) => {\n cb?.();\n throw e;\n },\n );\n }\n const value = Reflect.get(target, prop);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n }) as Promise<unknown>;\n}\n\n/**\n * Proxy an async-iterable object so `for await` runs `makeIterator()` (our tap),\n * while every other property/method (a Stream's `.tee()`, `.toReadableStream()`, …)\n * passes straight through. Taps iteration WITHOUT replacing the object.\n */\nfunction proxyAsyncIterable(source: object, makeIterator: () => AsyncIterator<unknown>): object {\n return new Proxy(source, {\n get(target, prop) {\n if (prop === Symbol.asyncIterator) return makeIterator;\n const value = Reflect.get(target, prop);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n}\n\n/** Usage + model folded out of a streamed response, ready for {@link buildTokenRecord}. */\ninterface StreamUsage {\n usage?: unknown;\n model?: unknown;\n}\n\n/**\n * Meter a streamed response: tap the stream (yielding every chunk untouched), fold\n * each chunk into an accumulator via `reduce`, and record ONE usage event when\n * iteration finishes. This is the reusable middle between the generic\n * {@link tapAsyncIterable} and a specific provider's chunk shape — a new streaming\n * source (Anthropic SSE, etc.) plugs in by supplying only its `reduce`.\n */\nfunction meterStream<C, P>(\n stream: AsyncIterable<C>,\n params: P,\n options: MeterLLMOptions<P>,\n reduce: (chunk: C, acc: StreamUsage) => void,\n): AsyncGenerator<C> {\n const acc: StreamUsage = {};\n return tapAsyncIterable(stream, {\n onValue: (chunk) => reduce(chunk, acc),\n onDone: () => {\n const record = buildTokenRecord(options, params, acc.usage, acc.model);\n if (record) options.meter.record(record);\n },\n });\n}\n\nfunction tapOpenAIStream(\n stream: AsyncIterable<unknown>,\n params: Record<string, unknown>,\n options: MeterLLMOptions<Record<string, unknown>>,\n): AsyncGenerator<unknown> {\n return meterStream(stream, params, options, (chunk, acc) => {\n if (typeof chunk !== \"object\" || chunk === null) return;\n const c = chunk as { usage?: unknown; model?: unknown };\n if (c.usage) acc.usage = c.usage; // the final chunk carries usage (include_usage)\n if (c.model && acc.model === undefined) acc.model = c.model;\n });\n}\n\n/**\n * Wrap an OpenAI-compatible client so every `chat.completions.create` call\n * auto-meters its token usage. Returns a proxy of the SAME type — use it exactly as\n * before; every other property/method passes straight through untouched.\n *\n * Reads the token count from the provider's own response (no re-tokenizing), for\n * both blocking and streaming calls. For a STREAMED call, pass\n * `stream_options: { include_usage: true }` so the final chunk carries usage —\n * otherwise a streamed call can't be metered (billow never mutates your request).\n *\n * A streamed response is metered when consumed the standard way: `await` the call,\n * then `for await` the stream. The `Stream` helpers (`.tee()`, `.toReadableStream()`)\n * and the un-awaited `APIPromise` helpers (`.withResponse()`, `.asResponse()`) are\n * PRESERVED (they keep working), but consuming through them bypasses the iteration\n * tap, so those calls are not token-metered.\n *\n * Works with any client exposing `chat.completions.create` returning a `.usage`\n * shape: OpenAI, Azure OpenAI, Together, Groq, OpenRouter, and other compatibles.\n *\n * const openai = meterOpenAI(new OpenAI(), { meter, feature: \"tokens\", customer });\n * const res = await openai.chat.completions.create({ model, messages }); // metered\n */\nexport function meterOpenAI<T extends object>(\n client: T,\n options: MeterLLMOptions<Record<string, unknown>>,\n): T {\n const root = client as { chat?: { completions?: { create?: unknown } } };\n const completions = root.chat?.completions;\n const create = completions?.create;\n if (typeof create !== \"function\" || !completions) {\n throw new TypeError(\"meterOpenAI: expected client.chat.completions.create to be a function\");\n }\n const wrapped = wrapOpenAICreate(create as (...args: unknown[]) => unknown, completions, options);\n return replaceCreate(client, wrapped);\n}\n\n/**\n * Return a non-mutating proxy of `client` that swaps ONLY `chat.completions.create`\n * for `wrapped`, passing every other access straight through. Untouched methods keep\n * their real receiver, so `this` stays correct.\n */\nfunction replaceCreate<T extends object>(client: T, wrapped: (...args: CreateArgs) => unknown): T {\n const root = client as { chat: { completions: object } };\n const completionsProxy = new Proxy(root.chat.completions, {\n get: (t, p, r) => (p === \"create\" ? wrapped : Reflect.get(t, p, r)),\n });\n const chatProxy = new Proxy(root.chat, {\n get: (t, p, r) => (p === \"completions\" ? completionsProxy : Reflect.get(t, p, r)),\n });\n return new Proxy(client, {\n get: (t, p, r) => (p === \"chat\" ? chatProxy : Reflect.get(t, p, r)),\n });\n}\n\n/**\n * Meter a Vercel AI SDK result (the object `generateText`/`streamText` resolves to).\n * Reads `result.usage` — an object (`generateText`) or a Promise (`streamText`) — and\n * records the token count, non-blocking. Never touches `result.textStream`, so a\n * streamed response is delivered to your consumer untouched. Accepts AI SDK v4\n * (`promptTokens`/`completionTokens`) and v5 (`inputTokens`/`outputTokens`) shapes.\n *\n * const result = streamText({ model, prompt });\n * meterAIResult(result, { meter, feature: \"tokens\", customer: customerId });\n * return result.toDataStreamResponse();\n */\nexport function meterAIResult(\n result: { usage?: unknown; response?: unknown },\n options: MeterLLMOptions<void>,\n): void {\n const model = extractAIModel(result.response);\n const usage = result.usage;\n if (isPromiseLike(usage)) {\n // `streamText` resolves usage LATE — hand the meter the whole resolve→record\n // chain so `flush()` (the README's serverless guidance) awaits it instead of\n // snapshotting an empty in-flight set and dropping the usage.\n options.meter.recordAsync(usage.then((u) => buildTokenRecord(options, undefined, u, model)));\n return;\n }\n const record = buildTokenRecord(options, undefined, usage, model);\n if (record) options.meter.record(record);\n}\n\n/** Best-effort model id from an AI SDK result's `response` (skipped if it's a promise). */\nfunction extractAIModel(response: unknown): unknown {\n if (typeof response !== \"object\" || response === null) return undefined;\n const m = (response as { modelId?: unknown }).modelId;\n return typeof m === \"string\" ? m : undefined;\n}\n"]}
1
+ {"version":3,"sources":["../src/ingestion.ts"],"names":[],"mappings":"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","file":"ingestion.js","sourcesContent":["/**\n * Auto-metering (PRD-16): ingestion strategies that call billow's `track()` for you,\n * so you never hand-instrument usage.\n *\n * Two layers:\n * 1. A generic primitive - {@link createUsageMeter} + {@link meterFunction} - a\n * non-blocking, buffered, idempotent dispatcher you can point at ANY usage\n * source (an S3 upload, a job run, a DB write).\n * 2. LLM strategies built on it - {@link meterOpenAI} (OpenAI-compatible clients)\n * and {@link meterAIResult} (Vercel AI SDK) that auto-count prompt+completion\n * tokens from the provider's own response (never re-tokenizing).\n *\n * Posture (every strategy): SERVER-SIDE ONLY - a browser can't be trusted to\n * self-report usage - and NON-BLOCKING: metering is a side effect that can never\n * delay or break the wrapped call. A failed `track()` buffers and retries under an\n * idempotency key; if it ultimately fails it is dropped (via `onError`), never thrown.\n *\n * import { Billow } from \"@usebillow/sdk\";\n * import { createUsageMeter, meterOpenAI } from \"@usebillow/sdk/ingestion\";\n *\n * const billow = new Billow(process.env.BILLOW_SECRET_KEY!);\n * const meter = createUsageMeter(billow);\n * const openai = meterOpenAI(new OpenAI(), {\n * meter,\n * feature: \"tokens\",\n * customer: (params) => String(params.user), // your billow customer id\n * });\n * // use `openai` exactly as before - every completion auto-meters its tokens.\n */\n\nimport type { Billow } from \"./index\";\n\n/** One usage event handed to `track()`. Mirrors {@link Billow.track}'s input. */\nexport interface UsageRecord {\n customerId: string;\n featureId: string;\n /** Units to record (the engine defaults to 1). */\n value?: number;\n /** Event properties for meter filters / property-based aggregations. */\n properties?: Record<string, string | number>;\n /**\n * Dedup key. Auto-generated per record when omitted, so a retried dispatch is a\n * no-op - set it yourself to dedupe across process restarts.\n */\n idempotencyKey?: string;\n}\n\n/** The minimal client the meter needs - the real {@link Billow} satisfies it. */\nexport type TrackClient = Pick<Billow, \"track\">;\n\nexport interface MeterOptions {\n /** Max dispatch attempts for one record before it's dropped (default 5). */\n maxAttempts?: number;\n /** Base retry backoff in ms; doubles each attempt (default 250). */\n retryBaseMs?: number;\n /** Called once when a record is permanently dropped after exhausting retries. */\n onError?: (error: unknown, record: UsageRecord) => void;\n}\n\n/**\n * A buffered, non-blocking dispatcher to `track()`. `record()` returns immediately\n * and never throws; delivery (with retries under an idempotency key) happens in the\n * background. Call {@link UsageMeter.flush} before a serverless handler returns so\n * pending records aren't lost when the process freezes.\n */\nexport interface UsageMeter {\n /** Enqueue a usage event. Returns immediately, never throws. */\n record(record: UsageRecord): void;\n /**\n * Enqueue a usage event whose record resolves LATER (e.g. a streamed response\n * whose token totals arrive as a promise). Registers the pending work immediately\n * so {@link UsageMeter.flush} awaits it - a `null` resolution or a rejected source\n * is dropped. Returns immediately, never throws.\n */\n recordAsync(source: Promise<UsageRecord | null>): void;\n /** Resolve once every buffered dispatch has settled (delivered or dropped). */\n flush(): Promise<void>;\n /** Records still in flight (queued or retrying). */\n readonly pending: number;\n}\n\nconst DEFAULT_MAX_ATTEMPTS = 5;\nconst DEFAULT_RETRY_BASE_MS = 250;\n\nfunction newIdempotencyKey(): string {\n return crypto.randomUUID();\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Build a {@link UsageMeter} bound to a billow client (or any `{ track }`). */\nexport function createUsageMeter(client: TrackClient, options: MeterOptions = {}): UsageMeter {\n const maxAttempts = Math.max(1, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS);\n const retryBaseMs = Math.max(0, options.retryBaseMs ?? DEFAULT_RETRY_BASE_MS);\n const inFlight = new Set<Promise<void>>();\n\n async function dispatch(record: UsageRecord): Promise<void> {\n // Freeze one idempotency key for the record so every retry is the SAME call.\n const input: UsageRecord = {\n ...record,\n idempotencyKey: record.idempotencyKey ?? newIdempotencyKey(),\n };\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await client.track(input);\n return;\n } catch (error) {\n lastError = error;\n if (attempt < maxAttempts) await sleep(retryBaseMs * 2 ** (attempt - 1));\n }\n }\n try {\n options.onError?.(lastError, input);\n } catch {\n // A throwing onError must not reject the tracked dispatch (which would make\n // flush() reject) - the meter's contract is that it never throws.\n }\n }\n\n /** Register a unit of metering work so `flush()`/`pending` account for it. */\n function track(work: Promise<void>): void {\n const p = work.finally(() => {\n inFlight.delete(p);\n });\n inFlight.add(p);\n }\n\n return {\n record(record) {\n track(dispatch(record));\n },\n recordAsync(source) {\n // Chain resolve-then-dispatch into ONE tracked unit so flush() covers work\n // whose record isn't known yet. A null record or a rejected source is dropped.\n track(\n source.then(\n (record) => (record ? dispatch(record) : undefined),\n () => undefined,\n ),\n );\n },\n flush() {\n return Promise.all([...inFlight]).then(() => undefined);\n },\n get pending() {\n return inFlight.size;\n },\n };\n}\n\n/**\n * Derive a usage record from a completed call. Return `null` to skip metering it\n * (e.g. a cache hit with no billable usage). Receives the resolved result and the\n * original call arguments.\n */\nexport type UsageExtractor<A extends unknown[], R> = (result: R, args: A) => UsageRecord | null;\n\n/**\n * Wrap any async function so each successful call auto-records usage via `meter` -\n * the generic ingestion primitive. Point it at any source by supplying an `extract`.\n * Metering runs AFTER the call resolves and never blocks it; an extractor that\n * throws is swallowed. If the wrapped call itself rejects, nothing is recorded and\n * the error propagates unchanged.\n */\nexport function meterFunction<A extends unknown[], R>(\n fn: (...args: A) => Promise<R>,\n meter: UsageMeter,\n extract: UsageExtractor<A, R>,\n): (...args: A) => Promise<R> {\n return async (...args: A) => {\n const result = await fn(...args);\n try {\n const record = extract(result, args);\n if (record) meter.record(record);\n } catch {\n // Metering must never break the wrapped call - drop extractor errors.\n }\n return result;\n };\n}\n\n/**\n * Yield every value of `source` straight through to the consumer while calling\n * `onValue` for each and `onDone` when iteration finishes (including an early\n * `break`/`return` - it runs in a `finally`). Taps a stream WITHOUT consuming it:\n * the caller still receives every chunk. Tap callbacks that throw are swallowed so\n * they can't break the stream.\n */\nexport async function* tapAsyncIterable<T>(\n source: AsyncIterable<T>,\n taps: { onValue?: (value: T) => void; onDone?: () => void },\n): AsyncGenerator<T> {\n try {\n for await (const value of source) {\n try {\n taps.onValue?.(value);\n } catch {\n // never let a tap break the passthrough\n }\n yield value;\n }\n } finally {\n try {\n taps.onDone?.();\n } catch {\n // never let a tap break the passthrough\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// LLM token metering\n// ---------------------------------------------------------------------------\n\n/** Normalized token counts, whatever the provider's field naming. */\nexport interface TokenCounts {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n}\n\n/** Which token count a meter records as its `value` (default `\"total\"`). */\nexport type TokenCount = \"total\" | \"prompt\" | \"completion\";\n\nfunction firstNumber(...values: unknown[]): number | null {\n for (const v of values) if (typeof v === \"number\" && Number.isFinite(v)) return v;\n return null;\n}\n\n/**\n * Read token counts from a provider usage object, accepting OpenAI (`prompt_tokens`\n * / `completion_tokens` / `total_tokens`), Vercel AI SDK v4 (`promptTokens` / …) and\n * v5 (`inputTokens` / `outputTokens`) shapes. Returns `null` when no token fields\n * are present. `totalTokens` is derived from prompt+completion when not supplied.\n */\nexport function normalizeTokenUsage(usage: unknown): TokenCounts | null {\n if (typeof usage !== \"object\" || usage === null) return null;\n const u = usage as {\n prompt_tokens?: unknown;\n promptTokens?: unknown;\n inputTokens?: unknown;\n completion_tokens?: unknown;\n completionTokens?: unknown;\n outputTokens?: unknown;\n total_tokens?: unknown;\n totalTokens?: unknown;\n };\n const prompt = firstNumber(u.prompt_tokens, u.promptTokens, u.inputTokens);\n const completion = firstNumber(u.completion_tokens, u.completionTokens, u.outputTokens);\n const total = firstNumber(u.total_tokens, u.totalTokens);\n if (prompt === null && completion === null && total === null) return null;\n const promptTokens = prompt ?? 0;\n const completionTokens = completion ?? 0;\n return {\n promptTokens,\n completionTokens,\n totalTokens: total ?? promptTokens + completionTokens,\n };\n}\n\nfunction pickCount(counts: TokenCounts, which: TokenCount): number {\n if (which === \"prompt\") return counts.promptTokens;\n if (which === \"completion\") return counts.completionTokens;\n return counts.totalTokens;\n}\n\n/** A value supplied directly, or resolved from a call's params. */\ntype Resolvable<T, P> = T | ((params: P) => T);\n\nfunction resolveValue<T, P>(r: Resolvable<T, P>, params: P): T {\n return typeof r === \"function\" ? (r as (p: P) => T)(params) : r;\n}\n\nexport interface MeterLLMOptions<P = unknown> {\n /** The buffered meter that dispatches to `track()`. */\n meter: UsageMeter;\n /** The metered feature (token meter) id - static, or resolved per call. */\n feature: Resolvable<string, P>;\n /**\n * The billow customer to bill. Static for single-tenant, or resolved per call\n * (e.g. from the OpenAI `user` param) for multi-tenant. Returning `null`/`undefined`\n * skips metering that call - there's no customer to attribute it to.\n */\n customer: Resolvable<string | null | undefined, P>;\n /** Which token count to record as the meter value (default `\"total\"`). */\n count?: TokenCount;\n /** Extra event properties merged onto each record (`model` is added automatically). */\n properties?: (params: P, tokens: TokenCounts) => Record<string, string | number>;\n}\n\n/**\n * Build the usage record for one LLM call from its usage + model, or `null` to skip.\n * TOTAL by contract - a throwing `customer`/`feature`/`properties` resolver (e.g. an\n * unexpected params shape) drops the record rather than surfacing in the wrapped\n * call, so every caller (sync or async) inherits the non-blocking guarantee here.\n */\nfunction buildTokenRecord<P>(\n options: MeterLLMOptions<P>,\n params: P,\n usage: unknown,\n model: unknown,\n): UsageRecord | null {\n const counts = normalizeTokenUsage(usage);\n if (!counts) return null;\n try {\n const customerId = resolveValue(options.customer, params);\n if (!customerId) return null;\n const properties: Record<string, string | number> = {\n tokens_prompt: counts.promptTokens,\n tokens_completion: counts.completionTokens,\n tokens_total: counts.totalTokens,\n ...(typeof model === \"string\" ? { model } : {}),\n ...options.properties?.(params, counts),\n };\n return {\n customerId,\n featureId: resolveValue(options.feature, params),\n value: pickCount(counts, options.count ?? \"total\"),\n properties,\n };\n } catch {\n return null; // a resolver mistake must never break the wrapped call\n }\n}\n\nfunction isPromiseLike(v: unknown): v is Promise<unknown> {\n return (\n (typeof v === \"object\" || typeof v === \"function\") &&\n v !== null &&\n typeof (v as { then?: unknown }).then === \"function\"\n );\n}\n\n/** The two params slots an OpenAI-style `create(body, requestOptions?)` takes. */\ntype CreateArgs = [params: Record<string, unknown>, ...rest: unknown[]];\n\nfunction wrapOpenAICreate(\n create: (...args: unknown[]) => unknown,\n thisArg: unknown,\n options: MeterLLMOptions<Record<string, unknown>>,\n): (...args: CreateArgs) => unknown {\n return (params, ...rest) => {\n const returned = Reflect.apply(create, thisArg, [params, ...rest]);\n // openai-node returns a thenable (APIPromise) for BOTH streaming and not.\n if (!isPromiseLike(returned)) return returned;\n\n if ((params as { stream?: unknown }).stream === true) {\n // Streaming: TAP iteration to read the usage the final chunk carries (present\n // when the caller sets `stream_options.include_usage`) WITHOUT dropping the\n // OpenAI return contract - the APIPromise keeps its helpers and the resolved\n // Stream keeps its methods; we only override `Symbol.asyncIterator`.\n return proxyResolved(returned, (stream) =>\n typeof stream === \"object\" && stream !== null\n ? proxyAsyncIterable(stream, () =>\n tapOpenAIStream(stream as AsyncIterable<unknown>, params, options),\n )\n : stream,\n );\n }\n // Non-streaming: record from the resolved completion's `.usage`, tracked by the\n // meter so `flush()` awaits it. The caller receives the ORIGINAL promise\n // untouched (APIPromise helpers intact); a failed call resolves to no record.\n options.meter.recordAsync(returned.then((resp) => completionRecord(resp, params, options)));\n return returned;\n };\n}\n\nfunction completionRecord(\n resp: unknown,\n params: Record<string, unknown>,\n options: MeterLLMOptions<Record<string, unknown>>,\n): UsageRecord | null {\n if (typeof resp !== \"object\" || resp === null) return null;\n const r = resp as { usage?: unknown; model?: unknown };\n return buildTokenRecord(options, params, r.usage, r.model);\n}\n\n/**\n * Proxy a thenable so awaiting it yields `map(resolvedValue)`, while every other\n * property/method (an APIPromise's `.withResponse()`/`.asResponse()`, …) passes\n * straight through. Lets us tap the resolved value without dropping the return\n * contract.\n */\nfunction proxyResolved(\n source: Promise<unknown>,\n map: (value: unknown) => unknown,\n): Promise<unknown> {\n const mappedThen = (onF?: (v: unknown) => unknown, onR?: (e: unknown) => unknown) =>\n source.then((v) => (onF ? onF(map(v)) : map(v)), onR);\n return new Proxy(source, {\n get(target, prop) {\n // Route `then`/`catch`/`finally` through the mapping so awaiting via ANY of\n // them yields the tapped value - binding the native methods to `target` would\n // resolve the raw (un-tapped) stream and silently skip metering.\n if (prop === \"then\") return mappedThen;\n if (prop === \"catch\") return (onR: (e: unknown) => unknown) => mappedThen(undefined, onR);\n if (prop === \"finally\") {\n return (cb?: () => void) =>\n mappedThen(\n (v) => {\n cb?.();\n return v;\n },\n (e) => {\n cb?.();\n throw e;\n },\n );\n }\n const value = Reflect.get(target, prop);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n }) as Promise<unknown>;\n}\n\n/**\n * Proxy an async-iterable object so `for await` runs `makeIterator()` (our tap),\n * while every other property/method (a Stream's `.tee()`, `.toReadableStream()`, …)\n * passes straight through. Taps iteration WITHOUT replacing the object.\n */\nfunction proxyAsyncIterable(source: object, makeIterator: () => AsyncIterator<unknown>): object {\n return new Proxy(source, {\n get(target, prop) {\n if (prop === Symbol.asyncIterator) return makeIterator;\n const value = Reflect.get(target, prop);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n}\n\n/** Usage + model folded out of a streamed response, ready for {@link buildTokenRecord}. */\ninterface StreamUsage {\n usage?: unknown;\n model?: unknown;\n}\n\n/**\n * Meter a streamed response: tap the stream (yielding every chunk untouched), fold\n * each chunk into an accumulator via `reduce`, and record ONE usage event when\n * iteration finishes. This is the reusable middle between the generic\n * {@link tapAsyncIterable} and a specific provider's chunk shape - a new streaming\n * source (Anthropic SSE, etc.) plugs in by supplying only its `reduce`.\n */\nfunction meterStream<C, P>(\n stream: AsyncIterable<C>,\n params: P,\n options: MeterLLMOptions<P>,\n reduce: (chunk: C, acc: StreamUsage) => void,\n): AsyncGenerator<C> {\n const acc: StreamUsage = {};\n return tapAsyncIterable(stream, {\n onValue: (chunk) => reduce(chunk, acc),\n onDone: () => {\n const record = buildTokenRecord(options, params, acc.usage, acc.model);\n if (record) options.meter.record(record);\n },\n });\n}\n\nfunction tapOpenAIStream(\n stream: AsyncIterable<unknown>,\n params: Record<string, unknown>,\n options: MeterLLMOptions<Record<string, unknown>>,\n): AsyncGenerator<unknown> {\n return meterStream(stream, params, options, (chunk, acc) => {\n if (typeof chunk !== \"object\" || chunk === null) return;\n const c = chunk as { usage?: unknown; model?: unknown };\n if (c.usage) acc.usage = c.usage; // the final chunk carries usage (include_usage)\n if (c.model && acc.model === undefined) acc.model = c.model;\n });\n}\n\n/**\n * Wrap an OpenAI-compatible client so every `chat.completions.create` call\n * auto-meters its token usage. Returns a proxy of the SAME type - use it exactly as\n * before; every other property/method passes straight through untouched.\n *\n * Reads the token count from the provider's own response (no re-tokenizing), for\n * both blocking and streaming calls. For a STREAMED call, pass\n * `stream_options: { include_usage: true }` so the final chunk carries usage -\n * otherwise a streamed call can't be metered (billow never mutates your request).\n *\n * A streamed response is metered when consumed the standard way: `await` the call,\n * then `for await` the stream. The `Stream` helpers (`.tee()`, `.toReadableStream()`)\n * and the un-awaited `APIPromise` helpers (`.withResponse()`, `.asResponse()`) are\n * PRESERVED (they keep working), but consuming through them bypasses the iteration\n * tap, so those calls are not token-metered.\n *\n * Works with any client exposing `chat.completions.create` returning a `.usage`\n * shape: OpenAI, Azure OpenAI, Together, Groq, OpenRouter, and other compatibles.\n *\n * const openai = meterOpenAI(new OpenAI(), { meter, feature: \"tokens\", customer });\n * const res = await openai.chat.completions.create({ model, messages }); // metered\n */\nexport function meterOpenAI<T extends object>(\n client: T,\n options: MeterLLMOptions<Record<string, unknown>>,\n): T {\n const root = client as { chat?: { completions?: { create?: unknown } } };\n const completions = root.chat?.completions;\n const create = completions?.create;\n if (typeof create !== \"function\" || !completions) {\n throw new TypeError(\"meterOpenAI: expected client.chat.completions.create to be a function\");\n }\n const wrapped = wrapOpenAICreate(create as (...args: unknown[]) => unknown, completions, options);\n return replaceCreate(client, wrapped);\n}\n\n/**\n * Return a non-mutating proxy of `client` that swaps ONLY `chat.completions.create`\n * for `wrapped`, passing every other access straight through. Untouched methods keep\n * their real receiver, so `this` stays correct.\n */\nfunction replaceCreate<T extends object>(client: T, wrapped: (...args: CreateArgs) => unknown): T {\n const root = client as { chat: { completions: object } };\n const completionsProxy = new Proxy(root.chat.completions, {\n get: (t, p, r) => (p === \"create\" ? wrapped : Reflect.get(t, p, r)),\n });\n const chatProxy = new Proxy(root.chat, {\n get: (t, p, r) => (p === \"completions\" ? completionsProxy : Reflect.get(t, p, r)),\n });\n return new Proxy(client, {\n get: (t, p, r) => (p === \"chat\" ? chatProxy : Reflect.get(t, p, r)),\n });\n}\n\n/**\n * Meter a Vercel AI SDK result (the object `generateText`/`streamText` resolves to).\n * Reads `result.usage` - an object (`generateText`) or a Promise (`streamText`) - and\n * records the token count, non-blocking. Never touches `result.textStream`, so a\n * streamed response is delivered to your consumer untouched. Accepts AI SDK v4\n * (`promptTokens`/`completionTokens`) and v5 (`inputTokens`/`outputTokens`) shapes.\n *\n * const result = streamText({ model, prompt });\n * meterAIResult(result, { meter, feature: \"tokens\", customer: customerId });\n * return result.toDataStreamResponse();\n */\nexport function meterAIResult(\n result: { usage?: unknown; response?: unknown },\n options: MeterLLMOptions<void>,\n): void {\n const model = extractAIModel(result.response);\n const usage = result.usage;\n if (isPromiseLike(usage)) {\n // `streamText` resolves usage LATE - hand the meter the whole resolve→record\n // chain so `flush()` (the README's serverless guidance) awaits it instead of\n // snapshotting an empty in-flight set and dropping the usage.\n options.meter.recordAsync(usage.then((u) => buildTokenRecord(options, undefined, u, model)));\n return;\n }\n const record = buildTokenRecord(options, undefined, usage, model);\n if (record) options.meter.record(record);\n}\n\n/** Best-effort model id from an AI SDK result's `response` (skipped if it's a promise). */\nfunction extractAIModel(response: unknown): unknown {\n if (typeof response !== \"object\" || response === null) return undefined;\n const m = (response as { modelId?: unknown }).modelId;\n return typeof m === \"string\" ? m : undefined;\n}\n"]}