openprotein-python 0.8.2__1-py3-none-any.whl

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 (84) hide show
  1. openprotein/__init__.py +164 -0
  2. openprotein/_version.py +48 -0
  3. openprotein/align/__init__.py +8 -0
  4. openprotein/align/align.py +395 -0
  5. openprotein/align/api.py +428 -0
  6. openprotein/align/future.py +55 -0
  7. openprotein/align/msa.py +129 -0
  8. openprotein/align/schemas.py +165 -0
  9. openprotein/base.py +181 -0
  10. openprotein/chains.py +88 -0
  11. openprotein/common/__init__.py +5 -0
  12. openprotein/common/features.py +7 -0
  13. openprotein/common/model_metadata.py +33 -0
  14. openprotein/common/reduction.py +8 -0
  15. openprotein/config.py +9 -0
  16. openprotein/csv.py +31 -0
  17. openprotein/data/__init__.py +9 -0
  18. openprotein/data/api.py +218 -0
  19. openprotein/data/assaydataset.py +178 -0
  20. openprotein/data/data.py +93 -0
  21. openprotein/data/schemas.py +27 -0
  22. openprotein/design/__init__.py +16 -0
  23. openprotein/design/api.py +259 -0
  24. openprotein/design/design.py +125 -0
  25. openprotein/design/future.py +146 -0
  26. openprotein/design/schemas.py +607 -0
  27. openprotein/embeddings/__init__.py +27 -0
  28. openprotein/embeddings/api.py +619 -0
  29. openprotein/embeddings/embeddings.py +151 -0
  30. openprotein/embeddings/esm.py +33 -0
  31. openprotein/embeddings/future.py +146 -0
  32. openprotein/embeddings/models.py +421 -0
  33. openprotein/embeddings/openprotein.py +21 -0
  34. openprotein/embeddings/poet.py +446 -0
  35. openprotein/embeddings/poet2.py +505 -0
  36. openprotein/embeddings/schemas.py +78 -0
  37. openprotein/errors.py +76 -0
  38. openprotein/fasta.py +92 -0
  39. openprotein/fold/__init__.py +21 -0
  40. openprotein/fold/alphafold2.py +131 -0
  41. openprotein/fold/api.py +287 -0
  42. openprotein/fold/boltz.py +691 -0
  43. openprotein/fold/esmfold.py +54 -0
  44. openprotein/fold/fold.py +107 -0
  45. openprotein/fold/future.py +509 -0
  46. openprotein/fold/models.py +139 -0
  47. openprotein/fold/schemas.py +39 -0
  48. openprotein/jobs/__init__.py +9 -0
  49. openprotein/jobs/api.py +71 -0
  50. openprotein/jobs/futures.py +746 -0
  51. openprotein/jobs/jobs.py +69 -0
  52. openprotein/jobs/schemas.py +135 -0
  53. openprotein/models/__init__.py +4 -0
  54. openprotein/models/base.py +63 -0
  55. openprotein/models/foundation/rfdiffusion.py +283 -0
  56. openprotein/models/models.py +33 -0
  57. openprotein/predictor/__init__.py +25 -0
  58. openprotein/predictor/api.py +384 -0
  59. openprotein/predictor/models.py +374 -0
  60. openprotein/predictor/prediction.py +79 -0
  61. openprotein/predictor/predictor.py +242 -0
  62. openprotein/predictor/schemas.py +113 -0
  63. openprotein/predictor/validate.py +40 -0
  64. openprotein/prompt/__init__.py +9 -0
  65. openprotein/prompt/api.py +505 -0
  66. openprotein/prompt/models.py +142 -0
  67. openprotein/prompt/prompt.py +130 -0
  68. openprotein/prompt/schemas.py +49 -0
  69. openprotein/protein.py +587 -0
  70. openprotein/svd/__init__.py +9 -0
  71. openprotein/svd/api.py +206 -0
  72. openprotein/svd/models.py +288 -0
  73. openprotein/svd/schemas.py +31 -0
  74. openprotein/svd/svd.py +134 -0
  75. openprotein/umap/__init__.py +9 -0
  76. openprotein/umap/api.py +259 -0
  77. openprotein/umap/models.py +211 -0
  78. openprotein/umap/schemas.py +35 -0
  79. openprotein/umap/umap.py +175 -0
  80. openprotein/utils/uuid.py +29 -0
  81. openprotein_python-0.8.2.dist-info/METADATA +176 -0
  82. openprotein_python-0.8.2.dist-info/RECORD +84 -0
  83. openprotein_python-0.8.2.dist-info/WHEEL +4 -0
  84. openprotein_python-0.8.2.dist-info/licenses/LICENSE.txt +30 -0
@@ -0,0 +1,54 @@
1
+ """Community-based ESMFold model."""
2
+
3
+ from collections.abc import Sequence
4
+
5
+ from openprotein.base import APISession
6
+ from openprotein.common import ModelMetadata
7
+
8
+ from . import api
9
+ from .future import FoldResultFuture
10
+ from .models import FoldModel
11
+
12
+
13
+ class ESMFoldModel(FoldModel):
14
+ """
15
+ Class providing inference endpoints for Facebook's ESMFold structure prediction models.
16
+ """
17
+
18
+ model_id: str = "esmfold"
19
+
20
+ def __init__(
21
+ self,
22
+ session: APISession,
23
+ model_id: str,
24
+ metadata: ModelMetadata | None = None,
25
+ ):
26
+ super().__init__(session=session, model_id=model_id, metadata=metadata)
27
+
28
+ def fold(
29
+ self, sequences: Sequence[bytes | str], num_recycles: int | None = None
30
+ ) -> FoldResultFuture:
31
+ """
32
+ Fold sequences using this model.
33
+
34
+ Parameters
35
+ ----------
36
+ sequences : Sequence[bytes | str]
37
+ sequences to fold
38
+ num_recycles : int | None
39
+ number of times to recycle models
40
+ Returns
41
+ -------
42
+ FoldResultFuture
43
+ """
44
+ result = FoldResultFuture.create(
45
+ session=self.session,
46
+ job=api.fold_models_post(
47
+ session=self.session,
48
+ model_id=self.model_id,
49
+ sequences=sequences,
50
+ num_recycles=num_recycles,
51
+ ),
52
+ )
53
+ assert isinstance(result, FoldResultFuture)
54
+ return result
@@ -0,0 +1,107 @@
1
+ """Fold API interface for making structure prediction requests."""
2
+
3
+ from openprotein.base import APISession
4
+
5
+ from . import api
6
+ from .alphafold2 import AlphaFold2Model
7
+ from .boltz import Boltz1Model, Boltz1xModel, Boltz2Model
8
+ from .esmfold import ESMFoldModel
9
+ from .future import FoldComplexResultFuture, FoldResultFuture
10
+ from .models import (
11
+ FoldModel,
12
+ )
13
+
14
+
15
+ class FoldAPI:
16
+ """
17
+ Fold API provides a high level interface for making protein structure predictions.
18
+ """
19
+
20
+ #: Boltz-2 model
21
+ boltz2: Boltz2Model
22
+ boltz_2: Boltz2Model
23
+ #: Boltz-1x model
24
+ boltz1x: Boltz1xModel
25
+ boltz_1x: Boltz1xModel
26
+ #: Boltz-1 model
27
+ boltz1: Boltz1Model
28
+ boltz_1: Boltz1Model
29
+ af2: AlphaFold2Model
30
+ #: AlphaFold-2 model
31
+ alphafold2: AlphaFold2Model
32
+ #: ESMFold model
33
+ esmfold: ESMFoldModel
34
+
35
+ def __init__(self, session: APISession):
36
+ self.session = session
37
+ self._load_models()
38
+
39
+ def _load_models(self):
40
+ # Dynamically add model instances as attributes - precludes any drift
41
+ models = self.list_models()
42
+ for model in models:
43
+ model_name = model.id.replace("-", "_") # hyphens out
44
+ setattr(self, model_name, model)
45
+ # Setup aliases safely
46
+ if getattr(self, "alphafold2", None):
47
+ self.af2 = self.alphafold2
48
+ if getattr(self, "boltz_1", None):
49
+ self.boltz1 = self.boltz_1
50
+ if getattr(self, "boltz_1x", None):
51
+ self.boltz1x = self.boltz_1x
52
+ if getattr(self, "boltz_2", None):
53
+ self.boltz2 = self.boltz_2
54
+
55
+ def list_models(self) -> list[FoldModel]:
56
+ """list models available for creating folds of your sequences"""
57
+ models = []
58
+ for model_id in api.fold_models_list_get(self.session):
59
+ models.append(
60
+ FoldModel.create(
61
+ session=self.session, model_id=model_id, default=FoldModel
62
+ )
63
+ )
64
+ return models
65
+
66
+ def get_model(self, model_id: str) -> FoldModel:
67
+ """
68
+ Get model by model_id.
69
+
70
+ FoldModel allows all the usual job manipulation: \
71
+ e.g. making POST and GET requests for this model specifically.
72
+
73
+
74
+ Parameters
75
+ ----------
76
+ model_id : str
77
+ the model identifier
78
+
79
+ Returns
80
+ -------
81
+ FoldModel
82
+ The model
83
+
84
+ Raises
85
+ ------
86
+ HTTPError
87
+ If the GET request does not succeed.
88
+ """
89
+ return FoldModel.create(
90
+ session=self.session, model_id=model_id, default=FoldModel
91
+ )
92
+
93
+ def get_results(self, job) -> FoldResultFuture | FoldComplexResultFuture:
94
+ """
95
+ Retrieves the results of a fold job.
96
+
97
+ Parameters
98
+ ----------
99
+ job : Job
100
+ The fold job whose results are to be retrieved.
101
+
102
+ Returns
103
+ -------
104
+ FoldResultFuture
105
+ An instance of FoldResultFuture
106
+ """
107
+ return FoldResultFuture.create(job=job, session=self.session)
@@ -0,0 +1,509 @@
1
+ """Fold prediction results represented as futures."""
2
+
3
+ from typing import TYPE_CHECKING, Literal
4
+
5
+ import numpy as np
6
+ from pydantic.type_adapter import TypeAdapter
7
+ from typing_extensions import Self
8
+
9
+ from openprotein import config
10
+ from openprotein.base import APISession
11
+ from openprotein.chains import DNA, RNA, Ligand
12
+ from openprotein.jobs import Future, MappedFuture
13
+ from openprotein.protein import Protein
14
+
15
+ from . import api
16
+ from .schemas import FoldJob
17
+
18
+ if TYPE_CHECKING:
19
+ from .boltz import BoltzAffinity, BoltzConfidence
20
+
21
+
22
+ class FoldResultFuture(MappedFuture, Future):
23
+ """
24
+ Fold results represented as a future.
25
+
26
+ Attributes
27
+ ----------
28
+ job : FoldJob
29
+ The fold job associated with this future.
30
+ """
31
+
32
+ job: FoldJob
33
+
34
+ def __init__(
35
+ self,
36
+ session: APISession,
37
+ job: FoldJob,
38
+ sequences: list[bytes] | None = None,
39
+ max_workers: int = config.MAX_CONCURRENT_WORKERS,
40
+ ):
41
+ """
42
+ Initialize a FoldResultFuture instance.
43
+
44
+ Parameters
45
+ ----------
46
+ session : APISession
47
+ The API session to use for requests.
48
+ job : FoldJob
49
+ The fold job associated with this future.
50
+ sequences : list[bytes], optional
51
+ List of sequences submitted for the fold request. If None, sequences will be fetched.
52
+ max_workers : int, optional
53
+ Maximum number of concurrent workers. Default is config.MAX_CONCURRENT_WORKERS.
54
+ """
55
+ super().__init__(session, job, max_workers)
56
+ if sequences is None:
57
+ sequences = api.fold_get_sequences(self.session, job_id=job.job_id)
58
+ self._sequences = sequences
59
+
60
+ @classmethod
61
+ def create(
62
+ cls: type[Self],
63
+ session: APISession,
64
+ job: FoldJob,
65
+ **kwargs,
66
+ ) -> "Self | FoldComplexResultFuture":
67
+ """
68
+ Factory method to create a FoldResultFuture or FoldComplexResultFuture.
69
+
70
+ Parameters
71
+ ----------
72
+ session : APISession
73
+ The API session to use for requests.
74
+ job : FoldJob
75
+ The fold job associated with this future.
76
+ **kwargs
77
+ Additional keyword arguments.
78
+
79
+ Returns
80
+ -------
81
+ FoldResultFuture or FoldComplexResultFuture
82
+ An instance of FoldResultFuture or FoldComplexResultFuture depending on the model.
83
+ """
84
+ model_id = api.fold_get(session=session, job_id=job.job_id).model_id
85
+ if model_id.startswith("boltz") or model_id.startswith("alphafold"):
86
+ return FoldComplexResultFuture(session=session, job=job, **kwargs)
87
+ else:
88
+ return cls(session=session, job=job, **kwargs)
89
+
90
+ @property
91
+ def sequences(self) -> list[bytes]:
92
+ """
93
+ Get the sequences submitted for the fold request.
94
+
95
+ Returns
96
+ -------
97
+ list[bytes]
98
+ List of sequences.
99
+ """
100
+ if self._sequences is None:
101
+ self._sequences = api.fold_get_sequences(self.session, self.job.job_id)
102
+ return self._sequences
103
+
104
+ @property
105
+ def model_id(self) -> str:
106
+ """
107
+ Get the model ID used for the fold request.
108
+
109
+ Returns
110
+ -------
111
+ str
112
+ Model ID.
113
+ """
114
+ if self._model_id is None:
115
+ self._model_id = api.fold_get(
116
+ session=self.session, job_id=self.job.job_id
117
+ ).model_id
118
+ return self._model_id
119
+
120
+ @property
121
+ def id(self):
122
+ """
123
+ Get the ID of the fold request.
124
+
125
+ Returns
126
+ -------
127
+ str
128
+ Fold job ID.
129
+ """
130
+ return self.job.job_id
131
+
132
+ def __keys__(self):
133
+ """
134
+ Get the list of sequences submitted for the fold request.
135
+
136
+ Returns
137
+ -------
138
+ list of bytes
139
+ List of sequences.
140
+ """
141
+ return self.sequences
142
+
143
+ def get(self, verbose=False) -> list[tuple[str, bytes]]:
144
+ """
145
+ Retrieve the fold results as a list of tuples mapping sequence to PDB-encoded string.
146
+
147
+ Parameters
148
+ ----------
149
+ verbose : bool, optional
150
+ If True, print verbose output. Default is False.
151
+
152
+ Returns
153
+ -------
154
+ list[tuple[str, str]]
155
+ List of tuples mapping sequence to PDB-encoded string.
156
+ """
157
+ return super().get(verbose=verbose)
158
+
159
+ def get_item(self, sequence: bytes) -> bytes:
160
+ """
161
+ Get fold results for a specified sequence.
162
+
163
+ Parameters
164
+ ----------
165
+ sequence : bytes
166
+ Sequence to fetch results for.
167
+
168
+ Returns
169
+ -------
170
+ bytes
171
+ Fold result for the specified sequence.
172
+ """
173
+ data = api.fold_get_sequence_result(self.session, self.job.job_id, sequence)
174
+ return data
175
+
176
+
177
+ class FoldComplexResultFuture(Future):
178
+ """
179
+ Future for manipulating results of a fold complex request.
180
+
181
+ Attributes
182
+ ----------
183
+ job : FoldJob
184
+ The fold job associated with this future.
185
+ """
186
+
187
+ job: FoldJob
188
+
189
+ def __init__(
190
+ self,
191
+ session: APISession,
192
+ job: FoldJob,
193
+ model_id: str | None = None,
194
+ proteins: list[Protein] | None = None,
195
+ ligands: list[Ligand] | None = None,
196
+ dnas: list[DNA] | None = None,
197
+ rnas: list[RNA] | None = None,
198
+ ):
199
+ """
200
+ Initialize a FoldComplexResultFuture instance.
201
+
202
+ Parameters
203
+ ----------
204
+ session : APISession
205
+ The API session to use for requests.
206
+ job : FoldJob
207
+ The fold job associated with this future.
208
+ model_id : str, optional
209
+ Model ID used for the fold request.
210
+ proteins : list[Protein], optional
211
+ List of proteins submitted for fold request.
212
+ ligands : list[Ligand], optional
213
+ List of ligands submitted for fold request.
214
+ dnas : list[DNA], optional
215
+ List of DNAs submitted for fold request.
216
+ rnas : list[RNA], optional
217
+ List of RNAs submitted for fold request.
218
+ """
219
+ super().__init__(session, job)
220
+ self._model_id = model_id
221
+ self._proteins = proteins
222
+ self._ligands = ligands
223
+ self._dnas = dnas
224
+ self._rnas = rnas
225
+ self._initialized = not (proteins == ligands == dnas == rnas == None)
226
+ self._pae: np.ndarray | None = None
227
+ self._pde: np.ndarray | None = None
228
+ self._plddt: np.ndarray | None = None
229
+ self._confidence: list["BoltzConfidence"] | None = None
230
+ self._affinity: "BoltzAffinity | None" = None
231
+
232
+ @property
233
+ def model_id(self) -> str:
234
+ """
235
+ Get the model ID used for the fold request.
236
+
237
+ Returns
238
+ -------
239
+ str
240
+ Model ID.
241
+ """
242
+ if self._model_id is None:
243
+ self._model_id = api.fold_get(
244
+ session=self.session, job_id=self.job.job_id
245
+ ).model_id
246
+ return self._model_id
247
+
248
+ def __get_chains(self):
249
+ """
250
+ Internal method to initialize chain objects (proteins, dnas, rnas, ligands)
251
+ from the fold job arguments.
252
+ """
253
+ args = api.fold_get(session=self.session, job_id=self.job.job_id).args
254
+ assert args is not None and "sequences" in args
255
+ for chain in args["sequences"]:
256
+ assert isinstance(chain, dict)
257
+ for chain_type, chain_info in chain:
258
+ if chain_type == "protein":
259
+ self._proteins = self._proteins or []
260
+ protein = Protein(sequence=chain_info["sequence"])
261
+ protein.chain_id = chain_info.get("id")
262
+ protein.msa = chain_info.get("msa_id")
263
+ self._proteins.append(protein)
264
+ elif chain_type == "dna":
265
+ self._dnas = self._dnas or []
266
+ dna = DNA(sequence=chain_info["sequence"])
267
+ dna.chain_id = chain_info.get("id")
268
+ self._dnas.append(dna)
269
+ elif chain_type == "rna":
270
+ self._rnas = self._rnas or []
271
+ rna = RNA(sequence=chain_info["sequence"])
272
+ rna.chain_id = chain_info.get("id")
273
+ self._rnas.append(rna)
274
+ elif chain_type == "ligand":
275
+ self._ligands = self._ligands or []
276
+ ligand = Ligand(
277
+ chain_id=chain_info.get("id"),
278
+ ccd=chain_info.get("ccd"),
279
+ smiles=chain_info.get("smiles"),
280
+ )
281
+ self._ligands.append(ligand)
282
+ else:
283
+ pass
284
+ self._initialized = True
285
+
286
+ @property
287
+ def proteins(self) -> list[Protein] | None:
288
+ """
289
+ Get the proteins submitted for the fold request.
290
+
291
+ Returns
292
+ -------
293
+ list[Protein] or None
294
+ List of Protein objects or None.
295
+ """
296
+ if not self._initialized:
297
+ self.__get_chains()
298
+ return self._proteins
299
+
300
+ @property
301
+ def dnas(self) -> list[DNA] | None:
302
+ """
303
+ Get the DNAs submitted for the fold request.
304
+
305
+ Returns
306
+ -------
307
+ list[DNA] or None
308
+ List of DNA objects or None.
309
+ """
310
+ if not self._initialized:
311
+ self.__get_chains()
312
+ return self._dnas
313
+
314
+ @property
315
+ def rnas(self) -> list[RNA] | None:
316
+ """
317
+ Get the RNAs submitted for the fold request.
318
+
319
+ Returns
320
+ -------
321
+ list[RNA] or None
322
+ List of RNA objects or None.
323
+ """
324
+ if not self._initialized:
325
+ self.__get_chains()
326
+ return self._rnas
327
+
328
+ @property
329
+ def ligands(self) -> list[Ligand] | None:
330
+ """
331
+ Get the ligands submitted for the fold request.
332
+
333
+ Returns
334
+ -------
335
+ list[Ligand] or None
336
+ List of Ligand objects or None.
337
+ """
338
+ if not self._initialized:
339
+ self.__get_chains()
340
+ return self._ligands
341
+
342
+ @property
343
+ def pae(self) -> np.ndarray:
344
+ """
345
+ Get the Predicted Aligned Error (PAE) matrix.
346
+
347
+ Returns
348
+ -------
349
+ np.ndarray
350
+ PAE matrix.
351
+
352
+ Raises
353
+ ------
354
+ AttributeError
355
+ If PAE is not supported for the model.
356
+ """
357
+ if self.model_id not in {"boltz-1", "boltz-1x", "boltz-2"}:
358
+ raise AttributeError("pae not supported for non-Boltz model")
359
+ if self._pae is None:
360
+ pae = api.fold_get_complex_extra_result(
361
+ session=self.session, job_id=self.job.job_id, key="pae"
362
+ )
363
+ assert isinstance(pae, np.ndarray)
364
+ self._pae = pae
365
+ return self._pae
366
+
367
+ @property
368
+ def pde(self) -> np.ndarray:
369
+ """
370
+ Get the Predicted Distance Error (PDE) matrix.
371
+
372
+ Returns
373
+ -------
374
+ np.ndarray
375
+ PDE matrix.
376
+
377
+ Raises
378
+ ------
379
+ AttributeError
380
+ If PDE is not supported for the model.
381
+ """
382
+ if self.model_id not in {"boltz-1", "boltz-1x", "boltz-2"}:
383
+ raise AttributeError("pde not supported for non-Boltz model")
384
+ if self._pde is None:
385
+ pde = api.fold_get_complex_extra_result(
386
+ session=self.session, job_id=self.job.job_id, key="pde"
387
+ )
388
+ assert isinstance(pde, np.ndarray)
389
+ self._pde = pde
390
+ return self._pde
391
+
392
+ @property
393
+ def plddt(self) -> np.ndarray:
394
+ """
395
+ Get the Predicted Local Distance Difference Test (pLDDT) scores.
396
+
397
+ Returns
398
+ -------
399
+ np.ndarray
400
+ pLDDT scores.
401
+
402
+ Raises
403
+ ------
404
+ AttributeError
405
+ If pLDDT is not supported for the model.
406
+ """
407
+ if self.model_id not in {"boltz-1", "boltz-1x", "boltz-2"}:
408
+ raise AttributeError("plddt not supported for non-Boltz model")
409
+ if self._plddt is None:
410
+ plddt = api.fold_get_complex_extra_result(
411
+ session=self.session, job_id=self.job.job_id, key="plddt"
412
+ )
413
+ assert isinstance(plddt, np.ndarray)
414
+ self._plddt = plddt
415
+ return self._plddt
416
+
417
+ @property
418
+ def confidence(self) -> list["BoltzConfidence"]:
419
+ """
420
+ Retrieve the confidences of the structure prediction.
421
+
422
+ Note
423
+ ----
424
+ This is only currently supported for Boltz models.
425
+
426
+ Returns
427
+ -------
428
+ list[BoltzConfidence]
429
+ List of BoltzConfidence objects.
430
+
431
+ Raises
432
+ ------
433
+ AttributeError
434
+ If confidence is not supported for the model.
435
+ """
436
+ if self.model_id not in {"boltz-1", "boltz-1x", "boltz-2"}:
437
+ raise AttributeError("confidence not supported for non-Boltz model")
438
+ if self._confidence is None:
439
+ confidence = api.fold_get_complex_extra_result(
440
+ session=self.session, job_id=self.job.job_id, key="confidence"
441
+ )
442
+ assert isinstance(confidence, list)
443
+ self._confidence = TypeAdapter(list[BoltzConfidence]).validate_python(
444
+ confidence
445
+ )
446
+ return self._confidence
447
+
448
+ @property
449
+ def affinity(self) -> "BoltzAffinity":
450
+ """
451
+ Retrieve the predicted binding affinities.
452
+
453
+ Note
454
+ ----
455
+ This is only currently supported for Boltz models.
456
+
457
+ Returns
458
+ -------
459
+ BoltzAffinity
460
+ BoltzAffinity object containing the predicted affinities.
461
+
462
+ Raises
463
+ ------
464
+ AttributeError
465
+ If affinity is not supported for the model.
466
+ """
467
+ if self.model_id not in {"boltz-1", "boltz-1x", "boltz-2"}:
468
+ raise AttributeError("affinity not supported for non-Boltz model")
469
+ if self._affinity is None:
470
+ affinity = api.fold_get_complex_extra_result(
471
+ session=self.session, job_id=self.job.job_id, key="affinity"
472
+ )
473
+ assert isinstance(affinity, dict)
474
+ self._affinity = BoltzAffinity.parse_obj_with_models(affinity)
475
+ return self._affinity
476
+
477
+ @property
478
+ def id(self):
479
+ """
480
+ Get the ID of the fold request.
481
+
482
+ Returns
483
+ -------
484
+ str
485
+ Fold job ID.
486
+ """
487
+ return self.job.job_id
488
+
489
+ def get(self, format: Literal["pdb", "mmcif"] = "mmcif", verbose=False) -> bytes:
490
+ """
491
+ Retrieve the fold results as a single bytestring.
492
+
493
+ Defaults to mmCIF for complexes. Additional predicted properties like plddt and pae should be accessed from their respective properties, i.e. `.plddt` and `.pae`.
494
+
495
+ Parameters
496
+ ----------
497
+ format : {'pdb', 'mmcif'}, optional
498
+ Output format. Default is 'mmcif'.
499
+ verbose : bool, optional
500
+ If True, print verbose output. Default is False.
501
+
502
+ Returns
503
+ -------
504
+ bytes
505
+ Fold result as a bytestring.
506
+ """
507
+ return api.fold_get_complex_result(
508
+ session=self.session, job_id=self.id, format=format
509
+ )