@broberg/ai-sdk 0.6.0 → 0.7.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
@@ -13,7 +13,7 @@ interface TierSpec {
13
13
  transport: Transport;
14
14
  }
15
15
  /** High-level capability a call exercises. Mirrors the capability layer (F5). */
16
- type Capability = "chat" | "vision" | "video" | "translate" | "image" | "embedding" | "transcribe" | "mockup" | "design" | "extract" | "classify" | "rerank";
16
+ type Capability = "chat" | "vision" | "video" | "translate" | "image" | "embedding" | "transcribe" | "ocr" | "moderation" | "mockup" | "design" | "extract" | "classify" | "rerank";
17
17
  type Role = "system" | "user" | "assistant" | "tool";
18
18
  /** A piece of message content. Text everywhere; image parts feed vision. */
19
19
  type ContentPart = {
@@ -180,6 +180,34 @@ interface TranscribeResult {
180
180
  text: string;
181
181
  usage: Usage;
182
182
  }
183
+ interface OcrRequest {
184
+ /** A URL, data-URL, or raw bytes of the document/image. */
185
+ document: string | Uint8Array;
186
+ mimeType?: string;
187
+ spec: TierSpec;
188
+ }
189
+ interface OcrPage {
190
+ index: number;
191
+ markdown: string;
192
+ }
193
+ interface OcrResult {
194
+ pages: OcrPage[];
195
+ usage: Usage;
196
+ }
197
+ interface ModerationRequest {
198
+ input: string[];
199
+ spec: TierSpec;
200
+ }
201
+ interface ModerationItem {
202
+ /** True if any category tripped. */
203
+ flagged: boolean;
204
+ categories: Record<string, boolean>;
205
+ categoryScores: Record<string, number>;
206
+ }
207
+ interface ModerationResult {
208
+ results: ModerationItem[];
209
+ usage: Usage;
210
+ }
183
211
  /** The thin contract every provider implements (F4). A provider need only
184
212
  * support the capabilities it offers — `chat` is the baseline; vision/image/
185
213
  * embedding are optional and absence is a typed capability gap. */
@@ -196,6 +224,8 @@ interface ProviderAdapter {
196
224
  image?(req: ImageRequest): Promise<ImageResult>;
197
225
  embedding?(req: EmbeddingRequest): Promise<EmbeddingResult>;
198
226
  transcribe?(req: TranscribeRequest): Promise<TranscribeResult>;
227
+ ocr?(req: OcrRequest): Promise<OcrResult>;
228
+ moderate?(req: ModerationRequest): Promise<ModerationResult>;
199
229
  }
200
230
  interface TranslateResult {
201
231
  text: string;
@@ -971,6 +1001,135 @@ declare const transcribeInputSchema: z.ZodObject<{
971
1001
  labels?: Record<string, string> | undefined;
972
1002
  durationSec?: number | undefined;
973
1003
  }>;
1004
+ declare const ocrInputSchema: z.ZodObject<{
1005
+ tier: z.ZodOptional<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>>;
1006
+ override: z.ZodOptional<z.ZodObject<{
1007
+ provider: z.ZodOptional<z.ZodString>;
1008
+ model: z.ZodOptional<z.ZodString>;
1009
+ transport: z.ZodOptional<z.ZodEnum<["http", "subprocess"]>>;
1010
+ }, "strip", z.ZodTypeAny, {
1011
+ provider?: string | undefined;
1012
+ model?: string | undefined;
1013
+ transport?: "http" | "subprocess" | undefined;
1014
+ }, {
1015
+ provider?: string | undefined;
1016
+ model?: string | undefined;
1017
+ transport?: "http" | "subprocess" | undefined;
1018
+ }>>;
1019
+ fallback: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>, z.ZodObject<{
1020
+ provider: z.ZodString;
1021
+ model: z.ZodString;
1022
+ transport: z.ZodEnum<["http", "subprocess"]>;
1023
+ }, "strip", z.ZodTypeAny, {
1024
+ provider: string;
1025
+ model: string;
1026
+ transport: "http" | "subprocess";
1027
+ }, {
1028
+ provider: string;
1029
+ model: string;
1030
+ transport: "http" | "subprocess";
1031
+ }>]>, "many">>;
1032
+ purpose: z.ZodOptional<z.ZodString>;
1033
+ labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
1034
+ /** A URL, data-URL, or raw bytes of the document/image. */
1035
+ document: z.ZodUnion<[z.ZodString, z.ZodType<Uint8Array<ArrayBuffer>, z.ZodTypeDef, Uint8Array<ArrayBuffer>>]>;
1036
+ /** image/* → routed as an image; anything else → a document (PDF etc.). */
1037
+ mimeType: z.ZodOptional<z.ZodString>;
1038
+ }, "strip", z.ZodTypeAny, {
1039
+ document: string | Uint8Array<ArrayBuffer>;
1040
+ mimeType?: string | undefined;
1041
+ tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
1042
+ override?: {
1043
+ provider?: string | undefined;
1044
+ model?: string | undefined;
1045
+ transport?: "http" | "subprocess" | undefined;
1046
+ } | undefined;
1047
+ fallback?: ("fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | {
1048
+ provider: string;
1049
+ model: string;
1050
+ transport: "http" | "subprocess";
1051
+ })[] | undefined;
1052
+ purpose?: string | undefined;
1053
+ labels?: Record<string, string> | undefined;
1054
+ }, {
1055
+ document: string | Uint8Array<ArrayBuffer>;
1056
+ mimeType?: string | undefined;
1057
+ tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
1058
+ override?: {
1059
+ provider?: string | undefined;
1060
+ model?: string | undefined;
1061
+ transport?: "http" | "subprocess" | undefined;
1062
+ } | undefined;
1063
+ fallback?: ("fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | {
1064
+ provider: string;
1065
+ model: string;
1066
+ transport: "http" | "subprocess";
1067
+ })[] | undefined;
1068
+ purpose?: string | undefined;
1069
+ labels?: Record<string, string> | undefined;
1070
+ }>;
1071
+ declare const moderationInputSchema: z.ZodObject<{
1072
+ tier: z.ZodOptional<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>>;
1073
+ override: z.ZodOptional<z.ZodObject<{
1074
+ provider: z.ZodOptional<z.ZodString>;
1075
+ model: z.ZodOptional<z.ZodString>;
1076
+ transport: z.ZodOptional<z.ZodEnum<["http", "subprocess"]>>;
1077
+ }, "strip", z.ZodTypeAny, {
1078
+ provider?: string | undefined;
1079
+ model?: string | undefined;
1080
+ transport?: "http" | "subprocess" | undefined;
1081
+ }, {
1082
+ provider?: string | undefined;
1083
+ model?: string | undefined;
1084
+ transport?: "http" | "subprocess" | undefined;
1085
+ }>>;
1086
+ fallback: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>, z.ZodObject<{
1087
+ provider: z.ZodString;
1088
+ model: z.ZodString;
1089
+ transport: z.ZodEnum<["http", "subprocess"]>;
1090
+ }, "strip", z.ZodTypeAny, {
1091
+ provider: string;
1092
+ model: string;
1093
+ transport: "http" | "subprocess";
1094
+ }, {
1095
+ provider: string;
1096
+ model: string;
1097
+ transport: "http" | "subprocess";
1098
+ }>]>, "many">>;
1099
+ purpose: z.ZodOptional<z.ZodString>;
1100
+ labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
1101
+ input: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>;
1102
+ }, "strip", z.ZodTypeAny, {
1103
+ input: string | string[];
1104
+ tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
1105
+ override?: {
1106
+ provider?: string | undefined;
1107
+ model?: string | undefined;
1108
+ transport?: "http" | "subprocess" | undefined;
1109
+ } | undefined;
1110
+ fallback?: ("fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | {
1111
+ provider: string;
1112
+ model: string;
1113
+ transport: "http" | "subprocess";
1114
+ })[] | undefined;
1115
+ purpose?: string | undefined;
1116
+ labels?: Record<string, string> | undefined;
1117
+ }, {
1118
+ input: string | string[];
1119
+ tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
1120
+ override?: {
1121
+ provider?: string | undefined;
1122
+ model?: string | undefined;
1123
+ transport?: "http" | "subprocess" | undefined;
1124
+ } | undefined;
1125
+ fallback?: ("fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | {
1126
+ provider: string;
1127
+ model: string;
1128
+ transport: "http" | "subprocess";
1129
+ })[] | undefined;
1130
+ purpose?: string | undefined;
1131
+ labels?: Record<string, string> | undefined;
1132
+ }>;
974
1133
  declare const aiConfigSchema: z.ZodObject<{
975
1134
  defaults: z.ZodOptional<z.ZodRecord<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>, z.ZodObject<{
976
1135
  provider: z.ZodString;
@@ -1029,6 +1188,8 @@ type TranslateInput = z.infer<typeof translateInputSchema>;
1029
1188
  type ImageInput = z.infer<typeof imageInputSchema>;
1030
1189
  type EmbeddingInput = z.infer<typeof embeddingInputSchema>;
1031
1190
  type TranscribeInput = z.infer<typeof transcribeInputSchema>;
1191
+ type OcrInput = z.infer<typeof ocrInputSchema>;
1192
+ type ModerationInput = z.infer<typeof moderationInputSchema>;
1032
1193
  type AiConfig = z.infer<typeof aiConfigSchema>;
1033
1194
  /** The public facade. Defined here because it depends on the derived inputs. */
1034
1195
  interface AiClient {
@@ -1043,6 +1204,10 @@ interface AiClient {
1043
1204
  image(input: ImageInput): Promise<ImageResult>;
1044
1205
  embedding(input: EmbeddingInput): Promise<EmbeddingResult>;
1045
1206
  transcribe(input: TranscribeInput): Promise<TranscribeResult>;
1207
+ /** OCR (F016.2) — document/image → structured markdown, billed per page. Mistral. */
1208
+ ocr(input: OcrInput): Promise<OcrResult>;
1209
+ /** Moderation (F016.4) — classify text against safety categories. Mistral. */
1210
+ moderate(input: ModerationInput): Promise<ModerationResult>;
1046
1211
  /** Prompt-contract capabilities (F5.5) layered on chat/vision. */
1047
1212
  contracts: Contracts;
1048
1213
  }
@@ -1094,6 +1259,8 @@ declare function openrouterAdapter(config?: {
1094
1259
  declare function mistralAdapter(config?: {
1095
1260
  apiKey?: string;
1096
1261
  baseUrl?: string;
1262
+ fetch?: typeof fetch;
1263
+ pricePerPage?: number;
1097
1264
  }): ProviderAdapter;
1098
1265
 
1099
1266
  interface FalAdapterConfig {
@@ -1143,8 +1310,8 @@ declare const falStubAdapter: ProviderAdapter;
1143
1310
  * wires the live adapters. */
1144
1311
  declare const stubProviders: Record<string, ProviderAdapter>;
1145
1312
 
1146
- declare const VERSION: "0.6.0";
1147
- declare const SDK_TAG: "@broberg/ai-sdk@0.6.0";
1313
+ declare const VERSION: "0.7.0";
1314
+ declare const SDK_TAG: "@broberg/ai-sdk@0.7.0";
1148
1315
 
1149
1316
  /** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
1150
1317
  * per-call override. Model IDs are current at scaffold time; callers pin their
@@ -1337,4 +1504,4 @@ interface StreamTransportRequest extends TransportRequest {
1337
1504
  */
1338
1505
  declare function streamTransport(req: StreamTransportRequest): AsyncIterable<string>;
1339
1506
 
1340
- export { type AiClient, type AiConfig, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostSink, type CostSummary, DEFAULT_TIER_MAP, type DesignInput, type DesignResult, type DiscordSinkConfig, type EmbeddingInput, type EmbeddingRequest, type EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type Message, type MockupInput, type MockupResult, type OpenAICompatibleConfig, type PricingEntry, type ProviderAdapter, type RerankInput, type RerankResult, type Role, SDK_TAG, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, type Tier, type TierSpec, type Tool, type ToolCall, type TranscribeInput, type TranscribeRequest, type TranscribeResult, type TranslateInput, type TranslateResult, type Transport, type TransportRequest, type TransportResponse, type UpmetricsSinkConfig, type Usage, VERSION, type VideoInput, type VisionInput, aiConfigSchema, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, chatInputSchema, computeCost, createAI, deepinfraAdapter, defaultProviders, discordSink, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, makeContracts, makeOpenAICompatibleAdapter, messageSchema, mistralAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, resolveTier, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsSink, visionInputSchema };
1507
+ export { type AiClient, type AiConfig, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostSink, type CostSummary, DEFAULT_TIER_MAP, type DesignInput, type DesignResult, type DiscordSinkConfig, type EmbeddingInput, type EmbeddingRequest, type EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type Message, type MockupInput, type MockupResult, type ModerationInput, type ModerationItem, type ModerationRequest, type ModerationResult, type OcrInput, type OcrPage, type OcrRequest, type OcrResult, type OpenAICompatibleConfig, type PricingEntry, type ProviderAdapter, type RerankInput, type RerankResult, type Role, SDK_TAG, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, type Tier, type TierSpec, type Tool, type ToolCall, type TranscribeInput, type TranscribeRequest, type TranscribeResult, type TranslateInput, type TranslateResult, type Transport, type TransportRequest, type TransportResponse, type UpmetricsSinkConfig, type Usage, VERSION, type VideoInput, type VisionInput, aiConfigSchema, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, chatInputSchema, computeCost, createAI, deepinfraAdapter, defaultProviders, discordSink, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, makeContracts, makeOpenAICompatibleAdapter, messageSchema, mistralAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, resolveTier, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsSink, visionInputSchema };
package/dist/index.js CHANGED
@@ -278,7 +278,9 @@ var PRICING = {
278
278
  "mistral:magistral-small-latest": { inputPer1M: 0.5, outputPer1M: 1.5, version: MS },
279
279
  "mistral:devstral-latest": { inputPer1M: 0.4, outputPer1M: 2, version: MS },
280
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 }
281
+ "mistral:open-mistral-nemo": { inputPer1M: 0.15, outputPer1M: 0.15, version: MS },
282
+ // Moderation (F016.4) — per input token; output 0. (OCR is per-page in the adapter.)
283
+ "mistral:mistral-moderation-latest": { inputPer1M: 0.1, outputPer1M: 0, version: MS }
282
284
  };
283
285
  function getPrice(provider, model) {
284
286
  const exact = PRICING[`${provider}:${model}`];
@@ -1025,12 +1027,77 @@ function openrouterAdapter(config = {}) {
1025
1027
  }
1026
1028
 
1027
1029
  // src/providers/mistral.ts
1030
+ var MISTRAL_OCR_PRICE_PER_PAGE = 2e-3;
1028
1031
  function mistralAdapter(config = {}) {
1029
- return makeOpenAICompatibleAdapter({
1030
- name: "mistral",
1031
- baseUrl: config.baseUrl ?? "https://api.mistral.ai/v1",
1032
- apiKey: config.apiKey
1033
- });
1032
+ const baseUrl = config.baseUrl ?? "https://api.mistral.ai/v1";
1033
+ const base = makeOpenAICompatibleAdapter({ name: "mistral", baseUrl, apiKey: config.apiKey });
1034
+ function key() {
1035
+ const k = config.apiKey ?? process.env.MISTRAL_API_KEY;
1036
+ if (!k) throw new Error("mistral adapter: API key not set (env MISTRAL_API_KEY)");
1037
+ return k;
1038
+ }
1039
+ const fetchImpl = config.fetch ?? fetch;
1040
+ async function ocr(req) {
1041
+ const isImage = (req.mimeType ?? "").startsWith("image/");
1042
+ const url = typeof req.document === "string" ? req.document : `data:${req.mimeType ?? "application/pdf"};base64,${Buffer.from(req.document).toString("base64")}`;
1043
+ const document = isImage ? { type: "image_url", image_url: url } : { type: "document_url", document_url: url };
1044
+ const res = await fetchImpl(`${baseUrl}/ocr`, {
1045
+ method: "POST",
1046
+ headers: { "content-type": "application/json", authorization: `Bearer ${key()}` },
1047
+ body: JSON.stringify({ model: req.spec.model, document })
1048
+ });
1049
+ if (!res.ok) {
1050
+ const body = await res.text().catch(() => "");
1051
+ throw new Error(`mistral ocr ${res.status}: ${body.slice(0, 300)}`);
1052
+ }
1053
+ const data = await res.json();
1054
+ const pages = (data.pages ?? []).map((p, i) => ({
1055
+ index: p.index ?? i,
1056
+ markdown: p.markdown ?? ""
1057
+ }));
1058
+ const pagesProcessed = data.usage_info?.pages_processed ?? pages.length;
1059
+ const usage = freshUsage({
1060
+ provider: "mistral",
1061
+ model: req.spec.model,
1062
+ transport: "http",
1063
+ capability: "ocr",
1064
+ inputTokens: 0,
1065
+ outputTokens: 0
1066
+ });
1067
+ usage.costUsd = pagesProcessed * (config.pricePerPage ?? MISTRAL_OCR_PRICE_PER_PAGE);
1068
+ return { pages, usage };
1069
+ }
1070
+ async function moderate(req) {
1071
+ const res = await fetchImpl(`${baseUrl}/moderations`, {
1072
+ method: "POST",
1073
+ headers: { "content-type": "application/json", authorization: `Bearer ${key()}` },
1074
+ body: JSON.stringify({ model: req.spec.model, input: req.input })
1075
+ });
1076
+ if (!res.ok) {
1077
+ const body = await res.text().catch(() => "");
1078
+ throw new Error(`mistral moderation ${res.status}: ${body.slice(0, 300)}`);
1079
+ }
1080
+ const data = await res.json();
1081
+ const results = (data.results ?? []).map((r) => {
1082
+ const categories = r.categories ?? {};
1083
+ return {
1084
+ flagged: Object.values(categories).some(Boolean),
1085
+ categories,
1086
+ categoryScores: r.category_scores ?? {}
1087
+ };
1088
+ });
1089
+ const estIn = req.input.reduce((n, s) => n + Math.ceil(s.length / 4), 0);
1090
+ const usage = freshUsage({
1091
+ provider: "mistral",
1092
+ model: req.spec.model,
1093
+ transport: "http",
1094
+ capability: "moderation",
1095
+ inputTokens: data.usage?.prompt_tokens ?? estIn,
1096
+ outputTokens: 0
1097
+ });
1098
+ return { results, usage };
1099
+ }
1100
+ return { ...base, ocr, moderate };
1034
1101
  }
1035
1102
 
1036
1103
  // src/providers/fal.ts
@@ -1432,6 +1499,17 @@ var transcribeInputSchema = z.object({
1432
1499
  durationSec: z.number().positive().optional(),
1433
1500
  ...callOptions
1434
1501
  });
1502
+ var ocrInputSchema = z.object({
1503
+ /** A URL, data-URL, or raw bytes of the document/image. */
1504
+ document: z.union([z.string(), z.instanceof(Uint8Array)]),
1505
+ /** image/* → routed as an image; anything else → a document (PDF etc.). */
1506
+ mimeType: z.string().optional(),
1507
+ ...callOptions
1508
+ });
1509
+ var moderationInputSchema = z.object({
1510
+ input: z.union([z.string(), z.array(z.string())]),
1511
+ ...callOptions
1512
+ });
1435
1513
  var budgetSchema = z.object({
1436
1514
  perCallUsd: z.number().positive().optional(),
1437
1515
  rollingUsd: z.number().positive().optional()
@@ -1451,6 +1529,8 @@ var DEFAULT_IMAGE_SPEC = {
1451
1529
  model: "fal-ai/flux/schnell",
1452
1530
  transport: "http"
1453
1531
  };
1532
+ var DEFAULT_OCR_SPEC = { provider: "mistral", model: "mistral-ocr-latest", transport: "http" };
1533
+ var DEFAULT_MODERATION_SPEC = { provider: "mistral", model: "mistral-moderation-latest", transport: "http" };
1454
1534
  function createAI(config = {}) {
1455
1535
  const cfg = aiConfigSchema.parse(config);
1456
1536
  const providers = cfg.providers ?? defaultProviders;
@@ -1694,6 +1774,42 @@ function createAI(config = {}) {
1694
1774
  }
1695
1775
  });
1696
1776
  },
1777
+ async ocr(input) {
1778
+ input = ocrInputSchema.parse(input);
1779
+ return runCapability({
1780
+ primary: { ...DEFAULT_OCR_SPEC, ...input.override },
1781
+ fallback: input.fallback,
1782
+ capability: "ocr",
1783
+ purpose: input.purpose,
1784
+ labels: input.labels,
1785
+ estIn: 0,
1786
+ // OCR cost is per-page, not token-based
1787
+ estOut: 0,
1788
+ invoke: async (spec) => {
1789
+ const adapter = pickProvider(spec.provider);
1790
+ if (!adapter.ocr) throw new Error(`createAI: provider "${spec.provider}" does not support ocr`);
1791
+ return adapter.ocr({ document: input.document, mimeType: input.mimeType, spec });
1792
+ }
1793
+ });
1794
+ },
1795
+ async moderate(input) {
1796
+ input = moderationInputSchema.parse(input);
1797
+ const items = Array.isArray(input.input) ? input.input : [input.input];
1798
+ return runCapability({
1799
+ primary: { ...DEFAULT_MODERATION_SPEC, ...input.override },
1800
+ fallback: input.fallback,
1801
+ capability: "moderation",
1802
+ purpose: input.purpose,
1803
+ labels: input.labels,
1804
+ estIn: items.reduce((n, s) => n + estTokens(s), 0),
1805
+ estOut: 0,
1806
+ invoke: async (spec) => {
1807
+ const adapter = pickProvider(spec.provider);
1808
+ if (!adapter.moderate) throw new Error(`createAI: provider "${spec.provider}" does not support moderation`);
1809
+ return adapter.moderate({ input: items, spec });
1810
+ }
1811
+ });
1812
+ },
1697
1813
  async embedding(input) {
1698
1814
  input = embeddingInputSchema.parse(input);
1699
1815
  const tier = input.tier ?? EMBEDDING_DEFAULT_TIER;
@@ -1820,8 +1936,8 @@ var stubProviders = {
1820
1936
  };
1821
1937
 
1822
1938
  // src/version.ts
1823
- var VERSION = "0.6.0";
1824
- var SDK_TAG = "@broberg/ai-sdk@0.6.0";
1939
+ var VERSION = "0.7.0";
1940
+ var SDK_TAG = "@broberg/ai-sdk@0.7.0";
1825
1941
 
1826
1942
  // src/cost/budget-store.ts
1827
1943
  function sqliteBudgetStore(config) {