From 5b9f2a58bc24549f8497d655d1bdc2ae06b6031d Mon Sep 17 00:00:00 2001 From: Mateo Saldain Date: Mon, 27 Jul 2026 23:39:25 -0300 Subject: [PATCH] Ajustes tras validar el harness contra la API real MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- configs/data/distill.yaml | 6 ++++++ enlace/data/distill.py | 6 +++++- tests/test_distill.py | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/configs/data/distill.yaml b/configs/data/distill.yaml index 39d26b9..0958f19 100644 --- a/configs/data/distill.yaml +++ b/configs/data/distill.yaml @@ -34,3 +34,9 @@ output_dir: data/distill usd_per_mtok_input: 5.0 usd_per_mtok_output: 25.0 batch_discount: 0.5 + +# NOTA sobre el caché: el prompt de sistema compartido va marcado con +# cache_control, pero el mínimo cacheable de Opus 5 son 512 tokens. Un prompt +# más corto no se cachea y la marca no hace nada — sin error, en silencio. +# Verificalo con `distill usage`: si cache_read_input_tokens queda en 0 sobre +# un trabajo grande, alargá el prompt de sistema o asumí el costo completo. diff --git a/enlace/data/distill.py b/enlace/data/distill.py index b0aa72e..9fee570 100644 --- a/enlace/data/distill.py +++ b/enlace/data/distill.py @@ -470,6 +470,10 @@ def resumen_de_uso(trabajo: Trabajo, config: DistillConfig) -> dict[str, Any]: # Los tokens leídos de caché cuestan ~0,1x y los escritos ~1,25x; el # descuento del lote se aplica sobre todo. Es una estimación para saber en # qué orden de magnitud estamos, no una factura. + # + # Si cache_read_input_tokens da 0 en un trabajo grande, el prompt de sistema + # es más corto que el mínimo cacheable del modelo (512 tokens en Opus 5) y + # la marca cache_control no está haciendo nada. entrada_efectiva = entrada + cache_read * 0.1 + cache_write * 1.25 usd = ( entrada_efectiva / 1e6 * config.usd_per_mtok_input @@ -482,7 +486,7 @@ def resumen_de_uso(trabajo: Trabajo, config: DistillConfig) -> dict[str, Any]: "output_tokens": salida, "cache_read_input_tokens": cache_read, "cache_creation_input_tokens": cache_write, - "usd_estimado": round(usd, 2), + "usd_estimado": round(usd, 4), } diff --git a/tests/test_distill.py b/tests/test_distill.py index ac22577..1afba14 100644 --- a/tests/test_distill.py +++ b/tests/test_distill.py @@ -274,7 +274,7 @@ def test_el_resumen_de_uso_aplica_el_descuento_y_la_cache(trabajo, config): # (200 entrada + 1800*0.1 caché)/1e6*5 + 400/1e6*25, todo al 50%. esperado = ((200 + 180) / 1e6 * 5 + 400 / 1e6 * 25) * 0.5 - assert uso["usd_estimado"] == pytest.approx(round(esperado, 2)) + assert uso["usd_estimado"] == pytest.approx(round(esperado, 4)) def test_sin_resultados_el_costo_es_cero(trabajo, config):