Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assistant generator creates a basic UI #112

Merged
merged 22 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
/pkg/
/spec/reports/
/tmp/
.ruby-version

# rspec failure tracking
.rspec_status
**/.DS_Store
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,18 @@ prompt.render(adjective: "funny", subject: "elephants")
# => "Tell me a funny joke about elephants."
```

### Assistant Generator - adds assistant capabilities to your ActiveRecord model
### Assistant Generator - adds Langchain::Assistant capabilities to your Rails app

This generator adds Langchain::Assistant-related ActiveRecord models, migrations, controllers, views and route to your Rails app. You can start creating assistants and chatting with them in immediately.

```bash
rails generate langchainrb_rails:assistant --llm=openai
```

Available `--llm` options: `anthropic`, `cohere`, `google_palm`, `google_gemini`, `google_vertex_ai`, `hugging_face`, `llama_cpp`, `mistral_ai`, `ollama`, `openai`, and `replicate`. The selected LLM will be used to generate completions.

To remove the generated files, run:

```bash
rails generate langchainrb_rails:assistant
```
rails destroy langchainrb_rails:assistant
```
3 changes: 2 additions & 1 deletion lib/langchainrb_overrides/assistant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ def load(id)
llm: ar_assistant.llm,
tools: tools,
instructions: ar_assistant.instructions,
tool_choice: ar_assistant.tool_choice
# Default to auto to match the behavior of the original Langchain::Assistant
tool_choice: ar_assistant.tool_choice || "auto"
)

ar_assistant.messages.each do |ar_message|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,60 @@ def migration_version
"[#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}]"
end

def create_controller_file
template "assistant/controllers/assistants_controller.rb", "app/controllers/assistants_controller.rb"
end

def create_view_files
template "assistant/views/_message.html.erb", "app/views/assistants/_message.html.erb"
template "assistant/views/_message_form.html.erb", "app/views/assistants/_message_form.html.erb"
template "assistant/views/chat.turbo_stream.erb", "app/views/assistants/chat.turbo_stream.erb"
template "assistant/views/edit.html.erb", "app/views/assistants/edit.html.erb"
template "assistant/views/index.html.erb", "app/views/assistants/index.html.erb"
template "assistant/views/new.html.erb", "app/views/assistants/new.html.erb"
template "assistant/views/show.html.erb", "app/views/assistants/show.html.erb"
end

def add_routes
route <<~EOS
resources :assistants do
member do
post 'chat'
end
end
EOS
end

# TODO: Copy stylesheet into app/assets/stylesheets or whatever the host app uses
def copy_stylesheets
template "assistant/stylesheets/chat.css", "app/assets/stylesheets/chat.css"
end

# TODO: Depending on the LLM provider, we may need to add additional gems
# def add_to_gemfile
# end
def add_to_gemfile
gem_name = "turbo-rails"

if gem_exists?(gem_name)
say_status :skipped, "#{gem_name} already exists in Gemfile"
else
inside Rails.root do
run "bundle add #{gem_name}"
end
end
end

def post_install_message
say "1. Set an environment variable ENV['#{llm.upcase}_API_KEY'] for your #{llm_class}."
say "2. Run `rails db:migrate` to apply the database migrations to create the assistants and messages tables."
say "3. Start your Rails server and navigate to `/assistants` to create your first assistant!"
end

private

def gem_exists?(gem_name)
File.read(Rails.root.join("Gemfile")).include?(gem_name)
end

# @return [String] LLM provider to use
def llm
options["llm"]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

class AssistantsController < ApplicationController
before_action :set_assistant, only: [:show, :edit, :update, :chat, :destroy]

def index
@assistants = Assistant.all
end

def new
@assistant = Assistant.new
end

def create
@assistant = Assistant.new(assistant_params)
if @assistant.save
redirect_to @assistant, notice: "Assistant was successfully created."
else
render :new
end
end

def show
@assistants = Assistant.all
@assistant = Assistant.find(params[:id])
@messages = @assistant.messages
@message = Message.new
end

def edit
end

def update
if @assistant.update(assistant_params)
redirect_to @assistant, notice: "Assistant was successfully updated."
else
render :edit
end
end

def chat
@assistant = Assistant.find(params[:id])
@message = @assistant.messages.create(role: "user", content: params[:message][:content])

langchain_assistant = Langchain::Assistant.load(@assistant.id)
messages = langchain_assistant.add_message_and_run!(content: params[:message][:content])
response = messages.last

@response = @assistant.messages.create(role: "assistant", content: response.content)

respond_to do |format|
format.turbo_stream
end
end

def destroy
@assistant.destroy
redirect_to assistants_path, notice: "Assistant was successfully deleted."
end

private

def set_assistant
@assistant = Assistant.find(params[:id])
end

def assistant_params
params.require(:assistant).permit(:name, :instructions, :tool_choice)
end
end
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
def change
create_table :assistants do |t|
t.string :name, null: false
t.string :instructions
t.string :tool_choice
t.json :tools
t.json :tools, default: []
t.timestamps
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
def change
create_table :messages do |t|
t.references :assistant, foreign_key: true
t.string :role
t.string :role, null: false
t.text :content
t.json :tool_calls
t.json :tool_calls, default: []
t.string :tool_call_id
t.timestamps
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# frozen_string_literal: true

class Assistant < ActiveRecord::Base
has_many :messages
has_many :messages, dependent: :destroy

validates :name, presence: true

# TODO: Validate tool_choice

def llm
<%= llm_class %>.new(api_key: ENV["<%= llm.upcase %>_API_KEY"])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

class Message < ActiveRecord::Base
belongs_to :assistant

validates :role, presence: true
end
Loading