@hostwebhook/node-types 1.85.0 → 1.86.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/esm/index.d.ts +4 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/sentiment.d.ts +39 -0
- package/dist/esm/sentiment.js +53 -0
- package/dist/esm/types.d.ts +111 -76
- package/dist/esm/types.js +26 -0
- package/dist/esm/ui.js +10 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +14 -4
- package/dist/sentiment.d.ts +39 -0
- package/dist/sentiment.js +57 -0
- package/dist/types.d.ts +111 -76
- package/dist/types.js +28 -0
- package/dist/ui.js +10 -1
- package/package.json +1 -1
package/dist/esm/index.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ export type { NodeType, NodeRole, HandlePosition, NodeConnectionConfig, NodeUICo
|
|
|
2
2
|
export { singleMeta, iterableMeta } from './types.js';
|
|
3
3
|
export { NODE_CONNECTIONS, canReceiveFrom, canReceiveFromNodes, canSendToNodes, isTerminal, isNodeType, ALL_NODE_TYPES, } from './connections.js';
|
|
4
4
|
export { NODE_UI, PREFIX_TO_TYPE, resolveNodeId, } from './ui.js';
|
|
5
|
+
export { SENTIMENT_LABELS, SENTIMENT_UNCLEAR, SENTIMENT_PORTS, SENTIMENT_PORT_LABEL, SENTIMENT_PORT_SUMMARY, esEtiquetaDeSentimiento, } from './sentiment.js';
|
|
6
|
+
export type { SentimentLabel, SentimentPort } from './sentiment.js';
|
|
7
|
+
export { outputGroupsFor, todosLosOutputGroups } from './types.js';
|
|
8
|
+
export type { OutputGroupsConfig } from './types.js';
|
|
5
9
|
export { NODE_DISPATCH, getAllNodeCollections, getNodeDispatchConfig, pasaLoQueRecibe, } from './dispatch.js';
|
|
6
10
|
export type { NodeRegistryEntry, NodeGroup, NodeVersionSpec, VersionCatalog, } from './registry.js';
|
|
7
11
|
export { NODE_REGISTRY, getNodeRegistryEntry, NODE_DETAIL_PATHS, NODE_COLORS, NODE_STATE_KEYS, PREFIX_TO_NODE_TYPE, NODE_TYPE_TO_PREFIX, } from './registry.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -3,6 +3,8 @@ export { singleMeta, iterableMeta } from './types.js';
|
|
|
3
3
|
export { NODE_CONNECTIONS, canReceiveFrom, canReceiveFromNodes, canSendToNodes, isTerminal, isNodeType, ALL_NODE_TYPES, } from './connections.js';
|
|
4
4
|
// ── UI config (Dashboard only) ──
|
|
5
5
|
export { NODE_UI, PREFIX_TO_TYPE, resolveNodeId, } from './ui.js';
|
|
6
|
+
export { SENTIMENT_LABELS, SENTIMENT_UNCLEAR, SENTIMENT_PORTS, SENTIMENT_PORT_LABEL, SENTIMENT_PORT_SUMMARY, esEtiquetaDeSentimiento, } from './sentiment.js';
|
|
7
|
+
export { outputGroupsFor, todosLosOutputGroups } from './types.js';
|
|
6
8
|
// ── Dispatch config ──
|
|
7
9
|
/* Ya no es «API only»: `downstreamPayload` lo lee tambien el panel, que sin el
|
|
8
10
|
ensenaba como entrada de un nodo la salida del que pasa de largo. */
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentiment analysis en el AI Node — las etiquetas, los puertos y el orden.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué vive aquí y no en cada repo
|
|
5
|
+
*
|
|
6
|
+
* Las tres etiquetas son a la vez TRES cosas: el `enum` del esquema que se le
|
|
7
|
+
* da al modelo, el nombre de los puertos por los que el nodo despacha, y las
|
|
8
|
+
* pastillas de la tarjeta del lienzo. Si el api, hw-nodes y el dashboard
|
|
9
|
+
* escriben su propia lista, la primera que alguien reordene o rebautice deja
|
|
10
|
+
* una arista apuntando a un puerto que ya no existe — y eso no lo dice ningún
|
|
11
|
+
* compilador. Es el mismo motivo por el que las operaciones de cada nodo
|
|
12
|
+
* viven en este paquete.
|
|
13
|
+
*
|
|
14
|
+
* ## Por qué `unclear` no es una etiqueta más
|
|
15
|
+
*
|
|
16
|
+
* El modelo elige entre las TRES de `SENTIMENT_LABELS`; `unclear` no se le
|
|
17
|
+
* ofrece nunca. Es lo que hace el NODO cuando la confianza queda por debajo
|
|
18
|
+
* del umbral (`sentimentThreshold`) o cuando la respuesta no se puede leer.
|
|
19
|
+
* Ofrecérsela al modelo sería darle una salida cómoda para no mojarse, que es
|
|
20
|
+
* justo lo contrario de lo que se busca al poner un umbral.
|
|
21
|
+
*
|
|
22
|
+
* A diferencia del `other` del clasificador, `unclear` SÍ tiene puerto: el
|
|
23
|
+
* caso de uso es mandar a revisión humana lo que el modelo no ve claro, y sin
|
|
24
|
+
* puerto eso obligaba a adivinar por qué un item no salió por ninguna rama.
|
|
25
|
+
*/
|
|
26
|
+
/** Lo que el modelo puede contestar, en orden de peor a mejor. */
|
|
27
|
+
export declare const SENTIMENT_LABELS: readonly ["negative", "neutral", "positive"];
|
|
28
|
+
export type SentimentLabel = (typeof SENTIMENT_LABELS)[number];
|
|
29
|
+
/** La salida del nodo cuando no hay etiqueta fiable. No se le ofrece al modelo. */
|
|
30
|
+
export declare const SENTIMENT_UNCLEAR = "unclear";
|
|
31
|
+
/** Los cuatro puertos del modo sentiment, en el orden en que se pintan. */
|
|
32
|
+
export declare const SENTIMENT_PORTS: readonly ["negative", "neutral", "positive", "unclear"];
|
|
33
|
+
export type SentimentPort = (typeof SENTIMENT_PORTS)[number];
|
|
34
|
+
/** El nombre que ve la persona, por puerto. */
|
|
35
|
+
export declare const SENTIMENT_PORT_LABEL: Record<SentimentPort, string>;
|
|
36
|
+
/** Qué significa cada puerto, para el modal de conexión y la doc. */
|
|
37
|
+
export declare const SENTIMENT_PORT_SUMMARY: Record<SentimentPort, string>;
|
|
38
|
+
/** ¿Es una de las tres que el modelo puede contestar? */
|
|
39
|
+
export declare function esEtiquetaDeSentimiento(v: unknown): v is SentimentLabel;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentiment analysis en el AI Node — las etiquetas, los puertos y el orden.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué vive aquí y no en cada repo
|
|
5
|
+
*
|
|
6
|
+
* Las tres etiquetas son a la vez TRES cosas: el `enum` del esquema que se le
|
|
7
|
+
* da al modelo, el nombre de los puertos por los que el nodo despacha, y las
|
|
8
|
+
* pastillas de la tarjeta del lienzo. Si el api, hw-nodes y el dashboard
|
|
9
|
+
* escriben su propia lista, la primera que alguien reordene o rebautice deja
|
|
10
|
+
* una arista apuntando a un puerto que ya no existe — y eso no lo dice ningún
|
|
11
|
+
* compilador. Es el mismo motivo por el que las operaciones de cada nodo
|
|
12
|
+
* viven en este paquete.
|
|
13
|
+
*
|
|
14
|
+
* ## Por qué `unclear` no es una etiqueta más
|
|
15
|
+
*
|
|
16
|
+
* El modelo elige entre las TRES de `SENTIMENT_LABELS`; `unclear` no se le
|
|
17
|
+
* ofrece nunca. Es lo que hace el NODO cuando la confianza queda por debajo
|
|
18
|
+
* del umbral (`sentimentThreshold`) o cuando la respuesta no se puede leer.
|
|
19
|
+
* Ofrecérsela al modelo sería darle una salida cómoda para no mojarse, que es
|
|
20
|
+
* justo lo contrario de lo que se busca al poner un umbral.
|
|
21
|
+
*
|
|
22
|
+
* A diferencia del `other` del clasificador, `unclear` SÍ tiene puerto: el
|
|
23
|
+
* caso de uso es mandar a revisión humana lo que el modelo no ve claro, y sin
|
|
24
|
+
* puerto eso obligaba a adivinar por qué un item no salió por ninguna rama.
|
|
25
|
+
*/
|
|
26
|
+
/** Lo que el modelo puede contestar, en orden de peor a mejor. */
|
|
27
|
+
export const SENTIMENT_LABELS = ['negative', 'neutral', 'positive'];
|
|
28
|
+
/** La salida del nodo cuando no hay etiqueta fiable. No se le ofrece al modelo. */
|
|
29
|
+
export const SENTIMENT_UNCLEAR = 'unclear';
|
|
30
|
+
/** Los cuatro puertos del modo sentiment, en el orden en que se pintan. */
|
|
31
|
+
export const SENTIMENT_PORTS = [
|
|
32
|
+
...SENTIMENT_LABELS,
|
|
33
|
+
SENTIMENT_UNCLEAR,
|
|
34
|
+
];
|
|
35
|
+
/** El nombre que ve la persona, por puerto. */
|
|
36
|
+
export const SENTIMENT_PORT_LABEL = {
|
|
37
|
+
negative: 'Negative',
|
|
38
|
+
neutral: 'Neutral',
|
|
39
|
+
positive: 'Positive',
|
|
40
|
+
unclear: 'Unclear',
|
|
41
|
+
};
|
|
42
|
+
/** Qué significa cada puerto, para el modal de conexión y la doc. */
|
|
43
|
+
export const SENTIMENT_PORT_SUMMARY = {
|
|
44
|
+
negative: 'Complaints, frustration, criticism',
|
|
45
|
+
neutral: 'Factual, mixed, or no clear feeling',
|
|
46
|
+
positive: 'Praise, satisfaction, enthusiasm',
|
|
47
|
+
unclear: 'Below the confidence floor, or unreadable',
|
|
48
|
+
};
|
|
49
|
+
/** ¿Es una de las tres que el modelo puede contestar? */
|
|
50
|
+
export function esEtiquetaDeSentimiento(v) {
|
|
51
|
+
return (typeof v === 'string' &&
|
|
52
|
+
SENTIMENT_LABELS.includes(v));
|
|
53
|
+
}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -54,83 +54,15 @@ export interface NodeUIConfig {
|
|
|
54
54
|
connectModal?: 'branch' | 'output-group';
|
|
55
55
|
routerTargetLabel?: string;
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
57
|
+
* Los juegos de salidas de un nodo que ramifica: UNO, o VARIOS con su
|
|
58
|
+
* `gate` cada uno [2026-09-22]. Con varios, quien pregunta se queda con
|
|
59
|
+
* el PRIMERO cuya puerta pasa — ver `outputGroupsFor`. El AI Node los
|
|
60
|
+
* necesita porque ramifica de dos maneras según el modo: por las
|
|
61
|
+
* categorías que escribe el usuario (`classifier`) o por los cuatro
|
|
62
|
+
* puertos fijos del análisis de sentimiento (`sentiment`). La forma
|
|
63
|
+
* vieja (un objeto suelto) sigue valiendo y significa «sin alternativas».
|
|
62
64
|
*/
|
|
63
|
-
outputGroups?:
|
|
64
|
-
/** Entity field containing the array of groups (e.g. 'outputs', 'branches') */
|
|
65
|
-
field?: string;
|
|
66
|
-
/** Field within each group for display name (e.g. 'name') */
|
|
67
|
-
nameField?: string;
|
|
68
|
-
/** Field within each group for summary/description (e.g. 'field', 'filterMode') */
|
|
69
|
-
summaryField?: string;
|
|
70
|
-
/**
|
|
71
|
-
* Varios campos del grupo, unidos por espacios, cuando el resumen que
|
|
72
|
-
* de verdad identifica al grupo no cabe en uno solo.
|
|
73
|
-
*
|
|
74
|
-
* Existe por las reglas del router: su condición son TRES campos
|
|
75
|
-
* (`field`, `operator`, `value`) y el modal a medida que se retiró las
|
|
76
|
-
* pintaba juntas — «status eq active». Con un único `summaryField` una
|
|
77
|
-
* regla sin `label` quedaba en «rule 1» y «status», que no dice a qué
|
|
78
|
-
* se está conectando uno.
|
|
79
|
-
*
|
|
80
|
-
* Los campos vacíos se saltan, para que una regla con operador
|
|
81
|
-
* `exists` no salga con un espacio colgando al final.
|
|
82
|
-
*/
|
|
83
|
-
summaryFields?: string[];
|
|
84
|
-
/** Label shown in the modal (e.g. 'output', 'branch') */
|
|
85
|
-
label: string;
|
|
86
|
-
/** Entity field for the fallback/else path (e.g. 'elseOutputNodes') */
|
|
87
|
-
elseField?: string;
|
|
88
|
-
/**
|
|
89
|
-
* Fixed output groups — for nodes with a static set of outputs (e.g. approval: approve/reject).
|
|
90
|
-
* Each entry maps to a top-level entity field that holds outputNodes[].
|
|
91
|
-
* When set, `field`/`nameField` are ignored — modal shows these fixed options.
|
|
92
|
-
*/
|
|
93
|
-
fixedGroups?: Array<{
|
|
94
|
-
name: string;
|
|
95
|
-
field: string;
|
|
96
|
-
}>;
|
|
97
|
-
/**
|
|
98
|
-
* Optional content-aware gate — the output-group modal only opens
|
|
99
|
-
* when `entity[gate.field] === gate.value`. Lets a node opt into
|
|
100
|
-
* multi-branch routing only in some modes (e.g. AI Node in
|
|
101
|
-
* classifier mode) without trapping every connection in the
|
|
102
|
-
* picker the rest of the time. When the gate fails, the
|
|
103
|
-
* FlowCanvas falls through to the universal
|
|
104
|
-
* `entity.outputNodes` connect path.
|
|
105
|
-
*/
|
|
106
|
-
gate?: {
|
|
107
|
-
field: string;
|
|
108
|
-
value: unknown;
|
|
109
|
-
};
|
|
110
|
-
/**
|
|
111
|
-
* Qué es el puerto `main` de este nodo MIENTRAS su contenedor está
|
|
112
|
-
* vacío. Sin esto, conectar a un nodo que ramifica y todavía no tiene
|
|
113
|
-
* ninguna salida escribía una arista en `main` **en silencio**, y el
|
|
114
|
-
* usuario no tenía forma de saber qué había pasado con ella.
|
|
115
|
-
*
|
|
116
|
-
* No hay una respuesta única, y por eso es un dato y no una regla.
|
|
117
|
-
* Medido en `api/src/common/pipeline-run.service.ts`:
|
|
118
|
-
*
|
|
119
|
-
* 'passthrough' — router sin reglas: `rules.length === 0
|
|
120
|
-
* ? refsForPort(entity, MAIN_PORT) : []`. La
|
|
121
|
-
* arista SÍ dispara... hasta que se cree la
|
|
122
|
-
* primera regla, y entonces deja de hacerlo
|
|
123
|
-
* sin avisar.
|
|
124
|
-
* 'never-dispatched' — split: no tiene salida `main`, y el propio
|
|
125
|
-
* despachador registra un warning por cada
|
|
126
|
-
* arista que se quedó en ese puerto.
|
|
127
|
-
*
|
|
128
|
-
* Se declara sólo donde se ha medido. Sin declarar, el lienzo se
|
|
129
|
-
* comporta como siempre: conecta por el camino universal y no dice
|
|
130
|
-
* nada, que es lo que hacía antes de existir este campo.
|
|
131
|
-
*/
|
|
132
|
-
emptyContainerMain?: 'passthrough' | 'never-dispatched';
|
|
133
|
-
};
|
|
65
|
+
outputGroups?: OutputGroupsConfig | OutputGroupsConfig[];
|
|
134
66
|
/**
|
|
135
67
|
* Maps output fields to alternate payload fields on the source entity.
|
|
136
68
|
* When a downstream node is connected via a specific output field,
|
|
@@ -224,3 +156,106 @@ export interface PrefixMapping {
|
|
|
224
156
|
/** Canvas node type used in React Flow nodeTypes map (e.g. "cache-node", "email-action") */
|
|
225
157
|
canvasType: string;
|
|
226
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Un juego de salidas de un nodo que ramifica.
|
|
161
|
+
*
|
|
162
|
+
* Vivía dentro de `NodeUIConfig.special` como un objeto anónimo; sale aquí
|
|
163
|
+
* [2026-09-22] para poder declarar varios por nodo y para que quien los lea
|
|
164
|
+
* nombre el tipo en vez de repetir su forma.
|
|
165
|
+
*/
|
|
166
|
+
export interface OutputGroupsConfig {
|
|
167
|
+
/** Entity field containing the array of groups (e.g. 'outputs', 'branches') */
|
|
168
|
+
field?: string;
|
|
169
|
+
/** Field within each group for display name (e.g. 'name') */
|
|
170
|
+
nameField?: string;
|
|
171
|
+
/** Field within each group for summary/description (e.g. 'field', 'filterMode') */
|
|
172
|
+
summaryField?: string;
|
|
173
|
+
/**
|
|
174
|
+
* Varios campos del grupo, unidos por espacios, cuando el resumen que
|
|
175
|
+
* de verdad identifica al grupo no cabe en uno solo.
|
|
176
|
+
*
|
|
177
|
+
* Existe por las reglas del router: su condición son TRES campos
|
|
178
|
+
* (`field`, `operator`, `value`) y el modal a medida que se retiró las
|
|
179
|
+
* pintaba juntas — «status eq active». Con un único `summaryField` una
|
|
180
|
+
* regla sin `label` quedaba en «rule 1» y «status», que no dice a qué
|
|
181
|
+
* se está conectando uno.
|
|
182
|
+
*
|
|
183
|
+
* Los campos vacíos se saltan, para que una regla con operador
|
|
184
|
+
* `exists` no salga con un espacio colgando al final.
|
|
185
|
+
*/
|
|
186
|
+
summaryFields?: string[];
|
|
187
|
+
/** Label shown in the modal (e.g. 'output', 'branch') */
|
|
188
|
+
label: string;
|
|
189
|
+
/** Entity field for the fallback/else path (e.g. 'elseOutputNodes') */
|
|
190
|
+
elseField?: string;
|
|
191
|
+
/**
|
|
192
|
+
* Fixed output groups — for nodes with a static set of outputs (e.g. approval: approve/reject).
|
|
193
|
+
* When set, `field`/`nameField` are ignored — modal shows these fixed options.
|
|
194
|
+
*
|
|
195
|
+
* Cada entrada nombra su puerto de una de dos formas, y sólo una:
|
|
196
|
+
* · `field` — un campo de primer nivel de los de ANTES de que los puertos
|
|
197
|
+
* existieran (`rejectionOutputNodes` → puerto `rejected`); se traduce
|
|
198
|
+
* con el mapa de campos heredados.
|
|
199
|
+
* · `port` — el puerto, tal cual. Es lo que usan las salidas nacidas ya
|
|
200
|
+
* con puertos (las cuatro de sentiment): inventarles un campo heredado
|
|
201
|
+
* que nunca existió sería mentir en el registro.
|
|
202
|
+
*/
|
|
203
|
+
fixedGroups?: Array<{
|
|
204
|
+
name: string;
|
|
205
|
+
field?: string;
|
|
206
|
+
port?: string;
|
|
207
|
+
/** Una línea de qué entrega ese puerto, para el modal de conexión. */
|
|
208
|
+
summary?: string;
|
|
209
|
+
}>;
|
|
210
|
+
/**
|
|
211
|
+
* Optional content-aware gate — the output-group modal only opens
|
|
212
|
+
* when `entity[gate.field] === gate.value`. Lets a node opt into
|
|
213
|
+
* multi-branch routing only in some modes (e.g. AI Node in
|
|
214
|
+
* classifier mode) without trapping every connection in the
|
|
215
|
+
* picker the rest of the time. When the gate fails, the
|
|
216
|
+
* FlowCanvas falls through to the universal
|
|
217
|
+
* `entity.outputNodes` connect path.
|
|
218
|
+
*/
|
|
219
|
+
gate?: {
|
|
220
|
+
field: string;
|
|
221
|
+
value: unknown;
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Qué es el puerto `main` de este nodo MIENTRAS su contenedor está
|
|
225
|
+
* vacío. Sin esto, conectar a un nodo que ramifica y todavía no tiene
|
|
226
|
+
* ninguna salida escribía una arista en `main` **en silencio**, y el
|
|
227
|
+
* usuario no tenía forma de saber qué había pasado con ella.
|
|
228
|
+
*
|
|
229
|
+
* No hay una respuesta única, y por eso es un dato y no una regla.
|
|
230
|
+
* Medido en `api/src/common/pipeline-run.service.ts`:
|
|
231
|
+
*
|
|
232
|
+
* 'passthrough' — router sin reglas: `rules.length === 0
|
|
233
|
+
* ? refsForPort(entity, MAIN_PORT) : []`. La
|
|
234
|
+
* arista SÍ dispara... hasta que se cree la
|
|
235
|
+
* primera regla, y entonces deja de hacerlo
|
|
236
|
+
* sin avisar.
|
|
237
|
+
* 'never-dispatched' — split: no tiene salida `main`, y el propio
|
|
238
|
+
* despachador registra un warning por cada
|
|
239
|
+
* arista que se quedó en ese puerto.
|
|
240
|
+
*
|
|
241
|
+
* Se declara sólo donde se ha medido. Sin declarar, el lienzo se
|
|
242
|
+
* comporta como siempre: conecta por el camino universal y no dice
|
|
243
|
+
* nada, que es lo que hacía antes de existir este campo.
|
|
244
|
+
*/
|
|
245
|
+
emptyContainerMain?: 'passthrough' | 'never-dispatched';
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* El juego de salidas que aplica AHORA a esta entidad, o `undefined` si el
|
|
249
|
+
* nodo no ramifica en este momento.
|
|
250
|
+
*
|
|
251
|
+
* Es la única lectura de `special.outputGroups` que entiende las dos formas
|
|
252
|
+
* (uno o varios) y las dos puertas (con `gate` o sin ella). Todo el que
|
|
253
|
+
* pregunte tiene que pasar por aquí: el lienzo, el modal de conexión y el
|
|
254
|
+
* despachador. Escribirlo cada uno a su manera es como el router acabó con un
|
|
255
|
+
* camino propio que nadie arregló al arreglar el de los demás.
|
|
256
|
+
*/
|
|
257
|
+
export declare function outputGroupsFor(config: NodeUIConfig | undefined | null, entity: Record<string, unknown> | undefined | null): OutputGroupsConfig | undefined;
|
|
258
|
+
/** Todos los juegos declarados, pasen o no su puerta. Para quien necesita el
|
|
259
|
+
* UNIVERSO de puertos que un tipo puede emitir (la auditoría de puertos),
|
|
260
|
+
* no el de ahora mismo. */
|
|
261
|
+
export declare function todosLosOutputGroups(config: NodeUIConfig | undefined | null): OutputGroupsConfig[];
|
package/dist/esm/types.js
CHANGED
|
@@ -6,3 +6,29 @@ export function singleMeta() {
|
|
|
6
6
|
export function iterableMeta(field, count) {
|
|
7
7
|
return { iterable: true, iterateField: field, count };
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* El juego de salidas que aplica AHORA a esta entidad, o `undefined` si el
|
|
11
|
+
* nodo no ramifica en este momento.
|
|
12
|
+
*
|
|
13
|
+
* Es la única lectura de `special.outputGroups` que entiende las dos formas
|
|
14
|
+
* (uno o varios) y las dos puertas (con `gate` o sin ella). Todo el que
|
|
15
|
+
* pregunte tiene que pasar por aquí: el lienzo, el modal de conexión y el
|
|
16
|
+
* despachador. Escribirlo cada uno a su manera es como el router acabó con un
|
|
17
|
+
* camino propio que nadie arregló al arreglar el de los demás.
|
|
18
|
+
*/
|
|
19
|
+
export function outputGroupsFor(config, entity) {
|
|
20
|
+
const declarado = config?.special?.outputGroups;
|
|
21
|
+
if (!declarado)
|
|
22
|
+
return undefined;
|
|
23
|
+
const lista = Array.isArray(declarado) ? declarado : [declarado];
|
|
24
|
+
return lista.find((c) => !c.gate || entity?.[c.gate.field] === c.gate.value);
|
|
25
|
+
}
|
|
26
|
+
/** Todos los juegos declarados, pasen o no su puerta. Para quien necesita el
|
|
27
|
+
* UNIVERSO de puertos que un tipo puede emitir (la auditoría de puertos),
|
|
28
|
+
* no el de ahora mismo. */
|
|
29
|
+
export function todosLosOutputGroups(config) {
|
|
30
|
+
const declarado = config?.special?.outputGroups;
|
|
31
|
+
if (!declarado)
|
|
32
|
+
return [];
|
|
33
|
+
return Array.isArray(declarado) ? declarado : [declarado];
|
|
34
|
+
}
|
package/dist/esm/ui.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Dashboard UI config — handle positions, dot visibility, canvas prefixes.
|
|
3
3
|
* Only imported by the Dashboard. Broker and API don't need this.
|
|
4
4
|
*/
|
|
5
|
+
import { SENTIMENT_PORTS, SENTIMENT_PORT_LABEL, SENTIMENT_PORT_SUMMARY, } from './sentiment.js';
|
|
5
6
|
export const NODE_UI = {
|
|
6
7
|
// ── Source nodes ──
|
|
7
8
|
// Una fuente arranca el flujo: `fromNodes: false` significa que nada puede
|
|
@@ -45,7 +46,15 @@ export const NODE_UI = {
|
|
|
45
46
|
// no handle and no route. In any other outputMode the node falls back to
|
|
46
47
|
// the single right-out handle NodeShell renders when no dynamic outputs
|
|
47
48
|
// materialize.
|
|
48
|
-
|
|
49
|
+
// En modo `sentiment` (2026-09-22) el abanico son CUATRO puertos fijos
|
|
50
|
+
// —negative / neutral / positive / unclear— y no un contenedor del usuario:
|
|
51
|
+
// el modelo elige entre las tres primeras y el nodo manda a `unclear` lo que
|
|
52
|
+
// no llega al umbral de confianza. Por eso el AI Node declara DOS juegos de
|
|
53
|
+
// salidas, uno por modo; `outputGroupsFor` elige el que toca.
|
|
54
|
+
ai: { fromNodes: true, toNodes: true, outputHandles: ['right'], special: { loopBackAllowed: true, connectModal: 'output-group', outputGroups: [
|
|
55
|
+
{ field: 'categories', nameField: 'name', summaryField: 'description', label: 'category', gate: { field: 'outputMode', value: 'classifier' } },
|
|
56
|
+
{ label: 'sentiment', gate: { field: 'outputMode', value: 'sentiment' }, fixedGroups: SENTIMENT_PORTS.map((p) => ({ name: SENTIMENT_PORT_LABEL[p], port: p, summary: SENTIMENT_PORT_SUMMARY[p] })) },
|
|
57
|
+
] } },
|
|
49
58
|
markdown: { fromNodes: true, toNodes: true, special: { loopBackAllowed: true } },
|
|
50
59
|
fileTransform: { fromNodes: true, toNodes: true },
|
|
51
60
|
limit: { fromNodes: true, toNodes: true, special: { loopBackAllowed: true } },
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ export type { NodeType, NodeRole, HandlePosition, NodeConnectionConfig, NodeUICo
|
|
|
2
2
|
export { singleMeta, iterableMeta } from './types.js';
|
|
3
3
|
export { NODE_CONNECTIONS, canReceiveFrom, canReceiveFromNodes, canSendToNodes, isTerminal, isNodeType, ALL_NODE_TYPES, } from './connections.js';
|
|
4
4
|
export { NODE_UI, PREFIX_TO_TYPE, resolveNodeId, } from './ui.js';
|
|
5
|
+
export { SENTIMENT_LABELS, SENTIMENT_UNCLEAR, SENTIMENT_PORTS, SENTIMENT_PORT_LABEL, SENTIMENT_PORT_SUMMARY, esEtiquetaDeSentimiento, } from './sentiment.js';
|
|
6
|
+
export type { SentimentLabel, SentimentPort } from './sentiment.js';
|
|
7
|
+
export { outputGroupsFor, todosLosOutputGroups } from './types.js';
|
|
8
|
+
export type { OutputGroupsConfig } from './types.js';
|
|
5
9
|
export { NODE_DISPATCH, getAllNodeCollections, getNodeDispatchConfig, pasaLoQueRecibe, } from './dispatch.js';
|
|
6
10
|
export type { NodeRegistryEntry, NodeGroup, NodeVersionSpec, VersionCatalog, } from './registry.js';
|
|
7
11
|
export { NODE_REGISTRY, getNodeRegistryEntry, NODE_DETAIL_PATHS, NODE_COLORS, NODE_STATE_KEYS, PREFIX_TO_NODE_TYPE, NODE_TYPE_TO_PREFIX, } from './registry.js';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
5
|
-
exports.
|
|
6
|
-
exports.armarRespuestaDelSync = exports.RESPUESTA_DEL_SYNC_POR_DEFECTO = exports.TOPE_DE_ESPERA_DEL_SYNC = exports.TOPES_DE_ESTADO = exports.SOBRES_DE_RESPUESTA = exports.MODOS_DE_PAYLOAD = exports.NIVELES_DE_DETALLE = exports.RESULTADOS_DEL_VALIDADOR = exports.contestaEnSync = exports.NODOS_QUE_CONTESTAN_EN_SYNC = exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.ventanaDeContextoDeOpenRouter = exports.opcionesDeModelosDeOpenRouter = exports.URL_DE_MODELOS_DE_OPENROUTER = exports.getModelLabel = exports.getDefaultModel = exports.getModelsFor = exports.MODEL_CONTEXT_WINDOWS = exports.LLM_MODELS = exports.LLM_PROVIDERS = exports.DOCS_TOOLKIT_DEFAULTABLE = exports.DOCS_TOOLKIT_BY_TOOL_NAME = exports.DOCS_TOOLKIT_SPECS = exports.isDocsOperation = exports.DOCS_OPERATION_SPECS = exports.DOCS_OPERATIONS = exports.isMongoOperation = void 0;
|
|
3
|
+
exports.GMAIL_SEND_LEGACY_FIELDS = exports.NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME = exports.GMAIL_TOOLKIT_BY_TOOL_NAME = exports.NATIVE_EMAIL_TOOLKIT_SPECS = exports.GMAIL_SEND_AND_WAIT_TOOL_SPEC = exports.GMAIL_ALL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_OPERATIONS = exports.GMAIL_DROPDOWN_OPERATIONS = exports.GMAIL_OPERATION_GROUPS = exports.GMAIL_OPERATION_SPECS = exports.GMAIL_OPERATIONS = exports.versionCatalogErrors = exports.fieldsLost = exports.fieldsLostBetween = exports.currentVersion = exports.versionSpec = exports.versionsOf = exports.isVersioned = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.pasaLoQueRecibe = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.todosLosOutputGroups = exports.outputGroupsFor = exports.esEtiquetaDeSentimiento = exports.SENTIMENT_PORT_SUMMARY = exports.SENTIMENT_PORT_LABEL = exports.SENTIMENT_PORTS = exports.SENTIMENT_UNCLEAR = exports.SENTIMENT_LABELS = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
|
|
4
|
+
exports.isGithubOperation = exports.GITHUB_ITERABLE_OPERATIONS = exports.GITHUB_DROPDOWN_OPERATIONS = exports.GITHUB_OPERATION_SPECS = exports.GITHUB_OPERATIONS = exports.scopesQueFaltanEnShopify = exports.isShopifyOperation = exports.SHOPIFY_PRODUCT_STATUSES = exports.SHOPIFY_CANCEL_REASONS = exports.SHOPIFY_SEARCHABLE_RESOURCES = exports.SHOPIFY_TAGGABLE_RESOURCES = exports.SHOPIFY_OPERATION_SCOPES = exports.SHOPIFY_OPERATION_SPECS = exports.SHOPIFY_OPERATIONS = exports.isMailchimpOperation = exports.MAILCHIMP_CONTACT_STATUSES = exports.MAILCHIMP_OPERATION_SPECS = exports.MAILCHIMP_OPERATIONS = exports.herramientasDeDiscordPara = exports.DISCORD_TOOLKIT_BY_TOOL_NAME = exports.DISCORD_TOOLKIT_SPECS = exports.isDiscordOperation = exports.camposDeDiscordNoDisponibles = exports.discordPuedeEjecutar = exports.operacionesDeDiscordPara = exports.DISCORD_CAPACIDADES_POR_CREDENCIAL = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.camposNoDisponiblesPara = exports.puedeEjecutar = exports.operacionesPara = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = exports.TELEGRAM_TOOLKIT_SPECS = exports.isTelegramOperation = exports.TELEGRAM_OPERATION_SPECS = exports.TELEGRAM_OPERATIONS = exports.DRIVE_TOOLKIT_BY_TOOL_NAME = exports.DRIVE_TOOLKIT_SPECS = exports.isDriveOperation = exports.DRIVE_OPERATION_SPECS = exports.DRIVE_OPERATIONS = exports.GOOGLE_CALENDAR_TOOLKIT_BY_TOOL_NAME = exports.GOOGLE_CALENDAR_TOOLKIT_SPECS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATION_SPECS = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.resolveGmailSendFields = void 0;
|
|
5
|
+
exports.NOTION_OPERATION_SPECS = exports.NOTION_OPERATIONS = exports.isGoogleAnalyticsOperation = exports.GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS = exports.GOOGLE_ANALYTICS_OPERATION_SPECS = exports.GOOGLE_ANALYTICS_OPERATIONS = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATION_SPECS = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.SHEETS_TOOLKIT_DEFAULTABLE = exports.SHEETS_TOOLKIT_BY_TOOL_NAME = exports.SHEETS_TOOLKIT_SPECS = exports.isSheetsOperation = exports.SHEETS_OPERATION_SPECS = exports.SHEETS_OPERATIONS = exports.herramientasDeSlackPara = exports.SLACK_TOOLKIT_BY_TOOL_NAME = exports.SLACK_TOOLKIT_SPECS = exports.isSlackOperation = exports.camposDeSlackNoDisponibles = exports.slackPuedeEjecutar = exports.operacionesDeSlackPara = exports.SLACK_CAPACIDADES_POR_CREDENCIAL = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isAirtableOperation = exports.AIRTABLE_ITERABLE_OPERATIONS = exports.AIRTABLE_OPERATION_SPECS = exports.AIRTABLE_OPERATIONS = exports.isCalendlyOperation = exports.CALENDLY_ITERABLE_OPERATIONS = exports.CALENDLY_OPERATION_SPECS = exports.CALENDLY_OPERATIONS = exports.isApifyOperation = exports.APIFY_ITERABLE_OPERATIONS = exports.APIFY_OPERATION_SPECS = exports.APIFY_OPERATIONS = exports.isBucketOperation = exports.BUCKET_ITERABLE_OPERATIONS = exports.BUCKET_OPERATION_SPECS = exports.BUCKET_OPERATIONS = exports.isJiraOperation = exports.JIRA_ITERABLE_OPERATIONS = exports.JIRA_DROPDOWN_OPERATIONS = exports.JIRA_OPERATION_SPECS = exports.JIRA_OPERATIONS = void 0;
|
|
6
|
+
exports.armarRespuestaDelSync = exports.RESPUESTA_DEL_SYNC_POR_DEFECTO = exports.TOPE_DE_ESPERA_DEL_SYNC = exports.TOPES_DE_ESTADO = exports.SOBRES_DE_RESPUESTA = exports.MODOS_DE_PAYLOAD = exports.NIVELES_DE_DETALLE = exports.RESULTADOS_DEL_VALIDADOR = exports.contestaEnSync = exports.NODOS_QUE_CONTESTAN_EN_SYNC = exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.ventanaDeContextoDeOpenRouter = exports.opcionesDeModelosDeOpenRouter = exports.URL_DE_MODELOS_DE_OPENROUTER = exports.getModelLabel = exports.getDefaultModel = exports.getModelsFor = exports.MODEL_CONTEXT_WINDOWS = exports.LLM_MODELS = exports.LLM_PROVIDERS = exports.DOCS_TOOLKIT_DEFAULTABLE = exports.DOCS_TOOLKIT_BY_TOOL_NAME = exports.DOCS_TOOLKIT_SPECS = exports.isDocsOperation = exports.DOCS_OPERATION_SPECS = exports.DOCS_OPERATIONS = exports.isMongoOperation = exports.MONGO_OPERATION_SPECS = exports.MONGO_OPERATIONS = exports.isPostgresOperation = exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_OPERATIONS = exports.isNotionOperation = exports.NOTION_DROPDOWN_OPERATIONS = void 0;
|
|
7
7
|
var types_js_1 = require("./types.js");
|
|
8
8
|
Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_js_1.singleMeta; } });
|
|
9
9
|
Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_js_1.iterableMeta; } });
|
|
@@ -21,6 +21,16 @@ var ui_js_1 = require("./ui.js");
|
|
|
21
21
|
Object.defineProperty(exports, "NODE_UI", { enumerable: true, get: function () { return ui_js_1.NODE_UI; } });
|
|
22
22
|
Object.defineProperty(exports, "PREFIX_TO_TYPE", { enumerable: true, get: function () { return ui_js_1.PREFIX_TO_TYPE; } });
|
|
23
23
|
Object.defineProperty(exports, "resolveNodeId", { enumerable: true, get: function () { return ui_js_1.resolveNodeId; } });
|
|
24
|
+
var sentiment_js_1 = require("./sentiment.js");
|
|
25
|
+
Object.defineProperty(exports, "SENTIMENT_LABELS", { enumerable: true, get: function () { return sentiment_js_1.SENTIMENT_LABELS; } });
|
|
26
|
+
Object.defineProperty(exports, "SENTIMENT_UNCLEAR", { enumerable: true, get: function () { return sentiment_js_1.SENTIMENT_UNCLEAR; } });
|
|
27
|
+
Object.defineProperty(exports, "SENTIMENT_PORTS", { enumerable: true, get: function () { return sentiment_js_1.SENTIMENT_PORTS; } });
|
|
28
|
+
Object.defineProperty(exports, "SENTIMENT_PORT_LABEL", { enumerable: true, get: function () { return sentiment_js_1.SENTIMENT_PORT_LABEL; } });
|
|
29
|
+
Object.defineProperty(exports, "SENTIMENT_PORT_SUMMARY", { enumerable: true, get: function () { return sentiment_js_1.SENTIMENT_PORT_SUMMARY; } });
|
|
30
|
+
Object.defineProperty(exports, "esEtiquetaDeSentimiento", { enumerable: true, get: function () { return sentiment_js_1.esEtiquetaDeSentimiento; } });
|
|
31
|
+
var types_js_2 = require("./types.js");
|
|
32
|
+
Object.defineProperty(exports, "outputGroupsFor", { enumerable: true, get: function () { return types_js_2.outputGroupsFor; } });
|
|
33
|
+
Object.defineProperty(exports, "todosLosOutputGroups", { enumerable: true, get: function () { return types_js_2.todosLosOutputGroups; } });
|
|
24
34
|
// ── Dispatch config ──
|
|
25
35
|
/* Ya no es «API only»: `downstreamPayload` lo lee tambien el panel, que sin el
|
|
26
36
|
ensenaba como entrada de un nodo la salida del que pasa de largo. */
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentiment analysis en el AI Node — las etiquetas, los puertos y el orden.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué vive aquí y no en cada repo
|
|
5
|
+
*
|
|
6
|
+
* Las tres etiquetas son a la vez TRES cosas: el `enum` del esquema que se le
|
|
7
|
+
* da al modelo, el nombre de los puertos por los que el nodo despacha, y las
|
|
8
|
+
* pastillas de la tarjeta del lienzo. Si el api, hw-nodes y el dashboard
|
|
9
|
+
* escriben su propia lista, la primera que alguien reordene o rebautice deja
|
|
10
|
+
* una arista apuntando a un puerto que ya no existe — y eso no lo dice ningún
|
|
11
|
+
* compilador. Es el mismo motivo por el que las operaciones de cada nodo
|
|
12
|
+
* viven en este paquete.
|
|
13
|
+
*
|
|
14
|
+
* ## Por qué `unclear` no es una etiqueta más
|
|
15
|
+
*
|
|
16
|
+
* El modelo elige entre las TRES de `SENTIMENT_LABELS`; `unclear` no se le
|
|
17
|
+
* ofrece nunca. Es lo que hace el NODO cuando la confianza queda por debajo
|
|
18
|
+
* del umbral (`sentimentThreshold`) o cuando la respuesta no se puede leer.
|
|
19
|
+
* Ofrecérsela al modelo sería darle una salida cómoda para no mojarse, que es
|
|
20
|
+
* justo lo contrario de lo que se busca al poner un umbral.
|
|
21
|
+
*
|
|
22
|
+
* A diferencia del `other` del clasificador, `unclear` SÍ tiene puerto: el
|
|
23
|
+
* caso de uso es mandar a revisión humana lo que el modelo no ve claro, y sin
|
|
24
|
+
* puerto eso obligaba a adivinar por qué un item no salió por ninguna rama.
|
|
25
|
+
*/
|
|
26
|
+
/** Lo que el modelo puede contestar, en orden de peor a mejor. */
|
|
27
|
+
export declare const SENTIMENT_LABELS: readonly ["negative", "neutral", "positive"];
|
|
28
|
+
export type SentimentLabel = (typeof SENTIMENT_LABELS)[number];
|
|
29
|
+
/** La salida del nodo cuando no hay etiqueta fiable. No se le ofrece al modelo. */
|
|
30
|
+
export declare const SENTIMENT_UNCLEAR = "unclear";
|
|
31
|
+
/** Los cuatro puertos del modo sentiment, en el orden en que se pintan. */
|
|
32
|
+
export declare const SENTIMENT_PORTS: readonly ["negative", "neutral", "positive", "unclear"];
|
|
33
|
+
export type SentimentPort = (typeof SENTIMENT_PORTS)[number];
|
|
34
|
+
/** El nombre que ve la persona, por puerto. */
|
|
35
|
+
export declare const SENTIMENT_PORT_LABEL: Record<SentimentPort, string>;
|
|
36
|
+
/** Qué significa cada puerto, para el modal de conexión y la doc. */
|
|
37
|
+
export declare const SENTIMENT_PORT_SUMMARY: Record<SentimentPort, string>;
|
|
38
|
+
/** ¿Es una de las tres que el modelo puede contestar? */
|
|
39
|
+
export declare function esEtiquetaDeSentimiento(v: unknown): v is SentimentLabel;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Sentiment analysis en el AI Node — las etiquetas, los puertos y el orden.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué vive aquí y no en cada repo
|
|
6
|
+
*
|
|
7
|
+
* Las tres etiquetas son a la vez TRES cosas: el `enum` del esquema que se le
|
|
8
|
+
* da al modelo, el nombre de los puertos por los que el nodo despacha, y las
|
|
9
|
+
* pastillas de la tarjeta del lienzo. Si el api, hw-nodes y el dashboard
|
|
10
|
+
* escriben su propia lista, la primera que alguien reordene o rebautice deja
|
|
11
|
+
* una arista apuntando a un puerto que ya no existe — y eso no lo dice ningún
|
|
12
|
+
* compilador. Es el mismo motivo por el que las operaciones de cada nodo
|
|
13
|
+
* viven en este paquete.
|
|
14
|
+
*
|
|
15
|
+
* ## Por qué `unclear` no es una etiqueta más
|
|
16
|
+
*
|
|
17
|
+
* El modelo elige entre las TRES de `SENTIMENT_LABELS`; `unclear` no se le
|
|
18
|
+
* ofrece nunca. Es lo que hace el NODO cuando la confianza queda por debajo
|
|
19
|
+
* del umbral (`sentimentThreshold`) o cuando la respuesta no se puede leer.
|
|
20
|
+
* Ofrecérsela al modelo sería darle una salida cómoda para no mojarse, que es
|
|
21
|
+
* justo lo contrario de lo que se busca al poner un umbral.
|
|
22
|
+
*
|
|
23
|
+
* A diferencia del `other` del clasificador, `unclear` SÍ tiene puerto: el
|
|
24
|
+
* caso de uso es mandar a revisión humana lo que el modelo no ve claro, y sin
|
|
25
|
+
* puerto eso obligaba a adivinar por qué un item no salió por ninguna rama.
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.SENTIMENT_PORT_SUMMARY = exports.SENTIMENT_PORT_LABEL = exports.SENTIMENT_PORTS = exports.SENTIMENT_UNCLEAR = exports.SENTIMENT_LABELS = void 0;
|
|
29
|
+
exports.esEtiquetaDeSentimiento = esEtiquetaDeSentimiento;
|
|
30
|
+
/** Lo que el modelo puede contestar, en orden de peor a mejor. */
|
|
31
|
+
exports.SENTIMENT_LABELS = ['negative', 'neutral', 'positive'];
|
|
32
|
+
/** La salida del nodo cuando no hay etiqueta fiable. No se le ofrece al modelo. */
|
|
33
|
+
exports.SENTIMENT_UNCLEAR = 'unclear';
|
|
34
|
+
/** Los cuatro puertos del modo sentiment, en el orden en que se pintan. */
|
|
35
|
+
exports.SENTIMENT_PORTS = [
|
|
36
|
+
...exports.SENTIMENT_LABELS,
|
|
37
|
+
exports.SENTIMENT_UNCLEAR,
|
|
38
|
+
];
|
|
39
|
+
/** El nombre que ve la persona, por puerto. */
|
|
40
|
+
exports.SENTIMENT_PORT_LABEL = {
|
|
41
|
+
negative: 'Negative',
|
|
42
|
+
neutral: 'Neutral',
|
|
43
|
+
positive: 'Positive',
|
|
44
|
+
unclear: 'Unclear',
|
|
45
|
+
};
|
|
46
|
+
/** Qué significa cada puerto, para el modal de conexión y la doc. */
|
|
47
|
+
exports.SENTIMENT_PORT_SUMMARY = {
|
|
48
|
+
negative: 'Complaints, frustration, criticism',
|
|
49
|
+
neutral: 'Factual, mixed, or no clear feeling',
|
|
50
|
+
positive: 'Praise, satisfaction, enthusiasm',
|
|
51
|
+
unclear: 'Below the confidence floor, or unreadable',
|
|
52
|
+
};
|
|
53
|
+
/** ¿Es una de las tres que el modelo puede contestar? */
|
|
54
|
+
function esEtiquetaDeSentimiento(v) {
|
|
55
|
+
return (typeof v === 'string' &&
|
|
56
|
+
exports.SENTIMENT_LABELS.includes(v));
|
|
57
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -54,83 +54,15 @@ export interface NodeUIConfig {
|
|
|
54
54
|
connectModal?: 'branch' | 'output-group';
|
|
55
55
|
routerTargetLabel?: string;
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
57
|
+
* Los juegos de salidas de un nodo que ramifica: UNO, o VARIOS con su
|
|
58
|
+
* `gate` cada uno [2026-09-22]. Con varios, quien pregunta se queda con
|
|
59
|
+
* el PRIMERO cuya puerta pasa — ver `outputGroupsFor`. El AI Node los
|
|
60
|
+
* necesita porque ramifica de dos maneras según el modo: por las
|
|
61
|
+
* categorías que escribe el usuario (`classifier`) o por los cuatro
|
|
62
|
+
* puertos fijos del análisis de sentimiento (`sentiment`). La forma
|
|
63
|
+
* vieja (un objeto suelto) sigue valiendo y significa «sin alternativas».
|
|
62
64
|
*/
|
|
63
|
-
outputGroups?:
|
|
64
|
-
/** Entity field containing the array of groups (e.g. 'outputs', 'branches') */
|
|
65
|
-
field?: string;
|
|
66
|
-
/** Field within each group for display name (e.g. 'name') */
|
|
67
|
-
nameField?: string;
|
|
68
|
-
/** Field within each group for summary/description (e.g. 'field', 'filterMode') */
|
|
69
|
-
summaryField?: string;
|
|
70
|
-
/**
|
|
71
|
-
* Varios campos del grupo, unidos por espacios, cuando el resumen que
|
|
72
|
-
* de verdad identifica al grupo no cabe en uno solo.
|
|
73
|
-
*
|
|
74
|
-
* Existe por las reglas del router: su condición son TRES campos
|
|
75
|
-
* (`field`, `operator`, `value`) y el modal a medida que se retiró las
|
|
76
|
-
* pintaba juntas — «status eq active». Con un único `summaryField` una
|
|
77
|
-
* regla sin `label` quedaba en «rule 1» y «status», que no dice a qué
|
|
78
|
-
* se está conectando uno.
|
|
79
|
-
*
|
|
80
|
-
* Los campos vacíos se saltan, para que una regla con operador
|
|
81
|
-
* `exists` no salga con un espacio colgando al final.
|
|
82
|
-
*/
|
|
83
|
-
summaryFields?: string[];
|
|
84
|
-
/** Label shown in the modal (e.g. 'output', 'branch') */
|
|
85
|
-
label: string;
|
|
86
|
-
/** Entity field for the fallback/else path (e.g. 'elseOutputNodes') */
|
|
87
|
-
elseField?: string;
|
|
88
|
-
/**
|
|
89
|
-
* Fixed output groups — for nodes with a static set of outputs (e.g. approval: approve/reject).
|
|
90
|
-
* Each entry maps to a top-level entity field that holds outputNodes[].
|
|
91
|
-
* When set, `field`/`nameField` are ignored — modal shows these fixed options.
|
|
92
|
-
*/
|
|
93
|
-
fixedGroups?: Array<{
|
|
94
|
-
name: string;
|
|
95
|
-
field: string;
|
|
96
|
-
}>;
|
|
97
|
-
/**
|
|
98
|
-
* Optional content-aware gate — the output-group modal only opens
|
|
99
|
-
* when `entity[gate.field] === gate.value`. Lets a node opt into
|
|
100
|
-
* multi-branch routing only in some modes (e.g. AI Node in
|
|
101
|
-
* classifier mode) without trapping every connection in the
|
|
102
|
-
* picker the rest of the time. When the gate fails, the
|
|
103
|
-
* FlowCanvas falls through to the universal
|
|
104
|
-
* `entity.outputNodes` connect path.
|
|
105
|
-
*/
|
|
106
|
-
gate?: {
|
|
107
|
-
field: string;
|
|
108
|
-
value: unknown;
|
|
109
|
-
};
|
|
110
|
-
/**
|
|
111
|
-
* Qué es el puerto `main` de este nodo MIENTRAS su contenedor está
|
|
112
|
-
* vacío. Sin esto, conectar a un nodo que ramifica y todavía no tiene
|
|
113
|
-
* ninguna salida escribía una arista en `main` **en silencio**, y el
|
|
114
|
-
* usuario no tenía forma de saber qué había pasado con ella.
|
|
115
|
-
*
|
|
116
|
-
* No hay una respuesta única, y por eso es un dato y no una regla.
|
|
117
|
-
* Medido en `api/src/common/pipeline-run.service.ts`:
|
|
118
|
-
*
|
|
119
|
-
* 'passthrough' — router sin reglas: `rules.length === 0
|
|
120
|
-
* ? refsForPort(entity, MAIN_PORT) : []`. La
|
|
121
|
-
* arista SÍ dispara... hasta que se cree la
|
|
122
|
-
* primera regla, y entonces deja de hacerlo
|
|
123
|
-
* sin avisar.
|
|
124
|
-
* 'never-dispatched' — split: no tiene salida `main`, y el propio
|
|
125
|
-
* despachador registra un warning por cada
|
|
126
|
-
* arista que se quedó en ese puerto.
|
|
127
|
-
*
|
|
128
|
-
* Se declara sólo donde se ha medido. Sin declarar, el lienzo se
|
|
129
|
-
* comporta como siempre: conecta por el camino universal y no dice
|
|
130
|
-
* nada, que es lo que hacía antes de existir este campo.
|
|
131
|
-
*/
|
|
132
|
-
emptyContainerMain?: 'passthrough' | 'never-dispatched';
|
|
133
|
-
};
|
|
65
|
+
outputGroups?: OutputGroupsConfig | OutputGroupsConfig[];
|
|
134
66
|
/**
|
|
135
67
|
* Maps output fields to alternate payload fields on the source entity.
|
|
136
68
|
* When a downstream node is connected via a specific output field,
|
|
@@ -224,3 +156,106 @@ export interface PrefixMapping {
|
|
|
224
156
|
/** Canvas node type used in React Flow nodeTypes map (e.g. "cache-node", "email-action") */
|
|
225
157
|
canvasType: string;
|
|
226
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Un juego de salidas de un nodo que ramifica.
|
|
161
|
+
*
|
|
162
|
+
* Vivía dentro de `NodeUIConfig.special` como un objeto anónimo; sale aquí
|
|
163
|
+
* [2026-09-22] para poder declarar varios por nodo y para que quien los lea
|
|
164
|
+
* nombre el tipo en vez de repetir su forma.
|
|
165
|
+
*/
|
|
166
|
+
export interface OutputGroupsConfig {
|
|
167
|
+
/** Entity field containing the array of groups (e.g. 'outputs', 'branches') */
|
|
168
|
+
field?: string;
|
|
169
|
+
/** Field within each group for display name (e.g. 'name') */
|
|
170
|
+
nameField?: string;
|
|
171
|
+
/** Field within each group for summary/description (e.g. 'field', 'filterMode') */
|
|
172
|
+
summaryField?: string;
|
|
173
|
+
/**
|
|
174
|
+
* Varios campos del grupo, unidos por espacios, cuando el resumen que
|
|
175
|
+
* de verdad identifica al grupo no cabe en uno solo.
|
|
176
|
+
*
|
|
177
|
+
* Existe por las reglas del router: su condición son TRES campos
|
|
178
|
+
* (`field`, `operator`, `value`) y el modal a medida que se retiró las
|
|
179
|
+
* pintaba juntas — «status eq active». Con un único `summaryField` una
|
|
180
|
+
* regla sin `label` quedaba en «rule 1» y «status», que no dice a qué
|
|
181
|
+
* se está conectando uno.
|
|
182
|
+
*
|
|
183
|
+
* Los campos vacíos se saltan, para que una regla con operador
|
|
184
|
+
* `exists` no salga con un espacio colgando al final.
|
|
185
|
+
*/
|
|
186
|
+
summaryFields?: string[];
|
|
187
|
+
/** Label shown in the modal (e.g. 'output', 'branch') */
|
|
188
|
+
label: string;
|
|
189
|
+
/** Entity field for the fallback/else path (e.g. 'elseOutputNodes') */
|
|
190
|
+
elseField?: string;
|
|
191
|
+
/**
|
|
192
|
+
* Fixed output groups — for nodes with a static set of outputs (e.g. approval: approve/reject).
|
|
193
|
+
* When set, `field`/`nameField` are ignored — modal shows these fixed options.
|
|
194
|
+
*
|
|
195
|
+
* Cada entrada nombra su puerto de una de dos formas, y sólo una:
|
|
196
|
+
* · `field` — un campo de primer nivel de los de ANTES de que los puertos
|
|
197
|
+
* existieran (`rejectionOutputNodes` → puerto `rejected`); se traduce
|
|
198
|
+
* con el mapa de campos heredados.
|
|
199
|
+
* · `port` — el puerto, tal cual. Es lo que usan las salidas nacidas ya
|
|
200
|
+
* con puertos (las cuatro de sentiment): inventarles un campo heredado
|
|
201
|
+
* que nunca existió sería mentir en el registro.
|
|
202
|
+
*/
|
|
203
|
+
fixedGroups?: Array<{
|
|
204
|
+
name: string;
|
|
205
|
+
field?: string;
|
|
206
|
+
port?: string;
|
|
207
|
+
/** Una línea de qué entrega ese puerto, para el modal de conexión. */
|
|
208
|
+
summary?: string;
|
|
209
|
+
}>;
|
|
210
|
+
/**
|
|
211
|
+
* Optional content-aware gate — the output-group modal only opens
|
|
212
|
+
* when `entity[gate.field] === gate.value`. Lets a node opt into
|
|
213
|
+
* multi-branch routing only in some modes (e.g. AI Node in
|
|
214
|
+
* classifier mode) without trapping every connection in the
|
|
215
|
+
* picker the rest of the time. When the gate fails, the
|
|
216
|
+
* FlowCanvas falls through to the universal
|
|
217
|
+
* `entity.outputNodes` connect path.
|
|
218
|
+
*/
|
|
219
|
+
gate?: {
|
|
220
|
+
field: string;
|
|
221
|
+
value: unknown;
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Qué es el puerto `main` de este nodo MIENTRAS su contenedor está
|
|
225
|
+
* vacío. Sin esto, conectar a un nodo que ramifica y todavía no tiene
|
|
226
|
+
* ninguna salida escribía una arista en `main` **en silencio**, y el
|
|
227
|
+
* usuario no tenía forma de saber qué había pasado con ella.
|
|
228
|
+
*
|
|
229
|
+
* No hay una respuesta única, y por eso es un dato y no una regla.
|
|
230
|
+
* Medido en `api/src/common/pipeline-run.service.ts`:
|
|
231
|
+
*
|
|
232
|
+
* 'passthrough' — router sin reglas: `rules.length === 0
|
|
233
|
+
* ? refsForPort(entity, MAIN_PORT) : []`. La
|
|
234
|
+
* arista SÍ dispara... hasta que se cree la
|
|
235
|
+
* primera regla, y entonces deja de hacerlo
|
|
236
|
+
* sin avisar.
|
|
237
|
+
* 'never-dispatched' — split: no tiene salida `main`, y el propio
|
|
238
|
+
* despachador registra un warning por cada
|
|
239
|
+
* arista que se quedó en ese puerto.
|
|
240
|
+
*
|
|
241
|
+
* Se declara sólo donde se ha medido. Sin declarar, el lienzo se
|
|
242
|
+
* comporta como siempre: conecta por el camino universal y no dice
|
|
243
|
+
* nada, que es lo que hacía antes de existir este campo.
|
|
244
|
+
*/
|
|
245
|
+
emptyContainerMain?: 'passthrough' | 'never-dispatched';
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* El juego de salidas que aplica AHORA a esta entidad, o `undefined` si el
|
|
249
|
+
* nodo no ramifica en este momento.
|
|
250
|
+
*
|
|
251
|
+
* Es la única lectura de `special.outputGroups` que entiende las dos formas
|
|
252
|
+
* (uno o varios) y las dos puertas (con `gate` o sin ella). Todo el que
|
|
253
|
+
* pregunte tiene que pasar por aquí: el lienzo, el modal de conexión y el
|
|
254
|
+
* despachador. Escribirlo cada uno a su manera es como el router acabó con un
|
|
255
|
+
* camino propio que nadie arregló al arreglar el de los demás.
|
|
256
|
+
*/
|
|
257
|
+
export declare function outputGroupsFor(config: NodeUIConfig | undefined | null, entity: Record<string, unknown> | undefined | null): OutputGroupsConfig | undefined;
|
|
258
|
+
/** Todos los juegos declarados, pasen o no su puerta. Para quien necesita el
|
|
259
|
+
* UNIVERSO de puertos que un tipo puede emitir (la auditoría de puertos),
|
|
260
|
+
* no el de ahora mismo. */
|
|
261
|
+
export declare function todosLosOutputGroups(config: NodeUIConfig | undefined | null): OutputGroupsConfig[];
|
package/dist/types.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.singleMeta = singleMeta;
|
|
4
4
|
exports.iterableMeta = iterableMeta;
|
|
5
|
+
exports.outputGroupsFor = outputGroupsFor;
|
|
6
|
+
exports.todosLosOutputGroups = todosLosOutputGroups;
|
|
5
7
|
/** Helper to create _meta for single-object outputs */
|
|
6
8
|
function singleMeta() {
|
|
7
9
|
return { iterable: false, count: 1 };
|
|
@@ -10,3 +12,29 @@ function singleMeta() {
|
|
|
10
12
|
function iterableMeta(field, count) {
|
|
11
13
|
return { iterable: true, iterateField: field, count };
|
|
12
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* El juego de salidas que aplica AHORA a esta entidad, o `undefined` si el
|
|
17
|
+
* nodo no ramifica en este momento.
|
|
18
|
+
*
|
|
19
|
+
* Es la única lectura de `special.outputGroups` que entiende las dos formas
|
|
20
|
+
* (uno o varios) y las dos puertas (con `gate` o sin ella). Todo el que
|
|
21
|
+
* pregunte tiene que pasar por aquí: el lienzo, el modal de conexión y el
|
|
22
|
+
* despachador. Escribirlo cada uno a su manera es como el router acabó con un
|
|
23
|
+
* camino propio que nadie arregló al arreglar el de los demás.
|
|
24
|
+
*/
|
|
25
|
+
function outputGroupsFor(config, entity) {
|
|
26
|
+
const declarado = config?.special?.outputGroups;
|
|
27
|
+
if (!declarado)
|
|
28
|
+
return undefined;
|
|
29
|
+
const lista = Array.isArray(declarado) ? declarado : [declarado];
|
|
30
|
+
return lista.find((c) => !c.gate || entity?.[c.gate.field] === c.gate.value);
|
|
31
|
+
}
|
|
32
|
+
/** Todos los juegos declarados, pasen o no su puerta. Para quien necesita el
|
|
33
|
+
* UNIVERSO de puertos que un tipo puede emitir (la auditoría de puertos),
|
|
34
|
+
* no el de ahora mismo. */
|
|
35
|
+
function todosLosOutputGroups(config) {
|
|
36
|
+
const declarado = config?.special?.outputGroups;
|
|
37
|
+
if (!declarado)
|
|
38
|
+
return [];
|
|
39
|
+
return Array.isArray(declarado) ? declarado : [declarado];
|
|
40
|
+
}
|
package/dist/ui.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.PREFIX_TO_TYPE = exports.NODE_UI = void 0;
|
|
8
8
|
exports.resolveNodeId = resolveNodeId;
|
|
9
|
+
const sentiment_js_1 = require("./sentiment.js");
|
|
9
10
|
exports.NODE_UI = {
|
|
10
11
|
// ── Source nodes ──
|
|
11
12
|
// Una fuente arranca el flujo: `fromNodes: false` significa que nada puede
|
|
@@ -49,7 +50,15 @@ exports.NODE_UI = {
|
|
|
49
50
|
// no handle and no route. In any other outputMode the node falls back to
|
|
50
51
|
// the single right-out handle NodeShell renders when no dynamic outputs
|
|
51
52
|
// materialize.
|
|
52
|
-
|
|
53
|
+
// En modo `sentiment` (2026-09-22) el abanico son CUATRO puertos fijos
|
|
54
|
+
// —negative / neutral / positive / unclear— y no un contenedor del usuario:
|
|
55
|
+
// el modelo elige entre las tres primeras y el nodo manda a `unclear` lo que
|
|
56
|
+
// no llega al umbral de confianza. Por eso el AI Node declara DOS juegos de
|
|
57
|
+
// salidas, uno por modo; `outputGroupsFor` elige el que toca.
|
|
58
|
+
ai: { fromNodes: true, toNodes: true, outputHandles: ['right'], special: { loopBackAllowed: true, connectModal: 'output-group', outputGroups: [
|
|
59
|
+
{ field: 'categories', nameField: 'name', summaryField: 'description', label: 'category', gate: { field: 'outputMode', value: 'classifier' } },
|
|
60
|
+
{ label: 'sentiment', gate: { field: 'outputMode', value: 'sentiment' }, fixedGroups: sentiment_js_1.SENTIMENT_PORTS.map((p) => ({ name: sentiment_js_1.SENTIMENT_PORT_LABEL[p], port: p, summary: sentiment_js_1.SENTIMENT_PORT_SUMMARY[p] })) },
|
|
61
|
+
] } },
|
|
53
62
|
markdown: { fromNodes: true, toNodes: true, special: { loopBackAllowed: true } },
|
|
54
63
|
fileTransform: { fromNodes: true, toNodes: true },
|
|
55
64
|
limit: { fromNodes: true, toNodes: true, special: { loopBackAllowed: true } },
|
package/package.json
CHANGED