Appearance
AI Writer
Your own AI key for translations and other texts the modules write.
kitloom/ai-writer · admins: kitloom/ai-writer-filament, kitloom/ai-writer-nova
Video lessons
Recorded in the Filament admin; the Nova admin works the same way.
Your own AI model for the texts the site's modules write for you: a translation of a post, a meta description, an image's alt text. You choose the provider, paste its API key and set how much a month may be spent; the modules ask, the provider bills you for what they ask, and every question is written down with what it cost.
The admin is kitloom/ai-writer-filament or kitloom/ai-writer-nova: the AI Writer section with Usage, Settings and this page.
This is the site asking a model with its own key. Connecting your own AI agent (Claude, ChatGPT, Cursor) to the site is a different thing: that is kitloom/ai, which needs no key here and costs nothing on the site's side.
Setting it up
- Get an API key from the provider: for Anthropic (Claude) at console.anthropic.com → API keys. Keep its billing limit there too — the budget here is a second fence, not the only one.
- AI Writer → Settings: pick the Provider, paste the API key, leave Model empty for the provider's cheapest text model (Claude Haiku for Anthropic, plenty for translations and descriptions) or name another, and set a Monthly budget.
- Press Test the key (on Settings in Filament, on Usage in Nova): the model is asked one short question, and the answer — or the provider's error — is shown.
The key is encrypted with the application key before it is saved and never shown again; leave the field empty to keep it. If the application key changes, the saved key no longer decrypts and has to be entered again. Until a key is saved on the page, the provider, key and model from the server's .env are used (see Configuration).
What it costs, and the budget
- Every question is a row of Usage: when, what for (
languages.translate,seo.description,ai-writer.test), who asked, the model, the tokens in and out, and the cost in US dollars. A question that failed is there too, with the provider's error. - The cost is counted from the prices in
kitloom-ai-writer.prices(US dollars per million tokens): every provider's cheapest text model is there, and the common others — Claude Haiku, Sonnet, Opus and Fable; OpenAI's GPT-6 and smaller GPT-5 models; Gemini; Mistral; DeepSeek; Grok; the OpenRouter defaults. A provider that bills a long prompt at a higher rate (Claude Haiku past 100K tokens, OpenAI past 272K) is counted at that rate. Where a price is higher at some hours or from a date already announced, the higher one is listed, so the budget stops early rather than late. - The Monthly budget counts from the first of the month at the site's midnight (Settings → General → Timezone). Once the month's questions cost that much, the modules get no more answers — their buttons disappear — until the next month, or until you raise the budget. Empty — no limit.
- A model whose price is not listed cannot be counted. While a budget is set it is not asked at all — its buttons disappear and "Test the key" says why — since the budget could never stop it. Without a budget it is asked and logged by its tokens, its cost shown as Price unknown. Either way the settings page and Usage say so: add its price to
kitloom-ai-writer.prices(published config), name a model that has one, or clear the budget. - The figure is an estimate from the tokens the provider reports; the provider's own invoice is what you pay.
Who sees it
One permission, Manage AI Writer (manage_ai_writer), covers Usage and Settings: the key and the budget are your money. Administrators have it. Modules that ask the model check their own permissions — an editor a module lets use its "with AI" button uses it without seeing the key.
AI agents
With kitloom/ai installed, an agent whose user has manage_ai_writer reads what Usage sums (ai_writer_usage): this month's spending against the budget, the questions of unknown price, the sums by purpose and model, and the latest failures with the provider's error. Not the key, and not who asked. It changes nothing: the provider, the model and the budget are settings a person changes. kitloom-ai-writer.ai.register false — no such tool.
Personal data
With kitloom/privacy, Tools → Export Personal Data lists the questions the user the address belongs to asked: when, what for, the provider and model, the cost and an error if there was one. What was asked and answered is not kept, so it is not in the archive either. Erase Personal Data takes the user off their questions (user_id empty) and keeps the rows: the month's budget and Usage are their sums, and deleting them would hand the money already spent back to the budget. Usage shows those questions without a name.
Where things are kept
| What | Where |
|---|---|
| Usage | table ai_writer_usage (the site's table prefix in front), model Kitloom\AiWriter\Models\Usage |
| Settings | options ai_writer_provider, ai_writer_key (encrypted), ai_writer_model, ai_writer_monthly_budget |
| Configuration | config/kitloom-ai-writer.php |
Configuration
config/kitloom-ai-writer.php of the package, merged into the application (php artisan vendor:publish --tag=kitloom-ai-writer-config).
| Key | Environment | Default | What |
|---|---|---|---|
provider, key, model | KITLOOM_AI_WRITER_PROVIDER, KITLOOM_AI_WRITER_KEY, KITLOOM_AI_WRITER_MODEL | anthropic, —, — | Used together while no key is saved on the settings page |
monthly_budget | KITLOOM_AI_WRITER_MONTHLY_BUDGET | — | US dollars, while none is saved on the page |
budget_ceiling | KITLOOM_AI_WRITER_BUDGET_CEILING | — | US dollars a month no budget saved on the page goes past, nor an empty one — for a site whose administrators are not the ones who pay, such as a public demo; the settings page says so |
providers | — | Anthropic, OpenAI, Gemini, Mistral, DeepSeek, xAI, OpenRouter | What the page offers: laravel/ai driver => label |
prices | — | The listed providers' models | US dollars per million input and output tokens, by model; long — the rate past above prompt tokens. A published config replaces the whole list |
timeout, max_tokens | — | 120, 16000 | Seconds to wait for one answer, and the most tokens it may have |
filament.register, nova.register | — | true | Whether the admin section is added |
privacy.register | — | true | Whether the personal data export and erasure are tagged for kitloom/privacy |
For developers
The module binds Kitloom\WpSchema\Contracts\TextGenerator. A module that writes text asks the contract, never this package, so it works — without its buttons — on a site that does not install AI Writer:
php
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Kitloom\WpSchema\Contracts\TextGenerator;
use Kitloom\WpSchema\Text\TextGenerationFailed;
$writer = app(TextGenerator::class);
if ($writer->available()) { // a key is set and the budget not spent: draw the button
try {
$fields = $writer->generate(
'Translate the fields into French (Canada). Keep the HTML.',
json_encode(['title' => $post->title, 'content' => $post->content]),
fn (JsonSchema $schema): array => ['title' => $schema->string()->required(), 'content' => $schema->string()->required()],
purpose: 'languages.translate',
actor: $user,
);
} catch (TextGenerationFailed $e) {
// say $e->getMessage() to the person who pressed the button; nothing was saved
}
}- The instructions and the text go apart (system prompt and user message), so a text that says "ignore the above" is only text.
- Without a schema the answer is a string; with one, an array of those fields.
- The provider is built for each question from what is saved now, never from
config/ai.php; laravel/ai's own config is not needed. - In tests, fake the provider:
Kitloom\AiWriter\Prompting\TextAgent::fake(['Bonjour'])(StructuredTextAgent::fake([[...]])for a schema).
What the application overrides
- The model:
ModelRegistry::use(Usage::KEY, App\Models\AiUsage::class)in a provider of the application; the package's own is used only when the key is free, and the admins read the class from the registry. - The writer itself: bind your own
TextGeneratorafter the package's provider. - The admin sections:
kitloom-ai-writer.filament.registerandkitloom-ai-writer.nova.registerturn them off.
Tests
From the application that installs the package: php vendor/bin/phpunit --testsuite AiWriter.