93 lines
3.8 KiB
Markdown
93 lines
3.8 KiB
Markdown
# Analytics & usage audit (Descrybe v2)
|
||||
|
|
|
|||
|
|
Date: 2026-08-04
|
|||
|
|
Scope: company billing usage, admin analytics, meters vs live Postgres.
|
|||
|
|
|
|||
|
|
## Surfaces
|
|||
|
|
|
|||
|
|
| Surface | Path / API | Role |
|
|||
|
|
|---------|------------|------|
|
|||
|
|
| Company billing / usage | `/billing` → `GET /api/billing/credits`, `GET /api/billing/usage?range=` | Tenant |
|
|||
|
|
| Dashboard meters | `/dashboard` → `/api/auth/me` credits + product count | Tenant |
|
|||
|
|
| Admin overview | `/admin` → summary from `/api/admin/analytics` | Platform admin |
|
|||
|
|
| Admin analytics | `/admin/analytics` → `GET /api/admin/analytics?days=` | Platform admin |
|
|||
|
|
| Admin billing | `/admin/billing` | Plans / credits / cycles |
|
|||
|
|
|
|||
|
|
## Bugs found
|
|||
|
|
|
|||
|
|
1. **Stale cycle override on company usage**
|
|||
|
|
`UsageSummary` preferred the latest `billing_cycles` row (`credits_used=22`, `products_processed=12`, June–July window) over live wallet and catalog. Demo wallet was `used_credits=0` with **~4323+** processed products and an **Aug–Sep** `company_plans` cycle.
|
|||
|
|
|
|||
|
|
2. **Misleading empty / range UI on `/billing`**
|
|||
|
|
“Usage history” gated on `credits_used > 0`, so a rich catalog with zero wallet spend looked empty. The 7d/30d selector did not change any numbers.
|
|||
|
|
|
|||
|
|
3. **Admin provider cards at zero on running binary**
|
|||
|
|
Disk already had `ai_provider_mode` rollups; the old `api.exe` still returned stub zeros. After rebuild, internal mode matches DB (`internal` on all products/jobs).
|
|||
|
|
|
|||
|
|
4. **Feeds missing from admin volume**
|
|||
|
|
Input/export feed counts were not exposed on the analytics summary.
|
|||
|
|
|
|||
|
|
## Fixes
|
|||
|
|
|
|||
|
|
### `GET /api/billing/usage?range=7d|30d|cycle|all` (default `30d`)
|
|||
|
|
|
|||
|
|
- **Credits** always from live `credit_balances` (not historical cycle rows).
|
|||
|
|
- **Products / tokens** filtered by range; `products_total` is all-time catalog.
|
|||
|
|
- **Cycle dates** from active `company_plans` (`billing_cycle_start` / `next_billing_date`).
|
|||
|
|
- **Feeds** (`feeds_input`, `feeds_export`) and `jobs_total` from live tables.
|
|||
|
|
- **Series** (products + tokens by UTC day) for 7d / 30d / cycle.
|
|||
|
|
- Notes clarify that credit debits are not daily-ledgered yet.
|
|||
|
|
|
|||
|
|
### Admin analytics
|
|||
|
|
|
|||
|
|
- Summary includes `feeds_input` / `feeds_export`.
|
|||
|
|
- Provider breakdown + detail + per-day class series from `ai_provider_mode`.
|
|||
|
|
- Notes updated; billing-cycle table labeled as historical rollups that may lag the active plan window.
|
|||
|
|
|
|||
|
|
### UI
|
|||
|
|
|
|||
|
|
- `/billing`: catalog card, range metric cards, honest empty states, range-driven chart via `AdminSeriesChart`.
|
|||
|
|
- `/admin/analytics`: feed counts on Volume card; clearer cycle table copy.
|
|||
|
|
|
|||
|
|
## Demo verification (Local Demo Co)
|
|||
|
|
|
|||
|
|
Against `postgres://…@localhost:5433/descrybe` after rebuild, logged in as `demo@descrybe.local`:
|
|||
|
|
|
|||
|
|
| Meter | API | DB |
|
|||
|
|
|-------|-----|-----|
|
|||
|
|
| Wallet used / total | 0 / 1_000_000 | 0 / 1_000_000 |
|
|||
|
|
| Products (all) | 4326 | 4326 |
|
|||
|
|
| Input / export feeds | 12 / 5 | 12 / 5 |
|
|||
|
|
| Plan cycle | 2026-08-04 → 2026-09-04 | `company_plans` |
|
|||
|
|
| Platform products | 7225 | 7225 |
|
|||
|
|
| Platform credits used | 26 | 26 |
|
|||
|
|
| Platform feeds in/out | 35 / 12 | 35 / 12 |
|
|||
|
|
| Provider internal tokens/products | 11_383_374 / 7225 | `ai_provider_mode=internal` |
|
|||
|
|
|
|||
|
|
Previously the usage API returned **22 credits / 12 products** from a stale `billing_cycles` row — fixed.
|
|||
|
|
|
|||
|
|
## Remaining limitations
|
|||
|
|
|
|||
|
|
- No per-day **credit** ledger; range filters apply to products/tokens only.
|
|||
|
|
- `billing_cycles` historical rows can lag or disagree with the active plan window; they remain an admin history table, not the tenant source of truth.
|
|||
|
|
- Token series for migrated catalogs often clumps on import day(s).
|
|||
|
|
|
|||
|
|
## Verification commands
|
|||
|
|
|
|||
|
|
```powershell
|
|||
|
|
cd apps/api
|
|||
|
|
go test ./internal/billing/ -run ParseUsageRange -count=1
|
|||
|
|
go build -o bin/api.exe ./cmd/api
|
|||
|
|
|
|||
|
|
cd ../web
|
|||
|
|
npm run check
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Demo smoke (after API restart):
|
|||
|
|
|
|||
|
|
```powershell
|
|||
|
|
# login as demo@descrybe.local then:
|
|||
|
|
# GET /api/billing/usage?range=30d
|
|||
|
|
# GET /api/admin/analytics?days=30
|
|||
|
|
```
|