Módulo 4: Generación de Imágenes

7. Integración en Pipelines

Descripción

Generar una imagen aislada es útil para prototipos. Pero en producción, la generación de imágenes es un paso dentro de un pipeline más grande: un sistema que genera un artículo y necesita ilustraciones, un e-commerce que crea fichas de producto automáticas, un flujo que genera y valida imágenes antes de publicar. En esta cápsula vas a construir 4 pipelines concretos que combinan LLMs, generación de imágenes y Vision APIs.

Por qué importa: La generación sin verificación es un riesgo en producción. DALL-E puede generar una imagen que no coincide con el prompt. Sin un paso de verificación con Vision API, publicas imágenes que no sabes si son correctas. Los pipelines que construyas aquí incluyen verificación, fallback, tracking de costos y manejo de errores.

Conexión con el módulo: Esta cápsula integra todo lo anterior: generación con DALL-E (cápsula 02) y SD (cápsula 03), prompts optimizados (cápsula 05), edición (cápsula 06). El proyecto final (cápsula 08) es un pipeline productizado con fallback.


Cost Tracking Reutilizable

Antes de los pipelines, definimos el tracker de costos que usaremos en todos:

from dataclasses import dataclass, field
from datetime import datetime


@dataclass
class CostTracker:
    items: list[dict] = field(default_factory=list)

    def add(self, step: str, model: str, cost: float):
        self.items.append({"step": step, "model": model, "cost_usd": cost})

    @property
    def total(self) -> float:
        return sum(item["cost_usd"] for item in self.items)

    def summary(self) -> str:
        lines = [f"  {i['step']} ({i['model']}): ${i['cost_usd']:.4f}" for i in self.items]
        lines.append(f"  TOTAL: ${self.total:.4f}")
        return "\n".join(lines)


COST_TABLE = {
    "dall-e-3-standard-1024": 0.040,
    "dall-e-3-hd-1024": 0.080,
    "gpt-4o-vision": 0.005,
    "gpt-4o-mini-per-1k": 0.000150,
    "sdxl-replicate": 0.004,
}

Pipeline 1: Texto → Imagen → Verificación con Vision

Genera una imagen y usa GPT-4o Vision para verificar que el resultado coincide con el prompt.

import json
import time
from dataclasses import dataclass, field
from openai import OpenAI

client = OpenAI()


@dataclass
class VerifiedImageResult:
    success: bool
    image_b64: str = ""
    score: float = 0.0
    verification: dict = field(default_factory=dict)
    prompt_used: str = ""
    revised_prompt: str = ""
    cost: float = 0.0
    latency_seconds: float = 0.0


def pipeline_generate_and_verify(
    prompt: str,
    min_score: float = 0.7,
    max_retries: int = 2,
) -> VerifiedImageResult:
    costs = CostTracker()
    start = time.time()
    best_result = None

    for attempt in range(max_retries + 1):
        try:
            gen = client.images.generate(
                model="dall-e-3", prompt=prompt, size="1024x1024",
                quality="standard", response_format="b64_json", n=1,
            )
            b64 = gen.data[0].b64_json
            costs.add(f"generation_{attempt}", "dall-e-3", COST_TABLE["dall-e-3-standard-1024"])

            verify = client.chat.completions.create(
                model="gpt-4o",
                messages=[{
                    "role": "user",
                    "content": [
                        {
                            "type": "text",
                            "text": (
                                f"Evalúa si esta imagen corresponde a: '{prompt}'\n"
                                'JSON: {{"score": 0-1, "matches_subject": true/false, '
                                '"issues": [...], "description": "..."}}'
                            ),
                        },
                        {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}},
                    ],
                }],
                max_tokens=200,
            )
            costs.add(f"verification_{attempt}", "gpt-4o", COST_TABLE["gpt-4o-vision"])

            try:
                eval_data = json.loads(verify.choices[0].message.content)
            except json.JSONDecodeError:
                eval_data = {"score": 0.5, "issues": ["Parse error"]}

            score = eval_data.get("score", 0)
            result = VerifiedImageResult(
                success=score >= min_score, image_b64=b64, score=score,
                verification=eval_data, prompt_used=prompt,
                revised_prompt=gen.data[0].revised_prompt,
                cost=costs.total, latency_seconds=round(time.time() - start, 2),
            )

            if best_result is None or score > best_result.score:
                best_result = result

            if score >= min_score:
                return result

        except Exception as e:
            best_result = VerifiedImageResult(
                success=False, cost=costs.total,
                latency_seconds=round(time.time() - start, 2),
            )

    return best_result


result = pipeline_generate_and_verify(
    "Minimalist logo for an AI company, blue and white, geometric shapes",
    min_score=0.75, max_retries=2,
)
print(f"Score: {result.score}, Costo: ${result.cost:.4f}, Latencia: {result.latency_seconds}s")

Pipeline 2: Documento → Resumen → Ilustración

Recibe un documento, genera un resumen visual con LLM, convierte el resumen en prompt de imagen, y genera la ilustración.

import json
import time
from openai import OpenAI

client = OpenAI()


def pipeline_document_to_illustration(
    document_text: str,
    style: str = "modern flat illustration",
) -> dict:
    costs = CostTracker()
    start = time.time()
    truncated = document_text[:4000]

    summary_resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": (
                    "Dado un documento, genera:\n"
                    "1. Resumen de 2-3 oraciones\n"
                    "2. Concepto visual que represente la idea central\n"
                    'JSON: {"summary": "...", "visual_concept": "..."}'
                ),
            },
            {"role": "user", "content": truncated},
        ],
        max_tokens=200,
    )
    costs.add("summarization", "gpt-4o-mini", 0.000300)

    try:
        summary = json.loads(summary_resp.choices[0].message.content)
    except json.JSONDecodeError:
        summary = {"summary": summary_resp.choices[0].message.content, "visual_concept": "abstract representation"}

    prompt_resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": f"Convierte en prompt para DALL-E 3. Estilo: {style}. Inglés, 2-3 oraciones. Solo el prompt.",
            },
            {"role": "user", "content": summary["visual_concept"]},
        ],
        max_tokens=150,
    )
    costs.add("prompt_generation", "gpt-4o-mini", 0.000150)
    image_prompt = prompt_resp.choices[0].message.content.strip()

    gen = client.images.generate(
        model="dall-e-3", prompt=image_prompt, size="1024x1024",
        quality="standard", response_format="b64_json", n=1,
    )
    costs.add("image_generation", "dall-e-3", COST_TABLE["dall-e-3-standard-1024"])

    return {
        "summary": summary["summary"],
        "visual_concept": summary["visual_concept"],
        "image_prompt": image_prompt,
        "image_b64": gen.data[0].b64_json,
        "total_cost": costs.total,
        "latency": round(time.time() - start, 2),
        "cost_breakdown": costs.summary(),
    }


document = """
La inteligencia artificial está transformando la manera en que las empresas
gestionan sus datos. Los modelos de lenguaje pueden analizar documentos,
extraer información clave y generar reportes automáticos. Las empresas que
adoptan estas tecnologías reportan un 40% de reducción en tiempo de procesamiento.
"""

illust = pipeline_document_to_illustration(document)
print(f"Resumen: {illust['summary']}")
print(f"Prompt: {illust['image_prompt']}")
print(f"Costo: ${illust['total_cost']:.4f}")

Pipeline 3: Descripción de Producto → Imagen → Quality Check

Pipeline para e-commerce: recibe descripción, genera foto profesional, valida calidad antes de publicar.

import json
import time
from openai import OpenAI

client = OpenAI()

QUALITY_CRITERIA = {
    "product_visible": "Producto claramente visible y reconocible",
    "background_clean": "Fondo limpio y no distrae",
    "lighting_pro": "Iluminación profesional y uniforme",
    "no_artifacts": "Sin artefactos, distorsiones o texto generado",
    "proportions": "Proporciones correctas del producto",
}


def pipeline_product_image(
    product_description: str,
    product_name: str = "",
    approval_threshold: float = 0.75,
) -> dict:
    costs = CostTracker()
    start = time.time()

    prompt_resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": (
                    "Genera prompt de fotografía de producto profesional en inglés. "
                    "Incluye: producto centrado, fondo blanco, studio lighting, 4K. Solo el prompt."
                ),
            },
            {"role": "user", "content": product_description},
        ],
        max_tokens=150,
    )
    costs.add("prompt", "gpt-4o-mini", 0.000150)
    img_prompt = prompt_resp.choices[0].message.content.strip()

    gen = client.images.generate(
        model="dall-e-3", prompt=img_prompt, size="1024x1024",
        quality="hd", response_format="b64_json", n=1,
    )
    costs.add("generation", "dall-e-3", COST_TABLE["dall-e-3-hd-1024"])
    b64 = gen.data[0].b64_json

    criteria_text = "\n".join(f"- {k}: {v}" for k, v in QUALITY_CRITERIA.items())
    qc_resp = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": (
                        f"Evalúa esta imagen de producto ({product_description}).\n"
                        f"Criterios:\n{criteria_text}\n"
                        'JSON: {{"scores": {{...}}, "overall": 0-1, "issues": [...], '
                        '"ecommerce_ready": true/false}}'
                    ),
                },
                {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}},
            ],
        }],
        max_tokens=300,
    )
    costs.add("quality_check", "gpt-4o", COST_TABLE["gpt-4o-vision"])

    try:
        qc = json.loads(qc_resp.choices[0].message.content)
    except json.JSONDecodeError:
        qc = {"overall": 0.5, "issues": ["Parse error"], "ecommerce_ready": False}

    approved = qc.get("overall", 0) >= approval_threshold and qc.get("ecommerce_ready", False)

    return {
        "product": product_name or product_description[:50],
        "approved": approved,
        "quality_score": qc.get("overall", 0),
        "issues": qc.get("issues", []),
        "image_prompt": img_prompt,
        "total_cost": costs.total,
        "latency": round(time.time() - start, 2),
    }


product = pipeline_product_image(
    "Audífonos inalámbricos over-ear en color negro mate",
    product_name="WH-1000XM5",
)
print(f"Aprobado: {'SÍ' if product['approved'] else 'NO'} (score: {product['quality_score']})")
print(f"Costo: ${product['total_cost']:.4f}")

Batch de productos

def batch_product_images(products: list[dict], threshold: float = 0.75) -> dict:
    results = [
        pipeline_product_image(p["description"], p.get("name", ""), threshold)
        for p in products
    ]
    approved = [r for r in results if r["approved"]]
    return {
        "total": len(results),
        "approved": len(approved),
        "approval_rate": len(approved) / len(results) if results else 0,
        "total_cost": sum(r["total_cost"] for r in results),
        "results": results,
    }

Pipeline 4: Input → Generar → Editar → Verificar → Final

El pipeline más completo: genera una imagen base, la edita (inpainting), verifica con Vision, y produce el entregable final.

import json
import time
import base64
import io
import tempfile
import urllib.request
from PIL import Image, ImageDraw
from openai import OpenAI

client = OpenAI()


def pipeline_full_generation(
    user_input: str,
    edit_instruction: str = "",
    min_quality: float = 0.7,
) -> dict:
    costs = CostTracker()
    start = time.time()
    steps_done = []

    prompt_resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "Convierte en prompt profesional para DALL-E 3. Inglés, 2-3 oraciones."},
            {"role": "user", "content": user_input},
        ],
        max_tokens=200,
    )
    costs.add("prompt_optimization", "gpt-4o-mini", 0.000150)
    optimized = prompt_resp.choices[0].message.content.strip()
    steps_done.append("prompt_optimization")

    gen = client.images.generate(
        model="dall-e-3", prompt=optimized, size="1024x1024",
        quality="standard", response_format="b64_json", n=1,
    )
    costs.add("generation", "dall-e-3", COST_TABLE["dall-e-3-standard-1024"])
    current_b64 = gen.data[0].b64_json
    steps_done.append("generation")

    if edit_instruction:
        img_bytes = base64.b64decode(current_b64)
        img = Image.open(io.BytesIO(img_bytes)).convert("RGBA")
        w, h = img.size

        mask = Image.new("RGBA", (w, h), (0, 0, 0, 255))
        draw = ImageDraw.Draw(mask)
        draw.rectangle([0, int(h * 0.6), w, h], fill=(0, 0, 0, 0))

        with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as fi:
            img.save(fi, "PNG")
            img_path = fi.name
        with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as fm:
            mask.save(fm, "PNG")
            mask_path = fm.name

        with open(img_path, "rb") as fi, open(mask_path, "rb") as fm:
            edit_resp = client.images.edit(image=fi, mask=fm, prompt=edit_instruction, n=1, size="1024x1024")

        costs.add("editing", "dall-e-2", 0.020)
        edited_bytes = urllib.request.urlopen(edit_resp.data[0].url).read()
        current_b64 = base64.b64encode(edited_bytes).decode("utf-8")
        steps_done.append("editing")

    verify = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": (
                        f"Evalúa para: '{user_input}'\n"
                        'JSON: {{"quality": 0-1, "relevance": 0-1, "production_ready": true/false}}'
                    ),
                },
                {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{current_b64}"}},
            ],
        }],
        max_tokens=100,
    )
    costs.add("verification", "gpt-4o", COST_TABLE["gpt-4o-vision"])
    steps_done.append("verification")

    try:
        v_data = json.loads(verify.choices[0].message.content)
    except json.JSONDecodeError:
        v_data = {"quality": 0.5, "relevance": 0.5, "production_ready": False}

    avg = (v_data.get("quality", 0) + v_data.get("relevance", 0)) / 2
    return {
        "steps": steps_done,
        "approved": avg >= min_quality and v_data.get("production_ready", False),
        "verification": v_data,
        "total_cost": costs.total,
        "cost_breakdown": costs.summary(),
        "latency": round(time.time() - start, 2),
    }


final = pipeline_full_generation(
    "Banner para blog post sobre machine learning en salud",
    edit_instruction="add text space on the left side with subtle gradient overlay",
)
print(f"Pasos: {final['steps']}")
print(f"Aprobado: {final['approved']}")
print(f"Costo: ${final['total_cost']:.4f}")

Troubleshooting

Rate limit en batch

Síntoma: Error 429 al generar imágenes en batch.

import time


def rate_limited_generation(prompts: list[str], delay: float = 15.0) -> list[dict]:
    results = []
    for i, prompt in enumerate(prompts):
        for attempt in range(3):
            try:
                resp = client.images.generate(model="dall-e-3", prompt=prompt, size="1024x1024", n=1)
                results.append({"index": i, "url": resp.data[0].url, "success": True})
                break
            except Exception as e:
                if "rate" in str(e).lower() or "429" in str(e):
                    time.sleep(delay * (attempt + 1))
                else:
                    results.append({"index": i, "success": False, "error": str(e)})
                    break
        if i < len(prompts) - 1:
            time.sleep(delay)
    return results

Verificación con Vision inconsistente

Causa: Variabilidad inherente en evaluaciones subjetivas.

Solución: Usa múltiples criterios específicos (subject_match, quality, coherence) en lugar de un score general, y promedia los resultados. Esto reduce la varianza entre evaluaciones.

Costo del pipeline se dispara

Causa: Reintentos excesivos, HD innecesario, verificaciones redundantes.

Solución: Usa CostTracker para identificar el paso más caro. Baja calidad a "standard" en iteraciones de prueba, reduce max_retries, verifica solo en el paso final.


Ejercicios

Ejercicio 1: Pipeline multi-idioma con ilustraciones

Crea un pipeline que reciba un artículo en español, lo resuma, genere un prompt de ilustración, genere la imagen, y verifique relevancia. Retorna resumen, ilustración y score.

Ver solución
import json
from openai import OpenAI

client = OpenAI()


def pipeline_article_illustration(article_text: str, style: str = "modern editorial illustration") -> dict:
    costs = CostTracker()

    summary_resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": 'Resume el artículo y identifica tema visual. JSON: {"summary_es": "...", "visual_theme": "..."}'},
            {"role": "user", "content": article_text[:4000]},
        ],
        max_tokens=200,
    )
    costs.add("summary", "gpt-4o-mini", 0.000300)
    summary = json.loads(summary_resp.choices[0].message.content)

    prompt_resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": f"Crea prompt DALL-E 3 basado en tema visual. Estilo: {style}. Inglés. Solo prompt."},
            {"role": "user", "content": summary["visual_theme"]},
        ],
        max_tokens=150,
    )
    costs.add("prompt", "gpt-4o-mini", 0.000150)
    img_prompt = prompt_resp.choices[0].message.content.strip()

    gen = client.images.generate(
        model="dall-e-3", prompt=img_prompt, size="1024x1024", response_format="b64_json", n=1,
    )
    costs.add("generation", "dall-e-3", 0.040)

    verify = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": f"Artículo: {summary['summary_es']}\n¿Ilustración relevante? JSON: {{\"relevance\": 0-1}}"},
                {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{gen.data[0].b64_json}"}},
            ],
        }],
        max_tokens=50,
    )
    costs.add("verification", "gpt-4o", 0.005)
    relevance = json.loads(verify.choices[0].message.content).get("relevance", 0)

    return {
        "summary": summary["summary_es"],
        "image_prompt": img_prompt,
        "relevance": relevance,
        "approved": relevance >= 0.7,
        "cost_breakdown": costs.summary(),
    }


r = pipeline_article_illustration(
    "La computación cuántica promete revolucionar la criptografía y la simulación molecular."
)
print(f"Relevancia: {r['relevance']}, Aprobado: {r['approved']}")

Ejercicio 2: Pipeline de catálogo con fallback multi-provider

Genera imágenes para 3 productos. Para cada uno: intenta DALL-E 3 primero; si falla, usa SD como fallback. Incluye quality check y cost tracking.

Ver solución
import json
import time
import base64
import urllib.request
from openai import OpenAI
import replicate

client = OpenAI()


def catalog_with_fallback(products: list[dict], delay: float = 5.0) -> dict:
    costs = CostTracker()
    results = []

    for i, prod in enumerate(products):
        name = prod.get("name", f"Product {i}")
        image_b64 = None
        provider = None

        prompt_resp = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": "Prompt de foto producto profesional en inglés. Solo prompt."},
                {"role": "user", "content": prod["description"]},
            ],
            max_tokens=100,
        )
        costs.add(f"{name}_prompt", "gpt-4o-mini", 0.000150)
        img_prompt = prompt_resp.choices[0].message.content.strip()

        try:
            gen = client.images.generate(
                model="dall-e-3", prompt=img_prompt, size="1024x1024",
                response_format="b64_json", n=1,
            )
            image_b64 = gen.data[0].b64_json
            provider = "dall-e-3"
            costs.add(f"{name}_gen", "dall-e-3", 0.040)
        except Exception:
            try:
                sd_out = replicate.run(
                    "stability-ai/sdxl:39a52a2a03a4faf0651640ac8a542059c52f2d04fc26c8b83e22b0a957ffedd3",
                    input={"prompt": img_prompt, "negative_prompt": "blurry, low quality",
                           "width": 1024, "height": 1024},
                )
                url = sd_out[0] if isinstance(sd_out, list) else str(sd_out)
                image_b64 = base64.b64encode(urllib.request.urlopen(url).read()).decode("utf-8")
                provider = "sd"
                costs.add(f"{name}_gen", "sdxl", 0.004)
            except Exception as e:
                results.append({"name": name, "success": False, "error": str(e)})
                continue

        qc = client.chat.completions.create(
            model="gpt-4o",
            messages=[{
                "role": "user",
                "content": [
                    {"type": "text", "text": f"Foto de producto '{prod['description']}'. JSON: {{\"score\": 0-1}}"},
                    {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_b64}"}},
                ],
            }],
            max_tokens=50,
        )
        costs.add(f"{name}_qc", "gpt-4o", 0.005)
        score = json.loads(qc.choices[0].message.content).get("score", 0)

        results.append({"name": name, "provider": provider, "score": score, "approved": score >= 0.7, "success": True})
        if i < len(products) - 1:
            time.sleep(delay)

    return {
        "approved": sum(1 for r in results if r.get("approved")),
        "total": len(results),
        "results": results,
        "cost_breakdown": costs.summary(),
    }


cat = catalog_with_fallback([
    {"name": "Headphones", "description": "Premium wireless headphones, matte black"},
    {"name": "Watch", "description": "Smartwatch con pantalla AMOLED, correa azul"},
    {"name": "Lamp", "description": "Lámpara LED escritorio, brazo articulado"},
])
print(f"Aprobados: {cat['approved']}/{cat['total']}")
print(cat["cost_breakdown"])

Ejercicio 3: Pipeline de generación iterativa con edición

Crea un pipeline que: (1) genere imagen base, (2) evalúe con Vision, (3) si score < 0.8 identifique zona problemática, (4) use inpainting para corregir, (5) verifique de nuevo. Máximo 2 ciclos.

Ver solución
import json
import time
import base64
import io
import tempfile
import urllib.request
from PIL import Image, ImageDraw
from openai import OpenAI

client = OpenAI()


def iterative_generate_and_fix(prompt: str, threshold: float = 0.8, max_fixes: int = 2) -> dict:
    costs = CostTracker()
    history = []

    gen = client.images.generate(
        model="dall-e-3", prompt=prompt, size="1024x1024", response_format="b64_json", n=1,
    )
    costs.add("initial_gen", "dall-e-3", 0.040)
    current_b64 = gen.data[0].b64_json

    for fix_round in range(max_fixes + 1):
        ev = client.chat.completions.create(
            model="gpt-4o",
            messages=[{
                "role": "user",
                "content": [
                    {"type": "text", "text": (
                        f"Evalúa para: '{prompt}'\n"
                        'JSON: {{"score": 0-1, "issues": [{{"area": "top/center/bottom", "description": "..."}}], '
                        '"fix_prompt": "prompt para corregir"}}'
                    )},
                    {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{current_b64}"}},
                ],
            }],
            max_tokens=300,
        )
        costs.add(f"eval_{fix_round}", "gpt-4o", 0.005)

        try:
            eval_data = json.loads(ev.choices[0].message.content)
        except json.JSONDecodeError:
            eval_data = {"score": 0.5, "issues": [], "fix_prompt": ""}

        score = eval_data.get("score", 0)
        history.append({"round": fix_round, "score": score, "issues": eval_data.get("issues", [])})

        if score >= threshold or fix_round == max_fixes or not eval_data.get("issues"):
            break

        area = eval_data["issues"][0].get("area", "center")
        img = Image.open(io.BytesIO(base64.b64decode(current_b64))).convert("RGBA")
        w, h = img.size
        mask = Image.new("RGBA", (w, h), (0, 0, 0, 255))
        draw = ImageDraw.Draw(mask)
        coords = {"top": (0, 0, w, h//3), "center": (w//4, h//4, 3*w//4, 3*h//4), "bottom": (0, 2*h//3, w, h)}
        draw.rectangle(coords.get(area, coords["center"]), fill=(0, 0, 0, 0))

        with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as fi:
            img.save(fi, "PNG"); img_path = fi.name
        with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as fm:
            mask.save(fm, "PNG"); mask_path = fm.name

        with open(img_path, "rb") as fi, open(mask_path, "rb") as fm:
            edit = client.images.edit(image=fi, mask=fm, prompt=eval_data.get("fix_prompt", prompt), n=1, size="1024x1024")

        costs.add(f"fix_{fix_round}", "dall-e-2", 0.020)
        current_b64 = base64.b64encode(urllib.request.urlopen(edit.data[0].url).read()).decode("utf-8")

    return {
        "final_score": history[-1]["score"],
        "fix_rounds": len(history) - 1,
        "history": history,
        "approved": history[-1]["score"] >= threshold,
        "cost_breakdown": costs.summary(),
    }


r = iterative_generate_and_fix(
    "Professional team meeting in modern glass conference room", threshold=0.8, max_fixes=2,
)
print(f"Score final: {r['final_score']}, Rondas: {r['fix_rounds']}, Aprobado: {r['approved']}")

Recursos Adicionales

  1. OpenAI Vision API — Verificación de imágenes
  2. OpenAI Image Generation — Generación y edición
  3. OpenAI Pricing — Precios para cálculo de costos
  4. Replicate Pricing — Costos de SD vía Replicate
  5. Building Multimodal Pipelines — Cookbook de OpenAI