@tanstack/ai-byteplus 0.0.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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/dist/esm/adapters/image.d.ts +89 -0
  4. package/dist/esm/adapters/image.js +229 -0
  5. package/dist/esm/adapters/image.js.map +1 -0
  6. package/dist/esm/adapters/text.d.ts +163 -0
  7. package/dist/esm/adapters/text.js +347 -0
  8. package/dist/esm/adapters/text.js.map +1 -0
  9. package/dist/esm/adapters/transcription.d.ts +102 -0
  10. package/dist/esm/adapters/transcription.js +274 -0
  11. package/dist/esm/adapters/transcription.js.map +1 -0
  12. package/dist/esm/adapters/tts.d.ts +143 -0
  13. package/dist/esm/adapters/tts.js +307 -0
  14. package/dist/esm/adapters/tts.js.map +1 -0
  15. package/dist/esm/adapters/video.d.ts +182 -0
  16. package/dist/esm/adapters/video.js +442 -0
  17. package/dist/esm/adapters/video.js.map +1 -0
  18. package/dist/esm/audio/transcription-provider-options.d.ts +46 -0
  19. package/dist/esm/audio/tts-provider-options.d.ts +114 -0
  20. package/dist/esm/audio/wire-types.d.ts +261 -0
  21. package/dist/esm/audio/wire-types.js +28 -0
  22. package/dist/esm/audio/wire-types.js.map +1 -0
  23. package/dist/esm/image/image-provider-options.d.ts +165 -0
  24. package/dist/esm/image/image-provider-options.js +134 -0
  25. package/dist/esm/image/image-provider-options.js.map +1 -0
  26. package/dist/esm/image/wire-types.d.ts +149 -0
  27. package/dist/esm/index.d.ts +25 -0
  28. package/dist/esm/index.js +11 -0
  29. package/dist/esm/message-types.d.ts +154 -0
  30. package/dist/esm/model-meta.d.ts +594 -0
  31. package/dist/esm/model-meta.js +619 -0
  32. package/dist/esm/model-meta.js.map +1 -0
  33. package/dist/esm/text/text-provider-options.d.ts +109 -0
  34. package/dist/esm/utils/client.d.ts +183 -0
  35. package/dist/esm/utils/client.js +253 -0
  36. package/dist/esm/utils/client.js.map +1 -0
  37. package/dist/esm/video/video-provider-options.d.ts +197 -0
  38. package/dist/esm/video/video-provider-options.js +191 -0
  39. package/dist/esm/video/video-provider-options.js.map +1 -0
  40. package/dist/esm/video/wire-types.d.ts +248 -0
  41. package/package.json +77 -0
  42. package/src/adapters/image.ts +409 -0
  43. package/src/adapters/text.ts +539 -0
  44. package/src/adapters/transcription.ts +479 -0
  45. package/src/adapters/tts.ts +447 -0
  46. package/src/adapters/video.ts +732 -0
  47. package/src/audio/transcription-provider-options.ts +46 -0
  48. package/src/audio/tts-provider-options.ts +122 -0
  49. package/src/audio/wire-types.ts +290 -0
  50. package/src/image/image-provider-options.ts +288 -0
  51. package/src/image/wire-types.ts +169 -0
  52. package/src/index.ts +222 -0
  53. package/src/message-types.ts +169 -0
  54. package/src/model-meta.ts +954 -0
  55. package/src/text/text-provider-options.ts +151 -0
  56. package/src/utils/client.ts +377 -0
  57. package/src/video/video-provider-options.ts +361 -0
  58. package/src/video/wire-types.ts +293 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":[],"sources":["../../../src/utils/client.ts"],"sourcesContent":["import { getApiKeyFromEnv } from '@tanstack/ai-utils'\nimport type { ClientOptions } from 'openai'\n\n/**\n * BytePlus splits its APIs across two hosts with two different products,\n * two different auth headers, and two different API keys:\n *\n * - **Ark (ModelArk)** — chat, video (Seedance) and image (Seedream).\n * `Authorization: Bearer $ARK_API_KEY`.\n * - **Seed Speech** — TTS and ASR on the voice host.\n * `X-Api-Key: $BYTEPLUS_VOICE_API_KEY`.\n *\n * Ark keys are region-isolated: a key issued for `ap-southeast` does not work\n * against the EU host and vice versa.\n */\n\n/**\n * Default Ark data-plane base URL (Asia-Pacific south-east).\n *\n * Per the BytePlus docs the EU endpoint\n * (`https://ark.eu-west.bytepluses.com/api/v3`) serves chat and image only —\n * Seedance video is not available there. Docs-derived: only the ap-southeast\n * host was exercised live.\n */\nexport const BYTEPLUS_ARK_BASE_URL =\n 'https://ark.ap-southeast.bytepluses.com/api/v3'\n\n/**\n * Default Seed Speech base URL. Endpoint paths are appended under\n * `/api/v3` (e.g. `/api/v3/tts/create`).\n */\nexport const BYTEPLUS_VOICE_BASE_URL =\n 'https://voice.ap-southeast-1.bytepluses.com'\n\n/**\n * Configuration for the Ark-hosted adapters (chat, video, image).\n *\n * Extends the OpenAI SDK's client options because the chat adapter drives the\n * OpenAI-compatible `/chat/completions` endpoint through the shared\n * `@tanstack/openai-base` adapter. `fetch` and `defaultHeaders` are inherited\n * from `ClientOptions`, and the video/image adapters — which issue plain JSON\n * requests rather than SDK calls — honour the same two fields so tests can\n * inject a fetch instead of patching the global one.\n *\n * Two inherited fields differ in reach, because the fetch-based adapters have\n * no SDK to delegate to:\n * - `timeout` is honoured everywhere — the fetch-based adapters convert it to\n * an `AbortSignal` (see {@link bytePlusTimeoutSignal}).\n * - `maxRetries` applies to the **chat adapter only**. The video, image and\n * speech adapters do not retry; video polling is driven by core's loop,\n * which owns its own retry policy.\n */\nexport interface BytePlusArkConfig extends Omit<ClientOptions, 'apiKey'> {\n apiKey: string\n}\n\n/**\n * Configuration for the Seed Speech adapters (TTS, ASR).\n *\n * Seed Speech is not OpenAI-compatible, so this is a minimal config for\n * direct `fetch` calls rather than an OpenAI `ClientOptions` extension.\n */\nexport interface BytePlusVoiceConfig {\n /** Seed Speech API key — *not* the Ark key. Sent as `X-Api-Key`. */\n apiKey: string\n\n /** Overrides {@link BYTEPLUS_VOICE_BASE_URL}. */\n baseURL?: string\n\n /** Additional headers merged into every request (e.g., test ids). */\n defaultHeaders?: Record<string, string>\n\n /**\n * Override the underlying fetch. Defaults to the global `fetch`. Useful for\n * proxying, instrumentation, or pointing requests at a mock in tests.\n */\n fetch?: typeof fetch\n}\n\n/**\n * Gets the BytePlus Ark API key from environment variables, preferring\n * `ARK_API_KEY` and falling back to `BYTEPLUS_API_KEY`.\n * @throws Error if neither variable is set\n */\nexport function getBytePlusArkApiKeyFromEnv(): string {\n try {\n return getApiKeyFromEnv('ARK_API_KEY')\n } catch {\n try {\n return getApiKeyFromEnv('BYTEPLUS_API_KEY')\n } catch {\n throw new Error(\n 'ARK_API_KEY or BYTEPLUS_API_KEY is required. Please set one of these environment variables or use the factory function with an explicit API key.',\n )\n }\n }\n}\n\n/**\n * Gets the Seed Speech API key from environment variables.\n *\n * Seed Speech is a separate BytePlus product from Ark with its own key — an\n * Ark key sent as `X-Api-Key` is rejected with `45000010 Invalid X-Api-Key`.\n *\n * @throws Error if BYTEPLUS_VOICE_API_KEY is not found\n */\nexport function getBytePlusVoiceApiKeyFromEnv(): string {\n try {\n return getApiKeyFromEnv('BYTEPLUS_VOICE_API_KEY')\n } catch {\n throw new Error(\n 'BYTEPLUS_VOICE_API_KEY is required for Seed Speech (TTS/ASR). This is a different key from ARK_API_KEY. Please set it in your environment variables or use the factory function with an explicit API key.',\n )\n }\n}\n\n/**\n * Returns an Ark client config with the default Ark base URL applied when not\n * already set, and any trailing slashes trimmed so path joins stay\n * single-slashed.\n *\n * The returned `baseURL` is always a string: adapters that build request paths\n * by interpolation can use it directly without re-applying a default (which\n * would otherwise risk interpolating `undefined` into a URL). The config's own\n * type is preserved, so adapter-specific config fields survive the call.\n */\nexport function withBytePlusArkDefaults<TConfig extends BytePlusArkConfig>(\n config: TConfig,\n): Omit<TConfig, 'baseURL'> & { baseURL: string } {\n return {\n ...config,\n baseURL: (config.baseURL || BYTEPLUS_ARK_BASE_URL).replace(/\\/+$/, ''),\n }\n}\n\n/**\n * Returns a Seed Speech config with the default voice base URL applied (and\n * any trailing slashes trimmed) when not already set.\n *\n * As with {@link withBytePlusArkDefaults}, the returned `baseURL` is always a\n * string, so adapters can interpolate it without re-applying a fallback, and\n * the config's own type is preserved.\n */\nexport function withBytePlusVoiceDefaults<TConfig extends BytePlusVoiceConfig>(\n config: TConfig,\n): Omit<TConfig, 'baseURL'> & { baseURL: string } {\n return {\n ...config,\n baseURL: (config.baseURL || BYTEPLUS_VOICE_BASE_URL).replace(/\\/+$/, ''),\n }\n}\n\n/**\n * Normalizes the OpenAI-shaped `defaultHeaders` config field (which accepts a\n * `Headers` instance, an entry list, or a record with nullable values) into\n * the plain record the header builders below take. Non-string values are\n * dropped rather than serialized.\n *\n * Shared by every fetch-based adapter (image, video, speech): they all read\n * `defaultHeaders` off a config typed by the OpenAI SDK but issue plain JSON\n * requests.\n */\nexport function toHeaderRecord(\n headers: BytePlusArkConfig['defaultHeaders'],\n): Record<string, string> {\n const record: Record<string, string> = {}\n if (!headers) return record\n\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n record[key] = value\n })\n return record\n }\n\n // The entry-list form is typed as arrays of nullable values rather than\n // strict [name, value] tuples, so both halves are checked.\n if (Array.isArray(headers)) {\n for (const [key, value] of headers) {\n if (typeof key === 'string' && typeof value === 'string') {\n record[key] = value\n }\n }\n return record\n }\n\n // Record form. A value may be null/undefined (openai's \"unset this header\"\n // signal) or an array for a repeated header; neither maps onto a single\n // string, so both are dropped.\n for (const [key, value] of Object.entries(headers)) {\n if (typeof value === 'string') record[key] = value\n }\n\n return record\n}\n\n/**\n * Drops any caller-supplied header whose name case-insensitively collides with\n * one the adapter sets itself, then applies the adapter's own.\n *\n * Spreading `reserved` last is not enough on its own: HTTP header names are\n * case-insensitive, but plain object keys are not, so `authorization` and\n * `Authorization` are two distinct properties that both survive the spread.\n * `fetch` then feeds the object to the `Headers` constructor, which *appends*\n * rather than replaces — turning the pair into\n * `authorization: \"Bearer wrong, Bearer right\"` and 401ing every request with\n * what reads like a bad key. `toHeaderRecord` lowercases names whenever\n * `defaultHeaders` arrives as a `Headers` instance, so that collision is\n * reachable through ordinary config, not just a hand-built record.\n */\nfunction applyReservedHeaders(\n extraHeaders: Record<string, string> | undefined,\n reserved: Record<string, string>,\n): Record<string, string> {\n const blocked = new Set(Object.keys(reserved).map((key) => key.toLowerCase()))\n const merged: Record<string, string> = {}\n for (const [key, value] of Object.entries(extraHeaders ?? {})) {\n if (!blocked.has(key.toLowerCase())) merged[key] = value\n }\n return { ...merged, ...reserved }\n}\n\n/**\n * Turns the OpenAI-shaped `timeout` config field (milliseconds) into the\n * `signal` a plain `fetch` needs, or `undefined` when no timeout is set.\n *\n * `BytePlusArkConfig` extends the OpenAI SDK's `ClientOptions` because the\n * chat adapter drives the SDK, which honours `timeout` and `maxRetries`\n * itself. The video, image and speech adapters issue plain JSON requests, so\n * without this they would accept a `timeout` and ignore it — a stalled Ark\n * connection hanging the caller forever despite an explicit setting.\n *\n * `maxRetries` has no equivalent here and stays SDK-path-only; it is\n * documented as such on {@link BytePlusArkConfig}.\n */\nexport function bytePlusTimeoutSignal(\n timeout: number | undefined,\n): AbortSignal | undefined {\n return typeof timeout === 'number' && timeout > 0\n ? AbortSignal.timeout(timeout)\n : undefined\n}\n\n/**\n * Headers for a JSON request against the Ark data plane.\n *\n * A caller-supplied `Authorization` or `Content-Type` in `defaultHeaders` is\n * dropped in any casing — see {@link applyReservedHeaders}.\n */\nexport function bytePlusArkHeaders(\n apiKey: string,\n extraHeaders?: Record<string, string>,\n): Record<string, string> {\n return applyReservedHeaders(extraHeaders, {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n })\n}\n\n/**\n * Headers for a JSON request against the Seed Speech host.\n *\n * As with {@link bytePlusArkHeaders}, a caller-supplied `X-Api-Key` is dropped\n * in any casing. Seed Speech answers a clobbered key with\n * `45000010 Invalid X-Api-Key`, which reads as a misconfigured key rather than\n * a header collision.\n */\nexport function bytePlusVoiceHeaders(\n apiKey: string,\n extraHeaders?: Record<string, string>,\n): Record<string, string> {\n return applyReservedHeaders(extraHeaders, {\n 'Content-Type': 'application/json',\n 'X-Api-Key': apiKey,\n })\n}\n\n/**\n * Reads a response body as JSON, tolerating the non-JSON failures both\n * BytePlus hosts can return (an empty body, or an HTML error page from a proxy\n * in front of the API).\n *\n * Returns the parsed value, the raw text when it is not JSON, or `undefined`\n * for an empty body — all three of which {@link bytePlusArkError} and\n * {@link bytePlusVoiceError} know how to render.\n */\nexport async function readJsonBody(response: Response): Promise<unknown> {\n const text = await response.text()\n if (!text) return undefined\n try {\n return JSON.parse(text)\n } catch {\n return text\n }\n}\n\n/**\n * Best-effort human-readable rendering of a response body we could not pull a\n * `message` out of — a raw string passes through, any other object is\n * serialized so the detail reaches the error instead of being dropped.\n *\n * Exported for adapters that need to attach a body to an error they raise\n * themselves rather than one derived from a non-OK response — e.g. the image\n * adapter reporting a 200 whose `data[]` items match no known shape.\n */\nexport function describeBody(body: unknown): string | undefined {\n if (typeof body === 'string') return body || undefined\n if (typeof body !== 'object' || body === null) return undefined\n try {\n return JSON.stringify(body)\n } catch {\n // Circular or otherwise unserializable — the status code stands alone.\n return undefined\n }\n}\n\nfunction readStringField(value: unknown, field: string): string | undefined {\n if (typeof value !== 'object' || value === null || !(field in value)) {\n return undefined\n }\n const candidate = Reflect.get(value, field)\n if (typeof candidate === 'string') return candidate\n if (typeof candidate === 'number') return String(candidate)\n return undefined\n}\n\n/**\n * Formats an Ark error response into an `Error`.\n *\n * Ark uses the OpenAI error envelope with dotted string codes:\n * `{\"error\": {\"code\": \"InvalidEndpointOrModel.NotFound\", \"message\": \"…\"}}`.\n * Bodies that don't match (HTML error pages, proxy responses) fall back to\n * the raw text so the failure stays diagnosable.\n */\nexport function bytePlusArkError(\n status: number,\n body: unknown,\n context?: string,\n): Error {\n const prefix = context ? `BytePlus Ark ${context}` : 'BytePlus Ark request'\n const error =\n typeof body === 'object' && body !== null && 'error' in body\n ? Reflect.get(body, 'error')\n : undefined\n const code = readStringField(error, 'code')\n const message = readStringField(error, 'message')\n const detail = message ?? describeBody(body)\n return new Error(\n `${prefix} failed (${status}${code ? ` ${code}` : ''})${\n detail ? `: ${detail}` : ''\n }`,\n )\n}\n\n/**\n * Formats a Seed Speech error response into an `Error`.\n *\n * Seed Speech does not use the Ark envelope — it returns a flat numeric code:\n * `{\"code\": 45000010, \"message\": \"Invalid X-Api-Key\"}`.\n */\nexport function bytePlusVoiceError(\n status: number,\n body: unknown,\n context?: string,\n): Error {\n const prefix = context\n ? `BytePlus Seed Speech ${context}`\n : 'BytePlus Seed Speech request'\n const code = readStringField(body, 'code')\n const message = readStringField(body, 'message')\n const detail = message ?? describeBody(body)\n return new Error(\n `${prefix} failed (${status}${code ? ` ${code}` : ''})${\n detail ? `: ${detail}` : ''\n }`,\n )\n}\n"],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import { BytePlusVideoModel, BytePlusVideoModelOrString, BytePlusVideoRatio, BytePlusVideoResolution } from '../model-meta.js';
2
+ /**
3
+ * Inference queue for the request.
4
+ *
5
+ * - `default` — online inference: lower RPM and concurrency quotas, lowest
6
+ * latency.
7
+ * - `flex` — offline batch inference: higher daily token quotas at half the
8
+ * price, with no latency guarantee. Task ids come back with a `cgt-batch-`
9
+ * prefix (live-verified).
10
+ *
11
+ * Only the Seedance 1.x models accept this field. The Seedance 2.0 family
12
+ * rejects it ("service_tier is not supported … must be empty").
13
+ *
14
+ * @experimental Video generation is an experimental feature and may change.
15
+ */
16
+ export type BytePlusVideoServiceTier = 'default' | 'flex';
17
+ /**
18
+ * Provider-specific options for Seedance video generation. These map one-to-one
19
+ * onto the create-task request body and take precedence over the values the
20
+ * adapter derives from the generic `size` / `duration` options.
21
+ *
22
+ * Fields are model-dependent; each one documents where it applies. Passing a
23
+ * field to a model that does not accept it is a 400 from Ark, not a silent
24
+ * ignore.
25
+ *
26
+ * @experimental Video generation is an experimental feature and may change.
27
+ */
28
+ export interface BytePlusVideoProviderOptions {
29
+ /**
30
+ * Output aspect ratio. Overrides the ratio half of the generic `size`.
31
+ *
32
+ * `adaptive` (follow the input frame) is the default on Seedance 2.0 and
33
+ * 1.5-pro but is rejected by Seedance 1.0-pro / 1.0-pro-fast for
34
+ * text-to-video.
35
+ */
36
+ ratio?: BytePlusVideoRatio;
37
+ /**
38
+ * Output resolution tier. Overrides the resolution half of the generic
39
+ * `size`. Matched case-insensitively by the API; this package uses
40
+ * lowercase throughout. `4k` exists only on `dreamina-seedance-2-0-260128`,
41
+ * and there is no 2K tier on any model.
42
+ */
43
+ resolution?: BytePlusVideoResolution;
44
+ /**
45
+ * Whole seconds of output. Overrides the generic `duration`, and unlike it
46
+ * is sent verbatim rather than snapped into the model's range.
47
+ *
48
+ * `-1` asks the model to choose its own length; accepted by Seedance 2.0
49
+ * and 1.5-pro only.
50
+ */
51
+ duration?: number;
52
+ /**
53
+ * Frame count instead of `duration`, for fractional-second output. Takes
54
+ * precedence over `duration` server-side. Valid values are the integers of
55
+ * the form `25 + 4n` within `[29, 289]`, at 24 fps.
56
+ *
57
+ * Seedance 1.0-pro and 1.0-pro-fast only.
58
+ */
59
+ frames?: number;
60
+ /**
61
+ * Randomness seed, an integer in `[-1, 2^32-1]`. `-1` (the default) leaves
62
+ * generation unseeded. Accepted by every Seedance model.
63
+ */
64
+ seed?: number;
65
+ /**
66
+ * Appends a "fix the camera" instruction to the prompt. Best-effort — the
67
+ * model is not constrained to obey it.
68
+ *
69
+ * Seedance 1.5-pro, 1.0-pro and 1.0-pro-fast only; the 2.0 family rejects
70
+ * it.
71
+ */
72
+ camera_fixed?: boolean;
73
+ /** Burn a watermark into the output. Defaults to `false`. */
74
+ watermark?: boolean;
75
+ /**
76
+ * Generate an audio track synchronized with the visuals — dialogue, effects
77
+ * and score inferred from the prompt. Quote dialogue in the prompt for
78
+ * better results.
79
+ *
80
+ * Accepted by every model at the API's validation layer, but only Seedance
81
+ * 2.0 and 1.5-pro actually produce audio.
82
+ */
83
+ generate_audio?: boolean;
84
+ /**
85
+ * Inference queue. Seedance 1.x only — the 2.0 family has no offline tier.
86
+ */
87
+ service_tier?: BytePlusVideoServiceTier;
88
+ /**
89
+ * Also return the video's final frame as a watermark-free PNG, readable from
90
+ * the finished task as `content.last_frame_url`. Chain it into the next
91
+ * task's first frame to extend a shot. Accepted by every model.
92
+ */
93
+ return_last_frame?: boolean;
94
+ /**
95
+ * Render a cheap, low-fidelity preview to sanity-check staging and camera
96
+ * work before paying for the real thing.
97
+ *
98
+ * Seedance 1.5-pro only.
99
+ */
100
+ draft?: boolean;
101
+ /**
102
+ * Queue priority, `[0, 9]`. Seedance 2.0 family only — 1.5-pro rejects it,
103
+ * and the 1.0 models accept it without acting on it.
104
+ */
105
+ priority?: number;
106
+ /**
107
+ * Seconds after `created_at` at which an unfinished task is abandoned and
108
+ * marked `expired`. Documented range `[3600, 259200]`, default 172800
109
+ * (48 hours). The floor is enforced on Seedance 1.x but not on the 2.0
110
+ * family.
111
+ */
112
+ execution_expires_after?: number;
113
+ /**
114
+ * URL that receives a POST with the full task payload on every status
115
+ * change. BytePlus retries a failed delivery three times.
116
+ */
117
+ callback_url?: string;
118
+ /**
119
+ * Stable opaque identifier for the end user driving the request, for abuse
120
+ * attribution. Max 64 characters — hash the real identifier rather than
121
+ * sending it.
122
+ */
123
+ safety_identifier?: string;
124
+ }
125
+ /**
126
+ * Type-only map from video model name to its provider options. Seedance takes
127
+ * the same option surface across models; applicability is per-field and
128
+ * documented on {@link BytePlusVideoProviderOptions}.
129
+ *
130
+ * @experimental Video generation is an experimental feature and may change.
131
+ */
132
+ export type BytePlusVideoModelProviderOptionsByName = {
133
+ [K in BytePlusVideoModel]: BytePlusVideoProviderOptions;
134
+ };
135
+ /**
136
+ * True when the model is *known* to support reference-media mode (reference
137
+ * images, video and audio). An id this package has no metadata for answers
138
+ * `false`; callers must decide whether that means "no" or "unknown" — the
139
+ * adapter treats it as unknown and lets Ark rule.
140
+ *
141
+ * @experimental Video generation is an experimental feature and may change.
142
+ */
143
+ export declare function supportsReferenceMedia(model: string): boolean;
144
+ /**
145
+ * True when the model is *known* to support pinning the video's closing
146
+ * frame. Same unknown-id caveat as {@link supportsReferenceMedia}.
147
+ *
148
+ * @experimental Video generation is an experimental feature and may change.
149
+ */
150
+ export declare function supportsLastFrame(model: string): boolean;
151
+ /**
152
+ * Splits a `size` template into its Seedance request fields.
153
+ *
154
+ * The template is either a bare aspect ratio (`'16:9'`) or
155
+ * `ratio_resolution` (`'16:9_720p'`), mirroring the grok video adapter.
156
+ * Returns `undefined` when the string doesn't match the template at all.
157
+ *
158
+ * The resolution half comes back lowercased. Ark itself matches the field
159
+ * case-insensitively, but this package standardizes on lowercase so callers
160
+ * can compare the result against {@link BytePlusVideoResolution} directly.
161
+ *
162
+ * @experimental Video generation is an experimental feature and may change.
163
+ */
164
+ export declare function parseBytePlusVideoSize(size: string): {
165
+ ratio: string;
166
+ resolution?: string;
167
+ } | undefined;
168
+ /**
169
+ * Validates a resolution against a model's tiers, returning it lowercased.
170
+ *
171
+ * Used for both halves of the request: the resolution parsed out of the
172
+ * generic `size`, and a `modelOptions.resolution` that overrides it. A model
173
+ * this package has no table for is normalized but not checked — see
174
+ * {@link BytePlusVideoModelOrString}.
175
+ *
176
+ * @throws Error when a known model does not offer the tier.
177
+ *
178
+ * @experimental Video generation is an experimental feature and may change.
179
+ */
180
+ export declare function resolveBytePlusVideoResolution(model: BytePlusVideoModelOrString, resolution: string): string;
181
+ /**
182
+ * Validates a `size` template against a model and returns the request fields
183
+ * it maps onto, with the resolution lowercased.
184
+ *
185
+ * For an unknown model only the template's *shape* is checked — enough to
186
+ * split it into `ratio` and `resolution` — because a future model may bring
187
+ * ratios and tiers that do not exist today. Ark validates the values.
188
+ *
189
+ * @throws Error when the template is malformed, or (known models only) the
190
+ * ratio is unknown or the resolution is not offered by this model.
191
+ *
192
+ * @experimental Video generation is an experimental feature and may change.
193
+ */
194
+ export declare function resolveBytePlusVideoSize(model: BytePlusVideoModelOrString, size: string): {
195
+ ratio: string;
196
+ resolution?: string;
197
+ };
@@ -0,0 +1,191 @@
1
+ import { isKnownBytePlusVideoModel } from "../model-meta.js";
2
+ //#region src/video/video-provider-options.ts
3
+ /**
4
+ * Provider options and per-model capability tables for the BytePlus Seedance
5
+ * video models.
6
+ *
7
+ * Every applicability claim below was probed live against
8
+ * `https://ark.ap-southeast.bytepluses.com/api/v3` on 2026-07-31. The probe
9
+ * sent an out-of-range `seed` alongside the field under test, so requests that
10
+ * passed validation still failed before a task was created (nothing billed):
11
+ * an error naming the field under test means "rejected", an error naming
12
+ * `seed` means "accepted". Ark reports only one arbitrary invalid parameter
13
+ * per request, so each cell was retried until a verdict repeated.
14
+ *
15
+ * Ark rejects an inapplicable field outright — "the specified parameter
16
+ * `draft` is not supported for model seedance-1-0-pro in t2v, must be empty" —
17
+ * so these tables are not cosmetic: sending a field to the wrong model is a
18
+ * 400, not a no-op.
19
+ *
20
+ * **Where the adapter guards, and where it doesn't** (deliberate, not an
21
+ * oversight). Scalar applicability — `service_tier`, `draft`, `priority`,
22
+ * `frames`, `camera_fixed` — is left to Ark, whose 400 names the offending
23
+ * field and the model precisely enough to act on, and whose per-model rules
24
+ * shift as BytePlus ships models. Duplicating that here would mean a table
25
+ * that silently goes stale and starts rejecting requests the API would have
26
+ * accepted. The adapter guards locally only where the API's own error is
27
+ * misleading or arrives too late to be actionable: prompt media shape (role
28
+ * vocabulary, frame-vs-reference exclusivity, frame cardinality) and the
29
+ * resolution tier, both of which are derived from a caller's `prompt` /
30
+ * `size` rather than passed through verbatim.
31
+ *
32
+ * @experimental Video generation is an experimental feature and may change.
33
+ */
34
+ /**
35
+ * Aspect ratios accepted by the create endpoint.
36
+ *
37
+ * `adaptive` is rejected by Seedance 1.0-pro / 1.0-pro-fast for text-to-video
38
+ * but is the documented default for their image-to-video path, so it is not
39
+ * filtered per model here.
40
+ */
41
+ var BYTEPLUS_VIDEO_RATIOS = [
42
+ "16:9",
43
+ "9:16",
44
+ "4:3",
45
+ "3:4",
46
+ "1:1",
47
+ "21:9",
48
+ "adaptive"
49
+ ];
50
+ /**
51
+ * Resolutions each model accepts, live-probed.
52
+ *
53
+ * Two findings here contradict the BytePlus prose docs and are worth calling
54
+ * out: there is no 2K tier on any Seedance model (`2k`/`2K` is rejected
55
+ * everywhere, including on the 2.0 flagship whose docs advertise "up to 4K"),
56
+ * and `seedance-1-0-pro-fast-251015` does accept `1080p` despite being
57
+ * documented as 480p/720p only.
58
+ */
59
+ var BYTEPLUS_VIDEO_RESOLUTIONS = {
60
+ "dreamina-seedance-2-0-260128": [
61
+ "480p",
62
+ "720p",
63
+ "1080p",
64
+ "4k"
65
+ ],
66
+ "dreamina-seedance-2-0-fast-260128": ["480p", "720p"],
67
+ "dreamina-seedance-2-0-mini-260615": ["480p", "720p"],
68
+ "seedance-1-5-pro-251215": [
69
+ "480p",
70
+ "720p",
71
+ "1080p"
72
+ ],
73
+ "seedance-1-0-pro-250528": [
74
+ "480p",
75
+ "720p",
76
+ "1080p"
77
+ ],
78
+ "seedance-1-0-pro-fast-251015": [
79
+ "480p",
80
+ "720p",
81
+ "1080p"
82
+ ]
83
+ };
84
+ /**
85
+ * Models accepting reference-media mode (`r2v`): reference images, video and
86
+ * audio that the output draws on without pinning specific frames. The 1.x
87
+ * models reject it with "the specified task_type r2v does not support model …".
88
+ */
89
+ var BYTEPLUS_VIDEO_REFERENCE_MEDIA_MODELS = /* @__PURE__ */ new Set([
90
+ "dreamina-seedance-2-0-260128",
91
+ "dreamina-seedance-2-0-fast-260128",
92
+ "dreamina-seedance-2-0-mini-260615"
93
+ ]);
94
+ /**
95
+ * Models accepting a closing frame (`flf2v`, first-and-last-frame mode).
96
+ * `seedance-1-0-pro-fast-251015` is the one Seedance model without it — it
97
+ * does text-to-video and single-first-frame image-to-video only.
98
+ */
99
+ var BYTEPLUS_VIDEO_LAST_FRAME_MODELS = /* @__PURE__ */ new Set([
100
+ "dreamina-seedance-2-0-260128",
101
+ "dreamina-seedance-2-0-fast-260128",
102
+ "dreamina-seedance-2-0-mini-260615",
103
+ "seedance-1-5-pro-251215",
104
+ "seedance-1-0-pro-250528"
105
+ ]);
106
+ /**
107
+ * True when the model is *known* to support reference-media mode (reference
108
+ * images, video and audio). An id this package has no metadata for answers
109
+ * `false`; callers must decide whether that means "no" or "unknown" — the
110
+ * adapter treats it as unknown and lets Ark rule.
111
+ *
112
+ * @experimental Video generation is an experimental feature and may change.
113
+ */
114
+ function supportsReferenceMedia(model) {
115
+ return BYTEPLUS_VIDEO_REFERENCE_MEDIA_MODELS.has(model);
116
+ }
117
+ /**
118
+ * True when the model is *known* to support pinning the video's closing
119
+ * frame. Same unknown-id caveat as {@link supportsReferenceMedia}.
120
+ *
121
+ * @experimental Video generation is an experimental feature and may change.
122
+ */
123
+ function supportsLastFrame(model) {
124
+ return BYTEPLUS_VIDEO_LAST_FRAME_MODELS.has(model);
125
+ }
126
+ /**
127
+ * Splits a `size` template into its Seedance request fields.
128
+ *
129
+ * The template is either a bare aspect ratio (`'16:9'`) or
130
+ * `ratio_resolution` (`'16:9_720p'`), mirroring the grok video adapter.
131
+ * Returns `undefined` when the string doesn't match the template at all.
132
+ *
133
+ * The resolution half comes back lowercased. Ark itself matches the field
134
+ * case-insensitively, but this package standardizes on lowercase so callers
135
+ * can compare the result against {@link BytePlusVideoResolution} directly.
136
+ *
137
+ * @experimental Video generation is an experimental feature and may change.
138
+ */
139
+ function parseBytePlusVideoSize(size) {
140
+ const [, ratio, resolution] = /^(\d+:\d+|adaptive)(?:_(.+))?$/.exec(size) ?? [];
141
+ if (ratio === void 0) return void 0;
142
+ return {
143
+ ratio,
144
+ ...resolution !== void 0 && { resolution: resolution.toLowerCase() }
145
+ };
146
+ }
147
+ /**
148
+ * Validates a resolution against a model's tiers, returning it lowercased.
149
+ *
150
+ * Used for both halves of the request: the resolution parsed out of the
151
+ * generic `size`, and a `modelOptions.resolution` that overrides it. A model
152
+ * this package has no table for is normalized but not checked — see
153
+ * {@link BytePlusVideoModelOrString}.
154
+ *
155
+ * @throws Error when a known model does not offer the tier.
156
+ *
157
+ * @experimental Video generation is an experimental feature and may change.
158
+ */
159
+ function resolveBytePlusVideoResolution(model, resolution) {
160
+ const normalized = resolution.toLowerCase();
161
+ if (!isKnownBytePlusVideoModel(model)) return normalized;
162
+ const allowed = BYTEPLUS_VIDEO_RESOLUTIONS[model];
163
+ if (!allowed.includes(normalized)) throw new Error(`byteplus: resolution "${resolution}" is not supported by model "${model}". Supported resolutions: ${allowed.join(", ")}.`);
164
+ return normalized;
165
+ }
166
+ /**
167
+ * Validates a `size` template against a model and returns the request fields
168
+ * it maps onto, with the resolution lowercased.
169
+ *
170
+ * For an unknown model only the template's *shape* is checked — enough to
171
+ * split it into `ratio` and `resolution` — because a future model may bring
172
+ * ratios and tiers that do not exist today. Ark validates the values.
173
+ *
174
+ * @throws Error when the template is malformed, or (known models only) the
175
+ * ratio is unknown or the resolution is not offered by this model.
176
+ *
177
+ * @experimental Video generation is an experimental feature and may change.
178
+ */
179
+ function resolveBytePlusVideoSize(model, size) {
180
+ const parsed = parseBytePlusVideoSize(size);
181
+ const known = isKnownBytePlusVideoModel(model);
182
+ if (!parsed || known && !BYTEPLUS_VIDEO_RATIOS.includes(parsed.ratio)) throw new Error(`byteplus: size "${size}" is not supported by model "${model}". Expected "ratio" or "ratio_resolution" (e.g. "16:9_720p") with ratio one of: ${BYTEPLUS_VIDEO_RATIOS.join(", ")}.`);
183
+ return {
184
+ ratio: parsed.ratio,
185
+ ...parsed.resolution !== void 0 && { resolution: resolveBytePlusVideoResolution(model, parsed.resolution) }
186
+ };
187
+ }
188
+ //#endregion
189
+ export { parseBytePlusVideoSize, resolveBytePlusVideoResolution, resolveBytePlusVideoSize, supportsLastFrame, supportsReferenceMedia };
190
+
191
+ //# sourceMappingURL=video-provider-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-provider-options.js","names":[],"sources":["../../../src/video/video-provider-options.ts"],"sourcesContent":["/**\n * Provider options and per-model capability tables for the BytePlus Seedance\n * video models.\n *\n * Every applicability claim below was probed live against\n * `https://ark.ap-southeast.bytepluses.com/api/v3` on 2026-07-31. The probe\n * sent an out-of-range `seed` alongside the field under test, so requests that\n * passed validation still failed before a task was created (nothing billed):\n * an error naming the field under test means \"rejected\", an error naming\n * `seed` means \"accepted\". Ark reports only one arbitrary invalid parameter\n * per request, so each cell was retried until a verdict repeated.\n *\n * Ark rejects an inapplicable field outright — \"the specified parameter\n * `draft` is not supported for model seedance-1-0-pro in t2v, must be empty\" —\n * so these tables are not cosmetic: sending a field to the wrong model is a\n * 400, not a no-op.\n *\n * **Where the adapter guards, and where it doesn't** (deliberate, not an\n * oversight). Scalar applicability — `service_tier`, `draft`, `priority`,\n * `frames`, `camera_fixed` — is left to Ark, whose 400 names the offending\n * field and the model precisely enough to act on, and whose per-model rules\n * shift as BytePlus ships models. Duplicating that here would mean a table\n * that silently goes stale and starts rejecting requests the API would have\n * accepted. The adapter guards locally only where the API's own error is\n * misleading or arrives too late to be actionable: prompt media shape (role\n * vocabulary, frame-vs-reference exclusivity, frame cardinality) and the\n * resolution tier, both of which are derived from a caller's `prompt` /\n * `size` rather than passed through verbatim.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\n\nimport { isKnownBytePlusVideoModel } from '../model-meta'\nimport type {\n BytePlusVideoModel,\n BytePlusVideoModelOrString,\n BytePlusVideoRatio,\n BytePlusVideoResolution,\n} from '../model-meta'\n\n/**\n * Inference queue for the request.\n *\n * - `default` — online inference: lower RPM and concurrency quotas, lowest\n * latency.\n * - `flex` — offline batch inference: higher daily token quotas at half the\n * price, with no latency guarantee. Task ids come back with a `cgt-batch-`\n * prefix (live-verified).\n *\n * Only the Seedance 1.x models accept this field. The Seedance 2.0 family\n * rejects it (\"service_tier is not supported … must be empty\").\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type BytePlusVideoServiceTier = 'default' | 'flex'\n\n/**\n * Provider-specific options for Seedance video generation. These map one-to-one\n * onto the create-task request body and take precedence over the values the\n * adapter derives from the generic `size` / `duration` options.\n *\n * Fields are model-dependent; each one documents where it applies. Passing a\n * field to a model that does not accept it is a 400 from Ark, not a silent\n * ignore.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport interface BytePlusVideoProviderOptions {\n /**\n * Output aspect ratio. Overrides the ratio half of the generic `size`.\n *\n * `adaptive` (follow the input frame) is the default on Seedance 2.0 and\n * 1.5-pro but is rejected by Seedance 1.0-pro / 1.0-pro-fast for\n * text-to-video.\n */\n ratio?: BytePlusVideoRatio\n\n /**\n * Output resolution tier. Overrides the resolution half of the generic\n * `size`. Matched case-insensitively by the API; this package uses\n * lowercase throughout. `4k` exists only on `dreamina-seedance-2-0-260128`,\n * and there is no 2K tier on any model.\n */\n resolution?: BytePlusVideoResolution\n\n /**\n * Whole seconds of output. Overrides the generic `duration`, and unlike it\n * is sent verbatim rather than snapped into the model's range.\n *\n * `-1` asks the model to choose its own length; accepted by Seedance 2.0\n * and 1.5-pro only.\n */\n duration?: number\n\n /**\n * Frame count instead of `duration`, for fractional-second output. Takes\n * precedence over `duration` server-side. Valid values are the integers of\n * the form `25 + 4n` within `[29, 289]`, at 24 fps.\n *\n * Seedance 1.0-pro and 1.0-pro-fast only.\n */\n frames?: number\n\n /**\n * Randomness seed, an integer in `[-1, 2^32-1]`. `-1` (the default) leaves\n * generation unseeded. Accepted by every Seedance model.\n */\n seed?: number\n\n /**\n * Appends a \"fix the camera\" instruction to the prompt. Best-effort — the\n * model is not constrained to obey it.\n *\n * Seedance 1.5-pro, 1.0-pro and 1.0-pro-fast only; the 2.0 family rejects\n * it.\n */\n camera_fixed?: boolean\n\n /** Burn a watermark into the output. Defaults to `false`. */\n watermark?: boolean\n\n /**\n * Generate an audio track synchronized with the visuals — dialogue, effects\n * and score inferred from the prompt. Quote dialogue in the prompt for\n * better results.\n *\n * Accepted by every model at the API's validation layer, but only Seedance\n * 2.0 and 1.5-pro actually produce audio.\n */\n generate_audio?: boolean\n\n /**\n * Inference queue. Seedance 1.x only — the 2.0 family has no offline tier.\n */\n service_tier?: BytePlusVideoServiceTier\n\n /**\n * Also return the video's final frame as a watermark-free PNG, readable from\n * the finished task as `content.last_frame_url`. Chain it into the next\n * task's first frame to extend a shot. Accepted by every model.\n */\n return_last_frame?: boolean\n\n /**\n * Render a cheap, low-fidelity preview to sanity-check staging and camera\n * work before paying for the real thing.\n *\n * Seedance 1.5-pro only.\n */\n draft?: boolean\n\n /**\n * Queue priority, `[0, 9]`. Seedance 2.0 family only — 1.5-pro rejects it,\n * and the 1.0 models accept it without acting on it.\n */\n priority?: number\n\n /**\n * Seconds after `created_at` at which an unfinished task is abandoned and\n * marked `expired`. Documented range `[3600, 259200]`, default 172800\n * (48 hours). The floor is enforced on Seedance 1.x but not on the 2.0\n * family.\n */\n execution_expires_after?: number\n\n /**\n * URL that receives a POST with the full task payload on every status\n * change. BytePlus retries a failed delivery three times.\n */\n callback_url?: string\n\n /**\n * Stable opaque identifier for the end user driving the request, for abuse\n * attribution. Max 64 characters — hash the real identifier rather than\n * sending it.\n */\n safety_identifier?: string\n}\n\n/**\n * Type-only map from video model name to its provider options. Seedance takes\n * the same option surface across models; applicability is per-field and\n * documented on {@link BytePlusVideoProviderOptions}.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type BytePlusVideoModelProviderOptionsByName = {\n [K in BytePlusVideoModel]: BytePlusVideoProviderOptions\n}\n\n/**\n * Aspect ratios accepted by the create endpoint.\n *\n * `adaptive` is rejected by Seedance 1.0-pro / 1.0-pro-fast for text-to-video\n * but is the documented default for their image-to-video path, so it is not\n * filtered per model here.\n */\nconst BYTEPLUS_VIDEO_RATIOS: ReadonlyArray<string> = [\n '16:9',\n '9:16',\n '4:3',\n '3:4',\n '1:1',\n '21:9',\n 'adaptive',\n]\n\n/**\n * Resolutions each model accepts, live-probed.\n *\n * Two findings here contradict the BytePlus prose docs and are worth calling\n * out: there is no 2K tier on any Seedance model (`2k`/`2K` is rejected\n * everywhere, including on the 2.0 flagship whose docs advertise \"up to 4K\"),\n * and `seedance-1-0-pro-fast-251015` does accept `1080p` despite being\n * documented as 480p/720p only.\n */\nconst BYTEPLUS_VIDEO_RESOLUTIONS: {\n readonly [K in BytePlusVideoModel]: ReadonlyArray<BytePlusVideoResolution>\n} = {\n 'dreamina-seedance-2-0-260128': ['480p', '720p', '1080p', '4k'],\n 'dreamina-seedance-2-0-fast-260128': ['480p', '720p'],\n 'dreamina-seedance-2-0-mini-260615': ['480p', '720p'],\n 'seedance-1-5-pro-251215': ['480p', '720p', '1080p'],\n 'seedance-1-0-pro-250528': ['480p', '720p', '1080p'],\n 'seedance-1-0-pro-fast-251015': ['480p', '720p', '1080p'],\n}\n\n/**\n * Models accepting reference-media mode (`r2v`): reference images, video and\n * audio that the output draws on without pinning specific frames. The 1.x\n * models reject it with \"the specified task_type r2v does not support model …\".\n */\nconst BYTEPLUS_VIDEO_REFERENCE_MEDIA_MODELS: ReadonlySet<string> = new Set([\n 'dreamina-seedance-2-0-260128',\n 'dreamina-seedance-2-0-fast-260128',\n 'dreamina-seedance-2-0-mini-260615',\n])\n\n/**\n * Models accepting a closing frame (`flf2v`, first-and-last-frame mode).\n * `seedance-1-0-pro-fast-251015` is the one Seedance model without it — it\n * does text-to-video and single-first-frame image-to-video only.\n */\nconst BYTEPLUS_VIDEO_LAST_FRAME_MODELS: ReadonlySet<string> = new Set([\n 'dreamina-seedance-2-0-260128',\n 'dreamina-seedance-2-0-fast-260128',\n 'dreamina-seedance-2-0-mini-260615',\n 'seedance-1-5-pro-251215',\n 'seedance-1-0-pro-250528',\n])\n\n/**\n * True when the model is *known* to support reference-media mode (reference\n * images, video and audio). An id this package has no metadata for answers\n * `false`; callers must decide whether that means \"no\" or \"unknown\" — the\n * adapter treats it as unknown and lets Ark rule.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport function supportsReferenceMedia(model: string): boolean {\n return BYTEPLUS_VIDEO_REFERENCE_MEDIA_MODELS.has(model)\n}\n\n/**\n * True when the model is *known* to support pinning the video's closing\n * frame. Same unknown-id caveat as {@link supportsReferenceMedia}.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport function supportsLastFrame(model: string): boolean {\n return BYTEPLUS_VIDEO_LAST_FRAME_MODELS.has(model)\n}\n\n/**\n * Splits a `size` template into its Seedance request fields.\n *\n * The template is either a bare aspect ratio (`'16:9'`) or\n * `ratio_resolution` (`'16:9_720p'`), mirroring the grok video adapter.\n * Returns `undefined` when the string doesn't match the template at all.\n *\n * The resolution half comes back lowercased. Ark itself matches the field\n * case-insensitively, but this package standardizes on lowercase so callers\n * can compare the result against {@link BytePlusVideoResolution} directly.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport function parseBytePlusVideoSize(\n size: string,\n): { ratio: string; resolution?: string } | undefined {\n const match = /^(\\d+:\\d+|adaptive)(?:_(.+))?$/.exec(size)\n const [, ratio, resolution] = match ?? []\n if (ratio === undefined) return undefined\n return {\n ratio,\n ...(resolution !== undefined && { resolution: resolution.toLowerCase() }),\n }\n}\n\n/**\n * Validates a resolution against a model's tiers, returning it lowercased.\n *\n * Used for both halves of the request: the resolution parsed out of the\n * generic `size`, and a `modelOptions.resolution` that overrides it. A model\n * this package has no table for is normalized but not checked — see\n * {@link BytePlusVideoModelOrString}.\n *\n * @throws Error when a known model does not offer the tier.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport function resolveBytePlusVideoResolution(\n model: BytePlusVideoModelOrString,\n resolution: string,\n): string {\n const normalized = resolution.toLowerCase()\n if (!isKnownBytePlusVideoModel(model)) return normalized\n\n const allowed = BYTEPLUS_VIDEO_RESOLUTIONS[model]\n if (!allowed.includes(normalized as BytePlusVideoResolution)) {\n throw new Error(\n `byteplus: resolution \"${resolution}\" is not supported by model ` +\n `\"${model}\". Supported resolutions: ${allowed.join(', ')}.`,\n )\n }\n return normalized\n}\n\n/**\n * Validates a `size` template against a model and returns the request fields\n * it maps onto, with the resolution lowercased.\n *\n * For an unknown model only the template's *shape* is checked — enough to\n * split it into `ratio` and `resolution` — because a future model may bring\n * ratios and tiers that do not exist today. Ark validates the values.\n *\n * @throws Error when the template is malformed, or (known models only) the\n * ratio is unknown or the resolution is not offered by this model.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport function resolveBytePlusVideoSize(\n model: BytePlusVideoModelOrString,\n size: string,\n): { ratio: string; resolution?: string } {\n const parsed = parseBytePlusVideoSize(size)\n const known = isKnownBytePlusVideoModel(model)\n if (!parsed || (known && !BYTEPLUS_VIDEO_RATIOS.includes(parsed.ratio))) {\n throw new Error(\n `byteplus: size \"${size}\" is not supported by model \"${model}\". Expected ` +\n `\"ratio\" or \"ratio_resolution\" (e.g. \"16:9_720p\") with ratio one of: ` +\n `${BYTEPLUS_VIDEO_RATIOS.join(', ')}.`,\n )\n }\n\n return {\n ratio: parsed.ratio,\n ...(parsed.resolution !== undefined && {\n resolution: resolveBytePlusVideoResolution(model, parsed.resolution),\n }),\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqMA,IAAM,wBAA+C;CACnD;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;AAWA,IAAM,6BAEF;CACF,gCAAgC;EAAC;EAAQ;EAAQ;EAAS;CAAI;CAC9D,qCAAqC,CAAC,QAAQ,MAAM;CACpD,qCAAqC,CAAC,QAAQ,MAAM;CACpD,2BAA2B;EAAC;EAAQ;EAAQ;CAAO;CACnD,2BAA2B;EAAC;EAAQ;EAAQ;CAAO;CACnD,gCAAgC;EAAC;EAAQ;EAAQ;CAAO;AAC1D;;;;;;AAOA,IAAM,wDAA6D,IAAI,IAAI;CACzE;CACA;CACA;AACF,CAAC;;;;;;AAOD,IAAM,mDAAwD,IAAI,IAAI;CACpE;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;AAUD,SAAgB,uBAAuB,OAAwB;CAC7D,OAAO,sCAAsC,IAAI,KAAK;AACxD;;;;;;;AAQA,SAAgB,kBAAkB,OAAwB;CACxD,OAAO,iCAAiC,IAAI,KAAK;AACnD;;;;;;;;;;;;;;AAeA,SAAgB,uBACd,MACoD;CAEpD,MAAM,GAAG,OAAO,cADF,iCAAiC,KAAK,IACtB,KAAS,CAAC;CACxC,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,OAAO;EACL;EACA,GAAI,eAAe,KAAA,KAAa,EAAE,YAAY,WAAW,YAAY,EAAE;CACzE;AACF;;;;;;;;;;;;;AAcA,SAAgB,+BACd,OACA,YACQ;CACR,MAAM,aAAa,WAAW,YAAY;CAC1C,IAAI,CAAC,0BAA0B,KAAK,GAAG,OAAO;CAE9C,MAAM,UAAU,2BAA2B;CAC3C,IAAI,CAAC,QAAQ,SAAS,UAAqC,GACzD,MAAM,IAAI,MACR,yBAAyB,WAAW,+BAC9B,MAAM,4BAA4B,QAAQ,KAAK,IAAI,EAAE,EAC7D;CAEF,OAAO;AACT;;;;;;;;;;;;;;AAeA,SAAgB,yBACd,OACA,MACwC;CACxC,MAAM,SAAS,uBAAuB,IAAI;CAC1C,MAAM,QAAQ,0BAA0B,KAAK;CAC7C,IAAI,CAAC,UAAW,SAAS,CAAC,sBAAsB,SAAS,OAAO,KAAK,GACnE,MAAM,IAAI,MACR,mBAAmB,KAAK,+BAA+B,MAAM,kFAExD,sBAAsB,KAAK,IAAI,EAAE,EACxC;CAGF,OAAO;EACL,OAAO,OAAO;EACd,GAAI,OAAO,eAAe,KAAA,KAAa,EACrC,YAAY,+BAA+B,OAAO,OAAO,UAAU,EACrE;CACF;AACF"}