iints-sdk-python35 1.5.10__tar.gz → 1.5.11__tar.gz

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 (291) hide show
  1. {iints_sdk_python35-1.5.10/src/iints_sdk_python35.egg-info → iints_sdk_python35-1.5.11}/PKG-INFO +3 -1
  2. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/pyproject.toml +3 -1
  3. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/__init__.py +4 -2
  4. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/assistant.py +3 -0
  5. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/prompts.py +17 -0
  6. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/reporting.py +250 -69
  7. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/api/base_algorithm.py +2 -0
  8. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/cli/cli.py +529 -22
  9. iints_sdk_python35-1.5.11/src/iints/core/algorithms/mpc_controller.py +207 -0
  10. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/devices/models.py +98 -14
  11. iints_sdk_python35-1.5.11/src/iints/core/digital_twin.py +168 -0
  12. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/patient/bergman_model.py +40 -16
  13. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/patient/hovorka_model.py +162 -42
  14. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/physiology_variation.py +39 -2
  15. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/safety/config.py +6 -0
  16. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/simulator.py +240 -0
  17. iints_sdk_python35-1.5.11/src/iints/presets/evidence_sources.yaml +235 -0
  18. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/presets/presets.json +80 -49
  19. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/__init__.py +30 -0
  20. iints_sdk_python35-1.5.11/src/iints/research/forecasting.py +679 -0
  21. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/predictor.py +8 -0
  22. iints_sdk_python35-1.5.11/src/iints/research/results_manager.py +535 -0
  23. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/utils/plotting.py +9 -0
  24. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11/src/iints_sdk_python35.egg-info}/PKG-INFO +3 -1
  25. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints_sdk_python35.egg-info/SOURCES.txt +4 -0
  26. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints_sdk_python35.egg-info/requires.txt +2 -0
  27. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_bergman.py +17 -0
  28. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_onboarding.py +84 -0
  29. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_physiology_variation.py +27 -1
  30. iints_sdk_python35-1.5.10/src/iints/presets/evidence_sources.yaml +0 -123
  31. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/LICENSE +0 -0
  32. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/LICENSE-MIT-IINTS-LEGACY +0 -0
  33. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/NOTICE +0 -0
  34. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/README.md +0 -0
  35. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/setup.cfg +0 -0
  36. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/__init__.py +0 -0
  37. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/backends/__init__.py +0 -0
  38. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/backends/base.py +0 -0
  39. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/backends/mistral_api.py +0 -0
  40. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/backends/ollama.py +0 -0
  41. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/cli.py +0 -0
  42. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/mdmp_guard.py +0 -0
  43. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/model_catalog.py +0 -0
  44. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/ai/prepare.py +0 -0
  45. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/__init__.py +0 -0
  46. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/algorithm_xray.py +0 -0
  47. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/baseline.py +0 -0
  48. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/booth_demo.py +0 -0
  49. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/carelink_workbench.py +0 -0
  50. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/clinical_benchmark.py +0 -0
  51. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/clinical_metrics.py +0 -0
  52. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/clinical_tir_analyzer.py +0 -0
  53. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/diabetes_metrics.py +0 -0
  54. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/edge_efficiency.py +0 -0
  55. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/edge_performance_monitor.py +0 -0
  56. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/eucys_results.py +0 -0
  57. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/evidence_bundle.py +0 -0
  58. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/explainability.py +0 -0
  59. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/explainable_ai.py +0 -0
  60. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/hardware_benchmark.py +0 -0
  61. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/metrics.py +0 -0
  62. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/population_report.py +0 -0
  63. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/poster.py +0 -0
  64. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/run_quality.py +0 -0
  65. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/safety_index.py +0 -0
  66. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/safety_visualizer.py +0 -0
  67. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/sensor_filtering.py +0 -0
  68. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/study_analysis.py +0 -0
  69. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/study_engine.py +0 -0
  70. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/study_experiment.py +0 -0
  71. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/study_poster.py +0 -0
  72. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/study_protocol.py +0 -0
  73. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/analysis/validator.py +0 -0
  74. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/api/__init__.py +0 -0
  75. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/api/registry.py +0 -0
  76. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/api/template_algorithm.py +0 -0
  77. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/assets/iints_logo.png +0 -0
  78. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/cli/__init__.py +0 -0
  79. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/cli/patient_cli.py +0 -0
  80. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/__init__.py +0 -0
  81. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/__init__.py +0 -0
  82. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/battle_runner.py +0 -0
  83. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/clinical_baseline.py +0 -0
  84. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/correction_bolus.py +0 -0
  85. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/discovery.py +0 -0
  86. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/fixed_basal_bolus.py +0 -0
  87. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/hybrid_algorithm.py +0 -0
  88. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/imitation_controller.py +0 -0
  89. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/lstm_algorithm.py +0 -0
  90. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/mock_algorithms.py +0 -0
  91. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/neural_controller.py +0 -0
  92. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/pid_controller.py +0 -0
  93. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/algorithms/standard_pump_algo.py +0 -0
  94. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/device.py +0 -0
  95. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/device_manager.py +0 -0
  96. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/devices/__init__.py +0 -0
  97. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/patient/__init__.py +0 -0
  98. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/patient/models.py +0 -0
  99. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/patient/patient_factory.py +0 -0
  100. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/patient/profile.py +0 -0
  101. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/safety/__init__.py +0 -0
  102. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/safety/input_validator.py +0 -0
  103. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/safety/supervisor.py +0 -0
  104. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/simulation/__init__.py +0 -0
  105. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/simulation/scenario_parser.py +0 -0
  106. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/core/supervisor.py +0 -0
  107. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/__init__.py +0 -0
  108. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/adapter.py +0 -0
  109. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/certify.py +0 -0
  110. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/column_mapper.py +0 -0
  111. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/contracts.py +0 -0
  112. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/datasets.json +0 -0
  113. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/demo/__init__.py +0 -0
  114. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/demo/demo_cgm.csv +0 -0
  115. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/evidence.py +0 -0
  116. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/guardians.py +0 -0
  117. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/importer.py +0 -0
  118. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/ingestor.py +0 -0
  119. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/mdmp_visualizer.py +0 -0
  120. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/medtronic_live.py +0 -0
  121. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/nightscout.py +0 -0
  122. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/physiology_residual_profiles.json +0 -0
  123. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/quality_checker.py +0 -0
  124. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/realism_dashboard.py +0 -0
  125. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/realism_governance.py +0 -0
  126. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/realism_reference.py +0 -0
  127. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/realism_references.json +0 -0
  128. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/realism_validator.py +0 -0
  129. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/registry.py +0 -0
  130. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/research_catalog.py +0 -0
  131. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/runner.py +0 -0
  132. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/study_corruption.py +0 -0
  133. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/synthetic_mirror.py +0 -0
  134. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/tidepool.py +0 -0
  135. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/universal_parser.py +0 -0
  136. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/clinic_safe_baseline.yaml +0 -0
  137. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/clinic_safe_hyper_challenge.yaml +0 -0
  138. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/clinic_safe_hypo_prone.yaml +0 -0
  139. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/clinic_safe_midnight.yaml +0 -0
  140. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/clinic_safe_pizza.yaml +0 -0
  141. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/clinic_safe_stress_meal.yaml +0 -0
  142. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/default_patient.yaml +0 -0
  143. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/patient_559_config.yaml +0 -0
  144. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/reference_azt1d_t1d.yaml +0 -0
  145. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/reference_free_living_t1d.yaml +0 -0
  146. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/data/virtual_patients/reference_hupa_ucm_t1d.yaml +0 -0
  147. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/demo_assets.py +0 -0
  148. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/emulation/__init__.py +0 -0
  149. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/emulation/legacy_base.py +0 -0
  150. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/emulation/medtronic_780g.py +0 -0
  151. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/emulation/omnipod_5.py +0 -0
  152. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/emulation/tandem_controliq.py +0 -0
  153. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/highlevel.py +0 -0
  154. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/jetson/__init__.py +0 -0
  155. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/jetson/endurance.py +0 -0
  156. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/jetson/research_pipeline.py +0 -0
  157. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/learning/__init__.py +0 -0
  158. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/learning/autonomous_optimizer.py +0 -0
  159. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/learning/learning_system.py +0 -0
  160. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/__init__.py +0 -0
  161. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/api.py +0 -0
  162. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/daemon.py +0 -0
  163. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/edge_benchmark.py +0 -0
  164. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/edge_ops.py +0 -0
  165. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/fpga.py +0 -0
  166. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/long_study.py +0 -0
  167. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/medtronic_direct.py +0 -0
  168. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/pico_pump.py +0 -0
  169. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/runtime.py +0 -0
  170. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/service_export.py +0 -0
  171. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/live_patient/uno_q.py +0 -0
  172. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/mdmp/__init__.py +0 -0
  173. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/mdmp/backend.py +0 -0
  174. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/mdmp/eu_ai_pact.py +0 -0
  175. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/metrics.py +0 -0
  176. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/population/__init__.py +0 -0
  177. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/population/generator.py +0 -0
  178. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/population/runner.py +0 -0
  179. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/presets/__init__.py +0 -0
  180. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/presets/forecast_calibration_profiles.yaml +0 -0
  181. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/presets/golden_benchmark.yaml +0 -0
  182. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/presets/safety_contract_default.yaml +0 -0
  183. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/presets/validation_profiles.yaml +0 -0
  184. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/audit.py +0 -0
  185. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/calibration_gate.py +0 -0
  186. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/config.py +0 -0
  187. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/control.py +0 -0
  188. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/control_eval.py +0 -0
  189. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/data_blend.py +0 -0
  190. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/dataset.py +0 -0
  191. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/evaluation.py +0 -0
  192. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/local_ai.py +0 -0
  193. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/local_ai_gate.py +0 -0
  194. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/losses.py +0 -0
  195. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/metrics.py +0 -0
  196. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/model_registry.py +0 -0
  197. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/research/neural_control.py +0 -0
  198. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/scenarios/__init__.py +0 -0
  199. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/scenarios/generator.py +0 -0
  200. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/scenarios/study_pack.py +0 -0
  201. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/__init__.py +0 -0
  202. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/default_algorithm.py +0 -0
  203. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/demos/__init__.py +0 -0
  204. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/demos/live_stage_demo.py +0 -0
  205. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/pico_pump/README.md +0 -0
  206. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/pico_pump/code.py +0 -0
  207. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/pico_pump/serial_protocol.txt +0 -0
  208. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/scenarios/__init__.py +0 -0
  209. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/scenarios/chaos_insulin_stacking.json +0 -0
  210. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/scenarios/chaos_runaway_ai.json +0 -0
  211. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/scenarios/example_scenario.json +0 -0
  212. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/scenarios/exercise_stress.json +0 -0
  213. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/uno_q/README.md +0 -0
  214. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/templates/uno_q/iints_supervisor_bridge.ino +0 -0
  215. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/utils/__init__.py +0 -0
  216. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/utils/csv_safety.py +0 -0
  217. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/utils/run_io.py +0 -0
  218. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/utils/url_safety.py +0 -0
  219. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/validation/__init__.py +0 -0
  220. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/validation/golden.py +0 -0
  221. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/validation/replay.py +0 -0
  222. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/validation/run_doctor.py +0 -0
  223. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/validation/run_validation.py +0 -0
  224. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/validation/safety_contract.py +0 -0
  225. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/validation/schemas.py +0 -0
  226. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/visualization/__init__.py +0 -0
  227. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/visualization/cockpit.py +0 -0
  228. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints/visualization/uncertainty_cloud.py +0 -0
  229. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints_sdk_python35.egg-info/dependency_links.txt +0 -0
  230. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints_sdk_python35.egg-info/entry_points.txt +0 -0
  231. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/iints_sdk_python35.egg-info/top_level.txt +0 -0
  232. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_ai/__init__.py +0 -0
  233. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_ai/lineage.py +0 -0
  234. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/__init__.py +0 -0
  235. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/audit.py +0 -0
  236. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/bias_hooks.py +0 -0
  237. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/bundle.py +0 -0
  238. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/certification.py +0 -0
  239. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/cli.py +0 -0
  240. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/compare.py +0 -0
  241. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/conformance.py +0 -0
  242. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/contracts.py +0 -0
  243. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/crypto.py +0 -0
  244. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/data/conformance/vectors/delegation.json +0 -0
  245. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/data/conformance/vectors/fingerprint.json +0 -0
  246. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/data/conformance/vectors/grading.json +0 -0
  247. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/data/conformance/vectors/signing.json +0 -0
  248. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/delegate.py +0 -0
  249. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/diffing.py +0 -0
  250. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/drift.py +0 -0
  251. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/exceptions.py +0 -0
  252. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/fingerprint.py +0 -0
  253. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/fingerprint_store.py +0 -0
  254. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/hf.py +0 -0
  255. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/keys/mdmp_pub_v1.pem +0 -0
  256. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/llm_provenance.py +0 -0
  257. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/migrate.py +0 -0
  258. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/policy.py +0 -0
  259. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/prov.py +0 -0
  260. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/registry.py +0 -0
  261. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/runner.py +0 -0
  262. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/schema_export.py +0 -0
  263. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/synthetic.py +0 -0
  264. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/trust.py +0 -0
  265. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_core/visualizer.py +0 -0
  266. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_flavors/__init__.py +0 -0
  267. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_integrations/__init__.py +0 -0
  268. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_integrations/dvc.py +0 -0
  269. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_integrations/mlflow.py +0 -0
  270. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/src/mdmp_integrations/wandb.py +0 -0
  271. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_ci_governance.py +0 -0
  272. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_algorithm_loading.py +0 -0
  273. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_booth_demo.py +0 -0
  274. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_carelink_workbench.py +0 -0
  275. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_cloud_import_security.py +0 -0
  276. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_demo_export.py +0 -0
  277. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_demo_live.py +0 -0
  278. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_edge_runtime.py +0 -0
  279. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_expo_tools.py +0 -0
  280. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_fpga.py +0 -0
  281. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_patient_runtime.py +0 -0
  282. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_poster.py +0 -0
  283. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_research_workflows.py +0 -0
  284. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_cli_run_summary.py +0 -0
  285. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_install_doctor.py +0 -0
  286. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_jetson_endurance.py +0 -0
  287. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_plugin_system.py +0 -0
  288. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_population.py +0 -0
  289. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_presets_realism.py +0 -0
  290. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_research_realism_tools.py +0 -0
  291. {iints_sdk_python35-1.5.10 → iints_sdk_python35-1.5.11}/tests/test_simulator_calibration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iints-sdk-python35
3
- Version: 1.5.10
3
+ Version: 1.5.11
4
4
  Summary: A pre-clinical Edge-AI SDK for diabetes management validation.
5
5
  Author-email: Rune Bobbaers <rune.bobbaers@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -34,6 +34,7 @@ Requires-Dist: fpdf2<3.0.0,>=2.8.0; extra == "reports"
34
34
  Requires-Dist: matplotlib<4.0.0,>=3.5.0; extra == "reports"
35
35
  Requires-Dist: openpyxl<4.0.0,>=3.0.0; extra == "reports"
36
36
  Requires-Dist: pillow<13.0.0,>=12.1.1; extra == "reports"
37
+ Requires-Dist: SciencePlots<3.0.0,>=2.1.0; extra == "reports"
37
38
  Requires-Dist: seaborn<1.0.0,>=0.11.0; extra == "reports"
38
39
  Provides-Extra: full
39
40
  Requires-Dist: fpdf2<3.0.0,>=2.8.0; extra == "full"
@@ -41,6 +42,7 @@ Requires-Dist: matplotlib<4.0.0,>=3.5.0; extra == "full"
41
42
  Requires-Dist: openpyxl<4.0.0,>=3.0.0; extra == "full"
42
43
  Requires-Dist: pyserial<4.0,>=3.5; extra == "full"
43
44
  Requires-Dist: pillow<13.0.0,>=12.1.1; extra == "full"
45
+ Requires-Dist: SciencePlots<3.0.0,>=2.1.0; extra == "full"
44
46
  Requires-Dist: seaborn<1.0.0,>=0.11.0; extra == "full"
45
47
  Provides-Extra: dev
46
48
  Requires-Dist: pytest<10.0.0,>=7.0.0; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "iints-sdk-python35"
7
- version = "1.5.10"
7
+ version = "1.5.11"
8
8
  authors = [
9
9
  { name="Rune Bobbaers", email="rune.bobbaers@gmail.com" },
10
10
  ]
@@ -44,6 +44,7 @@ reports = [
44
44
  "matplotlib>=3.5.0,<4.0.0",
45
45
  "openpyxl>=3.0.0,<4.0.0",
46
46
  "pillow>=12.1.1,<13.0.0",
47
+ "SciencePlots>=2.1.0,<3.0.0",
47
48
  "seaborn>=0.11.0,<1.0.0",
48
49
  ]
49
50
  full = [
@@ -52,6 +53,7 @@ full = [
52
53
  "openpyxl>=3.0.0,<4.0.0",
53
54
  "pyserial>=3.5,<4.0",
54
55
  "pillow>=12.1.1,<13.0.0",
56
+ "SciencePlots>=2.1.0,<3.0.0",
55
57
  "seaborn>=0.11.0,<1.0.0",
56
58
  ]
57
59
  dev = [
@@ -11,7 +11,7 @@ except ImportError: # pragma: no cover - Python < 3.8 fallback
11
11
  try:
12
12
  __version__ = version("iints-sdk-python35")
13
13
  except PackageNotFoundError: # pragma: no cover - source tree fallback
14
- __version__ = "1.5.10"
14
+ __version__ = "1.5.11"
15
15
 
16
16
  # Note to developers: this SDK is currently maintained by a single author.
17
17
  # Please report bugs via GitHub issues and feel free to contribute fixes via PRs.
@@ -237,9 +237,10 @@ def generate_agp_assets(
237
237
  output_dir: Optional[str] = None,
238
238
  subject_name: str = "Research simulation",
239
239
  summary_json_path: Optional[str] = None,
240
+ export_svg: bool = True,
240
241
  ) -> Optional[dict]:
241
242
  """
242
- Export AGP-style PNG assets and summary JSON from dense CGM/simulation data.
243
+ Export AGP-style PNG/SVG assets and summary JSON from dense CGM/simulation data.
243
244
  """
244
245
  if output_dir is None:
245
246
  return None
@@ -249,6 +250,7 @@ def generate_agp_assets(
249
250
  output_dir,
250
251
  subject_name=subject_name,
251
252
  summary_json_path=summary_json_path,
253
+ export_svg=export_svg,
252
254
  )
253
255
 
254
256
  # You can also define __all__ to explicitly control what gets imported with `from iints import *`
@@ -133,3 +133,6 @@ class IINTSAssistant:
133
133
 
134
134
  def review_realism(self, run: dict[str, Any]) -> AIResponse:
135
135
  return self._run_task("review_realism", run)
136
+
137
+ def predict_insulin(self, payload: dict[str, Any]) -> AIResponse:
138
+ return self._run_task("predict_insulin", payload)
@@ -10,6 +10,7 @@ TaskName = Literal[
10
10
  "detect_anomalies",
11
11
  "generate_report",
12
12
  "review_realism",
13
+ "predict_insulin",
13
14
  ]
14
15
  MAX_PROMPT_PAYLOAD_CHARS = 12000
15
16
 
@@ -71,6 +72,22 @@ TASK_TEMPLATES: dict[TaskName, str] = {
71
72
  "Focus on glycemic ranges, excursion patterns, insulin behavior, safety overrides, and whether the data looks internally coherent.\n\n"
72
73
  "Input JSON:\n{data}"
73
74
  ),
75
+ "predict_insulin": (
76
+ "You are reviewing a simulated bi-hormonal (Insulin + Glucagon) dose candidate inside the IINTS-AF research sandbox.\n"
77
+ "You are not controlling a real pump, not treating a patient, and not giving medical advice.\n"
78
+ "You will be provided with the current biological state of the patient, which now includes deep science metrics like "
79
+ "active_insulin, insulin_effect, plasma_glucagon_pg_ml, and the haaf_metric (Hypoglycemia-Associated Autonomic Failure memory). "
80
+ "You will also see a mathematically optimal insulin dose calculated by a deterministic MPC solver running differential equations.\n\n"
81
+ "Your task is to critique the candidate and decide on both Insulin and Glucagon doses for research simulation only. "
82
+ "Never increase insulin above the deterministic MPC dose or safety caps. "
83
+ "If glucose is low, predicted low, falling quickly, or active insulin is high, reduce the insulin dose to 0.0. "
84
+ "If the patient is in severe hypoglycemia risk and HAAF is high (meaning natural rescue is failing), "
85
+ "you may propose a simulation-only glucagon rescue candidate. The simulator will still apply hard glucagon safety caps.\n"
86
+ "Respond with a short explanation followed by exactly this format:\n"
87
+ "FINAL_DOSE: <number>\n"
88
+ "FINAL_GLUCAGON_DOSE_MG: <number>\n\n"
89
+ "Input JSON:\n{data}"
90
+ ),
74
91
  }
75
92
 
76
93
 
@@ -3,21 +3,31 @@ import tempfile
3
3
  import logging
4
4
  import json
5
5
  from pathlib import Path
6
- from typing import Any, Dict, Optional
6
+ from typing import Any, Dict, List, Optional
7
7
 
8
8
  os.environ.setdefault("MPLBACKEND", "Agg")
9
+ os.environ.setdefault("MPLCONFIGDIR", str(Path(tempfile.gettempdir()) / "iints-matplotlib"))
9
10
 
10
11
  import matplotlib.pyplot as plt
11
12
  import numpy as np
12
13
  import pandas as pd
13
14
  from fpdf import FPDF
14
15
  from fpdf.enums import XPos, YPos
16
+ from matplotlib.patches import Patch
15
17
 
16
18
  from iints.analysis.clinical_metrics import ClinicalMetricsCalculator
17
19
  from iints.utils.plotting import apply_plot_style
18
20
 
19
21
  logger = logging.getLogger("iints")
20
22
 
23
+ AGP_TIR_STACK = [
24
+ ("very_low_lt_54", "Very Low", "<54 mg/dL", "#8B0000", "white"),
25
+ ("low_54_69", "Low", "54-69 mg/dL", "#D32F2F", "white"),
26
+ ("target_70_180", "Target", "70-180 mg/dL", "#4CAF50", "black"),
27
+ ("high_181_250", "High", "181-250 mg/dL", "#FFD54F", "black"),
28
+ ("very_high_gt_250", "Very High", ">250 mg/dL", "#F57C00", "black"),
29
+ ]
30
+
21
31
 
22
32
  class ClinicalReportGenerator:
23
33
  """Generate a clean, publication-ready PDF report."""
@@ -170,6 +180,7 @@ class ClinicalReportGenerator:
170
180
  *,
171
181
  target_low: float,
172
182
  target_high: float,
183
+ svg_path: Optional[Path] = None,
173
184
  ) -> None:
174
185
  apply_plot_style()
175
186
  step = max(1, int(round(self._infer_step_minutes(df))))
@@ -187,37 +198,78 @@ class ClinicalReportGenerator:
187
198
  p75 = q[0.75].to_numpy(dtype=float)
188
199
  p95 = q[0.95].to_numpy(dtype=float)
189
200
 
190
- import seaborn as sns
191
-
192
- sns.set_style("whitegrid")
201
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 4.6), gridspec_kw={"width_ratios": [4.5, 1.4]})
193
202
 
194
- plt.figure(figsize=(10, 4.2))
195
- plt.fill_between(x, p05, p95, color="#E0E0E0", label="5-95%", linewidth=0)
196
- plt.fill_between(x, p25, p75, color="#A6A6A6", label="25-75%", linewidth=0)
197
- plt.plot(x, p50, color="#000000", linewidth=2.5, label="Median")
198
- plt.axhspan(target_low, target_high, color="#2E7D32", alpha=0.08)
199
- plt.axhline(target_low, color="#424242", linestyle="--", linewidth=1.0)
200
- plt.axhline(target_high, color="#424242", linestyle="--", linewidth=1.0)
203
+ # Panel 1: AGP Curve
204
+ ax1.fill_between(x, p05, p95, color="#D0D0D0", label="5-95%", alpha=0.5, linewidth=0)
205
+ ax1.fill_between(x, p25, p75, color="#A0A0A0", label="25-75%", alpha=0.8, linewidth=0)
206
+ ax1.plot(x, p50, color="#103b5c", linewidth=2.5, label="Median")
201
207
 
202
- ax = plt.gca()
203
- ax.spines['top'].set_visible(False)
204
- ax.spines['right'].set_visible(False)
205
- ax.spines['left'].set_color('#333333')
206
- ax.spines['bottom'].set_color('#333333')
208
+ ax1.axhspan(target_low, target_high, color="#2ca02c", alpha=0.1)
209
+ ax1.axhline(target_low, color="#2ca02c", linestyle=":", linewidth=1)
210
+ ax1.axhline(target_high, color="#2ca02c", linestyle=":", linewidth=1)
207
211
 
208
- plt.xlim(0, 24)
212
+ ax1.set_xlim(0, 24)
209
213
  ymax = max(350.0, float(np.nanmax(p95)) + 20.0)
210
- plt.ylim(40, ymax)
211
- plt.xticks([0, 3, 6, 9, 12, 15, 18, 21, 24], ["12a", "3a", "6a", "9a", "12p", "3p", "6p", "9p", "12a"])
212
- plt.ylabel("mg/dL", fontweight="bold")
213
- plt.title("Ambulatory Glucose Profile (AGP-style modal day)", fontweight="bold")
214
+ ax1.set_ylim(40, ymax)
215
+ ax1.set_xticks([0, 3, 6, 9, 12, 15, 18, 21, 24])
216
+ ax1.set_xticklabels(["12a", "3a", "6a", "9a", "12p", "3p", "6p", "9p", "12a"])
217
+ ax1.set_ylabel("Glucose (mg/dL)", fontweight="bold")
214
218
 
215
- ax.grid(True, axis="y", alpha=0.3, linestyle="-", color="#CCCCCC")
216
- ax.grid(False, axis="x")
219
+ report_day_count = int(max(1, df["day_index"].nunique()))
220
+ title = "Single-Day Glucose Profile" if report_day_count < 2 else "Ambulatory Glucose Profile (AGP)"
221
+ ax1.set_title(title, fontweight="bold")
217
222
 
218
- plt.tight_layout()
219
- plt.savefig(output_path, dpi=300)
220
- plt.close()
223
+ # Panel 2: TIR Bar
224
+ summary = self._agp_summary(df, target_low=target_low, target_high=target_high)
225
+ pct_map = summary["time_ranges_pct"]
226
+ bottom = 0.0
227
+ legend_handles = []
228
+ for key, label, range_text, color, text_color in AGP_TIR_STACK:
229
+ val = float(pct_map.get(key, 0.0))
230
+ legend_handles.append(
231
+ Patch(
232
+ facecolor=color,
233
+ edgecolor="none",
234
+ label=f"{label} ({range_text}) {val:.0f}%",
235
+ )
236
+ )
237
+ if val > 0:
238
+ ax2.bar([0], [val], bottom=[bottom], color=color, width=0.48)
239
+ if val >= 6:
240
+ ax2.text(
241
+ 0,
242
+ bottom + val / 2.0,
243
+ f"{val:.0f}%",
244
+ ha="center",
245
+ va="center",
246
+ color=text_color,
247
+ fontweight="bold",
248
+ fontsize=9,
249
+ )
250
+ bottom += val
251
+
252
+ ax2.set_ylim(0, 100)
253
+ ax2.set_xlim(-0.5, 0.5)
254
+ ax2.set_xticks([0])
255
+ ax2.set_xticklabels(["TIR"])
256
+ ax2.set_yticks([0, 25, 50, 75, 100])
257
+ ax2.set_ylabel("% Time", fontweight="bold")
258
+ ax2.set_title("Time in Range", fontweight="bold")
259
+ ax2.legend(
260
+ handles=list(reversed(legend_handles)),
261
+ loc="center left",
262
+ bbox_to_anchor=(1.02, 0.5),
263
+ frameon=False,
264
+ fontsize=7,
265
+ borderaxespad=0.0,
266
+ )
267
+
268
+ fig.tight_layout()
269
+ fig.savefig(output_path, dpi=600, bbox_inches="tight")
270
+ if svg_path is not None:
271
+ fig.savefig(svg_path, format="svg", bbox_inches="tight")
272
+ plt.close(fig)
221
273
 
222
274
  def _plot_daily_profiles(
223
275
  self,
@@ -227,14 +279,21 @@ class ClinicalReportGenerator:
227
279
  target_low: float,
228
280
  target_high: float,
229
281
  max_days: int = 14,
282
+ svg_path: Optional[Path] = None,
230
283
  ) -> None:
231
284
  apply_plot_style()
232
285
  day_ids = sorted(df["day_index"].unique().tolist())[:max_days]
233
286
  if not day_ids:
234
287
  day_ids = [0]
235
- cols = 7
288
+ # A single-day AGP should not render as one tiny panel in a 7-day grid.
289
+ # Keep the 7-column layout for longer reports, but let short reports breathe.
290
+ if len(day_ids) <= 3:
291
+ cols = len(day_ids)
292
+ else:
293
+ cols = 7
236
294
  rows = int(np.ceil(len(day_ids) / cols))
237
- fig, axes = plt.subplots(rows, cols, figsize=(10, max(1.6, rows * 1.35)), squeeze=False)
295
+ fig_height = max(1.9, rows * 1.45 if cols >= 7 else 2.35)
296
+ fig, axes = plt.subplots(rows, cols, figsize=(10, fig_height), squeeze=False)
238
297
  for ax in axes.ravel():
239
298
  ax.axis("off")
240
299
 
@@ -262,45 +321,94 @@ class ClinicalReportGenerator:
262
321
  ax.spines['bottom'].set_color("#888888")
263
322
  ax.spines['bottom'].set_linewidth(0.5)
264
323
 
265
- fig.suptitle("Daily Glucose Profiles", fontsize=12, fontweight="bold")
266
- fig.tight_layout(rect=(0, 0, 1, 0.92))
267
- fig.savefig(output_path, dpi=300)
324
+ fig.tight_layout()
325
+ fig.savefig(output_path, dpi=600, bbox_inches="tight")
326
+ if svg_path is not None:
327
+ fig.savefig(svg_path, format="svg", bbox_inches="tight")
268
328
  plt.close(fig)
269
329
 
270
330
  @staticmethod
271
- def _section_header(pdf: FPDF, title: str) -> None:
272
- pdf.set_fill_color(0, 0, 0)
331
+ def _extract_xai_events(df: pd.DataFrame) -> List[Dict[str, Any]]:
332
+ """Extract explainable-event strings into a structured export list."""
333
+ if "explainable_events" not in df.columns:
334
+ return []
335
+
336
+ events: List[Dict[str, Any]] = []
337
+ for row_index, row in df.dropna(subset=["explainable_events"]).iterrows():
338
+ raw_events = row.get("explainable_events")
339
+ if isinstance(raw_events, str):
340
+ parts = [part.strip() for part in raw_events.split(";") if part.strip()]
341
+ elif isinstance(raw_events, (list, tuple)):
342
+ parts = [str(part).strip() for part in raw_events if str(part).strip()]
343
+ else:
344
+ continue
345
+
346
+ time_minutes = row.get("time_minutes")
347
+ try:
348
+ normalized_time = None if pd.isna(time_minutes) else float(time_minutes)
349
+ except (TypeError, ValueError):
350
+ normalized_time = None
351
+
352
+ for event in parts:
353
+ events.append(
354
+ {
355
+ "row_index": str(row_index),
356
+ "time_minutes": normalized_time,
357
+ "event": event,
358
+ }
359
+ )
360
+ return events
361
+
362
+ @staticmethod
363
+ def _section_header(pdf: FPDF, title: str, width: float = 0) -> None:
364
+ pdf.set_fill_color(35, 55, 67)
273
365
  pdf.set_text_color(255, 255, 255)
274
366
  pdf.set_font("Helvetica", "B", 9)
275
- pdf.cell(0, 6, title, fill=True, new_x=XPos.LMARGIN, new_y=YPos.NEXT)
276
- pdf.set_text_color(0, 0, 0)
367
+ pdf.cell(width, 5.5, title, fill=True, new_x=XPos.LMARGIN, new_y=YPos.NEXT)
368
+ pdf.set_text_color(25, 35, 43)
277
369
 
278
370
  @staticmethod
279
371
  def _range_bar(pdf: FPDF, x: float, y: float, width: float, height: float, summary: Dict[str, Any]) -> None:
280
372
  ranges = [
281
- ("very_high_gt_250", (245, 176, 0), "Very High >250"),
282
- ("high_181_250", (255, 218, 58), "High 181-250"),
283
- ("target_70_180", (98, 181, 86), "Target 70-180"),
284
- ("low_54_69", (211, 47, 47), "Low 54-69"),
285
- ("very_low_lt_54", (124, 20, 20), "Very Low <54"),
373
+ ("very_high_gt_250", (230, 126, 34), "Very High", ">250 mg/dL"),
374
+ ("high_181_250", (248, 200, 48), "High", "181-250 mg/dL"),
375
+ ("target_70_180", (86, 160, 88), "Target Range", "70-180 mg/dL"),
376
+ ("low_54_69", (204, 57, 57), "Low", "54-69 mg/dL"),
377
+ ("very_low_lt_54", (126, 29, 33), "Very Low", "<54 mg/dL"),
286
378
  ]
287
- current_y = y
288
379
  total_h = height
289
380
  pct_map = summary["time_ranges_pct"]
290
- min_visible = 2.0
291
- for key, color, label in ranges:
381
+
382
+ current_y = y
383
+ for key, color, _label, _range_text in ranges:
292
384
  pct = float(pct_map.get(key, 0.0))
293
- part_h = max(min_visible if pct > 0 else 0, total_h * pct / 100.0)
385
+ part_h = total_h * pct / 100.0
294
386
  pdf.set_fill_color(*color)
295
- pdf.rect(x, current_y, width, part_h, style="F")
296
- pdf.set_xy(x + width + 4, current_y)
297
- pdf.set_font("Helvetica", "", 8)
298
- minutes = summary["time_ranges_minutes"].get(key, 0.0)
299
- pdf.cell(0, 4, f"{label}: {pct:.0f}% ({ClinicalReportGenerator._format_duration(minutes)})")
387
+ if part_h > 0:
388
+ pdf.rect(x, current_y, width, part_h, style="F")
300
389
  current_y += part_h
301
- pdf.set_draw_color(0, 0, 0)
390
+ pdf.set_draw_color(85, 95, 103)
302
391
  pdf.rect(x, y, width, height)
303
392
 
393
+ label_x = x + width + 7
394
+ row_y = y - 1
395
+ pdf.set_font("Helvetica", "", 7.3)
396
+ for idx, (key, color, label, range_text) in enumerate(ranges):
397
+ pct = float(pct_map.get(key, 0.0))
398
+ minutes = float(summary["time_ranges_minutes"].get(key, 0.0))
399
+ line_y = row_y + idx * 10.8
400
+ pdf.set_fill_color(*color)
401
+ pdf.rect(label_x, line_y + 1.4, 3.4, 3.4, style="F")
402
+ pdf.set_xy(label_x + 5, line_y)
403
+ pdf.set_font("Helvetica", "B", 7.3)
404
+ pdf.cell(24, 3.6, label)
405
+ pdf.set_font("Helvetica", "", 7.0)
406
+ pdf.cell(22, 3.6, range_text)
407
+ pdf.set_font("Helvetica", "B", 7.3)
408
+ pdf.cell(12, 3.6, f"{pct:.0f}%", align="R")
409
+ pdf.set_font("Helvetica", "", 6.8)
410
+ pdf.cell(0, 3.6, f" ({ClinicalReportGenerator._format_duration(minutes)})")
411
+
304
412
  def generate_agp_pdf(
305
413
  self,
306
414
  simulation_data: pd.DataFrame,
@@ -337,19 +445,44 @@ class ClinicalReportGenerator:
337
445
  pdf.add_page()
338
446
  pdf.set_margins(8, 8, 8)
339
447
 
340
- pdf.set_font("Helvetica", "B", 18)
341
- pdf.cell(88, 8, title)
342
- pdf.set_font("Helvetica", "", 9)
343
- pdf.cell(0, 4, f"Name: {subject_name}", new_x=XPos.LMARGIN, new_y=YPos.NEXT)
344
- pdf.set_x(96)
345
- pdf.cell(0, 4, "Report type: research simulation / educational", new_x=XPos.LMARGIN, new_y=YPos.NEXT)
346
- pdf.ln(3)
448
+ header_y = 8
449
+ title_w = 121
450
+ meta_x = 134
451
+ pdf.set_xy(8, header_y)
452
+ title_size = 15 if len(title) <= 72 else 13
453
+ pdf.set_text_color(25, 35, 43)
454
+ pdf.set_font("Helvetica", "B", title_size)
455
+ pdf.multi_cell(title_w, 6.2, title, align="L")
456
+ title_bottom = pdf.get_y()
457
+
458
+ pdf.set_xy(meta_x, header_y)
459
+ pdf.set_font("Helvetica", "B", 8)
460
+ pdf.set_text_color(25, 35, 43)
461
+ pdf.cell(0, 4, "Name", new_x=XPos.LMARGIN, new_y=YPos.NEXT)
462
+ pdf.set_xy(meta_x, header_y + 4.5)
463
+ pdf.set_font("Helvetica", "", 7.2)
464
+ pdf.multi_cell(64, 3.5, subject_name)
465
+ meta_bottom = pdf.get_y()
466
+ pdf.set_xy(meta_x, max(meta_bottom + 1.5, header_y + 12))
467
+ pdf.set_font("Helvetica", "B", 8)
468
+ pdf.cell(0, 4, "Report type", new_x=XPos.LMARGIN, new_y=YPos.NEXT)
469
+ pdf.set_xy(meta_x, pdf.get_y())
470
+ pdf.set_font("Helvetica", "", 7.2)
471
+ pdf.multi_cell(64, 3.5, "Research simulation / educational")
472
+ meta_bottom = pdf.get_y()
473
+
474
+ header_bottom = max(title_bottom, meta_bottom, header_y + 23)
475
+ pdf.set_draw_color(170, 181, 188)
476
+ pdf.line(8, header_bottom + 1.5, 202, header_bottom + 1.5)
477
+ pdf.set_y(header_bottom + 5)
347
478
 
348
479
  left_x = 8
349
480
  right_x = 112
481
+ left_w = 98
482
+ right_w = 90
350
483
  top_y = pdf.get_y()
351
484
  pdf.set_xy(left_x, top_y)
352
- self._section_header(pdf, "GLUCOSE STATISTICS AND TARGETS")
485
+ self._section_header(pdf, "GLUCOSE STATISTICS AND TARGETS", left_w)
353
486
  pdf.set_font("Helvetica", "", 8)
354
487
  pdf.cell(0, 5, f"Report period: {summary['report_days']} day(s)", new_x=XPos.LMARGIN, new_y=YPos.NEXT)
355
488
  pdf.cell(0, 5, f"Time CGM/simulation active: {summary['data_active_pct']:.1f}%", new_x=XPos.LMARGIN, new_y=YPos.NEXT)
@@ -382,22 +515,36 @@ class ClinicalReportGenerator:
382
515
  pdf.cell(0, 5, str(safety_report.get("bolus_interventions_count", 0)), new_x=XPos.LMARGIN, new_y=YPos.NEXT)
383
516
 
384
517
  pdf.set_xy(right_x, top_y)
385
- self._section_header(pdf, "TIME IN RANGES")
386
- self._range_bar(pdf, right_x + 8, top_y + 12, 14, 58, summary)
518
+ self._section_header(pdf, "TIME IN RANGES", right_w)
519
+ self._range_bar(pdf, right_x + 3, top_y + 12, 13, 55, summary)
387
520
 
388
- pdf.set_xy(8, 94)
521
+ agp_y = max(100, top_y + 74)
522
+ pdf.set_xy(8, agp_y)
389
523
  self._section_header(pdf, "AMBULATORY GLUCOSE PROFILE (AGP-STYLE)")
390
524
  pdf.set_font("Helvetica", "", 7)
525
+ if summary["report_days"] < 2:
526
+ agp_note = (
527
+ "Single-day AGP-style view: percentile bands collapse toward the visible trace. "
528
+ "Use multi-day CGM/simulation data for a full AGP percentile profile."
529
+ )
530
+ else:
531
+ agp_note = (
532
+ "AGP-style summary of glucose values over the report period, with median (50%) "
533
+ "and percentile bands shown as a single modal day."
534
+ )
391
535
  pdf.multi_cell(
392
536
  0,
393
537
  4,
394
- "AGP-style summary of glucose values over the report period, with median (50%) and percentile bands shown as a single modal day.",
538
+ agp_note,
395
539
  )
396
- pdf.image(str(agp_plot), x=12, y=108, w=186)
540
+ agp_image_y = pdf.get_y() + 2
541
+ pdf.image(str(agp_plot), x=12, y=agp_image_y, w=186)
397
542
 
398
- pdf.set_xy(8, 186)
543
+ daily_y = agp_image_y + 78
544
+ pdf.set_xy(8, daily_y)
399
545
  self._section_header(pdf, "DAILY GLUCOSE PROFILES")
400
- pdf.image(str(daily_plot), x=10, y=199, w=190)
546
+ daily_image_y = pdf.get_y() + 4
547
+ pdf.image(str(daily_plot), x=10, y=daily_image_y, w=190)
401
548
 
402
549
  pdf.set_xy(8, 285)
403
550
  pdf.set_font("Helvetica", "I", 7)
@@ -420,8 +567,9 @@ class ClinicalReportGenerator:
420
567
  target_low: float = 70.0,
421
568
  target_high: float = 180.0,
422
569
  summary_json_path: Optional[str] = None,
570
+ export_svg: bool = True,
423
571
  ) -> Dict[str, str]:
424
- """Export AGP-style PNGs and summary JSON without creating a PDF."""
572
+ """Export AGP-style PNG/SVG assets and summary JSON without creating a PDF."""
425
573
  output_path = Path(output_dir)
426
574
  output_path.mkdir(parents=True, exist_ok=True)
427
575
  df = self._prepare_agp_frame(simulation_data)
@@ -430,18 +578,51 @@ class ClinicalReportGenerator:
430
578
 
431
579
  agp_plot = output_path / "agp_profile.png"
432
580
  daily_plot = output_path / "daily_profiles.png"
581
+ agp_svg = output_path / "agp_profile.svg" if export_svg else None
582
+ daily_svg = output_path / "daily_profiles.svg" if export_svg else None
433
583
  summary_file = Path(summary_json_path) if summary_json_path else output_path / "agp_summary.json"
434
584
  summary_file.parent.mkdir(parents=True, exist_ok=True)
435
585
 
436
- self._plot_agp_profile(df, agp_plot, target_low=target_low, target_high=target_high)
437
- self._plot_daily_profiles(df, daily_plot, target_low=target_low, target_high=target_high)
586
+ self._plot_agp_profile(
587
+ df,
588
+ agp_plot,
589
+ target_low=target_low,
590
+ target_high=target_high,
591
+ svg_path=agp_svg,
592
+ )
593
+ self._plot_daily_profiles(
594
+ df,
595
+ daily_plot,
596
+ target_low=target_low,
597
+ target_high=target_high,
598
+ svg_path=daily_svg,
599
+ )
438
600
  summary_file.write_text(json.dumps(summary, indent=2), encoding="utf-8")
439
601
 
440
- return {
602
+ xai_file = output_path / "xai_events.txt"
603
+ xai_json_file = output_path / "xai_events.json"
604
+ xai_events = self._extract_xai_events(df)
605
+
606
+ with open(xai_file, "w", encoding="utf-8") as f:
607
+ f.write("=== IINTS Explainable AI (XAI) Events Log ===\n")
608
+ if xai_events:
609
+ for entry in xai_events:
610
+ f.write(f"- {entry['event']}\n")
611
+ else:
612
+ f.write("No significant XAI events detected.\n")
613
+ xai_json_file.write_text(json.dumps(xai_events, indent=2), encoding="utf-8")
614
+
615
+ outputs = {
441
616
  "agp_profile_png": str(agp_plot),
442
617
  "daily_profiles_png": str(daily_plot),
443
618
  "summary_json": str(summary_file),
619
+ "xai_events_txt": str(xai_file),
620
+ "xai_events_json": str(xai_json_file),
444
621
  }
622
+ if agp_svg is not None and daily_svg is not None:
623
+ outputs["agp_profile_svg"] = str(agp_svg)
624
+ outputs["daily_profiles_svg"] = str(daily_svg)
625
+ return outputs
445
626
 
446
627
  def export_plots(self, simulation_data: pd.DataFrame, output_dir: str) -> Dict[str, str]:
447
628
  output_path = Path(output_dir)
@@ -56,6 +56,7 @@ class AlgorithmMetadata:
56
56
  class AlgorithmResult:
57
57
  """Result of an insulin prediction with uncertainty"""
58
58
  total_insulin_delivered: float
59
+ total_glucagon_delivered_mg: float = 0.0
59
60
  bolus_insulin: float = 0.0
60
61
  basal_insulin: float = 0.0
61
62
  correction_bolus: float = 0.0
@@ -77,6 +78,7 @@ class AlgorithmResult:
77
78
  def to_dict(self) -> Dict:
78
79
  return {
79
80
  'total_insulin_delivered': self.total_insulin_delivered,
81
+ 'total_glucagon_delivered_mg': self.total_glucagon_delivered_mg,
80
82
  'bolus_insulin': self.bolus_insulin,
81
83
  'basal_insulin': self.basal_insulin,
82
84
  'correction_bolus': self.correction_bolus,