@anvailabs/sandhi 0.6.1 → 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,10 +1,15 @@
1
1
  // Generated by scripts/gen-binding-contract-facades.py; DO NOT EDIT.
2
- // schema-sha256: 812a2d001ca2d5566be1458cdb98242aab3a828a3f4b14c7b02d12efb4d39cfa
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
+ 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 }
8
13
  export type ContentPart =
9
14
  | { type: "text"; text: string }
10
15
  | { type: "image_url"; image_url: string; detail?: string }
@@ -30,9 +35,11 @@ export interface ChatRequestV1 {
30
35
  }
31
36
  export interface UsageV2 {
32
37
  tokens_in: number; tokens_out: number; cache_creation_tokens: number; cache_read_tokens: number
38
+ cache_read_observation?: CacheReadObservation
33
39
  completeness?: UsageCompleteness; basis?: UsageBasis; attempts?: number; outcome?: string
34
40
  upstream_request_id?: string
35
- duration_ms?: number; time_to_first_token_ms?: number
41
+ duration_ms?: number; duration_source?: LatencySource
42
+ time_to_first_token_ms?: number; time_to_first_token_source?: LatencySource
36
43
  audio_input_tokens?: number; audio_output_tokens?: number; reasoning_tokens?: number
37
44
  reasoning_included?: boolean
38
45
  accepted_prediction_tokens?: number; rejected_prediction_tokens?: number
@@ -41,6 +48,7 @@ export interface LatencySummary { samples: number; p50_ms: number; p95_ms: numbe
41
48
  export interface UsageAggregateV1 {
42
49
  key: string; calls: number; tokens_in: number; tokens_out: number;
43
50
  cache_creation_tokens: number; cache_read_tokens: number; reasoning_tokens: number;
51
+ cache_read_coverage?: CacheReadCoverage
44
52
  billable_tokens: number; latency?: LatencySummary
45
53
  }
46
54
  export interface AssistantOutputV1 { content?: MessageContent; tool_calls?: ToolCallV1[]; refusal?: string }
package/index.d.ts CHANGED
@@ -4,13 +4,20 @@
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
19
+ durationMs?: number
20
+ timeToFirstTokenMs?: number
14
21
  }
15
22
  /** A neutral usage event (mirrors `usage-event.v1.schema.json`). */
16
23
  export interface Event {
@@ -31,11 +38,16 @@ export interface Event {
31
38
  reasoningIncluded?: boolean
32
39
  cacheCreationTokens: number
33
40
  cacheReadTokens: number
41
+ cacheReadObservation?: CacheReadObservationValue
34
42
  usageCompleteness: string
35
43
  attempts: number
36
44
  outcome?: string
37
45
  upstreamRequestId?: string
38
46
  gpuSeconds?: number
47
+ durationMs?: number
48
+ durationSource?: string
49
+ timeToFirstTokenMs?: number
50
+ timeToFirstTokenSource?: string
39
51
  }
40
52
  /** The usage-event wire-contract major version this build targets. */
41
53
  export declare function wireContractVersion(): string
@@ -129,7 +141,7 @@ export declare class Gateway {
129
141
  * provider Sandhi doesn't natively parse: do your own parsing and pass the counts. Same
130
142
  * attribution + budget + emit as `meter()`.
131
143
  */
132
- 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
133
145
  /** All events emitted so far (in-memory). */
134
146
  events(): Array<Event>
135
147
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anvailabs/sandhi",
3
- "version": "0.6.1",
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.6.1",
51
- "@anvailabs/sandhi-darwin-arm64": "0.6.1"
50
+ "@anvailabs/sandhi-linux-x64-gnu": "0.8.0",
51
+ "@anvailabs/sandhi-darwin-arm64": "0.8.0"
52
52
  }
53
53
  }