decision_agent 0.2.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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +313 -8
  3. data/bin/decision_agent +104 -0
  4. data/lib/decision_agent/agent.rb +72 -1
  5. data/lib/decision_agent/context.rb +1 -0
  6. data/lib/decision_agent/data_enrichment/cache/memory_adapter.rb +86 -0
  7. data/lib/decision_agent/data_enrichment/cache_adapter.rb +49 -0
  8. data/lib/decision_agent/data_enrichment/circuit_breaker.rb +135 -0
  9. data/lib/decision_agent/data_enrichment/client.rb +220 -0
  10. data/lib/decision_agent/data_enrichment/config.rb +78 -0
  11. data/lib/decision_agent/data_enrichment/errors.rb +36 -0
  12. data/lib/decision_agent/decision.rb +102 -2
  13. data/lib/decision_agent/dmn/adapter.rb +135 -0
  14. data/lib/decision_agent/dmn/cache.rb +306 -0
  15. data/lib/decision_agent/dmn/decision_graph.rb +327 -0
  16. data/lib/decision_agent/dmn/decision_tree.rb +192 -0
  17. data/lib/decision_agent/dmn/errors.rb +30 -0
  18. data/lib/decision_agent/dmn/exporter.rb +217 -0
  19. data/lib/decision_agent/dmn/feel/evaluator.rb +819 -0
  20. data/lib/decision_agent/dmn/feel/functions.rb +420 -0
  21. data/lib/decision_agent/dmn/feel/parser.rb +349 -0
  22. data/lib/decision_agent/dmn/feel/simple_parser.rb +276 -0
  23. data/lib/decision_agent/dmn/feel/transformer.rb +372 -0
  24. data/lib/decision_agent/dmn/feel/types.rb +276 -0
  25. data/lib/decision_agent/dmn/importer.rb +77 -0
  26. data/lib/decision_agent/dmn/model.rb +197 -0
  27. data/lib/decision_agent/dmn/parser.rb +191 -0
  28. data/lib/decision_agent/dmn/testing.rb +333 -0
  29. data/lib/decision_agent/dmn/validator.rb +315 -0
  30. data/lib/decision_agent/dmn/versioning.rb +229 -0
  31. data/lib/decision_agent/dmn/visualizer.rb +513 -0
  32. data/lib/decision_agent/dsl/condition_evaluator.rb +984 -838
  33. data/lib/decision_agent/dsl/schema_validator.rb +53 -14
  34. data/lib/decision_agent/evaluators/dmn_evaluator.rb +308 -0
  35. data/lib/decision_agent/evaluators/json_rule_evaluator.rb +69 -9
  36. data/lib/decision_agent/explainability/condition_trace.rb +83 -0
  37. data/lib/decision_agent/explainability/explainability_result.rb +52 -0
  38. data/lib/decision_agent/explainability/rule_trace.rb +39 -0
  39. data/lib/decision_agent/explainability/trace_collector.rb +24 -0
  40. data/lib/decision_agent/monitoring/alert_manager.rb +5 -1
  41. data/lib/decision_agent/simulation/errors.rb +18 -0
  42. data/lib/decision_agent/simulation/impact_analyzer.rb +498 -0
  43. data/lib/decision_agent/simulation/monte_carlo_simulator.rb +635 -0
  44. data/lib/decision_agent/simulation/replay_engine.rb +486 -0
  45. data/lib/decision_agent/simulation/scenario_engine.rb +318 -0
  46. data/lib/decision_agent/simulation/scenario_library.rb +163 -0
  47. data/lib/decision_agent/simulation/shadow_test_engine.rb +287 -0
  48. data/lib/decision_agent/simulation/what_if_analyzer.rb +1002 -0
  49. data/lib/decision_agent/simulation.rb +17 -0
  50. data/lib/decision_agent/version.rb +1 -1
  51. data/lib/decision_agent/versioning/activerecord_adapter.rb +23 -8
  52. data/lib/decision_agent/web/dmn_editor.rb +426 -0
  53. data/lib/decision_agent/web/public/app.js +119 -0
  54. data/lib/decision_agent/web/public/dmn-editor.css +596 -0
  55. data/lib/decision_agent/web/public/dmn-editor.html +250 -0
  56. data/lib/decision_agent/web/public/dmn-editor.js +553 -0
  57. data/lib/decision_agent/web/public/index.html +52 -0
  58. data/lib/decision_agent/web/public/simulation.html +130 -0
  59. data/lib/decision_agent/web/public/simulation_impact.html +478 -0
  60. data/lib/decision_agent/web/public/simulation_replay.html +551 -0
  61. data/lib/decision_agent/web/public/simulation_shadow.html +546 -0
  62. data/lib/decision_agent/web/public/simulation_whatif.html +532 -0
  63. data/lib/decision_agent/web/public/styles.css +86 -0
  64. data/lib/decision_agent/web/server.rb +1059 -23
  65. data/lib/decision_agent.rb +60 -2
  66. metadata +105 -61
  67. data/spec/ab_testing/ab_test_assignment_spec.rb +0 -253
  68. data/spec/ab_testing/ab_test_manager_spec.rb +0 -612
  69. data/spec/ab_testing/ab_test_spec.rb +0 -270
  70. data/spec/ab_testing/ab_testing_agent_spec.rb +0 -481
  71. data/spec/ab_testing/storage/adapter_spec.rb +0 -64
  72. data/spec/ab_testing/storage/memory_adapter_spec.rb +0 -485
  73. data/spec/activerecord_thread_safety_spec.rb +0 -553
  74. data/spec/advanced_operators_spec.rb +0 -3150
  75. data/spec/agent_spec.rb +0 -289
  76. data/spec/api_contract_spec.rb +0 -430
  77. data/spec/audit_adapters_spec.rb +0 -92
  78. data/spec/auth/access_audit_logger_spec.rb +0 -394
  79. data/spec/auth/authenticator_spec.rb +0 -112
  80. data/spec/auth/password_reset_spec.rb +0 -294
  81. data/spec/auth/permission_checker_spec.rb +0 -207
  82. data/spec/auth/permission_spec.rb +0 -73
  83. data/spec/auth/rbac_adapter_spec.rb +0 -550
  84. data/spec/auth/rbac_config_spec.rb +0 -82
  85. data/spec/auth/role_spec.rb +0 -51
  86. data/spec/auth/session_manager_spec.rb +0 -172
  87. data/spec/auth/session_spec.rb +0 -112
  88. data/spec/auth/user_spec.rb +0 -130
  89. data/spec/comprehensive_edge_cases_spec.rb +0 -1777
  90. data/spec/context_spec.rb +0 -127
  91. data/spec/decision_agent_spec.rb +0 -96
  92. data/spec/decision_spec.rb +0 -423
  93. data/spec/dsl/condition_evaluator_spec.rb +0 -774
  94. data/spec/dsl_validation_spec.rb +0 -648
  95. data/spec/edge_cases_spec.rb +0 -353
  96. data/spec/evaluation_spec.rb +0 -364
  97. data/spec/evaluation_validator_spec.rb +0 -165
  98. data/spec/examples/feedback_aware_evaluator_spec.rb +0 -460
  99. data/spec/examples.txt +0 -1633
  100. data/spec/issue_verification_spec.rb +0 -759
  101. data/spec/json_rule_evaluator_spec.rb +0 -587
  102. data/spec/monitoring/alert_manager_spec.rb +0 -378
  103. data/spec/monitoring/metrics_collector_spec.rb +0 -499
  104. data/spec/monitoring/monitored_agent_spec.rb +0 -222
  105. data/spec/monitoring/prometheus_exporter_spec.rb +0 -242
  106. data/spec/monitoring/storage/activerecord_adapter_spec.rb +0 -498
  107. data/spec/monitoring/storage/base_adapter_spec.rb +0 -61
  108. data/spec/monitoring/storage/memory_adapter_spec.rb +0 -247
  109. data/spec/performance_optimizations_spec.rb +0 -486
  110. data/spec/replay_edge_cases_spec.rb +0 -699
  111. data/spec/replay_spec.rb +0 -210
  112. data/spec/rfc8785_canonicalization_spec.rb +0 -215
  113. data/spec/scoring_spec.rb +0 -225
  114. data/spec/spec_helper.rb +0 -60
  115. data/spec/testing/batch_test_importer_spec.rb +0 -693
  116. data/spec/testing/batch_test_runner_spec.rb +0 -307
  117. data/spec/testing/test_coverage_analyzer_spec.rb +0 -292
  118. data/spec/testing/test_result_comparator_spec.rb +0 -392
  119. data/spec/testing/test_scenario_spec.rb +0 -113
  120. data/spec/thread_safety_spec.rb +0 -482
  121. data/spec/thread_safety_spec.rb.broken +0 -878
  122. data/spec/versioning/adapter_spec.rb +0 -156
  123. data/spec/versioning_spec.rb +0 -1030
  124. data/spec/web/middleware/auth_middleware_spec.rb +0 -133
  125. data/spec/web/middleware/permission_middleware_spec.rb +0 -247
  126. data/spec/web_ui_rack_spec.rb +0 -1840
@@ -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