pen-stack 6.13.0__tar.gz → 6.14.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pen_stack-6.13.0 → pen_stack-6.14.0}/CHANGELOG.md +34 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/CITATION.cff +1 -1
- {pen_stack-6.13.0 → pen_stack-6.14.0}/PKG-INFO +3 -1
- pen_stack-6.14.0/benchmarks/writespec/SHA256SUMS +4 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/offtarget.md +4 -2
- pen_stack-6.14.0/docs/writespec_bench.md +44 -0
- pen_stack-6.14.0/docs/writespec_profile.md +58 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/__init__.py +1 -1
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/mcp_server.py +12 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/api/manifest.py +6 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/server/api.py +12 -0
- pen_stack-6.14.0/pen_stack/spec/__init__.py +18 -0
- pen_stack-6.14.0/pen_stack/spec/clarify.py +42 -0
- pen_stack-6.14.0/pen_stack/spec/extract.py +232 -0
- pen_stack-6.14.0/pen_stack/spec/resolvers/__init__.py +17 -0
- pen_stack-6.14.0/pen_stack/spec/resolvers/cell.py +51 -0
- pen_stack-6.14.0/pen_stack/spec/resolvers/chem.py +32 -0
- pen_stack-6.14.0/pen_stack/spec/resolvers/feature.py +36 -0
- pen_stack-6.14.0/pen_stack/spec/resolvers/gene.py +43 -0
- pen_stack-6.14.0/pen_stack/spec/resolvers/locus.py +29 -0
- pen_stack-6.14.0/pen_stack/spec/resolvers/phenotype.py +37 -0
- pen_stack-6.14.0/pen_stack/spec/satisfy.py +114 -0
- pen_stack-6.14.0/pen_stack/spec/service.py +33 -0
- pen_stack-6.14.0/pen_stack/spec/writespec.py +244 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/offtarget_predict.py +4 -2
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack.egg-info/PKG-INFO +3 -1
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack.egg-info/SOURCES.txt +18 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack.egg-info/requires.txt +3 -0
- pen_stack-6.14.0/prereg/SHA256_LOCK_ws_writespec.json +8 -0
- pen_stack-6.14.0/prereg/ws_writespec.yaml +50 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pyproject.toml +2 -1
- {pen_stack-6.13.0 → pen_stack-6.14.0}/LICENSE +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/MANIFEST.in +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/README.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/bench/run.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/delivery/SHA256SUMS +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/genome_writing_bench/LEADERBOARD.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/genome_writing_bench/README.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/genome_writing_bench/SHA256SUMS +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/genome_writing_bench/tasks.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/genome_writing_challenge/README.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/genome_writing_challenge/SUBMISSIONS.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/offtarget/SHA256SUMS +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/oracle/SHA256SUMS +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/position_effect/README.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/position_effect/SHA256SUMS +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/verify/SHA256SUMS +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/writer_efficiency/README.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/benchmarks/writer_efficiency/SHA256SUMS +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/aav_serotype_tropism.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/antipeg.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/atlas_families.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/bridge_offtarget_profile.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/calibration/preexisting_nab_independent.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/capsid_epitope_oracle.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/capsid_sequences.fasta +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/cargo_polish.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/cell_types.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/datasets.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/delivery_constraints.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/delivery_rules.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/delivery_vehicles.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/expression/modifiers.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/expression/promoters.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/gates_v3.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/genotoxicity_oracle.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/gsh_validated_heldout.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/intent_weights.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/known_unknowns.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/llm.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/metric_guide.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/mhc_epitope_oracle.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/monitor_queries.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/oracles/execution.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/oracles/reliability.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/oracles/scope_cards.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/rules/delivery.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/rules/fold.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/rules/multiplex.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/rules/payload.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/rules/reachability.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/safety/hazard_registry.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/safety/policy.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/safety/probes.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/score_axes.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/seroprevalence.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/target_sites.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/universe_crosswalk.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/write_types.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/writer_sequences.fasta +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/configs/wtkb_curated.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/data/curated/bridge_offtarget_energetics.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/data/curated/gene_coords.parquet +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/data/curated/unified_editor_universe.parquet +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/BACKLOG.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/DEPLOY.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/INFRA.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/MCP.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/RELEASING.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/REPRO.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/STABILITY.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/agent.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/alphagenome_feasibility.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/autonomy.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/benchmark_circularity.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/biosecurity.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/build_interface.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/cards/atlas.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/cards/durability.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/cards/offtarget_data.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/cards/position_effect_data.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/cards/safety.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/cards/writer_efficiency_data.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/challenge.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/closed_loop.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/co_scientist.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/co_scientist_loop.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/delivery.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/delivery_immunology.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/delivery_recommender.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/digital_twin.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/dissemination.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/environment.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/experiment_design.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/generative_design.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/immune_profiler.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/index.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/integrations.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/live_oracles.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/mechanistic_constraints.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/oracle_mesh.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/oracles.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/position_effect.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/positioning.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/private_data_formats.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/quickstart.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/responsible_use.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/rule_spec.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/rules.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/scope.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/scorecard.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/tpe_bench.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/tutorials/compare-families.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/tutorials/score-deliverability.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/tutorials/where-can-i-write.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/tutorials/which-writer-reaches-locus.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/uncertainty.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/verify.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/verify_service.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/world_model.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/writer_efficiency.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/writer_verification.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/docs/wtkb.md +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/_resources.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/active/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/active/acquire.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/active/design.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/active/validate.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/adapt/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/adapt/finetune.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/adapt/ingest.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/adapt/pipeline.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/adapt/recalibrate.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/adapt/report.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/cite.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/co_scientist.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/epistemic.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/guardrails.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/orchestrator.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/orchestrator_live.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/pen_agent.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/scope.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/agent/tools.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/api/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/build_wtkb.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/crosslink.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/expand.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/guide_design.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/schema.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/scorecard.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/universe.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/variant_propose.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/writer_efficiency.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/writer_predict.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/writer_recommend.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/atlas/writer_verify.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/bridge/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/bridge/activity.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/bridge/cli.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/bridge/fold_qc.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/bridge/guide_qc.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/bridge/ingest.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/bridge/offtarget.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/bridge/offtarget_energetics.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/bridge/ortholog_screen.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/bridge/pipeline.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/build/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/build/ingest.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/build/protocol.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/build/simlab.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/cli.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/data/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/data/encode.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/data/genome.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/data/ingest_chromatin.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/data/ingest_integration.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/data/ingest_safety_annot.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/data/ingest_trip.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/design/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/design/capsid_generate.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/design/generate.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/design/pareto.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/design/space.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/design/writer_variants.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/env/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/env/genome_writing_env.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/env/policies.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/graph/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/graph/build.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/graph/cell_types.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/graph/ingest.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/graph/query.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/graph/schema.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/loop/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/loop/continual.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/loop/cycle.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/loop/drift.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/mech/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/mech/classify_atlas.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/mech/whitelist.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/monitor/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/monitor/europepmc.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/monitor/run.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/monitor/triage.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/affinity.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/cache.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/energetics.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/genome.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/protein_design.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/reliability.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/rna.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/schema.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/status.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/structure.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/structure_run.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/oracles/vcell.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/ada_risk.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/antipeg_oracle.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/capsid_epitope_oracle.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/cargo.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/cargo_polish.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/delivery.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/delivery_constraints.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/delivery_immune.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/delivery_immunology.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/delivery_predict.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/delivery_vehicles.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/genotoxicity_oracle.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/immune_mhc2.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/immune_profile.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/innate_sensing.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/multiplex.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/optimize.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/pipeline.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/report.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/router.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/seroprevalence_oracle.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/planner/target_site.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/rag/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/rag/index.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/rag/llm.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/rag/qa.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/rules/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/rules/evaluators.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/rules/loader.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/rules/schema.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/rules/solver.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/rules/spec.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/safety/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/safety/audit.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/safety/gate.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/safety/policy.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/safety/redteam.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/safety/registry.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/safety/screen.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/safety/standards.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/score/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/score/recalibrate.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/score/therapeutic.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/server/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/twin/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/twin/calibrate.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/twin/data/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/twin/data/position_effect.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/twin/mechanistic.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/twin/outcome.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/twin/position_effect.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/ui/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/ui/app.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/adapt_demo.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/agent_eval.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/bench_coscientist_tasks.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/bench_graph_tasks.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/bench_rule_tasks.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/bench_trust_tasks.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/bench_writetype_tasks.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/blind_gsh_discovery.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/cargo_directionality.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/closed_loop.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/durability_baselines.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/experiment_design.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/expr_controls.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/forward_hypotheses.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/generative_design.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/guide_qc_demo.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/heldout_celltype_expr.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/immune_calibration.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/intent_specification.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/known_biology_expr.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/out_of_scope_refusal.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/outcome_calibration.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/outcome_prediction.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/paper3_benchmark.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/paper4_real_validation.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/paper4_validation.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/protocol_safety.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/safety_screening.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/selective_prediction.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/seq_vs_measured.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/target_site_controls.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/uncertainty_eval.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/ungrounded_baseline.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/within_locus_ranking.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/validate/writer_recovery.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/verify/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/verify/proof.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/verify/schema.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/verify/service.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/web/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/web/guide.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/web/llm.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/web/router.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/web/server.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/web/tools.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/__init__.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/chromatin_seq.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/durability.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/export_tracks.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/features.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/gsh_baseline.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/mesh_features.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/offtarget_assay.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/offtarget_data.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/ood.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/providers.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/safety.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/structure3d.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/uncertainty.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack/wgenome/writability.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack.egg-info/dependency_links.txt +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack.egg-info/entry_points.txt +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/pen_stack.egg-info/top_level.txt +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_phase0.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_phase1_5.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_phase2.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_phase3.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_a.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_acq.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_aldesign.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_alvalidate.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_b.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_ba.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_bench.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_c.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_cal.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_calib.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_challenge.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_chat.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_cite.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_continual.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_cosci2.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_crit.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_ct.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_d.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_delivery.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_drift.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_e.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_env.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_ep.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_epitope.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_expr2.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_f.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_frontend.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_g.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_gen.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_genotox.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_graph.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_h.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_hybrid.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_immune.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_immune2.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_ingest.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_innate.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_loop.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_manifest.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_mc.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_mcp.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_mech.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_mon.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_o.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_offtarget.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_openapi.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_oracle.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_orch.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_outcome.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_pareto.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_peg.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_plan.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_policy.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_profile.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_proto.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_r.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_redteam.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_route.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_screen.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_seroprev.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_simlab.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_twincal.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_uq.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_v.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_vcell.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_verify.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_writer.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/SHA256_LOCK_ws_wv.json +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/paper1.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/paper2.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/paper3.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/paper4.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/phase0.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_a.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_acq.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_aldesign.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_alvalidate.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_atlas.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_b.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_ba.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_ba_v33.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_ba_v45.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_bench.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_c.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_cal.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_calib.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_challenge.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_chat.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_cite.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_continual.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_cosci2.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_crit.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_ct.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_d.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_delivery.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_drift.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_e.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_env.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_ep.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_epitope.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_expr2.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_f.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_frontend.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_g.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_gen.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_genotox.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_graph.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_h.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_hybrid.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_immune.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_immune2.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_ingest.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_innate.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_loop.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_manifest.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_mc.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_mcp.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_mech.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_mon.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_o.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_offtarget.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_openapi.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_oracle.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_orch.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_outcome.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_pareto.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_peg.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_plan.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_policy.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_profile.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_proto.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_r.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_redteam.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_route.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_screen.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_seroprev.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_simlab.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_twincal.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_uq.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_v.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_vcell.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_verify.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_writer.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/prereg/ws_wv.yaml +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/build_capsid_fitness.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/calibrate_immune_axes.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/fetch_licensed_sources.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/offtarget_chromatin_incremental.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/offtarget_chromatin_matched.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/offtarget_chromatin_validation.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p1_build_atlas.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p1_build_durability.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p1_build_position_effect.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p1_build_writer_eff.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p1_export_tracks.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p1_safety_concordance.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p1_train_safety.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p1_validation_report.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p2_build_atlas.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p3_benchmark_report.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p4_genome_scan.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p52_build_genotox_oracle.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/p53_build_epitope_oracle.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/ws_b_report.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/scripts/ws_c_report.py +0 -0
- {pen_stack-6.13.0 → pen_stack-6.14.0}/setup.cfg +0 -0
|
@@ -3,6 +3,40 @@
|
|
|
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
|
+
## [6.14.0] - 2026-06-22 - WriteSpec: a typed, ontology-backed intent layer with a grounded extractor and a feasibility check
|
|
7
|
+
|
|
8
|
+
Replaces the keyword parser with a typed, machine-checkable genome-writing request (an SBOL3 profile), a grounded
|
|
9
|
+
prose-to-spec extractor that labels every inference and never fabricates intent, and a feasibility check. This is
|
|
10
|
+
the agentic front-end: one contract the whole stack consumes. A WriteSpec is a request, not a claim.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **The WriteRequest type** (`pen_stack/spec/writespec.py`, `schemas/writespec.json`). A typed model carrying the
|
|
14
|
+
write semantics (write-type, cargo with Sequence-Ontology roles, target gene/locus/att-site/phenotype, cell
|
|
15
|
+
type, constraints) plus a per-field provenance map (explicit / inferred / user / unresolved). Lossless JSON
|
|
16
|
+
round-trip; SBOL3 round-trip via the real `sbol3` library (the `[spec]` extra; native Components + SO roles for
|
|
17
|
+
interoperability); GenBank export for a cargo with a sequence; and `to_legacy_design`, the adapter that lets
|
|
18
|
+
every existing stage consume a WriteRequest without a rewrite.
|
|
19
|
+
- **Vocabulary resolvers** (`pen_stack/spec/resolvers/`). Free text to canonical id: HGNC genes (atlas-grounded),
|
|
20
|
+
Cellosaurus / Cell Ontology cell types, Sequence Ontology feature roles, MONDO phenotypes, ChEBI molecules,
|
|
21
|
+
GRCh38 coordinates. Every curated id was verified against the live ontology services before commit; an
|
|
22
|
+
unresolved term stays null, never invented.
|
|
23
|
+
- **Grounded extractor + clarifying-question planner** (`pen_stack/spec/extract.py`, `clarify.py`). A
|
|
24
|
+
deterministic backbone (so the benchmark is reproducible) that labels every inferred field, asks a clarifying
|
|
25
|
+
question for anything underspecified or ambiguous, and keeps unresolved terms null.
|
|
26
|
+
- **Feasibility check** (`pen_stack/spec/satisfy.py`). Wraps reachability (the atlas), deliverability (the
|
|
27
|
+
delivery recommender), and legality (the verification proof object) into feasible / infeasible plus named
|
|
28
|
+
blocking constraints and repair hints; feasibility is necessary, not sufficient (not efficacy).
|
|
29
|
+
- **WriteSpec-Bench** (`benchmarks/writespec/`). The first prose-to-write-spec corpus, grounded in real
|
|
30
|
+
experiments, with an ambiguity subset and a sealed held-out. Reported verbatim: sealed-test structural fidelity
|
|
31
|
+
1.0 and value accuracy 0.96 versus a 0.46 keyword-dict baseline; inferred-field labelling recall 1.0.
|
|
32
|
+
- **Surfaces**: REST `POST /api/writespec`, MCP `writespec_parse`, the manifest tool, the web "Describe a Write"
|
|
33
|
+
builder page, and `docs/writespec_profile.md` + `docs/writespec_bench.md`.
|
|
34
|
+
|
|
35
|
+
### Notes
|
|
36
|
+
- Feasibility rules out unreachable / undeliverable / illegal, not whether the write will work.
|
|
37
|
+
- The extractor backbone is deterministic; an LLM pass is optional and adds no ground truth (it may only propose
|
|
38
|
+
values that still pass the resolvers).
|
|
39
|
+
|
|
6
40
|
## [6.13.0] - 2026-06-22 - Oracle mesh: binding-affinity dimension, per-oracle reliability, disagreement-to-uncertainty
|
|
7
41
|
|
|
8
42
|
Hardens the foundation-model oracle mesh under one result contract. Adds a binding-affinity dimension
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pen-stack
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.14.0
|
|
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
|
|
@@ -67,6 +67,8 @@ Requires-Dist: mkdocs>=1.6; extra == "docs"
|
|
|
67
67
|
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
|
|
68
68
|
Provides-Extra: env
|
|
69
69
|
Requires-Dist: gymnasium>=0.29; extra == "env"
|
|
70
|
+
Provides-Extra: spec
|
|
71
|
+
Requires-Dist: sbol3>=1.1; extra == "spec"
|
|
70
72
|
Provides-Extra: dev
|
|
71
73
|
Requires-Dist: pytest>=8; extra == "dev"
|
|
72
74
|
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
5bbfd8b7d73c0828a72d84d68b12155f7047e45d0d227e8c9654cc15687991f5 benchmarks/writespec/corpus.json
|
|
2
|
+
d3133084f098a6eeed46ab49555731057c9b106c787f090331df6b019cd249f2 benchmarks/writespec/harness.py
|
|
3
|
+
bc6126634628e92baedf68404636889f07c77c53d59ca057b18b7f54511a11fe benchmarks/writespec/writespec_bench_metrics.json
|
|
4
|
+
051ddbaccca5faab72d66f2737e634b5b7170aa919323aab73f98e53fd042b36 benchmarks/writespec/splits.json
|
|
@@ -20,8 +20,10 @@ A nominated off-target is a candidate, and every result ships with the empirical
|
|
|
20
20
|
controls) found that with a **cell-type-matched** track (ENCODE HEK293T DNase, v6.10.3) accessibility predicts
|
|
21
21
|
WT-Cas9 cell-based off-target activity: **GUIDE-seq AUROC 0.58 (cross-cell K562) → 0.671 (matched), CI
|
|
22
22
|
[0.642, 0.701]**, in-vitro control null. **VALIDATED (moderate, cell-type-matched)**; it is surfaced as an
|
|
23
|
-
annotation and
|
|
24
|
-
|
|
23
|
+
annotation and is **intentionally not folded into the numeric risk score** (the v6.10.4 incremental analysis
|
|
24
|
+
found no held-out ranking improvement over CRISOT, so the score stays sequence/CRISOT-driven; TTISS, a
|
|
25
|
+
Cas9-variant assay, is the expected outlier). Full results:
|
|
26
|
+
`benchmarks/offtarget/chromatin_{validation,incremental}.json`.
|
|
25
27
|
- **Serine integrase (Bxb1):** a cryptic **pseudo-attB** scan that seeds on the *real documented* Bxb1 attB core
|
|
26
28
|
(`GCGGTCTC`, central GT; FlyBase FBto0000359, Ghosh 2003) and reports candidate cryptic sites by arm mismatches.
|
|
27
29
|
- **Bridge recombinase:** delegates to the existing Perry-DMS pseudosite engine (`pen_stack.bridge.offtarget`).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# WriteSpec-Bench (Stage A)
|
|
2
|
+
|
|
3
|
+
WriteSpec-Bench is the first prose-to-write-spec benchmark for genome writing: paired
|
|
4
|
+
`(plain-language request -> gold WriteSpec)`, grounded in real, well-documented genome-writing experiments
|
|
5
|
+
(cited per row). It is `benchmarks/writespec/` (corpus, splits, harness, metrics).
|
|
6
|
+
|
|
7
|
+
## Construction
|
|
8
|
+
|
|
9
|
+
- Each gold WriteSpec is the structured value a careful curator extracts: write-type, target kind, the gene
|
|
10
|
+
(HGNC) or phenotype (MONDO) id, the cell (Cellosaurus / Cell Ontology) id, the cargo Sequence-Ontology role
|
|
11
|
+
set, and the key constraints. Every ontology id was verified against the live services before commit.
|
|
12
|
+
- The **standard** subset is fully specified. The **ambiguity** subset is deliberately underspecified or
|
|
13
|
+
ambiguous, to test that the extractor surfaces assumptions and asks clarifying questions rather than guessing.
|
|
14
|
+
- **Leakage control**: the test split is sealed and disjoint from train (`splits.json`); paraphrases do not
|
|
15
|
+
co-occur across splits.
|
|
16
|
+
|
|
17
|
+
## Scoring (SO-Bench-style)
|
|
18
|
+
|
|
19
|
+
- **schema adherence**: the output validates as a typed `WriteRequest`.
|
|
20
|
+
- **structural fidelity**: write_type and target_kind match the gold.
|
|
21
|
+
- **value accuracy**: canonical-id match for gene / phenotype / cell, the cargo role set, and the listed
|
|
22
|
+
constraints.
|
|
23
|
+
- **grounding**: on the ambiguity subset, clarifying questions fire and the inferred write_type is labelled;
|
|
24
|
+
inferred-field labelling recall must be 100% (no field is set without provenance).
|
|
25
|
+
|
|
26
|
+
The baseline is the legacy keyword dictionary (`web.tools.parse_goal`), which emits raw tokens, not canonical
|
|
27
|
+
ids.
|
|
28
|
+
|
|
29
|
+
## Results (reported verbatim, deterministic extractor, not tuned to the sealed test)
|
|
30
|
+
|
|
31
|
+
- Sealed test: schema adherence 1.0, structural fidelity 1.0, value accuracy 0.964; baseline value accuracy
|
|
32
|
+
0.464.
|
|
33
|
+
- Train: structural fidelity 1.0, value accuracy 0.988; baseline 0.407.
|
|
34
|
+
- Ambiguity subset: clarifying questions fire, inferred write_type labelled.
|
|
35
|
+
- Inferred-field labelling recall: 1.0.
|
|
36
|
+
|
|
37
|
+
The extractor beats the keyword-dict baseline because it resolves terms to verified ontology ids; the baseline
|
|
38
|
+
keeps raw tokens. The corpus is small and curated, grounded in real experiments; its value is bounded by curation
|
|
39
|
+
quality, reported verbatim. Two structural mismatches found during development were genuine extractor bugs (a
|
|
40
|
+
cell-line token mis-read as a gene; a disease prioritized over a named gene target), fixed in the extractor, not
|
|
41
|
+
papered over in the gold.
|
|
42
|
+
|
|
43
|
+
The full per-run metrics are in `benchmarks/writespec/writespec_bench_metrics.json`; re-run with
|
|
44
|
+
`python -m benchmarks.writespec.harness`.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# The WriteSpec profile (Stage A)
|
|
2
|
+
|
|
3
|
+
WriteSpec is a typed, ontology-backed, machine-checkable representation of a genome-writing request. It replaces
|
|
4
|
+
the keyword parser with one contract every downstream stage consumes, and it is an SBOL3 profile, so it is
|
|
5
|
+
interoperable with the genetic-design-automation ecosystem and GenBank from day one. A WriteSpec is a request,
|
|
6
|
+
not a claim.
|
|
7
|
+
|
|
8
|
+
## The type
|
|
9
|
+
|
|
10
|
+
`WriteRequest` (`pen_stack/spec/writespec.py`) carries:
|
|
11
|
+
|
|
12
|
+
- **write_type**: one of insertion, excision, inversion, replacement, regulatory_rewrite, landing_pad_install,
|
|
13
|
+
multiplex.
|
|
14
|
+
- **cargo**: a list of components, each with a Sequence-Ontology role (promoter `SO:0000167`, CDS `SO:0000316`,
|
|
15
|
+
polyA signal `SO:0000551`, insulator `SO:0000627`, ...) and an optional sequence and length.
|
|
16
|
+
- **target**: the edit site, as a gene (HGNC), a locus (GRCh38 coordinate), an att/landing site, or, when only a
|
|
17
|
+
disease goal is given, a phenotype (MONDO). A named gene or att site is the target; a disease is attached as the
|
|
18
|
+
goal.
|
|
19
|
+
- **cell_type**: a Cellosaurus cell line (HEK293T `CVCL_0063`) or a Cell-Ontology cell type (T cell `CL:0000084`),
|
|
20
|
+
carrying the irreducible subline / karyotype-drift caveat.
|
|
21
|
+
- **constraints**: efficiency floor, scarless, safety-switch-required, copy number, germline guardrail, max cargo
|
|
22
|
+
bp, delivery limit / vehicle, and a ChEBI small-molecule inducer.
|
|
23
|
+
|
|
24
|
+
## The grounding discipline
|
|
25
|
+
|
|
26
|
+
Every field records its provenance in the `provenance` map: `explicit` (stated in the prose), `inferred`
|
|
27
|
+
(defaulted, with the rationale in `assumptions`), `user` (supplied through a structured override), or
|
|
28
|
+
`unresolved`. A required field that is unspecified or ambiguous yields a `clarifications` question rather than a
|
|
29
|
+
guess. A term that cannot be resolved is listed in `unresolved` and its field stays null. Nothing is invented.
|
|
30
|
+
|
|
31
|
+
All curated ontology ids were verified against the live services (Cellosaurus, EBI OLS for SO / MONDO / CL /
|
|
32
|
+
ChEBI) before they were committed; a resolver degrades to `unresolved`, never a fabricated id.
|
|
33
|
+
|
|
34
|
+
## Round-trips and the downstream adapter
|
|
35
|
+
|
|
36
|
+
- **JSON**: always available (`to_json` / `from_json`), lossless.
|
|
37
|
+
- **SBOL3**: `to_sbol3` / `from_sbol3` via the real `sbol3` library (the `[spec]` extra). The export emits native
|
|
38
|
+
Components with Sequence-Ontology roles for interoperability and carries the full typed spec losslessly in a
|
|
39
|
+
PROV-O-namespaced annotation.
|
|
40
|
+
- **GenBank**: `to_genbank` exports the cargo as a GenBank record when it carries a DNA sequence (intent-only
|
|
41
|
+
specs return None).
|
|
42
|
+
- **`to_legacy_design`**: the adapter that emits the dict the existing stages (verify / plan / safety / delivery)
|
|
43
|
+
already consume, so the whole stack reads one contract without a per-stage rewrite.
|
|
44
|
+
|
|
45
|
+
## Feasibility
|
|
46
|
+
|
|
47
|
+
`pen_stack/spec/satisfy.py` tests the necessary conditions a write must meet by wrapping the existing stages:
|
|
48
|
+
reachability (the writable-genome atlas), deliverability (the Stage D recommender), and legality (the Stage F
|
|
49
|
+
rule set, via the repair-oriented proof object). It returns feasible or infeasible plus the named blocking
|
|
50
|
+
constraint(s) and repair hints, so an agent can repair the spec and re-check. Feasibility is necessary, not
|
|
51
|
+
sufficient: it rules out unreachable / undeliverable / illegal, not whether the write will work (that is the
|
|
52
|
+
downstream stages' calibrated prediction).
|
|
53
|
+
|
|
54
|
+
## Surfaces
|
|
55
|
+
|
|
56
|
+
- REST `POST /api/writespec` (body `{prose, overrides?, check_feasibility?}`).
|
|
57
|
+
- MCP `writespec_parse`.
|
|
58
|
+
- The web "Describe a Write" builder page.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""PEN-STACK v3.0 - open infrastructure for genome writing."""
|
|
2
|
-
__version__ = "6.
|
|
2
|
+
__version__ = "6.14.0"
|
|
@@ -60,6 +60,18 @@ def verify_proof(design: dict) -> dict:
|
|
|
60
60
|
return _vp(design).model_dump()
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
@mcp.tool()
|
|
64
|
+
def writespec_parse(prose: str, check_feasibility: bool = True) -> dict:
|
|
65
|
+
"""v6.14 Stage A: parse a plain-language genome-writing request into a typed, ontology-backed WriteSpec (an
|
|
66
|
+
SBOL3 profile). Returns the typed spec with per-field provenance (explicit / inferred / user / unresolved),
|
|
67
|
+
the assumptions behind every inferred field, clarifying questions for anything underspecified or ambiguous,
|
|
68
|
+
the unresolved terms (kept null, never invented), the downstream design adapter, and a feasibility verdict
|
|
69
|
+
(reachability + deliverability + legality, with named blocking constraints + repair hints). A WriteSpec is a
|
|
70
|
+
REQUEST, not a claim; the extractor never fabricates intent."""
|
|
71
|
+
from pen_stack.spec.service import parse_request
|
|
72
|
+
return parse_request(prose, check_feasibility=check_feasibility)
|
|
73
|
+
|
|
74
|
+
|
|
63
75
|
@mcp.tool()
|
|
64
76
|
def oracle_query(oracle: str | None = None, protein_seq: str | None = None, ligand_smiles: str | None = None,
|
|
65
77
|
pair_type: str = "ligand", ligand_name: str | None = None) -> dict:
|
|
@@ -67,6 +67,12 @@ _TOOLS = [
|
|
|
67
67
|
"NOT an in-vivo tropism claim (v6.11 PEN-DELIVER)",
|
|
68
68
|
"input": "AAV VP1 capsid amino-acid sequence", "output": "predicted packaging-fitness (candidate) or abstain",
|
|
69
69
|
"entrypoint": "pen_stack.planner.delivery_predict.capsid_fitness", "fabricates": False},
|
|
70
|
+
{"name": "writespec_parse", "summary": "parse a plain-language genome-writing request into a typed, "
|
|
71
|
+
"ontology-backed WriteSpec (an SBOL3 profile): per-field provenance (explicit/inferred/user/unresolved), "
|
|
72
|
+
"assumptions, clarifying questions on underspecification, and a feasibility verdict (reachability + "
|
|
73
|
+
"deliverability + legality); a REQUEST not a claim, never fabricates intent (v6.14 Stage A WriteSpec)",
|
|
74
|
+
"input": "prose request (+ optional structured overrides)", "output": "typed WriteSpec + clarifications + "
|
|
75
|
+
"feasibility; unresolved stays null", "entrypoint": "pen_stack.spec.service.parse_request", "fabricates": False},
|
|
70
76
|
{"name": "oracle_query", "summary": "query the oracle mesh under one contract: per-oracle execution + latency "
|
|
71
77
|
"+ live status + PUBLISHED reliability (verbatim from public benchmarks, cited) + disagreement-to-interval; "
|
|
72
78
|
"or a CANDIDATE protein-ligand binding affinity (Boltz-2 head) with native uncertainty, cache-or-abstain "
|
|
@@ -238,6 +238,18 @@ def offtarget_assay_endpoint(writer_family: str):
|
|
|
238
238
|
return recommend_assay(writer_family)
|
|
239
239
|
|
|
240
240
|
|
|
241
|
+
@app.post("/writespec", tags=["v6.14 writespec"])
|
|
242
|
+
def writespec_endpoint(req: dict):
|
|
243
|
+
"""v6.14 Stage A: parse a plain-language genome-writing request into a typed, ontology-backed WriteSpec.
|
|
244
|
+
Body: {prose, overrides?, check_feasibility?}. Returns the typed spec (with per-field provenance), the
|
|
245
|
+
assumptions behind every inferred field, clarifying questions for anything underspecified, the unresolved
|
|
246
|
+
terms (kept null, never invented), the downstream design adapter, and the feasibility verdict. A WriteSpec is
|
|
247
|
+
a REQUEST, not a claim; the extractor never fabricates intent."""
|
|
248
|
+
from pen_stack.spec.service import parse_request
|
|
249
|
+
return parse_request(req.get("prose", ""), overrides=req.get("overrides"),
|
|
250
|
+
check_feasibility=bool(req.get("check_feasibility", True)))
|
|
251
|
+
|
|
252
|
+
|
|
241
253
|
@app.post("/oracle/affinity", tags=["v6.13 oracle"])
|
|
242
254
|
def oracle_affinity_endpoint(req: dict):
|
|
243
255
|
"""v6.13 PEN-ORACLE protein-ligand binding-affinity (Boltz-2 head) under the oracle contract. Body:
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""The WriteSpec intent layer (v6.14, Stage A).
|
|
2
|
+
|
|
3
|
+
A typed, ontology-backed, machine-checkable representation of a genome-writing request (``WriteRequest``, an
|
|
4
|
+
SBOL3 profile), a grounded prose-to-spec extractor that labels every inference and never fabricates intent, and a
|
|
5
|
+
feasibility (satisfiability) check. This is the agentic front-end: one contract every downstream stage consumes.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pen_stack.spec.writespec import (
|
|
10
|
+
CargoComponent,
|
|
11
|
+
Constraints,
|
|
12
|
+
Resolved,
|
|
13
|
+
Target,
|
|
14
|
+
WriteRequest,
|
|
15
|
+
WRITE_TYPES,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = ["WriteRequest", "CargoComponent", "Target", "Constraints", "Resolved", "WRITE_TYPES"]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Clarifying-question planner (v6.14, Stage A, A-WS3).
|
|
2
|
+
|
|
3
|
+
Given a :class:`WriteRequest`, return the minimal set of questions needed to make it actionable: the required
|
|
4
|
+
fields that are unspecified or ambiguous. The extractor already populates ``clarifications`` during parsing;
|
|
5
|
+
this module is the standalone planner an agent calls to decide whether to ask before composing a write, and to
|
|
6
|
+
get a clean, de-duplicated question list. It never guesses a value: a missing required field becomes a question.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pen_stack.spec.writespec import WriteRequest
|
|
11
|
+
|
|
12
|
+
# fields that must be present for a write to be composable (by write-type)
|
|
13
|
+
_REQUIRED_TARGET = {"insertion", "excision", "inversion", "replacement", "regulatory_rewrite",
|
|
14
|
+
"landing_pad_install", "multiplex"}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def clarifying_questions(spec: WriteRequest) -> list[str]:
|
|
18
|
+
"""The minimal de-duplicated question list for the underspecified / ambiguous required fields."""
|
|
19
|
+
qs: list[str] = list(spec.clarifications) # carry the extractor's questions
|
|
20
|
+
if spec.write_type in _REQUIRED_TARGET and spec.target.kind == "unspecified":
|
|
21
|
+
q = "Which gene, locus, att/landing site, or disease phenotype should the write target?"
|
|
22
|
+
if q not in qs:
|
|
23
|
+
qs.append(q)
|
|
24
|
+
# an insertion with no cargo at all is underspecified
|
|
25
|
+
if spec.write_type == "insertion" and not spec.cargo:
|
|
26
|
+
qs.append("What cargo should be inserted (size in bp/kb, and any features such as promoter / CDS / polyA)?")
|
|
27
|
+
# an unresolved term needs disambiguation, not a guess
|
|
28
|
+
for term in spec.unresolved:
|
|
29
|
+
qs.append(f"The term '{term}' could not be resolved to a gene / locus / cell / phenotype; please clarify it.")
|
|
30
|
+
# de-duplicate, preserve order
|
|
31
|
+
seen: set[str] = set()
|
|
32
|
+
out: list[str] = []
|
|
33
|
+
for q in qs:
|
|
34
|
+
if q not in seen:
|
|
35
|
+
seen.add(q)
|
|
36
|
+
out.append(q)
|
|
37
|
+
return out
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def is_actionable(spec: WriteRequest) -> bool:
|
|
41
|
+
"""True when no clarifying question remains (the spec is complete enough to compose a write)."""
|
|
42
|
+
return not clarifying_questions(spec)
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""Grounded prose-to-WriteSpec extractor (v6.14, Stage A, A-WS3).
|
|
2
|
+
|
|
3
|
+
Maps a plain-language genome-writing request to a typed :class:`WriteRequest`. The backbone is DETERMINISTIC (so
|
|
4
|
+
the benchmark is reproducible and CI-safe); an LLM pass is optional and never required. Three grounding
|
|
5
|
+
safeguards are mandatory and encoded here:
|
|
6
|
+
|
|
7
|
+
1. assumption surfacing - every field not explicit in the prose is recorded in ``provenance`` as ``inferred``
|
|
8
|
+
with the rationale in ``assumptions`` (never a silent default);
|
|
9
|
+
2. clarifying questions - a required field that is unspecified or ambiguous yields a ``clarifications``
|
|
10
|
+
question rather than a guess;
|
|
11
|
+
3. no fabrication - a term that cannot be resolved is listed in ``unresolved`` and its field stays null.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import re
|
|
16
|
+
|
|
17
|
+
from pen_stack.spec.resolvers import (
|
|
18
|
+
resolve_cell,
|
|
19
|
+
resolve_chem,
|
|
20
|
+
resolve_feature,
|
|
21
|
+
resolve_gene,
|
|
22
|
+
resolve_locus,
|
|
23
|
+
resolve_phenotype,
|
|
24
|
+
)
|
|
25
|
+
from pen_stack.spec.resolvers.phenotype import _PHENO
|
|
26
|
+
from pen_stack.spec.writespec import (
|
|
27
|
+
CargoComponent,
|
|
28
|
+
Constraints,
|
|
29
|
+
Target,
|
|
30
|
+
WriteRequest,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
_GENE_RE = re.compile(r"\b([A-Z][A-Z0-9]{1,7})\b")
|
|
34
|
+
_WRITE_TYPE_KW = [
|
|
35
|
+
("excision", ["excise", "delete", "knock out", "knockout", "remove ", "disrupt"]),
|
|
36
|
+
("inversion", ["invert", "inversion", "flip "]),
|
|
37
|
+
("replacement", ["replace", "correct the", "correct a", "swap", "base edit", "prime edit", "repair the"]),
|
|
38
|
+
("regulatory_rewrite", ["regulatory", "promoter swap", "rewrite the promoter", "tune expression", "upregulate", "knock down"]),
|
|
39
|
+
("landing_pad_install", ["landing pad", "landing-pad", "install an att", "install a landing", "attp", "bxb1 site"]),
|
|
40
|
+
("multiplex", ["multiplex", "multiple loci", "simultaneously edit"]),
|
|
41
|
+
("insertion", ["insert", "integrate", "knock in", "knock-in", "knockin", "add a", "deliver a transgene", "place a"]),
|
|
42
|
+
]
|
|
43
|
+
_ROLE_KW = {"promoter": "promoter", "cds": "CDS", "coding": "CDS", "transgene": "CDS", "gfp": "CDS",
|
|
44
|
+
"polya": "polyA", "poly-a": "polyA", "insulator": "insulator", "enhancer": "enhancer",
|
|
45
|
+
"terminator": "terminator", "ires": "IRES"}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _detect_write_type(low: str) -> tuple[str, bool]:
|
|
49
|
+
for wt, kws in _WRITE_TYPE_KW:
|
|
50
|
+
if any(k in low for k in kws):
|
|
51
|
+
return wt, True
|
|
52
|
+
return "insertion", False # default, labelled inferred by the caller
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _gene_tokens(prose: str) -> list[str]:
|
|
56
|
+
"""Gene-symbol tokens, excluding jargon and any token that resolves as a cell line / type (e.g. HEK293T)."""
|
|
57
|
+
from pen_stack.spec.resolvers.cell import _CELLS
|
|
58
|
+
from pen_stack.spec.resolvers.gene import _STOP
|
|
59
|
+
cell_keys = {k.upper() for k in _CELLS}
|
|
60
|
+
return [g for g in _GENE_RE.findall(prose) if g not in _STOP and g.upper() not in cell_keys]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _detect_bp(low: str) -> int | None:
|
|
64
|
+
m = re.search(r"(\d+(?:\.\d+)?)\s*kb", low)
|
|
65
|
+
if m:
|
|
66
|
+
return int(float(m.group(1)) * 1000)
|
|
67
|
+
m = re.search(r"(\d{2,6})\s*(?:bp|base pairs)", low)
|
|
68
|
+
if m:
|
|
69
|
+
return int(m.group(1))
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _detect_phenotype(prose: str):
|
|
74
|
+
low = prose.lower()
|
|
75
|
+
for phrase in sorted(_PHENO, key=len, reverse=True):
|
|
76
|
+
if phrase in low:
|
|
77
|
+
return resolve_phenotype(phrase)
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _detect_cell(prose: str):
|
|
82
|
+
from pen_stack.spec.resolvers.cell import _CELLS
|
|
83
|
+
low = prose.lower()
|
|
84
|
+
for phrase in sorted(_CELLS, key=len, reverse=True):
|
|
85
|
+
if phrase in low:
|
|
86
|
+
return resolve_cell(phrase)
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _detect_inducer(prose: str):
|
|
91
|
+
from pen_stack.spec.resolvers.chem import _CHEM
|
|
92
|
+
low = prose.lower()
|
|
93
|
+
for phrase in sorted(_CHEM, key=len, reverse=True):
|
|
94
|
+
if phrase in low:
|
|
95
|
+
return resolve_chem(phrase)
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def extract_writespec(prose: str, *, overrides: dict | None = None, allow_llm: bool = False) -> WriteRequest:
|
|
100
|
+
"""Extract a typed WriteRequest from prose, deterministically, with grounding safeguards.
|
|
101
|
+
|
|
102
|
+
``overrides`` lets a caller (or the web builder) supply a field directly; those are marked provenance ``user``.
|
|
103
|
+
``allow_llm`` is accepted for surface parity but the deterministic backbone is what runs and is benchmarked;
|
|
104
|
+
an LLM pass, when wired, may only PROPOSE values that still pass the resolvers (it can never set an
|
|
105
|
+
unresolved id).
|
|
106
|
+
"""
|
|
107
|
+
prose = prose or ""
|
|
108
|
+
low = prose.lower()
|
|
109
|
+
prov: dict[str, str] = {}
|
|
110
|
+
assumptions: list[str] = []
|
|
111
|
+
unresolved: list[str] = []
|
|
112
|
+
clarifications: list[str] = []
|
|
113
|
+
overrides = overrides or {}
|
|
114
|
+
|
|
115
|
+
# --- write type ---
|
|
116
|
+
if "write_type" in overrides:
|
|
117
|
+
wt = overrides["write_type"]
|
|
118
|
+
prov["write_type"] = "user"
|
|
119
|
+
else:
|
|
120
|
+
wt, explicit = _detect_write_type(low)
|
|
121
|
+
prov["write_type"] = "explicit" if explicit else "inferred"
|
|
122
|
+
if not explicit:
|
|
123
|
+
assumptions.append("write_type inferred as 'insertion' (no explicit write verb; the default for a "
|
|
124
|
+
"cargo-placement request)")
|
|
125
|
+
|
|
126
|
+
# --- target: a named gene/locus or att site is the EDIT SITE (target_kind); a disease is the GOAL ---
|
|
127
|
+
target = Target()
|
|
128
|
+
pheno = _detect_phenotype(prose)
|
|
129
|
+
gene_toks = _gene_tokens(prose)
|
|
130
|
+
att = None
|
|
131
|
+
for k in ("attb", "attp", "landing pad", "landing-pad", "landing site"):
|
|
132
|
+
if k in low:
|
|
133
|
+
att = k.replace(" ", "_").replace("-", "_")
|
|
134
|
+
break
|
|
135
|
+
gene_resolved = None
|
|
136
|
+
if gene_toks:
|
|
137
|
+
g = resolve_gene(gene_toks[0])
|
|
138
|
+
if g.resolved:
|
|
139
|
+
gene_resolved = g
|
|
140
|
+
else:
|
|
141
|
+
unresolved.append(gene_toks[0])
|
|
142
|
+
if gene_resolved is not None:
|
|
143
|
+
loc = resolve_locus(gene_toks[0])
|
|
144
|
+
target = Target(kind="gene", gene=gene_resolved, locus=loc if loc.resolved else None)
|
|
145
|
+
prov["target.gene"] = "explicit"
|
|
146
|
+
if pheno is not None and pheno.resolved: # the disease is the goal, attached to the gene target
|
|
147
|
+
target.phenotype = pheno
|
|
148
|
+
prov["target.phenotype"] = "explicit"
|
|
149
|
+
elif att:
|
|
150
|
+
target = Target(kind="att_site", att_site=att)
|
|
151
|
+
prov["target.att_site"] = "explicit"
|
|
152
|
+
if pheno is not None and pheno.resolved:
|
|
153
|
+
target.phenotype = pheno
|
|
154
|
+
prov["target.phenotype"] = "explicit"
|
|
155
|
+
elif pheno is not None and pheno.resolved:
|
|
156
|
+
target = Target(kind="phenotype", phenotype=pheno)
|
|
157
|
+
prov["target.phenotype"] = "explicit"
|
|
158
|
+
if target.kind == "unspecified":
|
|
159
|
+
clarifications.append("Which gene, locus, att/landing site, or disease phenotype should the write target?")
|
|
160
|
+
|
|
161
|
+
# --- cell type (optional but recommended) ---
|
|
162
|
+
cell = None
|
|
163
|
+
ct = _detect_cell(prose)
|
|
164
|
+
if ct is not None and ct.resolved:
|
|
165
|
+
cell = ct
|
|
166
|
+
prov["cell_type"] = "explicit"
|
|
167
|
+
elif ct is not None and ct.candidates:
|
|
168
|
+
cell = ct
|
|
169
|
+
prov["cell_type"] = "explicit"
|
|
170
|
+
clarifications.append(f"Cell term '{ct.text}' is ambiguous: did you mean "
|
|
171
|
+
f"{', '.join(c['label'] for c in ct.candidates)}?")
|
|
172
|
+
else:
|
|
173
|
+
clarifications.append("Which cell type or cell line is the target (e.g. HEK293T, primary T cells, HSPCs)?")
|
|
174
|
+
|
|
175
|
+
# --- cargo (size + feature roles) ---
|
|
176
|
+
cargo: list[CargoComponent] = []
|
|
177
|
+
bp = _detect_bp(low)
|
|
178
|
+
seen_roles: set[str] = set()
|
|
179
|
+
for kw, label in _ROLE_KW.items():
|
|
180
|
+
if kw in low and label not in seen_roles:
|
|
181
|
+
seen_roles.add(label)
|
|
182
|
+
role = resolve_feature(label)
|
|
183
|
+
cargo.append(CargoComponent(name=kw, role=role if role.resolved else None))
|
|
184
|
+
prov[f"cargo[{len(cargo) - 1}].role"] = "explicit"
|
|
185
|
+
if bp is not None and cargo:
|
|
186
|
+
cargo[0].length_bp = bp
|
|
187
|
+
elif bp is not None:
|
|
188
|
+
cargo.append(CargoComponent(name="cargo", length_bp=bp))
|
|
189
|
+
|
|
190
|
+
# --- constraints ---
|
|
191
|
+
cons = Constraints()
|
|
192
|
+
if "scarless" in low or "seamless" in low:
|
|
193
|
+
cons.scarless = True
|
|
194
|
+
prov["constraints.scarless"] = "explicit"
|
|
195
|
+
if "safety switch" in low or "kill switch" in low or "icasp9" in low or "suicide gene" in low:
|
|
196
|
+
cons.safety_switch_required = True
|
|
197
|
+
prov["constraints.safety_switch_required"] = "explicit"
|
|
198
|
+
# named delivery vehicle (so the legality / capacity check can run) - most specific first
|
|
199
|
+
_VEH = [("dual aav", "AAV_dual"), ("aav", "AAV_single"), ("lentivir", "lentivirus"), ("lnp", "LNP"),
|
|
200
|
+
("electroporat", "electroporation"), ("rnp", "electroporation"), ("adenovir", "adenovirus")]
|
|
201
|
+
for kw, veh in _VEH:
|
|
202
|
+
if kw in low:
|
|
203
|
+
cons.delivery_limit = veh
|
|
204
|
+
prov["constraints.delivery_limit"] = "explicit"
|
|
205
|
+
break
|
|
206
|
+
if cons.delivery_limit is None and ("non-integrating" in low or "nonintegrating" in low
|
|
207
|
+
or "episomal" in low or "transient" in low):
|
|
208
|
+
cons.delivery_limit = "non_integrating"
|
|
209
|
+
prov["constraints.delivery_limit"] = "explicit"
|
|
210
|
+
if "germline" in low or "embryo" in low or "heritable" in low:
|
|
211
|
+
cons.germline = True
|
|
212
|
+
prov["constraints.germline"] = "explicit"
|
|
213
|
+
m = re.search(r"(?:at least|>=|over|above)\s*(\d{1,3})\s*%\s*(?:efficien|edit|integrat)", low)
|
|
214
|
+
if m:
|
|
215
|
+
cons.efficiency_floor = float(m.group(1)) / 100.0
|
|
216
|
+
prov["constraints.efficiency_floor"] = "explicit"
|
|
217
|
+
inducer = _detect_inducer(prose)
|
|
218
|
+
if inducer is not None and inducer.resolved:
|
|
219
|
+
cons.inducer = inducer
|
|
220
|
+
prov["constraints.inducer"] = "explicit"
|
|
221
|
+
if bp is not None:
|
|
222
|
+
cons.max_cargo_bp = bp
|
|
223
|
+
prov["constraints.max_cargo_bp"] = "explicit"
|
|
224
|
+
|
|
225
|
+
# apply structured overrides (provenance=user) last
|
|
226
|
+
for k, v in overrides.items():
|
|
227
|
+
if k != "write_type":
|
|
228
|
+
prov[k] = "user"
|
|
229
|
+
|
|
230
|
+
return WriteRequest(write_type=wt, cargo=cargo, target=target, cell_type=cell, constraints=cons,
|
|
231
|
+
provenance=prov, assumptions=assumptions, clarifications=clarifications,
|
|
232
|
+
unresolved=unresolved, free_text_note=prose.strip() or None)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Vocabulary resolvers (v6.14, Stage A, A-WS2): free text -> canonical ontology id.
|
|
2
|
+
|
|
3
|
+
Each resolver returns a :class:`pen_stack.spec.writespec.Resolved` with a canonical id + confidence + (when
|
|
4
|
+
ambiguous) a ranked candidate set, or a result with ``id=None`` for an unresolved term. Unresolved stays null:
|
|
5
|
+
a resolver never invents an id. All curated ids were verified against the live ontology services (Cellosaurus,
|
|
6
|
+
EBI OLS for SO / MONDO / CL / ChEBI) before they were committed.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pen_stack.spec.resolvers.cell import resolve_cell
|
|
11
|
+
from pen_stack.spec.resolvers.chem import resolve_chem
|
|
12
|
+
from pen_stack.spec.resolvers.feature import resolve_feature
|
|
13
|
+
from pen_stack.spec.resolvers.gene import resolve_gene
|
|
14
|
+
from pen_stack.spec.resolvers.locus import resolve_locus
|
|
15
|
+
from pen_stack.spec.resolvers.phenotype import resolve_phenotype
|
|
16
|
+
|
|
17
|
+
__all__ = ["resolve_gene", "resolve_cell", "resolve_feature", "resolve_phenotype", "resolve_chem", "resolve_locus"]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Cell-line / cell-type resolver (A-WS2): free text -> Cellosaurus (cell lines) or Cell Ontology (cell types).
|
|
2
|
+
|
|
3
|
+
Curated offline cache (ids verified against the Cellosaurus API and EBI OLS). Cell lines carry the irreducible
|
|
4
|
+
subline / karyotype-drift caveat as a note. An unrecognized term stays unresolved (id=None), never invented.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from pen_stack.spec.writespec import Resolved
|
|
9
|
+
|
|
10
|
+
# (id, canonical label, ontology) - all ids verified live before commit
|
|
11
|
+
_CELLS: dict[str, tuple[str, str, str]] = {
|
|
12
|
+
# cell lines (Cellosaurus)
|
|
13
|
+
"hek293t": ("CVCL_0063", "HEK293T", "Cellosaurus"), "293t": ("CVCL_0063", "HEK293T", "Cellosaurus"),
|
|
14
|
+
"k562": ("CVCL_0004", "K-562", "Cellosaurus"), "k-562": ("CVCL_0004", "K-562", "Cellosaurus"),
|
|
15
|
+
"hepg2": ("CVCL_0027", "Hep-G2", "Cellosaurus"), "hep-g2": ("CVCL_0027", "Hep-G2", "Cellosaurus"),
|
|
16
|
+
"jurkat": ("CVCL_0065", "Jurkat", "Cellosaurus"),
|
|
17
|
+
"hela": ("CVCL_0030", "HeLa", "Cellosaurus"),
|
|
18
|
+
# cell types (Cell Ontology)
|
|
19
|
+
"t cell": ("CL:0000084", "T cell", "CL"), "t-cell": ("CL:0000084", "T cell", "CL"),
|
|
20
|
+
"cd8 t cell": ("CL:0000084", "T cell", "CL"), "car-t": ("CL:0000084", "T cell", "CL"),
|
|
21
|
+
"primary t cell": ("CL:0000084", "T cell", "CL"),
|
|
22
|
+
"hspc": ("CL:0000037", "hematopoietic stem cell", "CL"), "hsc": ("CL:0000037", "hematopoietic stem cell", "CL"),
|
|
23
|
+
"cd34+": ("CL:0000037", "hematopoietic stem cell", "CL"), "cd34": ("CL:0000037", "hematopoietic stem cell", "CL"),
|
|
24
|
+
"neuron": ("CL:0000540", "neuron", "CL"),
|
|
25
|
+
}
|
|
26
|
+
_LINE_CAVEAT = ("cell-line identity carries irreducible subline / karyotype drift (e.g. HEK293T); the canonical "
|
|
27
|
+
"id does not resolve that away")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def resolve_cell(text: str) -> Resolved:
|
|
31
|
+
"""Resolve a cell line / type to Cellosaurus or Cell Ontology; unrecognized stays null."""
|
|
32
|
+
if not text:
|
|
33
|
+
return Resolved(text=text, note="empty")
|
|
34
|
+
key = text.strip().lower()
|
|
35
|
+
hit = _CELLS.get(key)
|
|
36
|
+
if hit is None:
|
|
37
|
+
# try a loose contains-match for a ranked candidate set (never a silent pick)
|
|
38
|
+
cands = [{"id": v[0], "label": v[1], "ontology": v[2]}
|
|
39
|
+
for k, v in _CELLS.items() if k in key or key in k]
|
|
40
|
+
uniq = {c["id"]: c for c in cands}
|
|
41
|
+
if len(uniq) == 1:
|
|
42
|
+
v = next(iter(uniq.values()))
|
|
43
|
+
hit = (v["id"], v["label"], v["ontology"])
|
|
44
|
+
elif len(uniq) > 1:
|
|
45
|
+
return Resolved(text=text, ontology=None, confidence=0.3, candidates=list(uniq.values()),
|
|
46
|
+
note="ambiguous cell term; resolve to one of the candidates")
|
|
47
|
+
else:
|
|
48
|
+
return Resolved(text=text, confidence=None, note="unresolved cell term")
|
|
49
|
+
cid, label, onto = hit
|
|
50
|
+
note = _LINE_CAVEAT if onto == "Cellosaurus" else None
|
|
51
|
+
return Resolved(text=text, id=cid, label=label, ontology=onto, confidence=0.95, note=note)
|