ruby_llm-agents 2.0.0 → 2.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 +4 -4
- data/README.md +12 -3
- data/app/models/ruby_llm/agents/execution.rb +6 -2
- data/app/views/layouts/ruby_llm/agents/application.html.erb +10 -8
- data/lib/generators/ruby_llm_agents/install_generator.rb +4 -3
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +24 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +22 -0
- data/lib/ruby_llm/agents/base_agent.rb +2 -17
- data/lib/ruby_llm/agents/core/configuration.rb +39 -0
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +8 -0
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b341c701b87662c5947fec756ba5a925759f8179700683dda02c1b30951d3213
|
|
4
|
+
data.tar.gz: bec52ce05c7958b6954b8f6df23a8a9058ac2a9f900decdb858ed108623ff5af
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fefed6d947fb4e9dc8e85a683c72754062d22c016c1de61b9922d49ae9a54cef9e84e638cadb1000e221fdd7616bfd1764fbf47bf5ba11404126acedcebe7c03
|
|
7
|
+
data.tar.gz: f93588c36b8057878b951c8411e00c2cd94ecc6189be8d33f70cb49e7d7e81098755606fbc3dcb6b2b8dfe85efc5c043a3d1884b7ecf48fc185a4863b4d0f16d
|
data/README.md
CHANGED
|
@@ -174,7 +174,16 @@ This creates `app/agents/search_intent_agent.rb` with the agent class ready to c
|
|
|
174
174
|
mount RubyLLM::Agents::Engine => "/agents"
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
<table>
|
|
178
|
+
<tr>
|
|
179
|
+
<td><img src="screenshots/dashboard.png" alt="Dashboard Overview" width="400"></td>
|
|
180
|
+
<td><img src="screenshots/agents.png" alt="Agent Registry" width="400"></td>
|
|
181
|
+
</tr>
|
|
182
|
+
<tr>
|
|
183
|
+
<td><img src="screenshots/executions.png" alt="Execution Log" width="400"></td>
|
|
184
|
+
<td><img src="screenshots/tenants.png" alt="Multi-Tenancy" width="400"></td>
|
|
185
|
+
</tr>
|
|
186
|
+
</table>
|
|
178
187
|
|
|
179
188
|
## Documentation
|
|
180
189
|
|
|
@@ -193,7 +202,7 @@ mount RubyLLM::Agents::Engine => "/agents"
|
|
|
193
202
|
| [Testing Agents](https://github.com/adham90/ruby_llm-agents/wiki/Testing-Agents) | RSpec patterns, mocking, dry_run mode |
|
|
194
203
|
| [Error Handling](https://github.com/adham90/ruby_llm-agents/wiki/Error-Handling) | Error types, recovery patterns |
|
|
195
204
|
| [Embeddings](https://github.com/adham90/ruby_llm-agents/wiki/Embeddings) | Vector embeddings, batching, caching, preprocessing |
|
|
196
|
-
| [Image Generation](https://github.com/adham90/ruby_llm-agents/wiki/Image-Generation) | Text-to-image, templates,
|
|
205
|
+
| [Image Generation](https://github.com/adham90/ruby_llm-agents/wiki/Image-Generation) | Text-to-image, templates, pipelines, cost tracking |
|
|
197
206
|
| [Dashboard](https://github.com/adham90/ruby_llm-agents/wiki/Dashboard) | Setup, authentication, analytics |
|
|
198
207
|
| [Production](https://github.com/adham90/ruby_llm-agents/wiki/Production-Deployment) | Deployment best practices, background jobs |
|
|
199
208
|
| [API Reference](https://github.com/adham90/ruby_llm-agents/wiki/API-Reference) | Complete class documentation |
|
|
@@ -203,7 +212,7 @@ mount RubyLLM::Agents::Engine => "/agents"
|
|
|
203
212
|
|
|
204
213
|
- **Ruby** >= 3.1.0
|
|
205
214
|
- **Rails** >= 7.0
|
|
206
|
-
- **RubyLLM** >= 1.0
|
|
215
|
+
- **RubyLLM** >= 1.11.0
|
|
207
216
|
|
|
208
217
|
## Contributing
|
|
209
218
|
|
|
@@ -359,14 +359,18 @@ module RubyLLM
|
|
|
359
359
|
|
|
360
360
|
# Resolves model info for cost calculation
|
|
361
361
|
#
|
|
362
|
+
# Uses Models.find (local registry lookup) rather than Models.resolve
|
|
363
|
+
# because cost calculation only needs pricing data, not a provider instance.
|
|
364
|
+
# Models.resolve requires API keys to instantiate the provider, which may
|
|
365
|
+
# not be available in background jobs or instrumentation contexts.
|
|
366
|
+
#
|
|
362
367
|
# @param lookup_model_id [String, nil] The model identifier (defaults to self.model_id)
|
|
363
368
|
# @return [Object, nil] Model info or nil
|
|
364
369
|
def resolve_model_info(lookup_model_id = nil)
|
|
365
370
|
lookup_model_id ||= model_id
|
|
366
371
|
return nil unless lookup_model_id
|
|
367
372
|
|
|
368
|
-
|
|
369
|
-
model
|
|
373
|
+
RubyLLM::Models.find(lookup_model_id)
|
|
370
374
|
rescue StandardError
|
|
371
375
|
nil
|
|
372
376
|
end
|
|
@@ -148,12 +148,13 @@
|
|
|
148
148
|
|
|
149
149
|
<!-- Desktop Navigation -->
|
|
150
150
|
<nav class="hidden md:flex items-center ml-8 gap-0.5 font-mono text-xs">
|
|
151
|
-
<% [
|
|
151
|
+
<% nav_items = [
|
|
152
152
|
[ruby_llm_agents.root_path, "dashboard"],
|
|
153
153
|
[ruby_llm_agents.agents_path, "agents"],
|
|
154
|
-
[ruby_llm_agents.executions_path, "executions"]
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
[ruby_llm_agents.executions_path, "executions"]
|
|
155
|
+
]
|
|
156
|
+
nav_items << [ruby_llm_agents.tenants_path, "tenants"] if tenant_filter_enabled?
|
|
157
|
+
nav_items.each do |path, label| %>
|
|
157
158
|
<% active = current_page?(path) %>
|
|
158
159
|
<%= link_to label, path, class: "px-2.5 py-1 rounded transition-colors #{active ? 'text-gray-900 dark:text-gray-100 bg-gray-100 dark:bg-gray-800' : 'text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'}" %>
|
|
159
160
|
<% end %>
|
|
@@ -197,12 +198,13 @@
|
|
|
197
198
|
class="md:hidden border-t border-gray-200 dark:border-gray-800"
|
|
198
199
|
>
|
|
199
200
|
<nav class="max-w-7xl mx-auto px-4 py-2 font-mono text-xs space-y-0.5">
|
|
200
|
-
<% [
|
|
201
|
+
<% mobile_nav_items = [
|
|
201
202
|
[ruby_llm_agents.root_path, "dashboard"],
|
|
202
203
|
[ruby_llm_agents.agents_path, "agents"],
|
|
203
|
-
[ruby_llm_agents.executions_path, "executions"]
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
[ruby_llm_agents.executions_path, "executions"]
|
|
205
|
+
]
|
|
206
|
+
mobile_nav_items << [ruby_llm_agents.tenants_path, "tenants"] if tenant_filter_enabled?
|
|
207
|
+
mobile_nav_items.each do |path, label| %>
|
|
206
208
|
<% active = current_page?(path) %>
|
|
207
209
|
<%= link_to label, path, class: "block px-3 py-2 rounded transition-colors #{active ? 'text-gray-900 dark:text-gray-100 bg-gray-100 dark:bg-gray-800' : 'text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'}" %>
|
|
208
210
|
<% end %>
|
|
@@ -108,9 +108,10 @@ module RubyLlmAgents
|
|
|
108
108
|
say "Skill files (*.md) help AI coding assistants understand how to use this gem."
|
|
109
109
|
say ""
|
|
110
110
|
say "Next steps:"
|
|
111
|
-
say " 1.
|
|
112
|
-
say " 2.
|
|
113
|
-
say " 3.
|
|
111
|
+
say " 1. Set your API keys in config/initializers/ruby_llm_agents.rb"
|
|
112
|
+
say " 2. Run migrations: rails db:migrate"
|
|
113
|
+
say " 3. Generate an agent: rails generate ruby_llm_agents:agent MyAgent query:required"
|
|
114
|
+
say " 4. Access the dashboard at: /agents"
|
|
114
115
|
say ""
|
|
115
116
|
say "Generator commands:"
|
|
116
117
|
say " rails generate ruby_llm_agents:agent CustomerSupport query:required"
|
|
@@ -5,6 +5,30 @@
|
|
|
5
5
|
# For more information, see: https://github.com/adham90/ruby_llm-agents
|
|
6
6
|
|
|
7
7
|
RubyLLM::Agents.configure do |config|
|
|
8
|
+
# ============================================
|
|
9
|
+
# LLM Provider API Keys
|
|
10
|
+
# ============================================
|
|
11
|
+
# Configure at least one provider. Set these in your environment
|
|
12
|
+
# or replace ENV[] calls with your keys directly.
|
|
13
|
+
|
|
14
|
+
# config.openai_api_key = ENV["OPENAI_API_KEY"]
|
|
15
|
+
# config.anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
|
|
16
|
+
# config.gemini_api_key = ENV["GOOGLE_API_KEY"]
|
|
17
|
+
|
|
18
|
+
# Additional providers:
|
|
19
|
+
# config.deepseek_api_key = ENV["DEEPSEEK_API_KEY"]
|
|
20
|
+
# config.openrouter_api_key = ENV["OPENROUTER_API_KEY"]
|
|
21
|
+
# config.mistral_api_key = ENV["MISTRAL_API_KEY"]
|
|
22
|
+
# config.xai_api_key = ENV["XAI_API_KEY"]
|
|
23
|
+
|
|
24
|
+
# Custom endpoints (e.g., Azure OpenAI, local Ollama):
|
|
25
|
+
# config.openai_api_base = "https://your-resource.openai.azure.com"
|
|
26
|
+
# config.ollama_api_base = "http://localhost:11434"
|
|
27
|
+
|
|
28
|
+
# Connection settings:
|
|
29
|
+
# config.request_timeout = 120
|
|
30
|
+
# config.max_retries = 3
|
|
31
|
+
|
|
8
32
|
# ============================================
|
|
9
33
|
# Model Defaults
|
|
10
34
|
# ============================================
|
|
@@ -60,6 +60,28 @@ module RubyLlmAgents
|
|
|
60
60
|
)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
def suggest_config_consolidation
|
|
64
|
+
ruby_llm_initializer = File.join(destination_root, "config/initializers/ruby_llm.rb")
|
|
65
|
+
agents_initializer = File.join(destination_root, "config/initializers/ruby_llm_agents.rb")
|
|
66
|
+
|
|
67
|
+
return unless File.exist?(ruby_llm_initializer) && File.exist?(agents_initializer)
|
|
68
|
+
|
|
69
|
+
say ""
|
|
70
|
+
say "Optional: You can now consolidate your API key configuration.", :yellow
|
|
71
|
+
say ""
|
|
72
|
+
say "Move your API keys from config/initializers/ruby_llm.rb"
|
|
73
|
+
say "into config/initializers/ruby_llm_agents.rb:"
|
|
74
|
+
say ""
|
|
75
|
+
say " RubyLLM::Agents.configure do |config|"
|
|
76
|
+
say " config.openai_api_key = ENV['OPENAI_API_KEY']"
|
|
77
|
+
say " config.anthropic_api_key = ENV['ANTHROPIC_API_KEY']"
|
|
78
|
+
say " # ... rest of your agent config"
|
|
79
|
+
say " end"
|
|
80
|
+
say ""
|
|
81
|
+
say "Then delete config/initializers/ruby_llm.rb if it only contained API keys."
|
|
82
|
+
say ""
|
|
83
|
+
end
|
|
84
|
+
|
|
63
85
|
def show_post_upgrade_message
|
|
64
86
|
say ""
|
|
65
87
|
say "RubyLLM::Agents upgrade complete!", :green
|
|
@@ -613,29 +613,14 @@ module RubyLLM
|
|
|
613
613
|
input_tokens = context.input_tokens || 0
|
|
614
614
|
output_tokens = context.output_tokens || 0
|
|
615
615
|
|
|
616
|
-
input_price =
|
|
617
|
-
output_price =
|
|
616
|
+
input_price = model_info.pricing&.text_tokens&.input || 0
|
|
617
|
+
output_price = model_info.pricing&.text_tokens&.output || 0
|
|
618
618
|
|
|
619
619
|
context.input_cost = (input_tokens / 1_000_000.0) * input_price
|
|
620
620
|
context.output_cost = (output_tokens / 1_000_000.0) * output_price
|
|
621
621
|
context.total_cost = (context.input_cost + context.output_cost).round(6)
|
|
622
622
|
end
|
|
623
623
|
|
|
624
|
-
# Extracts price from model info (supports both hash and object access)
|
|
625
|
-
#
|
|
626
|
-
# @param model_info [Hash, Object] Model info
|
|
627
|
-
# @param key [Symbol] The price key
|
|
628
|
-
# @return [Float] The price, or 0 if not found
|
|
629
|
-
def extract_model_price(model_info, key)
|
|
630
|
-
if model_info.respond_to?(key)
|
|
631
|
-
model_info.send(key) || 0
|
|
632
|
-
elsif model_info.respond_to?(:[])
|
|
633
|
-
model_info[key] || 0
|
|
634
|
-
else
|
|
635
|
-
0
|
|
636
|
-
end
|
|
637
|
-
end
|
|
638
|
-
|
|
639
624
|
# Finds model pricing info
|
|
640
625
|
#
|
|
641
626
|
# @param model_id [String] The model ID
|
|
@@ -351,6 +351,45 @@ module RubyLLM
|
|
|
351
351
|
# max_value_length: 5000
|
|
352
352
|
# }
|
|
353
353
|
|
|
354
|
+
# API key and provider attributes forwarded to RubyLLM.
|
|
355
|
+
# These let users configure everything in one place through
|
|
356
|
+
# RubyLLM::Agents.configure instead of a separate RubyLLM.configure block.
|
|
357
|
+
FORWARDED_RUBY_LLM_ATTRIBUTES = %i[
|
|
358
|
+
openai_api_key
|
|
359
|
+
anthropic_api_key
|
|
360
|
+
gemini_api_key
|
|
361
|
+
deepseek_api_key
|
|
362
|
+
openrouter_api_key
|
|
363
|
+
bedrock_api_key
|
|
364
|
+
bedrock_secret_key
|
|
365
|
+
bedrock_session_token
|
|
366
|
+
bedrock_region
|
|
367
|
+
mistral_api_key
|
|
368
|
+
perplexity_api_key
|
|
369
|
+
xai_api_key
|
|
370
|
+
gpustack_api_key
|
|
371
|
+
openai_api_base
|
|
372
|
+
openai_organization_id
|
|
373
|
+
openai_project_id
|
|
374
|
+
gemini_api_base
|
|
375
|
+
gpustack_api_base
|
|
376
|
+
ollama_api_base
|
|
377
|
+
vertexai_project_id
|
|
378
|
+
vertexai_location
|
|
379
|
+
request_timeout
|
|
380
|
+
max_retries
|
|
381
|
+
].freeze
|
|
382
|
+
|
|
383
|
+
FORWARDED_RUBY_LLM_ATTRIBUTES.each do |attr|
|
|
384
|
+
define_method(:"#{attr}=") do |value|
|
|
385
|
+
RubyLLM.config.public_send(:"#{attr}=", value)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
define_method(attr) do
|
|
389
|
+
RubyLLM.config.public_send(attr)
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
354
393
|
# Attributes without validation (simple accessors)
|
|
355
394
|
attr_accessor :default_model,
|
|
356
395
|
:async_logging,
|
|
@@ -25,10 +25,18 @@ module RubyLLM
|
|
|
25
25
|
# @param execution_data [Hash] Execution attributes from instrumentation
|
|
26
26
|
# @return [void]
|
|
27
27
|
def perform(execution_data)
|
|
28
|
+
# Extract detail data before filtering (stored in separate table)
|
|
29
|
+
detail_data = execution_data.delete(:_detail_data) || execution_data.delete("_detail_data")
|
|
30
|
+
|
|
28
31
|
# Filter to only known attributes to prevent schema mismatches
|
|
29
32
|
filtered_data = filter_known_attributes(execution_data)
|
|
30
33
|
execution = Execution.create!(filtered_data)
|
|
31
34
|
|
|
35
|
+
# Create detail record if present
|
|
36
|
+
if detail_data && detail_data.values.any? { |v| v.present? && v != {} && v != [] }
|
|
37
|
+
execution.create_detail!(detail_data)
|
|
38
|
+
end
|
|
39
|
+
|
|
32
40
|
# Calculate costs if token data is available
|
|
33
41
|
if execution.input_tokens && execution.output_tokens
|
|
34
42
|
execution.calculate_costs!
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_llm-agents
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- adham90
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 1.
|
|
32
|
+
version: 1.12.0
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 1.
|
|
39
|
+
version: 1.12.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: csv
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|