factorforge-cds 3.4.5__tar.gz → 3.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. {factorforge_cds-3.4.5/src/factorforge_cds.egg-info → factorforge_cds-3.5.2}/PKG-INFO +49 -5
  2. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/README.md +44 -4
  3. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/pyproject.toml +7 -2
  4. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/__init__.py +3 -1
  5. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/analysis/metrics.py +69 -0
  6. factorforge_cds-3.5.2/src/factorforge/analysis/sequence_risks.py +97 -0
  7. factorforge_cds-3.5.2/src/factorforge/benchmark/models.py +62 -0
  8. factorforge_cds-3.5.2/src/factorforge/benchmark/runner.py +144 -0
  9. factorforge_cds-3.5.2/src/factorforge/cli/benchmark_cli.py +124 -0
  10. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/cli/main.py +461 -18
  11. factorforge_cds-3.5.2/src/factorforge/constraints/__init__.py +5 -0
  12. factorforge_cds-3.5.2/src/factorforge/constraints/type_iis.py +22 -0
  13. factorforge_cds-3.5.2/src/factorforge/core/host_model.py +155 -0
  14. factorforge_cds-3.5.2/src/factorforge/core/reranker.py +353 -0
  15. factorforge_cds-3.5.2/src/factorforge/core/slate_engine.py +241 -0
  16. factorforge_cds-3.5.2/src/factorforge/core/slate_generator.py +363 -0
  17. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/database.py +1 -1
  18. factorforge_cds-3.5.2/src/factorforge/db/__init__.py +5 -0
  19. factorforge_cds-3.5.2/src/factorforge/db/connector.py +514 -0
  20. factorforge_cds-3.5.2/src/factorforge/discovery/__init__.py +48 -0
  21. factorforge_cds-3.5.2/src/factorforge/discovery/acquisition.py +236 -0
  22. factorforge_cds-3.5.2/src/factorforge/discovery/acquisition_logger.py +263 -0
  23. factorforge_cds-3.5.2/src/factorforge/discovery/filter.py +165 -0
  24. factorforge_cds-3.5.2/src/factorforge/discovery/generator.py +431 -0
  25. factorforge_cds-3.5.2/src/factorforge/discovery/panel_builder.py +512 -0
  26. factorforge_cds-3.5.2/src/factorforge/discovery/schemas.py +129 -0
  27. factorforge_cds-3.5.2/src/factorforge/discovery/slate.py +310 -0
  28. factorforge_cds-3.5.2/src/factorforge/discovery/traits.py +95 -0
  29. factorforge_cds-3.5.2/src/factorforge/engines/__init__.py +63 -0
  30. factorforge_cds-3.5.2/src/factorforge/engines/balanced_optimizer.py +153 -0
  31. factorforge_cds-3.5.2/src/factorforge/engines/dp_adapter.py +80 -0
  32. factorforge_cds-3.5.2/src/factorforge/engines/dp_v2.py +203 -0
  33. factorforge_cds-3.5.2/src/factorforge/engines/dp_v2_1.py +312 -0
  34. factorforge_cds-3.5.2/src/factorforge/engines/dp_v2_1_1.py +547 -0
  35. factorforge_cds-3.5.2/src/factorforge/engines/lm/__init__.py +9 -0
  36. factorforge_cds-3.5.2/src/factorforge/engines/lm/adapter.py +103 -0
  37. factorforge_cds-3.5.2/src/factorforge/engines/lm/diagnostics.py +42 -0
  38. factorforge_cds-3.5.2/src/factorforge/engines/lm/inference.py +323 -0
  39. factorforge_cds-3.5.2/src/factorforge/engines/lm/models/__init__.py +5 -0
  40. factorforge_cds-3.5.2/src/factorforge/engines/lm/models/mbart_codon.py +146 -0
  41. factorforge_cds-3.5.2/src/factorforge/engines/lm/tokenizer/control_tokenizer.py +203 -0
  42. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/__init__.py +3 -1
  43. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/optimizer.py +43 -15
  44. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/scoring.py +58 -0
  45. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/registry.py +7 -0
  46. factorforge_cds-3.5.2/src/factorforge/engines/sllm/adapters.py +193 -0
  47. factorforge_cds-3.5.2/src/factorforge/engines/sllm/automaton.py +95 -0
  48. factorforge_cds-3.5.2/src/factorforge/engines/sllm/compiler.py +115 -0
  49. factorforge_cds-3.5.2/src/factorforge/engines/sllm/data/__init__.py +28 -0
  50. factorforge_cds-3.5.2/src/factorforge/engines/sllm/data/corpus_builder.py +314 -0
  51. factorforge_cds-3.5.2/src/factorforge/engines/sllm/data/tokenizer.py +166 -0
  52. factorforge_cds-3.5.2/src/factorforge/engines/sllm/decoder.py +522 -0
  53. factorforge_cds-3.5.2/src/factorforge/engines/sllm/interfaces.py +375 -0
  54. factorforge_cds-3.5.2/src/factorforge/engines/sllm/model/__init__.py +35 -0
  55. factorforge_cds-3.5.2/src/factorforge/engines/sllm/model/arch.py +179 -0
  56. factorforge_cds-3.5.2/src/factorforge/engines/sllm/model/baselines.py +138 -0
  57. factorforge_cds-3.5.2/src/factorforge/engines/sllm/model/export_onnx.py +110 -0
  58. factorforge_cds-3.5.2/src/factorforge/engines/targeted_remediator.py +93 -0
  59. factorforge_cds-3.5.2/src/factorforge/evaluation/evaluator.py +219 -0
  60. factorforge_cds-3.5.2/src/factorforge/evaluation/evidence_logger.py +120 -0
  61. factorforge_cds-3.5.2/src/factorforge/evaluation/metrics.py +29 -0
  62. factorforge_cds-3.5.2/src/factorforge/evaluation/models.py +78 -0
  63. factorforge_cds-3.5.2/src/factorforge/learning/__init__.py +14 -0
  64. factorforge_cds-3.5.2/src/factorforge/learning/ranker.py +272 -0
  65. factorforge_cds-3.5.2/src/factorforge/profiles/profile_snapshot.py +86 -0
  66. factorforge_cds-3.5.2/src/factorforge/registry/method_registry.py +61 -0
  67. factorforge_cds-3.5.2/src/factorforge/registry/model_registry.py +190 -0
  68. factorforge_cds-3.5.2/src/factorforge/registry/version_manifest.json +65 -0
  69. factorforge_cds-3.5.2/src/factorforge/registry/versioning.py +46 -0
  70. factorforge_cds-3.5.2/src/factorforge/review/evaluation_integrity.py +91 -0
  71. factorforge_cds-3.5.2/src/factorforge/rules/models.py +93 -0
  72. factorforge_cds-3.5.2/src/factorforge/rules/registry.py +474 -0
  73. factorforge_cds-3.5.2/src/factorforge/schemas/evidence.py +68 -0
  74. factorforge_cds-3.5.2/src/factorforge/schemas/run_config.schema.json +29 -0
  75. factorforge_cds-3.5.2/src/factorforge/scoring/quantization.py +98 -0
  76. factorforge_cds-3.5.2/src/factorforge/sops/config.py +125 -0
  77. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/utils/exceptions.py +5 -0
  78. factorforge_cds-3.5.2/src/factorforge/utils/sequence_identity.py +43 -0
  79. factorforge_cds-3.5.2/src/factorforge/validation/hub.py +303 -0
  80. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2/src/factorforge_cds.egg-info}/PKG-INFO +49 -5
  81. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge_cds.egg-info/SOURCES.txt +87 -0
  82. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge_cds.egg-info/requires.txt +5 -0
  83. factorforge_cds-3.5.2/src/factorforge_cds.egg-info/top_level.txt +3 -0
  84. factorforge_cds-3.5.2/src/scratch/integrity_test.py +100 -0
  85. factorforge_cds-3.5.2/src/scripts/seed_fixtures.py +107 -0
  86. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_benchmark_codon_table_metadata.py +1 -0
  87. factorforge_cds-3.5.2/tests/test_benchmark_harness.py +45 -0
  88. factorforge_cds-3.5.2/tests/test_cross_engine_evaluation.py +69 -0
  89. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_database.py +4 -0
  90. factorforge_cds-3.5.2/tests/test_db_connector.py +83 -0
  91. factorforge_cds-3.5.2/tests/test_dp_v2_1_1.py +176 -0
  92. factorforge_cds-3.5.2/tests/test_dp_v2_1_initiation.py +250 -0
  93. factorforge_cds-3.5.2/tests/test_lm_alpha_contract.py +69 -0
  94. factorforge_cds-3.5.2/tests/test_math_v2_dp_optimality.py +145 -0
  95. factorforge_cds-3.5.2/tests/test_multi_engine_governance.py +86 -0
  96. factorforge_cds-3.5.2/tests/test_optional_postgres.py +36 -0
  97. factorforge_cds-3.5.2/tests/test_partial_dp_rescue.py +198 -0
  98. factorforge_cds-3.5.2/tests/test_prepare_lm_dataset.py +50 -0
  99. factorforge_cds-3.5.2/tests/test_quantized_scoring.py +58 -0
  100. factorforge_cds-3.5.2/tests/test_rule_registry.py +112 -0
  101. factorforge_cds-3.5.2/tests/test_sllm_biological_pipeline.py +177 -0
  102. factorforge_cds-3.5.2/tests/test_slm_decoder.py +220 -0
  103. factorforge_cds-3.5.2/tests/test_slm_interface.py +80 -0
  104. factorforge_cds-3.5.2/tests/test_sop_profile.py +66 -0
  105. factorforge_cds-3.5.2/tests/test_suite_capability_compat.py +62 -0
  106. factorforge_cds-3.5.2/tests/test_version_manifest.py +21 -0
  107. factorforge_cds-3.4.5/src/factorforge/engines/__init__.py +0 -24
  108. factorforge_cds-3.4.5/src/factorforge_cds.egg-info/top_level.txt +0 -1
  109. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/LICENSE +0 -0
  110. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/setup.cfg +0 -0
  111. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/__main__.py +0 -0
  112. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/analysis/__init__.py +0 -0
  113. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/analysis/feasibility.py +0 -0
  114. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/cli/__init__.py +0 -0
  115. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/cli/legacy_cli.py +0 -0
  116. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/core/interfaces/__init__.py +0 -0
  117. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/core/interfaces/exporter.py +0 -0
  118. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/core/interfaces/optimizer.py +0 -0
  119. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/core/interfaces/validator.py +0 -0
  120. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/nbenthamiana_codons.json +0 -0
  121. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/nbenthamiana_golden_set.json +0 -0
  122. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/ntabacum_codons.json +0 -0
  123. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/profiles/nbev11_cds_all_derived_codons.json +0 -0
  124. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/profiles/nbev11_cds_all_derived_filtered_stats.json +0 -0
  125. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/profiles/nbev11_cds_all_derived_manifest.json +0 -0
  126. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/profiles/nbev11_cds_hc_derived_codons.json +0 -0
  127. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/profiles/nbev11_cds_hc_derived_filtered_stats.json +0 -0
  128. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/profiles/nbev11_cds_hc_derived_manifest.json +0 -0
  129. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/profiles/qld183_v103_derived_codons.json +0 -0
  130. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/profiles/qld183_v103_derived_manifest.json +0 -0
  131. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/profiles/qld183_v103_filtered_stats.json +0 -0
  132. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/reference/reference_policy_manifest.json +0 -0
  133. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/templates/high_expression.json +0 -0
  134. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/templates/standard_expression.json +0 -0
  135. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/data/wolffia_globosa_codons.json +0 -0
  136. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/design_review.py +0 -0
  137. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/codon_table_builder.py +0 -0
  138. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/construct_builder.py +0 -0
  139. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/exporter.py +0 -0
  140. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/pipeline.py +0 -0
  141. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/rules/__init__.py +0 -0
  142. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/rules/domesticator.py +0 -0
  143. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/rules/reverse_translator.py +0 -0
  144. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/rules/rule_engine.py +0 -0
  145. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/scoring_ml.py +0 -0
  146. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/utils.py +0 -0
  147. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/engines/profile/validator.py +0 -0
  148. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/io/__init__.py +0 -0
  149. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/io/fasta.py +0 -0
  150. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/io/validation.py +0 -0
  151. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/protein_risk/__init__.py +0 -0
  152. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/protein_risk/annotate.py +0 -0
  153. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/protein_risk/kd_scale.py +0 -0
  154. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/protein_risk/risk_classifier.py +0 -0
  155. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/protein_risk/sp_predict.py +0 -0
  156. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/protein_risk/tm_predict.py +0 -0
  157. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/registry/__init__.py +0 -0
  158. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/registry/registry_loader.py +0 -0
  159. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/review/__init__.py +0 -0
  160. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/schemas/__init__.py +0 -0
  161. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/schemas/design_package.py +0 -0
  162. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/schemas/design_package.schema.json +0 -0
  163. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/utils/__init__.py +0 -0
  164. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/utils/construct_id.py +0 -0
  165. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/utils/restriction_sites.py +0 -0
  166. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/utils/sequence_validator.py +0 -0
  167. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/utils/validation.py +0 -0
  168. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/validation/__init__.py +0 -0
  169. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/validation/cli.py +0 -0
  170. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/validation/package_generator.py +0 -0
  171. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/validation_registry.py +0 -0
  172. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge/validation_report.py +0 -0
  173. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge_cds.egg-info/dependency_links.txt +0 -0
  174. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/src/factorforge_cds.egg-info/entry_points.txt +0 -0
  175. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_baselines.py +0 -0
  176. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_benchmark_cli_output_guard.py +0 -0
  177. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_benchmark_regression.py +0 -0
  178. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_benchmark_scoring.py +0 -0
  179. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_benchmark_smoke.py +0 -0
  180. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_cai.py +0 -0
  181. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_codon_table_manifest.py +0 -0
  182. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_design_package_schema.py +0 -0
  183. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_design_package_semantics.py +0 -0
  184. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_design_package_serialization.py +0 -0
  185. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_design_review.py +0 -0
  186. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_docs_consistency.py +0 -0
  187. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_fasta_io.py +0 -0
  188. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_gc_content.py +0 -0
  189. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_host_profile_metadata.py +0 -0
  190. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_iupac_validation.py +0 -0
  191. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_legacy_cli.py +0 -0
  192. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_no_raw_sequence_logging.py +0 -0
  193. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_openbio_missing_metric_contract.py +0 -0
  194. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_parameter_registry.py +0 -0
  195. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_protein_risk.py +0 -0
  196. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_registry_production_sync.py +0 -0
  197. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_restriction_sites.py +0 -0
  198. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_sequence_validator.py +0 -0
  199. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_translation_integrity.py +0 -0
  200. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_validation_contract_compat.py +0 -0
  201. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_validation_registry.py +0 -0
  202. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_validation_report.py +0 -0
  203. {factorforge_cds-3.4.5 → factorforge_cds-3.5.2}/tests/test_worked_example.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: factorforge-cds
3
- Version: 3.4.5
3
+ Version: 3.5.2
4
4
  Summary: FactorForge - open-source CDS design and pre-synthesis sequence review by Eijex.
5
5
  Author-email: Eijex <eijex.lab@gmail.com>
6
6
  License-Expression: AGPL-3.0-only
@@ -19,6 +19,9 @@ Requires-Dist: biopython>=1.81
19
19
  Requires-Dist: requests>=2.31
20
20
  Requires-Dist: click>=8.4.1
21
21
  Requires-Dist: pydantic>=2.0
22
+ Provides-Extra: postgres
23
+ Requires-Dist: psycopg2-binary>=2.9; extra == "postgres"
24
+ Requires-Dist: sqlalchemy>=2.0; extra == "postgres"
22
25
  Provides-Extra: dev
23
26
  Requires-Dist: jsonschema>=4.0; extra == "dev"
24
27
  Requires-Dist: pandas>=2.0; extra == "dev"
@@ -29,6 +32,7 @@ Requires-Dist: pyyaml>=6.0; extra == "dev"
29
32
  Provides-Extra: ml
30
33
  Requires-Dist: transformers>=4.40; extra == "ml"
31
34
  Requires-Dist: torch>=2.0; extra == "ml"
35
+ Requires-Dist: onnxruntime>=1.16; extra == "ml"
32
36
  Dynamic: license-file
33
37
 
34
38
  # FactorForge
@@ -42,10 +46,20 @@ Dynamic: license-file
42
46
  [![codecov](https://codecov.io/gh/eijex/factorforge-cds/branch/main/graph/badge.svg)](https://codecov.io/gh/eijex/factorforge-cds)
43
47
  [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20407330.svg)](https://doi.org/10.5281/zenodo.20407330)
44
48
  [![Web App](https://img.shields.io/badge/web-factorforge.eijex.com-brightgreen.svg)](https://factorforge.eijex.com)
45
- [![Sponsor](https://img.shields.io/badge/Sponsor-EA4AAA?style=flat&logo=github-sponsors&logoColor=white)](https://github.com/sponsors/eijex)
49
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-Donate-yellow?style=flat&logo=buy-me-a-coffee)](https://www.buymeacoffee.com/eijex)
46
50
 
47
51
 
48
- FactorForge performs profile-guided CDS design with CAI/GC metrics, PolyA-signal screening, and Golden Gate/MoClo-aware checks. It is positioned as a pre-synthesis review harness: it helps teams generate reproducible CDS candidates, inspect assembly-relevant sequence constraints, and package design metadata before downstream synthesis, cloning, or experimental review. Primary support: *N. benthamiana* (agroinfiltration). Experimental host context: Tobacco BY-2 (`--host by2`).
52
+ FactorForge performs deterministic CDS design with CAI/GC metrics, PolyA-signal screening, and Golden Gate/MoClo-aware checks. It is positioned as a pre-synthesis review harness: it helps teams generate reproducible CDS candidates, inspect assembly-relevant sequence constraints, and package design metadata before downstream synthesis, cloning, or experimental review. Primary support: *N. benthamiana* (agroinfiltration). Experimental host context: Tobacco BY-2 (`--host by2`).
53
+
54
+ FactorForge v3.5.1 uses independently versioned engines:
55
+
56
+ | Generation | Engine | Version | Availability |
57
+ |---|---|---:|---|
58
+ | Gen 1 | Rule/profile | 1.0.0 | Stable, public |
59
+ | Gen 2 | DP v2 | 2.0.1 | Stable, public default feasibility path |
60
+ | Gen 2 | DP v2.1.1 | 2.1.1 | Explicit local-guard path; computational evidence only |
61
+ | Gen 3 | sLLM Hybrid | 0.2.0-preview.1 | Feature-gated constrained-generation research preview |
62
+ | Rescue | Adaptive partial DP | 1.0.0 | Exact suffix rescue conditioned on a verified prefix |
49
63
 
50
64
  **→ [Full Documentation](https://eijex.github.io/factorforge-cds/)** · **[Roadmap](https://eijex.github.io/factorforge-cds/roadmap/)**
51
65
 
@@ -75,12 +89,30 @@ Or use the **[web app](https://factorforge.eijex.com)** — no installation requ
75
89
 
76
90
  ## Repository Structure
77
91
 
78
- The supported production engine is the deterministic profile engine under:
92
+ The supported deterministic engines are the profile engine, stable DP v2, and
93
+ the explicit DP v2.1.1 development candidate under:
79
94
 
80
95
  ```text
81
96
  src/factorforge/engines/profile/
97
+ src/factorforge/engines/dp_v2.py
98
+ src/factorforge/engines/dp_v2_1_1.py
99
+ src/factorforge/engines/sllm/
100
+ src/factorforge/discovery/
82
101
  ```
83
102
 
103
+ DP v2.1.1 adds an exact active-layer 5′ GC guard and Aho-Corasick rejection of
104
+ homopolymers of 6 nt or longer to the v2.1 initiation-aware objective. It emits
105
+ local-composition metrics and, when ViennaRNA and sufficient transcript context
106
+ are available, a separately evaluated 5′ MFE value. It is not the default; its
107
+ single-target calibration does not establish holdout generalization or biological
108
+ performance.
109
+
110
+ The v3.5.1 discovery-slate surface generates versioned Top-K research candidates,
111
+ applies a shared deterministic hard-constraint filter, and records generator and
112
+ fallback lineage. The sLLM path is disabled by default. A partial-DP rescue solves
113
+ an exact suffix conditioned on the retained prefix; it is not a claim of global
114
+ optimality and does not establish biological performance.
115
+
84
116
  Historical implementation tracks are preserved under `archive/` for provenance
85
117
  and are not imported by the installed package or exposed as supported engines.
86
118
 
@@ -95,7 +127,7 @@ FactorForge outputs are **in-silico only** and have not been experimentally vali
95
127
  ## Citing
96
128
 
97
129
  ```
98
- FactorForge v3.4.5 (2026). Open-source constraint-based CDS design and sequence review.
130
+ FactorForge v3.5.2 (2026). Open-source constraint-based CDS design and sequence review.
99
131
  Eijex. https://github.com/eijex/factorforge-cds
100
132
  ```
101
133
 
@@ -105,6 +137,12 @@ Eijex. https://github.com/eijex/factorforge-cds
105
137
 
106
138
  Mun-Kyu Kim ([@eijex](https://github.com/eijex))
107
139
 
140
+ ## Optional PostgreSQL support
141
+
142
+ Install `pip install "factorforge-cds[postgres]"` only when using the PostgreSQL
143
+ connector. Explicit local SQLite checkpoints do not require a PostgreSQL driver.
144
+ Database integration tests require an explicitly configured `DATABASE_URL`.
145
+
108
146
  ## License
109
147
 
110
148
  GNU Affero General Public License v3.0 — see [LICENSE](LICENSE).
@@ -118,6 +156,12 @@ GNU Affero General Public License v3.0 — see [LICENSE](LICENSE).
118
156
  - **Sponsor** — Support our research via [GitHub Sponsors](https://github.com/sponsors/eijex) or [PayPal](https://paypal.me/munkyukim)
119
157
  - **Docs** — [eijex.github.io/factorforge-cds](https://eijex.github.io/factorforge-cds/)
120
158
  - **Wet-lab Feedback** — Public-safe feedback summaries are welcome via [Share Wet-lab Feedback (GitHub)](https://github.com/eijex/factorforge-cds/issues/new?template=wet_lab_result.yml). Do not submit raw sequences, confidential construct details, internal batch IDs, patient data, private contact information, exact process parameters, or confidential partner/customer data. Email `eijex.lab@gmail.com` for private or sensitive summaries. See [VALIDATION.md](VALIDATION.md) before submitting.
159
+ - **Sequence Policy Profiles** — FactorForge applies the sequence-review policy
160
+ chosen by the user; it does not define or approve a laboratory SOP. The web
161
+ app provides an illustrative YAML example and active-profile export.
162
+ Sequence-free, non-confidential suggestions can be submitted through the
163
+ public policy-template form; use `eijex.lab@gmail.com` for private or
164
+ laboratory-specific profiles. See the [sequence policy boundary](docs/sequence-policy.md).
121
165
  - **GitHub Issues** — bugs, features: [github.com/eijex/factorforge-cds/issues](https://github.com/eijex/factorforge-cds/issues)
122
166
  - **Email** — eijex.lab@gmail.com
123
167
  - **FactorForge** — [factorforge.eijex.com](https://factorforge.eijex.com)
@@ -9,10 +9,20 @@
9
9
  [![codecov](https://codecov.io/gh/eijex/factorforge-cds/branch/main/graph/badge.svg)](https://codecov.io/gh/eijex/factorforge-cds)
10
10
  [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20407330.svg)](https://doi.org/10.5281/zenodo.20407330)
11
11
  [![Web App](https://img.shields.io/badge/web-factorforge.eijex.com-brightgreen.svg)](https://factorforge.eijex.com)
12
- [![Sponsor](https://img.shields.io/badge/Sponsor-EA4AAA?style=flat&logo=github-sponsors&logoColor=white)](https://github.com/sponsors/eijex)
12
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-Donate-yellow?style=flat&logo=buy-me-a-coffee)](https://www.buymeacoffee.com/eijex)
13
13
 
14
14
 
15
- FactorForge performs profile-guided CDS design with CAI/GC metrics, PolyA-signal screening, and Golden Gate/MoClo-aware checks. It is positioned as a pre-synthesis review harness: it helps teams generate reproducible CDS candidates, inspect assembly-relevant sequence constraints, and package design metadata before downstream synthesis, cloning, or experimental review. Primary support: *N. benthamiana* (agroinfiltration). Experimental host context: Tobacco BY-2 (`--host by2`).
15
+ FactorForge performs deterministic CDS design with CAI/GC metrics, PolyA-signal screening, and Golden Gate/MoClo-aware checks. It is positioned as a pre-synthesis review harness: it helps teams generate reproducible CDS candidates, inspect assembly-relevant sequence constraints, and package design metadata before downstream synthesis, cloning, or experimental review. Primary support: *N. benthamiana* (agroinfiltration). Experimental host context: Tobacco BY-2 (`--host by2`).
16
+
17
+ FactorForge v3.5.1 uses independently versioned engines:
18
+
19
+ | Generation | Engine | Version | Availability |
20
+ |---|---|---:|---|
21
+ | Gen 1 | Rule/profile | 1.0.0 | Stable, public |
22
+ | Gen 2 | DP v2 | 2.0.1 | Stable, public default feasibility path |
23
+ | Gen 2 | DP v2.1.1 | 2.1.1 | Explicit local-guard path; computational evidence only |
24
+ | Gen 3 | sLLM Hybrid | 0.2.0-preview.1 | Feature-gated constrained-generation research preview |
25
+ | Rescue | Adaptive partial DP | 1.0.0 | Exact suffix rescue conditioned on a verified prefix |
16
26
 
17
27
  **→ [Full Documentation](https://eijex.github.io/factorforge-cds/)** · **[Roadmap](https://eijex.github.io/factorforge-cds/roadmap/)**
18
28
 
@@ -42,12 +52,30 @@ Or use the **[web app](https://factorforge.eijex.com)** — no installation requ
42
52
 
43
53
  ## Repository Structure
44
54
 
45
- The supported production engine is the deterministic profile engine under:
55
+ The supported deterministic engines are the profile engine, stable DP v2, and
56
+ the explicit DP v2.1.1 development candidate under:
46
57
 
47
58
  ```text
48
59
  src/factorforge/engines/profile/
60
+ src/factorforge/engines/dp_v2.py
61
+ src/factorforge/engines/dp_v2_1_1.py
62
+ src/factorforge/engines/sllm/
63
+ src/factorforge/discovery/
49
64
  ```
50
65
 
66
+ DP v2.1.1 adds an exact active-layer 5′ GC guard and Aho-Corasick rejection of
67
+ homopolymers of 6 nt or longer to the v2.1 initiation-aware objective. It emits
68
+ local-composition metrics and, when ViennaRNA and sufficient transcript context
69
+ are available, a separately evaluated 5′ MFE value. It is not the default; its
70
+ single-target calibration does not establish holdout generalization or biological
71
+ performance.
72
+
73
+ The v3.5.1 discovery-slate surface generates versioned Top-K research candidates,
74
+ applies a shared deterministic hard-constraint filter, and records generator and
75
+ fallback lineage. The sLLM path is disabled by default. A partial-DP rescue solves
76
+ an exact suffix conditioned on the retained prefix; it is not a claim of global
77
+ optimality and does not establish biological performance.
78
+
51
79
  Historical implementation tracks are preserved under `archive/` for provenance
52
80
  and are not imported by the installed package or exposed as supported engines.
53
81
 
@@ -62,7 +90,7 @@ FactorForge outputs are **in-silico only** and have not been experimentally vali
62
90
  ## Citing
63
91
 
64
92
  ```
65
- FactorForge v3.4.5 (2026). Open-source constraint-based CDS design and sequence review.
93
+ FactorForge v3.5.2 (2026). Open-source constraint-based CDS design and sequence review.
66
94
  Eijex. https://github.com/eijex/factorforge-cds
67
95
  ```
68
96
 
@@ -72,6 +100,12 @@ Eijex. https://github.com/eijex/factorforge-cds
72
100
 
73
101
  Mun-Kyu Kim ([@eijex](https://github.com/eijex))
74
102
 
103
+ ## Optional PostgreSQL support
104
+
105
+ Install `pip install "factorforge-cds[postgres]"` only when using the PostgreSQL
106
+ connector. Explicit local SQLite checkpoints do not require a PostgreSQL driver.
107
+ Database integration tests require an explicitly configured `DATABASE_URL`.
108
+
75
109
  ## License
76
110
 
77
111
  GNU Affero General Public License v3.0 — see [LICENSE](LICENSE).
@@ -85,6 +119,12 @@ GNU Affero General Public License v3.0 — see [LICENSE](LICENSE).
85
119
  - **Sponsor** — Support our research via [GitHub Sponsors](https://github.com/sponsors/eijex) or [PayPal](https://paypal.me/munkyukim)
86
120
  - **Docs** — [eijex.github.io/factorforge-cds](https://eijex.github.io/factorforge-cds/)
87
121
  - **Wet-lab Feedback** — Public-safe feedback summaries are welcome via [Share Wet-lab Feedback (GitHub)](https://github.com/eijex/factorforge-cds/issues/new?template=wet_lab_result.yml). Do not submit raw sequences, confidential construct details, internal batch IDs, patient data, private contact information, exact process parameters, or confidential partner/customer data. Email `eijex.lab@gmail.com` for private or sensitive summaries. See [VALIDATION.md](VALIDATION.md) before submitting.
122
+ - **Sequence Policy Profiles** — FactorForge applies the sequence-review policy
123
+ chosen by the user; it does not define or approve a laboratory SOP. The web
124
+ app provides an illustrative YAML example and active-profile export.
125
+ Sequence-free, non-confidential suggestions can be submitted through the
126
+ public policy-template form; use `eijex.lab@gmail.com` for private or
127
+ laboratory-specific profiles. See the [sequence policy boundary](docs/sequence-policy.md).
88
128
  - **GitHub Issues** — bugs, features: [github.com/eijex/factorforge-cds/issues](https://github.com/eijex/factorforge-cds/issues)
89
129
  - **Email** — eijex.lab@gmail.com
90
130
  - **FactorForge** — [factorforge.eijex.com](https://factorforge.eijex.com)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "factorforge-cds"
7
- version = "3.4.5"
7
+ version = "3.5.2"
8
8
  description = "FactorForge - open-source CDS design and pre-synthesis sequence review by Eijex."
9
9
  readme = "README.md"
10
10
  license = "AGPL-3.0-only"
@@ -27,6 +27,10 @@ dependencies = [
27
27
  ]
28
28
 
29
29
  [project.optional-dependencies]
30
+ postgres = [
31
+ "psycopg2-binary>=2.9",
32
+ "sqlalchemy>=2.0",
33
+ ]
30
34
  dev = [
31
35
  "jsonschema>=4.0",
32
36
  "pandas>=2.0",
@@ -38,6 +42,7 @@ dev = [
38
42
  ml = [
39
43
  "transformers>=4.40",
40
44
  "torch>=2.0",
45
+ "onnxruntime>=1.16",
41
46
  ]
42
47
 
43
48
  [project.urls]
@@ -53,7 +58,7 @@ factorforge-validate = "factorforge.validation.cli:main"
53
58
  where = ["src"]
54
59
 
55
60
  [tool.setuptools.package-data]
56
- "factorforge" = ["data/*.json", "data/templates/*.json", "data/profiles/*.json", "data/reference/*.json", "schemas/*.json"]
61
+ "factorforge" = ["data/*.json", "data/templates/*.json", "data/profiles/*.json", "data/reference/*.json", "schemas/*.json", "registry/*.json"]
57
62
 
58
63
  [tool.pytest.ini_options]
59
64
  testpaths = ["tests"]
@@ -4,7 +4,9 @@ FactorForge - Codon Optimization Platform
4
4
  profile: constraint-aware rule/profile engine
5
5
  """
6
6
 
7
- __version__ = "3.4.5"
7
+ from factorforge.registry.versioning import product_version
8
+
9
+ __version__ = product_version()
8
10
  __author__ = "Eijex"
9
11
 
10
12
  # Auto-register engines (safe when running from source tree)
@@ -434,3 +434,72 @@ def detect_invalid_codons(sequence: str) -> list[dict[str, Any]]:
434
434
  elif codon not in STANDARD_GENETIC_CODE:
435
435
  findings.append({"start": start, "end": start + 3, "codon": codon, "reason": "unknown"})
436
436
  return findings
437
+ import math
438
+ import collections
439
+
440
+ def calculate_synonymous_entropy(sequence, codon_to_aa):
441
+ from factorforge.analysis.metrics import _codons
442
+ codons = _codons(sequence)
443
+ aa_to_codons = collections.defaultdict(list)
444
+ for c in codons:
445
+ aa = codon_to_aa.get(c, 'X')
446
+ aa_to_codons[aa].append(c)
447
+
448
+ entropies = {}
449
+ for aa, used_codons in aa_to_codons.items():
450
+ if aa in ['M', 'W', 'X']:
451
+ entropies[aa] = 0.0
452
+ continue
453
+ total = len(used_codons)
454
+ counts = collections.Counter(used_codons)
455
+ h = 0.0
456
+ for c, count in counts.items():
457
+ p = count / total
458
+ h -= p * math.log2(p)
459
+
460
+ # Max entropy for this AA
461
+ possible_codons = [k for k, v in codon_to_aa.items() if v == aa]
462
+ k = len(possible_codons)
463
+ h_max = math.log2(k) if k > 1 else 1.0
464
+ entropies[aa] = h / h_max if h_max > 0 else 0.0
465
+
466
+ if not entropies:
467
+ return 0.0
468
+ return sum(entropies.values()) / len(entropies)
469
+
470
+ def calculate_max_synonymous_share(sequence, codon_to_aa):
471
+ from factorforge.analysis.metrics import _codons
472
+ codons = _codons(sequence)
473
+ aa_to_codons = collections.defaultdict(list)
474
+ for c in codons:
475
+ aa = codon_to_aa.get(c, 'X')
476
+ aa_to_codons[aa].append(c)
477
+
478
+ max_shares = {}
479
+ for aa, used_codons in aa_to_codons.items():
480
+ if aa in ['M', 'W', 'X']:
481
+ continue
482
+ total = len(used_codons)
483
+ counts = collections.Counter(used_codons)
484
+ if total == 0:
485
+ continue
486
+ max_share = max(counts.values()) / total
487
+ max_shares[aa] = max_share
488
+ return max_shares
489
+
490
+ def calculate_reference_distance(sequence, reference_weights, codon_to_aa):
491
+ from factorforge.analysis.metrics import _codons
492
+ codons = _codons(sequence)
493
+ counts = collections.Counter(codons)
494
+
495
+ distance = 0.0
496
+ for codon, ref_freq in reference_weights.items():
497
+ aa = codon_to_aa.get(codon, 'X')
498
+ # find sequence frequency for this codon among its synonymous peers
499
+ syn_codons = [c for c, a in codon_to_aa.items() if a == aa]
500
+ total_aa = sum(counts.get(c, 0) for c in syn_codons)
501
+
502
+ seq_freq = counts.get(codon, 0) / total_aa if total_aa > 0 else 0.0
503
+ # simple absolute distance
504
+ distance += abs(seq_freq - ref_freq)
505
+ return distance
@@ -0,0 +1,97 @@
1
+ """Sequence risk scanners (PolyA, Repeats, Homopolymers, etc.)
2
+
3
+ Extracted from legacy RuleEngine to serve as shared evaluation metrics.
4
+ """
5
+ import re
6
+ from typing import Dict, Any, List, Set, Tuple
7
+
8
+ # Polyadenylation signals (true biological motifs)
9
+ POLYA_PATTERNS = {
10
+ # Tier 1 (canonical, highest efficiency in plants/eukaryotes)
11
+ "AATAAA": "canonical",
12
+ "ATTAAA": "variant_1",
13
+ "AGTAAA": "variant_2",
14
+ # Tier 2 (lower-frequency but functional in plants)
15
+ "AATACA": "tier_2",
16
+ "AAGAAA": "tier_2",
17
+ "AATGAA": "tier_2",
18
+ }
19
+
20
+ UNSTABLE_MOTIFS = {
21
+ "ATTTA": "ARE (AU-rich element)",
22
+ "WWWWWW": "W=A/T, 6+ in a row"
23
+ }
24
+
25
+ def scan_polya(seq: str) -> List[Dict[str, Any]]:
26
+ """Detect biological PolyA signal motifs (e.g. AATAAA)."""
27
+ seq_upper = seq.upper()
28
+ violations: List[Dict[str, Any]] = []
29
+
30
+ for pattern, pattern_type in POLYA_PATTERNS.items():
31
+ pos = 0
32
+ while True:
33
+ idx = seq_upper.find(pattern, pos)
34
+ if idx == -1:
35
+ break
36
+ violations.append({
37
+ "type": "polya_signal",
38
+ "pattern": pattern,
39
+ "pattern_type": pattern_type,
40
+ "position": idx,
41
+ "context": seq_upper[max(0, idx - 10):min(len(seq_upper), idx + len(pattern) + 10)]
42
+ })
43
+ pos = idx + 1
44
+
45
+ return violations
46
+
47
+ def scan_homopolymers(seq: str, threshold: int = 6) -> List[Dict[str, Any]]:
48
+ """Find homopolymers (runs of any single nucleotide)."""
49
+ seq_upper = seq.upper()
50
+ violations: List[Dict[str, Any]] = []
51
+ for nt in "ATGC":
52
+ pattern = nt * threshold
53
+ pos = 0
54
+ while True:
55
+ idx = seq_upper.find(pattern, pos)
56
+ if idx == -1:
57
+ break
58
+ match = re.match(f"{nt}+", seq_upper[idx:])
59
+ length = match.end() if match else threshold
60
+ violations.append({
61
+ "type": "homopolymer",
62
+ "base": nt,
63
+ "length": length,
64
+ "position": idx,
65
+ "context": seq_upper[max(0, idx - 10):min(len(seq_upper), idx + length + 10)]
66
+ })
67
+ pos = idx + length
68
+ return violations
69
+
70
+ def scan_repeats(seq: str, min_length: int = 20, max_distance: int = 1000) -> List[Dict[str, Any]]:
71
+ """Detect direct or inverted repeats causing assembly/stability issues.
72
+
73
+ Direct repeats (exact k-mer matches) >= 20 bp are known to cause issues with
74
+ DNA synthesis (e.g. Twist/IDT) and homologous recombination in host organisms.
75
+ """
76
+ seq_upper = seq.upper()
77
+ violations: List[Dict[str, Any]] = []
78
+
79
+ # Naive k-mer search for direct repeats
80
+ # For a real implementation, a suffix tree or kmer hash map is better.
81
+ # We will use a fast localized search.
82
+ seen_kmers = {}
83
+ for i in range(len(seq_upper) - min_length + 1):
84
+ kmer = seq_upper[i:i + min_length]
85
+ if kmer in seen_kmers:
86
+ dist = i - seen_kmers[kmer]
87
+ if dist <= max_distance:
88
+ violations.append({
89
+ "type": "direct_repeat",
90
+ "pattern": kmer,
91
+ "position1": seen_kmers[kmer],
92
+ "position2": i,
93
+ "distance": dist
94
+ })
95
+ seen_kmers[kmer] = i
96
+
97
+ return violations
@@ -0,0 +1,62 @@
1
+ from datetime import datetime
2
+ from typing import Dict, Any, List, Optional
3
+ from pydantic import BaseModel, Field
4
+
5
+ from factorforge.evaluation.models import EvaluationResult
6
+
7
+
8
+ class BenchmarkProvenance(BaseModel):
9
+ """Immutable provenance tracking for benchmark runs."""
10
+ input_sequence_hash: str
11
+ input_type: str = Field(description="protein or CDS")
12
+ terminal_stop_policy: str
13
+ codon_reference_id: str
14
+ constraint_profile_id: str
15
+ evaluator_version: str
16
+ method_version: str
17
+ git_commit: str
18
+ seed: Optional[int] = None
19
+ inference_mode: str
20
+ runtime_environment: str
21
+ timestamp: str = Field(default_factory=lambda: datetime.utcnow().isoformat())
22
+
23
+
24
+ class BenchmarkTarget(BaseModel):
25
+ """A target sequence for benchmarking."""
26
+ target_id: str
27
+ protein_sequence: str
28
+ metadata: Dict[str, Any] = Field(default_factory=dict)
29
+
30
+
31
+ class BenchmarkRunConfig(BaseModel):
32
+ """Configuration for a benchmark execution."""
33
+ suite_name: str = Field(..., description="e.g., Engineering Benchmark Suite")
34
+ host: str = "nbenthamiana"
35
+ target_gc_min_percent: float
36
+ target_gc_max_percent: float
37
+ forbidden_type_iis: List[str] = Field(default_factory=list)
38
+ terminal_stop_policy: str = "append"
39
+ seed: int = 42
40
+
41
+
42
+ class EngineBenchmarkResult(BaseModel):
43
+ """Benchmark result for a single engine on a single target."""
44
+ engine_name: str
45
+ target_id: str
46
+ provenance: BenchmarkProvenance
47
+ optimized_sequence: str
48
+ evaluation: EvaluationResult
49
+ runtime_seconds: float
50
+
51
+
52
+ class BenchmarkSuiteReport(BaseModel):
53
+ """Aggregated benchmark report across all targets and engines."""
54
+ suite_name: str
55
+ run_timestamp: str = Field(default_factory=lambda: datetime.utcnow().isoformat())
56
+ config: BenchmarkRunConfig
57
+ results: List[EngineBenchmarkResult]
58
+
59
+ # Summary statistics mapped by engine
60
+ pass_counts: Dict[str, str] = Field(description="Raw pass counts e.g., '3/4'")
61
+ average_cai: Dict[str, Optional[float]]
62
+ average_gc: Dict[str, float]
@@ -0,0 +1,144 @@
1
+ from typing import List, Dict, Any, Type
2
+ import time
3
+ import hashlib
4
+
5
+ from factorforge.engines import EngineRegistry, register_builtin_engines
6
+ from factorforge.evaluation.evaluator import SharedEvaluator
7
+ from factorforge.core.interfaces import OptimizerEngine
8
+ from factorforge.benchmark.models import (
9
+ BenchmarkTarget,
10
+ BenchmarkRunConfig,
11
+ EngineBenchmarkResult,
12
+ BenchmarkSuiteReport,
13
+ BenchmarkProvenance
14
+ )
15
+ from factorforge.analysis.metrics import load_codon_usage_table
16
+
17
+ # Initialize engines
18
+ register_builtin_engines()
19
+
20
+ class BenchmarkRunner:
21
+ """AgentOS Benchmark Harness for evaluating sequence generation methods."""
22
+
23
+ def __init__(self, config: BenchmarkRunConfig):
24
+ self.config = config
25
+
26
+ # Load canonical host table to enable CAI in the SharedEvaluator
27
+ # Default internal path loads N. benthamiana by default, which is our benchmark host.
28
+ table = load_codon_usage_table()
29
+
30
+ self.evaluator = SharedEvaluator(
31
+ version="1.0.1",
32
+ codon_weights=table.codon_weights
33
+ )
34
+
35
+ def run_suite(self, targets: List[BenchmarkTarget], engine_names: List[str]) -> BenchmarkSuiteReport:
36
+ """Execute the benchmark suite across requested engines and targets."""
37
+
38
+ engines: Dict[str, OptimizerEngine] = {}
39
+ for name in engine_names:
40
+ engine_instance = EngineRegistry.get(name)
41
+ if not engine_instance:
42
+ raise ValueError(f"Engine '{name}' not found in registry.")
43
+ engines[name] = engine_instance
44
+
45
+ results: List[EngineBenchmarkResult] = []
46
+
47
+ for target in targets:
48
+ input_hash = hashlib.sha256(target.protein_sequence.encode()).hexdigest()
49
+
50
+ for engine_name, engine in engines.items():
51
+
52
+ mode = getattr(engine, "inference_mode", "standard")
53
+
54
+ provenance = BenchmarkProvenance(
55
+ input_sequence_hash=input_hash,
56
+ input_type="protein",
57
+ terminal_stop_policy=self.config.terminal_stop_policy,
58
+ codon_reference_id=f"host_{self.config.host}",
59
+ constraint_profile_id="default_gc",
60
+ evaluator_version=self.evaluator.version,
61
+ method_version=engine.version,
62
+ git_commit="current", # In real usage, this would be git rev-parse HEAD
63
+ seed=self.config.seed,
64
+ inference_mode=mode,
65
+ runtime_environment="default"
66
+ )
67
+
68
+ start_time = time.time()
69
+
70
+ # Execute generation
71
+ opt_result = engine.optimize(
72
+ sequence=target.protein_sequence,
73
+ host=self.config.host,
74
+ target_gc_min=self.config.target_gc_min_percent / 100.0, # Some engines still expect fraction
75
+ target_gc_max=self.config.target_gc_max_percent / 100.0,
76
+ forbidden_type_iis=set(self.config.forbidden_type_iis),
77
+ terminal_stop_policy=self.config.terminal_stop_policy,
78
+ seed=self.config.seed
79
+ )
80
+
81
+ runtime = time.time() - start_time
82
+
83
+ # ONE SharedEvaluator for all generated candidates
84
+ eval_result_obj = self.evaluator.evaluate_candidate(
85
+ candidate_dna=opt_result.sequence,
86
+ expected_protein=target.protein_sequence,
87
+ candidate_id=f"{engine_name}-{target.target_id}",
88
+ target_gc_min_percent=self.config.target_gc_min_percent,
89
+ target_gc_max_percent=self.config.target_gc_max_percent,
90
+ forbidden_type_iis=set(self.config.forbidden_type_iis)
91
+ )
92
+
93
+ res = EngineBenchmarkResult(
94
+ engine_name=engine_name,
95
+ target_id=target.target_id,
96
+ provenance=provenance,
97
+ optimized_sequence=opt_result.sequence,
98
+ evaluation=eval_result_obj,
99
+ runtime_seconds=runtime
100
+ )
101
+ results.append(res)
102
+
103
+ return self._aggregate_results(results)
104
+
105
+ def _aggregate_results(self, results: List[EngineBenchmarkResult]) -> BenchmarkSuiteReport:
106
+ """Aggregate results into a full BenchmarkSuiteReport."""
107
+
108
+ pass_counts = {}
109
+ total_targets = len(set(r.target_id for r in results))
110
+
111
+ cai_sums = {}
112
+ cai_vals = {}
113
+ gc_sums = {}
114
+ engine_counts = {}
115
+
116
+ for r in results:
117
+ eng = r.engine_name
118
+ if eng not in pass_counts:
119
+ pass_counts[eng] = 0
120
+ cai_vals[eng] = []
121
+ gc_sums[eng] = 0.0
122
+ engine_counts[eng] = 0
123
+
124
+ if r.evaluation.passed:
125
+ pass_counts[eng] += 1
126
+
127
+ if r.evaluation.metrics.cai is not None:
128
+ cai_vals[eng].append(r.evaluation.metrics.cai)
129
+
130
+ gc_sums[eng] += r.evaluation.metrics.gc_percent
131
+ engine_counts[eng] += 1
132
+
133
+ pass_counts_str = {e: f"{count}/{total_targets}" for e, count in pass_counts.items()}
134
+ avg_cai = {e: (sum(cai_vals[e]) / len(cai_vals[e]) if cai_vals[e] else None) for e in pass_counts}
135
+ avg_gc = {e: (gc_sums[e] / engine_counts[e]) for e in pass_counts}
136
+
137
+ return BenchmarkSuiteReport(
138
+ suite_name=self.config.suite_name,
139
+ config=self.config,
140
+ results=results,
141
+ pass_counts=pass_counts_str,
142
+ average_cai=avg_cai,
143
+ average_gc=avg_gc
144
+ )