# PhoenixKitAI
AI module for PhoenixKit — provides endpoint management, prompt templates, completions via OpenRouter, and usage tracking.
## Features
- **Endpoint Management** — Create and manage AI endpoint configurations (provider credentials, model selection, generation parameters)
- **Prompt Templates** — Reusable prompts with `{{Variable}}` substitution syntax
- **Completions API** — Simple single-turn (`ask/3`), multi-turn (`complete/3`), and embeddings (`embed/3`)
- **Usage Tracking** — Every API call logged with tokens, cost (nanodollars), latency, and status
- **Admin UI** — 5 LiveView pages: Endpoints, Endpoint Form, Prompts, Prompt Form, Playground
- **Real-time Updates** — PubSub broadcasts for endpoint/prompt/request changes
## Quick start
Add to your parent app's `mix.exs`:
```elixir
{:phoenix_kit_ai, path: "../phoenix_kit_ai"}
```
Run `mix deps.get` and start the server. The module appears in:
- **Admin sidebar** (under Modules section) — AI with subtabs for Endpoints, Prompts, Playground, Usage
- **Admin > Modules** — toggle it on/off
- **Admin > Roles** — grant/revoke access per role
## Installation
### Local development
```elixir
{:phoenix_kit_ai, path: "../phoenix_kit_ai"}
```
### Git dependency
```elixir
{:phoenix_kit_ai, git: "https://github.com/mdon/phoenix_kit_ai.git"}
```
### Hex package
```elixir
{:phoenix_kit_ai, "~> 0.1.0"}
```
## Usage
```elixir
# Enable the module
PhoenixKitAI.enable_system()
# Create an endpoint
{:ok, endpoint} = PhoenixKitAI.create_endpoint(%{
name: "Claude Fast",
provider: "openrouter",
api_key: "sk-or-v1-...",
model: "anthropic/claude-3-haiku",
temperature: 0.7
})
# Simple completion
{:ok, response} = PhoenixKitAI.ask(endpoint.uuid, "Hello!")
{:ok, text} = PhoenixKitAI.extract_content(response)
# Multi-turn conversation
messages = [
%{role: "system", content: "You are a helpful assistant."},
%{role: "user", content: "What is Elixir?"}
]
{:ok, response} = PhoenixKitAI.complete(endpoint.uuid, messages)
# Prompt templates
{:ok, prompt} = PhoenixKitAI.create_prompt(%{
name: "Translator",
content: "Translate to {{Language}}: {{Text}}"
})
{:ok, response} = PhoenixKitAI.ask_with_prompt(
endpoint.uuid,
prompt.uuid,
%{"Language" => "French", "Text" => "Hello!"}
)
# Embeddings
{:ok, response} = PhoenixKitAI.embed(endpoint.uuid, "Hello world")
```
## Project structure
```
lib/
phoenix_kit_ai.ex # Main module (behaviour + context)
phoenix_kit_ai/
endpoint.ex # Endpoint schema
prompt.ex # Prompt template schema
request.ex # Request logging schema
completion.ex # OpenRouter HTTP client
openrouter_client.ex # API key validation & model discovery
ai_model.ex # Normalized model struct
routes.ex # Admin sub-routes (new/edit forms)
migrations/
v1.ex # Consolidated migration (IF NOT EXISTS)
web/
endpoints.ex/.heex # Endpoints list + usage page
endpoint_form.ex/.heex # Create/edit endpoint
prompts.ex/.heex # Prompts list
prompt_form.ex/.heex # Create/edit prompt
playground.ex/.heex # Interactive testing
test/
phoenix_kit_ai_test.exs # Behaviour compliance tests
phoenix_kit_ai/
prompt_test.exs # Prompt template tests
```
## Database tables
All tables use UUIDv7 primary keys and timestamptz columns.
- **`phoenix_kit_ai_endpoints`** — Endpoint configurations (28 columns)
- **`phoenix_kit_ai_prompts`** — Prompt templates (14 columns)
- **`phoenix_kit_ai_requests`** — Request logs (18 columns, FK to endpoints/prompts/users)
The consolidated migration (`PhoenixKitAI.Migrations.V1`) uses `IF NOT EXISTS` throughout, so it's safe to run even if tables already exist from PhoenixKit core migrations.
## Admin pages
| Page | Path | Description |
|------|------|-------------|
| Endpoints | `/admin/ai/endpoints` | List, create, edit, delete, validate endpoints |
| Endpoint Form | `/admin/ai/endpoints/new`, `.../edit` | Create/edit endpoint with model selection |
| Prompts | `/admin/ai/prompts` | List, create, edit, delete, reorder prompts |
| Prompt Form | `/admin/ai/prompts/new`, `.../edit` | Create/edit with variable extraction |
| Playground | `/admin/ai/playground` | Interactive testing with live variable substitution |
| Usage | `/admin/ai/usage` | Dashboard stats and request history |
## Supported providers
Currently supports **OpenRouter** (100+ models from Anthropic, OpenAI, Google, Meta, Mistral, and more).
## Callbacks implemented
| Callback | Value |
|----------|-------|
| `module_key/0` | `"ai"` |
| `module_name/0` | `"AI"` |
| `enabled?/0` | DB-backed boolean with rescue fallback |
| `enable_system/0` | Persists via Settings API |
| `disable_system/0` | Persists via Settings API |
| `version/0` | `"0.1.0"` |
| `permission_metadata/0` | key: `"ai"`, icon: `hero-sparkles` |
| `admin_tabs/0` | 5 tabs (parent + 4 subtabs) |
| `css_sources/0` | `[:phoenix_kit_ai]` |
| `route_module/0` | `PhoenixKitAI.Routes` |
| `get_config/0` | Endpoint count, request count, token totals |
## Development
```bash
mix deps.get # Install dependencies
mix test # Run all tests
mix format # Format code
mix credo --strict # Linting
mix dialyzer # Type checking
mix quality # Format + Credo + Dialyzer
```
## Testing
```bash
# Unit tests (always work, no DB needed)
mix test
# Integration tests (need PostgreSQL)
createdb phoenix_kit_ai_test
mix test
```