decision_agent 0.1.7 → 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 (56) 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 +1132 -12
  24. data/lib/decision_agent/dsl/schema_validator.rb +12 -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/app.js +119 -1
  29. data/lib/decision_agent/web/public/dmn-editor.css +596 -0
  30. data/lib/decision_agent/web/public/dmn-editor.html +250 -0
  31. data/lib/decision_agent/web/public/dmn-editor.js +553 -0
  32. data/lib/decision_agent/web/public/index.html +71 -0
  33. data/lib/decision_agent/web/public/styles.css +21 -0
  34. data/lib/decision_agent/web/server.rb +465 -0
  35. data/spec/ab_testing/ab_testing_agent_spec.rb +174 -0
  36. data/spec/advanced_operators_spec.rb +2147 -0
  37. data/spec/auth/rbac_adapter_spec.rb +228 -0
  38. data/spec/dmn/decision_graph_spec.rb +282 -0
  39. data/spec/dmn/decision_tree_spec.rb +203 -0
  40. data/spec/dmn/feel/errors_spec.rb +18 -0
  41. data/spec/dmn/feel/functions_spec.rb +400 -0
  42. data/spec/dmn/feel/simple_parser_spec.rb +274 -0
  43. data/spec/dmn/feel/types_spec.rb +176 -0
  44. data/spec/dmn/feel_parser_spec.rb +489 -0
  45. data/spec/dmn/hit_policy_spec.rb +202 -0
  46. data/spec/dmn/integration_spec.rb +226 -0
  47. data/spec/examples.txt +1909 -0
  48. data/spec/fixtures/dmn/complex_decision.dmn +81 -0
  49. data/spec/fixtures/dmn/invalid_structure.dmn +31 -0
  50. data/spec/fixtures/dmn/simple_decision.dmn +40 -0
  51. data/spec/monitoring/metrics_collector_spec.rb +37 -35
  52. data/spec/monitoring/monitored_agent_spec.rb +14 -11
  53. data/spec/performance_optimizations_spec.rb +10 -3
  54. data/spec/thread_safety_spec.rb +10 -2
  55. data/spec/web_ui_rack_spec.rb +294 -0
  56. metadata +66 -1
@@ -307,12 +307,80 @@ class RuleBuilder {
307
307
  'between': '[min, max] or {"min": 0, "max": 100}',
308
308
  'modulo': '[divisor, remainder] or {"divisor": 2, "remainder": 0}',
309
309
 
310
+ // Mathematical functions
311
+ 'sin': 'expected result (e.g., 0.0)',
312
+ 'cos': 'expected result (e.g., 1.0)',
313
+ 'tan': 'expected result (e.g., 0.0)',
314
+ 'sqrt': 'expected result (e.g., 3.0)',
315
+ 'power': '[exponent, result] or {"exponent": 2, "result": 4}',
316
+ 'exp': 'expected result (e.g., 2.718)',
317
+ 'log': 'expected result (e.g., 0.0)',
318
+ 'round': 'expected rounded value (e.g., 3)',
319
+ 'floor': 'expected floor value (e.g., 3)',
320
+ 'ceil': 'expected ceiling value (e.g., 4)',
321
+ 'abs': 'expected absolute value (e.g., 5)',
322
+ 'min': 'expected minimum value (e.g., 1)',
323
+ 'max': 'expected maximum value (e.g., 10)',
324
+
325
+ // Statistical aggregations
326
+ 'sum': 'expected sum (e.g., 100) or {"min": 50, "max": 150}',
327
+ 'average': 'expected average (e.g., 25.5) or {"gt": 20, "lt": 30}',
328
+ 'mean': 'expected mean (e.g., 25.5) or {"gt": 20, "lt": 30}',
329
+ 'median': 'expected median (e.g., 25) or {"gt": 20}',
330
+ 'stddev': 'expected stddev (e.g., 5.2) or {"lt": 10}',
331
+ 'standard_deviation': 'expected stddev (e.g., 5.2) or {"lt": 10}',
332
+ 'variance': 'expected variance (e.g., 27.04) or {"lt": 100}',
333
+ 'percentile': '{"percentile": 95, "threshold": 200} or {"percentile": 95, "gt": 100}',
334
+ 'count': 'expected count (e.g., 10) or {"min": 5, "max": 20}',
335
+
310
336
  // Date/time operators
311
337
  'before_date': '2025-12-31',
312
338
  'after_date': '2024-01-01',
313
339
  'within_days': '7',
314
340
  'day_of_week': 'monday or 1',
315
341
 
342
+ // Duration calculations
343
+ 'duration_seconds': '{"end": "now", "max": 3600} or {"end": "field.end_time", "min": 60}',
344
+ 'duration_minutes': '{"end": "now", "max": 60} or {"end": "field.end_time", "min": 5}',
345
+ 'duration_hours': '{"end": "now", "max": 24} or {"end": "field.end_time", "min": 1}',
346
+ 'duration_days': '{"end": "now", "max": 7} or {"end": "field.end_time", "min": 1}',
347
+
348
+ // Date arithmetic
349
+ 'add_days': '{"days": 7, "compare": "lt", "target": "now"}',
350
+ 'subtract_days': '{"days": 1, "compare": "gt", "target": "now"}',
351
+ 'add_hours': '{"hours": 2, "compare": "lt", "target": "now"}',
352
+ 'subtract_hours': '{"hours": 1, "compare": "gt", "target": "now"}',
353
+ 'add_minutes': '{"minutes": 30, "compare": "lt", "target": "now"}',
354
+ 'subtract_minutes': '{"minutes": 15, "compare": "gt", "target": "now"}',
355
+
356
+ // Time components
357
+ 'hour_of_day': '9 or {"min": 9, "max": 17}',
358
+ 'day_of_month': '15 or {"gte": 1, "lte": 31}',
359
+ 'month': '12 or {"gte": 1, "lte": 12}',
360
+ 'year': '2025 or {"gte": 2024}',
361
+ 'week_of_year': '25 or {"gte": 1, "lte": 52}',
362
+
363
+ // Rate calculations
364
+ 'rate_per_second': '{"max": 10} or {"min": 5, "max": 100}',
365
+ 'rate_per_minute': '{"max": 600} or {"min": 50, "max": 1000}',
366
+ 'rate_per_hour': '{"max": 36000} or {"min": 5000, "max": 50000}',
367
+
368
+ // Moving window
369
+ 'moving_average': '{"window": 5, "threshold": 100} or {"window": 5, "gt": 50}',
370
+ 'moving_sum': '{"window": 10, "threshold": 1000} or {"window": 10, "lt": 2000}',
371
+ 'moving_max': '{"window": 5, "threshold": 200} or {"window": 5, "gt": 100}',
372
+ 'moving_min': '{"window": 5, "threshold": 10} or {"window": 5, "lt": 50}',
373
+
374
+ // Financial calculations
375
+ 'compound_interest': '{"rate": 0.05, "periods": 12, "result": 1050}',
376
+ 'present_value': '{"rate": 0.05, "periods": 10, "result": 613.91}',
377
+ 'future_value': '{"rate": 0.05, "periods": 10, "result": 1628.89}',
378
+ 'payment': '{"rate": 0.05, "periods": 12, "result": 100}',
379
+
380
+ // String aggregations
381
+ 'join': '{"separator": ",", "result": "a,b,c"} or {"separator": ",", "contains": "a"}',
382
+ 'length': '{"max": 500} or {"min": 10, "max": 100}',
383
+
316
384
  // Collection operators
317
385
  'contains_all': '["item1", "item2"]',
318
386
  'contains_any': '["item1", "item2"]',
@@ -332,9 +400,59 @@ class RuleBuilder {
332
400
  'modulo': 'Modulo: [divisor, remainder] or {"divisor": 2, "remainder": 0}',
333
401
  'matches': 'Regular expression pattern (e.g., ^user@company\\.com$)',
334
402
  'within_days': 'Number of days from now (e.g., 7 for within a week)',
403
+ 'sum': 'Numeric or {"min": 50, "max": 150}',
404
+ 'average': 'Numeric or {"gt": 20, "lt": 30}',
405
+ 'mean': 'Numeric or {"gt": 20, "lt": 30}',
406
+ 'median': 'Numeric or {"gt": 20}',
407
+ 'stddev': 'Numeric or {"lt": 10}',
408
+ 'standard_deviation': 'Numeric or {"lt": 10}',
409
+ 'variance': 'Numeric or {"lt": 100}',
410
+ 'percentile': '{"percentile": 95, "threshold": 200}',
411
+ 'count': 'Numeric or {"min": 5, "max": 20}',
412
+ 'duration_seconds': '{"end": "now" or "field.path", "max": 3600}',
413
+ 'duration_minutes': '{"end": "now" or "field.path", "max": 60}',
414
+ 'duration_hours': '{"end": "now" or "field.path", "max": 24}',
415
+ 'duration_days': '{"end": "now" or "field.path", "max": 7}',
416
+ 'add_days': '{"days": 7, "compare": "lt", "target": "now" or "field.path"}',
417
+ 'subtract_days': '{"days": 1, "compare": "gt", "target": "now" or "field.path"}',
418
+ 'add_hours': '{"hours": 2, "compare": "lt", "target": "now" or "field.path"}',
419
+ 'subtract_hours': '{"hours": 1, "compare": "gt", "target": "now" or "field.path"}',
420
+ 'add_minutes': '{"minutes": 30, "compare": "lt", "target": "now" or "field.path"}',
421
+ 'subtract_minutes': '{"minutes": 15, "compare": "gt", "target": "now" or "field.path"}',
422
+ 'hour_of_day': 'Numeric (0-23) or {"min": 9, "max": 17}',
423
+ 'day_of_month': 'Numeric (1-31) or {"gte": 1, "lte": 31}',
424
+ 'month': 'Numeric (1-12) or {"gte": 1, "lte": 12}',
425
+ 'year': 'Numeric or {"gte": 2024}',
426
+ 'week_of_year': 'Numeric (1-52) or {"gte": 1, "lte": 52}',
427
+ 'rate_per_second': '{"max": 10} or {"min": 5, "max": 100}',
428
+ 'rate_per_minute': '{"max": 600} or {"min": 50, "max": 1000}',
429
+ 'rate_per_hour': '{"max": 36000} or {"min": 5000, "max": 50000}',
430
+ 'moving_average': '{"window": 5, "threshold": 100}',
431
+ 'moving_sum': '{"window": 10, "threshold": 1000}',
432
+ 'moving_max': '{"window": 5, "threshold": 200}',
433
+ 'moving_min': '{"window": 5, "threshold": 10}',
434
+ 'compound_interest': '{"rate": 0.05, "periods": 12, "result": 1050}',
435
+ 'present_value': '{"rate": 0.05, "periods": 10, "result": 613.91}',
436
+ 'future_value': '{"rate": 0.05, "periods": 10, "result": 1628.89}',
437
+ 'payment': '{"rate": 0.05, "periods": 12, "result": 100}',
438
+ 'join': '{"separator": ",", "result": "a,b,c"}',
439
+ 'length': '{"max": 500} or {"min": 10, "max": 100}',
335
440
  'day_of_week': 'Day name (monday) or number (0=Sunday, 1=Monday, ...)',
336
441
  'within_radius': 'JSON: {"center": {"lat": y, "lon": x}, "radius": km}',
337
- 'in_polygon': 'Array of coordinates: [{"lat": y, "lon": x}, ...]'
442
+ 'in_polygon': 'Array of coordinates: [{"lat": y, "lon": x}, ...]',
443
+ 'sin': 'Expected result of sin(field_value). Example: 0.0 for sin(0)',
444
+ 'cos': 'Expected result of cos(field_value). Example: 1.0 for cos(0)',
445
+ 'tan': 'Expected result of tan(field_value). Example: 0.0 for tan(0)',
446
+ 'sqrt': 'Expected result of sqrt(field_value). Example: 3.0 for sqrt(9)',
447
+ 'power': 'Power: [exponent, result] or {"exponent": 2, "result": 4}. Checks if field^exponent == result',
448
+ 'exp': 'Expected result of exp(field_value) = e^field_value. Example: 2.718 for exp(1)',
449
+ 'log': 'Expected result of log(field_value) = natural logarithm. Example: 0.0 for log(1)',
450
+ 'round': 'Expected rounded value. Example: 3 for round(3.4)',
451
+ 'floor': 'Expected floor value. Example: 3 for floor(3.9)',
452
+ 'ceil': 'Expected ceiling value. Example: 4 for ceil(3.1)',
453
+ 'abs': 'Expected absolute value. Example: 5 for abs(-5) or abs(5)',
454
+ 'min': 'Expected minimum value from array. Example: 1 for min([3, 1, 5, 2])',
455
+ 'max': 'Expected maximum value from array. Example: 5 for max([3, 1, 5, 2])'
338
456
  };
339
457
 
340
458
  if (hints[operator]) {