A reusable Rails engine for integrating OpenAI's ChatKit into your Rails applications. This engine provides both OpenAI-hosted and self-hosted ChatKit implementations with easy configuration and customization.
- OpenAI-hosted ChatKit: Seamless integration with OpenAI's hosted ChatKit service
- Self-hosted fallback: Optional self-hosted implementation for full control
- Flexible workflow resolution: Support for per-model workflow IDs
- Stimulus controllers: Pre-built controllers for common UI patterns
- Multi-tenancy support: Built-in support for account-based tenancy
- Thread & message persistence: Database-backed conversation storage
- File attachments: Support for file and image attachments
Add this line to your application's Gemfile:
gem 'chatkit', github: 'schappim/chatkit'Then run:
bundle installCopy the Stimulus controllers to your application:
# Copy the base controllers
mkdir -p app/javascript/controllers/chatkit
curl -o app/javascript/controllers/chatkit/chatkit_controller.js https://raw.githubusercontent.com/schappim/chatkit/main/app/javascript/controllers/chatkit_controller.js
curl -o app/javascript/controllers/chatkit/chatkit_sidebar_controller.js https://raw.githubusercontent.com/schappim/chatkit/main/app/javascript/controllers/chatkit_sidebar_controller.jsOr manually download from the app/javascript/controllers/ directory in this repository.
Add to your config/importmap.rb:
pin "chatkit/chatkit_controller", to: "chatkit/chatkit_controller.js"
pin "chatkit/chatkit_sidebar_controller", to: "chatkit/chatkit_sidebar_controller.js"rails generate chatkit:installThis will:
- Create database migrations
- Generate an initializer at
config/initializers/chatkit.rb - Display setup instructions
rails db:migrateAdd to your config/routes.rb:
mount Chatkit::Engine, at: "/chatkit"Add your OpenAI credentials to config/credentials.yml.enc:
rails credentials:editAdd:
openai:
api_key: your_api_key_here
chatkit_workflow_id: your_default_workflow_id_hereConfigure Chatkit in config/initializers/chatkit.rb:
Chatkit.configure do |config|
# OpenAI API key (auto-loaded from credentials if nil)
config.api_key = nil
# Default workflow ID (auto-loaded from credentials if nil)
config.workflow_id = nil
# Default AI model
config.model = 'gpt-5'
endUse the base ChatKit controller for a full-page interface:
<div data-controller="chatkit" data-chatkit-theme-value="light">
<!-- ChatKit interface will render here -->
</div>For a sidebar or overlay interface, extend the chatkit-sidebar controller:
import ChatkitSidebarController from "chatkit/chatkit_sidebar_controller"
export default class extends ChatkitSidebarController {
static values = {
...ChatkitSidebarController.values,
contextType: String,
contextId: String
}
connect() {
this.contextTypeValue = 'YourModel'
this.contextIdValue = this.yourModelIdValue
super.connect()
}
getStartScreenPrompts() {
return [
{
label: 'Custom prompt 1',
prompt: 'Ask something specific...'
}
]
}
}To use different workflows for different models, add a chatkit_workflow_id column:
rails g migration AddChatkitWorkflowIdToYourModel chatkit_workflow_id:string
rails db:migrateThen set the workflow ID on your model instances:
your_model.update(chatkit_workflow_id: 'workflow_xyz')Pass the context in your view:
<div data-controller="your-chatkit-controller"
data-your-chatkit-controller-context-type-value="YourModel"
data-your-chatkit-controller-context-id-value="<%= your_model.id %>">
</div>The engine provides these endpoints (mounted at /chatkit by default):
POST /chatkit/session- Create a new ChatKit sessionPOST /chatkit/refresh- Refresh an expired sessionPOST /chatkit/process- Self-hosted message processingGET /chatkit- Full-page ChatKit interface
- Models:
Chatkit::Thread,Chatkit::ThreadItem,Chatkit::Attachment - Services:
Chatkit::Server- Self-hosted message processingChatkit::AgentService- OpenAI API interactionsChatkit::Store- Data persistence layer
- Controller:
Chatkit::SessionsController- Session & request handling
- chatkit_controller.js - Base full-page implementation
- chatkit_sidebar_controller.js - Base sidebar/overlay implementation
Create application-specific controllers by extending the base controllers:
import ChatkitSidebarController from "chatkit/chatkit_sidebar_controller"
export default class extends ChatkitSidebarController {
// Override methods or add new functionality
}Override resolve_workflow_id in your application controller:
class ApplicationController < ActionController::Base
private
def resolve_workflow_id
# Custom logic here
super
end
endimport ChatkitSidebarController from "chatkit/chatkit_sidebar_controller"
export default class extends ChatkitSidebarController {
static values = {
...ChatkitSidebarController.values,
investorLandingId: String
}
connect() {
this.contextTypeValue = 'InvestorLanding'
this.contextIdValue = this.investorLandingIdValue
this.greetingValue = 'Hi! I\'m your AI investment assistant.'
this.headerTitleValue = '🤖 AI Investment Assistant'
super.connect()
}
}import ChatkitSidebarController from "chatkit/chatkit_sidebar_controller"
export default class extends ChatkitSidebarController {
static values = {
...ChatkitSidebarController.values,
deckId: String
}
connect() {
this.contextTypeValue = 'Deck'
this.contextIdValue = this.deckIdValue
this.greetingValue = 'Hi! I\'m your AI pitch assistant.'
super.connect()
}
}The engine creates these tables:
chatkit_threads- Conversation threadschatkit_thread_items- Messages, tool calls, widgets, etc.chatkit_attachments- File and image attachments
- Rails 8.0+
- PostgreSQL (for UUID and JSONB support)
- Ruby 3.0+
- Gems:
ruby-openai,jwt
Copyright (c) 2025. All rights reserved.