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
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Agents
|
|
5
|
+
class Base
|
|
6
|
+
# Result object construction from LLM responses
|
|
7
|
+
#
|
|
8
|
+
# Handles building Result objects with full execution metadata
|
|
9
|
+
# including tokens, costs, timing, and tool calls.
|
|
10
|
+
module ResponseBuilding
|
|
11
|
+
# Builds a Result object from processed content and response metadata
|
|
12
|
+
#
|
|
13
|
+
# @param content [Hash, String] The processed response content
|
|
14
|
+
# @param response [RubyLLM::Message] The raw LLM response
|
|
15
|
+
# @return [Result] A Result object with full execution metadata
|
|
16
|
+
def build_result(content, response)
|
|
17
|
+
completed_at = Time.current
|
|
18
|
+
input_tokens = result_response_value(response, :input_tokens)
|
|
19
|
+
output_tokens = result_response_value(response, :output_tokens)
|
|
20
|
+
response_model_id = result_response_value(response, :model_id)
|
|
21
|
+
|
|
22
|
+
Result.new(
|
|
23
|
+
content: content,
|
|
24
|
+
input_tokens: input_tokens,
|
|
25
|
+
output_tokens: output_tokens,
|
|
26
|
+
cached_tokens: result_response_value(response, :cached_tokens, 0),
|
|
27
|
+
cache_creation_tokens: result_response_value(response, :cache_creation_tokens, 0),
|
|
28
|
+
model_id: model,
|
|
29
|
+
chosen_model_id: response_model_id || model,
|
|
30
|
+
temperature: temperature,
|
|
31
|
+
started_at: @execution_started_at,
|
|
32
|
+
completed_at: completed_at,
|
|
33
|
+
duration_ms: result_duration_ms(completed_at),
|
|
34
|
+
time_to_first_token_ms: @time_to_first_token_ms,
|
|
35
|
+
finish_reason: result_finish_reason(response),
|
|
36
|
+
streaming: self.class.streaming,
|
|
37
|
+
input_cost: result_input_cost(input_tokens, response_model_id),
|
|
38
|
+
output_cost: result_output_cost(output_tokens, response_model_id),
|
|
39
|
+
total_cost: result_total_cost(input_tokens, output_tokens, response_model_id),
|
|
40
|
+
tool_calls: @accumulated_tool_calls,
|
|
41
|
+
tool_calls_count: @accumulated_tool_calls.size
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Safely extracts a value from the response object
|
|
46
|
+
#
|
|
47
|
+
# @param response [Object] The response object
|
|
48
|
+
# @param method [Symbol] The method to call
|
|
49
|
+
# @param default [Object] Default value if method doesn't exist
|
|
50
|
+
# @return [Object] The extracted value or default
|
|
51
|
+
def result_response_value(response, method, default = nil)
|
|
52
|
+
return default unless response.respond_to?(method)
|
|
53
|
+
response.send(method) || default
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Calculates execution duration in milliseconds
|
|
57
|
+
#
|
|
58
|
+
# @param completed_at [Time] When execution completed
|
|
59
|
+
# @return [Integer, nil] Duration in ms or nil
|
|
60
|
+
def result_duration_ms(completed_at)
|
|
61
|
+
return nil unless @execution_started_at
|
|
62
|
+
((completed_at - @execution_started_at) * 1000).to_i
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Extracts finish reason from response
|
|
66
|
+
#
|
|
67
|
+
# @param response [Object] The response object
|
|
68
|
+
# @return [String, nil] Normalized finish reason
|
|
69
|
+
def result_finish_reason(response)
|
|
70
|
+
reason = result_response_value(response, :finish_reason) ||
|
|
71
|
+
result_response_value(response, :stop_reason)
|
|
72
|
+
return nil unless reason
|
|
73
|
+
|
|
74
|
+
# Normalize to standard values
|
|
75
|
+
case reason.to_s.downcase
|
|
76
|
+
when "stop", "end_turn" then "stop"
|
|
77
|
+
when "length", "max_tokens" then "length"
|
|
78
|
+
when "content_filter", "safety" then "content_filter"
|
|
79
|
+
when "tool_calls", "tool_use" then "tool_calls"
|
|
80
|
+
else "other"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Agents
|
|
5
|
+
class Base
|
|
6
|
+
# Tool call tracking for agent executions
|
|
7
|
+
#
|
|
8
|
+
# Handles accumulating and serializing tool calls made during
|
|
9
|
+
# an agent's execution cycle.
|
|
10
|
+
module ToolTracking
|
|
11
|
+
# Resets accumulated tool calls for a new execution
|
|
12
|
+
#
|
|
13
|
+
# @return [void]
|
|
14
|
+
def reset_accumulated_tool_calls!
|
|
15
|
+
@accumulated_tool_calls = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Extracts tool calls from all assistant messages in the conversation
|
|
19
|
+
#
|
|
20
|
+
# RubyLLM handles tool call loops internally. After ask() completes,
|
|
21
|
+
# the conversation history contains all intermediate assistant messages
|
|
22
|
+
# that had tool_calls. This method extracts those tool calls.
|
|
23
|
+
#
|
|
24
|
+
# @param client [RubyLLM::Chat] The chat client with conversation history
|
|
25
|
+
# @return [void]
|
|
26
|
+
def extract_tool_calls_from_client(client)
|
|
27
|
+
return unless client.respond_to?(:messages)
|
|
28
|
+
|
|
29
|
+
client.messages.each do |message|
|
|
30
|
+
next unless message.role == :assistant
|
|
31
|
+
next unless message.respond_to?(:tool_calls) && message.tool_calls.present?
|
|
32
|
+
|
|
33
|
+
message.tool_calls.each_value do |tool_call|
|
|
34
|
+
@accumulated_tool_calls << serialize_tool_call(tool_call)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Serializes a single tool call to a hash
|
|
40
|
+
#
|
|
41
|
+
# @param tool_call [Object] The tool call object
|
|
42
|
+
# @return [Hash] Serialized tool call
|
|
43
|
+
def serialize_tool_call(tool_call)
|
|
44
|
+
if tool_call.respond_to?(:to_h)
|
|
45
|
+
tool_call.to_h.transform_keys(&:to_s)
|
|
46
|
+
else
|
|
47
|
+
{
|
|
48
|
+
"id" => tool_call.id,
|
|
49
|
+
"name" => tool_call.name,
|
|
50
|
+
"arguments" => tool_call.arguments
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|