refactor(mitm-addon): split usage.py into shared layers and per-billable-object modules
Repository: vm0-ai/vm0
Author: seven332
## Background
`crates/runner/mitm-addon/src/usage.py` has grown to **912 lines** and houses every piece of billing-related logic inside a single module:
- Flow / report counters (`set_pending_path`, `increment_flows`, `_write_pending`)
- Token extraction (SSE streaming parser, JSON body parser)
- Webhook delivery (`_post_webhook`, retry, `_enqueue_webhook`)
- Model-provider entry point (`maybe_report_proxy_usage`)
- x-specific connector billing (NDJSON parser, request/response metadata, billable counts, `log_connector_usage`)
As more billing surfaces come online (additional model providers, new billable connectors like Slack/Gmail, per-object pricing tables), this file will keep growing and every new billable object will add hundreds of lines to an already-mixed module.
## Proposed layout
Hybrid split: **shared layers** for cross-cutting concerns, **per-object modules** for each billable surface.
```
crates/runner/mitm-addon/src/usage/
├── __init__.py # re-exports the public surface used by mitm_addon.py
├── counters.py # flow/report counters + pending-path state
├── extract.py # SSE parser, JSON body parser (token usage shapes)
├── webhook.py # _post_webhook, retry logic, _enqueue_webhook
└── providers/
├── __init__.py
├── model_provider.py # maybe_report_proxy_usage
└── connectors/
├── __init__.py
└── x.py # NDJSON parser, _parse_x_*, _compute_x_billable_counts, log_connector_usage
```
### Why hybrid
- **Shared layers** (`counters` / `extract` / `webhook`) stay small and testable; adding a new billable object doesn't grow them.
- **Per-object modules** keep domain logic (field names, pricing categories, API quirks) co-located — the x connector's ~450 lines of Twitter-specific parsing don't leak into unrelated providers.
- When Slack / Gmail connector billing lands, it becomes a new file under `providers/connectors/` without touching existing ones.
## Scope
- Pure restructuring. No behavior change, no new tests.
- Keep `log_proxy_entry`, `get_api_url`, and anything cross-module imported through stable re-exports in `usage/__init__.py` so `mitm_addon.py` imports don't churn.
- Module-level state (`_flows_seen`, `usage_executor`, pending path) moves with the primary owner (counters / webhook).
- Existing tests (`test_handlers.py`) must pass untouched — patch sites (`patch.object(usage, ...)`) may need to be retargeted to the new module, but no test logic changes.
## Non-goals
- No change to billing policy or gate logic — that is handled in **#10380**.
- No migration of `_BILLABLE_CONNECTORS` / `is_billable_connector` — those are removed as part of #10380.
- No new billing mechanisms or providers.
## Dependency order
**Must land after #10380.** Doing the billing-gate unification first avoids carving `_BILLABLE_CONNECTORS` and `is_billable_connector` into a newly extracted module only to delete them. Once #10380 has removed them, the x connector module becomes the natural home for the remaining billing-count logic.
## Acceptance criteria
- [ ] `usage.py` deleted; contents distributed across the module tree above.
- [ ] `usage/__init__.py` re-exports the public API consumed by `mitm_addon.py` and tests.
- [ ] `pytest` passes unchanged (no test-logic edits beyond `patch.object` target retargeting).
- [ ] No net change to generated addon files shipped into the runner image.
- [ ] `cargo check -p runner` and `cargo test -p runner` pass (since the build step embeds these Python files).
## Files touched
- `crates/runner/mitm-addon/src/usage.py` — deleted
- `crates/runner/mitm-addon/src/usage/` — new directory
- `crates/runner/mitm-addon/src/mitm_addon.py` — import-site updates only
- `crates/runner/mitm-addon/tests/test_handlers.py` — `patch.object` target retargeting only
- `crates/runner/build.rs` — if it globs `src/*.py`, extend to walk the tree
## Related
- #10340 — introduced `billableFirewalls` (unblocks #10380)
- #10380 — unify connector billing gate (must land first)
- #9504, #9655, #9269 — parent connector-billing work
---
Created from conversation — future billing surfaces will keep accumulating in this module; hybrid split gives shared infrastructure a clean home while isolating per-object quirks.