decision_agent 0.1.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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +1060 -0
  4. data/bin/decision_agent +104 -0
  5. data/lib/decision_agent/agent.rb +147 -0
  6. data/lib/decision_agent/audit/adapter.rb +9 -0
  7. data/lib/decision_agent/audit/logger_adapter.rb +27 -0
  8. data/lib/decision_agent/audit/null_adapter.rb +8 -0
  9. data/lib/decision_agent/context.rb +42 -0
  10. data/lib/decision_agent/decision.rb +51 -0
  11. data/lib/decision_agent/dsl/condition_evaluator.rb +133 -0
  12. data/lib/decision_agent/dsl/rule_parser.rb +36 -0
  13. data/lib/decision_agent/dsl/schema_validator.rb +275 -0
  14. data/lib/decision_agent/errors.rb +62 -0
  15. data/lib/decision_agent/evaluation.rb +52 -0
  16. data/lib/decision_agent/evaluators/base.rb +15 -0
  17. data/lib/decision_agent/evaluators/json_rule_evaluator.rb +51 -0
  18. data/lib/decision_agent/evaluators/static_evaluator.rb +31 -0
  19. data/lib/decision_agent/replay/replay.rb +147 -0
  20. data/lib/decision_agent/scoring/base.rb +19 -0
  21. data/lib/decision_agent/scoring/consensus.rb +40 -0
  22. data/lib/decision_agent/scoring/max_weight.rb +16 -0
  23. data/lib/decision_agent/scoring/threshold.rb +40 -0
  24. data/lib/decision_agent/scoring/weighted_average.rb +26 -0
  25. data/lib/decision_agent/version.rb +3 -0
  26. data/lib/decision_agent/web/public/app.js +580 -0
  27. data/lib/decision_agent/web/public/index.html +190 -0
  28. data/lib/decision_agent/web/public/styles.css +558 -0
  29. data/lib/decision_agent/web/server.rb +255 -0
  30. data/lib/decision_agent.rb +29 -0
  31. data/spec/agent_spec.rb +249 -0
  32. data/spec/api_contract_spec.rb +430 -0
  33. data/spec/audit_adapters_spec.rb +74 -0
  34. data/spec/comprehensive_edge_cases_spec.rb +1777 -0
  35. data/spec/context_spec.rb +84 -0
  36. data/spec/dsl_validation_spec.rb +648 -0
  37. data/spec/edge_cases_spec.rb +353 -0
  38. data/spec/examples/feedback_aware_evaluator_spec.rb +460 -0
  39. data/spec/json_rule_evaluator_spec.rb +587 -0
  40. data/spec/replay_edge_cases_spec.rb +699 -0
  41. data/spec/replay_spec.rb +210 -0
  42. data/spec/scoring_spec.rb +225 -0
  43. data/spec/spec_helper.rb +28 -0
  44. metadata +133 -0
@@ -0,0 +1,558 @@
1
+ /* Reset and Base Styles */
2
+ * {
3
+ margin: 0;
4
+ padding: 0;
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ :root {
9
+ --primary-color: #4f46e5;
10
+ --secondary-color: #6b7280;
11
+ --success-color: #10b981;
12
+ --danger-color: #ef4444;
13
+ --warning-color: #f59e0b;
14
+ --bg-color: #f9fafb;
15
+ --panel-bg: #ffffff;
16
+ --border-color: #e5e7eb;
17
+ --text-color: #1f2937;
18
+ --text-secondary: #6b7280;
19
+ --hover-bg: #f3f4f6;
20
+ --code-bg: #1f2937;
21
+ }
22
+
23
+ body {
24
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
25
+ background-color: var(--bg-color);
26
+ color: var(--text-color);
27
+ line-height: 1.6;
28
+ }
29
+
30
+ /* Container */
31
+ .container {
32
+ max-width: 1600px;
33
+ margin: 0 auto;
34
+ padding: 20px;
35
+ }
36
+
37
+ /* Header */
38
+ header {
39
+ text-align: center;
40
+ margin-bottom: 30px;
41
+ padding: 30px 0;
42
+ background: linear-gradient(135deg, var(--primary-color), #6366f1);
43
+ color: white;
44
+ border-radius: 10px;
45
+ }
46
+
47
+ header h1 {
48
+ font-size: 2.5rem;
49
+ margin-bottom: 10px;
50
+ }
51
+
52
+ .subtitle {
53
+ font-size: 1.1rem;
54
+ opacity: 0.9;
55
+ }
56
+
57
+ /* Main Layout */
58
+ .main-layout {
59
+ display: grid;
60
+ grid-template-columns: 1fr 1fr;
61
+ gap: 20px;
62
+ margin-bottom: 30px;
63
+ }
64
+
65
+ @media (max-width: 1024px) {
66
+ .main-layout {
67
+ grid-template-columns: 1fr;
68
+ }
69
+ }
70
+
71
+ /* Panel */
72
+ .panel {
73
+ background: var(--panel-bg);
74
+ border-radius: 10px;
75
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
76
+ overflow: hidden;
77
+ }
78
+
79
+ .panel-header {
80
+ padding: 20px;
81
+ border-bottom: 2px solid var(--border-color);
82
+ display: flex;
83
+ justify-content: space-between;
84
+ align-items: center;
85
+ background: var(--hover-bg);
86
+ }
87
+
88
+ .panel-header h2 {
89
+ font-size: 1.5rem;
90
+ color: var(--text-color);
91
+ }
92
+
93
+ .header-actions {
94
+ display: flex;
95
+ gap: 10px;
96
+ }
97
+
98
+ /* Builder Panel */
99
+ .builder-panel {
100
+ display: flex;
101
+ flex-direction: column;
102
+ max-height: 80vh;
103
+ overflow-y: auto;
104
+ }
105
+
106
+ /* Section */
107
+ .section {
108
+ padding: 20px;
109
+ border-bottom: 1px solid var(--border-color);
110
+ }
111
+
112
+ .section:last-child {
113
+ border-bottom: none;
114
+ }
115
+
116
+ .section h3 {
117
+ font-size: 1.2rem;
118
+ margin-bottom: 15px;
119
+ color: var(--text-color);
120
+ }
121
+
122
+ .section-header {
123
+ display: flex;
124
+ justify-content: space-between;
125
+ align-items: center;
126
+ margin-bottom: 15px;
127
+ }
128
+
129
+ /* Form Elements */
130
+ .form-group {
131
+ margin-bottom: 15px;
132
+ }
133
+
134
+ .form-group label {
135
+ display: block;
136
+ margin-bottom: 5px;
137
+ font-weight: 500;
138
+ color: var(--text-color);
139
+ }
140
+
141
+ .required {
142
+ color: var(--danger-color);
143
+ }
144
+
145
+ .input, .input-sm {
146
+ width: 100%;
147
+ padding: 10px 12px;
148
+ border: 1px solid var(--border-color);
149
+ border-radius: 6px;
150
+ font-size: 14px;
151
+ transition: border-color 0.2s;
152
+ }
153
+
154
+ .input-sm {
155
+ padding: 6px 10px;
156
+ font-size: 13px;
157
+ }
158
+
159
+ .input:focus, .input-sm:focus {
160
+ outline: none;
161
+ border-color: var(--primary-color);
162
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
163
+ }
164
+
165
+ textarea.input {
166
+ resize: vertical;
167
+ font-family: inherit;
168
+ }
169
+
170
+ .form-row {
171
+ display: grid;
172
+ grid-template-columns: 1fr 1fr;
173
+ gap: 15px;
174
+ margin-bottom: 15px;
175
+ }
176
+
177
+ .form-col {
178
+ display: flex;
179
+ flex-direction: column;
180
+ }
181
+
182
+ .form-col.full-width {
183
+ grid-column: 1 / -1;
184
+ }
185
+
186
+ /* Buttons */
187
+ .btn {
188
+ padding: 10px 20px;
189
+ border: none;
190
+ border-radius: 6px;
191
+ font-size: 14px;
192
+ font-weight: 500;
193
+ cursor: pointer;
194
+ transition: all 0.2s;
195
+ display: inline-flex;
196
+ align-items: center;
197
+ gap: 5px;
198
+ }
199
+
200
+ .btn:hover {
201
+ transform: translateY(-1px);
202
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
203
+ }
204
+
205
+ .btn-primary {
206
+ background: var(--primary-color);
207
+ color: white;
208
+ }
209
+
210
+ .btn-primary:hover {
211
+ background: #4338ca;
212
+ }
213
+
214
+ .btn-secondary {
215
+ background: var(--secondary-color);
216
+ color: white;
217
+ }
218
+
219
+ .btn-secondary:hover {
220
+ background: #4b5563;
221
+ }
222
+
223
+ .btn-success {
224
+ background: var(--success-color);
225
+ color: white;
226
+ }
227
+
228
+ .btn-success:hover {
229
+ background: #059669;
230
+ }
231
+
232
+ .btn-sm {
233
+ padding: 5px 10px;
234
+ font-size: 12px;
235
+ }
236
+
237
+ .btn-remove {
238
+ background: var(--danger-color);
239
+ color: white;
240
+ border: none;
241
+ border-radius: 50%;
242
+ width: 24px;
243
+ height: 24px;
244
+ cursor: pointer;
245
+ font-size: 16px;
246
+ display: flex;
247
+ align-items: center;
248
+ justify-content: center;
249
+ flex-shrink: 0;
250
+ }
251
+
252
+ .btn-remove:hover {
253
+ background: #dc2626;
254
+ }
255
+
256
+ /* Rules Container */
257
+ .rules-container {
258
+ max-height: 400px;
259
+ overflow-y: auto;
260
+ }
261
+
262
+ .rule-card {
263
+ background: var(--hover-bg);
264
+ border: 1px solid var(--border-color);
265
+ border-radius: 8px;
266
+ padding: 15px;
267
+ margin-bottom: 10px;
268
+ transition: all 0.2s;
269
+ }
270
+
271
+ .rule-card:hover {
272
+ border-color: var(--primary-color);
273
+ box-shadow: 0 2px 4px rgba(79, 70, 229, 0.1);
274
+ }
275
+
276
+ .rule-header {
277
+ display: flex;
278
+ justify-content: space-between;
279
+ align-items: center;
280
+ margin-bottom: 10px;
281
+ }
282
+
283
+ .rule-id {
284
+ font-weight: 600;
285
+ color: var(--primary-color);
286
+ }
287
+
288
+ .rule-actions {
289
+ display: flex;
290
+ gap: 10px;
291
+ }
292
+
293
+ .rule-summary {
294
+ font-size: 13px;
295
+ color: var(--text-secondary);
296
+ }
297
+
298
+ /* Actions */
299
+ .actions {
300
+ padding: 20px;
301
+ background: var(--hover-bg);
302
+ display: flex;
303
+ gap: 10px;
304
+ justify-content: flex-end;
305
+ }
306
+
307
+ /* JSON Preview */
308
+ .preview-panel {
309
+ display: flex;
310
+ flex-direction: column;
311
+ max-height: 80vh;
312
+ }
313
+
314
+ .json-preview {
315
+ flex: 1;
316
+ padding: 20px;
317
+ overflow: auto;
318
+ background: var(--code-bg);
319
+ }
320
+
321
+ .json-code {
322
+ color: #e5e7eb;
323
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
324
+ font-size: 13px;
325
+ line-height: 1.5;
326
+ white-space: pre-wrap;
327
+ word-wrap: break-word;
328
+ }
329
+
330
+ /* Validation Status */
331
+ .validation-status {
332
+ padding: 15px 20px;
333
+ display: flex;
334
+ align-items: center;
335
+ gap: 10px;
336
+ border-top: 1px solid var(--border-color);
337
+ }
338
+
339
+ .validation-status.success {
340
+ background: #d1fae5;
341
+ color: #065f46;
342
+ }
343
+
344
+ .validation-status.error {
345
+ background: #fee2e2;
346
+ color: #991b1b;
347
+ }
348
+
349
+ .status-icon {
350
+ font-size: 20px;
351
+ }
352
+
353
+ .validation-status.success .status-icon::before {
354
+ content: '✓';
355
+ }
356
+
357
+ .validation-status.error .status-icon::before {
358
+ content: '✗';
359
+ }
360
+
361
+ .validation-errors {
362
+ padding: 20px;
363
+ background: #fef2f2;
364
+ border-top: 1px solid var(--border-color);
365
+ }
366
+
367
+ .validation-errors h4 {
368
+ color: var(--danger-color);
369
+ margin-bottom: 10px;
370
+ }
371
+
372
+ .validation-errors ul {
373
+ list-style: none;
374
+ padding-left: 0;
375
+ }
376
+
377
+ .validation-errors li {
378
+ padding: 5px 0;
379
+ color: #991b1b;
380
+ font-size: 14px;
381
+ }
382
+
383
+ .validation-errors li::before {
384
+ content: '• ';
385
+ color: var(--danger-color);
386
+ }
387
+
388
+ /* Modal */
389
+ .modal {
390
+ position: fixed;
391
+ top: 0;
392
+ left: 0;
393
+ width: 100%;
394
+ height: 100%;
395
+ background: rgba(0, 0, 0, 0.5);
396
+ display: flex;
397
+ align-items: center;
398
+ justify-content: center;
399
+ z-index: 1000;
400
+ }
401
+
402
+ .modal.hidden {
403
+ display: none;
404
+ }
405
+
406
+ .modal-content {
407
+ background: white;
408
+ border-radius: 10px;
409
+ width: 90%;
410
+ max-width: 800px;
411
+ max-height: 90vh;
412
+ overflow-y: auto;
413
+ box-shadow: 0 20px 25px rgba(0, 0, 0, 0.2);
414
+ }
415
+
416
+ .modal-header {
417
+ padding: 20px;
418
+ border-bottom: 2px solid var(--border-color);
419
+ display: flex;
420
+ justify-content: space-between;
421
+ align-items: center;
422
+ background: var(--hover-bg);
423
+ }
424
+
425
+ .modal-header h2 {
426
+ font-size: 1.5rem;
427
+ }
428
+
429
+ .close-btn {
430
+ background: none;
431
+ border: none;
432
+ font-size: 28px;
433
+ cursor: pointer;
434
+ color: var(--text-secondary);
435
+ width: 32px;
436
+ height: 32px;
437
+ display: flex;
438
+ align-items: center;
439
+ justify-content: center;
440
+ border-radius: 4px;
441
+ }
442
+
443
+ .close-btn:hover {
444
+ background: var(--hover-bg);
445
+ color: var(--text-color);
446
+ }
447
+
448
+ .modal-body {
449
+ padding: 20px;
450
+ }
451
+
452
+ .modal-footer {
453
+ padding: 20px;
454
+ border-top: 1px solid var(--border-color);
455
+ display: flex;
456
+ justify-content: flex-end;
457
+ gap: 10px;
458
+ background: var(--hover-bg);
459
+ }
460
+
461
+ /* Condition Builder */
462
+ .condition-builder {
463
+ border: 2px dashed var(--border-color);
464
+ border-radius: 8px;
465
+ padding: 15px;
466
+ min-height: 100px;
467
+ }
468
+
469
+ .condition-item {
470
+ background: white;
471
+ border: 1px solid var(--border-color);
472
+ border-radius: 6px;
473
+ padding: 12px;
474
+ margin-bottom: 10px;
475
+ }
476
+
477
+ .field-condition {
478
+ display: flex;
479
+ align-items: center;
480
+ gap: 10px;
481
+ }
482
+
483
+ .field-condition-inputs {
484
+ display: flex;
485
+ gap: 10px;
486
+ flex: 1;
487
+ }
488
+
489
+ .composite-condition {
490
+ background: #f0f9ff;
491
+ border-color: #3b82f6;
492
+ }
493
+
494
+ .composite-header {
495
+ display: flex;
496
+ align-items: center;
497
+ gap: 10px;
498
+ margin-bottom: 10px;
499
+ }
500
+
501
+ .subconditions-container {
502
+ margin-left: 20px;
503
+ padding-left: 15px;
504
+ border-left: 2px solid var(--border-color);
505
+ }
506
+
507
+ .condition-type-select {
508
+ padding: 6px 10px;
509
+ border: 1px solid var(--border-color);
510
+ border-radius: 6px;
511
+ font-size: 13px;
512
+ background: white;
513
+ }
514
+
515
+ .operator-select {
516
+ min-width: 150px;
517
+ }
518
+
519
+ /* Footer */
520
+ .footer {
521
+ text-align: center;
522
+ padding: 20px;
523
+ color: var(--text-secondary);
524
+ font-size: 14px;
525
+ }
526
+
527
+ .footer a {
528
+ color: var(--primary-color);
529
+ text-decoration: none;
530
+ }
531
+
532
+ .footer a:hover {
533
+ text-decoration: underline;
534
+ }
535
+
536
+ /* Utility Classes */
537
+ .hidden {
538
+ display: none !important;
539
+ }
540
+
541
+ /* Scrollbar Styling */
542
+ ::-webkit-scrollbar {
543
+ width: 8px;
544
+ height: 8px;
545
+ }
546
+
547
+ ::-webkit-scrollbar-track {
548
+ background: var(--hover-bg);
549
+ }
550
+
551
+ ::-webkit-scrollbar-thumb {
552
+ background: var(--border-color);
553
+ border-radius: 4px;
554
+ }
555
+
556
+ ::-webkit-scrollbar-thumb:hover {
557
+ background: var(--secondary-color);
558
+ }