@hostwebhook/platform-contracts 0.10.0 → 0.11.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.
@@ -32,14 +32,16 @@
32
32
  * Antes la puerta leía `memoryEnabled` del documento para decidir; esa decisión
33
33
  * se va con el dato, que es donde puede comprobarse.
34
34
  */
35
+ import type { LlmProvider } from '@hostwebhook/node-types';
35
36
  /**
36
- * El nodo conectado, en lo que la puerta lee de él. Siete campos, medidos.
37
+ * El nodo conectado, en lo que la puerta lee de él. Ocho campos, medidos.
37
38
  *
38
- * Los cuatro primeros son lo que la puerta necesitaba para EJECUTAR. Los tres
39
+ * Los cuatro primeros son lo que la puerta necesitaba para EJECUTAR. Los cuatro
39
40
  * últimos son lo que necesita para PONERLE PRECIO al turno antes de ejecutarlo:
40
41
  * el Chat Trigger tiene topes en dólares y reserva por adelantado, y esa reserva
41
- * no se puede calcular sin saber a qué modelo se va a llamar ni cuánto puede
42
- * devolver. Sin ellos la estimación caía a sus valores por defecto en silencio.
42
+ * no se puede calcular sin saber de quién ni a qué modelo se va a llamar, ni
43
+ * cuánto puede devolver. Sin ellos la estimación caía a sus valores por defecto
44
+ * en silencio.
43
45
  */
44
46
  export interface NodoDeIaConectado {
45
47
  id: string;
@@ -48,6 +50,24 @@ export interface NodoDeIaConectado {
48
50
  workspaceId: string | null;
49
51
  /** Si el widget enseña las llamadas a herramientas mientras ocurren. */
50
52
  muestraLasHerramientas: boolean;
53
+ /**
54
+ * El proveedor configurado en el nodo. Viaja porque el nombre del modelo NO
55
+ * lo determina, y adivinarlo se rompe de la peor manera: en silencio y con
56
+ * cara de acierto.
57
+ *
58
+ * 🔥 El caso que lo cierra: `openai/gpt-oss-20b` es un id REAL de OpenRouter.
59
+ * Empieza por `openai/`, así que cualquier heurística sobre el nombre lo da
60
+ * por OpenAI, le pone la tarifa de OpenAI y reserva el tope equivocado. Y no
61
+ * es un caso de laboratorio: TODO el catálogo de OpenRouter va prefijado con
62
+ * el nombre del laboratorio de origen (`anthropic/…`, `google/…`), o sea que
63
+ * el choque es la norma ahí, no la excepción.
64
+ *
65
+ * ⚠️ Es la LlmProvider de `node-types`, la misma que elige el selector, y va
66
+ * obligatorio: opcional dejaría intacto el camino de adivinar, que es justo
67
+ * lo que sobra. Quien sirva este contrato lee el campo del nodo; no lo
68
+ * deduzca del modelo.
69
+ */
70
+ provider: LlmProvider;
51
71
  /** El modelo configurado en el nodo. Sin el nombre no hay tarifa, y sin
52
72
  * tarifa la reserva del tope EN DÓLARES no se puede calcular: los tokens
53
73
  * estimados no son un precio hasta que alguien dice de qué modelo son. */
@@ -1,38 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Lo que el Chat Trigger necesita del nodo de IA.
4
- *
5
- * ## Por qué existe
6
- *
7
- * `chat-triggers` vive en el gateway —es uno de los cinco nodos de ORIGEN— y
8
- * `ai-nodes` se muda a hw-nodes. Era el último cruce de la puerta hacia el
9
- * ejecutor, y el más difícil de los tres: los otros dos eran filas de base de
10
- * datos; éste lleva STREAMING.
11
- *
12
- * ⚠️ Y no se resolvía con el traspaso del motor. El chat NO pasa por el motor,
13
- * y está escrito en tres sitios de su propio fichero: «chatTrigger is an
14
- * ingress, not dispatched in-pipeline».
15
- *
16
- * ## 🔥 Lo que NO puede cruzar: el documento mutado
17
- *
18
- * El código de antes hacía esto:
19
- *
20
- * const aiNode = await this.aiNodeModel.findOne({…});
21
- * if (trigger.systemPromptOverride) an.systemPrompt = trigger.systemPromptOverride;
22
- * if (an.memoryEnabled) an.memoryUserId = sessionId;
23
- * await this.aiNodesService.executeForEvent(aiNode, payload, {…});
24
- *
25
- * O sea: la puerta cargaba el documento del nodo, le PARCHEABA dos campos en
26
- * memoria y se lo pasaba al ejecutor. Un documento de Mongoose parcheado no
27
- * cruza una red.
28
- *
29
- * Así que la puerta deja de cargar el nodo. Manda el id y los dos ajustes, y
30
- * quien tiene el nodo los aplica.
31
- *
32
- * ⚠️ El `sessionId` viaja SIEMPRE y se aplica sólo si el nodo tiene memoria.
33
- * Antes la puerta leía `memoryEnabled` del documento para decidir; esa decisión
34
- * se va con el dato, que es donde puede comprobarse.
35
- */
36
2
  Object.defineProperty(exports, "__esModule", { value: true });
37
3
  exports.EJECUCION_DE_IA = void 0;
38
4
  /** ⚠️ Cadena y no `Symbol`. */
@@ -16,13 +16,34 @@
16
16
  * cliente real, que reenvía lo que conteste el servicio de trazas sin darle
17
17
  * forma. Tipar aquí una forma que allí no se garantiza sería inventarla.
18
18
  */
19
+ import type { LlmProvider } from '@hostwebhook/node-types';
20
+ /**
21
+ * El proveedor que atendió la llamada, tal como se apunta en la traza.
22
+ *
23
+ * 🔥 Se DERIVA de `LlmProvider` en vez de repetirse, y ése es el arreglo. La
24
+ * lista escrita a mano que había aquí se quedó en los tres primeros: no tenía
25
+ * ni `groq` —que es proveedor del catálogo desde hace tiempo— ni `openrouter`,
26
+ * que se añadió en la 1.68.0 de `node-types`. El síntoma no fue un fallo de
27
+ * compilación sino su contrario: los llamantes escribían
28
+ * `provider: entity.provider as any` para poder emitir la traza, y ese `as any`
29
+ * apagaba de paso cualquier otra comprobación del objeto. Derivando, un
30
+ * proveedor nuevo en el catálogo entra aquí solo.
31
+ *
32
+ * ⚠️ Los cuatro de abajo NO están en `LlmProvider` y se quedan a propósito:
33
+ * esto describe lo que hay APUNTADO, no lo que se puede elegir hoy. `ollama`
34
+ * tiene su `llm_ollama` en el registro de credenciales aunque no sea un
35
+ * `LlmProvider`; `mistral`, `cohere` y `custom` son lo que devuelve
36
+ * `detectProvider` en `precios/tabla-de-precios.ts`. Quitarlos no arreglaría
37
+ * nada: dejaría de compilar la lectura de trazas que ya existen.
38
+ */
39
+ export type ProveedorDeTraza = LlmProvider | 'ollama' | 'mistral' | 'cohere' | 'custom';
19
40
  /** Lo que se apunta de una llamada al modelo. */
20
41
  export interface TrazaDeLlm {
21
42
  traceId?: string;
22
43
  spanId?: string;
23
44
  startTime: string | Date;
24
45
  endTime?: string | Date;
25
- provider: 'openai' | 'anthropic' | 'google' | 'ollama' | 'mistral' | 'cohere' | 'custom';
46
+ provider: ProveedorDeTraza;
26
47
  model: string;
27
48
  operation?: 'chat' | 'completion' | 'embedding' | 'image' | 'audio';
28
49
  inputTokens?: number;
@@ -62,6 +83,14 @@ export interface FiltrosDeTrazas {
62
83
  aiNodeId?: string;
63
84
  status?: 'success' | 'error' | 'timeout';
64
85
  model?: string;
86
+ /**
87
+ * ⚠️ `string` y NO `ProveedorDeTraza`, a diferencia del campo de la traza.
88
+ * No es un descuido: esto es un filtro y llega de la query de una pantalla,
89
+ * o sea de `req.query.provider`, que es una cadena cualquiera. Estrecharlo
90
+ * obligaría a castear en el borde exactamente igual que hacía el `as any`
91
+ * que este cambio viene a quitar, y sin ganar nada: un proveedor que no
92
+ * exista aquí no rompe nada, simplemente no casa con ninguna traza.
93
+ */
65
94
  provider?: string;
66
95
  startAfter?: string;
67
96
  startBefore?: string;
@@ -1,22 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Lo que el AI Node apunta y consulta sobre sus llamadas al modelo.
4
- *
5
- * Costura de la Fase 2, del lote de las pequeñas. `LlmTracesClientService` es
6
- * un CLIENTE HTTP contra `hw-llm-traces`, que ya es un servicio aparte — el
7
- * coste de los logs no vive en esta api. Aun así el nodo no debe conocer al
8
- * cliente: arrastra su configuración, su reintento y su autenticación.
9
- *
10
- * ## Cómo viaja
11
- *
12
- * ⚠️ `emitTrace` devuelve `Promise<void>` y su llamante NO la espera: apuntar
13
- * una traza no puede retrasar una respuesta del modelo ni tumbarla. Eso hay
14
- * que conservarlo.
15
- *
16
- * Las dos consultas devuelven `unknown` a propósito — es lo que devuelve el
17
- * cliente real, que reenvía lo que conteste el servicio de trazas sin darle
18
- * forma. Tipar aquí una forma que allí no se garantiza sería inventarla.
19
- */
20
2
  Object.defineProperty(exports, "__esModule", { value: true });
21
3
  exports.TRAZAS_DE_LLM = void 0;
22
4
  /** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/platform-contracts",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Contratos compartidos entre los servicios de HostWebhook: addons del plan, identidad interna, y las formas que cruzan una frontera de red",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,7 +9,7 @@
9
9
  ],
10
10
  "scripts": {
11
11
  "build": "npm --prefix ../node-types run build && tsc",
12
- "test": "vitest run",
12
+ "test": "npm --prefix ../node-types run build && tsc -p tsconfig.tests.json && vitest run",
13
13
  "prepublishOnly": "npm run build"
14
14
  },
15
15
  "keywords": [
@@ -26,6 +26,6 @@
26
26
  },
27
27
  "peerDependencies": {
28
28
  "@nestjs/common": ">=11.0.0 <12",
29
- "@hostwebhook/node-types": ">=1.68.0 <2"
29
+ "@hostwebhook/node-types": ">=1.69.0 <2"
30
30
  }
31
31
  }