3 Commits

Author SHA1 Message Date
msaldain 4048936067 Corregir lo que reportó ruff, que nunca se había ejecutado
ruff estaba configurado en pyproject.toml desde el primer commit y jamás se
había corrido. Tenía 15 hallazgos. Dos importan más allá del estilo:

- zip() sin strict= trunca en silencio al más corto. En las comparaciones de
  lotes eso significa que un test podía pasar sin haber comparado todo. Donde
  los largos deben coincidir ahora es strict=True; donde difieren a propósito
  (pares consecutivos) queda strict=False, que documenta la intención.
- Un import sin usar delataba algo peor: BraveBackend se había escrito sin una
  sola prueba. Se agregan ocho, contra una respuesta con la forma que devuelve
  la API, incluidas la limpieza de etiquetas, el caso de límite de tasa —que
  tiene que distinguirse de 'no respondió'— y que la credencial viaje en la
  cabecera y nunca en la URL. El respaldo tiene que funcionar justo cuando el
  primario ya falló; merecía la misma cobertura.

El resto es orden de imports, collections.abc y líneas largas.
2026-07-28 07:25:51 -03:00
msaldain 5b9f2a58bc Ajustes tras validar el harness contra la API real
Un lote de humo de 2 pedidos contra la Batch API confirmó el camino completo
—crear, consultar estado, recoger, normalizar— y dejó dos cosas a corregir:

- El costo estimado se redondeaba a 2 decimales, así que un trabajo chico
  informaba US$ 0.0. Pasa a 4 decimales: en una prueba de humo importa saber
  que gastaste algo, no que gastaste nada.
- cache_read_input_tokens quedó en 0. La causa es que el mínimo cacheable de
  Opus 5 son 512 tokens y el prompt de sistema de la prueba tenía 86: la marca
  cache_control no hace nada por debajo de ese umbral, y no avisa. Queda
  documentado en el código y en la config, con cómo detectarlo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 23:39:25 -03:00
msaldain 8805b0541b Generación de datos sintéticos con la Batch API
Etapa 3 del plan. Se elige la Batch API sobre llamadas sueltas porque cuesta
la mitad y generar un dataset no es sensible a la latencia: es exactamente su
caso de uso. A cambio el trabajo es asincrónico y puede tardar horas, así que
todo el estado vive en disco y es reanudable — matar el proceso, perder la
conexión o reiniciar el servidor no pierde nada ni gasta de nuevo.

Cuatro decisiones que sostienen eso:

- El custom_id se deriva del hash del contenido del pedido. El trabajo queda
  idempotente: re-generar la misma especificación produce los mismos IDs, así
  que lo ya resuelto se reconoce y no se vuelve a pagar.
- Los resultados se indexan por custom_id, nunca por posición. La API los
  devuelve en cualquier orden, y emparejarlos por índice mezcla las respuestas
  en silencio; un dataset mal alineado es peor que uno vacío porque parece
  correcto. El cliente falso de los tests los invierte a propósito.
- Los fallos se registran en errors.jsonl con su motivo en vez de descartarse,
  y --retry-failed reenvía solo esos.
- El prompt de sistema compartido va marcado para caché: es idéntico entre
  miles de pedidos, y a ~0,1x del precio de entrada deja de contar.

Los lotes se persisten después de cada envío y no al final, para que una caída
a mitad de camino no deje lotes huérfanos sin registrar.

El SDK de anthropic se importa de forma perezosa y queda como extra opcional:
el servidor de entrenamiento no lo necesita y el paquete importa sin él.

21 tests nuevos (92 en total), todos contra un cliente falso — lo que hay que
verificar es el harness, no el SDK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 23:26:41 -03:00