@nodaro/shared 2.2.1 → 2.5.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 +166 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +171 -4
- package/dist/index.d.ts +171 -4
- package/dist/index.js +158 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/effective-tier.test.ts +116 -0
- package/src/__tests__/normalize-model-input.test.ts +153 -0
- package/src/__tests__/normalize-node-params.test.ts +95 -0
- package/src/effective-tier.ts +72 -0
- package/src/index.ts +18 -0
- package/src/model-catalog.ts +161 -0
- package/src/model-constants.ts +6 -1
- package/src/normalize-node-params.ts +126 -0
- package/src/pipeline-defaults.ts +2 -0
package/src/model-constants.ts
CHANGED
|
@@ -605,8 +605,13 @@ export const IMAGE_I2I_PROVIDERS = [
|
|
|
605
605
|
"kontext-multi",
|
|
606
606
|
// BFL Flux 2 Pro — runs through Replicate with safety_tolerance=5 (max for Pro)
|
|
607
607
|
"flux-2-pro",
|
|
608
|
+
// BFL FLUX Fill Pro — dedicated masked inpainting via Replicate (white = edit area)
|
|
609
|
+
"flux-fill",
|
|
608
610
|
// BFL Flux 2 Max — runs through Replicate with safety_tolerance=5, up to 8 refs
|
|
609
611
|
"flux-2-max",
|
|
612
|
+
// BFL FLUX Fill Pro — dedicated inpainting via Replicate (image + mask + prompt,
|
|
613
|
+
// white = edit area). Second mask-capable i2i provider alongside ideogram-edit.
|
|
614
|
+
"flux-fill",
|
|
610
615
|
] as const
|
|
611
616
|
|
|
612
617
|
/** Image editing providers (upscale, remove bg, etc.) */
|
|
@@ -1039,7 +1044,7 @@ export type VoiceDesignModel = typeof VOICE_DESIGN_MODELS[number]
|
|
|
1039
1044
|
export const DEFAULT_VOICE_DESIGN_MODEL: VoiceDesignModel = "eleven_ttv_v3"
|
|
1040
1045
|
|
|
1041
1046
|
/** I2I providers that support mask-based inpainting */
|
|
1042
|
-
export const I2I_MASK_SUPPORT = new Set(["ideogram-edit"])
|
|
1047
|
+
export const I2I_MASK_SUPPORT = new Set(["ideogram-edit", "flux-fill"])
|
|
1043
1048
|
|
|
1044
1049
|
/**
|
|
1045
1050
|
* Mask edit tier per image-gen provider (single source of truth for inpaint).
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-node parameter normalization.
|
|
3
|
+
*
|
|
4
|
+
* The config panels already prevent an impossible provider/parameter pair from
|
|
5
|
+
* being *selected*: their dropdowns are provider-aware and a fail-safe effect
|
|
6
|
+
* snaps stale values when the provider changes. But both of those are React
|
|
7
|
+
* effects — they only run when that specific node's panel or hover strip is
|
|
8
|
+
* mounted. A node written straight into workflow JSON by a non-UI author (an
|
|
9
|
+
* agent, an import, a template) is never seen by either, so its invalid pair
|
|
10
|
+
* survives all the way to the provider call.
|
|
11
|
+
*
|
|
12
|
+
* That is the 2026-08-09 incident: one `generate-image` node carrying
|
|
13
|
+
* `gpt-image` + `16:9` (GPT Image 1.5 renders 1:1 / 3:2 / 2:3 only) reached KIE,
|
|
14
|
+
* got rejected, and aborted a run whose five sibling nodes had already produced
|
|
15
|
+
* — and billed for — their images.
|
|
16
|
+
*
|
|
17
|
+
* This module is the write-boundary guard: it heals the node instead of
|
|
18
|
+
* rejecting it, so the user sees a valid value on the canvas rather than a
|
|
19
|
+
* failed run. It reads exclusively from `MODEL_CATALOG`, so a model added later
|
|
20
|
+
* is covered without touching this file.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { normalizeModelInput, type ModelInputAdjustment } from "./model-catalog.js"
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Node types whose `data` carries catalog-governed model parameters under the
|
|
27
|
+
* shape this module understands (`provider` + aspectRatio/resolution/quality).
|
|
28
|
+
*
|
|
29
|
+
* IMAGE ONLY, deliberately. The video nodes route several of these fields
|
|
30
|
+
* through mode-dependent defaults (`"adaptive"` for Seedance/Hailuo, duration
|
|
31
|
+
* composites tied to pricing) that this flat normalizer would flatten wrongly;
|
|
32
|
+
* they get their own pass once those defaults are catalog-derived too.
|
|
33
|
+
*/
|
|
34
|
+
export const MODEL_PARAM_NODE_TYPES: ReadonlySet<string> = new Set([
|
|
35
|
+
"generate-image",
|
|
36
|
+
"image-to-image",
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
export interface NodeParamAdjustment extends ModelInputAdjustment {
|
|
40
|
+
nodeId: string
|
|
41
|
+
provider: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface NormalizedNodes<T> {
|
|
45
|
+
nodes: T[]
|
|
46
|
+
/** Empty when nothing needed correcting. */
|
|
47
|
+
adjustments: NodeParamAdjustment[]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface NodeLike {
|
|
51
|
+
id?: unknown
|
|
52
|
+
type?: unknown
|
|
53
|
+
data?: unknown
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Return `nodes` with every catalog-governed image parameter coerced into a
|
|
58
|
+
* combination its provider actually accepts, plus the list of what changed.
|
|
59
|
+
*
|
|
60
|
+
* Immutable: nodes that need no correction are returned by reference, and a
|
|
61
|
+
* corrected node is a fresh object (never a mutation of the caller's input).
|
|
62
|
+
*
|
|
63
|
+
* Multi-provider nodes (`data.providers` holding 2+ entries) are SKIPPED: the
|
|
64
|
+
* valid set there is the intersection across every selected provider, and when
|
|
65
|
+
* a stored value falls outside it there is no single defensible replacement —
|
|
66
|
+
* picking one provider's default would silently misconfigure the others. Those
|
|
67
|
+
* nodes are still guarded interactively by the panel's intersection dropdown.
|
|
68
|
+
*/
|
|
69
|
+
export function normalizeNodeModelParams<T extends NodeLike>(
|
|
70
|
+
nodes: readonly T[],
|
|
71
|
+
): NormalizedNodes<T> {
|
|
72
|
+
const adjustments: NodeParamAdjustment[] = []
|
|
73
|
+
const out = nodes.map((node) => {
|
|
74
|
+
const type = typeof node.type === "string" ? node.type : ""
|
|
75
|
+
if (!MODEL_PARAM_NODE_TYPES.has(type)) return node
|
|
76
|
+
|
|
77
|
+
const data = node.data
|
|
78
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return node
|
|
79
|
+
const d = data as Record<string, unknown>
|
|
80
|
+
|
|
81
|
+
const multi = Array.isArray(d.providers) ? (d.providers as unknown[]) : []
|
|
82
|
+
if (multi.length > 1) return node
|
|
83
|
+
|
|
84
|
+
const provider =
|
|
85
|
+
typeof d.provider === "string"
|
|
86
|
+
? d.provider
|
|
87
|
+
: typeof multi[0] === "string"
|
|
88
|
+
? (multi[0] as string)
|
|
89
|
+
: undefined
|
|
90
|
+
if (!provider) return node
|
|
91
|
+
|
|
92
|
+
const normalized = normalizeModelInput(provider, {
|
|
93
|
+
aspectRatio: typeof d.aspectRatio === "string" ? d.aspectRatio : undefined,
|
|
94
|
+
resolution: typeof d.resolution === "string" ? d.resolution : undefined,
|
|
95
|
+
quality: typeof d.quality === "string" ? d.quality : undefined,
|
|
96
|
+
})
|
|
97
|
+
if (normalized.adjustments.length === 0) return node
|
|
98
|
+
|
|
99
|
+
const nodeId = typeof node.id === "string" ? node.id : "(unknown node)"
|
|
100
|
+
for (const adj of normalized.adjustments) {
|
|
101
|
+
adjustments.push({ ...adj, nodeId, provider })
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Only the three governed keys are rewritten; everything else on the node
|
|
105
|
+
// is passed through untouched. A dropped lever is written as `undefined`
|
|
106
|
+
// rather than deleted so the shape stays stable for downstream readers.
|
|
107
|
+
return {
|
|
108
|
+
...node,
|
|
109
|
+
data: {
|
|
110
|
+
...d,
|
|
111
|
+
aspectRatio: normalized.aspectRatio,
|
|
112
|
+
resolution: normalized.resolution,
|
|
113
|
+
quality: normalized.quality,
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
return { nodes: out, adjustments }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** One-line-per-change summary, for surfacing back to an agent or a log. */
|
|
122
|
+
export function describeNodeAdjustments(adjustments: readonly NodeParamAdjustment[]): string[] {
|
|
123
|
+
return adjustments.map(
|
|
124
|
+
(a) => `${a.nodeId} (${a.provider}): ${a.field} "${a.from}" → ${a.to === undefined ? "removed" : `"${a.to}"`} — ${a.reason}`,
|
|
125
|
+
)
|
|
126
|
+
}
|
package/src/pipeline-defaults.ts
CHANGED
|
@@ -65,6 +65,7 @@ export function validateModeActivation(
|
|
|
65
65
|
// Tier → max parallel pipelines (Architecture §5.4)
|
|
66
66
|
export const TIER_PIPELINE_PARALLELISM: Record<string, number> = {
|
|
67
67
|
free: 0,
|
|
68
|
+
payg: 1, // derived tier — pipeline entitlements copy basic's
|
|
68
69
|
basic: 1,
|
|
69
70
|
standard: 2,
|
|
70
71
|
pro: 3,
|
|
@@ -80,6 +81,7 @@ export const TIER_PIPELINE_PARALLELISM: Record<string, number> = {
|
|
|
80
81
|
// the user paid for, instead of the intended two-thirds.
|
|
81
82
|
export const TIER_MAX_PIPELINE_COST_CREDITS: Record<string, number> = {
|
|
82
83
|
free: 0,
|
|
84
|
+
payg: 3000, // derived tier — copies basic
|
|
83
85
|
basic: 3000,
|
|
84
86
|
standard: 8000,
|
|
85
87
|
pro: 20000,
|