Skip to content

Commit 60831fb

Browse files
Assistant generator (#108)
* Assistant geneerator * fix linter * cleanup * fix
1 parent a08ade3 commit 60831fb

File tree

21 files changed

+394
-35
lines changed

21 files changed

+394
-35
lines changed

Gemfile.lock

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ PATH
22
remote: .
33
specs:
44
langchainrb_rails (0.1.11)
5-
langchainrb (>= 0.7, < 0.15)
5+
langchainrb (>= 0.7, < 0.17)
66

77
GEM
88
remote: https://rubygems.org/
@@ -81,10 +81,10 @@ GEM
8181
minitest (>= 5.1)
8282
mutex_m
8383
tzinfo (~> 2.0)
84-
addressable (2.8.6)
85-
public_suffix (>= 2.0.2, < 6.0)
84+
addressable (2.8.7)
85+
public_suffix (>= 2.0.2, < 7.0)
8686
ast (2.4.2)
87-
baran (0.1.11)
87+
baran (0.1.12)
8888
base64 (0.2.0)
8989
bigdecimal (3.1.8)
9090
brakeman (6.2.1)
@@ -95,7 +95,6 @@ GEM
9595
thor (~> 1.0)
9696
byebug (11.1.3)
9797
coderay (1.1.3)
98-
colorize (1.1.0)
9998
concurrent-ruby (1.3.4)
10099
connection_pool (2.4.1)
101100
crass (1.0.6)
@@ -108,24 +107,21 @@ GEM
108107
railties (>= 3.0.0)
109108
globalid (1.2.1)
110109
activesupport (>= 6.1)
111-
i18n (1.14.5)
110+
i18n (1.14.6)
112111
concurrent-ruby (~> 1.0)
113112
io-console (0.7.2)
114113
irb (1.14.0)
115114
rdoc (>= 4.0.0)
116115
reline (>= 0.4.2)
117116
json (2.7.2)
118-
json-schema (4.3.0)
117+
json-schema (4.3.1)
119118
addressable (>= 2.8)
120-
langchainrb (0.11.4)
121-
activesupport (>= 7.0.8)
119+
langchainrb (0.16.0)
122120
baran (~> 0.1.9)
123-
colorize (~> 1.1.0)
124121
json-schema (~> 4)
125122
matrix
126123
pragmatic_segmenter (~> 0.3.0)
127-
tiktoken_ruby (~> 0.0.8)
128-
to_bool (~> 2.0.0)
124+
rainbow (~> 3.1.0)
129125
zeitwerk (~> 2.5)
130126
language_server-protocol (3.17.0.3)
131127
lint_roller (1.1.0)
@@ -165,8 +161,7 @@ GEM
165161
parser (3.3.5.0)
166162
ast (~> 2.4.1)
167163
racc
168-
pragmatic_segmenter (0.3.23)
169-
unicode
164+
pragmatic_segmenter (0.3.24)
170165
pry (0.14.2)
171166
coderay (~> 1.1)
172167
method_source (~> 1.0)
@@ -175,7 +170,7 @@ GEM
175170
pry (>= 0.13, < 0.15)
176171
psych (5.1.2)
177172
stringio
178-
public_suffix (5.0.5)
173+
public_suffix (6.0.1)
179174
racc (1.8.1)
180175
rack (3.1.7)
181176
rack-session (2.0.0)
@@ -216,7 +211,6 @@ GEM
216211
zeitwerk (~> 2.6)
217212
rainbow (3.1.1)
218213
rake (13.2.1)
219-
rb_sys (0.9.96)
220214
rdoc (6.7.0)
221215
psych (>= 4.0.0)
222216
regexp_parser (2.9.2)
@@ -267,15 +261,9 @@ GEM
267261
standard
268262
stringio (3.1.1)
269263
thor (1.3.2)
270-
tiktoken_ruby (0.0.8)
271-
rb_sys (>= 0.9.86)
272-
tiktoken_ruby (0.0.8-x86_64-darwin)
273-
tiktoken_ruby (0.0.8-x86_64-linux)
274264
timeout (0.4.1)
275-
to_bool (2.0.0)
276265
tzinfo (2.0.6)
277266
concurrent-ruby (~> 1.0)
278-
unicode (0.4.4.5)
279267
unicode-display_width (2.5.0)
280268
webrick (1.8.1)
281269
websocket-driver (0.7.6)

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,8 @@ prompt = Prompt.create!(template: "Tell me a {adjective} joke about {subject}.")
146146
prompt.render(adjective: "funny", subject: "elephants")
147147
# => "Tell me a funny joke about elephants."
148148
```
149+
150+
### Assistant Generator - adds assistant capabilities to your ActiveRecord model
151+
```bash
152+
rails generate langchainrb_rails:assistant
153+
```

langchainrb_rails.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
3030
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
3131
spec.require_paths = ["lib"]
3232

33-
spec.add_dependency "langchainrb", ">= 0.7", "< 0.15"
33+
spec.add_dependency "langchainrb", ">= 0.7", "< 0.17"
3434

3535
spec.add_development_dependency "pry-byebug", "~> 3.10.0"
3636
spec.add_development_dependency "yard", "~> 0.9.34"
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# frozen_string_literal: true
2+
3+
require "active_record"
4+
5+
module Langchain
6+
class Assistant
7+
attr_accessor :id
8+
9+
alias_method :original_initialize, :initialize
10+
11+
def initialize(id: nil, **kwargs) # rubocop:disable Style/ArgumentsForwarding
12+
@id = id
13+
original_initialize(**kwargs) # rubocop:disable Style/ArgumentsForwarding
14+
end
15+
16+
def save
17+
::ActiveRecord::Base.transaction do
18+
ar_assistant = if id
19+
self.class.find_assistant(id)
20+
else
21+
::Assistant.new
22+
end
23+
24+
ar_assistant.update!(
25+
instructions: instructions,
26+
tool_choice: tool_choice,
27+
tools: tools.map(&:class).map(&:name)
28+
)
29+
30+
messages.each do |message|
31+
ar_message = ar_assistant.messages.find_or_initialize_by(id: message.id)
32+
ar_message.update!(
33+
role: message.role,
34+
content: message.content,
35+
tool_calls: message.tool_calls,
36+
tool_call_id: message.tool_call_id
37+
)
38+
message.id = ar_message.id
39+
end
40+
41+
@id = ar_assistant.id
42+
true
43+
end
44+
end
45+
46+
class << self
47+
def find_assistant(id)
48+
::Assistant.find(id)
49+
end
50+
51+
def load(id)
52+
ar_assistant = find_assistant(id)
53+
54+
tools = ar_assistant.tools.map { |tool_name| Object.const_get(tool_name).new }
55+
56+
assistant = Langchain::Assistant.new(
57+
id: ar_assistant.id,
58+
llm: ar_assistant.llm,
59+
tools: tools,
60+
instructions: ar_assistant.instructions,
61+
tool_choice: ar_assistant.tool_choice
62+
)
63+
64+
ar_assistant.messages.each do |ar_message|
65+
messages = assistant.add_message(
66+
role: ar_message.role,
67+
content: ar_message.content,
68+
tool_calls: ar_message.tool_calls,
69+
tool_call_id: ar_message.tool_call_id
70+
)
71+
messages.last.id = ar_message.id
72+
end
73+
74+
assistant
75+
end
76+
end
77+
end
78+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Langchain
2+
module Messages
3+
class Base
4+
attr_accessor :id
5+
end
6+
end
7+
end

lib/langchainrb_rails.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
require "forwardable"
44
require "langchain"
55
require "rails"
6-
require_relative "langchainrb_rails/version"
7-
require "langchainrb_rails/railtie"
6+
87
require "langchainrb_rails/config"
98
require "langchainrb_rails/prompting"
9+
require "langchainrb_rails/railtie"
10+
require "langchainrb_rails/version"
11+
1012
require_relative "langchainrb_overrides/vectorsearch/pgvector"
13+
require_relative "langchainrb_overrides/assistant"
14+
require_relative "langchainrb_overrides/message"
1115

1216
module LangchainrbRails
1317
class Error < StandardError; end
@@ -18,6 +22,7 @@ module ActiveRecord
1822

1923
module Generators
2024
autoload :BaseGenerator, "langchainrb_rails/generators/langchainrb_rails/base_generator"
25+
autoload :AssistantGenerator, "langchainrb_rails/generators/langchainrb_rails/assistant_generator"
2126
autoload :ChromaGenerator, "langchainrb_rails/generators/langchainrb_rails/chroma_generator"
2227
autoload :PgvectorGenerator, "langchainrb_rails/generators/langchainrb_rails/pgvector_generator"
2328
autoload :QdrantGenerator, "langchainrb_rails/generators/langchainrb_rails/qdrant_generator"
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# frozen_string_literal: true
2+
3+
require "rails/generators"
4+
require "rails/generators/active_record"
5+
6+
module LangchainrbRails
7+
module Generators
8+
#
9+
# Usage:
10+
# rails generate langchainrb_rails:assistant --llm=openai
11+
#
12+
class AssistantGenerator < Rails::Generators::Base
13+
include ::ActiveRecord::Generators::Migration
14+
15+
# TODO: Move constant this to a shared place
16+
LLMS = {
17+
"anthropic" => "Langchain::LLM::Anthropic",
18+
"cohere" => "Langchain::LLM::Cohere",
19+
"google_palm" => "Langchain::LLM::GooglePalm",
20+
"google_gemini" => "Langchain::LLM::GoogleGemini",
21+
"google_vertex_ai" => "Langchain::LLM::GoogleVertexAI",
22+
"hugging_face" => "Langchain::LLM::HuggingFace",
23+
"llama_cpp" => "Langchain::LLM::LlamaCpp",
24+
"mistral_ai" => "Langchain::LLM::MistralAI",
25+
"ollama" => "Langchain::LLM::Ollama",
26+
"openai" => "Langchain::LLM::OpenAI",
27+
"replicate" => "Langchain::LLM::Replicate"
28+
}.freeze
29+
30+
class_option :llm,
31+
type: :string,
32+
required: true,
33+
default: "openai",
34+
desc: "LLM provider that will be used to generate embeddings and completions",
35+
enum: LLMS.keys
36+
37+
desc "This generator adds Assistant and Message models and tables to your Rails app"
38+
source_root File.join(__dir__, "templates")
39+
40+
def copy_migration
41+
migration_template "assistant/migrations/create_assistants.rb", "db/migrate/create_assistants.rb", migration_version: migration_version
42+
migration_template "assistant/migrations/create_messages.rb", "db/migrate/create_messages.rb", migration_version: migration_version
43+
end
44+
45+
def create_model_file
46+
template "assistant/models/assistant.rb", "app/models/assistant.rb"
47+
template "assistant/models/message.rb", "app/models/message.rb"
48+
end
49+
50+
def migration_version
51+
"[#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}]"
52+
end
53+
54+
# TODO: Depending on the LLM provider, we may need to add additional gems
55+
# def add_to_gemfile
56+
# end
57+
58+
private
59+
60+
# @return [String] LLM provider to use
61+
def llm
62+
options["llm"]
63+
end
64+
65+
# @return [Langchain::LLM::*] LLM class
66+
def llm_class
67+
Langchain::LLM.const_get(LLMS[llm])
68+
end
69+
end
70+
end
71+
end

lib/langchainrb_rails/generators/langchainrb_rails/base_generator.rb

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,39 @@ module Generators
88
class BaseGenerator < Rails::Generators::Base
99
include ::ActiveRecord::Generators::Migration
1010

11-
class_option :model, type: :string, required: true, desc: "ActiveRecord Model to add vectorsearch to", aliases: "-m"
12-
class_option :llm, type: :string, required: true, desc: "LLM provider that will be used to generate embeddings and completions"
13-
1411
# Available LLM providers to be passed in as --llm option
1512
LLMS = {
13+
"anthropic" => "Langchain::LLM::Anthropic",
1614
"cohere" => "Langchain::LLM::Cohere",
1715
"google_palm" => "Langchain::LLM::GooglePalm",
16+
"google_gemini" => "Langchain::LLM::GoogleGemini",
17+
"google_vertex_ai" => "Langchain::LLM::GoogleVertexAI",
1818
"hugging_face" => "Langchain::LLM::HuggingFace",
1919
"llama_cpp" => "Langchain::LLM::LlamaCpp",
20+
"mistral_ai" => "Langchain::LLM::MistralAI",
2021
"ollama" => "Langchain::LLM::Ollama",
2122
"openai" => "Langchain::LLM::OpenAI",
2223
"replicate" => "Langchain::LLM::Replicate"
2324
}.freeze
2425

26+
class_option :model,
27+
type: :string,
28+
required: true,
29+
aliases: "-m",
30+
desc: "ActiveRecord Model to add vectorsearch to"
31+
32+
class_option :llm,
33+
type: :string,
34+
required: true,
35+
default: "openai",
36+
desc: "LLM provider that will be used to generate embeddings and completions",
37+
enum: LLMS.keys
38+
39+
# Run bundle install after running the generator
40+
def after_generate
41+
run "bundle install"
42+
end
43+
2544
def post_install_message
2645
say "Please do the following to start Q&A with your #{model_name} records:", :green
2746
say "1. Run `bundle install` to install the new gems."

lib/langchainrb_rails/generators/langchainrb_rails/chroma_generator.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ def add_to_model
2828
end
2929

3030
# Adds `chroma-db` gem to the Gemfile
31-
# TODO: Can we automatically run `bundle install`?
3231
def add_to_gemfile
33-
gem "chroma-db", version: "~> 0.6.0"
32+
gem "chroma-db"
3433
end
3534

3635
private

lib/langchainrb_rails/generators/langchainrb_rails/pinecone_generator.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ def add_to_model
2828
end
2929

3030
# Adds `pinecone` gem to the Gemfile
31-
# TODO: Can we automatically run `bundle install`?
3231
def add_to_gemfile
33-
gem "pinecone", version: "~> 0.1.6"
32+
gem "pinecone"
3433
end
3534

3635
private

0 commit comments

Comments
 (0)