Status: Deployed in commit 37a07919 (with follow-up 5133862b).
All four AI surfaces from the original spec are live, every one gated by AnthropicClient::enabled() — which checks both the AI_FEATURES_ENABLED env switch (managed at /admin/config?tab=environment) and the presence of ANTHROPIC_API_KEY. If either is missing the surface silently disappears: no broken UI, no errors, no API spend.
(a) Thread summarization
- Service:
App\Services\AiThreadSummary::forThread(int $threadId) returns {summary, key_points[], generated_at, cached}
- Cached per
thread_id; invalidates automatically when reply_count grows
- Only fires on threads ≥ 8 posts (config:
AiThreadSummary::MIN_POSTS)
- UI: "AI summary" card above the posts list on
/thread/{slug}, lazy-loaded on click so we never bill the API on a thread nobody reads
- Endpoint:
POST /api/ai/thread-summary/{id}
(b) AI-suggested tags
- Service:
App\Services\AiTagSuggester::suggest($title, $body) returns up to 5 lowercase 1–2-word tags
- Cached by
sha256(title + body) so identical compose attempts don't re-bill
- UI: "✨ Suggest tags" button next to the tags input on
/forums/.../new-thread; chips fill the existing tags field on click (respecting the 5-tag cap)
- Endpoint:
POST /api/ai/suggest-tags
(c) AI-powered search
- Service:
App\Services\AiSearchAssist::synthesize($query, $hits) returns a 2–3-sentence synthesis with [1] [2] style citations
- Cached by
sha256(query) for 6 hours
- Grounded on the top 8 LIKE-matched threads from the existing search (not a separate embedding store — keeps infra footprint zero)
- UI: "AI synthesis" card above
/search?q=... results, lazy-loaded on click, cites the matched threads inline as a clickable list
- Endpoint:
POST /api/ai/search-assist
(d) AI moderation assistant
- Service:
App\Services\AiReportExplain::forReport(int $reportId) returns a 1–2-sentence neutral, factual explanation of the signal (never recommends an action)
- Cached per
report_id
- UI: wand-magic-sparkles button on every report row in
/admin/moderation; AJAX-loads an inline explanation grounded in the report reason + the reported content
- Endpoint:
POST /api/ai/explain-report/{id} (requires requireRole(3))
Core infrastructure
- New
App\Services\AnthropicClient shared HTTP client (complete() + completeJson()) — reuses the AltTextGenerator proven pattern (8s timeout, IPv4 force, fail-soft returns null)
- 4 cache tables (
ai_thread_summaries, ai_tag_suggestions, ai_search_assists, ai_report_explanations) applied to support, fort-smith-live, dev
- 4 routes added to the auth + CSRF group
- Admin help text on
AI_FEATURES_ENABLED updated to describe all five AI features (alt-text, summaries, tag suggestions, search synthesis, mod-report explanations)
Operational notes
- Each per-call surface is lazy-loaded (button click, not page render) so AI cost scales with engagement, not pageviews.
- All four caches store
generated_at so housekeeping / monthly-cap analytics can be added later without schema churn.
- The bot-block middleware (
LlmBotBlockMiddleware) is unchanged — these new endpoints serve human users, not crawlers.
Live on support, fort-smith-live, dev. Locking thread.