@brotu/ai 0.2.0 → 0.4.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/README.md +53 -6
- package/dist/adapters/brotu.adapter.d.ts +46 -0
- package/dist/adapters/brotu.adapter.d.ts.map +1 -0
- package/dist/catalog.cjs +36 -14
- package/dist/catalog.d.ts +1 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +3 -1
- package/dist/{chunk-PL534BFN.js → chunk-QOWHHKSI.js} +35 -14
- package/dist/client.d.ts +11 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/helpers/result.d.ts +1 -1
- package/dist/helpers/result.d.ts.map +1 -1
- package/dist/index.cjs +574 -47
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +537 -33
- package/dist/lib/webhook.d.ts +28 -0
- package/dist/lib/webhook.d.ts.map +1 -0
- package/dist/ports/content-generator.port.d.ts +6 -0
- package/dist/ports/content-generator.port.d.ts.map +1 -1
- package/dist/types.d.ts +21 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -32,7 +32,9 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
BYTEPLUS_CATALOG: () => BYTEPLUS_CATALOG,
|
|
34
34
|
BYTEPLUS_MODELS: () => BYTEPLUS_MODELS,
|
|
35
|
+
BrotuAdapter: () => BrotuAdapter,
|
|
35
36
|
BytePlusAdapter: () => BytePlusAdapter,
|
|
37
|
+
DEFAULT_BROTU_API_URL: () => DEFAULT_BROTU_API_URL,
|
|
36
38
|
ELEVENLABS_CATALOG: () => ELEVENLABS_CATALOG,
|
|
37
39
|
ELEVENLABS_MODELS: () => ELEVENLABS_MODELS,
|
|
38
40
|
ELEVENLABS_OUTPUT_FORMATS: () => ELEVENLABS_OUTPUT_FORMATS,
|
|
@@ -61,6 +63,7 @@ __export(index_exports, {
|
|
|
61
63
|
QwenAdapter: () => QwenAdapter,
|
|
62
64
|
brotuClient: () => brotuClient,
|
|
63
65
|
createS3Storage: () => createS3Storage,
|
|
66
|
+
deliverWebhook: () => deliverWebhook,
|
|
64
67
|
fail: () => fail,
|
|
65
68
|
getAvailableModels: () => getAvailableModels,
|
|
66
69
|
getModel: () => getModel,
|
|
@@ -72,7 +75,8 @@ __export(index_exports, {
|
|
|
72
75
|
persistOutputs: () => persistOutputs,
|
|
73
76
|
registerModels: () => registerModels,
|
|
74
77
|
resetCatalog: () => resetCatalog,
|
|
75
|
-
resolveProvider: () => resolveProvider
|
|
78
|
+
resolveProvider: () => resolveProvider,
|
|
79
|
+
resolveWebhook: () => resolveWebhook
|
|
76
80
|
});
|
|
77
81
|
module.exports = __toCommonJS(index_exports);
|
|
78
82
|
|
|
@@ -1265,8 +1269,16 @@ var PROVIDER_BASE_URLS = {
|
|
|
1265
1269
|
elevenlabs: "https://api.elevenlabs.io",
|
|
1266
1270
|
google: "https://generativelanguage.googleapis.com",
|
|
1267
1271
|
openai: "https://api.openai.com",
|
|
1268
|
-
qwen: "https://dashscope-intl.aliyuncs.com"
|
|
1272
|
+
qwen: "https://dashscope-intl.aliyuncs.com",
|
|
1273
|
+
brotu: "https://api.brotu.app"
|
|
1269
1274
|
};
|
|
1275
|
+
var DEFAULT_BROTU_API_URL = PROVIDER_BASE_URLS.brotu;
|
|
1276
|
+
function vendorProviders(options) {
|
|
1277
|
+
return options.providers ?? {};
|
|
1278
|
+
}
|
|
1279
|
+
function brotuApiKey(options) {
|
|
1280
|
+
return options.apiKey?.trim() || void 0;
|
|
1281
|
+
}
|
|
1270
1282
|
var BUILT_IN = [
|
|
1271
1283
|
...KLING_CATALOG,
|
|
1272
1284
|
...KLING_AUDIO_CATALOG,
|
|
@@ -1322,24 +1334,36 @@ function resolveProvider(modelId, options) {
|
|
|
1322
1334
|
`Model "${modelId}" declares no provider, so nothing knows how to run it.`
|
|
1323
1335
|
);
|
|
1324
1336
|
}
|
|
1325
|
-
const configured = options
|
|
1326
|
-
if (
|
|
1327
|
-
const
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1337
|
+
const configured = vendorProviders(options)[id];
|
|
1338
|
+
if (configured) {
|
|
1339
|
+
const baseUrl = configured.baseUrl ?? PROVIDER_BASE_URLS[id];
|
|
1340
|
+
if (!baseUrl) {
|
|
1341
|
+
throw new Error(
|
|
1342
|
+
`Provider "${id}" has no known base URL \u2014 pass providers.${id}.baseUrl.`
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
return { id, apiKey: configured.apiKey, baseUrl: baseUrl.replace(/\/$/, "") };
|
|
1331
1346
|
}
|
|
1332
|
-
const
|
|
1333
|
-
if (
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1347
|
+
const platformKey = brotuApiKey(options);
|
|
1348
|
+
if (platformKey) {
|
|
1349
|
+
return {
|
|
1350
|
+
id: "brotu",
|
|
1351
|
+
apiKey: platformKey,
|
|
1352
|
+
baseUrl: (options.apiUrl ?? DEFAULT_BROTU_API_URL).replace(/\/$/, "")
|
|
1353
|
+
};
|
|
1337
1354
|
}
|
|
1338
|
-
|
|
1355
|
+
const owned = Object.keys(vendorProviders(options)).join(", ") || "none";
|
|
1356
|
+
throw new Error(
|
|
1357
|
+
`Model "${modelId}" runs on "${id}", but no API key was given for it (configured: ${owned}). Pass a Brotu key to generate on the platform.`
|
|
1358
|
+
);
|
|
1339
1359
|
}
|
|
1340
1360
|
function getAvailableModels(options) {
|
|
1361
|
+
if (brotuApiKey(options)) {
|
|
1362
|
+
return catalog.filter((model) => Boolean(model.provider));
|
|
1363
|
+
}
|
|
1364
|
+
const vendors = vendorProviders(options);
|
|
1341
1365
|
return catalog.filter(
|
|
1342
|
-
(model) => model.provider && model.provider in
|
|
1366
|
+
(model) => model.provider && model.provider in vendors
|
|
1343
1367
|
);
|
|
1344
1368
|
}
|
|
1345
1369
|
|
|
@@ -1369,12 +1393,316 @@ function estimateFor(provider, type, params, defaults) {
|
|
|
1369
1393
|
};
|
|
1370
1394
|
}
|
|
1371
1395
|
|
|
1396
|
+
// src/adapters/brotu.adapter.ts
|
|
1397
|
+
var DEFAULT_API_URL = "https://api.brotu.app";
|
|
1398
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
1399
|
+
var DEFAULT_MAX_POLL_ATTEMPTS = 400;
|
|
1400
|
+
var PLATFORM_MODEL_IDS = {
|
|
1401
|
+
"kling/v2-6": "kling-2.6",
|
|
1402
|
+
"kling/v3": "kling-3.0/video",
|
|
1403
|
+
"dreamina-seedance-2-5-260628": "bytedance/seedance-2-5",
|
|
1404
|
+
"dreamina-seedance-2-0-260128": "bytedance/seedance-2",
|
|
1405
|
+
"dreamina-seedance-2-0-fast-260128": "bytedance/seedance-2-fast",
|
|
1406
|
+
"gpt-image-1.5": "gpt-image/1.5"
|
|
1407
|
+
};
|
|
1408
|
+
var UNSUPPORTED_VIA_CREDITS = "Speech and text generate on the vendor. Pass that provider's key.";
|
|
1409
|
+
var BrotuAdapter = class {
|
|
1410
|
+
constructor(opts) {
|
|
1411
|
+
this.opts = opts;
|
|
1412
|
+
}
|
|
1413
|
+
opts;
|
|
1414
|
+
providerName = "brotu";
|
|
1415
|
+
supportedTypes = ["image", "video"];
|
|
1416
|
+
workspaceIdCache;
|
|
1417
|
+
get origin() {
|
|
1418
|
+
return (this.opts.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
1419
|
+
}
|
|
1420
|
+
headers() {
|
|
1421
|
+
return {
|
|
1422
|
+
Authorization: `Bearer ${this.opts.apiKey}`,
|
|
1423
|
+
"Content-Type": "application/json"
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
async workspaceId() {
|
|
1427
|
+
if (this.opts.workspaceId) return this.opts.workspaceId;
|
|
1428
|
+
if (this.workspaceIdCache) return this.workspaceIdCache;
|
|
1429
|
+
const payload = await this.request("/api/v1/studio/default-workspace");
|
|
1430
|
+
const id = payload.workspaceId?.trim();
|
|
1431
|
+
if (!id) {
|
|
1432
|
+
throw new Error(
|
|
1433
|
+
"This Brotu account has no workspace. Open https://brotu.app and create one."
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
this.workspaceIdCache = id;
|
|
1437
|
+
return id;
|
|
1438
|
+
}
|
|
1439
|
+
studioPath(path, workspaceId) {
|
|
1440
|
+
const joiner = path.includes("?") ? "&" : "?";
|
|
1441
|
+
return `${path}${joiner}workspaceId=${encodeURIComponent(workspaceId)}`;
|
|
1442
|
+
}
|
|
1443
|
+
async request(path, init) {
|
|
1444
|
+
const response = await fetch(`${this.origin}${path}`, {
|
|
1445
|
+
...init,
|
|
1446
|
+
headers: { ...this.headers(), ...init?.headers }
|
|
1447
|
+
});
|
|
1448
|
+
const text = await response.text();
|
|
1449
|
+
let body = void 0;
|
|
1450
|
+
if (text) {
|
|
1451
|
+
try {
|
|
1452
|
+
body = JSON.parse(text);
|
|
1453
|
+
} catch {
|
|
1454
|
+
body = text;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
if (!response.ok) {
|
|
1458
|
+
const parsed = body;
|
|
1459
|
+
const message = parsed && typeof parsed === "object" ? parsed.error?.message || parsed.message || text : text;
|
|
1460
|
+
throw new Error(message || `Brotu API ${response.status}`);
|
|
1461
|
+
}
|
|
1462
|
+
return body;
|
|
1463
|
+
}
|
|
1464
|
+
platformModel(modelId) {
|
|
1465
|
+
return PLATFORM_MODEL_IDS[modelId] ?? modelId;
|
|
1466
|
+
}
|
|
1467
|
+
async startGeneration(kind, params) {
|
|
1468
|
+
const modelId = params.model ?? "";
|
|
1469
|
+
const workspaceId = await this.workspaceId();
|
|
1470
|
+
const path = kind === "video" ? "/api/v1/studio/videos" : "/api/v1/studio/images";
|
|
1471
|
+
const body = kind === "video" ? this.videoBody(params, modelId) : this.imageBody(params, modelId);
|
|
1472
|
+
const result = await this.request(
|
|
1473
|
+
this.studioPath(path, workspaceId),
|
|
1474
|
+
{ method: "POST", body: JSON.stringify(body) }
|
|
1475
|
+
);
|
|
1476
|
+
const generationId = result.generationId?.trim();
|
|
1477
|
+
if (!generationId) {
|
|
1478
|
+
throw new Error("Brotu accepted the request but returned no generation id.");
|
|
1479
|
+
}
|
|
1480
|
+
return { generationId, workspaceId };
|
|
1481
|
+
}
|
|
1482
|
+
imageBody(params, modelId) {
|
|
1483
|
+
return {
|
|
1484
|
+
prompt: params.prompt,
|
|
1485
|
+
model: this.platformModel(modelId),
|
|
1486
|
+
aspectRatio: params.aspectRatio,
|
|
1487
|
+
resolution: params.resolution,
|
|
1488
|
+
negativePrompt: params.negativePrompt,
|
|
1489
|
+
outputFormat: params.outputFormat,
|
|
1490
|
+
seed: params.seed,
|
|
1491
|
+
referenceImages: params.referenceImages
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
videoBody(params, modelId) {
|
|
1495
|
+
return {
|
|
1496
|
+
prompt: params.prompt,
|
|
1497
|
+
model: this.platformModel(modelId),
|
|
1498
|
+
duration: params.duration,
|
|
1499
|
+
resolution: params.resolution,
|
|
1500
|
+
aspectRatio: params.aspectRatio,
|
|
1501
|
+
mode: params.mode,
|
|
1502
|
+
withAudio: params.withAudio,
|
|
1503
|
+
seed: params.seed,
|
|
1504
|
+
imageUrl: params.imageUrl,
|
|
1505
|
+
imageUrls: params.imageUrls,
|
|
1506
|
+
videoUrl: params.videoUrl,
|
|
1507
|
+
videoUrls: params.videoUrls,
|
|
1508
|
+
referenceImages: params.referenceImages
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
async run(kind, params) {
|
|
1512
|
+
const startedAt = Date.now();
|
|
1513
|
+
const modelId = params.model ?? "(none)";
|
|
1514
|
+
const failure = (error) => ({
|
|
1515
|
+
success: false,
|
|
1516
|
+
outputs: [],
|
|
1517
|
+
creditsUsed: 0,
|
|
1518
|
+
provider: this.providerName,
|
|
1519
|
+
model: modelId,
|
|
1520
|
+
processingTimeMs: Date.now() - startedAt,
|
|
1521
|
+
error
|
|
1522
|
+
});
|
|
1523
|
+
let submitted;
|
|
1524
|
+
try {
|
|
1525
|
+
submitted = await this.startGeneration(kind, params);
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
return failure(error instanceof Error ? error.message : String(error));
|
|
1528
|
+
}
|
|
1529
|
+
const pollEndpoint = this.studioPath(
|
|
1530
|
+
`/api/v1/studio/generations/${submitted.generationId}`,
|
|
1531
|
+
submitted.workspaceId
|
|
1532
|
+
);
|
|
1533
|
+
if (isSubmitMode()) {
|
|
1534
|
+
throw new PendingJob(submitted.generationId, pollEndpoint);
|
|
1535
|
+
}
|
|
1536
|
+
const job = {
|
|
1537
|
+
id: submitted.generationId,
|
|
1538
|
+
provider: this.providerName,
|
|
1539
|
+
model: modelId,
|
|
1540
|
+
kind,
|
|
1541
|
+
pollEndpoint,
|
|
1542
|
+
params,
|
|
1543
|
+
submittedAt: new Date(startedAt).toISOString()
|
|
1544
|
+
};
|
|
1545
|
+
const maxAttempts = this.opts.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS;
|
|
1546
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1547
|
+
const snapshot = await this.completeJob(job);
|
|
1548
|
+
if (snapshot.status === "failed") {
|
|
1549
|
+
return failure(snapshot.error ?? "Brotu generation failed.");
|
|
1550
|
+
}
|
|
1551
|
+
if (snapshot.status === "succeeded" && snapshot.result) {
|
|
1552
|
+
return { ...snapshot.result, processingTimeMs: Date.now() - startedAt };
|
|
1553
|
+
}
|
|
1554
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
1555
|
+
}
|
|
1556
|
+
return failure(
|
|
1557
|
+
`Brotu generation ${submitted.generationId} did not finish after ${maxAttempts} checks.`
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
async completeJob(job) {
|
|
1561
|
+
const workspaceId = this.workspaceFromPoll(job.pollEndpoint) ?? await this.workspaceId();
|
|
1562
|
+
const path = this.studioPath(
|
|
1563
|
+
`/api/v1/studio/generations/${job.id}`,
|
|
1564
|
+
workspaceId
|
|
1565
|
+
);
|
|
1566
|
+
const generation = await this.request(path);
|
|
1567
|
+
const status = (generation.status ?? "").toLowerCase();
|
|
1568
|
+
if (status === "failed") {
|
|
1569
|
+
return {
|
|
1570
|
+
status: "failed",
|
|
1571
|
+
error: generation.errorMessage ?? "Brotu generation failed."
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
if (status !== "completed") {
|
|
1575
|
+
return { status: "pending" };
|
|
1576
|
+
}
|
|
1577
|
+
const outputs = outputsFrom(generation, job.kind);
|
|
1578
|
+
return {
|
|
1579
|
+
status: "succeeded",
|
|
1580
|
+
result: {
|
|
1581
|
+
success: true,
|
|
1582
|
+
outputs,
|
|
1583
|
+
creditsUsed: generation.creditsUsed ?? 0,
|
|
1584
|
+
provider: this.providerName,
|
|
1585
|
+
model: job.model,
|
|
1586
|
+
processingTimeMs: 0
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
workspaceFromPoll(pollEndpoint) {
|
|
1591
|
+
if (!pollEndpoint) return void 0;
|
|
1592
|
+
try {
|
|
1593
|
+
const url = new URL(pollEndpoint, "https://brotu.invalid");
|
|
1594
|
+
return url.searchParams.get("workspaceId") ?? void 0;
|
|
1595
|
+
} catch {
|
|
1596
|
+
return void 0;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
generateImage(params) {
|
|
1600
|
+
return this.run("image", params);
|
|
1601
|
+
}
|
|
1602
|
+
generateVideo(params) {
|
|
1603
|
+
return this.run("video", params);
|
|
1604
|
+
}
|
|
1605
|
+
async generateText(params) {
|
|
1606
|
+
return {
|
|
1607
|
+
success: false,
|
|
1608
|
+
outputs: [],
|
|
1609
|
+
creditsUsed: 0,
|
|
1610
|
+
provider: this.providerName,
|
|
1611
|
+
model: params.model ?? "(none)",
|
|
1612
|
+
processingTimeMs: 0,
|
|
1613
|
+
error: UNSUPPORTED_VIA_CREDITS
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
async generateAudio(params) {
|
|
1617
|
+
return {
|
|
1618
|
+
success: false,
|
|
1619
|
+
outputs: [],
|
|
1620
|
+
creditsUsed: 0,
|
|
1621
|
+
provider: this.providerName,
|
|
1622
|
+
model: params.model ?? "(none)",
|
|
1623
|
+
processingTimeMs: 0,
|
|
1624
|
+
error: UNSUPPORTED_VIA_CREDITS
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
async estimateCost(type, params) {
|
|
1628
|
+
if (type === "audio" || type === "text") {
|
|
1629
|
+
const estimate = estimateFor(this.providerName, type, params);
|
|
1630
|
+
return {
|
|
1631
|
+
...estimate,
|
|
1632
|
+
usd: null,
|
|
1633
|
+
note: UNSUPPORTED_VIA_CREDITS
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
try {
|
|
1637
|
+
const workspaceId = await this.workspaceId();
|
|
1638
|
+
const video = params;
|
|
1639
|
+
const image = params;
|
|
1640
|
+
const payload = await this.request(
|
|
1641
|
+
this.studioPath("/api/v1/studio/estimate", workspaceId),
|
|
1642
|
+
{
|
|
1643
|
+
method: "POST",
|
|
1644
|
+
body: JSON.stringify({
|
|
1645
|
+
model: this.platformModel(params.model ?? ""),
|
|
1646
|
+
duration: video.duration,
|
|
1647
|
+
resolution: video.resolution ?? image.resolution,
|
|
1648
|
+
aspectRatio: video.aspectRatio ?? image.aspectRatio,
|
|
1649
|
+
withAudio: video.withAudio,
|
|
1650
|
+
hasReferenceImage: Boolean(
|
|
1651
|
+
video.imageUrl || video.imageUrls?.length || video.referenceImages?.length || image.referenceImages?.length
|
|
1652
|
+
),
|
|
1653
|
+
hasReferenceVideo: Boolean(video.videoUrl || video.videoUrls?.length),
|
|
1654
|
+
quality: image.quality
|
|
1655
|
+
})
|
|
1656
|
+
}
|
|
1657
|
+
);
|
|
1658
|
+
const credits = payload.estimatedCredits ?? 0;
|
|
1659
|
+
return {
|
|
1660
|
+
unit: type === "video" ? "second" : "image",
|
|
1661
|
+
units: credits,
|
|
1662
|
+
usd: null,
|
|
1663
|
+
note: `${credits} Brotu credit${credits === 1 ? "" : "s"}. A vendor key generates on that provider.`,
|
|
1664
|
+
provider: this.providerName,
|
|
1665
|
+
model: params.model ?? ""
|
|
1666
|
+
};
|
|
1667
|
+
} catch (error) {
|
|
1668
|
+
const estimate = estimateFor(this.providerName, type, params);
|
|
1669
|
+
return {
|
|
1670
|
+
...estimate,
|
|
1671
|
+
usd: null,
|
|
1672
|
+
note: error instanceof Error ? error.message : "Could not estimate Brotu credits."
|
|
1673
|
+
};
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
supportsModel() {
|
|
1677
|
+
return true;
|
|
1678
|
+
}
|
|
1679
|
+
getAvailableModels() {
|
|
1680
|
+
return [];
|
|
1681
|
+
}
|
|
1682
|
+
};
|
|
1683
|
+
function outputsFrom(generation, kind) {
|
|
1684
|
+
const bucket = generation.outputs;
|
|
1685
|
+
const urls = kind === "video" ? bucket?.videos ?? bucket?.images ?? [] : bucket?.images ?? bucket?.videos ?? [];
|
|
1686
|
+
const copies = bucket?.copies ?? [];
|
|
1687
|
+
const mimeType = kind === "video" ? "video/mp4" : "image/png";
|
|
1688
|
+
const fromUrls = urls.filter(Boolean).map((url) => ({
|
|
1689
|
+
url,
|
|
1690
|
+
mimeType
|
|
1691
|
+
}));
|
|
1692
|
+
if (fromUrls.length > 0) return fromUrls;
|
|
1693
|
+
return copies.filter(Boolean).map((url) => ({
|
|
1694
|
+
url,
|
|
1695
|
+
mimeType: "text/plain",
|
|
1696
|
+
raw: { text: url }
|
|
1697
|
+
}));
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1372
1700
|
// src/adapters/byteplus.adapter.ts
|
|
1373
1701
|
var DEFAULT_BASE_URL = "https://ark.ap-southeast.bytepluses.com";
|
|
1374
1702
|
var TASKS_PATH = "/api/v3/contents/generations/tasks";
|
|
1375
1703
|
var IMAGES_PATH = "/api/v3/images/generations";
|
|
1376
|
-
var
|
|
1377
|
-
var
|
|
1704
|
+
var POLL_INTERVAL_MS2 = 5e3;
|
|
1705
|
+
var DEFAULT_MAX_POLL_ATTEMPTS2 = 240;
|
|
1378
1706
|
var BytePlusAdapter = class {
|
|
1379
1707
|
providerName = "byteplus";
|
|
1380
1708
|
supportedTypes = ["image", "video"];
|
|
@@ -1574,7 +1902,7 @@ var BytePlusAdapter = class {
|
|
|
1574
1902
|
params,
|
|
1575
1903
|
submittedAt: new Date(startedAt).toISOString()
|
|
1576
1904
|
};
|
|
1577
|
-
const maxAttempts = this.opts.maxPollAttempts ??
|
|
1905
|
+
const maxAttempts = this.opts.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS2;
|
|
1578
1906
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1579
1907
|
const snapshot = await this.completeJob(job);
|
|
1580
1908
|
if (snapshot.status === "failed") {
|
|
@@ -1583,7 +1911,7 @@ var BytePlusAdapter = class {
|
|
|
1583
1911
|
if (snapshot.status === "succeeded" && snapshot.result) {
|
|
1584
1912
|
return { ...snapshot.result, processingTimeMs: Date.now() - startedAt };
|
|
1585
1913
|
}
|
|
1586
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
1914
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS2));
|
|
1587
1915
|
}
|
|
1588
1916
|
return failure(
|
|
1589
1917
|
`Ark task ${taskId} did not finish after ${maxAttempts} checks.`
|
|
@@ -1860,8 +2188,8 @@ var ElevenLabsAdapter = class {
|
|
|
1860
2188
|
// src/adapters/google.adapter.ts
|
|
1861
2189
|
var DEFAULT_BASE_URL3 = "https://generativelanguage.googleapis.com";
|
|
1862
2190
|
var INTERACTIONS_PATH = "/v1beta/interactions";
|
|
1863
|
-
var
|
|
1864
|
-
var
|
|
2191
|
+
var POLL_INTERVAL_MS3 = 1e4;
|
|
2192
|
+
var DEFAULT_MAX_POLL_ATTEMPTS3 = 120;
|
|
1865
2193
|
var GoogleAdapter = class {
|
|
1866
2194
|
providerName = "google";
|
|
1867
2195
|
supportedTypes = [
|
|
@@ -2133,7 +2461,7 @@ var GoogleAdapter = class {
|
|
|
2133
2461
|
params,
|
|
2134
2462
|
submittedAt: new Date(startedAt).toISOString()
|
|
2135
2463
|
};
|
|
2136
|
-
const maxAttempts = this.opts.maxPollAttempts ??
|
|
2464
|
+
const maxAttempts = this.opts.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS3;
|
|
2137
2465
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2138
2466
|
const snapshot = await this.completeJob(job);
|
|
2139
2467
|
if (snapshot.status === "failed") {
|
|
@@ -2142,7 +2470,7 @@ var GoogleAdapter = class {
|
|
|
2142
2470
|
if (snapshot.status === "succeeded" && snapshot.result) {
|
|
2143
2471
|
return { ...snapshot.result, processingTimeMs: Date.now() - startedAt };
|
|
2144
2472
|
}
|
|
2145
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
2473
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS3));
|
|
2146
2474
|
}
|
|
2147
2475
|
return failure(`Veo operation ${operation} did not finish in time.`);
|
|
2148
2476
|
}
|
|
@@ -2357,8 +2685,8 @@ var GoogleAdapter = class {
|
|
|
2357
2685
|
|
|
2358
2686
|
// src/adapters/kling.adapter.ts
|
|
2359
2687
|
var DEFAULT_BASE_URL4 = "https://api-singapore.klingai.com";
|
|
2360
|
-
var
|
|
2361
|
-
var
|
|
2688
|
+
var POLL_INTERVAL_MS4 = 5e3;
|
|
2689
|
+
var DEFAULT_MAX_POLL_ATTEMPTS4 = 240;
|
|
2362
2690
|
function stateOf(task) {
|
|
2363
2691
|
const raw = (task.status ?? task.task_status ?? "").toLowerCase();
|
|
2364
2692
|
if (raw === "failed") return "failed";
|
|
@@ -2555,7 +2883,7 @@ var KlingAdapter = class {
|
|
|
2555
2883
|
params,
|
|
2556
2884
|
submittedAt: new Date(startedAt).toISOString()
|
|
2557
2885
|
};
|
|
2558
|
-
const maxAttempts = this.opts.maxPollAttempts ??
|
|
2886
|
+
const maxAttempts = this.opts.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS4;
|
|
2559
2887
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2560
2888
|
const snapshot = await this.completeJob(job);
|
|
2561
2889
|
if (snapshot.status === "failed") {
|
|
@@ -2564,7 +2892,7 @@ var KlingAdapter = class {
|
|
|
2564
2892
|
if (snapshot.status === "succeeded" && snapshot.result) {
|
|
2565
2893
|
return { ...snapshot.result, processingTimeMs: Date.now() - startedAt };
|
|
2566
2894
|
}
|
|
2567
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
2895
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS4));
|
|
2568
2896
|
}
|
|
2569
2897
|
return failure(
|
|
2570
2898
|
`Kling task ${submitted.taskId} did not finish after ${maxAttempts} checks.`
|
|
@@ -2935,8 +3263,8 @@ var OpenAIAdapter = class {
|
|
|
2935
3263
|
// src/adapters/qwen.adapter.ts
|
|
2936
3264
|
var DEFAULT_BASE_URL6 = "https://dashscope-intl.aliyuncs.com";
|
|
2937
3265
|
var TASKS_PATH2 = "/api/v1/tasks";
|
|
2938
|
-
var
|
|
2939
|
-
var
|
|
3266
|
+
var POLL_INTERVAL_MS5 = 5e3;
|
|
3267
|
+
var DEFAULT_MAX_POLL_ATTEMPTS5 = 240;
|
|
2940
3268
|
var QwenAdapter = class {
|
|
2941
3269
|
providerName = "qwen";
|
|
2942
3270
|
supportedTypes = ["image", "video"];
|
|
@@ -3143,7 +3471,7 @@ var QwenAdapter = class {
|
|
|
3143
3471
|
params,
|
|
3144
3472
|
submittedAt: new Date(startedAt).toISOString()
|
|
3145
3473
|
};
|
|
3146
|
-
const maxAttempts = this.opts.maxPollAttempts ??
|
|
3474
|
+
const maxAttempts = this.opts.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS5;
|
|
3147
3475
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
3148
3476
|
const snapshot = await this.completeJob(job);
|
|
3149
3477
|
if (snapshot.status === "failed") {
|
|
@@ -3152,7 +3480,7 @@ var QwenAdapter = class {
|
|
|
3152
3480
|
if (snapshot.status === "succeeded" && snapshot.result) {
|
|
3153
3481
|
return { ...snapshot.result, processingTimeMs: Date.now() - startedAt };
|
|
3154
3482
|
}
|
|
3155
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
3483
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS5));
|
|
3156
3484
|
}
|
|
3157
3485
|
return failure(
|
|
3158
3486
|
`DashScope task ${taskId} did not finish after ${maxAttempts} checks.`
|
|
@@ -3534,6 +3862,45 @@ async function persistOutputs(storage, outputs) {
|
|
|
3534
3862
|
);
|
|
3535
3863
|
}
|
|
3536
3864
|
|
|
3865
|
+
// src/lib/webhook.ts
|
|
3866
|
+
function resolveWebhook(value) {
|
|
3867
|
+
if (!value) return void 0;
|
|
3868
|
+
if (typeof value === "string") {
|
|
3869
|
+
return isHttpUrl(value) ? { url: value } : void 0;
|
|
3870
|
+
}
|
|
3871
|
+
if (!isHttpUrl(value.url)) return void 0;
|
|
3872
|
+
return {
|
|
3873
|
+
url: value.url,
|
|
3874
|
+
secret: value.secret,
|
|
3875
|
+
headers: value.headers
|
|
3876
|
+
};
|
|
3877
|
+
}
|
|
3878
|
+
function isHttpUrl(value) {
|
|
3879
|
+
try {
|
|
3880
|
+
const parsed = new URL(value);
|
|
3881
|
+
return parsed.protocol === "https:" || parsed.protocol === "http:";
|
|
3882
|
+
} catch {
|
|
3883
|
+
return false;
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
async function deliverWebhook(config, payload) {
|
|
3887
|
+
try {
|
|
3888
|
+
await fetch(config.url, {
|
|
3889
|
+
method: "POST",
|
|
3890
|
+
headers: {
|
|
3891
|
+
"content-type": "application/json",
|
|
3892
|
+
"user-agent": "@brotu/ai",
|
|
3893
|
+
"x-brotu-event": payload.event,
|
|
3894
|
+
...config.secret ? { "x-brotu-webhook-secret": config.secret } : {},
|
|
3895
|
+
...config.headers
|
|
3896
|
+
},
|
|
3897
|
+
body: JSON.stringify(payload),
|
|
3898
|
+
signal: AbortSignal.timeout(1e4)
|
|
3899
|
+
});
|
|
3900
|
+
} catch {
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
|
|
3537
3904
|
// src/client.ts
|
|
3538
3905
|
var NATIVE_PROVIDERS = [
|
|
3539
3906
|
"byteplus",
|
|
@@ -3544,10 +3911,17 @@ var NATIVE_PROVIDERS = [
|
|
|
3544
3911
|
"qwen"
|
|
3545
3912
|
];
|
|
3546
3913
|
function brotuClient(options) {
|
|
3547
|
-
|
|
3548
|
-
|
|
3914
|
+
const apiKey = options.apiKey?.trim();
|
|
3915
|
+
if (!apiKey) {
|
|
3916
|
+
throw new Error(
|
|
3917
|
+
"Pass a Brotu API key (brotu_sk_\u2026). Get one at https://brotu.app."
|
|
3918
|
+
);
|
|
3549
3919
|
}
|
|
3920
|
+
const optionsWithKey = { ...options, apiKey };
|
|
3921
|
+
const vendors = optionsWithKey.providers ?? {};
|
|
3550
3922
|
registerModels(options.models);
|
|
3923
|
+
let registeredWebhook = resolveWebhook(options.webhook);
|
|
3924
|
+
const notifiedJobIds = /* @__PURE__ */ new Set();
|
|
3551
3925
|
const storage = options.storage ? createS3Storage(options.storage) : void 0;
|
|
3552
3926
|
const shouldPersist = storage && options.storage?.persistOutputs !== false;
|
|
3553
3927
|
const adapters = /* @__PURE__ */ new Map();
|
|
@@ -3568,7 +3942,7 @@ function brotuClient(options) {
|
|
|
3568
3942
|
}
|
|
3569
3943
|
let provider;
|
|
3570
3944
|
try {
|
|
3571
|
-
provider = resolveProvider(modelId,
|
|
3945
|
+
provider = resolveProvider(modelId, optionsWithKey);
|
|
3572
3946
|
} catch (error) {
|
|
3573
3947
|
return failFrom("missing_key", error, { model: modelId });
|
|
3574
3948
|
}
|
|
@@ -3589,6 +3963,13 @@ function brotuClient(options) {
|
|
|
3589
3963
|
return ok({ adapter, provider: provider.id, model: modelId });
|
|
3590
3964
|
}
|
|
3591
3965
|
function buildAdapter(provider) {
|
|
3966
|
+
if (provider.id === "brotu") {
|
|
3967
|
+
return new BrotuAdapter({
|
|
3968
|
+
apiKey,
|
|
3969
|
+
apiUrl: optionsWithKey.apiUrl,
|
|
3970
|
+
workspaceId: optionsWithKey.workspaceId
|
|
3971
|
+
});
|
|
3972
|
+
}
|
|
3592
3973
|
if (provider.id === "kling") {
|
|
3593
3974
|
return new KlingAdapter({
|
|
3594
3975
|
apiKey: provider.apiKey,
|
|
@@ -3637,6 +4018,31 @@ function brotuClient(options) {
|
|
|
3637
4018
|
return adapter.generateAudio(params);
|
|
3638
4019
|
return adapter.generateText(params);
|
|
3639
4020
|
}
|
|
4021
|
+
function webhookFor(params) {
|
|
4022
|
+
return resolveWebhook(params?.webhook) ?? registeredWebhook;
|
|
4023
|
+
}
|
|
4024
|
+
async function notifySettled(input) {
|
|
4025
|
+
const config = webhookFor(input.params);
|
|
4026
|
+
if (!config) return;
|
|
4027
|
+
const jobId = input.job?.id;
|
|
4028
|
+
if (jobId) {
|
|
4029
|
+
if (notifiedJobIds.has(jobId)) return;
|
|
4030
|
+
notifiedJobIds.add(jobId);
|
|
4031
|
+
}
|
|
4032
|
+
const payload = {
|
|
4033
|
+
event: input.event,
|
|
4034
|
+
jobId,
|
|
4035
|
+
provider: input.provider ?? input.job?.provider,
|
|
4036
|
+
model: input.model ?? input.job?.model,
|
|
4037
|
+
kind: input.kind ?? input.job?.kind,
|
|
4038
|
+
outputs: input.outputs,
|
|
4039
|
+
error: input.error ? { code: input.error.code, message: input.error.message } : void 0,
|
|
4040
|
+
metadata: input.metadata ?? input.job?.metadata,
|
|
4041
|
+
processingTimeMs: input.processingTimeMs,
|
|
4042
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4043
|
+
};
|
|
4044
|
+
await deliverWebhook(config, payload);
|
|
4045
|
+
}
|
|
3640
4046
|
async function toGeneration(raw, metadata) {
|
|
3641
4047
|
if (!raw.success) {
|
|
3642
4048
|
return fail({
|
|
@@ -3659,15 +4065,50 @@ function brotuClient(options) {
|
|
|
3659
4065
|
const routed = route(params.model);
|
|
3660
4066
|
if (routed.error) return fail(routed.error);
|
|
3661
4067
|
try {
|
|
3662
|
-
|
|
4068
|
+
const result = await toGeneration(
|
|
3663
4069
|
await generateWith(routed.data.adapter, kind, params),
|
|
3664
4070
|
params.metadata
|
|
3665
4071
|
);
|
|
4072
|
+
if (result.error) {
|
|
4073
|
+
await notifySettled({
|
|
4074
|
+
event: "generation.failed",
|
|
4075
|
+
params,
|
|
4076
|
+
kind,
|
|
4077
|
+
provider: routed.data.provider,
|
|
4078
|
+
model: routed.data.model,
|
|
4079
|
+
error: result.error,
|
|
4080
|
+
metadata: params.metadata
|
|
4081
|
+
});
|
|
4082
|
+
return result;
|
|
4083
|
+
}
|
|
4084
|
+
await notifySettled({
|
|
4085
|
+
event: "generation.succeeded",
|
|
4086
|
+
params,
|
|
4087
|
+
kind,
|
|
4088
|
+
provider: result.data.provider,
|
|
4089
|
+
model: result.data.model,
|
|
4090
|
+
outputs: result.data.outputs,
|
|
4091
|
+
metadata: result.data.metadata,
|
|
4092
|
+
processingTimeMs: result.data.processingTimeMs
|
|
4093
|
+
});
|
|
4094
|
+
return result;
|
|
3666
4095
|
} catch (error) {
|
|
3667
|
-
|
|
4096
|
+
const failed = failFrom("provider_error", error, {
|
|
3668
4097
|
provider: routed.data.provider,
|
|
3669
4098
|
model: routed.data.model
|
|
3670
4099
|
});
|
|
4100
|
+
if (failed.error) {
|
|
4101
|
+
await notifySettled({
|
|
4102
|
+
event: "generation.failed",
|
|
4103
|
+
params,
|
|
4104
|
+
kind,
|
|
4105
|
+
provider: routed.data.provider,
|
|
4106
|
+
model: routed.data.model,
|
|
4107
|
+
error: failed.error,
|
|
4108
|
+
metadata: params.metadata
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
4111
|
+
return failed;
|
|
3671
4112
|
}
|
|
3672
4113
|
}
|
|
3673
4114
|
async function submit(kind, params) {
|
|
@@ -3686,18 +4127,40 @@ function brotuClient(options) {
|
|
|
3686
4127
|
() => generateWith(routed.data.adapter, kind, params)
|
|
3687
4128
|
);
|
|
3688
4129
|
if (!raw.success) {
|
|
3689
|
-
|
|
4130
|
+
const error = {
|
|
3690
4131
|
code: "provider_error",
|
|
3691
4132
|
message: raw.error ?? "The provider rejected the request.",
|
|
3692
4133
|
provider: raw.provider,
|
|
3693
4134
|
model: raw.model
|
|
4135
|
+
};
|
|
4136
|
+
await notifySettled({
|
|
4137
|
+
event: "generation.failed",
|
|
4138
|
+
params,
|
|
4139
|
+
kind,
|
|
4140
|
+
provider: raw.provider,
|
|
4141
|
+
model: raw.model,
|
|
4142
|
+
error,
|
|
4143
|
+
metadata: params.metadata
|
|
3694
4144
|
});
|
|
4145
|
+
return fail(error);
|
|
3695
4146
|
}
|
|
3696
|
-
|
|
4147
|
+
const job = {
|
|
3697
4148
|
...base,
|
|
3698
4149
|
id: `inline-${base.model}-${base.submittedAt}`,
|
|
3699
4150
|
result: raw
|
|
4151
|
+
};
|
|
4152
|
+
await notifySettled({
|
|
4153
|
+
event: "generation.succeeded",
|
|
4154
|
+
params,
|
|
4155
|
+
job,
|
|
4156
|
+
kind,
|
|
4157
|
+
provider: raw.provider,
|
|
4158
|
+
model: raw.model,
|
|
4159
|
+
outputs: raw.outputs,
|
|
4160
|
+
metadata: params.metadata,
|
|
4161
|
+
processingTimeMs: raw.processingTimeMs
|
|
3700
4162
|
});
|
|
4163
|
+
return ok(job);
|
|
3701
4164
|
} catch (error) {
|
|
3702
4165
|
if (!isPendingJob(error)) {
|
|
3703
4166
|
return failFrom("provider_error", error, {
|
|
@@ -3712,11 +4175,54 @@ function brotuClient(options) {
|
|
|
3712
4175
|
});
|
|
3713
4176
|
}
|
|
3714
4177
|
}
|
|
4178
|
+
async function notifyFromSnapshot(job, snapshot) {
|
|
4179
|
+
if (snapshot.status === "succeeded" && snapshot.result) {
|
|
4180
|
+
await notifySettled({
|
|
4181
|
+
event: "generation.succeeded",
|
|
4182
|
+
params: job.params,
|
|
4183
|
+
job,
|
|
4184
|
+
outputs: snapshot.result.outputs,
|
|
4185
|
+
provider: snapshot.result.provider,
|
|
4186
|
+
model: snapshot.result.model,
|
|
4187
|
+
processingTimeMs: snapshot.result.processingTimeMs,
|
|
4188
|
+
metadata: job.metadata
|
|
4189
|
+
});
|
|
4190
|
+
return;
|
|
4191
|
+
}
|
|
4192
|
+
if (snapshot.status === "failed") {
|
|
4193
|
+
await notifySettled({
|
|
4194
|
+
event: "generation.failed",
|
|
4195
|
+
params: job.params,
|
|
4196
|
+
job,
|
|
4197
|
+
error: {
|
|
4198
|
+
code: "provider_error",
|
|
4199
|
+
message: snapshot.error ?? "The job failed.",
|
|
4200
|
+
provider: job.provider,
|
|
4201
|
+
model: job.model
|
|
4202
|
+
},
|
|
4203
|
+
metadata: job.metadata
|
|
4204
|
+
});
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
async function finalizeSnapshot(job, snapshot) {
|
|
4208
|
+
if (snapshot.status === "succeeded" && snapshot.result) {
|
|
4209
|
+
const persisted = await toGeneration(snapshot.result, job.metadata);
|
|
4210
|
+
if (!persisted.error) {
|
|
4211
|
+
snapshot = {
|
|
4212
|
+
status: "succeeded",
|
|
4213
|
+
result: {
|
|
4214
|
+
...snapshot.result,
|
|
4215
|
+
outputs: persisted.data.outputs
|
|
4216
|
+
}
|
|
4217
|
+
};
|
|
4218
|
+
}
|
|
4219
|
+
}
|
|
4220
|
+
await notifyFromSnapshot(job, snapshot);
|
|
4221
|
+
return snapshot;
|
|
4222
|
+
}
|
|
3715
4223
|
async function pollJob(job) {
|
|
3716
4224
|
if (job.result) {
|
|
3717
|
-
return ok(
|
|
3718
|
-
job.result.success ? { status: "succeeded", result: job.result } : { status: "failed", error: job.result.error }
|
|
3719
|
-
);
|
|
4225
|
+
return ok(await finalizeSnapshot(job, job.result.success ? { status: "succeeded", result: job.result } : { status: "failed", error: job.result.error }));
|
|
3720
4226
|
}
|
|
3721
4227
|
const routed = route(job.model);
|
|
3722
4228
|
if (routed.error) return fail(routed.error);
|
|
@@ -3729,7 +4235,7 @@ function brotuClient(options) {
|
|
|
3729
4235
|
});
|
|
3730
4236
|
}
|
|
3731
4237
|
try {
|
|
3732
|
-
return ok(await adapter.completeJob(job));
|
|
4238
|
+
return ok(await finalizeSnapshot(job, await adapter.completeJob(job)));
|
|
3733
4239
|
} catch (error) {
|
|
3734
4240
|
return failFrom("provider_error", error, {
|
|
3735
4241
|
provider: job.provider,
|
|
@@ -3738,7 +4244,7 @@ function brotuClient(options) {
|
|
|
3738
4244
|
}
|
|
3739
4245
|
}
|
|
3740
4246
|
function klingNamespace() {
|
|
3741
|
-
const configured =
|
|
4247
|
+
const configured = vendors.kling;
|
|
3742
4248
|
if (!configured) return void 0;
|
|
3743
4249
|
const adapter = new KlingAdapter({
|
|
3744
4250
|
apiKey: configured.apiKey,
|
|
@@ -3763,7 +4269,7 @@ function brotuClient(options) {
|
|
|
3763
4269
|
};
|
|
3764
4270
|
}
|
|
3765
4271
|
function googleNamespace() {
|
|
3766
|
-
const configured =
|
|
4272
|
+
const configured = vendors.google;
|
|
3767
4273
|
if (!configured) return void 0;
|
|
3768
4274
|
const adapter = new GoogleAdapter({
|
|
3769
4275
|
apiKey: configured.apiKey,
|
|
@@ -3798,6 +4304,17 @@ function brotuClient(options) {
|
|
|
3798
4304
|
submit: (params) => submit("audio", params),
|
|
3799
4305
|
generate: (params) => generate("audio", params)
|
|
3800
4306
|
},
|
|
4307
|
+
webhook: {
|
|
4308
|
+
set(value) {
|
|
4309
|
+
registeredWebhook = resolveWebhook(value);
|
|
4310
|
+
},
|
|
4311
|
+
clear() {
|
|
4312
|
+
registeredWebhook = void 0;
|
|
4313
|
+
},
|
|
4314
|
+
get() {
|
|
4315
|
+
return registeredWebhook;
|
|
4316
|
+
}
|
|
4317
|
+
},
|
|
3801
4318
|
jobs: {
|
|
3802
4319
|
poll: pollJob,
|
|
3803
4320
|
wait: async (job, waitOptions) => {
|
|
@@ -3807,7 +4324,13 @@ function brotuClient(options) {
|
|
|
3807
4324
|
if (polled.error) return fail(polled.error);
|
|
3808
4325
|
const snapshot = polled.data;
|
|
3809
4326
|
if (snapshot.status === "succeeded" && snapshot.result) {
|
|
3810
|
-
return
|
|
4327
|
+
return ok({
|
|
4328
|
+
outputs: snapshot.result.outputs,
|
|
4329
|
+
provider: snapshot.result.provider,
|
|
4330
|
+
model: snapshot.result.model,
|
|
4331
|
+
processingTimeMs: snapshot.result.processingTimeMs,
|
|
4332
|
+
metadata: job.metadata
|
|
4333
|
+
});
|
|
3811
4334
|
}
|
|
3812
4335
|
if (snapshot.status === "failed") {
|
|
3813
4336
|
return fail({
|
|
@@ -3829,7 +4352,7 @@ function brotuClient(options) {
|
|
|
3829
4352
|
}
|
|
3830
4353
|
}
|
|
3831
4354
|
},
|
|
3832
|
-
models: () => getAvailableModels(
|
|
4355
|
+
models: () => getAvailableModels(optionsWithKey),
|
|
3833
4356
|
estimateCost: async (type, params) => {
|
|
3834
4357
|
const routed = route(params.model);
|
|
3835
4358
|
if (routed.error) return fail(routed.error);
|
|
@@ -3848,7 +4371,9 @@ function brotuClient(options) {
|
|
|
3848
4371
|
0 && (module.exports = {
|
|
3849
4372
|
BYTEPLUS_CATALOG,
|
|
3850
4373
|
BYTEPLUS_MODELS,
|
|
4374
|
+
BrotuAdapter,
|
|
3851
4375
|
BytePlusAdapter,
|
|
4376
|
+
DEFAULT_BROTU_API_URL,
|
|
3852
4377
|
ELEVENLABS_CATALOG,
|
|
3853
4378
|
ELEVENLABS_MODELS,
|
|
3854
4379
|
ELEVENLABS_OUTPUT_FORMATS,
|
|
@@ -3877,6 +4402,7 @@ function brotuClient(options) {
|
|
|
3877
4402
|
QwenAdapter,
|
|
3878
4403
|
brotuClient,
|
|
3879
4404
|
createS3Storage,
|
|
4405
|
+
deliverWebhook,
|
|
3880
4406
|
fail,
|
|
3881
4407
|
getAvailableModels,
|
|
3882
4408
|
getModel,
|
|
@@ -3888,5 +4414,6 @@ function brotuClient(options) {
|
|
|
3888
4414
|
persistOutputs,
|
|
3889
4415
|
registerModels,
|
|
3890
4416
|
resetCatalog,
|
|
3891
|
-
resolveProvider
|
|
4417
|
+
resolveProvider,
|
|
4418
|
+
resolveWebhook
|
|
3892
4419
|
});
|