@broberg/ai-sdk 0.42.0 → 0.42.1

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
@@ -6,12 +6,51 @@ cost control on every call**.
6
6
  A provider-agnostic facade: your code calls `ai.chat()`, `ai.vision()`,
7
7
  `ai.image()` — never a provider SDK directly. Swap providers by changing a tier,
8
8
  not your call-sites. Every call returns a `Usage` (tokens, cost, latency,
9
- transport) and can fan that out to any cost sink.
9
+ transport, **data-residency region**) and can fan that out to any cost sink.
10
10
 
11
11
  ```bash
12
12
  bun add @broberg/ai-sdk # or: npm i @broberg/ai-sdk
13
13
  ```
14
14
 
15
+ ## Where did the data go? — `usage.region`
16
+
17
+ Every response carries the **data residency of the endpoint that actually answered**:
18
+
19
+ ```ts
20
+ const { text, usage } = await ai.chat({ prompt, tier: "smart" });
21
+ if (usage.region !== "eu") throw new Error(`personal data would have left the EU (${usage.region})`);
22
+ ```
23
+
24
+ **Only `"eu"` is a positive claim.** `"unknown"` means we cannot say, so
25
+ `region !== "us"` is **not** an EU check — it passes every single OpenRouter call.
26
+
27
+ `region` is derived from the **host** the request used, not from the provider's name —
28
+ `vertex`, `azure` and `bfl` are EU by default but each takes an override, so a
29
+ name-based table would report `eu` for a call someone had pointed at `us-central1`.
30
+
31
+ **Three things that decide whether your guard works:**
32
+
33
+ | | |
34
+ |---|---|
35
+ | **After the call** | `usage.region` — `"eu"` \| `"us"` \| `"cn"` \| `"unknown"` |
36
+ | **Before the call** | `regionOfHost(urlOrHostname)` — the only way to refuse *before* bytes leave |
37
+ | **Never** | `regionOfProvider(name)` — see below |
38
+
39
+ ```ts
40
+ import { regionOfHost } from "@broberg/ai-sdk";
41
+ if (regionOfHost("https://api.mistral.ai/v1") !== "eu") throw new Error("not EU");
42
+ ```
43
+
44
+ **Do not build a residency guard on `regionOfProvider`.** A name cannot answer for
45
+ hosting when the provider takes a `baseUrl`, so `regionOfProvider("mistral")` is
46
+ `"unknown"` — and a guard written as `regionOfProvider(p) === "eu"` therefore rejects
47
+ **Mistral, the only EU route there is.** Fail-closed and useless: it filters out the
48
+ thing it exists to allow.
49
+
50
+ An allowlist of `(provider, model)` pairs has the same hole from the other side: a
51
+ gateway in front of Mistral is still `"mistral"`/`"mistral-large-latest"`, matches both
52
+ fields, and passes. `regionOfHost` answers `"unknown"` for it, which is the honest answer.
53
+
15
54
  ## Quick start
16
55
 
17
56
  ```ts
package/dist/index.d.ts CHANGED
@@ -2366,8 +2366,8 @@ declare const falStubAdapter: ProviderAdapter;
2366
2366
  * wires the live adapters. */
2367
2367
  declare const stubProviders: Record<string, ProviderAdapter>;
2368
2368
 
2369
- declare const VERSION: "0.42.0";
2370
- declare const SDK_TAG: "@broberg/ai-sdk@0.42.0";
2369
+ declare const VERSION: "0.42.1";
2370
+ declare const SDK_TAG: "@broberg/ai-sdk@0.42.1";
2371
2371
 
2372
2372
  /** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
2373
2373
  * per-call override.
package/dist/index.js CHANGED
@@ -3110,8 +3110,8 @@ var aiConfigSchema = z.object({
3110
3110
  });
3111
3111
 
3112
3112
  // src/version.ts
3113
- var VERSION = "0.42.0";
3114
- var SDK_TAG = "@broberg/ai-sdk@0.42.0";
3113
+ var VERSION = "0.42.1";
3114
+ var SDK_TAG = "@broberg/ai-sdk@0.42.1";
3115
3115
 
3116
3116
  // src/cost/sinks/upmetrics.ts
3117
3117
  function upmetricsSink(config) {