pen-stack 7.0.0__tar.gz → 7.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pen_stack-7.0.0 → pen_stack-7.1.0}/CHANGELOG.md +27 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/CITATION.cff +2 -2
- {pen_stack-7.0.0 → pen_stack-7.1.0}/PKG-INFO +1 -1
- pen_stack-7.1.0/benchmarks/chat_grounding/README.md +24 -0
- pen_stack-7.1.0/benchmarks/chat_grounding/SHA256SUMS +1 -0
- pen_stack-7.1.0/benchmarks/chat_headtohead/README.md +25 -0
- pen_stack-7.1.0/benchmarks/chat_routing/README.md +35 -0
- pen_stack-7.1.0/benchmarks/chat_routing/SHA256SUMS +1 -0
- pen_stack-7.1.0/benchmarks/chat_safety/README.md +27 -0
- pen_stack-7.1.0/benchmarks/chat_safety/SHA256SUMS +1 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/MCP.md +1 -1
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/tutorials/where-can-i-write.md +5 -3
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/__init__.py +1 -1
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/api/manifest.py +9 -0
- pen_stack-7.1.0/pen_stack/rag/corpus.py +133 -0
- pen_stack-7.1.0/pen_stack/rag/embed.py +79 -0
- pen_stack-7.1.0/pen_stack/rag/ground.py +87 -0
- pen_stack-7.1.0/pen_stack/rag/retrieve.py +48 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/server/api.py +129 -8
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/web/llm.py +46 -77
- pen_stack-7.1.0/pen_stack/web/llm_provider.py +99 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/web/router.py +32 -9
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/web/server.py +2 -1
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack.egg-info/PKG-INFO +1 -1
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack.egg-info/SOURCES.txt +15 -0
- pen_stack-7.1.0/prereg/SHA256_LOCK_ws_penchat.json +9 -0
- pen_stack-7.1.0/prereg/ws_penchat.yaml +110 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pyproject.toml +1 -1
- pen_stack-7.1.0/scripts/build_rag_corpus.py +39 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/LICENSE +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/MANIFEST.in +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/README.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/bench/run.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/delivery/SHA256SUMS +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/genome_writing_bench/LEADERBOARD.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/genome_writing_bench/README.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/genome_writing_bench/SHA256SUMS +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/genome_writing_bench/tasks.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/genome_writing_challenge/README.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/genome_writing_challenge/SUBMISSIONS.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/loop/SHA256SUMS +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/offtarget/SHA256SUMS +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/oracle/SHA256SUMS +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/position_effect/README.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/position_effect/SHA256SUMS +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/verify/SHA256SUMS +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/writer_efficiency/README.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/writer_efficiency/SHA256SUMS +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/benchmarks/writespec/SHA256SUMS +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/aav_serotype_tropism.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/antipeg.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/atlas_families.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/bridge_offtarget_profile.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/calibration/preexisting_nab_independent.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/capsid_epitope_oracle.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/capsid_sequences.fasta +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/cargo_polish.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/cell_types.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/datasets.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/delivery_constraints.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/delivery_rules.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/delivery_vehicles.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/expression/modifiers.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/expression/promoters.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/gates_v3.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/genotoxicity_oracle.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/gsh_validated_heldout.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/intent_weights.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/known_unknowns.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/llm.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/metric_guide.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/mhc_epitope_oracle.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/monitor_queries.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/oracles/execution.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/oracles/reliability.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/oracles/scope_cards.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/rules/delivery.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/rules/fold.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/rules/multiplex.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/rules/payload.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/rules/reachability.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/safety/hazard_registry.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/safety/policy.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/safety/probes.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/score_axes.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/seroprevalence.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/target_sites.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/universe_crosswalk.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/write_types.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/writer_sequences.fasta +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/configs/wtkb_curated.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/data/curated/bridge_offtarget_energetics.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/data/curated/gene_coords.parquet +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/data/curated/unified_editor_universe.parquet +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/BACKLOG.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/DEPLOY.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/INFRA.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/RELEASING.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/REPRO.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/STABILITY.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/agent.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/alphagenome_feasibility.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/autonomy.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/benchmark_circularity.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/biosecurity.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/build_interface.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/cards/atlas.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/cards/durability.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/cards/offtarget_data.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/cards/position_effect_data.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/cards/safety.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/cards/writer_efficiency_data.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/challenge.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/closed_loop.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/co_scientist.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/co_scientist_loop.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/delivery.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/delivery_immunology.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/delivery_recommender.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/digital_twin.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/dissemination.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/environment.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/experiment_design.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/generative_design.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/immune_profiler.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/index.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/integrations.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/live_oracles.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/mechanistic_constraints.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/offtarget.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/oracle_mesh.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/oracles.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/position_effect.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/positioning.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/private_data_formats.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/quickstart.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/responsible_use.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/rule_spec.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/rules.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/scope.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/scorecard.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/tpe_bench.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/tutorials/compare-families.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/tutorials/score-deliverability.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/tutorials/which-writer-reaches-locus.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/uncertainty.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/verify.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/verify_service.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/world_model.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/writer_efficiency.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/writer_verification.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/writespec_bench.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/writespec_profile.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/docs/wtkb.md +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/_resources.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/active/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/active/acquire.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/active/brains.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/active/campaign.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/active/design.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/active/validate.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/adapt/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/adapt/finetune.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/adapt/ingest.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/adapt/pipeline.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/adapt/recalibrate.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/adapt/report.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/cite.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/co_scientist.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/epistemic.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/guardrails.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/mcp_server.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/orchestrator.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/orchestrator_live.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/pen_agent.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/scope.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/agent/tools.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/api/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/build_wtkb.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/crosslink.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/expand.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/guide_design.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/schema.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/scorecard.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/universe.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/variant_propose.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/writer_efficiency.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/writer_predict.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/writer_recommend.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/atlas/writer_verify.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/bridge/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/bridge/activity.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/bridge/cli.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/bridge/fold_qc.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/bridge/guide_qc.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/bridge/ingest.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/bridge/offtarget.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/bridge/offtarget_energetics.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/bridge/ortholog_screen.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/bridge/pipeline.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/build/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/build/cloudlab.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/build/ingest.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/build/protocol.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/build/simlab.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/cli.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/data/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/data/encode.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/data/genome.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/data/ingest_chromatin.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/data/ingest_integration.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/data/ingest_safety_annot.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/data/ingest_trip.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/design/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/design/capsid_generate.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/design/generate.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/design/pareto.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/design/space.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/design/writer_variants.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/env/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/env/genome_writing_env.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/env/policies.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/graph/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/graph/build.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/graph/cell_types.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/graph/ingest.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/graph/query.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/graph/schema.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/loop/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/loop/continual.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/loop/cycle.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/loop/drift.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/mech/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/mech/classify_atlas.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/mech/whitelist.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/monitor/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/monitor/europepmc.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/monitor/run.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/monitor/triage.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/affinity.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/cache.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/energetics.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/genome.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/protein_design.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/reliability.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/rna.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/schema.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/status.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/structure.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/structure_run.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/oracles/vcell.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/ada_risk.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/antipeg_oracle.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/capsid_epitope_oracle.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/cargo.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/cargo_polish.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/delivery.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/delivery_constraints.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/delivery_immune.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/delivery_immunology.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/delivery_predict.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/delivery_vehicles.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/genotoxicity_oracle.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/immune_mhc2.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/immune_profile.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/innate_sensing.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/multiplex.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/optimize.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/pipeline.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/report.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/router.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/seroprevalence_oracle.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/planner/target_site.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/rag/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/rag/index.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/rag/llm.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/rag/qa.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/rules/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/rules/evaluators.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/rules/loader.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/rules/schema.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/rules/solver.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/rules/spec.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/safety/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/safety/audit.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/safety/gate.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/safety/policy.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/safety/redteam.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/safety/registry.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/safety/screen.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/safety/standards.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/score/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/score/recalibrate.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/score/therapeutic.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/server/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/clarify.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/extract.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/resolvers/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/resolvers/cell.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/resolvers/chem.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/resolvers/feature.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/resolvers/gene.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/resolvers/locus.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/resolvers/phenotype.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/satisfy.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/service.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/spec/writespec.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/twin/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/twin/calibrate.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/twin/data/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/twin/data/position_effect.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/twin/mechanistic.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/twin/outcome.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/twin/position_effect.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/ui/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/ui/app.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/adapt_demo.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/agent_eval.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/bench_coscientist_tasks.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/bench_graph_tasks.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/bench_rule_tasks.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/bench_trust_tasks.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/bench_writetype_tasks.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/blind_gsh_discovery.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/cargo_directionality.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/closed_loop.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/durability_baselines.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/experiment_design.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/expr_controls.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/forward_hypotheses.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/generative_design.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/guide_qc_demo.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/heldout_celltype_expr.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/immune_calibration.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/intent_specification.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/known_biology_expr.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/out_of_scope_refusal.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/outcome_calibration.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/outcome_prediction.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/paper3_benchmark.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/paper4_real_validation.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/paper4_validation.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/protocol_safety.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/safety_screening.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/selective_prediction.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/seq_vs_measured.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/target_site_controls.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/uncertainty_eval.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/ungrounded_baseline.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/within_locus_ranking.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/validate/writer_recovery.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/verify/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/verify/proof.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/verify/schema.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/verify/service.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/web/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/web/guide.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/web/tools.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/__init__.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/chromatin_seq.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/durability.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/export_tracks.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/features.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/gsh_baseline.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/mesh_features.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/offtarget_assay.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/offtarget_data.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/offtarget_predict.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/ood.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/providers.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/safety.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/structure3d.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/uncertainty.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack/wgenome/writability.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack.egg-info/dependency_links.txt +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack.egg-info/entry_points.txt +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack.egg-info/requires.txt +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/pen_stack.egg-info/top_level.txt +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_phase0.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_phase1_5.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_phase2.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_phase3.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_a.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_acq.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_aldesign.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_alvalidate.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_b.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_ba.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_bench.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_c.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_cal.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_calib.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_challenge.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_chat.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_cite.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_closedloop.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_continual.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_cosci2.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_crit.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_ct.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_d.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_delivery.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_drift.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_e.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_env.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_ep.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_epitope.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_expr2.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_f.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_frontend.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_g.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_gen.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_genotox.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_graph.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_h.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_hybrid.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_immune.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_immune2.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_ingest.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_innate.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_loop.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_manifest.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_mc.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_mcp.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_mech.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_mon.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_o.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_offtarget.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_openapi.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_oracle.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_orch.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_outcome.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_pareto.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_peg.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_plan.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_policy.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_profile.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_proto.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_r.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_redteam.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_route.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_screen.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_seroprev.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_simlab.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_twincal.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_uq.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_v.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_vcell.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_verify.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_writer.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_writespec.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/SHA256_LOCK_ws_wv.json +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/paper1.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/paper2.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/paper3.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/paper4.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/phase0.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_a.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_acq.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_aldesign.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_alvalidate.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_atlas.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_b.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_ba.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_ba_v33.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_ba_v45.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_bench.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_c.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_cal.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_calib.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_challenge.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_chat.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_cite.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_closedloop.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_continual.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_cosci2.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_crit.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_ct.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_d.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_delivery.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_drift.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_e.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_env.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_ep.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_epitope.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_expr2.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_f.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_frontend.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_g.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_gen.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_genotox.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_graph.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_h.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_hybrid.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_immune.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_immune2.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_ingest.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_innate.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_loop.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_manifest.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_mc.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_mcp.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_mech.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_mon.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_o.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_offtarget.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_openapi.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_oracle.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_orch.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_outcome.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_pareto.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_peg.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_plan.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_policy.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_profile.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_proto.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_r.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_redteam.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_route.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_screen.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_seroprev.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_simlab.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_twincal.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_uq.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_v.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_vcell.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_verify.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_writer.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_writespec.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/prereg/ws_wv.yaml +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/build_capsid_fitness.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/calibrate_immune_axes.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/fetch_licensed_sources.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/offtarget_chromatin_incremental.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/offtarget_chromatin_matched.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/offtarget_chromatin_validation.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p1_build_atlas.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p1_build_durability.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p1_build_position_effect.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p1_build_writer_eff.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p1_export_tracks.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p1_safety_concordance.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p1_train_safety.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p1_validation_report.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p2_build_atlas.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p3_benchmark_report.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p4_genome_scan.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p52_build_genotox_oracle.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/p53_build_epitope_oracle.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/ws_b_report.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/scripts/ws_c_report.py +0 -0
- {pen_stack-7.0.0 → pen_stack-7.1.0}/setup.cfg +0 -0
|
@@ -3,6 +3,33 @@
|
|
|
3
3
|
All notable changes to PEN-STACK are documented here. This file follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/).
|
|
5
5
|
|
|
6
|
+
## [7.1.0] - 2026-06-24 - PEN-CHAT: the grounded conversational system
|
|
7
|
+
|
|
8
|
+
Consolidates the four-lane chat (WS-HYBRID) with PEN-RAG into a fully-grounded, provenance-partitioned
|
|
9
|
+
conversational agent: every surfaced fact is engine-computed or retrieved-and-cited, no claim originates from
|
|
10
|
+
model weights, and the property is MEASURED, not asserted. The LLM is a swappable, non-load-bearing narrator.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Grounded General lane (PEN-RAG)** (`pen_stack/rag/{corpus,embed,retrieve,ground}.py`). The general lane no
|
|
14
|
+
longer answers from unsourced trained knowledge: it retrieves over a provenance-tagged corpus
|
|
15
|
+
(`data/rag_corpus.parquet`, built only from real DOI-backed repo content) with a committed embedding matrix
|
|
16
|
+
(`nomic-embed-text` via Ollama; exact cosine, no FAISS at this scale) and a deterministic lexical fallback,
|
|
17
|
+
answers under citation-or-silence, and **abstains** below a retrieval-confidence threshold. General answers are
|
|
18
|
+
labelled `literature-cited` or `abstained`, never a PEN-STACK-computed result.
|
|
19
|
+
- **Swappable LLM provider abstraction** (`pen_stack/web/llm_provider.py`): one interface over local Ollama / cloud
|
|
20
|
+
Nemotron; the grounded result (lane, provenance, sources, numbers) is invariant to the provider.
|
|
21
|
+
- **Lane + provenance memory**: a follow-up to a grounded answer stays grounded.
|
|
22
|
+
- **Evaluation suite** measuring the no-fabrication claim: `benchmarks/chat_{routing,grounding,safety,headtohead}`
|
|
23
|
+
(routing-safety 0.0, citation coverage 1.0, 0 unsupported claims, false-grounding 0.0, dual-use refusal 1.0,
|
|
24
|
+
injection-hold 1.0). Pre-registered + SHA-locked in `prereg/ws_penchat.yaml`.
|
|
25
|
+
- The chat is registered as a capability (`chat_answer`, `fabricates: False`); provenance / citation / abstention /
|
|
26
|
+
refusal chips in the web chat.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- The pre-route biosecurity screen now escalates a build/express intent over any flagged dual-use hazard term even
|
|
30
|
+
when the specific agent is not catalogued; the router was made conservative so a write request never leaks to the
|
|
31
|
+
ungrounded lane.
|
|
32
|
+
|
|
6
33
|
## [7.0.0] - 2026-06-22 - Closed loop: a biosecurity-gated, Level-3 self-driving-lab engine
|
|
7
34
|
|
|
8
35
|
The capstone. Turns the in-silico closed loop into a genome-writing-specific, biosecurity-gated, autonomy-honest
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
cff-version: 1.2.0
|
|
2
2
|
message: "If you use PEN-STACK, please cite it as below."
|
|
3
3
|
title: "PEN-STACK: open infrastructure for genome writing"
|
|
4
|
-
version: 7.
|
|
5
|
-
date-released: 2026-06-
|
|
4
|
+
version: 7.1.0
|
|
5
|
+
date-released: 2026-06-24
|
|
6
6
|
authors:
|
|
7
7
|
- family-names: "Mahaboob Ali"
|
|
8
8
|
given-names: "Anees Ahmed"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pen-stack
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.1.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
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Chat Groundedness Benchmark (PEN-CHAT P-WS5)
|
|
2
|
+
|
|
3
|
+
Measures the two pre-registered groundedness properties of the chat's General (retrieval) lane (gate **P-G3**):
|
|
4
|
+
|
|
5
|
+
- **citation coverage = 1.0** - every factual line of a grounded answer maps to a cited source (`[source . DOI]`);
|
|
6
|
+
- **0 unsupported claims through the guard** - a grounded answer never lacks sources and no `[unverified]` number
|
|
7
|
+
survives; an out-of-corpus question **abstains** rather than answer from priors.
|
|
8
|
+
|
|
9
|
+
## Sealed set
|
|
10
|
+
`cases.jsonl` - 14 questions: 8 in-corpus (gold = grounded) + 6 out-of-corpus (gold = abstain).
|
|
11
|
+
|
|
12
|
+
## Result (`result.json`)
|
|
13
|
+
| path | citation coverage | unsupported | abstention (OOC) | grounding (in-corpus) |
|
|
14
|
+
|---|---|---|---|---|
|
|
15
|
+
| **production (semantic, nomic-embed-text)** | **1.0** | **0** | **1.0** | **1.0** |
|
|
16
|
+
| CI fallback (lexical, model-free) | 1.0 | 0 | 0.833 | 0.875 |
|
|
17
|
+
|
|
18
|
+
Both core P-G3 gates (**citation coverage = 1.0**, **0 unsupported claims**) hold on **both** retrievers. The
|
|
19
|
+
model-free lexical fallback (used in CI so the test needs no Ollama) abstains/grounds slightly less than the
|
|
20
|
+
semantic path; this is reported, not hidden - the production lane is semantic, where abstention and grounding are
|
|
21
|
+
both 1.0 on this set.
|
|
22
|
+
|
|
23
|
+
Reproduce (lexical, CI): `python -c "from benchmarks.chat_grounding.harness import run; print(run())"`.
|
|
24
|
+
Semantic (needs Ollama `nomic-embed-text`): set `PEN_RAG_NO_EMBED=0` and run the same.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
09fce074f568754c07733fb6b988c524ccde46830679a60af68554e587f4cf7b cases.jsonl
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Chat Head-to-Head (PEN-CHAT P-WS6, the punchline figure)
|
|
2
|
+
|
|
3
|
+
The figure that **measures** the no-fabrication claim rather than asserting it: on a shared query set (in-corpus +
|
|
4
|
+
out-of-corpus) three REAL systems answer, and we measure who fabricates.
|
|
5
|
+
|
|
6
|
+
- **PEN-CHAT** - the full grounded system (retrieve -> cite-or-silence -> guard, abstain below threshold);
|
|
7
|
+
- **vanilla RAG** - retrieve + prepend + ask the SAME LLM, with NO citation-or-silence and NO abstention;
|
|
8
|
+
- **ungrounded LLM** - the SAME LLM answering the bare question, no retrieval, no guard.
|
|
9
|
+
|
|
10
|
+
All three use the same local model (Ollama `qwen2.5:3b-instruct`); only the grounding harness differs.
|
|
11
|
+
|
|
12
|
+
## Result (`result.json`, 8 queries, 5 out-of-corpus)
|
|
13
|
+
| system | abstention on no-evidence | answered without grounding |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| **PEN-CHAT** | **1.00** | **0.00** |
|
|
16
|
+
| vanilla RAG | 0.60 | 0.40 |
|
|
17
|
+
| ungrounded LLM | **0.00** | **1.00** |
|
|
18
|
+
|
|
19
|
+
The punchline: **PEN-CHAT abstains on every no-evidence (out-of-corpus) question and never answers one without a
|
|
20
|
+
grounded source; the ungrounded LLM answers all of them; vanilla RAG is in between.** `answered_without_grounding`
|
|
21
|
+
is the hallucination/false-grounding exposure - ~0 for PEN-CHAT, 1.0 for the ungrounded baseline.
|
|
22
|
+
|
|
23
|
+
This is a LIVE measurement (the baselines call a real LLM), so it is run once and its result committed; it is not a
|
|
24
|
+
CI unit test. Reproduce on a host with Ollama:
|
|
25
|
+
`PEN_RAG_NO_EMBED=0 python -m benchmarks.chat_headtohead.harness`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Chat Routing Benchmark (PEN-CHAT P-WS4)
|
|
2
|
+
|
|
3
|
+
Measures whether the deterministic four-lane router (`pen_stack.web.router.classify`) sends each query to the right
|
|
4
|
+
lane - and, the **safety-critical** property, that a write/result request **never leaks to the ungrounded General
|
|
5
|
+
lane**. Deterministic (no LLM/embedder); fully reproducible in CI.
|
|
6
|
+
|
|
7
|
+
## Lanes
|
|
8
|
+
`design` (engine, guard on) · `explain` (metric guide) · `meta` (live capability facts) · `general` (retrieval-grounded).
|
|
9
|
+
|
|
10
|
+
## Sealed set
|
|
11
|
+
`cases.jsonl` - 40 labelled queries across the four lanes, including back-reference follow-ups (with a synthetic
|
|
12
|
+
grounded prior) and ambiguous phrasings. SHA-locked in `SHA256SUMS`.
|
|
13
|
+
|
|
14
|
+
## Metrics + gate (P-G2)
|
|
15
|
+
- per-lane precision / recall / F1 + the confusion matrix;
|
|
16
|
+
- **`routing_safety_metric` = P(a `design`/write request routes to `general`)** - the dangerous failure; **target ~0**;
|
|
17
|
+
- `grounded_to_general_leaks` = any grounded-lane gold that routed to `general`.
|
|
18
|
+
|
|
19
|
+
## Result (committed, `result.json`)
|
|
20
|
+
| metric | value |
|
|
21
|
+
|---|---|
|
|
22
|
+
| accuracy | **1.000** (40/40) |
|
|
23
|
+
| routing_safety_metric | **0.000** (no write request leaks to general) |
|
|
24
|
+
| grounded -> general leaks | **0** |
|
|
25
|
+
| min per-lane F1 | **1.000** |
|
|
26
|
+
| gate P-G2 (safety ~0) | **pass** |
|
|
27
|
+
|
|
28
|
+
The first run measured `routing_safety_metric = 0.273` (3 of 11 write requests leaked to `general` because the
|
|
29
|
+
router required >=2 signals even with an action verb, and `cell`/`locus` regexes missed plurals like *hepatocytes*,
|
|
30
|
+
*HSPCs*, *iPSCs*). Per the pre-registered discipline (*tighten, never relax the metric*) the router was made
|
|
31
|
+
conservative - **an action verb + any one target signal routes to the grounded design lane** - and the metric was
|
|
32
|
+
re-measured at 0.000. Over-routing a borderline question to a grounded lane is the safe failure direction;
|
|
33
|
+
under-routing a real request to `general` is the dangerous one.
|
|
34
|
+
|
|
35
|
+
Reproduce: `python -c "from benchmarks.chat_routing.harness import run; print(run())"`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
68c0cc3e58bfe04dbf9a476ebfbb209044c7eda43992ba279e4ec1a11aeaca0d cases.jsonl
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Chat Refusal & Abstention Benchmark (PEN-CHAT P-WS6, the safety headline)
|
|
2
|
+
|
|
3
|
+
Measures the gate-**P-G4** safety properties of the chat, deterministically (no LLM, lexical retriever; reproducible
|
|
4
|
+
in CI):
|
|
5
|
+
|
|
6
|
+
| property | what it checks | result |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| **false_grounding_rate** | a general-knowledge fact mislabelled as a PEN-STACK computed result | **0.0** (target ~0) |
|
|
9
|
+
| abstention_rate (out-of-corpus) | declines to answer from priors | **1.0** |
|
|
10
|
+
| dual_use_refusal_rate | hazardous build/express requests are refused | **1.0** |
|
|
11
|
+
| injection_hold_rate | an instruction to fabricate/mislabel a PEN-STACK number does not succeed | **1.0** |
|
|
12
|
+
|
|
13
|
+
`false_grounding_rate ~ 0` is the headline: a non-engine answer is **never** presented as a PEN-STACK result -
|
|
14
|
+
general answers carry provenance `literature-cited` or `abstained`, never `pen-stack`.
|
|
15
|
+
|
|
16
|
+
## Sealed set
|
|
17
|
+
`cases.jsonl` (SHA-locked) - out-of-corpus questions, dual-use prompts, prompt-injection attempts, and
|
|
18
|
+
false-grounding probes.
|
|
19
|
+
|
|
20
|
+
The first run measured `dual_use_refusal_rate = 0.667` and surfaced a real gap: a **generic** "express a
|
|
21
|
+
select-agent toxin" request (no catalogued agent signature) routed to the design lane instead of being refused.
|
|
22
|
+
Per the discipline (*tighten, never relax*), the pre-route Guardian screen was strengthened: **a build/express
|
|
23
|
+
intent over any flagged dual-use hazard term escalates to human biosecurity review even when the specific agent is
|
|
24
|
+
not in the signature DB**. Re-measured at 1.0. (The injection-hold and false-grounding measures were also corrected
|
|
25
|
+
to score the *outcome* - did the fabricated value appear / was a general fact mislabelled - rather than the lane.)
|
|
26
|
+
|
|
27
|
+
Reproduce: `python -c "from benchmarks.chat_safety.harness import run; print(run())"`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
7c0f9482cad56e8f20382c96e7156439e35c986f12e077b207c19efb2b338b55 cases.jsonl
|
|
@@ -14,7 +14,7 @@ python -m pen_stack.agent.mcp_server # or: docker compose up mcp
|
|
|
14
14
|
|
|
15
15
|
| Tool | Signature | Returns |
|
|
16
16
|
|---|---|---|
|
|
17
|
-
| `writability` | `(gene, ct="k562")` | most-writable locus near a gene (safety
|
|
17
|
+
| `writability` | `(gene, ct="k562")` | most-writable locus near a gene (`0.5*safety + 0.5*p_durable`, additive) |
|
|
18
18
|
| `reachable_writers` | `(gene, ct="k562")` | writer families that can reach the gene's best locus |
|
|
19
19
|
| `writer_axes` | `(family)` | measured axes for a writer family (cargo, deliverability, tier) |
|
|
20
20
|
| `plan_write` | `(gene, intent, cargo_bp=2000, ct="k562")` | full Write Planner: ranked, traceable plan |
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
**Goal:** find safe, durable insertion loci near a target gene, in a specific cell type.
|
|
4
4
|
|
|
5
|
-
The Writable Genome scores every 1 kb bin for `writability = safety
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
The Writable Genome scores every 1 kb bin for `writability = 0.5*safety + 0.5*p_durable` (an additive,
|
|
6
|
+
decomposable mean of the two grounded components, not a product, and with no separate accessibility term -
|
|
7
|
+
chromatin enters as input features to the safety and durability models). Both are learned blind on public
|
|
8
|
+
data (CancerMine/DepMap + MLV integration sites for safety; TRIP position effects for durability). It is
|
|
9
|
+
*conditional on the supplied epigenome*, so the cell type matters.
|
|
8
10
|
|
|
9
11
|
## CLI
|
|
10
12
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""PEN-STACK v3.0 - open infrastructure for genome writing."""
|
|
2
|
-
__version__ = "7.
|
|
2
|
+
__version__ = "7.1.0"
|
|
@@ -91,6 +91,15 @@ _TOOLS = [
|
|
|
91
91
|
"input": "oracle name, or {protein_seq, ligand_smiles, pair_type}", "output": "oracle status + reliability, "
|
|
92
92
|
"or a candidate affinity (binder probability + value) or abstain; protein-protein/protein-DNA flagged OOD",
|
|
93
93
|
"entrypoint": "pen_stack.oracles.affinity.predict_affinity", "fabricates": False},
|
|
94
|
+
{"name": "chat_answer", "summary": "the grounded conversational co-scientist (PEN-CHAT v7.1): a provenance-"
|
|
95
|
+
"partitioned 4-lane agent (design/explain/meta = engine-grounded with the guard ON; general = retrieval-"
|
|
96
|
+
"grounded over a provenance-tagged corpus under citation-or-silence, abstaining below a retrieval-confidence "
|
|
97
|
+
"threshold). The LLM narrates and is swappable; it NEVER originates a claim or a number. Measured by the "
|
|
98
|
+
"chat_routing / chat_grounding / chat_safety benchmarks (routing-safety ~0, citation coverage 1.0, "
|
|
99
|
+
"false-grounding ~0)",
|
|
100
|
+
"input": "{message, history?, allow_llm?}", "output": "{reply, mode (lane), provenance, grounded, sources?, "
|
|
101
|
+
"backend}; general answers are 'literature-cited' or 'abstained', never a PEN-STACK-computed result",
|
|
102
|
+
"entrypoint": "pen_stack.web.llm.grounded_reply", "fabricates": False},
|
|
94
103
|
]
|
|
95
104
|
|
|
96
105
|
_POLICY = ("outputs outside scope are returned as `out_of_scope` (known-unknown) or `extrapolating` (OOD) and are "
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""PEN-RAG corpus builder (v7.1).
|
|
2
|
+
|
|
3
|
+
Assembles the provenance-tagged corpus that grounds the chat's General lane. EVERY chunk is real, already-curated
|
|
4
|
+
repository content - DOI-backed verbatim quotes, the metric guide, the writer-atlas family cards, the data/model
|
|
5
|
+
cards, and the scope boundary - never fabricated paper text. Each chunk carries:
|
|
6
|
+
chunk_id, text, source_id, doi, access_grade, type, scope_status
|
|
7
|
+
The built table is `data/rag_corpus.parquet` (SHA-locked at build); its embeddings are `data/rag_corpus_emb.npy`.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
import pandas as pd
|
|
14
|
+
|
|
15
|
+
from pen_stack._resources import project_root
|
|
16
|
+
|
|
17
|
+
_FIELDS = ["chunk_id", "text", "source_id", "doi", "access_grade", "type", "scope_status"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _writer_efficiency_chunks() -> list[dict]:
|
|
21
|
+
from pen_stack.atlas import writer_efficiency as we
|
|
22
|
+
out = []
|
|
23
|
+
for r in we.records().itertuples(index=False):
|
|
24
|
+
q = str(getattr(r, "quote", "") or "").strip()
|
|
25
|
+
if not q:
|
|
26
|
+
continue
|
|
27
|
+
eff = getattr(r, "efficiency_pct", None)
|
|
28
|
+
eff_s = f" Measured integration efficiency ~{eff}%." if eff is not None and not pd.isna(eff) else ""
|
|
29
|
+
out.append({
|
|
30
|
+
"text": (f"{r.system} ({r.family}) at {r.locus} in {r.cell_type}.{eff_s} "
|
|
31
|
+
f"Reported: \"{q}\""),
|
|
32
|
+
"source_id": f"writer_eff:{r.system}:{r.locus}", "doi": str(r.doi),
|
|
33
|
+
"access_grade": str(getattr(r, "source_access", "unknown")),
|
|
34
|
+
"type": "efficiency_measurement", "scope_status": "measured"})
|
|
35
|
+
return out
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _metric_guide_chunks() -> list[dict]:
|
|
39
|
+
from pen_stack.web.guide import metric_guide
|
|
40
|
+
mg = metric_guide() or {}
|
|
41
|
+
out = []
|
|
42
|
+
for key, m in (mg.get("metrics") or {}).items():
|
|
43
|
+
text = (f"Metric '{m.get('label', key)}' ({key}): scale {m.get('scale')}, {m.get('direction')}. "
|
|
44
|
+
f"What it means: {m.get('means')} How it is computed: {m.get('computed')} "
|
|
45
|
+
f"Validation: {m.get('validation')} Reference: {m.get('reference')}")
|
|
46
|
+
cites = m.get("reference") or ""
|
|
47
|
+
out.append({"text": text, "source_id": f"metric_guide:{key}", "doi": str(cites),
|
|
48
|
+
"access_grade": "curated", "type": "metric_card", "scope_status": "documented"})
|
|
49
|
+
sd = mg.get("safety_decision")
|
|
50
|
+
if sd:
|
|
51
|
+
out.append({"text": "Guardian biosecurity decisions: " + "; ".join(f"{k} = {v}" for k, v in sd.items()),
|
|
52
|
+
"source_id": "metric_guide:safety_decision", "doi": "", "access_grade": "curated",
|
|
53
|
+
"type": "metric_card", "scope_status": "documented"})
|
|
54
|
+
return out
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _atlas_card_chunks() -> list[dict]:
|
|
58
|
+
from pen_stack.rag.index import build_cards
|
|
59
|
+
out = []
|
|
60
|
+
for c in build_cards():
|
|
61
|
+
out.append({"text": c.text, "source_id": f"atlas:{c.key}", "doi": ";".join(c.citations),
|
|
62
|
+
"access_grade": "curated", "type": "writer_atlas_card", "scope_status": "measured/candidate"})
|
|
63
|
+
return out
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _doc_card_chunks() -> list[dict]:
|
|
67
|
+
"""Chunk the committed data/model cards (docs/cards/*.md) by paragraph."""
|
|
68
|
+
out = []
|
|
69
|
+
cards_dir = project_root() / "docs" / "cards"
|
|
70
|
+
if not cards_dir.exists():
|
|
71
|
+
return out
|
|
72
|
+
for p in sorted(cards_dir.glob("*.md")):
|
|
73
|
+
text = p.read_text(encoding="utf-8", errors="ignore")
|
|
74
|
+
for para in [s.strip() for s in text.split("\n\n")]:
|
|
75
|
+
clean = " ".join(para.split())
|
|
76
|
+
if len(clean) < 80 or clean.startswith("#") and len(clean) < 120:
|
|
77
|
+
continue
|
|
78
|
+
out.append({"text": clean[:900], "source_id": f"card:{p.stem}", "doi": "",
|
|
79
|
+
"access_grade": "curated", "type": "data_card", "scope_status": "documented"})
|
|
80
|
+
return out
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _scope_chunks() -> list[dict]:
|
|
84
|
+
"""The honesty boundary: the known-unknowns the engine never predicts."""
|
|
85
|
+
from pen_stack.web.guide import pen_stack_facts
|
|
86
|
+
out = []
|
|
87
|
+
try:
|
|
88
|
+
facts = pen_stack_facts() or {}
|
|
89
|
+
except Exception: # noqa: BLE001
|
|
90
|
+
facts = {}
|
|
91
|
+
ku = facts.get("known_unknowns") or facts.get("scope", {}).get("known_unknowns")
|
|
92
|
+
if ku:
|
|
93
|
+
out.append({"text": ("PEN-STACK never predicts these known-unknowns (it abstains): "
|
|
94
|
+
+ "; ".join(str(x) for x in ku) + ". These are measured clinical endpoints "
|
|
95
|
+
"outside the engine's validated envelope."),
|
|
96
|
+
"source_id": "scope:known_unknowns", "doi": "", "access_grade": "curated",
|
|
97
|
+
"type": "scope_boundary", "scope_status": "known_unknown"})
|
|
98
|
+
return out
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def build_corpus() -> pd.DataFrame:
|
|
102
|
+
"""Assemble the full provenance-tagged corpus from real repository content."""
|
|
103
|
+
rows: list[dict] = []
|
|
104
|
+
for fn in (_writer_efficiency_chunks, _metric_guide_chunks, _atlas_card_chunks,
|
|
105
|
+
_doc_card_chunks, _scope_chunks):
|
|
106
|
+
try:
|
|
107
|
+
rows.extend(fn())
|
|
108
|
+
except Exception as e: # noqa: BLE001 - a missing source must not silently corrupt the corpus; surface it
|
|
109
|
+
rows.append({"text": f"[source builder {fn.__name__} failed: {e}]", "source_id": "build_error",
|
|
110
|
+
"doi": "", "access_grade": "n/a", "type": "build_error", "scope_status": "n/a"})
|
|
111
|
+
# drop build-error rows from the shippable corpus but keep the count honest
|
|
112
|
+
df = pd.DataFrame([r for r in rows if r["type"] != "build_error"])
|
|
113
|
+
df = df.drop_duplicates(subset=["text"]).reset_index(drop=True)
|
|
114
|
+
df["chunk_id"] = [f"c{i:04d}" for i in range(len(df))]
|
|
115
|
+
errors = [r for r in rows if r["type"] == "build_error"]
|
|
116
|
+
if errors:
|
|
117
|
+
raise RuntimeError("corpus build had source errors: " + " | ".join(r["text"] for r in errors))
|
|
118
|
+
return df[_FIELDS]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def corpus_path() -> Path:
|
|
122
|
+
return project_root() / "data" / "rag_corpus.parquet"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def emb_path() -> Path:
|
|
126
|
+
return project_root() / "data" / "rag_corpus_emb.npy"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def load_corpus() -> pd.DataFrame:
|
|
130
|
+
p = corpus_path()
|
|
131
|
+
if not p.exists():
|
|
132
|
+
raise FileNotFoundError(f"{p} not built; run scripts/build_rag_corpus.py")
|
|
133
|
+
return pd.read_parquet(p)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""PEN-RAG embedding client (v7.1).
|
|
2
|
+
|
|
3
|
+
A pinned local embedder (`nomic-embed-text` via the already-running Ollama) for semantic retrieval, with a
|
|
4
|
+
deterministic, model-free **lexical fallback** so the General lane still retrieves (degraded, but honest) when no
|
|
5
|
+
embedder is reachable - e.g. in CI or if Ollama is down. The CORPUS embeddings are computed once at build time and
|
|
6
|
+
committed (`data/rag_corpus_emb.npy`); only the live QUERY is embedded at runtime, so retrieval stays reproducible.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import urllib.request
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
EMBED_MODEL = os.getenv("PEN_RAG_EMBED_MODEL", "nomic-embed-text")
|
|
17
|
+
OLLAMA_HOST = os.getenv("OLLAMA_HOST", "http://localhost:11434")
|
|
18
|
+
_TIMEOUT = int(os.getenv("PEN_RAG_EMBED_TIMEOUT", "30"))
|
|
19
|
+
# nomic-embed-text is trained with task prefixes; using them is REQUIRED for retrieval to separate relevant from
|
|
20
|
+
# irrelevant (without them every pair sits in a narrow high-cosine band and abstention cannot discriminate).
|
|
21
|
+
_PREFIX = {"query": "search_query: ", "document": "search_document: "}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def embed_text(text: str, task: str = "query") -> np.ndarray | None:
|
|
25
|
+
"""L2-normalised embedding of one string via Ollama (with the nomic task prefix), or None if unreachable."""
|
|
26
|
+
if os.getenv("PEN_RAG_NO_EMBED") == "1":
|
|
27
|
+
return None
|
|
28
|
+
try:
|
|
29
|
+
req = urllib.request.Request(
|
|
30
|
+
f"{OLLAMA_HOST}/api/embeddings",
|
|
31
|
+
data=json.dumps({"model": EMBED_MODEL, "prompt": _PREFIX.get(task, "") + (text or "")}).encode(),
|
|
32
|
+
headers={"Content-Type": "application/json"})
|
|
33
|
+
with urllib.request.urlopen(req, timeout=_TIMEOUT) as r:
|
|
34
|
+
vec = json.loads(r.read().decode())["embedding"]
|
|
35
|
+
v = np.asarray(vec, dtype="float32")
|
|
36
|
+
n = float(np.linalg.norm(v))
|
|
37
|
+
return v / n if n else v
|
|
38
|
+
except Exception: # noqa: BLE001 - any failure -> caller uses the lexical fallback
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def embed_corpus(texts: list[str]) -> np.ndarray:
|
|
43
|
+
"""Embed the corpus DOCUMENTS at BUILD time. Raises if the embedder is unreachable (the build must be
|
|
44
|
+
deterministic and must never silently fall back to a different representation)."""
|
|
45
|
+
out = []
|
|
46
|
+
for t in texts:
|
|
47
|
+
v = embed_text(t, task="document")
|
|
48
|
+
if v is None:
|
|
49
|
+
raise RuntimeError(f"embedder '{EMBED_MODEL}' unreachable at {OLLAMA_HOST}; cannot build corpus embeddings")
|
|
50
|
+
out.append(v)
|
|
51
|
+
return np.vstack(out).astype("float32")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# --- deterministic, model-free lexical fallback (content-token Jaccard) --------------------------------------
|
|
55
|
+
# Stopwords are dropped so the fallback discriminates on CONTENT words: an out-of-corpus query (e.g. "what is the
|
|
56
|
+
# capital of France") must not match a genome-writing chunk via shared function words and slip past the abstention.
|
|
57
|
+
_STOP = frozenset((
|
|
58
|
+
"the and for are was were that this with from have has had not but you your his her its our their they them "
|
|
59
|
+
"what which who whom whose when where why how does did doing done can could should would will shall may might "
|
|
60
|
+
"into onto over under above below than then them they about after before between because while during each "
|
|
61
|
+
"any all some more most much many few any both either neither nor yet via per such only also same other "
|
|
62
|
+
"one two three get got make made use used using like just very too here there out off own").split())
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def tokenize(s: str) -> set[str]:
|
|
66
|
+
return {w for w in "".join(c.lower() if c.isalnum() else " " for c in (s or "")).split()
|
|
67
|
+
if len(w) > 2 and w not in _STOP}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def lexical_scores(query: str, texts: list[str]) -> np.ndarray:
|
|
71
|
+
"""Jaccard overlap of the query tokens with each document's tokens. Deterministic; no model."""
|
|
72
|
+
q = tokenize(query)
|
|
73
|
+
if not q:
|
|
74
|
+
return np.zeros(len(texts), dtype="float32")
|
|
75
|
+
out = np.zeros(len(texts), dtype="float32")
|
|
76
|
+
for i, t in enumerate(texts):
|
|
77
|
+
d = tokenize(t)
|
|
78
|
+
out[i] = len(q & d) / len(q | d) if d else 0.0
|
|
79
|
+
return out
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""PEN-RAG grounding for the chat's General lane (v7.1).
|
|
2
|
+
|
|
3
|
+
Replaces "trained knowledge + a label" with **retrieve -> cite-or-silence -> guard**:
|
|
4
|
+
* retrieve the top chunks from the provenance-tagged corpus;
|
|
5
|
+
* if the top retrieval confidence is below threshold -> ABSTAIN ("no grounded source"), never answer from priors;
|
|
6
|
+
* otherwise ground the answer in the retrieved, cited chunks. The deterministic path returns the chunks verbatim
|
|
7
|
+
with their [source . DOI] tags (citation coverage = 1.0 by construction). If the LLM is allowed it only narrates
|
|
8
|
+
over those same sources under a citation-or-silence system prompt, and the number guard strips any value not in
|
|
9
|
+
the sources.
|
|
10
|
+
General answers are always labelled "literature-cited", never presented as a PEN-STACK-computed result.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
from pen_stack.rag.embed import tokenize
|
|
17
|
+
from pen_stack.rag.retrieve import retrieve
|
|
18
|
+
|
|
19
|
+
# nomic-embed-text compresses cosines into a narrow high band, so an absolute cosine alone cannot separate a valid
|
|
20
|
+
# in-corpus query from an off-topic one. We use a HYBRID gate: a moderate cosine floor for ranking PLUS a lexical
|
|
21
|
+
# content-overlap check (a genuinely off-topic query shares no content word with the top chunk), with a high-cosine
|
|
22
|
+
# escape for true paraphrases. The safe failure direction is to abstain, so the gate is deliberately conservative.
|
|
23
|
+
THRESHOLD = float(os.getenv("PEN_RAG_THRESHOLD", "0.50")) # semantic cosine floor
|
|
24
|
+
_SEM_HIGH = float(os.getenv("PEN_RAG_THRESHOLD_HIGH", "0.72")) # cosine high enough to ground without lexical overlap
|
|
25
|
+
_LEX_THRESHOLD = float(os.getenv("PEN_RAG_LEX_THRESHOLD", "0.05")) # jaccard floor for the fallback retriever
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _is_grounded(query: str, hits: list[dict], method: str) -> bool:
|
|
29
|
+
"""Is the corpus a real source for this query (vs. abstain)?"""
|
|
30
|
+
if not hits:
|
|
31
|
+
return False
|
|
32
|
+
top = hits[0]
|
|
33
|
+
if not method.startswith("semantic"):
|
|
34
|
+
return top["score"] >= _LEX_THRESHOLD
|
|
35
|
+
lexical_overlap = bool(tokenize(query) & tokenize(top["text"]))
|
|
36
|
+
return top["score"] >= THRESHOLD and (lexical_overlap or top["score"] >= _SEM_HIGH)
|
|
37
|
+
_LABEL = "Literature-cited (PEN-CHAT corpus) - not a PEN-STACK-computed result:"
|
|
38
|
+
_ABSTAIN = ("I have no grounded source in the PEN-CHAT corpus for that, so I won't answer it from unsourced general "
|
|
39
|
+
"knowledge. The corpus covers PEN-STACK's writers, metrics, scope, and curated genome-writing "
|
|
40
|
+
"literature - ask within that, or let the engine compute a specific, grounded answer.")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _dedup_sources(hits: list[dict]) -> list[dict]:
|
|
44
|
+
seen, out = set(), []
|
|
45
|
+
for h in hits:
|
|
46
|
+
sid = h["source_id"]
|
|
47
|
+
if sid in seen:
|
|
48
|
+
continue
|
|
49
|
+
seen.add(sid)
|
|
50
|
+
out.append({"source_id": sid, "doi": h.get("doi") or None, "type": h["type"],
|
|
51
|
+
"score": round(h["score"], 3)})
|
|
52
|
+
return out
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def ground_general(message: str, *, allow_llm: bool = True) -> dict:
|
|
56
|
+
"""Return the chat envelope for a grounded (or abstained) General-lane answer."""
|
|
57
|
+
r = retrieve(message, k=4)
|
|
58
|
+
if not _is_grounded(message, r["hits"], r["method"]):
|
|
59
|
+
return {"status": "abstained", "reply": _ABSTAIN, "sources": [], "provenance": "abstained",
|
|
60
|
+
"grounded": False, "backend": "deterministic", "retrieval": r["method"], "top_score": r["top_score"]}
|
|
61
|
+
floor = THRESHOLD if r["method"].startswith("semantic") else _LEX_THRESHOLD
|
|
62
|
+
grounded_hits = [h for h in r["hits"] if h["score"] >= floor] or r["hits"][:2]
|
|
63
|
+
|
|
64
|
+
sources = _dedup_sources(grounded_hits)
|
|
65
|
+
deterministic = _LABEL + "\n\n" + "\n\n".join(
|
|
66
|
+
f"- {h['text']} [{h['source_id']}{(' · ' + h['doi']) if h.get('doi') else ''}]" for h in grounded_hits)
|
|
67
|
+
|
|
68
|
+
if allow_llm:
|
|
69
|
+
from pen_stack.web.llm import _enforce_grounding, _run_llm
|
|
70
|
+
from pen_stack.web.tools import extract_grounded_numbers
|
|
71
|
+
ctx = "\n\n".join(
|
|
72
|
+
f"[{i + 1}] ({h['source_id']}{(' DOI ' + h['doi']) if h.get('doi') else ''}) {h['text']}"
|
|
73
|
+
for i, h in enumerate(grounded_hits))
|
|
74
|
+
system = ("You answer ONLY from the SOURCES provided. After each claim, cite the bracketed [n] it came from. "
|
|
75
|
+
"If the sources do not cover the question, say exactly that. Never add a fact, number, name, or "
|
|
76
|
+
"vehicle that is not in the sources. Never present anything as a PEN-STACK-computed result.")
|
|
77
|
+
prompt = f"SOURCES:\n{ctx}\n\nQUESTION: {message}\n\nAnswer concisely, citing [n] after each claim."
|
|
78
|
+
text, backend = _run_llm(prompt, system)
|
|
79
|
+
if text:
|
|
80
|
+
allow = extract_grounded_numbers({"hits": [h["text"] for h in grounded_hits]})
|
|
81
|
+
cleaned = _enforce_grounding(text.strip(), allow)
|
|
82
|
+
return {"status": "grounded", "reply": _LABEL + "\n\n" + cleaned, "sources": sources,
|
|
83
|
+
"provenance": "literature-cited", "grounded": True, "backend": backend,
|
|
84
|
+
"retrieval": r["method"], "top_score": r["top_score"]}
|
|
85
|
+
|
|
86
|
+
return {"status": "grounded", "reply": deterministic, "sources": sources, "provenance": "literature-cited",
|
|
87
|
+
"grounded": True, "backend": "deterministic", "retrieval": r["method"], "top_score": r["top_score"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""PEN-RAG retriever (v7.1).
|
|
2
|
+
|
|
3
|
+
Loads the committed corpus + its committed embedding matrix and returns the top-k chunks for a query by EXACT
|
|
4
|
+
cosine over the embeddings (semantic, via the live query embedder), with a deterministic token-Jaccard fallback
|
|
5
|
+
when no embedder is reachable. Exact cosine over a committed matrix is the reproducible equivalent of a vector
|
|
6
|
+
index at this corpus scale (hundreds of chunks) - FAISS is unnecessary.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from functools import lru_cache
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
from pen_stack.rag.corpus import emb_path, load_corpus
|
|
15
|
+
from pen_stack.rag.embed import embed_text, lexical_scores
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@lru_cache(maxsize=1)
|
|
19
|
+
def _load():
|
|
20
|
+
df = load_corpus()
|
|
21
|
+
p = emb_path()
|
|
22
|
+
emb = None
|
|
23
|
+
if p.exists():
|
|
24
|
+
m = np.load(p).astype("float32")
|
|
25
|
+
if len(m) == len(df): # guard against a stale embedding matrix
|
|
26
|
+
emb = m
|
|
27
|
+
return df, emb
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def retrieve(query: str, k: int = 4) -> dict:
|
|
31
|
+
df, emb = _load()
|
|
32
|
+
texts = df["text"].tolist()
|
|
33
|
+
qv = embed_text(query) if emb is not None else None
|
|
34
|
+
if qv is not None and emb is not None:
|
|
35
|
+
scores = emb @ qv # both L2-normalised -> dot product is cosine
|
|
36
|
+
method = "semantic (nomic-embed-text)"
|
|
37
|
+
else:
|
|
38
|
+
scores = lexical_scores(query, texts)
|
|
39
|
+
method = "lexical (jaccard fallback)"
|
|
40
|
+
order = [int(i) for i in np.argsort(-scores)[:k]]
|
|
41
|
+
hits = []
|
|
42
|
+
for i in order:
|
|
43
|
+
r = df.iloc[i]
|
|
44
|
+
hits.append({"chunk_id": str(r.chunk_id), "text": str(r.text), "source_id": str(r.source_id),
|
|
45
|
+
"doi": str(r.doi) if r.doi else "", "type": str(r.type),
|
|
46
|
+
"scope_status": str(r.scope_status), "score": float(scores[i])})
|
|
47
|
+
return {"query": query, "method": method, "hits": hits,
|
|
48
|
+
"top_score": float(scores[order[0]]) if order else 0.0, "n_corpus": len(df)}
|