@broberg/ai-sdk 0.5.0 → 0.6.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/dist/index.js CHANGED
@@ -5,6 +5,8 @@ var DEFAULT_TIER_MAP = {
5
5
  powerful: { provider: "anthropic", model: "claude-opus-4-8", transport: "http" },
6
6
  cheap: { provider: "anthropic", model: "claude-haiku-4-5", transport: "subprocess" },
7
7
  vision: { provider: "anthropic", model: "claude-sonnet-4-6", transport: "http" },
8
+ // Native video understanding — Gemini leads; flash-lite is the cheap default (F019).
9
+ video: { provider: "gemini", model: "gemini-2.5-flash-lite", transport: "http" },
8
10
  embedding: { provider: "openai", model: "text-embedding-3-small", transport: "http" }
9
11
  };
10
12
  function resolveTier(tier, override, configMap) {
@@ -205,6 +207,7 @@ function parseArgs(raw) {
205
207
 
206
208
  // src/cost/pricing.ts
207
209
  var V = "2026-06-02";
210
+ var MS = "2026-06-04-mistral.ai";
208
211
  var PRICING = {
209
212
  // Anthropic (direct API). DEFAULT_TIER_MAP: fast/cheap=haiku, smart/vision=sonnet, powerful=opus.
210
213
  "anthropic:claude-haiku-4-5": {
@@ -236,17 +239,46 @@ var PRICING = {
236
239
  // Whisper is priced per minute, not per token — not representable here; transcribe
237
240
  // (F5.6) computes its own cost. Listed as 0 so token-based compute never charges it.
238
241
  "openai:whisper-1": { inputPer1M: 0, outputPer1M: 0, version: V },
239
- // OpenRouter (meta-router — model slugs include the upstream vendor).
240
- "openrouter:anthropic/claude-sonnet-4-6": { inputPer1M: 3, outputPer1M: 15, version: V },
241
- "openrouter:anthropic/claude-haiku-4-5": { inputPer1M: 0.8, outputPer1M: 4, version: V },
242
+ // OpenRouter (meta-router — model slugs include the upstream vendor). Slugs use
243
+ // dots (claude-sonnet-4.6) to match OpenRouter's live ids; the dashed forms
244
+ // never matched a real call. Caught by the F014 catalogue research.
245
+ "openrouter:anthropic/claude-sonnet-4.6": { inputPer1M: 3, outputPer1M: 15, version: V },
246
+ // OpenRouter ground-truth $1/$5 — a markup over Anthropic-direct's $0.8/$4
247
+ // (the `anthropic:` entry above). Was masked while the slug used dashes.
248
+ "openrouter:anthropic/claude-haiku-4.5": { inputPer1M: 1, outputPer1M: 5, version: "2026-06-04" },
242
249
  "openrouter:google/gemini-2.5-flash": { inputPer1M: 0.3, outputPer1M: 2.5, version: V },
250
+ // Ground-truth from OpenRouter /api/v1/models (was a 0.3 estimate; now 0.279).
243
251
  "openrouter:minimax/minimax-m2.7": {
244
- inputPer1M: 0.3,
252
+ inputPer1M: 0.279,
245
253
  outputPer1M: 1.2,
246
- version: `${V}-estimate`
254
+ version: "2026-06-04"
247
255
  },
248
- // Google Gemini (direct). Image-gen model used by cms.
249
- "google:gemini-2.5-flash": { inputPer1M: 0.3, outputPer1M: 2.5, version: V }
256
+ // Google Gemini (direct). Provider key is "gemini" — matches the adapter's
257
+ // usage.provider + the override.provider callers pass. (Image-gen models are
258
+ // priced per-image in the adapter, not here.)
259
+ "gemini:gemini-2.5-flash": { inputPer1M: 0.3, outputPer1M: 2.5, version: V },
260
+ // flash-lite is the default `video` tier (F019) — cheap native video understanding.
261
+ "gemini:gemini-2.5-flash-lite": { inputPer1M: 0.1, outputPer1M: 0.4, version: "2026-06-04-or-xref" },
262
+ // Mistral (direct, La Plateforme). Official prices from mistral.ai/pricing
263
+ // (2026-06-04, per Christian's CD report). EU/Paris-hosted — the designated
264
+ // GDPR-safe provider for client/personal-data workloads (see F015). NB:
265
+ // medium-3.5 is the premium "Vibe" coding tier ($1.5/$7.5); Large 3 ($0.5/$1.5)
266
+ // is the cheaper frontier general-purpose model despite the higher number.
267
+ "mistral:mistral-large-latest": { inputPer1M: 0.5, outputPer1M: 1.5, version: MS },
268
+ "mistral:mistral-large-2512": { inputPer1M: 0.5, outputPer1M: 1.5, version: MS },
269
+ "mistral:mistral-medium-latest": { inputPer1M: 1.5, outputPer1M: 7.5, version: MS },
270
+ "mistral:mistral-medium-3.5": { inputPer1M: 1.5, outputPer1M: 7.5, version: MS },
271
+ "mistral:mistral-medium-3": { inputPer1M: 0.4, outputPer1M: 2, version: "2026-06-04-or-xref" },
272
+ "mistral:mistral-small-latest": { inputPer1M: 0.1, outputPer1M: 0.3, version: MS },
273
+ "mistral:mistral-small-2603": { inputPer1M: 0.1, outputPer1M: 0.3, version: MS },
274
+ "mistral:ministral-3b-latest": { inputPer1M: 0.1, outputPer1M: 0.1, version: MS },
275
+ "mistral:ministral-8b-latest": { inputPer1M: 0.15, outputPer1M: 0.15, version: MS },
276
+ "mistral:ministral-14b-latest": { inputPer1M: 0.2, outputPer1M: 0.2, version: MS },
277
+ "mistral:magistral-medium-latest": { inputPer1M: 2, outputPer1M: 5, version: MS },
278
+ "mistral:magistral-small-latest": { inputPer1M: 0.5, outputPer1M: 1.5, version: MS },
279
+ "mistral:devstral-latest": { inputPer1M: 0.4, outputPer1M: 2, version: MS },
280
+ "mistral:codestral-latest": { inputPer1M: 0.3, outputPer1M: 0.9, version: MS },
281
+ "mistral:open-mistral-nemo": { inputPer1M: 0.15, outputPer1M: 0.15, version: MS }
250
282
  };
251
283
  function getPrice(provider, model) {
252
284
  const exact = PRICING[`${provider}:${model}`];
@@ -300,6 +332,9 @@ function contentBlocks(content) {
300
332
  if (typeof content === "string") return content;
301
333
  return content.map((p) => {
302
334
  if (p.type === "text") return { type: "text", text: p.text };
335
+ if (p.type === "video") {
336
+ throw new Error("anthropic adapter: video input is not supported \u2014 use a video provider (e.g. gemini)");
337
+ }
303
338
  if (typeof p.image === "string" && /^https?:\/\//.test(p.image)) {
304
339
  return { type: "image", source: { type: "url", url: p.image } };
305
340
  }
@@ -550,6 +585,10 @@ function toOpenAIMessage(m) {
550
585
  }
551
586
  const content = m.content.map((p) => {
552
587
  if (p.type === "text") return { type: "text", text: p.text };
588
+ if (p.type === "video") {
589
+ const url2 = typeof p.video === "string" ? p.video : `data:${p.mimeType ?? "video/mp4"};base64,${Buffer.from(p.video).toString("base64")}`;
590
+ return { type: "video_url", video_url: { url: url2 } };
591
+ }
553
592
  const url = typeof p.image === "string" ? p.image : `data:${p.mimeType ?? "image/png"};base64,${Buffer.from(p.image).toString("base64")}`;
554
593
  return { type: "image_url", image_url: { url } };
555
594
  });
@@ -784,6 +823,10 @@ function partsFrom(content) {
784
823
  if (typeof content === "string") return [{ text: content }];
785
824
  return content.map((p) => {
786
825
  if (p.type === "text") return { text: p.text };
826
+ if (p.type === "video") {
827
+ const data2 = typeof p.video === "string" ? p.video.replace(/^data:[^;]+;base64,/, "") : Buffer.from(p.video).toString("base64");
828
+ return { inlineData: { mimeType: p.mimeType ?? "video/mp4", data: data2 } };
829
+ }
787
830
  const data = typeof p.image === "string" ? p.image.replace(/^data:[^;]+;base64,/, "") : Buffer.from(p.image).toString("base64");
788
831
  return { inlineData: { mimeType: p.mimeType ?? "image/png", data } };
789
832
  });
@@ -981,6 +1024,15 @@ function openrouterAdapter(config = {}) {
981
1024
  });
982
1025
  }
983
1026
 
1027
+ // src/providers/mistral.ts
1028
+ function mistralAdapter(config = {}) {
1029
+ return makeOpenAICompatibleAdapter({
1030
+ name: "mistral",
1031
+ baseUrl: config.baseUrl ?? "https://api.mistral.ai/v1",
1032
+ apiKey: config.apiKey
1033
+ });
1034
+ }
1035
+
984
1036
  // src/providers/fal.ts
985
1037
  var FAL_IMAGE_PRICE_ESTIMATE = {
986
1038
  "fal-ai/flux/schnell": 3e-3,
@@ -1068,6 +1120,7 @@ var defaultProviders = {
1068
1120
  gemini: geminiAdapter(),
1069
1121
  deepinfra: deepinfraAdapter(),
1070
1122
  openrouter: openrouterAdapter(),
1123
+ mistral: mistralAdapter(),
1071
1124
  fal: falAdapter()
1072
1125
  };
1073
1126
 
@@ -1142,6 +1195,20 @@ function buildVisionMessages(input) {
1142
1195
  ];
1143
1196
  }
1144
1197
 
1198
+ // src/capabilities/video.ts
1199
+ var VIDEO_DEFAULT_TIER = "video";
1200
+ function buildVideoMessages(input) {
1201
+ return [
1202
+ {
1203
+ role: "user",
1204
+ content: [
1205
+ { type: "video", video: input.video, mimeType: input.mimeType },
1206
+ { type: "text", text: input.prompt }
1207
+ ]
1208
+ }
1209
+ ];
1210
+ }
1211
+
1145
1212
  // src/capabilities/translate.ts
1146
1213
  var TRANSLATE_DEFAULT_TIER = "fast";
1147
1214
  var TRANSLATE_SYSTEM = "You are a translation engine. Translate the user's text only. Return the translation and nothing else \u2014 no preamble, no quotes.";
@@ -1277,6 +1344,7 @@ var tierSchema = z.enum([
1277
1344
  "powerful",
1278
1345
  "cheap",
1279
1346
  "vision",
1347
+ "video",
1280
1348
  "embedding"
1281
1349
  ]);
1282
1350
  var tierSpecSchema = z.object({
@@ -1334,6 +1402,12 @@ var visionInputSchema = z.object({
1334
1402
  mimeType: z.string().optional(),
1335
1403
  ...callOptions
1336
1404
  });
1405
+ var videoInputSchema = z.object({
1406
+ video: z.union([z.string(), z.instanceof(Uint8Array)]),
1407
+ prompt: z.string(),
1408
+ mimeType: z.string().optional(),
1409
+ ...callOptions
1410
+ });
1337
1411
  var translateInputSchema = z.object({
1338
1412
  text: z.string(),
1339
1413
  to: z.string(),
@@ -1559,6 +1633,27 @@ function createAI(config = {}) {
1559
1633
  }
1560
1634
  });
1561
1635
  },
1636
+ async video(input) {
1637
+ input = videoInputSchema.parse(input);
1638
+ const tier = input.tier ?? VIDEO_DEFAULT_TIER;
1639
+ const messages = buildVideoMessages(input);
1640
+ return runCapability({
1641
+ primary: resolveTier(tier, input.override, cfg.defaults),
1642
+ fallback: input.fallback,
1643
+ capability: "video",
1644
+ tier,
1645
+ purpose: input.purpose,
1646
+ labels: input.labels,
1647
+ estIn: estTokens(input.prompt) + 4e3,
1648
+ // prompt + video tokens (native video ≈ frames)
1649
+ estOut: 512,
1650
+ invoke: async (spec) => {
1651
+ const adapter = pickProvider(spec.provider);
1652
+ if (!adapter.vision) throw new Error(`createAI: provider "${spec.provider}" does not support video`);
1653
+ return adapter.vision({ messages, spec });
1654
+ }
1655
+ });
1656
+ },
1562
1657
  async translate(input) {
1563
1658
  input = translateInputSchema.parse(input);
1564
1659
  const tier = input.tier ?? TRANSLATE_DEFAULT_TIER;
@@ -1725,8 +1820,8 @@ var stubProviders = {
1725
1820
  };
1726
1821
 
1727
1822
  // src/version.ts
1728
- var VERSION = "0.5.0";
1729
- var SDK_TAG = "@broberg/ai-sdk@0.5.0";
1823
+ var VERSION = "0.6.0";
1824
+ var SDK_TAG = "@broberg/ai-sdk@0.6.0";
1730
1825
 
1731
1826
  // src/cost/budget-store.ts
1732
1827
  function sqliteBudgetStore(config) {
@@ -1984,6 +2079,7 @@ export {
1984
2079
  makeContracts,
1985
2080
  makeOpenAICompatibleAdapter,
1986
2081
  messageSchema,
2082
+ mistralAdapter,
1987
2083
  multiSink,
1988
2084
  noopSink,
1989
2085
  openaiAdapter,