AI (Phase 7)

CreatorOS uses Anthropic Claude for text and a placeholder renderer for graphics. Everything the AI creates is a draft: inactive products, status = draft posts, disabled automations, media assets flagged ai_generated. Publishing, activating, pricing and deleting always require a person in the dashboard.

Configuration

VariableEffect
ANTHROPIC_API_KEYBlank → MockAIProvider (deterministic, every output labelled "(mock)"), dashboard shows an amber banner.
ANTHROPIC_MODELModel id sent to the Messages API (default claude-sonnet-5; claude-opus-5 recommended for copilot quality).
IMAGE_PROVIDEROnly mock exists today (GD gradient + headline). Real provider is an open decision (CLAUDE.md §15).

Plan limits: tenants.limits.ai_monthly (free 20 / creator 500 / network 5000). Each generation inserts one ai_usage row (tokens + estimated cost in micros); exceeding the limit returns HTTP 429 "AI limit reached". GET /dashboard/ai/usage shows the log.

Code map

  • app/Providers/AI/AIProvider.php — interface. AnthropicProvider (raw Messages API via App\Core\Http: system, adaptive thinking, tools + runTools() tool-result loop, output_config.format JSON schema with prompt fallback, refusal handling). MockAIProvider for keyless dev/test.
  • app/Providers/AI/prompts/*.vN.md — versioned templates loaded by PromptLoader with {{var}} substitution. Bump the version suffix instead of editing a template in place.
  • app/Providers/Image/ImageProvider, MockImageProvider, Presets (ig_post, ig_story, facebook, tiktok, yt_thumb, discord_banner, twitch_panel, kick, x_header).
  • app/Modules/AI/Services/AIService.php — bound as $app->make('ai'). Methods: complete, structured, generateContent, adaptPost(tenantId, body, providers), contentPlan, summarizeCreator, serverSetup, graphicsBrief, renderGraphic, copilot.
  • app/Modules/AI/Services/CopilotTools.php — tool definitions and executors (all reads via TenantScope; writes are drafts and audited).

Routes (auth + tenant + role:staff; POSTs throttled throttle:ai)

RoutePurpose
GET/POST /dashboard/copilot, POST /dashboard/copilot/resetChat copilot; conversation in session per tenant (last 20 turns). JSON in/out.
GET/POST /dashboard/ai/content, POST /dashboard/ai/content/sendContent studio (captions, hashtags, titles, descriptions, CTAs, thumbnail text, short-form). ?plan=1 renders a 7-day plan. "→ Publisher" creates a draft post.
GET/POST /dashboard/ai/graphics, GET /dashboard/ai/galleryPreset-size graphics saved to storage + media_assets.
GET /dashboard/onboarding-wizard, POST /dashboard/onboarding-wizard/{1-5}Creator type → accounts → AI summary (Apply buttons) → community/server → "Launch your community" campaign drafts.
GET/POST /dashboard/ai/server-setup, POST /dashboard/ai/server-setup/applyPlain-language description → launch kit (VIP product inactive, posts draft, automation disabled, graphic, FAQ/rules proposals).
GET /dashboard/ai/usageMetering.

Copilot tools

get_overview, list_products, analyze_sales (read-only) · create_draft_post, create_draft_product, generate_graphic, create_automation_draft (draft-only, staff role required, audited as ai.*). The system prompt (copilot.system.v1.md) instructs the model to refuse publish/price/refund requests and point at the dashboard.

Adding a real image provider

Implement ImageProvider::generate(prompt, width, height, opts) returning ['bytes','mime','width','height'], return it from AIService::imageProvider() based on config('providers.image.driver'), and add the credentials to .env.example.