@nodaro/prompts 1.16.0 → 1.17.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/index.cjs +179 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +121 -2
- package/dist/index.d.ts +121 -2
- package/dist/index.js +176 -50
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/assemble-image-input.test.ts +30 -0
- package/src/__tests__/described-references-video.test.ts +250 -0
- package/src/__tests__/described-references.test.ts +595 -0
- package/src/assemble-image-input.ts +11 -1
- package/src/described-references.ts +138 -0
- package/src/index.ts +1 -0
- package/src/prompt-builder.ts +196 -19
- package/src/video-reference-resolver.ts +85 -10
|
@@ -32,7 +32,13 @@ import { findCharacterMentionTokens, type CharacterMentionTokenInfo } from "@nod
|
|
|
32
32
|
import { resolveCharacterMentions, applyReferenceOrderToVideo } from "./prompt-builder.js"
|
|
33
33
|
import { roleToPhrase, REFERENCE_ROLE_PRESETS, resolveDefaultRole } from "@nodaro/shared"
|
|
34
34
|
import { buildIdentityLockLine, withForcedIdentityLock } from "./identity-lock.js"
|
|
35
|
-
import type { ConnectedReference } from "@nodaro/shared"
|
|
35
|
+
import type { ConnectedReference, DescribedReference } from "@nodaro/shared"
|
|
36
|
+
import {
|
|
37
|
+
appendReferenceLines,
|
|
38
|
+
referenceDescriptionLine,
|
|
39
|
+
renderDescribedReferenceLines,
|
|
40
|
+
renderReferenceCaptionLines,
|
|
41
|
+
} from "./described-references.js"
|
|
36
42
|
import { REF_BINDING } from "./ref-binding.js"
|
|
37
43
|
import { resolveRefIdTokens } from "./ref-id-tokens.js"
|
|
38
44
|
import { insertBeforeStyleSection } from "./prompt-style-section.js"
|
|
@@ -152,6 +158,13 @@ export interface VideoExtraRef {
|
|
|
152
158
|
* orchestrator extras leave this unset (they resolve via `CharacterMeta`).
|
|
153
159
|
*/
|
|
154
160
|
defaultRole?: string
|
|
161
|
+
/**
|
|
162
|
+
* The `ConnectedReference.descriptionOverride` analogue — the PER-USE identity
|
|
163
|
+
* description for this extra. Fills the row's own description slot (the
|
|
164
|
+
* pair-back tail, the name-mode bullet, the first-sight descriptor), winning
|
|
165
|
+
* over `description`, which keeps its label semantics. Absent → unchanged.
|
|
166
|
+
*/
|
|
167
|
+
descriptionOverride?: string
|
|
155
168
|
}
|
|
156
169
|
|
|
157
170
|
/**
|
|
@@ -230,6 +243,24 @@ export interface ResolveVideoReferenceCoreArgs {
|
|
|
230
243
|
* The wired character refs' `defaultName`s are known without this.
|
|
231
244
|
*/
|
|
232
245
|
refNamesById?: ReadonlyMap<string, string>
|
|
246
|
+
/**
|
|
247
|
+
* References the caller can NAME and DESCRIBE but has no media for — an
|
|
248
|
+
* un-bound cast role, an analysis slot. They attach no URL and claim no
|
|
249
|
+
* `@image_N` seat: they are rendered as prose by `renderDescribedReferenceLines`
|
|
250
|
+
* and joined the way this lane joins its trailing directives. The
|
|
251
|
+
* described-ONLY request (no wired characters, no extras) is the normal case,
|
|
252
|
+
* so the early return below carries the join too.
|
|
253
|
+
*/
|
|
254
|
+
describedReferences?: readonly DescribedReference[]
|
|
255
|
+
/**
|
|
256
|
+
* Captions for the node's video / audio rail references, INDEX-ALIGNED with
|
|
257
|
+
* the caller's `referenceVideoUrls` / `referenceAudioUrls`. Rendered as
|
|
258
|
+
* `@video_N: <caption>.` / `@audio_N: <caption>.` and bounded by
|
|
259
|
+
* `videoRefCount` / `audioRefCount`, so a caption for a url the provider cap
|
|
260
|
+
* dropped never binds a slot the payload does not ship.
|
|
261
|
+
*/
|
|
262
|
+
videoCaptions?: readonly string[]
|
|
263
|
+
audioCaptions?: readonly string[]
|
|
233
264
|
}
|
|
234
265
|
|
|
235
266
|
/** Result of the HYBRID mention pass — inline role phrases + surfaced opt-in
|
|
@@ -368,6 +399,11 @@ function resolveVideoCharacterMentionsHybrid(
|
|
|
368
399
|
const binding = bindingFor(m.url)
|
|
369
400
|
const lock = buildIdentityLockLine(withForcedIdentityLock(ref, lockOverrideByUrl.get(m.url)), binding)
|
|
370
401
|
if (lock) lockLines.push(lock)
|
|
402
|
+
// A per-use `descriptionOverride` has no slot in a hybrid role phrase, so it
|
|
403
|
+
// rides the trailing-directive channel as its own binding-subject line —
|
|
404
|
+
// exactly as on the image lane (`pushOverrideDirective`).
|
|
405
|
+
const overrideLine = referenceDescriptionLine(binding, ref.descriptionOverride)
|
|
406
|
+
if (overrideLine) elementDirectives.push(overrideLine)
|
|
371
407
|
const inject = ref.elementInjection?.trim()
|
|
372
408
|
if (inject) elementDirectives.push(inject)
|
|
373
409
|
}
|
|
@@ -429,6 +465,18 @@ export function resolveVideoReferenceCore(
|
|
|
429
465
|
video: args.videoRefCount ?? 0,
|
|
430
466
|
audio: args.audioRefCount ?? 0,
|
|
431
467
|
})
|
|
468
|
+
// Described references (name + description, no media) and the video/audio rail
|
|
469
|
+
// captions — rendered ONCE, ahead of every exit, because both the early return
|
|
470
|
+
// below and the main assembly have to carry them. They attach no URL and claim
|
|
471
|
+
// no `@image_N` seat, so they take no part in the numbering walk. Empty for
|
|
472
|
+
// every caller that sends neither → every existing output is untouched.
|
|
473
|
+
const describedAndCaptionLines = [
|
|
474
|
+
...renderDescribedReferenceLines(args.describedReferences),
|
|
475
|
+
...renderReferenceCaptionLines(args.videoCaptions, args.audioCaptions, {
|
|
476
|
+
video: args.videoRefCount ?? 0,
|
|
477
|
+
audio: args.audioRefCount ?? 0,
|
|
478
|
+
}),
|
|
479
|
+
]
|
|
432
480
|
let wiredCharRefs = [...args.wiredCharRefs]
|
|
433
481
|
const suppressedSlugs = new Set(args.suppressedCanonicalCharacterIds ?? [])
|
|
434
482
|
if (suppressedSlugs.size > 0) {
|
|
@@ -469,14 +517,20 @@ export function resolveVideoReferenceCore(
|
|
|
469
517
|
// leading refs were passed); the leading URLs are returned for the payload.
|
|
470
518
|
// Nothing was seated, so every `{ref:}` degrades (label → name → "").
|
|
471
519
|
const counts = tokenCounts(leadingRefUrls.length)
|
|
520
|
+
// tokenCounts(leadingRefUrls.length) → image count == offset (no assets here):
|
|
521
|
+
// leadingRefUrls mode counts the leading refs; ordinalOffset mode counts the
|
|
522
|
+
// caller-owned leading refs the offset stands in for.
|
|
523
|
+
const resolved = resolveReferenceTokens(
|
|
524
|
+
resolveRefIdTokens(args.prompt, { slotById: slotByRefId, nameById: nameByRefId, imageCount: counts.image }),
|
|
525
|
+
counts,
|
|
526
|
+
)
|
|
472
527
|
return {
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
//
|
|
476
|
-
prompt:
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
),
|
|
528
|
+
// Described references and rail captions are the ONE thing this branch can
|
|
529
|
+
// still contribute: they need no seat, so a request that carries only them
|
|
530
|
+
// (a story landing before any entity exists) reaches the model here.
|
|
531
|
+
prompt: describedAndCaptionLines.length > 0
|
|
532
|
+
? appendReferenceLines(resolved ?? "", describedAndCaptionLines, args.hybridRoles === true ? "hybrid" : "legacy")
|
|
533
|
+
: resolved,
|
|
480
534
|
additionalUrls: [...leadingRefUrls],
|
|
481
535
|
}
|
|
482
536
|
}
|
|
@@ -579,6 +633,8 @@ export function resolveVideoReferenceCore(
|
|
|
579
633
|
canonicalPhrases.push(roleToPhrase(resolveDefaultRole(r.defaultRole, r.defaultUsageMode, r.source), binding))
|
|
580
634
|
const lock = buildIdentityLockLine(r, binding)
|
|
581
635
|
if (lock) canonicalLockLines.push(lock)
|
|
636
|
+
const overrideLine = referenceDescriptionLine(binding, r.descriptionOverride)
|
|
637
|
+
if (overrideLine) canonicalElementDirectives.push(overrideLine)
|
|
582
638
|
const inject = r.elementInjection?.trim()
|
|
583
639
|
if (inject) canonicalElementDirectives.push(inject)
|
|
584
640
|
continue
|
|
@@ -601,7 +657,13 @@ export function resolveVideoReferenceCore(
|
|
|
601
657
|
// mirrors the shared image-side `composeIdentityDescPart`. The subject here
|
|
602
658
|
// is the bare display name (video numbering is applied separately above).
|
|
603
659
|
const descBodyParts: string[] = []
|
|
604
|
-
|
|
660
|
+
// The per-use override IS the caller describing this subject for this run,
|
|
661
|
+
// so it fills the identity slot ahead of the stored canonical description —
|
|
662
|
+
// and rides every mode that emits a bullet at all ("none" returned above).
|
|
663
|
+
const canonicalOverride = r.descriptionOverride?.trim()
|
|
664
|
+
if (canonicalOverride) {
|
|
665
|
+
descBodyParts.push(canonicalOverride)
|
|
666
|
+
} else if (includeCanonicalDesc && r.characterCanonicalDescription?.trim()) {
|
|
605
667
|
descBodyParts.push(r.characterCanonicalDescription.trim())
|
|
606
668
|
}
|
|
607
669
|
if (r.elementInjection?.trim()) descBodyParts.push(r.elementInjection.trim())
|
|
@@ -637,7 +699,9 @@ export function resolveVideoReferenceCore(
|
|
|
637
699
|
if (!ex.url) continue
|
|
638
700
|
position += 1
|
|
639
701
|
if (ex.id && !slotByRefId.has(ex.id)) slotByRefId.set(ex.id, position)
|
|
640
|
-
|
|
702
|
+
// Per-use override fills this row's description slot; `description` keeps
|
|
703
|
+
// its own label semantics (mirrors the image extras).
|
|
704
|
+
const desc = (ex.descriptionOverride ?? "").trim() || (ex.description ?? "").trim()
|
|
641
705
|
if (ex.characterSlug) {
|
|
642
706
|
// First sight of this character via an extra. Resolution chain
|
|
643
707
|
// matches the image side: per-ref override → upstream character
|
|
@@ -820,6 +884,17 @@ export function resolveVideoReferenceCore(
|
|
|
820
884
|
if (u && !seen.has(u)) { seen.add(u); merged.push(u) }
|
|
821
885
|
}
|
|
822
886
|
|
|
887
|
+
// Described references + rail captions — joined the way this format joins its
|
|
888
|
+
// own trailing directives (hybrid: ahead of the `[style]` section; legacy:
|
|
889
|
+
// bullets consolidated into the "Use these characters:" block the branch above
|
|
890
|
+
// may just have created). Ahead of the reorder, like every other assembled
|
|
891
|
+
// line; they carry no `@image_N` binding for the renumber to move.
|
|
892
|
+
finalPrompt = appendReferenceLines(
|
|
893
|
+
finalPrompt,
|
|
894
|
+
describedAndCaptionLines,
|
|
895
|
+
hybrid ? "hybrid" : "legacy",
|
|
896
|
+
)
|
|
897
|
+
|
|
823
898
|
// `{ref:<id>}` tokens resolve HERE — after the walk has seated every reference
|
|
824
899
|
// (the slot map is complete) and BEFORE the user reorder below, so the
|
|
825
900
|
// reorder's `@image_N` renumber pass carries the freshly emitted binding to
|