rails_chatbot 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +284 -0
- data/Rakefile +8 -0
- data/app/assets/stylesheets/rails_chatbot/application.css +15 -0
- data/app/controllers/rails_chatbot/application_controller.rb +14 -0
- data/app/controllers/rails_chatbot/chat_controller.rb +27 -0
- data/app/controllers/rails_chatbot/conversations_controller.rb +48 -0
- data/app/controllers/rails_chatbot/messages_controller.rb +55 -0
- data/app/helpers/rails_chatbot/application_helper.rb +12 -0
- data/app/javascript/rails_chatbot/application.js +163 -0
- data/app/jobs/rails_chatbot/application_job.rb +4 -0
- data/app/mailers/rails_chatbot/application_mailer.rb +6 -0
- data/app/models/rails_chatbot/application_record.rb +5 -0
- data/app/models/rails_chatbot/conversation.rb +25 -0
- data/app/models/rails_chatbot/knowledge_base.rb +57 -0
- data/app/models/rails_chatbot/message.rb +13 -0
- data/app/services/rails_chatbot/chat_service.rb +41 -0
- data/app/services/rails_chatbot/knowledge_retrieval_service.rb +42 -0
- data/app/services/rails_chatbot/llm_service.rb +64 -0
- data/app/views/layouts/rails_chatbot/application.html.erb +17 -0
- data/app/views/rails_chatbot/chat/index.html.erb +165 -0
- data/config/initializers/rails_chatbot.rb +13 -0
- data/config/routes.rb +13 -0
- data/db/migrate/001_create_rails_chatbot_conversations.rb +10 -0
- data/db/migrate/002_create_rails_chatbot_messages.rb +11 -0
- data/db/migrate/003_create_rails_chatbot_knowledge_bases.rb +16 -0
- data/db/seeds.rb +86 -0
- data/lib/generators/rails_chatbot/install/install_generator.rb +19 -0
- data/lib/generators/rails_chatbot/install/templates/README +16 -0
- data/lib/generators/rails_chatbot/install/templates/rails_chatbot.rb +13 -0
- data/lib/rails_chatbot/engine.rb +19 -0
- data/lib/rails_chatbot/knowledge_indexer.rb +71 -0
- data/lib/rails_chatbot/railtie.rb +22 -0
- data/lib/rails_chatbot/version.rb +3 -0
- data/lib/rails_chatbot.rb +29 -0
- data/lib/tasks/rails_chatbot_tasks.rake +81 -0
- metadata +123 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module RailsChatbot
|
|
2
|
+
class Conversation < ApplicationRecord
|
|
3
|
+
self.table_name = 'rails_chatbot_conversations'
|
|
4
|
+
|
|
5
|
+
has_many :messages, dependent: :destroy, class_name: 'RailsChatbot::Message'
|
|
6
|
+
|
|
7
|
+
validates :session_id, presence: true
|
|
8
|
+
|
|
9
|
+
scope :recent, -> { order(created_at: :desc) }
|
|
10
|
+
|
|
11
|
+
def add_user_message(content)
|
|
12
|
+
messages.create!(content: content, role: 'user')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add_assistant_message(content, metadata: {})
|
|
16
|
+
messages.create!(content: content, role: 'assistant', metadata: metadata)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def conversation_history
|
|
20
|
+
messages.order(created_at: :asc).map do |msg|
|
|
21
|
+
{ role: msg.role, content: msg.content }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module RailsChatbot
|
|
2
|
+
class KnowledgeBase < ApplicationRecord
|
|
3
|
+
self.table_name = 'rails_chatbot_knowledge_bases'
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
if defined?(PgSearch)
|
|
7
|
+
include PgSearch::Model
|
|
8
|
+
|
|
9
|
+
pg_search_scope :search_by_content,
|
|
10
|
+
against: [:title, :content],
|
|
11
|
+
using: {
|
|
12
|
+
tsearch: { prefix: true, any_word: true }
|
|
13
|
+
}
|
|
14
|
+
else
|
|
15
|
+
raise LoadError
|
|
16
|
+
end
|
|
17
|
+
rescue LoadError, NameError
|
|
18
|
+
# Fallback search scope without pg_search (works with any database)
|
|
19
|
+
scope :search_by_content, ->(query) {
|
|
20
|
+
sanitized_query = connection.quote_string(query.to_s)
|
|
21
|
+
where("title LIKE ? OR content LIKE ?", "%#{sanitized_query}%", "%#{sanitized_query}%")
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
validates :title, presence: true
|
|
26
|
+
validates :content, presence: true
|
|
27
|
+
validates :source_type, presence: true
|
|
28
|
+
|
|
29
|
+
scope :by_source, ->(type, id = nil) {
|
|
30
|
+
scope = where(source_type: type)
|
|
31
|
+
scope = scope.where(source_id: id) if id
|
|
32
|
+
scope
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def self.index_model(model_class, fields: [:name, :description, :content])
|
|
36
|
+
model_class.find_each do |record|
|
|
37
|
+
content_parts = fields.map { |field| record.send(field) if record.respond_to?(field) }.compact
|
|
38
|
+
content_text = content_parts.join("\n\n")
|
|
39
|
+
|
|
40
|
+
source_url = begin
|
|
41
|
+
Rails.application.routes.url_helpers.polymorphic_path(record)
|
|
42
|
+
rescue
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
find_or_initialize_by(
|
|
47
|
+
source_type: model_class.name,
|
|
48
|
+
source_id: record.id.to_s
|
|
49
|
+
).update!(
|
|
50
|
+
title: record.try(:name) || record.try(:title) || "#{model_class.name} ##{record.id}",
|
|
51
|
+
content: content_text,
|
|
52
|
+
source_url: source_url
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module RailsChatbot
|
|
2
|
+
class Message < ApplicationRecord
|
|
3
|
+
self.table_name = 'rails_chatbot_messages'
|
|
4
|
+
|
|
5
|
+
belongs_to :conversation, class_name: 'RailsChatbot::Conversation'
|
|
6
|
+
|
|
7
|
+
validates :content, presence: true
|
|
8
|
+
validates :role, inclusion: { in: %w[user assistant system] }
|
|
9
|
+
|
|
10
|
+
scope :user_messages, -> { where(role: 'user') }
|
|
11
|
+
scope :assistant_messages, -> { where(role: 'assistant') }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module RailsChatbot
|
|
2
|
+
class ChatService
|
|
3
|
+
attr_reader :conversation, :llm_service
|
|
4
|
+
|
|
5
|
+
def initialize(conversation:, api_key: nil)
|
|
6
|
+
@conversation = conversation
|
|
7
|
+
@llm_service = LlmService.new(api_key: api_key)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def process_message(user_message)
|
|
11
|
+
# Retrieve relevant knowledge
|
|
12
|
+
knowledge_service = KnowledgeRetrievalService.new(query: user_message)
|
|
13
|
+
context = knowledge_service.format_context
|
|
14
|
+
|
|
15
|
+
# Get conversation history
|
|
16
|
+
messages = conversation.conversation_history
|
|
17
|
+
|
|
18
|
+
# Add the new user message
|
|
19
|
+
conversation.add_user_message(user_message)
|
|
20
|
+
|
|
21
|
+
# Generate response
|
|
22
|
+
response = llm_service.chat(
|
|
23
|
+
messages: messages,
|
|
24
|
+
context: context
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Save assistant response
|
|
28
|
+
conversation.add_assistant_message(
|
|
29
|
+
response,
|
|
30
|
+
metadata: {
|
|
31
|
+
knowledge_results: knowledge_service.retrieve.map { |r| r.slice(:title, :source_type, :source_id) }
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
{
|
|
36
|
+
response: response,
|
|
37
|
+
knowledge_sources: knowledge_service.retrieve
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module RailsChatbot
|
|
2
|
+
class KnowledgeRetrievalService
|
|
3
|
+
def initialize(query:, limit: 5)
|
|
4
|
+
@query = query
|
|
5
|
+
@limit = limit
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def retrieve
|
|
9
|
+
return [] if @query.blank?
|
|
10
|
+
|
|
11
|
+
# Search knowledge base using pg_search
|
|
12
|
+
results = KnowledgeBase.search_by_content(@query).limit(@limit)
|
|
13
|
+
|
|
14
|
+
# Format results for context
|
|
15
|
+
results.map do |kb|
|
|
16
|
+
{
|
|
17
|
+
title: kb.title,
|
|
18
|
+
content: kb.content,
|
|
19
|
+
source_type: kb.source_type,
|
|
20
|
+
source_id: kb.source_id,
|
|
21
|
+
source_url: kb.source_url
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def format_context
|
|
27
|
+
results = retrieve
|
|
28
|
+
return nil if results.empty?
|
|
29
|
+
|
|
30
|
+
context_parts = results.map do |result|
|
|
31
|
+
<<~CONTEXT
|
|
32
|
+
Title: #{result[:title]}
|
|
33
|
+
Source: #{result[:source_type]}#{" (ID: #{result[:source_id]})" if result[:source_id]}
|
|
34
|
+
Content: #{result[:content]}
|
|
35
|
+
#{result[:source_url] ? "URL: #{result[:source_url]}" : ""}
|
|
36
|
+
CONTEXT
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context_parts.join("\n\n---\n\n")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module RailsChatbot
|
|
2
|
+
class LlmService
|
|
3
|
+
attr_reader :client, :model
|
|
4
|
+
|
|
5
|
+
def initialize(api_key: nil, model: nil)
|
|
6
|
+
api_key ||= RailsChatbot.configuration.openai_api_key || ENV['OPENAI_API_KEY']
|
|
7
|
+
model ||= RailsChatbot.configuration.openai_model || 'gpt-4o-mini'
|
|
8
|
+
|
|
9
|
+
raise ArgumentError, "OpenAI API key is required" unless api_key
|
|
10
|
+
|
|
11
|
+
@client = OpenAI::Client.new(access_token: api_key)
|
|
12
|
+
@model = model
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def chat(messages:, context: nil, temperature: 0.7)
|
|
16
|
+
system_message = build_system_message(context)
|
|
17
|
+
conversation_messages = [system_message] + messages
|
|
18
|
+
|
|
19
|
+
response = client.chat(
|
|
20
|
+
parameters: {
|
|
21
|
+
model: model,
|
|
22
|
+
messages: conversation_messages,
|
|
23
|
+
temperature: temperature
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
response.dig('choices', 0, 'message', 'content')
|
|
28
|
+
rescue => e
|
|
29
|
+
Rails.logger.error("LLM Service Error: #{e.message}")
|
|
30
|
+
"I apologize, but I'm experiencing technical difficulties. Please try again later."
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def build_system_message(context)
|
|
36
|
+
base_prompt = <<~PROMPT
|
|
37
|
+
You are a helpful AI assistant integrated into a Ruby on Rails application.
|
|
38
|
+
Your role is to answer questions about the application and help users navigate and understand the system.
|
|
39
|
+
|
|
40
|
+
You have access to the application's knowledge base, which contains information about:
|
|
41
|
+
- Models and their attributes
|
|
42
|
+
- Application features and functionality
|
|
43
|
+
- User guides and documentation
|
|
44
|
+
- Common questions and answers
|
|
45
|
+
|
|
46
|
+
When answering questions:
|
|
47
|
+
1. Use the provided context from the knowledge base to give accurate answers
|
|
48
|
+
2. Be concise but thorough
|
|
49
|
+
3. If you don't know something, admit it rather than guessing
|
|
50
|
+
4. Provide helpful examples when relevant
|
|
51
|
+
5. Format your responses clearly with proper markdown when appropriate
|
|
52
|
+
PROMPT
|
|
53
|
+
|
|
54
|
+
if context.present?
|
|
55
|
+
base_prompt += "\n\nRelevant Context:\n#{context}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
{
|
|
59
|
+
role: 'system',
|
|
60
|
+
content: base_prompt
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Rails chatbot</title>
|
|
5
|
+
<%= csrf_meta_tags %>
|
|
6
|
+
<%= csp_meta_tag %>
|
|
7
|
+
|
|
8
|
+
<%= yield :head %>
|
|
9
|
+
|
|
10
|
+
<%= stylesheet_link_tag "rails_chatbot/application", media: "all" %>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
|
|
14
|
+
<%= yield %>
|
|
15
|
+
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<%= content_for :head do %>
|
|
2
|
+
<style>
|
|
3
|
+
.chatbot-container {
|
|
4
|
+
max-width: 800px;
|
|
5
|
+
margin: 0 auto;
|
|
6
|
+
height: 600px;
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
border: 1px solid #ddd;
|
|
10
|
+
border-radius: 8px;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
background: #fff;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.chatbot-header {
|
|
16
|
+
background: #4a5568;
|
|
17
|
+
color: white;
|
|
18
|
+
padding: 1rem;
|
|
19
|
+
font-weight: bold;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.chatbot-messages {
|
|
23
|
+
flex: 1;
|
|
24
|
+
overflow-y: auto;
|
|
25
|
+
padding: 1rem;
|
|
26
|
+
background: #f7fafc;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.message {
|
|
30
|
+
margin-bottom: 1rem;
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.message.user {
|
|
36
|
+
align-items: flex-end;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.message.assistant {
|
|
40
|
+
align-items: flex-start;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.message-bubble {
|
|
44
|
+
max-width: 70%;
|
|
45
|
+
padding: 0.75rem 1rem;
|
|
46
|
+
border-radius: 1rem;
|
|
47
|
+
word-wrap: break-word;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.message.user .message-bubble {
|
|
51
|
+
background: #4299e1;
|
|
52
|
+
color: white;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.message.assistant .message-bubble {
|
|
56
|
+
background: white;
|
|
57
|
+
color: #2d3748;
|
|
58
|
+
border: 1px solid #e2e8f0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.message-time {
|
|
62
|
+
font-size: 0.75rem;
|
|
63
|
+
color: #718096;
|
|
64
|
+
margin-top: 0.25rem;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.chatbot-input-container {
|
|
68
|
+
display: flex;
|
|
69
|
+
padding: 1rem;
|
|
70
|
+
background: white;
|
|
71
|
+
border-top: 1px solid #e2e8f0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.chatbot-input {
|
|
75
|
+
flex: 1;
|
|
76
|
+
padding: 0.75rem;
|
|
77
|
+
border: 1px solid #cbd5e0;
|
|
78
|
+
border-radius: 0.5rem;
|
|
79
|
+
font-size: 1rem;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.chatbot-send {
|
|
83
|
+
margin-left: 0.5rem;
|
|
84
|
+
padding: 0.75rem 1.5rem;
|
|
85
|
+
background: #4299e1;
|
|
86
|
+
color: white;
|
|
87
|
+
border: none;
|
|
88
|
+
border-radius: 0.5rem;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
font-weight: bold;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.chatbot-send:hover {
|
|
94
|
+
background: #3182ce;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.chatbot-send:disabled {
|
|
98
|
+
background: #cbd5e0;
|
|
99
|
+
cursor: not-allowed;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.loading {
|
|
103
|
+
display: inline-block;
|
|
104
|
+
width: 20px;
|
|
105
|
+
height: 20px;
|
|
106
|
+
border: 3px solid #f3f3f3;
|
|
107
|
+
border-top: 3px solid #4299e1;
|
|
108
|
+
border-radius: 50%;
|
|
109
|
+
animation: spin 1s linear infinite;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@keyframes spin {
|
|
113
|
+
0% { transform: rotate(0deg); }
|
|
114
|
+
100% { transform: rotate(360deg); }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.knowledge-sources {
|
|
118
|
+
margin-top: 0.5rem;
|
|
119
|
+
font-size: 0.75rem;
|
|
120
|
+
color: #718096;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.knowledge-sources a {
|
|
124
|
+
color: #4299e1;
|
|
125
|
+
text-decoration: none;
|
|
126
|
+
}
|
|
127
|
+
</style>
|
|
128
|
+
<% end %>
|
|
129
|
+
|
|
130
|
+
<div class="chatbot-container" data-controller="chatbot">
|
|
131
|
+
<div class="chatbot-header">
|
|
132
|
+
<%= RailsChatbot.configuration.chatbot_title %>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div class="chatbot-messages" data-chatbot-target="messages">
|
|
136
|
+
<div class="message assistant">
|
|
137
|
+
<div class="message-bubble">
|
|
138
|
+
Hello! I'm your application assistant. How can I help you today?
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div class="chatbot-input-container">
|
|
144
|
+
<input
|
|
145
|
+
type="text"
|
|
146
|
+
class="chatbot-input"
|
|
147
|
+
placeholder="Type your message..."
|
|
148
|
+
data-chatbot-target="input"
|
|
149
|
+
data-action="keydown->chatbot#handleKeyDown"
|
|
150
|
+
>
|
|
151
|
+
<button
|
|
152
|
+
class="chatbot-send"
|
|
153
|
+
data-action="click->chatbot#sendMessage"
|
|
154
|
+
data-chatbot-target="sendButton"
|
|
155
|
+
>
|
|
156
|
+
Send
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<script>
|
|
162
|
+
window.RailsChatbot = window.RailsChatbot || {};
|
|
163
|
+
window.RailsChatbot.routes = <%= rails_chatbot_routes %>;
|
|
164
|
+
</script>
|
|
165
|
+
<%= javascript_include_tag "rails_chatbot/application" %>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Rails.application.config.rails_chatbot = RailsChatbot.configuration
|
|
2
|
+
|
|
3
|
+
Rails.application.config.to_prepare do
|
|
4
|
+
# Allow configuration to be set in initializers
|
|
5
|
+
if Rails.application.config.rails_chatbot
|
|
6
|
+
RailsChatbot.configuration.openai_api_key ||= Rails.application.config.rails_chatbot.openai_api_key
|
|
7
|
+
RailsChatbot.configuration.openai_model ||= Rails.application.config.rails_chatbot.openai_model
|
|
8
|
+
RailsChatbot.configuration.chatbot_title ||= Rails.application.config.rails_chatbot.chatbot_title
|
|
9
|
+
RailsChatbot.configuration.current_user_proc ||= Rails.application.config.rails_chatbot.current_user_proc
|
|
10
|
+
RailsChatbot.configuration.enable_knowledge_base_indexing ||= Rails.application.config.rails_chatbot.enable_knowledge_base_indexing
|
|
11
|
+
RailsChatbot.configuration.indexable_models ||= Rails.application.config.rails_chatbot.indexable_models
|
|
12
|
+
end
|
|
13
|
+
end
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
RailsChatbot::Engine.routes.draw do
|
|
2
|
+
root to: 'chat#index'
|
|
3
|
+
|
|
4
|
+
resources :conversations, only: [:index, :show, :create, :destroy] do
|
|
5
|
+
resources :messages, only: [:create]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
post 'messages', to: 'messages#create'
|
|
9
|
+
get 'search', to: 'chat#search'
|
|
10
|
+
|
|
11
|
+
# Add explicit route for conversation messages
|
|
12
|
+
post 'conversations/:conversation_id/messages', to: 'messages#create'
|
|
13
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class CreateRailsChatbotConversations < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :rails_chatbot_conversations do |t|
|
|
4
|
+
t.string :session_id, null: false, index: true
|
|
5
|
+
t.references :user, polymorphic: true, null: true, index: true
|
|
6
|
+
t.string :title
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class CreateRailsChatbotMessages < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :rails_chatbot_messages do |t|
|
|
4
|
+
t.references :conversation, null: false, foreign_key: { to_table: :rails_chatbot_conversations }, index: true
|
|
5
|
+
t.text :content, null: false
|
|
6
|
+
t.string :role, null: false, default: 'user' # user, assistant, system
|
|
7
|
+
t.json :metadata
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CreateRailsChatbotKnowledgeBases < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :rails_chatbot_knowledge_bases do |t|
|
|
4
|
+
t.string :title, null: false
|
|
5
|
+
t.text :content, null: false
|
|
6
|
+
t.string :source_type, null: false # model, page, document, etc.
|
|
7
|
+
t.string :source_id
|
|
8
|
+
t.string :source_url
|
|
9
|
+
t.text :embedding_data # For vector search if needed
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_index :rails_chatbot_knowledge_bases, [:source_type, :source_id]
|
|
14
|
+
add_index :rails_chatbot_knowledge_bases, :title
|
|
15
|
+
end
|
|
16
|
+
end
|
data/db/seeds.rb
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# This file contains sample data for the RailsChatbot knowledge base
|
|
2
|
+
# Run with: rails db:seed
|
|
3
|
+
|
|
4
|
+
RailsChatbot::KnowledgeIndexer.add_custom_knowledge(
|
|
5
|
+
title: "Getting Started with RailsChatbot",
|
|
6
|
+
content: <<~CONTENT
|
|
7
|
+
RailsChatbot is a powerful AI-powered chatbot system for Rails applications.
|
|
8
|
+
|
|
9
|
+
To get started:
|
|
10
|
+
1. Install the gem and run migrations
|
|
11
|
+
2. Configure your OpenAI API key
|
|
12
|
+
3. Mount the engine in your routes
|
|
13
|
+
4. Index your models or add custom knowledge
|
|
14
|
+
|
|
15
|
+
The chatbot will then be able to answer questions about your application.
|
|
16
|
+
CONTENT,
|
|
17
|
+
source_type: "documentation",
|
|
18
|
+
source_url: "/docs/getting-started"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
RailsChatbot::KnowledgeIndexer.add_custom_knowledge(
|
|
22
|
+
title: "API Configuration",
|
|
23
|
+
content: <<~CONTENT
|
|
24
|
+
Configure RailsChatbot in config/initializers/rails_chatbot.rb:
|
|
25
|
+
|
|
26
|
+
RailsChatbot.configure do |config|
|
|
27
|
+
config.openai_api_key = ENV['OPENAI_API_KEY']
|
|
28
|
+
config.openai_model = 'gpt-4o-mini'
|
|
29
|
+
config.chatbot_title = 'Your Assistant'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Make sure to set your OPENAI_API_KEY environment variable.
|
|
33
|
+
CONTENT,
|
|
34
|
+
source_type: "configuration",
|
|
35
|
+
source_url: "/docs/configuration"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
RailsChatbot::KnowledgeIndexer.add_custom_knowledge(
|
|
39
|
+
title: "Knowledge Base Management",
|
|
40
|
+
content: <<~CONTENT
|
|
41
|
+
You can manage the knowledge base using these rake tasks:
|
|
42
|
+
|
|
43
|
+
- rake rails_chatbot:index_all - Index all models
|
|
44
|
+
- rake rails_chatbot:index_models[User,Post] - Index specific models
|
|
45
|
+
- rake rails_chatbot:add_knowledge['Title','Content'] - Add custom knowledge
|
|
46
|
+
- rake rails_chatbot:clear_knowledge_base - Clear all entries
|
|
47
|
+
- rake rails_chatbot:stats - View statistics
|
|
48
|
+
|
|
49
|
+
The knowledge base uses PostgreSQL full-text search for fast retrieval.
|
|
50
|
+
CONTENT,
|
|
51
|
+
source_type: "management",
|
|
52
|
+
source_url: "/docs/knowledge-base"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
RailsChatbot::KnowledgeIndexer.add_custom_knowledge(
|
|
56
|
+
title: "Troubleshooting Common Issues",
|
|
57
|
+
content: <<~CONTENT
|
|
58
|
+
Common issues and solutions:
|
|
59
|
+
|
|
60
|
+
1. OpenAI API errors: Check your API key and model selection
|
|
61
|
+
2. Search not working: Ensure PostgreSQL is properly configured
|
|
62
|
+
3. No knowledge found: Run rake rails_chatbot:index_all to populate the base
|
|
63
|
+
4. Routing errors: Verify the engine is mounted correctly in routes.rb
|
|
64
|
+
5. Permission errors: Check database permissions for the chatbot tables
|
|
65
|
+
CONTENT,
|
|
66
|
+
source_type: "troubleshooting",
|
|
67
|
+
source_url: "/docs/troubleshooting"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
RailsChatbot::KnowledgeIndexer.add_custom_knowledge(
|
|
71
|
+
title: "Customizing the Chat Interface",
|
|
72
|
+
content: <<~CONTENT
|
|
73
|
+
You can customize the chat interface by:
|
|
74
|
+
|
|
75
|
+
1. Overriding views in your app/views/rails_chatbot/
|
|
76
|
+
2. Modifying CSS styles in app/assets/stylesheets/rails_chatbot/
|
|
77
|
+
3. Extending JavaScript functionality in app/javascript/rails_chatbot/
|
|
78
|
+
4. Customizing the system prompt in LlmService
|
|
79
|
+
|
|
80
|
+
The interface is built with vanilla JavaScript and can be easily customized.
|
|
81
|
+
CONTENT,
|
|
82
|
+
source_type: "customization",
|
|
83
|
+
source_url: "/docs/customization"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
puts "✓ RailsChatbot sample knowledge base seeded successfully!"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module RailsChatbot
|
|
2
|
+
module Generators
|
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
|
5
|
+
|
|
6
|
+
def create_initializer
|
|
7
|
+
copy_file 'rails_chatbot.rb', 'config/initializers/rails_chatbot.rb'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def add_routes
|
|
11
|
+
route "mount RailsChatbot::Engine => '/chatbot'"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def show_readme
|
|
15
|
+
readme 'README' if behavior == :invoke
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
RailsChatbot Installation Complete!
|
|
2
|
+
|
|
3
|
+
Next steps:
|
|
4
|
+
|
|
5
|
+
1. Set your OpenAI API key:
|
|
6
|
+
export OPENAI_API_KEY=your_api_key_here
|
|
7
|
+
|
|
8
|
+
2. Run migrations:
|
|
9
|
+
rails db:migrate
|
|
10
|
+
|
|
11
|
+
3. Index your models:
|
|
12
|
+
rake rails_chatbot:index_all
|
|
13
|
+
|
|
14
|
+
4. Visit /chatbot in your application to start using the chatbot!
|
|
15
|
+
|
|
16
|
+
For more information, see the README.md file.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
RailsChatbot.configure do |config|
|
|
2
|
+
# Set your OpenAI API key
|
|
3
|
+
config.openai_api_key = ENV['OPENAI_API_KEY']
|
|
4
|
+
|
|
5
|
+
# Choose the model (gpt-4o-mini, gpt-4, gpt-3.5-turbo, etc.)
|
|
6
|
+
config.openai_model = 'gpt-4o-mini'
|
|
7
|
+
|
|
8
|
+
# Customize the chatbot title
|
|
9
|
+
config.chatbot_title = 'Application Assistant'
|
|
10
|
+
|
|
11
|
+
# Optional: Define how to get current user (uncomment and customize)
|
|
12
|
+
# config.current_user_proc = proc { |controller| controller.current_user }
|
|
13
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module RailsChatbot
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
isolate_namespace RailsChatbot
|
|
4
|
+
|
|
5
|
+
initializer "rails_chatbot.assets.precompile" do |app|
|
|
6
|
+
app.config.assets.precompile += %w[rails_chatbot/application.js rails_chatbot/application.css]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
config.generators do |g|
|
|
10
|
+
g.test_framework :rspec
|
|
11
|
+
g.fixture_replacement :factory_bot
|
|
12
|
+
g.factory_bot dir: 'spec/factories'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
initializer "rails_chatbot.load_config" do
|
|
16
|
+
require_relative '../../config/initializers/rails_chatbot' if File.exist?(Rails.root.join('config/initializers/rails_chatbot.rb'))
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|