@tanstack/ai-byteplus 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/esm/adapters/video.d.ts +2 -1
- package/dist/esm/adapters/video.js +7 -6
- package/dist/esm/adapters/video.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/model-meta.d.ts +36 -32
- package/dist/esm/model-meta.js +23 -4
- package/dist/esm/model-meta.js.map +1 -1
- package/dist/esm/video/video-provider-options.d.ts +37 -14
- package/dist/esm/video/video-provider-options.js +40 -19
- package/dist/esm/video/video-provider-options.js.map +1 -1
- package/dist/esm/video/wire-types.d.ts +17 -9
- package/package.json +19 -22
- package/src/adapters/video.ts +21 -13
- package/src/index.ts +2 -0
- package/src/model-meta.ts +49 -37
- package/src/video/video-provider-options.ts +74 -31
- package/src/video/wire-types.ts +18 -9
package/README.md
CHANGED
|
@@ -166,7 +166,8 @@ Result URLs expire after 24 hours; pass `response_format: 'b64_json'` in
|
|
|
166
166
|
- **Chat** — `dola-seed-2-1-turbo-260628`, the `seed-2-0-*` family,
|
|
167
167
|
`seed-1-8-251228`, the `seed-1-6-*` family, plus `glm-*`, `deepseek-*` and
|
|
168
168
|
`gpt-oss-120b-250805`.
|
|
169
|
-
- **Video** — `dreamina-seedance-2-
|
|
169
|
+
- **Video** — `dreamina-seedance-2-5-260628`,
|
|
170
|
+
`dreamina-seedance-2-0-260128` (and `-fast-`/`-mini-`),
|
|
170
171
|
`seedance-1-5-pro-251215`, `seedance-1-0-pro-250528`,
|
|
171
172
|
`seedance-1-0-pro-fast-251015`.
|
|
172
173
|
- **Image** — `dola-seedream-5-0-pro-260628`, `seedream-5-0-260128`,
|
|
@@ -29,7 +29,8 @@ export interface BytePlusVideoConfig extends BytePlusArkConfig {
|
|
|
29
29
|
* `seedance-1-0-pro-fast-251015` does not support it at all.
|
|
30
30
|
* - `'reference'` / `'character'` → `reference_image`, video parts →
|
|
31
31
|
* `reference_video`, audio parts → `reference_audio` — subject and style
|
|
32
|
-
* references the model draws on (`r2v`, Seedance 2.0 family
|
|
32
|
+
* references the model draws on (`r2v`, Seedance 2.5 and 2.0 family).
|
|
33
|
+
* Seedance 2.5 also accepts audio-only reference input; 2.0 does not.
|
|
33
34
|
*
|
|
34
35
|
* Frame roles and reference roles cannot be combined in one request, so the
|
|
35
36
|
* adapter rejects a mix up front rather than surfacing a raw 400.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { bytePlusArkError, bytePlusArkHeaders, bytePlusTimeoutSignal, getBytePlusArkApiKeyFromEnv, readJsonBody, toHeaderRecord, withBytePlusArkDefaults } from "../utils/client.js";
|
|
2
2
|
import { getBytePlusVideoDurationOptions, isKnownBytePlusVideoModel } from "../model-meta.js";
|
|
3
|
-
import { resolveBytePlusVideoResolution, resolveBytePlusVideoSize, supportsLastFrame, supportsReferenceMedia } from "../video/video-provider-options.js";
|
|
3
|
+
import { resolveBytePlusVideoResolution, resolveBytePlusVideoSize, supportsAudioOnlyReference, supportsLastFrame, supportsReferenceMedia } from "../video/video-provider-options.js";
|
|
4
4
|
import { resolveMediaPrompt } from "@tanstack/ai";
|
|
5
5
|
import { BaseVideoAdapter, snapToDurationOption } from "@tanstack/ai/adapters";
|
|
6
6
|
import { toRunErrorPayload } from "@tanstack/ai/adapter-internals";
|
|
@@ -87,7 +87,8 @@ function describeTaskFailure(task) {
|
|
|
87
87
|
* `seedance-1-0-pro-fast-251015` does not support it at all.
|
|
88
88
|
* - `'reference'` / `'character'` → `reference_image`, video parts →
|
|
89
89
|
* `reference_video`, audio parts → `reference_audio` — subject and style
|
|
90
|
-
* references the model draws on (`r2v`, Seedance 2.0 family
|
|
90
|
+
* references the model draws on (`r2v`, Seedance 2.5 and 2.0 family).
|
|
91
|
+
* Seedance 2.5 also accepts audio-only reference input; 2.0 does not.
|
|
91
92
|
*
|
|
92
93
|
* Frame roles and reference roles cannot be combined in one request, so the
|
|
93
94
|
* adapter rejects a mix up front rather than surfacing a raw 400.
|
|
@@ -160,7 +161,7 @@ var BytePlusVideoAdapter = class extends BaseVideoAdapter {
|
|
|
160
161
|
break;
|
|
161
162
|
case "reference":
|
|
162
163
|
case "character":
|
|
163
|
-
if (gated && !supportsReferenceMedia(model)) throw new Error(`byteplus: ${model} does not support reference images. Reference media is available on
|
|
164
|
+
if (gated && !supportsReferenceMedia(model)) throw new Error(`byteplus: ${model} does not support reference images. Reference media is available on Seedance 2.5 and the 2.0 family; on this model use 'start_frame' / 'end_frame' images instead.`);
|
|
164
165
|
visualReferences++;
|
|
165
166
|
content.push({
|
|
166
167
|
type: "image_url",
|
|
@@ -180,7 +181,7 @@ var BytePlusVideoAdapter = class extends BaseVideoAdapter {
|
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
for (const part of resolved.videos) {
|
|
183
|
-
if (gated && !supportsReferenceMedia(model)) throw new Error(`byteplus: ${model} does not accept video prompt parts. Reference video is available on
|
|
184
|
+
if (gated && !supportsReferenceMedia(model)) throw new Error(`byteplus: ${model} does not accept video prompt parts. Reference video is available on Seedance 2.5 and the 2.0 family only.`);
|
|
184
185
|
visualReferences++;
|
|
185
186
|
content.push({
|
|
186
187
|
type: "video_url",
|
|
@@ -189,7 +190,7 @@ var BytePlusVideoAdapter = class extends BaseVideoAdapter {
|
|
|
189
190
|
});
|
|
190
191
|
}
|
|
191
192
|
for (const part of resolved.audios) {
|
|
192
|
-
if (gated && !supportsReferenceMedia(model)) throw new Error(`byteplus: ${model} does not accept audio prompt parts. Reference audio is available on
|
|
193
|
+
if (gated && !supportsReferenceMedia(model)) throw new Error(`byteplus: ${model} does not accept audio prompt parts. Reference audio is available on Seedance 2.5 and the 2.0 family only.`);
|
|
193
194
|
audioReferences++;
|
|
194
195
|
content.push({
|
|
195
196
|
type: "audio_url",
|
|
@@ -202,7 +203,7 @@ var BytePlusVideoAdapter = class extends BaseVideoAdapter {
|
|
|
202
203
|
if (gated && firstFrames > 1) throw new Error(`byteplus: ${model} accepts at most one opening frame; received ${firstFrames} un-roled or 'start_frame' images. Use metadata.role ('end_frame', 'reference') to disambiguate the others.`);
|
|
203
204
|
if (gated && lastFrames > 1) throw new Error(`byteplus: ${model} accepts at most one closing frame; received ${lastFrames} 'end_frame' images.`);
|
|
204
205
|
if (gated && lastFrames > 0 && firstFrames === 0) throw new Error(`byteplus: a closing frame needs an opening frame alongside it on model ${model}. Add a 'start_frame' image, or drop the 'end_frame' role.`);
|
|
205
|
-
if (gated && audioReferences > 0 && visualReferences === 0) throw new Error(`byteplus: a reference audio input cannot be the only reference on model ${model}. Pair it with a reference image or video.`);
|
|
206
|
+
if (gated && audioReferences > 0 && visualReferences === 0 && !supportsAudioOnlyReference(model)) throw new Error(`byteplus: a reference audio input cannot be the only reference on model ${model}. Pair it with a reference image or video, or use Seedance 2.5 which accepts audio-only reference input.`);
|
|
206
207
|
if (content.length === 0) throw new Error(`byteplus: a video prompt must carry text or at least one media input (model: ${model}).`);
|
|
207
208
|
return content;
|
|
208
209
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video.js","names":[],"sources":["../../../src/adapters/video.ts"],"sourcesContent":["import { resolveMediaPrompt } from '@tanstack/ai'\nimport { BaseVideoAdapter, snapToDurationOption } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport {\n bytePlusArkError,\n bytePlusArkHeaders,\n bytePlusTimeoutSignal,\n getBytePlusArkApiKeyFromEnv,\n readJsonBody,\n toHeaderRecord,\n withBytePlusArkDefaults,\n} from '../utils/client'\nimport {\n getBytePlusVideoDurationOptions,\n isKnownBytePlusVideoModel,\n} from '../model-meta'\nimport {\n resolveBytePlusVideoResolution,\n resolveBytePlusVideoSize,\n supportsLastFrame,\n supportsReferenceMedia,\n} from '../video/video-provider-options'\nimport type { DurationOptions } from '@tanstack/ai/adapters'\nimport type {\n AudioPart,\n ImagePart,\n MediaInputMetadata,\n TokenUsage,\n VideoGenerationOptions,\n VideoJobResult,\n VideoPart,\n VideoStatusResult,\n VideoUrlResult,\n} from '@tanstack/ai'\nimport type {\n BytePlusVideoContentPart,\n BytePlusVideoCreateRequest,\n BytePlusVideoCreateResponse,\n BytePlusVideoTask,\n BytePlusVideoTaskStatus,\n BytePlusVideoTaskUsage,\n} from '../video/wire-types'\nimport type { BytePlusVideoProviderOptions } from '../video/video-provider-options'\nimport type {\n BytePlusVideoModelOrString,\n ResolveBytePlusVideoInputModalities,\n ResolveBytePlusVideoSize,\n} from '../model-meta'\nimport type { BytePlusArkConfig } from '../utils/client'\n\n/**\n * Configuration for the BytePlus Seedance video adapter.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport interface BytePlusVideoConfig extends BytePlusArkConfig {}\n\n/** Path of the Seedance task API, relative to the Ark base URL. */\nconst TASKS_PATH = '/contents/generations/tasks'\n\n/**\n * `content.video_url` and `content.last_frame_url` are deleted 24 hours after\n * the task produces them.\n */\nconst VIDEO_URL_TTL_MS = 24 * 60 * 60 * 1000\n\n/**\n * Converts a media prompt part into the URL string Seedance's `content[]`\n * takes: public URLs pass through (BytePlus fetches them server-side), data\n * sources become base64 data URIs.\n */\nfunction mediaPartToUrl(\n part:\n | ImagePart<MediaInputMetadata>\n | VideoPart<MediaInputMetadata>\n | AudioPart<MediaInputMetadata>,\n): string {\n const { source } = part\n if (source.type === 'url') return source.value\n if (source.value.startsWith('data:')) return source.value\n return `data:${source.mimeType.toLowerCase()};base64,${source.value}`\n}\n\n/** Coerces a usage count that the API types as a string but sends as a number. */\nfunction toTokenCount(value: number | string | undefined): number | undefined {\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n }\n return undefined\n}\n\n/**\n * Maps a finished task's usage onto `TokenUsage`.\n *\n * Seedance bills output only — the API documents input tokens as always 0 and\n * `total_tokens` as equal to `completion_tokens` — so `promptTokens` is 0 and\n * the completion count doubles as `unitsBilled`.\n */\nfunction buildBytePlusVideoUsage(\n usage: BytePlusVideoTaskUsage | undefined,\n): TokenUsage | undefined {\n if (!usage) return undefined\n\n const completionTokens = toTokenCount(usage.completion_tokens)\n const totalTokens = toTokenCount(usage.total_tokens)\n if (completionTokens === undefined && totalTokens === undefined) {\n return undefined\n }\n\n const completion = completionTokens ?? totalTokens ?? 0\n return {\n promptTokens: 0,\n completionTokens: completion,\n totalTokens: totalTokens ?? completion,\n unitsBilled: completion,\n }\n}\n\n/**\n * Formats a terminal task's error detail for a status / failure message.\n *\n * Always returns a string. Core surfaces a failed job as\n * `throw new Error(statusResult.error || 'Video generation failed')`, so\n * returning `undefined` for a failure Ark reported without an `error` block\n * would hand the caller an unattributable error. The final fallback is a\n * snapshot of the identifying fields instead.\n */\nfunction describeTaskFailure(task: BytePlusVideoTask): string {\n const { code, message } = task.error ?? {}\n if (code && message) return `${code}: ${message}`\n if (message) return message\n if (code) return code\n // `expired` and `cancelled` are terminal without an `error` block.\n if (task.status === 'expired') {\n return 'Task expired before it finished (execution_expires_after elapsed).'\n }\n if (task.status === 'cancelled') return 'Task was cancelled.'\n return (\n `Task reported status \"${task.status ?? 'unknown'}\" with no error detail ` +\n `(id=${task.id ?? 'unknown'}, model=${task.model ?? 'unknown'}).`\n )\n}\n\n/**\n * BytePlus Seedance video generation adapter.\n *\n * Drives Ark's asynchronous task API — `POST /contents/generations/tasks` to\n * submit, `GET /contents/generations/tasks/{id}` to poll and to read the\n * finished video URL. Core owns the polling loop; this adapter implements the\n * three primitives plus the duration metadata.\n *\n * Prompt parts map onto Seedance's `content[]` roles, which the API sorts into\n * mutually exclusive task types:\n *\n * - `'start_frame'` (or a single un-roled image) → `first_frame` — the frame\n * the video opens on (`i2v`).\n * - `'end_frame'` → `last_frame` — the frame it closes on (`flf2v`); Seedance\n * requires a `first_frame` alongside it, and\n * `seedance-1-0-pro-fast-251015` does not support it at all.\n * - `'reference'` / `'character'` → `reference_image`, video parts →\n * `reference_video`, audio parts → `reference_audio` — subject and style\n * references the model draws on (`r2v`, Seedance 2.0 family only).\n *\n * Frame roles and reference roles cannot be combined in one request, so the\n * adapter rejects a mix up front rather than surfacing a raw 400.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @example\n * ```typescript\n * const adapter = byteplusVideo('seedance-1-0-pro-fast-251015')\n *\n * const { jobId } = await generateVideo({\n * adapter,\n * prompt: 'a guitar being played in a store',\n * size: '16:9_720p',\n * duration: 4,\n * modelOptions: { service_tier: 'flex' },\n * })\n * ```\n */\nexport class BytePlusVideoAdapter<\n TModel extends BytePlusVideoModelOrString,\n> extends BaseVideoAdapter<\n TModel,\n BytePlusVideoProviderOptions,\n Record<TModel, BytePlusVideoProviderOptions>,\n Record<TModel, ResolveBytePlusVideoSize<TModel>>,\n Record<TModel, ResolveBytePlusVideoInputModalities<TModel>>,\n Record<TModel, number>\n> {\n readonly name = 'byteplus' as const\n\n /** Config with the Ark base URL resolved and its trailing slashes trimmed. */\n private readonly clientConfig: Omit<BytePlusVideoConfig, 'baseURL'> & {\n baseURL: string\n }\n\n constructor(config: BytePlusVideoConfig, model: TModel) {\n super({}, model)\n this.clientConfig = withBytePlusArkDefaults(config)\n }\n\n private async request(\n path: string,\n init?: Omit<RequestInit, 'headers'>,\n ): Promise<{ response: Response; body: unknown }> {\n const fetchImpl = this.clientConfig.fetch ?? fetch\n const signal = bytePlusTimeoutSignal(this.clientConfig.timeout)\n const response = await fetchImpl(`${this.clientConfig.baseURL}${path}`, {\n ...init,\n ...(signal && { signal }),\n headers: bytePlusArkHeaders(\n this.clientConfig.apiKey,\n toHeaderRecord(this.clientConfig.defaultHeaders),\n ),\n })\n return { response, body: await readJsonBody(response) }\n }\n\n /**\n * Builds the `content[]` array from the resolved prompt, enforcing\n * Seedance's role vocabulary and mode exclusivity.\n */\n private buildContent(\n resolved: ReturnType<typeof resolveMediaPrompt>,\n ): Array<BytePlusVideoContentPart> {\n const model = this.model\n const content: Array<BytePlusVideoContentPart> = []\n if (resolved.text) content.push({ type: 'text', text: resolved.text })\n\n // Every rule below except the role vocabulary itself is a claim about a\n // *specific* model's capabilities, drawn from probing the six models that\n // exist today. None of it can be true of a model that does not exist yet,\n // so for an unknown id the guards stand down and Ark rules — otherwise the\n // escape hatch would block exactly the requests it exists to enable (see\n // BytePlusVideoModelOrString). 'mask' / 'control' still throw: Seedance's\n // wire format has no field to carry them on any model.\n const gated = isKnownBytePlusVideoModel(model)\n\n let firstFrames = 0\n let lastFrames = 0\n // Audio counts as a reference for the mode-exclusivity check but not for\n // the \"audio can't be the only reference\" rule, which wants a visual.\n let visualReferences = 0\n let audioReferences = 0\n\n for (const part of resolved.images) {\n const role = part.metadata?.role\n switch (role) {\n case 'mask':\n case 'control':\n throw new Error(\n `byteplus: Seedance has no '${role}' image input on model ${model}. ` +\n `Use 'start_frame', 'end_frame' or 'reference'.`,\n )\n case 'end_frame': {\n if (gated && !supportsLastFrame(model)) {\n throw new Error(\n `byteplus: ${model} does not support a closing frame — it does ` +\n `text-to-video and first-frame image-to-video only. Drop the ` +\n `'end_frame' image or switch to a model with first-and-last-frame support.`,\n )\n }\n lastFrames++\n content.push({\n type: 'image_url',\n image_url: { url: mediaPartToUrl(part) },\n role: 'last_frame',\n })\n break\n }\n case 'reference':\n case 'character': {\n if (gated && !supportsReferenceMedia(model)) {\n throw new Error(\n `byteplus: ${model} does not support reference images. Reference ` +\n `media is available on the Seedance 2.0 family; on this model use ` +\n `'start_frame' / 'end_frame' images instead.`,\n )\n }\n visualReferences++\n content.push({\n type: 'image_url',\n image_url: { url: mediaPartToUrl(part) },\n role: 'reference_image',\n })\n break\n }\n // An un-roled image is the opening frame, matching the API's own\n // default and the fal / Veo adapters' positional convention.\n case 'start_frame':\n case undefined: {\n firstFrames++\n content.push({\n type: 'image_url',\n image_url: { url: mediaPartToUrl(part) },\n role: 'first_frame',\n })\n break\n }\n }\n }\n\n // Video and audio parts only exist in reference mode: Seedance rejects an\n // un-roled video with \"reference media mode requires video role to be\n // reference_video\", and has no frame-style role for either modality.\n for (const part of resolved.videos) {\n if (gated && !supportsReferenceMedia(model)) {\n throw new Error(\n `byteplus: ${model} does not accept video prompt parts. Reference ` +\n `video is available on the Seedance 2.0 family only.`,\n )\n }\n visualReferences++\n content.push({\n type: 'video_url',\n video_url: { url: mediaPartToUrl(part) },\n role: 'reference_video',\n })\n }\n\n for (const part of resolved.audios) {\n if (gated && !supportsReferenceMedia(model)) {\n throw new Error(\n `byteplus: ${model} does not accept audio prompt parts. Reference ` +\n `audio is available on the Seedance 2.0 family only.`,\n )\n }\n audioReferences++\n content.push({\n type: 'audio_url',\n audio_url: { url: mediaPartToUrl(part) },\n role: 'reference_audio',\n })\n }\n\n const frames = firstFrames + lastFrames\n if (gated && frames > 0 && visualReferences + audioReferences > 0) {\n throw new Error(\n `byteplus: first/last frame inputs cannot be combined with reference ` +\n `media on model ${model}. Use either frame roles ('start_frame', ` +\n `'end_frame') or reference roles ('reference', 'character', video, ` +\n `audio) — not both.`,\n )\n }\n\n if (gated && firstFrames > 1) {\n throw new Error(\n `byteplus: ${model} accepts at most one opening frame; received ` +\n `${firstFrames} un-roled or 'start_frame' images. Use metadata.role ` +\n `('end_frame', 'reference') to disambiguate the others.`,\n )\n }\n\n if (gated && lastFrames > 1) {\n throw new Error(\n `byteplus: ${model} accepts at most one closing frame; received ` +\n `${lastFrames} 'end_frame' images.`,\n )\n }\n\n // Seedance treats a closing frame as the second half of first-and-last-\n // frame mode: on its own it fails with \"last frame image content cannot be\n // mixed with first frame or reference image content\".\n if (gated && lastFrames > 0 && firstFrames === 0) {\n throw new Error(\n `byteplus: a closing frame needs an opening frame alongside it on ` +\n `model ${model}. Add a 'start_frame' image, or drop the 'end_frame' role.`,\n )\n }\n\n if (gated && audioReferences > 0 && visualReferences === 0) {\n throw new Error(\n `byteplus: a reference audio input cannot be the only reference on ` +\n `model ${model}. Pair it with a reference image or video.`,\n )\n }\n\n if (content.length === 0) {\n throw new Error(\n `byteplus: a video prompt must carry text or at least one media input ` +\n `(model: ${model}).`,\n )\n }\n\n return content\n }\n\n async createVideoJob(\n options: VideoGenerationOptions<\n BytePlusVideoProviderOptions,\n ResolveBytePlusVideoSize<TModel>,\n number\n >,\n ): Promise<VideoJobResult> {\n const { size, modelOptions, logger } = options\n const model = this.model\n\n const content = this.buildContent(resolveMediaPrompt(options.prompt))\n\n // The generic `size` carries a \"ratio_resolution\" template and splits back\n // into Seedance's separate fields. Explicit modelOptions win below.\n const parsedSize =\n size !== undefined ? resolveBytePlusVideoSize(model, size) : undefined\n\n // Coerce the requested duration into the model's range rather than letting\n // the API reject it. `modelOptions.duration` is deliberately not snapped:\n // it is the escape hatch for `-1` (model picks the length).\n //\n // An unknown model's duration goes through verbatim. Snapping it would\n // mean clamping against the ranges today's models happen to have, so a\n // future model's legitimate 20-second request would silently become 15 —\n // corrupting the request instead of protecting it.\n const duration =\n options.duration !== undefined\n ? isKnownBytePlusVideoModel(model)\n ? this.snapDuration(options.duration)\n : options.duration\n : undefined\n\n const request: BytePlusVideoCreateRequest = {\n ...(parsedSize && {\n ratio: parsedSize.ratio,\n ...(parsedSize.resolution !== undefined && {\n resolution: parsedSize.resolution,\n }),\n }),\n ...(duration !== undefined && { duration }),\n // Explicit provider options win over everything derived above.\n ...modelOptions,\n model,\n content,\n }\n\n // Validate what actually ships, not just what `size` contributed: a\n // `modelOptions.resolution` overriding an already-checked size would\n // otherwise reach Ark unchecked.\n if (request.resolution !== undefined) {\n request.resolution = resolveBytePlusVideoResolution(\n model,\n request.resolution,\n )\n }\n\n try {\n logger.request(\n `activity=video.create provider=${this.name} model=${model} size=${size ?? 'default'} duration=${request.duration ?? 'default'}`,\n { provider: this.name, model },\n )\n\n const { response, body } = await this.request(TASKS_PATH, {\n method: 'POST',\n body: JSON.stringify(request),\n })\n if (!response.ok) {\n throw bytePlusArkError(response.status, body, 'video task creation')\n }\n\n const { id } = (body ?? {}) as BytePlusVideoCreateResponse\n if (!id) {\n throw new Error('byteplus: video task creation returned no task id.')\n }\n\n return { jobId: id, model }\n } catch (error: unknown) {\n logger.errors(`${this.name}.createVideoJob fatal`, {\n error: toRunErrorPayload(error, `${this.name}.createVideoJob failed`),\n source: `${this.name}.createVideoJob`,\n })\n throw error\n }\n }\n\n /**\n * Fetches a task, tagging the thrown error with the HTTP status.\n *\n * The 200 body is validated rather than cast. `readJsonBody` returns\n * `undefined` for an empty body and the raw text for a non-JSON one — both\n * documented failure modes of these hosts (an HTML error page from a proxy\n * in front of the API). Casting either to `BytePlusVideoTask` yields a task\n * whose `status` is `undefined`, which {@link mapStatus} would have to\n * interpret; the honest answer is that the response was not a task at all,\n * so say so while the body is still in hand.\n */\n private async retrieveTask(jobId: string): Promise<BytePlusVideoTask> {\n const { response, body } = await this.request(\n `${TASKS_PATH}/${encodeURIComponent(jobId)}`,\n )\n if (!response.ok) {\n const error = bytePlusArkError(response.status, body, 'video task lookup')\n ;(error as { status?: number }).status = response.status\n throw error\n }\n if (typeof body !== 'object' || body === null) {\n throw bytePlusArkError(\n response.status,\n body,\n `video task lookup (job ${jobId}) returned a non-object body`,\n )\n }\n return body as BytePlusVideoTask\n }\n\n async getVideoStatus(jobId: string): Promise<VideoStatusResult> {\n let task: BytePlusVideoTask\n try {\n task = await this.retrieveTask(jobId)\n } catch (error) {\n // A task record lives 7 days from creation; past that the id 404s. Keep\n // Ark's own code/message: a 404 from a wrong baseURL, a proxy, or a\n // region mismatch is not an expired job id, and collapsing them all to\n // \"Job not found\" sends the caller hunting the wrong thing.\n if ((error as { status?: number }).status === 404) {\n return {\n jobId,\n status: 'failed',\n error: `Job not found: ${jobId} (${(error as Error).message})`,\n }\n }\n throw error\n }\n\n const status = this.mapStatus(task.status)\n const failure = status === 'failed' ? describeTaskFailure(task) : undefined\n return {\n jobId,\n status,\n ...(failure !== undefined && { error: failure }),\n }\n }\n\n async getVideoUrl(jobId: string): Promise<VideoUrlResult> {\n let task: BytePlusVideoTask\n try {\n task = await this.retrieveTask(jobId)\n } catch (error) {\n // See getVideoStatus: Ark's detail distinguishes an expired id from a\n // misrouted request.\n if ((error as { status?: number }).status === 404) {\n throw new Error(\n `Video job not found: ${jobId} (${(error as Error).message})`,\n )\n }\n throw error\n }\n\n const status = this.mapStatus(task.status)\n if (status === 'failed') {\n throw new Error(\n `Video generation failed: ${describeTaskFailure(task)}. Job ID: ${jobId}`,\n )\n }\n\n const url = task.content?.video_url\n if (!url) {\n throw new Error(\n `Video is not ready for download. Check status first. Job ID: ${jobId}`,\n )\n }\n\n // The 24-hour window runs from when the output was produced, which is the\n // last status change on a succeeded task — `created_at` anchors the\n // separate 7-day retention of the task record itself, and can be far\n // earlier (a live `flex` task sat queued ~15 minutes). Corroborated by the\n // signed TOS link itself, which carries `X-Tos-Expires=86400` from an\n // `X-Tos-Date` matching `updated_at`. Fall back to `created_at` only when\n // `updated_at` is missing.\n const anchorSeconds = task.updated_at ?? task.created_at\n const expiresAt =\n anchorSeconds !== undefined\n ? new Date(anchorSeconds * 1000 + VIDEO_URL_TTL_MS)\n : undefined\n\n const usage = buildBytePlusVideoUsage(task.usage)\n return {\n jobId,\n url,\n ...(expiresAt && { expiresAt }),\n ...(usage && { usage }),\n }\n }\n\n /**\n * Maps Seedance task states onto the generic video status set. `expired`\n * (the task outlived `execution_expires_after`) and `cancelled` are\n * terminal non-successes, so both report as failed.\n *\n * An unrecognized state throws rather than defaulting to `processing`.\n * Core's poll loop treats `processing` as \"keep waiting\", so mapping an\n * unknown state — a missing `status`, or a terminal one Ark adds later such\n * as `rejected` — onto it means polling until `maxDuration` and then\n * reporting a generic timeout, with the state Ark actually sent never\n * reaching the caller. Failing here names it.\n */\n protected mapStatus(\n apiStatus: BytePlusVideoTaskStatus | string | undefined,\n ): VideoStatusResult['status'] {\n switch (apiStatus) {\n case 'queued':\n return 'pending'\n case 'running':\n return 'processing'\n case 'succeeded':\n return 'completed'\n case 'failed':\n case 'expired':\n case 'cancelled':\n return 'failed'\n case undefined:\n default:\n throw new Error(\n `byteplus: unrecognized Seedance task status ` +\n `${apiStatus === undefined ? '(missing)' : `\"${apiStatus}\"`}. ` +\n `Known states: queued, running, succeeded, failed, expired, cancelled.`,\n )\n }\n }\n\n /**\n * Seedance accepts any whole second inside a per-model range: 4–15s on the\n * 2.0 family, 4–12s on 1.5-pro, 2–12s on the 1.0 models. An unknown model\n * reports the union of those ranges as a UI hint — see\n * `BYTEPLUS_VIDEO_FALLBACK_DURATIONS`, which `createVideoJob` does not snap\n * against.\n */\n override availableDurations(): DurationOptions<number> {\n return getBytePlusVideoDurationOptions(this.model)\n }\n\n /**\n * Coerce a raw seconds value to the closest duration this model accepts\n * (clamped to its range and rounded to whole seconds).\n */\n override snapDuration(seconds: number): number | undefined {\n return snapToDurationOption(seconds, this.availableDurations())\n }\n}\n\n/**\n * Creates a BytePlus Seedance video adapter with an explicit API key.\n * Type resolution happens here at the call site.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @param model - The model name (e.g., 'seedance-1-0-pro-fast-251015')\n * @param apiKey - Your BytePlus Ark API key\n * @param config - Optional additional configuration\n * @returns Configured BytePlus video adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createBytePlusVideo('seedance-1-5-pro-251215', 'ark-...')\n *\n * const { jobId } = await generateVideo({\n * adapter,\n * prompt: 'a guitar being played in a store',\n * size: '16:9_1080p',\n * duration: 5,\n * })\n * ```\n */\nexport function createBytePlusVideo<TModel extends BytePlusVideoModelOrString>(\n model: TModel,\n apiKey: string,\n config?: Omit<BytePlusVideoConfig, 'apiKey'>,\n): BytePlusVideoAdapter<TModel> {\n return new BytePlusVideoAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a BytePlus Seedance video adapter, reading `ARK_API_KEY` from the\n * environment. Type resolution happens here at the call site.\n *\n * Note that Ark keys are region-isolated and Seedance is only served from the\n * Asia-Pacific endpoint — an EU key will not work here.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @param model - The model name (e.g., 'dreamina-seedance-2-0-260128')\n * @param config - Optional configuration (excluding apiKey, auto-detected)\n * @returns Configured BytePlus video adapter instance with resolved types\n * @throws Error if ARK_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * const adapter = byteplusVideo('dreamina-seedance-2-0-260128')\n *\n * // Image-to-video: an un-roled image is the opening frame.\n * const { jobId } = await generateVideo({\n * adapter,\n * prompt: [\n * { type: 'text', content: 'the guitarist starts playing' },\n * { type: 'image', source: { type: 'url', value: 'https://example.com/shop.jpg' } },\n * ],\n * })\n *\n * const status = await getVideoJobStatus({ adapter, jobId })\n * ```\n *\n * ## Models this package does not know yet\n *\n * `model` also accepts any string, so a Seedance id BytePlus publishes after\n * this release works without upgrading. **Seedance 2.5 is the case this exists\n * for**: `dreamina-seedance-2-5-260628` is real and reachable, but its\n * capability cells could not be probed from this repo's account (Ark answers\n * 404 `ModelNotOpen` until the model is activated in the Ark Console), so it\n * is deliberately absent from the narrowed model tables. Passing it here works\n * for an account that has activated it.\n *\n * An unknown id relaxes both halves of the adapter: the `size` type widens to\n * any string, provider options are ungated, and the runtime guards that encode\n * per-model capabilities — resolution tiers, closing-frame and reference-media\n * support, frame cardinality and mode exclusivity, duration snapping — stand\n * down so Ark decides. Known ids are unaffected. See\n * {@link BytePlusVideoModelOrString} for how to discover and probe an id.\n *\n * @example\n * ```typescript\n * // Seedance 2.5, before this package ships probe-verified metadata for it:\n * const adapter = byteplusVideo('dreamina-seedance-2-5-260628')\n * ```\n */\nexport function byteplusVideo<TModel extends BytePlusVideoModelOrString>(\n model: TModel,\n config?: Omit<BytePlusVideoConfig, 'apiKey'>,\n): BytePlusVideoAdapter<TModel> {\n const apiKey = getBytePlusArkApiKeyFromEnv()\n return createBytePlusVideo(model, apiKey, config)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"video.js","names":[],"sources":["../../../src/adapters/video.ts"],"sourcesContent":["import { resolveMediaPrompt } from '@tanstack/ai'\nimport { BaseVideoAdapter, snapToDurationOption } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport {\n bytePlusArkError,\n bytePlusArkHeaders,\n bytePlusTimeoutSignal,\n getBytePlusArkApiKeyFromEnv,\n readJsonBody,\n toHeaderRecord,\n withBytePlusArkDefaults,\n} from '../utils/client'\nimport {\n getBytePlusVideoDurationOptions,\n isKnownBytePlusVideoModel,\n} from '../model-meta'\nimport {\n resolveBytePlusVideoResolution,\n resolveBytePlusVideoSize,\n supportsAudioOnlyReference,\n supportsLastFrame,\n supportsReferenceMedia,\n} from '../video/video-provider-options'\nimport type { DurationOptions } from '@tanstack/ai/adapters'\nimport type {\n AudioPart,\n ImagePart,\n MediaInputMetadata,\n TokenUsage,\n VideoGenerationOptions,\n VideoJobResult,\n VideoPart,\n VideoStatusResult,\n VideoUrlResult,\n} from '@tanstack/ai'\nimport type {\n BytePlusVideoContentPart,\n BytePlusVideoCreateRequest,\n BytePlusVideoCreateResponse,\n BytePlusVideoTask,\n BytePlusVideoTaskStatus,\n BytePlusVideoTaskUsage,\n} from '../video/wire-types'\nimport type { BytePlusVideoProviderOptions } from '../video/video-provider-options'\nimport type {\n BytePlusVideoModelOrString,\n ResolveBytePlusVideoInputModalities,\n ResolveBytePlusVideoSize,\n} from '../model-meta'\nimport type { BytePlusArkConfig } from '../utils/client'\n\n/**\n * Configuration for the BytePlus Seedance video adapter.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport interface BytePlusVideoConfig extends BytePlusArkConfig {}\n\n/** Path of the Seedance task API, relative to the Ark base URL. */\nconst TASKS_PATH = '/contents/generations/tasks'\n\n/**\n * `content.video_url` and `content.last_frame_url` are deleted 24 hours after\n * the task produces them.\n */\nconst VIDEO_URL_TTL_MS = 24 * 60 * 60 * 1000\n\n/**\n * Converts a media prompt part into the URL string Seedance's `content[]`\n * takes: public URLs pass through (BytePlus fetches them server-side), data\n * sources become base64 data URIs.\n */\nfunction mediaPartToUrl(\n part:\n | ImagePart<MediaInputMetadata>\n | VideoPart<MediaInputMetadata>\n | AudioPart<MediaInputMetadata>,\n): string {\n const { source } = part\n if (source.type === 'url') return source.value\n if (source.value.startsWith('data:')) return source.value\n return `data:${source.mimeType.toLowerCase()};base64,${source.value}`\n}\n\n/** Coerces a usage count that the API types as a string but sends as a number. */\nfunction toTokenCount(value: number | string | undefined): number | undefined {\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n }\n return undefined\n}\n\n/**\n * Maps a finished task's usage onto `TokenUsage`.\n *\n * Seedance bills output only — the API documents input tokens as always 0 and\n * `total_tokens` as equal to `completion_tokens` — so `promptTokens` is 0 and\n * the completion count doubles as `unitsBilled`.\n */\nfunction buildBytePlusVideoUsage(\n usage: BytePlusVideoTaskUsage | undefined,\n): TokenUsage | undefined {\n if (!usage) return undefined\n\n const completionTokens = toTokenCount(usage.completion_tokens)\n const totalTokens = toTokenCount(usage.total_tokens)\n if (completionTokens === undefined && totalTokens === undefined) {\n return undefined\n }\n\n const completion = completionTokens ?? totalTokens ?? 0\n return {\n promptTokens: 0,\n completionTokens: completion,\n totalTokens: totalTokens ?? completion,\n unitsBilled: completion,\n }\n}\n\n/**\n * Formats a terminal task's error detail for a status / failure message.\n *\n * Always returns a string. Core surfaces a failed job as\n * `throw new Error(statusResult.error || 'Video generation failed')`, so\n * returning `undefined` for a failure Ark reported without an `error` block\n * would hand the caller an unattributable error. The final fallback is a\n * snapshot of the identifying fields instead.\n */\nfunction describeTaskFailure(task: BytePlusVideoTask): string {\n const { code, message } = task.error ?? {}\n if (code && message) return `${code}: ${message}`\n if (message) return message\n if (code) return code\n // `expired` and `cancelled` are terminal without an `error` block.\n if (task.status === 'expired') {\n return 'Task expired before it finished (execution_expires_after elapsed).'\n }\n if (task.status === 'cancelled') return 'Task was cancelled.'\n return (\n `Task reported status \"${task.status ?? 'unknown'}\" with no error detail ` +\n `(id=${task.id ?? 'unknown'}, model=${task.model ?? 'unknown'}).`\n )\n}\n\n/**\n * BytePlus Seedance video generation adapter.\n *\n * Drives Ark's asynchronous task API — `POST /contents/generations/tasks` to\n * submit, `GET /contents/generations/tasks/{id}` to poll and to read the\n * finished video URL. Core owns the polling loop; this adapter implements the\n * three primitives plus the duration metadata.\n *\n * Prompt parts map onto Seedance's `content[]` roles, which the API sorts into\n * mutually exclusive task types:\n *\n * - `'start_frame'` (or a single un-roled image) → `first_frame` — the frame\n * the video opens on (`i2v`).\n * - `'end_frame'` → `last_frame` — the frame it closes on (`flf2v`); Seedance\n * requires a `first_frame` alongside it, and\n * `seedance-1-0-pro-fast-251015` does not support it at all.\n * - `'reference'` / `'character'` → `reference_image`, video parts →\n * `reference_video`, audio parts → `reference_audio` — subject and style\n * references the model draws on (`r2v`, Seedance 2.5 and 2.0 family).\n * Seedance 2.5 also accepts audio-only reference input; 2.0 does not.\n *\n * Frame roles and reference roles cannot be combined in one request, so the\n * adapter rejects a mix up front rather than surfacing a raw 400.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @example\n * ```typescript\n * const adapter = byteplusVideo('seedance-1-0-pro-fast-251015')\n *\n * const { jobId } = await generateVideo({\n * adapter,\n * prompt: 'a guitar being played in a store',\n * size: '16:9_720p',\n * duration: 4,\n * modelOptions: { service_tier: 'flex' },\n * })\n * ```\n */\nexport class BytePlusVideoAdapter<\n TModel extends BytePlusVideoModelOrString,\n> extends BaseVideoAdapter<\n TModel,\n BytePlusVideoProviderOptions,\n Record<TModel, BytePlusVideoProviderOptions>,\n Record<TModel, ResolveBytePlusVideoSize<TModel>>,\n Record<TModel, ResolveBytePlusVideoInputModalities<TModel>>,\n Record<TModel, number>\n> {\n readonly name = 'byteplus' as const\n\n /** Config with the Ark base URL resolved and its trailing slashes trimmed. */\n private readonly clientConfig: Omit<BytePlusVideoConfig, 'baseURL'> & {\n baseURL: string\n }\n\n constructor(config: BytePlusVideoConfig, model: TModel) {\n super({}, model)\n this.clientConfig = withBytePlusArkDefaults(config)\n }\n\n private async request(\n path: string,\n init?: Omit<RequestInit, 'headers'>,\n ): Promise<{ response: Response; body: unknown }> {\n const fetchImpl = this.clientConfig.fetch ?? fetch\n const signal = bytePlusTimeoutSignal(this.clientConfig.timeout)\n const response = await fetchImpl(`${this.clientConfig.baseURL}${path}`, {\n ...init,\n ...(signal && { signal }),\n headers: bytePlusArkHeaders(\n this.clientConfig.apiKey,\n toHeaderRecord(this.clientConfig.defaultHeaders),\n ),\n })\n return { response, body: await readJsonBody(response) }\n }\n\n /**\n * Builds the `content[]` array from the resolved prompt, enforcing\n * Seedance's role vocabulary and mode exclusivity.\n */\n private buildContent(\n resolved: ReturnType<typeof resolveMediaPrompt>,\n ): Array<BytePlusVideoContentPart> {\n const model = this.model\n const content: Array<BytePlusVideoContentPart> = []\n if (resolved.text) content.push({ type: 'text', text: resolved.text })\n\n // Every rule below except the role vocabulary itself is a claim about a\n // *specific* model's capabilities, drawn from the known Seedance catalog.\n // None of it can be true of a model that does not exist yet, so for an\n // unknown id the guards stand down and Ark rules — otherwise the escape\n // hatch would block exactly the requests it exists to enable (see\n // BytePlusVideoModelOrString). 'mask' / 'control' still throw: Seedance's\n // wire format has no field to carry them on any model.\n const gated = isKnownBytePlusVideoModel(model)\n\n let firstFrames = 0\n let lastFrames = 0\n // Audio counts as a reference for the mode-exclusivity check. On Seedance\n // 2.0 it also needs a visual reference; 2.5 allows audio-only.\n let visualReferences = 0\n let audioReferences = 0\n\n for (const part of resolved.images) {\n const role = part.metadata?.role\n switch (role) {\n case 'mask':\n case 'control':\n throw new Error(\n `byteplus: Seedance has no '${role}' image input on model ${model}. ` +\n `Use 'start_frame', 'end_frame' or 'reference'.`,\n )\n case 'end_frame': {\n if (gated && !supportsLastFrame(model)) {\n throw new Error(\n `byteplus: ${model} does not support a closing frame — it does ` +\n `text-to-video and first-frame image-to-video only. Drop the ` +\n `'end_frame' image or switch to a model with first-and-last-frame support.`,\n )\n }\n lastFrames++\n content.push({\n type: 'image_url',\n image_url: { url: mediaPartToUrl(part) },\n role: 'last_frame',\n })\n break\n }\n case 'reference':\n case 'character': {\n if (gated && !supportsReferenceMedia(model)) {\n throw new Error(\n `byteplus: ${model} does not support reference images. Reference ` +\n `media is available on Seedance 2.5 and the 2.0 family; on this ` +\n `model use 'start_frame' / 'end_frame' images instead.`,\n )\n }\n visualReferences++\n content.push({\n type: 'image_url',\n image_url: { url: mediaPartToUrl(part) },\n role: 'reference_image',\n })\n break\n }\n // An un-roled image is the opening frame, matching the API's own\n // default and the fal / Veo adapters' positional convention.\n case 'start_frame':\n case undefined: {\n firstFrames++\n content.push({\n type: 'image_url',\n image_url: { url: mediaPartToUrl(part) },\n role: 'first_frame',\n })\n break\n }\n }\n }\n\n // Video and audio parts only exist in reference mode: Seedance rejects an\n // un-roled video with \"reference media mode requires video role to be\n // reference_video\", and has no frame-style role for either modality.\n for (const part of resolved.videos) {\n if (gated && !supportsReferenceMedia(model)) {\n throw new Error(\n `byteplus: ${model} does not accept video prompt parts. Reference ` +\n `video is available on Seedance 2.5 and the 2.0 family only.`,\n )\n }\n visualReferences++\n content.push({\n type: 'video_url',\n video_url: { url: mediaPartToUrl(part) },\n role: 'reference_video',\n })\n }\n\n for (const part of resolved.audios) {\n if (gated && !supportsReferenceMedia(model)) {\n throw new Error(\n `byteplus: ${model} does not accept audio prompt parts. Reference ` +\n `audio is available on Seedance 2.5 and the 2.0 family only.`,\n )\n }\n audioReferences++\n content.push({\n type: 'audio_url',\n audio_url: { url: mediaPartToUrl(part) },\n role: 'reference_audio',\n })\n }\n\n const frames = firstFrames + lastFrames\n if (gated && frames > 0 && visualReferences + audioReferences > 0) {\n throw new Error(\n `byteplus: first/last frame inputs cannot be combined with reference ` +\n `media on model ${model}. Use either frame roles ('start_frame', ` +\n `'end_frame') or reference roles ('reference', 'character', video, ` +\n `audio) — not both.`,\n )\n }\n\n if (gated && firstFrames > 1) {\n throw new Error(\n `byteplus: ${model} accepts at most one opening frame; received ` +\n `${firstFrames} un-roled or 'start_frame' images. Use metadata.role ` +\n `('end_frame', 'reference') to disambiguate the others.`,\n )\n }\n\n if (gated && lastFrames > 1) {\n throw new Error(\n `byteplus: ${model} accepts at most one closing frame; received ` +\n `${lastFrames} 'end_frame' images.`,\n )\n }\n\n // Seedance treats a closing frame as the second half of first-and-last-\n // frame mode: on its own it fails with \"last frame image content cannot be\n // mixed with first frame or reference image content\".\n if (gated && lastFrames > 0 && firstFrames === 0) {\n throw new Error(\n `byteplus: a closing frame needs an opening frame alongside it on ` +\n `model ${model}. Add a 'start_frame' image, or drop the 'end_frame' role.`,\n )\n }\n\n if (\n gated &&\n audioReferences > 0 &&\n visualReferences === 0 &&\n !supportsAudioOnlyReference(model)\n ) {\n throw new Error(\n `byteplus: a reference audio input cannot be the only reference on ` +\n `model ${model}. Pair it with a reference image or video, or use ` +\n `Seedance 2.5 which accepts audio-only reference input.`,\n )\n }\n\n if (content.length === 0) {\n throw new Error(\n `byteplus: a video prompt must carry text or at least one media input ` +\n `(model: ${model}).`,\n )\n }\n\n return content\n }\n\n async createVideoJob(\n options: VideoGenerationOptions<\n BytePlusVideoProviderOptions,\n ResolveBytePlusVideoSize<TModel>,\n number\n >,\n ): Promise<VideoJobResult> {\n const { size, modelOptions, logger } = options\n const model = this.model\n\n const content = this.buildContent(resolveMediaPrompt(options.prompt))\n\n // The generic `size` carries a \"ratio_resolution\" template and splits back\n // into Seedance's separate fields. Explicit modelOptions win below.\n const parsedSize =\n size !== undefined ? resolveBytePlusVideoSize(model, size) : undefined\n\n // Coerce the requested duration into the model's range rather than letting\n // the API reject it. `modelOptions.duration` is deliberately not snapped:\n // it is the escape hatch for `-1` (model picks the length).\n //\n // An unknown model's duration goes through verbatim. Snapping it would\n // mean clamping against the ranges today's models happen to have, so a\n // future model's legitimate 20-second request would silently become 15 —\n // corrupting the request instead of protecting it.\n const duration =\n options.duration !== undefined\n ? isKnownBytePlusVideoModel(model)\n ? this.snapDuration(options.duration)\n : options.duration\n : undefined\n\n const request: BytePlusVideoCreateRequest = {\n ...(parsedSize && {\n ratio: parsedSize.ratio,\n ...(parsedSize.resolution !== undefined && {\n resolution: parsedSize.resolution,\n }),\n }),\n ...(duration !== undefined && { duration }),\n // Explicit provider options win over everything derived above.\n ...modelOptions,\n model,\n content,\n }\n\n // Validate what actually ships, not just what `size` contributed: a\n // `modelOptions.resolution` overriding an already-checked size would\n // otherwise reach Ark unchecked.\n if (request.resolution !== undefined) {\n request.resolution = resolveBytePlusVideoResolution(\n model,\n request.resolution,\n )\n }\n\n try {\n logger.request(\n `activity=video.create provider=${this.name} model=${model} size=${size ?? 'default'} duration=${request.duration ?? 'default'}`,\n { provider: this.name, model },\n )\n\n const { response, body } = await this.request(TASKS_PATH, {\n method: 'POST',\n body: JSON.stringify(request),\n })\n if (!response.ok) {\n throw bytePlusArkError(response.status, body, 'video task creation')\n }\n\n const { id } = (body ?? {}) as BytePlusVideoCreateResponse\n if (!id) {\n throw new Error('byteplus: video task creation returned no task id.')\n }\n\n return { jobId: id, model }\n } catch (error: unknown) {\n logger.errors(`${this.name}.createVideoJob fatal`, {\n error: toRunErrorPayload(error, `${this.name}.createVideoJob failed`),\n source: `${this.name}.createVideoJob`,\n })\n throw error\n }\n }\n\n /**\n * Fetches a task, tagging the thrown error with the HTTP status.\n *\n * The 200 body is validated rather than cast. `readJsonBody` returns\n * `undefined` for an empty body and the raw text for a non-JSON one — both\n * documented failure modes of these hosts (an HTML error page from a proxy\n * in front of the API). Casting either to `BytePlusVideoTask` yields a task\n * whose `status` is `undefined`, which {@link mapStatus} would have to\n * interpret; the honest answer is that the response was not a task at all,\n * so say so while the body is still in hand.\n */\n private async retrieveTask(jobId: string): Promise<BytePlusVideoTask> {\n const { response, body } = await this.request(\n `${TASKS_PATH}/${encodeURIComponent(jobId)}`,\n )\n if (!response.ok) {\n const error = bytePlusArkError(response.status, body, 'video task lookup')\n ;(error as { status?: number }).status = response.status\n throw error\n }\n if (typeof body !== 'object' || body === null) {\n throw bytePlusArkError(\n response.status,\n body,\n `video task lookup (job ${jobId}) returned a non-object body`,\n )\n }\n return body as BytePlusVideoTask\n }\n\n async getVideoStatus(jobId: string): Promise<VideoStatusResult> {\n let task: BytePlusVideoTask\n try {\n task = await this.retrieveTask(jobId)\n } catch (error) {\n // A task record lives 7 days from creation; past that the id 404s. Keep\n // Ark's own code/message: a 404 from a wrong baseURL, a proxy, or a\n // region mismatch is not an expired job id, and collapsing them all to\n // \"Job not found\" sends the caller hunting the wrong thing.\n if ((error as { status?: number }).status === 404) {\n return {\n jobId,\n status: 'failed',\n error: `Job not found: ${jobId} (${(error as Error).message})`,\n }\n }\n throw error\n }\n\n const status = this.mapStatus(task.status)\n const failure = status === 'failed' ? describeTaskFailure(task) : undefined\n return {\n jobId,\n status,\n ...(failure !== undefined && { error: failure }),\n }\n }\n\n async getVideoUrl(jobId: string): Promise<VideoUrlResult> {\n let task: BytePlusVideoTask\n try {\n task = await this.retrieveTask(jobId)\n } catch (error) {\n // See getVideoStatus: Ark's detail distinguishes an expired id from a\n // misrouted request.\n if ((error as { status?: number }).status === 404) {\n throw new Error(\n `Video job not found: ${jobId} (${(error as Error).message})`,\n )\n }\n throw error\n }\n\n const status = this.mapStatus(task.status)\n if (status === 'failed') {\n throw new Error(\n `Video generation failed: ${describeTaskFailure(task)}. Job ID: ${jobId}`,\n )\n }\n\n const url = task.content?.video_url\n if (!url) {\n throw new Error(\n `Video is not ready for download. Check status first. Job ID: ${jobId}`,\n )\n }\n\n // The 24-hour window runs from when the output was produced, which is the\n // last status change on a succeeded task — `created_at` anchors the\n // separate 7-day retention of the task record itself, and can be far\n // earlier (a live `flex` task sat queued ~15 minutes). Corroborated by the\n // signed TOS link itself, which carries `X-Tos-Expires=86400` from an\n // `X-Tos-Date` matching `updated_at`. Fall back to `created_at` only when\n // `updated_at` is missing.\n const anchorSeconds = task.updated_at ?? task.created_at\n const expiresAt =\n anchorSeconds !== undefined\n ? new Date(anchorSeconds * 1000 + VIDEO_URL_TTL_MS)\n : undefined\n\n const usage = buildBytePlusVideoUsage(task.usage)\n return {\n jobId,\n url,\n ...(expiresAt && { expiresAt }),\n ...(usage && { usage }),\n }\n }\n\n /**\n * Maps Seedance task states onto the generic video status set. `expired`\n * (the task outlived `execution_expires_after`) and `cancelled` are\n * terminal non-successes, so both report as failed.\n *\n * An unrecognized state throws rather than defaulting to `processing`.\n * Core's poll loop treats `processing` as \"keep waiting\", so mapping an\n * unknown state — a missing `status`, or a terminal one Ark adds later such\n * as `rejected` — onto it means polling until `maxDuration` and then\n * reporting a generic timeout, with the state Ark actually sent never\n * reaching the caller. Failing here names it.\n */\n protected mapStatus(\n apiStatus: BytePlusVideoTaskStatus | string | undefined,\n ): VideoStatusResult['status'] {\n switch (apiStatus) {\n case 'queued':\n return 'pending'\n case 'running':\n return 'processing'\n case 'succeeded':\n return 'completed'\n case 'failed':\n case 'expired':\n case 'cancelled':\n return 'failed'\n case undefined:\n default:\n throw new Error(\n `byteplus: unrecognized Seedance task status ` +\n `${apiStatus === undefined ? '(missing)' : `\"${apiStatus}\"`}. ` +\n `Known states: queued, running, succeeded, failed, expired, cancelled.`,\n )\n }\n }\n\n /**\n * Seedance accepts any whole second inside a per-model range: 4–15s on the\n * 2.0 family, 4–12s on 1.5-pro, 2–12s on the 1.0 models. An unknown model\n * reports the union of those ranges as a UI hint — see\n * `BYTEPLUS_VIDEO_FALLBACK_DURATIONS`, which `createVideoJob` does not snap\n * against.\n */\n override availableDurations(): DurationOptions<number> {\n return getBytePlusVideoDurationOptions(this.model)\n }\n\n /**\n * Coerce a raw seconds value to the closest duration this model accepts\n * (clamped to its range and rounded to whole seconds).\n */\n override snapDuration(seconds: number): number | undefined {\n return snapToDurationOption(seconds, this.availableDurations())\n }\n}\n\n/**\n * Creates a BytePlus Seedance video adapter with an explicit API key.\n * Type resolution happens here at the call site.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @param model - The model name (e.g., 'seedance-1-0-pro-fast-251015')\n * @param apiKey - Your BytePlus Ark API key\n * @param config - Optional additional configuration\n * @returns Configured BytePlus video adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createBytePlusVideo('seedance-1-5-pro-251215', 'ark-...')\n *\n * const { jobId } = await generateVideo({\n * adapter,\n * prompt: 'a guitar being played in a store',\n * size: '16:9_1080p',\n * duration: 5,\n * })\n * ```\n */\nexport function createBytePlusVideo<TModel extends BytePlusVideoModelOrString>(\n model: TModel,\n apiKey: string,\n config?: Omit<BytePlusVideoConfig, 'apiKey'>,\n): BytePlusVideoAdapter<TModel> {\n return new BytePlusVideoAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a BytePlus Seedance video adapter, reading `ARK_API_KEY` from the\n * environment. Type resolution happens here at the call site.\n *\n * Note that Ark keys are region-isolated and Seedance is only served from the\n * Asia-Pacific endpoint — an EU key will not work here.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @param model - The model name (e.g., 'dreamina-seedance-2-0-260128')\n * @param config - Optional configuration (excluding apiKey, auto-detected)\n * @returns Configured BytePlus video adapter instance with resolved types\n * @throws Error if ARK_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * const adapter = byteplusVideo('dreamina-seedance-2-0-260128')\n *\n * // Image-to-video: an un-roled image is the opening frame.\n * const { jobId } = await generateVideo({\n * adapter,\n * prompt: [\n * { type: 'text', content: 'the guitarist starts playing' },\n * { type: 'image', source: { type: 'url', value: 'https://example.com/shop.jpg' } },\n * ],\n * })\n *\n * const status = await getVideoJobStatus({ adapter, jobId })\n * ```\n *\n * ## Models this package does not know yet\n *\n * `model` also accepts any string, so a Seedance id BytePlus publishes after\n * this release works without upgrading. **Seedance 2.5 is the case this exists\n * for**: `dreamina-seedance-2-5-260628` is real and reachable, but its\n * capability cells could not be probed from this repo's account (Ark answers\n * 404 `ModelNotOpen` until the model is activated in the Ark Console), so it\n * is deliberately absent from the narrowed model tables. Passing it here works\n * for an account that has activated it.\n *\n * An unknown id relaxes both halves of the adapter: the `size` type widens to\n * any string, provider options are ungated, and the runtime guards that encode\n * per-model capabilities — resolution tiers, closing-frame and reference-media\n * support, frame cardinality and mode exclusivity, duration snapping — stand\n * down so Ark decides. Known ids are unaffected. See\n * {@link BytePlusVideoModelOrString} for how to discover and probe an id.\n *\n * @example\n * ```typescript\n * // Seedance 2.5, before this package ships probe-verified metadata for it:\n * const adapter = byteplusVideo('dreamina-seedance-2-5-260628')\n * ```\n */\nexport function byteplusVideo<TModel extends BytePlusVideoModelOrString>(\n model: TModel,\n config?: Omit<BytePlusVideoConfig, 'apiKey'>,\n): BytePlusVideoAdapter<TModel> {\n const apiKey = getBytePlusArkApiKeyFromEnv()\n return createBytePlusVideo(model, apiKey, config)\n}\n"],"mappings":"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"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { BytePlusVideoAdapter, byteplusVideo, createBytePlusVideo, } from './adapters/video.js';
|
|
2
2
|
export type { BytePlusVideoConfig } from './adapters/video.js';
|
|
3
|
-
export { parseBytePlusVideoSize, resolveBytePlusVideoResolution, resolveBytePlusVideoSize, supportsLastFrame, supportsReferenceMedia, } from './video/video-provider-options.js';
|
|
4
|
-
export type { BytePlusVideoModelProviderOptionsByName, BytePlusVideoProviderOptions, BytePlusVideoServiceTier, } from './video/video-provider-options.js';
|
|
3
|
+
export { parseBytePlusVideoSize, resolveBytePlusVideoResolution, resolveBytePlusVideoSize, supportsAudioOnlyReference, supportsLastFrame, supportsReferenceMedia, } from './video/video-provider-options.js';
|
|
4
|
+
export type { BytePlusVideoModelProviderOptionsByName, BytePlusVideoOutputFormat, BytePlusVideoProviderOptions, BytePlusVideoServiceTier, } from './video/video-provider-options.js';
|
|
5
5
|
export type { BytePlusVideoContentPart, BytePlusVideoContentRole, BytePlusVideoCreateRequest, BytePlusVideoCreateResponse, BytePlusVideoTask, BytePlusVideoTaskContent, BytePlusVideoTaskError, BytePlusVideoTaskListItem, BytePlusVideoTaskListResponse, BytePlusVideoTaskStatus, BytePlusVideoTaskUsage, } from './video/wire-types.js';
|
|
6
6
|
export { BYTEPLUS_DEFAULT_TTS_SPEAKER, BYTEPLUS_TTS_MAX_OUTPUT_SECONDS, BytePlusTTSAdapter, byteplusSpeech, createBytePlusSpeech, toSpeechRate, } from './adapters/tts.js';
|
|
7
7
|
export type { BytePlusTTSProviderOptions, BytePlusTTSResult, BytePlusTTSVoice, } from './audio/tts-provider-options.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BYTEPLUS_ARK_BASE_URL, BYTEPLUS_VOICE_BASE_URL, bytePlusArkError, bytePlusArkHeaders, bytePlusVoiceError, bytePlusVoiceHeaders, getBytePlusArkApiKeyFromEnv, getBytePlusVoiceApiKeyFromEnv, withBytePlusArkDefaults, withBytePlusVoiceDefaults } from "./utils/client.js";
|
|
2
2
|
import { BYTEPLUS_CHAT_MODELS, BYTEPLUS_IMAGE_MAX_REFERENCE_IMAGES, BYTEPLUS_IMAGE_MODELS, BYTEPLUS_STRUCTURED_OUTPUT_CHAT_MODELS, BYTEPLUS_THINKING_SUMMARY_MODELS, BYTEPLUS_TRANSCRIPTION_MODELS, BYTEPLUS_TTS_MODELS, BYTEPLUS_VIDEO_DURATIONS, BYTEPLUS_VIDEO_FALLBACK_DURATIONS, BYTEPLUS_VIDEO_MODELS, emitsEncryptedContent, getBytePlusVideoDurationOptions, isKnownBytePlusVideoModel, supportsStructuredOutput } from "./model-meta.js";
|
|
3
|
-
import { parseBytePlusVideoSize, resolveBytePlusVideoResolution, resolveBytePlusVideoSize, supportsLastFrame, supportsReferenceMedia } from "./video/video-provider-options.js";
|
|
3
|
+
import { parseBytePlusVideoSize, resolveBytePlusVideoResolution, resolveBytePlusVideoSize, supportsAudioOnlyReference, supportsLastFrame, supportsReferenceMedia } from "./video/video-provider-options.js";
|
|
4
4
|
import { BytePlusVideoAdapter, byteplusVideo, createBytePlusVideo } from "./adapters/video.js";
|
|
5
5
|
import { BYTEPLUS_DEFAULT_TTS_SPEAKER, BYTEPLUS_TTS_MAX_OUTPUT_SECONDS, BytePlusTTSAdapter, byteplusSpeech, createBytePlusSpeech, toSpeechRate } from "./adapters/tts.js";
|
|
6
6
|
import { BYTEPLUS_ASR_RESOURCE_HEADER, BYTEPLUS_ASR_RESOURCE_ID, BYTEPLUS_TTS_SAMPLE_RATES } from "./audio/wire-types.js";
|
|
@@ -8,4 +8,4 @@ import { BytePlusTranscriptionAdapter, byteplusTranscription, createBytePlusTran
|
|
|
8
8
|
import { BYTEPLUS_IMAGE_MAX_PROMPT_WORDS, BYTEPLUS_IMAGE_MAX_SEQUENTIAL_IMAGES, BYTEPLUS_OUTPUT_FORMAT_IMAGE_MODELS, parseBytePlusImageSize } from "./image/image-provider-options.js";
|
|
9
9
|
import { BytePlusImageAdapter, byteplusImage, createBytePlusImage } from "./adapters/image.js";
|
|
10
10
|
import { BytePlusTextAdapter, byteplusText, createBytePlusText } from "./adapters/text.js";
|
|
11
|
-
export { BYTEPLUS_ARK_BASE_URL, BYTEPLUS_ASR_RESOURCE_HEADER, BYTEPLUS_ASR_RESOURCE_ID, BYTEPLUS_CHAT_MODELS, BYTEPLUS_DEFAULT_TTS_SPEAKER, BYTEPLUS_IMAGE_MAX_PROMPT_WORDS, BYTEPLUS_IMAGE_MAX_REFERENCE_IMAGES, BYTEPLUS_IMAGE_MAX_SEQUENTIAL_IMAGES, BYTEPLUS_IMAGE_MODELS, BYTEPLUS_OUTPUT_FORMAT_IMAGE_MODELS, BYTEPLUS_STRUCTURED_OUTPUT_CHAT_MODELS, BYTEPLUS_THINKING_SUMMARY_MODELS, BYTEPLUS_TRANSCRIPTION_MODELS, BYTEPLUS_TTS_MAX_OUTPUT_SECONDS, BYTEPLUS_TTS_MODELS, BYTEPLUS_TTS_SAMPLE_RATES, BYTEPLUS_VIDEO_DURATIONS, BYTEPLUS_VIDEO_FALLBACK_DURATIONS, BYTEPLUS_VIDEO_MODELS, BYTEPLUS_VOICE_BASE_URL, BytePlusImageAdapter, BytePlusTTSAdapter, BytePlusTextAdapter, BytePlusTranscriptionAdapter, BytePlusVideoAdapter, bytePlusArkError, bytePlusArkHeaders, bytePlusVoiceError, bytePlusVoiceHeaders, byteplusImage, byteplusSpeech, byteplusText, byteplusTranscription, byteplusVideo, createBytePlusImage, createBytePlusSpeech, createBytePlusText, createBytePlusTranscription, createBytePlusVideo, emitsEncryptedContent, getBytePlusArkApiKeyFromEnv, getBytePlusVideoDurationOptions, getBytePlusVoiceApiKeyFromEnv, isKnownBytePlusVideoModel, parseBytePlusImageSize, parseBytePlusVideoSize, resolveBytePlusVideoResolution, resolveBytePlusVideoSize, supportsLastFrame, supportsReferenceMedia, supportsStructuredOutput, toSpeechRate, withBytePlusArkDefaults, withBytePlusVoiceDefaults };
|
|
11
|
+
export { BYTEPLUS_ARK_BASE_URL, BYTEPLUS_ASR_RESOURCE_HEADER, BYTEPLUS_ASR_RESOURCE_ID, BYTEPLUS_CHAT_MODELS, BYTEPLUS_DEFAULT_TTS_SPEAKER, BYTEPLUS_IMAGE_MAX_PROMPT_WORDS, BYTEPLUS_IMAGE_MAX_REFERENCE_IMAGES, BYTEPLUS_IMAGE_MAX_SEQUENTIAL_IMAGES, BYTEPLUS_IMAGE_MODELS, BYTEPLUS_OUTPUT_FORMAT_IMAGE_MODELS, BYTEPLUS_STRUCTURED_OUTPUT_CHAT_MODELS, BYTEPLUS_THINKING_SUMMARY_MODELS, BYTEPLUS_TRANSCRIPTION_MODELS, BYTEPLUS_TTS_MAX_OUTPUT_SECONDS, BYTEPLUS_TTS_MODELS, BYTEPLUS_TTS_SAMPLE_RATES, BYTEPLUS_VIDEO_DURATIONS, BYTEPLUS_VIDEO_FALLBACK_DURATIONS, BYTEPLUS_VIDEO_MODELS, BYTEPLUS_VOICE_BASE_URL, BytePlusImageAdapter, BytePlusTTSAdapter, BytePlusTextAdapter, BytePlusTranscriptionAdapter, BytePlusVideoAdapter, bytePlusArkError, bytePlusArkHeaders, bytePlusVoiceError, bytePlusVoiceHeaders, byteplusImage, byteplusSpeech, byteplusText, byteplusTranscription, byteplusVideo, createBytePlusImage, createBytePlusSpeech, createBytePlusText, createBytePlusTranscription, createBytePlusVideo, emitsEncryptedContent, getBytePlusArkApiKeyFromEnv, getBytePlusVideoDurationOptions, getBytePlusVoiceApiKeyFromEnv, isKnownBytePlusVideoModel, parseBytePlusImageSize, parseBytePlusVideoSize, resolveBytePlusVideoResolution, resolveBytePlusVideoSize, supportsAudioOnlyReference, supportsLastFrame, supportsReferenceMedia, supportsStructuredOutput, toSpeechRate, withBytePlusArkDefaults, withBytePlusVoiceDefaults };
|
package/dist/esm/model-meta.d.ts
CHANGED
|
@@ -336,10 +336,11 @@ export type BytePlusVideoRatio = '16:9' | '9:16' | '4:3' | '3:4' | '1:1' | '21:9
|
|
|
336
336
|
/**
|
|
337
337
|
* Resolution tiers accepted by the Seedance task API.
|
|
338
338
|
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
339
|
+
* Resolution tiers are model-specific (see
|
|
340
|
+
* {@link BytePlusVideoModelResolutionByName}). Two findings that still
|
|
341
|
+
* contradict older BytePlus prose: there is **no 2K tier on any Seedance
|
|
342
|
+
* model**, and `4k` exists only on `dreamina-seedance-2-0-260128` (Seedance
|
|
343
|
+
* 2.5 is 480p/720p only, per the live ModelArk docs).
|
|
343
344
|
*
|
|
344
345
|
* The API matches this field case-insensitively (`4K`, `4k` and `1080P` are
|
|
345
346
|
* all accepted), so this package standardizes on the lowercase spelling.
|
|
@@ -352,6 +353,13 @@ export type BytePlusVideoResolution = '480p' | '720p' | '1080p' | '4k';
|
|
|
352
353
|
* template back apart.
|
|
353
354
|
*/
|
|
354
355
|
export type BytePlusVideoSize<TResolution extends BytePlusVideoResolution = BytePlusVideoResolution> = BytePlusVideoRatio | `${BytePlusVideoRatio}_${TResolution}`;
|
|
356
|
+
declare const DREAMINA_SEEDANCE_2_5: {
|
|
357
|
+
readonly name: "dreamina-seedance-2-5-260628";
|
|
358
|
+
readonly supports: {
|
|
359
|
+
readonly input: readonly ["text", "image", "video", "audio"];
|
|
360
|
+
readonly output: readonly ["video", "audio"];
|
|
361
|
+
};
|
|
362
|
+
};
|
|
355
363
|
declare const DREAMINA_SEEDANCE_2_0: {
|
|
356
364
|
readonly name: "dreamina-seedance-2-0-260128";
|
|
357
365
|
readonly supports: {
|
|
@@ -397,18 +405,19 @@ declare const SEEDANCE_1_0_PRO_FAST: {
|
|
|
397
405
|
/**
|
|
398
406
|
* All supported Seedance video model identifiers.
|
|
399
407
|
*/
|
|
400
|
-
export declare const BYTEPLUS_VIDEO_MODELS: readonly ["dreamina-seedance-2-0-260128", "dreamina-seedance-2-0-fast-260128", "dreamina-seedance-2-0-mini-260615", "seedance-1-5-pro-251215", "seedance-1-0-pro-250528", "seedance-1-0-pro-fast-251015"];
|
|
408
|
+
export declare const BYTEPLUS_VIDEO_MODELS: readonly ["dreamina-seedance-2-5-260628", "dreamina-seedance-2-0-260128", "dreamina-seedance-2-0-fast-260128", "dreamina-seedance-2-0-mini-260615", "seedance-1-5-pro-251215", "seedance-1-0-pro-250528", "seedance-1-0-pro-fast-251015"];
|
|
401
409
|
/**
|
|
402
410
|
* Union of all supported Seedance video model names.
|
|
403
411
|
*/
|
|
404
412
|
export type BytePlusVideoModel = (typeof BYTEPLUS_VIDEO_MODELS)[number];
|
|
405
413
|
/**
|
|
406
414
|
* Type-only map from video model name to the non-text prompt modalities it
|
|
407
|
-
* accepts.
|
|
408
|
-
* frames, reference images, reference video and audio); the 1.x
|
|
409
|
-
* start/end frames only.
|
|
415
|
+
* accepts. Seedance 2.5 and the 2.0 family take multimodal references
|
|
416
|
+
* (start/end frames, reference images, reference video and audio); the 1.x
|
|
417
|
+
* models take start/end frames only.
|
|
410
418
|
*/
|
|
411
419
|
export type BytePlusVideoModelInputModalitiesByName = {
|
|
420
|
+
[DREAMINA_SEEDANCE_2_5.name]: readonly ['image', 'video', 'audio'];
|
|
412
421
|
[DREAMINA_SEEDANCE_2_0.name]: readonly ['image', 'video', 'audio'];
|
|
413
422
|
[DREAMINA_SEEDANCE_2_0_FAST.name]: readonly ['image', 'video', 'audio'];
|
|
414
423
|
[DREAMINA_SEEDANCE_2_0_MINI.name]: readonly ['image', 'video', 'audio'];
|
|
@@ -419,10 +428,13 @@ export type BytePlusVideoModelInputModalitiesByName = {
|
|
|
419
428
|
/**
|
|
420
429
|
* Type-only map from video model name to the resolutions it accepts.
|
|
421
430
|
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
431
|
+
* 2.0 / 1.x cells were probe-verified on 2026-07-31; 2.5 comes from the
|
|
432
|
+
* public ModelArk create-task docs (2026-08-07). Note
|
|
433
|
+
* `seedance-1-0-pro-fast-251015` does accept `1080p`, despite older BytePlus
|
|
434
|
+
* prose listing it as 480p/720p.
|
|
424
435
|
*/
|
|
425
436
|
export type BytePlusVideoModelResolutionByName = {
|
|
437
|
+
[DREAMINA_SEEDANCE_2_5.name]: '480p' | '720p';
|
|
426
438
|
[DREAMINA_SEEDANCE_2_0.name]: '480p' | '720p' | '1080p' | '4k';
|
|
427
439
|
[DREAMINA_SEEDANCE_2_0_FAST.name]: '480p' | '720p';
|
|
428
440
|
[DREAMINA_SEEDANCE_2_0_MINI.name]: '480p' | '720p';
|
|
@@ -440,24 +452,16 @@ export type BytePlusVideoModelSizeByName = {
|
|
|
440
452
|
* A Seedance model id: one this package knows, or any other string.
|
|
441
453
|
*
|
|
442
454
|
* The open half is a deliberate escape hatch for models BytePlus ships between
|
|
443
|
-
* releases of this package.
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
* account has not activated the model"), so no capability question can be
|
|
449
|
-
* answered until someone enables it in the Ark Console. Passing it through
|
|
450
|
-
* the escape hatch works today for an account that has.
|
|
451
|
-
*
|
|
452
|
-
* Adding a model here *narrows* it — the adapter's guards switch on and reject
|
|
453
|
-
* against this file's tables. For a model whose real limits are unknown that
|
|
454
|
-
* is strictly worse than the open path, which lets Ark judge. So an id lands
|
|
455
|
-
* here only once probed.
|
|
455
|
+
* releases of this package. Adding a model to {@link BYTEPLUS_VIDEO_MODELS}
|
|
456
|
+
* *narrows* it — the adapter's guards switch on and reject against this
|
|
457
|
+
* file's tables. For a model whose real limits are unknown that is strictly
|
|
458
|
+
* worse than the open path, which lets Ark judge. So an id lands in the
|
|
459
|
+
* known table only once its capability cells are documented or probed.
|
|
456
460
|
*
|
|
457
461
|
* Discovering ids: `GET /models` on the Ark data plane enumerates the catalog
|
|
458
|
-
* (id, `task_type`, `modalities`, `status`)
|
|
459
|
-
*
|
|
460
|
-
*
|
|
462
|
+
* (id, `task_type`, `modalities`, `status`). It is not exhaustive —
|
|
463
|
+
* `seedream-5-0-lite-260128` answers requests but is missing from the
|
|
464
|
+
* listing — so absence there is not evidence of absence. The ModelArk
|
|
461
465
|
* release notes (https://docs.byteplus.com/en/docs/ModelArk/1159178) are the
|
|
462
466
|
* other watch surface.
|
|
463
467
|
*
|
|
@@ -470,7 +474,7 @@ export type BytePlusVideoModelSizeByName = {
|
|
|
470
474
|
* Unknown ids trade compile-time narrowing for reach: the full size surface is
|
|
471
475
|
* accepted, provider options are ungated, and the adapter's model-specific
|
|
472
476
|
* runtime guards stand down so a new model's legitimate request reaches Ark.
|
|
473
|
-
* Known ids keep their probe-verified narrowing.
|
|
477
|
+
* Known ids keep their documented / probe-verified narrowing.
|
|
474
478
|
*/
|
|
475
479
|
export type BytePlusVideoModelOrString = BytePlusVideoModel | (string & {});
|
|
476
480
|
/**
|
|
@@ -497,8 +501,8 @@ export declare function isKnownBytePlusVideoModel(model: string): model is ByteP
|
|
|
497
501
|
* Per-model duration type. Seedance accepts any integer second inside the
|
|
498
502
|
* model's range, so this is a continuous range expressed as `number` — a
|
|
499
503
|
* literal union cannot represent it. (The API also accepts `duration: -1` on
|
|
500
|
-
* Seedance 2.0 and 1.5-pro to let the model choose; that is reachable
|
|
501
|
-
* provider options, not through the generic `duration`.)
|
|
504
|
+
* Seedance 2.5, 2.0 and 1.5-pro to let the model choose; that is reachable
|
|
505
|
+
* through provider options, not through the generic `duration`.)
|
|
502
506
|
*/
|
|
503
507
|
export type BytePlusVideoModelDurationByName = {
|
|
504
508
|
[K in BytePlusVideoModel]: number;
|
|
@@ -513,10 +517,10 @@ export declare const BYTEPLUS_VIDEO_DURATIONS: {
|
|
|
513
517
|
* Duration hint for a model this package has no table for.
|
|
514
518
|
*
|
|
515
519
|
* Spans every range Seedance has shipped so far (2s on the 1.0 models through
|
|
516
|
-
*
|
|
520
|
+
* 30s on Seedance 2.5) so `availableDurations()` can still drive a UI. It is
|
|
517
521
|
* a hint, not a contract: the adapter does **not** snap an unknown model's
|
|
518
|
-
* duration against it, because clamping a future model's legitimate
|
|
519
|
-
* request down to
|
|
522
|
+
* duration against it, because clamping a future model's legitimate longer
|
|
523
|
+
* request down to 30 would corrupt the request rather than protect it.
|
|
520
524
|
*/
|
|
521
525
|
export declare const BYTEPLUS_VIDEO_FALLBACK_DURATIONS: DurationOptions<number>;
|
|
522
526
|
/**
|
package/dist/esm/model-meta.js
CHANGED
|
@@ -393,6 +393,18 @@ function supportsStructuredOutput(model) {
|
|
|
393
393
|
* All supported Seedance video model identifiers.
|
|
394
394
|
*/
|
|
395
395
|
var BYTEPLUS_VIDEO_MODELS = [
|
|
396
|
+
{
|
|
397
|
+
name: "dreamina-seedance-2-5-260628",
|
|
398
|
+
supports: {
|
|
399
|
+
input: [
|
|
400
|
+
"text",
|
|
401
|
+
"image",
|
|
402
|
+
"video",
|
|
403
|
+
"audio"
|
|
404
|
+
],
|
|
405
|
+
output: ["video", "audio"]
|
|
406
|
+
}
|
|
407
|
+
}.name,
|
|
396
408
|
{
|
|
397
409
|
name: "dreamina-seedance-2-0-260128",
|
|
398
410
|
supports: {
|
|
@@ -465,6 +477,13 @@ function isKnownBytePlusVideoModel(model) {
|
|
|
465
477
|
* Runtime duration table backing `availableDurations()` / `snapDuration()`.
|
|
466
478
|
*/
|
|
467
479
|
var BYTEPLUS_VIDEO_DURATIONS = {
|
|
480
|
+
"dreamina-seedance-2-5-260628": {
|
|
481
|
+
kind: "range",
|
|
482
|
+
min: 4,
|
|
483
|
+
max: 30,
|
|
484
|
+
step: 1,
|
|
485
|
+
unit: "seconds"
|
|
486
|
+
},
|
|
468
487
|
"dreamina-seedance-2-0-260128": {
|
|
469
488
|
kind: "range",
|
|
470
489
|
min: 4,
|
|
@@ -512,15 +531,15 @@ var BYTEPLUS_VIDEO_DURATIONS = {
|
|
|
512
531
|
* Duration hint for a model this package has no table for.
|
|
513
532
|
*
|
|
514
533
|
* Spans every range Seedance has shipped so far (2s on the 1.0 models through
|
|
515
|
-
*
|
|
534
|
+
* 30s on Seedance 2.5) so `availableDurations()` can still drive a UI. It is
|
|
516
535
|
* a hint, not a contract: the adapter does **not** snap an unknown model's
|
|
517
|
-
* duration against it, because clamping a future model's legitimate
|
|
518
|
-
* request down to
|
|
536
|
+
* duration against it, because clamping a future model's legitimate longer
|
|
537
|
+
* request down to 30 would corrupt the request rather than protect it.
|
|
519
538
|
*/
|
|
520
539
|
var BYTEPLUS_VIDEO_FALLBACK_DURATIONS = {
|
|
521
540
|
kind: "range",
|
|
522
541
|
min: 2,
|
|
523
|
-
max:
|
|
542
|
+
max: 30,
|
|
524
543
|
step: 1,
|
|
525
544
|
unit: "seconds"
|
|
526
545
|
};
|