@byte_fluffy/nexra-sdk 0.1.0-alpha.5 → 0.1.0-alpha.50
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 +524 -15
- package/dist/catalog.d.ts +47 -1
- package/dist/catalog.js +10 -3
- package/dist/chunk-BS3B6F7W.js +943 -0
- package/dist/chunk-WXPCZYOK.js +62321 -0
- package/dist/errors-DJZuAKoO.d.ts +33 -0
- package/dist/index.d.ts +76 -1056
- package/dist/index.js +127 -1279
- package/dist/json-Cz0MkWQn.d.ts +11 -0
- package/dist/language.d.ts +38 -0
- package/dist/language.js +933 -0
- package/dist/media-DzOxLGs-.d.ts +343 -0
- package/dist/{index-FyUOK0F-.d.ts → media-geometry-COln1N_2.d.ts} +124 -99
- package/dist/media.d.ts +2 -0
- package/dist/media.js +26 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.js +0 -0
- package/dist/voices-xyPqhQqR.d.ts +348 -0
- package/package.json +48 -1
- package/dist/chunk-2C2ZZKUC.js +0 -6724
package/dist/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ceilDecimal,
|
|
3
|
+
multiplyDecimal
|
|
4
|
+
} from "./chunk-FCH5IQP5.js";
|
|
1
5
|
import {
|
|
2
6
|
CatalogClient,
|
|
3
7
|
HttpTransport,
|
|
@@ -5,1146 +9,25 @@ import {
|
|
|
5
9
|
NexraTaskError,
|
|
6
10
|
NexraWaitTimeoutError,
|
|
7
11
|
applyMediaGeometry,
|
|
12
|
+
applyNoWatermark,
|
|
8
13
|
decodeMediaGeometry,
|
|
9
14
|
encodeMediaGeometry,
|
|
15
|
+
formatModelDisplayName,
|
|
10
16
|
getModelSpec,
|
|
11
17
|
jsonBody,
|
|
12
18
|
listModelSpecs,
|
|
19
|
+
parseServerSentEvents,
|
|
13
20
|
projectMediaGeometry,
|
|
14
21
|
queryString,
|
|
15
22
|
sleep
|
|
16
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-WXPCZYOK.js";
|
|
17
24
|
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"image",
|
|
25
|
-
"video",
|
|
26
|
-
"audio",
|
|
27
|
-
"document",
|
|
28
|
-
"archive",
|
|
29
|
-
"other"
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
// ../contracts/src/uploaded-media.ts
|
|
33
|
-
function modelUploadRegion(regions) {
|
|
34
|
-
return regions.length > 0 && regions.every((region) => /^cn(?:-|$)/.test(region.trim().toLowerCase())) ? "cn" : "global";
|
|
35
|
-
}
|
|
36
|
-
function uploadedMediaUrl(value, region) {
|
|
37
|
-
if (!value.startsWith("https://")) return value;
|
|
38
|
-
try {
|
|
39
|
-
const url = new URL(value);
|
|
40
|
-
if (url.username || url.password || url.port || !["tos.nexra-ai.com", "tos2.nexra-ai.com", "upload.nexra-ai.com"].includes(url.hostname) || !url.pathname.startsWith("/user-assets/") || url.pathname === "/user-assets/") return value;
|
|
41
|
-
url.hostname = region === "cn" ? "tos.nexra-ai.com" : "tos2.nexra-ai.com";
|
|
42
|
-
return url.href;
|
|
43
|
-
} catch {
|
|
44
|
-
return value;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function uploadedMediaInput(input, region) {
|
|
48
|
-
const visit = (value) => {
|
|
49
|
-
if (typeof value === "string") return uploadedMediaUrl(value, region);
|
|
50
|
-
if (Array.isArray(value)) return value.map(visit);
|
|
51
|
-
if (value !== null && typeof value === "object") {
|
|
52
|
-
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
53
|
-
key,
|
|
54
|
-
["text", "texts", "prompt", "negative_prompt", "negativePrompt"].includes(key) ? item : visit(item)
|
|
55
|
-
]));
|
|
56
|
-
}
|
|
57
|
-
return value;
|
|
58
|
-
};
|
|
59
|
-
return visit(input);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ../contracts/src/admin-usage.ts
|
|
63
|
-
var USAGE_SOURCES = ["provider", "estimated", "reconciled"];
|
|
64
|
-
var USAGE_NORMALIZATION_STATUSES = [
|
|
65
|
-
"normalized",
|
|
66
|
-
"partial",
|
|
67
|
-
"invalid",
|
|
68
|
-
"missing"
|
|
69
|
-
];
|
|
70
|
-
var USAGE_RATING_STATUSES = [
|
|
71
|
-
"billed",
|
|
72
|
-
"unsettled",
|
|
73
|
-
"billing_failed",
|
|
74
|
-
"unpriced",
|
|
75
|
-
"usage_missing"
|
|
76
|
-
];
|
|
77
|
-
var USAGE_GROUP_BYS = [
|
|
78
|
-
"minute",
|
|
79
|
-
"hour",
|
|
80
|
-
"day",
|
|
81
|
-
"month",
|
|
82
|
-
"user",
|
|
83
|
-
"apiKey",
|
|
84
|
-
"provider",
|
|
85
|
-
"channel",
|
|
86
|
-
"model",
|
|
87
|
-
"operation"
|
|
88
|
-
];
|
|
89
|
-
var USAGE_AMOUNT_KINDS = ["sale", "cost", "gross"];
|
|
90
|
-
var TOKEN_METRICS = [
|
|
91
|
-
"input_tokens",
|
|
92
|
-
"cached_input_tokens",
|
|
93
|
-
"cache_write_tokens",
|
|
94
|
-
"cache_creation_5m_input_tokens",
|
|
95
|
-
"cache_creation_1h_input_tokens",
|
|
96
|
-
"cache_read_input_tokens",
|
|
97
|
-
"output_tokens"
|
|
98
|
-
];
|
|
99
|
-
var MONEY_STRING_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d{1,8})?$/;
|
|
100
|
-
var USAGE_FILTER_SELECTION_LIMIT = 200;
|
|
101
|
-
function validateUsageFilters(value) {
|
|
102
|
-
if (!isObject(value)) throw new Error("Usage filters must be an object");
|
|
103
|
-
const filters = value;
|
|
104
|
-
const start = Date.parse(String(filters.start));
|
|
105
|
-
const end = Date.parse(String(filters.end));
|
|
106
|
-
if (!Number.isFinite(start)) throw new Error("filters.start must be an ISO-8601 instant");
|
|
107
|
-
if (!Number.isFinite(end)) throw new Error("filters.end must be an ISO-8601 instant");
|
|
108
|
-
if (start >= end) throw new Error("filters.end must be after filters.start ([start, end) window)");
|
|
109
|
-
if (filters.timeZoneOffsetMinutes !== void 0 && (!Number.isSafeInteger(filters.timeZoneOffsetMinutes) || filters.timeZoneOffsetMinutes < -720 || filters.timeZoneOffsetMinutes > 840)) {
|
|
110
|
-
throw new Error("filters.timeZoneOffsetMinutes must be an integer between -720 and 840");
|
|
111
|
-
}
|
|
112
|
-
for (const key of ["userIds", "apiKeyIds", "providerIds", "channelIds", "modelIds", "operations", "requestStatuses"]) {
|
|
113
|
-
const list = filters[key];
|
|
114
|
-
if (list !== void 0 && (!isStringArray(list) || list.length > USAGE_FILTER_SELECTION_LIMIT)) {
|
|
115
|
-
throw new Error(`filters.${key} must be at most ${USAGE_FILTER_SELECTION_LIMIT} non-empty strings`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
if (filters.upstreamModelId !== void 0 && (typeof filters.upstreamModelId !== "string" || filters.upstreamModelId === "")) {
|
|
119
|
-
throw new Error("filters.upstreamModelId must be a non-empty string");
|
|
120
|
-
}
|
|
121
|
-
for (const key of ["usageSources", "normalizationStatuses", "ratingStatuses"]) {
|
|
122
|
-
const list = filters[key];
|
|
123
|
-
if (list === void 0) continue;
|
|
124
|
-
if (!isStringArray(list)) throw new Error(`filters.${key} must be an array of non-empty strings`);
|
|
125
|
-
const allowed = key === "usageSources" ? USAGE_SOURCES : key === "normalizationStatuses" ? USAGE_NORMALIZATION_STATUSES : USAGE_RATING_STATUSES;
|
|
126
|
-
if (list.some((item) => !allowed.includes(item))) {
|
|
127
|
-
throw new Error(`filters.${key} contains an unsupported status`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
function validateUsageSummaryQuery(value) {
|
|
132
|
-
if (!isObject(value)) throw new Error("Usage summary query must be an object");
|
|
133
|
-
validateUsageFilters(value.filters);
|
|
134
|
-
if (!Array.isArray(value.groupBy) || value.groupBy.length === 0) {
|
|
135
|
-
throw new Error("groupBy must be a non-empty array");
|
|
136
|
-
}
|
|
137
|
-
const timeGranularities = value.groupBy.filter(
|
|
138
|
-
(key) => ["minute", "hour", "day", "month"].includes(String(key))
|
|
139
|
-
);
|
|
140
|
-
if (timeGranularities.length > 1) {
|
|
141
|
-
throw new Error("groupBy accepts at most one time granularity");
|
|
142
|
-
}
|
|
143
|
-
const seen = /* @__PURE__ */ new Set();
|
|
144
|
-
for (const key of value.groupBy) {
|
|
145
|
-
if (!USAGE_GROUP_BYS.includes(String(key))) {
|
|
146
|
-
throw new Error(`groupBy contains an unsupported key: ${String(key)}`);
|
|
147
|
-
}
|
|
148
|
-
if (seen.has(String(key))) throw new Error("groupBy contains duplicate keys");
|
|
149
|
-
seen.add(String(key));
|
|
150
|
-
}
|
|
151
|
-
if (!Number.isSafeInteger(value.limit) || value.limit < 1 || value.limit > 500) {
|
|
152
|
-
throw new Error("limit must be an integer between 1 and 500");
|
|
153
|
-
}
|
|
154
|
-
if (value.cursor !== void 0 && (typeof value.cursor !== "string" || value.cursor === "")) {
|
|
155
|
-
throw new Error("cursor must be a non-empty string");
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
var CURSOR_PREFIX = "v1.offset.";
|
|
159
|
-
function encodeUsageSummaryCursor(offset) {
|
|
160
|
-
return `${CURSOR_PREFIX}${offset}`;
|
|
161
|
-
}
|
|
162
|
-
function parseUsageSummaryCursor(value) {
|
|
163
|
-
if (!value.startsWith(CURSOR_PREFIX)) return null;
|
|
164
|
-
const offset = Number(value.slice(CURSOR_PREFIX.length));
|
|
165
|
-
return Number.isSafeInteger(offset) && offset >= 0 ? offset : null;
|
|
166
|
-
}
|
|
167
|
-
var KEYSET_CURSOR_PREFIX = "v1.key.";
|
|
168
|
-
function encodeUsageKeysetCursor(receivedAt, requestId) {
|
|
169
|
-
return `${KEYSET_CURSOR_PREFIX}${encodeURIComponent(receivedAt)}|${encodeURIComponent(requestId)}`;
|
|
170
|
-
}
|
|
171
|
-
function parseUsageKeysetCursor(value) {
|
|
172
|
-
if (!value.startsWith(KEYSET_CURSOR_PREFIX)) return null;
|
|
173
|
-
const parts = value.slice(KEYSET_CURSOR_PREFIX.length).split("|");
|
|
174
|
-
if (parts.length !== 2 || !parts[0] || !parts[1]) return null;
|
|
175
|
-
let receivedAt;
|
|
176
|
-
let requestId;
|
|
177
|
-
try {
|
|
178
|
-
receivedAt = decodeURIComponent(parts[0]);
|
|
179
|
-
requestId = decodeURIComponent(parts[1]);
|
|
180
|
-
} catch {
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(requestId)) {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?Z$/.test(receivedAt) || !Number.isFinite(Date.parse(receivedAt))) return null;
|
|
187
|
-
if (new Date(receivedAt).toISOString().slice(0, 19) !== receivedAt.slice(0, 19)) return null;
|
|
188
|
-
return { receivedAt, requestId };
|
|
189
|
-
}
|
|
190
|
-
function validateUsageDetailsQuery(value) {
|
|
191
|
-
if (!isObject(value)) throw new Error("Usage details query must be an object");
|
|
192
|
-
validateUsageFilters(value.filters);
|
|
193
|
-
if (!Number.isSafeInteger(value.limit) || value.limit < 1 || value.limit > 200) {
|
|
194
|
-
throw new Error("limit must be an integer between 1 and 200");
|
|
195
|
-
}
|
|
196
|
-
if (value.cursor !== void 0 && (typeof value.cursor !== "string" || value.cursor === "")) {
|
|
197
|
-
throw new Error("cursor must be a non-empty string");
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
var USAGE_ANOMALY_CATEGORIES = [
|
|
201
|
-
"usage_missing",
|
|
202
|
-
"normalization_failed",
|
|
203
|
-
"unpriced",
|
|
204
|
-
"dimension_mismatch",
|
|
205
|
-
"settlement_failed"
|
|
206
|
-
];
|
|
207
|
-
function validateUsageAnomaliesQuery(value) {
|
|
208
|
-
if (!isObject(value)) throw new Error("Usage anomalies query must be an object");
|
|
209
|
-
validateUsageFilters(value.filters);
|
|
210
|
-
if (value.categories !== void 0) {
|
|
211
|
-
if (!isStringArray(value.categories) || value.categories.some((item) => !USAGE_ANOMALY_CATEGORIES.includes(item))) {
|
|
212
|
-
throw new Error("categories contains an unsupported anomaly category");
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
if (!Number.isSafeInteger(value.limit) || value.limit < 1 || value.limit > 200) {
|
|
216
|
-
throw new Error("limit must be an integer between 1 and 200");
|
|
217
|
-
}
|
|
218
|
-
if (value.cursor !== void 0 && (typeof value.cursor !== "string" || value.cursor === "")) {
|
|
219
|
-
throw new Error("cursor must be a non-empty string");
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
function validateUsageAnomalySummaryQuery(value) {
|
|
223
|
-
if (!isObject(value)) throw new Error("Usage anomaly summary query must be an object");
|
|
224
|
-
validateUsageFilters(value.filters);
|
|
225
|
-
if (value.breakdowns !== void 0) {
|
|
226
|
-
if (!Array.isArray(value.breakdowns) || value.breakdowns.length > 2 || value.breakdowns.some((item) => !["provider", "channel", "model"].includes(String(item)))) {
|
|
227
|
-
throw new Error("breakdowns accepts at most two of provider, channel, model");
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
function isObject(value) {
|
|
232
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
233
|
-
}
|
|
234
|
-
function isStringArray(value) {
|
|
235
|
-
return Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item !== "");
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// ../contracts/src/admin-users.ts
|
|
239
|
-
var ADMIN_USER_ROLES = ["admin", "dealer", "user"];
|
|
240
|
-
var ADMIN_USER_STATUSES = ["active", "banned"];
|
|
241
|
-
function validateAdminUserListQuery(value) {
|
|
242
|
-
if (!isObject2(value)) throw new Error("User list query must be an object");
|
|
243
|
-
if (value.query !== void 0 && (typeof value.query !== "string" || value.query.trim() === "" || value.query.length > 320)) {
|
|
244
|
-
throw new Error("query must be a non-empty string of at most 320 characters");
|
|
245
|
-
}
|
|
246
|
-
if (value.role !== void 0 && !ADMIN_USER_ROLES.includes(String(value.role))) {
|
|
247
|
-
throw new Error("role is not supported");
|
|
248
|
-
}
|
|
249
|
-
if (value.status !== void 0 && !ADMIN_USER_STATUSES.includes(String(value.status))) {
|
|
250
|
-
throw new Error("status is not supported");
|
|
251
|
-
}
|
|
252
|
-
if (!Number.isSafeInteger(value.limit) || value.limit < 1 || value.limit > 200) {
|
|
253
|
-
throw new Error("limit must be an integer between 1 and 200");
|
|
254
|
-
}
|
|
255
|
-
if (value.cursor !== void 0 && (typeof value.cursor !== "string" || value.cursor === "")) {
|
|
256
|
-
throw new Error("cursor must be a non-empty string");
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
var USER_CURSOR_PREFIX = "v1.user.";
|
|
260
|
-
function encodeAdminUserCursor(cursor) {
|
|
261
|
-
return `${USER_CURSOR_PREFIX}${encodeURIComponent(cursor.createdAt)}|${encodeURIComponent(cursor.userId)}`;
|
|
262
|
-
}
|
|
263
|
-
function parseAdminUserCursor(value) {
|
|
264
|
-
try {
|
|
265
|
-
if (!value.startsWith(USER_CURSOR_PREFIX)) return null;
|
|
266
|
-
const parts = value.slice(USER_CURSOR_PREFIX.length).split("|");
|
|
267
|
-
if (parts.length !== 2) return null;
|
|
268
|
-
const [encodedCreatedAt, encodedUserId] = parts;
|
|
269
|
-
if (!encodedCreatedAt || !encodedUserId) return null;
|
|
270
|
-
const createdAt = decodeURIComponent(encodedCreatedAt);
|
|
271
|
-
const userId = decodeURIComponent(encodedUserId);
|
|
272
|
-
return Number.isFinite(Date.parse(createdAt)) && userId !== "" ? { createdAt, userId } : null;
|
|
273
|
-
} catch {
|
|
274
|
-
return null;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
var ADMIN_USER_ACTIVITY_KINDS = ["ledger", "recharge", "hold", "audit"];
|
|
278
|
-
function isMoneyString(value) {
|
|
279
|
-
return MONEY_STRING_PATTERN.test(value);
|
|
280
|
-
}
|
|
281
|
-
function validateAdminUserUpdate(value) {
|
|
282
|
-
if (!isObject2(value)) throw new Error("User update must be an object");
|
|
283
|
-
if (typeof value.reason !== "string" || value.reason.trim() === "" || value.reason.length > 500) {
|
|
284
|
-
throw new Error("reason is required (at most 500 characters)");
|
|
285
|
-
}
|
|
286
|
-
if (value.role !== void 0 && !ADMIN_USER_ROLES.includes(String(value.role))) {
|
|
287
|
-
throw new Error("role is not supported");
|
|
288
|
-
}
|
|
289
|
-
if (value.status !== void 0 && !ADMIN_USER_STATUSES.includes(String(value.status))) {
|
|
290
|
-
throw new Error("status is not supported");
|
|
291
|
-
}
|
|
292
|
-
if (value.qpsLimit !== void 0 && (!Number.isSafeInteger(value.qpsLimit) || value.qpsLimit < 1 || value.qpsLimit > 1e5)) {
|
|
293
|
-
throw new Error("qpsLimit must be an integer between 1 and 100000");
|
|
294
|
-
}
|
|
295
|
-
if (value.role === void 0 && value.status === void 0 && value.qpsLimit === void 0) {
|
|
296
|
-
throw new Error("At least one of role, status, or qpsLimit is required");
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
function validateAdminUserApiKeyUpdate(value) {
|
|
300
|
-
if (!isObject2(value)) throw new Error("API key update must be an object");
|
|
301
|
-
if (typeof value.reason !== "string" || value.reason.trim() === "" || value.reason.length > 500) {
|
|
302
|
-
throw new Error("reason is required (at most 500 characters)");
|
|
303
|
-
}
|
|
304
|
-
if (value.revoked !== void 0 && typeof value.revoked !== "boolean") {
|
|
305
|
-
throw new Error("revoked must be a boolean");
|
|
306
|
-
}
|
|
307
|
-
if (value.rateLimitPerSecond !== void 0 && value.rateLimitPerSecond !== null && (!Number.isSafeInteger(value.rateLimitPerSecond) || value.rateLimitPerSecond < 1 || value.rateLimitPerSecond > 1e5)) {
|
|
308
|
-
throw new Error("rateLimitPerSecond must be an integer between 1 and 100000, or null");
|
|
309
|
-
}
|
|
310
|
-
if (value.revoked === void 0 && value.rateLimitPerSecond === void 0) {
|
|
311
|
-
throw new Error("At least one of revoked or rateLimitPerSecond is required");
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
function validateAdminWalletRechargeInput(value) {
|
|
315
|
-
if (!isObject2(value)) throw new Error("Wallet recharge must be an object");
|
|
316
|
-
if (typeof value.userId !== "string" || value.userId === "") {
|
|
317
|
-
throw new Error("userId is required");
|
|
318
|
-
}
|
|
319
|
-
if (value.currency !== "CNY") {
|
|
320
|
-
throw new Error("currency must be CNY");
|
|
321
|
-
}
|
|
322
|
-
if (typeof value.amount !== "string" || !isMoneyString(value.amount) || value.amount.startsWith("-") || Number(value.amount) <= 0) {
|
|
323
|
-
throw new Error("amount must be a positive decimal string");
|
|
324
|
-
}
|
|
325
|
-
if ((value.amount.split(".")[0]?.length ?? 0) > 12) {
|
|
326
|
-
throw new Error("amount exceeds the 12-digit integer limit of the wallet balance");
|
|
327
|
-
}
|
|
328
|
-
if (typeof value.externalOrderId !== "string" || value.externalOrderId.trim() === "" || value.externalOrderId.trim().length > 240) {
|
|
329
|
-
throw new Error("externalOrderId is required (at most 240 characters)");
|
|
330
|
-
}
|
|
331
|
-
if (typeof value.reason !== "string" || value.reason.trim() === "" || value.reason.length > 500) {
|
|
332
|
-
throw new Error("reason is required (at most 500 characters)");
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
function isObject2(value) {
|
|
336
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// ../contracts/src/usage-filter-options.ts
|
|
340
|
-
var USAGE_FILTER_KINDS = ["model", "user", "apiKey", "provider", "channel", "operation"];
|
|
341
|
-
var MODEL_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
342
|
-
function validId(kind, id) {
|
|
343
|
-
return typeof id === "string" && id.length > 0 && id.length <= 128 && !/[\s,|]/u.test(id) && (kind === "user" || kind === "operation" || MODEL_ID.test(id));
|
|
344
|
-
}
|
|
345
|
-
function encodeUsageFilterCursor(kind, query, id) {
|
|
346
|
-
return `usage-filter-v1|${kind}|${encodeURIComponent(query)}|${encodeURIComponent(id)}`;
|
|
347
|
-
}
|
|
348
|
-
function parseUsageFilterCursor(value, kind, query) {
|
|
349
|
-
try {
|
|
350
|
-
const parts = value.split("|");
|
|
351
|
-
if (parts.length !== 4 || parts[0] !== "usage-filter-v1" || parts[1] !== kind || decodeURIComponent(parts[2]) !== query) return null;
|
|
352
|
-
const id = decodeURIComponent(parts[3]);
|
|
353
|
-
return validId(kind, id) ? id : null;
|
|
354
|
-
} catch {
|
|
355
|
-
return null;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
function validateUsageFilterOptionsQuery(kind, value) {
|
|
359
|
-
if (!value || typeof value !== "object") throw new Error("\u7B5B\u9009\u67E5\u8BE2\u683C\u5F0F\u4E0D\u6B63\u786E");
|
|
360
|
-
const query = value;
|
|
361
|
-
if (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 100) throw new Error("\u4E00\u6B21\u6700\u591A\u67E5\u8BE2 100 \u9879");
|
|
362
|
-
if (query.query !== void 0 && (typeof query.query !== "string" || !query.query.trim() || query.query.length > 320)) throw new Error("\u641C\u7D22\u5185\u5BB9\u5FC5\u987B\u4E3A 1 \u81F3 320 \u4E2A\u5B57\u7B26");
|
|
363
|
-
if (query.ids !== void 0) {
|
|
364
|
-
if (!Array.isArray(query.ids) || !query.ids.length || query.ids.length > query.limit || query.ids.some((id) => !validId(kind, id))) throw new Error("\u8BF7\u8F93\u5165\u6709\u6548\u7684\u5185\u90E8 ID\uFF0C\u4E00\u6B21\u6700\u591A 100 \u9879");
|
|
365
|
-
if (query.query !== void 0 || query.cursor !== void 0) throw new Error("ID \u786E\u8BA4\u4E0D\u80FD\u540C\u65F6\u4F7F\u7528\u641C\u7D22\u6216\u5206\u9875");
|
|
366
|
-
}
|
|
367
|
-
if (query.cursor !== void 0 && (typeof query.cursor !== "string" || query.cursor.length > 4096 || !parseUsageFilterCursor(query.cursor, kind, query.query ?? ""))) throw new Error("\u5206\u9875\u6E38\u6807\u4E0E\u5F53\u524D\u7B5B\u9009\u4E0D\u5339\u914D");
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// ../contracts/src/money.ts
|
|
371
|
-
var MONEY_SCALE = 100000000n;
|
|
372
|
-
var BILLING_CURRENCY = "CNY";
|
|
373
|
-
var WALLET_MAX_BALANCE_UNITS = 99999999999999999999n;
|
|
374
|
-
function moneyToUnits(value) {
|
|
375
|
-
if (!/^-?\d+(\.\d{1,8})?$/.test(value)) throw new Error("Invalid money amount");
|
|
376
|
-
const negative = value.startsWith("-");
|
|
377
|
-
const [whole = "0", fraction = ""] = (negative ? value.slice(1) : value).split(".");
|
|
378
|
-
const units = BigInt(whole) * MONEY_SCALE + BigInt(fraction.padEnd(8, "0"));
|
|
379
|
-
return negative ? -units : units;
|
|
380
|
-
}
|
|
381
|
-
function moneyFromUnits(value) {
|
|
382
|
-
const negative = value < 0n;
|
|
383
|
-
const absolute = negative ? -value : value;
|
|
384
|
-
const fraction = (absolute % MONEY_SCALE).toString().padStart(8, "0").replace(/0+$/, "");
|
|
385
|
-
return `${negative ? "-" : ""}${absolute / MONEY_SCALE}${fraction ? `.${fraction}` : ""}`;
|
|
386
|
-
}
|
|
387
|
-
function multiplyMoney(value, multiplier, ...additionalMultipliers) {
|
|
388
|
-
let product = moneyToUnits(value);
|
|
389
|
-
if (product < 0n) throw new Error("Money amount must be non-negative");
|
|
390
|
-
let divisor = 1n;
|
|
391
|
-
for (const multiplierValue of [multiplier, ...additionalMultipliers]) {
|
|
392
|
-
const factor = moneyToUnits(multiplierValue);
|
|
393
|
-
if (factor <= 0n) throw new Error("Money multiplier must be positive");
|
|
394
|
-
product *= factor;
|
|
395
|
-
divisor *= MONEY_SCALE;
|
|
396
|
-
}
|
|
397
|
-
return moneyFromUnits((product + divisor / 2n) / divisor);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// ../contracts/src/model-schema.ts
|
|
401
|
-
function object(value) {
|
|
402
|
-
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
403
|
-
}
|
|
404
|
-
function normalizeModelParameterSchema(source) {
|
|
405
|
-
const result = structuredClone(source);
|
|
406
|
-
const input = object(result.input);
|
|
407
|
-
const properties = object(input.properties);
|
|
408
|
-
const ui = object(result.ui);
|
|
409
|
-
const fields = object(ui.fields);
|
|
410
|
-
const defaults = object(result.defaults);
|
|
411
|
-
const order = Array.isArray(ui.order) ? ui.order : [];
|
|
412
|
-
for (const [key, value] of Object.entries(properties)) {
|
|
413
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
414
|
-
const position = order.indexOf(key);
|
|
415
|
-
const decoration = {
|
|
416
|
-
...position >= 0 ? { order: position } : {},
|
|
417
|
-
...object(fields[key]),
|
|
418
|
-
...object(value["x-ui"])
|
|
419
|
-
};
|
|
420
|
-
if (Object.keys(decoration).length) value["x-ui"] = decoration;
|
|
421
|
-
if (key in defaults) value.default = defaults[key];
|
|
422
|
-
}
|
|
423
|
-
delete result.ui;
|
|
424
|
-
delete result.defaults;
|
|
425
|
-
return result;
|
|
426
|
-
}
|
|
427
|
-
function modelInputDefaults(input) {
|
|
428
|
-
return Object.fromEntries(Object.entries(object(input.properties)).flatMap(([key, value]) => {
|
|
429
|
-
const field = object(value);
|
|
430
|
-
return "default" in field ? [[key, structuredClone(field.default)]] : [];
|
|
431
|
-
}));
|
|
432
|
-
}
|
|
433
|
-
function orderedSchemaProperties(schema) {
|
|
434
|
-
const rank = (value) => {
|
|
435
|
-
const order = object(object(value)["x-ui"]).order;
|
|
436
|
-
return typeof order === "number" ? order : Number.MAX_SAFE_INTEGER;
|
|
437
|
-
};
|
|
438
|
-
return Object.entries(object(schema.properties)).sort(([, a], [, b]) => rank(a) - rank(b));
|
|
439
|
-
}
|
|
440
|
-
function modelInputUi(input) {
|
|
441
|
-
const entries = orderedSchemaProperties(input);
|
|
442
|
-
return {
|
|
443
|
-
order: entries.map(([key]) => key),
|
|
444
|
-
fields: Object.fromEntries(entries.flatMap(([key, value]) => {
|
|
445
|
-
const { order: _order, ...ui } = object(object(value)["x-ui"]);
|
|
446
|
-
return Object.keys(ui).length ? [[key, ui]] : [];
|
|
447
|
-
}))
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// ../contracts/src/pricing.ts
|
|
452
|
-
var BILLING_METRICS = [
|
|
453
|
-
"requests",
|
|
454
|
-
"input_tokens",
|
|
455
|
-
"cached_input_tokens",
|
|
456
|
-
"cache_write_tokens",
|
|
457
|
-
"cache_creation_5m_input_tokens",
|
|
458
|
-
"cache_creation_1h_input_tokens",
|
|
459
|
-
"cache_read_input_tokens",
|
|
460
|
-
"output_tokens",
|
|
461
|
-
"input_characters",
|
|
462
|
-
"input_utf8_bytes",
|
|
463
|
-
"input_images",
|
|
464
|
-
"output_images",
|
|
465
|
-
"input_audio_seconds",
|
|
466
|
-
"output_audio_seconds",
|
|
467
|
-
"input_video_seconds",
|
|
468
|
-
"output_video_seconds",
|
|
469
|
-
"provider_credits",
|
|
470
|
-
"web_search_requests"
|
|
471
|
-
];
|
|
472
|
-
var BILLING_EXPORT_DIMENSIONS = [
|
|
473
|
-
"resolution",
|
|
474
|
-
"duration",
|
|
475
|
-
"layer_decomposition",
|
|
476
|
-
"with_audio",
|
|
477
|
-
"has_input_video",
|
|
478
|
-
"hd",
|
|
479
|
-
"prompt_extend",
|
|
480
|
-
"modality",
|
|
481
|
-
"tier",
|
|
482
|
-
"quality",
|
|
483
|
-
"image_count",
|
|
484
|
-
"input_token_tier",
|
|
485
|
-
"input_modality",
|
|
486
|
-
"service_tier",
|
|
487
|
-
"inference_geo",
|
|
488
|
-
"speed",
|
|
489
|
-
"region_scope",
|
|
490
|
-
"has_reference_image",
|
|
491
|
-
"mode"
|
|
492
|
-
];
|
|
493
|
-
function allocateDiscountedPriceLines(lines, saleAmount) {
|
|
494
|
-
const moneyPattern = /^(?:0|[1-9]\d*)(?:\.\d{1,8})?$/;
|
|
495
|
-
if (!moneyPattern.test(saleAmount) || lines.some((line) => !moneyPattern.test(line.amount))) {
|
|
496
|
-
throw new Error("Sale allocation requires non-negative eight-decimal money");
|
|
497
|
-
}
|
|
498
|
-
const target = priceScaled(saleAmount);
|
|
499
|
-
const weights = lines.map((line) => priceScaled(line.amount));
|
|
500
|
-
const total = weights.reduce((sum, amount) => sum + amount, 0n);
|
|
501
|
-
if (target > total) throw new Error("Discounted sale amount exceeds the frozen line total");
|
|
502
|
-
const allocations = weights.map((weight) => total === 0n ? 0n : target * weight / total);
|
|
503
|
-
let remainder = target - allocations.reduce((sum, amount) => sum + amount, 0n);
|
|
504
|
-
return lines.map((line, index) => {
|
|
505
|
-
let amount = allocations[index];
|
|
506
|
-
if (weights[index] > 0n && remainder > 0n) {
|
|
507
|
-
amount += 1n;
|
|
508
|
-
remainder -= 1n;
|
|
509
|
-
}
|
|
510
|
-
return { ...line, discountedAmount: scaledMoney(amount) };
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
function validateNormalizedUsage(value) {
|
|
514
|
-
if (!isObject3(value) || value.schemaVersion !== 1 || !Array.isArray(value.components)) {
|
|
515
|
-
throw new Error("Normalized usage must use the metered v1 envelope");
|
|
516
|
-
}
|
|
517
|
-
if (value.billingEvidenceComplete !== void 0 && typeof value.billingEvidenceComplete !== "boolean") {
|
|
518
|
-
throw new Error("billingEvidenceComplete must be boolean");
|
|
519
|
-
}
|
|
520
|
-
if (value.components.length === 0 || value.components.length > 64) {
|
|
521
|
-
throw new Error("Normalized usage must contain between 1 and 64 components");
|
|
522
|
-
}
|
|
523
|
-
const identities = /* @__PURE__ */ new Set();
|
|
524
|
-
for (const [index, raw] of value.components.entries()) {
|
|
525
|
-
if (!isObject3(raw) || !BILLING_METRICS.includes(raw.metric)) {
|
|
526
|
-
throw new Error(`Usage component ${index} has an unsupported billing metric`);
|
|
527
|
-
}
|
|
528
|
-
if (typeof raw.quantity !== "number" || !Number.isFinite(raw.quantity) || raw.quantity < 0) {
|
|
529
|
-
throw new Error(`Usage component ${index} quantity must be non-negative`);
|
|
530
|
-
}
|
|
531
|
-
if (raw.dimensions !== void 0 && (!isObject3(raw.dimensions) || Object.values(raw.dimensions).some((item) => !isPrimitive(item)))) {
|
|
532
|
-
throw new Error(`Usage component ${index} dimensions must contain only scalar values`);
|
|
533
|
-
}
|
|
534
|
-
const identity = `${String(raw.metric)}:${stableDimensions(raw.dimensions)}`;
|
|
535
|
-
if (identities.has(identity)) {
|
|
536
|
-
throw new Error(`Usage component ${index} duplicates a metric and dimension set`);
|
|
537
|
-
}
|
|
538
|
-
identities.add(identity);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
function evaluatePriceRule(ruleValue, usageValue) {
|
|
542
|
-
validatePriceRule(ruleValue);
|
|
543
|
-
validateNormalizedUsage(usageValue);
|
|
544
|
-
const rule = ruleValue;
|
|
545
|
-
const usage = usageValue;
|
|
546
|
-
if (usage.billingEvidenceComplete === false) {
|
|
547
|
-
return { status: "incomplete", amount: "0", lines: [], issues: [{ code: "missing_usage", metric: "input_tokens", componentIndex: 0 }] };
|
|
548
|
-
}
|
|
549
|
-
const lines = [];
|
|
550
|
-
const issues = [];
|
|
551
|
-
let total = 0n;
|
|
552
|
-
for (const [componentIndex, component] of usage.components.entries()) {
|
|
553
|
-
const metricRates = rule.rates.filter((rate2) => rate2.metric === component.metric);
|
|
554
|
-
if (metricRates.length === 0) continue;
|
|
555
|
-
const matching = metricRates.filter((rate2) => dimensionsMatch(
|
|
556
|
-
rate2.dimensions,
|
|
557
|
-
component.dimensions
|
|
558
|
-
));
|
|
559
|
-
if (matching.length === 0) {
|
|
560
|
-
if (component.quantity === 0) continue;
|
|
561
|
-
issues.push({ code: "dimension_mismatch", metric: component.metric, componentIndex });
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
const specificity = Math.max(...matching.map((rate2) => Object.keys(rate2.dimensions ?? {}).length));
|
|
565
|
-
const selected = matching.filter((rate2) => Object.keys(rate2.dimensions ?? {}).length === specificity);
|
|
566
|
-
if (selected.length !== 1) {
|
|
567
|
-
if (component.quantity === 0) continue;
|
|
568
|
-
issues.push({ code: "ambiguous_rate", metric: component.metric, componentIndex });
|
|
569
|
-
continue;
|
|
570
|
-
}
|
|
571
|
-
const rate = selected[0];
|
|
572
|
-
const amount = rateAmount(component.quantity, rate);
|
|
573
|
-
total += amount;
|
|
574
|
-
lines.push({
|
|
575
|
-
metric: component.metric,
|
|
576
|
-
quantity: component.quantity,
|
|
577
|
-
unitSize: rate.unitSize,
|
|
578
|
-
unitPrice: rate.unitPrice,
|
|
579
|
-
rounding: rate.rounding,
|
|
580
|
-
includedQuantity: rate.includedQuantity ?? 0,
|
|
581
|
-
...rate.dimensions ? { dimensions: { ...rate.dimensions } } : {},
|
|
582
|
-
amount: scaledMoney(amount),
|
|
583
|
-
...component.evidence ? { evidence: structuredClone(component.evidence) } : {}
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
return {
|
|
587
|
-
status: issues.length === 0 ? "rated" : "incomplete",
|
|
588
|
-
amount: scaledMoney(total),
|
|
589
|
-
lines,
|
|
590
|
-
issues
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
|
-
function validatePriceRule(value) {
|
|
594
|
-
if (!isObject3(value) || value.schemaVersion !== 1 || value.type !== "metered") {
|
|
595
|
-
throw new Error("Price rule must use the metered v1 envelope");
|
|
596
|
-
}
|
|
597
|
-
if (!Array.isArray(value.rates) || value.rates.length === 0 || value.rates.length > 32) {
|
|
598
|
-
throw new Error("Price rule must contain between 1 and 32 rates");
|
|
599
|
-
}
|
|
600
|
-
const identities = /* @__PURE__ */ new Set();
|
|
601
|
-
for (const [index, raw] of value.rates.entries()) {
|
|
602
|
-
if (!isObject3(raw) || !BILLING_METRICS.includes(raw.metric)) {
|
|
603
|
-
throw new Error(`Price rate ${index} has an unsupported billing metric`);
|
|
604
|
-
}
|
|
605
|
-
if (!Number.isSafeInteger(raw.unitSize) || raw.unitSize <= 0) {
|
|
606
|
-
throw new Error(`Price rate ${index} unitSize must be a positive integer`);
|
|
607
|
-
}
|
|
608
|
-
if (typeof raw.unitPrice !== "string" || !/^(?:0|[1-9]\d*)(?:\.\d{1,8})?$/.test(raw.unitPrice)) {
|
|
609
|
-
throw new Error(`Price rate ${index} unitPrice must be a non-negative decimal string`);
|
|
610
|
-
}
|
|
611
|
-
if (!["proportional", "ceil"].includes(String(raw.rounding))) {
|
|
612
|
-
throw new Error(`Price rate ${index} has an unsupported rounding mode`);
|
|
613
|
-
}
|
|
614
|
-
if (raw.includedQuantity !== void 0 && (typeof raw.includedQuantity !== "number" || !Number.isFinite(raw.includedQuantity) || raw.includedQuantity < 0)) {
|
|
615
|
-
throw new Error(`Price rate ${index} includedQuantity must be non-negative`);
|
|
616
|
-
}
|
|
617
|
-
if (raw.dimensions !== void 0 && (!isObject3(raw.dimensions) || Object.values(raw.dimensions).some((item) => !isPrimitive(item)))) {
|
|
618
|
-
throw new Error(`Price rate ${index} dimensions must contain only scalar values`);
|
|
619
|
-
}
|
|
620
|
-
const identity = `${String(raw.metric)}:${stableDimensions(raw.dimensions)}`;
|
|
621
|
-
if (identities.has(identity)) {
|
|
622
|
-
throw new Error(`Price rate ${index} duplicates a metric and dimension set`);
|
|
623
|
-
}
|
|
624
|
-
identities.add(identity);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
function isObject3(value) {
|
|
628
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
629
|
-
}
|
|
630
|
-
function isPrimitive(value) {
|
|
631
|
-
return value === null || ["string", "number", "boolean"].includes(typeof value);
|
|
632
|
-
}
|
|
633
|
-
function stableDimensions(value) {
|
|
634
|
-
if (!isObject3(value)) return "";
|
|
635
|
-
return Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${JSON.stringify(item)}`).join(",");
|
|
636
|
-
}
|
|
637
|
-
var MONEY_SCALE2 = 100000000n;
|
|
638
|
-
function dimensionsMatch(expected, actual) {
|
|
639
|
-
return Object.entries(expected ?? {}).every(([key, value]) => actual?.[key] === value);
|
|
640
|
-
}
|
|
641
|
-
function rateAmount(quantity, rate) {
|
|
642
|
-
const quantityFraction = subtractFractions(
|
|
643
|
-
numberFraction(quantity),
|
|
644
|
-
numberFraction(rate.includedQuantity ?? 0)
|
|
645
|
-
);
|
|
646
|
-
if (quantityFraction.numerator <= 0n) return 0n;
|
|
647
|
-
const price = priceScaled(rate.unitPrice);
|
|
648
|
-
const unitDenominator = quantityFraction.denominator * BigInt(rate.unitSize);
|
|
649
|
-
if (rate.rounding === "ceil") {
|
|
650
|
-
const units = divideCeil(quantityFraction.numerator, unitDenominator);
|
|
651
|
-
return units * price;
|
|
652
|
-
}
|
|
653
|
-
return divideHalfUp(quantityFraction.numerator * price, unitDenominator);
|
|
654
|
-
}
|
|
655
|
-
function priceScaled(value) {
|
|
656
|
-
const [whole = "0", fraction = ""] = value.split(".");
|
|
657
|
-
return BigInt(whole) * MONEY_SCALE2 + BigInt((fraction + "00000000").slice(0, 8));
|
|
658
|
-
}
|
|
659
|
-
function scaledMoney(value) {
|
|
660
|
-
const whole = value / MONEY_SCALE2;
|
|
661
|
-
const fraction = (value % MONEY_SCALE2).toString().padStart(8, "0").replace(/0+$/, "");
|
|
662
|
-
return fraction ? `${whole}.${fraction}` : whole.toString();
|
|
663
|
-
}
|
|
664
|
-
function numberFraction(value) {
|
|
665
|
-
const [mantissa, exponentText] = value.toString().toLowerCase().split("e");
|
|
666
|
-
const exponent = exponentText ? Number(exponentText) : 0;
|
|
667
|
-
const [whole = "0", fraction = ""] = mantissa.split(".");
|
|
668
|
-
const digits = `${whole}${fraction}`.replace(/^\+/, "");
|
|
669
|
-
const decimalPlaces = fraction.length - exponent;
|
|
670
|
-
if (decimalPlaces <= 0) {
|
|
671
|
-
return { numerator: BigInt(digits) * 10n ** BigInt(-decimalPlaces), denominator: 1n };
|
|
672
|
-
}
|
|
673
|
-
return { numerator: BigInt(digits), denominator: 10n ** BigInt(decimalPlaces) };
|
|
674
|
-
}
|
|
675
|
-
function subtractFractions(left, right) {
|
|
676
|
-
return {
|
|
677
|
-
numerator: left.numerator * right.denominator - right.numerator * left.denominator,
|
|
678
|
-
denominator: left.denominator * right.denominator
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
function divideCeil(numerator, denominator) {
|
|
682
|
-
return (numerator + denominator - 1n) / denominator;
|
|
683
|
-
}
|
|
684
|
-
function divideHalfUp(numerator, denominator) {
|
|
685
|
-
return (numerator * 2n + denominator) / (denominator * 2n);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// ../contracts/src/metering.ts
|
|
689
|
-
function validateMeteringContract(value) {
|
|
690
|
-
if (!isObject4(value) || value.schemaVersion !== 1 || !isMeteringKind(value.kind)) {
|
|
691
|
-
throw new Error("Metering contract must use schemaVersion 1 and a supported kind");
|
|
692
|
-
}
|
|
693
|
-
if (!Array.isArray(value.metrics) || value.metrics.length === 0 || value.metrics.length > BILLING_METRICS.length) {
|
|
694
|
-
throw new Error("Metering contract must contain supported metrics");
|
|
695
|
-
}
|
|
696
|
-
const seen = /* @__PURE__ */ new Set();
|
|
697
|
-
for (const entry of value.metrics) {
|
|
698
|
-
if (!isObject4(entry) || !BILLING_METRICS.includes(entry.metric)) {
|
|
699
|
-
throw new Error("Metering contract has an unsupported metric");
|
|
700
|
-
}
|
|
701
|
-
if (typeof entry.requirement !== "string" || !["required", "conditional", "informational"].includes(entry.requirement)) {
|
|
702
|
-
throw new Error("Metering metric has an unsupported requirement");
|
|
703
|
-
}
|
|
704
|
-
if (seen.has(String(entry.metric))) throw new Error("Metering contract duplicates a metric");
|
|
705
|
-
seen.add(String(entry.metric));
|
|
706
|
-
if (entry.requirement === "conditional") {
|
|
707
|
-
if (typeof entry.whenFeature !== "string" || !entry.whenFeature.trim()) {
|
|
708
|
-
throw new Error("Conditional metric requires whenFeature");
|
|
709
|
-
}
|
|
710
|
-
} else if ("whenFeature" in entry) {
|
|
711
|
-
throw new Error("Only conditional metrics may declare whenFeature");
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
function assessUsageEvidence(contract, usage, features = {}) {
|
|
716
|
-
const result = {
|
|
717
|
-
kind: "unknown",
|
|
718
|
-
evidenceStatus: "invalid",
|
|
719
|
-
expectedMetrics: [],
|
|
720
|
-
observedMetrics: [],
|
|
721
|
-
missingMetrics: [],
|
|
722
|
-
issues: []
|
|
723
|
-
};
|
|
724
|
-
if (contract === void 0 || contract === null) {
|
|
725
|
-
result.issues.push("contract_unknown");
|
|
726
|
-
return result;
|
|
727
|
-
}
|
|
728
|
-
try {
|
|
729
|
-
validateMeteringContract(contract);
|
|
730
|
-
} catch (error) {
|
|
731
|
-
result.issues.push(`contract_invalid:${errorMessage(error)}`);
|
|
732
|
-
return result;
|
|
733
|
-
}
|
|
734
|
-
result.kind = contract.kind;
|
|
735
|
-
for (const entry of contract.metrics) {
|
|
736
|
-
if (entry.requirement === "required") result.expectedMetrics.push(entry.metric);
|
|
737
|
-
if (entry.requirement === "conditional") {
|
|
738
|
-
const feature = entry.whenFeature;
|
|
739
|
-
if (!Object.hasOwn(features, feature) || typeof features[feature] !== "boolean") {
|
|
740
|
-
result.issues.push(`condition_unknown:${feature}`);
|
|
741
|
-
} else if (features[feature]) {
|
|
742
|
-
result.expectedMetrics.push(entry.metric);
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
if (result.issues.length > 0) return result;
|
|
747
|
-
let explicitlyIncomplete = false;
|
|
748
|
-
if (usage !== void 0 && usage !== null) {
|
|
749
|
-
try {
|
|
750
|
-
if (!isObject4(usage) || usage.schemaVersion !== 1 || !Array.isArray(usage.components)) {
|
|
751
|
-
throw new Error("Normalized usage must use the metered v1 envelope");
|
|
752
|
-
}
|
|
753
|
-
if (usage.billingEvidenceComplete !== void 0 && typeof usage.billingEvidenceComplete !== "boolean") {
|
|
754
|
-
throw new Error("billingEvidenceComplete must be boolean");
|
|
755
|
-
}
|
|
756
|
-
if (usage.components.length > 0) validateNormalizedUsage(usage);
|
|
757
|
-
const observed = /* @__PURE__ */ new Set();
|
|
758
|
-
for (const component of usage.components) {
|
|
759
|
-
if (!component.metric.endsWith("_seconds") && component.metric !== "provider_credits" && !Number.isSafeInteger(component.quantity)) {
|
|
760
|
-
throw new Error(`Quantity for ${component.metric} must be a safe integer`);
|
|
761
|
-
}
|
|
762
|
-
if (Object.values(component.dimensions ?? {}).some((value) => typeof value === "number" && !Number.isFinite(value))) {
|
|
763
|
-
throw new Error("Usage dimensions must contain finite numeric values");
|
|
764
|
-
}
|
|
765
|
-
observed.add(component.metric);
|
|
766
|
-
}
|
|
767
|
-
result.observedMetrics = [...observed];
|
|
768
|
-
explicitlyIncomplete = usage.billingEvidenceComplete === false;
|
|
769
|
-
} catch (error) {
|
|
770
|
-
result.issues.push(`usage_invalid:${errorMessage(error)}`);
|
|
771
|
-
return result;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
result.missingMetrics = result.expectedMetrics.filter((metric) => !result.observedMetrics.includes(metric));
|
|
775
|
-
if (explicitlyIncomplete) result.issues.push("billing_evidence_incomplete");
|
|
776
|
-
result.evidenceStatus = result.expectedMetrics.length === 0 ? "not_expected" : result.missingMetrics.length === result.expectedMetrics.length ? "missing" : result.missingMetrics.length > 0 || explicitlyIncomplete ? "partial" : "complete";
|
|
777
|
-
return result;
|
|
778
|
-
}
|
|
779
|
-
function isObject4(value) {
|
|
780
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
781
|
-
}
|
|
782
|
-
function isMeteringKind(value) {
|
|
783
|
-
return value === "token" || value === "non_token" || value === "mixed";
|
|
784
|
-
}
|
|
785
|
-
function errorMessage(error) {
|
|
786
|
-
return error instanceof Error ? error.message : String(error);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
// ../contracts/src/tasks.ts
|
|
790
|
-
var TASK_STATUSES = [
|
|
791
|
-
"queued",
|
|
792
|
-
"running",
|
|
793
|
-
"succeeded",
|
|
794
|
-
"failed",
|
|
795
|
-
"cancelling",
|
|
796
|
-
"cancelled"
|
|
797
|
-
];
|
|
798
|
-
function taskReceipt(task) {
|
|
799
|
-
return { id: task.id, status: task.status };
|
|
800
|
-
}
|
|
801
|
-
function taskResult(task) {
|
|
802
|
-
return {
|
|
803
|
-
id: task.id,
|
|
804
|
-
model: task.model,
|
|
805
|
-
status: task.status,
|
|
806
|
-
...task.assets.length ? { assets: task.assets } : {},
|
|
807
|
-
...task.requestId ? { requestId: task.requestId } : {},
|
|
808
|
-
...task.output !== void 0 ? { output: task.output } : {},
|
|
809
|
-
...task.error ? { error: task.error } : {},
|
|
810
|
-
...task.deliveryStatus ? { deliveryStatus: task.deliveryStatus } : {},
|
|
811
|
-
...task.settlement ? { settlement: task.settlement } : {}
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
function isTerminalTaskStatus(status) {
|
|
815
|
-
return status === "succeeded" || status === "failed" || status === "cancelled";
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
// ../contracts/src/monitoring.ts
|
|
819
|
-
function defaultProbeInterval(kind, model) {
|
|
820
|
-
if (/music/i.test(model)) return 172800;
|
|
821
|
-
if (kind === "audio" && /sound/i.test(model)) return 86400;
|
|
822
|
-
return ["language", "embedding", "audio"].includes(kind) ? 3600 : 86400;
|
|
823
|
-
}
|
|
824
|
-
function defaultProbeTimeout(kind) {
|
|
825
|
-
return ["video", "world"].includes(kind) ? 1800 : ["image", "audio"].includes(kind) ? 300 : 120;
|
|
826
|
-
}
|
|
827
|
-
function classifyObservation(status, httpStatus, code = "") {
|
|
828
|
-
if (/cancel|client_disconnect|invalid_input|validation_error/i.test(code) || status === "cancelled")
|
|
829
|
-
return "excluded";
|
|
830
|
-
if (httpStatus === 429) return "rate_limited";
|
|
831
|
-
if (httpStatus === 403 && /geo|region|location|country/i.test(code))
|
|
832
|
-
return "restricted";
|
|
833
|
-
if (/auth|api.?key|balance|credit|quota_exhaust|model.*(not.?found|not.?exist)|invalid_model/i.test(
|
|
834
|
-
code
|
|
835
|
-
))
|
|
836
|
-
return "failure";
|
|
837
|
-
if ([400, 413, 422].includes(httpStatus ?? 0)) return "excluded";
|
|
838
|
-
if (status === "failed" || status === "rejected" || httpStatus !== null && httpStatus >= 400 || /error|incomplete|truncat/i.test(code))
|
|
839
|
-
return "failure";
|
|
840
|
-
return status === "succeeded" ? "success" : "excluded";
|
|
841
|
-
}
|
|
842
|
-
var sampleCount = (items) => items.reduce((n, x) => n + (x.count ?? 1), 0);
|
|
843
|
-
function availabilityStats(items) {
|
|
844
|
-
const success = sampleCount(items.filter((x) => x.result === "success")), failure = sampleCount(items.filter((x) => x.result === "failure"));
|
|
845
|
-
return {
|
|
846
|
-
success,
|
|
847
|
-
failure,
|
|
848
|
-
excluded: sampleCount(items.filter((x) => x.result === "excluded")),
|
|
849
|
-
rateLimited: sampleCount(items.filter((x) => x.result === "rate_limited")),
|
|
850
|
-
restricted: sampleCount(items.filter((x) => x.result === "restricted")),
|
|
851
|
-
rate: success + failure ? success / (success + failure) : null
|
|
852
|
-
};
|
|
853
|
-
}
|
|
854
|
-
function availabilityBuckets(items, end, hours, intervalSeconds, core = false) {
|
|
855
|
-
const endMs = end.getTime(), first = Math.floor(endMs / 36e5) * 36e5 - (hours - 1) * 36e5;
|
|
856
|
-
const sorted = items.filter(
|
|
857
|
-
(x) => Number.isFinite(Date.parse(x.at)) && Date.parse(x.at) <= endMs
|
|
858
|
-
).slice().sort((a, b) => Date.parse(a.at) - Date.parse(b.at));
|
|
859
|
-
const grace = Math.min(3600, Math.max(300, intervalSeconds * 0.1)) * 1e3;
|
|
860
|
-
let cursor = 0;
|
|
861
|
-
let prior;
|
|
862
|
-
return Array.from({ length: hours }, (_, i) => {
|
|
863
|
-
const start = first + i * 36e5, stop = Math.min(start + 36e5, endMs);
|
|
864
|
-
const valid = [];
|
|
865
|
-
while (cursor < sorted.length && (Date.parse(sorted[cursor].at) < stop || i === hours - 1 && Date.parse(sorted[cursor].at) === endMs)) {
|
|
866
|
-
const item = sorted[cursor++];
|
|
867
|
-
if (item.result !== "success" && item.result !== "failure") continue;
|
|
868
|
-
prior = item;
|
|
869
|
-
if (Date.parse(item.at) >= start) valid.push(item);
|
|
870
|
-
}
|
|
871
|
-
const s = sampleCount(valid.filter((x) => x.result === "success")), f = sampleCount(valid) - s;
|
|
872
|
-
const fresh = prior && stop - Date.parse(prior.at) <= intervalSeconds * 1e3 + grace;
|
|
873
|
-
const state = valid.length ? s && f ? "mixed" : s ? "success" : "failure" : !core && fresh ? prior.result : "unknown";
|
|
874
|
-
const expected = core ? Math.ceil((stop - start) / 3e4) : null;
|
|
875
|
-
return {
|
|
876
|
-
start: new Date(start).toISOString(),
|
|
877
|
-
end: new Date(stop).toISOString(),
|
|
878
|
-
state,
|
|
879
|
-
evidence: valid.length ? "measured" : !core && fresh ? "carried" : prior ? "expired" : "none",
|
|
880
|
-
businessSuccess: sampleCount(
|
|
881
|
-
valid.filter((x) => x.source === "business" && x.result === "success")
|
|
882
|
-
),
|
|
883
|
-
businessFailure: sampleCount(
|
|
884
|
-
valid.filter((x) => x.source === "business" && x.result === "failure")
|
|
885
|
-
),
|
|
886
|
-
probeSuccess: sampleCount(
|
|
887
|
-
valid.filter((x) => x.source === "probe" && x.result === "success")
|
|
888
|
-
),
|
|
889
|
-
probeFailure: sampleCount(
|
|
890
|
-
valid.filter((x) => x.source === "probe" && x.result === "failure")
|
|
891
|
-
),
|
|
892
|
-
coreSuccess: sampleCount(
|
|
893
|
-
valid.filter((x) => x.source === "core" && x.result === "success")
|
|
894
|
-
),
|
|
895
|
-
coreFailure: sampleCount(
|
|
896
|
-
valid.filter((x) => x.source === "core" && x.result === "failure")
|
|
897
|
-
),
|
|
898
|
-
latestAt: prior?.at ?? null,
|
|
899
|
-
expectedSamples: expected,
|
|
900
|
-
incomplete: expected !== null && sampleCount(valid) < expected
|
|
901
|
-
};
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
function rateProbeUsage(price, usage) {
|
|
905
|
-
if (!usage || !Array.isArray(usage.components) || !usage.components.length)
|
|
906
|
-
return void 0;
|
|
907
|
-
try {
|
|
908
|
-
validatePriceRule(price);
|
|
909
|
-
const metrics = new Set(
|
|
910
|
-
usage.components.flatMap(
|
|
911
|
-
(c) => c && typeof c === "object" && !Array.isArray(c) && typeof c.metric === "string" ? [c.metric] : []
|
|
912
|
-
)
|
|
913
|
-
);
|
|
914
|
-
if (price.rates.some(
|
|
915
|
-
(r) => !/^cache|cached_|web_search/.test(r.metric) && !metrics.has(r.metric)
|
|
916
|
-
))
|
|
917
|
-
return void 0;
|
|
918
|
-
const e = evaluatePriceRule(price, usage);
|
|
919
|
-
return e.status === "rated" && e.lines.length > 0 ? e.amount : void 0;
|
|
920
|
-
} catch {
|
|
921
|
-
return void 0;
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
function probeCostExceeds(actual, limit) {
|
|
925
|
-
const units = (value) => {
|
|
926
|
-
if (!/^\d+(\.\d{1,8})?$/.test(value)) throw new Error("invalid_probe_cost");
|
|
927
|
-
const [whole, fraction = ""] = value.split(".");
|
|
928
|
-
return BigInt(whole) * 100000000n + BigInt(fraction.padEnd(8, "0"));
|
|
929
|
-
};
|
|
930
|
-
return units(actual) > units(limit);
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
// ../contracts/src/sandbox.ts
|
|
934
|
-
var SANDBOX_OPERATIONS = [
|
|
935
|
-
{ id: "text-to-speech", modality: "audio", label: "\u8BED\u97F3\u5408\u6210" },
|
|
936
|
-
{ id: "text-to-music", modality: "audio", label: "\u97F3\u4E50\u751F\u6210" },
|
|
937
|
-
{ id: "text-to-video", modality: "video", label: "\u53C2\u8003\u751F\u6210\u89C6\u9891" },
|
|
938
|
-
{ id: "image-to-video", modality: "video", label: "\u9996\u5C3E\u5E27\u751F\u6210\u89C6\u9891" },
|
|
939
|
-
{ id: "multimodal-to-video", modality: "video", label: "\u5168\u80FD\u53C2\u8003\u751F" },
|
|
940
|
-
{ id: "text-to-image", modality: "image", label: "\u6587\u751F\u56FE" },
|
|
941
|
-
{ id: "image-to-image", modality: "image", label: "\u56FE\u751F\u56FE" },
|
|
942
|
-
{ id: "text-to-text", modality: "language", label: "\u5BF9\u8BDD\u751F\u6210" }
|
|
943
|
-
];
|
|
944
|
-
var sandboxObject = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
945
|
-
var obj = sandboxObject;
|
|
946
|
-
var array = (v) => Array.isArray(v) ? v : [];
|
|
947
|
-
function sandboxIdentity(slug) {
|
|
948
|
-
return slug === "google/veo-3-1-fast" ? "google/veo-3-1-fast-generate-001" : slug;
|
|
949
|
-
}
|
|
950
|
-
function sandboxModelModes(model) {
|
|
951
|
-
if (model.modality === "language") return ["text-to-text"];
|
|
952
|
-
if (model.modality === "audio") return model.capabilities.inputModes.includes("text-to-speech") ? ["text-to-speech"] : ["elevenlabs/music_v2", "minimax/music-2.6"].includes(model.slug) ? ["text-to-music"] : [];
|
|
953
|
-
if (model.slug === "heygen/heygen-avatar-video" || model.slug === "yadan/minimax-h3-local") return [];
|
|
954
|
-
const modes = model.capabilities.inputModes.filter((mode) => ["text-to-image", "image-to-image", "text-to-video", "image-to-video"].includes(mode)).filter((mode) => !(model.slug === "pixverse/baidu-vod-pc1" && mode === "image-to-video"));
|
|
955
|
-
if (model.capabilities.inputModes.includes("audio-to-video") && model.capabilities.inputModes.includes("video-to-video")) modes.push("multimodal-to-video");
|
|
956
|
-
return modes;
|
|
957
|
-
}
|
|
958
|
-
function sandboxOperations(models) {
|
|
959
|
-
return SANDBOX_OPERATIONS.filter((op) => new Set(models.filter((m) => m.modality === op.modality && sandboxModelModes(m).includes(op.id)).map((m) => sandboxIdentity(m.slug))).size >= 2);
|
|
960
|
-
}
|
|
961
|
-
function sandboxProperties(schema) {
|
|
962
|
-
const root = obj(schema.inputSchema.properties);
|
|
963
|
-
return { root, parameters: obj(obj(root.parameters).properties) };
|
|
964
|
-
}
|
|
965
|
-
function values(field) {
|
|
966
|
-
return Array.isArray(field.enum) ? field.enum : [];
|
|
967
|
-
}
|
|
968
|
-
function normalized(value) {
|
|
969
|
-
return String(value).toLowerCase();
|
|
970
|
-
}
|
|
971
|
-
function choose(field, requested, numeric = false) {
|
|
972
|
-
const options = values(field);
|
|
973
|
-
if (options.length) {
|
|
974
|
-
const exact = options.find((v) => normalized(v) === normalized(requested));
|
|
975
|
-
if (exact !== void 0) return exact;
|
|
976
|
-
if (numeric) {
|
|
977
|
-
const n = parseFloat(String(requested));
|
|
978
|
-
const ranked = options.filter((v) => Number.isFinite(parseFloat(String(v)))).sort((a, b) => Math.abs(parseFloat(String(a)) - n) - Math.abs(parseFloat(String(b)) - n));
|
|
979
|
-
if (ranked.length && Number.isFinite(n)) return ranked[0];
|
|
980
|
-
}
|
|
981
|
-
throw new Error(`\u4E0D\u652F\u6301 ${String(requested)}`);
|
|
982
|
-
}
|
|
983
|
-
const branches = array(field.anyOf).map(obj);
|
|
984
|
-
const range = branches.find((b) => b.type === "integer" || typeof b.minimum === "number") ?? field;
|
|
985
|
-
if (numeric) {
|
|
986
|
-
const n = Number(requested);
|
|
987
|
-
if (!Number.isFinite(n)) throw new Error("\u9700\u8981\u6709\u6548\u6570\u503C");
|
|
988
|
-
const bounded = Math.min(Number(range.maximum ?? n), Math.max(Number(range.minimum ?? n), n));
|
|
989
|
-
return range.type === "integer" || field.type === "integer" ? Math.round(bounded) : bounded;
|
|
990
|
-
}
|
|
991
|
-
return requested;
|
|
992
|
-
}
|
|
993
|
-
function urls(value) {
|
|
994
|
-
return array(value).map((v) => typeof v === "string" ? v : String(obj(v).url ?? "")).filter(Boolean);
|
|
995
|
-
}
|
|
996
|
-
function mediaRoles(schema) {
|
|
997
|
-
const { root } = sandboxProperties(schema);
|
|
998
|
-
if (root.content) return array(obj(obj(root.content).items).oneOf).flatMap((v) => array(obj(obj(obj(v).properties).role).enum)).filter((v) => typeof v === "string");
|
|
999
|
-
return array(obj(obj(root.images)["x-ui"]).roles).filter((v) => typeof v === "string");
|
|
1000
|
-
}
|
|
1001
|
-
function sandboxSupports(schema, feature) {
|
|
1002
|
-
const { root } = sandboxProperties(schema);
|
|
1003
|
-
if (feature === "last_frame") return schema.model !== "yadan/minimax-h3-local" && mediaRoles(schema).includes("last_frame");
|
|
1004
|
-
if (feature === "reference_images") return mediaRoles(schema).includes("reference_image") || schema.model === "pixverse/baidu-vod-pc1";
|
|
1005
|
-
if (feature === "videos") return mediaRoles(schema).includes("reference_video") || array(obj(obj(root.videos)["x-ui"]).roles).includes("reference_video");
|
|
1006
|
-
if (feature === "audios") return mediaRoles(schema).includes("reference_audio") || !!root.audios && array(obj(obj(root.audios)["x-ui"]).accept).includes("url");
|
|
1007
|
-
return false;
|
|
1008
|
-
}
|
|
1009
|
-
function sandboxVoiceKey(model) {
|
|
1010
|
-
return `voice:${model}`;
|
|
1011
|
-
}
|
|
1012
|
-
function adaptSandboxInput(schema, mode, input) {
|
|
1013
|
-
const { root, parameters } = sandboxProperties(schema);
|
|
1014
|
-
const nested = !!root.input && !!root.parameters;
|
|
1015
|
-
const fields = nested ? parameters : root;
|
|
1016
|
-
const body = {};
|
|
1017
|
-
const settings = nested ? {} : body;
|
|
1018
|
-
for (const key of array(schema.inputSchema.required)) if (typeof key === "string" && obj(root[key]).default !== void 0) body[key] = obj(root[key]).default;
|
|
1019
|
-
if (root.model) body.model = String(schema.defaults?.model ?? schema.model.split("/").at(-1));
|
|
1020
|
-
const prompt = typeof input.prompt === "string" ? input.prompt.trim() : "";
|
|
1021
|
-
if (!prompt) throw new Error("\u8BF7\u586B\u5199\u63D0\u793A\u8BCD\u6216\u6717\u8BFB\u6587\u672C");
|
|
1022
|
-
if (nested) body.input = { messages: [{ role: "user", content: [{ text: prompt }, ...urls(input.images).map((image) => ({ image }))] }] };
|
|
1023
|
-
else if (root.content) body.content = [{ type: "text", text: prompt }];
|
|
1024
|
-
else body[root.prompt ? "prompt" : root.texts ? "texts" : "text"] = prompt;
|
|
1025
|
-
if (schema.model === "yadan/minimax-h3-local" && !["first_frame", "reference_images", "videos", "audios"].some((key) => urls(input[key]).length)) throw new Error("H3 Local \u5F53\u524D\u63A5\u5165\u9700\u8981\u81F3\u5C11\u4E00\u4EFD\u53C2\u8003\u7D20\u6750");
|
|
1026
|
-
const imageInput = urls(input.images);
|
|
1027
|
-
const first = urls(input.first_frame);
|
|
1028
|
-
if (mode === "image-to-image" && !imageInput.length) throw new Error("\u56FE\u751F\u56FE\u9700\u8981\u8F93\u5165\u56FE\u7247");
|
|
1029
|
-
if (mode === "image-to-video" && first.length !== 1) throw new Error("\u9996\u5C3E\u5E27\u751F\u6210\u89C6\u9891\u9700\u8981\u4E00\u5F20\u9996\u5E27");
|
|
1030
|
-
if (mode === "text-to-image" && imageInput.length) throw new Error("\u8BF7\u5207\u6362\u5230\u56FE\u751F\u56FE");
|
|
1031
|
-
if (mode === "text-to-video" && first.length) throw new Error("\u8BF7\u5207\u6362\u5230\u9996\u5C3E\u5E27\u751F\u6210\u89C6\u9891");
|
|
1032
|
-
if (!nested && mode === "image-to-image") {
|
|
1033
|
-
if (root.imageUrl) {
|
|
1034
|
-
if (imageInput.length > 1) throw new Error("\u6B64\u6A21\u578B\u53EA\u652F\u6301\u4E00\u5F20\u8F93\u5165\u56FE");
|
|
1035
|
-
body.imageUrl = imageInput[0];
|
|
1036
|
-
} else if (obj(root.image).type === "object") body.images = imageInput.map((url) => ({ url }));
|
|
1037
|
-
else if (root.image) body.image = imageInput;
|
|
1038
|
-
else if (root.images) body.images = imageInput;
|
|
1039
|
-
else throw new Error("\u6A21\u578B\u6CA1\u6709\u53EF\u6620\u5C04\u7684\u56FE\u7247\u8F93\u5165");
|
|
1040
|
-
}
|
|
1041
|
-
const media = [];
|
|
1042
|
-
for (const [key, role, type] of [["first_frame", "first_frame", "image_url"], ["last_frame", "last_frame", "image_url"], ["reference_images", "reference_image", "image_url"], ["videos", "reference_video", "video_url"], ["audios", "reference_audio", "audio_url"]]) {
|
|
1043
|
-
const entries = urls(input[key]);
|
|
1044
|
-
if (key !== "first_frame" && entries.length && !sandboxSupports(schema, key)) throw new Error(`\u6A21\u578B\u4E0D\u652F\u6301 ${key}`);
|
|
1045
|
-
for (const url of entries) media.push({ key, role, type, url });
|
|
1046
|
-
}
|
|
1047
|
-
if (urls(input.last_frame).length && !first.length) throw new Error("\u5C3E\u5E27\u9700\u8981\u540C\u65F6\u63D0\u4F9B\u9996\u5E27");
|
|
1048
|
-
if (first.length && (urls(input.reference_images).length || urls(input.videos).length || urls(input.audios).length)) throw new Error("\u9996\u5C3E\u5E27\u4E0E\u53C2\u8003\u7D20\u6750\u8BF7\u5206\u5F00\u8FD0\u884C");
|
|
1049
|
-
if (root.content) body.content = [...array(body.content), ...media.map((m) => ({ type: m.type, [m.type]: { url: m.url }, role: m.role }))];
|
|
1050
|
-
else for (const [key, type] of [["images", "image_url"], ["videos", "video_url"], ["audios", "audio_url"]]) {
|
|
1051
|
-
const entries = media.filter((m) => m.type === type);
|
|
1052
|
-
if (entries.length) body[key] = entries.map((m, i) => schema.model === "yadan/minimax-h3-local" ? { url: m.url } : schema.model === "pixverse/baidu-vod-pc1" ? { url: m.url, role: "subject", referenceName: `ref${i + 1}` } : { url: m.url, role: m.role });
|
|
1053
|
-
}
|
|
1054
|
-
if (schema.model === "pixverse/baidu-vod-pc1" && urls(input.reference_images).length) {
|
|
1055
|
-
const names = urls(input.reference_images).map((_, i) => `@ref${i + 1}`);
|
|
1056
|
-
if (!names.every((name) => prompt.includes(name))) throw new Error(`\u8BF7\u5728\u63D0\u793A\u8BCD\u4E2D\u5F15\u7528 ${names.join("\u3001")}`);
|
|
1057
|
-
}
|
|
1058
|
-
if (schema.model.startsWith("xai/grok-imagine-video")) body.mode = first.length ? "image-to-video" : urls(input.reference_images).length ? "reference-to-video" : "text-to-video";
|
|
1059
|
-
const ratio = typeof input.aspect_ratio === "string" && input.aspect_ratio !== "default" ? input.aspect_ratio : void 0;
|
|
1060
|
-
const resolution = typeof input.resolution === "string" && input.resolution !== "default" ? input.resolution : void 0;
|
|
1061
|
-
const ratioKey = ["ratio", "aspect_ratio", "aspectRatio"].find((k) => fields[k]);
|
|
1062
|
-
if (ratio && ratioKey && !(["kuaishou/baidu-vod-kling-v3", "pixverse/baidu-vod-p60", "bytedance/doubao-seedance-2-5-260628"].includes(schema.model) && first.length)) settings[ratioKey] = choose(obj(fields[ratioKey]), ratio);
|
|
1063
|
-
if (first.length && schema.model === "bytedance/doubao-seedance-2-5-260628") settings.ratio = "adaptive";
|
|
1064
|
-
if (resolution && fields.resolution) settings.resolution = choose(obj(fields.resolution), resolution);
|
|
1065
|
-
if (fields.size && (ratio || resolution)) {
|
|
1066
|
-
const [rw, rh] = (ratio ?? "1:1").split(":").map(Number);
|
|
1067
|
-
if (!rw || !rh) throw new Error("\u65E0\u6548\u753B\u9762\u6BD4\u4F8B");
|
|
1068
|
-
const edge = resolution ? parseFloat(resolution) * 1024 : 1024;
|
|
1069
|
-
let w = Math.round(edge * Math.sqrt(rw / rh) / 16) * 16;
|
|
1070
|
-
let h = Math.round(edge * Math.sqrt(rh / rw) / 16) * 16;
|
|
1071
|
-
const max = schema.model.startsWith("openai/") ? 3840 : schema.model.includes("qwen-image-edit") || nested && mode === "image-to-image" ? 2048 : 4096;
|
|
1072
|
-
if (Math.max(w, h) > max) {
|
|
1073
|
-
const scale = max / Math.max(w, h);
|
|
1074
|
-
w = Math.floor(w * scale / 16) * 16;
|
|
1075
|
-
h = Math.floor(h * scale / 16) * 16;
|
|
1076
|
-
}
|
|
1077
|
-
settings.size = `${w}${nested ? "*" : "x"}${h}`;
|
|
1078
|
-
}
|
|
1079
|
-
if (input.duration !== void 0 && fields.duration) settings.duration = choose(obj(fields.duration), input.duration, true);
|
|
1080
|
-
if (input.generate_audio !== void 0) {
|
|
1081
|
-
const key = fields.generate_audio ? "generate_audio" : fields.with_audio ? "with_audio" : void 0;
|
|
1082
|
-
if (!key) throw new Error("\u6A21\u578B\u4E0D\u652F\u6301\u63A7\u5236\u751F\u6210\u97F3\u9891");
|
|
1083
|
-
settings[key] = input.generate_audio;
|
|
1084
|
-
}
|
|
1085
|
-
if (mode === "text-to-speech") {
|
|
1086
|
-
const voice = input[sandboxVoiceKey(schema.model)] ?? obj(root.voice_id).default;
|
|
1087
|
-
if (!voice) throw new Error("\u8BF7\u9009\u62E9\u6B64\u6A21\u578B\u7684\u97F3\u8272");
|
|
1088
|
-
body.voice_id = voice;
|
|
1089
|
-
}
|
|
1090
|
-
if (mode === "text-to-music") {
|
|
1091
|
-
body.instrumental = input.instrumental ?? true;
|
|
1092
|
-
if (root.lyrics_optimizer && body.instrumental === false) body.lyrics_optimizer = true;
|
|
1093
|
-
}
|
|
1094
|
-
if (nested) body.parameters = settings;
|
|
1095
|
-
return body;
|
|
1096
|
-
}
|
|
1097
|
-
function sandboxInputSchema(schemas, mode) {
|
|
1098
|
-
const field = (title, type = "string", extra = {}) => ({ type, title, ...extra });
|
|
1099
|
-
const properties = { prompt: field(mode === "text-to-speech" ? "\u6717\u8BFB\u6587\u672C" : "\u63D0\u793A\u8BCD", "string", { minLength: 1, "x-ui": { control: "textarea", order: 0 } }) };
|
|
1100
|
-
const required = ["prompt"];
|
|
1101
|
-
const mediaField = (title, maxItems) => field(title, "array", { items: { type: "object", properties: { url: { type: "string", minLength: 1 }, role: { type: "string" } }, required: ["url"] }, maxItems, "x-ui": { control: "media", accept: ["url"] } });
|
|
1102
|
-
if (mode === "image-to-image") {
|
|
1103
|
-
properties.images = mediaField("\u8F93\u5165\u56FE\u7247", 16);
|
|
1104
|
-
required.push("images");
|
|
1105
|
-
obj(properties.images).minItems = 1;
|
|
1106
|
-
}
|
|
1107
|
-
if (mode === "image-to-video") {
|
|
1108
|
-
properties.first_frame = mediaField("\u9996\u5E27", 1);
|
|
1109
|
-
required.push("first_frame");
|
|
1110
|
-
obj(properties.first_frame).minItems = 1;
|
|
1111
|
-
}
|
|
1112
|
-
if (mode.endsWith("video")) {
|
|
1113
|
-
for (const [key, title, max] of [["last_frame", "\u5C3E\u5E27", 1], ["reference_images", "\u53C2\u8003\u56FE\u7247", 9], ["videos", "\u53C2\u8003\u89C6\u9891", 3], ["audios", "\u53C2\u8003\u97F3\u9891", 3]]) {
|
|
1114
|
-
if (key === "last_frame" && mode !== "image-to-video") continue;
|
|
1115
|
-
if (key !== "last_frame" && mode === "image-to-video") continue;
|
|
1116
|
-
if (new Set(schemas.map((s) => sandboxIdentity(s.model))).size >= 2 && schemas.every((s) => sandboxSupports(s, key))) properties[key] = mediaField(title, max);
|
|
1117
|
-
}
|
|
1118
|
-
properties.duration = field("\u76EE\u6807\u65F6\u957F\uFF08\u79D2\uFF09", "integer", { minimum: 1, maximum: 30 });
|
|
1119
|
-
}
|
|
1120
|
-
if (mode.endsWith("image") || mode.endsWith("video")) {
|
|
1121
|
-
const fields = schemas.map((s) => {
|
|
1122
|
-
const { root, parameters } = sandboxProperties(s);
|
|
1123
|
-
return root.parameters ? parameters : root;
|
|
1124
|
-
});
|
|
1125
|
-
const ratios = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "21:9"].filter((r) => fields.every((p) => {
|
|
1126
|
-
const k = ["ratio", "aspect_ratio", "aspectRatio"].find((k2) => p[k2]);
|
|
1127
|
-
return !k || !values(obj(p[k])).length || values(obj(p[k])).includes(r);
|
|
1128
|
-
}));
|
|
1129
|
-
properties.aspect_ratio = field("\u753B\u9762\u6BD4\u4F8B", "string", { enum: ["default", ...ratios], default: "default", "x-ui": { optionLabels: { default: "\u5404\u6A21\u578B\u9ED8\u8BA4" } } });
|
|
1130
|
-
const resolutions = (mode.endsWith("image") ? ["1K", "2K", "4K"] : ["480p", "540p", "720p", "768p", "1080p", "2K", "4K"]).filter((r) => fields.every((p) => !p.resolution || !values(obj(p.resolution)).length || values(obj(p.resolution)).some((v) => normalized(v) === r.toLowerCase())));
|
|
1131
|
-
properties.resolution = field("\u6E05\u6670\u5EA6", "string", { enum: ["default", ...resolutions], default: "default", "x-ui": { optionLabels: { default: "\u5404\u6A21\u578B\u9ED8\u8BA4" } } });
|
|
1132
|
-
if (schemas.length >= 2 && fields.every((p) => p.generate_audio || p.with_audio)) properties.generate_audio = field("\u751F\u6210\u58F0\u97F3", "boolean");
|
|
1133
|
-
}
|
|
1134
|
-
if (mode === "text-to-speech") for (const s of schemas) {
|
|
1135
|
-
const voice = obj(sandboxProperties(s).root.voice_id);
|
|
1136
|
-
properties[sandboxVoiceKey(s.model)] = { ...voice, title: `${s.model} \xB7 \u97F3\u8272`, "x-ui": { ...obj(voice["x-ui"]), resourceModel: s.model } };
|
|
1137
|
-
required.push(sandboxVoiceKey(s.model));
|
|
1138
|
-
}
|
|
1139
|
-
if (mode === "text-to-text") {
|
|
1140
|
-
properties.system = field("\u7CFB\u7EDF\u63D0\u793A\u8BCD", "string", { "x-ui": { control: "textarea" } });
|
|
1141
|
-
properties.messages = field("\u5BF9\u8BDD\u5386\u53F2\uFF08\u53EF\u9009\uFF09", "array", { items: { type: "object", properties: { role: { type: "string", enum: ["user", "assistant"] }, content: { type: "string" } }, required: ["role", "content"], additionalProperties: false }, "x-ui": { control: "json" } });
|
|
1142
|
-
}
|
|
1143
|
-
if (mode === "text-to-music") properties.instrumental = field("\u7EAF\u97F3\u4E50", "boolean", { default: true });
|
|
1144
|
-
const defaults = {};
|
|
1145
|
-
for (const [key, value] of Object.entries(properties)) if (obj(value).default !== void 0) defaults[key] = obj(value).default;
|
|
1146
|
-
return { model: schemas.map((s) => s.model).join(","), version: 1, dialect: "https://json-schema.org/draft/2020-12/schema", inputSchema: { type: "object", properties, required, additionalProperties: false }, outputSchema: {}, uiSchema: {}, defaults, examples: [] };
|
|
1147
|
-
}
|
|
25
|
+
MEDIA_KINDS,
|
|
26
|
+
TASK_STATUSES,
|
|
27
|
+
createTaskRequest,
|
|
28
|
+
extractUpstreamErrorMessage,
|
|
29
|
+
isTerminalTaskStatus
|
|
30
|
+
} from "./chunk-BS3B6F7W.js";
|
|
1148
31
|
|
|
1149
32
|
// src/assets.ts
|
|
1150
33
|
var AssetsClient = class {
|
|
@@ -1182,6 +65,38 @@ var AssetsClient = class {
|
|
|
1182
65
|
signal
|
|
1183
66
|
});
|
|
1184
67
|
}
|
|
68
|
+
certifyForSeedance(asset, request = {}, signal) {
|
|
69
|
+
const assetId = typeof asset === "string" ? asset : asset.id;
|
|
70
|
+
return this.transport.json(
|
|
71
|
+
`/assets/${encodeURIComponent(assetId)}/certifications/seedance`,
|
|
72
|
+
{
|
|
73
|
+
method: "POST",
|
|
74
|
+
...jsonBody(request),
|
|
75
|
+
signal
|
|
76
|
+
},
|
|
77
|
+
{ retryable: false }
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
getSeedanceCertification(asset, signal) {
|
|
81
|
+
const assetId = typeof asset === "string" ? asset : asset.id;
|
|
82
|
+
return this.transport.json(
|
|
83
|
+
`/assets/${encodeURIComponent(assetId)}/certifications/seedance`,
|
|
84
|
+
{ signal }
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
async waitForSeedanceCertification(asset, options = {}) {
|
|
88
|
+
const pollIntervalMs = positiveInteger(options.pollIntervalMs ?? 2e3, "pollIntervalMs");
|
|
89
|
+
const timeoutMs = positiveInteger(options.timeoutMs ?? 3e5, "timeoutMs");
|
|
90
|
+
const deadline = Date.now() + timeoutMs;
|
|
91
|
+
while (true) {
|
|
92
|
+
const certification = await this.getSeedanceCertification(asset, options.signal);
|
|
93
|
+
if (certification.status !== "processing") return certification;
|
|
94
|
+
if (Date.now() >= deadline) {
|
|
95
|
+
throw new Error("Timed out waiting for Seedance Asset certification");
|
|
96
|
+
}
|
|
97
|
+
await wait(Math.min(pollIntervalMs, Math.max(1, deadline - Date.now())), options.signal);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
1185
100
|
download(asset, signal) {
|
|
1186
101
|
const assetId = typeof asset === "string" ? asset : asset.id;
|
|
1187
102
|
return this.transport.response(`/assets/${encodeURIComponent(assetId)}/content`, {
|
|
@@ -1194,6 +109,20 @@ var AssetsClient = class {
|
|
|
1194
109
|
return response.body;
|
|
1195
110
|
}
|
|
1196
111
|
};
|
|
112
|
+
function positiveInteger(value, name) {
|
|
113
|
+
if (!Number.isSafeInteger(value) || value < 1) throw new TypeError(`${name} must be a positive integer`);
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
async function wait(milliseconds, signal) {
|
|
117
|
+
if (signal?.aborted) throw signal.reason;
|
|
118
|
+
await new Promise((resolve, reject) => {
|
|
119
|
+
const timer = setTimeout(resolve, milliseconds);
|
|
120
|
+
signal?.addEventListener("abort", () => {
|
|
121
|
+
clearTimeout(timer);
|
|
122
|
+
reject(signal.reason);
|
|
123
|
+
}, { once: true });
|
|
124
|
+
});
|
|
125
|
+
}
|
|
1197
126
|
function inferKind(mimeType) {
|
|
1198
127
|
if (mimeType.startsWith("image/")) return "image";
|
|
1199
128
|
if (mimeType.startsWith("video/")) return "video";
|
|
@@ -1207,13 +136,16 @@ function isFile(value) {
|
|
|
1207
136
|
}
|
|
1208
137
|
|
|
1209
138
|
// src/models.ts
|
|
139
|
+
function withModelDisplayName(model) {
|
|
140
|
+
return { ...model, displayName: formatModelDisplayName(model.displayName, model.slug ? { modelId: model.slug } : void 0) };
|
|
141
|
+
}
|
|
1210
142
|
var ModelsClient = class {
|
|
1211
143
|
constructor(transport) {
|
|
1212
144
|
this.transport = transport;
|
|
1213
145
|
}
|
|
1214
146
|
transport;
|
|
1215
|
-
search(params = {}, signal) {
|
|
1216
|
-
|
|
147
|
+
async search(params = {}, signal) {
|
|
148
|
+
const page = await this.transport.json(
|
|
1217
149
|
`/models${queryString({
|
|
1218
150
|
q: params.query,
|
|
1219
151
|
modality: params.modality,
|
|
@@ -1224,11 +156,16 @@ var ModelsClient = class {
|
|
|
1224
156
|
})}`,
|
|
1225
157
|
{ signal }
|
|
1226
158
|
);
|
|
159
|
+
return { ...page, data: page.data.map(withModelDisplayName) };
|
|
1227
160
|
}
|
|
1228
|
-
get(model, signal) {
|
|
1229
|
-
|
|
161
|
+
async get(model, signal) {
|
|
162
|
+
const detail = await this.transport.json(`/models/${encodeURIComponent(model)}`, {
|
|
1230
163
|
signal
|
|
1231
164
|
});
|
|
165
|
+
return {
|
|
166
|
+
...withModelDisplayName(detail),
|
|
167
|
+
...detail.familyVersions ? { familyVersions: detail.familyVersions.map(withModelDisplayName) } : {}
|
|
168
|
+
};
|
|
1232
169
|
}
|
|
1233
170
|
schema(model, signal) {
|
|
1234
171
|
return this.transport.json(
|
|
@@ -1236,15 +173,45 @@ var ModelsClient = class {
|
|
|
1236
173
|
{ signal }
|
|
1237
174
|
);
|
|
1238
175
|
}
|
|
176
|
+
resources(model, resource, signal) {
|
|
177
|
+
return this.transport.json(
|
|
178
|
+
`/models/${encodeURIComponent(model)}/resources/${encodeURIComponent(resource)}`,
|
|
179
|
+
{ signal }
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/voices.ts
|
|
185
|
+
var VoicesClient = class {
|
|
186
|
+
constructor(transport) {
|
|
187
|
+
this.transport = transport;
|
|
188
|
+
}
|
|
189
|
+
transport;
|
|
190
|
+
async list(signal) {
|
|
191
|
+
const response = await this.transport.json("/voices", { signal });
|
|
192
|
+
return response.data;
|
|
193
|
+
}
|
|
194
|
+
get(voiceId, signal) {
|
|
195
|
+
return this.transport.json(`/voices/${encodeURIComponent(voiceId)}`, { signal });
|
|
196
|
+
}
|
|
197
|
+
create(request, options) {
|
|
198
|
+
if (!options.idempotencyKey.trim()) throw new TypeError("Idempotency-Key is required");
|
|
199
|
+
return this.transport.json("/voices", {
|
|
200
|
+
method: "POST",
|
|
201
|
+
...jsonBody(request),
|
|
202
|
+
headers: { "content-type": "application/json", "idempotency-key": options.idempotencyKey },
|
|
203
|
+
signal: options.signal
|
|
204
|
+
}, { retryable: true });
|
|
205
|
+
}
|
|
1239
206
|
};
|
|
1240
207
|
|
|
1241
208
|
// src/input.ts
|
|
1242
209
|
async function normalizeTaskInput(input, assets, signal) {
|
|
1243
|
-
const
|
|
1244
|
-
if (!isPlainObject(
|
|
210
|
+
const normalized = await normalizeValue(input, assets, signal, /* @__PURE__ */ new WeakSet());
|
|
211
|
+
if (!isPlainObject(normalized)) {
|
|
1245
212
|
throw new TypeError("Task input must be a JSON object");
|
|
1246
213
|
}
|
|
1247
|
-
return
|
|
214
|
+
return normalized;
|
|
1248
215
|
}
|
|
1249
216
|
async function normalizeValue(value, assets, signal, ancestors) {
|
|
1250
217
|
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
@@ -1265,21 +232,21 @@ async function normalizeValue(value, assets, signal, ancestors) {
|
|
|
1265
232
|
if (isAssetReference(value)) return { assetId: value.assetId };
|
|
1266
233
|
if (Array.isArray(value)) {
|
|
1267
234
|
assertNotCircular(value, ancestors);
|
|
1268
|
-
const
|
|
235
|
+
const normalized = await Promise.all(
|
|
1269
236
|
value.map((item) => normalizeValue(item, assets, signal, ancestors))
|
|
1270
237
|
);
|
|
1271
238
|
ancestors.delete(value);
|
|
1272
|
-
return
|
|
239
|
+
return normalized;
|
|
1273
240
|
}
|
|
1274
241
|
if (isPlainObject(value)) {
|
|
1275
242
|
assertNotCircular(value, ancestors);
|
|
1276
|
-
const
|
|
243
|
+
const normalized = {};
|
|
1277
244
|
for (const [key, item] of Object.entries(value)) {
|
|
1278
245
|
if (item === void 0) continue;
|
|
1279
|
-
|
|
246
|
+
normalized[key] = await normalizeValue(item, assets, signal, ancestors);
|
|
1280
247
|
}
|
|
1281
248
|
ancestors.delete(value);
|
|
1282
|
-
return
|
|
249
|
+
return normalized;
|
|
1283
250
|
}
|
|
1284
251
|
throw new TypeError(`Task input contains a non-serializable ${typeof value} value`);
|
|
1285
252
|
}
|
|
@@ -1299,55 +266,6 @@ function isMediaAsset(value) {
|
|
|
1299
266
|
return isPlainObject(value) && typeof value.id === "string" && typeof value.kind === "string" && typeof value.status === "string" && typeof value.source === "string";
|
|
1300
267
|
}
|
|
1301
268
|
|
|
1302
|
-
// src/sse.ts
|
|
1303
|
-
async function* parseServerSentEvents(response) {
|
|
1304
|
-
if (!response.body) throw new Error("SSE response did not contain a body");
|
|
1305
|
-
const reader = response.body.getReader();
|
|
1306
|
-
const decoder = new TextDecoder();
|
|
1307
|
-
let buffer = "";
|
|
1308
|
-
let current = emptyEvent();
|
|
1309
|
-
while (true) {
|
|
1310
|
-
const { value, done } = await reader.read();
|
|
1311
|
-
buffer += decoder.decode(value, { stream: !done });
|
|
1312
|
-
const lines = buffer.split(/\r?\n/);
|
|
1313
|
-
buffer = done ? "" : lines.pop() ?? "";
|
|
1314
|
-
for (const line of lines) {
|
|
1315
|
-
if (line === "") {
|
|
1316
|
-
const event = buildEvent(current);
|
|
1317
|
-
current = emptyEvent();
|
|
1318
|
-
if (event) yield event;
|
|
1319
|
-
continue;
|
|
1320
|
-
}
|
|
1321
|
-
if (line.startsWith(":")) continue;
|
|
1322
|
-
const separator = line.indexOf(":");
|
|
1323
|
-
const field = separator === -1 ? line : line.slice(0, separator);
|
|
1324
|
-
let content = separator === -1 ? "" : line.slice(separator + 1);
|
|
1325
|
-
if (content.startsWith(" ")) content = content.slice(1);
|
|
1326
|
-
if (field === "data") current.data.push(content);
|
|
1327
|
-
else if (field === "event") current.event = content;
|
|
1328
|
-
else if (field === "id") current.id = content;
|
|
1329
|
-
else if (field === "retry") current.retry = Number(content);
|
|
1330
|
-
}
|
|
1331
|
-
if (done) {
|
|
1332
|
-
const event = buildEvent(current);
|
|
1333
|
-
if (event) yield event;
|
|
1334
|
-
return;
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
function emptyEvent() {
|
|
1339
|
-
return { data: [] };
|
|
1340
|
-
}
|
|
1341
|
-
function buildEvent(pending) {
|
|
1342
|
-
if (pending.data.length === 0) return void 0;
|
|
1343
|
-
return {
|
|
1344
|
-
id: pending.id,
|
|
1345
|
-
event: pending.event,
|
|
1346
|
-
retry: Number.isFinite(pending.retry) ? pending.retry : void 0,
|
|
1347
|
-
data: pending.data.join("\n")
|
|
1348
|
-
};
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
269
|
// src/tasks.ts
|
|
1352
270
|
var TasksClient = class {
|
|
1353
271
|
constructor(transport, assets) {
|
|
@@ -1356,20 +274,18 @@ var TasksClient = class {
|
|
|
1356
274
|
}
|
|
1357
275
|
transport;
|
|
1358
276
|
assets;
|
|
1359
|
-
async create(model, options) {
|
|
277
|
+
async create(model, input, options = {}) {
|
|
1360
278
|
const schema = getModelSpec(model)?.operations[0]?.inputSchema;
|
|
1361
|
-
const mapped = schema &&
|
|
1362
|
-
const
|
|
1363
|
-
const body = {
|
|
1364
|
-
model,
|
|
1365
|
-
input,
|
|
279
|
+
const mapped = schema && input && typeof input === "object" && !Array.isArray(input) ? applyNoWatermark(schema, applyMediaGeometry(schema, input)) : input;
|
|
280
|
+
const normalizedInput = await normalizeTaskInput(mapped, this.assets, options.signal);
|
|
281
|
+
const body = createTaskRequest(model, normalizedInput, {
|
|
1366
282
|
quoteToken: options.quoteToken,
|
|
1367
283
|
submittedAt: options.submittedAt,
|
|
1368
284
|
metadata: options.metadata,
|
|
1369
285
|
callback_url: options.callback_url,
|
|
1370
286
|
webhook: options.webhook,
|
|
1371
287
|
schemaVersion: options.schemaVersion
|
|
1372
|
-
};
|
|
288
|
+
});
|
|
1373
289
|
const idempotencyKey = options.idempotencyKey ?? crypto.randomUUID();
|
|
1374
290
|
return this.transport.json(
|
|
1375
291
|
"/tasks",
|
|
@@ -1428,7 +344,9 @@ var TasksClient = class {
|
|
|
1428
344
|
const event = JSON.parse(serverEvent.data);
|
|
1429
345
|
yield event;
|
|
1430
346
|
if (event.status && isTerminalTaskStatus(event.status)) {
|
|
1431
|
-
|
|
347
|
+
const task = event.payload?.task;
|
|
348
|
+
const deliveryPreparing = task && typeof task === "object" && !Array.isArray(task) && task.deliveryStatus === "preparing";
|
|
349
|
+
if (!deliveryPreparing) reachedTerminalState = true;
|
|
1432
350
|
}
|
|
1433
351
|
}
|
|
1434
352
|
if (reachedTerminalState) return;
|
|
@@ -1473,8 +391,8 @@ var TasksClient = class {
|
|
|
1473
391
|
if (timeout) clearTimeout(timeout);
|
|
1474
392
|
}
|
|
1475
393
|
}
|
|
1476
|
-
async run(model, options) {
|
|
1477
|
-
const task = await this.create(model, options);
|
|
394
|
+
async run(model, input, options = {}) {
|
|
395
|
+
const task = await this.create(model, input, options);
|
|
1478
396
|
let completed;
|
|
1479
397
|
try {
|
|
1480
398
|
completed = await this.wait(task.id, options);
|
|
@@ -1508,15 +426,17 @@ var NexraClient = class {
|
|
|
1508
426
|
catalog;
|
|
1509
427
|
models;
|
|
1510
428
|
tasks;
|
|
429
|
+
voices;
|
|
1511
430
|
constructor(options = {}) {
|
|
1512
431
|
const transport = new HttpTransport(options);
|
|
1513
432
|
this.assets = new AssetsClient(transport);
|
|
1514
433
|
this.catalog = new CatalogClient(transport);
|
|
1515
434
|
this.models = new ModelsClient(transport);
|
|
1516
435
|
this.tasks = new TasksClient(transport, this.assets);
|
|
436
|
+
this.voices = new VoicesClient(transport);
|
|
1517
437
|
}
|
|
1518
|
-
run(model, options) {
|
|
1519
|
-
return this.tasks.run(model, options);
|
|
438
|
+
run(model, input, options = {}) {
|
|
439
|
+
return this.tasks.run(model, input, options);
|
|
1520
440
|
}
|
|
1521
441
|
};
|
|
1522
442
|
function createNexra(options = {}) {
|
|
@@ -1565,100 +485,28 @@ function timingSafeEqual(left, right) {
|
|
|
1565
485
|
return difference === 0;
|
|
1566
486
|
}
|
|
1567
487
|
export {
|
|
1568
|
-
ADMIN_USER_ACTIVITY_KINDS,
|
|
1569
|
-
ADMIN_USER_ROLES,
|
|
1570
|
-
ADMIN_USER_STATUSES,
|
|
1571
488
|
AssetsClient,
|
|
1572
|
-
BILLING_CURRENCY,
|
|
1573
|
-
BILLING_EXPORT_DIMENSIONS,
|
|
1574
|
-
BILLING_METRICS,
|
|
1575
489
|
CatalogClient,
|
|
1576
|
-
HttpTransport,
|
|
1577
490
|
MEDIA_KINDS,
|
|
1578
|
-
MONEY_STRING_PATTERN,
|
|
1579
491
|
ModelsClient,
|
|
1580
492
|
NexraApiError,
|
|
1581
493
|
NexraClient,
|
|
1582
494
|
NexraTaskError,
|
|
1583
495
|
NexraWaitTimeoutError,
|
|
1584
|
-
SANDBOX_OPERATIONS,
|
|
1585
496
|
TASK_STATUSES,
|
|
1586
|
-
TOKEN_METRICS,
|
|
1587
497
|
TasksClient,
|
|
1588
|
-
|
|
1589
|
-
USAGE_ANOMALY_CATEGORIES,
|
|
1590
|
-
USAGE_FILTER_KINDS,
|
|
1591
|
-
USAGE_FILTER_SELECTION_LIMIT,
|
|
1592
|
-
USAGE_GROUP_BYS,
|
|
1593
|
-
USAGE_NORMALIZATION_STATUSES,
|
|
1594
|
-
USAGE_RATING_STATUSES,
|
|
1595
|
-
USAGE_SOURCES,
|
|
1596
|
-
WALLET_MAX_BALANCE_UNITS,
|
|
1597
|
-
adaptSandboxInput,
|
|
1598
|
-
allocateDiscountedPriceLines,
|
|
498
|
+
VoicesClient,
|
|
1599
499
|
applyMediaGeometry,
|
|
1600
|
-
assessUsageEvidence,
|
|
1601
|
-
availabilityBuckets,
|
|
1602
|
-
availabilityStats,
|
|
1603
500
|
ceilDecimal,
|
|
1604
|
-
classifyObservation,
|
|
1605
501
|
createNexra,
|
|
1606
502
|
decodeMediaGeometry,
|
|
1607
|
-
defaultProbeInterval,
|
|
1608
|
-
defaultProbeTimeout,
|
|
1609
|
-
encodeAdminUserCursor,
|
|
1610
503
|
encodeMediaGeometry,
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
encodeUsageSummaryCursor,
|
|
1614
|
-
evaluatePriceRule,
|
|
504
|
+
extractUpstreamErrorMessage,
|
|
505
|
+
formatModelDisplayName,
|
|
1615
506
|
getModelSpec,
|
|
1616
|
-
isMoneyString,
|
|
1617
507
|
isTerminalTaskStatus,
|
|
1618
|
-
jsonBody,
|
|
1619
508
|
listModelSpecs,
|
|
1620
|
-
modelInputDefaults,
|
|
1621
|
-
modelInputUi,
|
|
1622
|
-
modelUploadRegion,
|
|
1623
|
-
moneyFromUnits,
|
|
1624
|
-
moneyToUnits,
|
|
1625
509
|
multiplyDecimal,
|
|
1626
|
-
multiplyMoney,
|
|
1627
|
-
normalizeModelParameterSchema,
|
|
1628
|
-
orderedSchemaProperties,
|
|
1629
|
-
parseAdminUserCursor,
|
|
1630
|
-
parseUsageFilterCursor,
|
|
1631
|
-
parseUsageKeysetCursor,
|
|
1632
|
-
parseUsageSummaryCursor,
|
|
1633
|
-
probeCostExceeds,
|
|
1634
510
|
projectMediaGeometry,
|
|
1635
|
-
queryString,
|
|
1636
|
-
rateProbeUsage,
|
|
1637
|
-
sandboxIdentity,
|
|
1638
|
-
sandboxInputSchema,
|
|
1639
|
-
sandboxModelModes,
|
|
1640
|
-
sandboxObject,
|
|
1641
|
-
sandboxOperations,
|
|
1642
|
-
sandboxProperties,
|
|
1643
|
-
sandboxSupports,
|
|
1644
|
-
sandboxVoiceKey,
|
|
1645
|
-
sleep,
|
|
1646
|
-
taskReceipt,
|
|
1647
|
-
taskResult,
|
|
1648
|
-
uploadedMediaInput,
|
|
1649
|
-
uploadedMediaUrl,
|
|
1650
|
-
validateAdminUserApiKeyUpdate,
|
|
1651
|
-
validateAdminUserListQuery,
|
|
1652
|
-
validateAdminUserUpdate,
|
|
1653
|
-
validateAdminWalletRechargeInput,
|
|
1654
|
-
validateMeteringContract,
|
|
1655
|
-
validateNormalizedUsage,
|
|
1656
|
-
validatePriceRule,
|
|
1657
|
-
validateUsageAnomaliesQuery,
|
|
1658
|
-
validateUsageAnomalySummaryQuery,
|
|
1659
|
-
validateUsageDetailsQuery,
|
|
1660
|
-
validateUsageFilterOptionsQuery,
|
|
1661
|
-
validateUsageFilters,
|
|
1662
|
-
validateUsageSummaryQuery,
|
|
1663
511
|
verifyWebhook
|
|
1664
512
|
};
|