@broberg/ai-sdk 0.28.0 → 0.30.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
@@ -1,34 +1,16 @@
1
1
  import {
2
+ DEFAULT_TIER_MAP,
2
3
  ModelUnavailableError,
3
4
  listModels,
4
5
  providerIds,
5
6
  resetRegistry,
6
7
  resolveModel,
8
+ resolveTier,
7
9
  setAvailability
8
- } from "./chunk-IT7HNKLY.js";
10
+ } from "./chunk-V2PD522L.js";
9
11
  import {
10
12
  getPrice
11
- } from "./chunk-LUPUAAHZ.js";
12
-
13
- // src/routing/tier-map.ts
14
- var DEFAULT_TIER_MAP = {
15
- fast: { provider: "mistral", model: "mistral-small-latest", transport: "http" },
16
- smart: { provider: "mistral", model: "mistral-large-latest", transport: "http" },
17
- powerful: { provider: "mistral", model: "mistral-large-latest", transport: "http" },
18
- cheap: { provider: "mistral", model: "mistral-small-latest", transport: "http" },
19
- // Vision: small-latest (vision-capable, cheap EU) is the default; override to
20
- // mistral-large-latest for demanding image/spatial/composition work.
21
- vision: { provider: "mistral", model: "mistral-small-latest", transport: "http" },
22
- // Native video understanding — Gemini leads; flash-lite is the cheap default (F019).
23
- // NOT Anthropic → out of the F030 phase-out (its own EU epic if/when needed).
24
- video: { provider: "gemini", model: "gemini-2.5-flash-lite", transport: "http" },
25
- // NOT Anthropic → out of F030 (EU-embedding migration is its own future epic).
26
- embedding: { provider: "openai", model: "text-embedding-3-small", transport: "http" }
27
- };
28
- function resolveTier(tier, override, configMap) {
29
- const base = configMap?.[tier] ?? DEFAULT_TIER_MAP[tier];
30
- return { ...base, ...override };
31
- }
13
+ } from "./chunk-LKVCPMVI.js";
32
14
 
33
15
  // src/transport/http.ts
34
16
  async function httpTransport(req) {
@@ -541,6 +523,7 @@ function makeOpenAICompatibleAdapter(config) {
541
523
  if (req.maxTokens !== void 0) body.max_tokens = req.maxTokens;
542
524
  if (req.temperature !== void 0) body.temperature = req.temperature;
543
525
  if (req.responseFormat === "json") body.response_format = { type: "json_object" };
526
+ if (req.promptCacheKey !== void 0) body.prompt_cache_key = req.promptCacheKey;
544
527
  if (config.costFromResponseField) body.usage = { include: true };
545
528
  const res = await httpTransport({
546
529
  spec: req.spec,
@@ -563,13 +546,19 @@ function makeOpenAICompatibleAdapter(config) {
563
546
  const toolCalls = msg?.tool_calls?.map(
564
547
  (tc) => fromProviderToolCall(tc, "openai")
565
548
  );
549
+ const cachedIn = data.usage?.prompt_tokens_details?.cached_tokens;
566
550
  const usage = freshUsage({
567
551
  provider: config.name,
568
552
  model: req.spec.model,
569
553
  transport: "http",
570
554
  capability: "chat",
571
- inputTokens: data.usage?.prompt_tokens ?? 0,
572
- outputTokens: data.usage?.completion_tokens ?? 0
555
+ // prompt_tokens INCLUDES the cached ones; computeCost adds cacheReadTokens on
556
+ // top of inputTokens, so billing the raw figure would charge the cached prefix
557
+ // twice — at full rate AND at the cache rate.
558
+ inputTokens: (data.usage?.prompt_tokens ?? 0) - (cachedIn ?? 0),
559
+ outputTokens: data.usage?.completion_tokens ?? 0,
560
+ // Absent field stays undefined: "not reported" is not the same as "zero cached".
561
+ ...cachedIn === void 0 ? {} : { cacheReadTokens: cachedIn }
573
562
  });
574
563
  if (config.costFromResponseField && typeof data.usage?.cost === "number") {
575
564
  usage.costUsd = data.usage.cost;
@@ -2637,6 +2626,9 @@ var callOptions = {
2637
2626
  labels: z.record(z.string(), z.string()).optional()
2638
2627
  };
2639
2628
  var chatInputSchema = z.object({
2629
+ /** F039 — reuse a cached prompt prefix on Mistral at 10% of the input rate.
2630
+ * A stable id for "the same conversation" (conversation id, session id). */
2631
+ promptCacheKey: z.string().optional(),
2640
2632
  prompt: z.string().optional(),
2641
2633
  messages: z.array(messageSchema).optional(),
2642
2634
  system: z.string().optional(),
@@ -2761,7 +2753,15 @@ var ttsInputSchema = z.object({
2761
2753
  text: z.string(),
2762
2754
  voice: z.string(),
2763
2755
  /** F037: voice to use if `voice` is one we know the provider has retired. Without
2764
- * it a retired voice throws VoiceUnavailableError rather than reaching the API. */
2756
+ * it a retired voice throws VoiceUnavailableError rather than reaching the API.
2757
+ *
2758
+ * THINK BEFORE SETTING THIS. It is right for batch work where any acceptable voice
2759
+ * will do (a generated podcast, a bulk render). It is WRONG where the voice is an
2760
+ * identity a human recognises — a brand voice, a house narrator: falling back means
2761
+ * your product suddenly speaks with a stranger's voice and says nothing about why.
2762
+ * There, leave it unset and let the throw alarm you. Silence WITH a message beats
2763
+ * the wrong audio WITHOUT one. (torrent-search-api's call on their own house voice,
2764
+ * 2026-08-11 — they took the alarm over the fallback, deliberately.) */
2765
2765
  voiceFallback: z.string().optional(),
2766
2766
  lang: z.string().optional(),
2767
2767
  format: z.string().optional(),
@@ -2790,8 +2790,8 @@ var aiConfigSchema = z.object({
2790
2790
  });
2791
2791
 
2792
2792
  // src/version.ts
2793
- var VERSION = "0.28.0";
2794
- var SDK_TAG = "@broberg/ai-sdk@0.28.0";
2793
+ var VERSION = "0.30.0";
2794
+ var SDK_TAG = "@broberg/ai-sdk@0.30.0";
2795
2795
 
2796
2796
  // src/cost/sinks/upmetrics.ts
2797
2797
  function upmetricsSink(config) {
@@ -3075,7 +3075,7 @@ function createAI(config = {}) {
3075
3075
  invoke: async (spec) => {
3076
3076
  const adapter = pickProvider(spec.provider);
3077
3077
  if (!adapter.chat) throw new Error(`createAI: provider "${spec.provider}" does not support chat`);
3078
- return adapter.chat({ messages, spec, tools: input.tools, maxTokens: input.maxTokens, temperature: input.temperature, responseFormat: input.responseFormat });
3078
+ return adapter.chat({ messages, spec, tools: input.tools, maxTokens: input.maxTokens, temperature: input.temperature, responseFormat: input.responseFormat, promptCacheKey: input.promptCacheKey });
3079
3079
  }
3080
3080
  });
3081
3081
  },