@anvailabs/sandhi 0.7.0 → 0.8.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.
package/README.md CHANGED
@@ -86,4 +86,11 @@ for await (const eventJson of provider.streamJson(request)) {
86
86
  credentials cannot be overridden. Invalid documents fail before network I/O; provider failures
87
87
  throw `SandhiProviderError` with a structured `ProviderErrorV1` payload.
88
88
 
89
+ Canonical streams may emit accounting-only `usage` events before content or after a
90
+ numeric usage snapshot. When `usage.completeness` is `"unavailable"` and a valid
91
+ `cache_read_observation` is present, merge that metadata without adding its counters or replacing the last
92
+ numeric snapshot/completeness. It is not a second final usage verdict. These events
93
+ preserve evidence for consumers that stop reading after content; transparent and
94
+ translated provider wire responses are unchanged.
95
+
89
96
  Apache-2.0. The transport surface links `sandhi-providers` (async HTTP stack) into the addon.
package/contracts.d.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  // Generated by scripts/gen-binding-contract-facades.py; DO NOT EDIT.
2
- // schema-sha256: b6ef1e7aeedcf9356c5babbd40e95f457edda7b26719a91af16906b880481e14
2
+ // schema-sha256: 7f9ce844567386398289921d885209b0b49587bba0f148aac64e803f90a029f5
3
3
  export type JsonValue = unknown
4
4
  export type Role = "developer" | "system" | "user" | "assistant" | "tool" | "function"
5
5
  export type FinishReasonV1 = "stop" | "length" | "tool_calls" | "content_filter" | "function_call" | "unknown"
6
6
  export type UsageCompleteness = "final" | "partial" | "unavailable"
7
7
  export type UsageBasis = "provider_reported" | "estimated"
8
8
  export type LatencySource = "origin" | "boundary"
9
+ export type CacheReadStatus = "reported" | "absent" | "malformed" | "unsupported"
10
+ export type CacheReadSource = "origin_usage" | "caller_supplied" | "explicit_capability"
11
+ export interface CacheReadObservation { status: CacheReadStatus; source: CacheReadSource }
12
+ export interface CacheReadCoverage { reported: number; absent: number; malformed: number; unsupported: number; unknown: number }
9
13
  export type ContentPart =
10
14
  | { type: "text"; text: string }
11
15
  | { type: "image_url"; image_url: string; detail?: string }
@@ -31,6 +35,7 @@ export interface ChatRequestV1 {
31
35
  }
32
36
  export interface UsageV2 {
33
37
  tokens_in: number; tokens_out: number; cache_creation_tokens: number; cache_read_tokens: number
38
+ cache_read_observation?: CacheReadObservation
34
39
  completeness?: UsageCompleteness; basis?: UsageBasis; attempts?: number; outcome?: string
35
40
  upstream_request_id?: string
36
41
  duration_ms?: number; duration_source?: LatencySource
@@ -43,6 +48,7 @@ export interface LatencySummary { samples: number; p50_ms: number; p95_ms: numbe
43
48
  export interface UsageAggregateV1 {
44
49
  key: string; calls: number; tokens_in: number; tokens_out: number;
45
50
  cache_creation_tokens: number; cache_read_tokens: number; reasoning_tokens: number;
51
+ cache_read_coverage?: CacheReadCoverage
46
52
  billable_tokens: number; latency?: LatencySummary
47
53
  }
48
54
  export interface AssistantOutputV1 { content?: MessageContent; tool_calls?: ToolCallV1[]; refusal?: string }
package/index.d.ts CHANGED
@@ -4,11 +4,16 @@
4
4
  /* auto-generated by NAPI-RS */
5
5
 
6
6
  /** The neutral token breakdown parsed from a provider response. */
7
+ export interface CacheReadObservationValue {
8
+ status: string
9
+ source: string
10
+ }
7
11
  export interface UsageBreakdown {
8
12
  tokensIn: number
9
13
  tokensOut: number
10
14
  cacheCreationTokens: number
11
15
  cacheReadTokens: number
16
+ cacheReadObservation?: CacheReadObservationValue
12
17
  reasoningTokens: number
13
18
  reasoningIncluded?: boolean
14
19
  durationMs?: number
@@ -33,6 +38,7 @@ export interface Event {
33
38
  reasoningIncluded?: boolean
34
39
  cacheCreationTokens: number
35
40
  cacheReadTokens: number
41
+ cacheReadObservation?: CacheReadObservationValue
36
42
  usageCompleteness: string
37
43
  attempts: number
38
44
  outcome?: string
@@ -135,7 +141,7 @@ export declare class Gateway {
135
141
  * provider Sandhi doesn't natively parse: do your own parsing and pass the counts. Same
136
142
  * attribution + budget + emit as `meter()`.
137
143
  */
138
- meterTokens(virtualKey: string, provider: string, model: string, tokensIn: number, tokensOut: number, cacheCreationTokens?: number | undefined | null, cacheReadTokens?: number | undefined | null, sessionId?: string | undefined | null, route?: string | undefined | null): Event
144
+ meterTokens(virtualKey: string, provider: string, model: string, tokensIn: number, tokensOut: number, cacheCreationTokens?: number | undefined | null, cacheReadTokens?: number | undefined | null, sessionId?: string | undefined | null, route?: string | undefined | null, cacheReadObservation?: unknown | undefined | null): Event
139
145
  /** All events emitted so far (in-memory). */
140
146
  events(): Array<Event>
141
147
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anvailabs/sandhi",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Sandhi — the metering layer for AI agents (Node binding): in-process virtual keys, budgets, and neutral usage-event metering.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "https://github.com/anvai-labs/sandhi",
@@ -47,7 +47,7 @@
47
47
  "access": "public"
48
48
  },
49
49
  "optionalDependencies": {
50
- "@anvailabs/sandhi-linux-x64-gnu": "0.7.0",
51
- "@anvailabs/sandhi-darwin-arm64": "0.7.0"
50
+ "@anvailabs/sandhi-linux-x64-gnu": "0.8.0",
51
+ "@anvailabs/sandhi-darwin-arm64": "0.8.0"
52
52
  }
53
53
  }