Octolib 0.39.0: un modelo que no sabe escribir ni una frase acaba de unirse al stack

Desde el día en que lo extrajimos de Octocode, toda llamada a un modelo en nuestro stack ha sido una de tres cosas. Generar texto. Convertir texto en un vector. Ordenar vectores frente a una consulta. Octolib — la biblioteca Rust que hay bajo Octomind, OctoHub, Octocode y Octobrain — tenía un módulo para cada una: llm, embeddings, reranker. Después sumó media.

Hoy suma un cuarto, y este no se parece a los demás. octolib::evaluation habla con un modelo que no puede generar ni un solo token. Le entregas un estado y un conjunto de preguntas tipadas, y te devuelve probabilidades. Sí o no, una de N, una posición en una escala. En entre 70 y 500 milisegundos. Por $0.042 por millón de tokens de entrada, con la salida gratis — porque no hay salida que facturar.

El modelo es Jev, de TypeSafe AI, que salió del modo stealth el 15 de septiembre. TypeSafe llama a esta categoría modelos System One. Creemos que es la primitiva nueva más interesante que ha llegado al stack de IA este año, y Octolib 0.39.0 es nuestra apuesta: una cadena provider:model, tres formas de acceder al modelo y la misma contabilidad de costes que aplicamos a todo lo demás.

Ese es el titular. La release además convierte Cloudflare Workers AI en un proveedor de medios completo, añade un backend local de embeddings ONNX, incorpora dos proveedores de LLM nuevos y por fin hace que la salida estructurada se valide exactamente una vez. Todo lo ocurrido desde 0.36.1 está más abajo.

Por qué un agente necesita un modelo que solo decide

Si desmontas un agente de programación hasta el hueso, queda un modelo grande haciendo el trabajo. A su alrededor se sienta un plano de control que toma juicios pequeños y acotados sobre lo que ese modelo está haciendo. ¿Debería ejecutarse este comando de shell? ¿Cuál de las cuarenta habilidades encaja con esta petición, si es que alguna lo hace? ¿Es realmente relevante esta memoria recuperada? ¿Necesita el agente todo este resultado de herramienta de 40 KB, o nada de él?

Todo agente de programación serio tiene esa segunda capa. La nuestra es el supervisor de Octomind. La de Anthropic es el clasificador de modo automático en Claude Code. Y en ambos casos, un modelo de propósito general toma esas decisiones con su capacidad definitoria — escribir una explicación razonada — desactivada justo para esas llamadas. Un viaje de ida y vuelta a un modelo frontier, reenviado por cada decisión, para recibir una sola palabra.

Jev está construido para esas decisiones y para nada más. Tres primitivas:

  • Noul — una pregunta de sí/no, respondida como la probabilidad de que la respuesta sea sí.
  • Choice — una opción de un conjunto que tú defines, con una probabilidad para cada opción y una confianza que indica cuán concentrada está la distribución.
  • Score — una posición en una rúbrica ordenada de niveles descritos con palabras, que puede caer entre niveles.

Ingiere el estado una sola vez y evalúa cada pregunta contra él en una única pasada paralela, así que la décima pregunta sale casi gratis. Está entrenado para calibrar, no para preferir, así que una respuesta del 90% debería acertar el 90% de las veces a lo largo de muchas predicciones. Y el espacio de respuestas está cerrado por construcción: puede elegir la opción equivocada, pero no puede inventarse una, así que no hay JSON que reparar ni enums que emparejar de forma aproximada.

Escribimos la versión larga — qué es, dónde se rompe, qué construyó la gente con él en sus primeras 72 horas y cada decisión de plano de control dentro de un agente que podría asumir, con números en vivo — en el blog de Octomind: Jev explicado: el modelo System One de TypeSafe y las decisiones dentro de todo agente de IA. Este post va de la biblioteca.

La API

El módulo es evaluation, viene activado por defecto y compila solo si es lo único que necesitas:

octolib = { version = "0.39", default-features = false, features = ["evaluation"] }

La forma sigue la del resto de Octolib. Un tipo de petición, un helper de alto nivel, una cadena provider:model:

use octolib::{evaluate, Answer, EvaluationRequest, Question};

async fn triage() -> octolib::EvaluationResult<()> {
    // Requiere TYPESAFE_API_KEY; usa "cloudflare:typesafe/jev" para facturarlo contra créditos de AI Gateway.
    let request = EvaluationRequest::new("Help! My payouts have been failing for 3 days.")
        .with_question("is_urgent", Question::noul("Does this convey urgency?"))
        .with_question(
            "department",
            Question::choice(
                "Which team should handle this?",
                [("billing", "Payments, refunds"), ("technical", "Bugs, outages")],
            ),
        )
        .with_question(
            "frustration",
            Question::score("How frustrated is the customer?", ["Calm", "Frustrated", "Very angry"]),
        );
    let response = evaluate("typesafe:jev-latest", request).await?;
    if let Answer::Noul { noul } = response.answers["is_urgent"] {
        println!("urgent with p={noul:.2}, cost {:?}", response.usage.cost);
    }
    Ok(())
}

El estado puede ser una cadena, un objeto JSON o un array. Las respuestas vuelven tipadas bajo los mismos ids que las preguntas — Answer::Noul, Answer::Choice con su mapa de probabilidades y su confianza, Answer::Score con una leyenda — así que en Rust ramificas sobre números, no sobre texto parseado.

Tres rutas al mismo modelo:

Cadena de modelo Llega a Jev a través de Claves
typesafe:jev-latest La API de TypeSafe directamente (acceso anticipado) TYPESAFE_API_KEY
cloudflare:typesafe/jev Cloudflare AI, facturado vía créditos de AI Gateway CLOUDFLARE_API_KEY, CLOUDFLARE_ACCOUNT_ID
octohub:jev Tu propio proxy OctoHub, con el coste reportado por el hub OCTOHUB_API_KEY, OCTOHUB_API_URL

Unos cuantos detalles que solo importan cuando lo exprimes de verdad — que es la parte que nos importa:

  • Aquí los reintentos son seguros. Una evaluación no tiene efectos secundarios, así que una repetición no puede duplicar trabajo. El cliente reintenta en 429, 529 y 5xx con backoff, y respeta Retry-After hasta un tope de 30 segundos. En otras partes de Octolib, los POST de generación no se repiten deliberadamente; este sí.
  • El coste se calcula con la tarifa publicada, $0.042 por millón de tokens de entrada, y se refleja en usage.cost como en cualquier otra llamada. A través de OctoHub gana el coste reportado por el hub.
  • El campo model de la respuesta reporta el modelo versionado que respondió. jev-latest se mueve cuando TypeSafe publica una versión nueva; si ajustas umbrales, regístralo y fíjalo.
  • Los ids de modelo se validan antes de que la petición salga. openai:jev-latest es un proveedor no soportado; cloudflare:@cf/zai-org/glm-5.3 es un modelo de chat, no un modelo de evaluación. Ambos fallan en la construcción, no en el cable.

Jev tiene un contexto de 32k, y la propia documentación de TypeSafe dice que la precisión cae a medida que el estado se llena de material que la pregunta no necesita. Recorta el estado. Haz muchas preguntas; envía poco.

Dónde se enchufa: Octomind y OctoHub

No añadimos módulos a Octolib por diversión. Cada módulo existe porque un producto lo necesitaba, y este se necesitó dos veces.

El supervisor de Octomind ya funciona con el invariante de que las señales gratuitas filtran al modelo y las llamadas al modelo son raras. Hoy en main, [supervisor.evaluate] añade un tercer nivel calibrado entre "gratis" y "frontier" en tres costuras: un filtro de relevancia tras la recuperación de memoria, un Choice de roster cuando todas las reglas de activación de habilidades se abstuvieron, y un pre-filtro que decide si un lote de herramientas necesita despertar al modelo autorizador. Cada costura es un interruptor, apagado por defecto. Un intento por llamada, un timeout de cinco segundos, sin reintentos; cualquier fallo mantiene el comportamiento anterior para ese turno. Y una regla que pondríamos por encima de cualquier integración con Jev: el estado nunca lleva resultados de herramientas ni mensajes del asistente, porque el revisor nunca debe leer aquello que podría estar argumentando.

OctoHub recibe POST /v1/evaluations. Configura un alias una vez, en tu propio proxy, con tus propias claves:

[evaluation_models]
"jev" = ["typesafe:jev-latest", "cloudflare:typesafe/jev"]

y todo cliente en todo lenguaje — un hook de Python, un worker de TypeScript, un script de shell — llama a octohub:jev y obtiene las mismas respuestas tipadas, la misma fila de coste por petición y la misma atribución que en sus llamadas de chat. Por eso octohub es el tercer proveedor de evaluación en Octolib: la biblioteca llama al proxy, el proxy llama a Octolib.

Ambos llegan en las próximas releases de Octomind y OctoHub; la biblioteca se publicó primero porque la biblioteca siempre se publica primero.

Cloudflare Workers AI ya es proveedor de medios

En la ronda anterior, el stack media de Octolib cubría imagen, vídeo, voz y transcripción en OpenRouter, Replicate, fal, ElevenLabs y Runway. 0.39.0 añade Cloudflare Workers AI para imagen, voz y transcripción, a través del mismo endpoint síncrono /ai/run que usa el proveedor de chat.

Los modelos registrados, con capacidades, opciones y precios de referencia:

  • Imagen: FLUX.1 schnell, FLUX.2 dev, Leonardo Lucid Origin y Phoenix 1.0. Precio por tile de salida y paso.
  • Voz: Deepgram Aura 1 y Aura 2 (inglés y español), MeloTTS. Precio por carácter.
  • Transcripción: Whisper, Whisper large-v3-turbo, Deepgram Nova 3. Precio por minuto de audio, con segmentos, palabras, idiomas y metadatos parseados dentro de la transcripción tipada.

El catálogo se carga de forma diferida y cachea páginas, con un fallback a metadatos para modelos que Cloudflare aún no ha listado. Cuando el upstream reporta costes en neuronas, se conservan tal como se reportan en lugar de sustituirlos por una estimación de duración. Las seeds de imagen solo van a los modelos que las aceptan; los tipos de voz se conservan en vez de coercerse.

El lado de chat del proveedor de Cloudflare recibió el mismo tratamiento: elige y normaliza los niveles de razonamiento por modelo, aplica esquemas solo para los modelos con forma de OpenAI que de verdad los respetan, resuelve los ids de modelo solapados por coincidencia más larga y recurre a capacidades de referencia para modelos desconocidos en vez de fallar.

Todo lo demás desde 0.36.1

Diez releases en doce días. La versión condensada.

Dos proveedores de LLM nuevos. Inception Labs ofrece la familia de LLMs de difusión Mercury: inception:mercury-2.5, con Mercury 2 al lado, salida con esquema JSON y tool calling. Tinker, de Thinking Machines, ofrece su familia Inkling más modelos de pesos abiertos (Nemotron, GLM, Kimi, Qwen, GPT-OSS, DeepSeek) y checkpoints de sampler, con soporte para ids de modelo que contienen dos puntos y nombres cortos como tinker:inkling resueltos a ids de inferencia serverless. Ambos vienen con ejemplos que toman la especificación del modelo desde la línea de comandos.

Embeddings ONNX, en local. Un nuevo proveedor de embeddings onnx carga grafos y tokenizadores de HuggingFace en ONNX Runtime — onnx:<org>/<repo>, con un #path-to.onnx opcional para un grafo concreto — prefiriendo grafos cuantizados, respetando los metadatos de pooling y las dimensiones estáticas, y procesando por lotes con seguimiento del uso de tokens. Se suma a FastEmbed y al backend de HuggingFace basado en Candle como la tercera forma de generar embeddings sin clave de API. Este es el único breaking change de la ronda: EmbeddingProviderType ganó una variante Onnx, así que los matches exhaustivos necesitan un brazo nuevo.

La salida estructurada se valida una vez. El flujo antiguo podía reintentar una validación de esquema fallida y agregar el uso entre intentos. Era bienintencionado y erróneo: ocultaba el comportamiento del proveedor detrás de un bucle. Ahora hay exactamente una llamada de esquema al upstream, un paso centralizado de extracción y validación, y un error de parseo o validación se devuelve con la respuesta problemática registrada y sin reintento. Las tool calls del cliente sobreviven a la validación de la salida final. Dos proveedores dejaron de prometer de más en el mismo cambio: Ollama y Cloudflare ya no prometen una aplicación garantizada de esquemas que no pueden cumplir, y OpenAI falla de forma segura cuando las indicaciones de esquema se ignoran, en vez de devolver algo que simplemente parece correcto.

Los niveles de esfuerzo de razonamiento se mapean por proveedor. Los modelos GLM de Alibaba y Z.ai se mapean a los niveles de esfuerzo que soportan; Fireworks conserva el nivel máximo para GLM 5.2. Z.ai ahora acepta mensajes con pensamiento preservado y conserva los turnos de asistente de solo pensamiento en vez de descartarlos.

El roster ha cambiado. Los modelos retirados de Cerebras se sustituyen por qwen-3.8-27b, con soporte de vídeo y un límite de entrada de 128K. Las rutas V4 retiradas de DeepSeek se sustituyen por deepseek-flash, tratado como multimodal, con el precio de V4.1 Flash corregido y añadido en Alibaba. Los modelos retirados de Hetzner se cambian por Qwen. Precios de caché y de terceros de BytePlus actualizados. OctoHub ahora sondea las dimensiones de embeddings en la construcción y las cachea por endpoint y modelo, lo que hizo que la construcción del proveedor de OctoHub fuera asíncrona — el segundo cambio de API pequeño que conviene conocer. El mapeador de proveedores valida los alias de modelo, conserva los cuantizados y rechaza los modelos no mapeados en la construcción en vez de en la primera petición.

Pequeño pero real. Los formatos de audio de fal se mapean a valores que fal acepta (MP3 y PCM; los formatos no soportados recaen en el valor por defecto del modelo). Se vuelve a permitir contenido de mensaje vacío. rustls está en 0.23.45, dirs en 7.0.

Actualizar

  • cargo add [email protected] o sube la versión. Toda feature viene activada por defecto; la nueva feature evaluation no arrastra nada pesado.
  • Si haces match sobre EmbeddingProviderType, añade el brazo Onnx.
  • Si construyes el proveedor de embeddings de OctoHub directamente, ahora es async.
  • Si dependías de que la salida estructurada reintentara una validación fallida, ya no lo hace. El error lleva lo que el proveedor devolvió; decide en tu propio código.
  • Configura TYPESAFE_API_KEY, o CLOUDFLARE_API_KEY más CLOUDFLARE_ACCOUNT_ID, y prueba el ejemplo de arriba. Consume unos pocos cientos de tokens de entrada: menos de dos milésimas de centavo.

FAQ

¿Qué es un modelo de evaluación? Un modelo que responde preguntas tipadas sobre un estado con probabilidades calibradas en vez de generar texto. Jev, de TypeSafe, el primero, devuelve una probabilidad de sí/no (Noul), una opción de un conjunto (Choice) o una posición en una rúbrica (Score), en entre 70 y 500 ms. No puede escribir código ni prosa. Su trabajo son las decisiones alrededor de tu LLM: enrutamiento, admisión, filtrado de relevancia, selección de habilidades.

¿Necesito una cuenta de TypeSafe para usarlo? No. El acceso directo está en lista de espera, pero cloudflare:typesafe/jev funciona hoy con una cuenta de Cloudflare y créditos de AI Gateway cargados, y octohub:jev funciona a través de tu propio OctoHub con cualquiera de las dos claves detrás.

¿Rompe algo Octolib 0.39.0? Una variante de enum (EmbeddingProviderType::Onnx), un constructor que pasó a ser async (embeddings de OctoHub) y la salida estructurada ya no reintenta validaciones fallidas. Chat, medios, reranking y toda cadena de modelo que ya usas siguen igual.

¿Ya está Jev dentro de Octomind? Las puertas de evaluación están en el main de Octomind detrás de [supervisor.evaluate], apagadas por defecto, y se publican en la próxima release. Esta release de Octolib es la base sobre la que están construidas.

¿Qué productos funcionan sobre Octolib? Octomind (cada llamada de chat, embedding y puerta de evaluación), OctoHub (chat, embeddings, medios y la nueva ruta /v1/evaluations), Octocode (chat, embeddings, reranking) y Octobrain (embeddings, reranking). Cuando un proveedor cambia su API o sus precios, la corrección está en un solo lugar y todos los productos la reciben.

La clave

El trabajo de Octolib siempre ha sido convertir el próximo modelo en un cambio de una línea para todo lo que publicamos. Casi siempre eso significa un proveedor nuevo o un precio corregido. De vez en cuando significa un tipo nuevo de modelo, y la biblioteca tiene que decidir cómo encaja.

Los modelos de evaluación encajan como un cuarto módulo porque son una función distinta: no "escríbeme una respuesta" sino "aquí tienes un estado, aquí tienes mis preguntas, dime qué crees y con qué fuerza". Los agentes han necesitado esa primitiva desde el primer hook pre-tool. La hemos estado simulando con un solo token de un modelo de razonamiento. Ahora es una llamada tipada, con un precio honesto, a una cadena de distancia.

github.com/muvon/octolib · Apache-2.0 · crates.io · el análisis a fondo de Jev en el blog de Octomind.