decision_agent 0.3.0 → 1.0.1

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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +272 -7
  3. data/lib/decision_agent/agent.rb +72 -1
  4. data/lib/decision_agent/context.rb +1 -0
  5. data/lib/decision_agent/data_enrichment/cache/memory_adapter.rb +86 -0
  6. data/lib/decision_agent/data_enrichment/cache_adapter.rb +49 -0
  7. data/lib/decision_agent/data_enrichment/circuit_breaker.rb +135 -0
  8. data/lib/decision_agent/data_enrichment/client.rb +220 -0
  9. data/lib/decision_agent/data_enrichment/config.rb +78 -0
  10. data/lib/decision_agent/data_enrichment/errors.rb +36 -0
  11. data/lib/decision_agent/decision.rb +102 -2
  12. data/lib/decision_agent/dmn/feel/evaluator.rb +28 -6
  13. data/lib/decision_agent/dsl/condition_evaluator.rb +982 -839
  14. data/lib/decision_agent/dsl/schema_validator.rb +51 -13
  15. data/lib/decision_agent/evaluators/dmn_evaluator.rb +106 -19
  16. data/lib/decision_agent/evaluators/json_rule_evaluator.rb +69 -9
  17. data/lib/decision_agent/explainability/condition_trace.rb +83 -0
  18. data/lib/decision_agent/explainability/explainability_result.rb +52 -0
  19. data/lib/decision_agent/explainability/rule_trace.rb +39 -0
  20. data/lib/decision_agent/explainability/trace_collector.rb +24 -0
  21. data/lib/decision_agent/monitoring/alert_manager.rb +5 -1
  22. data/lib/decision_agent/simulation/errors.rb +18 -0
  23. data/lib/decision_agent/simulation/impact_analyzer.rb +498 -0
  24. data/lib/decision_agent/simulation/monte_carlo_simulator.rb +635 -0
  25. data/lib/decision_agent/simulation/replay_engine.rb +486 -0
  26. data/lib/decision_agent/simulation/scenario_engine.rb +318 -0
  27. data/lib/decision_agent/simulation/scenario_library.rb +163 -0
  28. data/lib/decision_agent/simulation/shadow_test_engine.rb +287 -0
  29. data/lib/decision_agent/simulation/what_if_analyzer.rb +1002 -0
  30. data/lib/decision_agent/simulation.rb +17 -0
  31. data/lib/decision_agent/version.rb +1 -1
  32. data/lib/decision_agent/versioning/activerecord_adapter.rb +23 -8
  33. data/lib/decision_agent/web/public/app.js +119 -0
  34. data/lib/decision_agent/web/public/index.html +49 -0
  35. data/lib/decision_agent/web/public/simulation.html +130 -0
  36. data/lib/decision_agent/web/public/simulation_impact.html +478 -0
  37. data/lib/decision_agent/web/public/simulation_replay.html +551 -0
  38. data/lib/decision_agent/web/public/simulation_shadow.html +546 -0
  39. data/lib/decision_agent/web/public/simulation_whatif.html +532 -0
  40. data/lib/decision_agent/web/public/styles.css +65 -0
  41. data/lib/decision_agent/web/server.rb +594 -23
  42. data/lib/decision_agent.rb +60 -2
  43. metadata +53 -73
  44. data/spec/ab_testing/ab_test_assignment_spec.rb +0 -253
  45. data/spec/ab_testing/ab_test_manager_spec.rb +0 -612
  46. data/spec/ab_testing/ab_test_spec.rb +0 -270
  47. data/spec/ab_testing/ab_testing_agent_spec.rb +0 -655
  48. data/spec/ab_testing/storage/adapter_spec.rb +0 -64
  49. data/spec/ab_testing/storage/memory_adapter_spec.rb +0 -485
  50. data/spec/activerecord_thread_safety_spec.rb +0 -553
  51. data/spec/advanced_operators_spec.rb +0 -3150
  52. data/spec/agent_spec.rb +0 -289
  53. data/spec/api_contract_spec.rb +0 -430
  54. data/spec/audit_adapters_spec.rb +0 -92
  55. data/spec/auth/access_audit_logger_spec.rb +0 -394
  56. data/spec/auth/authenticator_spec.rb +0 -112
  57. data/spec/auth/password_reset_spec.rb +0 -294
  58. data/spec/auth/permission_checker_spec.rb +0 -207
  59. data/spec/auth/permission_spec.rb +0 -73
  60. data/spec/auth/rbac_adapter_spec.rb +0 -778
  61. data/spec/auth/rbac_config_spec.rb +0 -82
  62. data/spec/auth/role_spec.rb +0 -51
  63. data/spec/auth/session_manager_spec.rb +0 -172
  64. data/spec/auth/session_spec.rb +0 -112
  65. data/spec/auth/user_spec.rb +0 -130
  66. data/spec/comprehensive_edge_cases_spec.rb +0 -1777
  67. data/spec/context_spec.rb +0 -127
  68. data/spec/decision_agent_spec.rb +0 -96
  69. data/spec/decision_spec.rb +0 -423
  70. data/spec/dmn/decision_graph_spec.rb +0 -282
  71. data/spec/dmn/decision_tree_spec.rb +0 -203
  72. data/spec/dmn/feel/errors_spec.rb +0 -18
  73. data/spec/dmn/feel/functions_spec.rb +0 -400
  74. data/spec/dmn/feel/simple_parser_spec.rb +0 -274
  75. data/spec/dmn/feel/types_spec.rb +0 -176
  76. data/spec/dmn/feel_parser_spec.rb +0 -489
  77. data/spec/dmn/hit_policy_spec.rb +0 -202
  78. data/spec/dmn/integration_spec.rb +0 -226
  79. data/spec/dsl/condition_evaluator_spec.rb +0 -774
  80. data/spec/dsl_validation_spec.rb +0 -648
  81. data/spec/edge_cases_spec.rb +0 -353
  82. data/spec/evaluation_spec.rb +0 -364
  83. data/spec/evaluation_validator_spec.rb +0 -165
  84. data/spec/examples/feedback_aware_evaluator_spec.rb +0 -460
  85. data/spec/examples.txt +0 -1909
  86. data/spec/fixtures/dmn/complex_decision.dmn +0 -81
  87. data/spec/fixtures/dmn/invalid_structure.dmn +0 -31
  88. data/spec/fixtures/dmn/simple_decision.dmn +0 -40
  89. data/spec/issue_verification_spec.rb +0 -759
  90. data/spec/json_rule_evaluator_spec.rb +0 -587
  91. data/spec/monitoring/alert_manager_spec.rb +0 -378
  92. data/spec/monitoring/metrics_collector_spec.rb +0 -501
  93. data/spec/monitoring/monitored_agent_spec.rb +0 -225
  94. data/spec/monitoring/prometheus_exporter_spec.rb +0 -242
  95. data/spec/monitoring/storage/activerecord_adapter_spec.rb +0 -498
  96. data/spec/monitoring/storage/base_adapter_spec.rb +0 -61
  97. data/spec/monitoring/storage/memory_adapter_spec.rb +0 -247
  98. data/spec/performance_optimizations_spec.rb +0 -493
  99. data/spec/replay_edge_cases_spec.rb +0 -699
  100. data/spec/replay_spec.rb +0 -210
  101. data/spec/rfc8785_canonicalization_spec.rb +0 -215
  102. data/spec/scoring_spec.rb +0 -225
  103. data/spec/spec_helper.rb +0 -60
  104. data/spec/testing/batch_test_importer_spec.rb +0 -693
  105. data/spec/testing/batch_test_runner_spec.rb +0 -307
  106. data/spec/testing/test_coverage_analyzer_spec.rb +0 -292
  107. data/spec/testing/test_result_comparator_spec.rb +0 -392
  108. data/spec/testing/test_scenario_spec.rb +0 -113
  109. data/spec/thread_safety_spec.rb +0 -490
  110. data/spec/thread_safety_spec.rb.broken +0 -878
  111. data/spec/versioning/adapter_spec.rb +0 -156
  112. data/spec/versioning_spec.rb +0 -1030
  113. data/spec/web/middleware/auth_middleware_spec.rb +0 -133
  114. data/spec/web/middleware/permission_middleware_spec.rb +0 -247
  115. data/spec/web_ui_rack_spec.rb +0 -2134
@@ -1,247 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "decision_agent/monitoring/storage/memory_adapter"
5
-
6
- RSpec.describe DecisionAgent::Monitoring::Storage::MemoryAdapter do
7
- let(:adapter) { described_class.new(window_size: 3600) }
8
-
9
- describe ".available?" do
10
- it "is always available" do
11
- expect(described_class.available?).to be true
12
- end
13
- end
14
-
15
- describe "#record_decision" do
16
- it "stores decision in memory" do
17
- expect do
18
- adapter.record_decision(
19
- "approve_loan",
20
- { user_id: 123, amount: 10_000 },
21
- confidence: 0.85,
22
- evaluations_count: 3,
23
- duration_ms: 45.5,
24
- status: "success"
25
- )
26
- end.to change { adapter.metrics_count[:decisions] }.by(1)
27
- end
28
- end
29
-
30
- describe "#record_evaluation" do
31
- it "stores evaluation in memory" do
32
- expect do
33
- adapter.record_evaluation(
34
- "CreditScoreEvaluator",
35
- score: 0.92,
36
- success: true,
37
- duration_ms: 12.3,
38
- details: { credit_score: 750 }
39
- )
40
- end.to change { adapter.metrics_count[:evaluations] }.by(1)
41
- end
42
- end
43
-
44
- describe "#record_performance" do
45
- it "stores performance metric in memory" do
46
- expect do
47
- adapter.record_performance(
48
- "database_query",
49
- duration_ms: 150.5,
50
- status: "success",
51
- metadata: { query: "SELECT * FROM users" }
52
- )
53
- end.to change { adapter.metrics_count[:performance] }.by(1)
54
- end
55
- end
56
-
57
- describe "#record_error" do
58
- it "stores error in memory" do
59
- expect do
60
- adapter.record_error(
61
- "ArgumentError",
62
- message: "Invalid input",
63
- stack_trace: ["line 1", "line 2"],
64
- severity: "medium",
65
- context: { input: "bad_value" }
66
- )
67
- end.to change { adapter.metrics_count[:errors] }.by(1)
68
- end
69
- end
70
-
71
- describe "#statistics" do
72
- before do
73
- # Create test data
74
- 5.times do |i|
75
- adapter.record_decision(
76
- "decision_#{i}",
77
- { index: i },
78
- confidence: 0.5 + (i * 0.05),
79
- evaluations_count: 2,
80
- duration_ms: 100,
81
- status: i.even? ? "success" : "failure"
82
- )
83
- end
84
-
85
- 3.times do |i|
86
- adapter.record_evaluation(
87
- "Evaluator#{i}",
88
- score: 0.8 + (i * 0.05),
89
- success: true
90
- )
91
- end
92
-
93
- 6.times do |i|
94
- adapter.record_performance(
95
- "operation",
96
- duration_ms: 100 + (i * 20),
97
- status: "success"
98
- )
99
- end
100
-
101
- 2.times do
102
- adapter.record_error("RuntimeError", severity: "critical")
103
- end
104
- end
105
-
106
- it "returns comprehensive statistics" do
107
- stats = adapter.statistics(time_range: 3600)
108
-
109
- expect(stats[:decisions][:total]).to eq(5)
110
- expect(stats[:decisions][:average_confidence]).to be_within(0.01).of(0.6)
111
- expect(stats[:decisions][:success_rate]).to eq(0.6) # 3 out of 5
112
-
113
- expect(stats[:evaluations][:total]).to eq(3)
114
- expect(stats[:evaluations][:average_score]).to be_within(0.01).of(0.85)
115
-
116
- expect(stats[:performance][:total]).to eq(6)
117
- expect(stats[:performance][:average_duration_ms]).to eq(150.0)
118
- expect(stats[:performance][:success_rate]).to eq(1.0)
119
-
120
- expect(stats[:errors][:total]).to eq(2)
121
- expect(stats[:errors][:critical_count]).to eq(2)
122
- end
123
-
124
- it "filters by time range" do
125
- # Record an old metric that should be filtered out
126
- adapter.instance_variable_get(:@metrics)[:decisions] << {
127
- decision: "old_decision",
128
- confidence: 0.5,
129
- timestamp: Time.now - 7200 # 2 hours ago
130
- }
131
-
132
- stats = adapter.statistics(time_range: 3600) # Last hour only
133
-
134
- expect(stats[:decisions][:total]).to eq(5) # Doesn't include the old one
135
- end
136
- end
137
-
138
- describe "#time_series" do
139
- before do
140
- # Create metrics at different times
141
- now = Time.now
142
- adapter.instance_variable_get(:@metrics)[:decisions] << { timestamp: now - 120 }
143
- adapter.instance_variable_get(:@metrics)[:decisions] << { timestamp: now - 70 }
144
- adapter.instance_variable_get(:@metrics)[:decisions] << { timestamp: now - 10 }
145
- end
146
-
147
- it "groups metrics into time buckets" do
148
- series = adapter.time_series(:decisions, bucket_size: 60, time_range: 200)
149
-
150
- expect(series[:timestamps]).to be_an(Array)
151
- expect(series[:data]).to be_an(Array)
152
- expect(series[:data].sum).to eq(3) # All 3 metrics
153
- end
154
-
155
- it "uses correct bucket size" do
156
- series = adapter.time_series(:decisions, bucket_size: 60, time_range: 200)
157
-
158
- # Metrics should be grouped into 60-second buckets
159
- expect(series[:data].max).to be <= 2 # No bucket should have more than 2
160
- end
161
- end
162
-
163
- describe "#metrics_count" do
164
- before do
165
- adapter.record_decision("test", {}, confidence: 0.8)
166
- adapter.record_decision("test2", {}, confidence: 0.9)
167
- adapter.record_evaluation("eval1", score: 0.85)
168
- adapter.record_performance("perf1", duration_ms: 100)
169
- adapter.record_error("Error1")
170
- end
171
-
172
- it "returns count for each metric type" do
173
- counts = adapter.metrics_count
174
-
175
- expect(counts[:decisions]).to eq(2)
176
- expect(counts[:evaluations]).to eq(1)
177
- expect(counts[:performance]).to eq(1)
178
- expect(counts[:errors]).to eq(1)
179
- end
180
- end
181
-
182
- describe "#cleanup" do
183
- let(:long_window_adapter) { described_class.new(window_size: 30 * 24 * 3_600) } # 30 day window
184
-
185
- before do
186
- now = Time.now
187
-
188
- # Add old metrics (8 days ago) to adapter with long window
189
- long_window_adapter.instance_variable_get(:@metrics)[:decisions] << {
190
- decision: "old",
191
- timestamp: now - (8 * 24 * 3600)
192
- }
193
- long_window_adapter.instance_variable_get(:@metrics)[:evaluations] << {
194
- evaluator_name: "old",
195
- timestamp: now - (8 * 24 * 3600)
196
- }
197
-
198
- # Add recent metrics
199
- long_window_adapter.record_decision("recent", {}, confidence: 0.8)
200
- long_window_adapter.record_evaluation("recent", score: 0.9)
201
- end
202
-
203
- it "removes old metrics and returns count" do
204
- count = long_window_adapter.cleanup(older_than: 7 * 24 * 3600) # 7 days
205
-
206
- expect(count).to eq(2) # 2 old metrics removed
207
- expect(long_window_adapter.metrics_count[:decisions]).to eq(1) # Only recent one
208
- expect(long_window_adapter.metrics_count[:evaluations]).to eq(1)
209
- end
210
- end
211
-
212
- describe "window-based cleanup" do
213
- let(:short_window_adapter) { described_class.new(window_size: 60) } # 1 minute window
214
-
215
- it "automatically removes metrics older than window_size" do
216
- now = Time.now
217
-
218
- # Add old metric
219
- short_window_adapter.instance_variable_get(:@metrics)[:decisions] << {
220
- decision: "old",
221
- timestamp: now - 120 # 2 minutes ago
222
- }
223
-
224
- # Add new metric (this should trigger cleanup)
225
- short_window_adapter.record_decision("new", {}, confidence: 0.8)
226
-
227
- # Only the new metric should remain
228
- expect(short_window_adapter.metrics_count[:decisions]).to eq(1)
229
- end
230
- end
231
-
232
- describe "thread safety" do
233
- it "handles concurrent writes" do
234
- threads = 10.times.map do
235
- Thread.new do
236
- 100.times do |i|
237
- adapter.record_decision("concurrent_#{i}", {}, confidence: 0.8)
238
- end
239
- end
240
- end
241
-
242
- threads.each(&:join)
243
-
244
- expect(adapter.metrics_count[:decisions]).to eq(1000)
245
- end
246
- end
247
- end