Embedding and Deployment Guide

Copy Markdown View Source

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"}
  ]
end

Configure the host repository:

config :aludel, repo: MyApp.Repo

Copy 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"
end

Router options

aludel_dashboard/2 accepts:

OptionDefaultPurpose
:as:aludel_dashboardLive session and route helper name
:aludel_nameAludelInstance label
:resolverAludel.Web.ResolverUser, 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_pathnilLink target for the dashboard logo
:csp_nonce_assign_keynilOne 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
end

Mount 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_000

The 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.AludelExecutor
defmodule 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
end

Only 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.Imagemagick

The 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.