pen-stack 6.4.1__tar.gz → 6.4.3__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.
- {pen_stack-6.4.1 → pen_stack-6.4.3}/CHANGELOG.md +46 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/CITATION.cff +2 -2
- {pen_stack-6.4.1 → pen_stack-6.4.3}/PKG-INFO +2 -2
- {pen_stack-6.4.1 → pen_stack-6.4.3}/README.md +1 -1
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/__init__.py +1 -1
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/web/llm.py +62 -15
- pen_stack-6.4.3/pen_stack/web/tools.py +197 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/PKG-INFO +2 -2
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pyproject.toml +1 -1
- pen_stack-6.4.1/pen_stack/web/tools.py +0 -105
- {pen_stack-6.4.1 → pen_stack-6.4.3}/LICENSE +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/MANIFEST.in +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/bench/run.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_bench/LEADERBOARD.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_bench/README.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_bench/SHA256SUMS +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_bench/tasks.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_challenge/README.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_challenge/SUBMISSIONS.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/antipeg.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/atlas_families.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/bridge_offtarget_profile.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/capsid_epitope_oracle.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/capsid_sequences.fasta +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/cargo_polish.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/cell_types.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/datasets.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/delivery_constraints.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/delivery_rules.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/delivery_vehicles.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/gates_v3.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/genotoxicity_oracle.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/gsh_validated_heldout.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/intent_weights.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/known_unknowns.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/llm.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/metric_guide.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/monitor_queries.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/oracles/execution.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/oracles/scope_cards.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/rules/delivery.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/rules/fold.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/rules/multiplex.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/rules/payload.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/rules/reachability.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/safety/hazard_registry.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/safety/policy.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/safety/probes.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/score_axes.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/seroprevalence.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/target_sites.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/universe_crosswalk.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/write_types.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/wtkb_curated.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/data/curated/bridge_offtarget_energetics.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/data/curated/gene_coords.parquet +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/data/curated/unified_editor_universe.parquet +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/BACKLOG.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/DEPLOY.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/INFRA.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/MCP.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/RELEASING.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/REPRO.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/STABILITY.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/agent.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/alphagenome_feasibility.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/autonomy.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/benchmark_circularity.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/biosecurity.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/build_interface.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/cards/atlas.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/cards/durability.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/cards/safety.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/challenge.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/closed_loop.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/co_scientist.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/co_scientist_loop.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/delivery.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/delivery_immunology.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/digital_twin.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/dissemination.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/environment.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/experiment_design.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/generative_design.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/index.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/integrations.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/live_oracles.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/mechanistic_constraints.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/oracles.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/positioning.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/private_data_formats.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/quickstart.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/responsible_use.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/rules.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/scope.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/scorecard.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/tutorials/compare-families.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/tutorials/score-deliverability.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/tutorials/where-can-i-write.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/tutorials/which-writer-reaches-locus.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/uncertainty.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/verify.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/world_model.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/writer_verification.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/wtkb.md +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/_resources.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/active/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/active/acquire.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/active/design.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/active/validate.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/finetune.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/ingest.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/pipeline.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/recalibrate.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/report.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/cite.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/co_scientist.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/epistemic.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/guardrails.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/mcp_server.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/orchestrator.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/orchestrator_live.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/pen_agent.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/scope.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/tools.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/api/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/api/manifest.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/build_wtkb.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/crosslink.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/expand.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/schema.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/scorecard.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/universe.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/variant_propose.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/writer_verify.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/activity.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/cli.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/fold_qc.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/guide_qc.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/ingest.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/offtarget.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/offtarget_energetics.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/ortholog_screen.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/pipeline.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/build/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/build/ingest.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/build/protocol.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/build/simlab.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/cli.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/encode.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/genome.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/ingest_chromatin.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/ingest_integration.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/ingest_safety_annot.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/ingest_trip.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/design/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/design/generate.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/design/pareto.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/design/space.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/env/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/env/genome_writing_env.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/env/policies.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/build.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/cell_types.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/ingest.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/query.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/schema.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/loop/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/loop/continual.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/loop/cycle.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/loop/drift.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/mech/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/mech/classify_atlas.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/mech/whitelist.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/monitor/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/monitor/europepmc.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/monitor/run.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/monitor/triage.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/cache.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/energetics.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/genome.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/protein_design.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/rna.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/schema.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/status.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/structure.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/vcell.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/antipeg_oracle.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/capsid_epitope_oracle.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/cargo.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/cargo_polish.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/delivery.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/delivery_constraints.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/delivery_immunology.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/delivery_vehicles.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/genotoxicity_oracle.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/immune_profile.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/innate_sensing.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/multiplex.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/optimize.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/pipeline.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/report.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/router.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/seroprevalence_oracle.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/target_site.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rag/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rag/index.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rag/llm.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rag/qa.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rules/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rules/evaluators.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rules/loader.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rules/schema.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rules/solver.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/audit.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/gate.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/policy.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/redteam.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/registry.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/screen.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/score/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/score/recalibrate.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/score/therapeutic.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/server/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/server/api.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/twin/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/twin/calibrate.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/twin/mechanistic.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/twin/outcome.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/ui/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/ui/app.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/adapt_demo.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/agent_eval.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_coscientist_tasks.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_graph_tasks.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_rule_tasks.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_trust_tasks.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_writetype_tasks.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/blind_gsh_discovery.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/cargo_directionality.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/closed_loop.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/durability_baselines.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/experiment_design.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/forward_hypotheses.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/generative_design.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/guide_qc_demo.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/immune_calibration.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/intent_specification.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/out_of_scope_refusal.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/outcome_calibration.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/outcome_prediction.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/paper3_benchmark.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/paper4_real_validation.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/paper4_validation.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/protocol_safety.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/safety_screening.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/selective_prediction.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/seq_vs_measured.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/target_site_controls.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/uncertainty_eval.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/ungrounded_baseline.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/within_locus_ranking.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/writer_recovery.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/verify/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/verify/schema.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/verify/service.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/web/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/web/guide.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/web/router.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/web/server.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/__init__.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/chromatin_seq.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/durability.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/export_tracks.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/features.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/gsh_baseline.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/mesh_features.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/ood.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/providers.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/safety.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/structure3d.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/uncertainty.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/writability.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/SOURCES.txt +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/dependency_links.txt +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/entry_points.txt +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/requires.txt +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/top_level.txt +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_phase0.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_phase1_5.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_phase2.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_phase3.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_a.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_acq.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_aldesign.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_alvalidate.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_b.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ba.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_bench.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_c.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_cal.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_calib.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_challenge.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_chat.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_cite.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_continual.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_cosci2.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_crit.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ct.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_d.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_drift.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_e.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_env.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ep.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_epitope.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_f.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_frontend.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_g.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_gen.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_genotox.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_graph.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_h.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_hybrid.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_immune.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ingest.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_innate.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_loop.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_manifest.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_mc.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_mcp.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_mech.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_mon.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_o.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_openapi.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_orch.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_outcome.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_pareto.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_peg.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_plan.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_policy.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_profile.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_proto.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_r.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_redteam.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_route.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_screen.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_seroprev.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_simlab.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_twincal.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_uq.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_v.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_vcell.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_wv.json +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/paper1.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/paper2.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/paper3.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/paper4.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/phase0.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_a.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_acq.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_aldesign.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_alvalidate.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_atlas.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_b.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ba.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ba_v33.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ba_v45.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_bench.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_c.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_cal.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_calib.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_challenge.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_chat.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_cite.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_continual.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_cosci2.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_crit.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ct.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_d.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_drift.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_e.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_env.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ep.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_epitope.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_f.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_frontend.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_g.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_gen.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_genotox.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_graph.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_h.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_hybrid.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_immune.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ingest.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_innate.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_loop.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_manifest.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_mc.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_mcp.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_mech.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_mon.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_o.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_openapi.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_orch.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_outcome.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_pareto.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_peg.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_plan.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_policy.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_profile.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_proto.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_r.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_redteam.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_route.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_screen.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_seroprev.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_simlab.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_twincal.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_uq.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_v.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_vcell.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_wv.yaml +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_build_atlas.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_build_durability.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_export_tracks.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_safety_concordance.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_train_safety.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_validation_report.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p2_build_atlas.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p3_benchmark_report.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p4_genome_scan.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p52_build_genotox_oracle.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p53_build_epitope_oracle.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/ws_b_report.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/ws_c_report.py +0 -0
- {pen_stack-6.4.1 → pen_stack-6.4.3}/setup.cfg +0 -0
|
@@ -3,6 +3,52 @@
|
|
|
3
3
|
All notable changes to PEN-STACK are documented here. This file follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/) and the program's phase structure.
|
|
5
5
|
|
|
6
|
+
## [6.4.3] - 2026-06-12 - Chat vehicle-parse fix (AAVS1 no longer hijacks the vehicle)
|
|
7
|
+
|
|
8
|
+
**PATCH.** `web/tools.py::parse_goal` matched the delivery vehicle by substring, so the safe-harbour nickname
|
|
9
|
+
**AAVS1** (which contains "aav") wrongly selected the **AAV** vehicle even when the user said *lentivirus* or
|
|
10
|
+
*LNP* — producing the wrong immune profile (e.g. genotoxicity 1.0 instead of lentivirus's 0.481). Fix: strip the
|
|
11
|
+
safe-harbour nicknames (AAVS1/H11/HIPP11/ROSA26) from the vehicle-search text before matching, so the user's
|
|
12
|
+
stated vehicle wins. Explicit "AAV" still selects AAV. New test locks all three cases.
|
|
13
|
+
|
|
14
|
+
## [6.4.2] - 2026-06-12 - Co-Scientist chat: real writer recommendations + self-explanatory values
|
|
15
|
+
|
|
16
|
+
**PATCH — fixes a stale/confusing chat experience reported from the live app.** Three problems, three fixes:
|
|
17
|
+
|
|
18
|
+
1. **The chat dossier was generic and barely changed per query.** `web/tools.py::parse_goal` hardcoded
|
|
19
|
+
`chrom="chr19"` (so a goal about ITGB2 — chr21 — was mis-located) and `run_tools` never called the planner,
|
|
20
|
+
so a "which writer can integrate N kb in GENE" question got a vehicle-keyed immune profile but **no named
|
|
21
|
+
writer**. Fix: `parse_goal` now resolves the gene's REAL chromosome (`planner.gene_region`, atlas-gated,
|
|
22
|
+
falls back offline), and `run_tools` runs the planner and attaches a `plan` block — the recommended writer
|
|
23
|
+
family, top site, safety/durability/score, cargo-capacity fit, and delivery — all engine-computed. Honest
|
|
24
|
+
when the gene isn't in the atlas ("not found — check the HGNC symbol"); never fabricated.
|
|
25
|
+
2. **The LLM narration produced `[unverified]` spam.** With no writer in the tool results, the model invented
|
|
26
|
+
one (`AAV9-CRISPR` + made-up numbers) and the grounding guard struck them all. Fix: the system prompt now
|
|
27
|
+
forbids inventing a writer/vehicle/table/number, and if the guard still strikes ≥2 numbers the reply falls
|
|
28
|
+
back to the fully-grounded deterministic narration — so the spam never reaches the user.
|
|
29
|
+
3. **Values + the "⚠ extrapolating" badge weren't self-explanatory.** Each immune axis now carries a
|
|
30
|
+
plain-language `meaning` ("0.55 on a 0–1 scale — moderate; higher = fewer patients excluded …; this is a
|
|
31
|
+
proxy, not validated against a measured outcome"), the deterministic narration uses it, and
|
|
32
|
+
`ImmuneProfileCard` renders it plus a one-line legend explaining that "extrapolating" = a proxy estimate.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- `tests/unit/test_ws_chat.py` — chrom resolution, axis-meaning, real writer plan, and the no-`[unverified]`-spam
|
|
36
|
+
fallback are locked by new tests.
|
|
37
|
+
|
|
38
|
+
## [6.4.1] - 2026-06-12 - Defence-in-depth: pre-route safety screen
|
|
39
|
+
|
|
40
|
+
**PATCH.** The grounded co-scientist chat ran the Guardian (biosecurity gate) only in the *design* lane (via
|
|
41
|
+
`run_tools`); a hazardous request with no design signal (no vehicle/locus) routed to `general`/`explain`/`meta`
|
|
42
|
+
and was never screened. Fix: `pen_stack/web/llm.py::_pre_route_safety` runs the Guardian — framing-stripped, the
|
|
43
|
+
authority on the decision — at the **top** of `grounded_reply()`, before lane routing. A `refuse`/`escalate`
|
|
44
|
+
verdict short-circuits to a clear decline (`mode="safety"`, with the decision in `tool_results`). Benign
|
|
45
|
+
hazard-adjacent biology (vaccines, generic pathogen questions) is **not** blocked — the Guardian clears it and
|
|
46
|
+
routing continues. A broad regex only decides *whether* to invoke the Guardian; a false trigger is harmless.
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- `tests/unit/test_ws_chat.py` — `test_pre_route_safety_screens_a_hazardous_general_query` and
|
|
50
|
+
`test_pre_route_safety_does_not_over_refuse_benign_questions` lock both sides of the screen.
|
|
51
|
+
|
|
6
52
|
## [6.4.0] - 2026-06-12 - v6.4.0: Live Oracles (the foundation models actually execute)
|
|
7
53
|
|
|
8
54
|
**The oracle mesh goes live.** The foundation-model adapters that were deferred contracts now run real backends —
|
|
@@ -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: 6.4.
|
|
5
|
-
date-released: 2026-06-
|
|
4
|
+
version: 6.4.3
|
|
5
|
+
date-released: 2026-06-12
|
|
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: 6.4.
|
|
3
|
+
Version: 6.4.3
|
|
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
|
|
@@ -90,7 +90,7 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
|
|
|
90
90
|
[](https://codecov.io/gh/ahmedanees-m/pen-stack)
|
|
91
91
|
[](LICENSE)
|
|
92
92
|
[](https://www.python.org/)
|
|
93
|
-
[](CHANGELOG.md)
|
|
94
94
|
[](docs/STABILITY.md)
|
|
95
95
|
[](tests/)
|
|
96
96
|
[](https://github.com/astral-sh/ruff)
|
|
@@ -15,7 +15,7 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
|
|
|
15
15
|
[](https://codecov.io/gh/ahmedanees-m/pen-stack)
|
|
16
16
|
[](LICENSE)
|
|
17
17
|
[](https://www.python.org/)
|
|
18
|
-
[](CHANGELOG.md)
|
|
19
19
|
[](docs/STABILITY.md)
|
|
20
20
|
[](tests/)
|
|
21
21
|
[](https://github.com/astral-sh/ruff)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""PEN-STACK v3.0 - open infrastructure for genome writing."""
|
|
2
|
-
__version__ = "6.4.
|
|
2
|
+
__version__ = "6.4.3"
|
|
@@ -28,11 +28,13 @@ from pen_stack.web.tools import extract_grounded_numbers, run_tools
|
|
|
28
28
|
# -------------------------------------------------------------------------------------- system prompts
|
|
29
29
|
SYSTEM_GROUNDED = (
|
|
30
30
|
"You are PEN-STACK's co-scientist for genome writing. The PEN-STACK ENGINE computed the TOOL RESULTS; you "
|
|
31
|
-
"explain and route, but you MUST NOT state any number, score,
|
|
32
|
-
"RESULTS or the METRIC GUIDE provided.
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
31
|
+
"explain and route, but you MUST NOT state any number, score, probability, writer/enzyme name, or delivery "
|
|
32
|
+
"vehicle that is not present in the TOOL RESULTS or the METRIC GUIDE provided. NEVER invent a recommendation, "
|
|
33
|
+
"a markdown table of made-up values, or a 'hypothetical' result — if the engine did not compute something, "
|
|
34
|
+
"say so plainly. For every key number, explain what it MEANS in plain words — its scale, whether higher is "
|
|
35
|
+
"better, the reference band, how it was computed — so the user understands the value, not just sees it. "
|
|
36
|
+
"Surface uncertainty and the scope ledger ('what I can't tell you'). Be concise, friendly, honest. "
|
|
37
|
+
"Decision-support, not a clinical directive."
|
|
36
38
|
)
|
|
37
39
|
SYSTEM_META = (
|
|
38
40
|
"You are PEN-STACK's co-scientist explaining the SYSTEM ITSELF (coverage, methods, accuracy). Answer using "
|
|
@@ -84,12 +86,46 @@ def _fmt(x):
|
|
|
84
86
|
return str(x)
|
|
85
87
|
|
|
86
88
|
|
|
89
|
+
def _narrate_plan(p: dict) -> str | None:
|
|
90
|
+
"""The actual writer/site recommendation as a self-explanatory line (or an honest 'not found' note)."""
|
|
91
|
+
if not p or not p.get("available"):
|
|
92
|
+
return None
|
|
93
|
+
if not p.get("found"):
|
|
94
|
+
return f"**Recommended writer.** {p.get('why', 'no writable plan for this target.')}"
|
|
95
|
+
bits = [f"the top writer family is **{p['recommended_writer']}**"]
|
|
96
|
+
site = p.get("site") or {}
|
|
97
|
+
if site.get("chrom"):
|
|
98
|
+
bits.append(f"at **{site['chrom']}:bin{site.get('bin')}**")
|
|
99
|
+
nums = []
|
|
100
|
+
if p.get("safety") is not None:
|
|
101
|
+
nums.append(f"safety {_fmt(p['safety'])}")
|
|
102
|
+
if p.get("durability") is not None:
|
|
103
|
+
nums.append(f"durability {_fmt(p['durability'])}")
|
|
104
|
+
if p.get("score") is not None:
|
|
105
|
+
nums.append(f"plan score {_fmt(p['score'])}")
|
|
106
|
+
cap = p.get("cargo_capacity_bp")
|
|
107
|
+
fit = p.get("cargo_fits_single_vector")
|
|
108
|
+
cargo_note = ""
|
|
109
|
+
if cap is not None:
|
|
110
|
+
cargo_note = (f" Cargo capacity is **{cap} bp**; the assembled cassette "
|
|
111
|
+
f"({p.get('assembled_bp', '?')} bp) "
|
|
112
|
+
+ ("fits a single vector" if fit else f"exceeds it → delivered as **{p.get('delivery', 'split/dual')}**")
|
|
113
|
+
+ ".")
|
|
114
|
+
alts = p.get("alternative_writers") or []
|
|
115
|
+
alt_note = f" Alternatives: {', '.join(alts)}." if alts else ""
|
|
116
|
+
return (f"**Recommended writer.** For this target {', '.join(bits)} "
|
|
117
|
+
f"({'; '.join(nums)}).{cargo_note}{alt_note} (0–1 scores, higher = better.)")
|
|
118
|
+
|
|
119
|
+
|
|
87
120
|
def _deterministic_narrate(tr: dict) -> str:
|
|
88
121
|
d = tr["parsed_design"]
|
|
89
122
|
v = tr["verdict"]
|
|
90
123
|
lines = [f"I read your goal as a **{d['edit_intent'].replace('_', ' ')}** of **{d['gene']}** "
|
|
91
|
-
f"(~{d['cargo_bp']} bp) by **{d['delivery_vehicle'].replace('_', ' ')}**
|
|
92
|
-
f"Every number below is engine-computed."]
|
|
124
|
+
f"({d['chrom']}, ~{d['cargo_bp']} bp) by **{d['delivery_vehicle'].replace('_', ' ')}** "
|
|
125
|
+
f"in **{d['cell_type']}**. Every number below is engine-computed."]
|
|
126
|
+
plan_line = _narrate_plan(tr.get("plan") or {})
|
|
127
|
+
if plan_line:
|
|
128
|
+
lines.append(plan_line)
|
|
93
129
|
legal = "legal" if v["legal"] else ("deferred" if v["legal"] is None else "ILLEGAL")
|
|
94
130
|
line = f"**Verification.** The design is **{legal}** ({v['epistemic_status']})."
|
|
95
131
|
if v["violations"]:
|
|
@@ -107,13 +143,14 @@ def _deterministic_narrate(tr: dict) -> str:
|
|
|
107
143
|
lines.append(f"**Safety (Guardian).** **{s['decision']}** — {s.get('reason', '')}. {sd}")
|
|
108
144
|
axes = (tr["immune_profile"].get("axes") or {})
|
|
109
145
|
if axes:
|
|
110
|
-
lines.append("**Immune-risk profile** (per-axis — never collapsed
|
|
146
|
+
lines.append("**Immune-risk profile** (per-axis — never collapsed; each is 0–1, higher = safer):")
|
|
111
147
|
for name, a in axes.items():
|
|
112
148
|
gd = guide_for(name) or {}
|
|
113
149
|
val = _fmt(a["value"]) if a.get("value") is not None else "n/a"
|
|
114
150
|
unc = f" ±{_fmt(a['uncertainty'])}" if a.get("uncertainty") is not None else ""
|
|
115
|
-
meaning
|
|
116
|
-
|
|
151
|
+
# prefer the self-explanatory engine 'meaning' (says what the value means + the proxy caveat in words)
|
|
152
|
+
meaning = a.get("meaning") or ((gd.get("means", "").split(".")[0]) if gd else "")
|
|
153
|
+
lines.append(f" • **{gd.get('label', name)}: {val}{unc}** — {meaning}")
|
|
117
154
|
ku = tr["immune_profile"].get("known_unknowns") or []
|
|
118
155
|
if ku:
|
|
119
156
|
lines.append("**What I can't tell you** (measured, never predicted): " + ", ".join(str(x) for x in ku) + ".")
|
|
@@ -353,14 +390,24 @@ def grounded_reply(message: str, history: list | None = None, *, allow_llm: bool
|
|
|
353
390
|
base = {"mode": mode, "provenance": "pen-stack", "grounded": True, "tool_results": tr,
|
|
354
391
|
"angles": None, "facts": None}
|
|
355
392
|
if allow_llm:
|
|
356
|
-
prompt = (f"TOOL RESULTS (the only source of THIS design's numbers
|
|
393
|
+
prompt = (f"TOOL RESULTS (the only source of THIS design's numbers — including the recommended writer in "
|
|
394
|
+
f"`plan`):\n{json.dumps(tr, default=str)}\n\n"
|
|
357
395
|
f"METRIC GUIDE (use to EXPLAIN what each number means — scale, direction, reference band; you "
|
|
358
396
|
f"may cite band thresholds):\n{json.dumps(guides, default=str)}\n"
|
|
359
397
|
f"SAFETY DECISIONS:\n{json.dumps(bands)}\n\n{hist}USER: {message}\n\n"
|
|
360
|
-
f"Reply with: (1) the engine's findings WITH the numbers
|
|
361
|
-
f"
|
|
362
|
-
f"
|
|
398
|
+
f"Reply with: (1) the engine's findings WITH the numbers — if the user asked which writer, lead "
|
|
399
|
+
f"with `plan.recommended_writer` and its site/scores (do NOT name any other writer or vehicle), "
|
|
400
|
+
f"(2) what each key number MEANS in plain words (scale, what's good/bad, reference range), "
|
|
401
|
+
f"(3) the uncertainty + scope ledger. STRICT: use ONLY numbers present in the tool results or the "
|
|
402
|
+
f"metric guide; do NOT invent a writer name, a vehicle, a table, or any number. If something "
|
|
403
|
+
f"isn't in the tool results, say it is not computed.")
|
|
363
404
|
text, backend = _run_llm(prompt, SYSTEM_GROUNDED)
|
|
364
405
|
if text:
|
|
365
|
-
|
|
406
|
+
cleaned = _enforce_grounding(text, allow)
|
|
407
|
+
# Defence against fabrication-spam: if the model invented a lot of numbers (the guard struck many),
|
|
408
|
+
# the narrated reply is unreadable ([unverified] everywhere) — fall back to the deterministic,
|
|
409
|
+
# fully-grounded narration so the user always gets a clean, traceable answer.
|
|
410
|
+
if cleaned.count(_UNVERIFIED) >= 2:
|
|
411
|
+
return {**base, "reply": _deterministic_narrate(tr), "backend": f"deterministic (guard:{backend})"}
|
|
412
|
+
return {**base, "reply": cleaned, "backend": backend}
|
|
366
413
|
return {**base, "reply": _deterministic_narrate(tr), "backend": "deterministic"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""Engine tool-runner for the grounded co-scientist (PEN-STACK v6.2, WS-CHAT support).
|
|
2
|
+
|
|
3
|
+
The chat NEVER sources a number. This module is where the ENGINE computes everything: it parses a plain-language
|
|
4
|
+
goal, runs the validated tools (verify -> legality + safety + calibrated confidence + immune profile; the scope
|
|
5
|
+
matcher for known-unknowns), and returns a structured "dossier" of grounded facts. `extract_grounded_numbers`
|
|
6
|
+
returns the allow-list of values the LLM is permitted to cite — anything else it emits is stripped by the
|
|
7
|
+
grounding guard. No LLM is involved here; this is deterministic.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
# tiny, transparent keyword maps (the engine grounds everything; this only routes a plain-language goal).
|
|
15
|
+
_VEHICLES = {"aav": "AAV_single", "aav single": "AAV_single", "aav dual": "AAV_dual", "dual aav": "AAV_dual",
|
|
16
|
+
"lentivir": "lentivirus", "lnp": "lnp_mrna", "mrna": "lnp_mrna", "adenovir": "helper_dependent_adenovirus",
|
|
17
|
+
"hsv": "hsv_amplicon", "electroporat": "electroporation"}
|
|
18
|
+
_INTENTS = {"safe harbour": "safe_harbour_insertion", "safe harbor": "safe_harbour_insertion",
|
|
19
|
+
"knock-in": "knock_in_with_disruption", "knock in": "knock_in_with_disruption",
|
|
20
|
+
"knockin": "knock_in_with_disruption", "durab": "high_durability_insertion",
|
|
21
|
+
"excis": "regulatory_element_excision", "regulator": "regulatory_element_excision",
|
|
22
|
+
"landing pad": "landing_pad_insertion", "repeat": "repeat_excision"}
|
|
23
|
+
_CELLS = {"liver": "hepg2", "hepato": "hepg2", "hepg2": "hepg2", "hspc": "hspc", "stem cell": "h1_hesc",
|
|
24
|
+
"ipsc": "ipsc", "k562": "k562", "t cell": "cd8_t", "t-cell": "cd8_t", "car-t": "cd8_t", "pbmc": "pbmc"}
|
|
25
|
+
_GENE_RE = re.compile(r"\b([A-Z][A-Z0-9]{1,7})\b") # crude gene-symbol token (AAVS1, TRAC, FIX, ...)
|
|
26
|
+
_KB_RE = re.compile(r"(\d+(?:\.\d+)?)\s*kb", re.I)
|
|
27
|
+
_BP_RE = re.compile(r"(\d{3,6})\s*bp", re.I)
|
|
28
|
+
# uppercase tokens that look like a gene but are not one (jargon / vehicle / form abbreviations)
|
|
29
|
+
_GENE_STOP = {"DNA", "RNA", "MRNA", "AAV", "LNP", "HSV", "CAR", "RNP", "PCR", "WT", "KO", "KI", "ITR",
|
|
30
|
+
"ORF", "UTR", "CDS", "GFP", "ID", "QC", "VG", "MOI", "HLA", "MHC", "CRISPR", "CAS", "TF"}
|
|
31
|
+
# safe-harbour locus nicknames whose text collides with a vehicle keyword (AAVS1 ⊃ "aav") — stripped before
|
|
32
|
+
# vehicle matching so the user's stated vehicle (e.g. lentivirus) is not overridden by the locus name.
|
|
33
|
+
_SAFE_HARBOUR_RE = re.compile(r"\b(aavs1|h11|hipp11|rosa26)\b", re.I)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _first(text: str, table: dict, default):
|
|
37
|
+
low = text.lower()
|
|
38
|
+
for key, val in table.items():
|
|
39
|
+
if key in low:
|
|
40
|
+
return val
|
|
41
|
+
return default
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _resolve_chrom(gene: str) -> str | None:
|
|
45
|
+
"""The real chromosome for a gene symbol (or a safe-harbour nickname like AAVS1); None if not resolvable
|
|
46
|
+
offline. So a chat goal about ITGB2 carries chr21, not a hardcoded default. Atlas-gated — never fabricates."""
|
|
47
|
+
try:
|
|
48
|
+
from pen_stack.planner.optimize import gene_region, resolve_gene
|
|
49
|
+
reg = gene_region(resolve_gene(gene))
|
|
50
|
+
return reg[0] if reg else None
|
|
51
|
+
except Exception: # noqa: BLE001 - data/atlas absent (CI/offline) -> caller falls back to the default
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def parse_goal(message: str) -> dict:
|
|
56
|
+
"""Best-effort parse of a plain-language goal into a Design/Goal dict. The engine grounds everything; this
|
|
57
|
+
just picks a starting point (with sensible defaults) so the tools can run."""
|
|
58
|
+
cargo = 3000
|
|
59
|
+
if (m := _KB_RE.search(message)):
|
|
60
|
+
cargo = int(float(m.group(1)) * 1000)
|
|
61
|
+
elif (m := _BP_RE.search(message)):
|
|
62
|
+
cargo = int(m.group(1))
|
|
63
|
+
genes = [g for g in _GENE_RE.findall(message) if g not in _GENE_STOP]
|
|
64
|
+
gene = genes[0] if genes else "AAVS1"
|
|
65
|
+
# Vehicle matching is substring-based (so "lentivir" catches "lentiviral"); but the safe-harbour nickname
|
|
66
|
+
# "AAVS1" contains "aav", which would wrongly match the AAV vehicle even when the user said lentivirus/LNP.
|
|
67
|
+
# Strip those nicknames from the vehicle-search text first so the stated vehicle wins.
|
|
68
|
+
veh_text = _SAFE_HARBOUR_RE.sub(" ", message.lower())
|
|
69
|
+
return {"write_type": "insertion", "gene": gene, "chrom": _resolve_chrom(gene) or "chr19",
|
|
70
|
+
"edit_intent": _first(message, _INTENTS, "safe_harbour_insertion"),
|
|
71
|
+
"delivery_vehicle": _first(veh_text, _VEHICLES, "AAV_single"), "cargo_bp": cargo,
|
|
72
|
+
"cell_type": _first(message, _CELLS, "k562"),
|
|
73
|
+
# the user's plain-language goal IS the cargo-function description the Guardian must screen — so a
|
|
74
|
+
# message like "express a ricin toxin" is biosecurity-screened, not silently passed as benign.
|
|
75
|
+
"cargo_function": message.strip()}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# the chat's free-text intents map onto the planner's EditIntent enum (landing-pad/regulatory have nearest valid).
|
|
79
|
+
_INTENT_TO_ENUM = {"safe_harbour_insertion": "safe_harbour_insertion",
|
|
80
|
+
"high_durability_insertion": "high_durability_insertion",
|
|
81
|
+
"knock_in_with_disruption": "knock_in_with_disruption",
|
|
82
|
+
"regulatory_element_excision": "regulatory_excision",
|
|
83
|
+
"repeat_excision": "repeat_excision",
|
|
84
|
+
"landing_pad_insertion": "safe_harbour_insertion"}
|
|
85
|
+
|
|
86
|
+
# plain-language reading of each immune axis (0–1, higher = safer) — so a value is self-explanatory in the reply.
|
|
87
|
+
_AXIS_DIRECTION = {
|
|
88
|
+
"genotoxicity": "higher = safer (less integration-site oncogene risk; 1.0 = episomal / non-integrating)",
|
|
89
|
+
"cd8_epitope": "higher = fewer strong CD8/MHC-I capsid epitopes (1.0 = non-viral / none)",
|
|
90
|
+
"innate": "higher = less innate (CpG/TLR9, RIG-I) sensing of the delivered cargo",
|
|
91
|
+
"preexisting_nab": "higher = fewer patients excluded by pre-existing neutralizing antibodies to the vector",
|
|
92
|
+
"anti_peg": "higher = lower pre-existing anti-PEG barrier to re-dosing (PEGylated vehicles only)",
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _band_word(v: float) -> str:
|
|
97
|
+
return "favourable" if v >= 0.7 else ("moderate" if v >= 0.4 else "a concern")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def axis_meaning(name: str, value, validation: str | None) -> str:
|
|
101
|
+
"""A self-explanatory, plain-language reading of one immune axis: what the number means + the proxy caveat."""
|
|
102
|
+
if value is None:
|
|
103
|
+
return "out of scope for this design — not predicted (no applicable mechanism)."
|
|
104
|
+
direction = _AXIS_DIRECTION.get(name, "0–1, higher = safer")
|
|
105
|
+
s = f"{float(value):.2f} on a 0–1 scale — {_band_word(float(value))}; {direction}."
|
|
106
|
+
if validation and ("proxy" in validation.lower() or "not outcome-validated" in validation.lower()):
|
|
107
|
+
s += (" This is a mechanistically/population-computed PROXY — it is not validated against a measured "
|
|
108
|
+
"clinical outcome, so read it as a directional estimate, not a guaranteed result.")
|
|
109
|
+
return s
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _run_planner(design: dict) -> dict[str, Any]:
|
|
113
|
+
"""The ACTUAL writer/site recommendation for the goal (atlas-gated). This is what makes a 'which writer can
|
|
114
|
+
integrate N kb in GENE' question real: a named writer family, the top site, cargo-capacity fit, delivery —
|
|
115
|
+
all engine-computed. Honest when the gene isn't in the atlas or the atlas isn't mounted; NEVER fabricates."""
|
|
116
|
+
try:
|
|
117
|
+
from pen_stack.planner.optimize import EditIntent
|
|
118
|
+
from pen_stack.planner.pipeline import plan_write
|
|
119
|
+
except Exception: # noqa: BLE001
|
|
120
|
+
return {"available": False, "why": "planner unavailable in this environment"}
|
|
121
|
+
intent = _INTENT_TO_ENUM.get(design.get("edit_intent"), "safe_harbour_insertion")
|
|
122
|
+
try:
|
|
123
|
+
plans = plan_write(design["gene"], EditIntent(intent), int(design["cargo_bp"]),
|
|
124
|
+
design.get("cell_type", "k562"), k=3)
|
|
125
|
+
except FileNotFoundError:
|
|
126
|
+
return {"available": False, "why": "writability atlas not mounted (planner runs on the live app only)"}
|
|
127
|
+
except Exception as e: # noqa: BLE001 - never let a planner error break the chat dossier
|
|
128
|
+
return {"available": False, "why": f"planner error: {type(e).__name__}"}
|
|
129
|
+
if not plans:
|
|
130
|
+
return {"available": True, "found": False, "gene": design["gene"],
|
|
131
|
+
"why": (f"no writable plan for '{design['gene']}' — it is not in the writability atlas. Check it "
|
|
132
|
+
"is an HGNC gene symbol or a known safe-harbour nickname (AAVS1, H11/HIPP11).")}
|
|
133
|
+
top = plans[0]
|
|
134
|
+
cargo = top.get("cargo") or {}
|
|
135
|
+
return {"available": True, "found": True, "n_plans": len(plans),
|
|
136
|
+
"recommended_writer": top.get("writer"), "site": top.get("site"),
|
|
137
|
+
"safety": top.get("safety"), "durability": top.get("durability"), "score": top.get("score"),
|
|
138
|
+
"writer_activity": top.get("writer_activity"), "reachability_tier": top.get("reachability_tier"),
|
|
139
|
+
"cargo_capacity_bp": cargo.get("cargo_capacity_bp"), "assembled_bp": cargo.get("assembled_bp"),
|
|
140
|
+
"cargo_fits_single_vector": cargo.get("size_ok"),
|
|
141
|
+
"delivery": (top.get("delivery") or {}).get("delivery"),
|
|
142
|
+
# distinct OTHER writer families (the top-k can repeat one family across sites — don't list it twice)
|
|
143
|
+
"alternative_writers": sorted({p.get("writer") for p in plans[1:]
|
|
144
|
+
if p.get("writer") and p.get("writer") != top.get("writer")})}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def run_tools(message: str, history: list | None = None) -> dict[str, Any]:
|
|
148
|
+
"""Run the validated engine over a plain-language message and return a grounded dossier. EVERY number here
|
|
149
|
+
is computed by the engine (verify / planner / scope) — no fabrication, no LLM."""
|
|
150
|
+
from pen_stack.agent.scope import match_scope
|
|
151
|
+
from pen_stack.verify import verify
|
|
152
|
+
|
|
153
|
+
design = parse_goal(message)
|
|
154
|
+
v = verify(dict(design), question=message)
|
|
155
|
+
imm = v.immune_profile or {}
|
|
156
|
+
axes = {k: {"value": a.get("value"), "uncertainty": a.get("uncertainty"),
|
|
157
|
+
"validation": a.get("validation"), "in_scope": a.get("in_scope"),
|
|
158
|
+
"meaning": axis_meaning(k, a.get("value"), a.get("validation"))}
|
|
159
|
+
for k, a in (imm.get("axes") or {}).items()}
|
|
160
|
+
oos = match_scope(message) # is the QUESTION out of scope (a known-unknown)?
|
|
161
|
+
return {
|
|
162
|
+
"parsed_design": design,
|
|
163
|
+
"plan": _run_planner(design), # the actual writer/site recommendation (atlas-gated)
|
|
164
|
+
"verdict": {"legal": v.legal, "confidence": v.confidence, "interval": v.interval,
|
|
165
|
+
"epistemic_status": v.epistemic_status,
|
|
166
|
+
"violations": [x.get("rule_id") for x in v.violations]},
|
|
167
|
+
"safety": {"decision": (v.safety.decision if v.safety else None),
|
|
168
|
+
"reason": (v.safety.reason if v.safety else None)},
|
|
169
|
+
"immune_profile": {"axes": axes, "collapsed_score": imm.get("collapsed_score"),
|
|
170
|
+
"known_unknowns": imm.get("known_unknowns")},
|
|
171
|
+
"scope": ({"out_of_scope": True, "id": oos["id"], "title": oos["title"], "why": oos.get("deferral")}
|
|
172
|
+
if oos else {"out_of_scope": False}),
|
|
173
|
+
"disclaimer": "Decision-support only; not a clinical directive. Every number is tool-sourced.",
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
_NUM_RE = re.compile(r"-?\d+(?:\.\d+)?")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def extract_grounded_numbers(tool_results: dict) -> set[str]:
|
|
181
|
+
"""The allow-list: every numeric string that appears in the engine's tool results. The grounding guard
|
|
182
|
+
permits the LLM to cite ONLY these; any other number it emits is stripped."""
|
|
183
|
+
import json
|
|
184
|
+
text = json.dumps(tool_results, default=str)
|
|
185
|
+
grounded = set(_NUM_RE.findall(text))
|
|
186
|
+
# also allow the common normalised forms (e.g. 0.5 / .5 / 50%) of each grounded value
|
|
187
|
+
extra = set()
|
|
188
|
+
for n in list(grounded):
|
|
189
|
+
try:
|
|
190
|
+
f = float(n)
|
|
191
|
+
except ValueError:
|
|
192
|
+
continue
|
|
193
|
+
extra.add(str(int(f)) if f.is_integer() else str(f))
|
|
194
|
+
extra.add(f"{f:.2f}")
|
|
195
|
+
if 0 <= f <= 1:
|
|
196
|
+
extra.add(str(round(f * 100))) # percent form of a [0,1] score
|
|
197
|
+
return grounded | extra
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pen-stack
|
|
3
|
-
Version: 6.4.
|
|
3
|
+
Version: 6.4.3
|
|
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
|
|
@@ -90,7 +90,7 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
|
|
|
90
90
|
[](https://codecov.io/gh/ahmedanees-m/pen-stack)
|
|
91
91
|
[](LICENSE)
|
|
92
92
|
[](https://www.python.org/)
|
|
93
|
-
[](CHANGELOG.md)
|
|
94
94
|
[](docs/STABILITY.md)
|
|
95
95
|
[](tests/)
|
|
96
96
|
[](https://github.com/astral-sh/ruff)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pen-stack"
|
|
7
|
-
version = "6.4.
|
|
7
|
+
version = "6.4.3"
|
|
8
8
|
description = "Open infrastructure for genome writing: the Writable Genome atlas, the Writer Atlas, and the Write Planner."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
"""Engine tool-runner for the grounded co-scientist (PEN-STACK v6.2, WS-CHAT support).
|
|
2
|
-
|
|
3
|
-
The chat NEVER sources a number. This module is where the ENGINE computes everything: it parses a plain-language
|
|
4
|
-
goal, runs the validated tools (verify -> legality + safety + calibrated confidence + immune profile; the scope
|
|
5
|
-
matcher for known-unknowns), and returns a structured "dossier" of grounded facts. `extract_grounded_numbers`
|
|
6
|
-
returns the allow-list of values the LLM is permitted to cite — anything else it emits is stripped by the
|
|
7
|
-
grounding guard. No LLM is involved here; this is deterministic.
|
|
8
|
-
"""
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
import re
|
|
12
|
-
from typing import Any
|
|
13
|
-
|
|
14
|
-
# tiny, transparent keyword maps (the engine grounds everything; this only routes a plain-language goal).
|
|
15
|
-
_VEHICLES = {"aav": "AAV_single", "aav single": "AAV_single", "aav dual": "AAV_dual", "dual aav": "AAV_dual",
|
|
16
|
-
"lentivir": "lentivirus", "lnp": "lnp_mrna", "mrna": "lnp_mrna", "adenovir": "helper_dependent_adenovirus",
|
|
17
|
-
"hsv": "hsv_amplicon", "electroporat": "electroporation"}
|
|
18
|
-
_INTENTS = {"safe harbour": "safe_harbour_insertion", "safe harbor": "safe_harbour_insertion",
|
|
19
|
-
"knock-in": "knock_in_with_disruption", "knock in": "knock_in_with_disruption",
|
|
20
|
-
"knockin": "knock_in_with_disruption", "durab": "high_durability_insertion",
|
|
21
|
-
"excis": "regulatory_element_excision", "regulator": "regulatory_element_excision",
|
|
22
|
-
"landing pad": "landing_pad_insertion", "repeat": "repeat_excision"}
|
|
23
|
-
_CELLS = {"liver": "hepg2", "hepato": "hepg2", "hepg2": "hepg2", "hspc": "hspc", "stem cell": "h1_hesc",
|
|
24
|
-
"ipsc": "ipsc", "k562": "k562", "t cell": "cd8_t", "t-cell": "cd8_t", "car-t": "cd8_t", "pbmc": "pbmc"}
|
|
25
|
-
_GENE_RE = re.compile(r"\b([A-Z][A-Z0-9]{1,7})\b") # crude gene-symbol token (AAVS1, TRAC, FIX, ...)
|
|
26
|
-
_KB_RE = re.compile(r"(\d+(?:\.\d+)?)\s*kb", re.I)
|
|
27
|
-
_BP_RE = re.compile(r"(\d{3,6})\s*bp", re.I)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _first(text: str, table: dict, default):
|
|
31
|
-
low = text.lower()
|
|
32
|
-
for key, val in table.items():
|
|
33
|
-
if key in low:
|
|
34
|
-
return val
|
|
35
|
-
return default
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def parse_goal(message: str) -> dict:
|
|
39
|
-
"""Best-effort parse of a plain-language goal into a Design/Goal dict. The engine grounds everything; this
|
|
40
|
-
just picks a starting point (with sensible defaults) so the tools can run."""
|
|
41
|
-
cargo = 3000
|
|
42
|
-
if (m := _KB_RE.search(message)):
|
|
43
|
-
cargo = int(float(m.group(1)) * 1000)
|
|
44
|
-
elif (m := _BP_RE.search(message)):
|
|
45
|
-
cargo = int(m.group(1))
|
|
46
|
-
genes = [g for g in _GENE_RE.findall(message) if g not in {"DNA", "RNA", "AAV", "LNP", "HSV", "CAR"}]
|
|
47
|
-
gene = genes[0] if genes else "AAVS1"
|
|
48
|
-
return {"write_type": "insertion", "gene": gene, "chrom": "chr19",
|
|
49
|
-
"edit_intent": _first(message, _INTENTS, "safe_harbour_insertion"),
|
|
50
|
-
"delivery_vehicle": _first(message, _VEHICLES, "AAV_single"), "cargo_bp": cargo,
|
|
51
|
-
"cell_type": _first(message, _CELLS, "k562"),
|
|
52
|
-
# the user's plain-language goal IS the cargo-function description the Guardian must screen — so a
|
|
53
|
-
# message like "express a ricin toxin" is biosecurity-screened, not silently passed as benign.
|
|
54
|
-
"cargo_function": message.strip()}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def run_tools(message: str, history: list | None = None) -> dict[str, Any]:
|
|
58
|
-
"""Run the validated engine over a plain-language message and return a grounded dossier. EVERY number here
|
|
59
|
-
is computed by the engine (verify / scope) — no fabrication, no LLM."""
|
|
60
|
-
from pen_stack.agent.scope import match_scope
|
|
61
|
-
from pen_stack.verify import verify
|
|
62
|
-
|
|
63
|
-
design = parse_goal(message)
|
|
64
|
-
v = verify(dict(design), question=message)
|
|
65
|
-
imm = v.immune_profile or {}
|
|
66
|
-
axes = {k: {"value": a.get("value"), "uncertainty": a.get("uncertainty"),
|
|
67
|
-
"validation": a.get("validation"), "in_scope": a.get("in_scope")}
|
|
68
|
-
for k, a in (imm.get("axes") or {}).items()}
|
|
69
|
-
oos = match_scope(message) # is the QUESTION out of scope (a known-unknown)?
|
|
70
|
-
return {
|
|
71
|
-
"parsed_design": design,
|
|
72
|
-
"verdict": {"legal": v.legal, "confidence": v.confidence, "interval": v.interval,
|
|
73
|
-
"epistemic_status": v.epistemic_status,
|
|
74
|
-
"violations": [x.get("rule_id") for x in v.violations]},
|
|
75
|
-
"safety": {"decision": (v.safety.decision if v.safety else None),
|
|
76
|
-
"reason": (v.safety.reason if v.safety else None)},
|
|
77
|
-
"immune_profile": {"axes": axes, "collapsed_score": imm.get("collapsed_score"),
|
|
78
|
-
"known_unknowns": imm.get("known_unknowns")},
|
|
79
|
-
"scope": ({"out_of_scope": True, "id": oos["id"], "title": oos["title"], "why": oos.get("deferral")}
|
|
80
|
-
if oos else {"out_of_scope": False}),
|
|
81
|
-
"disclaimer": "Decision-support only; not a clinical directive. Every number is tool-sourced.",
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
_NUM_RE = re.compile(r"-?\d+(?:\.\d+)?")
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def extract_grounded_numbers(tool_results: dict) -> set[str]:
|
|
89
|
-
"""The allow-list: every numeric string that appears in the engine's tool results. The grounding guard
|
|
90
|
-
permits the LLM to cite ONLY these; any other number it emits is stripped."""
|
|
91
|
-
import json
|
|
92
|
-
text = json.dumps(tool_results, default=str)
|
|
93
|
-
grounded = set(_NUM_RE.findall(text))
|
|
94
|
-
# also allow the common normalised forms (e.g. 0.5 / .5 / 50%) of each grounded value
|
|
95
|
-
extra = set()
|
|
96
|
-
for n in list(grounded):
|
|
97
|
-
try:
|
|
98
|
-
f = float(n)
|
|
99
|
-
except ValueError:
|
|
100
|
-
continue
|
|
101
|
-
extra.add(str(int(f)) if f.is_integer() else str(f))
|
|
102
|
-
extra.add(f"{f:.2f}")
|
|
103
|
-
if 0 <= f <= 1:
|
|
104
|
-
extra.add(str(round(f * 100))) # percent form of a [0,1] score
|
|
105
|
-
return grounded | extra
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|