Volver al blog
IAPor Adrián Pastora··10 min de lectura

FREEAI: un orquestador de LLMs gratuitos con API compatible con OpenAI

Las free tiers de Cerebras, Groq, Gemini, Mistral, OpenRouter, Cohere y HuggingFace por separado dan para un prototipo. Sumadas detrás de un mismo endpoint, dan para producción. Cómo lo construí y por qué lo abrí en MIT.

FREEAILLMsOpen SourceFastAPIPostgresStreamingMIT

Hoy, 18 de mayo de 2026, abro FREEAI en GitHub bajo licencia MIT. Un proyecto en el que llevaba meses trabajando. Es un orquestador que pone siete proveedores de LLMs gratuitos detrás de un único endpoint compatible con OpenAI. Este post es la versión larga del por qué y, sobre todo, de las decisiones técnicas que no son obvias.

El problema: free tiers sueltas no llegan a producción

Cerebras, Groq, Gemini, Mistral, OpenRouter, Cohere y HuggingFace tienen plan gratuito. Si los miras uno por uno, dan entre 10 y 30 peticiones por minuto y unos cientos al día. Suficiente para un prototipo, ridículo para algo serio.

A esto se le suma un segundo problema más insidioso: lock-in silencioso. Atarte a un único proveedor significa que cualquier cambio de precio, de rate-limit o una caída transitoria te bloquea el sistema entero. Y migrar de proveedor no es cambiar una URL: cada SDK tiene su contrato, sus errores, sus formatos de streaming.

La idea era simple: sumarlos. Si los siete corren en paralelo detrás del mismo endpoint, las cuotas se acumulan y la fiabilidad sube — porque cuando uno falla, el siguiente coge el relevo. El truco está en hacerlo sin romper el contrato OpenAI que tu app ya habla.

La arquitectura, en una página

   tu app (OpenAI format)
            │
            ▼
   ┌────────────────────┐
   │ FREEAI orquestador │
   │  · routing         │
   │  · rate atómico    │
   │  · fallback chain  │
   │  · streaming SSE   │
   │  · estado Postgres │
   └────────────────────┘
            │
            ▼
   ┌────────────────────┐
   │ Cerebras · Groq    │
   │ Gemini · Mistral   │
   │ OpenRouter         │
   │ Cohere · HF        │
   └────────────────────┘

Tu cliente sigue siendo OpenAI-compatible. El orquestador rankea candidatos, reserva el slot en el rate-limit del proveedor elegido, despacha, y si falla, cae al siguiente con criterios tipados.

Decisión #1: Postgres como única fuente de verdad

El estado vive en Postgres, no en memoria del proceso. Esto incluye la configuración, las claves cifradas, las ventanas de rate-limit y el ledger de uso.

Por qué importa: el día que tengas dos workers o dos pods detrás de un balanceador, un rate-limit en memoria se duplica. Y a las free tiers ese error les sienta mal — un único exceso te puede meter en cuarentena 24 horas.

La pieza clave es una función plpgsql llamada freeai_try_reserve que hace la reserva atómica dentro de la base de datos. El test test_concurrent_reservations_respect_limit lanza 50 sesiones contra un límite de 5 y verifica que pasan exactamente 5. Postgres me cuesta una operación extra de IO por petición; el LLM tarda 50–500 veces más. El balance es claro.

Decisión #2: errores como dato, no como excepción

Cada fallo upstream se traduce a un ErrorKind antes de subir por el stack:

  • auth → cuarentena 24h del proveedor (la key probablemente caducó).
  • rate_limited → fallback inmediato, sin marcar el proveedor unhealthy.
  • server_error y network → retry transitorio en sitio, después fallback.
  • client_error → no reintenta, devuelve el error al cliente.
  • parsing, unknown → fallback con menos confianza.

El orquestador decide qué hacer por el tipo, no inspeccionando el mensaje. Eso hace que el comportamiento sea testeable: hay casos para empty 200 OK, content-filtered finish reason, stream idle timeout y el circuit breaker por usuario.

Decisión #3: routing con EMA y reparto consciente de la carga

Rankear candidatos no es sortear. El score combina tres señales:

  • Latencia esperada del proveedor, medida con una EMA (media móvil exponencial) sobre las últimas peticiones. Picos transitorios bajan el ranking sin invalidarlo para siempre.
  • Fiabilidad reciente: una penalización proporcional a la ratio de fallos en la ventana corta. Un proveedor que acaba de devolver tres 502s se cae al final de la lista solo.
  • Carga en vuelo: cuántas peticiones tienes activas contra ese proveedor ahora. Empuja a repartir entre los que están libres en vez de saturar al más rápido.

El resultado es que cuando un proveedor se degrada, el sistema lo descarta antes de que dispare los timeouts — sin que tengas que tocar nada.

Decisión #4: auto-strategy sin llamar a otro LLM

La estrategia auto detecta qué tipo de petición es y enruta a coding, reasoning, vision, long_context o fastest según el contenido. La tentación obvia sería usar un LLM clasificador. No.

  • La detección de idioma (EN/ES/FR/DE/PT) sale de la frecuencia de stopwords.
  • Las señales de intención salen de marcadores en el prompt: bloques de código, presencia de image_url, longitud del contexto, palabras clave.

Es un heurístico, no IA. Es más barato, más rápido y suficientemente bueno — y, sobre todo, no añade un punto de fallo más al sistema. Usar IA para decidir cómo usar IA es una buena receta para encadenar latencias.

Decisión #5: circuit breaker por usuario, no global

Un usuario con un prompt malformado o un cliente con un bug puede generar 50 errores por minuto. Si el circuit breaker es global, ese ruido empuja al resto de usuarios fuera del sistema.

La ventana es por (usuario, proveedor) con cooldown exponencial. Si Alice rompe Groq, Alice no usa Groq durante un rato; Bob sigue tirando de Groq normalmente. Aislar el blast radius es más importante que reaccionar rápido a lo global — los proveedores ya tienen sus propios circuit breakers, este protege contra usuarios concretos.

Decisión #6: cifrado at rest con master key independiente del JWT

Las API keys de los proveedores se guardan cifradas con Fernet en Postgres. La master key sale de FREEAI_MASTER_KEY o se autogenera en el primer arranque y se persiste fuera de la base de datos.

Detalle que suele pasarse por alto: la clave de firma del JWT es independiente de la master key. Si te roban una, la otra sigue siendo segura. Es el tipo de separación que cuesta cero implementar y te salva el día que alguien dumpea la base de datos.

Trade-offs honestos

  • Latencia añadida frente a llamar directo al proveedor: una operación de Postgres por reserva, más el routing. En la práctica, una decena de ms; despreciable frente al LLM.
  • Calidad variable: no todos los proveedores libres dan la misma calidad. La estrategia best_quality te garantiza ir al mejor disponible, pero "mejor disponible gratis" no es GPT-4.
  • Coste de mantenimiento: cuando un proveedor cambia su API, hay que tocar su adaptador. Es un coste real, pero está localizado: la interfaz ProviderResponse aísla al resto del sistema.

Probarlo y por qué MIT

docker compose up --build y listo: setup wizard, metes las keys de proveedor, creas un cliente y ya tienes un endpoint OpenAI-compatible apuntable desde cualquier SDK existente — incluido multimodal (image_url enrutado automáticamente a proveedores con visión) y embeddings (POST /v1/embeddings), que pasan por la misma maquinaria de rate-limiting y fallback que chat.

Lo abro bajo licencia MIT a propósito. El acuerdo es honesto: tú traes tus claves de cada proveedor (no se comparten entre usuarios, no se intenta saltar ningún TOS), y respondes ante cada provider por tu propia cuenta. Forkéalo, audítalo, despliégalo en tu infra.

El repo está en github.com/adrianpastora/FREEAI. Si te interesa el enfoque, una estrella ayuda — y los issues con preguntas técnicas son bienvenidos.


¿Quieres montar algo parecido o integrar FREEAI en tu producto? Escríbeme.


— Adrián Pastora

Si te ha resultado útil y quieres aplicar algo de esto en tu empresa, hablemos.