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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f2dcacc45b93f72d6de4f0d8ac2f56d85288f9cacc5495838fbc40473714ddf
4
- data.tar.gz: d6e2e7d7c78d9f6443cf8f01fa43733951e552922418ba8ee7226df4977d51a1
3
+ metadata.gz: b341c701b87662c5947fec756ba5a925759f8179700683dda02c1b30951d3213
4
+ data.tar.gz: bec52ce05c7958b6954b8f6df23a8a9058ac2a9f900decdb858ed108623ff5af
5
5
  SHA512:
6
- metadata.gz: 3231be55f4d51f39bb8339ac878a82eef2c2ee5d2894a53356c43a7fc20fd9381141e19d1b447e7c755a6117d687a241e7d3be701792ebf5e97ce3f5e7cf4758
7
- data.tar.gz: d89ded2027aa6ef14494216a116fcf42a6b549128677ec24a3881a63109f06a15955d0c824d7b3ff9c8aeecaa1ebdd675099d38bb5a9dce8dd4344bf045cd15f
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
- ![RubyLLM Agents Dashboard](screenshot.png)
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, content policy, cost tracking |
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
- model, _provider = RubyLLM::Models.resolve(lookup_model_id)
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
- [ruby_llm_agents.tenants_path, "tenants"]
156
- ].each do |path, label| %>
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
- [ruby_llm_agents.tenants_path, "tenants"]
205
- ].each do |path, label| %>
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. Run migrations: rails db:migrate"
112
- say " 2. Generate an agent: rails generate ruby_llm_agents:agent MyAgent query:required"
113
- say " 3. Access the dashboard at: /agents"
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 = extract_model_price(model_info, :input_price)
617
- output_price = extract_model_price(model_info, :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,
@@ -4,6 +4,6 @@ module RubyLLM
4
4
  module Agents
5
5
  # Current version of the RubyLLM::Agents gem
6
6
  # @return [String] Semantic version string
7
- VERSION = "2.0.0"
7
+ VERSION = "2.1.0"
8
8
  end
9
9
  end
@@ -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.0.0
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.11.0
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.11.0
39
+ version: 1.12.0
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: csv
42
42
  requirement: !ruby/object:Gem::Requirement