Track and cap WordPress 7.0 AI Client usage — per plugin, per user — with an admin dashboard and optional, confidence-gated limits.
Status: working prototype (v0.1.0). Phase 1 (tracking) and Phase 2 (limits & enforcement) are both built and verified on a live WP 7.0 install. Cost figures are estimates (see Cost calculation).
WordPress 7.0 ships an AI Client in core with a single shared provider key that any plugin can spend against — and no built-in spend controls. The official AI plugin added usage logging but no enforcement, and has none on its roadmap.
This plugin fills that gap. Its wedge is attribution: answering "which plugin / which user spent this money?" inside one shared key — something the provider's billing dashboard cannot see — and then letting you cap it.
- Tracks every AI Client request: tokens (input/output/thinking), provider, model, and estimated cost, attributed to the originating plugin and user/role.
- Dashboard at Tools → AI Usage: total spend, per-plugin and per-user breakdowns (with attribution-confidence badges), a provider/model breakdown, and a usage-over-time chart.
- Limits & enforcement (Phase 2): configure caps per plugin / user / role / global, by
requests / tokens / cost, per day / month. Modes:
off(track only),soft(alert),hard(block). Hard blocks return a gracefulWP_Error— no fatals. - Alerts at 80% / 100% of a limit, via email (or a custom channel).
- Observe-only until you say otherwise. With no hard limit configured, the plugin only tracks — it never blocks. A cached flag makes this a zero-cost fast path.
- Fails open. Every capture and enforcement path is wrapped so a bug or misconfiguration allows the request. A usage/billing plugin must never take down a site's AI.
- WordPress 7.0+ (the AI Client API ships in core;
wp_ai_client_prompt()must exist). - PHP 7.4+.
- At least one configured AI provider (e.g. the AI Provider for Anthropic plugin) under Settings → Connectors for real requests to flow.
This is a source checkout, not a packaged plugin. To run it:
# PHP tooling
composer install
# Build the React dashboard
npm install
npm run build # emits build/index.js, build/index.asset.php, build/style-index.cssThen symlink or copy the directory into wp-content/plugins/ and activate. On activation
the plugin checks for WP 7.0 + the AI Client and creates its tables; if the environment is
unsupported it deactivates itself with an admin notice rather than erroring.
See docs/TESTING.md for the full build/verify workflow and the local live-test setup.
Open Tools → AI Usage. Data appears as soon as any plugin makes an AI Client request.
Attribution works automatically via call-stack inspection. A plugin can make itself precisely attributed (and eligible for high-confidence hard limits) by announcing itself right before its prompt:
do_action( 'wp_aiut_attribute', 'my-plugin-slug' );
$text = wp_ai_client_prompt( 'Summarize this.' )->generate_text();Tools → AI Usage → Limits → Add limit. Pick a scope, a meter (cost/tokens/requests), a
period, a threshold, and an enforcement mode. A hard cost limit on a plugin will block
that plugin's requests (with a WP_Error) once it exceeds the cap for the period.
No native mechanism tells us which plugin made a call, so attribution is layered, in confidence order:
- Self-ID (
high) — the plugin calledwp_aiut_attribute(above). - Backtrace (
medium) — we map the calling file to its plugin/theme slug. Works with zero cooperation — this is the default for most plugins. - Unknown (
low) —__unknown__. Still tracked, never dropped.
Enforcement is confidence-gated: hard limits block high+medium by default; a limit
can be set to require high (self-identified only); __unknown__ is never singled out (but
can be capped as a group). This is the deliberate answer to "we can't assume plugins
self-identify." See docs/DECISIONS.md.
cost (USD) = Σ (tokens × price_per_million) ÷ 1,000,000 # per input/output/thinking
- Prices are per 1,000,000 tokens, in USD, in a table keyed by
provider/model. - Stored as integer micros (1e-6 USD) to avoid floating-point drift.
- Model lookup is exact, then longest-prefix — so a versioned slug like
claude-opus-4-8resolves to theclaude-opus-4family entry, and future minor versions keep working without edits.
⚠️ The shipped prices are estimates, not verified live rates. Providers change pricing over time and by tier/region. Override any row in the pricing table (optionaiut_pricing) or via thewp_aiut_pricingfilter before relying on the absolute dollar figures. The UI labels figures as estimated for this reason.
The plugin exposes actions and filters for integration (self-ID, block notifications,
custom alert channels, pricing overrides, capability/recipient filters) and a REST API under
wp-aiut/v1. Full reference: docs/HOOKS.md.
| Doc | What's in it |
|---|---|
| docs/ARCHITECTURE.md | System map, request lifecycle, component responsibilities |
| docs/DATA-MODEL.md | Tables, columns, scope/period model, options |
| docs/HOOKS.md | Every action/filter + the REST API |
| docs/DECISIONS.md | Why things are built this way (read this first for a takeover) |
| docs/TESTING.md | Build, quality gates, and the live/no-cost test workflow |
CLAUDE.md |
Guidance for AI coding agents working in this repo |
wp-ai-rate-limiter.php bootstrap: constants, autoloader, activation guard
uninstall.php drops tables/options (opt-in)
src/ PHP, namespace \WP_AIUT\ (class-{kebab}.php)
Capture/ prevent_prompt hook, result capture, transporter decorator
Attribution/ caller + user resolution (self-ID / backtrace / unknown)
Accounting/ usage recorder, atomic counters, cost calculator
Data/ schema (dbDelta), usage repository
Periods/ timezone-aware day/month windows
Limits/ limit repository + evaluator (Phase 2)
Enforcement/ the block decision (Phase 2)
Alerts/ threshold watcher + notifier (Phase 2)
Admin/ REST controller, settings page
assets/src/ React dashboard (index.js, App.js, Limits.js, style.scss)
build/ compiled assets (generated)
docs/ this documentation set
WordPress Coding Standards (PHPCS WordPress ruleset), PHPStan level 10, php-parallel-lint,
and @wordpress/scripts JS linting — all wired into composer scripts and GitHub Actions.
See docs/TESTING.md.
GPL-2.0-or-later.