decision_agent 0.2.0 → 0.3.0

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -1
  3. data/bin/decision_agent +104 -0
  4. data/lib/decision_agent/dmn/adapter.rb +135 -0
  5. data/lib/decision_agent/dmn/cache.rb +306 -0
  6. data/lib/decision_agent/dmn/decision_graph.rb +327 -0
  7. data/lib/decision_agent/dmn/decision_tree.rb +192 -0
  8. data/lib/decision_agent/dmn/errors.rb +30 -0
  9. data/lib/decision_agent/dmn/exporter.rb +217 -0
  10. data/lib/decision_agent/dmn/feel/evaluator.rb +797 -0
  11. data/lib/decision_agent/dmn/feel/functions.rb +420 -0
  12. data/lib/decision_agent/dmn/feel/parser.rb +349 -0
  13. data/lib/decision_agent/dmn/feel/simple_parser.rb +276 -0
  14. data/lib/decision_agent/dmn/feel/transformer.rb +372 -0
  15. data/lib/decision_agent/dmn/feel/types.rb +276 -0
  16. data/lib/decision_agent/dmn/importer.rb +77 -0
  17. data/lib/decision_agent/dmn/model.rb +197 -0
  18. data/lib/decision_agent/dmn/parser.rb +191 -0
  19. data/lib/decision_agent/dmn/testing.rb +333 -0
  20. data/lib/decision_agent/dmn/validator.rb +315 -0
  21. data/lib/decision_agent/dmn/versioning.rb +229 -0
  22. data/lib/decision_agent/dmn/visualizer.rb +513 -0
  23. data/lib/decision_agent/dsl/condition_evaluator.rb +3 -0
  24. data/lib/decision_agent/dsl/schema_validator.rb +2 -1
  25. data/lib/decision_agent/evaluators/dmn_evaluator.rb +221 -0
  26. data/lib/decision_agent/version.rb +1 -1
  27. data/lib/decision_agent/web/dmn_editor.rb +426 -0
  28. data/lib/decision_agent/web/public/dmn-editor.css +596 -0
  29. data/lib/decision_agent/web/public/dmn-editor.html +250 -0
  30. data/lib/decision_agent/web/public/dmn-editor.js +553 -0
  31. data/lib/decision_agent/web/public/index.html +3 -0
  32. data/lib/decision_agent/web/public/styles.css +21 -0
  33. data/lib/decision_agent/web/server.rb +465 -0
  34. data/spec/ab_testing/ab_testing_agent_spec.rb +174 -0
  35. data/spec/auth/rbac_adapter_spec.rb +228 -0
  36. data/spec/dmn/decision_graph_spec.rb +282 -0
  37. data/spec/dmn/decision_tree_spec.rb +203 -0
  38. data/spec/dmn/feel/errors_spec.rb +18 -0
  39. data/spec/dmn/feel/functions_spec.rb +400 -0
  40. data/spec/dmn/feel/simple_parser_spec.rb +274 -0
  41. data/spec/dmn/feel/types_spec.rb +176 -0
  42. data/spec/dmn/feel_parser_spec.rb +489 -0
  43. data/spec/dmn/hit_policy_spec.rb +202 -0
  44. data/spec/dmn/integration_spec.rb +226 -0
  45. data/spec/examples.txt +1846 -1570
  46. data/spec/fixtures/dmn/complex_decision.dmn +81 -0
  47. data/spec/fixtures/dmn/invalid_structure.dmn +31 -0
  48. data/spec/fixtures/dmn/simple_decision.dmn +40 -0
  49. data/spec/monitoring/metrics_collector_spec.rb +37 -35
  50. data/spec/monitoring/monitored_agent_spec.rb +14 -11
  51. data/spec/performance_optimizations_spec.rb +10 -3
  52. data/spec/thread_safety_spec.rb +10 -2
  53. data/spec/web_ui_rack_spec.rb +294 -0
  54. metadata +65 -1
@@ -0,0 +1,596 @@
1
+ /* DMN Editor Styles */
2
+
3
+ :root {
4
+ --primary-color: #2196F3;
5
+ --secondary-color: #757575;
6
+ --success-color: #4CAF50;
7
+ --danger-color: #F44336;
8
+ --warning-color: #FF9800;
9
+ --border-color: #ddd;
10
+ --bg-light: #f5f5f5;
11
+ --bg-white: #fff;
12
+ --text-primary: #212121;
13
+ --text-secondary: #757575;
14
+ --shadow: 0 2px 4px rgba(0,0,0,0.1);
15
+ --shadow-lg: 0 4px 6px rgba(0,0,0,0.15);
16
+ }
17
+
18
+ * {
19
+ box-sizing: border-box;
20
+ margin: 0;
21
+ padding: 0;
22
+ }
23
+
24
+ body {
25
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
26
+ font-size: 14px;
27
+ line-height: 1.5;
28
+ color: var(--text-primary);
29
+ background: var(--bg-light);
30
+ }
31
+
32
+ .container {
33
+ display: flex;
34
+ flex-direction: column;
35
+ height: 100vh;
36
+ }
37
+
38
+ /* Header */
39
+ header {
40
+ background: var(--bg-white);
41
+ border-bottom: 1px solid var(--border-color);
42
+ padding: 1rem 2rem;
43
+ display: flex;
44
+ justify-content: space-between;
45
+ align-items: center;
46
+ box-shadow: var(--shadow);
47
+ }
48
+
49
+ header h1 {
50
+ font-size: 1.5rem;
51
+ font-weight: 600;
52
+ color: var(--primary-color);
53
+ }
54
+
55
+ .header-actions {
56
+ display: flex;
57
+ gap: 0.5rem;
58
+ }
59
+
60
+ /* Main Content */
61
+ .main-content {
62
+ display: flex;
63
+ flex: 1;
64
+ overflow: hidden;
65
+ }
66
+
67
+ /* Sidebar */
68
+ .sidebar {
69
+ width: 250px;
70
+ background: var(--bg-white);
71
+ border-right: 1px solid var(--border-color);
72
+ padding: 1rem;
73
+ overflow-y: auto;
74
+ }
75
+
76
+ .sidebar h3 {
77
+ font-size: 1rem;
78
+ font-weight: 600;
79
+ margin-bottom: 1rem;
80
+ color: var(--text-primary);
81
+ }
82
+
83
+ .model-list {
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: 0.5rem;
87
+ }
88
+
89
+ .model-item {
90
+ padding: 0.75rem;
91
+ background: var(--bg-light);
92
+ border: 1px solid var(--border-color);
93
+ border-radius: 4px;
94
+ cursor: pointer;
95
+ transition: all 0.2s;
96
+ }
97
+
98
+ .model-item:hover {
99
+ background: #e3f2fd;
100
+ border-color: var(--primary-color);
101
+ }
102
+
103
+ .model-item.active {
104
+ background: var(--primary-color);
105
+ color: white;
106
+ border-color: var(--primary-color);
107
+ }
108
+
109
+ .model-item h4 {
110
+ font-size: 0.875rem;
111
+ font-weight: 600;
112
+ margin-bottom: 0.25rem;
113
+ }
114
+
115
+ .model-item p {
116
+ font-size: 0.75rem;
117
+ color: var(--text-secondary);
118
+ }
119
+
120
+ .model-item.active p {
121
+ color: rgba(255,255,255,0.8);
122
+ }
123
+
124
+ /* Editor Area */
125
+ .editor-area {
126
+ flex: 1;
127
+ padding: 2rem;
128
+ overflow-y: auto;
129
+ background: var(--bg-light);
130
+ }
131
+
132
+ .welcome-screen {
133
+ text-align: center;
134
+ padding: 4rem 2rem;
135
+ background: var(--bg-white);
136
+ border-radius: 8px;
137
+ box-shadow: var(--shadow);
138
+ }
139
+
140
+ .welcome-screen h2 {
141
+ font-size: 2rem;
142
+ margin-bottom: 1rem;
143
+ color: var(--text-primary);
144
+ }
145
+
146
+ .welcome-screen p {
147
+ font-size: 1.125rem;
148
+ color: var(--text-secondary);
149
+ margin-bottom: 2rem;
150
+ }
151
+
152
+ .editor-container {
153
+ background: var(--bg-white);
154
+ border-radius: 8px;
155
+ box-shadow: var(--shadow);
156
+ padding: 1.5rem;
157
+ }
158
+
159
+ /* Model Info */
160
+ .model-info {
161
+ display: flex;
162
+ align-items: center;
163
+ gap: 1rem;
164
+ margin-bottom: 1.5rem;
165
+ padding-bottom: 1rem;
166
+ border-bottom: 1px solid var(--border-color);
167
+ }
168
+
169
+ .model-info h2 {
170
+ font-size: 1.5rem;
171
+ font-weight: 600;
172
+ flex: 1;
173
+ }
174
+
175
+ .model-id {
176
+ font-size: 0.875rem;
177
+ color: var(--text-secondary);
178
+ font-family: 'Courier New', monospace;
179
+ }
180
+
181
+ /* Tabs */
182
+ .tabs {
183
+ display: flex;
184
+ gap: 0.25rem;
185
+ border-bottom: 2px solid var(--border-color);
186
+ margin-bottom: 1.5rem;
187
+ }
188
+
189
+ .tab-btn {
190
+ padding: 0.75rem 1.5rem;
191
+ background: transparent;
192
+ border: none;
193
+ border-bottom: 2px solid transparent;
194
+ cursor: pointer;
195
+ font-size: 0.875rem;
196
+ font-weight: 500;
197
+ color: var(--text-secondary);
198
+ transition: all 0.2s;
199
+ margin-bottom: -2px;
200
+ }
201
+
202
+ .tab-btn:hover {
203
+ color: var(--primary-color);
204
+ }
205
+
206
+ .tab-btn.active {
207
+ color: var(--primary-color);
208
+ border-bottom-color: var(--primary-color);
209
+ }
210
+
211
+ .tab-pane {
212
+ display: none;
213
+ }
214
+
215
+ .tab-pane.active {
216
+ display: block;
217
+ }
218
+
219
+ /* Decision Selector */
220
+ .decision-selector {
221
+ display: flex;
222
+ align-items: center;
223
+ gap: 1rem;
224
+ margin-bottom: 1.5rem;
225
+ }
226
+
227
+ .decision-selector label {
228
+ font-weight: 500;
229
+ }
230
+
231
+ /* Decision Table Editor */
232
+ .decision-table-editor {
233
+ width: 100%;
234
+ }
235
+
236
+ .table-controls {
237
+ display: flex;
238
+ align-items: center;
239
+ gap: 1rem;
240
+ margin-bottom: 1rem;
241
+ padding: 0.75rem;
242
+ background: var(--bg-light);
243
+ border-radius: 4px;
244
+ }
245
+
246
+ .table-controls label {
247
+ font-weight: 500;
248
+ }
249
+
250
+ .table-wrapper {
251
+ overflow-x: auto;
252
+ border: 1px solid var(--border-color);
253
+ border-radius: 4px;
254
+ }
255
+
256
+ .decision-table {
257
+ width: 100%;
258
+ border-collapse: collapse;
259
+ background: var(--bg-white);
260
+ }
261
+
262
+ .decision-table th,
263
+ .decision-table td {
264
+ padding: 0.75rem;
265
+ border: 1px solid var(--border-color);
266
+ text-align: left;
267
+ }
268
+
269
+ .decision-table th {
270
+ background: var(--bg-light);
271
+ font-weight: 600;
272
+ font-size: 0.875rem;
273
+ }
274
+
275
+ .decision-table thead tr:first-child th {
276
+ background: var(--primary-color);
277
+ color: white;
278
+ }
279
+
280
+ .decision-table .rule-number {
281
+ width: 50px;
282
+ text-align: center;
283
+ font-weight: 600;
284
+ }
285
+
286
+ .decision-table .actions-col {
287
+ width: 100px;
288
+ text-align: center;
289
+ }
290
+
291
+ .decision-table tbody tr:hover {
292
+ background: #f5f5f5;
293
+ }
294
+
295
+ .decision-table input {
296
+ width: 100%;
297
+ padding: 0.5rem;
298
+ border: 1px solid var(--border-color);
299
+ border-radius: 4px;
300
+ font-size: 0.875rem;
301
+ }
302
+
303
+ .decision-table input:focus {
304
+ outline: none;
305
+ border-color: var(--primary-color);
306
+ }
307
+
308
+ .empty-row td {
309
+ text-align: center;
310
+ color: var(--text-secondary);
311
+ padding: 2rem;
312
+ }
313
+
314
+ /* Visualization Area */
315
+ .visualization-area {
316
+ min-height: 400px;
317
+ border: 1px solid var(--border-color);
318
+ border-radius: 4px;
319
+ padding: 1rem;
320
+ background: var(--bg-white);
321
+ overflow: auto;
322
+ }
323
+
324
+ .visualization-area svg {
325
+ max-width: 100%;
326
+ height: auto;
327
+ }
328
+
329
+ /* XML Content */
330
+ .xml-controls {
331
+ display: flex;
332
+ gap: 0.5rem;
333
+ margin-bottom: 1rem;
334
+ }
335
+
336
+ .xml-content {
337
+ background: #f5f5f5;
338
+ border: 1px solid var(--border-color);
339
+ border-radius: 4px;
340
+ padding: 1rem;
341
+ overflow-x: auto;
342
+ font-family: 'Courier New', monospace;
343
+ font-size: 0.875rem;
344
+ line-height: 1.6;
345
+ max-height: 600px;
346
+ }
347
+
348
+ /* Buttons */
349
+ .btn {
350
+ padding: 0.5rem 1rem;
351
+ border: 1px solid var(--border-color);
352
+ border-radius: 4px;
353
+ background: var(--bg-white);
354
+ color: var(--text-primary);
355
+ font-size: 0.875rem;
356
+ font-weight: 500;
357
+ cursor: pointer;
358
+ transition: all 0.2s;
359
+ text-decoration: none;
360
+ display: inline-block;
361
+ }
362
+
363
+ .btn:hover {
364
+ background: var(--bg-light);
365
+ }
366
+
367
+ .btn-primary {
368
+ background: var(--primary-color);
369
+ color: white;
370
+ border-color: var(--primary-color);
371
+ }
372
+
373
+ .btn-primary:hover {
374
+ background: #1976D2;
375
+ border-color: #1976D2;
376
+ }
377
+
378
+ .btn-secondary {
379
+ background: var(--secondary-color);
380
+ color: white;
381
+ border-color: var(--secondary-color);
382
+ }
383
+
384
+ .btn-secondary:hover {
385
+ background: #616161;
386
+ border-color: #616161;
387
+ }
388
+
389
+ .btn-danger {
390
+ background: var(--danger-color);
391
+ color: white;
392
+ border-color: var(--danger-color);
393
+ }
394
+
395
+ .btn-danger:hover {
396
+ background: #D32F2F;
397
+ border-color: #D32F2F;
398
+ }
399
+
400
+ .btn-sm {
401
+ padding: 0.375rem 0.75rem;
402
+ font-size: 0.8125rem;
403
+ }
404
+
405
+ .btn-large {
406
+ padding: 0.75rem 2rem;
407
+ font-size: 1rem;
408
+ }
409
+
410
+ .btn-link {
411
+ background: transparent;
412
+ border: none;
413
+ color: var(--primary-color);
414
+ }
415
+
416
+ .btn-link:hover {
417
+ background: transparent;
418
+ text-decoration: underline;
419
+ }
420
+
421
+ /* Form Controls */
422
+ .form-control {
423
+ padding: 0.5rem;
424
+ border: 1px solid var(--border-color);
425
+ border-radius: 4px;
426
+ font-size: 0.875rem;
427
+ width: 200px;
428
+ }
429
+
430
+ .form-control:focus {
431
+ outline: none;
432
+ border-color: var(--primary-color);
433
+ }
434
+
435
+ .form-control[type="file"] {
436
+ padding: 0.375rem;
437
+ }
438
+
439
+ /* Modals */
440
+ .modal {
441
+ display: none;
442
+ position: fixed;
443
+ top: 0;
444
+ left: 0;
445
+ width: 100%;
446
+ height: 100%;
447
+ background: rgba(0,0,0,0.5);
448
+ z-index: 1000;
449
+ align-items: center;
450
+ justify-content: center;
451
+ }
452
+
453
+ .modal.active {
454
+ display: flex;
455
+ }
456
+
457
+ .modal-content {
458
+ background: var(--bg-white);
459
+ border-radius: 8px;
460
+ padding: 2rem;
461
+ max-width: 500px;
462
+ width: 90%;
463
+ box-shadow: var(--shadow-lg);
464
+ }
465
+
466
+ .modal-content h3 {
467
+ font-size: 1.25rem;
468
+ margin-bottom: 1.5rem;
469
+ color: var(--text-primary);
470
+ }
471
+
472
+ .form-group {
473
+ margin-bottom: 1rem;
474
+ }
475
+
476
+ .form-group label {
477
+ display: block;
478
+ margin-bottom: 0.5rem;
479
+ font-weight: 500;
480
+ font-size: 0.875rem;
481
+ }
482
+
483
+ .form-group .form-control {
484
+ width: 100%;
485
+ }
486
+
487
+ .modal-actions {
488
+ display: flex;
489
+ gap: 0.5rem;
490
+ justify-content: flex-end;
491
+ margin-top: 1.5rem;
492
+ }
493
+
494
+ /* Notifications */
495
+ .notification {
496
+ position: fixed;
497
+ top: 1rem;
498
+ right: 1rem;
499
+ padding: 1rem 1.5rem;
500
+ border-radius: 4px;
501
+ box-shadow: var(--shadow-lg);
502
+ z-index: 2000;
503
+ display: none;
504
+ max-width: 400px;
505
+ }
506
+
507
+ .notification.show {
508
+ display: block;
509
+ animation: slideIn 0.3s ease-out;
510
+ }
511
+
512
+ .notification.success {
513
+ background: var(--success-color);
514
+ color: white;
515
+ }
516
+
517
+ .notification.error {
518
+ background: var(--danger-color);
519
+ color: white;
520
+ }
521
+
522
+ .notification.warning {
523
+ background: var(--warning-color);
524
+ color: white;
525
+ }
526
+
527
+ .notification.info {
528
+ background: var(--primary-color);
529
+ color: white;
530
+ }
531
+
532
+ @keyframes slideIn {
533
+ from {
534
+ transform: translateX(400px);
535
+ opacity: 0;
536
+ }
537
+ to {
538
+ transform: translateX(0);
539
+ opacity: 1;
540
+ }
541
+ }
542
+
543
+ /* Empty State */
544
+ .empty-state {
545
+ text-align: center;
546
+ color: var(--text-secondary);
547
+ padding: 2rem;
548
+ font-size: 0.875rem;
549
+ }
550
+
551
+ /* Responsive */
552
+ @media (max-width: 768px) {
553
+ .sidebar {
554
+ position: absolute;
555
+ left: -250px;
556
+ top: 0;
557
+ height: 100%;
558
+ z-index: 100;
559
+ transition: left 0.3s;
560
+ }
561
+
562
+ .sidebar.open {
563
+ left: 0;
564
+ }
565
+
566
+ .editor-area {
567
+ padding: 1rem;
568
+ }
569
+
570
+ .header-actions {
571
+ flex-wrap: wrap;
572
+ }
573
+ }
574
+
575
+ /* Tree Editor */
576
+ .tree-controls {
577
+ display: flex;
578
+ gap: 0.5rem;
579
+ margin-bottom: 1rem;
580
+ }
581
+
582
+ .tree-editor {
583
+ min-height: 300px;
584
+ border: 1px solid var(--border-color);
585
+ border-radius: 4px;
586
+ padding: 1rem;
587
+ margin-bottom: 1rem;
588
+ }
589
+
590
+ /* Graph Controls */
591
+ .graph-controls {
592
+ display: flex;
593
+ align-items: center;
594
+ gap: 1rem;
595
+ margin-bottom: 1rem;
596
+ }