openprotein-python 0.13.1__tar.gz → 0.14.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/PKG-INFO +2 -2
  2. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/__init__.py +12 -1
  3. openprotein_python-0.14.0/openprotein/clustering/__init__.py +14 -0
  4. openprotein_python-0.14.0/openprotein/clustering/api.py +117 -0
  5. openprotein_python-0.14.0/openprotein/clustering/clustering.py +194 -0
  6. openprotein_python-0.14.0/openprotein/clustering/models.py +75 -0
  7. openprotein_python-0.14.0/openprotein/clustering/schemas.py +85 -0
  8. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/data/data.py +3 -1
  9. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/ablang.py +1 -1
  10. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/api.py +35 -7
  11. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/embeddings.py +5 -0
  12. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/esm.py +4 -1
  13. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/models.py +14 -4
  14. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/openprotein.py +1 -1
  15. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/poet.py +5 -4
  16. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/poet2.py +3 -2
  17. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/__init__.py +6 -1
  18. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/api.py +5 -1
  19. openprotein_python-0.14.0/openprotein/fold/esmfold2.py +227 -0
  20. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/fold.py +8 -1
  21. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/future.py +98 -22
  22. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/protenix.py +11 -0
  23. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/jobs/jobs.py +3 -1
  24. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/jobs/schemas.py +2 -0
  25. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/models/__init__.py +5 -0
  26. openprotein_python-0.14.0/openprotein/models/foundation/esmif1.py +213 -0
  27. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/models/foundation/proteinmpnn.py +8 -9
  28. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/models/models.py +3 -0
  29. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/molecules/complex.py +2 -2
  30. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/molecules/protein.py +19 -5
  31. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/molecules/template.py +1 -0
  32. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/prompt/api.py +155 -103
  33. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/prompt/models.py +85 -8
  34. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/prompt/prompt.py +22 -15
  35. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/prompt/schemas.py +2 -2
  36. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/svd/api.py +13 -3
  37. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/svd/models.py +7 -2
  38. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/umap/api.py +13 -3
  39. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/umap/models.py +7 -2
  40. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/umap/umap.py +2 -0
  41. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/pyproject.toml +3 -1
  42. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/.gitignore +0 -0
  43. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/LICENSE.txt +0 -0
  44. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/README.md +0 -0
  45. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/_version.py +0 -0
  46. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/align/__init__.py +0 -0
  47. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/align/align.py +0 -0
  48. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/align/api.py +0 -0
  49. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/align/future.py +0 -0
  50. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/align/msa.py +0 -0
  51. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/align/schemas.py +0 -0
  52. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/__init__.py +0 -0
  53. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/align.py +0 -0
  54. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/assaydata.py +0 -0
  55. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/deprecated/__init__.py +0 -0
  56. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/deprecated/design.py +0 -0
  57. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/deprecated/poet.py +0 -0
  58. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/deprecated/predict.py +0 -0
  59. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/deprecated/train.py +0 -0
  60. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/design.py +0 -0
  61. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/designer.py +0 -0
  62. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/embedding.py +0 -0
  63. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/error.py +0 -0
  64. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/fold.py +0 -0
  65. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/job.py +0 -0
  66. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/predict.py +0 -0
  67. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/predictor.py +0 -0
  68. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/prompt.py +0 -0
  69. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/svd.py +0 -0
  70. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/train.py +0 -0
  71. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/api/umap.py +0 -0
  72. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/__init__.py +0 -0
  73. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/deprecated.py +0 -0
  74. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/__init__.py +0 -0
  75. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/align/__init__.py +0 -0
  76. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/align/base.py +0 -0
  77. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/align/msa.py +0 -0
  78. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/align/prompt.py +0 -0
  79. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/assaydata.py +0 -0
  80. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/deprecated/__init__.py +0 -0
  81. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/deprecated/design.py +0 -0
  82. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/deprecated/poet.py +0 -0
  83. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/deprecated/predict.py +0 -0
  84. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/deprecated/train.py +0 -0
  85. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/design.py +0 -0
  86. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/designer.py +0 -0
  87. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/embeddings/__init__.py +0 -0
  88. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/embeddings/base.py +0 -0
  89. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/embeddings/esm.py +0 -0
  90. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/embeddings/future.py +0 -0
  91. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/embeddings/openprotein.py +0 -0
  92. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/embeddings/poet.py +0 -0
  93. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/embeddings/poet2.py +0 -0
  94. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/embeddings/test.py +0 -0
  95. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/fold/__init__.py +0 -0
  96. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/fold/alphafold2.py +0 -0
  97. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/fold/base.py +0 -0
  98. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/fold/boltz.py +0 -0
  99. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/fold/esmfold.py +0 -0
  100. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/fold/future.py +0 -0
  101. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/futures.py +0 -0
  102. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/predict.py +0 -0
  103. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/predictor/__init__.py +0 -0
  104. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/predictor/predict.py +0 -0
  105. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/predictor/predictor.py +0 -0
  106. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/predictor/validate.py +0 -0
  107. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/prompt.py +0 -0
  108. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/svd.py +0 -0
  109. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/train.py +0 -0
  110. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/models/umap.py +0 -0
  111. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/__init__.py +0 -0
  112. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/align.py +0 -0
  113. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/assaydata.py +0 -0
  114. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/deprecated/__init__.py +0 -0
  115. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/deprecated/design.py +0 -0
  116. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/deprecated/predict.py +0 -0
  117. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/deprecated/train.py +0 -0
  118. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/design.py +0 -0
  119. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/designer.py +0 -0
  120. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/embeddings.py +0 -0
  121. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/fold.py +0 -0
  122. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/job.py +0 -0
  123. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/predict.py +0 -0
  124. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/predictor.py +0 -0
  125. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/prompt.py +0 -0
  126. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/svd.py +0 -0
  127. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/train.py +0 -0
  128. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/app/services/umap.py +0 -0
  129. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/base.py +0 -0
  130. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/chains.py +0 -0
  131. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/common/__init__.py +0 -0
  132. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/common/features.py +0 -0
  133. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/common/model_metadata.py +0 -0
  134. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/common/reduction.py +0 -0
  135. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/common/residue_contants.py +0 -0
  136. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/config.py +0 -0
  137. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/csv.py +0 -0
  138. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/data/__init__.py +0 -0
  139. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/data/api.py +0 -0
  140. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/data/assaydataset.py +0 -0
  141. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/data/schemas.py +0 -0
  142. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/design/__init__.py +0 -0
  143. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/design/api.py +0 -0
  144. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/design/design.py +0 -0
  145. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/design/future.py +0 -0
  146. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/design/schemas.py +0 -0
  147. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/__init__.py +0 -0
  148. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/future.py +0 -0
  149. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/embeddings/schemas.py +0 -0
  150. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/errors.py +0 -0
  151. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fasta.py +0 -0
  152. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/alphafold2.py +0 -0
  153. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/boltz.py +0 -0
  154. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/common.py +0 -0
  155. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/complex.py +0 -0
  156. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/esmfold.py +0 -0
  157. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/minifold.py +0 -0
  158. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/models.py +0 -0
  159. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/rosettafold3.py +0 -0
  160. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/fold/schemas.py +0 -0
  161. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/jobs/__init__.py +0 -0
  162. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/jobs/api.py +0 -0
  163. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/jobs/futures.py +0 -0
  164. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/models/base.py +0 -0
  165. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/models/foundation/boltzgen.py +0 -0
  166. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/models/foundation/boltzgen_schema.py +0 -0
  167. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/models/foundation/rfdiffusion.py +0 -0
  168. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/models/structure_generation.py +0 -0
  169. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/molecules/__init__.py +0 -0
  170. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/molecules/chains.py +0 -0
  171. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/molecules/structure.py +0 -0
  172. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/predictor/__init__.py +0 -0
  173. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/predictor/api.py +0 -0
  174. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/predictor/models.py +0 -0
  175. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/predictor/prediction.py +0 -0
  176. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/predictor/predictor.py +0 -0
  177. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/predictor/schemas.py +0 -0
  178. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/predictor/validate.py +0 -0
  179. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/prompt/__init__.py +0 -0
  180. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/protein.py +0 -0
  181. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/scaffolds.py +0 -0
  182. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/__init__.py +0 -0
  183. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/align.py +0 -0
  184. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/assaydata.py +0 -0
  185. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/deprecated/__init__.py +0 -0
  186. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/deprecated/design.py +0 -0
  187. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/deprecated/poet.py +0 -0
  188. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/deprecated/predict.py +0 -0
  189. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/deprecated/train.py +0 -0
  190. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/design.py +0 -0
  191. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/designer.py +0 -0
  192. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/embeddings.py +0 -0
  193. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/features.py +0 -0
  194. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/fold.py +0 -0
  195. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/job.py +0 -0
  196. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/predict.py +0 -0
  197. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/predictor.py +0 -0
  198. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/prompt.py +0 -0
  199. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/svd.py +0 -0
  200. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/train.py +0 -0
  201. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/schemas/umap.py +0 -0
  202. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/svd/__init__.py +0 -0
  203. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/svd/schemas.py +0 -0
  204. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/svd/svd.py +0 -0
  205. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/umap/__init__.py +0 -0
  206. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/umap/schemas.py +0 -0
  207. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/utils/__init__.py +0 -0
  208. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/utils/chain_id.py +0 -0
  209. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/utils/cif.py +0 -0
  210. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/utils/numpy.py +0 -0
  211. {openprotein_python-0.13.1 → openprotein_python-0.14.0}/openprotein/utils/sequence.py +0 -0
  212. {openprotein_python-0.13.1 → openprotein_python-0.14.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.13.1
3
+ Version: 0.14.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
@@ -14,7 +14,7 @@ Requires-Dist: pandas<3,>=2.2.2
14
14
  Requires-Dist: pydantic<3,>=2.5
15
15
  Requires-Dist: requests<3,>=2.32.3
16
16
  Requires-Dist: tomli<3,>=2.3.0
17
- Requires-Dist: tqdm<5,>=4.66.5
17
+ Requires-Dist: tqdm<5,>=4.66.4
18
18
  Description-Content-Type: text/markdown
19
19
 
20
20
  [![PyPI version](https://badge.fury.io/py/openprotein-python.svg)](https://pypi.org/project/openprotein-python/)
@@ -20,6 +20,7 @@ from openprotein.fold import FoldAPI
20
20
  from openprotein.models import ModelsAPI
21
21
  from openprotein.svd import SVDAPI
22
22
  from openprotein.umap import UMAPAPI
23
+ from openprotein.clustering import ClusteringAPI
23
24
  from openprotein.predictor import PredictorAPI
24
25
  from openprotein.design import DesignAPI
25
26
  from openprotein.jobs import Future
@@ -38,6 +39,7 @@ class OpenProtein(APISession):
38
39
  _embeddings = None
39
40
  _svd = None
40
41
  _umap = None
42
+ _clustering = None
41
43
  _fold = None
42
44
  _predictor = None
43
45
  _design = None
@@ -81,7 +83,7 @@ class OpenProtein(APISession):
81
83
  @property
82
84
  def prompt(self) -> PromptAPI:
83
85
  """
84
- The Align submodule gives access to the sequence alignment capabilities by building MSAs and prompts that can be used with PoET.
86
+ The prompt submodule gives access to creating and retrieving prompts and queries used to condition PoET models.
85
87
  """
86
88
  if self._prompt is None:
87
89
  self._prompt = PromptAPI(self)
@@ -120,6 +122,15 @@ class OpenProtein(APISession):
120
122
  )
121
123
  return self._umap
122
124
 
125
+ @property
126
+ def clustering(self) -> ClusteringAPI:
127
+ """The clustering API for fitting hierarchical clustering jobs."""
128
+ if self._clustering is None:
129
+ self._clustering = ClusteringAPI(
130
+ session=self,
131
+ )
132
+ return self._clustering
133
+
123
134
  @property
124
135
  def predictor(self) -> PredictorAPI:
125
136
  """
@@ -0,0 +1,14 @@
1
+ """Clustering module for OpenProtein.
2
+
3
+ isort:skip_file
4
+ """
5
+
6
+ from .schemas import (
7
+ ClusteringMetadata,
8
+ HierarchicalClusteringResult,
9
+ HierarchicalFitJob,
10
+ LinkageMethod,
11
+ Metric,
12
+ )
13
+ from .models import HierarchicalClusteringFuture
14
+ from .clustering import ClusteringAPI
@@ -0,0 +1,117 @@
1
+ """Clustering REST API — HTTP calls to the backend."""
2
+
3
+ from pydantic import TypeAdapter
4
+
5
+ from openprotein.base import APISession
6
+ from openprotein.errors import APIError, InvalidParameterError
7
+
8
+ from .schemas import (
9
+ ClusteringMetadata,
10
+ HierarchicalClusteringResult,
11
+ HierarchicalFitJob,
12
+ )
13
+
14
+ PATH_PREFIX = "v1/clustering"
15
+
16
+
17
+ def clustering_list_get(
18
+ session: APISession,
19
+ method: str | None = None,
20
+ page_size: int | None = None,
21
+ page_offset: int | None = None,
22
+ ) -> list[ClusteringMetadata]:
23
+ """List clustering jobs, optionally filtered by method."""
24
+ params: dict = {}
25
+ if method is not None:
26
+ params["method"] = method
27
+ if page_size is not None:
28
+ params["page_size"] = page_size
29
+ if page_offset is not None:
30
+ params["page_offset"] = page_offset
31
+ response = session.get(PATH_PREFIX, params=params or None)
32
+ return TypeAdapter(list[ClusteringMetadata]).validate_python(response.json())
33
+
34
+
35
+ def clustering_get(session: APISession, clustering_id: str) -> ClusteringMetadata:
36
+ """Fetch clustering job metadata."""
37
+ response = session.get(f"{PATH_PREFIX}/{clustering_id}")
38
+ return ClusteringMetadata.model_validate(response.json())
39
+
40
+
41
+ def clustering_get_result(
42
+ session: APISession, clustering_id: str
43
+ ) -> HierarchicalClusteringResult:
44
+ """Fetch the clustering result (linkage + leaf_order). Sequences are NOT
45
+ filled by this function — callers that need them should call
46
+ `clustering_get_sequences` and assign to `.sequences`."""
47
+ response = session.get(f"{PATH_PREFIX}/{clustering_id}/result")
48
+ return HierarchicalClusteringResult.model_validate(response.json())
49
+
50
+
51
+ def clustering_get_sequences(session: APISession, clustering_id: str) -> list[bytes]:
52
+ """Fetch the input sequences used for the clustering job."""
53
+ response = session.get(f"{PATH_PREFIX}/{clustering_id}/sequences")
54
+ return TypeAdapter(list[bytes]).validate_python(response.json())
55
+
56
+
57
+ def clustering_delete(session: APISession, clustering_id: str) -> bool:
58
+ """Delete a clustering job."""
59
+ response = session.delete(f"{PATH_PREFIX}/{clustering_id}")
60
+ if 200 <= response.status_code < 300:
61
+ return True
62
+ raise APIError(response.text)
63
+
64
+
65
+ def clustering_redispatch_post(
66
+ session: APISession, clustering_id: str
67
+ ) -> HierarchicalFitJob:
68
+ """Redispatch a clustering job."""
69
+ response = session.post(f"{PATH_PREFIX}/{clustering_id}/redispatch")
70
+ return HierarchicalFitJob.model_validate(response.json())
71
+
72
+
73
+ def clustering_hierarchical_post(
74
+ session: APISession,
75
+ model_id: str,
76
+ feature_type: str,
77
+ linkage_method: str,
78
+ metric: str,
79
+ sequences: list[bytes] | list[str] | None = None,
80
+ assay_id: str | None = None,
81
+ reduction: str | None = None,
82
+ svd_id: str | None = None,
83
+ force_recompute: bool = False,
84
+ **kwargs,
85
+ ) -> 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
+ """
93
+ body: dict = {
94
+ "model_id": model_id,
95
+ "feature_type": feature_type,
96
+ "linkage_method": linkage_method,
97
+ "metric": metric,
98
+ }
99
+ if reduction is not None:
100
+ body["reduction"] = reduction
101
+ if svd_id is not None:
102
+ body["svd_id"] = svd_id
103
+ if sequences is not None:
104
+ if assay_id is not None:
105
+ raise InvalidParameterError("Expected only either sequences or assay_id")
106
+ body["sequences"] = [
107
+ (s if isinstance(s, str) else s.decode()) for s in sequences
108
+ ]
109
+ else:
110
+ if assay_id is None:
111
+ raise InvalidParameterError("Expected either sequences or assay_id")
112
+ body["assay_id"] = assay_id
113
+ body.update(**kwargs)
114
+
115
+ params = {"force": "true"} if force_recompute else None
116
+ response = session.post(f"{PATH_PREFIX}/hierarchical", json=body, params=params)
117
+ return HierarchicalFitJob.model_validate(response.json())
@@ -0,0 +1,194 @@
1
+ """ClusteringAPI — user-facing entry point for clustering jobs."""
2
+
3
+ import typing
4
+
5
+ from openprotein.base import APISession
6
+ from openprotein.common import Feature, FeatureType, Reduction, ReductionType
7
+ from openprotein.data import AssayDataset, AssayMetadata
8
+ from openprotein.embeddings import EmbeddingModel, EmbeddingsAPI
9
+ from openprotein.errors import InvalidParameterError
10
+ from openprotein.svd import SVDAPI, SVDModel
11
+
12
+ from . import api
13
+ from .models import HierarchicalClusteringFuture
14
+ from .schemas import LinkageMethod, Metric
15
+
16
+
17
+ class ClusteringAPI:
18
+ """Top-level clustering API. Use `session.clustering.hierarchical(...)` to
19
+ fit a hierarchical clustering job on a sequence set."""
20
+
21
+ def __init__(self, session: APISession):
22
+ self.session = session
23
+
24
+ @typing.overload
25
+ def hierarchical(
26
+ self,
27
+ model: EmbeddingModel,
28
+ reduction: Reduction | ReductionType,
29
+ feature_type: FeatureType = FeatureType.PLM,
30
+ linkage_method: LinkageMethod | str = LinkageMethod.WARD,
31
+ metric: Metric | str = Metric.EUCLIDEAN,
32
+ sequences: list[bytes] | list[str] | None = None,
33
+ assay: AssayDataset | AssayMetadata | str | None = None,
34
+ **kwargs,
35
+ ) -> HierarchicalClusteringFuture: ...
36
+
37
+ @typing.overload
38
+ def hierarchical(
39
+ self,
40
+ model: SVDModel,
41
+ reduction: None = None,
42
+ feature_type: FeatureType = FeatureType.SVD,
43
+ linkage_method: LinkageMethod | str = LinkageMethod.WARD,
44
+ metric: Metric | str = Metric.EUCLIDEAN,
45
+ sequences: list[bytes] | list[str] | None = None,
46
+ assay: AssayDataset | AssayMetadata | str | None = None,
47
+ **kwargs,
48
+ ) -> HierarchicalClusteringFuture: ...
49
+
50
+ def hierarchical(
51
+ self,
52
+ model: EmbeddingModel | SVDModel | str,
53
+ reduction: Reduction | ReductionType | None = None,
54
+ feature_type: Feature | FeatureType | None = None,
55
+ linkage_method: LinkageMethod | str = LinkageMethod.WARD,
56
+ metric: Metric | str = Metric.EUCLIDEAN,
57
+ sequences: list[bytes] | list[str] | None = None,
58
+ assay: AssayDataset | AssayMetadata | str | None = None,
59
+ **kwargs,
60
+ ) -> HierarchicalClusteringFuture:
61
+ """Fit a hierarchical clustering on sequences, returning a future
62
+ that resolves to a HierarchicalClusteringResult (scipy linkage + leaf order)."""
63
+ # resolve assay_id
64
+ assay_id = (
65
+ assay.assay_id
66
+ if isinstance(assay, AssayMetadata)
67
+ else assay.id
68
+ if isinstance(assay, AssayDataset)
69
+ else assay
70
+ )
71
+ if sequences is not None and assay_id is not None:
72
+ raise InvalidParameterError(
73
+ "Expected only either sequences or assay, not both"
74
+ )
75
+ if sequences is not None:
76
+ n = len(sequences)
77
+ if n < 2:
78
+ raise InvalidParameterError(
79
+ f"clustering requires at least 2 sequences, got {n}"
80
+ )
81
+ if n > 10000:
82
+ raise InvalidParameterError(
83
+ f"clustering size cap exceeded: N={n} > 10000"
84
+ )
85
+ # infer feature_type from model type
86
+ feature_type = (
87
+ FeatureType.PLM
88
+ if isinstance(model, EmbeddingModel)
89
+ else FeatureType.SVD
90
+ if isinstance(model, SVDModel)
91
+ else feature_type
92
+ )
93
+ if feature_type is None:
94
+ raise InvalidParameterError(
95
+ "Expected feature_type to be provided if passing str model_id as model"
96
+ )
97
+ if isinstance(feature_type, str):
98
+ feature_type = FeatureType(feature_type)
99
+ if isinstance(reduction, str):
100
+ reduction = ReductionType(reduction)
101
+ # combo validation — mirrors the Go server rule
102
+ _WARD_ONLY_METRICS = {"ward", "centroid", "median"}
103
+ lm_val = (
104
+ linkage_method.value
105
+ if isinstance(linkage_method, LinkageMethod)
106
+ else str(linkage_method)
107
+ )
108
+ m_val = metric.value if isinstance(metric, Metric) else str(metric)
109
+ if lm_val in _WARD_ONLY_METRICS and m_val != "euclidean":
110
+ raise InvalidParameterError(
111
+ f"linkage_method={lm_val!r} requires metric='euclidean', got {m_val!r}"
112
+ )
113
+ # resolve model_id and svd_id
114
+ svd_id: str | None = None
115
+ if feature_type == FeatureType.PLM:
116
+ if reduction is None:
117
+ raise InvalidParameterError(
118
+ "Expected reduction when using PLM feature_type"
119
+ )
120
+ if isinstance(model, str):
121
+ embeddings_api = getattr(self.session, "embedding", None)
122
+ assert isinstance(embeddings_api, EmbeddingsAPI)
123
+ model = embeddings_api.get_model(model)
124
+ assert isinstance(model, EmbeddingModel), "Expected EmbeddingModel"
125
+ model_id = model.id
126
+ elif feature_type == FeatureType.SVD:
127
+ if reduction is not None:
128
+ raise InvalidParameterError(
129
+ "Unexpected reduction when using SVD feature_type"
130
+ )
131
+ if isinstance(model, str):
132
+ svd_api = getattr(self.session, "svd", None)
133
+ assert isinstance(svd_api, SVDAPI)
134
+ model = svd_api.get_svd(model)
135
+ assert isinstance(model, SVDModel), "Expected SVDModel"
136
+ model_id = model.id
137
+ svd_id = model.id
138
+ else:
139
+ raise InvalidParameterError(f"Unsupported feature_type: {feature_type}")
140
+
141
+ linkage_method = (
142
+ linkage_method.value
143
+ if isinstance(linkage_method, LinkageMethod)
144
+ else str(linkage_method)
145
+ )
146
+ metric = metric.value if isinstance(metric, Metric) else str(metric)
147
+ reduction_str = (
148
+ reduction.value if isinstance(reduction, ReductionType) else reduction
149
+ )
150
+
151
+ # Advanced flags such as `force_recompute` are accepted via **kwargs
152
+ # only (intentionally kept out of the typed signature / autocomplete):
153
+ # they bypass the backend result cache and are easy to misuse.
154
+ job = api.clustering_hierarchical_post(
155
+ session=self.session,
156
+ model_id=model_id,
157
+ feature_type=feature_type.value,
158
+ linkage_method=linkage_method,
159
+ metric=metric,
160
+ sequences=sequences,
161
+ assay_id=assay_id,
162
+ reduction=reduction_str,
163
+ svd_id=svd_id,
164
+ **kwargs,
165
+ )
166
+ return HierarchicalClusteringFuture(session=self.session, job=job)
167
+
168
+ def get(self, clustering_id: str) -> HierarchicalClusteringFuture:
169
+ """Fetch a clustering job by ID."""
170
+ # Single-method dispatch today; when new methods are added,
171
+ # branch on metadata.method to pick the concrete future type.
172
+ metadata = api.clustering_get(self.session, clustering_id)
173
+ return HierarchicalClusteringFuture(session=self.session, metadata=metadata)
174
+
175
+ def list(
176
+ self,
177
+ method: str | None = None,
178
+ page_size: int | None = None,
179
+ page_offset: int | None = None,
180
+ ) -> list[HierarchicalClusteringFuture]:
181
+ """List clustering jobs, optionally filtering by method.
182
+
183
+ Pass `page_size` / `page_offset` to paginate."""
184
+ # Single-method dispatch today; when new methods are added,
185
+ # branch on metadata.method to pick the concrete future type.
186
+ return [
187
+ HierarchicalClusteringFuture(session=self.session, metadata=md)
188
+ for md in api.clustering_list_get(
189
+ self.session,
190
+ method=method,
191
+ page_size=page_size,
192
+ page_offset=page_offset,
193
+ )
194
+ ]
@@ -0,0 +1,75 @@
1
+ """HierarchicalClusteringFuture — the future type returned by ClusteringAPI calls."""
2
+
3
+ from openprotein.base import APISession
4
+ from openprotein.jobs import Future, JobsAPI
5
+
6
+ from . import api
7
+ from .schemas import ClusteringMetadata, HierarchicalClusteringResult, HierarchicalFitJob
8
+
9
+
10
+ class HierarchicalClusteringFuture(Future["HierarchicalClusteringResult"]):
11
+ """Future for a hierarchical clustering job. Waits for the fit job to complete, then
12
+ returns a HierarchicalClusteringResult (linkage + leaf_order + sequences)."""
13
+
14
+ job: HierarchicalFitJob
15
+
16
+ def __init__(
17
+ self,
18
+ session: APISession,
19
+ job: HierarchicalFitJob | None = None,
20
+ metadata: ClusteringMetadata | None = None,
21
+ ):
22
+ if metadata is None:
23
+ if job is None:
24
+ raise ValueError("Expected clustering metadata or job")
25
+ metadata = api.clustering_get(session, job.job_id)
26
+ self._metadata = metadata
27
+ if job is None:
28
+ jobs_api = getattr(session, "jobs", None)
29
+ assert isinstance(jobs_api, JobsAPI)
30
+ job = HierarchicalFitJob.create(
31
+ jobs_api.get_job(job_id=metadata.id)
32
+ )
33
+ self._sequences: list[bytes] | None = None
34
+ super().__init__(session, job)
35
+
36
+ def __str__(self) -> str:
37
+ return str(self.metadata)
38
+
39
+ def __repr__(self) -> str:
40
+ return repr(self.metadata)
41
+
42
+ @property
43
+ def id(self) -> str:
44
+ return self._metadata.id
45
+
46
+ @property
47
+ def metadata(self) -> ClusteringMetadata:
48
+ """Metadata — lazily refreshes until the job reaches a terminal state."""
49
+ if not self._metadata.is_done():
50
+ self._metadata = api.clustering_get(self.session, self._metadata.id)
51
+ return self._metadata
52
+
53
+ @property
54
+ def sequences(self) -> list[bytes]:
55
+ """Input sequences used to fit the clustering job (cached)."""
56
+ if self._sequences is None:
57
+ self._sequences = api.clustering_get_sequences(
58
+ session=self.session, clustering_id=self.id
59
+ )
60
+ return self._sequences
61
+
62
+ def _get(self, verbose: bool = False) -> HierarchicalClusteringResult:
63
+ """Fetch the clustering result. Called by Future.get after SUCCESS."""
64
+ result = api.clustering_get_result(self.session, self.id)
65
+ result.sequences = self.sequences
66
+ return result
67
+
68
+ def delete(self) -> bool:
69
+ """Delete this clustering job."""
70
+ return api.clustering_delete(self.session, self.id)
71
+
72
+ def redispatch(self) -> "HierarchicalClusteringFuture":
73
+ """Redispatch this clustering job."""
74
+ job = api.clustering_redispatch_post(self.session, self.id)
75
+ return HierarchicalClusteringFuture(session=self.session, job=job)
@@ -0,0 +1,85 @@
1
+ """Schemas for OpenProtein clustering."""
2
+
3
+ from datetime import datetime
4
+ from enum import Enum
5
+ from typing import Literal
6
+
7
+ import numpy as np
8
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
9
+
10
+ from openprotein.common import FeatureType
11
+ from openprotein.jobs import Job, JobStatus, JobType
12
+
13
+
14
+ class LinkageMethod(str, Enum):
15
+ """Hierarchical clustering linkage methods (scipy.cluster.hierarchy)."""
16
+
17
+ WARD = "ward"
18
+ SINGLE = "single"
19
+ COMPLETE = "complete"
20
+ AVERAGE = "average"
21
+ WEIGHTED = "weighted"
22
+ CENTROID = "centroid"
23
+ MEDIAN = "median"
24
+
25
+
26
+ class Metric(str, Enum):
27
+ """Pairwise distance metrics supported by scipy.spatial.distance.pdist."""
28
+
29
+ EUCLIDEAN = "euclidean"
30
+ COSINE = "cosine"
31
+ CORRELATION = "correlation"
32
+ HAMMING = "hamming"
33
+ CHEBYSHEV = "chebyshev"
34
+ CITYBLOCK = "cityblock"
35
+ SQEUCLIDEAN = "sqeuclidean"
36
+ CANBERRA = "canberra"
37
+ BRAYCURTIS = "braycurtis"
38
+
39
+
40
+ class ClusteringMetadata(BaseModel):
41
+ id: str
42
+ status: JobStatus
43
+ created_date: datetime | None = None
44
+ method: str
45
+ linkage_method: LinkageMethod | None = None
46
+ metric: Metric | None = None
47
+ model_id: str
48
+ feature_type: FeatureType
49
+ reduction: str | None = None
50
+ svd_id: str | None = None
51
+
52
+ def is_done(self) -> bool:
53
+ return self.status.done()
54
+
55
+ model_config = ConfigDict(protected_namespaces=())
56
+
57
+
58
+ class HierarchicalFitJob(Job):
59
+ job_type: Literal[JobType.clustering_hierarchical]
60
+
61
+
62
+ class HierarchicalClusteringResult(BaseModel):
63
+ """Result of a hierarchical clustering job.
64
+
65
+ `linkage` is the scipy linkage matrix with shape (N-1, 4); pass it
66
+ directly to `scipy.cluster.hierarchy.dendrogram` or `fcluster`.
67
+ `sequences` is filled by `HierarchicalClusteringFuture._get` after the API fetch.
68
+
69
+ Note: `linkage` is a numpy array, so this model is not JSON-serializable via
70
+ `model_dump_json()` (use `linkage.tolist()` first if you need to serialize).
71
+ """
72
+
73
+ n_leaves: int
74
+ linkage: np.ndarray
75
+ leaf_order: list[int]
76
+ sequences: list[bytes] = Field(default_factory=list)
77
+
78
+ model_config = ConfigDict(arbitrary_types_allowed=True)
79
+
80
+ @field_validator("linkage", mode="before")
81
+ @classmethod
82
+ def _to_ndarray(cls, v):
83
+ if isinstance(v, np.ndarray):
84
+ return v
85
+ return np.asarray(v, dtype=float)
@@ -14,9 +14,11 @@ class DataAPI:
14
14
  def __init__(self, session: APISession):
15
15
  self.session = session
16
16
 
17
+ # `list` shadows the builtin, so the return annotation is quoted to keep it
18
+ # resolving to the builtin (not this method) under PEP 649 deferred evaluation.
17
19
  def list(
18
20
  self, limit: int | None = None, offset: int | None = None
19
- ) -> list[AssayDataset]:
21
+ ) -> "list[AssayDataset]":
20
22
  """
21
23
  List all assay datasets.
22
24
 
@@ -11,7 +11,7 @@ class AbLang2Model(EmbeddingModel):
11
11
  --------
12
12
  View specific model details (inc supported tokens) with the `?` operator.
13
13
 
14
- .. code-block:: python
14
+ .. code-block:: ipython3
15
15
 
16
16
  >>> import openprotein
17
17
  >>> session = openprotein.connect(username="user", password="password")