ai-lcr 0.1.0 → 0.2.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
@@ -108,6 +108,68 @@ The same pattern works for any vendor's native SDK provider — `@ai-sdk/anthrop
108
108
  <img src="assets/ai-lcr-routing.svg" alt="routing diagram: cheapest first, fallback on failure, recover after idle" width="820">
109
109
  </p>
110
110
 
111
+ ## See what happened (`onCall`)
112
+
113
+ `onError`/`onCost` fire separately and uncorrelated, so a failover is hard to read after the fact. `onCall` gives you **one record per request** — the full chain, the winner, the reason for each failed hop, latency, and cost — and `formatCallRecord` turns it into a one-liner you can scan:
114
+
115
+ ```ts
116
+ import { createLCR, formatCallRecord } from "ai-lcr";
117
+
118
+ const lcr = createLCR({
119
+ models: { /* … */ },
120
+ onCall: (record) => console.log(formatCallRecord(record)),
121
+ });
122
+ ```
123
+
124
+ ```text
125
+ ✓ text tokenmart 412ms $0.0003
126
+ ⚠ text tokenmart→openrouter 910ms $0.0004 ⤷ tokenmart 502
127
+ ✗ text deepseek→tokenmart→openrouter 1240ms FAILED ⤷ deepseek 401, tokenmart 502, openrouter 429
128
+ ```
129
+
130
+ `✓` served on the first try · `⚠` failed over but recovered · `✗` every provider failed. The `⤷` shows which provider died and why.
131
+
132
+ **Persist it anywhere — zero lock-in.** `record` is a plain `CallRecord` object. Log the JSON and point any log drain at it (Axiom, Datadog, your own DB); ai-lcr never decides where it goes:
133
+
134
+ ```ts
135
+ onCall: (record) => console.log(JSON.stringify(record)),
136
+ ```
137
+
138
+ Or ship each record to an HTTP collector with the built-in `createHttpSink` (fire-and-forget, never throws, dashboard-agnostic):
139
+
140
+ ```ts
141
+ import { createLCR, createHttpSink } from "ai-lcr";
142
+ import { after } from "next/server"; // serverless: don't block the response
143
+
144
+ const lcr = createLCR({
145
+ models: { /* … */ },
146
+ onCall: createHttpSink({
147
+ url: `${process.env.LCR_INGEST_URL}/api/ingest`,
148
+ headers: { authorization: `Bearer ${process.env.LCR_INGEST_KEY}` },
149
+ project: process.env.LCR_PROJECT, // optional tag if one collector serves several apps
150
+ dispatch: after, // run after the response is sent (serverless-safe)
151
+ }),
152
+ });
153
+ ```
154
+
155
+ Point `url` at anything that accepts the `CallRecord` JSON — including the self-hostable companion dashboard, **[ai-lcr-dashboard](https://github.com/victorzhrn/ai-lcr-dashboard)** (Spend / Calls / Failover rate + a live failover feed). You run your own instance, so the data never leaves your infrastructure; a [db9](https://db9.ai) database can be provisioned in seconds if you don't want to stand one up yourself.
156
+
157
+ ```ts
158
+ interface CallRecord {
159
+ id: string; // correlation id, one per request
160
+ model: string; // logical model name
161
+ attempts: { provider: string; ok: boolean; latencyMs: number; errorClass?: string }[];
162
+ winner?: string; // provider that served; undefined if all failed
163
+ ok: boolean;
164
+ failedOver: boolean; // more than one provider was tried
165
+ latencyMs: number;
166
+ inputTokens: number;
167
+ outputTokens: number;
168
+ costUsd: number; // what the winner charged for these tokens
169
+ baselineUsd: number; // what the priciest configured route would cost → savings = baselineUsd - costUsd
170
+ }
171
+ ```
172
+
111
173
  ## Supported providers
112
174
 
113
175
  Any OpenAI-compatible endpoint works — and so does any AI SDK provider package, including a model vendor's own official API.
@@ -225,6 +287,7 @@ Two OpenAI-compatible providers, same probe, same day. Cells cover both families
225
287
 
226
288
  - [x] Own failover engine — cheapest-first routing + streaming-safe fallback, no external routing dependency
227
289
  - [x] Real per-call cost accounting (`onCost`)
290
+ - [x] One correlated record per request with the full failover chain (`onCall` + `formatCallRecord`)
228
291
  - [x] Auto cheapest-first ordering (`autoSort`) from per-provider `cost`
229
292
  - [x] Offline capability + cost check (`scripts/check-provider.sh`) → per-model trust matrix
230
293
  - [ ] Bundled price table for zero-config pricing (drop the manual `cost` numbers)