F

Fireworks AI — Inferencia LLM Open-Source

Patrones de integración: chat streaming · JSON estructurado · tool use · fine-tuning

Cliente: NutriTrack Pro
🥗

NutriTrack Pro — SaaS de análisis nutricional para clínicas

Stack: Next.js 14 · Node.js · PostgreSQL. Se integran 3 capacidades de IA: asistente chat en streaming, extracción estructurada de alimentos, y clasificación de riesgo nutricional.

Chat Streaming JSON Schema extraction Risk classification Cost routing (8B/70B)
Configuración base
src/lib/fireworks.ts — Cliente singleton
TypeScript OpenAI-compatible SDK
// Zero-change migration: mismo SDK de OpenAI, distinto baseURL
import OpenAI from "openai";

// Modelos disponibles para NutriTrack
export const MODELS = {
  chat:       "accounts/fireworks/models/llama-v3p3-70b-instruct",  // asistente dietético
  extract:    "accounts/fireworks/models/llama-v3p3-70b-instruct",  // extracción alimentos
  classify:   "accounts/fireworks/models/llama-v3p1-8b-instruct",   // clasificación riesgo (coste bajo)
  tools:      "accounts/fireworks/models/firefunction-v2",          // function calling
} as const;

// Singleton reutilizable en toda la app
export const fireworks = new OpenAI({
  apiKey:   process.env.FIREWORKS_API_KEY!,
  baseURL:  "https://api.fireworks.ai/inference/v1",
});
Capacidades implementadas
1. Asistente dietético (streaming)
stream: true 70B
// src/lib/nutritrack/chat.ts
export async function streamNutriAnswer(
  question: string,
  onChunk: (text: string) => void
): Promise<string> {
  const stream = await fireworks.chat.completions.create({
    model: MODELS.chat,
    stream: true,
    messages: [
      {
        role: "system",
        content: "Eres un asistente nutricional experto. "
          + "Responde siempre en español, con rigor clínico "
          + "y referencias a evidencia científica cuando sea útil."
      },
      { role: "user", content: question },
    ],
    temperature: 0.4,
    max_tokens: 1024,
  });

  let full = "";
  for await (const chunk of stream) {
    const text = chunk.choices[0]?.delta?.content ?? "";
    full += text;
    onChunk(text);
  }
  return full;
}

// Uso en route handler de Next.js App Router
// await streamNutriAnswer(
//   "¿Qué alimentos ricos en hierro pueden tomar pacientes con IRC?",
//   (chunk) => controller.enqueue(encoder.encode(chunk))
// );
2. Extracción de alimentos (JSON schema)
json_object 70B
// src/lib/nutritrack/extract.ts
interface FoodEntry {
  alimento:    string;
  cantidad_g:  number;
  calorias:    number;
  proteinas_g: number;
  carbos_g:    number;
  grasas_g:    number;
}

export async function extractMeal(
  freeText: string
): Promise<FoodEntry[]> {
  const res = await fireworks.chat.completions.create({
    model: MODELS.extract,
    temperature: 0,
    response_format: {
      type: "json_object",
      schema: {
        type: "object",
        properties: {
          alimentos: {
            type: "array",
            items: {
              type: "object",
              properties: {
                alimento:    { type: "string" },
                cantidad_g:  { type: "number" },
                calorias:    { type: "number" },
                proteinas_g: { type: "number" },
                carbos_g:    { type: "number" },
                grasas_g:    { type: "number" },
              },
              required: ["alimento","cantidad_g","calorias"],
            },
          },
        },
        required: ["alimentos"],
      },
    } as any,
    messages: [
      {
        role: "system",
        content: "Extrae los alimentos de la descripción con sus macros estimados."
      },
      { role: "user", content: freeText },
    ],
  });
  const { alimentos } = JSON.parse(res.choices[0].message.content!);
  return alimentos;
}
3. Clasificación de riesgo nutricional
8B · bajo coste grammar-constrained
// src/lib/nutritrack/risk.ts
interface RiskResult {
  nivel:     "bajo" | "medio" | "alto";
  confianza: number;        // 0.0 – 1.0
  factores:  string[];     // e.g. ["déficit proteína", "exceso sodio"]
}

export async function classifyRisk(
  weeklyIntake: string
): Promise<RiskResult> {
  const res = await fetch(
    "https://api.fireworks.ai/inference/v1/chat/completions", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.FIREWORKS_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: MODELS.classify,
      messages: [
        {
          role: "system",
          content: "Eres un evaluador de riesgo nutricional. Analiza la ingesta semanal y clasifica."
        },
        { role: "user", content: weeklyIntake },
      ],
      response_format: {
        type: "json_object",
        schema: {
          type: "object",
          properties: {
            nivel:     { type: "string", enum: ["bajo","medio","alto"] },
            confianza: { type: "number", minimum: 0, maximum: 1 },
            factores:  { type: "array", items: { type: "string" } },
          },
          required: ["nivel","confianza","factores"],
        },
      },
      temperature: 0,
    }),
  });
  const data = await res.json();
  return JSON.parse(data.choices[0].message.content) as RiskResult;
}
4. Fine-tuning Llama 8B (dominio nutricional)
Python 8B base
# fine_tune_nutritrack.py — Llama 3.1 8B especializado en nutrición
import requests, os, json

API_KEY = os.environ["FIREWORKS_API_KEY"]
BASE    = "https://api.fireworks.ai/inference/v1"
HDRS    = { "Authorization": f"Bearer {API_KEY}" }

# 1. Dataset de entrenamiento (JSONL)
# Formato: par instrucción → respuesta del nutricionista
TRAINING_EXAMPLES = [
  { "messages": [
      { "role": "system",    "content": "Eres un dietista clínico experto." },
      { "role": "user",      "content": "Plan semanal para paciente diabético tipo 2, 70 kg." },
      { "role": "assistant", "content": "Lunes: desayuno..." },
  ]},
  # ... 500+ ejemplos validados por nutricionistas
]

def upload_dataset(filepath: str) -> str:
  with open(filepath, "rb") as f:
    r = requests.post(
      f"{BASE}/files", headers=HDRS,
      files={ "file": (filepath, f, "application/jsonl") },
      data={ "purpose": "fine-tune" },
    )
  return r.json()["id"]

def create_job(dataset_id: str) -> dict:
  return requests.post(
    f"{BASE}/fine_tuning/jobs",
    headers={ **HDRS, "Content-Type": "application/json" },
    json={
      "model": "accounts/fireworks/models/llama-v3p1-8b-instruct",
      "training_file": dataset_id,
      "hyperparameters": {
        "n_epochs": 4,               # 4 épocas para dominio especializado
        "learning_rate_multiplier": 0.8,
        "batch_size": 16,
      },
    },
  ).json()

# Después del fine-tuning: usar el modelo especializado
# model = "accounts/YOUR_ACCOUNT/models/nutritrack-llama-8b-v1"
# Resultado: ~60% más barato que 70B, respuestas nutricionales más precisas
Routing de modelos — NutriTrack Pro
Selección de modelo por caso de uso y coste estimado
Caso de uso Modelo Latencia est. Coste relativo Razón
Asistente chat (streaming) llama-v3p3-70b-instruct ~1.2s TTFT
alto
razonamientoespañol
Extracción de alimentos llama-v3p3-70b-instruct ~1.8s
alto
JSON schemaprecisión
Clasificación de riesgo llama-v3p1-8b-instruct ~0.6s
bajo
tarea simpleenums
Búsqueda en base datos (tool use) firefunction-v2 ~0.9s
medio
function callingfiable
Fine-tuned (futuro) dominio nutritrack-llama-8b-v1 ~0.5s
muy bajo
especializado-60% coste
Guidelines aplicados
OpenAI SDK compatible

Migración zero-change: mismo SDK de openai, solo cambia el baseURL. No se reescribe código existente.

JSON schema para extraccion

Fireworks soporta grammar-constrained generation con schema JSON completo: tipos, enums, required. Salida 100% estructurada sin parseo frágil.

Routing 8B / 70B por complejidad

Clasificación de riesgo usa 8B (tarea simple + enums). Chat usa 70B (razonamiento). Ahorro estimado: ~40-50% en costes de API.