Aludel can share a host Phoenix application's repository and authentication boundary, or run as a standalone dashboard.
Requirements
- Elixir 1.17 or later
- Phoenix 1.8
- PostgreSQL 12 or later
- ImageMagick when PDF-to-image conversion is required
Aludel relies on PostgreSQL features including JSONB, percentile_disc(), and DATE() aggregations. SQLite and MySQL are not supported.
Install in a Phoenix application
Add the dependency:
def deps do
[
{:aludel, "~> 0.6.1"}
]
endConfigure the host repository:
config :aludel, repo: MyApp.RepoCopy and run Aludel's migrations:
mix aludel.install
mix ecto.migrate
The installer skips migration names already present in the host application, so it can be rerun after upgrading.
Mount the dashboard:
use MyAppWeb, :router
import Aludel.Web.Router
scope "/admin" do
pipe_through [:browser, :require_authenticated_user]
aludel_dashboard "/aludel"
endRouter options
aludel_dashboard/2 accepts:
| Option | Default | Purpose |
|---|---|---|
:as | :aludel_dashboard | Live session and route helper name |
:aludel_name | Aludel | Instance label |
:resolver | Aludel.Web.Resolver | User, access, and refresh policy |
:on_mount | [] | Additional LiveView mount hooks run before Aludel authentication |
:socket_path | "/live" | Host LiveView socket path |
:transport | "websocket" | "websocket" or "longpoll" |
:logo_path | nil | Link target for the dashboard logo |
:csp_nonce_assign_key | nil | One assign key or a map of :img, :style, and :script nonce keys |
Example with a custom socket and CSP assignments:
aludel_dashboard "/aludel",
as: :llm_workbench,
aludel_name: MyApp,
socket_path: "/live",
transport: "websocket",
logo_path: "/admin",
csp_nonce_assign_key: %{
img: :img_nonce,
style: :style_nonce,
script: :script_nonce
}Access resolver
Implement Aludel.Web.Resolver to connect the dashboard to the host user and authorization model:
defmodule MyApp.AludelResolver do
@behaviour Aludel.Web.Resolver
@impl true
def resolve_user(conn) do
conn.assigns[:current_user]
end
@impl true
def resolve_access(%{role: :admin}) do
:all
end
def resolve_access(_user) do
:read_only
end
@impl true
def resolve_refresh(_user) do
5
end
endMount it with resolver: MyApp.AludelResolver. Full access enables mutations; read-only access keeps inspection workflows available. The refresh value is the polling interval in seconds used by result views.
Use the host router pipeline and :on_mount hooks for authentication. The resolver determines what an already authenticated user can do inside Aludel.
Provider credentials
Configure only the providers you use:
config :aludel, :llm,
openai_api_key: System.get_env("OPENAI_API_KEY"),
anthropic_api_key: System.get_env("ANTHROPIC_API_KEY"),
google_api_key: System.get_env("GOOGLE_API_KEY"),
xai_api_key: System.get_env("XAI_API_KEY"),
groq_api_key: System.get_env("GROQ_API_KEY"),
openrouter_api_key: System.get_env("OPENROUTER_API_KEY")Ollama does not require a key. Provider keys are read at runtime and are not persisted in provider records.
Run concurrency
Native multi-provider runs are concurrent by default:
config :aludel,
run_execution_mode: :concurrent
config :aludel, :llm,
max_concurrency: 5,
request_timeout_ms: 120_000The defaults are three concurrent calls and a 120-second request timeout. Set run_execution_mode: :sequential when provider calls must not overlap.
Host-app callback execution
Callback mode evaluates the real host workflow instead of calling a provider directly:
config :aludel,
execution_mode: :callback,
executor: MyApp.AludelExecutordefmodule MyApp.AludelExecutor do
@behaviour Aludel.Executor
@impl true
def run(input) do
case MyApp.AI.reply(%{
question: input.variables["question"],
messages: input.messages,
documents: input.documents,
model: input.provider && input.provider.model,
trace_context: input.metadata
}) do
{:ok, reply} ->
{:ok,
%{
output: reply.text,
input_tokens: reply.input_tokens,
output_tokens: reply.output_tokens,
latency_ms: reply.latency_ms,
cost_usd: reply.cost_usd,
metadata: %{trace_id: reply.trace_id}
}}
{:error, reason} ->
{:error, reason}
end
end
endOnly output is required on success. Omit token, latency, cost, or metadata fields when the host workflow does not provide them. The UI renders absent metrics as N/A.
Document storage
Local development configuration:
config :aludel, Aludel.Storage,
adapter: Aludel.Interfaces.Storage.Adapters.Local,
backends: [{Aludel.Interfaces.Storage.Adapters.Local, root: "tmp/aludel_uploads"}]Standalone production selects AWS S3 or Google Cloud Storage through environment variables:
export ALUDEL_STORAGE_BACKEND=aws
export AWS_S3_BUCKET=aludel-uploads
export AWS_REGION=us-east-1
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export ALUDEL_STORAGE_BACKEND=gcs
export GCS_BUCKET=aludel-uploads
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
export GCS_USER_PROJECT=optional-requester-pays-project
Inline GOOGLE_APPLICATION_CREDENTIALS_JSON is also supported. Applications can implement Aludel.Interfaces.Storage.Behaviour when documents must use another storage system.
PDF conversion
Configure the included ImageMagick adapter when a provider needs page images instead of a native PDF:
config :aludel, :document_converter,
adapter: Aludel.Interfaces.DocumentConverter.Adapters.ImagemagickThe convert executable must be available to the running application. A custom adapter can implement Aludel.Interfaces.DocumentConverter.Behaviour.
Standalone application
Run the repository's standalone app:
git clone https://github.com/ccarvalho-eng/aludel.git
cd aludel/standalone
mix deps.get
mix ecto.create
mix ecto.migrate
mix phx.server
Optional standalone access controls:
export BASIC_AUTH_USER=admin
export BASIC_AUTH_PASS=change-me
export READ_ONLY=true
Set both Basic Authentication values to enable the challenge. READ_ONLY=true keeps the dashboard visible while disabling mutations.
Docker Compose
From the repository root, configure .env from .env.example, then start the database and release:
docker compose up -d
The web container waits for PostgreSQL, runs all migrations, and starts the standalone dashboard on the configured port.
Demo catalog
For development and product exploration:
mix aludel.seed
The seed task is disabled in production. It builds a deterministic catalog covering all providers and major workflows, including datasets, suite history, artifacts, failures, and 60 days of analytics.