@nodaro/shared 2.18.0 → 2.20.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 +350 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +955 -564
- package/dist/index.d.ts +955 -564
- package/dist/index.js +334 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/credit-identifiers.test.ts +180 -0
- package/src/__tests__/gvp-supported-providers.test.ts +22 -1
- package/src/__tests__/image-pricing-catalog-coverage.test.ts +139 -0
- package/src/__tests__/normalize-node-params.test.ts +36 -0
- package/src/__tests__/organizations-types.test.ts +29 -1
- package/src/__tests__/prompt-length-limits.test.ts +40 -0
- package/src/__tests__/suno-credit-type.test.ts +54 -0
- package/src/__tests__/video-analysis-brief.test.ts +61 -0
- package/src/__tests__/video-audio-capability.test.ts +43 -0
- package/src/__tests__/video-mode-for-inputs.test.ts +1 -1
- package/src/__tests__/wan-3-catalog.test.ts +243 -0
- package/src/credit-identifiers.ts +191 -21
- package/src/i18n/held-prop.ar.ts +2 -0
- package/src/i18n/held-prop.de.ts +3 -0
- package/src/i18n/held-prop.es.ts +3 -0
- package/src/i18n/held-prop.fr.ts +2 -0
- package/src/i18n/held-prop.he.ts +3 -0
- package/src/i18n/held-prop.hi.ts +2 -0
- package/src/i18n/held-prop.ja.ts +3 -0
- package/src/i18n/held-prop.ko.ts +3 -0
- package/src/i18n/held-prop.pt-BR.ts +3 -0
- package/src/i18n/held-prop.ru.ts +2 -0
- package/src/i18n/held-prop.zh-CN.ts +3 -0
- package/src/i18n/person.ar.ts +3 -0
- package/src/i18n/person.de.ts +4 -0
- package/src/i18n/person.es.ts +4 -0
- package/src/i18n/person.fr.ts +3 -0
- package/src/i18n/person.he.ts +4 -0
- package/src/i18n/person.hi.ts +3 -0
- package/src/i18n/person.ja.ts +4 -0
- package/src/i18n/person.ko.ts +4 -0
- package/src/i18n/person.pt-BR.ts +4 -0
- package/src/i18n/person.ru.ts +3 -0
- package/src/i18n/person.zh-CN.ts +4 -0
- package/src/i18n/setting.ar.ts +2 -0
- package/src/i18n/setting.de.ts +3 -0
- package/src/i18n/setting.es.ts +3 -0
- package/src/i18n/setting.fr.ts +2 -0
- package/src/i18n/setting.he.ts +3 -0
- package/src/i18n/setting.hi.ts +2 -0
- package/src/i18n/setting.ja.ts +3 -0
- package/src/i18n/setting.ko.ts +3 -0
- package/src/i18n/setting.pt-BR.ts +3 -0
- package/src/i18n/setting.ru.ts +2 -0
- package/src/i18n/setting.zh-CN.ts +3 -0
- package/src/i18n/style.ar.ts +2 -0
- package/src/i18n/style.de.ts +3 -0
- package/src/i18n/style.es.ts +3 -0
- package/src/i18n/style.fr.ts +2 -0
- package/src/i18n/style.he.ts +3 -0
- package/src/i18n/style.hi.ts +2 -0
- package/src/i18n/style.ja.ts +3 -0
- package/src/i18n/style.ko.ts +3 -0
- package/src/i18n/style.pt-BR.ts +3 -0
- package/src/i18n/style.ru.ts +2 -0
- package/src/i18n/style.zh-CN.ts +3 -0
- package/src/i18n/styling.ar.ts +4 -0
- package/src/i18n/styling.de.ts +5 -0
- package/src/i18n/styling.es.ts +5 -0
- package/src/i18n/styling.fr.ts +4 -0
- package/src/i18n/styling.he.ts +5 -0
- package/src/i18n/styling.hi.ts +4 -0
- package/src/i18n/styling.ja.ts +5 -0
- package/src/i18n/styling.ko.ts +5 -0
- package/src/i18n/styling.pt-BR.ts +5 -0
- package/src/i18n/styling.ru.ts +4 -0
- package/src/i18n/styling.zh-CN.ts +5 -0
- package/src/index.ts +17 -0
- package/src/model-catalog.ts +101 -0
- package/src/model-constants.ts +251 -16
- package/src/node-default-mappings.ts +5 -0
- package/src/normalize-node-params.ts +8 -0
- package/src/organizations/types.ts +12 -0
- package/src/organizations/views.ts +114 -0
- package/src/video-analysis.ts +45 -0
- package/src/video-ui-defaults.ts +66 -0
|
@@ -56,6 +56,16 @@ export type GrantedAccess = (typeof GRANTED_ACCESS)[number]
|
|
|
56
56
|
export const SUBMISSION_STATUSES = ["submitted", "in_review", "returned", "approved"] as const
|
|
57
57
|
export type SubmissionStatus = (typeof SUBMISSION_STATUSES)[number]
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* How a usage report is bucketed. `workspace` is org-scope only; `none`
|
|
61
|
+
* = flat rows. A runtime list, like `ORG_ROLES`, so the plugin route's Zod is
|
|
62
|
+
* `z.enum(USAGE_GROUP_BYS)` and the migration guard asserts the SQL
|
|
63
|
+
* `NOT IN (...)` list equals this minus `none` — one vocabulary, never three
|
|
64
|
+
* hand-copies that drift.
|
|
65
|
+
*/
|
|
66
|
+
export const USAGE_GROUP_BYS = ["workspace", "member", "model", "day", "none"] as const
|
|
67
|
+
export type UsageGroupBy = (typeof USAGE_GROUP_BYS)[number]
|
|
68
|
+
|
|
59
69
|
/**
|
|
60
70
|
* Error codes the organization endpoints add to the standard envelope
|
|
61
71
|
* (`{ error: { code, message } }`). Clients dispatch on the code, never on
|
|
@@ -80,6 +90,8 @@ export const ORG_ERROR_CODES = [
|
|
|
80
90
|
"domain_not_allowed",
|
|
81
91
|
"already_started",
|
|
82
92
|
"collab_unavailable",
|
|
93
|
+
// A CSV usage export whose write-ahead audit row could not be written (503).
|
|
94
|
+
"audit_unavailable",
|
|
83
95
|
// Organization, workspace and membership endpoints.
|
|
84
96
|
"terms_required",
|
|
85
97
|
"not_org_member",
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
OrgRole,
|
|
5
5
|
OrgSettings,
|
|
6
6
|
OrgStatus,
|
|
7
|
+
UsageGroupBy,
|
|
7
8
|
WorkspaceRole,
|
|
8
9
|
WorkspaceSettings,
|
|
9
10
|
} from "./types.js"
|
|
@@ -218,3 +219,116 @@ export interface OrgPage<T> {
|
|
|
218
219
|
data: T[]
|
|
219
220
|
nextCursor: string | null
|
|
220
221
|
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Usage reporting. What `GET /v1/orgs/:id/usage` and
|
|
225
|
+
* `GET /v1/workspaces/:id/usage` return. No cost/USD field appears anywhere —
|
|
226
|
+
* a report shows CREDITS a class or team spent, never the platform's own rates
|
|
227
|
+
* (pricing-leak class, guarded by organizations-types.test.ts and the
|
|
228
|
+
* migration guard).
|
|
229
|
+
*/
|
|
230
|
+
|
|
231
|
+
/** One bucket of a usage report. Exactly one of workspace/member/model/day is set. */
|
|
232
|
+
export interface UsageReportRow {
|
|
233
|
+
key: string
|
|
234
|
+
workspace: { id: string; name: string | null; slug: string | null; archived: boolean } | null
|
|
235
|
+
member: { userId: string; displayName: string | null; email: string | null } | null
|
|
236
|
+
model: string | null
|
|
237
|
+
/** `YYYY-MM-DD` in the report's `tz`. */
|
|
238
|
+
day: string | null
|
|
239
|
+
runCount: number
|
|
240
|
+
appRunCount: number
|
|
241
|
+
/** Settled where known, the held reservation otherwise. = settledCredits + inFlightCredits. */
|
|
242
|
+
credits: number
|
|
243
|
+
settledCredits: number
|
|
244
|
+
inFlightCredits: number
|
|
245
|
+
inFlightRuns: number
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* A platform-absorbed line for one workspace, split by ORIGIN (never attributed
|
|
250
|
+
* to a member). Two ledgers share the `org_usage_variance` source: a
|
|
251
|
+
* `metered_overrun` (a metered run's overrun beyond the budget — it HAS a
|
|
252
|
+
* settled usage_logs counterpart) and an `app_markup` shortfall (an
|
|
253
|
+
* approved-app markup the budget could not cover — it has NO usage_logs row).
|
|
254
|
+
* `other` is a future/unrecognised origin.
|
|
255
|
+
*/
|
|
256
|
+
export interface UsageVarianceRow {
|
|
257
|
+
workspace: { id: string; name: string | null; slug: string | null } | null
|
|
258
|
+
kind: "metered_overrun" | "app_markup" | "other"
|
|
259
|
+
credits: number
|
|
260
|
+
rowCount: number
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Totals over the WHOLE window — every usage_logs row in [from, to] after the
|
|
265
|
+
* scope / userId / workspaceId narrowing — never over the returned `rows`.
|
|
266
|
+
* Unaffected by `truncated`; equals a `groupBy=day` report's column-wise sum.
|
|
267
|
+
*/
|
|
268
|
+
export interface UsageReportTotals {
|
|
269
|
+
runCount: number
|
|
270
|
+
credits: number
|
|
271
|
+
settledCredits: number
|
|
272
|
+
inFlightCredits: number
|
|
273
|
+
/** Metered-overrun variance in the window — a run's overrun the platform absorbed. */
|
|
274
|
+
platformAbsorbedCredits: number
|
|
275
|
+
/** Approved-app markup shortfall the platform absorbed. It has NO usage_logs run, so it is not in the figures above. */
|
|
276
|
+
appMarkupAbsorbedCredits: number
|
|
277
|
+
/**
|
|
278
|
+
* settledCredits − platformAbsorbedCredits: the METERED settlement that
|
|
279
|
+
* reached the workspace budget(s). App markup charged to a budget (migration
|
|
280
|
+
* 352) is not a usage_logs row and is not included here; when a markup
|
|
281
|
+
* shortfall is absorbed this figure under-reports and may go negative — it is
|
|
282
|
+
* NOT `workspace_budgets.spent_credits`.
|
|
283
|
+
*/
|
|
284
|
+
chargedToBudget: number
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface UsageReport {
|
|
288
|
+
scope: "org" | "workspace"
|
|
289
|
+
scopeId: string
|
|
290
|
+
from: string
|
|
291
|
+
to: string
|
|
292
|
+
tz: string
|
|
293
|
+
groupBy: Exclude<UsageGroupBy, "none">
|
|
294
|
+
/** Present when a member's self-view or an admin's `?userId=` narrowed the report. */
|
|
295
|
+
userId: string | null
|
|
296
|
+
/** Present when an org report was narrowed to one workspace. */
|
|
297
|
+
workspaceId: string | null
|
|
298
|
+
rows: UsageReportRow[]
|
|
299
|
+
variance: UsageVarianceRow[]
|
|
300
|
+
totals: UsageReportTotals
|
|
301
|
+
/**
|
|
302
|
+
* True when more than 5000 buckets existed and the tail of `rows` was dropped
|
|
303
|
+
* — narrow the window. Only `rows` is incomplete; `totals` and `variance`
|
|
304
|
+
* cover the whole window regardless.
|
|
305
|
+
*/
|
|
306
|
+
truncated: boolean
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** One usage_logs row as an organization sees it. No cost fields, ever. */
|
|
310
|
+
export interface UsageLogEntry {
|
|
311
|
+
id: string
|
|
312
|
+
createdAt: string
|
|
313
|
+
workspace: { id: string; name: string | null; slug: string | null } | null
|
|
314
|
+
member: { userId: string; displayName: string | null; email: string | null } | null
|
|
315
|
+
jobId: string | null
|
|
316
|
+
model: string
|
|
317
|
+
status: "reserved" | "committed"
|
|
318
|
+
creditsReserved: number
|
|
319
|
+
creditsSettled: number | null
|
|
320
|
+
credits: number
|
|
321
|
+
isAppRun: boolean
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Query parameters shared by both usage routes (dates inclusive, IANA tz). */
|
|
325
|
+
export interface UsageQuery {
|
|
326
|
+
from?: string
|
|
327
|
+
to?: string
|
|
328
|
+
tz?: string
|
|
329
|
+
groupBy?: UsageGroupBy
|
|
330
|
+
workspaceId?: string
|
|
331
|
+
userId?: string
|
|
332
|
+
cursor?: string
|
|
333
|
+
limit?: number
|
|
334
|
+
}
|
package/src/video-analysis.ts
CHANGED
|
@@ -633,3 +633,48 @@ export function inferMusicVideo(analysis: {
|
|
|
633
633
|
}),
|
|
634
634
|
)
|
|
635
635
|
}
|
|
636
|
+
|
|
637
|
+
// ---------------------------------------------------------------------------
|
|
638
|
+
// The analysis as a BRIEF — the compact projection an LLM is handed
|
|
639
|
+
// ---------------------------------------------------------------------------
|
|
640
|
+
|
|
641
|
+
/** Top-level keys the analyzer derives AFTER the model's pass (merge
|
|
642
|
+
* diagnostics, folded cast looks). A reader drafting FROM the analysis — a
|
|
643
|
+
* production plan, a script — needs none of them. */
|
|
644
|
+
const DERIVED_ANALYSIS_TOP_KEYS: ReadonlySet<string> = new Set(["warnings", "variationFolds"])
|
|
645
|
+
|
|
646
|
+
/** Per-scene keys the validator computes from `visual` and the slot list. */
|
|
647
|
+
const DERIVED_ANALYSIS_SCENE_KEYS: ReadonlySet<string> = new Set(["visualResolved", "slotRefs", "oversized"])
|
|
648
|
+
|
|
649
|
+
const asRecord = (v: unknown): Record<string, unknown> | null =>
|
|
650
|
+
typeof v === "object" && v !== null && !Array.isArray(v) ? (v as Record<string, unknown>) : null
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* The analysis with its server-derived fields removed — the form an LLM is
|
|
654
|
+
* handed when the analysis IS the brief (Nodaro Studio's Director and its
|
|
655
|
+
* job-id loader). Drops `warnings` and `variationFolds` at the top and
|
|
656
|
+
* `visualResolved`, `slotRefs`, `oversized` on every scene; keeps
|
|
657
|
+
* `refImageUrl` (a downstream cast image) and everything else. Never mutates;
|
|
658
|
+
* a non-object input comes back as-is.
|
|
659
|
+
*
|
|
660
|
+
* ONE strip list, shared by the worker that composes the brief server-side
|
|
661
|
+
* (`llm-structured` jobs with a `videoUrl`) and the client that loads a
|
|
662
|
+
* finished analysis into a textarea, so the two can never drift.
|
|
663
|
+
*/
|
|
664
|
+
export function stripDerivedAnalysisFields(json: unknown): unknown {
|
|
665
|
+
const doc = asRecord(json)
|
|
666
|
+
if (!doc) return json
|
|
667
|
+
const out: Record<string, unknown> = {}
|
|
668
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
669
|
+
if (DERIVED_ANALYSIS_TOP_KEYS.has(key)) continue
|
|
670
|
+
out[key] =
|
|
671
|
+
key === "scenes" && Array.isArray(value)
|
|
672
|
+
? value.map((scene) => {
|
|
673
|
+
const s = asRecord(scene)
|
|
674
|
+
if (!s) return scene
|
|
675
|
+
return Object.fromEntries(Object.entries(s).filter(([k]) => !DERIVED_ANALYSIS_SCENE_KEYS.has(k)))
|
|
676
|
+
})
|
|
677
|
+
: value
|
|
678
|
+
}
|
|
679
|
+
return out
|
|
680
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI-default fills for the unified video nodes — the ONE source of truth shared
|
|
3
|
+
* by the backend DAG payload builder, the config-panel fail-safe snaps and the
|
|
4
|
+
* node's hover/run strip.
|
|
5
|
+
*
|
|
6
|
+
* Several config panels RENDER a default without persisting it to node data, so
|
|
7
|
+
* an untouched node submits `aspectRatio` / `resolution` undefined and the
|
|
8
|
+
* enqueued job row (and the /v1/jobs echo) then disagrees with what actually
|
|
9
|
+
* renders. Worse, the panels' generic snap writes `resolutions[0]` for a stale
|
|
10
|
+
* value, which is the CHEAPEST tier under the repo's ascending-resolution
|
|
11
|
+
* convention — not necessarily the tier the model renders and bills.
|
|
12
|
+
*
|
|
13
|
+
* These helpers were local to `backend/src/services/workflow-engine/payload-builder.ts`
|
|
14
|
+
* until the Wan 3.0 launch gave the platform its first provider whose declared
|
|
15
|
+
* billing default differs from `resolutions[0]`; three surfaces then had to
|
|
16
|
+
* agree, so they live here.
|
|
17
|
+
*
|
|
18
|
+
* The resolution fill is per-family ON PURPOSE. Wan 3.0 reads its DECLARED
|
|
19
|
+
* billing default (PRICING_DEFAULT_RESOLUTION = 720p) because its catalog list
|
|
20
|
+
* is ascending, so an index-0 fill would write 480p — a tier both `runWan3`
|
|
21
|
+
* (which renders 720P) and the credit identifier (which bills the 720p row)
|
|
22
|
+
* disagree with. The Seedance family keeps its historical first-catalog-tier
|
|
23
|
+
* fill, deliberately: switching it to PRICING_DEFAULT_RESOLUTION would reprice
|
|
24
|
+
* live seedance-2-5 runs, whose declared default (720p) differs from the 480p
|
|
25
|
+
* this has always filled. That 480p-fill vs 720p-billing-default divergence on
|
|
26
|
+
* seedance-2-5 is a KNOWN pre-existing gap, out of scope here — do not "align"
|
|
27
|
+
* the branches without repricing it deliberately.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
PRICING_DEFAULT_RESOLUTION,
|
|
32
|
+
isSeedance2Provider,
|
|
33
|
+
isMinimaxH3Provider,
|
|
34
|
+
isWan3Provider,
|
|
35
|
+
isGeminiOmniProvider,
|
|
36
|
+
} from "./model-constants.js"
|
|
37
|
+
import { MODEL_CATALOG } from "./model-catalog.js"
|
|
38
|
+
|
|
39
|
+
/** `adaptive` is the aspect default for Seedance 2, MiniMax H3 and Wan 3.0. */
|
|
40
|
+
export function uiAspectRatioFill(provider: string): string | undefined {
|
|
41
|
+
return isSeedance2Provider(provider) || isMinimaxH3Provider(provider) || isWan3Provider(provider)
|
|
42
|
+
? "adaptive"
|
|
43
|
+
: undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** See the file docstring — per-family on purpose; never a generic
|
|
47
|
+
* PRICING_DEFAULT_RESOLUTION read (that would reprice seedance-2-5). */
|
|
48
|
+
export function uiResolutionFill(provider: string): string | undefined {
|
|
49
|
+
if (isWan3Provider(provider)) return PRICING_DEFAULT_RESOLUTION[provider]
|
|
50
|
+
if (isSeedance2Provider(provider)) return MODEL_CATALOG[provider]?.resolutions?.[0]
|
|
51
|
+
return undefined
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Duration the node RENDERS and BILLS when `data.duration` is unset, for the
|
|
56
|
+
* families whose credit identifier declares a duration fallback that is NOT
|
|
57
|
+
* `durations[0]`. Wan 3.0's bare identifier is the 5s tier while its ladder
|
|
58
|
+
* starts at 2s; the Gemini Omni family's is the 8s tier while its ladder starts
|
|
59
|
+
* at 4s. Everyone else falls back to the first listed duration, so this returns
|
|
60
|
+
* undefined and the caller keeps `durations[0]`.
|
|
61
|
+
*/
|
|
62
|
+
export function uiDurationFill(provider: string): number | undefined {
|
|
63
|
+
if (isWan3Provider(provider)) return 5
|
|
64
|
+
if (isGeminiOmniProvider(provider)) return 8
|
|
65
|
+
return undefined
|
|
66
|
+
}
|