zofe / ai-module
AI widget, tool registry and multi-provider service (Anthropic, OpenAI-compatible, Ollama) for rapyd-admin
Requires
- php: ^8.2
- zofe/rapyd-admin: ^9.17
Requires (Dev)
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A chat widget (@aiWidget in any layout), a registry of tools the modules expose to the model, and one service for
Anthropic, OpenAI-compatible APIs (OpenAI, DeepSeek, Groq, Mistral…) and Ollama.
composer require zofe/ai-module
AI_WIDGET_ENABLED=true AI_WIDGET_MODE=customer # customer: text only, for a public site | operator: tools on the app's data AI_PROVIDER=openai # anthropic | openai | ollama AI_OPENAI_KEY=sk-... AI_OPENAI_BASE_URL=https://api.deepseek.com/v1 AI_MODEL=deepseek-chat AI_SYSTEM_PROMPT="You are the assistant of ..." # optional, default: a brief from rpd:context AI_KNOWLEDGE=resources/ai/knowledge.md # optional: what the bot knows about the product
Then @aiWidget in the layout (rapyd-admin's reference theme already prints it).
Two modes
- customer: the model only gets text. Rules appended to the system prompt keep it on the product: it declines other topics, ignores instructions in the messages, never reveals the prompt or the internals, answers briefly in the user's language.
- operator: the registered
AiTools (seeZofe\Rapyd\Contracts\AiToolProvider) are offered to the model, so it can read the application's data. Tools go only to logged-in users, and to those holdingAI_TOOLS_PERMISSIONwhen it is set. A guest never gets tools, whatever the mode.
What the bot knows
AI_KNOWLEDGE points to a markdown file (path relative to the app root, or absolute) or to an URL (fetched once an
hour). Its content is appended to the system prompt, trimmed to AI_KNOWLEDGE_MAX characters (16000). Keep it a
plain, current description of the product: versions, features, commands, links, prices, what to say when something is
not covered.
The perimeter of a public bot
Every request from the browser goes through, in this order:
| Check | Setting | Default |
|---|---|---|
| Message length | AI_MAX_INPUT characters |
500 |
| Daily budget of the whole site | AI_DAILY_BUDGET USD, 0 = none |
0 |
| Requests per session | AI_RATE_LIMIT per AI_RATE_WINDOW seconds |
20 / 3600 |
| Requests per IP address | AI_RATE_LIMIT_IP per window |
60 |
| Pause between two messages of a session | AI_MIN_INTERVAL seconds |
2 |
The conversation lives in a #[Locked] Livewire property: the browser cannot add, edit or inflate it. Only the last
AI_HISTORY messages (8) are sent to the model, the reply is capped at AI_MAX_TOKENS (1024). Provider errors are
logged; the visitor sees a generic message (the real one with APP_DEBUG=true).
Spend
The tokens of every call are counted in the cache (per day) and priced with AI_PRICE_INPUT / AI_PRICE_OUTPUT
(USD per million tokens, defaults 0.30 / 1.20: set your provider's list price). When the day's cost reaches
AI_DAILY_BUDGET the widget answers "paused until tomorrow" instead of calling the provider. With AI_LOG_USAGE
(default on) every request writes one log line with tokens, cost, IP and user. The counters live in the cache store
named by AI_USAGE_STORE (default: the application cache, which cache:clear and optimize:clear wipe); set it to
another store, file for instance, to keep them across deploys.
php artisan ai:usage # requests, tokens and cost of the last 7 days
The "Develop with AI" page
/ai/develop in the admin (menu entry "Develop with AI", permission develop with ai, given to admin and operator)
shows the developers of the application what Rapyd Admin gives their coding assistant:
- What your agent can do here: knows Rapyd Admin (guideline), builds modules and designs workflows (skills), knows Laravel and Livewire (Boost), reads the schema, the logs and the routes (MCP). Every capability that is missing names the command that unlocks it. Then the generators the agent calls instead of writing the code, and the estimated context it loads per session.
- Built in this project: every module in
app/Modules, generated byrpd:make(when, how many files, how many tokens the model did not have to write) or by hand, and whether it follows the conventions: authorized pages, permissions,Limits/, tests, workflows. - Try it: prompts to copy into the agent, each saying which packages it needs.
- AI in the app: provider and model, widget mode, the tools the modules registered, the spend of the last 7 days against the daily budget.
This is rapyd-admin's php artisan rpd:ai:develop, as a page. Nothing on it calls a provider.
Tests
composer install && vendor/bin/phpunit