Skip to content
reaatechREAATECH

Files · Agnostic AI Cost Control for QuickBooks Online SMBs

87 (1 binary, 598.0 kB total)attempt 1

README.md·5395 B·markdown
markdown
# Agnostic AI Cost Control for QuickBooks Online SMBs
 
> Monitor and cap AI spending across any LLM provider, then sync categorized costs directly into QuickBooks Online as operational expenses.
 
A tutorialized reference solution from [reaatech.com](https://reaatech.com), demonstrating how to build production-grade AI systems with the `@reaatech/*` package family.
 
## Problem
 
SMBs using multiple LLM providers (OpenAI, Anthropic, DeepSeek, Google, etc.) lack a unified view of their AI spend. Without central budget enforcement, costs can spiral out of control. This reference solution provides:
 
- Pre-flight budget checks before every LLM call
- Real-time OTel-span-based spend recording
- Daily QBO journal entry sync for accounting
 
## How It Works
 
1. **Budget pre-check** — Every LLM request passes through `BudgetService.check()` to verify the estimated cost fits within the remaining budget. If not, the request is blocked or downgraded.
 
2. **Real-time spend recording** — After each LLM call completes, `BudgetService.record()` writes the actual spend to the `SpendStore`. OpenTelemetry spans are also bridged via `SpanListener` for automatic recording.
 
3. **QuickBooks Online sync** — The `QboSyncService` aggregates spend entries since the last sync, creates a journal entry (debit by model/provider, credit total to cash), and posts it to QBO via the Intuit API.
 
## Quick Start
 
```bash
cp .env.example .env
# Fill in QBO credentials and database URL
pnpm install
pnpm dev
```
 
## API Reference
 
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/api/budget/check` | Pre-flight budget check |
| `POST` | `/api/cost/record` | Record actual spend |
| `POST` | `/api/cost/sync` | Sync spend to QBO journal entry |
| `GET` | `/api/budget/status` | Get budget state for a scope |
| `GET` | `/api/spend/dashboard` | Get spend dashboard data |
 
### POST /api/budget/check
 
Request body:
```json
{
  "scopeType": "user",
  "scopeKey": "user-42",
  "estimatedCost": 0.05,
  "modelId": "openai/gpt-5.2"
}
```
 
Response (200):
```json
{ "allowed": true, "action": "Allow", "remaining": 99.95, "spent": 0.05 }
```
 
Response (403):
```json
{ "allowed": false, "action": "Blocked", "reason": "Hard cap reached" }
```
 
### POST /api/cost/record
 
Request body:
```json
{
  "requestId": "req-abc",
  "scopeType": "user",
  "scopeKey": "user-42",
  "cost": 0.045,
  "inputTokens": 1500,
  "outputTokens": 200,
  "modelId": "openai/gpt-5.2",
  "provider": "openai",
  "timestamp": "2026-05-21T00:00:00Z"
}
```
 
Response (200):
```json
{ "recorded": true, "state": { "spent": 0.045, "remaining": 99.955, ... } }
```
 
### POST /api/cost/sync
 
Response (200):
```json
{ "synced": true, "journalEntryId": "JE-12345", "totalCost": 12.50 }
```
 
### GET /api/budget/status?scopeType=user&scopeKey=user-42
 
Response (200):
```json
{ "spent": 0.045, "remaining": 99.955, "limit": 100, "state": "Active" }
```
 
### GET /api/spend/dashboard?scopeType=user&scopeKey=*
 
Response (200):
```json
{ "totalSpend": 0.045, "rate": 0.009, "projection": 0.54, "recentModels": [], "spikeCount": 0 }
```
 
## Configuration
 
| Env Var | Description | Default |
|---------|-------------|---------|
| `QBO_CLIENT_ID` | QuickBooks OAuth 2.0 client ID | — |
| `QBO_CLIENT_SECRET` | QuickBooks OAuth 2.0 client secret | — |
| `QBO_REFRESH_TOKEN` | QuickBooks OAuth 2.0 refresh token | — |
| `QBO_COMPANY_ID` | QuickBooks Online company ID | — |
| `QBO_ENVIRONMENT` | `sandbox` or `production` | sandbox |
| `DATABASE_URL` | PostgreSQL connection string | — |
| `PORT` | Express server port | 3000 |
| `DEFAULT_BUDGET_LIMIT` | Default budget limit in USD | 100.0 |
| `DEFAULT_SOFT_CAP` | Soft cap threshold (0.0–1.0) | 0.8 |
| `DEFAULT_HARD_CAP` | Hard cap threshold (0.0–1.0) | 1.0 |
 
## QuickBooks Online Setup
 
1. Go to [developer.intuit.com](https://developer.intuit.com) and create an OAuth 2.0 app.
2. Set the redirect URI to `https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl`.
3. Use the OAuth 2.0 Playground to generate a refresh token.
4. Map the journal entry accounts: "AI Expenses" (debit) = 5000, "Cash" (credit) = 1000.
5. Set the environment variables in `.env`.
 
## Architecture
 
- **BudgetController** (@reaatech/agent-budget-engine) — The governor that enforces budget limits, evaluates policies, and manages the per-scope state machine.
- **SpendStore** (@reaatech/agent-budget-spend-tracker) — In-memory circular buffer for O(1) spend lookups, rate calculations, projections, and spike detection.
- **SpanListener** (@reaatech/agent-budget-otel-bridge) — Bridges OpenTelemetry GenAI spans into budget spend entries automatically.
- **QboClient** — Handles OAuth 2.0 token management and posts journal entries to the QuickBooks Online API.
- **CostTrackingPricingProvider** — Maps model IDs to per-million-token costs for budget estimation.
 
## Project layout
 
```
app/                  Next.js App Router pages + API routes
src/                  services, lib, adapters
tests/                vitest suite (mirrors src/)
packages/             API references for every dependency (read these first)
DEV_PLAN.md           build plan for this recipe
```
 
## Testing
 
```bash
pnpm test            # vitest run with coverage
pnpm typecheck       # tsc --noEmit
pnpm lint            # eslint .
```
 
## License
 
MIT — see [LICENSE](./LICENSE).