@broberg/ai-sdk 0.9.1 → 0.10.1
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 +140 -4
- package/dist/index.js +238 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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" | "ocr" | "moderation" | "podcast" | "tts" | "mockup" | "design" | "extract" | "classify" | "rerank";
|
|
16
|
+
type Capability = "chat" | "vision" | "video" | "translate" | "image" | "embedding" | "transcribe" | "ocr" | "moderation" | "podcast" | "tts" | "trainStyle" | "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 = {
|
|
@@ -150,16 +150,44 @@ type ChatStreamEvent = {
|
|
|
150
150
|
message: string;
|
|
151
151
|
status?: number;
|
|
152
152
|
};
|
|
153
|
+
/** A trained LoRA to merge at inference time (F021). */
|
|
154
|
+
interface LoraWeight {
|
|
155
|
+
/** URL (or fal path) to the LoRA weights. */
|
|
156
|
+
path: string;
|
|
157
|
+
/** Scales the LoRA before merging (default 1). */
|
|
158
|
+
scale?: number;
|
|
159
|
+
}
|
|
153
160
|
interface ImageRequest {
|
|
154
161
|
prompt: string;
|
|
155
162
|
spec: TierSpec;
|
|
156
163
|
width?: number;
|
|
157
164
|
height?: number;
|
|
165
|
+
/** LoRAs to merge at inference (F021) — e.g. a trained brand/style LoRA. */
|
|
166
|
+
loras?: LoraWeight[];
|
|
158
167
|
}
|
|
159
168
|
interface ImageResult {
|
|
160
169
|
url: string;
|
|
161
170
|
usage: Usage;
|
|
162
171
|
}
|
|
172
|
+
/** Style/brand LoRA training (F021) — fal fal-ai/flux-lora-fast-training. */
|
|
173
|
+
interface TrainStyleRequest {
|
|
174
|
+
/** A hosted archive URL, or an array of image URLs the SDK zips in-memory. */
|
|
175
|
+
images: string | string[];
|
|
176
|
+
spec: TierSpec;
|
|
177
|
+
/** Style LoRA (disables captioning/masks). Default true. */
|
|
178
|
+
isStyle?: boolean;
|
|
179
|
+
triggerWord?: string;
|
|
180
|
+
/** Training steps (~1000 typical). */
|
|
181
|
+
steps?: number;
|
|
182
|
+
createMasks?: boolean;
|
|
183
|
+
}
|
|
184
|
+
interface TrainStyleResult {
|
|
185
|
+
/** URL to the trained LoRA weights — pass to ai.image({ lora }). */
|
|
186
|
+
loraUrl: string;
|
|
187
|
+
/** URL to the training config file. */
|
|
188
|
+
configUrl: string;
|
|
189
|
+
usage: Usage;
|
|
190
|
+
}
|
|
163
191
|
interface EmbeddingRequest {
|
|
164
192
|
input: string[];
|
|
165
193
|
spec: TierSpec;
|
|
@@ -257,6 +285,8 @@ interface ProviderAdapter {
|
|
|
257
285
|
chatStream?(req: ChatRequest): AsyncIterable<ChatStreamEvent>;
|
|
258
286
|
vision?(req: ChatRequest): Promise<ChatResult>;
|
|
259
287
|
image?(req: ImageRequest): Promise<ImageResult>;
|
|
288
|
+
/** Train a style/brand LoRA from images (F021). fal. */
|
|
289
|
+
trainStyle?(req: TrainStyleRequest): Promise<TrainStyleResult>;
|
|
260
290
|
embedding?(req: EmbeddingRequest): Promise<EmbeddingResult>;
|
|
261
291
|
transcribe?(req: TranscribeRequest): Promise<TranscribeResult>;
|
|
262
292
|
ocr?(req: OcrRequest): Promise<OcrResult>;
|
|
@@ -886,9 +916,27 @@ declare const imageInputSchema: z.ZodObject<{
|
|
|
886
916
|
prompt: z.ZodString;
|
|
887
917
|
width: z.ZodOptional<z.ZodNumber>;
|
|
888
918
|
height: z.ZodOptional<z.ZodNumber>;
|
|
919
|
+
/** LoRAs to merge at inference (F021). */
|
|
920
|
+
loras: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
921
|
+
path: z.ZodString;
|
|
922
|
+
scale: z.ZodOptional<z.ZodNumber>;
|
|
923
|
+
}, "strip", z.ZodTypeAny, {
|
|
924
|
+
path: string;
|
|
925
|
+
scale?: number | undefined;
|
|
926
|
+
}, {
|
|
927
|
+
path: string;
|
|
928
|
+
scale?: number | undefined;
|
|
929
|
+
}>, "many">>;
|
|
930
|
+
/** Shorthand for a single LoRA — normalized to loras:[{path, scale:1}]. */
|
|
931
|
+
lora: z.ZodOptional<z.ZodString>;
|
|
889
932
|
}, "strip", z.ZodTypeAny, {
|
|
890
933
|
prompt: string;
|
|
891
934
|
purpose?: string | undefined;
|
|
935
|
+
loras?: {
|
|
936
|
+
path: string;
|
|
937
|
+
scale?: number | undefined;
|
|
938
|
+
}[] | undefined;
|
|
939
|
+
lora?: string | undefined;
|
|
892
940
|
tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
|
|
893
941
|
override?: {
|
|
894
942
|
provider?: string | undefined;
|
|
@@ -906,6 +954,11 @@ declare const imageInputSchema: z.ZodObject<{
|
|
|
906
954
|
}, {
|
|
907
955
|
prompt: string;
|
|
908
956
|
purpose?: string | undefined;
|
|
957
|
+
loras?: {
|
|
958
|
+
path: string;
|
|
959
|
+
scale?: number | undefined;
|
|
960
|
+
}[] | undefined;
|
|
961
|
+
lora?: string | undefined;
|
|
909
962
|
tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
|
|
910
963
|
override?: {
|
|
911
964
|
provider?: string | undefined;
|
|
@@ -921,6 +974,82 @@ declare const imageInputSchema: z.ZodObject<{
|
|
|
921
974
|
width?: number | undefined;
|
|
922
975
|
height?: number | undefined;
|
|
923
976
|
}>;
|
|
977
|
+
declare const trainStyleInputSchema: z.ZodObject<{
|
|
978
|
+
tier: z.ZodOptional<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>>;
|
|
979
|
+
override: z.ZodOptional<z.ZodObject<{
|
|
980
|
+
provider: z.ZodOptional<z.ZodString>;
|
|
981
|
+
model: z.ZodOptional<z.ZodString>;
|
|
982
|
+
transport: z.ZodOptional<z.ZodEnum<["http", "subprocess"]>>;
|
|
983
|
+
}, "strip", z.ZodTypeAny, {
|
|
984
|
+
provider?: string | undefined;
|
|
985
|
+
model?: string | undefined;
|
|
986
|
+
transport?: "http" | "subprocess" | undefined;
|
|
987
|
+
}, {
|
|
988
|
+
provider?: string | undefined;
|
|
989
|
+
model?: string | undefined;
|
|
990
|
+
transport?: "http" | "subprocess" | undefined;
|
|
991
|
+
}>>;
|
|
992
|
+
fallback: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>, z.ZodObject<{
|
|
993
|
+
provider: z.ZodString;
|
|
994
|
+
model: z.ZodString;
|
|
995
|
+
transport: z.ZodEnum<["http", "subprocess"]>;
|
|
996
|
+
}, "strip", z.ZodTypeAny, {
|
|
997
|
+
provider: string;
|
|
998
|
+
model: string;
|
|
999
|
+
transport: "http" | "subprocess";
|
|
1000
|
+
}, {
|
|
1001
|
+
provider: string;
|
|
1002
|
+
model: string;
|
|
1003
|
+
transport: "http" | "subprocess";
|
|
1004
|
+
}>]>, "many">>;
|
|
1005
|
+
purpose: z.ZodOptional<z.ZodString>;
|
|
1006
|
+
labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1007
|
+
/** A hosted archive URL, or an array of image URLs the SDK zips in-memory. */
|
|
1008
|
+
images: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>;
|
|
1009
|
+
/** Style LoRA (disables captioning/masks). Default true. */
|
|
1010
|
+
isStyle: z.ZodOptional<z.ZodBoolean>;
|
|
1011
|
+
triggerWord: z.ZodOptional<z.ZodString>;
|
|
1012
|
+
steps: z.ZodOptional<z.ZodNumber>;
|
|
1013
|
+
createMasks: z.ZodOptional<z.ZodBoolean>;
|
|
1014
|
+
}, "strip", z.ZodTypeAny, {
|
|
1015
|
+
images: string | string[];
|
|
1016
|
+
purpose?: string | undefined;
|
|
1017
|
+
steps?: number | undefined;
|
|
1018
|
+
tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
|
|
1019
|
+
override?: {
|
|
1020
|
+
provider?: string | undefined;
|
|
1021
|
+
model?: string | undefined;
|
|
1022
|
+
transport?: "http" | "subprocess" | undefined;
|
|
1023
|
+
} | undefined;
|
|
1024
|
+
fallback?: ("fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | {
|
|
1025
|
+
provider: string;
|
|
1026
|
+
model: string;
|
|
1027
|
+
transport: "http" | "subprocess";
|
|
1028
|
+
})[] | undefined;
|
|
1029
|
+
labels?: Record<string, string> | undefined;
|
|
1030
|
+
isStyle?: boolean | undefined;
|
|
1031
|
+
triggerWord?: string | undefined;
|
|
1032
|
+
createMasks?: boolean | undefined;
|
|
1033
|
+
}, {
|
|
1034
|
+
images: string | string[];
|
|
1035
|
+
purpose?: string | undefined;
|
|
1036
|
+
steps?: number | undefined;
|
|
1037
|
+
tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
|
|
1038
|
+
override?: {
|
|
1039
|
+
provider?: string | undefined;
|
|
1040
|
+
model?: string | undefined;
|
|
1041
|
+
transport?: "http" | "subprocess" | undefined;
|
|
1042
|
+
} | undefined;
|
|
1043
|
+
fallback?: ("fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | {
|
|
1044
|
+
provider: string;
|
|
1045
|
+
model: string;
|
|
1046
|
+
transport: "http" | "subprocess";
|
|
1047
|
+
})[] | undefined;
|
|
1048
|
+
labels?: Record<string, string> | undefined;
|
|
1049
|
+
isStyle?: boolean | undefined;
|
|
1050
|
+
triggerWord?: string | undefined;
|
|
1051
|
+
createMasks?: boolean | undefined;
|
|
1052
|
+
}>;
|
|
924
1053
|
declare const embeddingInputSchema: z.ZodObject<{
|
|
925
1054
|
tier: z.ZodOptional<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>>;
|
|
926
1055
|
override: z.ZodOptional<z.ZodObject<{
|
|
@@ -1386,6 +1515,7 @@ type VisionInput = z.infer<typeof visionInputSchema>;
|
|
|
1386
1515
|
type VideoInput = z.infer<typeof videoInputSchema>;
|
|
1387
1516
|
type TranslateInput = z.infer<typeof translateInputSchema>;
|
|
1388
1517
|
type ImageInput = z.infer<typeof imageInputSchema>;
|
|
1518
|
+
type TrainStyleInput = z.infer<typeof trainStyleInputSchema>;
|
|
1389
1519
|
type EmbeddingInput = z.infer<typeof embeddingInputSchema>;
|
|
1390
1520
|
type TranscribeInput = z.infer<typeof transcribeInputSchema>;
|
|
1391
1521
|
type OcrInput = z.infer<typeof ocrInputSchema>;
|
|
@@ -1404,6 +1534,8 @@ interface AiClient {
|
|
|
1404
1534
|
video(input: VideoInput): Promise<ChatResult>;
|
|
1405
1535
|
translate(input: TranslateInput): Promise<TranslateResult>;
|
|
1406
1536
|
image(input: ImageInput): Promise<ImageResult>;
|
|
1537
|
+
/** Train a style/brand LoRA from images (F021) → { loraUrl, configUrl }. fal. */
|
|
1538
|
+
trainStyle(input: TrainStyleInput): Promise<TrainStyleResult>;
|
|
1407
1539
|
embedding(input: EmbeddingInput): Promise<EmbeddingResult>;
|
|
1408
1540
|
transcribe(input: TranscribeInput): Promise<TranscribeResult>;
|
|
1409
1541
|
/** OCR (F016.2) — document/image → structured markdown, billed per page. Mistral. */
|
|
@@ -1505,9 +1637,13 @@ interface FalAdapterConfig {
|
|
|
1505
1637
|
queueBaseUrl?: string;
|
|
1506
1638
|
pollIntervalMs?: number;
|
|
1507
1639
|
timeoutMs?: number;
|
|
1640
|
+
/** Deadline for training jobs — they take minutes (default 600000 = 10 min). */
|
|
1641
|
+
trainTimeoutMs?: number;
|
|
1508
1642
|
fetch?: typeof fetch;
|
|
1509
1643
|
/** Override the per-image USD price (else a built-in estimate per model, 0 if unknown). */
|
|
1510
1644
|
pricePerImage?: number;
|
|
1645
|
+
/** Override the flat per-training USD price (else ~$2 estimate). */
|
|
1646
|
+
pricePerTraining?: number;
|
|
1511
1647
|
}
|
|
1512
1648
|
declare function falAdapter(config?: FalAdapterConfig): ProviderAdapter;
|
|
1513
1649
|
|
|
@@ -1544,8 +1680,8 @@ declare const falStubAdapter: ProviderAdapter;
|
|
|
1544
1680
|
* wires the live adapters. */
|
|
1545
1681
|
declare const stubProviders: Record<string, ProviderAdapter>;
|
|
1546
1682
|
|
|
1547
|
-
declare const VERSION: "0.
|
|
1548
|
-
declare const SDK_TAG: "@broberg/ai-sdk@0.
|
|
1683
|
+
declare const VERSION: "0.10.1";
|
|
1684
|
+
declare const SDK_TAG: "@broberg/ai-sdk@0.10.1";
|
|
1549
1685
|
|
|
1550
1686
|
/** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
|
|
1551
1687
|
* per-call override. Model IDs are current at scaffold time; callers pin their
|
|
@@ -1738,4 +1874,4 @@ interface StreamTransportRequest extends TransportRequest {
|
|
|
1738
1874
|
*/
|
|
1739
1875
|
declare function streamTransport(req: StreamTransportRequest): AsyncIterable<string>;
|
|
1740
1876
|
|
|
1741
|
-
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 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 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 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, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsSink, visionInputSchema };
|
|
1877
|
+
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 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, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsSink, visionInputSchema };
|
package/dist/index.js
CHANGED
|
@@ -1284,12 +1284,15 @@ function elevenlabsAdapter(config = {}) {
|
|
|
1284
1284
|
}
|
|
1285
1285
|
|
|
1286
1286
|
// src/providers/fal.ts
|
|
1287
|
+
import { deflateRawSync, crc32 } from "zlib";
|
|
1287
1288
|
var FAL_IMAGE_PRICE_ESTIMATE = {
|
|
1288
1289
|
"fal-ai/flux/schnell": 3e-3,
|
|
1289
1290
|
"fal-ai/flux/dev": 0.025,
|
|
1291
|
+
"fal-ai/flux-lora": 0.025,
|
|
1290
1292
|
"fal-ai/flux-pro": 0.05,
|
|
1291
1293
|
"fal-ai/flux-pro/v1.1": 0.04
|
|
1292
1294
|
};
|
|
1295
|
+
var FAL_TRAIN_PRICE_ESTIMATE = 2;
|
|
1293
1296
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1294
1297
|
function falAdapter(config = {}) {
|
|
1295
1298
|
const doFetch = config.fetch ?? fetch;
|
|
@@ -1297,14 +1300,22 @@ function falAdapter(config = {}) {
|
|
|
1297
1300
|
const queueBase = config.queueBaseUrl ?? "https://queue.fal.run";
|
|
1298
1301
|
const pollIntervalMs = config.pollIntervalMs ?? 2e3;
|
|
1299
1302
|
const timeoutMs = config.timeoutMs ?? 6e4;
|
|
1303
|
+
const resolveKey = () => config.apiKey ?? process.env.FAL_KEY ?? process.env.FAL_API_KEY;
|
|
1304
|
+
const authHeaders = (apiKey) => ({
|
|
1305
|
+
"content-type": "application/json",
|
|
1306
|
+
Authorization: `Key ${apiKey}`
|
|
1307
|
+
});
|
|
1300
1308
|
async function image(req) {
|
|
1301
|
-
const apiKey =
|
|
1309
|
+
const apiKey = resolveKey();
|
|
1302
1310
|
if (!apiKey) throw new Error("fal adapter: FAL_KEY not set");
|
|
1303
|
-
const headers =
|
|
1311
|
+
const headers = authHeaders(apiKey);
|
|
1304
1312
|
const body = { prompt: req.prompt };
|
|
1305
1313
|
if (req.width !== void 0 && req.height !== void 0) {
|
|
1306
1314
|
body.image_size = { width: req.width, height: req.height };
|
|
1307
1315
|
}
|
|
1316
|
+
if (req.loras && req.loras.length > 0) {
|
|
1317
|
+
body.loras = req.loras.map((l) => ({ path: l.path, scale: l.scale ?? 1 }));
|
|
1318
|
+
}
|
|
1308
1319
|
const mode = config.mode ?? "sync";
|
|
1309
1320
|
const url = await (mode === "sync" ? runSync(req.spec.model, headers, body) : runQueue(req.spec.model, headers, body));
|
|
1310
1321
|
const usage = freshUsage({
|
|
@@ -1318,6 +1329,54 @@ function falAdapter(config = {}) {
|
|
|
1318
1329
|
usage.costUsd = config.pricePerImage ?? FAL_IMAGE_PRICE_ESTIMATE[req.spec.model] ?? 0;
|
|
1319
1330
|
return { url, usage };
|
|
1320
1331
|
}
|
|
1332
|
+
async function trainStyle(req) {
|
|
1333
|
+
const apiKey = resolveKey();
|
|
1334
|
+
if (!apiKey) throw new Error("fal adapter: FAL_KEY not set");
|
|
1335
|
+
const headers = authHeaders(apiKey);
|
|
1336
|
+
const body = {
|
|
1337
|
+
images_data_url: await resolveImagesDataUrl(req.images),
|
|
1338
|
+
is_style: req.isStyle ?? true
|
|
1339
|
+
};
|
|
1340
|
+
if (req.triggerWord !== void 0) body.trigger_word = req.triggerWord;
|
|
1341
|
+
if (req.steps !== void 0) body.steps = req.steps;
|
|
1342
|
+
if (req.createMasks !== void 0) body.create_masks = req.createMasks;
|
|
1343
|
+
const result = await queueResult(
|
|
1344
|
+
req.spec.model,
|
|
1345
|
+
headers,
|
|
1346
|
+
body,
|
|
1347
|
+
config.trainTimeoutMs ?? 6e5
|
|
1348
|
+
);
|
|
1349
|
+
const { loraUrl, configUrl } = extractTrainedFiles(result);
|
|
1350
|
+
if (!loraUrl) {
|
|
1351
|
+
throw new Error(
|
|
1352
|
+
`fal trainStyle: no LoRA file url in result \u2014 fal returned keys [${Object.keys(
|
|
1353
|
+
result ?? {}
|
|
1354
|
+
).join(", ")}]: ${JSON.stringify(result).slice(0, 800)}`
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
const usage = freshUsage({
|
|
1358
|
+
provider: "fal",
|
|
1359
|
+
model: req.spec.model,
|
|
1360
|
+
transport: "http",
|
|
1361
|
+
capability: "trainStyle",
|
|
1362
|
+
inputTokens: 0,
|
|
1363
|
+
outputTokens: 0
|
|
1364
|
+
});
|
|
1365
|
+
usage.costUsd = config.pricePerTraining ?? FAL_TRAIN_PRICE_ESTIMATE;
|
|
1366
|
+
return { loraUrl, configUrl: configUrl ?? "", usage };
|
|
1367
|
+
}
|
|
1368
|
+
async function resolveImagesDataUrl(images) {
|
|
1369
|
+
if (typeof images === "string") return images;
|
|
1370
|
+
const files = await Promise.all(
|
|
1371
|
+
images.map(async (url, i) => {
|
|
1372
|
+
const res = await doFetch(url);
|
|
1373
|
+
if (!res.ok) throw new Error(`fal trainStyle: failed to fetch image ${url} (${res.status})`);
|
|
1374
|
+
return { name: fileNameFromUrl(url, i), data: new Uint8Array(await res.arrayBuffer()) };
|
|
1375
|
+
})
|
|
1376
|
+
);
|
|
1377
|
+
const zip = buildZip(files);
|
|
1378
|
+
return `data:application/zip;base64,${Buffer.from(zip).toString("base64")}`;
|
|
1379
|
+
}
|
|
1321
1380
|
async function runSync(model, headers, body) {
|
|
1322
1381
|
const res = await doFetch(`${syncBase}/${model}`, {
|
|
1323
1382
|
method: "POST",
|
|
@@ -1333,34 +1392,133 @@ function falAdapter(config = {}) {
|
|
|
1333
1392
|
return out;
|
|
1334
1393
|
}
|
|
1335
1394
|
async function runQueue(model, headers, body) {
|
|
1395
|
+
const result = await queueResult(model, headers, body, timeoutMs);
|
|
1396
|
+
const out = result.images?.[0]?.url;
|
|
1397
|
+
if (!out) throw new Error("fal queue: no image url in result");
|
|
1398
|
+
return out;
|
|
1399
|
+
}
|
|
1400
|
+
async function queueResult(model, headers, body, deadlineMs) {
|
|
1336
1401
|
const submitRes = await doFetch(`${queueBase}/${model}`, {
|
|
1337
1402
|
method: "POST",
|
|
1338
1403
|
headers,
|
|
1339
1404
|
body: JSON.stringify(body)
|
|
1340
1405
|
});
|
|
1341
1406
|
if (!submitRes.ok) {
|
|
1342
|
-
throw new Error(
|
|
1407
|
+
throw new Error(
|
|
1408
|
+
`fal queue submit ${submitRes.status}: ${(await submitRes.text().catch(() => "")).slice(0, 200)}`
|
|
1409
|
+
);
|
|
1343
1410
|
}
|
|
1344
1411
|
const submit = await submitRes.json();
|
|
1345
1412
|
const statusUrl = submit.status_url;
|
|
1346
1413
|
const responseUrl = submit.response_url;
|
|
1347
1414
|
if (!statusUrl || !responseUrl) throw new Error("fal queue: missing status/response url");
|
|
1348
|
-
const deadline = Date.now() +
|
|
1415
|
+
const deadline = Date.now() + deadlineMs;
|
|
1349
1416
|
for (; ; ) {
|
|
1350
1417
|
const statusRes = await doFetch(statusUrl, { headers });
|
|
1351
1418
|
const status = await statusRes.json();
|
|
1352
1419
|
if (status.status === "COMPLETED") break;
|
|
1353
|
-
if (status.status === "FAILED") throw new Error("fal queue:
|
|
1354
|
-
if (Date.now() >= deadline) throw new Error(`fal queue: timed out after ${
|
|
1420
|
+
if (status.status === "FAILED") throw new Error("fal queue: job FAILED");
|
|
1421
|
+
if (Date.now() >= deadline) throw new Error(`fal queue: timed out after ${deadlineMs}ms`);
|
|
1355
1422
|
await sleep(pollIntervalMs);
|
|
1356
1423
|
}
|
|
1357
1424
|
const resultRes = await doFetch(responseUrl, { headers });
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1425
|
+
if (!resultRes.ok) {
|
|
1426
|
+
throw new Error(
|
|
1427
|
+
`fal queue result ${resultRes.status}: ${(await resultRes.text().catch(() => "")).slice(0, 300)}`
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
return resultRes.json();
|
|
1431
|
+
}
|
|
1432
|
+
return { name: "fal", image, trainStyle };
|
|
1433
|
+
}
|
|
1434
|
+
function urlOf(v) {
|
|
1435
|
+
if (typeof v === "string") return v;
|
|
1436
|
+
if (v && typeof v === "object" && typeof v.url === "string") {
|
|
1437
|
+
return v.url;
|
|
1362
1438
|
}
|
|
1363
|
-
return
|
|
1439
|
+
return void 0;
|
|
1440
|
+
}
|
|
1441
|
+
function deepFindUrl(obj, match) {
|
|
1442
|
+
const stack = [obj];
|
|
1443
|
+
while (stack.length) {
|
|
1444
|
+
const cur = stack.pop();
|
|
1445
|
+
if (typeof cur === "string") {
|
|
1446
|
+
if (match(cur)) return cur;
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
if (cur && typeof cur === "object") {
|
|
1450
|
+
const u = cur.url;
|
|
1451
|
+
if (typeof u === "string" && match(u)) return u;
|
|
1452
|
+
for (const v of Object.values(cur)) stack.push(v);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return void 0;
|
|
1456
|
+
}
|
|
1457
|
+
function extractTrainedFiles(result) {
|
|
1458
|
+
const r = result;
|
|
1459
|
+
const root = r?.data ?? r?.response ?? r?.output ?? r;
|
|
1460
|
+
const loraUrl = urlOf(root?.diffusers_lora_file) ?? urlOf(root?.lora_file) ?? urlOf(root?.safetensors) ?? urlOf(root?.lora) ?? deepFindUrl(root, (u) => /\.safetensors(\?|$)/i.test(u));
|
|
1461
|
+
const configUrl = urlOf(root?.config_file) ?? urlOf(root?.config) ?? deepFindUrl(root, (u) => /config[^/]*\.json(\?|$)/i.test(u));
|
|
1462
|
+
return { loraUrl, configUrl };
|
|
1463
|
+
}
|
|
1464
|
+
function fileNameFromUrl(url, i) {
|
|
1465
|
+
const base = url.split("?")[0].split("/").pop() || "";
|
|
1466
|
+
return /\.[a-z0-9]+$/i.test(base) ? base : `image_${i}.png`;
|
|
1467
|
+
}
|
|
1468
|
+
function buildZip(files) {
|
|
1469
|
+
const parts = [];
|
|
1470
|
+
const central = [];
|
|
1471
|
+
let offset = 0;
|
|
1472
|
+
for (const f of files) {
|
|
1473
|
+
const nameBuf = Buffer.from(f.name, "utf8");
|
|
1474
|
+
const data = Buffer.from(f.data);
|
|
1475
|
+
const comp = deflateRawSync(data);
|
|
1476
|
+
const crc = crc32(data) >>> 0;
|
|
1477
|
+
const lfh = Buffer.alloc(30);
|
|
1478
|
+
lfh.writeUInt32LE(67324752, 0);
|
|
1479
|
+
lfh.writeUInt16LE(20, 4);
|
|
1480
|
+
lfh.writeUInt16LE(0, 6);
|
|
1481
|
+
lfh.writeUInt16LE(8, 8);
|
|
1482
|
+
lfh.writeUInt16LE(0, 10);
|
|
1483
|
+
lfh.writeUInt16LE(33, 12);
|
|
1484
|
+
lfh.writeUInt32LE(crc, 14);
|
|
1485
|
+
lfh.writeUInt32LE(comp.length, 18);
|
|
1486
|
+
lfh.writeUInt32LE(data.length, 22);
|
|
1487
|
+
lfh.writeUInt16LE(nameBuf.length, 26);
|
|
1488
|
+
lfh.writeUInt16LE(0, 28);
|
|
1489
|
+
parts.push(lfh, nameBuf, comp);
|
|
1490
|
+
const cdh = Buffer.alloc(46);
|
|
1491
|
+
cdh.writeUInt32LE(33639248, 0);
|
|
1492
|
+
cdh.writeUInt16LE(20, 4);
|
|
1493
|
+
cdh.writeUInt16LE(20, 6);
|
|
1494
|
+
cdh.writeUInt16LE(0, 8);
|
|
1495
|
+
cdh.writeUInt16LE(8, 10);
|
|
1496
|
+
cdh.writeUInt16LE(0, 12);
|
|
1497
|
+
cdh.writeUInt16LE(33, 14);
|
|
1498
|
+
cdh.writeUInt32LE(crc, 16);
|
|
1499
|
+
cdh.writeUInt32LE(comp.length, 20);
|
|
1500
|
+
cdh.writeUInt32LE(data.length, 24);
|
|
1501
|
+
cdh.writeUInt16LE(nameBuf.length, 28);
|
|
1502
|
+
cdh.writeUInt16LE(0, 30);
|
|
1503
|
+
cdh.writeUInt16LE(0, 32);
|
|
1504
|
+
cdh.writeUInt16LE(0, 34);
|
|
1505
|
+
cdh.writeUInt16LE(0, 36);
|
|
1506
|
+
cdh.writeUInt32LE(0, 38);
|
|
1507
|
+
cdh.writeUInt32LE(offset, 42);
|
|
1508
|
+
central.push(cdh, nameBuf);
|
|
1509
|
+
offset += lfh.length + nameBuf.length + comp.length;
|
|
1510
|
+
}
|
|
1511
|
+
const cd = Buffer.concat(central);
|
|
1512
|
+
const eocd = Buffer.alloc(22);
|
|
1513
|
+
eocd.writeUInt32LE(101010256, 0);
|
|
1514
|
+
eocd.writeUInt16LE(0, 4);
|
|
1515
|
+
eocd.writeUInt16LE(0, 6);
|
|
1516
|
+
eocd.writeUInt16LE(files.length, 8);
|
|
1517
|
+
eocd.writeUInt16LE(files.length, 10);
|
|
1518
|
+
eocd.writeUInt32LE(cd.length, 12);
|
|
1519
|
+
eocd.writeUInt32LE(offset, 16);
|
|
1520
|
+
eocd.writeUInt16LE(0, 20);
|
|
1521
|
+
return Buffer.concat([...parts, cd, eocd]);
|
|
1364
1522
|
}
|
|
1365
1523
|
|
|
1366
1524
|
// src/providers/registry.ts
|
|
@@ -1665,10 +1823,28 @@ var translateInputSchema = z.object({
|
|
|
1665
1823
|
from: z.string().optional(),
|
|
1666
1824
|
...callOptions
|
|
1667
1825
|
});
|
|
1826
|
+
var loraWeightSchema = z.object({
|
|
1827
|
+
path: z.string(),
|
|
1828
|
+
scale: z.number().optional()
|
|
1829
|
+
});
|
|
1668
1830
|
var imageInputSchema = z.object({
|
|
1669
1831
|
prompt: z.string(),
|
|
1670
1832
|
width: z.number().int().positive().optional(),
|
|
1671
1833
|
height: z.number().int().positive().optional(),
|
|
1834
|
+
/** LoRAs to merge at inference (F021). */
|
|
1835
|
+
loras: z.array(loraWeightSchema).optional(),
|
|
1836
|
+
/** Shorthand for a single LoRA — normalized to loras:[{path, scale:1}]. */
|
|
1837
|
+
lora: z.string().optional(),
|
|
1838
|
+
...callOptions
|
|
1839
|
+
});
|
|
1840
|
+
var trainStyleInputSchema = z.object({
|
|
1841
|
+
/** A hosted archive URL, or an array of image URLs the SDK zips in-memory. */
|
|
1842
|
+
images: z.union([z.string(), z.array(z.string())]),
|
|
1843
|
+
/** Style LoRA (disables captioning/masks). Default true. */
|
|
1844
|
+
isStyle: z.boolean().optional(),
|
|
1845
|
+
triggerWord: z.string().optional(),
|
|
1846
|
+
steps: z.number().int().positive().optional(),
|
|
1847
|
+
createMasks: z.boolean().optional(),
|
|
1672
1848
|
...callOptions
|
|
1673
1849
|
});
|
|
1674
1850
|
var embeddingInputSchema = z.object({
|
|
@@ -1724,6 +1900,16 @@ var DEFAULT_IMAGE_SPEC = {
|
|
|
1724
1900
|
model: "fal-ai/flux/schnell",
|
|
1725
1901
|
transport: "http"
|
|
1726
1902
|
};
|
|
1903
|
+
var DEFAULT_LORA_IMAGE_SPEC = {
|
|
1904
|
+
provider: "fal",
|
|
1905
|
+
model: "fal-ai/flux-lora",
|
|
1906
|
+
transport: "http"
|
|
1907
|
+
};
|
|
1908
|
+
var DEFAULT_TRAINSTYLE_SPEC = {
|
|
1909
|
+
provider: "fal",
|
|
1910
|
+
model: "fal-ai/flux-lora-fast-training",
|
|
1911
|
+
transport: "http"
|
|
1912
|
+
};
|
|
1727
1913
|
var DEFAULT_OCR_SPEC = { provider: "mistral", model: "mistral-ocr-latest", transport: "http" };
|
|
1728
1914
|
var DEFAULT_MODERATION_SPEC = { provider: "mistral", model: "mistral-moderation-latest", transport: "http" };
|
|
1729
1915
|
var DEFAULT_PODCAST_SPEC = { provider: "elevenlabs", model: "eleven_v3", transport: "http" };
|
|
@@ -1956,8 +2142,13 @@ function createAI(config = {}) {
|
|
|
1956
2142
|
},
|
|
1957
2143
|
async image(input) {
|
|
1958
2144
|
input = imageInputSchema.parse(input);
|
|
2145
|
+
const loras = [
|
|
2146
|
+
...input.loras ?? [],
|
|
2147
|
+
...input.lora ? [{ path: input.lora }] : []
|
|
2148
|
+
];
|
|
2149
|
+
const base = loras.length > 0 ? DEFAULT_LORA_IMAGE_SPEC : DEFAULT_IMAGE_SPEC;
|
|
1959
2150
|
return runCapability({
|
|
1960
|
-
primary: { ...
|
|
2151
|
+
primary: { ...base, ...input.override },
|
|
1961
2152
|
fallback: input.fallback,
|
|
1962
2153
|
capability: "image",
|
|
1963
2154
|
purpose: input.purpose,
|
|
@@ -1968,7 +2159,39 @@ function createAI(config = {}) {
|
|
|
1968
2159
|
invoke: async (spec) => {
|
|
1969
2160
|
const adapter = pickProvider(spec.provider);
|
|
1970
2161
|
if (!adapter.image) throw new Error(`createAI: provider "${spec.provider}" does not support image`);
|
|
1971
|
-
return adapter.image({
|
|
2162
|
+
return adapter.image({
|
|
2163
|
+
prompt: input.prompt,
|
|
2164
|
+
spec,
|
|
2165
|
+
width: input.width,
|
|
2166
|
+
height: input.height,
|
|
2167
|
+
loras: loras.length ? loras : void 0
|
|
2168
|
+
});
|
|
2169
|
+
}
|
|
2170
|
+
});
|
|
2171
|
+
},
|
|
2172
|
+
async trainStyle(input) {
|
|
2173
|
+
input = trainStyleInputSchema.parse(input);
|
|
2174
|
+
return runCapability({
|
|
2175
|
+
primary: { ...DEFAULT_TRAINSTYLE_SPEC, ...input.override },
|
|
2176
|
+
fallback: input.fallback,
|
|
2177
|
+
capability: "trainStyle",
|
|
2178
|
+
purpose: input.purpose,
|
|
2179
|
+
labels: input.labels,
|
|
2180
|
+
estIn: 0,
|
|
2181
|
+
// training is priced flat by fal, not token-based
|
|
2182
|
+
estOut: 0,
|
|
2183
|
+
invoke: async (spec) => {
|
|
2184
|
+
const adapter = pickProvider(spec.provider);
|
|
2185
|
+
if (!adapter.trainStyle)
|
|
2186
|
+
throw new Error(`createAI: provider "${spec.provider}" does not support trainStyle`);
|
|
2187
|
+
return adapter.trainStyle({
|
|
2188
|
+
images: input.images,
|
|
2189
|
+
spec,
|
|
2190
|
+
isStyle: input.isStyle,
|
|
2191
|
+
triggerWord: input.triggerWord,
|
|
2192
|
+
steps: input.steps,
|
|
2193
|
+
createMasks: input.createMasks
|
|
2194
|
+
});
|
|
1972
2195
|
}
|
|
1973
2196
|
});
|
|
1974
2197
|
},
|
|
@@ -2196,8 +2419,8 @@ var stubProviders = {
|
|
|
2196
2419
|
};
|
|
2197
2420
|
|
|
2198
2421
|
// src/version.ts
|
|
2199
|
-
var VERSION = "0.
|
|
2200
|
-
var SDK_TAG = "@broberg/ai-sdk@0.
|
|
2422
|
+
var VERSION = "0.10.1";
|
|
2423
|
+
var SDK_TAG = "@broberg/ai-sdk@0.10.1";
|
|
2201
2424
|
|
|
2202
2425
|
// src/cost/budget-store.ts
|
|
2203
2426
|
function sqliteBudgetStore(config) {
|