Class: Pikuri::Agent::Control::StepLimit
- Inherits:
-
Object
- Object
- Pikuri::Agent::Control::StepLimit
- Defined in:
- lib/pikuri/agent/control/step_limit.rb
Overview
Caps the number of tool calls per Pikuri::Agent#run_loop invocation. ruby_llm has no built-in step budget; the Agent pokes #tick! on every before_tool_call callback and #reset! at the start of each turn. Once the counter exceeds the configured cap, #tick! raises Exceeded and the Agent applies the #on_exhausted policy: re-raise to the host (the default), or run the step-exhaustion synthesizer to salvage a partial answer.
Why the policy lives here, not on Agent
Synthesis can only ever fire off a tripped step limit, so an Agent.new(synthesize: …) kwarg would be meaningless whenever step_limit: is nil — an invalid combination the API would have to document away. Attaching the policy to the budget makes “what happens when the budget runs out” travel with the budget, and the nonsense state is unrepresentable. The host picks per wiring: a Q&A REPL wants :synthesize (salvage an answer from the evidence gathered so far); a coding agent wants the default :raise (a tools-free pass can’t finish writing code —stop, let the user say “continue”; #reset! at the next turn boundary refreshes the budget).
Defined Under Namespace
Classes: Exceeded
Constant Summary collapse
- ON_EXHAUSTED =
Valid #on_exhausted policies.
%i[raise synthesize].freeze
Instance Attribute Summary collapse
-
#max ⇒ Integer
readonly
The configured cap.
-
#on_exhausted ⇒ Symbol
readonly
What Pikuri::Agent#run_loop does when this budget trips:
:raiselets Exceeded propagate to the host;:synthesizeruns the tools-free synthesizer rescue. -
#step ⇒ Integer
readonly
Current step count; exposed so callers can introspect it (and so tests can assert it).
Instance Method Summary collapse
-
#initialize(max:, on_exhausted: :raise) ⇒ StepLimit
constructor
A new instance of StepLimit.
-
#reset! ⇒ void
Reset the counter back to zero.
- #tick! ⇒ void
-
#to_s ⇒ String
Short config dump for Pikuri::Agent#to_s.
Constructor Details
#initialize(max:, on_exhausted: :raise) ⇒ StepLimit
Returns a new instance of StepLimit.
61 62 63 64 65 66 67 68 69 |
# File 'lib/pikuri/agent/control/step_limit.rb', line 61 def initialize(max:, on_exhausted: :raise) raise ArgumentError, "max must be positive, got #{max}" if max <= 0 raise ArgumentError, "on_exhausted must be one of #{ON_EXHAUSTED.inspect}, got #{on_exhausted.inspect}" \ unless ON_EXHAUSTED.include?(on_exhausted) @max = max @on_exhausted = on_exhausted @step = 0 end |
Instance Attribute Details
#max ⇒ Integer (readonly)
Returns the configured cap.
47 48 49 |
# File 'lib/pikuri/agent/control/step_limit.rb', line 47 def max @max end |
#on_exhausted ⇒ Symbol (readonly)
Returns what Pikuri::Agent#run_loop does when this budget trips: :raise lets Exceeded propagate to the host; :synthesize runs the tools-free synthesizer rescue. See the class header for how to pick.
53 54 55 |
# File 'lib/pikuri/agent/control/step_limit.rb', line 53 def on_exhausted @on_exhausted end |
#step ⇒ Integer (readonly)
Returns current step count; exposed so callers can introspect it (and so tests can assert it).
99 100 101 |
# File 'lib/pikuri/agent/control/step_limit.rb', line 99 def step @step end |
Instance Method Details
#reset! ⇒ void
This method returns an undefined value.
Reset the counter back to zero. Called by Pikuri::Agent at the start of each turn (in Pikuri::Agent#run_loop before forwarding the user message to the chat) so the same instance can govern many turns across a long-running REPL. Mid-loop Interloper injections deliberately do not trigger a reset — those are additional context for the same turn, not a fresh one, and a chatty user could otherwise refresh the budget forever by injecting.
93 94 95 |
# File 'lib/pikuri/agent/control/step_limit.rb', line 93 def reset! @step = 0 end |
#tick! ⇒ void
This method returns an undefined value.
Increment the tool-call counter; raise Exceeded once it crosses #max. Called by Pikuri::Agent from its before_tool_call wiring.
78 79 80 81 |
# File 'lib/pikuri/agent/control/step_limit.rb', line 78 def tick! @step += 1 raise Exceeded, @max if @step > @max end |
#to_s ⇒ String
Returns short config dump for Pikuri::Agent#to_s. The policy only renders when it’s the non-default :synthesize, so existing banner output is unchanged.
104 105 106 107 |
# File 'lib/pikuri/agent/control/step_limit.rb', line 104 def to_s policy = @on_exhausted == :raise ? '' : ", on_exhausted=#{@on_exhausted}" "StepLimit(max=#{@max}#{policy})" end |