<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jose Eduardo Tirado Verbel</title>
    <description>The latest articles on DEV Community by Jose Eduardo Tirado Verbel (@jotive).</description>
    <link>https://dev.to/jotive</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1074530%2Fb580cfa7-9382-4871-9f02-8964e3c8c096.png</url>
      <title>DEV Community: Jose Eduardo Tirado Verbel</title>
      <link>https://dev.to/jotive</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jotive"/>
    <language>en</language>
    <item>
      <title>Un gateway para saber qué producto me quema la factura de LLM (y el día que mi README mintió)</title>
      <dc:creator>Jose Eduardo Tirado Verbel</dc:creator>
      <pubDate>Sat, 05 Sep 2026 16:19:17 +0000</pubDate>
      <link>https://dev.to/jotive/un-gateway-para-saber-que-producto-me-quema-la-factura-de-llm-y-el-dia-que-mi-readme-mintio-3ej3</link>
      <guid>https://dev.to/jotive/un-gateway-para-saber-que-producto-me-quema-la-factura-de-llm-y-el-dia-que-mi-readme-mintio-3ej3</guid>
      <description>&lt;p&gt;Tengo varios productos que llaman a modelos de lenguaje. Uno le pregunta a un corpus de tesis, otro procesa imágenes, otro arma video. Todos consumen tokens, y hasta hace poco todos gastaban en el mismo pozo sin que yo supiera cuál gastaba qué.&lt;/p&gt;

&lt;p&gt;El problema no es nuevo. Llega la factura, ves un número, y no tenés idea de qué feature lo disparó, qué producto se está yendo de rango, o si uno está repreguntando lo mismo mil veces. Te enterás cuando ya gastaste.&lt;/p&gt;

&lt;p&gt;Esto es lo que hice para dejar de estar a ciegas, y una cosa incómoda que encontré en el camino.&lt;/p&gt;




&lt;h2&gt;
  
  
  La idea: un peaje en el medio
&lt;/h2&gt;

&lt;p&gt;En vez de que cada producto llame directo al proveedor, todos pasan por un mismo servicio en el medio, un gateway. Expone la misma interfaz de siempre (&lt;code&gt;/v1/chat/completions&lt;/code&gt;), así que para el producto cambia una sola cosa: la URL base. Nada más.&lt;/p&gt;

&lt;p&gt;Y como todo pasa por ahí, el gateway puede hacer tres cosas que ningún producto por separado hacía:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contabilidad por producto.&lt;/strong&gt; Cada llamada llega con un header que dice quién es (&lt;code&gt;X-Tenant-ID&lt;/code&gt;). El gateway guarda tenant, modelo, tokens y costo de cada request. Ahora sé exactamente qué producto gastó qué.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache semántico.&lt;/strong&gt; Antes de llamar al modelo, compara la pregunta con las que ya respondió. Si es casi la misma, devuelve la respuesta guardada sin gastar un token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quota por producto.&lt;/strong&gt; Un tope de consumo por tenant, para que uno que se descontrola no se lleve todo por delante.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nada de esto es magia. Es un proxy con Postgres y un poco de criterio sobre qué guardar.&lt;/p&gt;




&lt;h2&gt;
  
  
  Por qué OpenRouter abajo
&lt;/h2&gt;

&lt;p&gt;El gateway no habla con un proveedor específico, habla con &lt;strong&gt;OpenRouter&lt;/strong&gt;, que es un agregador con una sola API compatible con OpenAI y acceso a muchos modelos (Gemini, Llama, Claude, y más) con una única key.&lt;/p&gt;

&lt;p&gt;Esto resolvió dos cosas a la vez. Primero, dejé de atarme a un proveedor. Segundo, y esto es lo bueno, OpenRouter me da el gasto total pero no me lo desglosa por producto mío. El gateway se sienta justo ahí en el medio y agrega ese desglose que a OpenRouter no le corresponde tener. Cada uno hace su parte.&lt;/p&gt;




&lt;h2&gt;
  
  
  La parte incómoda: el README mentía
&lt;/h2&gt;

&lt;p&gt;Cuando fui a revisar el repo para publicarlo, leí el README con ojos de desconocido. Prometía un failover automático: si un proveedor fallaba, la petición se reenrutaba sola a otro.&lt;/p&gt;

&lt;p&gt;Abrí el código. No estaba. En ningún lado. Era una intención que en algún momento escribí como si ya existiera.&lt;/p&gt;

&lt;p&gt;Es un detalle pequeño y a la vez no lo es. Un reclutador o un colega que abra ese código y note que el README dice algo que el programa no hace, deja de creerte el resto. Una feature inventada resta más de lo que suma cualquier feature real.&lt;/p&gt;

&lt;p&gt;Así que hice lo aburrido y correcto: moví el failover a una sección de "roadmap", marcada como no implementada, y dejé el README describiendo lo que el código hace hoy, no lo que me gustaría que hiciera. Prefiero un proyecto que promete menos y cumple todo, que uno que impresiona en el README y decepciona en el &lt;code&gt;git clone&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dónde quedó
&lt;/h2&gt;

&lt;p&gt;El gateway está corriendo en producción, y el primer producto que lo consume de verdad es mi buscador de tesis: le cambié el cliente para que apunte al gateway, le pasé su identificador de tenant, y ahora cada pregunta que responde queda registrada con su costo. Lo puedo mirar y decir "este producto va por acá de gasto", sin adivinar.&lt;/p&gt;

&lt;p&gt;El molde quedó listo para enchufar los demás productos igual: apuntar su cliente al gateway, ponerle su nombre de tenant, y aparece solo en la contabilidad.&lt;/p&gt;

&lt;p&gt;El código está acá: &lt;a href="https://github.com/GeosData/llm-quota-gateway" rel="noopener noreferrer"&gt;github.com/GeosData/llm-quota-gateway&lt;/a&gt;. Con los ADRs de las decisiones que sí tomé, y un roadmap honesto de las que todavía no.&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
