@usagetap/sdk 1.4.0 → 1.7.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 (52) hide show
  1. package/README.md +156 -18
  2. package/dist/adapters/anthropic.cjs +195 -235
  3. package/dist/adapters/anthropic.cjs.map +1 -1
  4. package/dist/adapters/anthropic.d.cts +1 -1
  5. package/dist/adapters/anthropic.d.ts +1 -1
  6. package/dist/adapters/anthropic.mjs +195 -235
  7. package/dist/adapters/anthropic.mjs.map +1 -1
  8. package/dist/adapters/openai.cjs +256 -274
  9. package/dist/adapters/openai.cjs.map +1 -1
  10. package/dist/adapters/openai.d.cts +1 -1
  11. package/dist/adapters/openai.d.ts +1 -1
  12. package/dist/adapters/openai.mjs +256 -274
  13. package/dist/adapters/openai.mjs.map +1 -1
  14. package/dist/adapters/openrouter.cjs +256 -274
  15. package/dist/adapters/openrouter.cjs.map +1 -1
  16. package/dist/adapters/openrouter.d.cts +1 -1
  17. package/dist/adapters/openrouter.d.ts +1 -1
  18. package/dist/adapters/openrouter.mjs +256 -274
  19. package/dist/adapters/openrouter.mjs.map +1 -1
  20. package/dist/anthropic/index.cjs +195 -235
  21. package/dist/anthropic/index.cjs.map +1 -1
  22. package/dist/anthropic/index.d.cts +1 -1
  23. package/dist/anthropic/index.d.ts +1 -1
  24. package/dist/anthropic/index.mjs +195 -235
  25. package/dist/anthropic/index.mjs.map +1 -1
  26. package/dist/{client-CExQ8e1T.d.cts → client-C0UiaqVB.d.cts} +141 -61
  27. package/dist/{client-CExQ8e1T.d.ts → client-C0UiaqVB.d.ts} +141 -61
  28. package/dist/express/index.cjs +189 -246
  29. package/dist/express/index.cjs.map +1 -1
  30. package/dist/express/index.d.cts +1 -1
  31. package/dist/express/index.d.ts +1 -1
  32. package/dist/express/index.mjs +189 -246
  33. package/dist/express/index.mjs.map +1 -1
  34. package/dist/index.cjs +391 -175
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +13 -4
  37. package/dist/index.d.ts +13 -4
  38. package/dist/index.mjs +391 -175
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/openai/index.cjs +256 -274
  41. package/dist/openai/index.cjs.map +1 -1
  42. package/dist/openai/index.d.cts +1 -1
  43. package/dist/openai/index.d.ts +1 -1
  44. package/dist/openai/index.mjs +256 -274
  45. package/dist/openai/index.mjs.map +1 -1
  46. package/dist/openrouter/index.cjs +256 -274
  47. package/dist/openrouter/index.cjs.map +1 -1
  48. package/dist/openrouter/index.d.cts +1 -1
  49. package/dist/openrouter/index.d.ts +1 -1
  50. package/dist/openrouter/index.mjs +256 -274
  51. package/dist/openrouter/index.mjs.map +1 -1
  52. package/package.json +2 -2
package/README.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  Server-only JavaScript/TypeScript client for UsageTap. The SDK helps you instrument `call_begin → vendor call → call_end` flows with built-in retries, idempotency helpers, and vendor adapters.
4
4
 
5
+ For API requests and MCP tool calls, use the dedicated call meter without model or token fields:
6
+
7
+ ```ts
8
+ await client.incrementCustomMeter(
9
+ { customerId, meterSlot: "AGENTIC_API", amount: 1, feature: "mcp.tools.call" },
10
+ { idempotencyKey: `mcp:${requestId}` },
11
+ );
12
+ ```
13
+
14
+ Configure its included allocation and single-rate or graduated overage pricing in **More > Agentic & API** on the usage plan. It uses the same reporting, credit funding, and asynchronous PAYG settlement as custom meters. This does not reserve credit or impose a rolling rate limit. See [Agentic/API integration and limits](../../docs/agentic-api-meter.md).
15
+
5
16
  ## Module formats
6
17
 
7
18
  `@usagetap/sdk` ships real dual ESM (`.mjs`) and CommonJS (`.cjs`) entrypoints. In ESM projects use `import { UsageTapClient } from "@usagetap/sdk";`. For CommonJS runtimes (including VS Code extensions) rely on `const { UsageTapClient } = require("@usagetap/sdk");`.
@@ -9,7 +20,7 @@ Server-only JavaScript/TypeScript client for UsageTap. The SDK helps you instrum
9
20
  Optional adapters live behind subpath exports so their peer dependencies stay out of the core bundle:
10
21
 
11
22
  - `@usagetap/sdk/openai` – OpenAI/OpenRouter helpers (`wrapOpenAI`, `streamOpenAIRoute`, etc.)
12
- - `@usagetap/sdk/anthropic` – Anthropic helper (`wrapAnthropic`)
23
+ - `@usagetap/sdk/anthropic` – Anthropic helpers (`withMetering`, `wrapAnthropic`)
13
24
  - `@usagetap/sdk/openrouter` – discoverable OpenRouter aliases for the OpenAI-compatible wrappers
14
25
  - `@usagetap/sdk/express` – Express middleware
15
26
  - `@usagetap/sdk/react` – React chat hook
@@ -123,6 +134,41 @@ const completion = await usageTap.gateway.chat.completions.create({
123
134
  console.log(completion.choices[0].message?.content);
124
135
  ```
125
136
 
137
+ The typed Responses resource supports portable built-in web search, structured
138
+ output, verbosity, prompt caching, and Gateway compression. It is buffered in
139
+ this release, so omit `stream` or set it to `false`:
140
+
141
+ ```ts
142
+ const response = await usageTap.gateway.responses.create({
143
+ model: "usagetap/standard",
144
+ customerId: "cust_123",
145
+ feature: "answer.with.sources",
146
+ instructions: stablePolicy,
147
+ input: "Find the current answer and return it as JSON.",
148
+ tools: [{ type: "web_search" }],
149
+ text: {
150
+ verbosity: "low",
151
+ format: {
152
+ type: "json_schema",
153
+ name: "sourced_answer",
154
+ strict: true,
155
+ schema: {
156
+ type: "object",
157
+ properties: { answer: { type: "string" } },
158
+ required: ["answer"],
159
+ additionalProperties: false,
160
+ },
161
+ },
162
+ },
163
+ prompt_cache_key: "answer-policy-v1",
164
+ prompt_cache_options: { mode: "implicit" },
165
+ compress: true,
166
+ });
167
+
168
+ console.log(response.output_text);
169
+ console.log(response.usage?.input_tokens_details?.cached_tokens);
170
+ ```
171
+
126
172
  The same resource exposes `models.list()` and the complete native batch
127
173
  lifecycle. Batch creation generates the required idempotency key unless one is
128
174
  provided:
@@ -368,6 +414,31 @@ export async function POST() {
368
414
 
369
415
  `wrapOpenAI` preserves the model already supplied by the application. It does not invent a model mapping or fallback. Use the manual `withUsage` pattern when BLOCK or DOWNGRADE must control provider invocation, model selection, reasoning effort, or search tools.
370
416
 
417
+ Starting in `@usagetap/sdk` 1.6.0, the Anthropic adapter preserves both
418
+ streaming forms. Use
419
+ `messages.create({ stream: true })` for the lower-level async iterable, or use
420
+ the synchronous `messages.stream()` helper when the application relies on
421
+ events and final-message accumulation:
422
+
423
+ ```ts
424
+ const stream = anthropic.messages
425
+ .stream({
426
+ model: "claude-sonnet-4-5",
427
+ max_tokens: 512,
428
+ messages: [{ role: "user", content: "Stream it" }],
429
+ })
430
+ .on("text", (text) => process.stdout.write(text));
431
+
432
+ const message = await stream.finalMessage();
433
+ ```
434
+
435
+ `messages.stream()` still returns Anthropic's `MessageStream` immediately and
436
+ retains `.on()`, async iteration, `.abort()`, `.done()`, `.finalText()`, and
437
+ `.finalMessage()`. UsageTap begins metering before the provider request and
438
+ finalizes once when the underlying stream completes, fails, or is aborted.
439
+ Wrapped `messages.create()` results also retain Anthropic's `.withResponse()`
440
+ and `.asResponse()` helpers.
441
+
371
442
  ### Overriding usage context per request
372
443
 
373
444
  You can override the UsageTap begin payload on a per-call basis via the `usageTap` option:
@@ -393,7 +464,12 @@ For streaming calls created with `{ stream: true }`, UsageTap automatically calc
393
464
 
394
465
  ### responses.create support
395
466
 
396
- The wrapper also instruments `openai.responses.create`, preserves the supplied model and request, and collects usage data the same way as chat completions.
467
+ The wrapper also instruments `openai.responses.create`, preserves text controls,
468
+ tools, prompt-cache controls and breakpoints through compression, and records
469
+ cache reads, cache writes, reasoning tokens, and completed web-search calls.
470
+ Install OpenAI SDK 7.5 or newer when you want its native TypeScript definitions
471
+ for the latest Responses fields; the UsageTap Gateway resource has its own types
472
+ and does not require the OpenAI package.
397
473
 
398
474
  ### OpenRouter support
399
475
 
@@ -540,6 +616,11 @@ const wrappedFetch = wrapFetch(usageTap, {
540
616
  feature: "chat",
541
617
  requested: { standard: true, premium: true, search: true, reasoningLevel: "MEDIUM" },
542
618
  },
619
+ onMeteringError: ({ stage, callId, error }) => {
620
+ console.error("UsageTap metering failed", { stage, callId, error });
621
+ },
622
+ // Set true when an unmetered vendor response must fail the application call.
623
+ strictMetering: false,
543
624
  });
544
625
 
545
626
  const openai = new OpenAI({
@@ -557,7 +638,7 @@ const completion = await openai.chat.completions.create({
557
638
  });
558
639
  ```
559
640
 
560
- `wrapFetch` detects OpenAI API endpoints, handles streaming and non-streaming responses, and automatically extracts usage data. It preserves the application's model. If limits must control provider selection, use an explicit begin decision before this layer. You can override metering context per request using special headers:
641
+ `wrapFetch` detects OpenAI API endpoints, handles streaming and non-streaming responses, requests final usage for chat streams, and automatically extracts usage data. It preserves the application's model. If limits must control provider selection, use an explicit begin decision before this layer. Use `onMeteringError` to export request parsing, begin, or end failures after SDK retries are exhausted. Set `strictMetering` when those failures should reject the application call. You can override metering context per request using special headers; the wrapper consumes these headers and removes them before calling the provider:
561
642
 
562
643
  ```ts
563
644
  await openai.chat.completions.create(
@@ -625,6 +706,48 @@ const { begin, end, vendor, endUsage } = envelope.data;
625
706
  - Non-2xx vendor responses still trigger `call_end`; the envelope returns `CALL_VENDOR_WARNING` alongside vendor error metadata.
626
707
  - The canonical media type `application/vnd.usagetap.v1+json` is required; the SDK already sends this header automatically when you rely on `UsageTapClient`.
627
708
 
709
+ ### Retrieve finalized call pricing (API-only)
710
+
711
+ An external server can retrieve the stored result later with `GET
712
+ /calls/{callId}`. Use a server API key containing `usage:read`; do not expose
713
+ the key in browser code.
714
+
715
+ ```ts
716
+ const response = await fetch(
717
+ `${baseUrl}/calls/${encodeURIComponent(callId)}`,
718
+ {
719
+ method: "GET",
720
+ headers: {
721
+ Authorization: `Bearer ${process.env.USAGETAP_API_KEY}`,
722
+ Accept: "application/vnd.usagetap.v1+json",
723
+ },
724
+ },
725
+ );
726
+
727
+ const envelope = await response.json();
728
+ if (!response.ok || envelope.result.status !== "ACCEPTED") {
729
+ throw new Error(`UsageTap call lookup failed: ${envelope.result.code}`);
730
+ }
731
+ if (envelope.data.pricingStatus !== "FINAL") {
732
+ throw new Error(`Call pricing is ${envelope.data.pricingStatus}`);
733
+ }
734
+
735
+ const providerCostUsd = envelope.data.costUSD;
736
+ const customerChargeUsd = envelope.data.payg.chargedUsd;
737
+ const customerId = envelope.data.customerId;
738
+ const customerUserId = envelope.data.customerUserId; // string or null
739
+ ```
740
+
741
+ `pricingStatus` is `PENDING` while the call is open and `UNAVAILABLE` when a
742
+ completed call could not resolve model pricing. Only treat `costUSD` as final
743
+ when the status is `FINAL`.
744
+
745
+ The response includes only the stable customer identifiers recorded on the
746
+ call: `customerId` and nullable `customerUserId`. Use opaque, non-PII values
747
+ for both. Customer and end-user names or emails, billing-provider identifiers,
748
+ `organizationId`, and the internal `orgIdCustomerId` composite key are not
749
+ returned.
750
+
628
751
  ## Exports
629
752
 
630
753
  Key exports from `@usagetap/sdk`:
@@ -686,9 +809,9 @@ console.log("New subscription:", result.data.subscription);
686
809
  ```
687
810
 
688
811
  **Strategy options:**
689
- - `IMMEDIATE_RESET`: Switch plan immediately and reset all usage counters to zero
690
- - `IMMEDIATE_PRORATED`: Switch plan immediately and prorate existing usage against new limits
691
- - `AT_NEXT_REPLENISH`: Schedule the plan change for the next replenishment cycle (default)
812
+ - `IMMEDIATE_RESET`: Switch immediately, grant the target plan's full allowances, and start a new replenishment cycle (default when the SDK option is omitted)
813
+ - `IMMEDIATE_PRORATED`: Switch immediately, preserve usage already consumed, adjust remaining allowances to the target limits, and keep the current replenishment date
814
+ - `AT_NEXT_REPLENISH`: Keep the current plan until its existing scheduled replenishment, then switch
692
815
 
693
816
  The response includes the updated subscription details, including the new plan version, limits, and next replenishment timestamp. If `strategy: "AT_NEXT_REPLENISH"` is used, the `subscription.pending` field will indicate the scheduled plan change.
694
817
 
@@ -715,7 +838,7 @@ Custom meters allow you to track usage beyond standard LLM metrics—ideal for a
715
838
  const result = await usageTap.incrementCustomMeter({
716
839
  customerId: "cust_123",
717
840
  customerUserId: currentUser.id,
718
- meterSlot: "CUSTOM1", // or "CUSTOM2"
841
+ meterSlot: "CUSTOM1", // or "CUSTOM2" or "AGENTIC_API"
719
842
  amount: 5,
720
843
  feature: "agent_actions",
721
844
  tags: ["workflow_automation"],
@@ -735,13 +858,13 @@ console.log("Blocked:", result.data.blocked);
735
858
  - `customerId` (string, required): Customer identifier
736
859
  - `customerUserId` (string, optional): Stable identifier for the end user responsible for the event
737
860
  - `customerUserName` and `customerUserEmail` (string, optional): Display fields for live activity
738
- - `meterSlot` ("CUSTOM1" | "CUSTOM2", required): Which custom meter to increment
861
+ - `meterSlot` ("CUSTOM1" | "CUSTOM2" | "AGENTIC_API", required): Which meter to increment
739
862
  - `amount` (number, required): Positive number to decrement from quota
740
863
  - `feature` (string, optional): Feature identifier for tracking
741
864
  - `tags` (string[], optional): Tags for categorization
742
865
  - `metadata` (object, optional): Additional metadata
743
866
 
744
- The method returns the updated meter snapshot showing remaining quota, limits, and usage. If the customer's plan has `limitType: "BLOCK"` and quota is exceeded, a `UsageTapError` is thrown with code `USAGETAP_AUTH_ERROR`.
867
+ The method returns the updated meter snapshot showing remaining quota, limits, and usage. If usage exceeds the allocation and eligible overage funding, it returns `data.blocked: true`. The SDK does not throw for this accepted usage report; your application must enforce the flag.
745
868
 
746
869
  **Use cases:**
747
870
 
@@ -749,10 +872,11 @@ The method returns the updated meter snapshot showing remaining quota, limits, a
749
872
  // Track agent tool invocations
750
873
  await usageTap.incrementCustomMeter({
751
874
  customerId: "cust_123",
752
- meterSlot: "CUSTOM1",
875
+ meterSlot: "AGENTIC_API",
753
876
  amount: 1,
754
877
  feature: "agent.tool_call",
755
878
  tags: ["web_search"],
879
+ metadata: { kind: "mcp", serverName: "search", toolName: "web_search" },
756
880
  });
757
881
 
758
882
  // Track document processing (10 pages)
@@ -767,20 +891,26 @@ await usageTap.incrementCustomMeter({
767
891
  // Track external API calls
768
892
  await usageTap.incrementCustomMeter({
769
893
  customerId: "cust_789",
770
- meterSlot: "CUSTOM1",
894
+ meterSlot: "AGENTIC_API",
771
895
  amount: 1,
772
896
  feature: "external_api.maps",
773
897
  tags: ["geocoding"],
898
+ metadata: { kind: "api", method: "GET", endpoint: "/v1/geocode" },
774
899
  });
775
900
  ```
776
901
 
902
+ For Agentic & API Calls, use `metadata.toolName` (and optionally
903
+ `metadata.serverName`) for MCP operations, or `metadata.endpoint` and
904
+ `metadata.method` for HTTP APIs. Live View displays these values and removes URL
905
+ query strings from endpoint labels.
906
+
777
907
  **Important notes:**
778
908
 
779
909
  1. Custom meters must be enabled in the customer's usage plan
780
- 2. The `amount` decrements the remaining quota (like token usage)
781
- 3. With `BLOCK` policy, exceeding quota throws an error
782
- 4. With `DOWNGRADE` policy, usage continues but quota can go negative
783
- 5. Unlimited meters don't track usage but still record events for analytics
910
+ 2. Report a positive safe integer quantity. Usage consumes allocation, then eligible purchased meter credits and/or PAYG funding. The last included unit is allowed.
911
+ 3. Unfunded usage is recorded with `blocked: true`; the application must enforce that flag. LLM `BLOCK`/`DOWNGRADE` policies do not apply to meter reports.
912
+ 4. PAYG overages require enabled PAYG and a configured single or graduated price. Settlement is asynchronous and does not reserve dollar credit or impose a rolling rate limit.
913
+ 5. Unlimited meters still record events for analytics but do not consume allocation or charge overages.
784
914
 
785
915
  ## Response envelope (canonical only)
786
916
 
@@ -875,7 +1005,7 @@ UsageTap responds exclusively with the canonical `{ result, data, correlationId
875
1005
  - `meters` is a per-counter snapshot including remaining quotas, total limits, usage to date, and convenience ratios. `remainingRatios` mirrors the same information in a compact map for quick lookups.
876
1006
  - `subscription` contains the active plan identity, versioning, and upcoming replenishment timestamps so you can render customer-facing UI without querying Dynamo yourself.
877
1007
  - `models` surfaces per-organization vendor hints (e.g. standard vs. premium model shortlists).
878
- - `idempotency` reveals the actual key that was persisted (`callId` mirrors this value). The SDK generates a unique key by default. With SDK auto-generation disabled, the backend derives a deterministic hash from organization, customer, feature, requested entitlements, call type, PAYG hold, and pricing mode; identical inputs can replay an earlier call.
1008
+ - `idempotency` reveals the actual key that was persisted (`callId` mirrors this value). The SDK generates a unique key by default. With SDK auto-generation disabled, the backend derives a deterministic hash from organization, customer, feature, requested entitlements, call type, and pricing mode; identical inputs can replay an earlier call.
879
1009
  - `plan` and `balances` remain available alongside the core begin payload for backwards compatibility with earlier SDK versions.
880
1010
 
881
1011
  ### Example `call_end` success
@@ -898,7 +1028,7 @@ UsageTap responds exclusively with the canonical `{ result, data, correlationId
898
1028
  "reasoningTokens": 0
899
1029
  },
900
1030
  "metered": {
901
- "tokens": 768,
1031
+ "tokens": 288,
902
1032
  "calls": 1,
903
1033
  "searches": 1
904
1034
  },
@@ -939,7 +1069,15 @@ includes cache reads. Anthropic reports ordinary input, cache reads, and cache
939
1069
  writes separately, so the Anthropic wrapper adds the three counters for
940
1070
  `inputTokens` while retaining both cache subsets.
941
1071
 
942
- `metered` is derived from the raw Dynamo deltas. Additional meters (audio seconds, reasoning tokens, balances) will populate in later phases without breaking the contract.
1072
+ `metered.tokens` is the provider-reported output-token count. Input tokens,
1073
+ including cache reads and cache writes, remain recorded and part of provider
1074
+ cost but do not consume the plan's Standard Output Tokens or Premium Output
1075
+ Tokens allowance and are not sent to the corresponding Stripe token meters.
1076
+ Reasoning tokens are a breakdown of provider output and are not added again.
1077
+
1078
+ The persisted meter keys remain `standardTokens` and `premiumTokens` for API
1079
+ compatibility; they represent standard and premium output tokens in both plan
1080
+ limits and Stripe usage metering.
943
1081
 
944
1082
  `spendVelocity` is aggregate-backed current UTC hour/day telemetry. UsageTap does not enforce limits from this section; `currentCallCostUsd` is included separately because aggregate updates are asynchronous.
945
1083