@broberg/ai-sdk 0.5.1 → 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,19 +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
256
  // Google Gemini (direct). Provider key is "gemini" — matches the adapter's
249
257
  // usage.provider + the override.provider callers pass. (Image-gen models are
250
258
  // priced per-image in the adapter, not here.)
251
- "gemini:gemini-2.5-flash": { inputPer1M: 0.3, outputPer1M: 2.5, version: V }
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 }
252
282
  };
253
283
  function getPrice(provider, model) {
254
284
  const exact = PRICING[`${provider}:${model}`];
@@ -302,6 +332,9 @@ function contentBlocks(content) {
302
332
  if (typeof content === "string") return content;
303
333
  return content.map((p) => {
304
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
+ }
305
338
  if (typeof p.image === "string" && /^https?:\/\//.test(p.image)) {
306
339
  return { type: "image", source: { type: "url", url: p.image } };
307
340
  }
@@ -552,6 +585,10 @@ function toOpenAIMessage(m) {
552
585
  }
553
586
  const content = m.content.map((p) => {
554
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
+ }
555
592
  const url = typeof p.image === "string" ? p.image : `data:${p.mimeType ?? "image/png"};base64,${Buffer.from(p.image).toString("base64")}`;
556
593
  return { type: "image_url", image_url: { url } };
557
594
  });
@@ -786,6 +823,10 @@ function partsFrom(content) {
786
823
  if (typeof content === "string") return [{ text: content }];
787
824
  return content.map((p) => {
788
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
+ }
789
830
  const data = typeof p.image === "string" ? p.image.replace(/^data:[^;]+;base64,/, "") : Buffer.from(p.image).toString("base64");
790
831
  return { inlineData: { mimeType: p.mimeType ?? "image/png", data } };
791
832
  });
@@ -983,6 +1024,15 @@ function openrouterAdapter(config = {}) {
983
1024
  });
984
1025
  }
985
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
+
986
1036
  // src/providers/fal.ts
987
1037
  var FAL_IMAGE_PRICE_ESTIMATE = {
988
1038
  "fal-ai/flux/schnell": 3e-3,
@@ -1070,6 +1120,7 @@ var defaultProviders = {
1070
1120
  gemini: geminiAdapter(),
1071
1121
  deepinfra: deepinfraAdapter(),
1072
1122
  openrouter: openrouterAdapter(),
1123
+ mistral: mistralAdapter(),
1073
1124
  fal: falAdapter()
1074
1125
  };
1075
1126
 
@@ -1144,6 +1195,20 @@ function buildVisionMessages(input) {
1144
1195
  ];
1145
1196
  }
1146
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
+
1147
1212
  // src/capabilities/translate.ts
1148
1213
  var TRANSLATE_DEFAULT_TIER = "fast";
1149
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.";
@@ -1279,6 +1344,7 @@ var tierSchema = z.enum([
1279
1344
  "powerful",
1280
1345
  "cheap",
1281
1346
  "vision",
1347
+ "video",
1282
1348
  "embedding"
1283
1349
  ]);
1284
1350
  var tierSpecSchema = z.object({
@@ -1336,6 +1402,12 @@ var visionInputSchema = z.object({
1336
1402
  mimeType: z.string().optional(),
1337
1403
  ...callOptions
1338
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
+ });
1339
1411
  var translateInputSchema = z.object({
1340
1412
  text: z.string(),
1341
1413
  to: z.string(),
@@ -1561,6 +1633,27 @@ function createAI(config = {}) {
1561
1633
  }
1562
1634
  });
1563
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
+ },
1564
1657
  async translate(input) {
1565
1658
  input = translateInputSchema.parse(input);
1566
1659
  const tier = input.tier ?? TRANSLATE_DEFAULT_TIER;
@@ -1727,8 +1820,8 @@ var stubProviders = {
1727
1820
  };
1728
1821
 
1729
1822
  // src/version.ts
1730
- var VERSION = "0.5.1";
1731
- var SDK_TAG = "@broberg/ai-sdk@0.5.1";
1823
+ var VERSION = "0.6.0";
1824
+ var SDK_TAG = "@broberg/ai-sdk@0.6.0";
1732
1825
 
1733
1826
  // src/cost/budget-store.ts
1734
1827
  function sqliteBudgetStore(config) {
@@ -1986,6 +2079,7 @@ export {
1986
2079
  makeContracts,
1987
2080
  makeOpenAICompatibleAdapter,
1988
2081
  messageSchema,
2082
+ mistralAdapter,
1989
2083
  multiSink,
1990
2084
  noopSink,
1991
2085
  openaiAdapter,