Skip to content

AI Writer ​

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.

This is the site asking a model with its own key. Connecting your own agent — Claude, Cursor — to run the site is the other way, AI Agents; the Assistant is a chat in the admin that answers with this key too; Three Ways to Use AI compares them.

PackageGives
kitloom/ai-writerThe model behind TextGenerator, the settings, the usage log; on laravel/ai
kitloom/ai-writer-filament / kitloom/ai-writer-novaThe AI Writer section: Usage, Settings, How it works
bash
composer require kitloom/ai-writer kitloom/ai-writer-filament   # or kitloom/ai-writer-nova
php artisan migrate

Video lessons ​

Recorded in the Filament admin; the Nova admin works the same way. The second shows AI Writer at work: the Languages module asks it for a French translation.

Set up AI Writer
Translate with AI (Languages)

Setting it up ​

  1. Get an API key from the provider — for Anthropic (Claude) at console.anthropic.com → API keys — and set a spending limit there too: the budget here is a second fence, not the only one.
  2. AI Writer → Settings:
    • Provider — Anthropic (Claude), OpenAI, Google Gemini, Mistral, DeepSeek, xAI or OpenRouter;
    • API key — encrypted with the application key before it is saved, never shown again; leave the field empty to keep the saved one;
    • Model — empty is the provider's cheapest text model (Claude Haiku 5.5 for Anthropic), plenty for translations and descriptions; or name another;
    • Monthly budget — US dollars; empty is no limit.
  3. Test the key — Filament on Settings, Nova on Usage — asks the model one short question and shows the answer, or the provider's error.

Until a key is saved on the page, the provider, key and model from the server's .env are used (KITLOOM_AI_WRITER_PROVIDER, KITLOOM_AI_WRITER_KEY, KITLOOM_AI_WRITER_MODEL); while the page's budget is empty, KITLOOM_AI_WRITER_MONTHLY_BUDGET is. If the application key changes, a saved key no longer decrypts and has to be entered again.

What it costs ​

AI Writer → Usage lists every question: when, what for (languages.translate, ai-writer.test), who asked, the model, the tokens in and out, and the cost; a question that failed is there too, with the provider's error. Above the list: what the month has cost against the budget.

  • The cost is counted from kitloom-ai-writer.prices, US dollars per million tokens, by model — for every provider on the list: Claude Haiku 5.5 ($0.10 in / $0.50 out), Sonnet 5.5 ($2 / $10), Opus 5.5 ($4 / $20) and Fable 5.1, and the common models of OpenAI, Gemini, Mistral, DeepSeek, xAI and OpenRouter. Where a provider bills a long prompt at another rate, the model has a long price too, used above its number of tokens.
  • A model without a price, while no budget is set, is logged by its tokens only, with no cost. While a budget is set it is not asked at all — its questions could not be counted — so the buttons disappear. The settings page says so either way: add its price to the config, choose a priced model, or clear the budget.
  • The month starts at the site's midnight on the first (Settings → General → Timezone). Once its questions cost the budget, the modules get no more answers and their buttons disappear until the next month, or until you raise the budget.
  • It is an estimate from the tokens the provider reports; the provider's invoice is what you pay.

For scale: a 1,000-word post is about 1,500 tokens, so translating one with Claude Haiku 5.5 — the post in, its translation out — costs well under a cent.

Who uses it ​

Modules that write text ask AI Writer through a contract and draw their "with AI" button only while a key is set and the budget not spent:

ModuleWhat it writes
LanguagesTranslate with AI: a post's or term's translation, saved as a draft; a bulk action for many

More modules will ask it as they get their buttons; each says so on its own page.

A module checks its own permissions: an editor who may translate a post uses the button without seeing the key. Who sees AI Writer itself is one permission, Manage AI Writer (manage_ai_writer): the key and the budget are your money. Administrators have it.

Where things are kept ​

WhatWhere
Usagetable ai_writer_usage (the site's table prefix in front)
Settingsoptions ai_writer_provider, ai_writer_key (encrypted), ai_writer_model, ai_writer_monthly_budget
Configurationconfig/kitloom-ai-writer.php (php artisan vendor:publish --tag=kitloom-ai-writer-config)
KeyDefaultMeaning
provider, key, model, monthly_budgetanthropic, —, —, —Used while nothing is saved on the settings page; from KITLOOM_AI_WRITER_PROVIDER, KITLOOM_AI_WRITER_KEY, KITLOOM_AI_WRITER_MODEL, KITLOOM_AI_WRITER_MONTHLY_BUDGET
providersseven providersWhat the page offers: laravel/ai driver => label
pricesmodels of every providerUS dollars per million input and output tokens, by model; long — the rate above a number of prompt tokens, where the provider bills one
timeout, max_tokens120, 16000Seconds to wait for one answer, the most tokens it may have
filament.register, nova.registertrueThe admin sections
privacy.registertrueA user's questions in WordPress's Export and Erase Personal Data (Privacy): the export lists them, the erasure takes the user off them and keeps them for the budget

For module developers ​

A module never requires AI Writer or a provider SDK: it asks Kitloom\WpSchema\Contracts\TextGenerator, which every site has — without AI Writer nothing is available.

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) {
        // show $e->getMessage() to the person who pressed the button; nothing was saved
    }
}
  • The instructions and the text go apart — the system prompt and the 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.
  • Save the answer for a person to look at — a draft, a field to confirm — not straight onto the live site.
  • In tests, fake the provider: Kitloom\AiWriter\Prompting\TextAgent::fake(['Bonjour']), or StructuredTextAgent::fake([[…]]) for a schema.

Next ​

kitloom — modules and themes for Laravel.
Pricing · Services · License · Refunds · Privacy · Community · [email protected]