@broberg/ai-sdk 0.30.0 → 0.31.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.d.ts CHANGED
@@ -121,8 +121,20 @@ interface ChatRequest {
121
121
  * conversation" (a conversation id, a session id). Requests sharing a prefix AND
122
122
  * this key reuse the cached prefix at 10% of the input rate. Not auto-generated:
123
123
  * only the caller knows what "the same conversation" means, and an SDK-invented
124
- * key would either collide across tenants or never hit. */
124
+ * key would either collide across tenants or never hit.
125
+ *
126
+ * BIND IT TO THE TENANT, not only to the conversation. This key IS a shared-prefix
127
+ * identity: two callers passing the same key can be served each other's cached
128
+ * prefix. So two tenants with identical transcripts must still get DIFFERENT keys
129
+ * — derive it from (tenant, conversation), never from the conversation alone.
130
+ * (components' requirement while building @broberg/chat, 2026-08-27, where two
131
+ * knowledge bases are written for readers with different permissions.) */
125
132
  promptCacheKey?: string;
133
+ /** F039.2 — prompt caching is ON by default where the provider supports it.
134
+ * Set false to opt out (e.g. a one-shot call whose prefix will never repeat).
135
+ * Off has no cost benefit and no penalty; a cache MISS was measured to cost
136
+ * exactly the same as a call with no key at all. */
137
+ promptCache?: boolean;
126
138
  temperature?: number;
127
139
  /** "json" → request JSON-object output where the provider supports it (F009). */
128
140
  responseFormat?: "json" | "text";
@@ -539,7 +551,6 @@ declare const messageSchema: z.ZodObject<{
539
551
  }>, "many">>;
540
552
  toolCallId: z.ZodOptional<z.ZodString>;
541
553
  }, "strip", z.ZodTypeAny, {
542
- role: "system" | "user" | "assistant" | "tool";
543
554
  content: string | ({
544
555
  text: string;
545
556
  type: "text";
@@ -548,6 +559,7 @@ declare const messageSchema: z.ZodObject<{
548
559
  type: "image";
549
560
  mimeType?: string | undefined;
550
561
  })[];
562
+ role: "system" | "user" | "assistant" | "tool";
551
563
  toolCalls?: {
552
564
  id: string;
553
565
  name: string;
@@ -555,7 +567,6 @@ declare const messageSchema: z.ZodObject<{
555
567
  }[] | undefined;
556
568
  toolCallId?: string | undefined;
557
569
  }, {
558
- role: "system" | "user" | "assistant" | "tool";
559
570
  content: string | ({
560
571
  text: string;
561
572
  type: "text";
@@ -564,6 +575,7 @@ declare const messageSchema: z.ZodObject<{
564
575
  type: "image";
565
576
  mimeType?: string | undefined;
566
577
  })[];
578
+ role: "system" | "user" | "assistant" | "tool";
567
579
  toolCalls?: {
568
580
  id: string;
569
581
  name: string;
@@ -602,8 +614,13 @@ declare const chatInputSchema: z.ZodObject<{
602
614
  purpose: z.ZodOptional<z.ZodString>;
603
615
  labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
604
616
  /** F039 — reuse a cached prompt prefix on Mistral at 10% of the input rate.
605
- * A stable id for "the same conversation" (conversation id, session id). */
617
+ * A stable id for "the same conversation" (conversation id, session id).
618
+ * Derive it from (tenant, conversation): the key is a shared-prefix identity,
619
+ * so two tenants with identical transcripts must get different keys. */
606
620
  promptCacheKey: z.ZodOptional<z.ZodString>;
621
+ /** F039.2 — prompt caching is ON by default on providers that support it.
622
+ * false opts out; an explicit promptCacheKey always wins. */
623
+ promptCache: z.ZodOptional<z.ZodBoolean>;
607
624
  prompt: z.ZodOptional<z.ZodString>;
608
625
  messages: z.ZodOptional<z.ZodArray<z.ZodObject<{
609
626
  role: z.ZodEnum<["system", "user", "assistant", "tool"]>;
@@ -644,7 +661,6 @@ declare const chatInputSchema: z.ZodObject<{
644
661
  }>, "many">>;
645
662
  toolCallId: z.ZodOptional<z.ZodString>;
646
663
  }, "strip", z.ZodTypeAny, {
647
- role: "system" | "user" | "assistant" | "tool";
648
664
  content: string | ({
649
665
  text: string;
650
666
  type: "text";
@@ -653,6 +669,7 @@ declare const chatInputSchema: z.ZodObject<{
653
669
  type: "image";
654
670
  mimeType?: string | undefined;
655
671
  })[];
672
+ role: "system" | "user" | "assistant" | "tool";
656
673
  toolCalls?: {
657
674
  id: string;
658
675
  name: string;
@@ -660,7 +677,6 @@ declare const chatInputSchema: z.ZodObject<{
660
677
  }[] | undefined;
661
678
  toolCallId?: string | undefined;
662
679
  }, {
663
- role: "system" | "user" | "assistant" | "tool";
664
680
  content: string | ({
665
681
  text: string;
666
682
  type: "text";
@@ -669,6 +685,7 @@ declare const chatInputSchema: z.ZodObject<{
669
685
  type: "image";
670
686
  mimeType?: string | undefined;
671
687
  })[];
688
+ role: "system" | "user" | "assistant" | "tool";
672
689
  toolCalls?: {
673
690
  id: string;
674
691
  name: string;
@@ -699,7 +716,6 @@ declare const chatInputSchema: z.ZodObject<{
699
716
  tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
700
717
  prompt?: string | undefined;
701
718
  messages?: {
702
- role: "system" | "user" | "assistant" | "tool";
703
719
  content: string | ({
704
720
  text: string;
705
721
  type: "text";
@@ -708,6 +724,7 @@ declare const chatInputSchema: z.ZodObject<{
708
724
  type: "image";
709
725
  mimeType?: string | undefined;
710
726
  })[];
727
+ role: "system" | "user" | "assistant" | "tool";
711
728
  toolCalls?: {
712
729
  id: string;
713
730
  name: string;
@@ -723,6 +740,7 @@ declare const chatInputSchema: z.ZodObject<{
723
740
  temperature?: number | undefined;
724
741
  purpose?: string | undefined;
725
742
  promptCacheKey?: string | undefined;
743
+ promptCache?: boolean | undefined;
726
744
  maxTokens?: number | undefined;
727
745
  responseFormat?: "text" | "json" | undefined;
728
746
  override?: {
@@ -741,7 +759,6 @@ declare const chatInputSchema: z.ZodObject<{
741
759
  tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
742
760
  prompt?: string | undefined;
743
761
  messages?: {
744
- role: "system" | "user" | "assistant" | "tool";
745
762
  content: string | ({
746
763
  text: string;
747
764
  type: "text";
@@ -750,6 +767,7 @@ declare const chatInputSchema: z.ZodObject<{
750
767
  type: "image";
751
768
  mimeType?: string | undefined;
752
769
  })[];
770
+ role: "system" | "user" | "assistant" | "tool";
753
771
  toolCalls?: {
754
772
  id: string;
755
773
  name: string;
@@ -765,6 +783,7 @@ declare const chatInputSchema: z.ZodObject<{
765
783
  temperature?: number | undefined;
766
784
  purpose?: string | undefined;
767
785
  promptCacheKey?: string | undefined;
786
+ promptCache?: boolean | undefined;
768
787
  maxTokens?: number | undefined;
769
788
  responseFormat?: "text" | "json" | undefined;
770
789
  override?: {
@@ -1707,6 +1726,9 @@ declare const ttsInputSchema: z.ZodObject<{
1707
1726
  rate?: number | undefined;
1708
1727
  }>;
1709
1728
  declare const aiConfigSchema: z.ZodObject<{
1729
+ /** F039.2 — client-wide opt-out from prompt caching (default: on where the
1730
+ * provider supports it). A per-call `promptCache` wins over this. */
1731
+ promptCache: z.ZodOptional<z.ZodBoolean>;
1710
1732
  defaults: z.ZodOptional<z.ZodRecord<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>, z.ZodObject<{
1711
1733
  provider: z.ZodString;
1712
1734
  model: z.ZodString;
@@ -1746,6 +1768,7 @@ declare const aiConfigSchema: z.ZodObject<{
1746
1768
  autoResolve?: boolean | undefined;
1747
1769
  }>>;
1748
1770
  }, "strip", z.ZodTypeAny, {
1771
+ promptCache?: boolean | undefined;
1749
1772
  defaults?: Partial<Record<"fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding", {
1750
1773
  provider: string;
1751
1774
  model: string;
@@ -1762,6 +1785,7 @@ declare const aiConfigSchema: z.ZodObject<{
1762
1785
  autoResolve?: boolean | undefined;
1763
1786
  } | undefined;
1764
1787
  }, {
1788
+ promptCache?: boolean | undefined;
1765
1789
  defaults?: Partial<Record<"fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding", {
1766
1790
  provider: string;
1767
1791
  model: string;
@@ -2051,6 +2075,13 @@ interface OpenAICompatibleConfig {
2051
2075
  * response's `usage.cost` (USD) as costUsd, falling back to the pricing table.
2052
2076
  * Only OpenRouter returns this field — openai/deepinfra leave it false. */
2053
2077
  costFromResponseField?: boolean;
2078
+ /** F039.2 — this provider accepts `prompt_cache_key` and caches a shared prefix
2079
+ * on it. Mistral only, deliberately: an unknown field is ignored by some
2080
+ * OpenAI-compatible servers and rejected with a 400 by others, so sending it
2081
+ * everywhere would trade a saving for an outage. Providers that cache
2082
+ * AUTOMATICALLY (openai, deepseek, gemini) need no key — they only need their
2083
+ * cached counts read back, which happens for every provider below. */
2084
+ supportsPromptCacheKey?: boolean;
2054
2085
  }
2055
2086
  declare function makeOpenAICompatibleAdapter(config: OpenAICompatibleConfig): ProviderAdapter;
2056
2087
 
@@ -2072,8 +2103,8 @@ declare const falStubAdapter: ProviderAdapter;
2072
2103
  * wires the live adapters. */
2073
2104
  declare const stubProviders: Record<string, ProviderAdapter>;
2074
2105
 
2075
- declare const VERSION: "0.30.0";
2076
- declare const SDK_TAG: "@broberg/ai-sdk@0.30.0";
2106
+ declare const VERSION: "0.31.0";
2107
+ declare const SDK_TAG: "@broberg/ai-sdk@0.31.0";
2077
2108
 
2078
2109
  /** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
2079
2110
  * per-call override.
package/dist/index.js CHANGED
@@ -485,6 +485,16 @@ function mapAnthropicStop(reason) {
485
485
  }
486
486
 
487
487
  // src/providers/openai-compatible.ts
488
+ function autoCacheKey(messages) {
489
+ const system = messages.find((m) => m.role === "system");
490
+ if (!system || typeof system.content !== "string" || system.content.length < 200) return void 0;
491
+ let h = 2166136261;
492
+ for (let i = 0; i < system.content.length; i++) {
493
+ h ^= system.content.charCodeAt(i);
494
+ h = Math.imul(h, 16777619) >>> 0;
495
+ }
496
+ return `ai-sdk-auto-${h.toString(16)}-${system.content.length}`;
497
+ }
488
498
  function toOpenAIMessage(m) {
489
499
  if (typeof m.content === "string") {
490
500
  const base = { role: m.role, content: m.content };
@@ -523,7 +533,10 @@ function makeOpenAICompatibleAdapter(config) {
523
533
  if (req.maxTokens !== void 0) body.max_tokens = req.maxTokens;
524
534
  if (req.temperature !== void 0) body.temperature = req.temperature;
525
535
  if (req.responseFormat === "json") body.response_format = { type: "json_object" };
526
- if (req.promptCacheKey !== void 0) body.prompt_cache_key = req.promptCacheKey;
536
+ if (config.supportsPromptCacheKey && req.promptCache !== false) {
537
+ const k = req.promptCacheKey ?? autoCacheKey(req.messages);
538
+ if (k !== void 0) body.prompt_cache_key = k;
539
+ }
527
540
  if (config.costFromResponseField) body.usage = { include: true };
528
541
  const res = await httpTransport({
529
542
  spec: req.spec,
@@ -1150,7 +1163,7 @@ var VOXTRAL_PRICE_PER_MIN = {
1150
1163
  };
1151
1164
  function mistralAdapter(config = {}) {
1152
1165
  const baseUrl = config.baseUrl ?? "https://api.mistral.ai/v1";
1153
- const base = makeOpenAICompatibleAdapter({ name: "mistral", baseUrl, apiKey: config.apiKey });
1166
+ const base = makeOpenAICompatibleAdapter({ name: "mistral", baseUrl, apiKey: config.apiKey, supportsPromptCacheKey: true });
1154
1167
  function key() {
1155
1168
  const k = config.apiKey ?? process.env.MISTRAL_API_KEY;
1156
1169
  if (!k) throw new Error("mistral adapter: API key not set (env MISTRAL_API_KEY)");
@@ -2627,8 +2640,13 @@ var callOptions = {
2627
2640
  };
2628
2641
  var chatInputSchema = z.object({
2629
2642
  /** 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). */
2643
+ * A stable id for "the same conversation" (conversation id, session id).
2644
+ * Derive it from (tenant, conversation): the key is a shared-prefix identity,
2645
+ * so two tenants with identical transcripts must get different keys. */
2631
2646
  promptCacheKey: z.string().optional(),
2647
+ /** F039.2 — prompt caching is ON by default on providers that support it.
2648
+ * false opts out; an explicit promptCacheKey always wins. */
2649
+ promptCache: z.boolean().optional(),
2632
2650
  prompt: z.string().optional(),
2633
2651
  messages: z.array(messageSchema).optional(),
2634
2652
  system: z.string().optional(),
@@ -2777,6 +2795,9 @@ var availabilitySchema = z.object({
2777
2795
  fallback: z.union([z.string(), z.array(z.string())]).optional()
2778
2796
  });
2779
2797
  var aiConfigSchema = z.object({
2798
+ /** F039.2 — client-wide opt-out from prompt caching (default: on where the
2799
+ * provider supports it). A per-call `promptCache` wins over this. */
2800
+ promptCache: z.boolean().optional(),
2780
2801
  defaults: z.record(tierSchema, tierSpecSchema).optional(),
2781
2802
  // Functions can't be deeply validated — z.custom asserts the TS type and
2782
2803
  // passes the value through untouched.
@@ -2790,8 +2811,8 @@ var aiConfigSchema = z.object({
2790
2811
  });
2791
2812
 
2792
2813
  // src/version.ts
2793
- var VERSION = "0.30.0";
2794
- var SDK_TAG = "@broberg/ai-sdk@0.30.0";
2814
+ var VERSION = "0.31.0";
2815
+ var SDK_TAG = "@broberg/ai-sdk@0.31.0";
2795
2816
 
2796
2817
  // src/cost/sinks/upmetrics.ts
2797
2818
  function upmetricsSink(config) {
@@ -3075,7 +3096,7 @@ function createAI(config = {}) {
3075
3096
  invoke: async (spec) => {
3076
3097
  const adapter = pickProvider(spec.provider);
3077
3098
  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, promptCacheKey: input.promptCacheKey });
3099
+ return adapter.chat({ messages, spec, tools: input.tools, maxTokens: input.maxTokens, temperature: input.temperature, responseFormat: input.responseFormat, promptCacheKey: input.promptCacheKey, promptCache: input.promptCache ?? cfg.promptCache });
3079
3100
  }
3080
3101
  });
3081
3102
  },