Proyecto final: pide JSON a Ollama sin que se rompa
Junta lo del bloque: un POST en streaming a Ollama, reintentos con espera creciente, lectura del NDJSON trozo a trozo y extracción segura del JSON.
Combina los 12 ejercicios del bloque Ingeniería LLM. Se desbloquea al terminarlos, pero puedes intentarlo ya.
Escribe askJson(prompt, opciones), la función que pondrías entre tu app y un modelo local. opciones trae fetchImpl (el fetch a usar), model, retries y baseMs.
1. Haz un POST a http://localhost:11434/api/generate con el cuerpo JSON { model, prompt, stream: true }.
2. Si la respuesta no es ok (por ejemplo un 503 porque el modelo aún carga) o fetchImpl lanza un error, espera sleep(baseMs * 2 ** intento) y repite, como mucho retries veces. Si se agotan, lanza un error que incluya el estado, por ejemplo HTTP 503.
3. Lee res.body con getReader() y TextDecoder: llega en trozos pequeños y una línea (o una tilde) puede quedar partida entre dos. Junta los campos response de cada línea JSON.
4. Devuelve el primer objeto JSON del texto (el modelo puede escribir algo antes) o lanza new Error('no JSON').
mockOllama({ failures, parts, chunk }) imita a Ollama y sleep(ms) anota cada espera. Un reto usa liveFetch, que devuelve la salida real de tu propio Ollama si activas «Mi Ollama», o una muestra distinta si no.
Retos 0/7
- Devuelve el objeto JSON de una respuesta en streaming
- Hace un POST a /api/generate con model, prompt y stream: true
- Reintenta un 503 esperando 100 y 200 ms
- Se rinde tras los reintentos con un error que dice HTTP 503
- Aguanta líneas cortadas byte a byte y texto antes del JSON
- Lanza 'no JSON' cuando el modelo no devuelve ningún objeto
- Funciona con el stream real de tu modelo (Mi Ollama) o con otra muestra
const OLLAMA = 'http://localhost:11434';
async function askJson(prompt, { fetchImpl = fetch, model = 'llama3.1:8b', retries = 2, baseMs = 200 } = {}) {
// 1. POST a OLLAMA + '/api/generate' con el cuerpo JSON { model, prompt, stream: true }
// 2. si la respuesta no es ok o salta un error: sleep(baseMs * 2 ** intento) y otra vez, como mucho `retries` veces
// 3. lee res.body trozo a trozo (una línea puede llegar partida) y junta los campos `response`
// 4. devuelve el primer objeto JSON de ese texto o lanza new Error('no JSON')
return null;
}
askJson('Resume el ticket', { fetchImpl: mockOllama({ failures: 1 }) }).then(console.log, (e) => console.error(e.message));Para profundizar: la guía relacionada →
¿Esto en producción, con tus datos? Hablemos 15 minutos →