ruby_llm-agents 3.11.0 → 3.13.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/app/controllers/ruby_llm/agents/agents_controller.rb +74 -0
- data/app/controllers/ruby_llm/agents/analytics_controller.rb +304 -0
- data/app/controllers/ruby_llm/agents/executions_controller.rb +5 -0
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +74 -2
- data/app/models/ruby_llm/agents/agent_override.rb +47 -0
- data/app/models/ruby_llm/agents/execution/analytics.rb +37 -16
- data/app/models/ruby_llm/agents/execution.rb +51 -1
- data/app/services/ruby_llm/agents/agent_registry.rb +8 -1
- data/app/views/layouts/ruby_llm/agents/application.html.erb +4 -2
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +93 -4
- data/app/views/ruby_llm/agents/agents/show.html.erb +17 -2
- data/app/views/ruby_llm/agents/analytics/index.html.erb +398 -0
- data/app/views/ruby_llm/agents/executions/_audio_player.html.erb +1 -1
- data/app/views/ruby_llm/agents/executions/_filters.html.erb +12 -8
- data/app/views/ruby_llm/agents/executions/show.html.erb +26 -12
- data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +46 -7
- data/app/views/ruby_llm/agents/shared/_tenant_filter.html.erb +2 -2
- data/app/views/ruby_llm/agents/system_config/show.html.erb +6 -2
- data/app/views/ruby_llm/agents/tenants/index.html.erb +3 -2
- data/app/views/ruby_llm/agents/tenants/show.html.erb +225 -0
- data/config/routes.rb +12 -4
- data/lib/generators/ruby_llm_agents/templates/create_overrides_migration.rb.tt +28 -0
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +27 -1
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +1 -1
- data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +1 -1
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +14 -0
- data/lib/ruby_llm/agents/base_agent.rb +90 -133
- data/lib/ruby_llm/agents/core/base.rb +9 -0
- data/lib/ruby_llm/agents/core/configuration.rb +93 -7
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +131 -4
- data/lib/ruby_llm/agents/dsl/knowledge.rb +157 -0
- data/lib/ruby_llm/agents/dsl.rb +1 -1
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +9 -5
- data/lib/ruby_llm/agents/infrastructure/retention_job.rb +118 -0
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +32 -20
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +22 -1
- data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +1 -1
- data/lib/ruby_llm/agents/rails/engine.rb +20 -4
- data/lib/ruby_llm/agents/routing.rb +28 -5
- data/lib/ruby_llm/agents/stream_event.rb +2 -10
- data/lib/ruby_llm/agents/tool.rb +1 -1
- data/lib/ruby_llm/agents.rb +1 -3
- data/lib/tasks/ruby_llm_agents.rake +7 -0
- metadata +9 -5
- data/lib/ruby_llm/agents/agent_tool.rb +0 -143
- data/lib/ruby_llm/agents/dsl/agents.rb +0 -141
|
@@ -115,6 +115,15 @@ module RubyLLM
|
|
|
115
115
|
@ask_message || options[:message] || super
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
+
# Override call to capture the caller's stream block so it can be
|
|
119
|
+
# forwarded to the delegated agent. Without this, chunks from the
|
|
120
|
+
# delegated agent are swallowed because build_result has no access
|
|
121
|
+
# to the original block.
|
|
122
|
+
def call(&block)
|
|
123
|
+
@delegation_stream_block = block
|
|
124
|
+
super
|
|
125
|
+
end
|
|
126
|
+
|
|
118
127
|
# Override process_response to parse the route from LLM output.
|
|
119
128
|
def process_response(response)
|
|
120
129
|
raw = response.content.to_s.strip.downcase.gsub(/[^a-z0-9_]/, "")
|
|
@@ -131,25 +140,39 @@ module RubyLLM
|
|
|
131
140
|
end
|
|
132
141
|
|
|
133
142
|
# Override build_result to return a RoutingResult.
|
|
134
|
-
# Auto-delegates to the mapped agent when the route has an `agent:` mapping
|
|
143
|
+
# Auto-delegates to the mapped agent when the route has an `agent:` mapping,
|
|
144
|
+
# unless the caller opts out with `auto_delegate: false`.
|
|
135
145
|
def build_result(content, response, context)
|
|
136
146
|
base = super
|
|
137
147
|
|
|
138
|
-
# Auto-delegate to the mapped agent
|
|
139
148
|
agent_class = content[:agent_class]
|
|
140
|
-
if agent_class
|
|
141
|
-
content[:delegated_result] =
|
|
149
|
+
if agent_class && auto_delegate?
|
|
150
|
+
content[:delegated_result] = if @delegation_stream_block
|
|
151
|
+
agent_class.call(**delegation_params, &@delegation_stream_block)
|
|
152
|
+
else
|
|
153
|
+
agent_class.call(**delegation_params)
|
|
154
|
+
end
|
|
142
155
|
end
|
|
143
156
|
|
|
144
157
|
RoutingResult.new(base_result: base, route_data: content)
|
|
145
158
|
end
|
|
146
159
|
|
|
160
|
+
# Whether auto-delegation to the mapped agent is enabled for this call.
|
|
161
|
+
# Defaults to true. Pass `auto_delegate: false` to receive a
|
|
162
|
+
# classification-only RoutingResult with `delegated? == false` and
|
|
163
|
+
# `agent_class` set so the caller can invoke it manually.
|
|
164
|
+
#
|
|
165
|
+
# @return [Boolean]
|
|
166
|
+
def auto_delegate?
|
|
167
|
+
@options.fetch(:auto_delegate, true)
|
|
168
|
+
end
|
|
169
|
+
|
|
147
170
|
# Builds params to forward to the delegated agent.
|
|
148
171
|
# Forwards original message and custom params, excludes routing internals.
|
|
149
172
|
#
|
|
150
173
|
# @return [Hash] Params for the delegated agent
|
|
151
174
|
def delegation_params
|
|
152
|
-
forward = @options.except(:dry_run, :skip_cache, :debug, :stream_events)
|
|
175
|
+
forward = @options.except(:dry_run, :skip_cache, :debug, :stream_events, :auto_delegate)
|
|
153
176
|
forward[:_parent_execution_id] = @parent_execution_id if @parent_execution_id
|
|
154
177
|
forward[:_root_execution_id] = @root_execution_id if @root_execution_id
|
|
155
178
|
forward
|
|
@@ -7,7 +7,7 @@ module RubyLLM
|
|
|
7
7
|
# When `stream_events: true` is passed to an agent call, the stream
|
|
8
8
|
# block receives StreamEvent objects instead of raw RubyLLM chunks.
|
|
9
9
|
# This provides visibility into the full execution lifecycle —
|
|
10
|
-
# text chunks, tool invocations,
|
|
10
|
+
# text chunks, tool invocations, and errors.
|
|
11
11
|
#
|
|
12
12
|
# @example Basic usage
|
|
13
13
|
# MyAgent.call(query: "test", stream_events: true) do |event|
|
|
@@ -15,15 +15,12 @@ module RubyLLM
|
|
|
15
15
|
# when :chunk then print event.data[:content]
|
|
16
16
|
# when :tool_start then puts "Running #{event.data[:tool_name]}..."
|
|
17
17
|
# when :tool_end then puts "Done (#{event.data[:duration_ms]}ms)"
|
|
18
|
-
# when :agent_start then puts "Delegated to #{event.data[:agent_name]}"
|
|
19
|
-
# when :agent_end then puts "Agent done (#{event.data[:duration_ms]}ms)"
|
|
20
18
|
# when :error then puts "Error: #{event.data[:message]}"
|
|
21
19
|
# end
|
|
22
20
|
# end
|
|
23
21
|
#
|
|
24
22
|
class StreamEvent
|
|
25
|
-
# @return [Symbol] Event type (:chunk, :tool_start, :tool_end,
|
|
26
|
-
# :agent_start, :agent_end, :error)
|
|
23
|
+
# @return [Symbol] Event type (:chunk, :tool_start, :tool_end, :error)
|
|
27
24
|
attr_reader :type
|
|
28
25
|
|
|
29
26
|
# @return [Hash] Event-specific data
|
|
@@ -48,11 +45,6 @@ module RubyLLM
|
|
|
48
45
|
@type == :tool_start || @type == :tool_end
|
|
49
46
|
end
|
|
50
47
|
|
|
51
|
-
# @return [Boolean] Whether this is an agent lifecycle event
|
|
52
|
-
def agent_event?
|
|
53
|
-
@type == :agent_start || @type == :agent_end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
48
|
# @return [Boolean] Whether this is an error event
|
|
57
49
|
def error?
|
|
58
50
|
@type == :error
|
data/lib/ruby_llm/agents/tool.rb
CHANGED
data/lib/ruby_llm/agents.rb
CHANGED
|
@@ -23,9 +23,6 @@ require_relative "agents/dsl"
|
|
|
23
23
|
# BaseAgent - new middleware-based agent architecture
|
|
24
24
|
require_relative "agents/base_agent"
|
|
25
25
|
|
|
26
|
-
# Agent-as-Tool adapter
|
|
27
|
-
require_relative "agents/agent_tool"
|
|
28
|
-
|
|
29
26
|
# Streaming events
|
|
30
27
|
require_relative "agents/stream_event"
|
|
31
28
|
|
|
@@ -99,6 +96,7 @@ if defined?(Rails)
|
|
|
99
96
|
require_relative "agents/core/inflections"
|
|
100
97
|
require_relative "agents/core/instrumentation"
|
|
101
98
|
require_relative "agents/infrastructure/execution_logger_job"
|
|
99
|
+
require_relative "agents/infrastructure/retention_job"
|
|
102
100
|
end
|
|
103
101
|
require_relative "agents/rails/engine" if defined?(Rails::Engine)
|
|
104
102
|
|
|
@@ -7,6 +7,13 @@ namespace :ruby_llm_agents do
|
|
|
7
7
|
RubyLlmAgents::DoctorGenerator.start([])
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
desc "Run the retention job synchronously (soft + hard purges per configuration)"
|
|
11
|
+
task purge: :environment do
|
|
12
|
+
result = RubyLLM::Agents::RetentionJob.new.perform
|
|
13
|
+
puts "Soft purged: #{result[:soft_purged]} executions (details destroyed)"
|
|
14
|
+
puts "Hard purged: #{result[:hard_purged]} executions (rows destroyed)"
|
|
15
|
+
end
|
|
16
|
+
|
|
10
17
|
desc "Rename an agent type in execution records. Usage: rake ruby_llm_agents:rename_agent FROM=OldName TO=NewName [DRY_RUN=1]"
|
|
11
18
|
task rename_agent: :environment do
|
|
12
19
|
from = ENV["FROM"]
|
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: 3.
|
|
4
|
+
version: 3.13.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.14.1
|
|
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.14.1
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: csv
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,12 +80,14 @@ files:
|
|
|
80
80
|
- app/controllers/concerns/ruby_llm/agents/paginatable.rb
|
|
81
81
|
- app/controllers/concerns/ruby_llm/agents/sortable.rb
|
|
82
82
|
- app/controllers/ruby_llm/agents/agents_controller.rb
|
|
83
|
+
- app/controllers/ruby_llm/agents/analytics_controller.rb
|
|
83
84
|
- app/controllers/ruby_llm/agents/dashboard_controller.rb
|
|
84
85
|
- app/controllers/ruby_llm/agents/executions_controller.rb
|
|
85
86
|
- app/controllers/ruby_llm/agents/requests_controller.rb
|
|
86
87
|
- app/controllers/ruby_llm/agents/system_config_controller.rb
|
|
87
88
|
- app/controllers/ruby_llm/agents/tenants_controller.rb
|
|
88
89
|
- app/helpers/ruby_llm/agents/application_helper.rb
|
|
90
|
+
- app/models/ruby_llm/agents/agent_override.rb
|
|
89
91
|
- app/models/ruby_llm/agents/execution.rb
|
|
90
92
|
- app/models/ruby_llm/agents/execution/analytics.rb
|
|
91
93
|
- app/models/ruby_llm/agents/execution/metrics.rb
|
|
@@ -112,6 +114,7 @@ files:
|
|
|
112
114
|
- app/views/ruby_llm/agents/agents/_sortable_header.html.erb
|
|
113
115
|
- app/views/ruby_llm/agents/agents/index.html.erb
|
|
114
116
|
- app/views/ruby_llm/agents/agents/show.html.erb
|
|
117
|
+
- app/views/ruby_llm/agents/analytics/index.html.erb
|
|
115
118
|
- app/views/ruby_llm/agents/dashboard/_action_center.html.erb
|
|
116
119
|
- app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb
|
|
117
120
|
- app/views/ruby_llm/agents/dashboard/_top_tenants.html.erb
|
|
@@ -185,6 +188,7 @@ files:
|
|
|
185
188
|
- lib/generators/ruby_llm_agents/templates/application_transcriber.rb.tt
|
|
186
189
|
- lib/generators/ruby_llm_agents/templates/background_remover.rb.tt
|
|
187
190
|
- lib/generators/ruby_llm_agents/templates/create_execution_details_migration.rb.tt
|
|
191
|
+
- lib/generators/ruby_llm_agents/templates/create_overrides_migration.rb.tt
|
|
188
192
|
- lib/generators/ruby_llm_agents/templates/create_tenant_budgets_migration.rb.tt
|
|
189
193
|
- lib/generators/ruby_llm_agents/templates/create_tenants_migration.rb.tt
|
|
190
194
|
- lib/generators/ruby_llm_agents/templates/embedder.rb.tt
|
|
@@ -221,7 +225,6 @@ files:
|
|
|
221
225
|
- lib/generators/ruby_llm_agents/upgrade_generator.rb
|
|
222
226
|
- lib/ruby_llm-agents.rb
|
|
223
227
|
- lib/ruby_llm/agents.rb
|
|
224
|
-
- lib/ruby_llm/agents/agent_tool.rb
|
|
225
228
|
- lib/ruby_llm/agents/audio/elevenlabs/model_registry.rb
|
|
226
229
|
- lib/ruby_llm/agents/audio/speaker.rb
|
|
227
230
|
- lib/ruby_llm/agents/audio/speaker/active_storage_support.rb
|
|
@@ -240,9 +243,9 @@ files:
|
|
|
240
243
|
- lib/ruby_llm/agents/core/llm_tenant.rb
|
|
241
244
|
- lib/ruby_llm/agents/core/version.rb
|
|
242
245
|
- lib/ruby_llm/agents/dsl.rb
|
|
243
|
-
- lib/ruby_llm/agents/dsl/agents.rb
|
|
244
246
|
- lib/ruby_llm/agents/dsl/base.rb
|
|
245
247
|
- lib/ruby_llm/agents/dsl/caching.rb
|
|
248
|
+
- lib/ruby_llm/agents/dsl/knowledge.rb
|
|
246
249
|
- lib/ruby_llm/agents/dsl/queryable.rb
|
|
247
250
|
- lib/ruby_llm/agents/dsl/reliability.rb
|
|
248
251
|
- lib/ruby_llm/agents/eval.rb
|
|
@@ -287,6 +290,7 @@ files:
|
|
|
287
290
|
- lib/ruby_llm/agents/infrastructure/circuit_breaker.rb
|
|
288
291
|
- lib/ruby_llm/agents/infrastructure/execution_logger_job.rb
|
|
289
292
|
- lib/ruby_llm/agents/infrastructure/reliability.rb
|
|
293
|
+
- lib/ruby_llm/agents/infrastructure/retention_job.rb
|
|
290
294
|
- lib/ruby_llm/agents/pipeline.rb
|
|
291
295
|
- lib/ruby_llm/agents/pipeline/builder.rb
|
|
292
296
|
- lib/ruby_llm/agents/pipeline/context.rb
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RubyLLM
|
|
4
|
-
module Agents
|
|
5
|
-
# Wraps an agent class as a RubyLLM::Tool so it can be used
|
|
6
|
-
# in another agent's `tools` list. The LLM sees the sub-agent
|
|
7
|
-
# as a callable tool and can invoke it with the agent's declared params.
|
|
8
|
-
module AgentTool
|
|
9
|
-
MAX_AGENT_TOOL_DEPTH = 5
|
|
10
|
-
|
|
11
|
-
# Wraps an agent class as a RubyLLM::Tool subclass.
|
|
12
|
-
#
|
|
13
|
-
# @param agent_class [Class] A BaseAgent subclass
|
|
14
|
-
# @param forwarded_params [Array<Symbol>] Params auto-injected from parent (excluded from LLM schema)
|
|
15
|
-
# @param description_override [String, nil] Custom description for the tool
|
|
16
|
-
# @param delegate [Boolean] Whether this tool represents an agent delegate (from `agents` DSL)
|
|
17
|
-
# @return [Class] An anonymous RubyLLM::Tool subclass
|
|
18
|
-
def self.for(agent_class, forwarded_params: [], description_override: nil, delegate: false)
|
|
19
|
-
tool_name = derive_tool_name(agent_class)
|
|
20
|
-
tool_desc = description_override || (agent_class.respond_to?(:description) ? agent_class.description : nil)
|
|
21
|
-
agent_params = agent_class.respond_to?(:params) ? agent_class.params : {}
|
|
22
|
-
captured_agent_class = agent_class
|
|
23
|
-
captured_forwarded = Array(forwarded_params).map(&:to_sym)
|
|
24
|
-
is_delegate = delegate
|
|
25
|
-
|
|
26
|
-
Class.new(RubyLLM::Tool) do
|
|
27
|
-
description tool_desc if tool_desc
|
|
28
|
-
|
|
29
|
-
# Map agent params to tool params, excluding forwarded ones
|
|
30
|
-
agent_params.each do |name, config|
|
|
31
|
-
next if name.to_s.start_with?("_")
|
|
32
|
-
next if captured_forwarded.include?(name.to_sym)
|
|
33
|
-
|
|
34
|
-
param name,
|
|
35
|
-
desc: config[:desc] || "#{name} parameter",
|
|
36
|
-
required: config[:required] == true,
|
|
37
|
-
type: AgentTool.map_type(config[:type])
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Store references on the class
|
|
41
|
-
define_singleton_method(:agent_class) { captured_agent_class }
|
|
42
|
-
define_singleton_method(:tool_name) { tool_name }
|
|
43
|
-
define_singleton_method(:agent_delegate?) { is_delegate }
|
|
44
|
-
define_singleton_method(:forwarded_params) { captured_forwarded }
|
|
45
|
-
|
|
46
|
-
# Instance #name returns the derived tool name
|
|
47
|
-
define_method(:name) { tool_name }
|
|
48
|
-
|
|
49
|
-
define_method(:execute) do |**kwargs|
|
|
50
|
-
depth = (Thread.current[:ruby_llm_agents_tool_depth] || 0) + 1
|
|
51
|
-
if depth > MAX_AGENT_TOOL_DEPTH
|
|
52
|
-
return "Error calling #{captured_agent_class.name}: Agent tool depth exceeded (max #{MAX_AGENT_TOOL_DEPTH})"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
Thread.current[:ruby_llm_agents_tool_depth] = depth
|
|
56
|
-
|
|
57
|
-
# Inject hierarchy context from thread-local (set by calling agent)
|
|
58
|
-
caller_ctx = Thread.current[:ruby_llm_agents_caller_context]
|
|
59
|
-
|
|
60
|
-
call_kwargs = kwargs.dup
|
|
61
|
-
if caller_ctx
|
|
62
|
-
call_kwargs[:_parent_execution_id] = caller_ctx.execution_id
|
|
63
|
-
call_kwargs[:_root_execution_id] = caller_ctx.root_execution_id || caller_ctx.execution_id
|
|
64
|
-
call_kwargs[:tenant] = caller_ctx.tenant_object if caller_ctx.tenant_id && !call_kwargs.key?(:tenant)
|
|
65
|
-
|
|
66
|
-
# Inject forwarded params from the parent agent instance
|
|
67
|
-
if captured_forwarded.any? && caller_ctx.agent_instance
|
|
68
|
-
captured_forwarded.each do |param_name|
|
|
69
|
-
next if call_kwargs.key?(param_name)
|
|
70
|
-
if caller_ctx.agent_instance.respond_to?(param_name)
|
|
71
|
-
call_kwargs[param_name] = caller_ctx.agent_instance.send(param_name)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
result = captured_agent_class.call(**call_kwargs)
|
|
78
|
-
content = result.respond_to?(:content) ? result.content : result
|
|
79
|
-
case content
|
|
80
|
-
when String then content
|
|
81
|
-
when Hash then content.to_json
|
|
82
|
-
when nil then "(no response)"
|
|
83
|
-
else content.to_s
|
|
84
|
-
end
|
|
85
|
-
rescue => e
|
|
86
|
-
"Error calling #{captured_agent_class.name}: #{e.message}"
|
|
87
|
-
ensure
|
|
88
|
-
Thread.current[:ruby_llm_agents_tool_depth] = depth - 1
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Converts agent class name to tool name.
|
|
94
|
-
#
|
|
95
|
-
# @example
|
|
96
|
-
# ResearchAgent -> "research"
|
|
97
|
-
# CodeReviewAgent -> "code_review"
|
|
98
|
-
#
|
|
99
|
-
# @param agent_class [Class] The agent class
|
|
100
|
-
# @return [String] Snake-cased tool name
|
|
101
|
-
def self.derive_tool_name(agent_class)
|
|
102
|
-
raw = agent_class.name.to_s.split("::").last
|
|
103
|
-
raw.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
104
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
105
|
-
.downcase
|
|
106
|
-
.sub(/_agent$/, "")
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Maps Ruby types to JSON Schema types for tool parameters.
|
|
110
|
-
#
|
|
111
|
-
# @param type [Class, Symbol, nil] Ruby type
|
|
112
|
-
# @return [Symbol] JSON Schema type
|
|
113
|
-
def self.map_type(type)
|
|
114
|
-
case type
|
|
115
|
-
when :integer then :integer
|
|
116
|
-
when :number, :float then :number
|
|
117
|
-
when :boolean then :boolean
|
|
118
|
-
when :array then :array
|
|
119
|
-
when :object then :object
|
|
120
|
-
else
|
|
121
|
-
# Handle class objects (Integer, Float, Array, Hash, etc.)
|
|
122
|
-
if type.is_a?(Class)
|
|
123
|
-
if type <= Integer
|
|
124
|
-
:integer
|
|
125
|
-
elsif type <= Float
|
|
126
|
-
:number
|
|
127
|
-
elsif type <= Array
|
|
128
|
-
:array
|
|
129
|
-
elsif type <= Hash
|
|
130
|
-
:object
|
|
131
|
-
elsif type == TrueClass || type == FalseClass
|
|
132
|
-
:boolean
|
|
133
|
-
else
|
|
134
|
-
:string
|
|
135
|
-
end
|
|
136
|
-
else
|
|
137
|
-
:string
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RubyLLM
|
|
4
|
-
module Agents
|
|
5
|
-
module DSL
|
|
6
|
-
# DSL module for declaring sub-agents on an agent class.
|
|
7
|
-
#
|
|
8
|
-
# Provides two forms — simple list for common cases, block for
|
|
9
|
-
# per-agent configuration:
|
|
10
|
-
#
|
|
11
|
-
# @example Simple form
|
|
12
|
-
# agents [ModelsAgent, ViewsAgent], forward: [:workspace_path]
|
|
13
|
-
#
|
|
14
|
-
# @example Block form
|
|
15
|
-
# agents do
|
|
16
|
-
# use ModelsAgent, timeout: 180, description: "Build models"
|
|
17
|
-
# use ViewsAgent
|
|
18
|
-
# forward :workspace_path, :project_id
|
|
19
|
-
# parallel true
|
|
20
|
-
# end
|
|
21
|
-
#
|
|
22
|
-
module Agents
|
|
23
|
-
# Declares sub-agents for this agent class.
|
|
24
|
-
#
|
|
25
|
-
# @param list [Array<Class>, nil] Agent classes (simple form)
|
|
26
|
-
# @param options [Hash] Global options (simple form)
|
|
27
|
-
# @yield Configuration block (block form)
|
|
28
|
-
# @return [Array<Hash>] Agent entries
|
|
29
|
-
def agents(list = nil, **options, &block)
|
|
30
|
-
if block
|
|
31
|
-
config = AgentsConfig.new
|
|
32
|
-
config.instance_eval(&block)
|
|
33
|
-
@agents_config = config
|
|
34
|
-
elsif list
|
|
35
|
-
config = AgentsConfig.new
|
|
36
|
-
Array(list).each { |a| config.use(a) }
|
|
37
|
-
options.each { |k, v| config.send(k, *Array(v)) }
|
|
38
|
-
@agents_config = config
|
|
39
|
-
end
|
|
40
|
-
@agents_config&.agent_entries || []
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Returns the agents configuration object.
|
|
44
|
-
#
|
|
45
|
-
# @return [AgentsConfig] Configuration (empty if no agents declared)
|
|
46
|
-
def agents_config
|
|
47
|
-
@agents_config ||
|
|
48
|
-
(superclass.respond_to?(:agents_config) ? superclass.agents_config : nil) ||
|
|
49
|
-
AgentsConfig.new
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Configuration object for the `agents` DSL.
|
|
55
|
-
#
|
|
56
|
-
# Holds the list of agent entries and global options like
|
|
57
|
-
# `parallel`, `forward`, `max_depth`, and `instructions`.
|
|
58
|
-
#
|
|
59
|
-
class AgentsConfig
|
|
60
|
-
attr_reader :agent_entries
|
|
61
|
-
|
|
62
|
-
def initialize
|
|
63
|
-
@agent_entries = []
|
|
64
|
-
@options = {
|
|
65
|
-
parallel: true,
|
|
66
|
-
timeout: nil,
|
|
67
|
-
max_depth: 5,
|
|
68
|
-
forward: [],
|
|
69
|
-
instructions: nil
|
|
70
|
-
}
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Registers an agent class with optional per-agent overrides.
|
|
74
|
-
#
|
|
75
|
-
# @param agent_class [Class] A BaseAgent subclass
|
|
76
|
-
# @param timeout [Integer, nil] Per-agent timeout override
|
|
77
|
-
# @param description [String, nil] Per-agent description override
|
|
78
|
-
def use(agent_class, timeout: nil, description: nil)
|
|
79
|
-
@agent_entries << {
|
|
80
|
-
agent_class: agent_class,
|
|
81
|
-
timeout: timeout,
|
|
82
|
-
description: description
|
|
83
|
-
}
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# @!group Global Options
|
|
87
|
-
|
|
88
|
-
def parallel(value = true)
|
|
89
|
-
@options[:parallel] = value
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def timeout(seconds)
|
|
93
|
-
@options[:timeout] = seconds
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def max_depth(depth)
|
|
97
|
-
@options[:max_depth] = depth
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def instructions(text)
|
|
101
|
-
@options[:instructions] = text
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def forward(*params)
|
|
105
|
-
@options[:forward] = params.flatten
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# @!endgroup
|
|
109
|
-
|
|
110
|
-
# @!group Query Methods
|
|
111
|
-
|
|
112
|
-
def parallel?
|
|
113
|
-
@options[:parallel]
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def timeout_for(agent_class)
|
|
117
|
-
entry = @agent_entries.find { |e| e[:agent_class] == agent_class }
|
|
118
|
-
entry&.dig(:timeout) || @options[:timeout]
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def forwarded_params
|
|
122
|
-
@options[:forward]
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def max_depth_value
|
|
126
|
-
@options[:max_depth]
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def instructions_text
|
|
130
|
-
@options[:instructions]
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def description_for(agent_class)
|
|
134
|
-
entry = @agent_entries.find { |e| e[:agent_class] == agent_class }
|
|
135
|
-
entry&.dig(:description)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# @!endgroup
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
end
|