Module: Models::AssistantConversationPlannable
- Extended by:
- ActiveSupport::Concern
- Included in:
- AssistantConversation
- Defined in:
- app/concerns/models/assistant_conversation_plannable.rb
Overview
Execution-plan tracking for AssistantConversation: reads the
execution_plan / continuation counters stored in metadata, reports plan
progress, and decides whether a turn may auto-continue past the tool-call
ceiling.
Instance Method Summary collapse
-
#can_auto_continue? ⇒ Boolean
Whether the turn may be auto-continued: capped by
AssistantConversation::MAX_AUTO_CONTINUATIONSfor active plans, orMAX_NO_PLAN_AUTO_CONTINUATIONSwhen no plan was declared. -
#increment_continuation_count! ⇒ AssistantConversation
Atomically bumps the appropriate continuation counter in
metadata(plancontinuation_countwhen a plan is active, otherwiseno_plan_continuation_count) via a JSONB merge, then reloads. -
#plan_active? ⇒ Boolean
Whether the persisted execution plan still has pending or in-progress steps.
-
#plan_completed_steps ⇒ Array<Hash>
Steps of the persisted plan already finished.
-
#plan_continuation_count ⇒ Integer
How many times an active plan has already been auto-continued.
-
#plan_pending_steps ⇒ Array<Hash>
Steps of the persisted plan still awaiting work.
-
#plan_progress_summary ⇒ String?
Human-readable one-line plan status, e.g.
Instance Method Details
#can_auto_continue? ⇒ Boolean
Whether the turn may be auto-continued: capped by
AssistantConversation::MAX_AUTO_CONTINUATIONS for active plans, or
MAX_NO_PLAN_AUTO_CONTINUATIONS when no plan was declared.
58 59 60 61 62 63 64 65 66 67 |
# File 'app/concerns/models/assistant_conversation_plannable.rb', line 58 def can_auto_continue? if plan_active? plan_continuation_count < AssistantConversation::MAX_AUTO_CONTINUATIONS else # No declared plan: allow a small, fixed number of continuations so a # broad single-turn question that brushed the tool-call ceiling isn't # dead-ended on a manual "Continue in new conversation" button. no_plan_continuation_count.to_i < AssistantConversation::MAX_NO_PLAN_AUTO_CONTINUATIONS end end |
#increment_continuation_count! ⇒ AssistantConversation
Atomically bumps the appropriate continuation counter in metadata
(plan continuation_count when a plan is active, otherwise
no_plan_continuation_count) via a JSONB merge, then reloads.
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'app/concerns/models/assistant_conversation_plannable.rb', line 74 def increment_continuation_count! # Branch on plan_active? (not execution_plan presence) so it stays aligned # with can_auto_continue?'s gate: a completed-but-persisted plan hash must # consume the no-plan budget, otherwise the no-plan cap is never hit and the # turn auto-continues unbounded. if plan_active? plan = execution_plan.dup plan['continuation_count'] = plan['continuation_count'].to_i + 1 patch = { 'execution_plan' => plan }.to_json else patch = { 'no_plan_continuation_count' => no_plan_continuation_count.to_i + 1 }.to_json end self.class.where(id: id).update_all(["metadata = metadata || ?::jsonb", patch]) reload end |
#plan_active? ⇒ Boolean
Whether the persisted execution plan still has pending or in-progress steps.
15 16 17 18 19 20 |
# File 'app/concerns/models/assistant_conversation_plannable.rb', line 15 def plan_active? plan = execution_plan plan.is_a?(Hash) && Array(plan['steps']).any? do |step| %w[pending in_progress].include?(step['status']) end end |
#plan_completed_steps ⇒ Array<Hash>
Steps of the persisted plan already finished.
36 37 38 39 40 41 42 |
# File 'app/concerns/models/assistant_conversation_plannable.rb', line 36 def plan_completed_steps return [] unless execution_plan.is_a?(Hash) Array(execution_plan['steps']).select do |step| %w[completed skipped].include?(step['status']) end end |
#plan_continuation_count ⇒ Integer
How many times an active plan has already been auto-continued.
47 48 49 50 51 |
# File 'app/concerns/models/assistant_conversation_plannable.rb', line 47 def plan_continuation_count return 0 unless execution_plan.is_a?(Hash) execution_plan['continuation_count'].to_i end |
#plan_pending_steps ⇒ Array<Hash>
Steps of the persisted plan still awaiting work.
25 26 27 28 29 30 31 |
# File 'app/concerns/models/assistant_conversation_plannable.rb', line 25 def plan_pending_steps return [] unless execution_plan.is_a?(Hash) Array(execution_plan['steps']).select do |step| %w[pending in_progress].include?(step['status']) end end |
#plan_progress_summary ⇒ String?
Human-readable one-line plan status, e.g. "2/5 steps completed. Remaining:
…". Nil when there is no plan.
94 95 96 97 98 99 100 101 102 103 104 |
# File 'app/concerns/models/assistant_conversation_plannable.rb', line 94 def plan_progress_summary return nil unless execution_plan.is_a?(Hash) steps = Array(execution_plan['steps']) completed = steps.count { |step| %w[completed skipped].include?(step['status']) } total = steps.size pending = steps.select { |step| %w[pending in_progress].include?(step['status']) } pending_descriptions = pending.pluck('description') "#{completed}/#{total} steps completed. Remaining: #{pending_descriptions.join(', ')}" end |