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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +284 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/stylesheets/rails_chatbot/application.css +15 -0
  6. data/app/controllers/rails_chatbot/application_controller.rb +14 -0
  7. data/app/controllers/rails_chatbot/chat_controller.rb +27 -0
  8. data/app/controllers/rails_chatbot/conversations_controller.rb +48 -0
  9. data/app/controllers/rails_chatbot/messages_controller.rb +55 -0
  10. data/app/helpers/rails_chatbot/application_helper.rb +12 -0
  11. data/app/javascript/rails_chatbot/application.js +163 -0
  12. data/app/jobs/rails_chatbot/application_job.rb +4 -0
  13. data/app/mailers/rails_chatbot/application_mailer.rb +6 -0
  14. data/app/models/rails_chatbot/application_record.rb +5 -0
  15. data/app/models/rails_chatbot/conversation.rb +25 -0
  16. data/app/models/rails_chatbot/knowledge_base.rb +57 -0
  17. data/app/models/rails_chatbot/message.rb +13 -0
  18. data/app/services/rails_chatbot/chat_service.rb +41 -0
  19. data/app/services/rails_chatbot/knowledge_retrieval_service.rb +42 -0
  20. data/app/services/rails_chatbot/llm_service.rb +64 -0
  21. data/app/views/layouts/rails_chatbot/application.html.erb +17 -0
  22. data/app/views/rails_chatbot/chat/index.html.erb +165 -0
  23. data/config/initializers/rails_chatbot.rb +13 -0
  24. data/config/routes.rb +13 -0
  25. data/db/migrate/001_create_rails_chatbot_conversations.rb +10 -0
  26. data/db/migrate/002_create_rails_chatbot_messages.rb +11 -0
  27. data/db/migrate/003_create_rails_chatbot_knowledge_bases.rb +16 -0
  28. data/db/seeds.rb +86 -0
  29. data/lib/generators/rails_chatbot/install/install_generator.rb +19 -0
  30. data/lib/generators/rails_chatbot/install/templates/README +16 -0
  31. data/lib/generators/rails_chatbot/install/templates/rails_chatbot.rb +13 -0
  32. data/lib/rails_chatbot/engine.rb +19 -0
  33. data/lib/rails_chatbot/knowledge_indexer.rb +71 -0
  34. data/lib/rails_chatbot/railtie.rb +22 -0
  35. data/lib/rails_chatbot/version.rb +3 -0
  36. data/lib/rails_chatbot.rb +29 -0
  37. data/lib/tasks/rails_chatbot_tasks.rake +81 -0
  38. metadata +123 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 13b20ee2717e914aea6e5634f28cee026b971f5b6ec3216f70e6cd48a860159a
4
+ data.tar.gz: 288ca5976edf919441a32264401b7fa26ea249f25de530d9b0319b8b937ba897
5
+ SHA512:
6
+ metadata.gz: ddfdc4e42f4c3bf733f002232879fb96d7a7bfecd8e1a9b1319497d0ded151c8c91853773f10f085f88ffdd1e13b5afc82213865b2d4d450b960cc3a75d1cb46
7
+ data.tar.gz: 05167d520483a23de9df8153f0b5e459e5986e0a6fde86940e5082b6b4b567771eb1433c6498ed2a656c6a1ef51510e5323194ff2d81dbc6e174df687502f9af
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,284 @@
1
+ # RailsChatbot
2
+
3
+ A powerful Rails engine gem that provides an intelligent chatbot system with knowledge base integration for your Ruby on Rails application. The chatbot can answer questions about your application by indexing your models and content.
4
+
5
+ ## � Quick Start Guide
6
+
7
+ ### Step 1: Install the Gem
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem "rails_chatbot"
13
+ ```
14
+
15
+ Then execute:
16
+
17
+ ```bash
18
+ $ bundle install
19
+ ```
20
+
21
+ ### Step 2: Run Migrations
22
+
23
+ ```bash
24
+ $ rails rails_chatbot:install:migrations
25
+ $ rails db:migrate
26
+ ```
27
+
28
+ ### Step 3: Configure OpenAI
29
+
30
+ Create `config/initializers/rails_chatbot.rb`:
31
+
32
+ ```ruby
33
+ RailsChatbot.configure do |config|
34
+ config.openai_api_key = ENV['OPENAI_API_KEY'] # Required
35
+ config.openai_model = 'gpt-4o-mini' # Optional
36
+ config.chatbot_title = 'My App Assistant' # Optional
37
+ end
38
+ ```
39
+
40
+ Set your OpenAI API key:
41
+
42
+ ```bash
43
+ export OPENAI_API_KEY=your_openai_api_key_here
44
+ ```
45
+
46
+ ### Step 4: Mount the Engine
47
+
48
+ Add to your `config/routes.rb`:
49
+
50
+ ```ruby
51
+ Rails.application.routes.draw do
52
+ mount RailsChatbot::Engine => "/chatbot"
53
+
54
+ # Your other routes...
55
+ end
56
+ ```
57
+
58
+ ### Step 5: Start Your Server
59
+
60
+ ```bash
61
+ rails server
62
+ ```
63
+
64
+ Visit `http://localhost:3000/chatbot` to see your chatbot!
65
+
66
+ ## 🧪 Testing Your Chatbot
67
+
68
+ ### 1. Test Basic Chat
69
+ - Open `http://localhost:3000/chatbot`
70
+ - Type "Hello" and send a message
71
+ - The chatbot should respond
72
+
73
+ ### 2. Add Knowledge Base Content
74
+
75
+ ```bash
76
+ # Add custom knowledge
77
+ rails runner "RailsChatbot::KnowledgeIndexer.add_custom_knowledge(
78
+ title: 'User Registration',
79
+ content: 'Users can register by clicking the Sign Up button...',
80
+ source_type: 'help'
81
+ )"
82
+ ```
83
+
84
+ ### 3. Test Knowledge Search
85
+ In the chat, try asking:
86
+ - "How do users register?"
87
+ - "What features are available?"
88
+ - "Tell me about user management"
89
+
90
+ ### 4. Test API Endpoints
91
+
92
+ ```bash
93
+ # Test search
94
+ curl "http://localhost:3000/chatbot/search?q=user"
95
+
96
+ # Test conversation creation
97
+ curl -X POST "http://localhost:3000/chatbot/conversations" \
98
+ -H "Content-Type: application/json" \
99
+ -d '{}'
100
+ ```
101
+
102
+ ### 5. Check Knowledge Base Stats
103
+
104
+ ```bash
105
+ rake app:rails_chatbot:stats
106
+ ```
107
+
108
+ ## 📚 Advanced Usage
109
+
110
+ ### Index Your Models
111
+
112
+ ```bash
113
+ # Index specific models
114
+ rake app:rails_chatbot:index_models[User,Post,Product]
115
+
116
+ # Index all models
117
+ rake app:rails_chatbot:index_all
118
+ ```
119
+
120
+ ### Add Custom Knowledge
121
+
122
+ ```ruby
123
+ # Via code
124
+ RailsChatbot::KnowledgeBase.create!(
125
+ title: "How to Reset Password",
126
+ content: "Click 'Forgot Password' on the login page...",
127
+ source_type: "help"
128
+ )
129
+
130
+ # Via rake task
131
+ rake app:rails_chatbot:add_knowledge['Title','Content','Type']
132
+ ```
133
+
134
+ ### Customize Configuration
135
+
136
+ ```ruby
137
+ RailsChatbot.configure do |config|
138
+ config.openai_api_key = ENV['OPENAI_API_KEY']
139
+ config.openai_model = 'gpt-4o-mini'
140
+ config.chatbot_title = 'Support Assistant'
141
+ config.current_user_proc = proc { |controller| controller.current_user }
142
+ config.indexable_models = [User, Product, Order] # Custom models to index
143
+ end
144
+ ```
145
+
146
+ ## 🔧 Management Commands
147
+
148
+ ```bash
149
+ # Knowledge base management
150
+ rake app:rails_chatbot:stats # View statistics
151
+ rake app:rails_chatbot:clear_knowledge_base # Clear all entries
152
+ rake app:rails_chatbot:add_knowledge['Title','Content','Type'] # Add knowledge
153
+ rake app:rails_chatbot:index_all # Index all models
154
+ rake app:rails_chatbot:index_models[User,Post] # Index specific models
155
+ ```
156
+
157
+ ## 🎯 Common Use Cases
158
+
159
+ ### E-commerce Site
160
+ ```ruby
161
+ # Index products
162
+ rake app:rails_chatbot:index_models[Product,Category]
163
+
164
+ # Add help content
165
+ RailsChatbot::KnowledgeIndexer.add_custom_knowledge(
166
+ title: "Shipping Policy",
167
+ content: "We ship within 3-5 business days...",
168
+ source_type: "policy"
169
+ )
170
+ ```
171
+
172
+ ### SaaS Application
173
+ ```ruby
174
+ # Index user models and features
175
+ rake app:rails_chatbot:index_models[User,Feature,Subscription]
176
+
177
+ # Add feature documentation
178
+ RailsChatbot::KnowledgeIndexer.add_custom_knowledge(
179
+ title: "Dashboard Overview",
180
+ content: "The dashboard shows your usage statistics...",
181
+ source_type: "documentation"
182
+ )
183
+ ```
184
+
185
+ ## 🐛 Troubleshooting
186
+
187
+ ### Common Issues
188
+
189
+ 1. **OpenAI API Errors**
190
+ - Check your API key is valid
191
+ - Verify you have credits in your OpenAI account
192
+ - Try a different model (gpt-3.5-turbo)
193
+
194
+ 2. **No Knowledge Found**
195
+ - Run `rake app:rails_chatbot:index_all` to populate knowledge base
196
+ - Add custom knowledge entries
197
+ - Check `rake app:rails_chatbot:stats` for entries
198
+
199
+ 3. **Search Not Working**
200
+ - Ensure PostgreSQL is configured
201
+ - Check database migrations ran successfully
202
+ - Verify knowledge base has content
203
+
204
+ 4. **Routing Issues**
205
+ - Confirm engine is mounted in routes.rb
206
+ - Check for route conflicts with existing paths
207
+ - Restart Rails server after route changes
208
+
209
+ ### Debug Mode
210
+
211
+ ```ruby
212
+ # In development, add to initializer
213
+ RailsChatbot.configure do |config|
214
+ # ... other config
215
+ Rails.logger.level = :debug
216
+ end
217
+ ```
218
+
219
+ ## 📱 API Reference
220
+
221
+ ### Endpoints
222
+
223
+ - `GET /chatbot` - Chat interface
224
+ - `POST /chatbot/conversations` - Create conversation
225
+ - `GET /chatbot/conversations` - List conversations
226
+ - `POST /chatbot/conversations/:id/messages` - Send message
227
+ - `GET /chatbot/search?q=query` - Search knowledge
228
+
229
+ ### Response Format
230
+
231
+ ```json
232
+ {
233
+ "message": {
234
+ "role": "assistant",
235
+ "content": "Here's the answer...",
236
+ "created_at": "2026-02-12T12:00:00Z"
237
+ },
238
+ "knowledge_sources": [
239
+ {
240
+ "title": "User Guide",
241
+ "source_type": "documentation",
242
+ "source_url": "/docs/users"
243
+ }
244
+ ]
245
+ }
246
+ ```
247
+
248
+ ## 🎨 Customization
249
+
250
+ ### Override Views
251
+
252
+ Create `app/views/rails_chatbot/chat/index.html.erb` in your app to customize the chat interface.
253
+
254
+ ### Custom Styles
255
+
256
+ Add to `app/assets/stylesheets/rails_chatbot/custom.css`:
257
+
258
+ ```css
259
+ .chatbot-container {
260
+ background: your-brand-color;
261
+ border-radius: your-preference;
262
+ }
263
+ ```
264
+
265
+ ### Custom JavaScript
266
+
267
+ Extend functionality in `app/javascript/rails_chatbot/custom.js`.
268
+
269
+ ## 📋 Requirements
270
+
271
+ - Ruby on Rails 8.0.4 or higher
272
+ - PostgreSQL (for full-text search)
273
+ - OpenAI API key
274
+ - Modern web browser
275
+
276
+ ## 📄 License
277
+
278
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
279
+
280
+ ## 🤝 Support
281
+
282
+ - 📖 [Documentation](https://github.com/yourusername/rails_chatbot)
283
+ - 🐛 [Issues](https://github.com/yourusername/rails_chatbot/issues)
284
+ - 💬 [Discussions](https://github.com/yourusername/rails_chatbot/discussions)
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,14 @@
1
+ module RailsChatbot
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception, if: -> { request.format.html? }
4
+ skip_before_action :verify_authenticity_token, if: -> { request.format.json? }
5
+
6
+ private
7
+
8
+ def current_user
9
+ # Override this method in your main application controller
10
+ # or define it in an initializer
11
+ instance_eval(&Rails.application.config.rails_chatbot.current_user_proc) if Rails.application.config.rails_chatbot&.current_user_proc
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module RailsChatbot
2
+ class ChatController < ApplicationController
3
+ def index
4
+ # Main chat interface
5
+ end
6
+
7
+ def search
8
+ query = params[:q].to_s.strip
9
+ return render json: { results: [] } if query.blank?
10
+
11
+ service = KnowledgeRetrievalService.new(query: query, limit: 10)
12
+ results = service.retrieve
13
+
14
+ render json: {
15
+ results: results.map do |result|
16
+ {
17
+ title: result[:title],
18
+ content: result[:content].truncate(200),
19
+ source_type: result[:source_type],
20
+ source_id: result[:source_id],
21
+ source_url: result[:source_url]
22
+ }
23
+ end
24
+ }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,48 @@
1
+ module RailsChatbot
2
+ class ConversationsController < ApplicationController
3
+ before_action :set_conversation, only: [:show, :destroy]
4
+
5
+ def index
6
+ @conversations = Conversation.where(session_id: session_id)
7
+ .recent
8
+ .limit(20)
9
+ render json: @conversations.map { |c| { id: c.id, title: c.title, created_at: c.created_at } }
10
+ end
11
+
12
+ def show
13
+ @messages = @conversation.messages.order(created_at: :asc)
14
+ render json: {
15
+ conversation: {
16
+ id: @conversation.id,
17
+ title: @conversation.title,
18
+ created_at: @conversation.created_at
19
+ },
20
+ messages: @messages.map { |m| { role: m.role, content: m.content, created_at: m.created_at } }
21
+ }
22
+ end
23
+
24
+ def create
25
+ @conversation = Conversation.find_or_create_by(session_id: session_id) do |conv|
26
+ conv.title = params[:title] || "Conversation #{Time.current.strftime('%Y-%m-%d %H:%M')}"
27
+ conv.user = current_user if respond_to?(:current_user) && current_user
28
+ end
29
+
30
+ render json: { conversation_id: @conversation.id, title: @conversation.title }
31
+ end
32
+
33
+ def destroy
34
+ @conversation.destroy
35
+ head :no_content
36
+ end
37
+
38
+ private
39
+
40
+ def set_conversation
41
+ @conversation = Conversation.find_by!(id: params[:id], session_id: session_id)
42
+ end
43
+
44
+ def session_id
45
+ @session_id ||= session[:chatbot_session_id] ||= SecureRandom.uuid
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,55 @@
1
+ module RailsChatbot
2
+ class MessagesController < ApplicationController
3
+ before_action :set_conversation
4
+
5
+ def create
6
+ user_message = params[:message].to_s.strip
7
+
8
+ if user_message.blank?
9
+ render json: { error: 'Message cannot be blank' }, status: :unprocessable_entity
10
+ return
11
+ end
12
+
13
+ chat_service = ChatService.new(conversation: @conversation)
14
+ result = chat_service.process_message(user_message)
15
+
16
+ render json: {
17
+ message: {
18
+ role: 'assistant',
19
+ content: result[:response],
20
+ created_at: Time.current
21
+ },
22
+ knowledge_sources: result[:knowledge_sources].map do |source|
23
+ {
24
+ title: source[:title],
25
+ source_type: source[:source_type],
26
+ source_id: source[:source_id],
27
+ source_url: source[:source_url]
28
+ }
29
+ end
30
+ }
31
+ rescue => e
32
+ Rails.logger.error("Chat error: #{e.message}\n#{e.backtrace.join("\n")}")
33
+ render json: { error: 'An error occurred while processing your message' }, status: :internal_server_error
34
+ end
35
+
36
+ private
37
+
38
+ def set_conversation
39
+ conversation_id = params[:conversation_id] || params[:conversation][:id] rescue nil
40
+
41
+ if conversation_id
42
+ @conversation = Conversation.find_by!(id: conversation_id, session_id: session_id)
43
+ else
44
+ @conversation = Conversation.find_or_create_by(session_id: session_id) do |conv|
45
+ conv.title = "Conversation #{Time.current.strftime('%Y-%m-%d %H:%M')}"
46
+ conv.user = current_user if respond_to?(:current_user) && current_user
47
+ end
48
+ end
49
+ end
50
+
51
+ def session_id
52
+ @session_id ||= session[:chatbot_session_id] ||= SecureRandom.uuid
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,12 @@
1
+ module RailsChatbot
2
+ module ApplicationHelper
3
+ def rails_chatbot_routes
4
+ routes = {
5
+ conversations_path: rails_chatbot.conversations_path,
6
+ messages_path: rails_chatbot.messages_path,
7
+ conversation_messages_path_template: rails_chatbot.conversation_messages_path(':id')
8
+ }
9
+ routes.to_json.html_safe
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,163 @@
1
+ // Chatbot controller using Stimulus (or vanilla JS)
2
+ document.addEventListener('DOMContentLoaded', function() {
3
+ const chatbotContainer = document.querySelector('[data-controller="chatbot"]');
4
+ if (!chatbotContainer) return;
5
+
6
+ const messagesContainer = chatbotContainer.querySelector('[data-chatbot-target="messages"]');
7
+ const input = chatbotContainer.querySelector('[data-chatbot-target="input"]');
8
+ const sendButton = chatbotContainer.querySelector('[data-chatbot-target="sendButton"]');
9
+ let conversationId = null;
10
+ let isLoading = false;
11
+
12
+ // Initialize conversation
13
+ function initializeConversation() {
14
+ fetch(window.RailsChatbot.routes.conversationsPath(), {
15
+ method: 'POST',
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.content || ''
19
+ },
20
+ body: JSON.stringify({})
21
+ })
22
+ .then(response => response.json())
23
+ .then(data => {
24
+ conversationId = data.conversation_id;
25
+ })
26
+ .catch(error => console.error('Error initializing conversation:', error));
27
+ }
28
+
29
+ // Add message to UI
30
+ function addMessage(role, content, knowledgeSources = []) {
31
+ const messageDiv = document.createElement('div');
32
+ messageDiv.className = `message ${role}`;
33
+
34
+ const bubble = document.createElement('div');
35
+ bubble.className = 'message-bubble';
36
+ bubble.textContent = content;
37
+
38
+ messageDiv.appendChild(bubble);
39
+
40
+ if (knowledgeSources && knowledgeSources.length > 0) {
41
+ const sourcesDiv = document.createElement('div');
42
+ sourcesDiv.className = 'knowledge-sources';
43
+ sourcesDiv.innerHTML = 'Sources: ' + knowledgeSources.map(s => {
44
+ if (s.source_url) {
45
+ return `<a href="${s.source_url}" target="_blank">${s.title}</a>`;
46
+ }
47
+ return s.title;
48
+ }).join(', ');
49
+ messageDiv.appendChild(sourcesDiv);
50
+ }
51
+
52
+ const timeDiv = document.createElement('div');
53
+ timeDiv.className = 'message-time';
54
+ timeDiv.textContent = new Date().toLocaleTimeString();
55
+ messageDiv.appendChild(timeDiv);
56
+
57
+ messagesContainer.appendChild(messageDiv);
58
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
59
+ }
60
+
61
+ // Show loading indicator
62
+ function showLoading() {
63
+ const loadingDiv = document.createElement('div');
64
+ loadingDiv.className = 'message assistant';
65
+ loadingDiv.id = 'loading-message';
66
+ loadingDiv.innerHTML = '<div class="message-bubble"><div class="loading"></div></div>';
67
+ messagesContainer.appendChild(loadingDiv);
68
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
69
+ }
70
+
71
+ // Remove loading indicator
72
+ function removeLoading() {
73
+ const loading = document.getElementById('loading-message');
74
+ if (loading) loading.remove();
75
+ }
76
+
77
+ // Send message
78
+ function sendMessage() {
79
+ const message = input.value.trim();
80
+ if (!message || isLoading) return;
81
+
82
+ // Add user message to UI
83
+ addMessage('user', message);
84
+ input.value = '';
85
+ isLoading = true;
86
+ sendButton.disabled = true;
87
+ showLoading();
88
+
89
+ // Determine endpoint
90
+ const url = conversationId
91
+ ? window.RailsChatbot.routes.conversationMessagesPath(conversationId)
92
+ : window.RailsChatbot.routes.messagesPath();
93
+
94
+ // Send to server
95
+ fetch(url, {
96
+ method: 'POST',
97
+ headers: {
98
+ 'Content-Type': 'application/json',
99
+ 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.content || ''
100
+ },
101
+ body: JSON.stringify({ message: message })
102
+ })
103
+ .then(response => response.json())
104
+ .then(data => {
105
+ removeLoading();
106
+
107
+ if (data.error) {
108
+ addMessage('assistant', `Error: ${data.error}`);
109
+ } else {
110
+ if (!conversationId && data.conversation_id) {
111
+ conversationId = data.conversation_id;
112
+ }
113
+ addMessage('assistant', data.message.content, data.knowledge_sources || []);
114
+ }
115
+ })
116
+ .catch(error => {
117
+ removeLoading();
118
+ addMessage('assistant', 'Sorry, there was an error processing your message. Please try again.');
119
+ console.error('Error:', error);
120
+ })
121
+ .finally(() => {
122
+ isLoading = false;
123
+ sendButton.disabled = false;
124
+ input.focus();
125
+ });
126
+ }
127
+
128
+ // Handle key press
129
+ function handleKeyDown(event) {
130
+ if (event.key === 'Enter' && !event.shiftKey) {
131
+ event.preventDefault();
132
+ sendMessage();
133
+ }
134
+ }
135
+
136
+ // Attach event listeners
137
+ sendButton.addEventListener('click', sendMessage);
138
+ input.addEventListener('keydown', handleKeyDown);
139
+
140
+ // Initialize conversation on load
141
+ initializeConversation();
142
+ });
143
+
144
+ // Routes helper - populated from server
145
+ if (window.RailsChatbot && window.RailsChatbot.routes) {
146
+ const routes = typeof window.RailsChatbot.routes === 'string'
147
+ ? JSON.parse(window.RailsChatbot.routes)
148
+ : window.RailsChatbot.routes;
149
+
150
+ window.RailsChatbot.routes = {
151
+ conversationsPath: () => routes.conversations_path,
152
+ conversationMessagesPath: (id) => routes.conversation_messages_path_template.replace(':id', id),
153
+ messagesPath: () => routes.messages_path
154
+ };
155
+ } else {
156
+ // Fallback routes
157
+ window.RailsChatbot = window.RailsChatbot || {};
158
+ window.RailsChatbot.routes = {
159
+ conversationsPath: () => '/rails_chatbot/conversations',
160
+ conversationMessagesPath: (id) => `/rails_chatbot/conversations/${id}/messages`,
161
+ messagesPath: () => '/rails_chatbot/messages'
162
+ };
163
+ }
@@ -0,0 +1,4 @@
1
+ module RailsChatbot
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module RailsChatbot
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module RailsChatbot
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end