llama_bot_rails 0.1.3 → 0.1.5
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 +19 -5
- data/app/views/llama_bot_rails/agent/chat.html.erb +176 -9
- data/lib/generators/llama_bot_rails/install/install_generator.rb +6 -0
- data/lib/generators/llama_bot_rails/install/templates/agent_prompt.txt +1 -0
- data/lib/llama_bot_rails/agent_state_builder.rb +1 -0
- data/lib/llama_bot_rails/engine.rb +1 -1
- data/lib/llama_bot_rails/version.rb +1 -1
- data/lib/llama_bot_rails.rb +17 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e2df41e770f0dbcc740af59357462c33b0bcb87d3dd0394e343729d4d1a4a45
|
4
|
+
data.tar.gz: b7032c39fe95fc8e5dc5ab892c8abd28366c77db7c4826a29f6096511124e9ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b4ad9c9ce5bbb3bbb3d645c3db6e87cff9fc398c0ce412c75d7687c79b7532c751518add6f583566ebb20423e465a1b6f81611448ddf08de4a97d2f5704fc79
|
7
|
+
data.tar.gz: 172d5d32f0b0742ca51f6c47468019c628d7043e20b212324fc4f8da987a3aabd0e06dd5375375bf645cb62377f7ca2b12b227f640ff54b494dc52c050a18484
|
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,29 @@ 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
|
40
|
+
|
41
41
|
cd llamabot
|
42
|
-
OPENAI_API_KEY=your_key
|
43
|
-
cd backend && uvicorn app:app --reload
|
44
42
|
|
45
|
-
#
|
43
|
+
# 4. Set up your environment
|
44
|
+
python3 -m venv venv
|
45
|
+
|
46
|
+
source venv/bin/activate
|
47
|
+
|
48
|
+
pip install -r requirements.txt
|
49
|
+
|
50
|
+
echo "OPENAI_API_KEY=your_openai_api_key_here" > .env
|
51
|
+
|
52
|
+
# 5. Run the agent
|
53
|
+
cd backend
|
54
|
+
uvicorn app:app --reload
|
55
|
+
|
56
|
+
# 6. Confirm our agent is running properly. You should see: Hello, World! 🦙💬
|
57
|
+
curl http://localhost:8000/hello
|
58
|
+
|
59
|
+
# 7. Start your Rails server.
|
46
60
|
rails server
|
47
61
|
|
48
|
-
# Visit the chat interface and start chatting.
|
62
|
+
# 8. Visit the chat interface and start chatting.
|
49
63
|
open http://localhost:3000/llama_bot/agent/chat
|
50
64
|
|
51
65
|
```
|
@@ -405,10 +405,121 @@
|
|
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>
|
409
|
-
|
512
|
+
|
513
|
+
<% if defined?(javascript_importmap_tags) %> <!-- Rails 7+ -->
|
514
|
+
<%= javascript_importmap_tags %>
|
515
|
+
<% else %> <!-- Rails 6 -->
|
516
|
+
<%= javascript_include_tag "application" %>
|
517
|
+
<% end %>
|
518
|
+
|
410
519
|
<%= javascript_include_tag "llama_bot_rails/application" %>
|
411
|
-
|
520
|
+
<% if defined?(action_cable_meta_tag) %>
|
521
|
+
<%= action_cable_meta_tag %>
|
522
|
+
<% end %>
|
412
523
|
<!-- Add Snarkdown CDN -->
|
413
524
|
<script src="https://unpkg.com/snarkdown/dist/snarkdown.umd.js"></script>
|
414
525
|
</head>
|
@@ -431,7 +542,7 @@
|
|
431
542
|
</button>
|
432
543
|
<div class="logo-container">
|
433
544
|
<img src="https://service-jobs-images.s3.us-east-2.amazonaws.com/7rl98t1weu387r43il97h6ipk1l7" alt="LlamaBot Logo" class="logo">
|
434
|
-
<div id="connectionStatusIconForLlamaBot" class="connection-status status-
|
545
|
+
<div id="connectionStatusIconForLlamaBot" class="connection-status status-yellow"></div>
|
435
546
|
</div>
|
436
547
|
<h1>LlamaBot Chat</h1>
|
437
548
|
</div>
|
@@ -445,6 +556,30 @@
|
|
445
556
|
<div class="chat-messages" id="chat-messages">
|
446
557
|
<!-- Messages will be added here dynamically -->
|
447
558
|
</div>
|
559
|
+
|
560
|
+
<!-- Simple loading indicator with just animated text -->
|
561
|
+
<div class="loading-indicator" id="loading-indicator">
|
562
|
+
<span class="loading-text">LlamaBot is thinking<span class="loading-dots"></span></span>
|
563
|
+
</div>
|
564
|
+
|
565
|
+
<!-- Suggested Prompts - Always visible above input -->
|
566
|
+
<div class="suggested-prompts" id="suggested-prompts">
|
567
|
+
<div class="prompts-label">Quick actions:</div>
|
568
|
+
<div class="prompts-container">
|
569
|
+
<div class="prompts-row">
|
570
|
+
<button class="prompt-button" onclick="selectPrompt(this)">What models are defined in this app?</button>
|
571
|
+
<button class="prompt-button" onclick="selectPrompt(this)">What routes exist?</button>
|
572
|
+
<button class="prompt-button" onclick="selectPrompt(this)">How many users are in the database?</button>
|
573
|
+
<button class="prompt-button" onclick="selectPrompt(this)">Show me the schema for the User model</button>
|
574
|
+
</div>
|
575
|
+
<div class="prompts-row">
|
576
|
+
<button class="prompt-button" onclick="selectPrompt(this)">Send a text with Twilio</button>
|
577
|
+
<button class="prompt-button" onclick="selectPrompt(this)">Create a BlogPost with title and body fields</button>
|
578
|
+
<button class="prompt-button" onclick="selectPrompt(this)">Generate a scaffolded Page model</button>
|
579
|
+
</div>
|
580
|
+
</div>
|
581
|
+
</div>
|
582
|
+
|
448
583
|
<div class="input-container">
|
449
584
|
<input type="text" id="message-input" placeholder="Type your message...">
|
450
585
|
<button onclick="sendMessage()">Send</button>
|
@@ -478,7 +613,9 @@
|
|
478
613
|
}
|
479
614
|
|
480
615
|
waitForCableConnection((consumer) => {
|
481
|
-
|
616
|
+
const sessionId = crypto.randomUUID();
|
617
|
+
|
618
|
+
subscription = consumer.subscriptions.create({channel: 'LlamaBotRails::ChatChannel', session_id: sessionId}, {
|
482
619
|
connected() {
|
483
620
|
console.log('Connected to chat channel');
|
484
621
|
lastPongTime = Date.now();
|
@@ -692,11 +829,6 @@
|
|
692
829
|
|
693
830
|
// Show welcome message
|
694
831
|
showWelcomeMessage();
|
695
|
-
|
696
|
-
// Clear active thread selection
|
697
|
-
document.querySelectorAll('.thread-item').forEach(item => {
|
698
|
-
item.classList.remove('active');
|
699
|
-
});
|
700
832
|
}
|
701
833
|
|
702
834
|
function showWelcomeMessage() {
|
@@ -710,6 +842,33 @@
|
|
710
842
|
messagesDiv.appendChild(welcomeDiv);
|
711
843
|
}
|
712
844
|
|
845
|
+
function showLoadingIndicator() {
|
846
|
+
const loadingIndicator = document.getElementById('loading-indicator');
|
847
|
+
loadingIndicator.classList.add('visible');
|
848
|
+
}
|
849
|
+
|
850
|
+
function hideLoadingIndicator() {
|
851
|
+
const loadingIndicator = document.getElementById('loading-indicator');
|
852
|
+
loadingIndicator.classList.remove('visible');
|
853
|
+
}
|
854
|
+
|
855
|
+
function selectPrompt(buttonElement) {
|
856
|
+
const promptText = buttonElement.textContent;
|
857
|
+
const messageInput = document.getElementById('message-input');
|
858
|
+
|
859
|
+
// Populate the input field
|
860
|
+
messageInput.value = promptText;
|
861
|
+
|
862
|
+
// Focus the input field for better UX
|
863
|
+
messageInput.focus();
|
864
|
+
|
865
|
+
// Add a subtle animation to show the prompt was selected
|
866
|
+
buttonElement.style.transform = 'scale(0.98)';
|
867
|
+
setTimeout(() => {
|
868
|
+
buttonElement.style.transform = '';
|
869
|
+
}, 150);
|
870
|
+
}
|
871
|
+
|
713
872
|
function sendMessage() {
|
714
873
|
const input = document.getElementById('message-input');
|
715
874
|
const message = input.value.trim();
|
@@ -731,6 +890,9 @@
|
|
731
890
|
addMessage(message, 'human');
|
732
891
|
input.value = '';
|
733
892
|
|
893
|
+
// Show loading indicator
|
894
|
+
showLoadingIndicator();
|
895
|
+
|
734
896
|
// Generate timestamp-based thread ID if we don't have one
|
735
897
|
let threadId = currentThreadId;
|
736
898
|
if (!threadId || threadId === 'global_thread_id') {
|
@@ -764,6 +926,11 @@
|
|
764
926
|
function addMessage(text, sender, base_message=null) {
|
765
927
|
console.log('🧠 Message from LlamaBot:', text, sender, base_message);
|
766
928
|
|
929
|
+
// Hide loading indicator when we receive an AI response
|
930
|
+
if (sender === 'ai') {
|
931
|
+
hideLoadingIndicator();
|
932
|
+
}
|
933
|
+
|
767
934
|
const messagesDiv = document.getElementById('chat-messages');
|
768
935
|
const messageDiv = document.createElement('div');
|
769
936
|
messageDiv.className = `message ${sender}-message`;
|
@@ -3,6 +3,11 @@ module LlamaBotRails
|
|
3
3
|
module Generators
|
4
4
|
class InstallGenerator < Rails::Generators::Base
|
5
5
|
source_root File.expand_path("templates", __dir__)
|
6
|
+
|
7
|
+
def create_config_file
|
8
|
+
empty_directory "config/llama_bot"
|
9
|
+
copy_file "agent_prompt.txt", "config/llama_bot/agent_prompt.txt"
|
10
|
+
end
|
6
11
|
|
7
12
|
def mount_engine
|
8
13
|
say <<~MSG, :yellow
|
@@ -32,6 +37,7 @@ module LlamaBotRails
|
|
32
37
|
create_file "config/initializers/llama_bot_rails.rb", <<~RUBY
|
33
38
|
Rails.application.configure do
|
34
39
|
config.llama_bot_rails.websocket_url = ENV.fetch("LLAMABOT_WEBSOCKET_URL", "ws://localhost:8000/ws")
|
40
|
+
config.llama_bot_rails.llamabot_api_url = ENV.fetch("LLAMABOT_API_URL", "http://localhost:8000")
|
35
41
|
config.llama_bot_rails.enable_console_tool = !Rails.env.production?
|
36
42
|
end
|
37
43
|
RUBY
|
@@ -0,0 +1 @@
|
|
1
|
+
You are LlamaBot, a helpful assistant inside a Ruby on Rails app.
|
@@ -10,6 +10,7 @@ module LlamaBotRails
|
|
10
10
|
user_message: @params[:message], # Rails param from JS/chat UI
|
11
11
|
thread_id: @context[:thread_id],
|
12
12
|
api_token: @context[:api_token],
|
13
|
+
agent_prompt: LlamaBotRails.agent_prompt_text,
|
13
14
|
agent_name: "llamabot" #Very important. This routes to the appropriate LangGraph agent as defined in langgraph.json
|
14
15
|
}
|
15
16
|
end
|
@@ -14,7 +14,7 @@ module LlamaBotRails
|
|
14
14
|
config.llama_bot_rails.websocket_url = 'ws://localhost:8000/ws' # <-- default
|
15
15
|
config.llama_bot_rails.llamabot_api_url = "http://localhost:8000"
|
16
16
|
config.llama_bot_rails.enable_console_tool = true
|
17
|
-
|
17
|
+
|
18
18
|
initializer "llama_bot_rails.assets.precompile" do |app|
|
19
19
|
app.config.assets.precompile += %w( llama_bot_rails/application.js )
|
20
20
|
end
|
data/lib/llama_bot_rails.rb
CHANGED
@@ -6,5 +6,22 @@ module LlamaBotRails
|
|
6
6
|
def config
|
7
7
|
Rails.application.config.llama_bot_rails
|
8
8
|
end
|
9
|
+
|
10
|
+
def agent_prompt_path
|
11
|
+
Rails.root.join("config", "llama_bot", "agent_prompt.txt")
|
12
|
+
end
|
13
|
+
|
14
|
+
def agent_prompt_text
|
15
|
+
if File.exist?(agent_prompt_path)
|
16
|
+
File.read(agent_prompt_path)
|
17
|
+
else
|
18
|
+
"You are LlamaBot, a helpful assistant." #Fallback default.
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_instruction_to_agent_prompt!(new_instruction)
|
23
|
+
FileUtils.mkdir_p(agent_prompt_path.dirname)
|
24
|
+
File.write(agent_prompt_path, "\n#{new_instruction}", mode: 'a')
|
25
|
+
end
|
9
26
|
end
|
10
27
|
end
|
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.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kody Kendall
|
@@ -15,7 +15,7 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - ">="
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: '
|
18
|
+
version: '6.0'
|
19
19
|
- - "<"
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '9.0'
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
requirements:
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: '
|
28
|
+
version: '6.0'
|
29
29
|
- - "<"
|
30
30
|
- !ruby/object:Gem::Version
|
31
31
|
version: '9.0'
|
@@ -35,7 +35,7 @@ dependencies:
|
|
35
35
|
requirements:
|
36
36
|
- - ">="
|
37
37
|
- !ruby/object:Gem::Version
|
38
|
-
version: '
|
38
|
+
version: '6.0'
|
39
39
|
- - "<"
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '9.0'
|
@@ -45,7 +45,7 @@ dependencies:
|
|
45
45
|
requirements:
|
46
46
|
- - ">="
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '
|
48
|
+
version: '6.0'
|
49
49
|
- - "<"
|
50
50
|
- !ruby/object:Gem::Version
|
51
51
|
version: '9.0'
|
@@ -122,6 +122,7 @@ files:
|
|
122
122
|
- config/initializers/llama_bot_rails.rb
|
123
123
|
- config/routes.rb
|
124
124
|
- lib/generators/llama_bot_rails/install/install_generator.rb
|
125
|
+
- lib/generators/llama_bot_rails/install/templates/agent_prompt.txt
|
125
126
|
- lib/llama_bot_rails.rb
|
126
127
|
- lib/llama_bot_rails/agent_state_builder.rb
|
127
128
|
- lib/llama_bot_rails/engine.rb
|