ruby_llm-agents 0.3.1 → 0.3.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 +88 -0
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +68 -4
- data/app/models/ruby_llm/agents/execution/analytics.rb +114 -13
- data/app/models/ruby_llm/agents/execution/scopes.rb +10 -0
- data/app/models/ruby_llm/agents/execution.rb +26 -58
- data/app/views/layouts/rubyllm/agents/application.html.erb +103 -352
- data/app/views/rubyllm/agents/agents/_agent.html.erb +87 -0
- data/app/views/rubyllm/agents/agents/index.html.erb +2 -71
- data/app/views/rubyllm/agents/agents/show.html.erb +349 -416
- data/app/views/rubyllm/agents/dashboard/_action_center.html.erb +7 -7
- data/app/views/rubyllm/agents/dashboard/_agent_comparison.html.erb +46 -0
- data/app/views/rubyllm/agents/dashboard/_budgets_bar.html.erb +0 -90
- data/app/views/rubyllm/agents/dashboard/_execution_item.html.erb +54 -39
- data/app/views/rubyllm/agents/dashboard/_now_strip.html.erb +79 -5
- data/app/views/rubyllm/agents/dashboard/_top_errors.html.erb +49 -0
- data/app/views/rubyllm/agents/dashboard/index.html.erb +76 -151
- data/app/views/rubyllm/agents/executions/show.html.erb +256 -93
- data/app/views/rubyllm/agents/settings/show.html.erb +1 -1
- data/app/views/rubyllm/agents/shared/_breadcrumbs.html.erb +48 -0
- data/app/views/rubyllm/agents/shared/_nav_link.html.erb +27 -0
- data/config/routes.rb +2 -0
- data/lib/generators/ruby_llm_agents/templates/add_tool_calls_migration.rb.tt +28 -0
- data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +7 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +13 -0
- data/lib/ruby_llm/agents/base/caching.rb +43 -0
- data/lib/ruby_llm/agents/base/cost_calculation.rb +103 -0
- data/lib/ruby_llm/agents/base/dsl.rb +261 -0
- data/lib/ruby_llm/agents/base/execution.rb +206 -0
- data/lib/ruby_llm/agents/base/reliability_execution.rb +131 -0
- data/lib/ruby_llm/agents/base/response_building.rb +86 -0
- data/lib/ruby_llm/agents/base/tool_tracking.rb +57 -0
- data/lib/ruby_llm/agents/base.rb +19 -619
- data/lib/ruby_llm/agents/instrumentation.rb +36 -3
- data/lib/ruby_llm/agents/result.rb +235 -0
- data/lib/ruby_llm/agents/version.rb +1 -1
- data/lib/ruby_llm/agents.rb +1 -0
- metadata +15 -20
- data/app/channels/ruby_llm/agents/executions_channel.rb +0 -46
- data/app/javascript/ruby_llm/agents/controllers/filter_controller.js +0 -56
- data/app/javascript/ruby_llm/agents/controllers/index.js +0 -12
- data/app/javascript/ruby_llm/agents/controllers/refresh_controller.js +0 -83
- data/app/views/rubyllm/agents/dashboard/_now_strip_values.html.erb +0 -71
|
@@ -385,6 +385,12 @@ module RubyLLM
|
|
|
385
385
|
update_data[:response] = redacted_response(@last_response)
|
|
386
386
|
end
|
|
387
387
|
|
|
388
|
+
# Add tool calls from accumulated_tool_calls (captured from all responses)
|
|
389
|
+
if respond_to?(:accumulated_tool_calls) && accumulated_tool_calls.present?
|
|
390
|
+
update_data[:tool_calls] = accumulated_tool_calls
|
|
391
|
+
update_data[:tool_calls_count] = accumulated_tool_calls.size
|
|
392
|
+
end
|
|
393
|
+
|
|
388
394
|
# Add error data if failed
|
|
389
395
|
if error
|
|
390
396
|
update_data.merge!(
|
|
@@ -566,7 +572,11 @@ module RubyLLM
|
|
|
566
572
|
# @param response [RubyLLM::Message, nil] The LLM response
|
|
567
573
|
# @return [Hash] Extracted response data (empty if response invalid)
|
|
568
574
|
def safe_extract_response_data(response)
|
|
569
|
-
return {} unless response.
|
|
575
|
+
return {} unless response.respond_to?(:input_tokens)
|
|
576
|
+
|
|
577
|
+
# Use accumulated_tool_calls which captures tool calls from ALL responses
|
|
578
|
+
# during multi-turn conversations (when tools are used)
|
|
579
|
+
tool_calls_data = respond_to?(:accumulated_tool_calls) ? accumulated_tool_calls : []
|
|
570
580
|
|
|
571
581
|
{
|
|
572
582
|
input_tokens: safe_response_value(response, :input_tokens),
|
|
@@ -575,7 +585,9 @@ module RubyLLM
|
|
|
575
585
|
cache_creation_tokens: safe_response_value(response, :cache_creation_tokens, 0),
|
|
576
586
|
model_id: safe_response_value(response, :model_id),
|
|
577
587
|
finish_reason: safe_extract_finish_reason(response),
|
|
578
|
-
response: safe_serialize_response(response)
|
|
588
|
+
response: safe_serialize_response(response),
|
|
589
|
+
tool_calls: tool_calls_data || [],
|
|
590
|
+
tool_calls_count: tool_calls_data&.size || 0
|
|
579
591
|
}.compact
|
|
580
592
|
end
|
|
581
593
|
|
|
@@ -689,16 +701,37 @@ module RubyLLM
|
|
|
689
701
|
# @param response [RubyLLM::Message] The LLM response
|
|
690
702
|
# @return [Hash] Serialized response data
|
|
691
703
|
def safe_serialize_response(response)
|
|
704
|
+
# Use accumulated_tool_calls which captures tool calls from ALL responses
|
|
705
|
+
tool_calls_data = respond_to?(:accumulated_tool_calls) ? accumulated_tool_calls : nil
|
|
706
|
+
|
|
692
707
|
{
|
|
693
708
|
content: safe_response_value(response, :content),
|
|
694
709
|
model_id: safe_response_value(response, :model_id),
|
|
695
710
|
input_tokens: safe_response_value(response, :input_tokens),
|
|
696
711
|
output_tokens: safe_response_value(response, :output_tokens),
|
|
697
712
|
cached_tokens: safe_response_value(response, :cached_tokens, 0),
|
|
698
|
-
cache_creation_tokens: safe_response_value(response, :cache_creation_tokens, 0)
|
|
713
|
+
cache_creation_tokens: safe_response_value(response, :cache_creation_tokens, 0),
|
|
714
|
+
tool_calls: tool_calls_data.presence
|
|
699
715
|
}.compact
|
|
700
716
|
end
|
|
701
717
|
|
|
718
|
+
# Serializes tool calls to an array of hashes for storage
|
|
719
|
+
#
|
|
720
|
+
# @param response [RubyLLM::Message] The LLM response
|
|
721
|
+
# @return [Array<Hash>, nil] Serialized tool calls or nil if none
|
|
722
|
+
def serialize_tool_calls(response)
|
|
723
|
+
tool_calls = safe_response_value(response, :tool_calls)
|
|
724
|
+
return nil if tool_calls.nil? || tool_calls.empty?
|
|
725
|
+
|
|
726
|
+
tool_calls.map do |id, tool_call|
|
|
727
|
+
if tool_call.respond_to?(:to_h)
|
|
728
|
+
tool_call.to_h
|
|
729
|
+
else
|
|
730
|
+
{ id: id, name: tool_call[:name], arguments: tool_call[:arguments] }
|
|
731
|
+
end
|
|
732
|
+
end
|
|
733
|
+
end
|
|
734
|
+
|
|
702
735
|
# Emergency fallback to mark execution as failed
|
|
703
736
|
#
|
|
704
737
|
# Uses update_all to bypass ActiveRecord callbacks and validations,
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Agents
|
|
5
|
+
# Wrapper for agent execution results with full metadata
|
|
6
|
+
#
|
|
7
|
+
# Provides access to the response content along with execution details
|
|
8
|
+
# like token usage, cost, timing, and model information.
|
|
9
|
+
#
|
|
10
|
+
# @example Basic usage
|
|
11
|
+
# result = MyAgent.call(query: "test")
|
|
12
|
+
# result.content # => processed response
|
|
13
|
+
# result.input_tokens # => 150
|
|
14
|
+
# result.total_cost # => 0.00025
|
|
15
|
+
#
|
|
16
|
+
# @example Backward compatible hash access
|
|
17
|
+
# result[:key] # delegates to result.content[:key]
|
|
18
|
+
# result.dig(:nested, :key)
|
|
19
|
+
#
|
|
20
|
+
# @api public
|
|
21
|
+
class Result
|
|
22
|
+
extend ActiveSupport::Delegation
|
|
23
|
+
|
|
24
|
+
# @!attribute [r] content
|
|
25
|
+
# @return [Hash, String] The processed response content
|
|
26
|
+
attr_reader :content
|
|
27
|
+
|
|
28
|
+
# @!group Token Usage
|
|
29
|
+
# @!attribute [r] input_tokens
|
|
30
|
+
# @return [Integer, nil] Number of input tokens consumed
|
|
31
|
+
# @!attribute [r] output_tokens
|
|
32
|
+
# @return [Integer, nil] Number of output tokens generated
|
|
33
|
+
# @!attribute [r] cached_tokens
|
|
34
|
+
# @return [Integer] Number of tokens served from cache
|
|
35
|
+
# @!attribute [r] cache_creation_tokens
|
|
36
|
+
# @return [Integer] Number of tokens used to create cache
|
|
37
|
+
attr_reader :input_tokens, :output_tokens, :cached_tokens, :cache_creation_tokens
|
|
38
|
+
|
|
39
|
+
# @!group Cost
|
|
40
|
+
# @!attribute [r] input_cost
|
|
41
|
+
# @return [Float, nil] Cost of input tokens in USD
|
|
42
|
+
# @!attribute [r] output_cost
|
|
43
|
+
# @return [Float, nil] Cost of output tokens in USD
|
|
44
|
+
# @!attribute [r] total_cost
|
|
45
|
+
# @return [Float, nil] Total cost in USD
|
|
46
|
+
attr_reader :input_cost, :output_cost, :total_cost
|
|
47
|
+
|
|
48
|
+
# @!group Model Info
|
|
49
|
+
# @!attribute [r] model_id
|
|
50
|
+
# @return [String, nil] The model that was requested
|
|
51
|
+
# @!attribute [r] chosen_model_id
|
|
52
|
+
# @return [String, nil] The model that actually responded (may differ if fallback used)
|
|
53
|
+
# @!attribute [r] temperature
|
|
54
|
+
# @return [Float, nil] Temperature setting used
|
|
55
|
+
attr_reader :model_id, :chosen_model_id, :temperature
|
|
56
|
+
|
|
57
|
+
# @!group Timing
|
|
58
|
+
# @!attribute [r] started_at
|
|
59
|
+
# @return [Time, nil] When execution started
|
|
60
|
+
# @!attribute [r] completed_at
|
|
61
|
+
# @return [Time, nil] When execution completed
|
|
62
|
+
# @!attribute [r] duration_ms
|
|
63
|
+
# @return [Integer, nil] Execution duration in milliseconds
|
|
64
|
+
# @!attribute [r] time_to_first_token_ms
|
|
65
|
+
# @return [Integer, nil] Time to first token (streaming only)
|
|
66
|
+
attr_reader :started_at, :completed_at, :duration_ms, :time_to_first_token_ms
|
|
67
|
+
|
|
68
|
+
# @!group Status
|
|
69
|
+
# @!attribute [r] finish_reason
|
|
70
|
+
# @return [String, nil] Why generation stopped (stop, length, tool_calls, etc.)
|
|
71
|
+
# @!attribute [r] streaming
|
|
72
|
+
# @return [Boolean] Whether streaming was enabled
|
|
73
|
+
attr_reader :finish_reason, :streaming
|
|
74
|
+
|
|
75
|
+
# @!group Error Info
|
|
76
|
+
# @!attribute [r] error_class
|
|
77
|
+
# @return [String, nil] Exception class name if failed
|
|
78
|
+
# @!attribute [r] error_message
|
|
79
|
+
# @return [String, nil] Exception message if failed
|
|
80
|
+
attr_reader :error_class, :error_message
|
|
81
|
+
|
|
82
|
+
# @!group Reliability
|
|
83
|
+
# @!attribute [r] attempts
|
|
84
|
+
# @return [Array<Hash>] Details of each attempt (for retries/fallbacks)
|
|
85
|
+
# @!attribute [r] attempts_count
|
|
86
|
+
# @return [Integer] Number of attempts made
|
|
87
|
+
attr_reader :attempts, :attempts_count
|
|
88
|
+
|
|
89
|
+
# @!group Tool Calls
|
|
90
|
+
# @!attribute [r] tool_calls
|
|
91
|
+
# @return [Array<Hash>] Tool calls made during execution
|
|
92
|
+
# @!attribute [r] tool_calls_count
|
|
93
|
+
# @return [Integer] Number of tool calls made
|
|
94
|
+
attr_reader :tool_calls, :tool_calls_count
|
|
95
|
+
|
|
96
|
+
# Creates a new Result instance
|
|
97
|
+
#
|
|
98
|
+
# @param content [Hash, String] The processed response content
|
|
99
|
+
# @param options [Hash] Execution metadata
|
|
100
|
+
def initialize(content:, **options)
|
|
101
|
+
@content = content
|
|
102
|
+
|
|
103
|
+
# Token usage
|
|
104
|
+
@input_tokens = options[:input_tokens]
|
|
105
|
+
@output_tokens = options[:output_tokens]
|
|
106
|
+
@cached_tokens = options[:cached_tokens] || 0
|
|
107
|
+
@cache_creation_tokens = options[:cache_creation_tokens] || 0
|
|
108
|
+
|
|
109
|
+
# Cost
|
|
110
|
+
@input_cost = options[:input_cost]
|
|
111
|
+
@output_cost = options[:output_cost]
|
|
112
|
+
@total_cost = options[:total_cost]
|
|
113
|
+
|
|
114
|
+
# Model info
|
|
115
|
+
@model_id = options[:model_id]
|
|
116
|
+
@chosen_model_id = options[:chosen_model_id] || options[:model_id]
|
|
117
|
+
@temperature = options[:temperature]
|
|
118
|
+
|
|
119
|
+
# Timing
|
|
120
|
+
@started_at = options[:started_at]
|
|
121
|
+
@completed_at = options[:completed_at]
|
|
122
|
+
@duration_ms = options[:duration_ms]
|
|
123
|
+
@time_to_first_token_ms = options[:time_to_first_token_ms]
|
|
124
|
+
|
|
125
|
+
# Status
|
|
126
|
+
@finish_reason = options[:finish_reason]
|
|
127
|
+
@streaming = options[:streaming] || false
|
|
128
|
+
|
|
129
|
+
# Error
|
|
130
|
+
@error_class = options[:error_class]
|
|
131
|
+
@error_message = options[:error_message]
|
|
132
|
+
|
|
133
|
+
# Reliability
|
|
134
|
+
@attempts = options[:attempts] || []
|
|
135
|
+
@attempts_count = options[:attempts_count] || 1
|
|
136
|
+
|
|
137
|
+
# Tool calls
|
|
138
|
+
@tool_calls = options[:tool_calls] || []
|
|
139
|
+
@tool_calls_count = options[:tool_calls_count] || 0
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Returns total tokens (input + output)
|
|
143
|
+
#
|
|
144
|
+
# @return [Integer] Total token count
|
|
145
|
+
def total_tokens
|
|
146
|
+
(input_tokens || 0) + (output_tokens || 0)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Returns whether streaming was enabled
|
|
150
|
+
#
|
|
151
|
+
# @return [Boolean] true if streaming was used
|
|
152
|
+
def streaming?
|
|
153
|
+
streaming == true
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Returns whether the execution succeeded
|
|
157
|
+
#
|
|
158
|
+
# @return [Boolean] true if no error occurred
|
|
159
|
+
def success?
|
|
160
|
+
error_class.nil?
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Returns whether the execution failed
|
|
164
|
+
#
|
|
165
|
+
# @return [Boolean] true if an error occurred
|
|
166
|
+
def error?
|
|
167
|
+
!success?
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Returns whether a fallback model was used
|
|
171
|
+
#
|
|
172
|
+
# @return [Boolean] true if chosen_model_id differs from model_id
|
|
173
|
+
def used_fallback?
|
|
174
|
+
chosen_model_id.present? && chosen_model_id != model_id
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Returns whether the response was truncated due to max tokens
|
|
178
|
+
#
|
|
179
|
+
# @return [Boolean] true if finish_reason is "length"
|
|
180
|
+
def truncated?
|
|
181
|
+
finish_reason == "length"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Returns whether tool calls were made during execution
|
|
185
|
+
#
|
|
186
|
+
# @return [Boolean] true if tool_calls_count > 0
|
|
187
|
+
def has_tool_calls?
|
|
188
|
+
tool_calls_count.to_i > 0
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Converts the result to a hash
|
|
192
|
+
#
|
|
193
|
+
# @return [Hash] All result data as a hash
|
|
194
|
+
def to_h
|
|
195
|
+
{
|
|
196
|
+
content: content,
|
|
197
|
+
input_tokens: input_tokens,
|
|
198
|
+
output_tokens: output_tokens,
|
|
199
|
+
total_tokens: total_tokens,
|
|
200
|
+
cached_tokens: cached_tokens,
|
|
201
|
+
cache_creation_tokens: cache_creation_tokens,
|
|
202
|
+
input_cost: input_cost,
|
|
203
|
+
output_cost: output_cost,
|
|
204
|
+
total_cost: total_cost,
|
|
205
|
+
model_id: model_id,
|
|
206
|
+
chosen_model_id: chosen_model_id,
|
|
207
|
+
temperature: temperature,
|
|
208
|
+
started_at: started_at,
|
|
209
|
+
completed_at: completed_at,
|
|
210
|
+
duration_ms: duration_ms,
|
|
211
|
+
time_to_first_token_ms: time_to_first_token_ms,
|
|
212
|
+
finish_reason: finish_reason,
|
|
213
|
+
streaming: streaming,
|
|
214
|
+
error_class: error_class,
|
|
215
|
+
error_message: error_message,
|
|
216
|
+
attempts_count: attempts_count,
|
|
217
|
+
attempts: attempts,
|
|
218
|
+
tool_calls: tool_calls,
|
|
219
|
+
tool_calls_count: tool_calls_count
|
|
220
|
+
}
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Delegate hash methods to content for backward compatibility
|
|
224
|
+
delegate :[], :dig, :keys, :values, :each, :map, to: :content, allow_nil: true
|
|
225
|
+
|
|
226
|
+
# Custom to_json that returns content as JSON for backward compatibility
|
|
227
|
+
#
|
|
228
|
+
# @param args [Array] Arguments passed to to_json
|
|
229
|
+
# @return [String] JSON representation
|
|
230
|
+
def to_json(*args)
|
|
231
|
+
content.to_json(*args)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
data/lib/ruby_llm/agents.rb
CHANGED
|
@@ -10,6 +10,7 @@ require_relative "agents/circuit_breaker"
|
|
|
10
10
|
require_relative "agents/budget_tracker"
|
|
11
11
|
require_relative "agents/alert_manager"
|
|
12
12
|
require_relative "agents/attempt_tracker"
|
|
13
|
+
require_relative "agents/result"
|
|
13
14
|
require_relative "agents/inflections" if defined?(Rails)
|
|
14
15
|
require_relative "agents/engine" if defined?(Rails::Engine)
|
|
15
16
|
|
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: 0.3.
|
|
4
|
+
version: 0.3.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- adham90
|
|
@@ -65,20 +65,6 @@ dependencies:
|
|
|
65
65
|
- - ">="
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '1.0'
|
|
68
|
-
- !ruby/object:Gem::Dependency
|
|
69
|
-
name: chartkick
|
|
70
|
-
requirement: !ruby/object:Gem::Requirement
|
|
71
|
-
requirements:
|
|
72
|
-
- - ">="
|
|
73
|
-
- !ruby/object:Gem::Version
|
|
74
|
-
version: '5.0'
|
|
75
|
-
type: :runtime
|
|
76
|
-
prerelease: false
|
|
77
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
-
requirements:
|
|
79
|
-
- - ">="
|
|
80
|
-
- !ruby/object:Gem::Version
|
|
81
|
-
version: '5.0'
|
|
82
68
|
- !ruby/object:Gem::Dependency
|
|
83
69
|
name: csv
|
|
84
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -104,7 +90,6 @@ extra_rdoc_files: []
|
|
|
104
90
|
files:
|
|
105
91
|
- LICENSE.txt
|
|
106
92
|
- README.md
|
|
107
|
-
- app/channels/ruby_llm/agents/executions_channel.rb
|
|
108
93
|
- app/controllers/concerns/ruby_llm/agents/filterable.rb
|
|
109
94
|
- app/controllers/concerns/ruby_llm/agents/paginatable.rb
|
|
110
95
|
- app/controllers/ruby_llm/agents/agents_controller.rb
|
|
@@ -112,25 +97,24 @@ files:
|
|
|
112
97
|
- app/controllers/ruby_llm/agents/executions_controller.rb
|
|
113
98
|
- app/controllers/ruby_llm/agents/settings_controller.rb
|
|
114
99
|
- app/helpers/ruby_llm/agents/application_helper.rb
|
|
115
|
-
- app/javascript/ruby_llm/agents/controllers/filter_controller.js
|
|
116
|
-
- app/javascript/ruby_llm/agents/controllers/index.js
|
|
117
|
-
- app/javascript/ruby_llm/agents/controllers/refresh_controller.js
|
|
118
100
|
- app/models/ruby_llm/agents/execution.rb
|
|
119
101
|
- app/models/ruby_llm/agents/execution/analytics.rb
|
|
120
102
|
- app/models/ruby_llm/agents/execution/metrics.rb
|
|
121
103
|
- app/models/ruby_llm/agents/execution/scopes.rb
|
|
122
104
|
- app/services/ruby_llm/agents/agent_registry.rb
|
|
123
105
|
- app/views/layouts/rubyllm/agents/application.html.erb
|
|
106
|
+
- app/views/rubyllm/agents/agents/_agent.html.erb
|
|
124
107
|
- app/views/rubyllm/agents/agents/_version_comparison.html.erb
|
|
125
108
|
- app/views/rubyllm/agents/agents/index.html.erb
|
|
126
109
|
- app/views/rubyllm/agents/agents/show.html.erb
|
|
127
110
|
- app/views/rubyllm/agents/dashboard/_action_center.html.erb
|
|
111
|
+
- app/views/rubyllm/agents/dashboard/_agent_comparison.html.erb
|
|
128
112
|
- app/views/rubyllm/agents/dashboard/_alerts_feed.html.erb
|
|
129
113
|
- app/views/rubyllm/agents/dashboard/_breaker_strip.html.erb
|
|
130
114
|
- app/views/rubyllm/agents/dashboard/_budgets_bar.html.erb
|
|
131
115
|
- app/views/rubyllm/agents/dashboard/_execution_item.html.erb
|
|
132
116
|
- app/views/rubyllm/agents/dashboard/_now_strip.html.erb
|
|
133
|
-
- app/views/rubyllm/agents/dashboard/
|
|
117
|
+
- app/views/rubyllm/agents/dashboard/_top_errors.html.erb
|
|
134
118
|
- app/views/rubyllm/agents/dashboard/index.html.erb
|
|
135
119
|
- app/views/rubyllm/agents/executions/_execution.html.erb
|
|
136
120
|
- app/views/rubyllm/agents/executions/_filters.html.erb
|
|
@@ -140,8 +124,10 @@ files:
|
|
|
140
124
|
- app/views/rubyllm/agents/executions/index.turbo_stream.erb
|
|
141
125
|
- app/views/rubyllm/agents/executions/show.html.erb
|
|
142
126
|
- app/views/rubyllm/agents/settings/show.html.erb
|
|
127
|
+
- app/views/rubyllm/agents/shared/_breadcrumbs.html.erb
|
|
143
128
|
- app/views/rubyllm/agents/shared/_executions_table.html.erb
|
|
144
129
|
- app/views/rubyllm/agents/shared/_filter_dropdown.html.erb
|
|
130
|
+
- app/views/rubyllm/agents/shared/_nav_link.html.erb
|
|
145
131
|
- app/views/rubyllm/agents/shared/_select_dropdown.html.erb
|
|
146
132
|
- app/views/rubyllm/agents/shared/_stat_card.html.erb
|
|
147
133
|
- app/views/rubyllm/agents/shared/_status_badge.html.erb
|
|
@@ -155,6 +141,7 @@ files:
|
|
|
155
141
|
- lib/generators/ruby_llm_agents/templates/add_prompts_migration.rb.tt
|
|
156
142
|
- lib/generators/ruby_llm_agents/templates/add_routing_migration.rb.tt
|
|
157
143
|
- lib/generators/ruby_llm_agents/templates/add_streaming_migration.rb.tt
|
|
144
|
+
- lib/generators/ruby_llm_agents/templates/add_tool_calls_migration.rb.tt
|
|
158
145
|
- lib/generators/ruby_llm_agents/templates/add_tracing_migration.rb.tt
|
|
159
146
|
- lib/generators/ruby_llm_agents/templates/agent.rb.tt
|
|
160
147
|
- lib/generators/ruby_llm_agents/templates/application_agent.rb.tt
|
|
@@ -166,6 +153,13 @@ files:
|
|
|
166
153
|
- lib/ruby_llm/agents/alert_manager.rb
|
|
167
154
|
- lib/ruby_llm/agents/attempt_tracker.rb
|
|
168
155
|
- lib/ruby_llm/agents/base.rb
|
|
156
|
+
- lib/ruby_llm/agents/base/caching.rb
|
|
157
|
+
- lib/ruby_llm/agents/base/cost_calculation.rb
|
|
158
|
+
- lib/ruby_llm/agents/base/dsl.rb
|
|
159
|
+
- lib/ruby_llm/agents/base/execution.rb
|
|
160
|
+
- lib/ruby_llm/agents/base/reliability_execution.rb
|
|
161
|
+
- lib/ruby_llm/agents/base/response_building.rb
|
|
162
|
+
- lib/ruby_llm/agents/base/tool_tracking.rb
|
|
169
163
|
- lib/ruby_llm/agents/budget_tracker.rb
|
|
170
164
|
- lib/ruby_llm/agents/circuit_breaker.rb
|
|
171
165
|
- lib/ruby_llm/agents/configuration.rb
|
|
@@ -175,6 +169,7 @@ files:
|
|
|
175
169
|
- lib/ruby_llm/agents/instrumentation.rb
|
|
176
170
|
- lib/ruby_llm/agents/redactor.rb
|
|
177
171
|
- lib/ruby_llm/agents/reliability.rb
|
|
172
|
+
- lib/ruby_llm/agents/result.rb
|
|
178
173
|
- lib/ruby_llm/agents/version.rb
|
|
179
174
|
homepage: https://github.com/adham90/ruby_llm-agents
|
|
180
175
|
licenses:
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RubyLLM
|
|
4
|
-
module Agents
|
|
5
|
-
# ActionCable channel for real-time execution updates
|
|
6
|
-
#
|
|
7
|
-
# Broadcasts execution create/update events to subscribed clients.
|
|
8
|
-
# Used by the dashboard to show live execution status changes.
|
|
9
|
-
#
|
|
10
|
-
# Inherits from the host app's ApplicationCable::Channel (note the :: prefix
|
|
11
|
-
# to reference the root namespace, not the engine's namespace).
|
|
12
|
-
#
|
|
13
|
-
# @example JavaScript subscription
|
|
14
|
-
# import { createConsumer } from "@rails/actioncable"
|
|
15
|
-
# const consumer = createConsumer()
|
|
16
|
-
# consumer.subscriptions.create("RubyLLM::Agents::ExecutionsChannel", {
|
|
17
|
-
# received(data) {
|
|
18
|
-
# console.log("Execution update:", data)
|
|
19
|
-
# }
|
|
20
|
-
# })
|
|
21
|
-
#
|
|
22
|
-
# @see Execution#broadcast_execution Broadcast trigger
|
|
23
|
-
# @api private
|
|
24
|
-
class ExecutionsChannel < ::ApplicationCable::Channel
|
|
25
|
-
# Subscribes the client to the executions broadcast stream
|
|
26
|
-
#
|
|
27
|
-
# Called automatically when a client connects to this channel.
|
|
28
|
-
# Streams from the "ruby_llm_agents:executions" channel name.
|
|
29
|
-
#
|
|
30
|
-
# @return [void]
|
|
31
|
-
def subscribed
|
|
32
|
-
stream_from "ruby_llm_agents:executions"
|
|
33
|
-
logger.info "[RubyLLM::Agents] Client subscribed to executions channel"
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Cleans up when a client disconnects
|
|
37
|
-
#
|
|
38
|
-
# Called automatically when the WebSocket connection is closed.
|
|
39
|
-
#
|
|
40
|
-
# @return [void]
|
|
41
|
-
def unsubscribed
|
|
42
|
-
logger.info "[RubyLLM::Agents] Client unsubscribed from executions channel"
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { Controller } from "@hotwired/stimulus"
|
|
2
|
-
|
|
3
|
-
// Filter controller for live filtering of executions
|
|
4
|
-
//
|
|
5
|
-
// Usage:
|
|
6
|
-
// <form data-controller="filter">
|
|
7
|
-
// <select data-action="change->filter#submit">...</select>
|
|
8
|
-
// </form>
|
|
9
|
-
//
|
|
10
|
-
export default class extends Controller {
|
|
11
|
-
static targets = ["form"]
|
|
12
|
-
static values = {
|
|
13
|
-
debounce: { type: Number, default: 300 }
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
connect() {
|
|
17
|
-
this.timeout = null
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
disconnect() {
|
|
21
|
-
if (this.timeout) {
|
|
22
|
-
clearTimeout(this.timeout)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Submit the form with debouncing
|
|
27
|
-
submit(event) {
|
|
28
|
-
if (this.timeout) {
|
|
29
|
-
clearTimeout(this.timeout)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
this.timeout = setTimeout(() => {
|
|
33
|
-
this.element.requestSubmit()
|
|
34
|
-
}, this.debounceValue)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Submit immediately without debounce
|
|
38
|
-
submitNow(event) {
|
|
39
|
-
this.element.requestSubmit()
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Update URL with current filter state
|
|
43
|
-
updateUrl() {
|
|
44
|
-
const formData = new FormData(this.element)
|
|
45
|
-
const params = new URLSearchParams()
|
|
46
|
-
|
|
47
|
-
for (const [key, value] of formData) {
|
|
48
|
-
if (value) {
|
|
49
|
-
params.set(key, value)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const newUrl = `${window.location.pathname}?${params.toString()}`
|
|
54
|
-
window.history.pushState({}, "", newUrl)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// Entry point for RubyLLM::Agents Stimulus controllers
|
|
2
|
-
// Import and register controllers with your Stimulus application
|
|
3
|
-
|
|
4
|
-
import FilterController from "./filter_controller"
|
|
5
|
-
import RefreshController from "./refresh_controller"
|
|
6
|
-
|
|
7
|
-
export { FilterController, RefreshController }
|
|
8
|
-
|
|
9
|
-
export function registerControllers(application) {
|
|
10
|
-
application.register("filter", FilterController)
|
|
11
|
-
application.register("refresh", RefreshController)
|
|
12
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { Controller } from "@hotwired/stimulus"
|
|
2
|
-
|
|
3
|
-
// Auto-refresh controller for dashboard with toggle support
|
|
4
|
-
//
|
|
5
|
-
// Usage:
|
|
6
|
-
// <div data-controller="refresh"
|
|
7
|
-
// data-refresh-interval-value="30000"
|
|
8
|
-
// data-refresh-enabled-value="false">
|
|
9
|
-
// <button data-action="refresh#toggle" data-refresh-target="button">
|
|
10
|
-
// Live Poll: Off
|
|
11
|
-
// </button>
|
|
12
|
-
// </div>
|
|
13
|
-
//
|
|
14
|
-
export default class extends Controller {
|
|
15
|
-
static values = {
|
|
16
|
-
interval: { type: Number, default: 30000 },
|
|
17
|
-
enabled: { type: Boolean, default: false }
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
static targets = ["button", "indicator"]
|
|
21
|
-
|
|
22
|
-
connect() {
|
|
23
|
-
this.updateUI()
|
|
24
|
-
if (this.enabledValue) {
|
|
25
|
-
this.startRefresh()
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
disconnect() {
|
|
30
|
-
this.stopRefresh()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
toggle() {
|
|
34
|
-
this.enabledValue = !this.enabledValue
|
|
35
|
-
|
|
36
|
-
if (this.enabledValue) {
|
|
37
|
-
this.startRefresh()
|
|
38
|
-
this.refresh() // Immediate refresh when enabled
|
|
39
|
-
} else {
|
|
40
|
-
this.stopRefresh()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
this.updateUI()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
startRefresh() {
|
|
47
|
-
if (this.intervalValue > 0 && !this.timer) {
|
|
48
|
-
this.timer = setInterval(() => this.refresh(), this.intervalValue)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
stopRefresh() {
|
|
53
|
-
if (this.timer) {
|
|
54
|
-
clearInterval(this.timer)
|
|
55
|
-
this.timer = null
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
refresh() {
|
|
60
|
-
const frame = this.element.closest("turbo-frame") ||
|
|
61
|
-
this.element.querySelector("turbo-frame")
|
|
62
|
-
|
|
63
|
-
if (frame && typeof frame.reload === "function") {
|
|
64
|
-
frame.reload()
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
updateUI() {
|
|
69
|
-
if (this.hasButtonTarget) {
|
|
70
|
-
if (this.enabledValue) {
|
|
71
|
-
this.buttonTarget.classList.remove("bg-gray-100", "text-gray-600")
|
|
72
|
-
this.buttonTarget.classList.add("bg-green-100", "text-green-700")
|
|
73
|
-
} else {
|
|
74
|
-
this.buttonTarget.classList.remove("bg-green-100", "text-green-700")
|
|
75
|
-
this.buttonTarget.classList.add("bg-gray-100", "text-gray-600")
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (this.hasIndicatorTarget) {
|
|
80
|
-
this.indicatorTarget.textContent = this.enabledValue ? "On" : "Off"
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|