llama_bot_rails 0.1.2 → 0.1.4
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 +4 -4
- data/README.md +18 -6
- data/app/assets/javascripts/llama_bot_rails/application.js +18 -3
- data/app/channels/llama_bot_rails/chat_channel.rb +9 -6
- data/app/views/llama_bot_rails/agent/chat.html.erb +209 -32
- data/lib/generators/llama_bot_rails/install/install_generator.rb +46 -0
- data/lib/llama_bot_rails/engine.rb +3 -0
- data/lib/llama_bot_rails/version.rb +1 -1
- metadata +16 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bd4c2d542ae02f6d430d6c6233792445b79dcff44b265d508c9d985db22b09b
|
4
|
+
data.tar.gz: '09ad72a1840000cf121133633dc104517e5937a843be0cba48dadd0a3bf60a66'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 192fe783f3c6e91170bb3109dbb116d21ca7bb6c68ecb064a92435d416efa16a1e765e5ff59d07af5978e348724e9f70569dbbf2faaeab8ac9a432f7df1712e6
|
7
|
+
data.tar.gz: 02362ff59435fd5a0b88038f276e2cc69d8fd70fac868b16ee4c1fa98bfb4ae36c298e541aec388a37856de636b9ee841bff81be971fbae50949925d2c9e3cd2
|
data/README.md
CHANGED
@@ -27,7 +27,6 @@ Chat with a powerful agent that has access to your models, your application cont
|
|
27
27
|
|
28
28
|
## 🚀 **Quickstart** →
|
29
29
|
|
30
|
-
|
31
30
|
```bash
|
32
31
|
|
33
32
|
# 1. Add the gem
|
@@ -38,14 +37,27 @@ rails generate llama_bot_rails:install
|
|
38
37
|
|
39
38
|
# 3. Clone & run the LangGraph backend
|
40
39
|
git clone https://github.com/kodykendall/llamabot
|
41
|
-
cd llamabot
|
42
|
-
OPENAI_API_KEY=your_key
|
43
|
-
cd backend && uvicorn app:app --reload
|
44
40
|
|
45
|
-
#
|
41
|
+
# 4. Set up your environment
|
42
|
+
python3 -m venv venv
|
43
|
+
|
44
|
+
source venv/bin/activate
|
45
|
+
|
46
|
+
pip install -r requirements.txt
|
47
|
+
|
48
|
+
echo "OPENAI_API_KEY=your_openai_api_key_here" > .env
|
49
|
+
|
50
|
+
# 5. Run the agent
|
51
|
+
cd backend
|
52
|
+
uvicorn app:app --reload
|
53
|
+
|
54
|
+
# 6. Confirm our agent is running properly. You should see: Hello, World! 🦙💬
|
55
|
+
curl http://localhost:8000/hello
|
56
|
+
|
57
|
+
# 7. Start your Rails server.
|
46
58
|
rails server
|
47
59
|
|
48
|
-
# Visit the chat interface and start chatting.
|
60
|
+
# 8. Visit the chat interface and start chatting.
|
49
61
|
open http://localhost:3000/llama_bot/agent/chat
|
50
62
|
|
51
63
|
```
|
@@ -1,7 +1,22 @@
|
|
1
1
|
//= require action_cable
|
2
2
|
//= require_self
|
3
3
|
|
4
|
-
(function() {
|
4
|
+
(function () {
|
5
5
|
this.LlamaBotRails = this.LlamaBotRails || {};
|
6
|
-
|
7
|
-
|
6
|
+
|
7
|
+
function createLlamaCable() {
|
8
|
+
if (window.ActionCable && !LlamaBotRails.cable) {
|
9
|
+
console.log("🦙 Creating LlamaBot ActionCable consumer");
|
10
|
+
LlamaBotRails.cable = ActionCable.createConsumer();
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
// Run immediately if ActionCable is already present (classic asset pipeline)
|
15
|
+
if (window.ActionCable) {
|
16
|
+
createLlamaCable();
|
17
|
+
} else {
|
18
|
+
// Wait until DOM + importmap load finishes
|
19
|
+
document.addEventListener("DOMContentLoaded", createLlamaCable);
|
20
|
+
document.addEventListener("turbo:load", createLlamaCable); // covers Turbo + importmap apps
|
21
|
+
}
|
22
|
+
}).call(this);
|
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'async'
|
2
|
+
require 'async/http'
|
3
|
+
require 'async/websocket'
|
4
|
+
|
1
5
|
require 'json' # Ensure JSON is required if not already
|
2
6
|
|
3
7
|
module LlamaBotRails
|
@@ -147,28 +151,27 @@ module LlamaBotRails
|
|
147
151
|
Rails.logger.info "[LlamaBot] Setting up external websocket for connection: #{connection_id}"
|
148
152
|
|
149
153
|
# Check if the WebSocket URL is configured
|
150
|
-
websocket_url =
|
154
|
+
websocket_url = Rails.application.config.llama_bot_rails.websocket_url
|
151
155
|
if websocket_url.blank?
|
152
|
-
Rails.logger.warn "[LlamaBot]
|
156
|
+
Rails.logger.warn "[LlamaBot] LlamaBot Websocket URL is not configured in the config/initializers/llama_bot_rails.rb file, skipping external WebSocket setup"
|
153
157
|
return
|
154
158
|
end
|
155
159
|
|
156
|
-
# endpoint = Async::HTTP::Endpoint.parse(ENV['LLAMABOT_WEBSOCKET_URL'])
|
157
160
|
uri = URI(websocket_url)
|
158
161
|
|
159
162
|
uri.scheme = 'wss'
|
160
|
-
uri.scheme = 'ws' if
|
163
|
+
uri.scheme = 'ws' if Rails.env.development?
|
161
164
|
|
162
165
|
endpoint = Async::HTTP::Endpoint.new(
|
163
166
|
uri,
|
164
167
|
ssl_context: OpenSSL::SSL::SSLContext.new.tap do |ctx|
|
165
168
|
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
166
|
-
if
|
169
|
+
if Rails.env.staging?
|
167
170
|
ctx.ca_file = '/usr/local/etc/ca-certificates/cert.pem'
|
168
171
|
# M2 Air : ctx.ca_file = '/etc//ssl/cert.pem'
|
169
172
|
ctx.cert = OpenSSL::X509::Certificate.new(File.read(File.expand_path('~/.ssl/llamapress/cert.pem')))
|
170
173
|
ctx.key = OpenSSL::PKey::RSA.new(File.read(File.expand_path('~/.ssl/llamapress/key.pem')))
|
171
|
-
elsif
|
174
|
+
elsif Rails.env.development?
|
172
175
|
# do no ctx stuff
|
173
176
|
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
174
177
|
else # production
|
@@ -405,7 +405,111 @@
|
|
405
405
|
font-size: 1em;
|
406
406
|
color: var(--text-secondary);
|
407
407
|
}
|
408
|
+
|
409
|
+
/* Clean loading indicator - just animated text */
|
410
|
+
.loading-indicator {
|
411
|
+
display: none;
|
412
|
+
align-items: center;
|
413
|
+
padding: 16px 20px;
|
414
|
+
color: var(--text-secondary);
|
415
|
+
font-size: 14px;
|
416
|
+
margin-bottom: 10px;
|
417
|
+
background: rgba(255, 255, 255, 0.02);
|
418
|
+
border-radius: 8px;
|
419
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
420
|
+
}
|
421
|
+
|
422
|
+
.loading-indicator.visible {
|
423
|
+
display: flex;
|
424
|
+
}
|
425
|
+
|
426
|
+
.loading-text {
|
427
|
+
font-style: italic;
|
428
|
+
}
|
429
|
+
|
430
|
+
.loading-dots::after {
|
431
|
+
content: '';
|
432
|
+
animation: dots 1.5s steps(4, end) infinite;
|
433
|
+
}
|
434
|
+
|
435
|
+
@keyframes dots {
|
436
|
+
0%, 20% { content: ''; }
|
437
|
+
40% { content: '.'; }
|
438
|
+
60% { content: '..'; }
|
439
|
+
80%, 100% { content: '...'; }
|
440
|
+
}
|
441
|
+
|
442
|
+
/* Suggested Prompts Styling - Always visible above input */
|
443
|
+
.suggested-prompts {
|
444
|
+
margin-bottom: 16px;
|
445
|
+
padding: 0 4px;
|
446
|
+
}
|
447
|
+
|
448
|
+
.prompts-label {
|
449
|
+
font-size: 13px;
|
450
|
+
color: var(--text-secondary);
|
451
|
+
margin-bottom: 8px;
|
452
|
+
font-weight: 500;
|
453
|
+
letter-spacing: 0.3px;
|
454
|
+
}
|
455
|
+
|
456
|
+
.prompts-container {
|
457
|
+
display: flex;
|
458
|
+
flex-direction: column;
|
459
|
+
gap: 6px;
|
460
|
+
}
|
461
|
+
|
462
|
+
.prompts-row {
|
463
|
+
display: flex;
|
464
|
+
gap: 8px;
|
465
|
+
overflow-x: auto;
|
466
|
+
padding: 2px;
|
467
|
+
scrollbar-width: none; /* Firefox */
|
468
|
+
-ms-overflow-style: none; /* IE and Edge */
|
469
|
+
}
|
470
|
+
|
471
|
+
.prompts-row::-webkit-scrollbar {
|
472
|
+
display: none; /* Chrome, Safari, Opera */
|
473
|
+
}
|
474
|
+
|
475
|
+
.prompt-button {
|
476
|
+
background: rgba(255, 255, 255, 0.03);
|
477
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
478
|
+
border-radius: 6px;
|
479
|
+
padding: 8px 12px;
|
480
|
+
color: var(--text-secondary);
|
481
|
+
font-size: 13px;
|
482
|
+
cursor: pointer;
|
483
|
+
transition: all 0.2s ease;
|
484
|
+
font-family: inherit;
|
485
|
+
white-space: nowrap;
|
486
|
+
flex-shrink: 0;
|
487
|
+
min-width: fit-content;
|
488
|
+
}
|
489
|
+
|
490
|
+
.prompt-button:hover {
|
491
|
+
background: rgba(33, 150, 243, 0.08);
|
492
|
+
border-color: rgba(33, 150, 243, 0.2);
|
493
|
+
color: var(--text-primary);
|
494
|
+
transform: translateY(-1px);
|
495
|
+
}
|
496
|
+
|
497
|
+
.prompt-button:active {
|
498
|
+
transform: translateY(0);
|
499
|
+
}
|
500
|
+
|
501
|
+
@media (max-width: 768px) {
|
502
|
+
.prompts-grid {
|
503
|
+
grid-template-columns: 1fr;
|
504
|
+
}
|
505
|
+
|
506
|
+
.prompt-button {
|
507
|
+
font-size: 13px;
|
508
|
+
padding: 10px 14px;
|
509
|
+
}
|
510
|
+
}
|
408
511
|
</style>
|
512
|
+
<%= javascript_importmap_tags %>
|
409
513
|
<%= javascript_include_tag "llama_bot_rails/application" %>
|
410
514
|
<%= action_cable_meta_tag %>
|
411
515
|
<!-- Add Snarkdown CDN -->
|
@@ -430,7 +534,7 @@
|
|
430
534
|
</button>
|
431
535
|
<div class="logo-container">
|
432
536
|
<img src="https://service-jobs-images.s3.us-east-2.amazonaws.com/7rl98t1weu387r43il97h6ipk1l7" alt="LlamaBot Logo" class="logo">
|
433
|
-
<div id="connectionStatusIconForLlamaBot" class="connection-status status-
|
537
|
+
<div id="connectionStatusIconForLlamaBot" class="connection-status status-yellow"></div>
|
434
538
|
</div>
|
435
539
|
<h1>LlamaBot Chat</h1>
|
436
540
|
</div>
|
@@ -444,6 +548,30 @@
|
|
444
548
|
<div class="chat-messages" id="chat-messages">
|
445
549
|
<!-- Messages will be added here dynamically -->
|
446
550
|
</div>
|
551
|
+
|
552
|
+
<!-- Simple loading indicator with just animated text -->
|
553
|
+
<div class="loading-indicator" id="loading-indicator">
|
554
|
+
<span class="loading-text">LlamaBot is thinking<span class="loading-dots"></span></span>
|
555
|
+
</div>
|
556
|
+
|
557
|
+
<!-- Suggested Prompts - Always visible above input -->
|
558
|
+
<div class="suggested-prompts" id="suggested-prompts">
|
559
|
+
<div class="prompts-label">Quick actions:</div>
|
560
|
+
<div class="prompts-container">
|
561
|
+
<div class="prompts-row">
|
562
|
+
<button class="prompt-button" onclick="selectPrompt(this)">What models are defined in this app?</button>
|
563
|
+
<button class="prompt-button" onclick="selectPrompt(this)">What routes exist?</button>
|
564
|
+
<button class="prompt-button" onclick="selectPrompt(this)">How many users are in the database?</button>
|
565
|
+
<button class="prompt-button" onclick="selectPrompt(this)">Show me the schema for the User model</button>
|
566
|
+
</div>
|
567
|
+
<div class="prompts-row">
|
568
|
+
<button class="prompt-button" onclick="selectPrompt(this)">Send a text with Twilio</button>
|
569
|
+
<button class="prompt-button" onclick="selectPrompt(this)">Create a BlogPost with title and body fields</button>
|
570
|
+
<button class="prompt-button" onclick="selectPrompt(this)">Generate a scaffolded Page model</button>
|
571
|
+
</div>
|
572
|
+
</div>
|
573
|
+
</div>
|
574
|
+
|
447
575
|
<div class="input-container">
|
448
576
|
<input type="text" id="message-input" placeholder="Type your message...">
|
449
577
|
<button onclick="sendMessage()">Send</button>
|
@@ -465,37 +593,49 @@
|
|
465
593
|
let redStatusStartTime = null;
|
466
594
|
let errorModalShown = false;
|
467
595
|
let connectionCheckInterval;
|
596
|
+
let subscription = null;
|
468
597
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
},
|
482
|
-
|
483
|
-
|
484
|
-
|
598
|
+
function waitForCableConnection(callback) {
|
599
|
+
const interval = setInterval(() => {
|
600
|
+
if (window.LlamaBotRails && LlamaBotRails.cable) {
|
601
|
+
clearInterval(interval);
|
602
|
+
callback(LlamaBotRails.cable);
|
603
|
+
}
|
604
|
+
}, 50);
|
605
|
+
}
|
606
|
+
|
607
|
+
waitForCableConnection((consumer) => {
|
608
|
+
const sessionId = crypto.randomUUID();
|
609
|
+
|
610
|
+
subscription = consumer.subscriptions.create({channel: 'LlamaBotRails::ChatChannel', session_id: sessionId}, {
|
611
|
+
connected() {
|
612
|
+
console.log('Connected to chat channel');
|
613
|
+
lastPongTime = Date.now();
|
614
|
+
loadThreads();
|
615
|
+
startConnectionCheck();
|
616
|
+
},
|
617
|
+
disconnected() {
|
618
|
+
console.log('Disconnected from chat channel');
|
619
|
+
updateStatusIcon('status-red');
|
620
|
+
},
|
621
|
+
received(data) {
|
622
|
+
const parsedData = JSON.parse(data).message;
|
623
|
+
switch (parsedData.type) {
|
485
624
|
case "ai":
|
486
625
|
addMessage(parsedData.content, parsedData.type, parsedData.base_message);
|
487
626
|
break;
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
627
|
+
case "tool":
|
628
|
+
addMessage(parsedData.content, parsedData.type, parsedData.base_message);
|
629
|
+
break;
|
630
|
+
case "error":
|
631
|
+
addMessage(parsedData.content, parsedData.type, parsedData.base_message);
|
632
|
+
break;
|
633
|
+
case "pong":
|
634
|
+
lastPongTime = Date.now();
|
635
|
+
break;
|
636
|
+
}
|
497
637
|
}
|
498
|
-
}
|
638
|
+
});
|
499
639
|
});
|
500
640
|
|
501
641
|
function startConnectionCheck() {
|
@@ -681,11 +821,6 @@
|
|
681
821
|
|
682
822
|
// Show welcome message
|
683
823
|
showWelcomeMessage();
|
684
|
-
|
685
|
-
// Clear active thread selection
|
686
|
-
document.querySelectorAll('.thread-item').forEach(item => {
|
687
|
-
item.classList.remove('active');
|
688
|
-
});
|
689
824
|
}
|
690
825
|
|
691
826
|
function showWelcomeMessage() {
|
@@ -699,11 +834,45 @@
|
|
699
834
|
messagesDiv.appendChild(welcomeDiv);
|
700
835
|
}
|
701
836
|
|
837
|
+
function showLoadingIndicator() {
|
838
|
+
const loadingIndicator = document.getElementById('loading-indicator');
|
839
|
+
loadingIndicator.classList.add('visible');
|
840
|
+
}
|
841
|
+
|
842
|
+
function hideLoadingIndicator() {
|
843
|
+
const loadingIndicator = document.getElementById('loading-indicator');
|
844
|
+
loadingIndicator.classList.remove('visible');
|
845
|
+
}
|
846
|
+
|
847
|
+
function selectPrompt(buttonElement) {
|
848
|
+
const promptText = buttonElement.textContent;
|
849
|
+
const messageInput = document.getElementById('message-input');
|
850
|
+
|
851
|
+
// Populate the input field
|
852
|
+
messageInput.value = promptText;
|
853
|
+
|
854
|
+
// Focus the input field for better UX
|
855
|
+
messageInput.focus();
|
856
|
+
|
857
|
+
// Add a subtle animation to show the prompt was selected
|
858
|
+
buttonElement.style.transform = 'scale(0.98)';
|
859
|
+
setTimeout(() => {
|
860
|
+
buttonElement.style.transform = '';
|
861
|
+
}, 150);
|
862
|
+
}
|
863
|
+
|
702
864
|
function sendMessage() {
|
703
865
|
const input = document.getElementById('message-input');
|
704
866
|
const message = input.value.trim();
|
705
867
|
|
706
868
|
if (message) {
|
869
|
+
// Check if subscription is available
|
870
|
+
if (!subscription) {
|
871
|
+
console.error('WebSocket connection not established yet');
|
872
|
+
addMessage('Connection not ready. Please wait...', 'error');
|
873
|
+
return;
|
874
|
+
}
|
875
|
+
|
707
876
|
// Clear welcome message if it exists
|
708
877
|
const welcomeMessage = document.querySelector('.welcome-message');
|
709
878
|
if (welcomeMessage) {
|
@@ -713,6 +882,9 @@
|
|
713
882
|
addMessage(message, 'human');
|
714
883
|
input.value = '';
|
715
884
|
|
885
|
+
// Show loading indicator
|
886
|
+
showLoadingIndicator();
|
887
|
+
|
716
888
|
// Generate timestamp-based thread ID if we don't have one
|
717
889
|
let threadId = currentThreadId;
|
718
890
|
if (!threadId || threadId === 'global_thread_id') {
|
@@ -746,6 +918,11 @@
|
|
746
918
|
function addMessage(text, sender, base_message=null) {
|
747
919
|
console.log('🧠 Message from LlamaBot:', text, sender, base_message);
|
748
920
|
|
921
|
+
// Hide loading indicator when we receive an AI response
|
922
|
+
if (sender === 'ai') {
|
923
|
+
hideLoadingIndicator();
|
924
|
+
}
|
925
|
+
|
749
926
|
const messagesDiv = document.getElementById('chat-messages');
|
750
927
|
const messageDiv = document.createElement('div');
|
751
928
|
messageDiv.className = `message ${sender}-message`;
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# lib/generators/llama_bot_rails/install/install_generator.rb
|
2
|
+
module LlamaBotRails
|
3
|
+
module Generators
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
5
|
+
source_root File.expand_path("templates", __dir__)
|
6
|
+
|
7
|
+
def mount_engine
|
8
|
+
say <<~MSG, :yellow
|
9
|
+
⚠️ NOTE: LlamaBotRails requires ActionCable to be available on the frontend.
|
10
|
+
|
11
|
+
If you're using ImportMap (Rails 7 default), run:
|
12
|
+
|
13
|
+
bin/importmap pin @rails/actioncable
|
14
|
+
|
15
|
+
And in app/javascript/application.js, add:
|
16
|
+
|
17
|
+
import * as ActionCable from "@rails/actioncable"
|
18
|
+
window.ActionCable = ActionCable
|
19
|
+
|
20
|
+
If you're using Webpacker or jsbundling-rails:
|
21
|
+
Add @rails/actioncable via yarn/npm
|
22
|
+
And import + expose it the same way in your JS pack.
|
23
|
+
|
24
|
+
📘 See README → “JavaScript Setup” for full details.
|
25
|
+
|
26
|
+
MSG
|
27
|
+
|
28
|
+
route 'mount LlamaBotRails::Engine => "/llama_bot"'
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_initializer
|
32
|
+
create_file "config/initializers/llama_bot_rails.rb", <<~RUBY
|
33
|
+
Rails.application.configure do
|
34
|
+
config.llama_bot_rails.websocket_url = ENV.fetch("LLAMABOT_WEBSOCKET_URL", "ws://localhost:8000/ws")
|
35
|
+
config.llama_bot_rails.enable_console_tool = !Rails.env.production?
|
36
|
+
end
|
37
|
+
RUBY
|
38
|
+
end
|
39
|
+
|
40
|
+
def finish
|
41
|
+
say "\n✅ LlamaBotRails installed! Visit http://localhost:3000/llama_bot/agent/chat\n", :green
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -11,6 +11,9 @@ module LlamaBotRails
|
|
11
11
|
end
|
12
12
|
|
13
13
|
config.llama_bot_rails = ActiveSupport::OrderedOptions.new
|
14
|
+
config.llama_bot_rails.websocket_url = 'ws://localhost:8000/ws' # <-- default
|
15
|
+
config.llama_bot_rails.llamabot_api_url = "http://localhost:8000"
|
16
|
+
config.llama_bot_rails.enable_console_tool = true
|
14
17
|
|
15
18
|
initializer "llama_bot_rails.assets.precompile" do |app|
|
16
19
|
app.config.assets.precompile += %w( llama_bot_rails/application.js )
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: llama_bot_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kody Kendall
|
@@ -77,6 +77,20 @@ dependencies:
|
|
77
77
|
- - ">="
|
78
78
|
- !ruby/object:Gem::Version
|
79
79
|
version: '0'
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: async
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
type: :runtime
|
88
|
+
prerelease: false
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
80
94
|
description: LlamaBotRails is a gem that turns your existing Rails App into an AI
|
81
95
|
Agent by connecting it to an open source LangGraph agent, LlamaBot.
|
82
96
|
email:
|
@@ -107,6 +121,7 @@ files:
|
|
107
121
|
- bin/rubocop
|
108
122
|
- config/initializers/llama_bot_rails.rb
|
109
123
|
- config/routes.rb
|
124
|
+
- lib/generators/llama_bot_rails/install/install_generator.rb
|
110
125
|
- lib/llama_bot_rails.rb
|
111
126
|
- lib/llama_bot_rails/agent_state_builder.rb
|
112
127
|
- lib/llama_bot_rails/engine.rb
|