Class: Collavre::Orchestration::PolicyResolver

Inherits:
Object
  • Object
show all
Defined in:
app/services/collavre/orchestration/policy_resolver.rb

Overview

PolicyResolver finds and merges applicable policies for a given context.

Policy precedence (later overrides earlier):

  1. Global policies

  2. Creative-level policies

  3. Topic-level policies

  4. Agent-level policies (for scheduling)

Usage:

resolver = PolicyResolver.new(context)
resolver.arbitration_config
# => { "strategy" => "primary_first", "max_responders" => 1 }

Constant Summary collapse

DEFAULTS =

Default configurations when no policy is set

{
  "arbitration" => {
    "strategy" => "all",
    "max_responders" => nil # nil means unlimited
  },
  "scheduling" => {
    "max_concurrent_jobs" => 5,
    "daily_token_limit" => 100_000,
    "rate_limit_per_minute" => 20,
    "backoff_strategy" => "exponential",
    "topic_max_concurrent_jobs" => 1,
    # Loop breaker settings
    "loop_breaker_enabled" => true,
    "ping_pong_threshold" => 5,           # Max back-and-forth between same agents
    "creative_retry_threshold" => 10,     # Max tasks on same creative in time window
    "creative_retry_window_minutes" => 30,
    "task_timeout_minutes" => 60,         # Max time for a single task
    "token_spike_threshold" => 50_000,    # Token usage spike in window
    "token_spike_window_minutes" => 10
  },
  "stuck_detection" => {
    "enabled" => false,
    "task_stuck_threshold_minutes" => 30,            # Task running for > N minutes
    "creative_stall_threshold_minutes" => 120,       # Creative no progress for > N minutes
    "queued_orphan_threshold_minutes" => 5,          # Queued waiter with no live blocker for > N minutes
    "create_system_comment" => true                  # Create system comment on escalation
  },
  "collaboration" => {
    "a2a_focus_instruction" => nil,         # nil = locale default
    "a2a_completion_instruction" => nil,
    "a2a_followup_instruction" => nil,
    "mention_rule" => nil,
    "confidence_rule" => nil,
    "escalation_rule" => nil,
    "review_rule" => nil
  }
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(context) ⇒ PolicyResolver

Returns a new instance of PolicyResolver.



58
59
60
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 58

def initialize(context)
  @context = context
end

Instance Method Details

#arbitration_configObject

Get merged arbitration config



63
64
65
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 63

def arbitration_config
  @arbitration_config ||= merge_policies("arbitration")
end

#arbitration_strategyObject

Convenience methods



91
92
93
94
95
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 91

def arbitration_strategy
  return "primary_first" if topic_primary_agent_id.present?

  arbitration_config["strategy"]
end

#bid_fallback_enabled?Boolean

Returns:

  • (Boolean)


110
111
112
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 110

def bid_fallback_enabled?
  arbitration_config["bid_fallback_enabled"] != false
end

#collaboration_configObject

Collaboration config



132
133
134
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 132

def collaboration_config
  @collaboration_config ||= merge_policies("collaboration")
end

#confidence_thresholdObject

Bid strategy specific



106
107
108
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 106

def confidence_threshold
  arbitration_config["confidence_threshold"]
end

#loop_breaker_configObject



119
120
121
122
123
124
125
126
127
128
129
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 119

def loop_breaker_config
  {
    "enabled" => scheduling_config["loop_breaker_enabled"],
    "ping_pong_threshold" => scheduling_config["ping_pong_threshold"],
    "creative_retry_threshold" => scheduling_config["creative_retry_threshold"],
    "creative_retry_window_minutes" => scheduling_config["creative_retry_window_minutes"],
    "task_timeout_minutes" => scheduling_config["task_timeout_minutes"],
    "token_spike_threshold" => scheduling_config["token_spike_threshold"],
    "token_spike_window_minutes" => scheduling_config["token_spike_window_minutes"]
  }
end

#loop_breaker_enabled?Boolean

Loop breaker settings

Returns:

  • (Boolean)


115
116
117
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 115

def loop_breaker_enabled?
  scheduling_config["loop_breaker_enabled"] == true
end

#max_respondersObject



97
98
99
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 97

def max_responders
  arbitration_config["max_responders"]
end

#primary_agent_idObject



101
102
103
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 101

def primary_agent_id
  topic_primary_agent_id || arbitration_config["primary_agent_id"]
end

#resolve(policy_type) ⇒ Object

Generic resolve method for any policy type



146
147
148
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 146

def resolve(policy_type)
  merge_policies(policy_type)
end

#scheduling_configObject

Get merged scheduling config (without agent-specific overrides)



81
82
83
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 81

def scheduling_config
  @scheduling_config ||= merge_policies("scheduling")
end

#scheduling_config_for(agent) ⇒ Object

Get merged scheduling config for a specific agent



68
69
70
71
72
73
74
75
76
77
78
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 68

def scheduling_config_for(agent)
  base = merge_policies("scheduling")

  # Layer agent-specific policies on top
  agent_policies = OrchestratorPolicy.for_agent(agent.id, policy_type: "scheduling")
  agent_policies.each do |policy|
    base = base.merge(policy.config)
  end

  base
end

#stuck_detection_configObject



141
142
143
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 141

def stuck_detection_config
  @stuck_detection_config ||= resolve("stuck_detection")
end

#stuck_detection_enabled?Boolean

Stuck detection settings

Returns:

  • (Boolean)


137
138
139
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 137

def stuck_detection_enabled?
  stuck_detection_config["enabled"] == true
end

#topic_max_concurrent_jobsObject

Topic-level concurrency limit



86
87
88
# File 'app/services/collavre/orchestration/policy_resolver.rb', line 86

def topic_max_concurrent_jobs
  scheduling_config["topic_max_concurrent_jobs"]
end