@nodaro/shared 2.17.0 → 2.18.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 +9 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/apply-video-negative-prompt.test.ts +38 -1
- package/src/index.ts +1 -0
- package/src/model-constants.ts +35 -1
package/package.json
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest"
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
applyVideoNegativePrompt,
|
|
4
|
+
videoNegativeSuffix,
|
|
5
|
+
NATIVE_NEGATIVE_VIDEO_PROVIDERS,
|
|
6
|
+
} from "../model-constants.js"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A composed prompt that ends with the `[style]` section — what
|
|
10
|
+
* `@nodaro/prompts` emits for any run that picks a look dimension. The section
|
|
11
|
+
* has no terminator, so a `"\n"`-joined `Avoid:` reads as one more look clause
|
|
12
|
+
* under its header.
|
|
13
|
+
*/
|
|
14
|
+
const SECTIONED = "a knight rides\n\n[style]:\nanime style\nwide shot"
|
|
15
|
+
|
|
16
|
+
describe("videoNegativeSuffix", () => {
|
|
17
|
+
it("joins on one newline for an ordinary prompt", () => {
|
|
18
|
+
expect(videoNegativeSuffix("blurry", "a knight rides")).toBe("\nAvoid: blurry")
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it("closes an open `[style]` section with a blank line", () => {
|
|
22
|
+
expect(videoNegativeSuffix("blurry", SECTIONED)).toBe("\n\nAvoid: blurry")
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it("joins on one newline once something else closed the section", () => {
|
|
26
|
+
expect(videoNegativeSuffix("blurry", `${SECTIONED}\n\nthe person from @image_1`))
|
|
27
|
+
.toBe("\nAvoid: blurry")
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it("is the WIDEST form with no base — what a caller reserving room must budget", () => {
|
|
31
|
+
// `effectiveVideoPromptCeiling` reserves before the prompt exists.
|
|
32
|
+
expect(videoNegativeSuffix("blurry")).toBe("\n\nAvoid: blurry")
|
|
33
|
+
})
|
|
34
|
+
})
|
|
3
35
|
|
|
4
36
|
describe("applyVideoNegativePrompt", () => {
|
|
5
37
|
it("returns prompt unchanged + nativeNegativePrompt when provider is native", () => {
|
|
@@ -14,6 +46,11 @@ describe("applyVideoNegativePrompt", () => {
|
|
|
14
46
|
expect(result.nativeNegativePrompt).toBeUndefined()
|
|
15
47
|
})
|
|
16
48
|
|
|
49
|
+
it("separates 'Avoid: …' from an unterminated `[style]` section", () => {
|
|
50
|
+
const result = applyVideoNegativePrompt(SECTIONED, "blurry", "wan-animate-move")
|
|
51
|
+
expect(result.prompt).toBe(`${SECTIONED}\n\nAvoid: blurry`)
|
|
52
|
+
})
|
|
53
|
+
|
|
17
54
|
it("returns 'Avoid: …' alone when prompt is empty and provider is non-native", () => {
|
|
18
55
|
const result = applyVideoNegativePrompt("", "blurry", "veo3")
|
|
19
56
|
expect(result.prompt).toBe("Avoid: blurry")
|
package/src/index.ts
CHANGED
package/src/model-constants.ts
CHANGED
|
@@ -295,6 +295,35 @@ export const NATIVE_NEGATIVE_VIDEO_PROVIDERS = new Set<string>([
|
|
|
295
295
|
"wan-s2v",
|
|
296
296
|
])
|
|
297
297
|
|
|
298
|
+
/**
|
|
299
|
+
* The `[style]` section header, verbatim. A composed prompt that ends with that
|
|
300
|
+
* section has NO terminator (`@nodaro/prompts`' `prompt-style-section.ts` owns
|
|
301
|
+
* the shape and the reasoning), so a `"\n"`-joined `Avoid:` would read as one
|
|
302
|
+
* more look clause under its header. Duplicated rather than imported because
|
|
303
|
+
* `@nodaro/prompts` depends on THIS package and never the reverse; the two are
|
|
304
|
+
* pinned together behaviourally by that package's
|
|
305
|
+
* `__tests__/style-section-boundary.test.ts`, which runs the real composer
|
|
306
|
+
* through this clamp.
|
|
307
|
+
*/
|
|
308
|
+
const STYLE_SECTION_HEADER_LINE = "[style]:\n"
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* The `Avoid: …` suffix `applyVideoNegativePrompt` folds into a prompt, with the
|
|
312
|
+
* separator the base prompt calls for: a blank line when the base ends INSIDE an
|
|
313
|
+
* unterminated `[style]` section, a single newline otherwise.
|
|
314
|
+
*
|
|
315
|
+
* `base` omitted = the WIDEST form, which is what a caller reserving room before
|
|
316
|
+
* the prompt exists must budget for (`effectiveVideoPromptCeiling`).
|
|
317
|
+
*/
|
|
318
|
+
export function videoNegativeSuffix(negativePrompt: string, base?: string): string {
|
|
319
|
+
const gapAt = base?.lastIndexOf("\n\n") ?? -1
|
|
320
|
+
const lastBlock = base === undefined ? undefined : (gapAt >= 0 ? base.slice(gapAt + 2) : base)
|
|
321
|
+
const separator = lastBlock === undefined || lastBlock.startsWith(STYLE_SECTION_HEADER_LINE)
|
|
322
|
+
? "\n\n"
|
|
323
|
+
: "\n"
|
|
324
|
+
return `${separator}Avoid: ${negativePrompt}`
|
|
325
|
+
}
|
|
326
|
+
|
|
298
327
|
/**
|
|
299
328
|
* Apply the negative prompt to a video-provider request.
|
|
300
329
|
*
|
|
@@ -306,6 +335,8 @@ export const NATIVE_NEGATIVE_VIDEO_PROVIDERS = new Set<string>([
|
|
|
306
335
|
* Otherwise, the helper appends `Avoid: <negativePrompt>` to the prompt
|
|
307
336
|
* (consistent with `buildImagePrompt` in `prompt-builder.ts`) so the model
|
|
308
337
|
* still sees the negative intent, and returns `nativeNegativePrompt = undefined`.
|
|
338
|
+
* The line is separated by {@link videoNegativeSuffix}, which closes an open
|
|
339
|
+
* `[style]` section with a blank line rather than reading as a look clause.
|
|
309
340
|
*
|
|
310
341
|
* Empty / missing negative is a no-op.
|
|
311
342
|
*
|
|
@@ -338,7 +369,10 @@ export function applyVideoNegativePrompt(
|
|
|
338
369
|
// assembler), then clamp the whole to the model cap.
|
|
339
370
|
const base = prompt && prompt.trim().length > 0 ? prompt : ""
|
|
340
371
|
if (!base) return { prompt: `Avoid: ${neg}`.slice(0, promptMax), nativeNegativePrompt: undefined }
|
|
341
|
-
|
|
372
|
+
// The separator is decided on the WHOLE base: the clamp below can only cut the
|
|
373
|
+
// tail, and a cut either leaves the `[style]` section behind or lands inside
|
|
374
|
+
// it, so the suffix never needs to grow after the reservation.
|
|
375
|
+
const avoid = videoNegativeSuffix(neg, base)
|
|
342
376
|
const room = Math.max(0, promptMax - avoid.length)
|
|
343
377
|
const injected = `${base.slice(0, room)}${avoid}`.slice(0, promptMax)
|
|
344
378
|
return { prompt: injected, nativeNegativePrompt: undefined }
|