openprotein-python 0.14.1__tar.gz → 0.15.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.
Files changed (214) hide show
  1. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/PKG-INFO +2 -1
  2. openprotein_python-0.15.0/openprotein/auth_store.py +79 -0
  3. openprotein_python-0.15.0/openprotein/base.py +372 -0
  4. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/clustering/api.py +2 -10
  5. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/clustering/clustering.py +0 -3
  6. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/api.py +8 -40
  7. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/poet2.py +25 -4
  8. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/api.py +6 -5
  9. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/prompt/api.py +6 -1
  10. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/svd/api.py +3 -10
  11. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/umap/api.py +3 -10
  12. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/pyproject.toml +1 -0
  13. openprotein_python-0.14.1/openprotein/base.py +0 -181
  14. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/.gitignore +0 -0
  15. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/LICENSE.txt +0 -0
  16. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/README.md +0 -0
  17. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/__init__.py +0 -0
  18. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/_version.py +0 -0
  19. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/align/__init__.py +0 -0
  20. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/align/align.py +0 -0
  21. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/align/api.py +0 -0
  22. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/align/future.py +0 -0
  23. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/align/msa.py +0 -0
  24. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/align/schemas.py +0 -0
  25. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/__init__.py +0 -0
  26. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/align.py +0 -0
  27. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/assaydata.py +0 -0
  28. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/deprecated/__init__.py +0 -0
  29. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/deprecated/design.py +0 -0
  30. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/deprecated/poet.py +0 -0
  31. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/deprecated/predict.py +0 -0
  32. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/deprecated/train.py +0 -0
  33. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/design.py +0 -0
  34. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/designer.py +0 -0
  35. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/embedding.py +0 -0
  36. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/error.py +0 -0
  37. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/fold.py +0 -0
  38. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/job.py +0 -0
  39. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/predict.py +0 -0
  40. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/predictor.py +0 -0
  41. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/prompt.py +0 -0
  42. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/svd.py +0 -0
  43. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/train.py +0 -0
  44. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/api/umap.py +0 -0
  45. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/__init__.py +0 -0
  46. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/deprecated.py +0 -0
  47. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/__init__.py +0 -0
  48. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/align/__init__.py +0 -0
  49. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/align/base.py +0 -0
  50. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/align/msa.py +0 -0
  51. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/align/prompt.py +0 -0
  52. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/assaydata.py +0 -0
  53. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/deprecated/__init__.py +0 -0
  54. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/deprecated/design.py +0 -0
  55. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/deprecated/poet.py +0 -0
  56. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/deprecated/predict.py +0 -0
  57. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/deprecated/train.py +0 -0
  58. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/design.py +0 -0
  59. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/designer.py +0 -0
  60. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/embeddings/__init__.py +0 -0
  61. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/embeddings/base.py +0 -0
  62. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/embeddings/esm.py +0 -0
  63. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/embeddings/future.py +0 -0
  64. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/embeddings/openprotein.py +0 -0
  65. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/embeddings/poet.py +0 -0
  66. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/embeddings/poet2.py +0 -0
  67. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/embeddings/test.py +0 -0
  68. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/fold/__init__.py +0 -0
  69. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/fold/alphafold2.py +0 -0
  70. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/fold/base.py +0 -0
  71. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/fold/boltz.py +0 -0
  72. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/fold/esmfold.py +0 -0
  73. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/fold/future.py +0 -0
  74. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/futures.py +0 -0
  75. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/predict.py +0 -0
  76. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/predictor/__init__.py +0 -0
  77. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/predictor/predict.py +0 -0
  78. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/predictor/predictor.py +0 -0
  79. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/predictor/validate.py +0 -0
  80. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/prompt.py +0 -0
  81. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/svd.py +0 -0
  82. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/train.py +0 -0
  83. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/models/umap.py +0 -0
  84. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/__init__.py +0 -0
  85. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/align.py +0 -0
  86. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/assaydata.py +0 -0
  87. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/deprecated/__init__.py +0 -0
  88. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/deprecated/design.py +0 -0
  89. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/deprecated/predict.py +0 -0
  90. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/deprecated/train.py +0 -0
  91. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/design.py +0 -0
  92. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/designer.py +0 -0
  93. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/embeddings.py +0 -0
  94. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/fold.py +0 -0
  95. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/job.py +0 -0
  96. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/predict.py +0 -0
  97. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/predictor.py +0 -0
  98. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/prompt.py +0 -0
  99. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/svd.py +0 -0
  100. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/train.py +0 -0
  101. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/app/services/umap.py +0 -0
  102. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/chains.py +0 -0
  103. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/clustering/__init__.py +0 -0
  104. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/clustering/models.py +0 -0
  105. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/clustering/schemas.py +0 -0
  106. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/common/__init__.py +0 -0
  107. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/common/features.py +0 -0
  108. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/common/model_metadata.py +0 -0
  109. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/common/reduction.py +0 -0
  110. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/common/residue_contants.py +0 -0
  111. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/config.py +0 -0
  112. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/csv.py +0 -0
  113. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/data/__init__.py +0 -0
  114. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/data/api.py +0 -0
  115. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/data/assaydataset.py +0 -0
  116. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/data/data.py +0 -0
  117. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/data/schemas.py +0 -0
  118. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/design/__init__.py +0 -0
  119. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/design/api.py +0 -0
  120. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/design/design.py +0 -0
  121. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/design/future.py +0 -0
  122. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/design/schemas.py +0 -0
  123. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/__init__.py +0 -0
  124. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/ablang.py +0 -0
  125. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/embeddings.py +0 -0
  126. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/esm.py +0 -0
  127. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/future.py +0 -0
  128. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/models.py +0 -0
  129. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/openprotein.py +0 -0
  130. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/poet.py +0 -0
  131. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/embeddings/schemas.py +0 -0
  132. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/errors.py +0 -0
  133. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fasta.py +0 -0
  134. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/__init__.py +0 -0
  135. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/alphafold2.py +0 -0
  136. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/boltz.py +0 -0
  137. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/common.py +0 -0
  138. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/complex.py +0 -0
  139. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/esmfold.py +0 -0
  140. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/esmfold2.py +0 -0
  141. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/fold.py +0 -0
  142. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/future.py +0 -0
  143. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/minifold.py +0 -0
  144. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/models.py +0 -0
  145. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/protenix.py +0 -0
  146. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/rosettafold3.py +0 -0
  147. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/fold/schemas.py +0 -0
  148. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/jobs/__init__.py +0 -0
  149. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/jobs/api.py +0 -0
  150. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/jobs/futures.py +0 -0
  151. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/jobs/jobs.py +0 -0
  152. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/jobs/schemas.py +0 -0
  153. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/models/__init__.py +0 -0
  154. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/models/base.py +0 -0
  155. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/models/foundation/boltzgen.py +0 -0
  156. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/models/foundation/boltzgen_schema.py +0 -0
  157. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/models/foundation/esmif1.py +0 -0
  158. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/models/foundation/proteinmpnn.py +0 -0
  159. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/models/foundation/rfdiffusion.py +0 -0
  160. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/models/models.py +0 -0
  161. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/models/structure_generation.py +0 -0
  162. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/molecules/__init__.py +0 -0
  163. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/molecules/chains.py +0 -0
  164. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/molecules/complex.py +0 -0
  165. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/molecules/protein.py +0 -0
  166. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/molecules/structure.py +0 -0
  167. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/molecules/template.py +0 -0
  168. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/predictor/__init__.py +0 -0
  169. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/predictor/api.py +0 -0
  170. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/predictor/models.py +0 -0
  171. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/predictor/prediction.py +0 -0
  172. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/predictor/predictor.py +0 -0
  173. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/predictor/schemas.py +0 -0
  174. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/predictor/validate.py +0 -0
  175. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/prompt/__init__.py +0 -0
  176. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/prompt/models.py +0 -0
  177. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/prompt/prompt.py +0 -0
  178. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/prompt/schemas.py +0 -0
  179. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/protein.py +0 -0
  180. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/scaffolds.py +0 -0
  181. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/__init__.py +0 -0
  182. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/align.py +0 -0
  183. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/assaydata.py +0 -0
  184. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/deprecated/__init__.py +0 -0
  185. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/deprecated/design.py +0 -0
  186. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/deprecated/poet.py +0 -0
  187. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/deprecated/predict.py +0 -0
  188. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/deprecated/train.py +0 -0
  189. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/design.py +0 -0
  190. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/designer.py +0 -0
  191. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/embeddings.py +0 -0
  192. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/features.py +0 -0
  193. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/fold.py +0 -0
  194. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/job.py +0 -0
  195. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/predict.py +0 -0
  196. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/predictor.py +0 -0
  197. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/prompt.py +0 -0
  198. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/svd.py +0 -0
  199. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/train.py +0 -0
  200. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/schemas/umap.py +0 -0
  201. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/svd/__init__.py +0 -0
  202. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/svd/models.py +0 -0
  203. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/svd/schemas.py +0 -0
  204. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/svd/svd.py +0 -0
  205. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/umap/__init__.py +0 -0
  206. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/umap/models.py +0 -0
  207. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/umap/schemas.py +0 -0
  208. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/umap/umap.py +0 -0
  209. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/utils/__init__.py +0 -0
  210. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/utils/chain_id.py +0 -0
  211. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/utils/cif.py +0 -0
  212. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/utils/numpy.py +0 -0
  213. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/utils/sequence.py +0 -0
  214. {openprotein_python-0.14.1 → openprotein_python-0.15.0}/openprotein/utils/uuid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openprotein-python
3
- Version: 0.14.1
3
+ Version: 0.15.0
4
4
  Summary: OpenProtein Python interface.
5
5
  Author-email: Mark Gee <markgee@ne47.bio>, "Timothy Truong Jr." <ttruong@ne47.bio>, Tristan Bepler <tbepler@ne47.bio>
6
6
  License-Expression: MIT
@@ -8,6 +8,7 @@ License-File: LICENSE.txt
8
8
  Classifier: Development Status :: 4 - Beta
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Requires-Python: <3.15,>=3.10
11
+ Requires-Dist: filelock<4,>=3.13
11
12
  Requires-Dist: gemmi<0.8,>=0.6.0
12
13
  Requires-Dist: numpy<3,>=1.9
13
14
  Requires-Dist: pandas<3,>=2.2.2
@@ -0,0 +1,79 @@
1
+ import json
2
+ import os
3
+ import tempfile
4
+ from pathlib import Path
5
+
6
+ from filelock import FileLock
7
+ from pydantic import BaseModel, ValidationError
8
+
9
+
10
+ class TokenRecord(BaseModel):
11
+ """A persisted auth record for one identity."""
12
+
13
+ access_token: str
14
+ refresh_token: str | None = None
15
+ expires_at: float = 0.0
16
+
17
+
18
+ class TokenStore:
19
+ """Persist the rotating refresh token across processes.
20
+
21
+ The on-disk file is a JSON object keyed by identity
22
+ ``"{backend}|{username}"`` -> ``TokenRecord``. The read-modify-write
23
+ critical section is guarded by a cross-process ``filelock`` so two
24
+ processes never refresh the same rotating token concurrently.
25
+ """
26
+
27
+ def __init__(self, dir: Path | None = None):
28
+ self.dir = Path(dir) if dir is not None else Path.home() / ".openprotein"
29
+ self.dir.mkdir(parents=True, exist_ok=True)
30
+ self.path = self.dir / "token.json"
31
+ # One FileLock instance per store -> reentrant within this process.
32
+ self._lock = FileLock(str(self.dir / "token.lock"))
33
+
34
+ @staticmethod
35
+ def identity(backend: str, username: str) -> str:
36
+ return f"{backend}|{username}"
37
+
38
+ def lock(self):
39
+ """Context manager for the cross-process critical section."""
40
+ return self._lock
41
+
42
+ def _read_all(self) -> dict:
43
+ # Caller must hold self._lock: this is the read half of the
44
+ # read-modify-write critical section in load()/save().
45
+ try:
46
+ return json.loads(self.path.read_text())
47
+ except (OSError, json.JSONDecodeError):
48
+ # Missing or corrupt file -> behave as if there were no records.
49
+ return {}
50
+
51
+ def load(self, identity: str) -> TokenRecord | None:
52
+ with self._lock:
53
+ data = self._read_all()
54
+ entry = data.get(identity)
55
+ if entry is None:
56
+ return None
57
+ try:
58
+ return TokenRecord.model_validate(entry)
59
+ except ValidationError:
60
+ return None
61
+
62
+ def save(self, identity: str, record: TokenRecord) -> None:
63
+ with self._lock:
64
+ data = self._read_all()
65
+ data[identity] = record.model_dump()
66
+ self._atomic_write(data)
67
+
68
+ def _atomic_write(self, data: dict) -> None:
69
+ fd, tmp = tempfile.mkstemp(dir=str(self.dir), prefix="token.", suffix=".tmp")
70
+ try:
71
+ with os.fdopen(fd, "w") as f:
72
+ json.dump(data, f)
73
+ os.chmod(tmp, 0o600)
74
+ os.replace(tmp, self.path)
75
+ except BaseException:
76
+ # Don't leave a temp file behind on failure.
77
+ if os.path.exists(tmp):
78
+ os.unlink(tmp)
79
+ raise
@@ -0,0 +1,372 @@
1
+ import sys
2
+ import threading
3
+ import time
4
+ import warnings
5
+ from typing import Mapping, Sequence
6
+ from urllib.parse import urljoin
7
+
8
+ import requests
9
+ import requests.auth
10
+ from requests.adapters import HTTPAdapter
11
+ from requests.packages.urllib3.util.retry import Retry # type: ignore
12
+
13
+ from openprotein._version import __version__
14
+ from openprotein.auth_store import TokenRecord, TokenStore
15
+ from openprotein.errors import AuthError, HTTPError
16
+
17
+ # Refresh the access token this many seconds before it actually expires,
18
+ # to absorb clock skew and request latency. 30s is a conservative buffer for
19
+ # typical clock drift plus a round-trip against a ~15-minute access token.
20
+ SKEW_SECONDS = 30
21
+
22
+
23
+ class BearerAuth(requests.auth.AuthBase):
24
+ """
25
+ See https://stackoverflow.com/a/58055668
26
+ """
27
+
28
+ def __init__(self, username, token):
29
+ self._username = username
30
+ self._token = token
31
+
32
+ @property
33
+ def token(self):
34
+ warnings.warn(
35
+ "DeprecationWarning: Accessing session.auth.token is deprecated and will throw an error in the future. Use session.auth._token if you need it."
36
+ )
37
+ return self._token
38
+
39
+ def __call__(self, r):
40
+ r.headers["Authorization"] = "Bearer " + self._token
41
+ return r
42
+
43
+
44
+ class APISession(requests.Session):
45
+ """
46
+ A class to handle API sessions. This class provides a connection session to the OpenProtein API.
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ username: str,
52
+ password: str,
53
+ backend: str,
54
+ timeout: int = 180,
55
+ ):
56
+ if not username or not password:
57
+ raise AuthError(
58
+ "Expected username and password. Or use environment variables `OPENPROTEIN_USERNAME` and `OPENPROTEIN_PASSWORD`. Or provide these variables (`username` and `password`) in ~/.openprotein/config.toml."
59
+ )
60
+ super().__init__()
61
+ self.backend = backend
62
+ self.verify = True
63
+ self.timeout = timeout
64
+
65
+ # Custom retry strategies
66
+ # auto retry for pesky connection reset errors and others
67
+ # 503 will catch if BE is refreshing
68
+ retry = Retry(
69
+ total=4,
70
+ backoff_factor=3, # 0,1,4,13s
71
+ status_forcelist=[500, 502, 503, 504, 101, 104],
72
+ )
73
+ adapter = HTTPAdapter(max_retries=retry)
74
+ self.mount("https://", adapter)
75
+ self.headers["User-Agent"] = f"openprotein-python/{__version__}"
76
+
77
+ # Auth / refresh state. Initialised before any network call so that
78
+ # request() can read _refreshing / _legacy_mode safely.
79
+ self._username = username
80
+ self._password = password
81
+ self._token_store = TokenStore()
82
+ self._identity = self._token_store.identity(backend, username)
83
+ # _refresh_lock serializes concurrent refreshes across threads.
84
+ # _refreshing (see the _refreshing property) is a thread-local recursion
85
+ # guard: the refresh POST flows back through request(), so the refreshing
86
+ # thread must skip the refresh logic, but OTHER threads sharing this
87
+ # session (e.g. via stream_parallel) must not be suppressed by it.
88
+ self._refresh_lock = threading.Lock()
89
+ self._tl = threading.local()
90
+ self._refresh_token: str | None = None
91
+ self._expires_at: float = 0.0
92
+ self._legacy_mode: bool = False
93
+
94
+ self._init_auth()
95
+
96
+ @property
97
+ def _refreshing(self) -> bool:
98
+ return getattr(self._tl, "refreshing", False)
99
+
100
+ @_refreshing.setter
101
+ def _refreshing(self, value: bool) -> None:
102
+ self._tl.refreshing = value
103
+
104
+ def _init_auth(self):
105
+ """Adopt a valid stored record if present, otherwise log in fresh."""
106
+ record = self._token_store.load(self._identity)
107
+ if record is not None and record.refresh_token:
108
+ self._adopt(record)
109
+ else:
110
+ self.login(self._username, self._password)
111
+
112
+ def post(self, url, data=None, json=None, **kwargs):
113
+ r"""Sends a POST request. Returns :class:`Response` object.
114
+
115
+ :param url: URL for the new :class:`Request` object.
116
+ :param data: (optional) Dictionary, list of tuples, bytes, or file-like
117
+ object to send in the body of the :class:`Request`.
118
+ :param json: (optional) json to send in the body of the :class:`Request`.
119
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
120
+ :rtype: requests.Response
121
+ """
122
+ timeout = self.timeout
123
+ if "timeout" in kwargs:
124
+ timeout = kwargs.pop("timeout")
125
+
126
+ return self.request(
127
+ "POST", url, data=data, json=json, timeout=timeout, **kwargs
128
+ )
129
+
130
+ def login(self, username: str, password: str):
131
+ """
132
+ Authenticate connection to OpenProtein with your credentials.
133
+
134
+ Parameters
135
+ -----------
136
+ username: str
137
+ username
138
+ password: str
139
+ password
140
+ """
141
+ # unset the auth first
142
+ self.auth = None
143
+ self._get_auth_token(username, password)
144
+
145
+ def _auth_post(self, url: str, **kwargs):
146
+ """POST for the auth endpoints, with the refresh logic suppressed.
147
+
148
+ Sets ``_refreshing`` so the proactive/reactive refresh in request()
149
+ does not recurse while we are logging in or refreshing.
150
+ """
151
+ self._refreshing = True
152
+ try:
153
+ return self.post(url, **kwargs)
154
+ finally:
155
+ self._refreshing = False
156
+
157
+ def _get_auth_token(self, username: str, password: str):
158
+ url = urljoin(self.backend, "v1/auth/login")
159
+ try:
160
+ response = self._auth_post(
161
+ url,
162
+ data={"username": username, "password": password},
163
+ headers={"X-Token-Delivery": "body"},
164
+ timeout=3,
165
+ )
166
+ except HTTPError as e:
167
+ if e.status_code == 404:
168
+ # Backend not yet upgraded -> use the legacy endpoint.
169
+ return self._legacy_login(username, password)
170
+ raise AuthError(
171
+ "Authentication failed. Please check your credentials and connection."
172
+ ) from e
173
+ self._apply_token_response(response.json(), username)
174
+
175
+ def _legacy_login(self, username: str, password: str):
176
+ url = urljoin(self.backend, "v1/login/access-token")
177
+ try:
178
+ response = self._auth_post(
179
+ url,
180
+ data={"username": username, "password": password},
181
+ timeout=3,
182
+ )
183
+ except HTTPError as e:
184
+ raise AuthError(
185
+ "Authentication failed. Please check your credentials and connection."
186
+ ) from e
187
+ token = response.json().get("access_token")
188
+ if token is None:
189
+ raise AuthError("Unable to authenticate with given credentials.")
190
+ self._legacy_mode = True
191
+ self._refresh_token = None
192
+ self._expires_at = 0.0
193
+ self._set_token(username, token)
194
+
195
+ def _apply_token_response(self, result: dict, username: str):
196
+ """Parse a login/refresh response, update memory + store, set the bearer."""
197
+ access_token = result.get("access_token")
198
+ if access_token is None:
199
+ raise AuthError("Unable to authenticate with given credentials.")
200
+ refresh_token = result.get("refresh_token")
201
+ if refresh_token is None:
202
+ # Cookie-mode / pre-rotation backend -> no refresh available.
203
+ self._legacy_mode = True
204
+ self._refresh_token = None
205
+ self._expires_at = 0.0
206
+ else:
207
+ self._legacy_mode = False
208
+ self._refresh_token = refresh_token
209
+ # A missing/zero expires_in is treated as "expired now", which makes
210
+ # the proactive check refresh on the next request rather than trust a
211
+ # token of unknown lifetime.
212
+ self._expires_at = time.time() + (result.get("expires_in") or 0)
213
+ self._token_store.save(
214
+ self._identity,
215
+ TokenRecord(
216
+ access_token=access_token,
217
+ refresh_token=refresh_token,
218
+ expires_at=self._expires_at,
219
+ ),
220
+ )
221
+ self._set_token(username, access_token)
222
+
223
+ def _set_token(self, username: str, access_token: str):
224
+ if isinstance(self.auth, BearerAuth):
225
+ self.auth._token = access_token
226
+ else:
227
+ self.auth = BearerAuth(username=username, token=access_token)
228
+
229
+ def _adopt(self, record: TokenRecord):
230
+ """Adopt an existing token record (from disk or a fresher in-store copy).
231
+
232
+ Callers must only pass a record whose ``refresh_token`` is set — a
233
+ record without one is not adoptable (it would leave the session in
234
+ non-legacy mode with nothing to refresh).
235
+ """
236
+ self._legacy_mode = False
237
+ self._refresh_token = record.refresh_token
238
+ self._expires_at = record.expires_at
239
+ self._set_token(self._username, record.access_token)
240
+
241
+ def _refresh(self):
242
+ """Refresh the access token, serialized across threads and processes.
243
+
244
+ Re-reads the store under the lock first: if another thread/process
245
+ already rotated the token, adopt that instead of refreshing again
246
+ (replaying a superseded refresh token would revoke the session).
247
+ """
248
+ with self._refresh_lock, self._token_store.lock():
249
+ record = self._token_store.load(self._identity)
250
+ if (
251
+ record is not None
252
+ and record.refresh_token
253
+ and record.expires_at > self._expires_at
254
+ ):
255
+ self._adopt(record)
256
+ return
257
+
258
+ if self._refresh_token is None:
259
+ self._relogin()
260
+ return
261
+
262
+ url = urljoin(self.backend, "v1/auth/refresh")
263
+ try:
264
+ response = self._auth_post(
265
+ url,
266
+ json={"refresh_token": self._refresh_token},
267
+ headers={"X-Token-Delivery": "body"},
268
+ timeout=10,
269
+ )
270
+ except HTTPError as e:
271
+ # Recover by re-logging-in when the refresh token is rejected
272
+ # (401/403) or when the endpoint is absent (404 from a backend
273
+ # that issued a refresh token but doesn't expose /auth/refresh,
274
+ # e.g. mid-rollback) — re-login itself drops to legacy mode if
275
+ # the backend has no refresh support. Other statuses are real
276
+ # errors and propagate.
277
+ if e.status_code in (401, 403, 404):
278
+ self._relogin()
279
+ return
280
+ raise
281
+ self._apply_token_response(response.json(), self._username)
282
+
283
+ def _relogin(self):
284
+ """Re-establish a session from the in-memory credentials."""
285
+ try:
286
+ self.login(self._username, self._password)
287
+ except AuthError:
288
+ raise
289
+ except Exception as e:
290
+ raise AuthError("Session expired and re-login failed.") from e
291
+
292
+ def _should_refresh_proactively(self) -> bool:
293
+ return (
294
+ not self._refreshing
295
+ and not self._legacy_mode
296
+ and self._refresh_token is not None
297
+ and time.time() >= self._expires_at - SKEW_SECONDS
298
+ )
299
+
300
+ def request(self, method: str, url: str, *args, **kwargs):
301
+ if self._should_refresh_proactively():
302
+ self._refresh()
303
+
304
+ full_url = urljoin(self.backend, url)
305
+ response = super().request(method, full_url, *args, **kwargs)
306
+
307
+ # Reactive refresh: a 401 means the access token expired between the
308
+ # proactive check and the call (or skew). Refresh once and retry once.
309
+ if (
310
+ response.status_code == 401
311
+ and not self._refreshing
312
+ and not self._legacy_mode
313
+ ):
314
+ self._refresh()
315
+ response = super().request(method, full_url, *args, **kwargs)
316
+
317
+ if (js := kwargs.get("json")) and js is not None:
318
+ if _total_size(js) > 1e6:
319
+ warnings.warn(
320
+ "The requested payload is >1MB. There might be some delays or issues in processing. If the request fails, please try again with smaller sizes."
321
+ )
322
+
323
+ # intercept CloudFront errors
324
+ if "cloudfront" in response.headers.get("Server", "").lower():
325
+ if response.status_code in (502, 503):
326
+ raise CloudFrontError(
327
+ f"We're experiencing a temporary backend issue via CloudFront. Please try again later. Error {response.status_code}."
328
+ )
329
+ elif response.status_code == 504:
330
+ raise TimeoutError(
331
+ "Your request took too long to process likely due to it's size. Please try breaking it up into smaller requests if possible."
332
+ )
333
+ elif not response.ok:
334
+ # raise custom exception that prints better error message than requests.HTTPError
335
+ raise HTTPError(response)
336
+ return response
337
+
338
+
339
+ def _total_size(o: Sequence | Mapping | object, seen=None):
340
+ """Recursively finds size of objects including contents."""
341
+ if seen is None:
342
+ seen = set()
343
+ obj_id = id(o)
344
+ if obj_id in seen:
345
+ return 0
346
+ seen.add(obj_id)
347
+ size = sys.getsizeof(o)
348
+ if isinstance(o, dict):
349
+ size += sum((_total_size(k, seen) + _total_size(v, seen)) for k, v in o.items())
350
+ elif isinstance(o, (list, tuple, set, frozenset)):
351
+ size += sum(_total_size(i, seen) for i in o)
352
+ return size
353
+
354
+
355
+ class TimeoutError(requests.exceptions.HTTPError):
356
+ """
357
+ An Exception raised due to timeout, possibly from overly large
358
+ requests.
359
+ """
360
+
361
+
362
+ class CloudFrontError(requests.exceptions.HTTPError):
363
+ """
364
+ An Exception raised due to CloudFront.
365
+
366
+ This is usually due to the strict timeout from CloudFront.
367
+ AWS CloudFront limits responses to return within 2 minutes.
368
+ This can be a bit prohibitive for our system that tends to
369
+ deal with large data. It is usually safe to just ignore/retry upon
370
+ hitting this error. Our system will scale up and still handle
371
+ the job.
372
+ """
@@ -80,16 +80,9 @@ def clustering_hierarchical_post(
80
80
  assay_id: str | None = None,
81
81
  reduction: str | None = None,
82
82
  svd_id: str | None = None,
83
- force_recompute: bool = False,
84
83
  **kwargs,
85
84
  ) -> HierarchicalFitJob:
86
- """POST to create a hierarchical clustering fit job.
87
-
88
- Parameters
89
- ----------
90
- force_recompute : bool
91
- If True, send ?force=true so the backend bypasses the result cache and recomputes. Default: False.
92
- """
85
+ """POST to create a hierarchical clustering fit job."""
93
86
  body: dict = {
94
87
  "model_id": model_id,
95
88
  "feature_type": feature_type,
@@ -112,6 +105,5 @@ def clustering_hierarchical_post(
112
105
  body["assay_id"] = assay_id
113
106
  body.update(**kwargs)
114
107
 
115
- params = {"force": "true"} if force_recompute else None
116
- response = session.post(f"{PATH_PREFIX}/hierarchical", json=body, params=params)
108
+ response = session.post(f"{PATH_PREFIX}/hierarchical", json=body)
117
109
  return HierarchicalFitJob.model_validate(response.json())
@@ -144,9 +144,6 @@ class ClusteringAPI:
144
144
  reduction.value if isinstance(reduction, ReductionType) else reduction
145
145
  )
146
146
 
147
- # Advanced flags such as `force_recompute` are accepted via **kwargs
148
- # only (intentionally kept out of the typed signature / autocomplete):
149
- # they bypass the backend result cache and are easy to misuse.
150
147
  job = api.clustering_hierarchical_post(
151
148
  session=self.session,
152
149
  model_id=model_id,