decision_agent 0.1.1 → 0.1.3
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.
- checksums.yaml +4 -4
- data/README.md +234 -919
- data/bin/decision_agent +5 -5
- data/lib/decision_agent/agent.rb +19 -26
- data/lib/decision_agent/audit/null_adapter.rb +1 -2
- data/lib/decision_agent/decision.rb +3 -1
- data/lib/decision_agent/dsl/condition_evaluator.rb +4 -3
- data/lib/decision_agent/dsl/rule_parser.rb +4 -6
- data/lib/decision_agent/dsl/schema_validator.rb +27 -31
- data/lib/decision_agent/errors.rb +21 -6
- data/lib/decision_agent/evaluation.rb +3 -1
- data/lib/decision_agent/evaluation_validator.rb +78 -0
- data/lib/decision_agent/evaluators/json_rule_evaluator.rb +26 -0
- data/lib/decision_agent/evaluators/static_evaluator.rb +2 -6
- data/lib/decision_agent/monitoring/alert_manager.rb +282 -0
- data/lib/decision_agent/monitoring/dashboard/public/dashboard.css +381 -0
- data/lib/decision_agent/monitoring/dashboard/public/dashboard.js +471 -0
- data/lib/decision_agent/monitoring/dashboard/public/index.html +161 -0
- data/lib/decision_agent/monitoring/dashboard_server.rb +340 -0
- data/lib/decision_agent/monitoring/metrics_collector.rb +278 -0
- data/lib/decision_agent/monitoring/monitored_agent.rb +71 -0
- data/lib/decision_agent/monitoring/prometheus_exporter.rb +247 -0
- data/lib/decision_agent/replay/replay.rb +12 -22
- data/lib/decision_agent/scoring/base.rb +1 -1
- data/lib/decision_agent/scoring/consensus.rb +5 -5
- data/lib/decision_agent/scoring/weighted_average.rb +1 -1
- data/lib/decision_agent/version.rb +1 -1
- data/lib/decision_agent/versioning/activerecord_adapter.rb +141 -0
- data/lib/decision_agent/versioning/adapter.rb +100 -0
- data/lib/decision_agent/versioning/file_storage_adapter.rb +290 -0
- data/lib/decision_agent/versioning/version_manager.rb +127 -0
- data/lib/decision_agent/web/public/app.js +318 -0
- data/lib/decision_agent/web/public/index.html +56 -1
- data/lib/decision_agent/web/public/styles.css +219 -0
- data/lib/decision_agent/web/server.rb +169 -9
- data/lib/decision_agent.rb +11 -0
- data/lib/generators/decision_agent/install/install_generator.rb +40 -0
- data/lib/generators/decision_agent/install/templates/README +47 -0
- data/lib/generators/decision_agent/install/templates/migration.rb +37 -0
- data/lib/generators/decision_agent/install/templates/rule.rb +30 -0
- data/lib/generators/decision_agent/install/templates/rule_version.rb +66 -0
- data/spec/activerecord_thread_safety_spec.rb +553 -0
- data/spec/agent_spec.rb +13 -13
- data/spec/api_contract_spec.rb +16 -16
- data/spec/audit_adapters_spec.rb +3 -3
- data/spec/comprehensive_edge_cases_spec.rb +86 -86
- data/spec/dsl_validation_spec.rb +83 -83
- data/spec/edge_cases_spec.rb +23 -23
- data/spec/examples/feedback_aware_evaluator_spec.rb +7 -7
- data/spec/examples.txt +548 -0
- data/spec/issue_verification_spec.rb +685 -0
- data/spec/json_rule_evaluator_spec.rb +15 -15
- data/spec/monitoring/alert_manager_spec.rb +378 -0
- data/spec/monitoring/metrics_collector_spec.rb +281 -0
- data/spec/monitoring/monitored_agent_spec.rb +222 -0
- data/spec/monitoring/prometheus_exporter_spec.rb +242 -0
- data/spec/replay_edge_cases_spec.rb +58 -58
- data/spec/replay_spec.rb +11 -11
- data/spec/rfc8785_canonicalization_spec.rb +215 -0
- data/spec/scoring_spec.rb +1 -1
- data/spec/spec_helper.rb +9 -0
- data/spec/thread_safety_spec.rb +482 -0
- data/spec/thread_safety_spec.rb.broken +878 -0
- data/spec/versioning_spec.rb +777 -0
- data/spec/web_ui_rack_spec.rb +135 -0
- metadata +84 -11
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "rack/test"
|
|
3
|
+
require_relative "../lib/decision_agent/web/server"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "DecisionAgent Web UI Rack Integration" do
|
|
6
|
+
include Rack::Test::Methods
|
|
7
|
+
|
|
8
|
+
def app
|
|
9
|
+
DecisionAgent::Web::Server
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe "Rack interface" do
|
|
13
|
+
it "responds to .call for Rack compatibility" do
|
|
14
|
+
expect(DecisionAgent::Web::Server).to respond_to(:call)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "serves the main page" do
|
|
18
|
+
get "/"
|
|
19
|
+
expect(last_response).to be_ok
|
|
20
|
+
expect(last_response.body).to include("DecisionAgent")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "serves the health endpoint" do
|
|
24
|
+
get "/health"
|
|
25
|
+
expect(last_response).to be_ok
|
|
26
|
+
expect(last_response.content_type).to include("application/json")
|
|
27
|
+
|
|
28
|
+
json = JSON.parse(last_response.body)
|
|
29
|
+
expect(json["status"]).to eq("ok")
|
|
30
|
+
expect(json["version"]).to eq(DecisionAgent::VERSION)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "validates rules via POST /api/validate" do
|
|
34
|
+
valid_rules = {
|
|
35
|
+
version: "1.0",
|
|
36
|
+
ruleset: "test_rules",
|
|
37
|
+
rules: [{
|
|
38
|
+
id: "test_rule",
|
|
39
|
+
if: { field: "amount", op: "gt", value: 100 },
|
|
40
|
+
then: { decision: "approve", weight: 0.9, reason: "Test" }
|
|
41
|
+
}]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
post "/api/validate", valid_rules.to_json, { "CONTENT_TYPE" => "application/json" }
|
|
45
|
+
|
|
46
|
+
expect(last_response).to be_ok
|
|
47
|
+
json = JSON.parse(last_response.body)
|
|
48
|
+
expect(json["valid"]).to be true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "returns error for invalid rules" do
|
|
52
|
+
invalid_rules = {
|
|
53
|
+
version: "1.0",
|
|
54
|
+
ruleset: "test_rules",
|
|
55
|
+
rules: [{
|
|
56
|
+
id: "bad_rule"
|
|
57
|
+
# Missing required fields
|
|
58
|
+
}]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
post "/api/validate", invalid_rules.to_json, { "CONTENT_TYPE" => "application/json" }
|
|
62
|
+
|
|
63
|
+
expect(last_response.status).to eq(422)
|
|
64
|
+
json = JSON.parse(last_response.body)
|
|
65
|
+
expect(json["valid"]).to be false
|
|
66
|
+
expect(json["errors"]).to be_an(Array)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "evaluates rules via POST /api/evaluate" do
|
|
70
|
+
rules = {
|
|
71
|
+
version: "1.0",
|
|
72
|
+
ruleset: "test_rules",
|
|
73
|
+
rules: [{
|
|
74
|
+
id: "high_value",
|
|
75
|
+
if: { field: "amount", op: "gt", value: 1000 },
|
|
76
|
+
then: { decision: "approve", weight: 0.9, reason: "High value" }
|
|
77
|
+
}]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
payload = {
|
|
81
|
+
rules: rules,
|
|
82
|
+
context: { amount: 1500 }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
post "/api/evaluate", payload.to_json, { "CONTENT_TYPE" => "application/json" }
|
|
86
|
+
|
|
87
|
+
expect(last_response).to be_ok
|
|
88
|
+
json = JSON.parse(last_response.body)
|
|
89
|
+
expect(json["success"]).to be true
|
|
90
|
+
expect(json["decision"]).to eq("approve")
|
|
91
|
+
expect(json["weight"]).to eq(0.9)
|
|
92
|
+
expect(json["reason"]).to eq("High value")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "serves example rules" do
|
|
96
|
+
get "/api/examples"
|
|
97
|
+
|
|
98
|
+
expect(last_response).to be_ok
|
|
99
|
+
json = JSON.parse(last_response.body)
|
|
100
|
+
expect(json).to be_an(Array)
|
|
101
|
+
expect(json.length).to be > 0
|
|
102
|
+
expect(json.first).to have_key("name")
|
|
103
|
+
expect(json.first).to have_key("rules")
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "handles CORS preflight requests" do
|
|
107
|
+
options "/api/validate"
|
|
108
|
+
|
|
109
|
+
expect(last_response.status).to eq(200)
|
|
110
|
+
expect(last_response.headers["Access-Control-Allow-Origin"]).to eq("*")
|
|
111
|
+
expect(last_response.headers["Access-Control-Allow-Methods"]).to include("POST")
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe "Rails mounting compatibility" do
|
|
116
|
+
it "can be mounted in a Rack app" do
|
|
117
|
+
# Simulate a Rails-style mount
|
|
118
|
+
rack_app = Rack::Builder.new do
|
|
119
|
+
map "/decision_agent" do
|
|
120
|
+
run DecisionAgent::Web::Server
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Create a test session for the mounted app
|
|
125
|
+
test_session = Rack::Test::Session.new(Rack::MockSession.new(rack_app))
|
|
126
|
+
|
|
127
|
+
# Test that the health endpoint works when mounted
|
|
128
|
+
test_session.get "/decision_agent/health"
|
|
129
|
+
expect(test_session.last_response).to be_ok
|
|
130
|
+
|
|
131
|
+
json = JSON.parse(test_session.last_response.body)
|
|
132
|
+
expect(json["status"]).to eq("ok")
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: decision_agent
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sam Aswin
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-12-
|
|
11
|
+
date: 2025-12-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: json-canonicalization
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.0'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: sinatra
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -25,19 +39,19 @@ dependencies:
|
|
|
25
39
|
- !ruby/object:Gem::Version
|
|
26
40
|
version: '3.0'
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
42
|
+
name: rack-test
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
30
44
|
requirements:
|
|
31
45
|
- - "~>"
|
|
32
46
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
47
|
+
version: '2.0'
|
|
34
48
|
type: :development
|
|
35
49
|
prerelease: false
|
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
51
|
requirements:
|
|
38
52
|
- - "~>"
|
|
39
53
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
54
|
+
version: '2.0'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: rake
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,11 +66,39 @@ dependencies:
|
|
|
52
66
|
- - "~>"
|
|
53
67
|
- !ruby/object:Gem::Version
|
|
54
68
|
version: '13.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.12'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.12'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rubocop
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.60'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.60'
|
|
55
97
|
description: A production-grade decision agent that provides deterministic rule evaluation,
|
|
56
98
|
conflict resolution, and full audit replay capabilities. Framework-agnostic and
|
|
57
99
|
AI-optional.
|
|
58
100
|
email:
|
|
59
|
-
-
|
|
101
|
+
- samaswin@gmail.com
|
|
60
102
|
executables:
|
|
61
103
|
- decision_agent
|
|
62
104
|
extensions: []
|
|
@@ -77,9 +119,18 @@ files:
|
|
|
77
119
|
- lib/decision_agent/dsl/schema_validator.rb
|
|
78
120
|
- lib/decision_agent/errors.rb
|
|
79
121
|
- lib/decision_agent/evaluation.rb
|
|
122
|
+
- lib/decision_agent/evaluation_validator.rb
|
|
80
123
|
- lib/decision_agent/evaluators/base.rb
|
|
81
124
|
- lib/decision_agent/evaluators/json_rule_evaluator.rb
|
|
82
125
|
- lib/decision_agent/evaluators/static_evaluator.rb
|
|
126
|
+
- lib/decision_agent/monitoring/alert_manager.rb
|
|
127
|
+
- lib/decision_agent/monitoring/dashboard/public/dashboard.css
|
|
128
|
+
- lib/decision_agent/monitoring/dashboard/public/dashboard.js
|
|
129
|
+
- lib/decision_agent/monitoring/dashboard/public/index.html
|
|
130
|
+
- lib/decision_agent/monitoring/dashboard_server.rb
|
|
131
|
+
- lib/decision_agent/monitoring/metrics_collector.rb
|
|
132
|
+
- lib/decision_agent/monitoring/monitored_agent.rb
|
|
133
|
+
- lib/decision_agent/monitoring/prometheus_exporter.rb
|
|
83
134
|
- lib/decision_agent/replay/replay.rb
|
|
84
135
|
- lib/decision_agent/scoring/base.rb
|
|
85
136
|
- lib/decision_agent/scoring/consensus.rb
|
|
@@ -87,10 +138,20 @@ files:
|
|
|
87
138
|
- lib/decision_agent/scoring/threshold.rb
|
|
88
139
|
- lib/decision_agent/scoring/weighted_average.rb
|
|
89
140
|
- lib/decision_agent/version.rb
|
|
141
|
+
- lib/decision_agent/versioning/activerecord_adapter.rb
|
|
142
|
+
- lib/decision_agent/versioning/adapter.rb
|
|
143
|
+
- lib/decision_agent/versioning/file_storage_adapter.rb
|
|
144
|
+
- lib/decision_agent/versioning/version_manager.rb
|
|
90
145
|
- lib/decision_agent/web/public/app.js
|
|
91
146
|
- lib/decision_agent/web/public/index.html
|
|
92
147
|
- lib/decision_agent/web/public/styles.css
|
|
93
148
|
- lib/decision_agent/web/server.rb
|
|
149
|
+
- lib/generators/decision_agent/install/install_generator.rb
|
|
150
|
+
- lib/generators/decision_agent/install/templates/README
|
|
151
|
+
- lib/generators/decision_agent/install/templates/migration.rb
|
|
152
|
+
- lib/generators/decision_agent/install/templates/rule.rb
|
|
153
|
+
- lib/generators/decision_agent/install/templates/rule_version.rb
|
|
154
|
+
- spec/activerecord_thread_safety_spec.rb
|
|
94
155
|
- spec/agent_spec.rb
|
|
95
156
|
- spec/api_contract_spec.rb
|
|
96
157
|
- spec/audit_adapters_spec.rb
|
|
@@ -98,12 +159,23 @@ files:
|
|
|
98
159
|
- spec/context_spec.rb
|
|
99
160
|
- spec/dsl_validation_spec.rb
|
|
100
161
|
- spec/edge_cases_spec.rb
|
|
162
|
+
- spec/examples.txt
|
|
101
163
|
- spec/examples/feedback_aware_evaluator_spec.rb
|
|
164
|
+
- spec/issue_verification_spec.rb
|
|
102
165
|
- spec/json_rule_evaluator_spec.rb
|
|
166
|
+
- spec/monitoring/alert_manager_spec.rb
|
|
167
|
+
- spec/monitoring/metrics_collector_spec.rb
|
|
168
|
+
- spec/monitoring/monitored_agent_spec.rb
|
|
169
|
+
- spec/monitoring/prometheus_exporter_spec.rb
|
|
103
170
|
- spec/replay_edge_cases_spec.rb
|
|
104
171
|
- spec/replay_spec.rb
|
|
172
|
+
- spec/rfc8785_canonicalization_spec.rb
|
|
105
173
|
- spec/scoring_spec.rb
|
|
106
174
|
- spec/spec_helper.rb
|
|
175
|
+
- spec/thread_safety_spec.rb
|
|
176
|
+
- spec/thread_safety_spec.rb.broken
|
|
177
|
+
- spec/versioning_spec.rb
|
|
178
|
+
- spec/web_ui_rack_spec.rb
|
|
107
179
|
homepage: https://github.com/samaswin87/decision_agent
|
|
108
180
|
licenses:
|
|
109
181
|
- MIT
|
|
@@ -111,7 +183,8 @@ metadata:
|
|
|
111
183
|
homepage_uri: https://github.com/samaswin87/decision_agent
|
|
112
184
|
source_code_uri: https://github.com/samaswin87/decision_agent
|
|
113
185
|
changelog_uri: https://github.com/samaswin87/decision_agent/blob/main/CHANGELOG.md
|
|
114
|
-
|
|
186
|
+
rubygems_mfa_required: 'true'
|
|
187
|
+
post_install_message:
|
|
115
188
|
rdoc_options: []
|
|
116
189
|
require_paths:
|
|
117
190
|
- lib
|
|
@@ -119,15 +192,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
119
192
|
requirements:
|
|
120
193
|
- - ">="
|
|
121
194
|
- !ruby/object:Gem::Version
|
|
122
|
-
version:
|
|
195
|
+
version: 3.0.0
|
|
123
196
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
197
|
requirements:
|
|
125
198
|
- - ">="
|
|
126
199
|
- !ruby/object:Gem::Version
|
|
127
200
|
version: '0'
|
|
128
201
|
requirements: []
|
|
129
|
-
rubygems_version: 3.
|
|
130
|
-
signing_key:
|
|
202
|
+
rubygems_version: 3.5.22
|
|
203
|
+
signing_key:
|
|
131
204
|
specification_version: 4
|
|
132
205
|
summary: Deterministic, explainable, auditable decision engine for Ruby
|
|
133
206
|
test_files: []
|