tab_1_model_comparison (#2)
Browse files- add multiple model predictions (a444e2745bd3207fdd514b2546b8249a9d67b96b)
Co-authored-by: Achille Soulie <[email protected]>
- .gitignore +3 -2
- Dockerfile +16 -17
- Makefile +1 -1
- app.py +0 -213
- folding_studio_demo/__init__.py +12 -0
- folding_studio_demo/app.py +167 -0
- folding_studio_demo/model_fasta_validators.py +367 -0
- molecule.py → folding_studio_demo/molecule.py +7 -9
- folding_studio_demo/predict.py +279 -0
- folding_studio_demo/protein.py +0 -0
- pyproject.toml +14 -1
- uv.lock +375 -47
.gitignore
CHANGED
@@ -1,3 +1,4 @@
|
|
1 |
.envrc
|
2 |
-
|
3 |
-
|
|
|
|
1 |
.envrc
|
2 |
+
__pycache__/
|
3 |
+
output/
|
4 |
+
sequences/
|
Dockerfile
CHANGED
@@ -8,7 +8,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
8 |
wget build-essential git && \
|
9 |
rm -rf /var/lib/apt/lists/*
|
10 |
|
11 |
-
COPY --from=uv-fetcher /uv /uvx /
|
12 |
|
13 |
ENV VIRTUAL_ENV=/modules/.venv
|
14 |
RUN uv venv "$VIRTUAL_ENV" && . "$VIRTUAL_ENV/bin/activate"
|
@@ -17,34 +17,33 @@ ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
|
17 |
# Set the working directory to the user's home directory
|
18 |
WORKDIR /app
|
19 |
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
-
RUN uv sync --
|
23 |
|
24 |
-
COPY
|
25 |
-
RUN
|
26 |
|
27 |
-
COPY app.py /app/app.py
|
28 |
-
COPY molecule.py /app/molecule.py
|
29 |
# COPY pred.cif /app/boltz_results/pred_model_0.cif
|
30 |
# COPY plddt_0.npz /app/boltz_results/plddt_0.npz
|
31 |
|
32 |
EXPOSE 7860
|
33 |
ENV GRADIO_SERVER_NAME="0.0.0.0"
|
34 |
-
ARG FOLDING_PROJECT_CODE
|
35 |
ENV FOLDING_PROJECT_CODE=$FOLDING_PROJECT_CODE
|
36 |
|
37 |
# Create directory for HTML output
|
38 |
-
RUN mkdir -p
|
39 |
|
40 |
# Create volume for HTML output
|
41 |
VOLUME ["/app/output/html"]
|
42 |
|
43 |
-
|
44 |
-
RUN useradd -m -u 1000 appuser && \
|
45 |
-
chown -R appuser:appuser /app
|
46 |
-
|
47 |
-
# Switch to non-root user
|
48 |
-
USER appuser
|
49 |
-
|
50 |
-
CMD ["python3", "app.py"]
|
|
|
8 |
wget build-essential git && \
|
9 |
rm -rf /var/lib/apt/lists/*
|
10 |
|
11 |
+
COPY --from=uv-fetcher /uv /uvx /bin/
|
12 |
|
13 |
ENV VIRTUAL_ENV=/modules/.venv
|
14 |
RUN uv venv "$VIRTUAL_ENV" && . "$VIRTUAL_ENV/bin/activate"
|
|
|
17 |
# Set the working directory to the user's home directory
|
18 |
WORKDIR /app
|
19 |
|
20 |
+
# Create a non-root user and set permissions
|
21 |
+
RUN useradd -m -u 1000 appuser && \
|
22 |
+
chown -R appuser:appuser /app
|
23 |
+
|
24 |
+
# Switch to non-root user
|
25 |
+
USER appuser
|
26 |
+
|
27 |
+
COPY pyproject.toml uv.lock README.md ./
|
28 |
+
COPY folding-studio ./folding-studio
|
29 |
|
30 |
+
RUN uv sync --locked --no-install-project --no-dev
|
31 |
|
32 |
+
COPY folding_studio_demo ./folding_studio_demo
|
33 |
+
RUN uv sync --locked --no-dev
|
34 |
|
|
|
|
|
35 |
# COPY pred.cif /app/boltz_results/pred_model_0.cif
|
36 |
# COPY plddt_0.npz /app/boltz_results/plddt_0.npz
|
37 |
|
38 |
EXPOSE 7860
|
39 |
ENV GRADIO_SERVER_NAME="0.0.0.0"
|
40 |
+
ARG FOLDING_PROJECT_CODE="folding_dev"
|
41 |
ENV FOLDING_PROJECT_CODE=$FOLDING_PROJECT_CODE
|
42 |
|
43 |
# Create directory for HTML output
|
44 |
+
RUN mkdir -p ./output/html
|
45 |
|
46 |
# Create volume for HTML output
|
47 |
VOLUME ["/app/output/html"]
|
48 |
|
49 |
+
CMD ["uv", "run", "folding-studio-demo"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Makefile
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
# Variables
|
2 |
-
DOCKER_IMAGE_NAME = folding-studio
|
3 |
DOCKER_IMAGE_TAG = latest
|
4 |
PORT = 7860
|
5 |
OUTPUT_DIR = ./output
|
|
|
1 |
# Variables
|
2 |
+
DOCKER_IMAGE_NAME = folding-studio-demo
|
3 |
DOCKER_IMAGE_TAG = latest
|
4 |
PORT = 7860
|
5 |
OUTPUT_DIR = ./output
|
app.py
DELETED
@@ -1,213 +0,0 @@
|
|
1 |
-
from Bio.PDB import MMCIFParser, PDBIO
|
2 |
-
from folding_studio.client import Client
|
3 |
-
from folding_studio.query.boltz import BoltzQuery, BoltzParameters
|
4 |
-
from pathlib import Path
|
5 |
-
import gradio as gr
|
6 |
-
import hashlib
|
7 |
-
import logging
|
8 |
-
import numpy as np
|
9 |
-
import os
|
10 |
-
import plotly.graph_objects as go
|
11 |
-
|
12 |
-
from molecule import molecule
|
13 |
-
|
14 |
-
# Configure logging
|
15 |
-
logging.basicConfig(
|
16 |
-
level=logging.INFO,
|
17 |
-
format='%(asctime)s - %(levelname)s - %(message)s',
|
18 |
-
handlers=[
|
19 |
-
logging.StreamHandler(),
|
20 |
-
]
|
21 |
-
)
|
22 |
-
logger = logging.getLogger(__name__)
|
23 |
-
|
24 |
-
|
25 |
-
def convert_cif_to_pdb(cif_path: str, pdb_path: str) -> None:
|
26 |
-
"""Convert a .cif file to .pdb format using Biopython.
|
27 |
-
|
28 |
-
Args:
|
29 |
-
cif_path (str): Path to input .cif file
|
30 |
-
pdb_path (str): Path to output .pdb file
|
31 |
-
"""
|
32 |
-
# Parse the CIF file
|
33 |
-
parser = MMCIFParser()
|
34 |
-
structure = parser.get_structure("structure", cif_path)
|
35 |
-
|
36 |
-
# Save as PDB
|
37 |
-
io = PDBIO()
|
38 |
-
io.set_structure(structure)
|
39 |
-
io.save(pdb_path)
|
40 |
-
|
41 |
-
def call_boltz(seq_file: Path | str, api_key: str, output_dir: Path) -> None:
|
42 |
-
"""Call Boltz prediction."""
|
43 |
-
# Initialize parameters with CLI-provided values
|
44 |
-
parameters = {
|
45 |
-
"recycling_steps": 3,
|
46 |
-
"sampling_steps": 200,
|
47 |
-
"diffusion_samples": 1,
|
48 |
-
"step_scale": 1.638,
|
49 |
-
"msa_pairing_strategy": "greedy",
|
50 |
-
"write_full_pae": False,
|
51 |
-
"write_full_pde": False,
|
52 |
-
"use_msa_server": True,
|
53 |
-
"seed": 0,
|
54 |
-
"custom_msa_paths": None,
|
55 |
-
}
|
56 |
-
|
57 |
-
# Create a client using API key
|
58 |
-
logger.info("Authenticating client with API key")
|
59 |
-
client = Client.from_api_key(api_key=api_key)
|
60 |
-
|
61 |
-
# Define query
|
62 |
-
seq_file = Path(seq_file)
|
63 |
-
query = BoltzQuery.from_file(seq_file, query_name="gradio", parameters=BoltzParameters(**parameters))
|
64 |
-
query.save_parameters(output_dir)
|
65 |
-
|
66 |
-
logger.info("Payload: %s", query.payload)
|
67 |
-
|
68 |
-
# Send a request
|
69 |
-
logger.info("Sending request to Folding Studio API")
|
70 |
-
response = client.send_request(query, project_code=os.environ["FOLDING_PROJECT_CODE"])
|
71 |
-
|
72 |
-
# Access confidence data
|
73 |
-
logger.info("Confidence data: %s", response.confidence_data)
|
74 |
-
|
75 |
-
response.download_results(output_dir=output_dir, force=True, unzip=True)
|
76 |
-
logger.info("Results downloaded to %s", output_dir)
|
77 |
-
|
78 |
-
|
79 |
-
def predict(sequence: str, api_key: str) -> str:
|
80 |
-
"""Predict protein structure from amino acid sequence using Boltz model.
|
81 |
-
|
82 |
-
Args:
|
83 |
-
sequence (str): Amino acid sequence to predict structure for
|
84 |
-
api_key (str): Folding API key
|
85 |
-
|
86 |
-
Returns:
|
87 |
-
str: HTML iframe containing 3D molecular visualization
|
88 |
-
"""
|
89 |
-
|
90 |
-
# Set up unique output directory based on sequence hash
|
91 |
-
seq_id = hashlib.sha1(sequence.encode()).hexdigest()
|
92 |
-
seq_file = Path(f"sequence_{seq_id}.fasta")
|
93 |
-
_write_fasta_file(seq_file, sequence)
|
94 |
-
output_dir = Path(f"sequence_{seq_id}")
|
95 |
-
output_dir.mkdir(parents=True, exist_ok=True)
|
96 |
-
|
97 |
-
# Check if prediction already exists
|
98 |
-
pred_cif = list(output_dir.rglob("*_model_0.cif"))
|
99 |
-
if not pred_cif:
|
100 |
-
# Run Boltz prediction
|
101 |
-
logger.info(f"Predicting {seq_file.stem}")
|
102 |
-
call_boltz(seq_file=seq_file, api_key=api_key, output_dir=output_dir)
|
103 |
-
logger.info("Prediction done. Output directory: %s", output_dir)
|
104 |
-
else:
|
105 |
-
logger.info("Prediction already exists. Output directory: %s", output_dir)
|
106 |
-
|
107 |
-
# output_dir = Path("boltz_results") # debug
|
108 |
-
# Convert output CIF to PDB
|
109 |
-
pred_cif = list(output_dir.rglob("*_model_0.cif"))[0]
|
110 |
-
logger.info("Output file: %s", pred_cif)
|
111 |
-
|
112 |
-
converted_pdb_path = str(output_dir / "pred.pdb")
|
113 |
-
convert_cif_to_pdb(str(pred_cif), str(converted_pdb_path))
|
114 |
-
logger.info("Converted PDB file: %s", converted_pdb_path)
|
115 |
-
|
116 |
-
|
117 |
-
# Generate molecular visualization
|
118 |
-
mol = _create_molecule_visualization(
|
119 |
-
converted_pdb_path,
|
120 |
-
sequence,
|
121 |
-
)
|
122 |
-
|
123 |
-
plddt_file = list(pred_cif.parent.glob("plddt_*.npz"))[0]
|
124 |
-
logger.info("plddt file: %s", plddt_file)
|
125 |
-
plddt_vals = np.load(plddt_file)["plddt"]
|
126 |
-
|
127 |
-
return _wrap_in_iframe(mol), add_plddt_plot(plddt_vals=plddt_vals)
|
128 |
-
|
129 |
-
|
130 |
-
def _write_fasta_file(filepath: Path, sequence: str) -> None:
|
131 |
-
"""Write sequence to FASTA file."""
|
132 |
-
with open(filepath, "w") as f:
|
133 |
-
f.write(f">A|protein\n{sequence}")
|
134 |
-
|
135 |
-
|
136 |
-
def _create_molecule_visualization(pdb_path: Path, sequence: str) -> str:
|
137 |
-
"""Create molecular visualization using molecule module."""
|
138 |
-
return molecule(
|
139 |
-
str(pdb_path),
|
140 |
-
lenSeqs=1,
|
141 |
-
num_res=len(sequence),
|
142 |
-
selectedResidues=list(range(1, len(sequence) + 1)),
|
143 |
-
allSeqs=[sequence],
|
144 |
-
sequences=[{
|
145 |
-
"Score": 0,
|
146 |
-
"RMSD": 0,
|
147 |
-
"Recovery": 0,
|
148 |
-
"Mean pLDDT": 0,
|
149 |
-
"seq": sequence
|
150 |
-
}],
|
151 |
-
)
|
152 |
-
|
153 |
-
|
154 |
-
def _wrap_in_iframe(content: str) -> str:
|
155 |
-
"""Wrap content in an HTML iframe with appropriate styling and permissions."""
|
156 |
-
return f"""<iframe
|
157 |
-
name="result"
|
158 |
-
style="width: 100%; height: 100vh;"
|
159 |
-
allow="midi; geolocation; microphone; camera; display-capture; encrypted-media;"
|
160 |
-
sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups allow-top-navigation-by-user-activation allow-downloads"
|
161 |
-
allowfullscreen=""
|
162 |
-
allowpaymentrequest=""
|
163 |
-
frameborder="0"
|
164 |
-
srcdoc='{content}'
|
165 |
-
></iframe>"""
|
166 |
-
|
167 |
-
def add_plddt_plot(plddt_vals: list[float]) -> str:
|
168 |
-
"""Create a plot of metrics."""
|
169 |
-
visible = True
|
170 |
-
plddt_trace = go.Scatter(
|
171 |
-
x=np.arange(len(plddt_vals)),
|
172 |
-
y=plddt_vals,
|
173 |
-
hovertemplate="<i>pLDDT</i>: %{y:.2f} <br><i>Residue index:</i> %{x}<br>",
|
174 |
-
name="seq",
|
175 |
-
visible=visible,
|
176 |
-
)
|
177 |
-
|
178 |
-
plddt_fig = go.Figure(data=[plddt_trace])
|
179 |
-
plddt_fig.update_layout(
|
180 |
-
title="pLDDT",
|
181 |
-
xaxis_title="Residue index",
|
182 |
-
yaxis_title="pLDDT",
|
183 |
-
height=500,
|
184 |
-
template="simple_white",
|
185 |
-
legend=dict(yanchor="bottom", y=0.01, xanchor="left", x=0.99),
|
186 |
-
)
|
187 |
-
return plddt_fig
|
188 |
-
|
189 |
-
demo = gr.Blocks(title="Folding Studio: structure prediction with Boltz-1")
|
190 |
-
|
191 |
-
with demo:
|
192 |
-
gr.Markdown("# Input")
|
193 |
-
with gr.Row():
|
194 |
-
with gr.Column():
|
195 |
-
sequence = gr.Textbox(label="Sequence", value="")
|
196 |
-
api_key = gr.Textbox(label="Folding API Key", type="password")
|
197 |
-
gr.Markdown("# Output")
|
198 |
-
with gr.Row():
|
199 |
-
predict_btn = gr.Button("Predict")
|
200 |
-
with gr.Row():
|
201 |
-
with gr.Column():
|
202 |
-
mol_output = gr.HTML()
|
203 |
-
with gr.Column():
|
204 |
-
metrics_plot = gr.Plot(label="pLDDT")
|
205 |
-
|
206 |
-
predict_btn.click(
|
207 |
-
fn=predict,
|
208 |
-
inputs=[sequence, api_key],
|
209 |
-
outputs=[mol_output, metrics_plot]
|
210 |
-
)
|
211 |
-
|
212 |
-
demo.launch()
|
213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
folding_studio_demo/__init__.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Folding Studio Demo."""
|
2 |
+
|
3 |
+
import logging
|
4 |
+
|
5 |
+
# Configure logging
|
6 |
+
logging.basicConfig(
|
7 |
+
level=logging.INFO,
|
8 |
+
format="%(asctime)s - %(levelname)s - %(message)s",
|
9 |
+
handlers=[
|
10 |
+
logging.StreamHandler(),
|
11 |
+
],
|
12 |
+
)
|
folding_studio_demo/app.py
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Folding Studio Demo App."""
|
2 |
+
|
3 |
+
import logging
|
4 |
+
|
5 |
+
import gradio as gr
|
6 |
+
from folding_studio_data_models import FoldingModel
|
7 |
+
from gradio_molecule3d import Molecule3D
|
8 |
+
|
9 |
+
from folding_studio_demo.predict import predict
|
10 |
+
|
11 |
+
logger = logging.getLogger(__name__)
|
12 |
+
|
13 |
+
|
14 |
+
MOLECULE_REPS = [
|
15 |
+
{
|
16 |
+
"model": 0,
|
17 |
+
"chain": "",
|
18 |
+
"resname": "",
|
19 |
+
"style": "cartoon",
|
20 |
+
"color": "alphafold",
|
21 |
+
# "residue_range": "",
|
22 |
+
"around": 0,
|
23 |
+
"byres": False,
|
24 |
+
# "visible": False,
|
25 |
+
# "opacity": 0.5
|
26 |
+
}
|
27 |
+
]
|
28 |
+
|
29 |
+
DEFAULT_PROTEIN_SEQ = ">protein description\nMALWMRLLPLLALLALWGPDPAAA"
|
30 |
+
|
31 |
+
MODEL_CHOICES = [
|
32 |
+
# ("AlphaFold2", FoldingModel.AF2),
|
33 |
+
# ("OpenFold", FoldingModel.OPENFOLD),
|
34 |
+
# ("SoloSeq", FoldingModel.SOLOSEQ),
|
35 |
+
("Boltz-1", FoldingModel.BOLTZ),
|
36 |
+
("Chai-1", FoldingModel.CHAI),
|
37 |
+
("Protenix", FoldingModel.PROTENIX),
|
38 |
+
]
|
39 |
+
|
40 |
+
|
41 |
+
def sequence_input() -> gr.Textbox:
|
42 |
+
"""Sequence input component.
|
43 |
+
|
44 |
+
Returns:
|
45 |
+
gr.Textbox: Sequence input component
|
46 |
+
"""
|
47 |
+
sequence = gr.Textbox(
|
48 |
+
label="Protein Sequence",
|
49 |
+
value=DEFAULT_PROTEIN_SEQ,
|
50 |
+
lines=2,
|
51 |
+
placeholder="Enter a protein sequence or upload a FASTA file",
|
52 |
+
)
|
53 |
+
file_input = gr.File(
|
54 |
+
label="Upload a FASTA file",
|
55 |
+
file_types=[".fasta", ".fa"],
|
56 |
+
)
|
57 |
+
|
58 |
+
def _process_file(file: gr.File | None) -> gr.Textbox:
|
59 |
+
if file is None:
|
60 |
+
return gr.Textbox()
|
61 |
+
try:
|
62 |
+
with open(file.name, "r") as f:
|
63 |
+
content = f.read().strip()
|
64 |
+
return gr.Textbox(value=content)
|
65 |
+
except Exception as e:
|
66 |
+
logger.error(f"Error reading file: {e}")
|
67 |
+
return gr.Textbox()
|
68 |
+
|
69 |
+
file_input.change(fn=_process_file, inputs=[file_input], outputs=[sequence])
|
70 |
+
return sequence
|
71 |
+
|
72 |
+
|
73 |
+
def simple_prediction(api_key: str) -> None:
|
74 |
+
"""Simple prediction tab.
|
75 |
+
|
76 |
+
Args:
|
77 |
+
api_key (str): Folding Studio API key
|
78 |
+
"""
|
79 |
+
gr.Markdown(
|
80 |
+
"""
|
81 |
+
### Predict a Protein Structure
|
82 |
+
|
83 |
+
It will be run in the background and the results will be displayed in the output section.
|
84 |
+
The output will contain the protein structure and the pLDDT plot.
|
85 |
+
|
86 |
+
Select a model to run the inference with and enter a protein sequence or upload a FASTA file.
|
87 |
+
"""
|
88 |
+
)
|
89 |
+
with gr.Row():
|
90 |
+
dropdown = gr.Dropdown(
|
91 |
+
label="Model",
|
92 |
+
choices=MODEL_CHOICES,
|
93 |
+
scale=0,
|
94 |
+
value=FoldingModel.BOLTZ,
|
95 |
+
)
|
96 |
+
with gr.Column():
|
97 |
+
sequence = sequence_input()
|
98 |
+
|
99 |
+
predict_btn = gr.Button("Predict")
|
100 |
+
|
101 |
+
with gr.Row():
|
102 |
+
mol_output = Molecule3D(label="Protein Structure", reps=MOLECULE_REPS)
|
103 |
+
metrics_plot = gr.Plot(label="pLDDT")
|
104 |
+
|
105 |
+
predict_btn.click(
|
106 |
+
fn=predict,
|
107 |
+
inputs=[sequence, api_key, dropdown],
|
108 |
+
outputs=[mol_output, metrics_plot],
|
109 |
+
)
|
110 |
+
|
111 |
+
|
112 |
+
def model_comparison(api_key: str) -> None:
|
113 |
+
"""Model comparison tab.
|
114 |
+
|
115 |
+
Args:
|
116 |
+
api_key (str): Folding Studio API key
|
117 |
+
"""
|
118 |
+
|
119 |
+
with gr.Row():
|
120 |
+
model = gr.Dropdown(
|
121 |
+
label="Model",
|
122 |
+
choices=MODEL_CHOICES,
|
123 |
+
multiselect=True,
|
124 |
+
scale=0,
|
125 |
+
min_width=300,
|
126 |
+
value=[FoldingModel.BOLTZ, FoldingModel.CHAI, FoldingModel.PROTENIX],
|
127 |
+
)
|
128 |
+
with gr.Column():
|
129 |
+
sequence = sequence_input()
|
130 |
+
|
131 |
+
predict_btn = gr.Button("Compare Models")
|
132 |
+
|
133 |
+
with gr.Row():
|
134 |
+
mol_output = Molecule3D(label="Protein Structure", reps=MOLECULE_REPS)
|
135 |
+
metrics_plot = gr.Plot(label="pLDDT")
|
136 |
+
|
137 |
+
predict_btn.click(
|
138 |
+
fn=predict,
|
139 |
+
inputs=[sequence, api_key, model],
|
140 |
+
outputs=[mol_output, metrics_plot],
|
141 |
+
)
|
142 |
+
|
143 |
+
|
144 |
+
def __main__():
|
145 |
+
with gr.Blocks(title="Folding Studio Demo") as demo:
|
146 |
+
gr.Markdown(
|
147 |
+
"""
|
148 |
+
# Folding Studio: Harness the Power of Protein Folding 🧬
|
149 |
+
|
150 |
+
Folding Studio is a platform for protein structure prediction.
|
151 |
+
It uses the latest AI-powered folding models to predict the structure of a protein.
|
152 |
+
|
153 |
+
Available models are : AlphaFold2, OpenFold, SoloSeq, Boltz-1, Chai and Protenix.
|
154 |
+
|
155 |
+
## API Key
|
156 |
+
To use the Folding Studio API, you need to provide an API key.
|
157 |
+
You can get your API key by asking to the Folding Studio team.
|
158 |
+
"""
|
159 |
+
)
|
160 |
+
api_key = gr.Textbox(label="Folding Studio API Key", type="password")
|
161 |
+
gr.Markdown("## Demo Usage")
|
162 |
+
with gr.Tab("🚀 Simple Prediction"):
|
163 |
+
simple_prediction(api_key)
|
164 |
+
with gr.Tab("📊 Model Comparison"):
|
165 |
+
model_comparison(api_key)
|
166 |
+
|
167 |
+
demo.launch()
|
folding_studio_demo/model_fasta_validators.py
ADDED
@@ -0,0 +1,367 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Utils for validating the FASTA files for the AF3 like models."""
|
2 |
+
|
3 |
+
import logging
|
4 |
+
import re
|
5 |
+
import shutil
|
6 |
+
from abc import abstractmethod
|
7 |
+
from collections import defaultdict
|
8 |
+
from enum import Enum
|
9 |
+
from pathlib import Path
|
10 |
+
|
11 |
+
from Bio import SeqIO
|
12 |
+
|
13 |
+
|
14 |
+
class EntityType(str, Enum):
|
15 |
+
"""Enum for the entity type of a given sequence."""
|
16 |
+
|
17 |
+
DNA = "dna"
|
18 |
+
RNA = "rna"
|
19 |
+
PROTEIN = "protein"
|
20 |
+
PEPTIDE = "peptide"
|
21 |
+
ION = "ion"
|
22 |
+
LIGAND = "ligand"
|
23 |
+
SMILES = "smiles"
|
24 |
+
CCD = "ccd"
|
25 |
+
|
26 |
+
|
27 |
+
def get_entity_type(sequence: str) -> EntityType:
|
28 |
+
"""Get the entity type of a given sequence.
|
29 |
+
|
30 |
+
The entity type is determined based on the sequence composition.
|
31 |
+
|
32 |
+
Args:
|
33 |
+
sequence (str): The input sequence.
|
34 |
+
|
35 |
+
Returns:
|
36 |
+
EntityType: The entity type of the input sequence.
|
37 |
+
"""
|
38 |
+
DNA_SEQUENCE_SET = set("ACGT")
|
39 |
+
RNA_SEQUENCE_SET = set("ACGU")
|
40 |
+
PROTEIN_SEQUENCE_SET = set("ACDEFGHIKLMNPQRSTVWY")
|
41 |
+
|
42 |
+
# Detect IONS (e.g., Mg2+, Na+, Cl-)
|
43 |
+
if re.fullmatch(r"[A-Za-z]{1,2}[\d\+\-]*", sequence):
|
44 |
+
return EntityType.ION
|
45 |
+
|
46 |
+
# Detect DNA
|
47 |
+
if set(sequence.upper()).issubset(DNA_SEQUENCE_SET):
|
48 |
+
return EntityType.DNA
|
49 |
+
|
50 |
+
# Detect RNA
|
51 |
+
elif set(sequence.upper()).issubset(RNA_SEQUENCE_SET):
|
52 |
+
return EntityType.RNA
|
53 |
+
|
54 |
+
# Detect PROTEIN
|
55 |
+
elif set(sequence.upper()).issubset(PROTEIN_SEQUENCE_SET):
|
56 |
+
return EntityType.PROTEIN
|
57 |
+
|
58 |
+
# Default to LIGAND
|
59 |
+
return EntityType.LIGAND
|
60 |
+
|
61 |
+
|
62 |
+
def has_multiple_chains(header: str) -> bool:
|
63 |
+
"""Check if a given header contains multiple chains in RCSB format.
|
64 |
+
|
65 |
+
A header with multiple chains will have the following in the header:
|
66 |
+
```
|
67 |
+
Chains A, B, C, ...
|
68 |
+
```
|
69 |
+
where `A`, `B`, `C`, ... are the chain identifiers.
|
70 |
+
|
71 |
+
|
72 |
+
Args:
|
73 |
+
header (str): The input header string containing chain information.
|
74 |
+
|
75 |
+
Returns:
|
76 |
+
bool: True if the header contains multiple chains, False otherwise.
|
77 |
+
"""
|
78 |
+
match = re.search(r"chains?\s+([A-Za-z, ]+)", header, re.I)
|
79 |
+
return len(match.group(1).replace(" ", "").split(",")) > 1 if match else False
|
80 |
+
|
81 |
+
|
82 |
+
class BaseFastaValidator:
|
83 |
+
"""Base class for validating FASTA files."""
|
84 |
+
|
85 |
+
@abstractmethod
|
86 |
+
def is_valid_fasta(self, fasta_path: Path) -> tuple[bool, str | None]:
|
87 |
+
"""Validate whether a given FASTA file follows the required format.
|
88 |
+
|
89 |
+
Args:
|
90 |
+
fasta_path (Path): Path to the FASTA file.
|
91 |
+
|
92 |
+
Returns:
|
93 |
+
tuple[bool, str | None]: Tuple containing a boolean indicating if the format is correct and an error message if not
|
94 |
+
"""
|
95 |
+
raise NotImplementedError("Subclasses must implement this method")
|
96 |
+
|
97 |
+
@abstractmethod
|
98 |
+
def transform_fasta(self, fasta_path: Path) -> str:
|
99 |
+
"""Transform a FASTA file into the required format.
|
100 |
+
|
101 |
+
Args:
|
102 |
+
fasta_path (Path): Path to the FASTA file.
|
103 |
+
|
104 |
+
Returns:
|
105 |
+
Transformed FASTA content in the required format.
|
106 |
+
"""
|
107 |
+
raise NotImplementedError("Subclasses must implement this method")
|
108 |
+
|
109 |
+
def process_directory(self, input_dir: str, output_dir: str) -> None:
|
110 |
+
"""Process all FASTA files in the input directory, validate or transform them, and save them to the output directory.
|
111 |
+
|
112 |
+
Args:
|
113 |
+
input_dir (str): Path to the directory containing FASTA files.
|
114 |
+
output_dir (str): Path to the output directory where processed files will be saved.
|
115 |
+
"""
|
116 |
+
|
117 |
+
output_path = Path(output_dir)
|
118 |
+
output_path.mkdir(parents=True, exist_ok=True)
|
119 |
+
|
120 |
+
for fasta_file in Path(input_dir).glob("*.fasta"):
|
121 |
+
output_file = output_path / fasta_file.name
|
122 |
+
if has_multiple_chains(fasta_file.read_text()):
|
123 |
+
logging.warning(
|
124 |
+
f"Skipping {fasta_file} because it contains multiple chains in a single sequence.\n"
|
125 |
+
"Please split multiple chains into separate sequences using the following format:\n"
|
126 |
+
">Chain A\n"
|
127 |
+
"MTEIVLKFL...\n"
|
128 |
+
">Chain B\n"
|
129 |
+
"MTEIVLKFL...\n\n"
|
130 |
+
"Instead of:\n"
|
131 |
+
">Chains A, B\n"
|
132 |
+
"MTEIVLKFL..."
|
133 |
+
)
|
134 |
+
continue
|
135 |
+
if self.is_valid_fasta(fasta_file):
|
136 |
+
shutil.copy(fasta_file, output_file)
|
137 |
+
else:
|
138 |
+
transformed_content = self.transform_fasta(fasta_file)
|
139 |
+
output_file.write_text(transformed_content)
|
140 |
+
|
141 |
+
|
142 |
+
class BoltzFastaValidator(BaseFastaValidator):
|
143 |
+
"""Validate whether a given FASTA file follows the required format for Boltz."""
|
144 |
+
|
145 |
+
SUPPORTED_ENTITY_TYPES = {
|
146 |
+
EntityType.PROTEIN,
|
147 |
+
EntityType.RNA,
|
148 |
+
EntityType.DNA,
|
149 |
+
EntityType.SMILES,
|
150 |
+
EntityType.CCD,
|
151 |
+
}
|
152 |
+
|
153 |
+
def is_valid_fasta(self, fasta_path: Path) -> tuple[bool, str | None]:
|
154 |
+
"""Validate whether a given FASTA file follows the required format.
|
155 |
+
|
156 |
+
The expected FASTA header format is:
|
157 |
+
```
|
158 |
+
>CHAIN_ID|ENTITY_TYPE
|
159 |
+
```
|
160 |
+
where `ENTITY_TYPE` must be one of: "protein", "rna", "dna", "smiles" or "ccd".
|
161 |
+
|
162 |
+
Args:
|
163 |
+
fasta_path (Path): Path to the FASTA file.
|
164 |
+
|
165 |
+
Returns:
|
166 |
+
tuple[bool, str | None]: Tuple containing a boolean indicating if the format is correct and an error message if not
|
167 |
+
"""
|
168 |
+
with fasta_path.open("r") as f:
|
169 |
+
for record in SeqIO.parse(f, "fasta"):
|
170 |
+
header_parts = record.id.split("|")
|
171 |
+
if not (1 < len(header_parts) <= 3):
|
172 |
+
msg = "BOLTZ Validation Error: Invalid header format. Expected '>CHAIN_ID|ENTITY_TYPE'"
|
173 |
+
return False, msg
|
174 |
+
if header_parts[1].lower() not in self.SUPPORTED_ENTITY_TYPES:
|
175 |
+
return (
|
176 |
+
False,
|
177 |
+
f"BOLTZ Validation Error: Invalid entity type '{header_parts[1]}'. Supported types: {', '.join(self.SUPPORTED_ENTITY_TYPES)}",
|
178 |
+
)
|
179 |
+
return True, None
|
180 |
+
|
181 |
+
def transform_fasta(self, fasta_path: Path) -> str:
|
182 |
+
"""Transform a FASTA file into the '>CHAIN_ID|ENTITY_TYPE|MSA_ID' format.
|
183 |
+
|
184 |
+
This function extracts chain identifiers from the FASTA header and determines
|
185 |
+
the entity type (DNA, RNA, or PROTEIN) based on the sequence composition.
|
186 |
+
|
187 |
+
Args:
|
188 |
+
fasta_path (Path): Path to the FASTA file.
|
189 |
+
|
190 |
+
Returns:
|
191 |
+
Transformed FASTA content in the required format.
|
192 |
+
"""
|
193 |
+
transformed_lines = []
|
194 |
+
|
195 |
+
with fasta_path.open("r") as f:
|
196 |
+
for record_index, record in enumerate(SeqIO.parse(f, "fasta")):
|
197 |
+
chain = chr(ord("A") + record_index)
|
198 |
+
# extract entity type
|
199 |
+
entity_type = get_entity_type(str(record.seq))
|
200 |
+
transformed_lines.append(f">{chain.upper()}|{entity_type.value}")
|
201 |
+
# append sequence
|
202 |
+
transformed_lines.append(str(record.seq))
|
203 |
+
|
204 |
+
return "\n".join(transformed_lines)
|
205 |
+
|
206 |
+
|
207 |
+
class ChaiFastaValidator(BaseFastaValidator):
|
208 |
+
"""Validate whether a given FASTA file follows the required format for Chai."""
|
209 |
+
|
210 |
+
SUPPORTED_ENTITY_TYPES = EntityType.__members__.values()
|
211 |
+
|
212 |
+
def is_valid_fasta(self, fasta_path: Path) -> tuple[bool, str | None]:
|
213 |
+
"""Validate whether a given FASTA file follows the required format.
|
214 |
+
|
215 |
+
The expected FASTA header format is:
|
216 |
+
```
|
217 |
+
>ENTITY_TYPE|name=NAME
|
218 |
+
```
|
219 |
+
Args:
|
220 |
+
fasta_path (Path): Path to the FASTA file.
|
221 |
+
|
222 |
+
Returns:
|
223 |
+
tuple[bool, str | None]: Tuple containing a boolean indicating if the format is correct and an error message if not
|
224 |
+
"""
|
225 |
+
|
226 |
+
seen_names = set()
|
227 |
+
with fasta_path.open("r") as f:
|
228 |
+
for record in SeqIO.parse(f, "fasta"):
|
229 |
+
# validate header format
|
230 |
+
match = re.match(r"^([A-Za-z]+)\|name=([\w\-]+)$", record.description)
|
231 |
+
if not match:
|
232 |
+
return (
|
233 |
+
False,
|
234 |
+
"CHAI Validation Error: Invalid header format. Expected '>ENTITY_TYPE|name=NAME'",
|
235 |
+
)
|
236 |
+
# validate entity type
|
237 |
+
entity_type, name = match.groups()
|
238 |
+
if entity_type not in self.SUPPORTED_ENTITY_TYPES or not name:
|
239 |
+
return (
|
240 |
+
False,
|
241 |
+
f"CHAI Validation Error: Invalid entity type '{entity_type}'. Supported types: {', '.join(self.SUPPORTED_ENTITY_TYPES)}",
|
242 |
+
)
|
243 |
+
# check uniqueness of name
|
244 |
+
if name in seen_names:
|
245 |
+
return (
|
246 |
+
False,
|
247 |
+
f"CHAI Validation Error: Duplicate name '{name}'. Each sequence must have a unique name",
|
248 |
+
)
|
249 |
+
seen_names.add(name)
|
250 |
+
# validate sequence format
|
251 |
+
sequence = str(record.seq).strip()
|
252 |
+
if (
|
253 |
+
entity_type in {EntityType.PEPTIDE, EntityType.PROTEIN}
|
254 |
+
and not get_entity_type(sequence) == entity_type
|
255 |
+
):
|
256 |
+
return (
|
257 |
+
False,
|
258 |
+
f"CHAI Validation Error: Sequence type mismatch. Expected '{entity_type}' but found '{get_entity_type(sequence)}'",
|
259 |
+
)
|
260 |
+
|
261 |
+
return True, None
|
262 |
+
|
263 |
+
def transform_fasta(self, fasta_path: Path) -> str:
|
264 |
+
"""Transform a FASTA file into the '>TYPE|name=NAME' format by ensuring each main header
|
265 |
+
is unique (adding a number if necessary).
|
266 |
+
|
267 |
+
The expected output format is:
|
268 |
+
'>protein|name=NAME'
|
269 |
+
'SEQUENCE'
|
270 |
+
|
271 |
+
Args:
|
272 |
+
fasta_path (Path): Path to the FASTA file.
|
273 |
+
|
274 |
+
Returns:
|
275 |
+
Transformed FASTA content in the required Chai format.
|
276 |
+
"""
|
277 |
+
transformed_lines = []
|
278 |
+
header_map = {}
|
279 |
+
|
280 |
+
with fasta_path.open("r") as f:
|
281 |
+
for record in SeqIO.parse(f, "fasta"):
|
282 |
+
main_header = record.description.split("|")[0].strip()
|
283 |
+
|
284 |
+
if main_header not in header_map:
|
285 |
+
header_map[main_header] = 1
|
286 |
+
updated_header = main_header
|
287 |
+
|
288 |
+
else:
|
289 |
+
header_map[main_header] += 1
|
290 |
+
updated_header = main_header + "_" + str(header_map[main_header])
|
291 |
+
|
292 |
+
entity_type = get_entity_type(str(record.seq))
|
293 |
+
header = f">{entity_type.value}|name={updated_header}"
|
294 |
+
|
295 |
+
transformed_lines.append(header)
|
296 |
+
transformed_lines.append(str(record.seq))
|
297 |
+
|
298 |
+
return "\n".join(transformed_lines)
|
299 |
+
|
300 |
+
|
301 |
+
class ProtenixFastaValidator(BaseFastaValidator):
|
302 |
+
"""Validate whether a given FASTA file follows the required format for Protenix."""
|
303 |
+
|
304 |
+
def is_valid_fasta(self, fasta_path: Path) -> tuple[bool, str | None]:
|
305 |
+
"""Validate whether a given FASTA file follows the required format.
|
306 |
+
|
307 |
+
The expected FASTA header format is:
|
308 |
+
```
|
309 |
+
> UNIQUE ID[|...]
|
310 |
+
```
|
311 |
+
|
312 |
+
Args:
|
313 |
+
fasta_path (Path): Path to the FASTA file.
|
314 |
+
|
315 |
+
Returns:
|
316 |
+
tuple[bool, str | None]: Tuple containing a boolean indicating if the format is correct and an error message if not
|
317 |
+
"""
|
318 |
+
seen_headers = set()
|
319 |
+
|
320 |
+
with fasta_path.open("r") as f:
|
321 |
+
for record in SeqIO.parse(f, "fasta"):
|
322 |
+
main_header = record.description.split("|")[0].strip()
|
323 |
+
if main_header in seen_headers:
|
324 |
+
return (
|
325 |
+
False,
|
326 |
+
f"PROTENIX Validation Error: Duplicate header '{main_header}'. Each sequence must have a unique header",
|
327 |
+
)
|
328 |
+
seen_headers.add(main_header)
|
329 |
+
|
330 |
+
return True, None
|
331 |
+
|
332 |
+
def transform_fasta(self, fasta_path: Path) -> str:
|
333 |
+
"""Transform a FASTA file into the '>NAME|Chain X' format by ensuring each main header
|
334 |
+
is unique (adding a number if necessary).
|
335 |
+
|
336 |
+
The expected output format is:
|
337 |
+
'>protein_1 | Chain A'
|
338 |
+
'SEQUENCE'
|
339 |
+
'>protein_2 | Chain B'
|
340 |
+
'SEQUENCE'
|
341 |
+
|
342 |
+
Args:
|
343 |
+
fasta_path (Path): Path to the FASTA file.
|
344 |
+
|
345 |
+
Returns:
|
346 |
+
Transformed FASTA content in the required Protenix format.
|
347 |
+
"""
|
348 |
+
transformed_lines = []
|
349 |
+
header_count = defaultdict(int)
|
350 |
+
|
351 |
+
with fasta_path.open("r") as f:
|
352 |
+
for record in SeqIO.parse(f, "fasta"):
|
353 |
+
header_parts = [part.strip() for part in record.description.split("|")]
|
354 |
+
main_header = header_parts[0]
|
355 |
+
|
356 |
+
# Ensure unique headers
|
357 |
+
header_count[main_header] += 1
|
358 |
+
updated_main_header = (
|
359 |
+
f"{main_header}_{header_count[main_header]}"
|
360 |
+
if header_count[main_header] > 1
|
361 |
+
else main_header
|
362 |
+
)
|
363 |
+
|
364 |
+
transformed_lines.append(f">{updated_main_header}")
|
365 |
+
transformed_lines.append(str(record.seq))
|
366 |
+
|
367 |
+
return "\n".join(transformed_lines)
|
molecule.py → folding_studio_demo/molecule.py
RENAMED
@@ -1,15 +1,16 @@
|
|
1 |
-
import logging
|
2 |
|
3 |
# Configure logging
|
4 |
logging.basicConfig(
|
5 |
level=logging.INFO,
|
6 |
-
format=
|
7 |
handlers=[
|
8 |
logging.StreamHandler(),
|
9 |
-
]
|
10 |
)
|
11 |
logger = logging.getLogger(__name__)
|
12 |
|
|
|
13 |
def read_mol(molpath):
|
14 |
with open(molpath, "r") as fp:
|
15 |
lines = fp.readlines()
|
@@ -19,10 +20,7 @@ def read_mol(molpath):
|
|
19 |
return mol
|
20 |
|
21 |
|
22 |
-
def molecule(
|
23 |
-
input_pdb, lenSeqs, num_res, selectedResidues, allSeqs, sequences
|
24 |
-
):
|
25 |
-
|
26 |
options = ""
|
27 |
pred_mol = "["
|
28 |
seqdata = "{"
|
@@ -265,5 +263,5 @@ def molecule(
|
|
265 |
</script>
|
266 |
</body></html>"""
|
267 |
)
|
268 |
-
|
269 |
-
return x
|
|
|
1 |
+
import logging
|
2 |
|
3 |
# Configure logging
|
4 |
logging.basicConfig(
|
5 |
level=logging.INFO,
|
6 |
+
format="%(asctime)s - %(levelname)s - %(message)s",
|
7 |
handlers=[
|
8 |
logging.StreamHandler(),
|
9 |
+
],
|
10 |
)
|
11 |
logger = logging.getLogger(__name__)
|
12 |
|
13 |
+
|
14 |
def read_mol(molpath):
|
15 |
with open(molpath, "r") as fp:
|
16 |
lines = fp.readlines()
|
|
|
20 |
return mol
|
21 |
|
22 |
|
23 |
+
def molecule(input_pdb, lenSeqs, num_res, selectedResidues, allSeqs, sequences):
|
|
|
|
|
|
|
24 |
options = ""
|
25 |
pred_mol = "["
|
26 |
seqdata = "{"
|
|
|
263 |
</script>
|
264 |
</body></html>"""
|
265 |
)
|
266 |
+
|
267 |
+
return x
|
folding_studio_demo/predict.py
ADDED
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Predict protein structure using Folding Studio."""
|
2 |
+
|
3 |
+
import hashlib
|
4 |
+
import logging
|
5 |
+
import os
|
6 |
+
from pathlib import Path
|
7 |
+
|
8 |
+
import gradio as gr
|
9 |
+
import numpy as np
|
10 |
+
import plotly.graph_objects as go
|
11 |
+
from Bio import SeqIO
|
12 |
+
from Bio.PDB import PDBIO, MMCIFParser
|
13 |
+
from folding_studio.client import Client
|
14 |
+
from folding_studio.query import Query
|
15 |
+
from folding_studio.query.boltz import BoltzQuery
|
16 |
+
from folding_studio.query.chai import ChaiQuery
|
17 |
+
from folding_studio.query.protenix import ProtenixQuery
|
18 |
+
from folding_studio_data_models import FoldingModel
|
19 |
+
|
20 |
+
from folding_studio_demo.model_fasta_validators import (
|
21 |
+
BaseFastaValidator,
|
22 |
+
BoltzFastaValidator,
|
23 |
+
ChaiFastaValidator,
|
24 |
+
ProtenixFastaValidator,
|
25 |
+
)
|
26 |
+
|
27 |
+
logger = logging.getLogger(__name__)
|
28 |
+
|
29 |
+
SEQUENCE_DIR = Path("sequences")
|
30 |
+
SEQUENCE_DIR.mkdir(parents=True, exist_ok=True)
|
31 |
+
|
32 |
+
OUTPUT_DIR = Path("output")
|
33 |
+
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
34 |
+
|
35 |
+
|
36 |
+
def convert_cif_to_pdb(cif_path: str, pdb_path: str) -> None:
|
37 |
+
"""Convert a .cif file to .pdb format using Biopython.
|
38 |
+
|
39 |
+
Args:
|
40 |
+
cif_path (str): Path to input .cif file
|
41 |
+
pdb_path (str): Path to output .pdb file
|
42 |
+
"""
|
43 |
+
# Parse the CIF file
|
44 |
+
parser = MMCIFParser()
|
45 |
+
structure = parser.get_structure("structure", cif_path)
|
46 |
+
|
47 |
+
# Save as PDB
|
48 |
+
io = PDBIO()
|
49 |
+
io.set_structure(structure)
|
50 |
+
io.save(pdb_path)
|
51 |
+
|
52 |
+
|
53 |
+
def add_plddt_plot(plddt_vals: list[float]) -> str:
|
54 |
+
"""Create a plot of metrics."""
|
55 |
+
visible = True
|
56 |
+
plddt_trace = go.Scatter(
|
57 |
+
x=np.arange(len(plddt_vals)),
|
58 |
+
y=plddt_vals,
|
59 |
+
hovertemplate="<i>pLDDT</i>: %{y:.2f} <br><i>Residue index:</i> %{x}<br>",
|
60 |
+
name="seq",
|
61 |
+
visible=visible,
|
62 |
+
)
|
63 |
+
|
64 |
+
plddt_fig = go.Figure(data=[plddt_trace])
|
65 |
+
plddt_fig.update_layout(
|
66 |
+
title="pLDDT",
|
67 |
+
xaxis_title="Residue index",
|
68 |
+
yaxis_title="pLDDT",
|
69 |
+
height=500,
|
70 |
+
template="simple_white",
|
71 |
+
legend=dict(yanchor="bottom", y=0.01, xanchor="left", x=0.99),
|
72 |
+
)
|
73 |
+
return plddt_fig
|
74 |
+
|
75 |
+
|
76 |
+
def _write_fasta_file(
|
77 |
+
sequence: str, directory: Path = SEQUENCE_DIR
|
78 |
+
) -> tuple[str, Path]:
|
79 |
+
"""Write sequence to FASTA file.
|
80 |
+
|
81 |
+
Args:
|
82 |
+
sequence (str): Sequence to write to FASTA file
|
83 |
+
directory (Path): Directory to write FASTA file to (default: SEQUENCE_DIR)
|
84 |
+
|
85 |
+
Returns:
|
86 |
+
tuple[str, Path]: Tuple containing the sequence ID and the path to the FASTA file
|
87 |
+
"""
|
88 |
+
seq_id = hashlib.sha1(sequence.encode()).hexdigest()
|
89 |
+
seq_file = directory / f"sequence_{seq_id}.fasta"
|
90 |
+
with open(seq_file, "w") as f:
|
91 |
+
f.write(sequence)
|
92 |
+
return seq_id, seq_file
|
93 |
+
|
94 |
+
|
95 |
+
class AF3Model:
|
96 |
+
def __init__(
|
97 |
+
self, api_key: str, model_name: str, query: Query, validator: BaseFastaValidator
|
98 |
+
):
|
99 |
+
self.api_key = api_key
|
100 |
+
self.model_name = model_name
|
101 |
+
self.query = query
|
102 |
+
self.validator = validator
|
103 |
+
|
104 |
+
def call(self, seq_file: Path | str, output_dir: Path) -> None:
|
105 |
+
"""Predict protein structure from amino acid sequence using AF3 model.
|
106 |
+
|
107 |
+
Args:
|
108 |
+
seq_file (Path | str): Path to FASTA file containing amino acid sequence
|
109 |
+
output_dir (Path): Path to output directory
|
110 |
+
"""
|
111 |
+
# Validate FASTA format before calling
|
112 |
+
is_valid, error_msg = self.check_file_description(seq_file)
|
113 |
+
if not is_valid:
|
114 |
+
logger.error(error_msg)
|
115 |
+
raise gr.Error(error_msg)
|
116 |
+
|
117 |
+
# Create a client using API key
|
118 |
+
logger.info("Authenticating client with API key")
|
119 |
+
client = Client.from_api_key(api_key=self.api_key)
|
120 |
+
|
121 |
+
# Define query
|
122 |
+
query: Query = self.query.from_file(path=seq_file, query_name="gradio")
|
123 |
+
query.save_parameters(output_dir)
|
124 |
+
|
125 |
+
logger.info("Payload: %s", query.payload)
|
126 |
+
|
127 |
+
# Send a request
|
128 |
+
logger.info(f"Sending {self.model_name} request to Folding Studio API")
|
129 |
+
response = client.send_request(
|
130 |
+
query, project_code=os.environ["FOLDING_PROJECT_CODE"]
|
131 |
+
)
|
132 |
+
|
133 |
+
# Access confidence data
|
134 |
+
logger.info("Confidence data: %s", response.confidence_data)
|
135 |
+
|
136 |
+
response.download_results(output_dir=output_dir, force=True, unzip=True)
|
137 |
+
logger.info("Results downloaded to %s", output_dir)
|
138 |
+
|
139 |
+
def format_fasta(self, sequence: str) -> str:
|
140 |
+
"""Format sequence to FASTA format."""
|
141 |
+
return f">{self.model_name}\n{sequence}"
|
142 |
+
|
143 |
+
def predictions(self, output_dir: Path) -> list[Path]:
|
144 |
+
"""Get the path to the prediction."""
|
145 |
+
raise NotImplementedError("Not implemented")
|
146 |
+
|
147 |
+
def has_prediction(self, output_dir: Path) -> bool:
|
148 |
+
"""Check if prediction exists in output directory."""
|
149 |
+
return any(self.predictions(output_dir))
|
150 |
+
|
151 |
+
def check_file_description(self, seq_file: Path | str) -> tuple[bool, str | None]:
|
152 |
+
"""Check if the file description is correct.
|
153 |
+
|
154 |
+
Args:
|
155 |
+
seq_file (Path | str): Path to FASTA file
|
156 |
+
|
157 |
+
Returns:
|
158 |
+
tuple[bool, str | None]: Tuple containing a boolean indicating if the format is correct and an error message if not
|
159 |
+
"""
|
160 |
+
input_rep = list(SeqIO.parse(seq_file, "fasta"))
|
161 |
+
if not input_rep:
|
162 |
+
error_msg = f"{self.model_name.upper()} Validation Error: No sequence found"
|
163 |
+
return False, error_msg
|
164 |
+
|
165 |
+
is_valid, error_msg = self.validator.is_valid_fasta(seq_file)
|
166 |
+
if not is_valid:
|
167 |
+
return False, error_msg
|
168 |
+
|
169 |
+
return True, None
|
170 |
+
|
171 |
+
|
172 |
+
class ChaiModel(AF3Model):
|
173 |
+
def __init__(self, api_key: str):
|
174 |
+
super().__init__(api_key, "Chai", ChaiQuery, ChaiFastaValidator())
|
175 |
+
|
176 |
+
def call(self, seq_file: Path | str, output_dir: Path) -> None:
|
177 |
+
"""Predict protein structure from amino acid sequence using Chai model.
|
178 |
+
|
179 |
+
Args:
|
180 |
+
seq_file (Path | str): Path to FASTA file containing amino acid sequence
|
181 |
+
output_dir (Path): Path to output directory
|
182 |
+
"""
|
183 |
+
super().call(seq_file, output_dir)
|
184 |
+
|
185 |
+
def predictions(self, output_dir: Path) -> list[Path]:
|
186 |
+
"""Get the path to the prediction."""
|
187 |
+
return list(output_dir.rglob("*_model_[0-9].cif"))
|
188 |
+
|
189 |
+
|
190 |
+
class ProtenixModel(AF3Model):
|
191 |
+
def __init__(self, api_key: str):
|
192 |
+
super().__init__(api_key, "Protenix", ProtenixQuery, ProtenixFastaValidator())
|
193 |
+
|
194 |
+
def call(self, seq_file: Path | str, output_dir: Path) -> None:
|
195 |
+
"""Predict protein structure from amino acid sequence using Protenix model.
|
196 |
+
|
197 |
+
Args:
|
198 |
+
seq_file (Path | str): Path to FASTA file containing amino acid sequence
|
199 |
+
output_dir (Path): Path to output directory
|
200 |
+
"""
|
201 |
+
super().call(seq_file, output_dir)
|
202 |
+
|
203 |
+
def predictions(self, output_dir: Path) -> list[Path]:
|
204 |
+
"""Get the path to the prediction."""
|
205 |
+
return list(output_dir.rglob("*_model_[0-9].cif"))
|
206 |
+
|
207 |
+
|
208 |
+
class BoltzModel(AF3Model):
|
209 |
+
def __init__(self, api_key: str):
|
210 |
+
super().__init__(api_key, "Boltz", BoltzQuery, BoltzFastaValidator())
|
211 |
+
|
212 |
+
def call(self, seq_file: Path | str, output_dir: Path) -> None:
|
213 |
+
"""Predict protein structure from amino acid sequence using Boltz model.
|
214 |
+
|
215 |
+
Args:
|
216 |
+
seq_file (Path | str): Path to FASTA file containing amino acid sequence
|
217 |
+
output_dir (Path): Path to output directory
|
218 |
+
"""
|
219 |
+
|
220 |
+
super().call(seq_file, output_dir)
|
221 |
+
|
222 |
+
def predictions(self, output_dir: Path) -> list[Path]:
|
223 |
+
"""Get the path to the prediction."""
|
224 |
+
return list(output_dir.rglob("*_model_[0-9].cif"))
|
225 |
+
|
226 |
+
|
227 |
+
def predict(sequence: str, api_key: str, model_type: FoldingModel) -> tuple[str, str]:
|
228 |
+
"""Predict protein structure from amino acid sequence using Boltz model.
|
229 |
+
|
230 |
+
Args:
|
231 |
+
sequence (str): Amino acid sequence to predict structure for
|
232 |
+
api_key (str): Folding API key
|
233 |
+
model (FoldingModel): Folding model to use
|
234 |
+
|
235 |
+
Returns:
|
236 |
+
tuple[str, str]: Tuple containing the path to the PDB file and the pLDDT plot
|
237 |
+
"""
|
238 |
+
|
239 |
+
# Set up unique output directory based on sequence hash
|
240 |
+
seq_id, seq_file = _write_fasta_file(sequence)
|
241 |
+
output_dir = OUTPUT_DIR / seq_id / model_type
|
242 |
+
output_dir.mkdir(parents=True, exist_ok=True)
|
243 |
+
|
244 |
+
if model_type == FoldingModel.BOLTZ:
|
245 |
+
model = BoltzModel(api_key)
|
246 |
+
elif model_type == FoldingModel.CHAI:
|
247 |
+
model = ChaiModel(api_key)
|
248 |
+
elif model_type == FoldingModel.PROTENIX:
|
249 |
+
model = ProtenixModel(api_key)
|
250 |
+
else:
|
251 |
+
raise ValueError(f"Model {model_type} not supported")
|
252 |
+
|
253 |
+
# Check if prediction already exists
|
254 |
+
if not model.has_prediction(output_dir):
|
255 |
+
# Run Boltz prediction
|
256 |
+
logger.info(f"Predicting {seq_id}")
|
257 |
+
model.call(seq_file=seq_file, output_dir=output_dir)
|
258 |
+
logger.info("Prediction done. Output directory: %s", output_dir)
|
259 |
+
else:
|
260 |
+
logger.info("Prediction already exists. Output directory: %s", output_dir)
|
261 |
+
|
262 |
+
# output_dir = Path("boltz_results") # debug
|
263 |
+
|
264 |
+
# Convert output CIF to PDB
|
265 |
+
if not model.has_prediction(output_dir):
|
266 |
+
raise gr.Error("No prediction found")
|
267 |
+
|
268 |
+
pred_cif = model.predictions(output_dir)[0]
|
269 |
+
logger.info("Output file: %s", pred_cif)
|
270 |
+
|
271 |
+
converted_pdb_path = str(output_dir / f"pred_{seq_id}.pdb")
|
272 |
+
convert_cif_to_pdb(str(pred_cif), str(converted_pdb_path))
|
273 |
+
logger.info("Converted PDB file: %s", converted_pdb_path)
|
274 |
+
|
275 |
+
plddt_file = list(pred_cif.parent.glob("plddt_*.npz"))[0]
|
276 |
+
logger.info("plddt file: %s", plddt_file)
|
277 |
+
plddt_vals = np.load(plddt_file)["plddt"]
|
278 |
+
|
279 |
+
return converted_pdb_path, add_plddt_plot(plddt_vals=plddt_vals)
|
folding_studio_demo/protein.py
ADDED
File without changes
|
pyproject.toml
CHANGED
@@ -1,12 +1,25 @@
|
|
1 |
[project]
|
2 |
-
name = "
|
3 |
version = "0.1.0"
|
4 |
description = "Add your description here"
|
5 |
readme = "README.md"
|
6 |
requires-python = ">=3.11"
|
7 |
dependencies = [
|
8 |
"gradio==5.30.0",
|
|
|
9 |
"ipython>=9.2.0",
|
10 |
"numpy>=2.2.6",
|
11 |
"plotly>=6.1.1",
|
|
|
|
|
12 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
[project]
|
2 |
+
name = "folding-studio-demo"
|
3 |
version = "0.1.0"
|
4 |
description = "Add your description here"
|
5 |
readme = "README.md"
|
6 |
requires-python = ">=3.11"
|
7 |
dependencies = [
|
8 |
"gradio==5.30.0",
|
9 |
+
"gradio-molecule3d>=0.0.7",
|
10 |
"ipython>=9.2.0",
|
11 |
"numpy>=2.2.6",
|
12 |
"plotly>=6.1.1",
|
13 |
+
"folding-studio",
|
14 |
+
"biopython>=1.85",
|
15 |
]
|
16 |
+
|
17 |
+
[build-system]
|
18 |
+
requires = ["hatchling"]
|
19 |
+
build-backend = "hatchling.build"
|
20 |
+
|
21 |
+
[tool.uv.sources]
|
22 |
+
folding-studio = { path = "./folding-studio" }
|
23 |
+
|
24 |
+
[project.scripts]
|
25 |
+
folding-studio-demo = "folding_studio_demo.app:__main__"
|
uv.lock
CHANGED
@@ -88,6 +88,45 @@ wheels = [
|
|
88 |
{ url = "https://files.pythonhosted.org/packages/5d/35/be73b6015511aa0173ec595fc579133b797ad532996f2998fd6b8d1bbe6b/audioop_lts-0.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:78bfb3703388c780edf900be66e07de5a3d4105ca8e8720c5c4d67927e0b15d0", size = 23918 },
|
89 |
]
|
90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
[[package]]
|
92 |
name = "certifi"
|
93 |
version = "2025.4.26"
|
@@ -157,6 +196,20 @@ wheels = [
|
|
157 |
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
|
158 |
]
|
159 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
[[package]]
|
161 |
name = "colorama"
|
162 |
version = "0.4.6"
|
@@ -217,11 +270,61 @@ wheels = [
|
|
217 |
]
|
218 |
|
219 |
[[package]]
|
220 |
-
name = "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
version = "0.1.0"
|
222 |
-
source = {
|
223 |
dependencies = [
|
|
|
|
|
224 |
{ name = "gradio" },
|
|
|
225 |
{ name = "ipython" },
|
226 |
{ name = "numpy" },
|
227 |
{ name = "plotly" },
|
@@ -229,7 +332,10 @@ dependencies = [
|
|
229 |
|
230 |
[package.metadata]
|
231 |
requires-dist = [
|
|
|
|
|
232 |
{ name = "gradio", specifier = "==5.30.0" },
|
|
|
233 |
{ name = "ipython", specifier = ">=9.2.0" },
|
234 |
{ name = "numpy", specifier = ">=2.2.6" },
|
235 |
{ name = "plotly", specifier = ">=6.1.1" },
|
@@ -244,6 +350,117 @@ wheels = [
|
|
244 |
{ url = "https://files.pythonhosted.org/packages/2c/a9/a7022f58e081149ec0184c31ea81dcee605e1d46380b48122e1ef94ac24e/fsspec-2025.5.0-py3-none-any.whl", hash = "sha256:0ca253eca6b5333d8a2b8bd98c7326fe821f1f0fdbd34e1b445bddde8e804c95", size = 196164 },
|
245 |
]
|
246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
247 |
[[package]]
|
248 |
name = "gradio"
|
249 |
version = "5.30.0"
|
@@ -301,6 +518,18 @@ wheels = [
|
|
301 |
{ url = "https://files.pythonhosted.org/packages/55/6f/03eb8e0e0ec80eced5ed35a63376dabfc7391b1538502f8e85e9dc5bab02/gradio_client-1.10.1-py3-none-any.whl", hash = "sha256:fcff53f6aad3dfa9dd082adedb94256172d6b20666b1ef66480d82023e1907db", size = 323141 },
|
302 |
]
|
303 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
304 |
[[package]]
|
305 |
name = "groovy"
|
306 |
version = "0.1.2"
|
@@ -778,6 +1007,32 @@ wheels = [
|
|
778 |
{ url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810 },
|
779 |
]
|
780 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
781 |
[[package]]
|
782 |
name = "ptyprocess"
|
783 |
version = "0.7.0"
|
@@ -796,65 +1051,105 @@ wheels = [
|
|
796 |
{ url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 },
|
797 |
]
|
798 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
799 |
[[package]]
|
800 |
name = "pydantic"
|
801 |
-
version = "2.
|
802 |
source = { registry = "https://pypi.org/simple" }
|
803 |
dependencies = [
|
804 |
{ name = "annotated-types" },
|
805 |
{ name = "pydantic-core" },
|
806 |
{ name = "typing-extensions" },
|
|
|
807 |
]
|
808 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
809 |
wheels = [
|
810 |
-
{ url = "https://files.pythonhosted.org/packages/
|
811 |
]
|
812 |
|
813 |
[[package]]
|
814 |
name = "pydantic-core"
|
815 |
-
version = "2.
|
816 |
source = { registry = "https://pypi.org/simple" }
|
817 |
dependencies = [
|
818 |
{ name = "typing-extensions" },
|
819 |
]
|
820 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
821 |
-
wheels = [
|
822 |
-
{ url = "https://files.pythonhosted.org/packages/
|
823 |
-
{ url = "https://files.pythonhosted.org/packages/
|
824 |
-
{ url = "https://files.pythonhosted.org/packages/
|
825 |
-
{ url = "https://files.pythonhosted.org/packages/
|
826 |
-
{ url = "https://files.pythonhosted.org/packages/d5/
|
827 |
-
{ url = "https://files.pythonhosted.org/packages/
|
828 |
-
{ url = "https://files.pythonhosted.org/packages/
|
829 |
-
{ url = "https://files.pythonhosted.org/packages/
|
830 |
-
{ url = "https://files.pythonhosted.org/packages/
|
831 |
-
{ url = "https://files.pythonhosted.org/packages/
|
832 |
-
{ url = "https://files.pythonhosted.org/packages/
|
833 |
-
{ url = "https://files.pythonhosted.org/packages/
|
834 |
-
{ url = "https://files.pythonhosted.org/packages/
|
835 |
-
{ url = "https://files.pythonhosted.org/packages/
|
836 |
-
{ url = "https://files.pythonhosted.org/packages/
|
837 |
-
{ url = "https://files.pythonhosted.org/packages/
|
838 |
-
{ url = "https://files.pythonhosted.org/packages/
|
839 |
-
{ url = "https://files.pythonhosted.org/packages/
|
840 |
-
{ url = "https://files.pythonhosted.org/packages/
|
841 |
-
{ url = "https://files.pythonhosted.org/packages/
|
842 |
-
{ url = "https://files.pythonhosted.org/packages/
|
843 |
-
{ url = "https://files.pythonhosted.org/packages/
|
844 |
-
{ url = "https://files.pythonhosted.org/packages/
|
845 |
-
{ url = "https://files.pythonhosted.org/packages/
|
846 |
-
{ url = "https://files.pythonhosted.org/packages/
|
847 |
-
{ url = "https://files.pythonhosted.org/packages/
|
848 |
-
{ url = "https://files.pythonhosted.org/packages/
|
849 |
-
{ url = "https://files.pythonhosted.org/packages/
|
850 |
-
{ url = "https://files.pythonhosted.org/packages/
|
851 |
-
{ url = "https://files.pythonhosted.org/packages/
|
852 |
-
{ url = "https://files.pythonhosted.org/packages/
|
853 |
-
{ url = "https://files.pythonhosted.org/packages/
|
854 |
-
{ url = "https://files.pythonhosted.org/packages/
|
855 |
-
{ url = "https://files.pythonhosted.org/packages/
|
856 |
-
{ url = "https://files.pythonhosted.org/packages/
|
857 |
-
{ url = "https://files.pythonhosted.org/packages/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
858 |
]
|
859 |
|
860 |
[[package]]
|
@@ -887,6 +1182,15 @@ wheels = [
|
|
887 |
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
|
888 |
]
|
889 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
890 |
[[package]]
|
891 |
name = "python-multipart"
|
892 |
version = "0.0.20"
|
@@ -968,6 +1272,18 @@ wheels = [
|
|
968 |
{ url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 },
|
969 |
]
|
970 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
971 |
[[package]]
|
972 |
name = "ruff"
|
973 |
version = "0.11.10"
|
@@ -1099,7 +1415,7 @@ wheels = [
|
|
1099 |
|
1100 |
[[package]]
|
1101 |
name = "typer"
|
1102 |
-
version = "0.
|
1103 |
source = { registry = "https://pypi.org/simple" }
|
1104 |
dependencies = [
|
1105 |
{ name = "click" },
|
@@ -1107,9 +1423,9 @@ dependencies = [
|
|
1107 |
{ name = "shellingham" },
|
1108 |
{ name = "typing-extensions" },
|
1109 |
]
|
1110 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
1111 |
wheels = [
|
1112 |
-
{ url = "https://files.pythonhosted.org/packages/
|
1113 |
]
|
1114 |
|
1115 |
[[package]]
|
@@ -1121,6 +1437,18 @@ wheels = [
|
|
1121 |
{ url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 },
|
1122 |
]
|
1123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1124 |
[[package]]
|
1125 |
name = "tzdata"
|
1126 |
version = "2025.2"
|
|
|
88 |
{ url = "https://files.pythonhosted.org/packages/5d/35/be73b6015511aa0173ec595fc579133b797ad532996f2998fd6b8d1bbe6b/audioop_lts-0.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:78bfb3703388c780edf900be66e07de5a3d4105ca8e8720c5c4d67927e0b15d0", size = 23918 },
|
89 |
]
|
90 |
|
91 |
+
[[package]]
|
92 |
+
name = "biopython"
|
93 |
+
version = "1.85"
|
94 |
+
source = { registry = "https://pypi.org/simple" }
|
95 |
+
dependencies = [
|
96 |
+
{ name = "numpy" },
|
97 |
+
]
|
98 |
+
sdist = { url = "https://files.pythonhosted.org/packages/db/ca/1d5fab0fedaf5c2f376d9746d447cdce04241c433602c3861693361ce54c/biopython-1.85.tar.gz", hash = "sha256:5dafab74059de4e78f49f6b5684eddae6e7ce46f09cfa059c1d1339e8b1ea0a6", size = 19909902 }
|
99 |
+
wheels = [
|
100 |
+
{ url = "https://files.pythonhosted.org/packages/c3/73/c3a1323a3fe0d07212b09c04fb903e2cbb98aebfbb58e55e8717473e1bc0/biopython-1.85-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db8822adab0cd75a6e6ae845acf312addd8eab5f9b731c191454b961fc2c2cdc", size = 2787585 },
|
101 |
+
{ url = "https://files.pythonhosted.org/packages/ff/cf/299524e896fa49beb7588143e1509cce4848572215ebafb8eea83a861820/biopython-1.85-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e2bbe58cc1a592b239ef6d68396745d3fbfaafc668ce38283871d8ff070dbab", size = 2765608 },
|
102 |
+
{ url = "https://files.pythonhosted.org/packages/b1/dd/be3e95b72a35ee6d52c84412e15af828951e5c69175080d4619985fd54ce/biopython-1.85-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5916eb56df7ecd4a3babc07a48d4894c40cfb45dc18ccda1c148d0131017ce04", size = 3237552 },
|
103 |
+
{ url = "https://files.pythonhosted.org/packages/25/9c/612821b946930b6caa5d795cfe4169ed6a522562eced9776914be7efaf21/biopython-1.85-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0cec8833bf3036049129944ee5382dd576dac9670c3814ff2314b52aa94f199", size = 3257287 },
|
104 |
+
{ url = "https://files.pythonhosted.org/packages/a1/77/316e51dd42fd8225429574a268bdc627ce4f42067a3976c4c8c457a42023/biopython-1.85-cp311-cp311-win32.whl", hash = "sha256:cf88a4c8d8af13138be115949639a5e4a201618185a72ff09adbe175b7946b28", size = 2786411 },
|
105 |
+
{ url = "https://files.pythonhosted.org/packages/f2/11/3c4e8c049b91998bbbd51ddebc6f790b1aa66211babfbf5ff008a72fb1f9/biopython-1.85-cp311-cp311-win_amd64.whl", hash = "sha256:d3c99db65d57ae4fc5034e42ac6cd8ddce069e664903f04c8a4f684d7609d6fa", size = 2820912 },
|
106 |
+
{ url = "https://files.pythonhosted.org/packages/a3/25/e46f05359df7f0049c3adc5eaeb9aee0f5fbde1d959d05c78eb1de8f4d12/biopython-1.85-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc5b981b9e3060db7c355b6145dfe3ce0b6572e1601b31211f6d742b10543874", size = 2789327 },
|
107 |
+
{ url = "https://files.pythonhosted.org/packages/54/5b/8b3b029c94c63ab4c1781d141615b4a837e658422381d460c5573d5d8262/biopython-1.85-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6fe47d704c2d3afac99aeb461219ec5f00273120d2d99835dc0a9a617f520141", size = 2765805 },
|
108 |
+
{ url = "https://files.pythonhosted.org/packages/69/0a/9a8a38eff03c4607b9cec8d0e08c76b346b1cee1f77bc6d00efebfc7ec83/biopython-1.85-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54e495239e623660ad367498c2f7a1a294b1997ba603f2ceafb36fd18f0eba6", size = 3249276 },
|
109 |
+
{ url = "https://files.pythonhosted.org/packages/b4/0d/b7a0f10f5100dcf51ae36ba31490169bfa45617323bd82af43e1fb0098fb/biopython-1.85-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d024ad48997ad53d53a77da24b072aaba8a550bd816af8f2e7e606a9918a3b43", size = 3268869 },
|
110 |
+
{ url = "https://files.pythonhosted.org/packages/07/51/646a4b7bdb4c1153786a70d33588ed09178bfcdda0542dfdc976294f4312/biopython-1.85-cp312-cp312-win32.whl", hash = "sha256:6985e17a2911defcbd30275a12f5ed5de2765e4bc91a60439740d572fdbfdf43", size = 2787011 },
|
111 |
+
{ url = "https://files.pythonhosted.org/packages/c1/84/c583fa2ac6e7d392d24ebdc5c99e95e517507de22cf143efb6cf1fc93ff5/biopython-1.85-cp312-cp312-win_amd64.whl", hash = "sha256:d6f8efb2db03f2ec115c5e8c764dbadf635e0c9ecd4c0e91fc8216c1b62f85f5", size = 2820972 },
|
112 |
+
{ url = "https://files.pythonhosted.org/packages/c3/3f/65814bf221f0bfdd2633830e573ac8594794686f54110571dce98cc32fd3/biopython-1.85-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bae6f17262f20e9587d419ba5683e9dc93a31ee1858b98fa0cff203694d5b786", size = 2789844 },
|
113 |
+
{ url = "https://files.pythonhosted.org/packages/be/61/1443ce34226e261c20ae4a154b2bab72c109cf31415c92c54c1aada36b00/biopython-1.85-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1e4918e6399ab0183dd863527fec18b53b7c61b6f0ef95db84b4adfa430ce75", size = 2765767 },
|
114 |
+
{ url = "https://files.pythonhosted.org/packages/53/51/bec4c763c704e2715691bb087cfab5907804a1bbef5873588698cece1a30/biopython-1.85-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86e487b6fe0f20a2b0138cb53f3d4dc26a7e4060ac4cb6fb1d19e194d445ef46", size = 3248867 },
|
115 |
+
{ url = "https://files.pythonhosted.org/packages/9e/a4/552f20253a7c95988067c4955831bd17dd9b864fd5c215d15c2f63f2d415/biopython-1.85-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:327184048b5a50634ae0970119bcb8a35b76d7cefb2658a01f772915f2fb7686", size = 3268479 },
|
116 |
+
{ url = "https://files.pythonhosted.org/packages/97/f4/6dfc6ef3e0997f792f93893551d4a9bf7f6052a70d34f4e1b1e5e4a359f6/biopython-1.85-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66b08905fb1b3f5194f908aaf04bbad474c4e3eaebad6d9f889a04e64dd1faf4", size = 3192338 },
|
117 |
+
{ url = "https://files.pythonhosted.org/packages/9d/8c/73da6588fab3a7d734118f21ac619c0a949f51d3cf7b1b8343d175c33460/biopython-1.85-cp313-cp313-win32.whl", hash = "sha256:5a236ab1e2797c7dcf1577d80fdaafabead2908bc338eaed0aa1509dab769fef", size = 2787017 },
|
118 |
+
{ url = "https://files.pythonhosted.org/packages/60/ff/fe8f03ac0ccc7219e0959010750c6ac1a5b1411c91c7f4ec3a37d8313673/biopython-1.85-cp313-cp313-win_amd64.whl", hash = "sha256:1b61593765e9ebdb71d73307d55fd4b46eb976608d329ae6803c084d90ed34c7", size = 2821012 },
|
119 |
+
]
|
120 |
+
|
121 |
+
[[package]]
|
122 |
+
name = "cachetools"
|
123 |
+
version = "5.5.2"
|
124 |
+
source = { registry = "https://pypi.org/simple" }
|
125 |
+
sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380 }
|
126 |
+
wheels = [
|
127 |
+
{ url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080 },
|
128 |
+
]
|
129 |
+
|
130 |
[[package]]
|
131 |
name = "certifi"
|
132 |
version = "2025.4.26"
|
|
|
196 |
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
|
197 |
]
|
198 |
|
199 |
+
[[package]]
|
200 |
+
name = "cloudpathlib"
|
201 |
+
version = "0.19.0"
|
202 |
+
source = { registry = "https://pypi.org/simple" }
|
203 |
+
sdist = { url = "https://files.pythonhosted.org/packages/10/c9/90cb80cf70669ed9a978ef46d8a8b39bad8ca768c4ce4ae086f0d05aeeb7/cloudpathlib-0.19.0.tar.gz", hash = "sha256:919edbfd9a4e935d2423da210b143df89cb0ec6d378366a0dffa2e9fd0664fe8", size = 41875 }
|
204 |
+
wheels = [
|
205 |
+
{ url = "https://files.pythonhosted.org/packages/4d/4e/f83794cb311019c385d061d9b7a9dc444c7023c5523c3f4161191221429c/cloudpathlib-0.19.0-py3-none-any.whl", hash = "sha256:eb7758648812d5906af44f14cf9a6a64f687342a6f547a1c20deb7241d769dcb", size = 49433 },
|
206 |
+
]
|
207 |
+
|
208 |
+
[package.optional-dependencies]
|
209 |
+
gs = [
|
210 |
+
{ name = "google-cloud-storage" },
|
211 |
+
]
|
212 |
+
|
213 |
[[package]]
|
214 |
name = "colorama"
|
215 |
version = "0.4.6"
|
|
|
270 |
]
|
271 |
|
272 |
[[package]]
|
273 |
+
name = "folding-studio"
|
274 |
+
version = "3.3.1"
|
275 |
+
source = { directory = "folding-studio" }
|
276 |
+
dependencies = [
|
277 |
+
{ name = "folding-studio-data-models" },
|
278 |
+
{ name = "pydantic" },
|
279 |
+
{ name = "python-dotenv" },
|
280 |
+
{ name = "pyyaml" },
|
281 |
+
{ name = "requests" },
|
282 |
+
{ name = "tqdm" },
|
283 |
+
{ name = "typer" },
|
284 |
+
]
|
285 |
+
|
286 |
+
[package.metadata]
|
287 |
+
requires-dist = [
|
288 |
+
{ name = "folding-studio-data-models", path = "folding-studio/offline-packages/folding_studio_data_models-0.13.3-py3-none-any.whl" },
|
289 |
+
{ name = "pydantic", specifier = ">=2.11.4,<3.0.0" },
|
290 |
+
{ name = "python-dotenv", specifier = ">=1.0.1,<2.0.0" },
|
291 |
+
{ name = "pyyaml", specifier = ">=6.0.2,<7.0.0" },
|
292 |
+
{ name = "requests", specifier = ">=2.32.3,<3.0.0" },
|
293 |
+
{ name = "tqdm", specifier = ">=4.67.0,<5.0.0" },
|
294 |
+
{ name = "typer", specifier = ">=0.15.4,<0.16.0" },
|
295 |
+
]
|
296 |
+
|
297 |
+
[[package]]
|
298 |
+
name = "folding-studio-data-models"
|
299 |
+
version = "0.13.3"
|
300 |
+
source = { path = "folding-studio/offline-packages/folding_studio_data_models-0.13.3-py3-none-any.whl" }
|
301 |
+
dependencies = [
|
302 |
+
{ name = "biopython" },
|
303 |
+
{ name = "cloudpathlib", extra = ["gs"] },
|
304 |
+
{ name = "pydantic" },
|
305 |
+
{ name = "typing-extensions" },
|
306 |
+
]
|
307 |
+
wheels = [
|
308 |
+
{ filename = "folding_studio_data_models-0.13.3-py3-none-any.whl", hash = "sha256:2dcf684bd47c255566f90631499bd1f5755077e74dbe18c98ecc32c77e9b1390" },
|
309 |
+
]
|
310 |
+
|
311 |
+
[package.metadata]
|
312 |
+
requires-dist = [
|
313 |
+
{ name = "biopython", specifier = ">=1.81,<2.0" },
|
314 |
+
{ name = "cloudpathlib", extras = ["gs"], specifier = ">=0.19.0,<0.20.0" },
|
315 |
+
{ name = "pydantic", specifier = ">=2.9.2,<3.0.0" },
|
316 |
+
{ name = "typing-extensions", specifier = ">=4.12.2,<5.0.0" },
|
317 |
+
]
|
318 |
+
|
319 |
+
[[package]]
|
320 |
+
name = "folding-studio-demo"
|
321 |
version = "0.1.0"
|
322 |
+
source = { editable = "." }
|
323 |
dependencies = [
|
324 |
+
{ name = "biopython" },
|
325 |
+
{ name = "folding-studio" },
|
326 |
{ name = "gradio" },
|
327 |
+
{ name = "gradio-molecule3d" },
|
328 |
{ name = "ipython" },
|
329 |
{ name = "numpy" },
|
330 |
{ name = "plotly" },
|
|
|
332 |
|
333 |
[package.metadata]
|
334 |
requires-dist = [
|
335 |
+
{ name = "biopython", specifier = ">=1.85" },
|
336 |
+
{ name = "folding-studio", directory = "folding-studio" },
|
337 |
{ name = "gradio", specifier = "==5.30.0" },
|
338 |
+
{ name = "gradio-molecule3d", specifier = ">=0.0.7" },
|
339 |
{ name = "ipython", specifier = ">=9.2.0" },
|
340 |
{ name = "numpy", specifier = ">=2.2.6" },
|
341 |
{ name = "plotly", specifier = ">=6.1.1" },
|
|
|
350 |
{ url = "https://files.pythonhosted.org/packages/2c/a9/a7022f58e081149ec0184c31ea81dcee605e1d46380b48122e1ef94ac24e/fsspec-2025.5.0-py3-none-any.whl", hash = "sha256:0ca253eca6b5333d8a2b8bd98c7326fe821f1f0fdbd34e1b445bddde8e804c95", size = 196164 },
|
351 |
]
|
352 |
|
353 |
+
[[package]]
|
354 |
+
name = "google-api-core"
|
355 |
+
version = "2.24.2"
|
356 |
+
source = { registry = "https://pypi.org/simple" }
|
357 |
+
dependencies = [
|
358 |
+
{ name = "google-auth" },
|
359 |
+
{ name = "googleapis-common-protos" },
|
360 |
+
{ name = "proto-plus" },
|
361 |
+
{ name = "protobuf" },
|
362 |
+
{ name = "requests" },
|
363 |
+
]
|
364 |
+
sdist = { url = "https://files.pythonhosted.org/packages/09/5c/085bcb872556934bb119e5e09de54daa07873f6866b8f0303c49e72287f7/google_api_core-2.24.2.tar.gz", hash = "sha256:81718493daf06d96d6bc76a91c23874dbf2fac0adbbf542831b805ee6e974696", size = 163516 }
|
365 |
+
wheels = [
|
366 |
+
{ url = "https://files.pythonhosted.org/packages/46/95/f472d85adab6e538da2025dfca9e976a0d125cc0af2301f190e77b76e51c/google_api_core-2.24.2-py3-none-any.whl", hash = "sha256:810a63ac95f3c441b7c0e43d344e372887f62ce9071ba972eacf32672e072de9", size = 160061 },
|
367 |
+
]
|
368 |
+
|
369 |
+
[[package]]
|
370 |
+
name = "google-auth"
|
371 |
+
version = "2.40.2"
|
372 |
+
source = { registry = "https://pypi.org/simple" }
|
373 |
+
dependencies = [
|
374 |
+
{ name = "cachetools" },
|
375 |
+
{ name = "pyasn1-modules" },
|
376 |
+
{ name = "rsa" },
|
377 |
+
]
|
378 |
+
sdist = { url = "https://files.pythonhosted.org/packages/66/84/f67f53c505a6b2c5da05c988e2a5483f5ba9eee4b1841d2e3ff22f547cd5/google_auth-2.40.2.tar.gz", hash = "sha256:a33cde547a2134273226fa4b853883559947ebe9207521f7afc707efbf690f58", size = 280990 }
|
379 |
+
wheels = [
|
380 |
+
{ url = "https://files.pythonhosted.org/packages/6a/c7/e2d82e6702e2a9e2311c138f8e1100f21d08aed0231290872b229ae57a86/google_auth-2.40.2-py2.py3-none-any.whl", hash = "sha256:f7e568d42eedfded58734f6a60c58321896a621f7c116c411550a4b4a13da90b", size = 216102 },
|
381 |
+
]
|
382 |
+
|
383 |
+
[[package]]
|
384 |
+
name = "google-cloud-core"
|
385 |
+
version = "2.4.3"
|
386 |
+
source = { registry = "https://pypi.org/simple" }
|
387 |
+
dependencies = [
|
388 |
+
{ name = "google-api-core" },
|
389 |
+
{ name = "google-auth" },
|
390 |
+
]
|
391 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d6/b8/2b53838d2acd6ec6168fd284a990c76695e84c65deee79c9f3a4276f6b4f/google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53", size = 35861 }
|
392 |
+
wheels = [
|
393 |
+
{ url = "https://files.pythonhosted.org/packages/40/86/bda7241a8da2d28a754aad2ba0f6776e35b67e37c36ae0c45d49370f1014/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e", size = 29348 },
|
394 |
+
]
|
395 |
+
|
396 |
+
[[package]]
|
397 |
+
name = "google-cloud-storage"
|
398 |
+
version = "3.1.0"
|
399 |
+
source = { registry = "https://pypi.org/simple" }
|
400 |
+
dependencies = [
|
401 |
+
{ name = "google-api-core" },
|
402 |
+
{ name = "google-auth" },
|
403 |
+
{ name = "google-cloud-core" },
|
404 |
+
{ name = "google-crc32c" },
|
405 |
+
{ name = "google-resumable-media" },
|
406 |
+
{ name = "requests" },
|
407 |
+
]
|
408 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f3/08/52143124415a889bbab60a8ecede1e31ea0e8d992ca078317886f26dc3be/google_cloud_storage-3.1.0.tar.gz", hash = "sha256:944273179897c7c8a07ee15f2e6466a02da0c7c4b9ecceac2a26017cb2972049", size = 7666527 }
|
409 |
+
wheels = [
|
410 |
+
{ url = "https://files.pythonhosted.org/packages/13/b8/c99c965659f45efa73080477c49ffddf7b9aecb00806be8422560bb5b824/google_cloud_storage-3.1.0-py2.py3-none-any.whl", hash = "sha256:eaf36966b68660a9633f03b067e4a10ce09f1377cae3ff9f2c699f69a81c66c6", size = 174861 },
|
411 |
+
]
|
412 |
+
|
413 |
+
[[package]]
|
414 |
+
name = "google-crc32c"
|
415 |
+
version = "1.7.1"
|
416 |
+
source = { registry = "https://pypi.org/simple" }
|
417 |
+
sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495 }
|
418 |
+
wheels = [
|
419 |
+
{ url = "https://files.pythonhosted.org/packages/f7/94/220139ea87822b6fdfdab4fb9ba81b3fff7ea2c82e2af34adc726085bffc/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6fbab4b935989e2c3610371963ba1b86afb09537fd0c633049be82afe153ac06", size = 30468 },
|
420 |
+
{ url = "https://files.pythonhosted.org/packages/94/97/789b23bdeeb9d15dc2904660463ad539d0318286d7633fe2760c10ed0c1c/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ed66cbe1ed9cbaaad9392b5259b3eba4a9e565420d734e6238813c428c3336c9", size = 30313 },
|
421 |
+
{ url = "https://files.pythonhosted.org/packages/81/b8/976a2b843610c211e7ccb3e248996a61e87dbb2c09b1499847e295080aec/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6547b657621b6cbed3562ea7826c3e11cab01cd33b74e1f677690652883e77", size = 33048 },
|
422 |
+
{ url = "https://files.pythonhosted.org/packages/c9/16/a3842c2cf591093b111d4a5e2bfb478ac6692d02f1b386d2a33283a19dc9/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d68e17bad8f7dd9a49181a1f5a8f4b251c6dbc8cc96fb79f1d321dfd57d66f53", size = 32669 },
|
423 |
+
{ url = "https://files.pythonhosted.org/packages/04/17/ed9aba495916fcf5fe4ecb2267ceb851fc5f273c4e4625ae453350cfd564/google_crc32c-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:6335de12921f06e1f774d0dd1fbea6bf610abe0887a1638f64d694013138be5d", size = 33476 },
|
424 |
+
{ url = "https://files.pythonhosted.org/packages/dd/b7/787e2453cf8639c94b3d06c9d61f512234a82e1d12d13d18584bd3049904/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194", size = 30470 },
|
425 |
+
{ url = "https://files.pythonhosted.org/packages/ed/b4/6042c2b0cbac3ec3a69bb4c49b28d2f517b7a0f4a0232603c42c58e22b44/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e", size = 30315 },
|
426 |
+
{ url = "https://files.pythonhosted.org/packages/29/ad/01e7a61a5d059bc57b702d9ff6a18b2585ad97f720bd0a0dbe215df1ab0e/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337", size = 33180 },
|
427 |
+
{ url = "https://files.pythonhosted.org/packages/3b/a5/7279055cf004561894ed3a7bfdf5bf90a53f28fadd01af7cd166e88ddf16/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65", size = 32794 },
|
428 |
+
{ url = "https://files.pythonhosted.org/packages/0f/d6/77060dbd140c624e42ae3ece3df53b9d811000729a5c821b9fd671ceaac6/google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6", size = 33477 },
|
429 |
+
{ url = "https://files.pythonhosted.org/packages/8b/72/b8d785e9184ba6297a8620c8a37cf6e39b81a8ca01bb0796d7cbb28b3386/google_crc32c-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:df8b38bdaf1629d62d51be8bdd04888f37c451564c2042d36e5812da9eff3c35", size = 30467 },
|
430 |
+
{ url = "https://files.pythonhosted.org/packages/34/25/5f18076968212067c4e8ea95bf3b69669f9fc698476e5f5eb97d5b37999f/google_crc32c-1.7.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:e42e20a83a29aa2709a0cf271c7f8aefaa23b7ab52e53b322585297bb94d4638", size = 30309 },
|
431 |
+
{ url = "https://files.pythonhosted.org/packages/92/83/9228fe65bf70e93e419f38bdf6c5ca5083fc6d32886ee79b450ceefd1dbd/google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:905a385140bf492ac300026717af339790921f411c0dfd9aa5a9e69a08ed32eb", size = 33133 },
|
432 |
+
{ url = "https://files.pythonhosted.org/packages/c3/ca/1ea2fd13ff9f8955b85e7956872fdb7050c4ace8a2306a6d177edb9cf7fe/google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b211ddaf20f7ebeec5c333448582c224a7c90a9d98826fbab82c0ddc11348e6", size = 32773 },
|
433 |
+
{ url = "https://files.pythonhosted.org/packages/89/32/a22a281806e3ef21b72db16f948cad22ec68e4bdd384139291e00ff82fe2/google_crc32c-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:0f99eaa09a9a7e642a61e06742856eec8b19fc0037832e03f941fe7cf0c8e4db", size = 33475 },
|
434 |
+
{ url = "https://files.pythonhosted.org/packages/b8/c5/002975aff514e57fc084ba155697a049b3f9b52225ec3bc0f542871dd524/google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32d1da0d74ec5634a05f53ef7df18fc646666a25efaaca9fc7dcfd4caf1d98c3", size = 33243 },
|
435 |
+
{ url = "https://files.pythonhosted.org/packages/61/cb/c585282a03a0cea70fcaa1bf55d5d702d0f2351094d663ec3be1c6c67c52/google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e10554d4abc5238823112c2ad7e4560f96c7bf3820b202660373d769d9e6e4c9", size = 32870 },
|
436 |
+
{ url = "https://files.pythonhosted.org/packages/16/1b/1693372bf423ada422f80fd88260dbfd140754adb15cbc4d7e9a68b1cb8e/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85fef7fae11494e747c9fd1359a527e5970fc9603c90764843caabd3a16a0a48", size = 28241 },
|
437 |
+
{ url = "https://files.pythonhosted.org/packages/fd/3c/2a19a60a473de48717b4efb19398c3f914795b64a96cf3fbe82588044f78/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efb97eb4369d52593ad6f75e7e10d053cf00c48983f7a973105bc70b0ac4d82", size = 28048 },
|
438 |
+
]
|
439 |
+
|
440 |
+
[[package]]
|
441 |
+
name = "google-resumable-media"
|
442 |
+
version = "2.7.2"
|
443 |
+
source = { registry = "https://pypi.org/simple" }
|
444 |
+
dependencies = [
|
445 |
+
{ name = "google-crc32c" },
|
446 |
+
]
|
447 |
+
sdist = { url = "https://files.pythonhosted.org/packages/58/5a/0efdc02665dca14e0837b62c8a1a93132c264bd02054a15abb2218afe0ae/google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0", size = 2163099 }
|
448 |
+
wheels = [
|
449 |
+
{ url = "https://files.pythonhosted.org/packages/82/35/b8d3baf8c46695858cb9d8835a53baa1eeb9906ddaf2f728a5f5b640fd1e/google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa", size = 81251 },
|
450 |
+
]
|
451 |
+
|
452 |
+
[[package]]
|
453 |
+
name = "googleapis-common-protos"
|
454 |
+
version = "1.70.0"
|
455 |
+
source = { registry = "https://pypi.org/simple" }
|
456 |
+
dependencies = [
|
457 |
+
{ name = "protobuf" },
|
458 |
+
]
|
459 |
+
sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903 }
|
460 |
+
wheels = [
|
461 |
+
{ url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530 },
|
462 |
+
]
|
463 |
+
|
464 |
[[package]]
|
465 |
name = "gradio"
|
466 |
version = "5.30.0"
|
|
|
518 |
{ url = "https://files.pythonhosted.org/packages/55/6f/03eb8e0e0ec80eced5ed35a63376dabfc7391b1538502f8e85e9dc5bab02/gradio_client-1.10.1-py3-none-any.whl", hash = "sha256:fcff53f6aad3dfa9dd082adedb94256172d6b20666b1ef66480d82023e1907db", size = 323141 },
|
519 |
]
|
520 |
|
521 |
+
[[package]]
|
522 |
+
name = "gradio-molecule3d"
|
523 |
+
version = "0.0.7"
|
524 |
+
source = { registry = "https://pypi.org/simple" }
|
525 |
+
dependencies = [
|
526 |
+
{ name = "gradio" },
|
527 |
+
]
|
528 |
+
sdist = { url = "https://files.pythonhosted.org/packages/4a/01/7ee3f6909385210fda7129bc3330545537ec814cc3b6e101bdb8704bf5ab/gradio_molecule3d-0.0.7.tar.gz", hash = "sha256:ab3a99e806c012c02c5a081085d442944b61fefc1ad952a0ff3844dcffc7dc42", size = 1468341 }
|
529 |
+
wheels = [
|
530 |
+
{ url = "https://files.pythonhosted.org/packages/ad/04/5883e077a8ff08b353de4be28bd8517aeaba14ad80ccb4d8d9ab7fe4291d/gradio_molecule3d-0.0.7-py3-none-any.whl", hash = "sha256:e40721d68c085f66c5594bf4b758ed31a32b0745f0f3d21d0c4adc248d1a7ed0", size = 1397339 },
|
531 |
+
]
|
532 |
+
|
533 |
[[package]]
|
534 |
name = "groovy"
|
535 |
version = "0.1.2"
|
|
|
1007 |
{ url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810 },
|
1008 |
]
|
1009 |
|
1010 |
+
[[package]]
|
1011 |
+
name = "proto-plus"
|
1012 |
+
version = "1.26.1"
|
1013 |
+
source = { registry = "https://pypi.org/simple" }
|
1014 |
+
dependencies = [
|
1015 |
+
{ name = "protobuf" },
|
1016 |
+
]
|
1017 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142 }
|
1018 |
+
wheels = [
|
1019 |
+
{ url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163 },
|
1020 |
+
]
|
1021 |
+
|
1022 |
+
[[package]]
|
1023 |
+
name = "protobuf"
|
1024 |
+
version = "6.31.0"
|
1025 |
+
source = { registry = "https://pypi.org/simple" }
|
1026 |
+
sdist = { url = "https://files.pythonhosted.org/packages/13/48/718c1e104a2e89970a8ff3b06d87e152834b576c570a6908f8c17ba88d65/protobuf-6.31.0.tar.gz", hash = "sha256:314fab1a6a316469dc2dd46f993cbbe95c861ea6807da910becfe7475bc26ffe", size = 441644 }
|
1027 |
+
wheels = [
|
1028 |
+
{ url = "https://files.pythonhosted.org/packages/b6/77/8671682038b08237c927215fa3296bc1c54e4086fe542c87017c1b626663/protobuf-6.31.0-cp310-abi3-win32.whl", hash = "sha256:10bd62802dfa0588649740a59354090eaf54b8322f772fbdcca19bc78d27f0d6", size = 423437 },
|
1029 |
+
{ url = "https://files.pythonhosted.org/packages/e4/07/cc9b0cbf7593f6ef8cf87fa9b0e55cd74c5cb526dd89ad84aa7d6547ef8d/protobuf-6.31.0-cp310-abi3-win_amd64.whl", hash = "sha256:3e987c99fd634be8347246a02123250f394ba20573c953de133dc8b2c107dd71", size = 435118 },
|
1030 |
+
{ url = "https://files.pythonhosted.org/packages/21/46/33f884aa8bc59114dc97e0d954ca4618c556483670236008c88fbb7e834f/protobuf-6.31.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2c812f0f96ceb6b514448cefeb1df54ec06dde456783f5099c0e2f8a0f2caa89", size = 425439 },
|
1031 |
+
{ url = "https://files.pythonhosted.org/packages/9b/f2/9a676b50229ce37b12777d7b21de90ae7bc0f9505d07e72e2e8d47b8d165/protobuf-6.31.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:67ce50195e4e584275623b8e6bc6d3d3dfd93924bf6116b86b3b8975ab9e4571", size = 321950 },
|
1032 |
+
{ url = "https://files.pythonhosted.org/packages/a1/a7/243fa2d3c1b7675d54744b32dacf30356f4c27c0d3ad940ca8745a1c6b2c/protobuf-6.31.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:5353e38844168a327acd2b2aa440044411cd8d1b6774d5701008bd1dba067c79", size = 320904 },
|
1033 |
+
{ url = "https://files.pythonhosted.org/packages/ee/01/1ed1d482960a5718fd99c82f6d79120181947cfd4667ec3944d448ed44a3/protobuf-6.31.0-py3-none-any.whl", hash = "sha256:6ac2e82556e822c17a8d23aa1190bbc1d06efb9c261981da95c71c9da09e9e23", size = 168558 },
|
1034 |
+
]
|
1035 |
+
|
1036 |
[[package]]
|
1037 |
name = "ptyprocess"
|
1038 |
version = "0.7.0"
|
|
|
1051 |
{ url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 },
|
1052 |
]
|
1053 |
|
1054 |
+
[[package]]
|
1055 |
+
name = "pyasn1"
|
1056 |
+
version = "0.6.1"
|
1057 |
+
source = { registry = "https://pypi.org/simple" }
|
1058 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 }
|
1059 |
+
wheels = [
|
1060 |
+
{ url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 },
|
1061 |
+
]
|
1062 |
+
|
1063 |
+
[[package]]
|
1064 |
+
name = "pyasn1-modules"
|
1065 |
+
version = "0.4.2"
|
1066 |
+
source = { registry = "https://pypi.org/simple" }
|
1067 |
+
dependencies = [
|
1068 |
+
{ name = "pyasn1" },
|
1069 |
+
]
|
1070 |
+
sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 }
|
1071 |
+
wheels = [
|
1072 |
+
{ url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 },
|
1073 |
+
]
|
1074 |
+
|
1075 |
[[package]]
|
1076 |
name = "pydantic"
|
1077 |
+
version = "2.11.5"
|
1078 |
source = { registry = "https://pypi.org/simple" }
|
1079 |
dependencies = [
|
1080 |
{ name = "annotated-types" },
|
1081 |
{ name = "pydantic-core" },
|
1082 |
{ name = "typing-extensions" },
|
1083 |
+
{ name = "typing-inspection" },
|
1084 |
]
|
1085 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102 }
|
1086 |
wheels = [
|
1087 |
+
{ url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229 },
|
1088 |
]
|
1089 |
|
1090 |
[[package]]
|
1091 |
name = "pydantic-core"
|
1092 |
+
version = "2.33.2"
|
1093 |
source = { registry = "https://pypi.org/simple" }
|
1094 |
dependencies = [
|
1095 |
{ name = "typing-extensions" },
|
1096 |
]
|
1097 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 }
|
1098 |
+
wheels = [
|
1099 |
+
{ url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584 },
|
1100 |
+
{ url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071 },
|
1101 |
+
{ url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823 },
|
1102 |
+
{ url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792 },
|
1103 |
+
{ url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338 },
|
1104 |
+
{ url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998 },
|
1105 |
+
{ url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200 },
|
1106 |
+
{ url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890 },
|
1107 |
+
{ url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359 },
|
1108 |
+
{ url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883 },
|
1109 |
+
{ url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074 },
|
1110 |
+
{ url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538 },
|
1111 |
+
{ url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909 },
|
1112 |
+
{ url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786 },
|
1113 |
+
{ url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 },
|
1114 |
+
{ url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 },
|
1115 |
+
{ url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 },
|
1116 |
+
{ url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 },
|
1117 |
+
{ url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 },
|
1118 |
+
{ url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 },
|
1119 |
+
{ url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 },
|
1120 |
+
{ url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 },
|
1121 |
+
{ url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 },
|
1122 |
+
{ url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 },
|
1123 |
+
{ url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 },
|
1124 |
+
{ url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 },
|
1125 |
+
{ url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 },
|
1126 |
+
{ url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 },
|
1127 |
+
{ url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 },
|
1128 |
+
{ url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 },
|
1129 |
+
{ url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 },
|
1130 |
+
{ url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 },
|
1131 |
+
{ url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 },
|
1132 |
+
{ url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 },
|
1133 |
+
{ url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 },
|
1134 |
+
{ url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 },
|
1135 |
+
{ url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 },
|
1136 |
+
{ url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 },
|
1137 |
+
{ url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 },
|
1138 |
+
{ url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 },
|
1139 |
+
{ url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 },
|
1140 |
+
{ url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 },
|
1141 |
+
{ url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 },
|
1142 |
+
{ url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 },
|
1143 |
+
{ url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 },
|
1144 |
+
{ url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200 },
|
1145 |
+
{ url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123 },
|
1146 |
+
{ url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852 },
|
1147 |
+
{ url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484 },
|
1148 |
+
{ url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896 },
|
1149 |
+
{ url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475 },
|
1150 |
+
{ url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013 },
|
1151 |
+
{ url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715 },
|
1152 |
+
{ url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757 },
|
1153 |
]
|
1154 |
|
1155 |
[[package]]
|
|
|
1182 |
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
|
1183 |
]
|
1184 |
|
1185 |
+
[[package]]
|
1186 |
+
name = "python-dotenv"
|
1187 |
+
version = "1.1.0"
|
1188 |
+
source = { registry = "https://pypi.org/simple" }
|
1189 |
+
sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 }
|
1190 |
+
wheels = [
|
1191 |
+
{ url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 },
|
1192 |
+
]
|
1193 |
+
|
1194 |
[[package]]
|
1195 |
name = "python-multipart"
|
1196 |
version = "0.0.20"
|
|
|
1272 |
{ url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 },
|
1273 |
]
|
1274 |
|
1275 |
+
[[package]]
|
1276 |
+
name = "rsa"
|
1277 |
+
version = "4.9.1"
|
1278 |
+
source = { registry = "https://pypi.org/simple" }
|
1279 |
+
dependencies = [
|
1280 |
+
{ name = "pyasn1" },
|
1281 |
+
]
|
1282 |
+
sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034 }
|
1283 |
+
wheels = [
|
1284 |
+
{ url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696 },
|
1285 |
+
]
|
1286 |
+
|
1287 |
[[package]]
|
1288 |
name = "ruff"
|
1289 |
version = "0.11.10"
|
|
|
1415 |
|
1416 |
[[package]]
|
1417 |
name = "typer"
|
1418 |
+
version = "0.15.4"
|
1419 |
source = { registry = "https://pypi.org/simple" }
|
1420 |
dependencies = [
|
1421 |
{ name = "click" },
|
|
|
1423 |
{ name = "shellingham" },
|
1424 |
{ name = "typing-extensions" },
|
1425 |
]
|
1426 |
+
sdist = { url = "https://files.pythonhosted.org/packages/6c/89/c527e6c848739be8ceb5c44eb8208c52ea3515c6cf6406aa61932887bf58/typer-0.15.4.tar.gz", hash = "sha256:89507b104f9b6a0730354f27c39fae5b63ccd0c95b1ce1f1a6ba0cfd329997c3", size = 101559 }
|
1427 |
wheels = [
|
1428 |
+
{ url = "https://files.pythonhosted.org/packages/c9/62/d4ba7afe2096d5659ec3db8b15d8665bdcb92a3c6ff0b95e99895b335a9c/typer-0.15.4-py3-none-any.whl", hash = "sha256:eb0651654dcdea706780c466cf06d8f174405a659ffff8f163cfbfee98c0e173", size = 45258 },
|
1429 |
]
|
1430 |
|
1431 |
[[package]]
|
|
|
1437 |
{ url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 },
|
1438 |
]
|
1439 |
|
1440 |
+
[[package]]
|
1441 |
+
name = "typing-inspection"
|
1442 |
+
version = "0.4.1"
|
1443 |
+
source = { registry = "https://pypi.org/simple" }
|
1444 |
+
dependencies = [
|
1445 |
+
{ name = "typing-extensions" },
|
1446 |
+
]
|
1447 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 }
|
1448 |
+
wheels = [
|
1449 |
+
{ url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 },
|
1450 |
+
]
|
1451 |
+
|
1452 |
[[package]]
|
1453 |
name = "tzdata"
|
1454 |
version = "2025.2"
|