@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodaro/shared",
3
- "version": "1.22.0",
3
+ "version": "1.23.0",
4
4
  "description": "Shared types, model catalog, wire contracts, and structural vocabularies for the Nodaro platform and SDK.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -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"
@@ -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: 30, note: "10-min ceiling (no duration given)" },
1819
- { identifier: "video-analysis:gemini-3-flash:60s", credits: 6 },
1820
- { identifier: "video-analysis:gemini-3-flash:180s", credits: 7 },
1821
- { identifier: "video-analysis:gemini-3-flash:360s", credits: 18 },
1822
- { identifier: "video-analysis:gemini-3-flash:600s", credits: 30, note: "10-min ceiling" },
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: 81, note: "10-min ceiling (no duration given)" },
1836
- { identifier: "video-analysis:gemini-3.6-flash:60s", credits: 14 },
1837
- { identifier: "video-analysis:gemini-3.6-flash:180s", credits: 19 },
1838
- { identifier: "video-analysis:gemini-3.6-flash:360s", credits: 49 },
1839
- { identifier: "video-analysis:gemini-3.6-flash:600s", credits: 81, note: "10-min ceiling" },
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: 120, note: "10-min ceiling (no duration given)" },
1853
- { identifier: "video-analysis:gemini-3.1-pro:60s", credits: 21 },
1854
- { identifier: "video-analysis:gemini-3.1-pro:180s", credits: 27 },
1855
- { identifier: "video-analysis:gemini-3.1-pro:360s", credits: 72 },
1856
- { identifier: "video-analysis:gemini-3.1-pro:600s", credits: 120, note: "10-min ceiling" },
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: 200, note: "10-min ceiling (no duration given)" },
1873
- { identifier: "video-analysis:mixed:60s", credits: 34 },
1874
- { identifier: "video-analysis:mixed:180s", credits: 46 },
1875
- { identifier: "video-analysis:mixed:360s", credits: 120 },
1876
- { identifier: "video-analysis:mixed:600s", credits: 200, note: "10-min ceiling" },
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": 6,
54
- "video-analysis:gemini-3-flash:180s": 7,
55
- "video-analysis:gemini-3-flash:360s": 18,
56
- "video-analysis:gemini-3-flash:600s": 30,
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": 14,
60
- "video-analysis:gemini-3.6-flash:180s": 19,
61
- "video-analysis:gemini-3.6-flash:360s": 49,
62
- "video-analysis:gemini-3.6-flash:600s": 81,
63
- "video-analysis:gemini-3.1-pro:60s": 21,
64
- "video-analysis:gemini-3.1-pro:180s": 27,
65
- "video-analysis:gemini-3.1-pro:360s": 72,
66
- "video-analysis:gemini-3.1-pro:600s": 120,
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": 34,
71
- "video-analysis:mixed:180s": 46,
72
- "video-analysis:mixed:360s": 120,
73
- "video-analysis:mixed:600s": 200,
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
  /**