layered-assistant-rails (v0.5.0)
An open source Rails 8+ engine built on `layered-ui-rails` that provides a multi-provider AI assistant with streaming responses and a full conversation UI.
Features
Multi-provider and model support
Use cloud (Anthropic, OpenAI, Gemini, Mistral, Groq, OpenRouter) or local (Ollama or LM Studio) providers with configurable models.
Streaming responses
Real-time streaming via Turbo Streams for a responsive chat experience with dynamic token fade-in and chunked markdown formatting.
Conversation UI
Full conversation interface with message history, model selection, and a slide-out panel mode.
Encrypted secrets
API keys are encrypted at rest using Active Record Encryption.
Requirements
- Ruby on Rails >= 8.0
layered-ui-railsgem
Agent skill
An agent skill is included so AI coding agents can work with layered-assistant-rails in your project. Once installed, the agent can handle the full setup - just ask it to add layered-assistant-rails to your app and it will install the gem, run the generator, and configure your layout.
Project install - scoped to a single repo, available to all contributors:
bin/rails generate layered:assistant:install_agent_skill
Global install - available across all your projects:
./install-skill.sh
# or install remotely without cloning the repo:
curl -fsSL https://raw.githubusercontent.com/layered-ai-public/layered-assistant-rails/main/install-skill.sh | sh
Installation
Add to your Gemfile:
gem "layered-assistant-rails"
Then run:
bundle install
Setup
Run the install generator to copy CSS, JS, and migrations to your application:
bin/rails generate layered:assistant:install
This will:
- Copy
layered_ui.csstoapp/assets/tailwind/ - Add
@import "./layered_ui";to yourapplication.css - Add
import "layered_ui"to yourapplication.js - Copy
layered_assistant.csstoapp/assets/tailwind/layered_assistant.css - Add
@import "./layered_assistant";to yourapplication.css(after the layered-ui import) - Add
import "layered_assistant"to yourapplication.js(after the layered-ui import) - Mount the engine at
/layered/assistantin yourconfig/routes.rb - Copy engine migrations into your application
All steps are idempotent - re-running the generator will not duplicate imports, routes, or migrations.
Authorization
All non-public engine routes are blocked by default (403 Forbidden) until you configure an
authorize block in config/initializers/layered_assistant.rb.
The install generator creates this file for you. Uncomment one of the examples to get started.
The block runs in controller context, so you have access to
request, current_user, redirect_to,
head, main_app, and all other controller methods.
Allow all requests
Layered::Assistant.authorize do
# No-op: all requests permitted
end
Require sign-in (Devise)
Layered::Assistant.authorize do
redirect_to main_app.new_user_session_path unless user_signed_in?
end
Restrict to admins
Layered::Assistant.authorize do
head :forbidden unless current_user&.admin?
end
Checking access in views
The l_assistant_accessible? helper evaluates the authorize block
without side effects. Use it to conditionally show navigation or links to the engine:
<% if l_assistant_accessible? %>
<%= link_to "Assistant", layered_assistant.root_path %>
<% end %>
Record scoping
By default, all records are visible to any authorised user. If your application is
multi-tenant or you need to restrict which records a user can see, configure a
scope block in the initialiser.
The block receives the model class, runs in controller context, and must return an
ActiveRecord::Relation. The following models are passed through the scope block:
| Model | Description |
|---|---|
Layered::Assistant::Conversation |
User conversations (has polymorphic owner) |
Layered::Assistant::Assistant |
Assistant configurations (has polymorphic owner) |
Layered::Assistant::Provider |
API provider credentials (has polymorphic owner) |
Scope all owned resources to the current user
Layered::Assistant.scope do |model_class|
model_class.where(owner: current_user)
end
Scope conversations only
Layered::Assistant.scope do |model_class|
if model_class == Layered::Assistant::Conversation
model_class.where(owner: current_user)
else
model_class.all
end
end
When no scope block is configured, queries are unscoped. Record-level access control is the host application's responsibility; the scope block is the integration point for it.
Panel helpers
Two helpers are available to wire the layered-ui panel to the assistant engine.
Add these to your application layout's content_for blocks:
<% content_for :l_ui_panel_heading do %>
<%= layered_assistant_panel_header %>
<% end %>
<% content_for :l_ui_panel_body do %>
<%= layered_assistant_panel_body %>
<% end %>
<%= render template: "layouts/layered_ui/application" %>
Both helpers accept keyword arguments forwarded as HTML attributes:
<%= layered_assistant_panel_body data: { controller: "panel" } %>
| Helper | Description |
|---|---|
layered_assistant_panel_header |
Empty Turbo Frame populated by the engine's panel views |
layered_assistant_panel_body |
Turbo Frame that loads the conversation list from the engine's panel routes |
Configuration
Optional settings can be added to your initialiser (config/initializers/layered_assistant.rb):
| Option | Default | Description |
|---|---|---|
log_errors |
false |
Log API errors to stdout from the AI API clients |
api_request_timeout |
210 |
Timeout in seconds for AI API requests |
skip_db_encryption |
false |
Disable Active Record Encryption on Provider#secret. Only for development/test environments without encryption keys configured |
Layered::Assistant.log_errors = true
Layered::Assistant.skip_db_encryption = true
Getting started
- Create a provider (e.g. Anthropic or OpenAI) with your API key
- Add one or more models to the provider
- Create an assistant and assign a default model
- Start a conversation
License
Released under the Apache 2.0 License.
Copyright 2026 LAYERED AI LIMITED (UK company number: 17056830). See NOTICE for attribution details.
Trademarks
The source code is fully open, but the layered.ai name, logo, and brand assets are trademarks of LAYERED AI LIMITED. The Apache 2.0 license does not grant rights to use the layered.ai branding. Forks and redistributions must use a distinct name. See TRADEMARK.md for the full policy.
Contributing
- CLA.md - contributor licence agreement
Configuration
Optional settings for config/initializers/layered_assistant.rb:
| Option | Default | Description |
|---|---|---|
log_errors |
false |
Log API errors to stdout. |
api_request_timeout |
210 |
Total timeout in seconds for API requests, including the full streaming response. Increase for models with high max_tokens limits or slow providers. |
skip_db_encryption |
false |
Disable Active Record Encryption on Provider#secret. Development/test only. |