CLI · Python 3.12 · sin dependencias

modelo vs modelo

Dos modelos, una tarea, los mismos tests. modelduel pide el código a dos modelos de IA (o a una liga de hasta seis), ejecuta tus tests sobre cada respuesta y te da un informe con quién acierta, cuánto tarda y cuánto cuesta. Tu propia comparativa, sin fiarte de rankings ajenos.

Un duelo pregrabado, en tu navegador: sin claves ni coste.

Duelo en directo

Mira cómo se resuelve una tarea: cada modelo escribe su código, los tests caen uno a uno y sale el marcador. Dura unos 25 segundos y puedes pausarlo o detenerlo cuando quieras. Es un resultado pregrabado: no se llama a ningún modelo ni se ejecuta código.

Esta reproducción necesita JavaScript. Ver el informe estático del mismo duelo.

Resultado pregrabado con respuestas de ejemplo (replay). Ver el informe completo.

Qué hace

Pregunta

Envía el mismo enunciado a cada modelo y les pide un único bloque de código Python.

Ejecuta

Guarda cada respuesta como solution.py en un directorio temporal y pasa los tests de la tarea con pytest y un límite de tiempo.

Compara

Genera results.json y un informe HTML con marcador, tiempos, tokens, coste estimado y el código de cada modelo.

Instalación y uso

Necesitas Python 3.12 o superior. modelduel no tiene dependencias; pytest hace falta para ejecutar los tests de las tareas.

pip install "modelduel[pytest]"    # o: pip install modelduel pytest

# Demo sin claves ni coste: una liga de tres contendientes con respuestas grabadas
modelduel demo
modelduel demo --copy MIS-EJEMPLOS  # copia las tareas de ejemplo como plantilla

# Dos modelos reales (o repite --model para una liga de 2 a 6)
modelduel run MIS-EJEMPLOS/tasks --a gemini:<modelo> --b openai:<modelo> --runs 3 --out runs/duelo

# Continuar un duelo cortado
modelduel run MIS-EJEMPLOS/tasks --a gemini:<modelo> --b openai:<modelo> --runs 3 --out runs/duelo --resume

# Otras órdenes
modelduel list-tasks MIS-EJEMPLOS/tasks
modelduel report runs/duelo/results.json --out runs/duelo

El paquete está en PyPI. Para la última versión sin publicar: pip install git+https://github.com/BertMarti/modelduel pytest.

Novedades de v0.7.0

  • Señal de vida: si una llamada a un modelo tarda más de unos 10 s, modelduel avisa cada 10 s por stderr de que sigue esperando. El límite de red por defecto baja a 90 s (MODELDUEL_HTTP_TIMEOUT lo cambia).
  • Desde v0.6.0: informe en Markdown: modelduel report results.json --format md --out DIR genera informe.md, listo para pegar en un PR o un issue, con el texto de results.json escapado.
  • Desde v0.5.0: el duelo en directo de esta página y los informes más legibles. Desde v0.4.0: proveedor omniroute: y clasificación pública. Desde v0.2.0: liga de 2 a 6, reintentos y --resume.

Todos los cambios, en el registro de cambios; el uso paso a paso, en la guía de uso.

Crear una tarea

Una tarea es una carpeta con tres archivos. El enunciado debe pedir una función concreta con nombre y firma; los tests la importan de solution.

mis-tareas/
└── invertir_palabras/
    ├── task.md        # enunciado para el modelo
    ├── test_task.py   # tests pytest: from solution import invertir_palabras
    └── meta.toml      # opcional: title, difficulty, order
# test_task.py
from solution import invertir_palabras


def test_basico():
    assert invertir_palabras("hola mundo") == "mundo hola"

Consejo: incluye casos límite. Son los que separan a un modelo que entiende el enunciado de uno que solo se parece a la respuesta correcta.

Proveedores

EspecificaciónQué usaVariables de entorno
replay:<nombre>Respuestas grabadas en examples/replays/<nombre>/<tarea>.md. Sin red ni coste.—
gemini:<modelo>API REST generateContent de Google.GEMINI_API_KEY
omniroute:<modelo>El router local OmniRoute (API compatible con OpenAI).OMNIROUTE_BASE_URL, OMNIROUTE_API_KEY (opcional)
openai:<modelo>Cualquier API compatible con Chat Completions: OpenAI, OpenRouter, Ollama…OPENAI_API_KEY, OPENAI_BASE_URL

Las claves se leen solo de variables de entorno y nunca aparecen en el informe. Con Ollama en local basta con OPENAI_BASE_URL=http://localhost:11434/v1.

Coste estimado

coste = entrada / 1e6 × tarifa_entrada + salida / 1e6 × tarifa_salida

Las tarifas van por millón de tokens y las pones tú con --prices precios.json, porque cambian a menudo. Si un modelo no tiene precio, el informe dice «sin datos»: nunca se inventa. Los precios de replay son ficticios y así se indica.

Seguridad

El código generado por los modelos se ejecuta en tu máquina. modelduel lo hace siempre en un directorio temporal, en un subproceso con límite de tiempo y sin tus variables de entorno secretas, pero eso no es un aislamiento real. Úsalo con tareas de confianza e, idealmente, dentro de un contenedor o una máquina virtual.

Una sola ejecución es una señal débil

Los modelos no son deterministas. Repite el duelo con --runs N, usa tareas de tu propio trabajo y lee el código, no solo el marcador.

Cómo se ha hecho

Lo ha desarrollado un equipo de agentes de IA (Claude Code), cada uno en su rama y con pull requests. Alberto Martínez supervisa y fusiona todo.

  • v0.1.0: lead, builder y qa con Claude Opus; docs con Claude Sonnet.
  • v0.2.0: todo con Claude Sonnet, guiado por issues del hito.
  • OpenCode estaba previsto para la documentación, pero no pudo ejecutarse en modo autónomo.