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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -0
  3. data/app/controllers/ruby_llm/agents/dashboard_controller.rb +68 -4
  4. data/app/models/ruby_llm/agents/execution/analytics.rb +114 -13
  5. data/app/models/ruby_llm/agents/execution/scopes.rb +10 -0
  6. data/app/models/ruby_llm/agents/execution.rb +26 -58
  7. data/app/views/layouts/rubyllm/agents/application.html.erb +103 -352
  8. data/app/views/rubyllm/agents/agents/_agent.html.erb +87 -0
  9. data/app/views/rubyllm/agents/agents/index.html.erb +2 -71
  10. data/app/views/rubyllm/agents/agents/show.html.erb +349 -416
  11. data/app/views/rubyllm/agents/dashboard/_action_center.html.erb +7 -7
  12. data/app/views/rubyllm/agents/dashboard/_agent_comparison.html.erb +46 -0
  13. data/app/views/rubyllm/agents/dashboard/_budgets_bar.html.erb +0 -90
  14. data/app/views/rubyllm/agents/dashboard/_execution_item.html.erb +54 -39
  15. data/app/views/rubyllm/agents/dashboard/_now_strip.html.erb +79 -5
  16. data/app/views/rubyllm/agents/dashboard/_top_errors.html.erb +49 -0
  17. data/app/views/rubyllm/agents/dashboard/index.html.erb +76 -151
  18. data/app/views/rubyllm/agents/executions/show.html.erb +256 -93
  19. data/app/views/rubyllm/agents/settings/show.html.erb +1 -1
  20. data/app/views/rubyllm/agents/shared/_breadcrumbs.html.erb +48 -0
  21. data/app/views/rubyllm/agents/shared/_nav_link.html.erb +27 -0
  22. data/config/routes.rb +2 -0
  23. data/lib/generators/ruby_llm_agents/templates/add_tool_calls_migration.rb.tt +28 -0
  24. data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +7 -0
  25. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +13 -0
  26. data/lib/ruby_llm/agents/base/caching.rb +43 -0
  27. data/lib/ruby_llm/agents/base/cost_calculation.rb +103 -0
  28. data/lib/ruby_llm/agents/base/dsl.rb +261 -0
  29. data/lib/ruby_llm/agents/base/execution.rb +206 -0
  30. data/lib/ruby_llm/agents/base/reliability_execution.rb +131 -0
  31. data/lib/ruby_llm/agents/base/response_building.rb +86 -0
  32. data/lib/ruby_llm/agents/base/tool_tracking.rb +57 -0
  33. data/lib/ruby_llm/agents/base.rb +19 -619
  34. data/lib/ruby_llm/agents/instrumentation.rb +36 -3
  35. data/lib/ruby_llm/agents/result.rb +235 -0
  36. data/lib/ruby_llm/agents/version.rb +1 -1
  37. data/lib/ruby_llm/agents.rb +1 -0
  38. metadata +15 -20
  39. data/app/channels/ruby_llm/agents/executions_channel.rb +0 -46
  40. data/app/javascript/ruby_llm/agents/controllers/filter_controller.js +0 -56
  41. data/app/javascript/ruby_llm/agents/controllers/index.js +0 -12
  42. data/app/javascript/ruby_llm/agents/controllers/refresh_controller.js +0 -83
  43. 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