richbrains/ai-textarea
AI-powered textarea component for Laravel that adds inline text generation, rewriting, and improvement to forms via a configurable AI provider, so users can draft and refine content without leaving the field.
This is a v0.1.0 initial release, so treat the source as the primary documentation. The package ships three things: a form field type (AITextareaType), a modal UI for prompting/generating, and an OpenAI-compatible HTTP backend.
composer require <vendor>/<package> # check package name from the release/registry
php artisan vendor:publish --provider="Vendor\Package\PackageServiceProvider"
Use php artisan vendor:publish with no arguments to list available tags — v0.1.0 will not have stable tag names documented yet. Expect a config file (endpoint, key, model, timeouts) and possibly publishable Blade views for the modal.
AI_TEXTAREA_BASE_URL=https://api.openai.com/v1
AI_TEXTAREA_API_KEY=sk-...
AI_TEXTAREA_MODEL=gpt-4o-mini
Because it is "OpenAI-compatible," the same config should point at Ollama (http://localhost:11434/v1), LM Studio, vLLM, OpenRouter, or a gateway/proxy. Verify the exact key names in the published config before wiring env vars.
use Vendor\Package\Forms\Components\AITextareaType;
AITextareaType::make('description')
->label('Description')
->rows(6),
Sanity-check the round trip before building on it: generate once against your configured endpoint, confirm the text populates the field, then save the record. If that works, everything else is configuration.
Treat the backend as an interface, not OpenAI specifically. Keep base_url and model in config and swap per environment: real provider in production, a local Ollama model in dev and CI so tests and local work cost nothing and need no key.
Context-aware prompts. The highest-value pattern is seeding the prompt from the current record rather than a blank modal. Pass existing field values, the model label, or sibling fields (category, tags, target audience) into the prompt so output is consistent with the rest of the record. Check whether the field exposes a callback/closures API for prompt building; if not, subclass or wrap the component.
Compose with other fields instead of replacing them. Keep the AI textarea as the same column the user edits manually — the modal is an accelerator, not a separate pipeline. This avoids two sources of truth and keeps validation, dehydrated(), and ->required() behaving normally.
Reuse the generator outside forms. If the package exposes a service/action class behind the field, call it from an artisan command, a queued job, or a Filament table bulk action (e.g. backfill missing descriptions). If only the form component is public, wrap the HTTP layer yourself rather than driving a Livewire component from a job.
Batch/backfill workflow: generate → store as draft → human review. Do not auto-publish generated text on save; add a generated_at/needs_review flag so you can report on and audit AI-authored content.
Testing. Use Http::fake() with a canned OpenAI-style chat completion response in feature tests, and assert the field dehydrates the faked content. Add one test that asserts a failed API call leaves the user's typed input intact. Keep a single real-endpoint smoke test behind an env flag.
Integration tip: pair with a policy/permission check so only certain roles see the generate button — it is a spend surface.
"vendor/pkg": "0.1.*") and re-read the source on every bump. Expect config keys, the field namespace, and modal view paths to move.fetch to the provider.max_execution_time, the Livewire request timeout, or a proxy/gateway timeout (often 30–60s). Set an explicit HTTP timeout in config, and be aware the UI blocks during generation. Longer or multi-field generation belongs in a queued job with polling.$state) and not just written into the DOM via JS — otherwise it silently fails to save. Test with a validation error round trip; the generated text should survive the re-render.resources/views/vendor/... for override paths; if absent, this is your first upstream feature request.RateLimiter) and cap max_tokens in config; log token usage so a runaway prompt is visible before the invoice is.Http::fake() test to prove wiring, (2) Log::debug() or Telescope around the outgoing request for status/body, (3) confirm base_url includes /v1 and the model name is valid for that provider, (4) check the provider returns the expected choices[0].message.content shape — some "compatible" servers differ slightly, (5) re-test with a public provider to isolate endpoint vs. package.Given the 0.1.0 maturity, budget time for reading the source and pin the version; the package is best adopted today as a single, well-scoped "generate draft" affordance with human review, not as a broad content-automation layer.
How can I help you explore Laravel packages today?