AI Provider Setup
All AI calls go through one service -- AiService. Point it at OpenAI or Anthropic and everything works: content generation, SEO analysis, accessibility checks, image comparison, the chat assistant.
Supported providers
| Provider | Key | Env prefix |
|---|---|---|
| OpenAI | openai |
OPENAI_ |
| Anthropic Claude | anthropic |
ANTHROPIC_ |
Both handle text, chat, vision, and tool calls.
Quick start
OpenAI
TEXTSTEM_AI_PROVIDER=openai
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o-mini
Anthropic
TEXTSTEM_AI_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
ANTHROPIC_MODEL=claude-haiku-4-5-20251001
TEXTSTEM_AI_PROVIDER decides which one runs. If you have keys for both, only the active provider is used.
Configuration
Active provider
TEXTSTEM_AI_PROVIDER=openai # 'openai' or 'anthropic' (default: openai)
TEXTSTEM_AI_MODEL= # blank = use the provider's default
TEXTSTEM_AI_MODEL overrides the model for everything. Leave it blank and it falls back to OPENAI_MODEL or ANTHROPIC_MODEL.
OpenAI
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o-mini
OPENAI_USE_QUEUE=true # default: true
Get a key at platform.openai.com. Models starting with gpt-, o1, or o3 are auto-routed to OpenAI if you don't set TEXTSTEM_AI_PROVIDER.
Anthropic
ANTHROPIC_API_KEY=sk-ant-...
ANTHROPIC_MODEL=claude-haiku-4-5-20251001
Get a key at console.anthropic.com. Models starting with claude- are auto-routed to Anthropic.
What uses AI
With a provider configured:
- Content generation -- article body, descriptions, teasers, tags
- Alt text -- generated when you upload an image
- Keywords -- pulled from page content
- SEO analysis -- scored and annotated (see
SEO_USE_AI) - Accessibility analysis -- WCAG 2.1 checks against the live page HTML
- Image duplicates -- vision comparison on upload (requires
textstemapp.images.comparison_driver=openai; defaults to fingerprint matching) - Chat assistant -- admin panel chat that can read and write content
Disable anything you don't need -- see the SEO and accessibility docs.
Queue
Jobs run through the queue by default. You need a worker running:
php artisan queue:work
To run synchronously (development only):
OPENAI_USE_QUEUE=false
ACCESSIBILITY_USE_QUEUE=false
SEO_USE_QUEUE=false
Usage tracking
Every call is logged to ai_token_usage -- provider, model, feature, token counts, cost. Find it in the admin panel under Tools > OpenAI Usage, or query it directly.
Custom providers
Implement LlmProvider and register it:
use Medialight\Textstem\Orchestration\Providers\LlmProviderRegistry;
$this->app->resolving(LlmProviderRegistry::class, function (LlmProviderRegistry $registry) {
$registry->register(new MyCustomProvider());
});
Set TEXTSTEM_AI_PROVIDER to whatever key() returns.