@nodaro/prompts 1.53.2 → 1.54.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-GRSKB3P7.cjs → chunk-56HWABF5.cjs} +52 -2
- package/dist/chunk-56HWABF5.cjs.map +1 -0
- package/dist/{chunk-EKRO42T3.cjs → chunk-CDNW7O7H.cjs} +77 -5
- package/dist/chunk-CDNW7O7H.cjs.map +1 -0
- package/dist/{chunk-WQ7FWGZ2.js → chunk-IVRSNUYX.js} +50 -3
- package/dist/chunk-IVRSNUYX.js.map +1 -0
- package/dist/{chunk-XRCK3BCF.cjs → chunk-XUMEDRML.cjs} +52 -10
- package/dist/chunk-XUMEDRML.cjs.map +1 -0
- package/dist/{chunk-PHCYQWJP.js → chunk-YDG3KMCN.js} +52 -10
- package/dist/chunk-YDG3KMCN.js.map +1 -0
- package/dist/{chunk-XR7RAHZI.js → chunk-Z4KWM6DV.js} +77 -5
- package/dist/chunk-Z4KWM6DV.js.map +1 -0
- package/dist/entries/assembly.cjs +24 -24
- package/dist/entries/assembly.d.cts +9 -0
- package/dist/entries/assembly.d.ts +9 -0
- 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 +47 -35
- package/dist/entries/presets.d.cts +17 -1
- package/dist/entries/presets.d.ts +17 -1
- package/dist/entries/presets.js +1 -1
- package/dist/index.cjs +117 -105
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/package.json +2 -2
- package/src/__tests__/factory-preset-shelves.test.ts +30 -0
- package/src/__tests__/seedance-extend.test.ts +24 -25
- package/src/__tests__/video-reference-features.test.ts +7 -5
- package/src/__tests__/video-reference-seatless-assets.test.ts +132 -0
- package/src/factory-presets/index.ts +2 -0
- package/src/factory-presets/shelves.ts +50 -0
- package/src/prompt-wizard-categories.ts +12 -3
- package/src/provider-prompt-doctrine.ts +44 -5
- package/src/seatless-assets.ts +141 -0
- package/src/video-reference-resolver.ts +31 -4
- package/dist/chunk-EKRO42T3.cjs.map +0 -1
- package/dist/chunk-GRSKB3P7.cjs.map +0 -1
- package/dist/chunk-PHCYQWJP.js.map +0 -1
- package/dist/chunk-WQ7FWGZ2.js.map +0 -1
- package/dist/chunk-XR7RAHZI.js.map +0 -1
- package/dist/chunk-XRCK3BCF.cjs.map +0 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assets that take no image seat: the reference walk for a video model whose
|
|
3
|
+
* image references are its FRAMES (a `keyframes` model — Grok Imagine Video 1.5,
|
|
4
|
+
* Kling 4.0 Keyframes; decided 2026-11-11). Only what is wired into the node's
|
|
5
|
+
* Image References becomes a frame, so a wired character, a character extra or
|
|
6
|
+
* an entity attaches no picture and is numbered nowhere: no `@image_N` may point
|
|
7
|
+
* at a picture that is never sent.
|
|
8
|
+
*
|
|
9
|
+
* What a character still gives is its identity text — its name and description,
|
|
10
|
+
* the one line both reference formats render for a described reference — and a
|
|
11
|
+
* mention of it (`@kira:1`, `@kira:1:smile`) or a `{ref:<id>}` naming it reads as
|
|
12
|
+
* its name. A non-character extra or entity adds nothing.
|
|
13
|
+
*
|
|
14
|
+
* Pure: no I/O, no mutation of inputs. `resolveVideoReferenceCore` calls it when
|
|
15
|
+
* its caller passes `assetsTakeNoSeat`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { DEFAULT_USAGE_MODE, findCharacterMentionTokens, type UsageMode } from "@nodaro/shared/entities"
|
|
19
|
+
import type { ConnectedReference, DescribedReference } from "@nodaro/shared/types"
|
|
20
|
+
|
|
21
|
+
/** The fields of a video extra this walk reads (structural: `VideoExtraRef` fits). */
|
|
22
|
+
export interface SeatlessExtraRef {
|
|
23
|
+
id?: string
|
|
24
|
+
characterSlug?: string
|
|
25
|
+
description?: string
|
|
26
|
+
descriptionOverride?: string
|
|
27
|
+
usageMode?: UsageMode
|
|
28
|
+
elementInjection?: string | null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The fields of a character's metadata this walk reads (structural: `CharacterMeta` fits). */
|
|
32
|
+
export interface SeatlessCharacterMeta {
|
|
33
|
+
characterName?: string
|
|
34
|
+
defaultUsageMode?: UsageMode
|
|
35
|
+
canonicalDescription?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SeatlessAssets {
|
|
39
|
+
/** The prompt with every wired character's mention replaced by its name. */
|
|
40
|
+
prompt: string | undefined
|
|
41
|
+
/** One name + description per character with an identity to give, in wiring order. */
|
|
42
|
+
identities: DescribedReference[]
|
|
43
|
+
/** `{ref:<id>}` names for the character extras, which the walk seats nowhere. */
|
|
44
|
+
extraNamesById: Map<string, string>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The identity text a seatless character gives. Mirrors what the legacy
|
|
49
|
+
* canonical line puts after the name — the per-use override in every mode that
|
|
50
|
+
* renders a line, else the stored description in the modes that carry it, then
|
|
51
|
+
* the wired element injection — minus the usage directive, which instructs how
|
|
52
|
+
* to use a picture the model does not receive.
|
|
53
|
+
*/
|
|
54
|
+
function identityDescription(
|
|
55
|
+
mode: UsageMode,
|
|
56
|
+
override: string | null | undefined,
|
|
57
|
+
canonical: string | null | undefined,
|
|
58
|
+
injection: string | null | undefined,
|
|
59
|
+
): string {
|
|
60
|
+
if (mode === "none" || mode === "name") return ""
|
|
61
|
+
const parts: string[] = []
|
|
62
|
+
const o = override?.trim()
|
|
63
|
+
const c = canonical?.trim()
|
|
64
|
+
if (o) parts.push(o)
|
|
65
|
+
else if ((mode === "identical" || mode === "face-pose") && c) parts.push(c)
|
|
66
|
+
const i = injection?.trim()
|
|
67
|
+
if (i) parts.push(i)
|
|
68
|
+
return parts.join(". ")
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function resolveSeatlessAssets(args: {
|
|
72
|
+
prompt: string | undefined
|
|
73
|
+
/** Every wired character reference, BEFORE canonical suppression. */
|
|
74
|
+
wiredCharRefs: readonly ConnectedReference[]
|
|
75
|
+
/** Character slugs whose canonical fallback the user hid. */
|
|
76
|
+
suppressedCanonicalSlugs: ReadonlySet<string>
|
|
77
|
+
extraRefs?: readonly SeatlessExtraRef[]
|
|
78
|
+
lookupCharacterBySlug?: (slug: string) => SeatlessCharacterMeta | undefined
|
|
79
|
+
}): SeatlessAssets {
|
|
80
|
+
// The reference each wired character is described from: its canonical entry,
|
|
81
|
+
// else the first entry wired for it.
|
|
82
|
+
const bySlug = new Map<string, ConnectedReference>()
|
|
83
|
+
for (const r of args.wiredCharRefs) {
|
|
84
|
+
if (r.source !== "wired-character" || !r.characterSlug) continue
|
|
85
|
+
const known = bySlug.get(r.characterSlug)
|
|
86
|
+
if (!known || (known.variantSlug && !r.variantSlug)) bySlug.set(r.characterSlug, r)
|
|
87
|
+
}
|
|
88
|
+
const nameOf = (slug: string): string => {
|
|
89
|
+
const r = bySlug.get(slug)
|
|
90
|
+
return r?.defaultName || r?.characterSlug || slug
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Mentions read as the character's name (right to left, so offsets hold).
|
|
94
|
+
let prompt = args.prompt
|
|
95
|
+
const mentioned = new Set<string>()
|
|
96
|
+
if (prompt && bySlug.size > 0) {
|
|
97
|
+
const tokens = findCharacterMentionTokens(prompt, [...bySlug.keys()])
|
|
98
|
+
for (const t of [...tokens].sort((a, b) => b.offset - a.offset)) {
|
|
99
|
+
mentioned.add(t.characterSlug)
|
|
100
|
+
prompt = prompt.slice(0, t.offset) + nameOf(t.characterSlug) + prompt.slice(t.offset + t.token.length)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const identities: DescribedReference[] = []
|
|
105
|
+
const described = new Set<string>()
|
|
106
|
+
const describe = (slug: string, name: string, description: string): void => {
|
|
107
|
+
if (described.has(slug)) return
|
|
108
|
+
described.add(slug)
|
|
109
|
+
if (description) identities.push({ name, description })
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// A wired character gives its identity unless the user hid its canonical
|
|
113
|
+
// fallback and the prompt does not mention it either.
|
|
114
|
+
for (const [slug, r] of bySlug) {
|
|
115
|
+
const shown = !args.suppressedCanonicalSlugs.has(slug) || mentioned.has(slug)
|
|
116
|
+
if (!shown) continue
|
|
117
|
+
describe(
|
|
118
|
+
slug,
|
|
119
|
+
nameOf(slug),
|
|
120
|
+
identityDescription(r.defaultUsageMode ?? DEFAULT_USAGE_MODE, r.descriptionOverride, r.characterCanonicalDescription, r.elementInjection),
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// A character extra (a variant picked on the node) speaks for its character
|
|
125
|
+
// once; a non-character extra has no frame and no text.
|
|
126
|
+
const extraNamesById = new Map<string, string>()
|
|
127
|
+
for (const ex of args.extraRefs ?? []) {
|
|
128
|
+
if (!ex.characterSlug) continue
|
|
129
|
+
const meta = args.lookupCharacterBySlug?.(ex.characterSlug)
|
|
130
|
+
const name = bySlug.has(ex.characterSlug) ? nameOf(ex.characterSlug) : meta?.characterName || ex.characterSlug
|
|
131
|
+
if (ex.id) extraNamesById.set(ex.id, name)
|
|
132
|
+
const own = (ex.descriptionOverride ?? "").trim() || (ex.description ?? "").trim()
|
|
133
|
+
describe(
|
|
134
|
+
ex.characterSlug,
|
|
135
|
+
name,
|
|
136
|
+
identityDescription(ex.usageMode ?? meta?.defaultUsageMode ?? DEFAULT_USAGE_MODE, own, meta?.canonicalDescription, ex.elementInjection),
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return { prompt, identities, extraNamesById }
|
|
141
|
+
}
|
|
@@ -50,6 +50,7 @@ import { REF_BINDING } from "./ref-binding.js"
|
|
|
50
50
|
import { resolveRefIdTokens } from "./ref-id-tokens.js"
|
|
51
51
|
import { insertBeforeStyleSection } from "./prompt-style-section.js"
|
|
52
52
|
import { tidyReferenceTokenGaps, unboundReferenceTokenText } from "./unbound-reference-tokens.js"
|
|
53
|
+
import { resolveSeatlessAssets } from "./seatless-assets.js"
|
|
53
54
|
|
|
54
55
|
// The binding surface string and the id-addressed token resolver live in their
|
|
55
56
|
// own modules (see them for the contracts); re-exported here so every existing
|
|
@@ -268,6 +269,15 @@ export interface ResolveVideoReferenceCoreArgs {
|
|
|
268
269
|
*/
|
|
269
270
|
videoCaptions?: readonly string[]
|
|
270
271
|
audioCaptions?: readonly string[]
|
|
272
|
+
/**
|
|
273
|
+
* No asset takes an image seat: set for a model whose image references are
|
|
274
|
+
* its FRAMES (the `keyframes` feature), where only what is wired into Image
|
|
275
|
+
* References is sent. Wired characters and extras attach no URL and are
|
|
276
|
+
* numbered nowhere; a character gives its name and description in both
|
|
277
|
+
* formats, and its mentions and `{ref:<id>}` read as its name. See
|
|
278
|
+
* `seatless-assets.ts`.
|
|
279
|
+
*/
|
|
280
|
+
assetsTakeNoSeat?: boolean
|
|
271
281
|
}
|
|
272
282
|
|
|
273
283
|
/** Result of the HYBRID mention pass — inline role phrases + surfaced opt-in
|
|
@@ -477,8 +487,21 @@ export function resolveVideoReferenceCore(
|
|
|
477
487
|
// below and the main assembly have to carry them. They attach no URL and claim
|
|
478
488
|
// no `@image_N` seat, so they take no part in the numbering walk. Empty for
|
|
479
489
|
// every caller that sends neither → every existing output is untouched.
|
|
490
|
+
// On a frames model (`assetsTakeNoSeat`) the wired characters join them as
|
|
491
|
+
// identity lines: no asset is seated, so the whole walk below is skipped.
|
|
492
|
+
const seatless = args.assetsTakeNoSeat === true
|
|
493
|
+
? resolveSeatlessAssets({
|
|
494
|
+
prompt: args.prompt,
|
|
495
|
+
wiredCharRefs: args.wiredCharRefs,
|
|
496
|
+
suppressedCanonicalSlugs: new Set(args.suppressedCanonicalCharacterIds ?? []),
|
|
497
|
+
extraRefs: args.extraRefs,
|
|
498
|
+
lookupCharacterBySlug: args.lookupCharacterBySlug,
|
|
499
|
+
})
|
|
500
|
+
: undefined
|
|
480
501
|
const describedAndCaptionLines = [
|
|
481
|
-
...renderDescribedReferenceLines(
|
|
502
|
+
...renderDescribedReferenceLines(
|
|
503
|
+
seatless ? [...seatless.identities, ...(args.describedReferences ?? [])] : args.describedReferences,
|
|
504
|
+
),
|
|
482
505
|
...renderReferenceCaptionLines(args.videoCaptions, args.audioCaptions, {
|
|
483
506
|
video: args.videoRefCount ?? 0,
|
|
484
507
|
audio: args.audioRefCount ?? 0,
|
|
@@ -510,6 +533,8 @@ export function resolveVideoReferenceCore(
|
|
|
510
533
|
for (const ex of args.extraRefs ?? []) {
|
|
511
534
|
if (ex.id && !nameByRefId.has(ex.id)) nameByRefId.set(ex.id, "")
|
|
512
535
|
}
|
|
536
|
+
// Seated nowhere, a character extra's `{ref:<id>}` reads as its character.
|
|
537
|
+
for (const [id, name] of seatless?.extraNamesById ?? []) nameByRefId.set(id, name)
|
|
513
538
|
for (const [id, name] of args.refNamesById ?? []) {
|
|
514
539
|
if (id) nameByRefId.set(id, name)
|
|
515
540
|
}
|
|
@@ -517,8 +542,10 @@ export function resolveVideoReferenceCore(
|
|
|
517
542
|
// recovered from URLs afterwards (the walk counts a duplicate-URL extra that
|
|
518
543
|
// `merged` dedups, so URL → index is ambiguous; id → position is not).
|
|
519
544
|
const slotByRefId = new Map<string, number>()
|
|
520
|
-
if (wiredCharRefs.length === 0 && !hasExtras) {
|
|
521
|
-
// No wired chars / extras
|
|
545
|
+
if (seatless || (wiredCharRefs.length === 0 && !hasExtras)) {
|
|
546
|
+
// No wired chars / extras — or none that may take a seat (`seatless`, whose
|
|
547
|
+
// characters already ride `describedAndCaptionLines` and whose mentions are
|
|
548
|
+
// already names) — but the node can still carry plain base reference
|
|
522
549
|
// images (leadingRefUrls), so `{image:N}` body tokens MUST still resolve. The
|
|
523
550
|
// count is the leading-ref count (or the legacy `imageRefCount` when no
|
|
524
551
|
// leading refs were passed); the leading URLs are returned for the payload.
|
|
@@ -528,7 +555,7 @@ export function resolveVideoReferenceCore(
|
|
|
528
555
|
// leadingRefUrls mode counts the leading refs; ordinalOffset mode counts the
|
|
529
556
|
// caller-owned leading refs the offset stands in for.
|
|
530
557
|
const resolved = resolveReferenceTokens(
|
|
531
|
-
resolveRefIdTokens(args.prompt, { slotById: slotByRefId, nameById: nameByRefId, imageCount: counts.image }),
|
|
558
|
+
resolveRefIdTokens(seatless ? seatless.prompt : args.prompt, { slotById: slotByRefId, nameById: nameByRefId, imageCount: counts.image }),
|
|
532
559
|
counts,
|
|
533
560
|
)
|
|
534
561
|
return {
|