patientzero 0.1.0__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 (251) hide show
  1. patientzero-0.1.0/.env.example +25 -0
  2. patientzero-0.1.0/.github/workflows/ci.yml +49 -0
  3. patientzero-0.1.0/.github/workflows/publish.yml +26 -0
  4. patientzero-0.1.0/.gitignore +43 -0
  5. patientzero-0.1.0/CLAUDE.md +50 -0
  6. patientzero-0.1.0/Dockerfile.test +34 -0
  7. patientzero-0.1.0/LICENSE +21 -0
  8. patientzero-0.1.0/PKG-INFO +96 -0
  9. patientzero-0.1.0/README.md +61 -0
  10. patientzero-0.1.0/backend/README.md +71 -0
  11. patientzero-0.1.0/backend/__init__.py +0 -0
  12. patientzero-0.1.0/backend/api/__init__.py +0 -0
  13. patientzero-0.1.0/backend/api/dependencies.py +8 -0
  14. patientzero-0.1.0/backend/api/main.py +81 -0
  15. patientzero-0.1.0/backend/api/routes/__init__.py +0 -0
  16. patientzero-0.1.0/backend/api/routes/agents.py +24 -0
  17. patientzero-0.1.0/backend/api/routes/analysis.py +138 -0
  18. patientzero-0.1.0/backend/api/routes/chat.py +109 -0
  19. patientzero-0.1.0/backend/api/routes/distributions.py +18 -0
  20. patientzero-0.1.0/backend/api/routes/experiments.py +150 -0
  21. patientzero-0.1.0/backend/api/routes/settings.py +12 -0
  22. patientzero-0.1.0/backend/api/routes/simulate.py +229 -0
  23. patientzero-0.1.0/backend/tests/__init__.py +0 -0
  24. patientzero-0.1.0/backend/tests/api/__init__.py +0 -0
  25. patientzero-0.1.0/backend/tests/api/test_chat.py +40 -0
  26. patientzero-0.1.0/backend/tests/api/test_experiments_optimization_targets.py +46 -0
  27. patientzero-0.1.0/backend/tests/api/test_experiments_optimize.py +20 -0
  28. patientzero-0.1.0/backend/tests/api/test_experiments_patch.py +10 -0
  29. patientzero-0.1.0/backend/tests/api/test_sessions.py +64 -0
  30. patientzero-0.1.0/backend/tests/api/test_simulate.py +75 -0
  31. patientzero-0.1.0/backend/tests/conftest.py +65 -0
  32. patientzero-0.1.0/conftest.py +54 -0
  33. patientzero-0.1.0/core/__init__.py +18 -0
  34. patientzero-0.1.0/core/agent.py +42 -0
  35. patientzero-0.1.0/core/agents/__init__.py +3 -0
  36. patientzero-0.1.0/core/agents/base.py +61 -0
  37. patientzero-0.1.0/core/analysis/__init__.py +0 -0
  38. patientzero-0.1.0/core/analysis/coverage.py +123 -0
  39. patientzero-0.1.0/core/config/__init__.py +0 -0
  40. patientzero-0.1.0/core/config/settings.py +41 -0
  41. patientzero-0.1.0/core/db/__init__.py +0 -0
  42. patientzero-0.1.0/core/db/database.py +42 -0
  43. patientzero-0.1.0/core/db/queries/__init__.py +0 -0
  44. patientzero-0.1.0/core/db/queries/sessions.py +69 -0
  45. patientzero-0.1.0/core/db/schema.sql +69 -0
  46. patientzero-0.1.0/core/distribution.py +391 -0
  47. patientzero-0.1.0/core/examples/__init__.py +1 -0
  48. patientzero-0.1.0/core/examples/medical/__init__.py +1 -0
  49. patientzero-0.1.0/core/examples/medical/config.py +32 -0
  50. patientzero-0.1.0/core/examples/medical/distributions.py +157 -0
  51. patientzero-0.1.0/core/examples/medical/prompts.py +25 -0
  52. patientzero-0.1.0/core/examples/medical/run.py +54 -0
  53. patientzero-0.1.0/core/experiment.py +171 -0
  54. patientzero-0.1.0/core/feedback/__init__.py +0 -0
  55. patientzero-0.1.0/core/feedback/feedback.py +141 -0
  56. patientzero-0.1.0/core/judge.py +107 -0
  57. patientzero-0.1.0/core/llm/__init__.py +0 -0
  58. patientzero-0.1.0/core/llm/base.py +8 -0
  59. patientzero-0.1.0/core/llm/claude_cli_provider.py +73 -0
  60. patientzero-0.1.0/core/llm/factory.py +52 -0
  61. patientzero-0.1.0/core/llm/mock.py +65 -0
  62. patientzero-0.1.0/core/llm/openai_provider.py +35 -0
  63. patientzero-0.1.0/core/logger.py +123 -0
  64. patientzero-0.1.0/core/repositories/__init__.py +44 -0
  65. patientzero-0.1.0/core/repositories/base.py +33 -0
  66. patientzero-0.1.0/core/repositories/evaluations.py +131 -0
  67. patientzero-0.1.0/core/repositories/experiments.py +182 -0
  68. patientzero-0.1.0/core/repositories/optimization_targets.py +83 -0
  69. patientzero-0.1.0/core/repositories/simulations.py +117 -0
  70. patientzero-0.1.0/core/sampling.py +10 -0
  71. patientzero-0.1.0/core/services/__init__.py +0 -0
  72. patientzero-0.1.0/core/services/feedback.py +83 -0
  73. patientzero-0.1.0/core/simulation.py +366 -0
  74. patientzero-0.1.0/core/tests/__init__.py +0 -0
  75. patientzero-0.1.0/core/tests/llm/__init__.py +0 -0
  76. patientzero-0.1.0/core/tests/llm/test_factory.py +44 -0
  77. patientzero-0.1.0/core/tests/llm/test_mock.py +33 -0
  78. patientzero-0.1.0/core/tests/test_distribution.py +441 -0
  79. patientzero-0.1.0/core/types/__init__.py +51 -0
  80. patientzero-0.1.0/core/types/analysis.py +28 -0
  81. patientzero-0.1.0/core/types/enums.py +6 -0
  82. patientzero-0.1.0/core/types/events.py +13 -0
  83. patientzero-0.1.0/core/types/feedback.py +54 -0
  84. patientzero-0.1.0/core/types/judge_result.py +44 -0
  85. patientzero-0.1.0/core/types/message.py +7 -0
  86. patientzero-0.1.0/core/types/records.py +257 -0
  87. patientzero-0.1.0/core/types/settings.py +9 -0
  88. patientzero-0.1.0/core/types/simulation.py +9 -0
  89. patientzero-0.1.0/core/types/trace.py +63 -0
  90. patientzero-0.1.0/core/types/transcript.py +33 -0
  91. patientzero-0.1.0/docs/architecture.md +104 -0
  92. patientzero-0.1.0/docs/domain-models.md +178 -0
  93. patientzero-0.1.0/docs/experiment.md +69 -0
  94. patientzero-0.1.0/docs/generators.md +100 -0
  95. patientzero-0.1.0/docs/optimization.md +85 -0
  96. patientzero-0.1.0/evaluations/__init__.py +0 -0
  97. patientzero-0.1.0/evaluations/feedback/RECOMMENDATIONS.md +60 -0
  98. patientzero-0.1.0/evaluations/feedback/RUNBOOK.md +104 -0
  99. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v1/analysis.csv +17 -0
  100. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v1/analysis.json +110 -0
  101. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v1/analysis_experiment.csv +4 -0
  102. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v1/run_summary.json +13 -0
  103. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n10_restart/analysis.csv +55 -0
  104. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n10_restart/analysis.json +110 -0
  105. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n10_restart/analysis_experiment.csv +11 -0
  106. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n10_restart/run_summary.json +13 -0
  107. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n10_stage1/analysis.csv +45 -0
  108. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n10_stage1/analysis.json +110 -0
  109. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n10_stage1/analysis_experiment.csv +9 -0
  110. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n10_stage1/run_summary.json +13 -0
  111. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n20/analysis.csv +23 -0
  112. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n20/analysis.json +110 -0
  113. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n20/run_summary.json +13 -0
  114. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n20_rerun1/analysis.csv +41 -0
  115. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n20_rerun1/analysis.json +110 -0
  116. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n20_rerun1/analysis_experiment.csv +15 -0
  117. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_low_lit_v2_n20_rerun1/run_summary.json +13 -0
  118. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_smoke/analysis.csv +1 -0
  119. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_smoke/analysis.json +8 -0
  120. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_smoke/run_summary.json +13 -0
  121. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_smoke2/analysis.csv +2 -0
  122. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_smoke2/analysis.json +38 -0
  123. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_smoke2/analysis_experiment.csv +2 -0
  124. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_smoke2/run_summary.json +13 -0
  125. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_v1/analysis.csv +14 -0
  126. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_v1/analysis.json +110 -0
  127. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_v1/analysis_experiment.csv +10 -0
  128. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_v1/hypotheses.md +39 -0
  129. patientzero-0.1.0/evaluations/feedback/artifacts/baseline_v1/run_summary.json +13 -0
  130. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v2/analysis.csv +23 -0
  131. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v2/analysis.json +110 -0
  132. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v2/analysis_experiment.csv +7 -0
  133. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v2/compare_vs_baseline.json +39 -0
  134. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v2/compare_vs_baseline.md +34 -0
  135. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v2/run_summary.json +13 -0
  136. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n10_restart/analysis.csv +61 -0
  137. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n10_restart/analysis.json +110 -0
  138. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n10_restart/analysis_experiment.csv +7 -0
  139. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n10_restart/run_summary.json +13 -0
  140. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n10_retry2/analysis.csv +71 -0
  141. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n10_retry2/analysis.json +110 -0
  142. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n10_retry2/analysis_experiment.csv +11 -0
  143. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n10_retry2/compare_vs_baseline.json +55 -0
  144. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n10_retry2/compare_vs_baseline.md +34 -0
  145. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n10_retry2/run_summary.json +13 -0
  146. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n20/analysis.csv +23 -0
  147. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n20/analysis.json +110 -0
  148. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_low_lit_v3_n20/run_summary.json +13 -0
  149. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_v2/analysis.csv +10 -0
  150. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_v2/analysis.json +102 -0
  151. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_v2/analysis_experiment.csv +4 -0
  152. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_v2/compare_vs_baseline.json +61 -0
  153. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_v2/compare_vs_baseline.md +20 -0
  154. patientzero-0.1.0/evaluations/feedback/artifacts/intervention_v2/run_summary.json +13 -0
  155. patientzero-0.1.0/evaluations/judge/__init__.py +0 -0
  156. patientzero-0.1.0/evaluations/judge/cases/__init__.py +0 -0
  157. patientzero-0.1.0/evaluations/judge/cases/bad_explanation.py +43 -0
  158. patientzero-0.1.0/evaluations/judge/cases/cbc_good.py +36 -0
  159. patientzero-0.1.0/evaluations/judge/cases/cbc_poor.py +32 -0
  160. patientzero-0.1.0/evaluations/judge/cases/confidence_gap.py +34 -0
  161. patientzero-0.1.0/evaluations/judge/cases/hba1c_good.py +43 -0
  162. patientzero-0.1.0/evaluations/judge/cases/liver_passive.py +40 -0
  163. patientzero-0.1.0/evaluations/judge/cases/metabolic_high_literacy.py +51 -0
  164. patientzero-0.1.0/evaluations/judge/cases/metformin_mixed.py +39 -0
  165. patientzero-0.1.0/evaluations/judge/cases/patient_contradicts.py +49 -0
  166. patientzero-0.1.0/evaluations/judge/cases/short_exchange.py +29 -0
  167. patientzero-0.1.0/evaluations/judge/output/bad_explanation.json +13 -0
  168. patientzero-0.1.0/evaluations/judge/output/cbc_good.json +13 -0
  169. patientzero-0.1.0/evaluations/judge/output/cbc_poor.json +13 -0
  170. patientzero-0.1.0/evaluations/judge/output/confidence_gap.json +13 -0
  171. patientzero-0.1.0/evaluations/judge/output/hba1c_good.json +13 -0
  172. patientzero-0.1.0/evaluations/judge/output/liver_passive.json +13 -0
  173. patientzero-0.1.0/evaluations/judge/output/metabolic_high_literacy.json +13 -0
  174. patientzero-0.1.0/evaluations/judge/output/metformin_mixed.json +13 -0
  175. patientzero-0.1.0/evaluations/judge/output/patient_contradicts.json +13 -0
  176. patientzero-0.1.0/evaluations/judge/output/short_exchange.json +13 -0
  177. patientzero-0.1.0/frontend/.gitignore +24 -0
  178. patientzero-0.1.0/frontend/README.md +58 -0
  179. patientzero-0.1.0/frontend/components.json +25 -0
  180. patientzero-0.1.0/frontend/eslint.config.js +23 -0
  181. patientzero-0.1.0/frontend/index.html +15 -0
  182. patientzero-0.1.0/frontend/package-lock.json +7772 -0
  183. patientzero-0.1.0/frontend/package.json +45 -0
  184. patientzero-0.1.0/frontend/public/favicon.svg +4 -0
  185. patientzero-0.1.0/frontend/src/App.tsx +34 -0
  186. patientzero-0.1.0/frontend/src/api/client.ts +9 -0
  187. patientzero-0.1.0/frontend/src/api/sessions.ts +412 -0
  188. patientzero-0.1.0/frontend/src/atoms/agents.ts +4 -0
  189. patientzero-0.1.0/frontend/src/atoms/experiment.ts +6 -0
  190. patientzero-0.1.0/frontend/src/atoms/model.ts +7 -0
  191. patientzero-0.1.0/frontend/src/atoms/simulation.ts +20 -0
  192. patientzero-0.1.0/frontend/src/components/agents/AgentConfigView.tsx +154 -0
  193. patientzero-0.1.0/frontend/src/components/chat/ChatInput.tsx +38 -0
  194. patientzero-0.1.0/frontend/src/components/chat/MessageBubble.tsx +22 -0
  195. patientzero-0.1.0/frontend/src/components/chat/MessageList.tsx +31 -0
  196. patientzero-0.1.0/frontend/src/components/chat/ModelSelector.tsx +31 -0
  197. patientzero-0.1.0/frontend/src/components/chat/Sidebar.tsx +59 -0
  198. patientzero-0.1.0/frontend/src/components/experiments/ExperimentHeader.tsx +38 -0
  199. patientzero-0.1.0/frontend/src/components/experiments/ExperimentSummary.tsx +121 -0
  200. patientzero-0.1.0/frontend/src/components/experiments/ExperimentTabs.tsx +40 -0
  201. patientzero-0.1.0/frontend/src/components/experiments/NewExperimentDialog.tsx +101 -0
  202. patientzero-0.1.0/frontend/src/components/experiments/distributions/ConditionalDistributionBlock.tsx +19 -0
  203. patientzero-0.1.0/frontend/src/components/experiments/distributions/DistributionBars.tsx +28 -0
  204. patientzero-0.1.0/frontend/src/components/experiments/distributions/DoctorDistributionCard.tsx +46 -0
  205. patientzero-0.1.0/frontend/src/components/experiments/distributions/PatientDistributionCard.tsx +59 -0
  206. patientzero-0.1.0/frontend/src/components/experiments/tabs/DistributionsTab.tsx +39 -0
  207. patientzero-0.1.0/frontend/src/components/experiments/tabs/ReproducibilityTab.tsx +114 -0
  208. patientzero-0.1.0/frontend/src/components/experiments/tabs/SimulationsTab.tsx +251 -0
  209. patientzero-0.1.0/frontend/src/components/experiments/tabs/TargetsTab.tsx +164 -0
  210. patientzero-0.1.0/frontend/src/components/experiments/targets/CurrentPromptsTabs.tsx +60 -0
  211. patientzero-0.1.0/frontend/src/components/experiments/targets/OptimizationResultCard.tsx +203 -0
  212. patientzero-0.1.0/frontend/src/components/experiments/targets/OptimizationTargetsList.tsx +211 -0
  213. patientzero-0.1.0/frontend/src/components/experiments/targets/OptimizeOptionsForm.tsx +103 -0
  214. patientzero-0.1.0/frontend/src/components/nav/AppSidebar.tsx +110 -0
  215. patientzero-0.1.0/frontend/src/components/nav/NavItem.tsx +41 -0
  216. patientzero-0.1.0/frontend/src/components/ui/badge.tsx +52 -0
  217. patientzero-0.1.0/frontend/src/components/ui/button.tsx +58 -0
  218. patientzero-0.1.0/frontend/src/components/ui/card.tsx +103 -0
  219. patientzero-0.1.0/frontend/src/components/ui/dialog.tsx +101 -0
  220. patientzero-0.1.0/frontend/src/components/ui/input.tsx +20 -0
  221. patientzero-0.1.0/frontend/src/components/ui/progress.tsx +81 -0
  222. patientzero-0.1.0/frontend/src/components/ui/scroll-area.tsx +53 -0
  223. patientzero-0.1.0/frontend/src/components/ui/select.tsx +199 -0
  224. patientzero-0.1.0/frontend/src/components/ui/separator.tsx +25 -0
  225. patientzero-0.1.0/frontend/src/components/ui/sonner.tsx +28 -0
  226. patientzero-0.1.0/frontend/src/components/ui/table.tsx +116 -0
  227. patientzero-0.1.0/frontend/src/components/ui/tabs.tsx +80 -0
  228. patientzero-0.1.0/frontend/src/containers/chat/ChatContainer.tsx +35 -0
  229. patientzero-0.1.0/frontend/src/contexts/ChatContext.tsx +150 -0
  230. patientzero-0.1.0/frontend/src/contexts/ErrorContext.tsx +39 -0
  231. patientzero-0.1.0/frontend/src/index.css +129 -0
  232. patientzero-0.1.0/frontend/src/layouts/AppLayout.tsx +13 -0
  233. patientzero-0.1.0/frontend/src/lib/utils.ts +6 -0
  234. patientzero-0.1.0/frontend/src/main.tsx +13 -0
  235. patientzero-0.1.0/frontend/src/pages/Chat.tsx +10 -0
  236. patientzero-0.1.0/frontend/src/pages/ExperimentsPage.tsx +66 -0
  237. patientzero-0.1.0/frontend/src/pages/SettingsPage.tsx +51 -0
  238. patientzero-0.1.0/frontend/src/pages/SimulationDetailPage.tsx +281 -0
  239. patientzero-0.1.0/frontend/src/pages/agents/AgentPage.tsx +36 -0
  240. patientzero-0.1.0/frontend/src/types/agents.ts +25 -0
  241. patientzero-0.1.0/frontend/src/types/chat.ts +18 -0
  242. patientzero-0.1.0/frontend/src/types/simulation.ts +223 -0
  243. patientzero-0.1.0/frontend/tsconfig.app.json +32 -0
  244. patientzero-0.1.0/frontend/tsconfig.json +13 -0
  245. patientzero-0.1.0/frontend/tsconfig.node.json +26 -0
  246. patientzero-0.1.0/frontend/vite.config.ts +13 -0
  247. patientzero-0.1.0/pyproject.toml +47 -0
  248. patientzero-0.1.0/slides/structure.md +131 -0
  249. patientzero-0.1.0/slides/text.md +303 -0
  250. patientzero-0.1.0/todos.md +76 -0
  251. patientzero-0.1.0/uv.lock +577 -0
@@ -0,0 +1,25 @@
1
+ # LLM Providers (set the ones you need)
2
+ OPENAI_API_KEY=
3
+ ANTHROPIC_API_KEY=
4
+ KIMI_API_KEY=your_kimi_api_key_here
5
+
6
+ # Local LLM (e.g. Ollama)
7
+ LOCAL_LLM_URL=http://localhost:11434
8
+
9
+ # Active provider: mock | openai | claude | kimi | local
10
+ LLM_PROVIDER=mock
11
+
12
+ # Model overrides (optional, defaults set in config/settings.py)
13
+ OPENAI_MODEL=gpt-4o
14
+ ANTHROPIC_MODEL=claude-sonnet-4-20250514
15
+
16
+ # Server
17
+ BACKEND_PORT=8000
18
+ FRONTEND_PORT=5173
19
+
20
+ # Concurrency (see GET /api/settings)
21
+ MAX_CONCURRENT_SIMULATIONS=5
22
+ MAX_CONCURRENT_OPTIMIZATIONS=1
23
+
24
+ # Database
25
+ DB_PATH=patientzero.db
@@ -0,0 +1,49 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, macos-latest, windows-latest]
16
+ python-version: ["3.11", "3.12", "3.13"]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - uses: astral-sh/setup-uv@v6
22
+
23
+ - name: Set up Python ${{ matrix.python-version }}
24
+ run: uv python install ${{ matrix.python-version }}
25
+
26
+ - name: Install dependencies
27
+ run: uv sync --extra dev --extra backend
28
+
29
+ - name: Run tests
30
+ run: uv run python -m pytest core/tests/ backend/tests/ -v
31
+
32
+ build:
33
+ runs-on: ubuntu-latest
34
+ needs: test
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+
38
+ - uses: astral-sh/setup-uv@v6
39
+
40
+ - name: Build package
41
+ run: |
42
+ uv sync --extra dev
43
+ uv run python -m build
44
+
45
+ - name: Upload artifacts
46
+ uses: actions/upload-artifact@v4
47
+ with:
48
+ name: dist
49
+ path: dist/
@@ -0,0 +1,26 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ id-token: write
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: astral-sh/setup-uv@v6
19
+
20
+ - name: Build package
21
+ run: |
22
+ uv sync --extra dev
23
+ uv run python -m build
24
+
25
+ - name: Publish to PyPI
26
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,43 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ *.egg
8
+
9
+ .auriga
10
+
11
+ # Virtual environments
12
+ .venv/
13
+ venv/
14
+ env/
15
+
16
+ # IDE
17
+ .idea/
18
+ .vscode/
19
+ *.swp
20
+ *.swo
21
+
22
+ # OS
23
+ .DS_Store
24
+ Thumbs.db
25
+
26
+ # Environment
27
+ .env
28
+
29
+ # Frontend
30
+ node_modules/
31
+ frontend/dist/
32
+
33
+ # Database
34
+ *.db
35
+ *.db-wal
36
+ *.db-shm
37
+ *.db-journal
38
+
39
+ # Data output
40
+ backend/data/
41
+
42
+ # Simulation logs
43
+ logs/
@@ -0,0 +1,50 @@
1
+ # CLAUDE.md
2
+
3
+ ## Project: PatientZero
4
+
5
+ Simulation system that uses LLM agents to simulate doctor-patient interactions around medical test results. A DoctorAgent explains, a PatientAgent responds, and a JudgeAgent scores comprehension.
6
+
7
+ ## Commands
8
+
9
+ All commands run from project root (`project/`), not `backend/`.
10
+
11
+ ```bash
12
+ uv sync # install python deps
13
+ uv run uvicorn backend.api.main:app --reload # run backend
14
+ uv run python -m pytest core/tests/ backend/tests/ -v # run tests
15
+ uv run python -m evaluations.judge.run [--model M] # run judge evals
16
+ cd frontend && npm install && npm run dev # run frontend (port 5173)
17
+ ```
18
+
19
+ ## Repo Structure
20
+
21
+ ```
22
+ core/ # All domain logic, no FastAPI dependency
23
+ agents/ # DoctorAgent, PatientAgent, JudgeAgent + prompts.py
24
+ llm/ # LLMProvider ABC, factory, Mock/OpenAI/ClaudeCLI providers
25
+ services/ # run_simulation_streaming() orchestration
26
+ simulation/ # Simulation runner (state machine: run/step/pause/resume/stop)
27
+ config/ # Settings, personas, 3 scenarios (CBC, HbA1c, Metformin)
28
+ db/ # SQLite (WAL), schema.sql, queries/{sessions,simulations,evaluations}.py
29
+ types/ # Dataclasses & enums split across modules, re-exported from __init__
30
+ backend/ # FastAPI routes (chat.py, simulate.py), tests
31
+ evaluations/ # Judge eval harness — hardcoded transcripts, auto-discovered
32
+ frontend/ # React 19 + Vite + TS, Tailwind + shadcn/ui
33
+ ```
34
+
35
+ ## Key Patterns
36
+
37
+ - **LLM models** use `"provider:model"` format (e.g. `"kimi:kimi-k2.5"`). Parsed by `parse_provider_model()` in `core/llm/factory.py`.
38
+ - **Providers**: `mock` (testing), `kimi`, `claude` (via CLI), `openai`, `local`. Factory in `core/llm/factory.py`.
39
+ - **Prompts** are format-string templates in `core/agents/prompts.py` (not a directory).
40
+ - **Simulation** alternates doctor/patient turns via SSE streaming. Service layer in `core/services/simulation.py` handles DB persistence.
41
+ - **API** endpoints all prefixed with `/api`. Routes in `backend/api/routes/`.
42
+ - **Environment**: `.env` at project root. Key vars: `LLM_PROVIDER` (default `mock`), `KIMI_API_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`. See `.env.example`.
43
+
44
+ ## Testing
45
+
46
+ Tests split by concern:
47
+ - `core/tests/` — domain logic (agents, analysis, db, feedback, generators, llm, simulation). Mirrors `core/` layout.
48
+ - `backend/tests/api/` — FastAPI route tests only.
49
+
50
+ All use `MockProvider(delay=0)`. Shared fixtures (`db`, `experiment`, `mock_provider`) live in project-root `conftest.py`. The `test_client` fixture lives in `backend/tests/conftest.py`.
@@ -0,0 +1,34 @@
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install the wheel (built on host, copied in)
6
+ COPY dist/*.whl /tmp/
7
+ RUN pip install /tmp/patientzero-*.whl
8
+
9
+ # 1. Verify core imports with only the base dependency (python-dotenv)
10
+ RUN python -c "\
11
+ import core; \
12
+ from core.db.database import Database; \
13
+ d = Database(':memory:'); d.init(); \
14
+ print('PASS: core import + schema init')"
15
+
16
+ # 2. Verify openai is NOT required for core
17
+ RUN python -c "\
18
+ from core.llm.factory import get_provider; \
19
+ p = get_provider('mock'); \
20
+ print('PASS: mock provider works without openai')"
21
+
22
+ # 3. Install with all extras and verify backend imports
23
+ RUN pip install "/tmp/$(ls /tmp/patientzero-*.whl | head -1 | xargs basename)[all]"
24
+ RUN python -c "\
25
+ from backend.api.main import app; \
26
+ print('PASS: backend app imports')"
27
+
28
+ # 4. Copy the full source and run the test suite against the installed package
29
+ COPY . /src
30
+ WORKDIR /src
31
+ RUN pip install pytest pytest-asyncio httpx
32
+ RUN python -m pytest core/tests/ backend/tests/ -v
33
+
34
+ CMD ["echo", "All tests passed"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Surya Mani, Aaron Tamte, Lile Zhang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.4
2
+ Name: patientzero
3
+ Version: 0.1.0
4
+ Summary: A generic framework for LLM-driven patient simulations.
5
+ Author: Surya Mani, Aaron Tamte, Lile Zhang
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Python: >=3.11
15
+ Requires-Dist: python-dotenv>=1.0.0
16
+ Provides-Extra: all
17
+ Requires-Dist: fastapi>=0.115; extra == 'all'
18
+ Requires-Dist: openai>=1.0.0; extra == 'all'
19
+ Requires-Dist: pydantic>=2.0.0; extra == 'all'
20
+ Requires-Dist: sse-starlette>=2.0; extra == 'all'
21
+ Requires-Dist: uvicorn>=0.30; extra == 'all'
22
+ Provides-Extra: backend
23
+ Requires-Dist: fastapi>=0.115; extra == 'backend'
24
+ Requires-Dist: pydantic>=2.0.0; extra == 'backend'
25
+ Requires-Dist: sse-starlette>=2.0; extra == 'backend'
26
+ Requires-Dist: uvicorn>=0.30; extra == 'backend'
27
+ Provides-Extra: dev
28
+ Requires-Dist: build>=1.0.0; extra == 'dev'
29
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
30
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
32
+ Provides-Extra: openai
33
+ Requires-Dist: openai>=1.0.0; extra == 'openai'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # PatientZero
37
+
38
+ Explaining Health: How AI Explanation Styles and Interaction Modalities Affect User Comprehension of Medical Information
39
+
40
+ ## Overview
41
+
42
+ A simulation system that uses LLM-powered agents to simulate doctor-patient interactions around medical test results and evaluate patient comprehension.
43
+
44
+ ## Tech Stack
45
+
46
+ - **Frontend**: React + Vite + TypeScript, Tailwind CSS, shadcn/ui
47
+ - **Backend**: Python, FastAPI, SQLite
48
+ - **LLM**: Abstracted provider layer (Mock, OpenAI, Claude, Local)
49
+
50
+ ## Project Structure
51
+
52
+ ```
53
+ PatientZero/
54
+ ├── core/ # Domain logic (imported by the backend)
55
+ ├── frontend/ # React app
56
+ ├── backend/ # FastAPI HTTP layer
57
+ └── report.txt # Research report
58
+ ```
59
+
60
+ ## Prerequisites
61
+
62
+ - Python 3.12+
63
+ - Node.js 20+
64
+ - [uv](https://docs.astral.sh/uv/) (Python package manager)
65
+
66
+ ## Quick Start
67
+
68
+ 1. Clone the repo:
69
+ ```bash
70
+ git clone https://github.com/tamteaa/PatientZero.git
71
+ cd PatientZero
72
+ ```
73
+
74
+ 2. Set up environment:
75
+ ```bash
76
+ cp .env.example .env
77
+ ```
78
+
79
+ 3. Install Python deps and start the backend (from the repo root so `core` imports resolve):
80
+ ```bash
81
+ uv sync
82
+ uv run uvicorn backend.api.main:app --reload
83
+ ```
84
+
85
+ 4. Start the frontend:
86
+ ```bash
87
+ cd frontend
88
+ npm install
89
+ npm run dev
90
+ ```
91
+
92
+ 5. Open http://localhost:5173
93
+
94
+ ## License
95
+
96
+ MIT
@@ -0,0 +1,61 @@
1
+ # PatientZero
2
+
3
+ Explaining Health: How AI Explanation Styles and Interaction Modalities Affect User Comprehension of Medical Information
4
+
5
+ ## Overview
6
+
7
+ A simulation system that uses LLM-powered agents to simulate doctor-patient interactions around medical test results and evaluate patient comprehension.
8
+
9
+ ## Tech Stack
10
+
11
+ - **Frontend**: React + Vite + TypeScript, Tailwind CSS, shadcn/ui
12
+ - **Backend**: Python, FastAPI, SQLite
13
+ - **LLM**: Abstracted provider layer (Mock, OpenAI, Claude, Local)
14
+
15
+ ## Project Structure
16
+
17
+ ```
18
+ PatientZero/
19
+ ├── core/ # Domain logic (imported by the backend)
20
+ ├── frontend/ # React app
21
+ ├── backend/ # FastAPI HTTP layer
22
+ └── report.txt # Research report
23
+ ```
24
+
25
+ ## Prerequisites
26
+
27
+ - Python 3.12+
28
+ - Node.js 20+
29
+ - [uv](https://docs.astral.sh/uv/) (Python package manager)
30
+
31
+ ## Quick Start
32
+
33
+ 1. Clone the repo:
34
+ ```bash
35
+ git clone https://github.com/tamteaa/PatientZero.git
36
+ cd PatientZero
37
+ ```
38
+
39
+ 2. Set up environment:
40
+ ```bash
41
+ cp .env.example .env
42
+ ```
43
+
44
+ 3. Install Python deps and start the backend (from the repo root so `core` imports resolve):
45
+ ```bash
46
+ uv sync
47
+ uv run uvicorn backend.api.main:app --reload
48
+ ```
49
+
50
+ 4. Start the frontend:
51
+ ```bash
52
+ cd frontend
53
+ npm install
54
+ npm run dev
55
+ ```
56
+
57
+ 5. Open http://localhost:5173
58
+
59
+ ## License
60
+
61
+ MIT
@@ -0,0 +1,71 @@
1
+ # PatientZero Backend
2
+
3
+ FastAPI server with SQLite database and abstracted LLM provider layer.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ uv sync
9
+ cp ../.env.example ../.env # if not done already
10
+ ```
11
+
12
+ ## Running
13
+
14
+ ```bash
15
+ uv run uvicorn api.main:app --reload
16
+
17
+ ```
18
+
19
+ Server runs at http://localhost:8000
20
+
21
+ ## Project Structure
22
+
23
+ ```
24
+ backend/
25
+ ├── api/
26
+ │ ├── main.py # FastAPI app, CORS, lifespan
27
+ │ ├── dependencies.py # Shared db + provider instances
28
+ │ └── routes/
29
+ │ └── chat.py # Chat + session endpoints
30
+ ├── config/
31
+ │ └── settings.py # Environment config
32
+ ├── db/
33
+ │ ├── database.py # Database class (SQLite, raw queries)
34
+ │ ├── schema.sql # Table definitions
35
+ │ └── queries/
36
+ │ └── sessions.py # Session + turn CRUD
37
+ ├── llm/
38
+ │ ├── base.py # Abstract LLMProvider
39
+ │ ├── mock.py # Mock provider (testing)
40
+ │ └── factory.py # Provider factory
41
+ └── pyproject.toml
42
+ ```
43
+
44
+ ## API Endpoints
45
+
46
+ | Method | Path | Description |
47
+ |--------|------|-------------|
48
+ | `POST` | `/api/sessions` | Create a new chat session |
49
+ | `GET` | `/api/sessions` | List all sessions |
50
+ | `GET` | `/api/sessions/{id}` | Get session with turns |
51
+ | `POST` | `/api/chat` | Send message, receive SSE stream |
52
+
53
+ ## LLM Providers
54
+
55
+ Set `LLM_PROVIDER` in `.env`:
56
+
57
+ | Provider | Value | Status |
58
+ |----------|-------|--------|
59
+ | Mock | `mock` | Available |
60
+ | OpenAI | `openai` | Planned |
61
+ | Claude | `claude` | Planned |
62
+ | Local | `local` | Planned |
63
+
64
+ ## Database
65
+
66
+ SQLite with WAL mode. Tables:
67
+
68
+ - `sessions` — chat sessions (id, title, created_at)
69
+ - `turns` — individual messages (session_id, role, content, turn_number)
70
+
71
+ Database file is created automatically on first run.
File without changes
File without changes
@@ -0,0 +1,8 @@
1
+ from core.config.settings import DB_PATH
2
+ from core.db.database import Database
3
+ from core.repositories import RepoSet
4
+ from core.logger import SimulationLogger
5
+
6
+ db = Database(DB_PATH)
7
+ repos = RepoSet.for_db(db)
8
+ logger = SimulationLogger()
@@ -0,0 +1,81 @@
1
+ import logging
2
+ import traceback
3
+ from contextlib import asynccontextmanager
4
+
5
+ from fastapi import FastAPI, Request
6
+ from fastapi.exceptions import HTTPException, RequestValidationError
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from fastapi.responses import JSONResponse
9
+
10
+ logger = logging.getLogger("patientzero.api")
11
+
12
+ from core import Experiment
13
+ from core.config.settings import FRONTEND_URL
14
+ from core.examples.medical.config import MEDICAL_EXAMPLE_CONFIG
15
+ from backend.api.dependencies import db, repos
16
+ from backend.api.routes.agents import router as agents_router
17
+ from backend.api.routes.analysis import router as analysis_router
18
+ from backend.api.routes.chat import router as chat_router
19
+ from backend.api.routes.distributions import router as distributions_router
20
+ from backend.api.routes.experiments import router as experiments_router
21
+ from backend.api.routes.settings import router as settings_router
22
+ from backend.api.routes.simulate import router as simulate_router
23
+
24
+
25
+ @asynccontextmanager
26
+ async def lifespan(app: FastAPI):
27
+ db.init()
28
+ if not repos.experiments.list_all():
29
+ Experiment(MEDICAL_EXAMPLE_CONFIG, repos)
30
+ yield
31
+ db.close()
32
+
33
+
34
+ app = FastAPI(title="PatientZero", lifespan=lifespan)
35
+
36
+ app.add_middleware(
37
+ CORSMiddleware,
38
+ allow_origins=[FRONTEND_URL],
39
+ allow_methods=["*"],
40
+ allow_headers=["*"],
41
+ )
42
+
43
+
44
+ @app.exception_handler(HTTPException)
45
+ async def http_exception_handler(request: Request, exc: HTTPException):
46
+ if exc.status_code >= 400:
47
+ print(
48
+ f"\033[33m[{exc.status_code}]\033[0m {request.method} {request.url.path} "
49
+ f"→ {exc.detail}",
50
+ flush=True,
51
+ )
52
+ return JSONResponse(status_code=exc.status_code, content={"detail": exc.detail})
53
+
54
+
55
+ @app.exception_handler(RequestValidationError)
56
+ async def validation_exception_handler(request: Request, exc: RequestValidationError):
57
+ print(
58
+ f"\033[33m[422]\033[0m {request.method} {request.url.path} → validation error:\n"
59
+ f" {exc.errors()}",
60
+ flush=True,
61
+ )
62
+ return JSONResponse(status_code=422, content={"detail": exc.errors()})
63
+
64
+
65
+ @app.exception_handler(Exception)
66
+ async def unhandled_exception_handler(request: Request, exc: Exception):
67
+ print(
68
+ f"\033[31m[500]\033[0m {request.method} {request.url.path} → {type(exc).__name__}: {exc}",
69
+ flush=True,
70
+ )
71
+ traceback.print_exc()
72
+ return JSONResponse(status_code=500, content={"detail": f"{type(exc).__name__}: {exc}"})
73
+
74
+
75
+ app.include_router(chat_router, prefix="/api")
76
+ app.include_router(simulate_router, prefix="/api")
77
+ app.include_router(analysis_router, prefix="/api")
78
+ app.include_router(settings_router, prefix="/api")
79
+ app.include_router(experiments_router, prefix="/api")
80
+ app.include_router(distributions_router, prefix="/api")
81
+ app.include_router(agents_router, prefix="/api")
File without changes
@@ -0,0 +1,24 @@
1
+ from fastapi import APIRouter, HTTPException
2
+
3
+ from backend.api.dependencies import repos
4
+
5
+ router = APIRouter()
6
+
7
+
8
+ @router.get("/experiments/{exp_id}/agents")
9
+ def get_experiment_agents(exp_id: str):
10
+ experiment = repos.experiments.get(exp_id)
11
+ if experiment is None:
12
+ raise HTTPException(status_code=404, detail="Experiment not found")
13
+ config = experiment.config
14
+ return {
15
+ "agents": [
16
+ {"name": a.name, "prompt": a.prompt, "model": a.model}
17
+ for a in config.agents
18
+ ],
19
+ "judge": {
20
+ "rubric": dict(config.judge.rubric),
21
+ "instructions": config.judge.instructions,
22
+ "model": config.judge.model,
23
+ },
24
+ }