fix(focus): re-send the last two whole days so late-flushed spend lands

LiteLLM writes DailyUserSpend in batches, so spend dated yesterday can be
flushed after the 00:05 UTC daily export. That export only covered the
previous UTC midnight onward, and the next day's export moved past it, so
the tail of each day was never sent. Start the window at UTC midnight two
days back: the following run re-sends yesterday whole, overwriting its
per-day object with the complete day.
This commit is contained in:
Aaron Sproul 2026-10-07 19:03:25 +00:00
parent 32bf19bbef
commit b9134f2d73
2 changed files with 41 additions and 4 deletions

View file

@ -256,9 +256,9 @@ class TernaryLogger(FocusLogger):
)
def _compute_time_window(self, now: datetime) -> FocusTimeWindow:
"""Snap the window start to the previous UTC midnight so each push carries whole days."""
"""Start at UTC midnight two days back so each push re-sends whole days, including spend flushed late."""
now_utc: Final = now.astimezone(timezone.utc)
start_time: Final = (now_utc - timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
start_time: Final = (now_utc - timedelta(days=2)).replace(hour=0, minute=0, second=0, microsecond=0)
return FocusTimeWindow(start_time=start_time, end_time=now_utc, frequency=self.frequency)
def _ensure_engine(self) -> FocusExportEngine:

View file

@ -94,9 +94,46 @@ def test_compute_time_window_is_day_aligned_and_carries_whole_days():
logger = TernaryLogger(api_key="k", connection_id="c", base_url="http://localhost:8080")
now = datetime(2026, 9, 4, 13, 47, 5, tzinfo=timezone.utc)
window = logger._compute_time_window(now)
assert window.start_time == datetime(2026, 9, 3, 0, 0, 0, tzinfo=timezone.utc)
assert window.start_time == datetime(2026, 9, 2, 0, 0, 0, tzinfo=timezone.utc)
assert window.end_time == now
assert window.start_time.hour == 0 and window.start_time.minute == 0
@pytest.mark.asyncio
async def test_daily_export_resends_yesterday_whole_including_spend_flushed_after_midnight():
"""LiteLLM flushes spend in batches, so a 2026-10-07 row can be written at 2026-10-08 00:10, after that
day's 00:05 export. The next daily run (2026-10-09 00:05) must still upload 2026-10-07 complete."""
from datetime import datetime, timezone
rows = pl.DataFrame(
{
"date": ["2026-10-07", "2026-10-07", "2026-10-08"],
"updated_at": [
datetime(2026, 10, 7, 12, 0, tzinfo=timezone.utc),
datetime(2026, 10, 8, 0, 10, tzinfo=timezone.utc),
datetime(2026, 10, 8, 9, 0, tzinfo=timezone.utc),
],
"spend": [1.0, 2.0, 3.0],
}
)
logger = TernaryLogger(api_key="k", connection_id="c", base_url="http://localhost:8080")
window = logger._compute_time_window(datetime(2026, 10, 9, 0, 5, tzinfo=timezone.utc))
engine = MagicMock()
async def fake_get_usage_data(*, limit, start_time_utc, end_time_utc):
return rows.filter((pl.col("updated_at") >= start_time_utc) & (pl.col("updated_at") <= end_time_utc))
engine._database.get_usage_data = fake_get_usage_data
captured = {}
async def fake_deliver(data, _window):
captured["data"] = data
engine._deliver_enriched = fake_deliver
await _TernaryExportEngine.export_window(engine, window=window, limit=None)
sent = captured["data"]
assert sent.filter(pl.col("date") == "2026-10-07")["spend"].sum() == 3.0
def test_drop_days_before_removes_older_days():