Module: DecisionAgent::Replay
- Defined in:
- lib/decision_agent/replay/replay.rb
Class Method Summary collapse
- .build_replay_evaluators(evaluations) ⇒ Object
- .instantiate_scoring_strategy(payload) ⇒ Object
- .log_differences(original_decision:, original_confidence:, replayed_decision:, replayed_confidence:) ⇒ Object
- .parse_evaluations(payload) ⇒ Object
- .run(audit_payload, strict: true) ⇒ Object
- .validate_payload!(payload) ⇒ Object
- .validate_strict_match!(original_decision:, original_confidence:, replayed_decision:, replayed_confidence:) ⇒ Object
Class Method Details
.build_replay_evaluators(evaluations) ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/decision_agent/replay/replay.rb', line 74 def self.build_replay_evaluators(evaluations) evaluations.map do |evaluation| Evaluators::StaticEvaluator.new( decision: evaluation.decision, weight: evaluation.weight, reason: evaluation.reason, name: evaluation.evaluator_name, metadata: evaluation. ) end end |
.instantiate_scoring_strategy(payload) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/decision_agent/replay/replay.rb', line 86 def self.instantiate_scoring_strategy(payload) strategy_name = payload[:scoring_strategy] || payload["scoring_strategy"] return Scoring::WeightedAverage.new unless strategy_name case strategy_name when /WeightedAverage/ Scoring::WeightedAverage.new when /MaxWeight/ Scoring::MaxWeight.new when /Consensus/ Scoring::Consensus.new when /Threshold/ Scoring::Threshold.new else Scoring::WeightedAverage.new end end |
.log_differences(original_decision:, original_confidence:, replayed_decision:, replayed_confidence:) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/decision_agent/replay/replay.rb', line 124 def self.log_differences(original_decision:, original_confidence:, replayed_decision:, replayed_confidence:) differences = [] differences << "Decision changed: #{original_decision} -> #{replayed_decision}" if original_decision.to_s != replayed_decision.to_s conf_diff = (original_confidence.to_f - replayed_confidence.to_f).abs differences << "Confidence changed: #{original_confidence} -> #{replayed_confidence}" if conf_diff > 0.0001 if differences.any? warn "[DecisionAgent::Replay] Non-strict mode differences detected:" differences.each { |diff| warn " - #{diff}" } end differences end |
.parse_evaluations(payload) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/decision_agent/replay/replay.rb', line 56 def self.parse_evaluations(payload) evals = payload[:evaluations] || payload["evaluations"] evals.map do |eval_data| if eval_data.is_a?(Evaluation) eval_data else Evaluation.new( decision: eval_data[:decision] || eval_data["decision"], weight: eval_data[:weight] || eval_data["weight"], reason: eval_data[:reason] || eval_data["reason"], evaluator_name: eval_data[:evaluator_name] || eval_data["evaluator_name"], metadata: eval_data[:metadata] || eval_data["metadata"] || {} ) end end end |
.run(audit_payload, strict: true) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/decision_agent/replay/replay.rb', line 7 def self.run(audit_payload, strict: true) raise ArgumentError, "audit_payload cannot be nil" if audit_payload.nil? validate_payload!(audit_payload) context = Context.new(audit_payload[:context] || audit_payload["context"]) feedback = audit_payload[:feedback] || audit_payload["feedback"] || {} original_evaluations = parse_evaluations(audit_payload) original_decision = audit_payload[:decision] || audit_payload["decision"] original_confidence = audit_payload[:confidence] || audit_payload["confidence"] scoring_strategy = instantiate_scoring_strategy(audit_payload) agent = Agent.new( evaluators: build_replay_evaluators(original_evaluations), scoring_strategy: scoring_strategy, audit_adapter: Audit::NullAdapter.new ) replayed_result = agent.decide(context: context, feedback: feedback) if strict validate_strict_match!( original_decision: original_decision, original_confidence: original_confidence, replayed_decision: replayed_result.decision, replayed_confidence: replayed_result.confidence ) else log_differences( original_decision: original_decision, original_confidence: original_confidence, replayed_decision: replayed_result.decision, replayed_confidence: replayed_result.confidence ) end replayed_result end |
.validate_payload!(payload) ⇒ Object
48 49 50 51 52 53 54 |
# File 'lib/decision_agent/replay/replay.rb', line 48 def self.validate_payload!(payload) required_keys = %w[context evaluations decision confidence] required_keys.each do |key| raise InvalidRuleDslError, "Audit payload missing required key: #{key}" unless payload.key?(key) || payload.key?(key.to_sym) end end |
.validate_strict_match!(original_decision:, original_confidence:, replayed_decision:, replayed_confidence:) ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/decision_agent/replay/replay.rb', line 105 def self.validate_strict_match!(original_decision:, original_confidence:, replayed_decision:, replayed_confidence:) differences = [] if original_decision.to_s != replayed_decision.to_s differences << "decision mismatch (expected: #{original_decision}, got: #{replayed_decision})" end conf_diff = (original_confidence.to_f - replayed_confidence.to_f).abs differences << "confidence mismatch (expected: #{original_confidence}, got: #{replayed_confidence})" if conf_diff > 0.0001 return unless differences.any? raise ReplayMismatchError.new( expected: { decision: original_decision, confidence: original_confidence }, actual: { decision: replayed_decision, confidence: replayed_confidence }, differences: differences ) end |