@picsart/ai-sdk 5.24.1 → 5.25.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 +54 -1
- package/index.d.ts +58 -1
- package/index.js +167 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -188,9 +188,61 @@ for await (const update of ai.subscribe(handle)) {
|
|
|
188
188
|
const status = await ai.status(handle)
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
+
## Error Handling
|
|
192
|
+
|
|
193
|
+
Every failure thrown by `generate()`, `generateText()`, `submit()`, and `result()`
|
|
194
|
+
is an `ApiError` with the same four fields, so you can branch on the error
|
|
195
|
+
instead of pattern-matching its message:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { createClient, Models, ApiError } from '@picsart/ai-sdk'
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const result = await ai.generate(Models.Flux2Pro, { prompt: 'a cat on mars' })
|
|
202
|
+
} catch (err) {
|
|
203
|
+
if (err instanceof ApiError) {
|
|
204
|
+
err.status // 402 — HTTP status, or its synthesized equivalent
|
|
205
|
+
err.code // 'payment_required' — platform `reason`, else an SDK code
|
|
206
|
+
err.reason // same value as `code`, named after the platform's own field
|
|
207
|
+
err.message // 'Submit failed (402): Not enough credits'
|
|
208
|
+
|
|
209
|
+
if (err.status === 402) return topUpCredits()
|
|
210
|
+
if (err.status === 429 || err.status >= 500) return retry()
|
|
211
|
+
if (err.code === 'validation_error') return showFormError(err.message)
|
|
212
|
+
}
|
|
213
|
+
throw err
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`code` carries the platform's `reason` verbatim whenever the API supplies one
|
|
218
|
+
(`content_moderation`, `unauthorized`, …). When it doesn't, the SDK fills in a
|
|
219
|
+
conventional slug for the status — `payment_required` for 402, `rate_limited`
|
|
220
|
+
for 429, and so on.
|
|
221
|
+
|
|
222
|
+
Failures that never reach the network get the status they semantically deserve,
|
|
223
|
+
so one retry predicate covers every case:
|
|
224
|
+
|
|
225
|
+
| Failure | `status` | `code` |
|
|
226
|
+
|---------|----------|--------|
|
|
227
|
+
| Unknown model id | 400 | `unknown_model` |
|
|
228
|
+
| `generate()` on a text model (or the reverse) | 400 | `wrong_model_mode` |
|
|
229
|
+
| Parameter validation | 400 | `validation_error` |
|
|
230
|
+
| Async lifecycle on an execute-only transport | 400 | `unsupported_transport` |
|
|
231
|
+
| HTTP error from the API | the response's status | platform `reason`, else the status slug |
|
|
232
|
+
| Poll deadline exceeded | 408 | `timeout` |
|
|
233
|
+
| Aborted via `options.signal`, or a canceled job | 499 | `aborted` / `canceled` |
|
|
234
|
+
| Job finished `FAILED` | the task's `statusCode`, else 502 | platform `reason`, else `generation_failed` |
|
|
235
|
+
| Response the SDK can't parse | 502 | `invalid_response` |
|
|
236
|
+
|
|
237
|
+
Aborts raised by `fetch` itself are deliberately **not** wrapped, so
|
|
238
|
+
`err.name === 'AbortError'` keeps working on the `DOMException`.
|
|
239
|
+
|
|
240
|
+
`message` is human-readable and may change between versions — branch on `status`
|
|
241
|
+
and `code`, not on the message text.
|
|
242
|
+
|
|
191
243
|
## Public API
|
|
192
244
|
|
|
193
|
-
The SDK exports
|
|
245
|
+
The SDK exports 8 symbols:
|
|
194
246
|
|
|
195
247
|
| Export | Type | Description |
|
|
196
248
|
|--------|------|-------------|
|
|
@@ -201,6 +253,7 @@ The SDK exports 7 symbols:
|
|
|
201
253
|
| `AuthenticatedFetch` | type | `(url, init?) => Promise<Response>` — for the custom-`fetch` path |
|
|
202
254
|
| `SdkTransport` | type | Advanced: custom transport interface |
|
|
203
255
|
| `WorkflowJobHandle` | type | Job handle for submit/status/cancel |
|
|
256
|
+
| `ApiError` | class | Unified error: `{ status, code, reason, message }` — see [Error Handling](#error-handling) |
|
|
204
257
|
|
|
205
258
|
## Package Structure
|
|
206
259
|
|
package/index.d.ts
CHANGED
|
@@ -49,6 +49,10 @@ interface WorkflowStatusResult<TResult = unknown> {
|
|
|
49
49
|
status: WorkflowStatus;
|
|
50
50
|
result?: TResult;
|
|
51
51
|
error?: string;
|
|
52
|
+
/** Platform error `reason` on a failed task, when the response carried one. */
|
|
53
|
+
reason?: string;
|
|
54
|
+
/** Numeric status on the error payload, when the response carried one. */
|
|
55
|
+
statusCode?: number;
|
|
52
56
|
progress?: WorkflowProgress;
|
|
53
57
|
/** Credit usage reported by the platform, when present on the response. */
|
|
54
58
|
usage?: CreditUsage;
|
|
@@ -2774,6 +2778,59 @@ declare function getVoiceById(id: string): VoiceOption | undefined;
|
|
|
2774
2778
|
/** @deprecated Load the model's catalog instead (`ai.catalogs.voices(modelId)`) — loaded voices are searched automatically. */
|
|
2775
2779
|
declare function getVoiceById(id: string, extra: VoiceOption[] | undefined): VoiceOption | undefined;
|
|
2776
2780
|
|
|
2781
|
+
/**
|
|
2782
|
+
* Failure codes the SDK synthesizes when the platform supplies no `reason`.
|
|
2783
|
+
* An API-supplied `reason` passes through unchanged, so the open `string`
|
|
2784
|
+
* member keeps arbitrary platform reasons assignable while preserving
|
|
2785
|
+
* autocomplete on the known set.
|
|
2786
|
+
*/
|
|
2787
|
+
type ApiErrorCode = 'unknown_model' | 'wrong_model_mode' | 'validation_error' | 'unsupported_transport' | 'timeout' | 'aborted' | 'canceled' | 'generation_failed' | 'invalid_response' | 'bad_request' | 'unauthorized' | 'payment_required' | 'forbidden' | 'not_found' | 'rate_limited' | 'server_error' | (string & {});
|
|
2788
|
+
/** Everything but the message needed to build a {@link ApiError}. */
|
|
2789
|
+
interface ApiErrorInit {
|
|
2790
|
+
/**
|
|
2791
|
+
* HTTP status of the failing response. When no HTTP exchange took place the
|
|
2792
|
+
* SDK synthesizes the semantically matching code: 400 for input the SDK
|
|
2793
|
+
* itself rejects, 408 for a poll deadline, 499 for an abort or cancel, 502
|
|
2794
|
+
* for a response it cannot make sense of.
|
|
2795
|
+
*/
|
|
2796
|
+
status: number;
|
|
2797
|
+
/** Platform `reason` when present, otherwise an SDK-synthesized code. */
|
|
2798
|
+
code: ApiErrorCode;
|
|
2799
|
+
}
|
|
2800
|
+
/**
|
|
2801
|
+
* The single error type thrown by the SDK's generation surface —
|
|
2802
|
+
* `generate()`, `generateText()`, `submit()`, and `result()`.
|
|
2803
|
+
*
|
|
2804
|
+
* Unrelated to the `Api*` types (`ApiResponse`, `ApiRunOptions`, …), which
|
|
2805
|
+
* describe the low-level `ai.apis` surface. `ai.apis.run()` throws the
|
|
2806
|
+
* workflows client's own errors, not this.
|
|
2807
|
+
*
|
|
2808
|
+
* ```ts
|
|
2809
|
+
* try {
|
|
2810
|
+
* await ai.generate(Models.Flux2Pro, { prompt: 'a cat' });
|
|
2811
|
+
* } catch (err) {
|
|
2812
|
+
* if (err instanceof ApiError) {
|
|
2813
|
+
* if (err.status === 402) return topUpCredits();
|
|
2814
|
+
* if (err.status === 429 || err.status >= 500) return retry();
|
|
2815
|
+
* if (err.code === 'validation_error') return showFormError(err.message);
|
|
2816
|
+
* }
|
|
2817
|
+
* throw err;
|
|
2818
|
+
* }
|
|
2819
|
+
* ```
|
|
2820
|
+
*
|
|
2821
|
+
* Aborts raised by `fetch` itself are never wrapped — a caller checking
|
|
2822
|
+
* `err.name === 'AbortError'` on a `DOMException` keeps working.
|
|
2823
|
+
*/
|
|
2824
|
+
declare class ApiError extends Error {
|
|
2825
|
+
/** HTTP status, or the synthesized equivalent for non-HTTP failures. */
|
|
2826
|
+
readonly status: number;
|
|
2827
|
+
/** Platform `reason`, or an SDK-synthesized code. Always equal to {@link reason}. */
|
|
2828
|
+
readonly code: ApiErrorCode;
|
|
2829
|
+
/** Alias of {@link code}, named after the platform's own error field. */
|
|
2830
|
+
readonly reason: ApiErrorCode;
|
|
2831
|
+
constructor(message: string, init: ApiErrorInit);
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2777
2834
|
/**
|
|
2778
2835
|
* Pricing internals — owns the ModelPricingClient, the per-model cache, and
|
|
2779
2836
|
* the credit-range lookup. The Model accessor delegates to the helpers below
|
|
@@ -2902,4 +2959,4 @@ declare const findModel: (ref: string) => ModelDefinition | undefined;
|
|
|
2902
2959
|
*/
|
|
2903
2960
|
declare const KLING_DUAL_IMAGE_EFFECTS: ReadonlySet<string>;
|
|
2904
2961
|
|
|
2905
|
-
export { ALL_MODELS, type AiClient, type ApiResponse, type ApiRunOptions, type ApiSchemas, type ApisClient, type AppIdentity, type AppType, type AuthenticatedFetch, type AvatarOption, type BooleanDescriptor, type BooleanEntry, type CatalogDescriptor, type CatalogEntry, type CatalogItem, type CatalogKind, type CatalogPage, type CatalogPageOptions, type CatalogPreview, type CatalogQuery, type CatalogResult, type CatalogSource, type CatalogsClient, type CatalogsOptions, type ClientConfig, type CreditRange, type CreditRangeContext, type CreditTier, type CreditUsage, DEFAULT_VISIBLE_RELEASES, type DeepLinkResult, type DriveAttributes, type DriveClient, type DriveConfig, type DriveFile, type DriveFileDetails, type DriveFolder, type DriveMediaItem, type DriveSaveResult, type EntryMeta, type EnumDescriptor, type EnumEntry, type EnumOption, type FileDescriptor, type FileEntry, type FlatParamEntry, type GenerateOptions, type GenerateResult, type GenerateResultItem, type GenerateTextResult, type GenerationContext, type GenerationFile, type GenerationMode, KLING_DUAL_IMAGE_EFFECTS, type ListOptions, type MediaModelId, type MediaTypeFilter, Model, type ModelDefinition, type ModelDescriptor, type ModelFilter$1 as ModelFilter, type ModelInput, type ModelInputById, type ModelMeta, type ModelParams, type ModelParamsAccessor, Models, type ObjectDescriptor, type ObjectEntry, type ParamDescriptor, type ParamEntry, type ParamOption, type PayloadDriveFolderOptions, type PayloadDriveOptions, type PricingOptions, type ProviderInfo, type RangeDescriptor, type RangeEntry, type ReleaseTag, type SaveParams, type SdkPayload, type SdkTransport, type TextDescriptor, type TextEntry, type TextModelId, type TextModelInputById, type ToolUsage, type TypedModelId, type UserReaction, type ValidationResult$1 as ValidationResult, type VoiceOption, type WorkflowJobHandle, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, toAvatarOption, toVoiceOption };
|
|
2962
|
+
export { ALL_MODELS, type AiClient, ApiError, type ApiErrorCode, type ApiErrorInit, type ApiResponse, type ApiRunOptions, type ApiSchemas, type ApisClient, type AppIdentity, type AppType, type AuthenticatedFetch, type AvatarOption, type BooleanDescriptor, type BooleanEntry, type CatalogDescriptor, type CatalogEntry, type CatalogItem, type CatalogKind, type CatalogPage, type CatalogPageOptions, type CatalogPreview, type CatalogQuery, type CatalogResult, type CatalogSource, type CatalogsClient, type CatalogsOptions, type ClientConfig, type CreditRange, type CreditRangeContext, type CreditTier, type CreditUsage, DEFAULT_VISIBLE_RELEASES, type DeepLinkResult, type DriveAttributes, type DriveClient, type DriveConfig, type DriveFile, type DriveFileDetails, type DriveFolder, type DriveMediaItem, type DriveSaveResult, type EntryMeta, type EnumDescriptor, type EnumEntry, type EnumOption, type FileDescriptor, type FileEntry, type FlatParamEntry, type GenerateOptions, type GenerateResult, type GenerateResultItem, type GenerateTextResult, type GenerationContext, type GenerationFile, type GenerationMode, KLING_DUAL_IMAGE_EFFECTS, type ListOptions, type MediaModelId, type MediaTypeFilter, Model, type ModelDefinition, type ModelDescriptor, type ModelFilter$1 as ModelFilter, type ModelInput, type ModelInputById, type ModelMeta, type ModelParams, type ModelParamsAccessor, Models, type ObjectDescriptor, type ObjectEntry, type ParamDescriptor, type ParamEntry, type ParamOption, type PayloadDriveFolderOptions, type PayloadDriveOptions, type PricingOptions, type ProviderInfo, type RangeDescriptor, type RangeEntry, type ReleaseTag, type SaveParams, type SdkPayload, type SdkTransport, type TextDescriptor, type TextEntry, type TextModelId, type TextModelInputById, type ToolUsage, type TypedModelId, type UserReaction, type ValidationResult$1 as ValidationResult, type VoiceOption, type WorkflowJobHandle, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, toAvatarOption, toVoiceOption };
|
package/index.js
CHANGED
|
@@ -441,6 +441,62 @@ var require_build = __commonJS({
|
|
|
441
441
|
}
|
|
442
442
|
});
|
|
443
443
|
|
|
444
|
+
// src/core/errors.ts
|
|
445
|
+
var ApiError = class extends Error {
|
|
446
|
+
/** HTTP status, or the synthesized equivalent for non-HTTP failures. */
|
|
447
|
+
status;
|
|
448
|
+
/** Platform `reason`, or an SDK-synthesized code. Always equal to {@link reason}. */
|
|
449
|
+
code;
|
|
450
|
+
/** Alias of {@link code}, named after the platform's own error field. */
|
|
451
|
+
reason;
|
|
452
|
+
constructor(message, init) {
|
|
453
|
+
super(message);
|
|
454
|
+
this.name = "ApiError";
|
|
455
|
+
this.status = init.status;
|
|
456
|
+
this.code = init.code;
|
|
457
|
+
this.reason = init.code;
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
var CODE_BY_STATUS = {
|
|
461
|
+
400: "bad_request",
|
|
462
|
+
401: "unauthorized",
|
|
463
|
+
402: "payment_required",
|
|
464
|
+
403: "forbidden",
|
|
465
|
+
404: "not_found",
|
|
466
|
+
408: "timeout",
|
|
467
|
+
409: "conflict",
|
|
468
|
+
413: "payload_too_large",
|
|
469
|
+
422: "unprocessable_entity",
|
|
470
|
+
429: "rate_limited",
|
|
471
|
+
500: "server_error",
|
|
472
|
+
502: "bad_gateway",
|
|
473
|
+
503: "service_unavailable",
|
|
474
|
+
504: "gateway_timeout"
|
|
475
|
+
};
|
|
476
|
+
function codeForStatus(status) {
|
|
477
|
+
return CODE_BY_STATUS[status] ?? (status >= 500 ? "server_error" : `http_${status}`);
|
|
478
|
+
}
|
|
479
|
+
async function readErrorBody(res) {
|
|
480
|
+
let text = "";
|
|
481
|
+
try {
|
|
482
|
+
text = await res.text();
|
|
483
|
+
} catch {
|
|
484
|
+
return { text: "" };
|
|
485
|
+
}
|
|
486
|
+
try {
|
|
487
|
+
const parsed = JSON.parse(text);
|
|
488
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
489
|
+
return { text, json: parsed };
|
|
490
|
+
}
|
|
491
|
+
} catch {
|
|
492
|
+
}
|
|
493
|
+
return { text };
|
|
494
|
+
}
|
|
495
|
+
function reasonFrom(json, status) {
|
|
496
|
+
const raw = json?.reason ?? json?.code;
|
|
497
|
+
return typeof raw === "string" && raw.length > 0 ? raw : codeForStatus(status);
|
|
498
|
+
}
|
|
499
|
+
|
|
444
500
|
// src/core/workflow.ts
|
|
445
501
|
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
446
502
|
var DEFAULT_MAX_ATTEMPTS = 300;
|
|
@@ -476,7 +532,9 @@ function parseWorkflowStatus(handle, raw) {
|
|
|
476
532
|
const result = pickFirst(raw, [["response", "result"], ["result"]]);
|
|
477
533
|
const usageRaw = pickFirst(raw, [["response", "usage"], ["usage"]]);
|
|
478
534
|
const usage = usageRaw && typeof usageRaw === "object" && (typeof usageRaw.credits === "number" || Array.isArray(usageRaw.details)) ? usageRaw : void 0;
|
|
479
|
-
const errorRaw = pickFirst(raw, [["response", "error"], ["error"], ["message"], ["reason"]]);
|
|
535
|
+
const errorRaw = pickFirst(raw, [["response", "error"], ["response", "message"], ["error"], ["message"], ["reason"]]);
|
|
536
|
+
const reasonRaw = pickFirst(raw, [["response", "reason"], ["reason"]]);
|
|
537
|
+
const statusCodeRaw = pickFirst(raw, [["response", "statusCode"], ["statusCode"]]);
|
|
480
538
|
const progressRaw = pickFirst(raw, [["response", "progress"], ["progress"]]);
|
|
481
539
|
const progress = progressRaw && typeof progressRaw === "object" ? {
|
|
482
540
|
percent: typeof progressRaw.percent === "number" ? progressRaw.percent : void 0,
|
|
@@ -487,6 +545,8 @@ function parseWorkflowStatus(handle, raw) {
|
|
|
487
545
|
status,
|
|
488
546
|
result,
|
|
489
547
|
error: typeof errorRaw === "string" ? errorRaw : void 0,
|
|
548
|
+
reason: typeof reasonRaw === "string" ? reasonRaw : void 0,
|
|
549
|
+
statusCode: typeof statusCodeRaw === "number" ? statusCodeRaw : void 0,
|
|
490
550
|
progress,
|
|
491
551
|
usage,
|
|
492
552
|
raw
|
|
@@ -502,13 +562,19 @@ function createWorkflowClient(transport, options = {}) {
|
|
|
502
562
|
const defaultMaxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
503
563
|
const submit = async (request) => {
|
|
504
564
|
if (!transport.submit) {
|
|
505
|
-
throw new
|
|
565
|
+
throw new ApiError("Transport does not support submit (execute-only transport)", {
|
|
566
|
+
status: 400,
|
|
567
|
+
code: "unsupported_transport"
|
|
568
|
+
});
|
|
506
569
|
}
|
|
507
570
|
return transport.submit(request);
|
|
508
571
|
};
|
|
509
572
|
const status = async (handle, signal) => {
|
|
510
573
|
if (!transport.status) {
|
|
511
|
-
throw new
|
|
574
|
+
throw new ApiError("Transport does not support status (execute-only transport)", {
|
|
575
|
+
status: 400,
|
|
576
|
+
code: "unsupported_transport"
|
|
577
|
+
});
|
|
512
578
|
}
|
|
513
579
|
const raw = await transport.status(handle, signal);
|
|
514
580
|
return parseStatus(handle, raw);
|
|
@@ -518,13 +584,16 @@ function createWorkflowClient(transport, options = {}) {
|
|
|
518
584
|
const maxAttempts = pollOptions.maxAttempts ?? defaultMaxAttempts;
|
|
519
585
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
520
586
|
if (pollOptions.signal?.aborted) {
|
|
521
|
-
throw new
|
|
587
|
+
throw new ApiError("Operation aborted", { status: 499, code: "aborted" });
|
|
522
588
|
}
|
|
523
589
|
const next = await status(handle, pollOptions.signal);
|
|
524
590
|
if (isTerminal(next.status)) return next;
|
|
525
591
|
await sleep2(intervalMs);
|
|
526
592
|
}
|
|
527
|
-
throw new
|
|
593
|
+
throw new ApiError(
|
|
594
|
+
`Timed out waiting for workflow ${handle.workflow}:${handle.id}`,
|
|
595
|
+
{ status: 408, code: "timeout" }
|
|
596
|
+
);
|
|
528
597
|
};
|
|
529
598
|
const run = async (request, runOptions = {}) => {
|
|
530
599
|
const runMode = runOptions.mode;
|
|
@@ -543,14 +612,17 @@ function createWorkflowClient(transport, options = {}) {
|
|
|
543
612
|
const maxAttempts = subscribeOptions.maxAttempts ?? defaultMaxAttempts;
|
|
544
613
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
545
614
|
if (subscribeOptions.signal?.aborted) {
|
|
546
|
-
throw new
|
|
615
|
+
throw new ApiError("Operation aborted", { status: 499, code: "aborted" });
|
|
547
616
|
}
|
|
548
617
|
const next = await status(handle, subscribeOptions.signal);
|
|
549
618
|
yield next;
|
|
550
619
|
if (isTerminal(next.status)) return next;
|
|
551
620
|
await sleep2(intervalMs);
|
|
552
621
|
}
|
|
553
|
-
throw new
|
|
622
|
+
throw new ApiError(
|
|
623
|
+
`Timed out waiting for workflow ${handle.workflow}:${handle.id}`,
|
|
624
|
+
{ status: 408, code: "timeout" }
|
|
625
|
+
);
|
|
554
626
|
};
|
|
555
627
|
return { submit, status, result, run, subscribe };
|
|
556
628
|
}
|
|
@@ -8676,14 +8748,22 @@ var getModelsByMode = (mode, includeDisabled = false) => ALL_MODELS.filter((m) =
|
|
|
8676
8748
|
// src/core/contracts.ts
|
|
8677
8749
|
function requireObject(value, message) {
|
|
8678
8750
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
8679
|
-
throw new
|
|
8751
|
+
throw new ApiError(message, { status: 400, code: "validation_error" });
|
|
8680
8752
|
}
|
|
8681
8753
|
}
|
|
8682
8754
|
function buildInputSchema(model) {
|
|
8683
8755
|
return {
|
|
8684
8756
|
parse(input) {
|
|
8685
8757
|
requireObject(input, `Invalid input for model "${model.id}"`);
|
|
8686
|
-
|
|
8758
|
+
try {
|
|
8759
|
+
validateAll(model.paramConfig, input);
|
|
8760
|
+
} catch (err) {
|
|
8761
|
+
if (err instanceof ApiError) throw err;
|
|
8762
|
+
throw new ApiError(err instanceof Error ? err.message : String(err), {
|
|
8763
|
+
status: 400,
|
|
8764
|
+
code: "validation_error"
|
|
8765
|
+
});
|
|
8766
|
+
}
|
|
8687
8767
|
return input;
|
|
8688
8768
|
}
|
|
8689
8769
|
};
|
|
@@ -8692,7 +8772,10 @@ function buildOutputSchema(model) {
|
|
|
8692
8772
|
return {
|
|
8693
8773
|
parse(output) {
|
|
8694
8774
|
if (output == null) {
|
|
8695
|
-
throw new
|
|
8775
|
+
throw new ApiError(`Model "${model.id}" returned empty output`, {
|
|
8776
|
+
status: 502,
|
|
8777
|
+
code: "invalid_response"
|
|
8778
|
+
});
|
|
8696
8779
|
}
|
|
8697
8780
|
return output;
|
|
8698
8781
|
}
|
|
@@ -8735,7 +8818,12 @@ function throwIfErrorResult(result, modelName) {
|
|
|
8735
8818
|
if (isError) {
|
|
8736
8819
|
const code = typeof status === "number" ? ` (${status})` : "";
|
|
8737
8820
|
const detail = message ? String(message) : "unknown error";
|
|
8738
|
-
|
|
8821
|
+
const httpStatus = typeof status === "number" ? status : 502;
|
|
8822
|
+
const reason = obj.reason;
|
|
8823
|
+
throw new ApiError(`${modelName} failed${code}: ${detail}`, {
|
|
8824
|
+
status: httpStatus,
|
|
8825
|
+
code: typeof reason === "string" && reason.length > 0 ? reason : codeForStatus(httpStatus)
|
|
8826
|
+
});
|
|
8739
8827
|
}
|
|
8740
8828
|
}
|
|
8741
8829
|
function extractSyncResult(raw) {
|
|
@@ -8937,7 +9025,9 @@ var findModel = (ref) => {
|
|
|
8937
9025
|
// src/core/resolve.ts
|
|
8938
9026
|
function resolveModel(id) {
|
|
8939
9027
|
const found = findModel(id);
|
|
8940
|
-
if (!found)
|
|
9028
|
+
if (!found) {
|
|
9029
|
+
throw new ApiError(`Unknown model: "${id}"`, { status: 400, code: "unknown_model" });
|
|
9030
|
+
}
|
|
8941
9031
|
return found;
|
|
8942
9032
|
}
|
|
8943
9033
|
|
|
@@ -8977,18 +9067,34 @@ function buildTransport(config) {
|
|
|
8977
9067
|
{ params: request.payload },
|
|
8978
9068
|
request.signal
|
|
8979
9069
|
);
|
|
8980
|
-
const
|
|
9070
|
+
const { text, json } = await readErrorBody(res);
|
|
8981
9071
|
if (!res.ok) {
|
|
8982
|
-
|
|
9072
|
+
const detail = json ? json.message ?? JSON.stringify(json) : text;
|
|
9073
|
+
throw new ApiError(`Submit failed (${res.status}): ${detail}`, {
|
|
9074
|
+
status: res.status,
|
|
9075
|
+
code: reasonFrom(json, res.status)
|
|
9076
|
+
});
|
|
9077
|
+
}
|
|
9078
|
+
const response = json?.response;
|
|
9079
|
+
const id = response?.id ?? json?.id;
|
|
9080
|
+
if (!id) {
|
|
9081
|
+
throw new ApiError(`No task id in response: ${json ? JSON.stringify(json) : text}`, {
|
|
9082
|
+
status: 502,
|
|
9083
|
+
code: "invalid_response"
|
|
9084
|
+
});
|
|
8983
9085
|
}
|
|
8984
|
-
const response = data.response;
|
|
8985
|
-
const id = response?.id ?? data.id;
|
|
8986
|
-
if (!id) throw new Error(`No task id in response: ${JSON.stringify(data)}`);
|
|
8987
9086
|
return { workflow: request.workflow, id: String(id) };
|
|
8988
9087
|
},
|
|
8989
9088
|
async status(handle, signal) {
|
|
8990
9089
|
const res = await f(`${apiUrl}/workflows/${handle.workflow}/${handle.id}/result`, { signal });
|
|
8991
|
-
if (!res.ok)
|
|
9090
|
+
if (!res.ok) {
|
|
9091
|
+
const { text, json } = await readErrorBody(res);
|
|
9092
|
+
const detail = json ? json.message ?? text : text;
|
|
9093
|
+
throw new ApiError(`Status check failed (${res.status}): ${detail}`, {
|
|
9094
|
+
status: res.status,
|
|
9095
|
+
code: reasonFrom(json, res.status)
|
|
9096
|
+
});
|
|
9097
|
+
}
|
|
8992
9098
|
return res.json();
|
|
8993
9099
|
},
|
|
8994
9100
|
async execute(request) {
|
|
@@ -8997,7 +9103,14 @@ function buildTransport(config) {
|
|
|
8997
9103
|
{ params: request.payload },
|
|
8998
9104
|
request.signal
|
|
8999
9105
|
);
|
|
9000
|
-
if (!res.ok)
|
|
9106
|
+
if (!res.ok) {
|
|
9107
|
+
const { text, json } = await readErrorBody(res);
|
|
9108
|
+
const detail = json ? json.message ?? text : text;
|
|
9109
|
+
throw new ApiError(`Execute failed (${res.status}): ${detail}`, {
|
|
9110
|
+
status: res.status,
|
|
9111
|
+
code: reasonFrom(json, res.status)
|
|
9112
|
+
});
|
|
9113
|
+
}
|
|
9001
9114
|
return res.json();
|
|
9002
9115
|
},
|
|
9003
9116
|
async options(workflow, payload) {
|
|
@@ -9035,13 +9148,19 @@ function prepareRequest(model, params2) {
|
|
|
9035
9148
|
const payload = resolved.buildPayload(validatedCtx);
|
|
9036
9149
|
return { ctx, workflow: resolved.workflow, payload, contract };
|
|
9037
9150
|
}
|
|
9038
|
-
function
|
|
9151
|
+
function throwIfTerminalFailure(completed, model) {
|
|
9039
9152
|
if (completed.status === "FAILED") {
|
|
9040
|
-
throw new
|
|
9153
|
+
throw new ApiError(`${model.name} failed: ${completed.error ?? "unknown error"}`, {
|
|
9154
|
+
status: completed.statusCode ?? 502,
|
|
9155
|
+
code: completed.reason ?? "generation_failed"
|
|
9156
|
+
});
|
|
9041
9157
|
}
|
|
9042
9158
|
if (completed.status === "CANCELED") {
|
|
9043
|
-
throw new
|
|
9159
|
+
throw new ApiError(`${model.name} was canceled`, { status: 499, code: "canceled" });
|
|
9044
9160
|
}
|
|
9161
|
+
}
|
|
9162
|
+
function parseResult(completed, model, contract) {
|
|
9163
|
+
throwIfTerminalFailure(completed, model);
|
|
9045
9164
|
throwIfErrorResult(completed.result, model.name);
|
|
9046
9165
|
const parsed = contract?.output ? contract.output.parse(completed.result) : completed.result;
|
|
9047
9166
|
const multiItems = extractAllResults(parsed);
|
|
@@ -9054,22 +9173,23 @@ function parseResult(completed, model, contract) {
|
|
|
9054
9173
|
}
|
|
9055
9174
|
const url = extractUrl(parsed);
|
|
9056
9175
|
if (!url) {
|
|
9057
|
-
throw new
|
|
9176
|
+
throw new ApiError(`${model.name}: unexpected response \u2014 no result URL`, {
|
|
9177
|
+
status: 502,
|
|
9178
|
+
code: "invalid_response"
|
|
9179
|
+
});
|
|
9058
9180
|
}
|
|
9059
9181
|
return { url, results: [{ url }], model: model.id, handle: completed.handle, raw: parsed, usage: completed.usage };
|
|
9060
9182
|
}
|
|
9061
9183
|
function parseTextResult(completed, model) {
|
|
9062
|
-
|
|
9063
|
-
throw new Error(`${model.name} failed: ${completed.error ?? "unknown error"}`);
|
|
9064
|
-
}
|
|
9065
|
-
if (completed.status === "CANCELED") {
|
|
9066
|
-
throw new Error(`${model.name} was canceled`);
|
|
9067
|
-
}
|
|
9184
|
+
throwIfTerminalFailure(completed, model);
|
|
9068
9185
|
throwIfErrorResult(completed.result, model.name);
|
|
9069
9186
|
throwIfErrorResult(completed.raw, model.name);
|
|
9070
9187
|
const text = extractText(completed.result) ?? extractText(completed.raw);
|
|
9071
9188
|
if (text == null) {
|
|
9072
|
-
throw new
|
|
9189
|
+
throw new ApiError(`${model.name}: unexpected response \u2014 no text`, {
|
|
9190
|
+
status: 502,
|
|
9191
|
+
code: "invalid_response"
|
|
9192
|
+
});
|
|
9073
9193
|
}
|
|
9074
9194
|
return { text, model: model.id, handle: completed.handle, raw: completed.raw ?? completed.result, usage: completed.usage };
|
|
9075
9195
|
}
|
|
@@ -10204,7 +10324,10 @@ function createClient(config) {
|
|
|
10204
10324
|
async generate(model, params2, options) {
|
|
10205
10325
|
const resolved = resolveModel(model);
|
|
10206
10326
|
if (resolved.mode === "text") {
|
|
10207
|
-
throw new
|
|
10327
|
+
throw new ApiError(`${resolved.name} is a text model \u2014 use generateText() instead.`, {
|
|
10328
|
+
status: 400,
|
|
10329
|
+
code: "wrong_model_mode"
|
|
10330
|
+
});
|
|
10208
10331
|
}
|
|
10209
10332
|
const { workflow, payload, contract } = prepareRequest(resolved, params2);
|
|
10210
10333
|
const drive = buildDrivePayloadOptions(resolved, params2, options);
|
|
@@ -10223,7 +10346,10 @@ function createClient(config) {
|
|
|
10223
10346
|
async generateText(model, params2, options) {
|
|
10224
10347
|
const resolved = resolveModel(model);
|
|
10225
10348
|
if (resolved.mode !== "text") {
|
|
10226
|
-
throw new
|
|
10349
|
+
throw new ApiError(`${resolved.name} is not a text model \u2014 use generate() instead.`, {
|
|
10350
|
+
status: 400,
|
|
10351
|
+
code: "wrong_model_mode"
|
|
10352
|
+
});
|
|
10227
10353
|
}
|
|
10228
10354
|
const { workflow, payload } = prepareRequest(resolved, params2);
|
|
10229
10355
|
const completed = await executeModel(resolved, workflow, payload, options);
|
|
@@ -10297,10 +10423,16 @@ function createClient(config) {
|
|
|
10297
10423
|
options
|
|
10298
10424
|
);
|
|
10299
10425
|
if (done.status === "FAILED" || done.status === "CANCELED") {
|
|
10300
|
-
throw new
|
|
10426
|
+
throw new ApiError(done.error ?? `${workflow} failed with status ${done.status}`, {
|
|
10427
|
+
status: done.statusCode ?? (done.status === "CANCELED" ? 499 : 502),
|
|
10428
|
+
code: done.reason ?? (done.status === "CANCELED" ? "canceled" : "generation_failed")
|
|
10429
|
+
});
|
|
10301
10430
|
}
|
|
10302
10431
|
if (done.result === void 0) {
|
|
10303
|
-
throw new
|
|
10432
|
+
throw new ApiError(`${workflow} completed but returned no result`, {
|
|
10433
|
+
status: 502,
|
|
10434
|
+
code: "invalid_response"
|
|
10435
|
+
});
|
|
10304
10436
|
}
|
|
10305
10437
|
return done.result;
|
|
10306
10438
|
},
|
|
@@ -11397,4 +11529,4 @@ function decodeDeepLinkPayload(encoded) {
|
|
|
11397
11529
|
return deserializePayload(encoded);
|
|
11398
11530
|
}
|
|
11399
11531
|
|
|
11400
|
-
export { ALL_MODELS, ExecutionMode as ApiRunMode, DEFAULT_VISIBLE_RELEASES, KLING_DUAL_IMAGE_EFFECTS, Model, Models, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, toAvatarOption, toVoiceOption };
|
|
11532
|
+
export { ALL_MODELS, ApiError, ExecutionMode as ApiRunMode, DEFAULT_VISIBLE_RELEASES, KLING_DUAL_IMAGE_EFFECTS, Model, Models, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, toAvatarOption, toVoiceOption };
|