@nodaro/shared 1.23.0 → 1.24.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 +116 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -4
- package/dist/index.d.ts +22 -4
- package/dist/index.js +116 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/video-analysis-catalog-sync.test.ts +14 -6
- package/src/__tests__/video-analysis-pricing.test.ts +6 -1
- package/src/llm-models.ts +37 -6
- package/src/model-catalog.ts +41 -20
- package/src/video-analysis-pricing.ts +24 -16
- package/src/video-analysis.ts +28 -0
package/package.json
CHANGED
|
@@ -86,17 +86,25 @@ describe("bare video-analysis node-type credit id", () => {
|
|
|
86
86
|
for (const [id, credits] of Object.entries(VIDEO_ANALYSIS_BUCKET_CREDITS)) {
|
|
87
87
|
expect(credits, `${id} exceeds the bare-id ceiling ${ceiling}`).toBeLessThanOrEqual(ceiling)
|
|
88
88
|
}
|
|
89
|
-
// The migration writes this number; keep them in lockstep (277 wrote 200,
|
|
90
|
-
//
|
|
91
|
-
|
|
89
|
+
// The migration writes this number; keep them in lockstep (277 wrote 200,
|
|
90
|
+
// 279 wrote 739, 280 writes 346 — every tier now dispatches one identical
|
|
91
|
+
// analyzer pass, so the whole schedule flattened and the ceiling fell with it).
|
|
92
|
+
expect(ceiling).toBe(346)
|
|
92
93
|
})
|
|
93
94
|
|
|
94
|
-
it("the default tier at the ceiling bucket
|
|
95
|
-
// Pins the reason the bare id is the table
|
|
95
|
+
it("the bare id still bounds the default tier at the ceiling bucket", () => {
|
|
96
|
+
// Pins the reason the bare id is the table MAX rather than the intuitive
|
|
96
97
|
// "default model, longest video" value, so nobody re-derives it wrongly.
|
|
98
|
+
//
|
|
99
|
+
// Was `toBeLessThan`, on the premise that `mixed` cost strictly more than the
|
|
100
|
+
// default tier. That premise is gone: all tiers resolve to the same single
|
|
101
|
+
// pass, so every row at a given bucket is equal and the correct assertion is
|
|
102
|
+
// the INVARIANT (the bare id must never under-quote), not the old strict gap.
|
|
103
|
+
// Should tiers ever diverge again this still holds, and the ceiling check
|
|
104
|
+
// above catches any row that outgrows it.
|
|
97
105
|
const defaultAtCeiling = VIDEO_ANALYSIS_BUCKET_CREDITS[
|
|
98
106
|
buildVideoAnalysisCreditId(DEFAULT_VIDEO_ANALYSIS_MODEL, VIDEO_ANALYSIS_MAX_DURATION_SEC)
|
|
99
107
|
]!
|
|
100
|
-
expect(defaultAtCeiling).
|
|
108
|
+
expect(defaultAtCeiling).toBeLessThanOrEqual(Math.max(...Object.values(VIDEO_ANALYSIS_BUCKET_CREDITS)))
|
|
101
109
|
})
|
|
102
110
|
})
|
|
@@ -84,9 +84,14 @@ describe("video-analysis-pricing", () => {
|
|
|
84
84
|
expect(DEFAULT_VIDEO_ANALYSIS_TIER).toBe("pro")
|
|
85
85
|
expect(DEFAULT_VIDEO_ANALYSIS_MODEL).toBe("gemini-3.1-pro")
|
|
86
86
|
expect(resolveVideoAnalysisModel("pro")).toBe("gemini-3.1-pro")
|
|
87
|
-
|
|
87
|
+
// The economy tiers moved to the cheaper flash generation when they moved to
|
|
88
|
+
// the cheaper transport; the newer flash now sits behind the `smart` sentinel.
|
|
89
|
+
expect(resolveVideoAnalysisModel("fast")).toBe("gemini-3-flash")
|
|
88
90
|
expect(resolveVideoAnalysisModel("mixed")).toBe("mixed") // roll-plan sentinel passthrough
|
|
89
91
|
expect(resolveVideoAnalysisModel("mixed-fast")).toBe("mixed-fast")
|
|
92
|
+
// `smart` is a sentinel too — it names an engine plan, never a model id, so the
|
|
93
|
+
// engine behind it stays unpublished exactly as the mixed plans' does.
|
|
94
|
+
expect(resolveVideoAnalysisModel("smart")).toBe("smart")
|
|
90
95
|
// Raw passthrough keeps LEGACY stored configs running (and priced) on the
|
|
91
96
|
// exact model they were saved with — never silently re-tiered.
|
|
92
97
|
expect(resolveVideoAnalysisModel("gemini-3-flash")).toBe("gemini-3-flash")
|
package/src/llm-models.ts
CHANGED
|
@@ -212,6 +212,17 @@ export const LLM_MODELS: readonly LlmModelDef[] = [
|
|
|
212
212
|
// Google documents low/medium/high for 3.1 Pro — no `minimal` tier, so
|
|
213
213
|
// this ladder is deliberately shorter than the flash models'.
|
|
214
214
|
directReasoningEfforts: ["low", "medium", "high"],
|
|
215
|
+
// NO `reasoningEfforts` (the proxied ladder) ON PURPOSE, not an omission.
|
|
216
|
+
// That endpoint accepts `reasoning_effort: low | high` and already DEFAULTS
|
|
217
|
+
// to "high", so leaving it unset gets its deepest setting and a ladder here
|
|
218
|
+
// would unlock nothing. The two lanes are NOT equivalent at their maxima:
|
|
219
|
+
// verified 2026-07-29 on identical input, the proxy's ceiling produces
|
|
220
|
+
// roughly a quarter of the reasoning tokens that the direct lane's
|
|
221
|
+
// `thinkingLevel: HIGH` does, and it does not expose Google's deeper tiers
|
|
222
|
+
// at all. That difference is visible in fine-detail reading (small on-screen
|
|
223
|
+
// text such as a name badge), which is exactly what video-analysis casts
|
|
224
|
+
// identities from — so adding an entry here would silently move analysis to
|
|
225
|
+
// the shallower lane. Don't. Full comparison lives with the analysis engine.
|
|
215
226
|
// The ONE Gemini model routed direct-first. It is the premium/low-volume
|
|
216
227
|
// tier (video-analysis `pro`, no LLM_FEATURE_DEFAULTS entry), so the ~4×
|
|
217
228
|
// list-price premium lands on the smallest call volume — and it is where
|
|
@@ -666,7 +677,7 @@ export const VIDEO_ANALYSIS_LLM_MODELS: string[] = LLM_MODELS
|
|
|
666
677
|
* VIDEO_ANALYSIS_LLM_MODELS member AND every such model is reachable by a tier,
|
|
667
678
|
* so adding a video model forces a tier decision instead of silently leaking.
|
|
668
679
|
*/
|
|
669
|
-
export const VIDEO_ANALYSIS_TIERS = { fast: "gemini-3
|
|
680
|
+
export const VIDEO_ANALYSIS_TIERS = { fast: "gemini-3-flash", pro: "gemini-3.1-pro" } as const
|
|
670
681
|
export type VideoAnalysisModelTier = keyof typeof VIDEO_ANALYSIS_TIERS
|
|
671
682
|
/**
|
|
672
683
|
* Models that PREVIOUSLY backed a tier, mapped to the tier they backed.
|
|
@@ -680,8 +691,13 @@ export type VideoAnalysisModelTier = keyof typeof VIDEO_ANALYSIS_TIERS
|
|
|
680
691
|
* two-sided decision.
|
|
681
692
|
*/
|
|
682
693
|
export const VIDEO_ANALYSIS_LEGACY_MODELS: Record<string, VideoAnalysisModelTier> = {
|
|
683
|
-
// fast tier
|
|
684
|
-
|
|
694
|
+
// Backed the fast tier from 2026-07 until 2026-07-29, when the economy tiers
|
|
695
|
+
// moved to the cheaper lane and the older, cheaper flash. It is now the engine
|
|
696
|
+
// behind the `smart` tier — but `smart` is a SENTINEL that never exposes a model
|
|
697
|
+
// id, so as far as this map is concerned it no longer backs a selectable tier and
|
|
698
|
+
// belongs here: stored raw `llmModel` values keep resolving and keep pricing
|
|
699
|
+
// under their own credit family.
|
|
700
|
+
"gemini-3.6-flash": "fast",
|
|
685
701
|
}
|
|
686
702
|
/**
|
|
687
703
|
* MIXED tiers — advanced multi-engine analysis plans whose identifier resolves
|
|
@@ -692,15 +708,30 @@ export const VIDEO_ANALYSIS_LEGACY_MODELS: Record<string, VideoAnalysisModelTier
|
|
|
692
708
|
* NOT published here (Apache irrevocability; only the wire vocabulary below is
|
|
693
709
|
* contract — the engine lives in the private analysis plugin).
|
|
694
710
|
*/
|
|
695
|
-
export const VIDEO_ANALYSIS_MIXED_TIERS = ["mixed", "mixed-fast"] as const
|
|
711
|
+
export const VIDEO_ANALYSIS_MIXED_TIERS = ["mixed", "mixed-fast", "smart"] as const
|
|
696
712
|
export type VideoAnalysisMixedTier = (typeof VIDEO_ANALYSIS_MIXED_TIERS)[number]
|
|
697
|
-
/**
|
|
698
|
-
|
|
713
|
+
/**
|
|
714
|
+
* `smart` is an ENGINE-PLAN sentinel like the mixed tiers — it names a plan, not a
|
|
715
|
+
* model — but it is the opposite kind of plan, and the distinction is the whole
|
|
716
|
+
* reason it exists as a separate tier rather than a repricing of the others.
|
|
717
|
+
*
|
|
718
|
+
* The economy tiers (`fast`, `pro`, `mixed`, `mixed-fast`) run several passes on
|
|
719
|
+
* the cheaper proxied transport and vote. That transport is 3-4x cheaper per token
|
|
720
|
+
* and additionally does no deep reasoning, which is why they cost single-digit
|
|
721
|
+
* credits — and also why they are less accurate. `smart` runs ONE pass on the
|
|
722
|
+
* native transport with everything turned up, which is where the accuracy is.
|
|
723
|
+
*
|
|
724
|
+
* As with the mixed sentinels, what the plan does internally is deliberately not
|
|
725
|
+
* published here; only the wire vocabulary is contract.
|
|
726
|
+
*/
|
|
727
|
+
/** UI/listing order — recommended (smart) first. */
|
|
728
|
+
export const VIDEO_ANALYSIS_TIER_ORDER = ["smart", "pro", "fast", "mixed", "mixed-fast"] as const
|
|
699
729
|
export type VideoAnalysisTier = (typeof VIDEO_ANALYSIS_TIER_ORDER)[number]
|
|
700
730
|
export const DEFAULT_VIDEO_ANALYSIS_TIER: VideoAnalysisTier = "pro"
|
|
701
731
|
export const DEFAULT_VIDEO_ANALYSIS_MODEL: string = VIDEO_ANALYSIS_TIERS[DEFAULT_VIDEO_ANALYSIS_TIER]
|
|
702
732
|
/** Neutral, vendor-free display labels for the UI. */
|
|
703
733
|
export const VIDEO_ANALYSIS_TIER_LABELS: Record<VideoAnalysisTier, string> = {
|
|
734
|
+
smart: "Smart",
|
|
704
735
|
fast: "Fast",
|
|
705
736
|
pro: "Pro",
|
|
706
737
|
mixed: "Mixed",
|
package/src/model-catalog.ts
CHANGED
|
@@ -1815,11 +1815,11 @@ const VIDEO_MODELS: Record<string, ModelCatalogEntry> = {
|
|
|
1815
1815
|
description: "Legacy fast-tier analysis model (pre-2026-07). Kept so stored raw-model configs keep running and keep pricing under their own identifier; new fast-tier runs use the current fast model.",
|
|
1816
1816
|
useCases: ["video-analysis", "shot-list", "fast"],
|
|
1817
1817
|
pricing: [
|
|
1818
|
-
{ identifier: "video-analysis:gemini-3-flash", credits:
|
|
1819
|
-
{ identifier: "video-analysis:gemini-3-flash:60s", credits:
|
|
1820
|
-
{ identifier: "video-analysis:gemini-3-flash:180s", credits:
|
|
1821
|
-
{ identifier: "video-analysis:gemini-3-flash:360s", credits:
|
|
1822
|
-
{ identifier: "video-analysis:gemini-3-flash:600s", credits:
|
|
1818
|
+
{ identifier: "video-analysis:gemini-3-flash", credits: 14, note: "10-min ceiling (no duration given)" },
|
|
1819
|
+
{ identifier: "video-analysis:gemini-3-flash:60s", credits: 3 },
|
|
1820
|
+
{ identifier: "video-analysis:gemini-3-flash:180s", credits: 4 },
|
|
1821
|
+
{ identifier: "video-analysis:gemini-3-flash:360s", credits: 9 },
|
|
1822
|
+
{ identifier: "video-analysis:gemini-3-flash:600s", credits: 14, note: "10-min ceiling" },
|
|
1823
1823
|
],
|
|
1824
1824
|
},
|
|
1825
1825
|
"gemini-3.6-flash-video-analysis": {
|
|
@@ -1832,11 +1832,11 @@ const VIDEO_MODELS: Record<string, ModelCatalogEntry> = {
|
|
|
1832
1832
|
description: "Analyze a video into a structured shot list (scenes, camera, audio) — fast, economy tier. Billed per duration bucket.",
|
|
1833
1833
|
useCases: ["video-analysis", "shot-list", "fast"],
|
|
1834
1834
|
pricing: [
|
|
1835
|
-
{ identifier: "video-analysis:gemini-3.6-flash", credits:
|
|
1836
|
-
{ identifier: "video-analysis:gemini-3.6-flash:60s", credits:
|
|
1837
|
-
{ identifier: "video-analysis:gemini-3.6-flash:180s", credits:
|
|
1838
|
-
{ identifier: "video-analysis:gemini-3.6-flash:360s", credits:
|
|
1839
|
-
{ identifier: "video-analysis:gemini-3.6-flash:600s", credits:
|
|
1835
|
+
{ identifier: "video-analysis:gemini-3.6-flash", credits: 38, note: "10-min ceiling (no duration given)" },
|
|
1836
|
+
{ identifier: "video-analysis:gemini-3.6-flash:60s", credits: 7 },
|
|
1837
|
+
{ identifier: "video-analysis:gemini-3.6-flash:180s", credits: 9 },
|
|
1838
|
+
{ identifier: "video-analysis:gemini-3.6-flash:360s", credits: 23 },
|
|
1839
|
+
{ identifier: "video-analysis:gemini-3.6-flash:600s", credits: 38, note: "10-min ceiling" },
|
|
1840
1840
|
],
|
|
1841
1841
|
},
|
|
1842
1842
|
"gemini-3.1-pro-video-analysis": {
|
|
@@ -1849,11 +1849,11 @@ const VIDEO_MODELS: Record<string, ModelCatalogEntry> = {
|
|
|
1849
1849
|
description: "Analyze a video into a structured shot list (scenes, camera, audio) — higher-fidelity, default tier. Billed per duration bucket.",
|
|
1850
1850
|
useCases: ["video-analysis", "shot-list", "cinematic"],
|
|
1851
1851
|
pricing: [
|
|
1852
|
-
{ identifier: "video-analysis:gemini-3.1-pro", credits:
|
|
1853
|
-
{ identifier: "video-analysis:gemini-3.1-pro:60s", credits:
|
|
1854
|
-
{ identifier: "video-analysis:gemini-3.1-pro:180s", credits:
|
|
1855
|
-
{ identifier: "video-analysis:gemini-3.1-pro:360s", credits:
|
|
1856
|
-
{ identifier: "video-analysis:gemini-3.1-pro:600s", credits:
|
|
1852
|
+
{ identifier: "video-analysis:gemini-3.1-pro", credits: 49, note: "10-min ceiling (no duration given)" },
|
|
1853
|
+
{ identifier: "video-analysis:gemini-3.1-pro:60s", credits: 9 },
|
|
1854
|
+
{ identifier: "video-analysis:gemini-3.1-pro:180s", credits: 12 },
|
|
1855
|
+
{ identifier: "video-analysis:gemini-3.1-pro:360s", credits: 30 },
|
|
1856
|
+
{ identifier: "video-analysis:gemini-3.1-pro:600s", credits: 49, note: "10-min ceiling" },
|
|
1857
1857
|
],
|
|
1858
1858
|
},
|
|
1859
1859
|
// Both mixed tiers are variants of the same advanced multi-engine analysis
|
|
@@ -1869,11 +1869,32 @@ const VIDEO_MODELS: Record<string, ModelCatalogEntry> = {
|
|
|
1869
1869
|
description: "Our most advanced analysis tier — multiple analysis engines combined into one result for maximum completeness and accuracy. Billed per duration bucket.",
|
|
1870
1870
|
useCases: ["video-analysis", "shot-list", "premium", "most-complete"],
|
|
1871
1871
|
pricing: [
|
|
1872
|
-
{ identifier: "video-analysis:mixed", credits:
|
|
1873
|
-
{ identifier: "video-analysis:mixed:60s", credits:
|
|
1874
|
-
{ identifier: "video-analysis:mixed:180s", credits:
|
|
1875
|
-
{ identifier: "video-analysis:mixed:360s", credits:
|
|
1876
|
-
{ identifier: "video-analysis:mixed:600s", credits:
|
|
1872
|
+
{ identifier: "video-analysis:mixed", credits: 63, note: "10-min ceiling (no duration given)" },
|
|
1873
|
+
{ identifier: "video-analysis:mixed:60s", credits: 11 },
|
|
1874
|
+
{ identifier: "video-analysis:mixed:180s", credits: 15 },
|
|
1875
|
+
{ identifier: "video-analysis:mixed:360s", credits: 38 },
|
|
1876
|
+
{ identifier: "video-analysis:mixed:600s", credits: 63, note: "10-min ceiling" },
|
|
1877
|
+
],
|
|
1878
|
+
},
|
|
1879
|
+
// SMART — the accuracy tier, and the only one that does not rely on voting.
|
|
1880
|
+
// Every other tier runs several cheap passes and has a grader pick between them;
|
|
1881
|
+
// this one runs a single pass with reasoning and frame sampling turned all the
|
|
1882
|
+
// way up. Priced well above them because it genuinely costs more to run.
|
|
1883
|
+
"smart-video-analysis": {
|
|
1884
|
+
id: "smart-video-analysis",
|
|
1885
|
+
kind: "video",
|
|
1886
|
+
modes: ["video-analysis"] as const,
|
|
1887
|
+
family: "Nodaro",
|
|
1888
|
+
label: "Video Analysis (Smart)",
|
|
1889
|
+
series: "Video Analysis",
|
|
1890
|
+
description: "Highest-accuracy analysis — a single deep pass that reads the footage frame by frame, finds its own shot boundaries, and identifies the cast by appearance. Best choice when the shot list will drive regeneration. Billed per duration bucket.",
|
|
1891
|
+
useCases: ["video-analysis", "shot-list", "premium", "most-accurate"],
|
|
1892
|
+
pricing: [
|
|
1893
|
+
{ identifier: "video-analysis:smart", credits: 346, note: "10-min ceiling (no duration given)" },
|
|
1894
|
+
{ identifier: "video-analysis:smart:60s", credits: 42 },
|
|
1895
|
+
{ identifier: "video-analysis:smart:180s", credits: 94 },
|
|
1896
|
+
{ identifier: "video-analysis:smart:360s", credits: 207 },
|
|
1897
|
+
{ identifier: "video-analysis:smart:600s", credits: 346, note: "10-min ceiling" },
|
|
1877
1898
|
],
|
|
1878
1899
|
},
|
|
1879
1900
|
}
|
|
@@ -50,27 +50,35 @@ export const VIDEO_ANALYSIS_WINDOW = { LEN: WINDOW_LEN, STRIDE: WINDOW_STRIDE, O
|
|
|
50
50
|
*/
|
|
51
51
|
export const VIDEO_ANALYSIS_BUCKET_CREDITS: Record<string, number> = {
|
|
52
52
|
// Legacy fast-tier model (pre-2026-07) — kept for stored raw-id configs.
|
|
53
|
-
"video-analysis:gemini-3-flash:60s":
|
|
54
|
-
"video-analysis:gemini-3-flash:180s":
|
|
55
|
-
"video-analysis:gemini-3-flash:360s":
|
|
56
|
-
"video-analysis:gemini-3-flash:600s":
|
|
53
|
+
"video-analysis:gemini-3-flash:60s": 3,
|
|
54
|
+
"video-analysis:gemini-3-flash:180s": 4,
|
|
55
|
+
"video-analysis:gemini-3-flash:360s": 9,
|
|
56
|
+
"video-analysis:gemini-3-flash:600s": 14,
|
|
57
57
|
// Current fast tier — regenerated from the private formula for its backing
|
|
58
58
|
// model; higher than the legacy fast schedule but still ≤ pro per bucket.
|
|
59
|
-
"video-analysis:gemini-3.6-flash:60s":
|
|
60
|
-
"video-analysis:gemini-3.6-flash:180s":
|
|
61
|
-
"video-analysis:gemini-3.6-flash:360s":
|
|
62
|
-
"video-analysis:gemini-3.6-flash:600s":
|
|
63
|
-
"video-analysis:gemini-3.1-pro:60s":
|
|
64
|
-
"video-analysis:gemini-3.1-pro:180s":
|
|
65
|
-
"video-analysis:gemini-3.1-pro:360s":
|
|
66
|
-
"video-analysis:gemini-3.1-pro:600s":
|
|
59
|
+
"video-analysis:gemini-3.6-flash:60s": 7,
|
|
60
|
+
"video-analysis:gemini-3.6-flash:180s": 9,
|
|
61
|
+
"video-analysis:gemini-3.6-flash:360s": 23,
|
|
62
|
+
"video-analysis:gemini-3.6-flash:600s": 38,
|
|
63
|
+
"video-analysis:gemini-3.1-pro:60s": 9,
|
|
64
|
+
"video-analysis:gemini-3.1-pro:180s": 12,
|
|
65
|
+
"video-analysis:gemini-3.1-pro:360s": 30,
|
|
66
|
+
"video-analysis:gemini-3.1-pro:600s": 49,
|
|
67
67
|
// Mixed tiers (`mixed` + `mixed-fast`) share ONE credit family — they are
|
|
68
68
|
// variants of the same engine plan (plan internals live in the private
|
|
69
69
|
// analysis plugin). Admin-tunable via model_pricing like every other row.
|
|
70
|
-
"video-analysis:mixed:60s":
|
|
71
|
-
"video-analysis:mixed:180s":
|
|
72
|
-
"video-analysis:mixed:360s":
|
|
73
|
-
"video-analysis:mixed:600s":
|
|
70
|
+
"video-analysis:mixed:60s": 11,
|
|
71
|
+
"video-analysis:mixed:180s": 15,
|
|
72
|
+
"video-analysis:mixed:360s": 38,
|
|
73
|
+
"video-analysis:mixed:600s": 63,
|
|
74
|
+
// SMART — the one native-transport plan: a single pass with reasoning and
|
|
75
|
+
// frame sampling turned all the way up. Priced well above the economy tiers
|
|
76
|
+
// because it genuinely costs more to run, and it is the only tier whose
|
|
77
|
+
// accuracy was measured against a hand-counted edit list.
|
|
78
|
+
"video-analysis:smart:60s": 42,
|
|
79
|
+
"video-analysis:smart:180s": 94,
|
|
80
|
+
"video-analysis:smart:360s": 207,
|
|
81
|
+
"video-analysis:smart:600s": 346,
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
/**
|
package/src/video-analysis.ts
CHANGED
|
@@ -205,6 +205,19 @@ export const entitySlotSchema = z.object({
|
|
|
205
205
|
* where this entity is clearly visible. Optional/additive: producers may
|
|
206
206
|
* omit it; consumers use it as an identity reference for recreation. */
|
|
207
207
|
refImageUrl: z.string().url().optional(),
|
|
208
|
+
/**
|
|
209
|
+
* Why `refImageUrl` is ABSENT, when the analyzer's vision pass actively
|
|
210
|
+
* refused every candidate frame rather than merely failing to produce one.
|
|
211
|
+
*
|
|
212
|
+
* Present only alongside a missing `refImageUrl`, and only for a real refusal
|
|
213
|
+
* — never for a technical failure, so its presence always carries meaning. The
|
|
214
|
+
* important case reads like "the shots bound to this slot show someone else —
|
|
215
|
+
* cast as X, but on screen: Y", which is the analyzer telling you the casting
|
|
216
|
+
* description and the footage disagree. A consumer should treat that as a
|
|
217
|
+
* reason to review the slot before spending on regeneration, since a wrong
|
|
218
|
+
* identity propagates into every regenerated shot.
|
|
219
|
+
*/
|
|
220
|
+
refRejectedReason: z.string().optional(),
|
|
208
221
|
/** NON-default looks only; present only when at least one exists. */
|
|
209
222
|
variations: z.array(slotVariationSchema).max(VIDEO_ANALYSIS_MAX_VARIATIONS).optional(),
|
|
210
223
|
})
|
|
@@ -327,6 +340,21 @@ export const videoAnalysisResultSchema = z.object({
|
|
|
327
340
|
variationId: z.string(),
|
|
328
341
|
label: z.string(),
|
|
329
342
|
})).optional(),
|
|
343
|
+
/**
|
|
344
|
+
* Analyzer diagnostics for THIS result — things the analysis noticed about
|
|
345
|
+
* itself that a reader should know before spending on regeneration.
|
|
346
|
+
*
|
|
347
|
+
* Added because there was previously no channel for them at all: the merge
|
|
348
|
+
* layer has always produced a warnings list and the auto-cast pass has always
|
|
349
|
+
* had findings, and every one of them died in a `console.warn` inside a worker
|
|
350
|
+
* the user cannot see. So a run could quietly drop a duplicated line, fold a
|
|
351
|
+
* cast look, or conclude a character is not the person their own description
|
|
352
|
+
* names, and the result looked indistinguishable from a clean one.
|
|
353
|
+
*
|
|
354
|
+
* Prose, not codes, and deliberately so — these are read by a human deciding
|
|
355
|
+
* whether to re-run, not branched on. Absent when there is nothing to report.
|
|
356
|
+
*/
|
|
357
|
+
warnings: z.array(z.string()).optional(),
|
|
330
358
|
})
|
|
331
359
|
export type VideoAnalysisResult = z.infer<typeof videoAnalysisResultSchema>
|
|
332
360
|
|