@broberg/ai-sdk 0.13.0 → 0.14.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
@@ -165,6 +165,11 @@ interface ImageRequest {
165
165
  height?: number;
166
166
  /** LoRAs to merge at inference (F021) — e.g. a trained brand/style LoRA. */
167
167
  loras?: LoraWeight[];
168
+ /** F023 — a BFL finetune id (a subject trained in the BFL dashboard). Routes to the
169
+ * EU-resident BFL finetuned-portrait endpoint. */
170
+ finetune?: string;
171
+ /** F023 — how strongly the finetune is applied (BFL finetune_strength, ~0–2). */
172
+ finetuneStrength?: number;
168
173
  /** F021.4 — re-roll once with a fresh seed if fal's safety-checker false-positives
169
174
  * and returns a black image (has_nsfw_concepts). fal only. */
170
175
  retryOnBlack?: boolean;
@@ -942,6 +947,11 @@ declare const imageInputSchema: z.ZodObject<{
942
947
  }>, "many">>;
943
948
  /** Shorthand for a single LoRA — normalized to loras:[{path, scale:1}]. */
944
949
  lora: z.ZodOptional<z.ZodString>;
950
+ /** F023 — a BFL finetune id (subject trained in the BFL dashboard). Routes to the
951
+ * EU-resident BFL finetuned-portrait endpoint. */
952
+ finetune: z.ZodOptional<z.ZodString>;
953
+ /** F023 — BFL finetune_strength (~0–2; higher = stronger likeness). */
954
+ finetuneStrength: z.ZodOptional<z.ZodNumber>;
945
955
  /** F021.4 — re-roll once if fal returns a black image (NSFW false-positive). */
946
956
  retryOnBlack: z.ZodOptional<z.ZodBoolean>;
947
957
  }, "strip", z.ZodTypeAny, {
@@ -966,6 +976,8 @@ declare const imageInputSchema: z.ZodObject<{
966
976
  labels?: Record<string, string> | undefined;
967
977
  width?: number | undefined;
968
978
  height?: number | undefined;
979
+ finetune?: string | undefined;
980
+ finetuneStrength?: number | undefined;
969
981
  retryOnBlack?: boolean | undefined;
970
982
  }, {
971
983
  prompt: string;
@@ -989,6 +1001,8 @@ declare const imageInputSchema: z.ZodObject<{
989
1001
  labels?: Record<string, string> | undefined;
990
1002
  width?: number | undefined;
991
1003
  height?: number | undefined;
1004
+ finetune?: string | undefined;
1005
+ finetuneStrength?: number | undefined;
992
1006
  retryOnBlack?: boolean | undefined;
993
1007
  }>;
994
1008
  declare const trainStyleInputSchema: z.ZodObject<{
@@ -1682,6 +1696,18 @@ interface FalAdapterConfig {
1682
1696
  }
1683
1697
  declare function falAdapter(config?: FalAdapterConfig): ProviderAdapter;
1684
1698
 
1699
+ interface BflAdapterConfig {
1700
+ apiKey?: string;
1701
+ /** EU-resident base. Do NOT point this at api.bfl.ai (global, US-failover) — see header. */
1702
+ baseUrl?: string;
1703
+ pollIntervalMs?: number;
1704
+ timeoutMs?: number;
1705
+ fetch?: typeof fetch;
1706
+ /** Override the per-image USD price (else the built-in finetuned-inference estimate). */
1707
+ pricePerImage?: number;
1708
+ }
1709
+ declare function bflAdapter(config?: BflAdapterConfig): ProviderAdapter;
1710
+
1685
1711
  interface OpenAICompatibleConfig {
1686
1712
  /** Provider name recorded on Usage (e.g. "openai", "deepinfra", "openrouter"). */
1687
1713
  name: string;
@@ -1715,8 +1741,8 @@ declare const falStubAdapter: ProviderAdapter;
1715
1741
  * wires the live adapters. */
1716
1742
  declare const stubProviders: Record<string, ProviderAdapter>;
1717
1743
 
1718
- declare const VERSION: "0.13.0";
1719
- declare const SDK_TAG: "@broberg/ai-sdk@0.13.0";
1744
+ declare const VERSION: "0.14.0";
1745
+ declare const SDK_TAG: "@broberg/ai-sdk@0.14.0";
1720
1746
 
1721
1747
  /** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
1722
1748
  * per-call override. Model IDs are current at scaffold time; callers pin their
@@ -1942,4 +1968,4 @@ interface StreamTransportRequest extends TransportRequest {
1942
1968
  */
1943
1969
  declare function streamTransport(req: StreamTransportRequest): AsyncIterable<string>;
1944
1970
 
1945
- export { type AiClient, type AiConfig, type BatchJob, type BatchRequestItem, type BatchResultItem, 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 DialogueRequest, type DialogueTurn, type DiscordSinkConfig, ELEVENLABS_DANISH_VOICES, type EmbeddingInput, type EmbeddingRequest, type EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type LoraWeight, 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 PodcastInput, type PodcastResult, type PricingEntry, type ProviderAdapter, type RefreshOptions, type RefreshResult, type RerankInput, type RerankResult, type Role, SDK_TAG, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, type Tier, type TierSpec, type Tool, type ToolCall, type TrainStyleInput, type TrainStyleRequest, type TrainStyleResult, type TranscribeInput, type TranscribeRequest, type TranscribeResult, type TranslateInput, type TranslateResult, type Transport, type TransportRequest, type TransportResponse, type TtsInput, type TtsRequest, type UpmetricsSinkConfig, type Usage, VERSION, type VideoInput, type VisionInput, aiConfigSchema, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, chatInputSchema, computeCost, createAI, deepinfraAdapter, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, makeContracts, makeOpenAICompatibleAdapter, messageSchema, mistralAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, resetRefreshState, resetRegistry, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsSink, visionInputSchema };
1971
+ export { type AiClient, type AiConfig, type BatchJob, type BatchRequestItem, type BatchResultItem, type BflAdapterConfig, 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 DialogueRequest, type DialogueTurn, type DiscordSinkConfig, ELEVENLABS_DANISH_VOICES, type EmbeddingInput, type EmbeddingRequest, type EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type LoraWeight, 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 PodcastInput, type PodcastResult, type PricingEntry, type ProviderAdapter, type RefreshOptions, type RefreshResult, type RerankInput, type RerankResult, type Role, SDK_TAG, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, type Tier, type TierSpec, type Tool, type ToolCall, type TrainStyleInput, type TrainStyleRequest, type TrainStyleResult, type TranscribeInput, type TranscribeRequest, type TranscribeResult, type TranslateInput, type TranslateResult, type Transport, type TransportRequest, type TransportResponse, type TtsInput, type TtsRequest, type UpmetricsSinkConfig, type Usage, VERSION, type VideoInput, type VisionInput, aiConfigSchema, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, bflAdapter, chatInputSchema, computeCost, createAI, deepinfraAdapter, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, makeContracts, makeOpenAICompatibleAdapter, messageSchema, mistralAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, resetRefreshState, resetRegistry, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsSink, visionInputSchema };
package/dist/index.js CHANGED
@@ -848,8 +848,15 @@ function openaiAdapter(config = {}) {
848
848
 
849
849
  // src/providers/gemini.ts
850
850
  var GEMINI_IMAGE_PRICE_PER_IMAGE = {
851
- "gemini-3-pro-image-preview": 0.039,
852
- "gemini-2.5-flash-image": 0.039
851
+ "gemini-2.5-flash-image": 0.039,
852
+ // "nano-banana" — 1024px = 1290 tok
853
+ "gemini-3.1-flash-image": 0.067,
854
+ // 1K=$0.067, 2K=$0.101, 4K=$0.151
855
+ "gemini-3.1-flash-image-preview": 0.067,
856
+ "gemini-3-pro-image": 0.134,
857
+ // premium — 1K/2K=$0.134, 4K=$0.24
858
+ "gemini-3-pro-image-preview": 0.134
859
+ // was $0.039 — wrong (that's the flash price); pro is $0.134
853
860
  };
854
861
  function partsFrom(content) {
855
862
  if (typeof content === "string") return [{ text: content }];
@@ -1566,6 +1573,85 @@ function buildZip(files) {
1566
1573
  return Buffer.concat([...parts, cd, eocd]);
1567
1574
  }
1568
1575
 
1576
+ // src/providers/bfl.ts
1577
+ var EU_BASE = "https://api.eu.bfl.ai";
1578
+ var BFL_IMAGE_PRICE = 0.06;
1579
+ var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
1580
+ function gcd(a, b) {
1581
+ return b === 0 ? a : gcd(b, a % b);
1582
+ }
1583
+ function bflAdapter(config = {}) {
1584
+ const doFetch = config.fetch ?? fetch;
1585
+ const base = config.baseUrl ?? EU_BASE;
1586
+ const pollIntervalMs = config.pollIntervalMs ?? 1500;
1587
+ const timeoutMs = config.timeoutMs ?? 12e4;
1588
+ const resolveKey = () => config.apiKey ?? process.env.BFL_API_KEY;
1589
+ async function image(req) {
1590
+ const apiKey = resolveKey();
1591
+ if (!apiKey) throw new Error("bfl adapter: BFL_API_KEY not set");
1592
+ if (!req.finetune) {
1593
+ throw new Error(
1594
+ "bfl adapter: requires a finetune id \u2014 call ai.image({ finetune, override: { provider: 'bfl' } }). Train the subject once in the BFL dashboard (dashboard.bfl.ai) \u2014 finetune-create is not in the public API."
1595
+ );
1596
+ }
1597
+ const headers = { "content-type": "application/json", "x-key": apiKey };
1598
+ const body = {
1599
+ finetune_id: req.finetune,
1600
+ prompt: req.prompt
1601
+ };
1602
+ if (req.finetuneStrength !== void 0) body.finetune_strength = req.finetuneStrength;
1603
+ if (req.width && req.height) {
1604
+ const g = gcd(req.width, req.height) || 1;
1605
+ body.aspect_ratio = `${req.width / g}:${req.height / g}`;
1606
+ }
1607
+ const submitRes = await doFetch(`${base}/v1/flux-pro-1.1-ultra-finetuned`, {
1608
+ method: "POST",
1609
+ headers,
1610
+ body: JSON.stringify(body)
1611
+ });
1612
+ if (!submitRes.ok) {
1613
+ throw new Error(`bfl ${submitRes.status}: ${(await submitRes.text().catch(() => "")).slice(0, 200)}`);
1614
+ }
1615
+ const submit = await submitRes.json();
1616
+ if (!submit.id) throw new Error("bfl: no task id in submit response");
1617
+ const sample = await poll(submit.id, apiKey);
1618
+ const usage = freshUsage({
1619
+ provider: "bfl",
1620
+ model: req.spec.model,
1621
+ transport: "http",
1622
+ capability: "image",
1623
+ inputTokens: 0,
1624
+ outputTokens: 0
1625
+ });
1626
+ usage.costUsd = config.pricePerImage ?? BFL_IMAGE_PRICE;
1627
+ return { url: sample, usage };
1628
+ }
1629
+ async function poll(id, apiKey) {
1630
+ const headers = { "x-key": apiKey };
1631
+ const deadline = Date.now() + timeoutMs;
1632
+ for (; ; ) {
1633
+ const res = await doFetch(`${base}/v1/get_result?id=${encodeURIComponent(id)}`, { headers });
1634
+ if (!res.ok) throw new Error(`bfl get_result ${res.status}`);
1635
+ const data = await res.json();
1636
+ const status = data.status ?? "";
1637
+ if (status === "Ready") {
1638
+ const sample = data.result?.sample;
1639
+ if (!sample) throw new Error("bfl: status Ready but no result.sample url");
1640
+ return sample;
1641
+ }
1642
+ if (status === "Error" || status === "Failed" || status === "Task not found") {
1643
+ throw new Error(`bfl: generation ${status}`);
1644
+ }
1645
+ if (status.includes("Moderated")) {
1646
+ throw new Error(`bfl: ${status} \u2014 prompt or output flagged by BFL safety`);
1647
+ }
1648
+ if (Date.now() >= deadline) throw new Error(`bfl: timed out after ${timeoutMs}ms`);
1649
+ await sleep2(pollIntervalMs);
1650
+ }
1651
+ }
1652
+ return { name: "bfl", image };
1653
+ }
1654
+
1569
1655
  // src/providers/registry.ts
1570
1656
  var defaultProviders = {
1571
1657
  anthropic: anthropicAdapter(),
@@ -1575,7 +1661,8 @@ var defaultProviders = {
1575
1661
  openrouter: openrouterAdapter(),
1576
1662
  mistral: mistralAdapter(),
1577
1663
  elevenlabs: elevenlabsAdapter(),
1578
- fal: falAdapter()
1664
+ fal: falAdapter(),
1665
+ bfl: bflAdapter()
1579
1666
  };
1580
1667
 
1581
1668
  // src/cost/budget.ts
@@ -1887,6 +1974,11 @@ var imageInputSchema = z.object({
1887
1974
  loras: z.array(loraWeightSchema).optional(),
1888
1975
  /** Shorthand for a single LoRA — normalized to loras:[{path, scale:1}]. */
1889
1976
  lora: z.string().optional(),
1977
+ /** F023 — a BFL finetune id (subject trained in the BFL dashboard). Routes to the
1978
+ * EU-resident BFL finetuned-portrait endpoint. */
1979
+ finetune: z.string().optional(),
1980
+ /** F023 — BFL finetune_strength (~0–2; higher = stronger likeness). */
1981
+ finetuneStrength: z.number().min(0).max(2).optional(),
1890
1982
  /** F021.4 — re-roll once if fal returns a black image (NSFW false-positive). */
1891
1983
  retryOnBlack: z.boolean().optional(),
1892
1984
  ...callOptions
@@ -1969,6 +2061,11 @@ var DEFAULT_TRAINSTYLE_SPEC = {
1969
2061
  model: "fal-ai/flux-lora-fast-training",
1970
2062
  transport: "http"
1971
2063
  };
2064
+ var DEFAULT_BFL_FINETUNE_SPEC = {
2065
+ provider: "bfl",
2066
+ model: "flux-pro-1.1-ultra-finetuned",
2067
+ transport: "http"
2068
+ };
1972
2069
  var DEFAULT_OCR_SPEC = { provider: "mistral", model: "mistral-ocr-latest", transport: "http" };
1973
2070
  var DEFAULT_MODERATION_SPEC = { provider: "mistral", model: "mistral-moderation-latest", transport: "http" };
1974
2071
  var DEFAULT_PODCAST_SPEC = { provider: "elevenlabs", model: "eleven_v3", transport: "http" };
@@ -2215,7 +2312,7 @@ function createAI(config = {}) {
2215
2312
  ...input.loras ?? [],
2216
2313
  ...input.lora ? [{ path: input.lora }] : []
2217
2314
  ];
2218
- const base = loras.length > 0 ? DEFAULT_LORA_IMAGE_SPEC : DEFAULT_IMAGE_SPEC;
2315
+ const base = input.finetune ? DEFAULT_BFL_FINETUNE_SPEC : loras.length > 0 ? DEFAULT_LORA_IMAGE_SPEC : DEFAULT_IMAGE_SPEC;
2219
2316
  return runCapability({
2220
2317
  primary: { ...base, ...input.override },
2221
2318
  fallback: input.fallback,
@@ -2234,6 +2331,8 @@ function createAI(config = {}) {
2234
2331
  width: input.width,
2235
2332
  height: input.height,
2236
2333
  loras: loras.length ? loras : void 0,
2334
+ finetune: input.finetune,
2335
+ finetuneStrength: input.finetuneStrength,
2237
2336
  retryOnBlack: input.retryOnBlack
2238
2337
  });
2239
2338
  }
@@ -2489,8 +2588,8 @@ var stubProviders = {
2489
2588
  };
2490
2589
 
2491
2590
  // src/version.ts
2492
- var VERSION = "0.13.0";
2493
- var SDK_TAG = "@broberg/ai-sdk@0.13.0";
2591
+ var VERSION = "0.14.0";
2592
+ var SDK_TAG = "@broberg/ai-sdk@0.14.0";
2494
2593
 
2495
2594
  // src/availability/refresh.ts
2496
2595
  var NOT_REFRESHED = { refreshed: false, checked: 0, markedUnavailable: [] };
@@ -2776,6 +2875,7 @@ export {
2776
2875
  anthropicAdapter,
2777
2876
  anthropicApiAdapter,
2778
2877
  anthropicSubprocessAdapter,
2878
+ bflAdapter,
2779
2879
  chatInputSchema,
2780
2880
  computeCost,
2781
2881
  createAI,