pen-stack 7.0.0__tar.gz → 7.1.1__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 (565) hide show
  1. {pen_stack-7.0.0 → pen_stack-7.1.1}/CHANGELOG.md +44 -0
  2. {pen_stack-7.0.0 → pen_stack-7.1.1}/CITATION.cff +2 -2
  3. {pen_stack-7.0.0 → pen_stack-7.1.1}/PKG-INFO +1 -1
  4. pen_stack-7.1.1/benchmarks/chat_grounding/README.md +26 -0
  5. pen_stack-7.1.1/benchmarks/chat_grounding/SHA256SUMS +1 -0
  6. pen_stack-7.1.1/benchmarks/chat_headtohead/README.md +29 -0
  7. pen_stack-7.1.1/benchmarks/chat_routing/README.md +35 -0
  8. pen_stack-7.1.1/benchmarks/chat_routing/SHA256SUMS +1 -0
  9. pen_stack-7.1.1/benchmarks/chat_safety/README.md +23 -0
  10. pen_stack-7.1.1/benchmarks/chat_safety/SHA256SUMS +1 -0
  11. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/expression/promoters.yaml +2 -2
  12. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/MCP.md +1 -1
  13. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/tutorials/where-can-i-write.md +5 -3
  14. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/__init__.py +1 -1
  15. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/api/manifest.py +9 -0
  16. pen_stack-7.1.1/pen_stack/rag/corpus.py +133 -0
  17. pen_stack-7.1.1/pen_stack/rag/embed.py +79 -0
  18. pen_stack-7.1.1/pen_stack/rag/ground.py +130 -0
  19. pen_stack-7.1.1/pen_stack/rag/retrieve.py +48 -0
  20. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/server/api.py +129 -8
  21. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/web/llm.py +52 -75
  22. pen_stack-7.1.1/pen_stack/web/llm_provider.py +99 -0
  23. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/web/router.py +32 -9
  24. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/web/server.py +2 -1
  25. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack.egg-info/PKG-INFO +1 -1
  26. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack.egg-info/SOURCES.txt +15 -0
  27. pen_stack-7.1.1/prereg/SHA256_LOCK_ws_penchat.json +10 -0
  28. pen_stack-7.1.1/prereg/ws_penchat.yaml +111 -0
  29. {pen_stack-7.0.0 → pen_stack-7.1.1}/pyproject.toml +1 -1
  30. pen_stack-7.1.1/scripts/build_rag_corpus.py +39 -0
  31. {pen_stack-7.0.0 → pen_stack-7.1.1}/LICENSE +0 -0
  32. {pen_stack-7.0.0 → pen_stack-7.1.1}/MANIFEST.in +0 -0
  33. {pen_stack-7.0.0 → pen_stack-7.1.1}/README.md +0 -0
  34. {pen_stack-7.0.0 → pen_stack-7.1.1}/bench/run.py +0 -0
  35. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/delivery/SHA256SUMS +0 -0
  36. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/genome_writing_bench/LEADERBOARD.md +0 -0
  37. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/genome_writing_bench/README.md +0 -0
  38. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/genome_writing_bench/SHA256SUMS +0 -0
  39. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
  40. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/genome_writing_bench/tasks.yaml +0 -0
  41. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/genome_writing_challenge/README.md +0 -0
  42. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/genome_writing_challenge/SUBMISSIONS.md +0 -0
  43. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/loop/SHA256SUMS +0 -0
  44. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/offtarget/SHA256SUMS +0 -0
  45. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/oracle/SHA256SUMS +0 -0
  46. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/position_effect/README.md +0 -0
  47. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/position_effect/SHA256SUMS +0 -0
  48. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/verify/SHA256SUMS +0 -0
  49. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/writer_efficiency/README.md +0 -0
  50. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/writer_efficiency/SHA256SUMS +0 -0
  51. {pen_stack-7.0.0 → pen_stack-7.1.1}/benchmarks/writespec/SHA256SUMS +0 -0
  52. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/aav_serotype_tropism.yaml +0 -0
  53. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/antipeg.yaml +0 -0
  54. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/atlas_families.yaml +0 -0
  55. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/bridge_offtarget_profile.yaml +0 -0
  56. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/calibration/preexisting_nab_independent.yaml +0 -0
  57. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/capsid_epitope_oracle.yaml +0 -0
  58. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/capsid_sequences.fasta +0 -0
  59. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/cargo_polish.yaml +0 -0
  60. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/cell_types.yaml +0 -0
  61. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/datasets.yaml +0 -0
  62. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/delivery_constraints.yaml +0 -0
  63. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/delivery_rules.yaml +0 -0
  64. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/delivery_vehicles.yaml +0 -0
  65. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/expression/modifiers.yaml +0 -0
  66. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/gates_v3.yaml +0 -0
  67. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/genotoxicity_oracle.yaml +0 -0
  68. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/gsh_validated_heldout.yaml +0 -0
  69. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/intent_weights.yaml +0 -0
  70. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/known_unknowns.yaml +0 -0
  71. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/llm.yaml +0 -0
  72. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/metric_guide.yaml +0 -0
  73. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/mhc_epitope_oracle.yaml +0 -0
  74. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/monitor_queries.yaml +0 -0
  75. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/oracles/execution.yaml +0 -0
  76. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/oracles/reliability.yaml +0 -0
  77. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/oracles/scope_cards.yaml +0 -0
  78. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/rules/delivery.yaml +0 -0
  79. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/rules/fold.yaml +0 -0
  80. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/rules/multiplex.yaml +0 -0
  81. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/rules/payload.yaml +0 -0
  82. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/rules/reachability.yaml +0 -0
  83. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/safety/hazard_registry.yaml +0 -0
  84. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/safety/policy.yaml +0 -0
  85. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/safety/probes.yaml +0 -0
  86. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/score_axes.yaml +0 -0
  87. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/seroprevalence.yaml +0 -0
  88. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/target_sites.yaml +0 -0
  89. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/universe_crosswalk.yaml +0 -0
  90. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/write_types.yaml +0 -0
  91. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/writer_sequences.fasta +0 -0
  92. {pen_stack-7.0.0 → pen_stack-7.1.1}/configs/wtkb_curated.yaml +0 -0
  93. {pen_stack-7.0.0 → pen_stack-7.1.1}/data/curated/bridge_offtarget_energetics.json +0 -0
  94. {pen_stack-7.0.0 → pen_stack-7.1.1}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
  95. {pen_stack-7.0.0 → pen_stack-7.1.1}/data/curated/gene_coords.parquet +0 -0
  96. {pen_stack-7.0.0 → pen_stack-7.1.1}/data/curated/unified_editor_universe.parquet +0 -0
  97. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/BACKLOG.md +0 -0
  98. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/DEPLOY.md +0 -0
  99. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/INFRA.md +0 -0
  100. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/RELEASING.md +0 -0
  101. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/REPRO.md +0 -0
  102. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/STABILITY.md +0 -0
  103. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/agent.md +0 -0
  104. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/alphagenome_feasibility.md +0 -0
  105. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/autonomy.md +0 -0
  106. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/benchmark_circularity.md +0 -0
  107. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/biosecurity.md +0 -0
  108. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/build_interface.md +0 -0
  109. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/cards/atlas.md +0 -0
  110. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/cards/durability.md +0 -0
  111. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/cards/offtarget_data.md +0 -0
  112. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/cards/position_effect_data.md +0 -0
  113. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/cards/safety.md +0 -0
  114. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/cards/writer_efficiency_data.md +0 -0
  115. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/challenge.md +0 -0
  116. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/closed_loop.md +0 -0
  117. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/co_scientist.md +0 -0
  118. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/co_scientist_loop.md +0 -0
  119. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/delivery.md +0 -0
  120. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/delivery_immunology.md +0 -0
  121. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/delivery_recommender.md +0 -0
  122. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/digital_twin.md +0 -0
  123. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/dissemination.md +0 -0
  124. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/environment.md +0 -0
  125. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/experiment_design.md +0 -0
  126. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/generative_design.md +0 -0
  127. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/immune_profiler.md +0 -0
  128. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/index.md +0 -0
  129. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/integrations.md +0 -0
  130. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/live_oracles.md +0 -0
  131. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/mechanistic_constraints.md +0 -0
  132. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/offtarget.md +0 -0
  133. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/oracle_mesh.md +0 -0
  134. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/oracles.md +0 -0
  135. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/position_effect.md +0 -0
  136. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/positioning.md +0 -0
  137. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/private_data_formats.md +0 -0
  138. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/quickstart.md +0 -0
  139. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/responsible_use.md +0 -0
  140. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/rule_spec.md +0 -0
  141. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/rules.md +0 -0
  142. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/scope.md +0 -0
  143. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/scorecard.md +0 -0
  144. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/tpe_bench.md +0 -0
  145. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/tutorials/compare-families.md +0 -0
  146. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/tutorials/score-deliverability.md +0 -0
  147. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/tutorials/which-writer-reaches-locus.md +0 -0
  148. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/uncertainty.md +0 -0
  149. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/verify.md +0 -0
  150. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/verify_service.md +0 -0
  151. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/world_model.md +0 -0
  152. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/writer_efficiency.md +0 -0
  153. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/writer_verification.md +0 -0
  154. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/writespec_bench.md +0 -0
  155. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/writespec_profile.md +0 -0
  156. {pen_stack-7.0.0 → pen_stack-7.1.1}/docs/wtkb.md +0 -0
  157. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/_resources.py +0 -0
  158. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/active/__init__.py +0 -0
  159. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/active/acquire.py +0 -0
  160. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/active/brains.py +0 -0
  161. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/active/campaign.py +0 -0
  162. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/active/design.py +0 -0
  163. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/active/validate.py +0 -0
  164. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/adapt/__init__.py +0 -0
  165. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/adapt/finetune.py +0 -0
  166. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/adapt/ingest.py +0 -0
  167. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/adapt/pipeline.py +0 -0
  168. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/adapt/recalibrate.py +0 -0
  169. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/adapt/report.py +0 -0
  170. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/__init__.py +0 -0
  171. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/cite.py +0 -0
  172. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/co_scientist.py +0 -0
  173. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/epistemic.py +0 -0
  174. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/guardrails.py +0 -0
  175. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/mcp_server.py +0 -0
  176. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/orchestrator.py +0 -0
  177. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/orchestrator_live.py +0 -0
  178. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/pen_agent.py +0 -0
  179. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/scope.py +0 -0
  180. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/agent/tools.py +0 -0
  181. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/api/__init__.py +0 -0
  182. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/__init__.py +0 -0
  183. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/build_wtkb.py +0 -0
  184. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/crosslink.py +0 -0
  185. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/expand.py +0 -0
  186. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/guide_design.py +0 -0
  187. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/schema.py +0 -0
  188. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/scorecard.py +0 -0
  189. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/universe.py +0 -0
  190. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/variant_propose.py +0 -0
  191. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/writer_efficiency.py +0 -0
  192. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/writer_predict.py +0 -0
  193. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/writer_recommend.py +0 -0
  194. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/atlas/writer_verify.py +0 -0
  195. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/bridge/__init__.py +0 -0
  196. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/bridge/activity.py +0 -0
  197. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/bridge/cli.py +0 -0
  198. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/bridge/fold_qc.py +0 -0
  199. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/bridge/guide_qc.py +0 -0
  200. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/bridge/ingest.py +0 -0
  201. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/bridge/offtarget.py +0 -0
  202. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/bridge/offtarget_energetics.py +0 -0
  203. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/bridge/ortholog_screen.py +0 -0
  204. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/bridge/pipeline.py +0 -0
  205. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/build/__init__.py +0 -0
  206. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/build/cloudlab.py +0 -0
  207. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/build/ingest.py +0 -0
  208. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/build/protocol.py +0 -0
  209. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/build/simlab.py +0 -0
  210. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/cli.py +0 -0
  211. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/data/__init__.py +0 -0
  212. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/data/encode.py +0 -0
  213. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/data/genome.py +0 -0
  214. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/data/ingest_chromatin.py +0 -0
  215. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/data/ingest_integration.py +0 -0
  216. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/data/ingest_safety_annot.py +0 -0
  217. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/data/ingest_trip.py +0 -0
  218. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/design/__init__.py +0 -0
  219. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/design/capsid_generate.py +0 -0
  220. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/design/generate.py +0 -0
  221. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/design/pareto.py +0 -0
  222. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/design/space.py +0 -0
  223. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/design/writer_variants.py +0 -0
  224. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/env/__init__.py +0 -0
  225. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/env/genome_writing_env.py +0 -0
  226. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/env/policies.py +0 -0
  227. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/graph/__init__.py +0 -0
  228. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/graph/build.py +0 -0
  229. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/graph/cell_types.py +0 -0
  230. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/graph/ingest.py +0 -0
  231. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/graph/query.py +0 -0
  232. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/graph/schema.py +0 -0
  233. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/loop/__init__.py +0 -0
  234. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/loop/continual.py +0 -0
  235. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/loop/cycle.py +0 -0
  236. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/loop/drift.py +0 -0
  237. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/mech/__init__.py +0 -0
  238. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/mech/classify_atlas.py +0 -0
  239. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/mech/whitelist.py +0 -0
  240. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/monitor/__init__.py +0 -0
  241. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/monitor/europepmc.py +0 -0
  242. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/monitor/run.py +0 -0
  243. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/monitor/triage.py +0 -0
  244. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/__init__.py +0 -0
  245. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/affinity.py +0 -0
  246. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/cache.py +0 -0
  247. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/energetics.py +0 -0
  248. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/genome.py +0 -0
  249. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/protein_design.py +0 -0
  250. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/reliability.py +0 -0
  251. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/rna.py +0 -0
  252. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/schema.py +0 -0
  253. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/status.py +0 -0
  254. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/structure.py +0 -0
  255. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/structure_run.py +0 -0
  256. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/oracles/vcell.py +0 -0
  257. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/__init__.py +0 -0
  258. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/ada_risk.py +0 -0
  259. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/antipeg_oracle.py +0 -0
  260. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/capsid_epitope_oracle.py +0 -0
  261. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/cargo.py +0 -0
  262. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/cargo_polish.py +0 -0
  263. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/delivery.py +0 -0
  264. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/delivery_constraints.py +0 -0
  265. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/delivery_immune.py +0 -0
  266. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/delivery_immunology.py +0 -0
  267. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/delivery_predict.py +0 -0
  268. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/delivery_vehicles.py +0 -0
  269. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/genotoxicity_oracle.py +0 -0
  270. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/immune_mhc2.py +0 -0
  271. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/immune_profile.py +0 -0
  272. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/innate_sensing.py +0 -0
  273. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/multiplex.py +0 -0
  274. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/optimize.py +0 -0
  275. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/pipeline.py +0 -0
  276. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/report.py +0 -0
  277. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/router.py +0 -0
  278. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/seroprevalence_oracle.py +0 -0
  279. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/planner/target_site.py +0 -0
  280. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/rag/__init__.py +0 -0
  281. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/rag/index.py +0 -0
  282. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/rag/llm.py +0 -0
  283. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/rag/qa.py +0 -0
  284. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/rules/__init__.py +0 -0
  285. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/rules/evaluators.py +0 -0
  286. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/rules/loader.py +0 -0
  287. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/rules/schema.py +0 -0
  288. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/rules/solver.py +0 -0
  289. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/rules/spec.py +0 -0
  290. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/safety/__init__.py +0 -0
  291. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/safety/audit.py +0 -0
  292. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/safety/gate.py +0 -0
  293. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/safety/policy.py +0 -0
  294. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/safety/redteam.py +0 -0
  295. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/safety/registry.py +0 -0
  296. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/safety/screen.py +0 -0
  297. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/safety/standards.py +0 -0
  298. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/score/__init__.py +0 -0
  299. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/score/recalibrate.py +0 -0
  300. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/score/therapeutic.py +0 -0
  301. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/server/__init__.py +0 -0
  302. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/__init__.py +0 -0
  303. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/clarify.py +0 -0
  304. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/extract.py +0 -0
  305. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/resolvers/__init__.py +0 -0
  306. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/resolvers/cell.py +0 -0
  307. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/resolvers/chem.py +0 -0
  308. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/resolvers/feature.py +0 -0
  309. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/resolvers/gene.py +0 -0
  310. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/resolvers/locus.py +0 -0
  311. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/resolvers/phenotype.py +0 -0
  312. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/satisfy.py +0 -0
  313. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/service.py +0 -0
  314. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/spec/writespec.py +0 -0
  315. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/twin/__init__.py +0 -0
  316. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/twin/calibrate.py +0 -0
  317. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/twin/data/__init__.py +0 -0
  318. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/twin/data/position_effect.py +0 -0
  319. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/twin/mechanistic.py +0 -0
  320. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/twin/outcome.py +0 -0
  321. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/twin/position_effect.py +0 -0
  322. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/ui/__init__.py +0 -0
  323. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/ui/app.py +0 -0
  324. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/__init__.py +0 -0
  325. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/adapt_demo.py +0 -0
  326. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/agent_eval.py +0 -0
  327. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
  328. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/bench_coscientist_tasks.py +0 -0
  329. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/bench_graph_tasks.py +0 -0
  330. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/bench_rule_tasks.py +0 -0
  331. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/bench_trust_tasks.py +0 -0
  332. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/bench_writetype_tasks.py +0 -0
  333. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/blind_gsh_discovery.py +0 -0
  334. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/cargo_directionality.py +0 -0
  335. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/closed_loop.py +0 -0
  336. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/durability_baselines.py +0 -0
  337. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/experiment_design.py +0 -0
  338. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/expr_controls.py +0 -0
  339. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/forward_hypotheses.py +0 -0
  340. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/generative_design.py +0 -0
  341. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/guide_qc_demo.py +0 -0
  342. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/heldout_celltype_expr.py +0 -0
  343. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/immune_calibration.py +0 -0
  344. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/intent_specification.py +0 -0
  345. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/known_biology_expr.py +0 -0
  346. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
  347. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/out_of_scope_refusal.py +0 -0
  348. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/outcome_calibration.py +0 -0
  349. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/outcome_prediction.py +0 -0
  350. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/paper3_benchmark.py +0 -0
  351. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/paper4_real_validation.py +0 -0
  352. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/paper4_validation.py +0 -0
  353. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/protocol_safety.py +0 -0
  354. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/safety_screening.py +0 -0
  355. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/selective_prediction.py +0 -0
  356. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/seq_vs_measured.py +0 -0
  357. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/target_site_controls.py +0 -0
  358. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/uncertainty_eval.py +0 -0
  359. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/ungrounded_baseline.py +0 -0
  360. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/within_locus_ranking.py +0 -0
  361. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/validate/writer_recovery.py +0 -0
  362. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/verify/__init__.py +0 -0
  363. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/verify/proof.py +0 -0
  364. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/verify/schema.py +0 -0
  365. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/verify/service.py +0 -0
  366. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/web/__init__.py +0 -0
  367. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/web/guide.py +0 -0
  368. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/web/tools.py +0 -0
  369. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/__init__.py +0 -0
  370. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/chromatin_seq.py +0 -0
  371. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/durability.py +0 -0
  372. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/export_tracks.py +0 -0
  373. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/features.py +0 -0
  374. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/gsh_baseline.py +0 -0
  375. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/mesh_features.py +0 -0
  376. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/offtarget_assay.py +0 -0
  377. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/offtarget_data.py +0 -0
  378. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/offtarget_predict.py +0 -0
  379. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/ood.py +0 -0
  380. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/providers.py +0 -0
  381. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/safety.py +0 -0
  382. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/structure3d.py +0 -0
  383. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/uncertainty.py +0 -0
  384. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack/wgenome/writability.py +0 -0
  385. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack.egg-info/dependency_links.txt +0 -0
  386. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack.egg-info/entry_points.txt +0 -0
  387. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack.egg-info/requires.txt +0 -0
  388. {pen_stack-7.0.0 → pen_stack-7.1.1}/pen_stack.egg-info/top_level.txt +0 -0
  389. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_phase0.json +0 -0
  390. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_phase1_5.json +0 -0
  391. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_phase2.json +0 -0
  392. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_phase3.json +0 -0
  393. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_a.json +0 -0
  394. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_acq.json +0 -0
  395. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_aldesign.json +0 -0
  396. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_alvalidate.json +0 -0
  397. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
  398. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_b.json +0 -0
  399. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_ba.json +0 -0
  400. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
  401. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
  402. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_bench.json +0 -0
  403. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_c.json +0 -0
  404. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_cal.json +0 -0
  405. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_calib.json +0 -0
  406. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_challenge.json +0 -0
  407. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_chat.json +0 -0
  408. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_cite.json +0 -0
  409. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_closedloop.json +0 -0
  410. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_continual.json +0 -0
  411. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_cosci2.json +0 -0
  412. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_crit.json +0 -0
  413. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_ct.json +0 -0
  414. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_d.json +0 -0
  415. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_delivery.json +0 -0
  416. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_drift.json +0 -0
  417. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_e.json +0 -0
  418. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_env.json +0 -0
  419. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_ep.json +0 -0
  420. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_epitope.json +0 -0
  421. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_expr2.json +0 -0
  422. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_f.json +0 -0
  423. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_frontend.json +0 -0
  424. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_g.json +0 -0
  425. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_gen.json +0 -0
  426. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_genotox.json +0 -0
  427. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_graph.json +0 -0
  428. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_h.json +0 -0
  429. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_hybrid.json +0 -0
  430. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_immune.json +0 -0
  431. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_immune2.json +0 -0
  432. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_ingest.json +0 -0
  433. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_innate.json +0 -0
  434. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_loop.json +0 -0
  435. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_manifest.json +0 -0
  436. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_mc.json +0 -0
  437. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_mcp.json +0 -0
  438. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_mech.json +0 -0
  439. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_mon.json +0 -0
  440. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_o.json +0 -0
  441. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_offtarget.json +0 -0
  442. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_openapi.json +0 -0
  443. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_oracle.json +0 -0
  444. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_orch.json +0 -0
  445. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_outcome.json +0 -0
  446. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_pareto.json +0 -0
  447. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_peg.json +0 -0
  448. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_plan.json +0 -0
  449. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_policy.json +0 -0
  450. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_profile.json +0 -0
  451. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_proto.json +0 -0
  452. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_r.json +0 -0
  453. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_redteam.json +0 -0
  454. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_route.json +0 -0
  455. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_screen.json +0 -0
  456. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_seroprev.json +0 -0
  457. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_simlab.json +0 -0
  458. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_twincal.json +0 -0
  459. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_uq.json +0 -0
  460. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_v.json +0 -0
  461. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_vcell.json +0 -0
  462. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_verify.json +0 -0
  463. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_writer.json +0 -0
  464. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_writespec.json +0 -0
  465. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/SHA256_LOCK_ws_wv.json +0 -0
  466. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/paper1.yaml +0 -0
  467. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/paper2.yaml +0 -0
  468. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/paper3.yaml +0 -0
  469. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/paper4.yaml +0 -0
  470. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/phase0.yaml +0 -0
  471. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_a.yaml +0 -0
  472. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_acq.yaml +0 -0
  473. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_aldesign.yaml +0 -0
  474. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_alvalidate.yaml +0 -0
  475. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_atlas.yaml +0 -0
  476. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_b.yaml +0 -0
  477. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_ba.yaml +0 -0
  478. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_ba_v33.yaml +0 -0
  479. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_ba_v45.yaml +0 -0
  480. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_bench.yaml +0 -0
  481. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_c.yaml +0 -0
  482. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_cal.yaml +0 -0
  483. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_calib.yaml +0 -0
  484. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_challenge.yaml +0 -0
  485. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_chat.yaml +0 -0
  486. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_cite.yaml +0 -0
  487. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_closedloop.yaml +0 -0
  488. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_continual.yaml +0 -0
  489. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_cosci2.yaml +0 -0
  490. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_crit.yaml +0 -0
  491. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_ct.yaml +0 -0
  492. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_d.yaml +0 -0
  493. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_delivery.yaml +0 -0
  494. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_drift.yaml +0 -0
  495. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_e.yaml +0 -0
  496. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_env.yaml +0 -0
  497. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_ep.yaml +0 -0
  498. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_epitope.yaml +0 -0
  499. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_expr2.yaml +0 -0
  500. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_f.yaml +0 -0
  501. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_frontend.yaml +0 -0
  502. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_g.yaml +0 -0
  503. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_gen.yaml +0 -0
  504. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_genotox.yaml +0 -0
  505. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_graph.yaml +0 -0
  506. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_h.yaml +0 -0
  507. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_hybrid.yaml +0 -0
  508. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_immune.yaml +0 -0
  509. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_immune2.yaml +0 -0
  510. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_ingest.yaml +0 -0
  511. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_innate.yaml +0 -0
  512. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_loop.yaml +0 -0
  513. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_manifest.yaml +0 -0
  514. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_mc.yaml +0 -0
  515. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_mcp.yaml +0 -0
  516. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_mech.yaml +0 -0
  517. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_mon.yaml +0 -0
  518. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_o.yaml +0 -0
  519. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_offtarget.yaml +0 -0
  520. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_openapi.yaml +0 -0
  521. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_oracle.yaml +0 -0
  522. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_orch.yaml +0 -0
  523. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_outcome.yaml +0 -0
  524. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_pareto.yaml +0 -0
  525. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_peg.yaml +0 -0
  526. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_plan.yaml +0 -0
  527. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_policy.yaml +0 -0
  528. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_profile.yaml +0 -0
  529. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_proto.yaml +0 -0
  530. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_r.yaml +0 -0
  531. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_redteam.yaml +0 -0
  532. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_route.yaml +0 -0
  533. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_screen.yaml +0 -0
  534. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_seroprev.yaml +0 -0
  535. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_simlab.yaml +0 -0
  536. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_twincal.yaml +0 -0
  537. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_uq.yaml +0 -0
  538. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_v.yaml +0 -0
  539. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_vcell.yaml +0 -0
  540. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_verify.yaml +0 -0
  541. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_writer.yaml +0 -0
  542. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_writespec.yaml +0 -0
  543. {pen_stack-7.0.0 → pen_stack-7.1.1}/prereg/ws_wv.yaml +0 -0
  544. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/build_capsid_fitness.py +0 -0
  545. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/calibrate_immune_axes.py +0 -0
  546. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/fetch_licensed_sources.py +0 -0
  547. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/offtarget_chromatin_incremental.py +0 -0
  548. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/offtarget_chromatin_matched.py +0 -0
  549. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/offtarget_chromatin_validation.py +0 -0
  550. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p1_build_atlas.py +0 -0
  551. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p1_build_durability.py +0 -0
  552. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p1_build_position_effect.py +0 -0
  553. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p1_build_writer_eff.py +0 -0
  554. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p1_export_tracks.py +0 -0
  555. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p1_safety_concordance.py +0 -0
  556. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p1_train_safety.py +0 -0
  557. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p1_validation_report.py +0 -0
  558. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p2_build_atlas.py +0 -0
  559. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p3_benchmark_report.py +0 -0
  560. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p4_genome_scan.py +0 -0
  561. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p52_build_genotox_oracle.py +0 -0
  562. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/p53_build_epitope_oracle.py +0 -0
  563. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/ws_b_report.py +0 -0
  564. {pen_stack-7.0.0 → pen_stack-7.1.1}/scripts/ws_c_report.py +0 -0
  565. {pen_stack-7.0.0 → pen_stack-7.1.1}/setup.cfg +0 -0
@@ -3,6 +3,50 @@
3
3
  All notable changes to PEN-STACK are documented here. This file follows
4
4
  [Keep a Changelog](https://keepachangelog.com/).
5
5
 
6
+ ## [7.1.1] - 2026-06-24 - PEN-CHAT: General-lane helpfulness fix + benchmark-validity correction
7
+
8
+ ### Fixed
9
+ - The General lane over-abstained: it declined simple/social ("hi") and general-knowledge ("what is DNA")
10
+ questions because P-WS1 made corpus retrieval a GATE on the whole lane. Retrieval is now **additive**
11
+ (`pen_stack/rag/ground.py`): the lane ANSWERS general + social questions, clearly labelled
12
+ "general - not PEN-STACK-verified" (a labelled general answer is honest, not a fabrication); a corpus hit
13
+ upgrades to "literature-cited"; abstention is reserved for a SPECIFIC unsourceable empirical claim. The "general"
14
+ provenance chip is restored in the web chat.
15
+
16
+ ### Changed
17
+ - Benchmark-validity correction: the head-to-head headline moves from "answers-without-grounding" (which rewarded
18
+ over-abstention) to the **false-grounding rate** (a non-engine fact presented as a PEN-STACK result; 0.0 by
19
+ construction). Abstention is re-scoped to specific unsourceable empirical claims; the routing/grounding/safety
20
+ sets now include positive general + social cases that require a helpful, labelled answer (a regression guard).
21
+ `prereg/ws_penchat.yaml` amended + re-locked accordingly.
22
+
23
+ ## [7.1.0] - 2026-06-24 - PEN-CHAT: the grounded conversational system
24
+
25
+ Consolidates the four-lane chat (WS-HYBRID) with PEN-RAG into a fully-grounded, provenance-partitioned
26
+ conversational agent: every surfaced fact is engine-computed or retrieved-and-cited, no claim originates from
27
+ model weights, and the property is MEASURED, not asserted. The LLM is a swappable, non-load-bearing narrator.
28
+
29
+ ### Added
30
+ - **Grounded General lane (PEN-RAG)** (`pen_stack/rag/{corpus,embed,retrieve,ground}.py`). The general lane no
31
+ longer answers from unsourced trained knowledge: it retrieves over a provenance-tagged corpus
32
+ (`data/rag_corpus.parquet`, built only from real DOI-backed repo content) with a committed embedding matrix
33
+ (`nomic-embed-text` via Ollama; exact cosine, no FAISS at this scale) and a deterministic lexical fallback,
34
+ answers under citation-or-silence, and **abstains** below a retrieval-confidence threshold. General answers are
35
+ labelled `literature-cited` or `abstained`, never a PEN-STACK-computed result.
36
+ - **Swappable LLM provider abstraction** (`pen_stack/web/llm_provider.py`): one interface over local Ollama / cloud
37
+ Nemotron; the grounded result (lane, provenance, sources, numbers) is invariant to the provider.
38
+ - **Lane + provenance memory**: a follow-up to a grounded answer stays grounded.
39
+ - **Evaluation suite** measuring the no-fabrication claim: `benchmarks/chat_{routing,grounding,safety,headtohead}`
40
+ (routing-safety 0.0, citation coverage 1.0, 0 unsupported claims, false-grounding 0.0, dual-use refusal 1.0,
41
+ injection-hold 1.0). Pre-registered + SHA-locked in `prereg/ws_penchat.yaml`.
42
+ - The chat is registered as a capability (`chat_answer`, `fabricates: False`); provenance / citation / abstention /
43
+ refusal chips in the web chat.
44
+
45
+ ### Changed
46
+ - The pre-route biosecurity screen now escalates a build/express intent over any flagged dual-use hazard term even
47
+ when the specific agent is not catalogued; the router was made conservative so a write request never leaks to the
48
+ ungrounded lane.
49
+
6
50
  ## [7.0.0] - 2026-06-22 - Closed loop: a biosecurity-gated, Level-3 self-driving-lab engine
7
51
 
8
52
  The capstone. Turns the in-silico closed loop into a genome-writing-specific, biosecurity-gated, autonomy-honest
@@ -1,8 +1,8 @@
1
1
  cff-version: 1.2.0
2
2
  message: "If you use PEN-STACK, please cite it as below."
3
3
  title: "PEN-STACK: open infrastructure for genome writing"
4
- version: 7.0.0
5
- date-released: 2026-06-20
4
+ version: 7.1.1
5
+ date-released: 2026-06-24
6
6
  authors:
7
7
  - family-names: "Mahaboob Ali"
8
8
  given-names: "Anees Ahmed"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pen-stack
3
- Version: 7.0.0
3
+ Version: 7.1.1
4
4
  Summary: Open infrastructure for genome writing: the Writable Genome atlas, the Writer Atlas, and the Write Planner.
5
5
  Author-email: Anees Ahmed Mahaboob Ali <ahmedaneesm@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,26 @@
1
+ # Chat Groundedness Benchmark (PEN-CHAT P-WS5, re-scoped v7.1.1)
2
+
3
+ Measures the corrected groundedness properties of the chat. The lane ANSWERS general and social questions
4
+ (labelled), grounds genome-writing questions in the cited corpus, and abstains ONLY on a specific unsourceable
5
+ empirical claim. The honesty metric is **false-grounding**, not answer-suppression. Deterministic (lexical
6
+ retriever, no LLM) -> reproducible in CI.
7
+
8
+ ## Sealed set
9
+ `cases.jsonl` (SHA-locked) - `cited` (in-corpus genome-writing) + `general` (general knowledge) + `social` +
10
+ `abstain` (specific unsourceable empirical) cases.
11
+
12
+ ## Result (`result.json`)
13
+ | metric | value |
14
+ |---|---|
15
+ | citation_coverage (on cited answers) | **1.0** |
16
+ | unsupported_claims_through_guard | **0** |
17
+ | **false_grounding_rate** (a general fact mislabelled as a PEN-STACK result) | **0.0** |
18
+ | **helpful_answer_rate** (general + social ANSWERED, not abstained) | **1.0** |
19
+ | abstention_on_specific_unsourceable | **1.0** |
20
+ | per-gold pass | cited 6/6 · general 4/4 · social 2/2 · abstain 2/2 |
21
+
22
+ The regression guard `helpful_answer_rate = 1.0` exists because v7.1.0 over-abstained (it declined "hi" and "what
23
+ is DNA"); v7.1.1 made retrieval additive, so general + social questions are answered (labelled) while the cited
24
+ answers keep citation coverage 1.0 and false-grounding stays 0.
25
+
26
+ Reproduce: `python -c "from benchmarks.chat_grounding.harness import run; print(run())"`.
@@ -0,0 +1 @@
1
+ e3e632af2b4728d03997585c946fb0b996fc2c8d5fd2f18cc154d16696004db5 cases.jsonl
@@ -0,0 +1,29 @@
1
+ # Chat Head-to-Head (PEN-CHAT P-WS6, re-framed v7.1.1)
2
+
3
+ The corrected punchline measures the property that actually encodes no-fabrication - **provenance labelling and
4
+ false grounding** - NOT answer suppression. All three systems ANSWER general questions (a labelled general answer
5
+ is honest); the differentiator is whether they tell you *what kind* of answer it is, and whether they invent a
6
+ statistic for a made-up entity.
7
+
8
+ Three REAL systems on a shared set, all using the same local model (Ollama `qwen2.5:3b-instruct`); only the
9
+ grounding harness differs.
10
+
11
+ ## Result (`result.json`, 6 queries: 3 general + 3 specific-unsourceable)
12
+ | system | provenance-labelled | fabricated a stat for a made-up entity |
13
+ |---|---|---|
14
+ | **PEN-CHAT** | **1.00** | **0.00** |
15
+ | vanilla RAG | 0.00 | 0.67 |
16
+ | ungrounded LLM | 0.00 | 0.67 |
17
+
18
+ - **provenance_labelled_rate** - every PEN-CHAT answer carries its lane + provenance (general / literature-cited /
19
+ pen-stack / abstained), so a reader can never mistake a general-knowledge answer for a platform result. The
20
+ ungrounded baselines carry NO provenance - every answer looks equally authoritative.
21
+ - **fabricated_stat_on_unsourceable_rate** - asked for a specific number about a made-up writer/locus, PEN-CHAT
22
+ routes to the engine or cites the corpus (a grounded number) or abstains - it never presents an *ungrounded*
23
+ number; the baselines invent a confident statistic 67% of the time.
24
+
25
+ This is the honest claim: PEN-CHAT answers general questions like any assistant, but it never presents an unsourced
26
+ fact as a platform result - measured - and the baselines do.
27
+
28
+ Live measurement (the baselines call a real LLM); result committed. Not a CI unit test (a CI test reads the result).
29
+ Reproduce on a host with Ollama: `PEN_RAG_NO_EMBED=0 python -m benchmarks.chat_headtohead.harness`.
@@ -0,0 +1,35 @@
1
+ # Chat Routing Benchmark (PEN-CHAT P-WS4)
2
+
3
+ Measures whether the deterministic four-lane router (`pen_stack.web.router.classify`) sends each query to the right
4
+ lane - and, the **safety-critical** property, that a write/result request **never leaks to the ungrounded General
5
+ lane**. Deterministic (no LLM/embedder); fully reproducible in CI.
6
+
7
+ ## Lanes
8
+ `design` (engine, guard on) · `explain` (metric guide) · `meta` (live capability facts) · `general` (retrieval-grounded).
9
+
10
+ ## Sealed set
11
+ `cases.jsonl` - 40 labelled queries across the four lanes, including back-reference follow-ups (with a synthetic
12
+ grounded prior) and ambiguous phrasings. SHA-locked in `SHA256SUMS`.
13
+
14
+ ## Metrics + gate (P-G2)
15
+ - per-lane precision / recall / F1 + the confusion matrix;
16
+ - **`routing_safety_metric` = P(a `design`/write request routes to `general`)** - the dangerous failure; **target ~0**;
17
+ - `grounded_to_general_leaks` = any grounded-lane gold that routed to `general`.
18
+
19
+ ## Result (committed, `result.json`)
20
+ | metric | value |
21
+ |---|---|
22
+ | accuracy | **1.000** (40/40) |
23
+ | routing_safety_metric | **0.000** (no write request leaks to general) |
24
+ | grounded -> general leaks | **0** |
25
+ | min per-lane F1 | **1.000** |
26
+ | gate P-G2 (safety ~0) | **pass** |
27
+
28
+ The first run measured `routing_safety_metric = 0.273` (3 of 11 write requests leaked to `general` because the
29
+ router required >=2 signals even with an action verb, and `cell`/`locus` regexes missed plurals like *hepatocytes*,
30
+ *HSPCs*, *iPSCs*). Per the pre-registered discipline (*tighten, never relax the metric*) the router was made
31
+ conservative - **an action verb + any one target signal routes to the grounded design lane** - and the metric was
32
+ re-measured at 0.000. Over-routing a borderline question to a grounded lane is the safe failure direction;
33
+ under-routing a real request to `general` is the dangerous one.
34
+
35
+ Reproduce: `python -c "from benchmarks.chat_routing.harness import run; print(run())"`.
@@ -0,0 +1 @@
1
+ 68c0cc3e58bfe04dbf9a476ebfbb209044c7eda43992ba279e4ec1a11aeaca0d cases.jsonl
@@ -0,0 +1,23 @@
1
+ # Chat Refusal & Abstention Benchmark (PEN-CHAT P-WS6, re-scoped v7.1.1)
2
+
3
+ Measures the gate-**P-G4** safety properties, deterministically (no LLM, lexical retriever; reproducible in CI).
4
+ The headline is **false-grounding** - a non-engine fact presented as a PEN-STACK result - NOT answer-suppression.
5
+
6
+ | property | what it checks | result |
7
+ |---|---|---|
8
+ | **false_grounding_rate** | a general fact mislabelled as a PEN-STACK computed result | **0.0** (target ~0) |
9
+ | **general_answered_rate** | general-knowledge questions are ANSWERED, labelled 'general' (no over-abstention) | **1.0** |
10
+ | specific_empirical_abstention_rate | a specific unsourceable empirical claim is declined / redirected | **1.0** |
11
+ | dual_use_refusal_rate | hazardous build/express requests are refused | **1.0** |
12
+ | injection_hold_rate | an instruction to fabricate/mislabel a PEN-STACK number does not succeed | **1.0** |
13
+
14
+ `false_grounding_rate ~ 0` is the metric that encodes no-fabrication: a non-engine answer is **never** presented as
15
+ a PEN-STACK result (general answers carry provenance `general`, never `pen-stack`). Note (v7.1.1): general-knowledge
16
+ questions like "what is the capital of France" are now ANSWERED (labelled general), not abstained - abstention is
17
+ reserved for a specific unsourceable empirical claim, which is the genuine citation-or-silence case.
18
+
19
+ ## Sealed set
20
+ `cases.jsonl` (SHA-locked) - general-knowledge questions, specific-unsourceable-empirical claims, dual-use prompts,
21
+ and prompt-injection attempts.
22
+
23
+ Reproduce: `python -c "from benchmarks.chat_safety.harness import run; print(run())"`.
@@ -0,0 +1 @@
1
+ 9b05cccd08632ff09b895ba2d39565ce788dea6b3c727fd78f9a1ab0e04cbbfc cases.jsonl
@@ -38,8 +38,8 @@ tissue_specific:
38
38
  lp1: { strength: 0.85, context: liver, confidence: medium, note: "HCR1 enhancer + hAAT core; clinical FIX from single AAV (2nd after TBG)", citation: "10.1182/blood-2005-10-4035" }
39
39
  haat: { strength: 0.80, context: liver, confidence: medium, note: "human α1-antitrypsin; base of ApoE-hAAT/LP1 hybrids", citation: "10.1182/blood-2005-10-4035" }
40
40
  hlp: { strength: 0.70, context: liver, confidence: medium, note: "HCR+hAAT+ApoE ~800 bp; below TBG/LP1; therapeutic at compact size", citation: "10.1016/j.gene.2012.07.009" }
41
- camkiia: { strength: 0.90, context: neuron, confidence: medium, note: "excitatory neurons; greater overall expression than hSyn1", citation: "10.1016/j.neulet.2021.135889" }
42
- hsyn: { strength: 0.70, context: neuron, confidence: medium, note: "pan-neuronal ~460 bp; robust but slower kinetics (CAG-level only by ~3 mo)", citation: "10.1016/j.neulet.2021.135889" }
41
+ camkiia: { strength: 0.90, context: neuron, confidence: low, note: "excitatory neurons; greater overall expression than hSyn1; prior DOI did not resolve, relative strength not independently sourced", citation: null }
42
+ hsyn: { strength: 0.70, context: neuron, confidence: low, note: "pan-neuronal ~460 bp; robust but slower kinetics; prior DOI did not resolve, relative strength not independently sourced", citation: null }
43
43
  mecp2: { strength: 0.50, context: neuron, confidence: low, note: "neuron-restricted compact; weaker, relative fold not sourced", citation: null }
44
44
  gfaabc1d: { strength: 0.80, context: astrocyte,confidence: high, note: "~681 bp; ~2× stronger than full gfa2; astrocyte-restricted", citation: "10.1002/glia.20622" }
45
45
  gfap: { strength: 0.60, context: astrocyte,confidence: medium,note: "gfa2 ~2.2 kb astrocyte-selective", citation: "10.1002/glia.20622" }
@@ -14,7 +14,7 @@ python -m pen_stack.agent.mcp_server # or: docker compose up mcp
14
14
 
15
15
  | Tool | Signature | Returns |
16
16
  |---|---|---|
17
- | `writability` | `(gene, ct="k562")` | most-writable locus near a gene (safety x durability) |
17
+ | `writability` | `(gene, ct="k562")` | most-writable locus near a gene (`0.5*safety + 0.5*p_durable`, additive) |
18
18
  | `reachable_writers` | `(gene, ct="k562")` | writer families that can reach the gene's best locus |
19
19
  | `writer_axes` | `(family)` | measured axes for a writer family (cargo, deliverability, tier) |
20
20
  | `plan_write` | `(gene, intent, cargo_bp=2000, ct="k562")` | full Write Planner: ranked, traceable plan |
@@ -2,9 +2,11 @@
2
2
 
3
3
  **Goal:** find safe, durable insertion loci near a target gene, in a specific cell type.
4
4
 
5
- The Writable Genome scores every 1 kb bin for `writability = safety x durability`, learned blind on
6
- public data (COSMIC/DepMap + 3.7M MLV integration sites for safety; TRIP position effects for
7
- durability). It is *conditional on the supplied epigenome*, so the cell type matters.
5
+ The Writable Genome scores every 1 kb bin for `writability = 0.5*safety + 0.5*p_durable` (an additive,
6
+ decomposable mean of the two grounded components, not a product, and with no separate accessibility term -
7
+ chromatin enters as input features to the safety and durability models). Both are learned blind on public
8
+ data (CancerMine/DepMap + MLV integration sites for safety; TRIP position effects for durability). It is
9
+ *conditional on the supplied epigenome*, so the cell type matters.
8
10
 
9
11
  ## CLI
10
12
 
@@ -1,2 +1,2 @@
1
1
  """PEN-STACK v3.0 - open infrastructure for genome writing."""
2
- __version__ = "7.0.0"
2
+ __version__ = "7.1.1"
@@ -91,6 +91,15 @@ _TOOLS = [
91
91
  "input": "oracle name, or {protein_seq, ligand_smiles, pair_type}", "output": "oracle status + reliability, "
92
92
  "or a candidate affinity (binder probability + value) or abstain; protein-protein/protein-DNA flagged OOD",
93
93
  "entrypoint": "pen_stack.oracles.affinity.predict_affinity", "fabricates": False},
94
+ {"name": "chat_answer", "summary": "the grounded conversational co-scientist (PEN-CHAT v7.1): a provenance-"
95
+ "partitioned 4-lane agent (design/explain/meta = engine-grounded with the guard ON; general = retrieval-"
96
+ "grounded over a provenance-tagged corpus under citation-or-silence, abstaining below a retrieval-confidence "
97
+ "threshold). The LLM narrates and is swappable; it NEVER originates a claim or a number. Measured by the "
98
+ "chat_routing / chat_grounding / chat_safety benchmarks (routing-safety ~0, citation coverage 1.0, "
99
+ "false-grounding ~0)",
100
+ "input": "{message, history?, allow_llm?}", "output": "{reply, mode (lane), provenance, grounded, sources?, "
101
+ "backend}; general answers are 'literature-cited' or 'abstained', never a PEN-STACK-computed result",
102
+ "entrypoint": "pen_stack.web.llm.grounded_reply", "fabricates": False},
94
103
  ]
95
104
 
96
105
  _POLICY = ("outputs outside scope are returned as `out_of_scope` (known-unknown) or `extrapolating` (OOD) and are "
@@ -0,0 +1,133 @@
1
+ """PEN-RAG corpus builder (v7.1).
2
+
3
+ Assembles the provenance-tagged corpus that grounds the chat's General lane. EVERY chunk is real, already-curated
4
+ repository content - DOI-backed verbatim quotes, the metric guide, the writer-atlas family cards, the data/model
5
+ cards, and the scope boundary - never fabricated paper text. Each chunk carries:
6
+ chunk_id, text, source_id, doi, access_grade, type, scope_status
7
+ The built table is `data/rag_corpus.parquet` (SHA-locked at build); its embeddings are `data/rag_corpus_emb.npy`.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+
13
+ import pandas as pd
14
+
15
+ from pen_stack._resources import project_root
16
+
17
+ _FIELDS = ["chunk_id", "text", "source_id", "doi", "access_grade", "type", "scope_status"]
18
+
19
+
20
+ def _writer_efficiency_chunks() -> list[dict]:
21
+ from pen_stack.atlas import writer_efficiency as we
22
+ out = []
23
+ for r in we.records().itertuples(index=False):
24
+ q = str(getattr(r, "quote", "") or "").strip()
25
+ if not q:
26
+ continue
27
+ eff = getattr(r, "efficiency_pct", None)
28
+ eff_s = f" Measured integration efficiency ~{eff}%." if eff is not None and not pd.isna(eff) else ""
29
+ out.append({
30
+ "text": (f"{r.system} ({r.family}) at {r.locus} in {r.cell_type}.{eff_s} "
31
+ f"Reported: \"{q}\""),
32
+ "source_id": f"writer_eff:{r.system}:{r.locus}", "doi": str(r.doi),
33
+ "access_grade": str(getattr(r, "source_access", "unknown")),
34
+ "type": "efficiency_measurement", "scope_status": "measured"})
35
+ return out
36
+
37
+
38
+ def _metric_guide_chunks() -> list[dict]:
39
+ from pen_stack.web.guide import metric_guide
40
+ mg = metric_guide() or {}
41
+ out = []
42
+ for key, m in (mg.get("metrics") or {}).items():
43
+ text = (f"Metric '{m.get('label', key)}' ({key}): scale {m.get('scale')}, {m.get('direction')}. "
44
+ f"What it means: {m.get('means')} How it is computed: {m.get('computed')} "
45
+ f"Validation: {m.get('validation')} Reference: {m.get('reference')}")
46
+ cites = m.get("reference") or ""
47
+ out.append({"text": text, "source_id": f"metric_guide:{key}", "doi": str(cites),
48
+ "access_grade": "curated", "type": "metric_card", "scope_status": "documented"})
49
+ sd = mg.get("safety_decision")
50
+ if sd:
51
+ out.append({"text": "Guardian biosecurity decisions: " + "; ".join(f"{k} = {v}" for k, v in sd.items()),
52
+ "source_id": "metric_guide:safety_decision", "doi": "", "access_grade": "curated",
53
+ "type": "metric_card", "scope_status": "documented"})
54
+ return out
55
+
56
+
57
+ def _atlas_card_chunks() -> list[dict]:
58
+ from pen_stack.rag.index import build_cards
59
+ out = []
60
+ for c in build_cards():
61
+ out.append({"text": c.text, "source_id": f"atlas:{c.key}", "doi": ";".join(c.citations),
62
+ "access_grade": "curated", "type": "writer_atlas_card", "scope_status": "measured/candidate"})
63
+ return out
64
+
65
+
66
+ def _doc_card_chunks() -> list[dict]:
67
+ """Chunk the committed data/model cards (docs/cards/*.md) by paragraph."""
68
+ out = []
69
+ cards_dir = project_root() / "docs" / "cards"
70
+ if not cards_dir.exists():
71
+ return out
72
+ for p in sorted(cards_dir.glob("*.md")):
73
+ text = p.read_text(encoding="utf-8", errors="ignore")
74
+ for para in [s.strip() for s in text.split("\n\n")]:
75
+ clean = " ".join(para.split())
76
+ if len(clean) < 80 or clean.startswith("#") and len(clean) < 120:
77
+ continue
78
+ out.append({"text": clean[:900], "source_id": f"card:{p.stem}", "doi": "",
79
+ "access_grade": "curated", "type": "data_card", "scope_status": "documented"})
80
+ return out
81
+
82
+
83
+ def _scope_chunks() -> list[dict]:
84
+ """The honesty boundary: the known-unknowns the engine never predicts."""
85
+ from pen_stack.web.guide import pen_stack_facts
86
+ out = []
87
+ try:
88
+ facts = pen_stack_facts() or {}
89
+ except Exception: # noqa: BLE001
90
+ facts = {}
91
+ ku = facts.get("known_unknowns") or facts.get("scope", {}).get("known_unknowns")
92
+ if ku:
93
+ out.append({"text": ("PEN-STACK never predicts these known-unknowns (it abstains): "
94
+ + "; ".join(str(x) for x in ku) + ". These are measured clinical endpoints "
95
+ "outside the engine's validated envelope."),
96
+ "source_id": "scope:known_unknowns", "doi": "", "access_grade": "curated",
97
+ "type": "scope_boundary", "scope_status": "known_unknown"})
98
+ return out
99
+
100
+
101
+ def build_corpus() -> pd.DataFrame:
102
+ """Assemble the full provenance-tagged corpus from real repository content."""
103
+ rows: list[dict] = []
104
+ for fn in (_writer_efficiency_chunks, _metric_guide_chunks, _atlas_card_chunks,
105
+ _doc_card_chunks, _scope_chunks):
106
+ try:
107
+ rows.extend(fn())
108
+ except Exception as e: # noqa: BLE001 - a missing source must not silently corrupt the corpus; surface it
109
+ rows.append({"text": f"[source builder {fn.__name__} failed: {e}]", "source_id": "build_error",
110
+ "doi": "", "access_grade": "n/a", "type": "build_error", "scope_status": "n/a"})
111
+ # drop build-error rows from the shippable corpus but keep the count honest
112
+ df = pd.DataFrame([r for r in rows if r["type"] != "build_error"])
113
+ df = df.drop_duplicates(subset=["text"]).reset_index(drop=True)
114
+ df["chunk_id"] = [f"c{i:04d}" for i in range(len(df))]
115
+ errors = [r for r in rows if r["type"] == "build_error"]
116
+ if errors:
117
+ raise RuntimeError("corpus build had source errors: " + " | ".join(r["text"] for r in errors))
118
+ return df[_FIELDS]
119
+
120
+
121
+ def corpus_path() -> Path:
122
+ return project_root() / "data" / "rag_corpus.parquet"
123
+
124
+
125
+ def emb_path() -> Path:
126
+ return project_root() / "data" / "rag_corpus_emb.npy"
127
+
128
+
129
+ def load_corpus() -> pd.DataFrame:
130
+ p = corpus_path()
131
+ if not p.exists():
132
+ raise FileNotFoundError(f"{p} not built; run scripts/build_rag_corpus.py")
133
+ return pd.read_parquet(p)
@@ -0,0 +1,79 @@
1
+ """PEN-RAG embedding client (v7.1).
2
+
3
+ A pinned local embedder (`nomic-embed-text` via the already-running Ollama) for semantic retrieval, with a
4
+ deterministic, model-free **lexical fallback** so the General lane still retrieves (degraded, but honest) when no
5
+ embedder is reachable - e.g. in CI or if Ollama is down. The CORPUS embeddings are computed once at build time and
6
+ committed (`data/rag_corpus_emb.npy`); only the live QUERY is embedded at runtime, so retrieval stays reproducible.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import os
12
+ import urllib.request
13
+
14
+ import numpy as np
15
+
16
+ EMBED_MODEL = os.getenv("PEN_RAG_EMBED_MODEL", "nomic-embed-text")
17
+ OLLAMA_HOST = os.getenv("OLLAMA_HOST", "http://localhost:11434")
18
+ _TIMEOUT = int(os.getenv("PEN_RAG_EMBED_TIMEOUT", "30"))
19
+ # nomic-embed-text is trained with task prefixes; using them is REQUIRED for retrieval to separate relevant from
20
+ # irrelevant (without them every pair sits in a narrow high-cosine band and abstention cannot discriminate).
21
+ _PREFIX = {"query": "search_query: ", "document": "search_document: "}
22
+
23
+
24
+ def embed_text(text: str, task: str = "query") -> np.ndarray | None:
25
+ """L2-normalised embedding of one string via Ollama (with the nomic task prefix), or None if unreachable."""
26
+ if os.getenv("PEN_RAG_NO_EMBED") == "1":
27
+ return None
28
+ try:
29
+ req = urllib.request.Request(
30
+ f"{OLLAMA_HOST}/api/embeddings",
31
+ data=json.dumps({"model": EMBED_MODEL, "prompt": _PREFIX.get(task, "") + (text or "")}).encode(),
32
+ headers={"Content-Type": "application/json"})
33
+ with urllib.request.urlopen(req, timeout=_TIMEOUT) as r:
34
+ vec = json.loads(r.read().decode())["embedding"]
35
+ v = np.asarray(vec, dtype="float32")
36
+ n = float(np.linalg.norm(v))
37
+ return v / n if n else v
38
+ except Exception: # noqa: BLE001 - any failure -> caller uses the lexical fallback
39
+ return None
40
+
41
+
42
+ def embed_corpus(texts: list[str]) -> np.ndarray:
43
+ """Embed the corpus DOCUMENTS at BUILD time. Raises if the embedder is unreachable (the build must be
44
+ deterministic and must never silently fall back to a different representation)."""
45
+ out = []
46
+ for t in texts:
47
+ v = embed_text(t, task="document")
48
+ if v is None:
49
+ raise RuntimeError(f"embedder '{EMBED_MODEL}' unreachable at {OLLAMA_HOST}; cannot build corpus embeddings")
50
+ out.append(v)
51
+ return np.vstack(out).astype("float32")
52
+
53
+
54
+ # --- deterministic, model-free lexical fallback (content-token Jaccard) --------------------------------------
55
+ # Stopwords are dropped so the fallback discriminates on CONTENT words: an out-of-corpus query (e.g. "what is the
56
+ # capital of France") must not match a genome-writing chunk via shared function words and slip past the abstention.
57
+ _STOP = frozenset((
58
+ "the and for are was were that this with from have has had not but you your his her its our their they them "
59
+ "what which who whom whose when where why how does did doing done can could should would will shall may might "
60
+ "into onto over under above below than then them they about after before between because while during each "
61
+ "any all some more most much many few any both either neither nor yet via per such only also same other "
62
+ "one two three get got make made use used using like just very too here there out off own").split())
63
+
64
+
65
+ def tokenize(s: str) -> set[str]:
66
+ return {w for w in "".join(c.lower() if c.isalnum() else " " for c in (s or "")).split()
67
+ if len(w) > 2 and w not in _STOP}
68
+
69
+
70
+ def lexical_scores(query: str, texts: list[str]) -> np.ndarray:
71
+ """Jaccard overlap of the query tokens with each document's tokens. Deterministic; no model."""
72
+ q = tokenize(query)
73
+ if not q:
74
+ return np.zeros(len(texts), dtype="float32")
75
+ out = np.zeros(len(texts), dtype="float32")
76
+ for i, t in enumerate(texts):
77
+ d = tokenize(t)
78
+ out[i] = len(q & d) / len(q | d) if d else 0.0
79
+ return out
@@ -0,0 +1,130 @@
1
+ """PEN-RAG grounding for the chat's General lane (v7.1.1).
2
+
3
+ Retrieval is ADDITIVE, not a gate. The General lane ANSWERS general and social questions by default (restoring the
4
+ WS-HYBRID behaviour), clearly LABELLED as general knowledge - a labelled general answer is honest, not a
5
+ fabrication, because it is never presented as a PEN-STACK result. Corpus retrieval, when it finds a relevant chunk,
6
+ UPGRADES the answer to 'literature-cited' (with its sources). Abstention is the rare exception: it fires only for a
7
+ SPECIFIC, unsourceable empirical / quantitative genome-writing claim the corpus does not cover and the engine is
8
+ not computing - the genuine citation-or-silence case - so the model cannot dress a fabricated statistic as fact.
9
+
10
+ Branches:
11
+ social -> a friendly answer + a pointer to what the engine can compute (no retrieval, no abstention).
12
+ cited -> a relevant corpus chunk was retrieved -> cite-or-silence answer, provenance 'literature-cited'.
13
+ general -> background / textbook knowledge -> answer from the LLM, labelled (provenance 'general').
14
+ abstained -> a specific unsourceable empirical claim -> decline + redirect to the engine (provenance 'abstained').
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import os
19
+ import re
20
+
21
+ from pen_stack.rag.embed import tokenize
22
+ from pen_stack.rag.retrieve import retrieve
23
+
24
+ # nomic-embed-text compresses cosines into a narrow high band, so an absolute cosine alone cannot separate a valid
25
+ # in-corpus query from an off-topic one. The hybrid gate (cosine floor + lexical content-overlap, with a high-cosine
26
+ # escape) decides only whether the corpus is a relevant SOURCE to cite - NOT whether to answer at all.
27
+ THRESHOLD = float(os.getenv("PEN_RAG_THRESHOLD", "0.50"))
28
+ _SEM_HIGH = float(os.getenv("PEN_RAG_THRESHOLD_HIGH", "0.72"))
29
+ _LEX_THRESHOLD = float(os.getenv("PEN_RAG_LEX_THRESHOLD", "0.05"))
30
+
31
+ # A social / conversational opener: answered naturally, never gated on the corpus.
32
+ _SOCIAL = re.compile(r"^\s*(hi|hii+|hey+|hello+|yo|hola|sup|greetings|good (morning|afternoon|evening|day)|"
33
+ r"thanks?|thank you|cheers|ok(ay)?|cool|nice|great|awesome|who are you|what are you|"
34
+ r"how are you|what'?s up|introduce yourself|help)\b", re.I)
35
+
36
+ # A SPECIFIC genome-writing QUANTITATIVE / empirical claim (not definitional / mechanistic background). When the
37
+ # corpus has no source for it and it is in the general lane (so the engine is not computing it), we abstain +
38
+ # redirect rather than let the model invent a statistic dressed up as fact.
39
+ _SPECIFIC_EMPIRICAL = re.compile(
40
+ r"\b(efficienc\w*|integration rate|specificit\w*|on-?target rate|off-?target rate|knock-?in rate|titer|titre|"
41
+ r"affinit\w*|\bkd\b|ic50|fold[- ]improvement|expression level|copy number|"
42
+ r"percent\w* (of )?(integration|editing|knock|insertion))\b", re.I)
43
+
44
+ _SOCIAL_REPLY = ("Hi - I'm PEN-STACK's genome-writing co-scientist. Ask a genome-writing question (where to insert, "
45
+ "which writer, delivery, immune risk, off-targets, safety) and the engine computes a grounded, "
46
+ "traceable answer. I can also answer general background questions, clearly labelled as such.")
47
+ _GENERAL_NO_LLM = ("I can answer general background questions when the language model is enabled. Meanwhile, "
48
+ "PEN-STACK's engine can compute grounded, specific answers for your case.")
49
+ _LABEL = "Literature-cited (PEN-CHAT corpus) - not a PEN-STACK-computed result:"
50
+ _ABSTAIN_SPECIFIC = ("I don't have a grounded source for that specific value and I won't invent one. PEN-STACK's "
51
+ "engine can compute it from a concrete design - give me the gene, cell type, and cargo (or "
52
+ "the writer/guide) and I'll run the site, writer-efficiency, or off-target tools.")
53
+
54
+
55
+ def _is_grounded(query: str, hits: list[dict], method: str) -> bool:
56
+ """Is the corpus a relevant SOURCE to cite for this query?"""
57
+ if not hits:
58
+ return False
59
+ top = hits[0]
60
+ if not method.startswith("semantic"):
61
+ return top["score"] >= _LEX_THRESHOLD
62
+ lexical_overlap = bool(tokenize(query) & tokenize(top["text"]))
63
+ return top["score"] >= THRESHOLD and (lexical_overlap or top["score"] >= _SEM_HIGH)
64
+
65
+
66
+ def _dedup_sources(hits: list[dict]) -> list[dict]:
67
+ seen, out = set(), []
68
+ for h in hits:
69
+ sid = h["source_id"]
70
+ if sid in seen:
71
+ continue
72
+ seen.add(sid)
73
+ out.append({"source_id": sid, "doi": h.get("doi") or None, "type": h["type"], "score": round(h["score"], 3)})
74
+ return out
75
+
76
+
77
+ def _cited_answer(message: str, r: dict, allow_llm: bool) -> dict:
78
+ floor = THRESHOLD if r["method"].startswith("semantic") else _LEX_THRESHOLD
79
+ grounded_hits = [h for h in r["hits"] if h["score"] >= floor] or r["hits"][:2]
80
+ sources = _dedup_sources(grounded_hits)
81
+ deterministic = _LABEL + "\n\n" + "\n\n".join(
82
+ f"- {h['text']} [{h['source_id']}{(' · ' + h['doi']) if h.get('doi') else ''}]" for h in grounded_hits)
83
+ base = {"status": "grounded", "sources": sources, "provenance": "literature-cited", "grounded": True,
84
+ "retrieval": r["method"], "top_score": r["top_score"]}
85
+ if allow_llm:
86
+ from pen_stack.web.llm import _enforce_grounding, _run_llm
87
+ from pen_stack.web.tools import extract_grounded_numbers
88
+ ctx = "\n\n".join(f"[{i + 1}] ({h['source_id']}{(' DOI ' + h['doi']) if h.get('doi') else ''}) {h['text']}"
89
+ for i, h in enumerate(grounded_hits))
90
+ system = ("You answer ONLY from the SOURCES provided. After each claim, cite the bracketed [n] it came from. "
91
+ "If the sources do not cover the question, say so. Never add a number, name, or vehicle that is "
92
+ "not in the sources. Never present anything as a PEN-STACK-computed result.")
93
+ text, backend = _run_llm(f"SOURCES:\n{ctx}\n\nQUESTION: {message}\n\nAnswer concisely, citing [n].", system)
94
+ if text:
95
+ allow = extract_grounded_numbers({"hits": [h["text"] for h in grounded_hits]})
96
+ return {**base, "reply": _LABEL + "\n\n" + _enforce_grounding(text.strip(), allow), "backend": backend}
97
+ return {**base, "reply": deterministic, "backend": "deterministic"}
98
+
99
+
100
+ def ground_general(message: str, *, allow_llm: bool = True) -> dict:
101
+ """Return the chat envelope for the General lane: answer (social / general / cited) by default; abstain only on a
102
+ specific unsourceable empirical claim."""
103
+ msg = (message or "").strip()
104
+ if _SOCIAL.match(msg):
105
+ return {"status": "social", "reply": _SOCIAL_REPLY, "sources": [], "provenance": "general",
106
+ "grounded": False, "backend": "deterministic", "retrieval": "none", "top_score": 0.0}
107
+
108
+ r = retrieve(msg, k=4)
109
+ if _is_grounded(msg, r["hits"], r["method"]):
110
+ return _cited_answer(msg, r, allow_llm)
111
+
112
+ # no corpus source. A specific empirical genome-writing claim -> abstain + redirect to the engine.
113
+ if _SPECIFIC_EMPIRICAL.search(msg):
114
+ return {"status": "abstained", "reply": _ABSTAIN_SPECIFIC, "sources": [], "provenance": "abstained",
115
+ "grounded": False, "backend": "deterministic", "retrieval": r["method"], "top_score": r["top_score"]}
116
+
117
+ # general / background knowledge -> ANSWER, clearly labelled as general (never a PEN-STACK result).
118
+ if allow_llm:
119
+ from pen_stack.web.llm import SYSTEM_GENERAL, _GENERAL_LABEL, _run_llm
120
+ text, backend = _run_llm(
121
+ f"USER: {msg}\n\nAnswer from general knowledge, clearly and at a graduate level. Do not present anything "
122
+ f"as a PEN-STACK result.", SYSTEM_GENERAL)
123
+ if text:
124
+ return {"status": "general", "reply": _GENERAL_LABEL + "\n\n" + text.strip(), "sources": [],
125
+ "provenance": "general", "grounded": False, "backend": backend, "retrieval": r["method"],
126
+ "top_score": r["top_score"]}
127
+ from pen_stack.web.llm import _GENERAL_LABEL
128
+ return {"status": "general", "reply": _GENERAL_LABEL + "\n\n" + _GENERAL_NO_LLM, "sources": [],
129
+ "provenance": "general", "grounded": False, "backend": "deterministic", "retrieval": r["method"],
130
+ "top_score": r["top_score"]}