@hostwebhook/platform-contracts 0.8.1 → 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,25 +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 },
117
+ 'gpt-5.6-sol': { input: 4.00, output: 20.00, umbralEntrada: 272000, inputLargo: 8.00, outputLargo: 30.00 },
78
118
  // `gpt-5.6` a secas es el alias documentado de Sol, y sin esta entrada caia
79
119
  // por prefijo en `gpt-5` — otro modelo y otro precio.
80
- 'gpt-5.6': { input: 4.00, output: 20.00 },
81
- 'gpt-5.6-terra': { input: 2.00, output: 12.00 },
82
- 'gpt-5.6-luna': { input: 0.20, output: 1.20 },
83
- 'gpt-5.5-pro': { input: 30.00, output: 180.00 },
84
- 'gpt-5.5': { input: 5.00, output: 30.00 },
85
- 'gpt-5.4-pro': { input: 30.00, output: 180.00 },
86
- 'gpt-5.4': { input: 2.50, output: 15.00 },
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 },
87
127
  'gpt-5.4-mini': { input: 0.75, output: 4.50 },
88
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.
89
131
  'gpt-5.2-pro': { input: 21.00, output: 168.00 },
90
132
  'gpt-5.2': { input: 1.75, output: 14.00 },
91
133
  'gpt-5.1': { input: 1.25, output: 10.00 },
@@ -169,10 +211,10 @@ exports.MODEL_PRICING = {
169
211
  'gemini-3.6-flash': { input: 0.75, output: 3.75 },
170
212
  'gemini-3.5-flash-lite': { input: 0.30, output: 2.50 },
171
213
  'gemini-3.5-flash': { input: 1.50, output: 9.00 },
172
- '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 },
173
215
  'gemini-3.1-flash-lite': { input: 0.25, output: 1.50 },
174
216
  'gemini-3-flash': { input: 0.50, output: 3.00 },
175
- '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 },
176
218
  'gemini-2.5-flash': { input: 0.30, output: 2.50 },
177
219
  'gemini-2.5-flash-lite': { input: 0.10, output: 0.40 },
178
220
  'gemini-2.0-flash': { input: 0.10, output: 0.40 },
@@ -304,13 +346,27 @@ function calculateCost(model, inputTokens, outputTokens, cacheReadInputTokens =
304
346
  return 0;
305
347
  const pricing = exports.MODEL_PRICING[key];
306
348
  const provider = detectProvider(key);
307
- const inputCost = (inputTokens / 1000000) * pricing.input;
308
- 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;
309
362
  // Caché: multiplicadores por proveedor
310
363
  const cacheReadMul = CACHE_READ_MULTIPLIERS[provider] ?? 0.1;
311
364
  const cacheCreationMul = CACHE_CREATION_MULTIPLIERS[provider] ?? 0;
312
- const cacheReadCost = (cacheReadInputTokens / 1000000) * pricing.input * cacheReadMul;
313
- 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;
314
370
  return (Math.round((inputCost + outputCost + cacheReadCost + cacheCreationCost) * 1000000) / 1000000);
315
371
  }
316
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.1",
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",