@nodaro/shared 1.22.0 → 1.23.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 +57 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +56 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/llm-models.test.ts +19 -0
- package/src/__tests__/video-analysis-catalog-sync.test.ts +39 -0
- package/src/index.ts +3 -0
- package/src/llm-models.ts +43 -0
- package/src/model-catalog.ts +20 -20
- package/src/video-analysis-pricing.ts +16 -16
package/package.json
CHANGED
|
@@ -678,6 +678,25 @@ describe("advanced mode", () => {
|
|
|
678
678
|
}
|
|
679
679
|
})
|
|
680
680
|
|
|
681
|
+
it("no advanced-capable model declares xhigh/max — the UI copy depends on it", () => {
|
|
682
|
+
// `EFFORT_LABELS` in reasoning-effort-select.tsx labels xhigh/max "may bill
|
|
683
|
+
// one tier up" and its comment explains that the effort bump and the
|
|
684
|
+
// advanced bump can never both apply, because Advanced is Gemini-only and
|
|
685
|
+
// no Gemini model reaches xhigh/max. That was a note asking a future reader
|
|
686
|
+
// to remember; this is the check that fails instead.
|
|
687
|
+
//
|
|
688
|
+
// If this ever goes red, the two bumps CAN stack: reword the labels to say
|
|
689
|
+
// so and revisit whether a double bump is the price we want.
|
|
690
|
+
for (const m of LLM_MODELS) {
|
|
691
|
+
if (!supportsAdvancedMode(m.id)) continue
|
|
692
|
+
const levels = [...(m.reasoningEfforts ?? []), ...(m.directReasoningEfforts ?? [])]
|
|
693
|
+
expect(levels, `${m.id} can run advanced AND declares a tier-bumping effort`)
|
|
694
|
+
.not.toEqual(expect.arrayContaining(["xhigh"]))
|
|
695
|
+
expect(levels, `${m.id} can run advanced AND declares a tier-bumping effort`)
|
|
696
|
+
.not.toEqual(expect.arrayContaining(["max"]))
|
|
697
|
+
}
|
|
698
|
+
})
|
|
699
|
+
|
|
681
700
|
it("never bumps past premium", () => {
|
|
682
701
|
expect(buildLlmCreditIdentifier("llm-chat", "gemini-3.1-pro", "max", true)).toBe("llm-chat:premium")
|
|
683
702
|
})
|
|
@@ -13,7 +13,9 @@ import {
|
|
|
13
13
|
VIDEO_ANALYSIS_BUCKET_CREDITS,
|
|
14
14
|
VIDEO_ANALYSIS_DURATION_BUCKETS,
|
|
15
15
|
VIDEO_ANALYSIS_MAX_DURATION_SEC,
|
|
16
|
+
buildVideoAnalysisCreditId,
|
|
16
17
|
} from "../video-analysis-pricing.js"
|
|
18
|
+
import { DEFAULT_VIDEO_ANALYSIS_MODEL } from "../llm-models.js"
|
|
17
19
|
|
|
18
20
|
/** Every `video-analysis:*` pricing row declared anywhere in the catalog. */
|
|
19
21
|
const catalogRows = Object.values(MODEL_CATALOG)
|
|
@@ -61,3 +63,40 @@ describe("model catalog video-analysis pricing", () => {
|
|
|
61
63
|
}
|
|
62
64
|
})
|
|
63
65
|
})
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The BARE `video-analysis` node-type id — the one every colon-scoped guard misses.
|
|
69
|
+
*
|
|
70
|
+
* `getModelIdentifier` (frontend) has no video-analysis branch, so it falls through
|
|
71
|
+
* to `return nodeType` and the run-credits estimate resolves this exact id. It was
|
|
72
|
+
* seeded at 3 credits in migration 247 with the comment "= flash 600s" and then
|
|
73
|
+
* skipped by FIVE consecutive repricings (248, 259, 273, 275, 276), every one of
|
|
74
|
+
* which matched only `video-analysis:` prefixes — including this test file's own
|
|
75
|
+
* `catalogRows` filter above. A real run reserves 21-200, so the pre-run balance
|
|
76
|
+
* check was quoting 3 for a run it would then refuse.
|
|
77
|
+
*
|
|
78
|
+
* The value is the MAXIMUM of the table, not the default tier at the ceiling
|
|
79
|
+
* bucket: this test's first version pinned the latter and failed immediately,
|
|
80
|
+
* because the default (pro) tops out at 120 while `mixed` reaches 200. Reached only
|
|
81
|
+
* when BOTH model and duration are unknown, so it has to bound every row.
|
|
82
|
+
*/
|
|
83
|
+
describe("bare video-analysis node-type credit id", () => {
|
|
84
|
+
it("bounds EVERY bucketed row — an unknown-duration estimate must never UNDER-quote", () => {
|
|
85
|
+
const ceiling = Math.max(...Object.values(VIDEO_ANALYSIS_BUCKET_CREDITS))
|
|
86
|
+
for (const [id, credits] of Object.entries(VIDEO_ANALYSIS_BUCKET_CREDITS)) {
|
|
87
|
+
expect(credits, `${id} exceeds the bare-id ceiling ${ceiling}`).toBeLessThanOrEqual(ceiling)
|
|
88
|
+
}
|
|
89
|
+
// The migration writes this number; keep them in lockstep (277 wrote 200, 278
|
|
90
|
+
// writes 739 after the measured reprice).
|
|
91
|
+
expect(ceiling).toBe(739)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it("the default tier at the ceiling bucket is NOT a safe fallback — mixed costs more", () => {
|
|
95
|
+
// Pins the reason the bare id is the table max rather than the intuitive
|
|
96
|
+
// "default model, longest video" value, so nobody re-derives it wrongly.
|
|
97
|
+
const defaultAtCeiling = VIDEO_ANALYSIS_BUCKET_CREDITS[
|
|
98
|
+
buildVideoAnalysisCreditId(DEFAULT_VIDEO_ANALYSIS_MODEL, VIDEO_ANALYSIS_MAX_DURATION_SEC)
|
|
99
|
+
]!
|
|
100
|
+
expect(defaultAtCeiling).toBeLessThan(Math.max(...Object.values(VIDEO_ANALYSIS_BUCKET_CREDITS)))
|
|
101
|
+
})
|
|
102
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -279,6 +279,9 @@ export {
|
|
|
279
279
|
resolveLlmCreditId,
|
|
280
280
|
supportsAdvancedMode,
|
|
281
281
|
availableReasoningEfforts,
|
|
282
|
+
LLM_ROUTE_DEFAULTS,
|
|
283
|
+
llmRouteDefaults,
|
|
284
|
+
type LlmRouteDefaults,
|
|
282
285
|
ADVANCED_MODE_UNAVAILABLE_REASON,
|
|
283
286
|
motionGraphicsFeature,
|
|
284
287
|
effectiveReasoningEffort,
|
package/src/llm-models.ts
CHANGED
|
@@ -550,6 +550,49 @@ export function effectiveReasoningEffort(
|
|
|
550
550
|
* premium stays premium). `high` is the Claude-family server default and
|
|
551
551
|
* never bumps.
|
|
552
552
|
*/
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* The sampling defaults each LLM feature's route sends when Advanced mode is
|
|
556
|
+
* OFF — and therefore the values its Advanced panel must seed the sliders with.
|
|
557
|
+
*
|
|
558
|
+
* SINGLE SOURCE because the two used to disagree: the routes hardcoded their
|
|
559
|
+
* own literals while the toggle fell back to 0.7/2048, so the panel displayed
|
|
560
|
+
* a temperature the run never used, and one arrow-key press on 3D Title's Max
|
|
561
|
+
* Tokens silently cut its budget from 3072 to 2048 on a node that emits
|
|
562
|
+
* structured JSON.
|
|
563
|
+
*
|
|
564
|
+
* `structuredOutput` marks the features whose prompt asks the model for JSON —
|
|
565
|
+
* a high temperature measurably degrades schema adherence there, so the panel
|
|
566
|
+
* warns. Absent `temperature` means the route sends none (vendor default).
|
|
567
|
+
*/
|
|
568
|
+
export interface LlmRouteDefaults {
|
|
569
|
+
temperature?: number
|
|
570
|
+
maxTokens?: number
|
|
571
|
+
structuredOutput?: true
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export const LLM_ROUTE_DEFAULTS: Record<string, LlmRouteDefaults> = {
|
|
575
|
+
"llm-chat": { temperature: 0.7, maxTokens: 8192 },
|
|
576
|
+
"ai-writer": { temperature: 0.7, maxTokens: 8192 },
|
|
577
|
+
"prompt-helper": { temperature: 0.7, maxTokens: 8192, structuredOutput: true },
|
|
578
|
+
"generate-script": { maxTokens: 16384, structuredOutput: true },
|
|
579
|
+
"qa-check": { maxTokens: 1024, structuredOutput: true },
|
|
580
|
+
"image-critic": { maxTokens: 1024, structuredOutput: true },
|
|
581
|
+
"image-to-text": { maxTokens: 1024 },
|
|
582
|
+
"describe-to-picker": { structuredOutput: true },
|
|
583
|
+
"scene-graph-ai": { temperature: 0.3, maxTokens: 4096, structuredOutput: true },
|
|
584
|
+
"after-effects": { temperature: 0.3, maxTokens: 2048, structuredOutput: true },
|
|
585
|
+
"lottie-overlay": { temperature: 0.3, maxTokens: 2048, structuredOutput: true },
|
|
586
|
+
"motion-graphics": { temperature: 0.3, maxTokens: 2048, structuredOutput: true },
|
|
587
|
+
"motion-graphics-lottie": { temperature: 0.3, maxTokens: 8192, structuredOutput: true },
|
|
588
|
+
"3d-title": { temperature: 0.4, maxTokens: 3072, structuredOutput: true },
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** Route defaults for a feature; `{}` for an unknown one. */
|
|
592
|
+
export function llmRouteDefaults(feature: string | undefined): LlmRouteDefaults {
|
|
593
|
+
return (feature && LLM_ROUTE_DEFAULTS[feature]) || {}
|
|
594
|
+
}
|
|
595
|
+
|
|
553
596
|
/** One step up the economy → standard → premium ladder. Premium is the ceiling. */
|
|
554
597
|
function bumpTier(tier: LlmTier): LlmTier {
|
|
555
598
|
if (tier === "economy") return "standard"
|
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: 112, note: "10-min ceiling (no duration given)" },
|
|
1819
|
+
{ identifier: "video-analysis:gemini-3-flash:60s", credits: 21 },
|
|
1820
|
+
{ identifier: "video-analysis:gemini-3-flash:180s", credits: 24 },
|
|
1821
|
+
{ identifier: "video-analysis:gemini-3-flash:360s", credits: 68 },
|
|
1822
|
+
{ identifier: "video-analysis:gemini-3-flash:600s", credits: 112, 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: 291, note: "10-min ceiling (no duration given)" },
|
|
1836
|
+
{ identifier: "video-analysis:gemini-3.6-flash:60s", credits: 54 },
|
|
1837
|
+
{ identifier: "video-analysis:gemini-3.6-flash:180s", credits: 63 },
|
|
1838
|
+
{ identifier: "video-analysis:gemini-3.6-flash:360s", credits: 175 },
|
|
1839
|
+
{ identifier: "video-analysis:gemini-3.6-flash:600s", credits: 291, 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: 448, note: "10-min ceiling (no duration given)" },
|
|
1853
|
+
{ identifier: "video-analysis:gemini-3.1-pro:60s", credits: 84 },
|
|
1854
|
+
{ identifier: "video-analysis:gemini-3.1-pro:180s", credits: 96 },
|
|
1855
|
+
{ identifier: "video-analysis:gemini-3.1-pro:360s", credits: 269 },
|
|
1856
|
+
{ identifier: "video-analysis:gemini-3.1-pro:600s", credits: 448, 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,11 @@ 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: 739, note: "10-min ceiling (no duration given)" },
|
|
1873
|
+
{ identifier: "video-analysis:mixed:60s", credits: 137 },
|
|
1874
|
+
{ identifier: "video-analysis:mixed:180s", credits: 158 },
|
|
1875
|
+
{ identifier: "video-analysis:mixed:360s", credits: 443 },
|
|
1876
|
+
{ identifier: "video-analysis:mixed:600s", credits: 739, note: "10-min ceiling" },
|
|
1877
1877
|
],
|
|
1878
1878
|
},
|
|
1879
1879
|
}
|
|
@@ -50,27 +50,27 @@ 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": 21,
|
|
54
|
+
"video-analysis:gemini-3-flash:180s": 24,
|
|
55
|
+
"video-analysis:gemini-3-flash:360s": 68,
|
|
56
|
+
"video-analysis:gemini-3-flash:600s": 112,
|
|
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": 54,
|
|
60
|
+
"video-analysis:gemini-3.6-flash:180s": 63,
|
|
61
|
+
"video-analysis:gemini-3.6-flash:360s": 175,
|
|
62
|
+
"video-analysis:gemini-3.6-flash:600s": 291,
|
|
63
|
+
"video-analysis:gemini-3.1-pro:60s": 84,
|
|
64
|
+
"video-analysis:gemini-3.1-pro:180s": 96,
|
|
65
|
+
"video-analysis:gemini-3.1-pro:360s": 269,
|
|
66
|
+
"video-analysis:gemini-3.1-pro:600s": 448,
|
|
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": 137,
|
|
71
|
+
"video-analysis:mixed:180s": 158,
|
|
72
|
+
"video-analysis:mixed:360s": 443,
|
|
73
|
+
"video-analysis:mixed:600s": 739,
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/**
|