@nodaro/prompts 1.39.0 → 1.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-XIWWDGT5.cjs → chunk-2EW5B7ZV.cjs} +7 -7
- package/dist/{chunk-XIWWDGT5.cjs.map → chunk-2EW5B7ZV.cjs.map} +1 -1
- package/dist/{chunk-22KZOAGL.cjs → chunk-C3NHNECH.cjs} +86 -48
- package/dist/chunk-C3NHNECH.cjs.map +1 -0
- package/dist/{chunk-XRLOMTBZ.js → chunk-C46256GZ.js} +43 -5
- package/dist/chunk-C46256GZ.js.map +1 -0
- package/dist/{chunk-NMWKXAYA.cjs → chunk-DQFQJECU.cjs} +43 -5
- package/dist/chunk-DQFQJECU.cjs.map +1 -0
- package/dist/{chunk-XU54YJXQ.js → chunk-HQTHDTHN.js} +85 -49
- package/dist/chunk-HQTHDTHN.js.map +1 -0
- package/dist/{chunk-KUBZVWME.cjs → chunk-LXPKTI2K.cjs} +12 -5
- package/dist/chunk-LXPKTI2K.cjs.map +1 -0
- package/dist/{chunk-3INXNDV7.cjs → chunk-LZ53F5BN.cjs} +8 -8
- package/dist/chunk-LZ53F5BN.cjs.map +1 -0
- package/dist/{chunk-7XMTVYS5.js → chunk-N6VMPG5S.js} +12 -6
- package/dist/chunk-N6VMPG5S.js.map +1 -0
- package/dist/{chunk-6MPZHFIS.js → chunk-RB2F5JQF.js} +5 -5
- package/dist/chunk-RB2F5JQF.js.map +1 -0
- package/dist/{chunk-Z3SZXWRU.js → chunk-YS6HOWMW.js} +3 -3
- package/dist/{chunk-Z3SZXWRU.js.map → chunk-YS6HOWMW.js.map} +1 -1
- package/dist/entries/assembly.cjs +30 -22
- package/dist/entries/assembly.d.cts +90 -34
- package/dist/entries/assembly.d.ts +90 -34
- package/dist/entries/assembly.js +1 -1
- package/dist/entries/doctrine.cjs +46 -46
- package/dist/entries/doctrine.js +1 -1
- package/dist/entries/presets.cjs +32 -32
- package/dist/entries/presets.d.cts +2 -0
- package/dist/entries/presets.d.ts +2 -0
- package/dist/entries/presets.js +2 -2
- package/dist/entries/prompt-fields.cjs +26 -26
- package/dist/entries/prompt-fields.d.cts +2 -1
- package/dist/entries/prompt-fields.d.ts +2 -1
- package/dist/entries/prompt-fields.js +2 -2
- package/dist/index.cjs +133 -125
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -5
- package/package.json +2 -2
- package/src/__tests__/factory-presets-prototype-keys.test.ts +43 -0
- package/src/__tests__/frame-reference-slots.test.ts +43 -0
- package/src/__tests__/gemini-omni-inputs.test.ts +12 -10
- package/src/__tests__/provider-prompt-doctrine.test.ts +14 -0
- package/src/__tests__/veo-i2v-inputs.test.ts +16 -11
- package/src/entries/assembly.ts +1 -0
- package/src/factory-presets/index.ts +4 -1
- package/src/factory-presets/references.ts +2 -1
- package/src/frame-reference-slots.ts +92 -0
- package/src/gemini-omni-inputs.ts +23 -22
- package/src/node-prompt-fields.ts +5 -3
- package/src/own-entry.ts +11 -0
- package/src/prompt-wizard-categories.ts +2 -0
- package/src/provider-prompt-doctrine.ts +45 -3
- package/src/ref-binding.ts +28 -3
- package/src/resolve-prompt.ts +2 -1
- package/src/seedance-2-inputs.ts +15 -40
- package/src/veo-i2v-inputs.ts +24 -17
- package/src/video-reference-resolver.ts +1 -1
- package/dist/chunk-22KZOAGL.cjs.map +0 -1
- package/dist/chunk-3INXNDV7.cjs.map +0 -1
- package/dist/chunk-6MPZHFIS.js.map +0 -1
- package/dist/chunk-7XMTVYS5.js.map +0 -1
- package/dist/chunk-KUBZVWME.cjs.map +0 -1
- package/dist/chunk-NMWKXAYA.cjs.map +0 -1
- package/dist/chunk-XRLOMTBZ.js.map +0 -1
- package/dist/chunk-XU54YJXQ.js.map +0 -1
|
@@ -245,7 +245,7 @@ const GEMINI_OMNI_DOCTRINE: ProviderPromptDoctrine = {
|
|
|
245
245
|
"Multimodal Google video with native audio: text-to-video, image-to-video, and video-edit through the same prompt surface. 4/6/8/10s; 720p/1080p or 4K tier.",
|
|
246
246
|
"Structure like the platform default: subject → action → scene → lighting → camera → style. Quote dialogue lines to have them spoken; describe SFX/ambience plainly in the prompt.",
|
|
247
247
|
"Text-to-video REQUIRES a concrete aspect ratio (the API hard-rejects a missing one); image runs infer aspect from the input.",
|
|
248
|
-
"Reference images ride
|
|
248
|
+
"Reference images ride in image_urls, your references first — bind them by ordinal ('the woman from @image_1'); a wired start frame is appended after them and bound as the opening frame. Video-edit: describe the change.",
|
|
249
249
|
],
|
|
250
250
|
doctrine: `Prompt structure (no public Google prompt guide exists for the Omni video endpoint —
|
|
251
251
|
the API contract is the doctrine source, like MiniMax H3; structure guidance mirrors the
|
|
@@ -254,7 +254,7 @@ subject → action → scene/environment → lighting → camera movement → st
|
|
|
254
254
|
|
|
255
255
|
**Modes (picked from the wired inputs)**
|
|
256
256
|
- Nothing visual → text-to-video. A concrete aspect ratio is REQUIRED — the API hard-rejects a missing one (Nodaro sends the node's ratio; there is no adaptive).
|
|
257
|
-
- Image(s) wired → image-to-video
|
|
257
|
+
- Image(s) wired → image-to-video. Your reference images come FIRST, in your order (\`@image_1\`, \`@image_2\`, …) — bind each in the prompt ("the woman from @image_1", "the interior from @image_2"); a wired start frame is appended after them and the platform names it as the opening frame.
|
|
258
258
|
- Source video wired → video-edit (served through the same handle): describe the CHANGE ("replace the daylight with dusk, keep the motion and framing"), not a full re-description.
|
|
259
259
|
|
|
260
260
|
**Audio (native)**
|
|
@@ -284,6 +284,7 @@ descriptions of static/unchanged parts — spend the words on what MOVES.
|
|
|
284
284
|
**Image-to-video (the primary mode)**
|
|
285
285
|
- The input image is the FIRST FRAME, not a loose reference: composition, subject identity, and visual style carry over. Describe motion and camera only ("she turns toward the window as the camera slowly pushes in"); re-describing the still wastes adherence.
|
|
286
286
|
- grok-imagine-video-1.5 accepts up to 7 images (identity/scene references beyond the first frame); at 1080p only ONE image is allowed.
|
|
287
|
+
- grok-i2v with reference images: images are addressed \`@image(n)\` in array order. Your references come first (\`@image(1)\` is your first reference); a wired start frame is appended after them and the platform names it as the opening frame.
|
|
287
288
|
|
|
288
289
|
**Audio (video-1.5)**
|
|
289
290
|
- Native audio generates with the clip — background music, SFX, and lip-synced dialogue. Quote the spoken line; describe the music/SFX plainly. There is no audio toggle on the KIE contract — cue (or exclude) sound in the prompt text.
|
|
@@ -382,6 +383,46 @@ Source: KIE Wan 3.0 market contract (docs.kie.ai/market/wan/3-0-video,
|
|
|
382
383
|
docs.kie.ai/market/wan/3-0-video-prime). Captured 2026-09-01.`,
|
|
383
384
|
}
|
|
384
385
|
|
|
386
|
+
// Vidu Q4 — no public Q4 prompt guide is captured, so like Wan 3.0 / MiniMax H3
|
|
387
|
+
// the body is KIE contract facts plus the platform-standard structure. The
|
|
388
|
+
// contract names references "reference image 2" / "reference audio 1"; the
|
|
389
|
+
// editor's reference tokens emit the platform-wide `@image_N` / `@audio_N`
|
|
390
|
+
// binding instead (see ref-binding.ts), which is what this doctrine teaches.
|
|
391
|
+
const VIDU_Q4_DOCTRINE: ProviderPromptDoctrine = {
|
|
392
|
+
providers: ["vidu-q4"],
|
|
393
|
+
heading: "Vidu Q4 (vidu-q4)",
|
|
394
|
+
tips: [
|
|
395
|
+
"A start frame alone animates that frame (prompt optional). Any reference image or audio switches to reference mode: the prompt is REQUIRED and the start frame joins the reference images.",
|
|
396
|
+
"Reference mode: 1-15 images, up to 3 audio clips (MP3 only, 3-12s each). Name each by ordinal — \"the man from @image_2 says … in the voice of @audio_1\".",
|
|
397
|
+
"No text-only runs and no end frame: wire a start frame or at least one reference image.",
|
|
398
|
+
"3-16s (default 5); 540p / 720p (default) / 1080p / 2K / 4K; reference runs take 16:9 / 9:16 / 1:1 / 4:3 / 3:4. Prompt cap 20,000 chars.",
|
|
399
|
+
"`audio` is ON by default (synchronized sound). Cue the sound you want or state the exclusion; dialogue is not verified here, so plan key lines as a TTS + lip-sync pass.",
|
|
400
|
+
],
|
|
401
|
+
doctrine: `Prompt structure (no public Vidu Q4 prompt guide is captured — the KIE API contract is the
|
|
402
|
+
doctrine source; platform-standard structure applies):
|
|
403
|
+
subject → action → scene/environment → lighting → camera movement → style → constraints.
|
|
404
|
+
|
|
405
|
+
**Modes (picked by what is wired)**
|
|
406
|
+
- Start frame only → image-to-video. The frame anchors the shot and its aspect ratio; describe MOTION and camera, not the still. The prompt is optional.
|
|
407
|
+
- Any reference image or reference audio → reference-to-video. The prompt is REQUIRED. A wired start frame cannot be sent as a frame here, so the platform adds it to the reference images (after the caller's own, ordinals unchanged) and names it in the prompt as the opening frame.
|
|
408
|
+
- There is no text-only mode and no first+last frame mode.
|
|
409
|
+
|
|
410
|
+
**Reference binding**
|
|
411
|
+
- Refer to inputs by ordinal in attachment order. In the editor the reference tokens emit "@image_N" / "@audio_N" — write the binding explicitly ("the woman from @image_1 turns to the man from @image_2 and says …, in the voice of @audio_1").
|
|
412
|
+
- Caps: 1-15 reference images (JPG / PNG / WEBP) and 0-3 reference audio clips. Audio must be MP3, each 3-12 seconds — the platform refuses any other format or length before the run starts.
|
|
413
|
+
|
|
414
|
+
**Duration, resolution, aspect**
|
|
415
|
+
- 3-16 seconds (default 5). 540p / 720p (default) / 1080p / 2K / 4K.
|
|
416
|
+
- Image-to-video has no aspect setting — the output inherits the start frame's shape. Reference runs take 16:9 (default) / 9:16 / 1:1 / 4:3 / 3:4.
|
|
417
|
+
- Prompts up to 20,000 characters.
|
|
418
|
+
|
|
419
|
+
**Audio**
|
|
420
|
+
- The "audio" boolean defaults ON and generates synchronized sound with the clip (the contract says "dialogue and sound effects"). Describe the soundscape plainly or state the exclusion, or turn the toggle off for a silent clip.
|
|
421
|
+
|
|
422
|
+
Source: KIE Vidu Q4 market contracts (docs.kie.ai/market/vidu-q4/image-to-video,
|
|
423
|
+
docs.kie.ai/market/vidu-q4/reference-to-video). Captured 2026-10-17.`,
|
|
424
|
+
}
|
|
425
|
+
|
|
385
426
|
const HAPPYHORSE_DOCTRINE: ProviderPromptDoctrine = {
|
|
386
427
|
providers: ["happyhorse", "happyhorse-i2v", "happyhorse-ref2v", "happyhorse-edit"],
|
|
387
428
|
heading: "HappyHorse 1.1 (happyhorse, happyhorse-i2v, happyhorse-ref2v)",
|
|
@@ -398,7 +439,7 @@ subject → action → scene/environment → lighting → camera movement → st
|
|
|
398
439
|
**Contract facts (KIE, per-mode pages)**
|
|
399
440
|
- Prompts: any language, up to 5000 non-Chinese / 2500 Chinese characters — excess is TRUNCATED silently, so put the load-bearing content first.
|
|
400
441
|
- Duration 3-15s (default 5), billed per second. Resolution 720p / 1080p (default). Aspect 16:9 (default) / 9:16 / 1:1 / 4:3 / 3:4.
|
|
401
|
-
- Modes: text-to-video (happyhorse), image-to-video (happyhorse-i2v), reference-to-video (happyhorse-ref2v) — ref2v preserves wired identities; name each reference in the prompt so the binding is explicit.
|
|
442
|
+
- Modes: text-to-video (happyhorse), image-to-video (happyhorse-i2v), reference-to-video (happyhorse-ref2v) — ref2v preserves wired identities; name each reference in the prompt so the binding is explicit, as \`[Image N]\` in array order: your references come first (\`[Image 1]\` is your first), and a wired start frame is appended after them and named as the opening frame.
|
|
402
443
|
|
|
403
444
|
**Style guidance (platform-standard, honestly generic)**
|
|
404
445
|
- One camera movement per shot; physical, quantified action ("slowly raises a hand") over abstract emotion words; state exclusions ("no on-screen text, no watermark") in the prompt.
|
|
@@ -440,6 +481,7 @@ export const PROVIDER_PROMPT_DOCTRINES: readonly ProviderPromptDoctrine[] = [
|
|
|
440
481
|
GROK_IMAGINE_DOCTRINE,
|
|
441
482
|
WAN_DOCTRINE,
|
|
442
483
|
WAN_3_DOCTRINE,
|
|
484
|
+
VIDU_Q4_DOCTRINE,
|
|
443
485
|
HAPPYHORSE_DOCTRINE,
|
|
444
486
|
RUNWAY_KIE_DOCTRINE,
|
|
445
487
|
]
|
package/src/ref-binding.ts
CHANGED
|
@@ -28,10 +28,14 @@
|
|
|
28
28
|
* says the two things a multimodal model needs to hear — match exactly, and
|
|
29
29
|
* these are not frames. One spelling; both resolvers ride it.
|
|
30
30
|
*/
|
|
31
|
-
export function identityRefsSentence(
|
|
31
|
+
export function identityRefsSentence(
|
|
32
|
+
firstOrdinal: number,
|
|
33
|
+
lastOrdinal: number,
|
|
34
|
+
ordinal: (n: number) => string = REF_BINDING.ordinal,
|
|
35
|
+
): string {
|
|
32
36
|
return firstOrdinal === lastOrdinal
|
|
33
|
-
? `${
|
|
34
|
-
: `${
|
|
37
|
+
? `${ordinal(firstOrdinal)} is an identity reference for this shot's subjects — match its subject's exact appearance; it is not a frame.`
|
|
38
|
+
: `${ordinal(firstOrdinal)} through ${ordinal(lastOrdinal)} are identity references for this shot's subjects — match each subject's exact appearance; they are not frames.`
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
export const REF_BINDING = {
|
|
@@ -44,6 +48,27 @@ export const REF_BINDING = {
|
|
|
44
48
|
`Use @image_${n} as the ${role} (${role === "opening" ? "first" : "last"}) frame of the video.`,
|
|
45
49
|
} as const
|
|
46
50
|
|
|
51
|
+
/** A binding sentence's ordinal + frame directive in one provider syntax. */
|
|
52
|
+
export interface ImageBinding {
|
|
53
|
+
ordinal: (n: number) => string
|
|
54
|
+
frame: (n: number, role: "opening" | "closing") => string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The image binding in a provider's OWN reference syntax
|
|
59
|
+
* (`videoProviderReferenceSyntax` in @nodaro/shared). `@image_N` IS
|
|
60
|
+
* {@link REF_BINDING} — the platform swap point, byte for byte; the other two
|
|
61
|
+
* spell the same sentence in the form the provider documents.
|
|
62
|
+
*/
|
|
63
|
+
export function imageBindingFor(syntax: "@image_N" | "@image(n)" | "[Image N]"): ImageBinding {
|
|
64
|
+
if (syntax === "@image_N") return { ordinal: REF_BINDING.ordinal, frame: REF_BINDING.frame }
|
|
65
|
+
const ordinal = syntax === "@image(n)" ? (n: number) => `@image(${n})` : (n: number) => `[Image ${n}]`
|
|
66
|
+
return {
|
|
67
|
+
ordinal,
|
|
68
|
+
frame: (n, role) => `Use ${ordinal(n)} as the ${role} (${role === "opening" ? "first" : "last"}) frame of the video.`,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
47
72
|
/**
|
|
48
73
|
* The instruction that makes Seedance EDIT a wired clip rather than use it as a
|
|
49
74
|
* style reference — the Video to Video node's Seedance lane prepends it to the
|
package/src/resolve-prompt.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { resolveNodeRefs } from "@nodaro/shared/workflow"
|
|
|
2
2
|
import { readPromptAffixes, type PromptAffixes } from "@nodaro/shared/nodes"
|
|
3
3
|
import { SOCIAL_POST_NODE_TYPES } from "@nodaro/shared/social"
|
|
4
4
|
import { joinSentences } from "./hint-join.js"
|
|
5
|
+
import { ownEntry } from "./own-entry.js"
|
|
5
6
|
|
|
6
7
|
export interface ResolvePromptArgs {
|
|
7
8
|
override?: string
|
|
@@ -165,7 +166,7 @@ export function computeNodePrompt(
|
|
|
165
166
|
? [data.directText as string | undefined]
|
|
166
167
|
: []
|
|
167
168
|
} else {
|
|
168
|
-
const fields = NODE_PROMPT_CANDIDATE_FIELDS
|
|
169
|
+
const fields = ownEntry(NODE_PROMPT_CANDIDATE_FIELDS, nodeType) ?? ["prompt"]
|
|
169
170
|
typed = fields.map((f) => data[f] as string | undefined)
|
|
170
171
|
}
|
|
171
172
|
return resolvePrompt({ override, typed, wired, refMap, appendWired, affixes: readPromptAffixes(data) })
|
package/src/seedance-2-inputs.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SEEDANCE_2_REF_LIMITS } from "@nodaro/shared/models"
|
|
2
|
-
import {
|
|
2
|
+
import { placeFramesAfterReferences } from "./frame-reference-slots.js"
|
|
3
3
|
|
|
4
4
|
export type Seedance2Mode = "first-frame" | "first-last-frame" | "reference"
|
|
5
5
|
|
|
@@ -46,18 +46,7 @@ const cleanList = (xs?: readonly string[]): string[] =>
|
|
|
46
46
|
* reference_image_urls (after the user's own images, so their @Image ordinals do
|
|
47
47
|
* not shift) and named in a doctrine-compliant prompt suffix.
|
|
48
48
|
*/
|
|
49
|
-
|
|
50
|
-
* e.g. the gvp overlap transport's colon-position directive ("use @image_1
|
|
51
|
-
* as the first frame, it is the last keyframe of @video_1"). Field finding
|
|
52
|
-
* (2026-07-20): the first-frame directive only works adjacent to the extend
|
|
53
|
-
* colon; a duplicate sentence at the END of the prompt (this resolver's
|
|
54
|
-
* default position) dilutes it back into coin-flip behavior — so when the
|
|
55
|
-
* prompt carries its own binding, the suffix is suppressed (the frame image
|
|
56
|
-
* itself still rides the reference list). */
|
|
57
|
-
export function promptBindsFirstFrame(prompt: string | undefined): boolean {
|
|
58
|
-
if (!prompt) return false
|
|
59
|
-
return /@image_\d+\s+as\s+the\s+(first|opening)\s*(\(first\))?\s*frame/i.test(prompt)
|
|
60
|
-
}
|
|
49
|
+
export { promptBindsFirstFrame } from "./frame-reference-slots.js"
|
|
61
50
|
|
|
62
51
|
export function resolveSeedance2Inputs(args: Seedance2InputsArgs): Seedance2InputsResult {
|
|
63
52
|
const limits = args.limits ?? SEEDANCE_2_REF_LIMITS
|
|
@@ -85,33 +74,19 @@ export function resolveSeedance2Inputs(args: Seedance2InputsArgs): Seedance2Inpu
|
|
|
85
74
|
}
|
|
86
75
|
|
|
87
76
|
// Reference mode: keep both frames (explicit intent), drop trailing user images
|
|
88
|
-
// if the
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
let promptSuffix = ""
|
|
102
|
-
const suppressFrameSentence = promptBindsFirstFrame(args.prompt)
|
|
103
|
-
if (suppressFrameSentence && firstOrdinal > 0 && lastOrdinal === 0) {
|
|
104
|
-
// The prompt already binds the first frame at its own (working) position.
|
|
105
|
-
} else if (firstOrdinal > 0 && lastOrdinal > 0) {
|
|
106
|
-
// Combined sentence — REF_BINDING.frame() emits a single-frame sentence, so use
|
|
107
|
-
// REF_BINDING.ordinal() for each ordinal inline to keep the combined wording AND
|
|
108
|
-
// route both ordinals through the single swap-point.
|
|
109
|
-
promptSuffix = `Use ${REF_BINDING.ordinal(firstOrdinal)} as the opening (first) frame and ${REF_BINDING.ordinal(lastOrdinal)} as the closing (last) frame of the video.`
|
|
110
|
-
} else if (firstOrdinal > 0) {
|
|
111
|
-
promptSuffix = REF_BINDING.frame(firstOrdinal, "opening")
|
|
112
|
-
} else if (lastOrdinal > 0) {
|
|
113
|
-
promptSuffix = REF_BINDING.frame(lastOrdinal, "closing")
|
|
114
|
-
}
|
|
77
|
+
// if the image cap is exceeded. Frames are appended AFTER the kept user images
|
|
78
|
+
// so existing user @Image ordinals are preserved — the platform convention
|
|
79
|
+
// (placeFramesAfterReferences), shared with every frames-in-the-list model.
|
|
80
|
+
const placed = placeFramesAfterReferences({
|
|
81
|
+
prompt: args.prompt,
|
|
82
|
+
firstFrameUrl,
|
|
83
|
+
lastFrameUrl,
|
|
84
|
+
refImageUrls: refImages,
|
|
85
|
+
imageCap: limits.images,
|
|
86
|
+
})
|
|
87
|
+
const referenceImageUrls = placed.imageUrls
|
|
88
|
+
const promptSuffix = placed.promptSuffix
|
|
89
|
+
const droppedRefImages = placed.droppedRefImages
|
|
115
90
|
|
|
116
91
|
return {
|
|
117
92
|
mode: "reference",
|
package/src/veo-i2v-inputs.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { identityRefsSentence, REF_BINDING } from "./video-reference-resolver.js"
|
|
1
|
+
import { placeFramesAfterReferences } from "./frame-reference-slots.js"
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* VEO 3.x i2v input resolution — the mutually-exclusive sibling of
|
|
@@ -8,13 +7,15 @@ import { identityRefsSentence, REF_BINDING } from "./video-reference-resolver.js
|
|
|
8
7
|
* [first(, last)] frames; REFERENCE_2_VIDEO reads every entry as a
|
|
9
8
|
* reference ingredient. Frames and identities cannot ride separate
|
|
10
9
|
* channels, so an anchored call that must carry identity references moves
|
|
11
|
-
* to REFERENCE_2_VIDEO with the
|
|
12
|
-
*
|
|
13
|
-
* same as seedance-2's reference mode).
|
|
10
|
+
* to REFERENCE_2_VIDEO with the frames bound in prose (requested, not
|
|
11
|
+
* pixel-guaranteed — the accepted trade, same as seedance-2's reference mode).
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* THE PLATFORM CONVENTION (2026-10-18, `placeFramesAfterReferences`): the
|
|
14
|
+
* caller's references take the first seats, so their image token N is their
|
|
15
|
+
* Nth reference; the start frame — and the end frame, VEO takes one — follow
|
|
16
|
+
* them, bound as the opening / closing frame. At the 3-ingredient cap the
|
|
17
|
+
* frames are kept (explicit intent) and trailing references drop, reported.
|
|
18
|
+
* (Until then the anchor took seat 1 and an end frame was surrendered.)
|
|
18
19
|
*
|
|
19
20
|
* BYTE-IDENTICAL with no references: plain frame mode, frames kept, no
|
|
20
21
|
* generationType, no suffix — exactly what every veo i2v call has always
|
|
@@ -30,14 +31,15 @@ export interface VeoI2vInputsArgs {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export interface VeoI2vInputsResult {
|
|
33
|
-
/** The `imageUrls` payload: frames in plain mode, [
|
|
34
|
+
/** The `imageUrls` payload: frames in plain mode, [...refs, start, end?] in
|
|
34
35
|
* reference mode — never more than VEO's 3-ingredient cap. */
|
|
35
36
|
imageUrls: string[]
|
|
36
37
|
/** Present (REFERENCE_2_VIDEO) exactly when references ride. */
|
|
37
38
|
generationType?: "REFERENCE_2_VIDEO"
|
|
38
39
|
promptSuffix: string
|
|
39
40
|
droppedRefImages: number
|
|
40
|
-
/**
|
|
41
|
+
/** Always false since the frames-after-references convention: an end frame
|
|
42
|
+
* is kept in reference mode. Kept for callers that log it. */
|
|
41
43
|
droppedEndFrame: boolean
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -55,14 +57,19 @@ export function resolveVeoI2vInputs(args: VeoI2vInputsArgs): VeoI2vInputsResult
|
|
|
55
57
|
droppedEndFrame: false,
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
const placed = placeFramesAfterReferences({
|
|
61
|
+
prompt: args.prompt,
|
|
62
|
+
firstFrameUrl: args.firstFrameUrl,
|
|
63
|
+
lastFrameUrl: args.endFrameUrl,
|
|
64
|
+
refImageUrls: refs,
|
|
65
|
+
imageCap: VEO_INGREDIENT_SLOTS,
|
|
66
|
+
bindIdentities: true,
|
|
67
|
+
})
|
|
61
68
|
return {
|
|
62
|
-
imageUrls:
|
|
69
|
+
imageUrls: placed.imageUrls,
|
|
63
70
|
generationType: "REFERENCE_2_VIDEO",
|
|
64
|
-
promptSuffix:
|
|
65
|
-
droppedRefImages,
|
|
66
|
-
droppedEndFrame:
|
|
71
|
+
promptSuffix: placed.promptSuffix,
|
|
72
|
+
droppedRefImages: placed.droppedRefImages,
|
|
73
|
+
droppedEndFrame: false,
|
|
67
74
|
}
|
|
68
75
|
}
|
|
@@ -54,7 +54,7 @@ import { tidyReferenceTokenGaps, unboundReferenceTokenText } from "./unbound-ref
|
|
|
54
54
|
// The binding surface string and the id-addressed token resolver live in their
|
|
55
55
|
// own modules (see them for the contracts); re-exported here so every existing
|
|
56
56
|
// importer of this module — and the package index's `export *` — keeps working.
|
|
57
|
-
export { REF_BINDING, identityRefsSentence, SEEDANCE_VIDEO_EDIT_PREFIX, buildSeedanceVideoEditPrompt } from "./ref-binding.js"
|
|
57
|
+
export { REF_BINDING, identityRefsSentence, imageBindingFor, SEEDANCE_VIDEO_EDIT_PREFIX, buildSeedanceVideoEditPrompt, type ImageBinding } from "./ref-binding.js"
|
|
58
58
|
export { resolveRefIdTokens, type RefIdTokenContext } from "./ref-id-tokens.js"
|
|
59
59
|
|
|
60
60
|
|