@hostwebhook/platform-contracts 0.8.0 → 0.9.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.
@@ -49,9 +49,32 @@
49
49
  *
50
50
  * ## Lo que esta tabla NO modela (aceptable para estimar coste)
51
51
  *
52
- * - Contexto largo: `gemini-3.1-pro` y `gemini-2.5-pro` casi doblan tarifa
53
- * por encima de 200k tokens de entrada. Se usa siempre la base, así que
54
- * INFRAVALORA los prompts muy largos.
52
+ * - (RESUELTO, ver `umbralEntrada`) Contexto largo. Ya se modela en los diez
53
+ * modelos que lo publican: los siete de OpenAI 5.4/5.5/5.6 a partir de
54
+ * 272.000 tokens de entrada, y `gemini-3.1-pro` / `gemini-2.5-pro` a partir
55
+ * de 200.000. Es un ACANTILADO: la tarifa alta cubre la petición entera.
56
+ * - (!) Lo que del contexto largo SIGUE sin modelarse: en `gpt-5.5` y en
57
+ * `gpt-5.4` la ficha dice «for the full SESSION», no por petición. Basta que
58
+ * UNA llamada de la sesión cruce 272k para que toda la sesión vaya a tarifa
59
+ * alta. `calculateCost` es una función pura por llamada y no tiene noción de
60
+ * sesión: ahí INFRAVALORA, y se dice en vez de fingir que se modela.
61
+ * - ✅ Anthropic NO tiene tramos, y conviene dejarlo escrito en afirmativo:
62
+ * su doc dice que «Claude 4.6 and later models include the full 1M token
63
+ * context window at standard pricing». La regla que circula por terceros
64
+ * diciendo que Sonnet 4.5 cobra 2x/1.5x por encima de 200k es FALSA. No
65
+ * reintroducirla.
66
+ * - (!) `gpt-5.6-sol` (y su alias `gpt-5.6`) está a precio PROMOCIONAL: la
67
+ * propia tabla dice «available at least through November 21, 2026», y no
68
+ * publica a cuánto revierte. Un tope calibrado sobre 4.00/20.00 tiene fecha
69
+ * de caducidad documentada.
70
+ * - (!) Google factura los tokens de RAZONAMIENTO a precio de salida — el
71
+ * encabezado de su columna dice «Output price (including thinking tokens)».
72
+ * Quien multiplique sólo los tokens visibles por `output` se queda corto, y
73
+ * el razonamiento no se ve en la respuesta pero sí en la factura.
74
+ * - (!) Claude 4.7 en adelante usa un tokenizador que produce ~30% MÁS tokens
75
+ * para el mismo texto, y eso incluye Sonnet 5, Opus 5, Opus 4.8 y Opus 4.7,
76
+ * no sólo Fable 5. Sonnet 5 a $2 frente a Sonnet 4.6 a $3 parece un 33%
77
+ * más barato; con un 30% más de tokens el ahorro real se acerca a cero.
55
78
  * - Audio: `gemini-2.5-flash` y `3.1-flash-lite` cobran más el audio que el
56
79
  * texto. Aquí todo va a tarifa de texto.
57
80
  * - Anthropic: `inference_geo` "us" multiplica por 1,1, y el modo rápido de
@@ -67,7 +90,42 @@
67
90
  export interface ModelPricing {
68
91
  input: number;
69
92
  output: number;
93
+ /**
94
+ * Tokens de ENTRADA por encima de los cuales la petición cambia de tarifa.
95
+ * Ausente = tarifa plana en toda la ventana, que es el caso de Anthropic y de
96
+ * casi todo lo demás.
97
+ *
98
+ * El nombre dice qué se mide: los dos proveedores que tienen tramos lo miden
99
+ * sobre el PROMPT, nunca sobre entrada+salida. En Google la palabra literal de
100
+ * las tres filas de su tabla es «prompts».
101
+ *
102
+ * (!) La comparación es ESTRICTA: OpenAI escribe «>272K» y Google «> 200k».
103
+ * Exactamente 272.000 va a tarifa base.
104
+ */
105
+ umbralEntrada?: number;
106
+ /**
107
+ * (!) NO es facturación marginal: es un ACANTILADO. La tarifa alta se aplica
108
+ * a TODOS los tokens de la petición, no sólo a los que exceden el umbral.
109
+ *
110
+ * Un prompt de 272.001 tokens en `gpt-5.5` se cobra ENTERO a $10, no 272.000
111
+ * a $5 más uno a $10. Cruzar el umbral por un token duplica la factura de
112
+ * entrada de esa llamada y encarece un 50 % su salida aunque la salida sean
113
+ * cincuenta tokens.
114
+ */
115
+ inputLargo?: number;
116
+ outputLargo?: number;
70
117
  }
118
+ /**
119
+ * La tarifa que toca, según cuánta entrada lleve la petición.
120
+ *
121
+ * Se saca aparte de `calculateCost` porque quien quiera estimar ANTES de llamar
122
+ * —la reserva del tope de gasto del Chat Trigger— necesita la misma decisión sin
123
+ * tener aún los tokens de salida.
124
+ */
125
+ export declare function tarifaDelTramo(precio: ModelPricing, tokensDeEntrada: number): {
126
+ input: number;
127
+ output: number;
128
+ };
71
129
  export declare const MODEL_PRICING: Record<string, ModelPricing>;
72
130
  /**
73
131
  * Qué entrada de la tabla pone precio a este modelo, o `null` cuando de verdad
@@ -50,9 +50,32 @@
50
50
  *
51
51
  * ## Lo que esta tabla NO modela (aceptable para estimar coste)
52
52
  *
53
- * - Contexto largo: `gemini-3.1-pro` y `gemini-2.5-pro` casi doblan tarifa
54
- * por encima de 200k tokens de entrada. Se usa siempre la base, así que
55
- * INFRAVALORA los prompts muy largos.
53
+ * - (RESUELTO, ver `umbralEntrada`) Contexto largo. Ya se modela en los diez
54
+ * modelos que lo publican: los siete de OpenAI 5.4/5.5/5.6 a partir de
55
+ * 272.000 tokens de entrada, y `gemini-3.1-pro` / `gemini-2.5-pro` a partir
56
+ * de 200.000. Es un ACANTILADO: la tarifa alta cubre la petición entera.
57
+ * - (!) Lo que del contexto largo SIGUE sin modelarse: en `gpt-5.5` y en
58
+ * `gpt-5.4` la ficha dice «for the full SESSION», no por petición. Basta que
59
+ * UNA llamada de la sesión cruce 272k para que toda la sesión vaya a tarifa
60
+ * alta. `calculateCost` es una función pura por llamada y no tiene noción de
61
+ * sesión: ahí INFRAVALORA, y se dice en vez de fingir que se modela.
62
+ * - ✅ Anthropic NO tiene tramos, y conviene dejarlo escrito en afirmativo:
63
+ * su doc dice que «Claude 4.6 and later models include the full 1M token
64
+ * context window at standard pricing». La regla que circula por terceros
65
+ * diciendo que Sonnet 4.5 cobra 2x/1.5x por encima de 200k es FALSA. No
66
+ * reintroducirla.
67
+ * - (!) `gpt-5.6-sol` (y su alias `gpt-5.6`) está a precio PROMOCIONAL: la
68
+ * propia tabla dice «available at least through November 21, 2026», y no
69
+ * publica a cuánto revierte. Un tope calibrado sobre 4.00/20.00 tiene fecha
70
+ * de caducidad documentada.
71
+ * - (!) Google factura los tokens de RAZONAMIENTO a precio de salida — el
72
+ * encabezado de su columna dice «Output price (including thinking tokens)».
73
+ * Quien multiplique sólo los tokens visibles por `output` se queda corto, y
74
+ * el razonamiento no se ve en la respuesta pero sí en la factura.
75
+ * - (!) Claude 4.7 en adelante usa un tokenizador que produce ~30% MÁS tokens
76
+ * para el mismo texto, y eso incluye Sonnet 5, Opus 5, Opus 4.8 y Opus 4.7,
77
+ * no sólo Fable 5. Sonnet 5 a $2 frente a Sonnet 4.6 a $3 parece un 33%
78
+ * más barato; con un 30% más de tokens el ahorro real se acerca a cero.
56
79
  * - Audio: `gemini-2.5-flash` y `3.1-flash-lite` cobran más el audio que el
57
80
  * texto. Aquí todo va a tarifa de texto.
58
81
  * - Anthropic: `inference_geo` "us" multiplica por 1,1, y el modo rápido de
@@ -67,22 +90,44 @@
67
90
  */
68
91
  Object.defineProperty(exports, "__esModule", { value: true });
69
92
  exports.MODEL_ALTERNATIVES = exports.MODEL_PRICING = void 0;
93
+ exports.tarifaDelTramo = tarifaDelTramo;
70
94
  exports.findPricingKey = findPricingKey;
71
95
  exports.isModelPriced = isModelPriced;
72
96
  exports.calculateCost = calculateCost;
73
97
  exports.getKnownModels = getKnownModels;
74
98
  exports.getSuggestedAlternative = getSuggestedAlternative;
99
+ /**
100
+ * La tarifa que toca, según cuánta entrada lleve la petición.
101
+ *
102
+ * Se saca aparte de `calculateCost` porque quien quiera estimar ANTES de llamar
103
+ * —la reserva del tope de gasto del Chat Trigger— necesita la misma decisión sin
104
+ * tener aún los tokens de salida.
105
+ */
106
+ function tarifaDelTramo(precio, tokensDeEntrada) {
107
+ if (precio.umbralEntrada === undefined) {
108
+ return { input: precio.input, output: precio.output };
109
+ }
110
+ if (tokensDeEntrada <= precio.umbralEntrada) {
111
+ return { input: precio.input, output: precio.output };
112
+ }
113
+ return { input: precio.inputLargo, output: precio.outputLargo };
114
+ }
75
115
  exports.MODEL_PRICING = {
76
116
  // OpenAI — GPT series
77
- 'gpt-5.6-sol': { input: 4.00, output: 20.00 },
78
- 'gpt-5.6-terra': { input: 2.00, output: 12.00 },
79
- 'gpt-5.6-luna': { input: 0.20, output: 1.20 },
80
- 'gpt-5.5-pro': { input: 30.00, output: 180.00 },
81
- 'gpt-5.5': { input: 5.00, output: 30.00 },
82
- 'gpt-5.4-pro': { input: 30.00, output: 180.00 },
83
- 'gpt-5.4': { input: 2.50, output: 15.00 },
117
+ 'gpt-5.6-sol': { input: 4.00, output: 20.00, umbralEntrada: 272000, inputLargo: 8.00, outputLargo: 30.00 },
118
+ // `gpt-5.6` a secas es el alias documentado de Sol, y sin esta entrada caia
119
+ // por prefijo en `gpt-5` otro modelo y otro precio.
120
+ 'gpt-5.6': { input: 4.00, output: 20.00, umbralEntrada: 272000, inputLargo: 8.00, outputLargo: 30.00 },
121
+ 'gpt-5.6-terra': { input: 2.00, output: 12.00, umbralEntrada: 272000, inputLargo: 4.00, outputLargo: 18.00 },
122
+ 'gpt-5.6-luna': { input: 0.20, output: 1.20, umbralEntrada: 272000, inputLargo: 0.40, outputLargo: 1.80 },
123
+ 'gpt-5.5-pro': { input: 30.00, output: 180.00, umbralEntrada: 272000, inputLargo: 60.00, outputLargo: 270.00 },
124
+ 'gpt-5.5': { input: 5.00, output: 30.00, umbralEntrada: 272000, inputLargo: 10.00, outputLargo: 45.00 },
125
+ 'gpt-5.4-pro': { input: 30.00, output: 180.00, umbralEntrada: 272000, inputLargo: 60.00, outputLargo: 270.00 },
126
+ 'gpt-5.4': { input: 2.50, output: 15.00, umbralEntrada: 272000, inputLargo: 5.00, outputLargo: 22.50 },
84
127
  'gpt-5.4-mini': { input: 0.75, output: 4.50 },
85
128
  'gpt-5.4-nano': { input: 0.20, output: 1.25 },
129
+ // (!) SIN tramo, y es deliberado: es el unico *-pro de la familia 5.x cuya
130
+ // ficha no publica umbral de contexto largo. Verificado fila a fila.
86
131
  'gpt-5.2-pro': { input: 21.00, output: 168.00 },
87
132
  'gpt-5.2': { input: 1.75, output: 14.00 },
88
133
  'gpt-5.1': { input: 1.25, output: 10.00 },
@@ -132,6 +177,30 @@ exports.MODEL_PRICING = {
132
177
  'claude-sonnet-4': { input: 3.00, output: 15.00 },
133
178
  'claude-sonnet-4-20250514': { input: 3.00, output: 15.00 },
134
179
  'claude-haiku-4-5-20251001': { input: 1.00, output: 5.00 },
180
+ // (!) Sin apellido de fecha, y NO es redundante: la clave fechada es mas
181
+ // LARGA, asi que la regla de prefijo nunca alcanzaba a `claude-haiku-4-5`.
182
+ // Y ese es `getDefaultModel('anthropic')` —con el nace todo nodo de IA— y
183
+ // tambien `PLATFORM_LLM_CHEAP_MODEL`. Mientras falto, cada nodo nuevo salia
184
+ // a coste CERO y su tope de gasto en dolares no podia saltar jamas.
185
+ 'claude-haiku-4-5': { input: 1.00, output: 5.00 },
186
+ // ── Groq ────────────────────────────────────────────────────────────────
187
+ // (!) Estas tres NO estan verificadas contra una pagina oficial de Groq:
188
+ // console.groq.com/docs/pricing da 404 y groq.com/pricing es marketing. Salen
189
+ // de agregadores de terceros coincidentes entre si (agosto 2026). Reverificar
190
+ // en cuanto Groq publique una pagina consultable.
191
+ //
192
+ // Aun asi entran: sin ellas el coste sale CERO, y cero no es "barato", es
193
+ // "un tope de gasto que no salta". Una cifra aproximada de la fuente correcta
194
+ // protege; la ausencia no protege nada.
195
+ 'openai/gpt-oss-20b': { input: 0.075, output: 0.30 },
196
+ 'openai/gpt-oss-120b': { input: 0.15, output: 0.60 },
197
+ 'qwen/qwen3.6-27b': { input: 0.60, output: 3.00 },
198
+ //
199
+ // `groq/compound` y `groq/compound-mini` NO se ponen, y no por falta de dato:
200
+ // es que NO TIENEN tarifa plana por token. Son sistemas agenticos que
201
+ // repercuten el modelo subyacente MAS el coste de las herramientas que usan,
202
+ // asi que no hay un numero unico que sea correcto. Inventar uno seria peor que
203
+ // el hueco. Quedan en la lista de excepciones del test, con este motivo.
135
204
  'claude-3-7-sonnet-20250219': { input: 3.00, output: 15.00 },
136
205
  'claude-3-5-sonnet-20241022': { input: 3.00, output: 15.00 },
137
206
  'claude-3-5-haiku-20241022': { input: 0.80, output: 4.00 },
@@ -142,10 +211,10 @@ exports.MODEL_PRICING = {
142
211
  'gemini-3.6-flash': { input: 0.75, output: 3.75 },
143
212
  'gemini-3.5-flash-lite': { input: 0.30, output: 2.50 },
144
213
  'gemini-3.5-flash': { input: 1.50, output: 9.00 },
145
- 'gemini-3.1-pro': { input: 2.00, output: 12.00 },
214
+ 'gemini-3.1-pro': { input: 2.00, output: 12.00, umbralEntrada: 200000, inputLargo: 4.00, outputLargo: 18.00 },
146
215
  'gemini-3.1-flash-lite': { input: 0.25, output: 1.50 },
147
216
  'gemini-3-flash': { input: 0.50, output: 3.00 },
148
- 'gemini-2.5-pro': { input: 1.25, output: 10.00 },
217
+ 'gemini-2.5-pro': { input: 1.25, output: 10.00, umbralEntrada: 200000, inputLargo: 2.50, outputLargo: 15.00 },
149
218
  'gemini-2.5-flash': { input: 0.30, output: 2.50 },
150
219
  'gemini-2.5-flash-lite': { input: 0.10, output: 0.40 },
151
220
  'gemini-2.0-flash': { input: 0.10, output: 0.40 },
@@ -277,13 +346,27 @@ function calculateCost(model, inputTokens, outputTokens, cacheReadInputTokens =
277
346
  return 0;
278
347
  const pricing = exports.MODEL_PRICING[key];
279
348
  const provider = detectProvider(key);
280
- const inputCost = (inputTokens / 1000000) * pricing.input;
281
- const outputCost = (outputTokens / 1000000) * pricing.output;
349
+ /**
350
+ * Los tokens cacheados CUENTAN para decidir el tramo: fisicamente son el
351
+ * prompt, y en un tope de gasto errar del lado caro es errar del lado seguro.
352
+ *
353
+ * (!) Ninguno de los dos proveedores lo dice explicitamente, asi que es una
354
+ * decision nuestra, no una cita. Hoy es INERTE —los llamadores pasan tres
355
+ * argumentos y el emisor de trazas nunca rellena los campos de cache—, pero
356
+ * hay que confirmarla antes de que alguien empiece a rellenarlos.
357
+ */
358
+ const entradaParaElTramo = inputTokens + cacheReadInputTokens + cacheCreationInputTokens;
359
+ const tarifa = tarifaDelTramo(pricing, entradaParaElTramo);
360
+ const inputCost = (inputTokens / 1000000) * tarifa.input;
361
+ const outputCost = (outputTokens / 1000000) * tarifa.output;
282
362
  // Caché: multiplicadores por proveedor
283
363
  const cacheReadMul = CACHE_READ_MULTIPLIERS[provider] ?? 0.1;
284
364
  const cacheCreationMul = CACHE_CREATION_MULTIPLIERS[provider] ?? 0;
285
- const cacheReadCost = (cacheReadInputTokens / 1000000) * pricing.input * cacheReadMul;
286
- const cacheCreationCost = (cacheCreationInputTokens / 1000000) * pricing.input * cacheCreationMul;
365
+ // Sobre `tarifa.input`, no sobre `pricing.input`: en tramo alto la cache
366
+ // tambien se encarece, que es lo que dice la tabla de Google —su fila de
367
+ // cache esta partida por tamano de prompt igual que las otras dos.
368
+ const cacheReadCost = (cacheReadInputTokens / 1000000) * tarifa.input * cacheReadMul;
369
+ const cacheCreationCost = (cacheCreationInputTokens / 1000000) * tarifa.input * cacheCreationMul;
287
370
  return (Math.round((inputCost + outputCost + cacheReadCost + cacheCreationCost) * 1000000) / 1000000);
288
371
  }
289
372
  /** Los modelos que la tabla sabe poner en precio, en orden de declaración. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/platform-contracts",
3
- "version": "0.8.0",
3
+ "version": "0.9.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",