amina-cli 0.2.4__tar.gz → 0.2.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {amina_cli-0.2.4 → amina_cli-0.2.6}/PKG-INFO +1 -1
- {amina_cli-0.2.4 → amina_cli-0.2.6}/pyproject.toml +1 -1
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/__init__.py +1 -1
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/auth.py +31 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/client.py +15 -3
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/jobs_cmd.py +70 -27
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/properties/esm2_embedding.py +49 -5
- {amina_cli-0.2.4 → amina_cli-0.2.6}/.gitignore +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/LICENSE +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/README.md +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/__init__.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/auth_cmd.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/run_cmd.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/__init__.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/analysis/__init__.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/analysis/hydrophobicity.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/analysis/mmseqs2_cluster.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/analysis/rmsd.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/analysis/sasa.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/analysis/simple_rmsd.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/analysis/surface_charge.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/analysis/usalign.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/design/__init__.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/design/esm_if1.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/design/protein_mc.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/design/proteinmpnn.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/design/rfdiffusion.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/display.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/folding/__init__.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/folding/boltz2.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/folding/esmfold.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/folding/openfold3.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/folding/protenix.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/__init__.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/autodock_vina.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/diffdock.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/dockq.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/emngly.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/glycosylation_ensemble.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/interface_identifier.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/isoglyp.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/lmngly.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/p2rank.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/pesto.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/properties/__init__.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/properties/aminosol.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/properties/esm1v.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/__init__.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/activesite_verifier.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/chain_select.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/distance_calculator.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/maxit_convert.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/mol_size_calculator.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/obabel_convert.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/pdb_bfactor_overwrite.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/pdb_cleaner.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/pdb_quality_assessment.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/pdb_to_fasta.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/protein_relaxer.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools_cmd.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/main.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/registry.py +0 -0
- {amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/storage.py +0 -0
|
@@ -336,3 +336,34 @@ def update_job_status(job_id: str, status: str) -> None:
|
|
|
336
336
|
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
|
|
337
337
|
with os.fdopen(os.open(JOBS_FILE, flags, 0o600), "w") as f:
|
|
338
338
|
f.write(content)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def update_job_info(job_id: str, updates: dict) -> None:
|
|
342
|
+
"""
|
|
343
|
+
Merge fields into a saved job entry.
|
|
344
|
+
|
|
345
|
+
Used to persist a resolved call_id so subsequent polls skip the queue check.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
job_id: Job ID to update
|
|
349
|
+
updates: Dict of fields to merge (e.g., {"call_id": "fc-xxx"})
|
|
350
|
+
"""
|
|
351
|
+
import os
|
|
352
|
+
|
|
353
|
+
if not JOBS_FILE.exists():
|
|
354
|
+
return
|
|
355
|
+
|
|
356
|
+
try:
|
|
357
|
+
jobs = json.loads(JOBS_FILE.read_text())
|
|
358
|
+
except (json.JSONDecodeError, IOError):
|
|
359
|
+
return
|
|
360
|
+
|
|
361
|
+
for job in jobs:
|
|
362
|
+
if job.get("job_id") == job_id:
|
|
363
|
+
job.update(updates)
|
|
364
|
+
break
|
|
365
|
+
|
|
366
|
+
content = json.dumps(jobs, indent=2)
|
|
367
|
+
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
|
|
368
|
+
with os.fdopen(os.open(JOBS_FILE, flags, 0o600), "w") as f:
|
|
369
|
+
f.write(content)
|
|
@@ -92,10 +92,12 @@ class InsufficientCreditsError(ClientError):
|
|
|
92
92
|
class JobLimitError(ClientError):
|
|
93
93
|
"""Raised when user has reached job concurrency limits."""
|
|
94
94
|
|
|
95
|
-
def __init__(self, limit_type: str, current: int, limit: int, message: str):
|
|
95
|
+
def __init__(self, limit_type: str, current: int, limit: int, message: str, **kwargs: Any):
|
|
96
96
|
self.limit_type = limit_type
|
|
97
97
|
self.current = current
|
|
98
98
|
self.limit = limit
|
|
99
|
+
self.running = kwargs.get("running", 0)
|
|
100
|
+
self.queued = kwargs.get("queued", 0)
|
|
99
101
|
super().__init__(message)
|
|
100
102
|
|
|
101
103
|
|
|
@@ -185,6 +187,7 @@ async def _poll_queued_until_submitted(
|
|
|
185
187
|
poll_interval: float = 5.0, # Longer interval for queue polling
|
|
186
188
|
max_poll_interval: float = 30.0,
|
|
187
189
|
on_position_update: Optional[QueuePositionCallback] = None,
|
|
190
|
+
headers: Optional[dict[str, str]] = None,
|
|
188
191
|
) -> tuple[str, str]:
|
|
189
192
|
"""
|
|
190
193
|
Poll queued_job_status until job is submitted.
|
|
@@ -229,7 +232,7 @@ async def _poll_queued_until_submitted(
|
|
|
229
232
|
status_url = f"{queued_endpoint}?job_id={job_id}"
|
|
230
233
|
|
|
231
234
|
try:
|
|
232
|
-
response = await client.get(status_url)
|
|
235
|
+
response = await client.get(status_url, headers=headers or {})
|
|
233
236
|
except httpx.RequestError as e:
|
|
234
237
|
# Log transient error and continue polling
|
|
235
238
|
print(f"Queue poll network error (will retry): {type(e).__name__}: {e}")
|
|
@@ -396,6 +399,8 @@ async def run_tool(
|
|
|
396
399
|
current=detail.get("current", 0),
|
|
397
400
|
limit=detail.get("limit", 0),
|
|
398
401
|
message=detail.get("message", "Job limit reached. Please wait for running jobs to complete."),
|
|
402
|
+
running=detail.get("running", 0),
|
|
403
|
+
queued=detail.get("queued", 0),
|
|
399
404
|
)
|
|
400
405
|
except (ValueError, KeyError):
|
|
401
406
|
raise JobLimitError(
|
|
@@ -438,6 +443,7 @@ async def run_tool(
|
|
|
438
443
|
poll_interval=5.0, # Slower for queue
|
|
439
444
|
max_poll_interval=30.0,
|
|
440
445
|
on_position_update=on_queue_position_update,
|
|
446
|
+
headers=auth_headers,
|
|
441
447
|
)
|
|
442
448
|
else:
|
|
443
449
|
# Job was submitted immediately
|
|
@@ -663,6 +669,8 @@ async def submit_tool(
|
|
|
663
669
|
current=detail.get("current", 0),
|
|
664
670
|
limit=detail.get("limit", 0),
|
|
665
671
|
message=detail.get("message", "Job limit reached. Please wait for running jobs to complete."),
|
|
672
|
+
running=detail.get("running", 0),
|
|
673
|
+
queued=detail.get("queued", 0),
|
|
666
674
|
)
|
|
667
675
|
except (ValueError, KeyError):
|
|
668
676
|
raise JobLimitError(
|
|
@@ -855,12 +863,16 @@ async def check_queued_job_status(job_id: str) -> dict[str, Any]:
|
|
|
855
863
|
- {"status": "submitted", "call_id": "..."} if spawned
|
|
856
864
|
- {"status": "failed", "error": "..."} if failed/expired
|
|
857
865
|
"""
|
|
866
|
+
from amina_cli.auth import get_api_key
|
|
867
|
+
|
|
858
868
|
queued_endpoint = get_queued_status_endpoint()
|
|
859
869
|
status_url = f"{queued_endpoint}?job_id={job_id}"
|
|
870
|
+
api_key = get_api_key()
|
|
871
|
+
headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
|
|
860
872
|
|
|
861
873
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
862
874
|
try:
|
|
863
|
-
response = await client.get(status_url)
|
|
875
|
+
response = await client.get(status_url, headers=headers)
|
|
864
876
|
except httpx.RequestError as e:
|
|
865
877
|
raise ToolExecutionError(f"Network error checking queued job status: {str(e)}")
|
|
866
878
|
|
|
@@ -21,6 +21,65 @@ app = typer.Typer(no_args_is_help=True)
|
|
|
21
21
|
console = Console()
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
def _resolve_job_status(job_info: dict) -> dict:
|
|
25
|
+
"""
|
|
26
|
+
Resolve the current status of a job, handling both spawned and queued jobs.
|
|
27
|
+
|
|
28
|
+
If call_id is present, polls Modal directly. If missing (queued job),
|
|
29
|
+
checks the queue first and resolves call_id when available.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
job_info: Full job info dict from local history
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Dict with status info (same shape as check_job_status_sync)
|
|
36
|
+
"""
|
|
37
|
+
from amina_cli.client import check_job_status_sync, check_queued_job_status_sync
|
|
38
|
+
from amina_cli.auth import update_job_info
|
|
39
|
+
|
|
40
|
+
call_id = job_info.get("call_id", "")
|
|
41
|
+
job_id = job_info.get("job_id", "")
|
|
42
|
+
|
|
43
|
+
if call_id:
|
|
44
|
+
# Normal path: job was spawned, poll Modal directly
|
|
45
|
+
return check_job_status_sync(
|
|
46
|
+
call_id=call_id,
|
|
47
|
+
job_id=job_id,
|
|
48
|
+
user_id=job_info.get("user_id", ""),
|
|
49
|
+
tool_name=job_info.get("tool_name", ""),
|
|
50
|
+
reserved_cost=job_info.get("reserved_cost", 0.0),
|
|
51
|
+
compute_type=job_info.get("compute_type", "cpu"),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Queued path: no call_id yet, check queue status
|
|
55
|
+
queue_result = check_queued_job_status_sync(job_id)
|
|
56
|
+
queue_status = queue_result.get("status", "")
|
|
57
|
+
|
|
58
|
+
if queue_status in ("queued", "not_found"):
|
|
59
|
+
return queue_result
|
|
60
|
+
|
|
61
|
+
if queue_status == "failed":
|
|
62
|
+
return queue_result
|
|
63
|
+
|
|
64
|
+
# Job has been spawned (submitted/running/completed) — try to get call_id
|
|
65
|
+
resolved_call_id = queue_result.get("call_id", "")
|
|
66
|
+
if not resolved_call_id:
|
|
67
|
+
# Race condition: status changed but call_id not yet written
|
|
68
|
+
return {"status": "queued", "message": "Job is being spawned"}
|
|
69
|
+
|
|
70
|
+
# Persist call_id so future polls skip the queue check
|
|
71
|
+
update_job_info(job_id, {"call_id": resolved_call_id})
|
|
72
|
+
|
|
73
|
+
return check_job_status_sync(
|
|
74
|
+
call_id=resolved_call_id,
|
|
75
|
+
job_id=job_id,
|
|
76
|
+
user_id=job_info.get("user_id", ""),
|
|
77
|
+
tool_name=job_info.get("tool_name", ""),
|
|
78
|
+
reserved_cost=job_info.get("reserved_cost", 0.0),
|
|
79
|
+
compute_type=job_info.get("compute_type", "cpu"),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
24
83
|
@app.command("list")
|
|
25
84
|
def list_jobs(
|
|
26
85
|
limit: int = typer.Option(
|
|
@@ -101,7 +160,7 @@ def status(
|
|
|
101
160
|
amina jobs status abc123 --json
|
|
102
161
|
"""
|
|
103
162
|
from amina_cli.auth import get_job_info
|
|
104
|
-
from amina_cli.client import
|
|
163
|
+
from amina_cli.client import ToolExecutionError
|
|
105
164
|
|
|
106
165
|
results = []
|
|
107
166
|
|
|
@@ -121,14 +180,7 @@ def status(
|
|
|
121
180
|
|
|
122
181
|
# Query server for current status
|
|
123
182
|
try:
|
|
124
|
-
status_result =
|
|
125
|
-
call_id=job_info.get("call_id", ""),
|
|
126
|
-
job_id=job_info.get("job_id", job_id),
|
|
127
|
-
user_id=job_info.get("user_id", ""),
|
|
128
|
-
tool_name=job_info.get("tool_name", ""),
|
|
129
|
-
reserved_cost=job_info.get("reserved_cost", 0.0),
|
|
130
|
-
compute_type=job_info.get("compute_type", "cpu"),
|
|
131
|
-
)
|
|
183
|
+
status_result = _resolve_job_status(job_info)
|
|
132
184
|
results.append(
|
|
133
185
|
{
|
|
134
186
|
"job_id": job_info.get("job_id", job_id),
|
|
@@ -212,7 +264,7 @@ def wait(
|
|
|
212
264
|
amina jobs wait abc123 --timeout 7200 --poll-interval 30
|
|
213
265
|
"""
|
|
214
266
|
from amina_cli.auth import get_job_info
|
|
215
|
-
from amina_cli.client import
|
|
267
|
+
from amina_cli.client import ToolExecutionError
|
|
216
268
|
|
|
217
269
|
start_time = time.time()
|
|
218
270
|
pending_jobs = set(job_ids)
|
|
@@ -237,14 +289,7 @@ def wait(
|
|
|
237
289
|
continue
|
|
238
290
|
|
|
239
291
|
try:
|
|
240
|
-
status_result =
|
|
241
|
-
call_id=job_info.get("call_id", ""),
|
|
242
|
-
job_id=job_info.get("job_id", job_id),
|
|
243
|
-
user_id=job_info.get("user_id", ""),
|
|
244
|
-
tool_name=job_info.get("tool_name", ""),
|
|
245
|
-
reserved_cost=job_info.get("reserved_cost", 0.0),
|
|
246
|
-
compute_type=job_info.get("compute_type", "cpu"),
|
|
247
|
-
)
|
|
292
|
+
status_result = _resolve_job_status(job_info)
|
|
248
293
|
|
|
249
294
|
if status_result.get("status") in ("completed", "failed"):
|
|
250
295
|
results[job_id] = {
|
|
@@ -312,7 +357,7 @@ def download(
|
|
|
312
357
|
amina jobs download abc123 -o ./results/
|
|
313
358
|
"""
|
|
314
359
|
from amina_cli.auth import get_job_info
|
|
315
|
-
from amina_cli.client import
|
|
360
|
+
from amina_cli.client import ToolExecutionError
|
|
316
361
|
from amina_cli.storage import download_results, StorageError
|
|
317
362
|
|
|
318
363
|
# Look up job info
|
|
@@ -324,18 +369,16 @@ def download(
|
|
|
324
369
|
|
|
325
370
|
# Check status
|
|
326
371
|
try:
|
|
327
|
-
status_result =
|
|
328
|
-
call_id=job_info.get("call_id", ""),
|
|
329
|
-
job_id=job_info.get("job_id", job_id),
|
|
330
|
-
user_id=job_info.get("user_id", ""),
|
|
331
|
-
tool_name=job_info.get("tool_name", ""),
|
|
332
|
-
reserved_cost=job_info.get("reserved_cost", 0.0),
|
|
333
|
-
compute_type=job_info.get("compute_type", "cpu"),
|
|
334
|
-
)
|
|
372
|
+
status_result = _resolve_job_status(job_info)
|
|
335
373
|
except ToolExecutionError as e:
|
|
336
374
|
console.print(f"[red]Error checking job status:[/red] {e}")
|
|
337
375
|
raise typer.Exit(1)
|
|
338
376
|
|
|
377
|
+
if status_result.get("status") == "queued":
|
|
378
|
+
console.print("[yellow]Job is still queued.[/yellow] Wait for completion first:")
|
|
379
|
+
console.print(f" amina jobs wait {job_id}")
|
|
380
|
+
raise typer.Exit(1)
|
|
381
|
+
|
|
339
382
|
if status_result.get("status") == "running":
|
|
340
383
|
console.print("[yellow]Job is still running.[/yellow] Wait for completion first:")
|
|
341
384
|
console.print(f" amina jobs wait {job_id}")
|
{amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/properties/esm2_embedding.py
RENAMED
|
@@ -9,16 +9,20 @@ METADATA = {
|
|
|
9
9
|
"name": "esm2-embedding",
|
|
10
10
|
"display_name": "ESM2 Embedding",
|
|
11
11
|
"category": "properties",
|
|
12
|
-
"description": "Extract
|
|
12
|
+
"description": "Extract protein embeddings from FASTA files using ESM2 models",
|
|
13
13
|
"modal_function_name": "esm2_embedding_worker",
|
|
14
14
|
"modal_app_name": "esm2-embedding-api",
|
|
15
15
|
"status": "available",
|
|
16
16
|
"outputs": {
|
|
17
|
+
# Sequence mode outputs
|
|
17
18
|
"csv_filepath": "CSV file with sequence embeddings (sequence_id, sequence, dim_0...dim_N)",
|
|
18
19
|
"tsne_plot_filepath": "Static t-SNE visualization of embeddings (PNG)",
|
|
19
20
|
"tsne_plot_html_filepath": "Interactive t-SNE visualization with zoom/pan/hover (HTML)",
|
|
20
21
|
"clusters_csv_filepath": "Cluster assignments (CSV, unless --no-cluster)",
|
|
21
22
|
"cluster_summary_filepath": "Cluster statistics and quality metrics (JSON, unless --no-cluster)",
|
|
23
|
+
# Residue mode outputs
|
|
24
|
+
"manifest_filepath": "Manifest JSON with file list and metadata (residue mode)",
|
|
25
|
+
"residue_N": "Per-sequence NPZ files with residue embeddings (residue mode)",
|
|
22
26
|
},
|
|
23
27
|
}
|
|
24
28
|
|
|
@@ -76,6 +80,17 @@ def register(app: typer.Typer):
|
|
|
76
80
|
"--cluster-min-samples",
|
|
77
81
|
help="Min samples for density estimation (auto-tuned if not specified)",
|
|
78
82
|
),
|
|
83
|
+
embedding_mode: str = typer.Option(
|
|
84
|
+
"sequence",
|
|
85
|
+
"--mode",
|
|
86
|
+
"-M",
|
|
87
|
+
help="Embedding mode: 'sequence' (default) for mean-pooled embeddings, 'residue' for per-residue embeddings",
|
|
88
|
+
),
|
|
89
|
+
include_special_tokens: bool = typer.Option(
|
|
90
|
+
False,
|
|
91
|
+
"--include-special-tokens",
|
|
92
|
+
help="Include BOS/EOS tokens in residue embeddings (residue mode only)",
|
|
93
|
+
),
|
|
79
94
|
job_name: Optional[str] = typer.Option(
|
|
80
95
|
None,
|
|
81
96
|
"--job-name",
|
|
@@ -94,8 +109,11 @@ def register(app: typer.Typer):
|
|
|
94
109
|
Generates dense vector representations (embeddings) of protein sequences
|
|
95
110
|
that capture evolutionary, structural, and functional information.
|
|
96
111
|
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
Two embedding modes:
|
|
113
|
+
- sequence (default): Mean-pooled sequence embeddings (CSV output)
|
|
114
|
+
with optional t-SNE visualization and HDBSCAN clustering
|
|
115
|
+
- residue: Per-residue embeddings (NPZ output per sequence)
|
|
116
|
+
with manifest JSON, no t-SNE or clustering
|
|
99
117
|
|
|
100
118
|
Model variants (embedding dimensions):
|
|
101
119
|
- 650M: 1280-dim - Highest accuracy (default)
|
|
@@ -104,11 +122,15 @@ def register(app: typer.Typer):
|
|
|
104
122
|
- 8M: 320-dim - Fastest, lower accuracy
|
|
105
123
|
|
|
106
124
|
Examples:
|
|
125
|
+
# Sequence mode (default)
|
|
107
126
|
amina run esm2-embedding --fasta sequences.fasta -o ./output/
|
|
108
127
|
amina run esm2-embedding -f seqs.fa -m 8M -o ./output/ # Fast mode
|
|
109
128
|
amina run esm2-embedding -f seqs.fa --no-tsne -o ./output/
|
|
110
|
-
amina run esm2-embedding -f seqs.fa --no-cluster -o ./output/
|
|
111
|
-
|
|
129
|
+
amina run esm2-embedding -f seqs.fa --no-cluster -o ./output/
|
|
130
|
+
|
|
131
|
+
# Residue mode
|
|
132
|
+
amina run esm2-embedding -f seqs.fa --mode residue -o ./output/
|
|
133
|
+
amina run esm2-embedding -f seqs.fa -M residue --include-special-tokens -o ./output/
|
|
112
134
|
"""
|
|
113
135
|
# Validate required options
|
|
114
136
|
if output is None and not background:
|
|
@@ -128,6 +150,26 @@ def register(app: typer.Typer):
|
|
|
128
150
|
console.print("[red]Error:[/red] Batch size must be between 1 and 32")
|
|
129
151
|
raise typer.Exit(1)
|
|
130
152
|
|
|
153
|
+
# Validate embedding mode
|
|
154
|
+
valid_modes = ["sequence", "residue"]
|
|
155
|
+
if embedding_mode.lower() not in valid_modes:
|
|
156
|
+
console.print(f"[red]Error:[/red] Invalid mode '{embedding_mode}'. Choose from: {', '.join(valid_modes)}")
|
|
157
|
+
raise typer.Exit(1)
|
|
158
|
+
embedding_mode = embedding_mode.lower()
|
|
159
|
+
|
|
160
|
+
# Validate mode-specific options
|
|
161
|
+
if embedding_mode == "sequence" and include_special_tokens:
|
|
162
|
+
console.print("[red]Error:[/red] --include-special-tokens only applies to residue mode")
|
|
163
|
+
raise typer.Exit(1)
|
|
164
|
+
|
|
165
|
+
if embedding_mode == "residue":
|
|
166
|
+
if not no_tsne:
|
|
167
|
+
console.print("[yellow]Note:[/yellow] t-SNE visualization is not available in residue mode (skipping)")
|
|
168
|
+
no_tsne = True
|
|
169
|
+
if not no_cluster:
|
|
170
|
+
console.print("[yellow]Note:[/yellow] Clustering is not available in residue mode (skipping)")
|
|
171
|
+
no_cluster = True
|
|
172
|
+
|
|
131
173
|
# Read FASTA content
|
|
132
174
|
fasta_content = fasta.read_text()
|
|
133
175
|
if not fasta_content.strip().startswith(">"):
|
|
@@ -148,6 +190,8 @@ def register(app: typer.Typer):
|
|
|
148
190
|
"cluster_by_embeddings": not no_cluster,
|
|
149
191
|
"cluster_min_size": cluster_min_size,
|
|
150
192
|
"cluster_min_samples": cluster_min_samples,
|
|
193
|
+
"embedding_mode": embedding_mode,
|
|
194
|
+
"include_special_tokens": include_special_tokens,
|
|
151
195
|
}
|
|
152
196
|
|
|
153
197
|
if job_name:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/analysis/mmseqs2_cluster.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/interactions/autodock_vina.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/activesite_verifier.py
RENAMED
|
File without changes
|
|
File without changes
|
{amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/distance_calculator.py
RENAMED
|
File without changes
|
|
File without changes
|
{amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/mol_size_calculator.py
RENAMED
|
File without changes
|
{amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/obabel_convert.py
RENAMED
|
File without changes
|
{amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/pdb_bfactor_overwrite.py
RENAMED
|
File without changes
|
|
File without changes
|
{amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/pdb_quality_assessment.py
RENAMED
|
File without changes
|
|
File without changes
|
{amina_cli-0.2.4 → amina_cli-0.2.6}/src/amina_cli/commands/tools/utilities/protein_relaxer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|