deepmirror 0.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 deepmirror
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: deepmirror
3
+ Version: 0.0.0
4
+ Summary: Command line interface for the deepmirror public API
5
+ Requires-Python: >=3.12
6
+ License-File: LICENSE
7
+ Requires-Dist: click>=8.1.8
8
+ Requires-Dist: requests>=2.31.0
9
+ Requires-Dist: pandas>=2.2.2
10
+ Requires-Dist: pydantic>=2.6.3
11
+ Requires-Dist: pydantic-settings>=2.9.1
12
+ Requires-Dist: scikit-learn>=1.6.1
13
+ Requires-Dist: matplotlib>=3.10.3
14
+ Requires-Dist: tqdm>=4.67.1
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
17
+ Requires-Dist: pre-commit>=3.6.0; extra == "dev"
18
+ Requires-Dist: pylint>=3.3.6; extra == "dev"
19
+ Requires-Dist: ruff>=0.11.0; extra == "dev"
20
+ Dynamic: license-file
@@ -0,0 +1,120 @@
1
+ # deepmirror-client
2
+
3
+ `deepmirror-cli` is a command-line interface for interacting with the [deepmirror API](https://api.app.deepmirror.ai/public/docs). It allows you to train models, run predictions, and submit structure prediction jobs directly from your terminal.
4
+
5
+ ---
6
+
7
+ ## 🚀 Installation
8
+
9
+ ```bash
10
+ pip install deepmirror
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 🔐 Authentication
16
+
17
+ Before using most commands, you need to log in to get your API token:
18
+
19
+ ```bash
20
+ dm login EMAIL
21
+ ```
22
+
23
+ This saves your token and host in `~/.config/deepmirror/` for reuse.
24
+
25
+ ---
26
+
27
+ ## 🧠 Model Commands
28
+
29
+ ### 📋 List Available Models
30
+
31
+ ```bash
32
+ dm model list
33
+ ```
34
+
35
+ ### 📄 View Model Metadata
36
+
37
+ ```bash
38
+ dm model metadata MODEL_ID
39
+ ```
40
+
41
+ ### 🔎 Get Full Model Info
42
+
43
+ ```bash
44
+ dm model info MODEL_ID
45
+ ```
46
+
47
+ ---
48
+
49
+ ## 🏋️ Train a Custom Model
50
+
51
+ ```bash
52
+ dm train --model-name mymodel \
53
+ --csv-file path/to/data.csv \
54
+ --smiles-column smiles \
55
+ --value-column target \
56
+ [--classification]
57
+ ```
58
+
59
+ - `--classification` enables classification mode.
60
+ - Default SMILES column is `smiles`, target column is `target`.
61
+
62
+ ---
63
+
64
+ ## 🔮 Run Inference
65
+
66
+ You can run inference using either a CSV file or direct SMILES input:
67
+
68
+ ```bash
69
+ # From a CSV or TXT file
70
+ dm predict --model-name mymodel --csv-file inputs.csv
71
+
72
+ # Direct SMILES
73
+ dm predict --model-name mymodel --smiles "CCO"
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 🧬 Structure Prediction
79
+
80
+ ### 🧠 Predict Protein-Ligand Structure
81
+
82
+ ```bash
83
+ dm structure predict protein.pdb ligand.sdf --model chai
84
+ ```
85
+
86
+ - Default model is `chai`.
87
+ - Protein and ligand must be valid file paths.
88
+
89
+ ### 📦 Download Prediction Result
90
+
91
+ ```bash
92
+ dm structure download TASK_ID result.zip
93
+ ```
94
+
95
+ ### 📃 List Submitted Jobs
96
+
97
+ ```bash
98
+ dm structure list
99
+ ```
100
+
101
+ ---
102
+
103
+ ## ⚙️ Configuration
104
+
105
+ - API host and token are saved under `~/.config/deepmirror/`.
106
+ - You can override the API host:
107
+
108
+ - via `--host` option on any command
109
+ - or by setting `DEEPMIRROR_API_ENV=local`
110
+
111
+ ---
112
+
113
+ ## 💡 Tips
114
+
115
+ - If a token is missing or expired, commands will prompt you to log in again.
116
+ - Use `--help` on any command for more details, e.g.:
117
+
118
+ ```bash
119
+ dm train --help
120
+ ```
@@ -0,0 +1,31 @@
1
+ """deepmirror CLI package."""
2
+
3
+ from .api import (
4
+ authenticate,
5
+ download_structure_prediction,
6
+ get_predict_hlm,
7
+ list_models,
8
+ list_structure_tasks,
9
+ model_info,
10
+ model_metadata,
11
+ predict,
12
+ predict_hlm,
13
+ save_token,
14
+ structure_predict,
15
+ train,
16
+ )
17
+
18
+ __all__ = [
19
+ "authenticate",
20
+ "list_models",
21
+ "predict",
22
+ "save_token",
23
+ "list_structure_tasks",
24
+ "download_structure_prediction",
25
+ "structure_predict",
26
+ "train",
27
+ "model_metadata",
28
+ "predict_hlm",
29
+ "get_predict_hlm",
30
+ "model_info",
31
+ ]
@@ -0,0 +1,264 @@
1
+ """deepmirror API client for interacting with the deepmirror platform.
2
+
3
+ This module provides functions for authentication, model management, and inference
4
+ using the deepmirror API. It handles API token management and provides a clean
5
+ interface for making API requests.
6
+ """
7
+
8
+ from typing import Any
9
+
10
+ import pandas as pd
11
+ import requests
12
+
13
+ from .config import settings
14
+
15
+
16
+ def save_token(token: str) -> None:
17
+ """Save the API token to the config directory."""
18
+ settings.CONFIG_DIR.mkdir(parents=True, exist_ok=True)
19
+ settings.TOKEN_FILE.write_text(token)
20
+ settings.TOKEN_FILE.chmod(0o600)
21
+ print(f"API token saved to {settings.TOKEN_FILE}")
22
+
23
+
24
+ def load_token() -> str:
25
+ """Load the API token from the config directory."""
26
+ if not settings.TOKEN_FILE.exists():
27
+ raise RuntimeError("API token not found; please login or provide one")
28
+ return settings.TOKEN_FILE.read_text().strip()
29
+
30
+
31
+ def authenticate(username: str, password: str) -> str:
32
+ """Authenticate with the deepmirror API."""
33
+ host = settings.HOST
34
+ url = f"{host}/api/v3/public/authenticate?token_lifetime_minutes=720"
35
+ data = {
36
+ "grant_type": "password",
37
+ "username": username,
38
+ "password": password,
39
+ "scope": "",
40
+ "client_id": "string",
41
+ "client_secret": "string",
42
+ }
43
+ headers = {
44
+ "accept": "application/json",
45
+ "Content-Type": "application/x-www-form-urlencoded",
46
+ }
47
+ response = requests.post(url, data=data, headers=headers, timeout=29)
48
+ if response.status_code != 200:
49
+ raise RuntimeError(f"Login failed: {response.text}")
50
+ return response.json().get("api_token")
51
+
52
+
53
+ def list_models() -> Any:
54
+ """List all models."""
55
+ token = load_token()
56
+ url = f"{settings.HOST}/api/v3/public/models/"
57
+ response = requests.post(url, json={"api_token": token}, timeout=29)
58
+ if response.status_code != 200:
59
+ raise RuntimeError(response.text)
60
+ return response.json()
61
+
62
+
63
+ def train(
64
+ model_name: str,
65
+ csv_file: str,
66
+ smiles_column: str,
67
+ value_column: str,
68
+ classification: bool = False,
69
+ ) -> Any:
70
+ """Train a model."""
71
+ token = load_token()
72
+ df = pd.read_csv(csv_file)
73
+ if smiles_column not in df.columns or value_column not in df.columns:
74
+ raise ValueError("CSV missing required columns")
75
+ x = df[smiles_column].astype(str).tolist()
76
+ y = df[value_column].astype(float).tolist()
77
+ payload = {
78
+ "model_name": model_name,
79
+ "api_token": token,
80
+ "x": x,
81
+ "y": y,
82
+ "is_classification": classification,
83
+ }
84
+ response = requests.post(
85
+ f"{settings.HOST}/api/v3/public/train/", json=payload, timeout=29
86
+ )
87
+ if response.status_code != 200:
88
+ raise RuntimeError(response.text)
89
+ return response.json()
90
+
91
+
92
+ def predict_hlm(
93
+ smiles: list[str],
94
+ ) -> Any:
95
+ """Predict using HLM."""
96
+ token = load_token()
97
+ payload = {
98
+ "api_token": token,
99
+ "x": smiles,
100
+ }
101
+
102
+ response = requests.post(
103
+ f"{settings.HOST}/api/v3/public/predict-hlm/", json=payload, timeout=29
104
+ )
105
+ if response.status_code != 200:
106
+ raise RuntimeError(response.text)
107
+ return response.json()
108
+
109
+
110
+ def get_predict_hlm(
111
+ task_id: str,
112
+ ) -> Any:
113
+ """Get predictions from HLM."""
114
+ token = load_token()
115
+ response = requests.post(
116
+ f"{settings.HOST}/api/v3/public/predict-hlm/{task_id}",
117
+ json={"api_token": token},
118
+ timeout=29,
119
+ )
120
+
121
+ if response.status_code not in [200, 202]:
122
+ raise RuntimeError(response.text)
123
+ return response.json()
124
+
125
+
126
+ def predict(
127
+ model_name: str,
128
+ csv_file: str | None = None,
129
+ smiles_column: str | None = None,
130
+ smiles: list[str] | None = None,
131
+ ) -> Any:
132
+ """Predict using a model."""
133
+ token = load_token()
134
+ # Handle direct SMILES input
135
+ if smiles is not None:
136
+ inputs = smiles
137
+ # Handle CSV input with pandas
138
+ elif csv_file and csv_file.endswith(".csv") and smiles_column:
139
+ df = pd.read_csv(csv_file)
140
+ if smiles_column not in df.columns:
141
+ raise ValueError(f"CSV missing required column: {smiles_column}")
142
+ inputs = df[smiles_column].astype(str).tolist()
143
+ else:
144
+ raise ValueError(
145
+ "Either csv_file & smiles_column or smiles must be provided"
146
+ )
147
+
148
+ payload = {"model_name": model_name, "input": inputs, "api_token": token}
149
+ response = requests.post(
150
+ f"{settings.HOST}/api/v3/public/inference/", json=payload, timeout=29
151
+ )
152
+ if response.status_code != 200:
153
+ raise RuntimeError(response.text)
154
+
155
+ predictions = response.json()
156
+
157
+ # Add predictions to CSV if using CSV input
158
+ if csv_file and csv_file.endswith(".csv") and smiles_column:
159
+ df["prediction"] = predictions["prediction"]
160
+ df["confidence"] = predictions["confidence"]
161
+ output_file = csv_file.replace(".csv", "_predictions.csv")
162
+ df.to_csv(output_file, index=False)
163
+ print(f"Predictions saved to {output_file}")
164
+ return {"output_file": output_file}
165
+
166
+ # Create predictions DataFrame for text file or direct SMILES input
167
+ results_df = pd.DataFrame(
168
+ {
169
+ "smiles": inputs,
170
+ "prediction": predictions["prediction"],
171
+ "confidence": predictions["confidence"],
172
+ }
173
+ )
174
+
175
+ if csv_file:
176
+ output_file = csv_file.rsplit(".", 1)[0] + "_predictions.csv"
177
+ results_df.to_csv(output_file, index=False)
178
+ return {"output_file": output_file}
179
+
180
+ return predictions
181
+
182
+
183
+ def structure_predict(
184
+ protein: str,
185
+ ligand: str,
186
+ model: str,
187
+ ) -> Any:
188
+ """Predict a structure."""
189
+ token = load_token()
190
+ payload = {
191
+ "protein": protein,
192
+ "ligand": ligand,
193
+ "model": model,
194
+ "api_token": token,
195
+ }
196
+ response = requests.post(
197
+ f"{settings.HOST}/api/v3/public/structure_prediction/",
198
+ json=payload,
199
+ timeout=29,
200
+ )
201
+ if response.status_code != 200:
202
+ raise RuntimeError(response.text)
203
+ return response.json()
204
+
205
+
206
+ def list_structure_tasks() -> Any:
207
+ """List all structure prediction tasks."""
208
+ token = load_token()
209
+ payload = {"api_token": token}
210
+ response = requests.post(
211
+ f"{settings.HOST}/api/v3/public/structure_prediction/get_all_tasks/",
212
+ json=payload,
213
+ timeout=29,
214
+ )
215
+ if response.status_code != 200:
216
+ raise RuntimeError(response.text)
217
+ return response.json()
218
+
219
+
220
+ def download_structure_prediction(
221
+ task_id: str,
222
+ ) -> bytes:
223
+ """Download a structure prediction task."""
224
+ token = load_token()
225
+ response = requests.post(
226
+ f"{settings.HOST}/api/v3/public/structure_prediction/download/{task_id}",
227
+ headers={
228
+ "accept": "application/json",
229
+ "Content-Type": "application/json",
230
+ },
231
+ json={
232
+ "api_token": token,
233
+ },
234
+ timeout=29,
235
+ )
236
+ if response.status_code != 200:
237
+ raise RuntimeError(response.text)
238
+ return response.content
239
+
240
+
241
+ def model_metadata(model_id: str) -> Any:
242
+ """Get metadata for a specific model."""
243
+ token = load_token()
244
+ response = requests.post(
245
+ f"{settings.HOST}/api/v3/public/models/metadata/{model_id}",
246
+ json={"api_token": token},
247
+ timeout=29,
248
+ )
249
+ if response.status_code != 200:
250
+ raise RuntimeError(response.text)
251
+ return response.json()
252
+
253
+
254
+ def model_info(model_id: str) -> Any:
255
+ """Get detailed information for a specific model."""
256
+ token = load_token()
257
+ response = requests.post(
258
+ f"{settings.HOST}/api/v3/public/models/{model_id}",
259
+ json={"api_token": token},
260
+ timeout=29,
261
+ )
262
+ if response.status_code != 200:
263
+ raise RuntimeError(response.text)
264
+ return response.json()
@@ -0,0 +1,193 @@
1
+ """Command line interface for interacting with the deepmirror API.
2
+
3
+ This module provides a CLI for authenticating, managing models, and making
4
+ predictions using the deepmirror platform. It wraps the API client functionality
5
+ in an easy-to-use command line tool.
6
+ """
7
+
8
+ import getpass
9
+ import json
10
+
11
+ import click
12
+
13
+ from . import api
14
+
15
+
16
+ def _save_token(token: str) -> None:
17
+ api.save_token(token)
18
+
19
+
20
+ def _load_token(token: str | None) -> str:
21
+ if token:
22
+ return token
23
+ try:
24
+ return api.load_token()
25
+ except RuntimeError as exc:
26
+ raise click.UsageError(
27
+ "API token required. Please login or pass --token"
28
+ ) from exc
29
+
30
+
31
+ @click.group()
32
+ def cli() -> None:
33
+ """Interact with the deepmirror public API."""
34
+
35
+
36
+ @cli.command()
37
+ @click.argument("username", required=True)
38
+ def login(username: str) -> None:
39
+ """Authenticate and obtain an API token."""
40
+ if not username:
41
+ username = input("Email: ")
42
+ password = getpass.getpass("Password: ")
43
+
44
+ try:
45
+ token = api.authenticate(username, password)
46
+ except RuntimeError as exc:
47
+ raise click.ClickException(str(exc)) from exc
48
+ _save_token(token)
49
+
50
+
51
+ @cli.group()
52
+ def model() -> None:
53
+ """Model operations."""
54
+
55
+
56
+ @model.command("list")
57
+ def model_list() -> None:
58
+ """List available models for inference."""
59
+ try:
60
+ data = api.list_models()
61
+ except RuntimeError as exc:
62
+ raise click.ClickException(str(exc)) from exc
63
+ click.echo(json.dumps(data, indent=2))
64
+
65
+
66
+ @model.command()
67
+ @click.argument("model_id")
68
+ def metadata(model_id: str) -> None:
69
+ """Get metadata for a specific model."""
70
+ try:
71
+ data = api.model_metadata(model_id)
72
+ except RuntimeError as exc:
73
+ raise click.ClickException(str(exc)) from exc
74
+ click.echo(json.dumps(data, indent=2))
75
+
76
+
77
+ @model.command()
78
+ @click.argument("model_id")
79
+ def info(model_id: str) -> None:
80
+ """Get detailed information for a specific model."""
81
+ try:
82
+ data = api.model_info(model_id)
83
+ except RuntimeError as exc:
84
+ raise click.ClickException(str(exc)) from exc
85
+ click.echo(json.dumps(data, indent=2))
86
+
87
+
88
+ @cli.command()
89
+ @click.option("--model-name", required=True)
90
+ @click.option("--csv-file", type=click.Path(exists=True), required=True)
91
+ @click.option("--smiles-column", default="smiles", show_default=True)
92
+ @click.option("--value-column", default="target", show_default=True)
93
+ @click.option("--classification", is_flag=True, default=False)
94
+ def train(
95
+ model_name: str,
96
+ csv_file: str,
97
+ smiles_column: str,
98
+ value_column: str,
99
+ classification: bool,
100
+ ) -> None:
101
+ """Train a custom model from a CSV file."""
102
+ try:
103
+ data = api.train(
104
+ model_name,
105
+ csv_file,
106
+ smiles_column,
107
+ value_column,
108
+ classification,
109
+ )
110
+ except (ValueError, RuntimeError) as exc:
111
+ raise click.ClickException(str(exc)) from exc
112
+ click.echo(json.dumps(data, indent=2))
113
+
114
+
115
+ @cli.command()
116
+ @click.option("--model-name", required=True)
117
+ @click.option("--input-file", type=click.Path(exists=True))
118
+ @click.option(
119
+ "--input-header",
120
+ default="smiles",
121
+ help="Column name for SMILES in CSV input",
122
+ )
123
+ @click.option("--input-smiles", multiple=True, help="Direct SMILES input")
124
+ def predict(
125
+ model_name: str,
126
+ csv_file: str | None,
127
+ smiles_column: str | None,
128
+ smiles: tuple[str, ...] | None,
129
+ ) -> None:
130
+ """Run prediction using a trained model.
131
+
132
+ Takes input as either a CSV file with SMILES column, a text file with SMILES per line,
133
+ or direct SMILES strings via --input-smiles.
134
+ """
135
+ if not csv_file and not smiles:
136
+ raise click.UsageError("Either --csv-file or --smiles must be provided")
137
+
138
+ try:
139
+ data = api.predict(
140
+ model_name,
141
+ csv_file=csv_file,
142
+ smiles_column=smiles_column,
143
+ smiles=list(smiles) if smiles else None,
144
+ )
145
+ except (ValueError, RuntimeError) as exc:
146
+ raise click.ClickException(str(exc)) from exc
147
+ click.echo(json.dumps(data, indent=2))
148
+
149
+
150
+ @cli.group()
151
+ def structure() -> None:
152
+ """Structure prediction operations."""
153
+
154
+
155
+ @structure.command("predict")
156
+ @click.argument("protein")
157
+ @click.argument("ligand")
158
+ @click.option("--model-name", default="chai", show_default=True)
159
+ def structure_predict(protein: str, ligand: str, model_name: str) -> None:
160
+ """Submit a structure prediction job."""
161
+ try:
162
+ data = api.structure_predict(protein, ligand, model_name)
163
+ except RuntimeError as exc:
164
+ raise click.ClickException(str(exc)) from exc
165
+ click.echo(json.dumps(data, indent=2))
166
+
167
+
168
+ @structure.command("list")
169
+ def structure_list() -> None:
170
+ """List submitted structure prediction tasks."""
171
+ try:
172
+ data = api.list_structure_tasks()
173
+ except RuntimeError as exc:
174
+ raise click.ClickException(str(exc)) from exc
175
+ click.echo(json.dumps(data, indent=2))
176
+
177
+
178
+ @structure.command("download")
179
+ @click.argument("task_id")
180
+ @click.argument("output_file", type=click.Path())
181
+ def structure_download(task_id: str, output_file: str) -> None:
182
+ """Download structure prediction results."""
183
+ try:
184
+ data = api.download_structure_prediction(task_id)
185
+ with open(output_file, "wb") as f:
186
+ f.write(data)
187
+ except RuntimeError as exc:
188
+ raise click.ClickException(str(exc)) from exc
189
+ click.echo(f"Downloaded structure prediction to {output_file}")
190
+
191
+
192
+ if __name__ == "__main__":
193
+ cli()
@@ -0,0 +1,31 @@
1
+ """Configuration for the deepmirror CLI.
2
+
3
+ This module defines the settings for the deepmirror CLI, including the API host,
4
+ and the directory where the API token is stored.
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+
10
+ from pydantic_settings import BaseSettings
11
+
12
+
13
+ # pylint: disable=too-few-public-methods
14
+ class Settings(BaseSettings):
15
+ """Settings for the deepmirror CLI."""
16
+
17
+ HOST: str = "https://api.app.deepmirror.ai"
18
+
19
+ if os.name == "nt":
20
+ BASE_DIR: Path = Path(os.getenv("APPDATA", str(Path.home()))) / "Local"
21
+ else:
22
+ BASE_DIR: Path = Path(
23
+ os.getenv("XDG_CONFIG_HOME", str(Path.home() / ".config"))
24
+ )
25
+
26
+ CONFIG_DIR: Path = BASE_DIR / "deepmirror"
27
+ TOKEN_FILE: Path = CONFIG_DIR / "token"
28
+ HOST_FILE: Path = CONFIG_DIR / "host"
29
+
30
+
31
+ settings = Settings()
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: deepmirror
3
+ Version: 0.0.0
4
+ Summary: Command line interface for the deepmirror public API
5
+ Requires-Python: >=3.12
6
+ License-File: LICENSE
7
+ Requires-Dist: click>=8.1.8
8
+ Requires-Dist: requests>=2.31.0
9
+ Requires-Dist: pandas>=2.2.2
10
+ Requires-Dist: pydantic>=2.6.3
11
+ Requires-Dist: pydantic-settings>=2.9.1
12
+ Requires-Dist: scikit-learn>=1.6.1
13
+ Requires-Dist: matplotlib>=3.10.3
14
+ Requires-Dist: tqdm>=4.67.1
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
17
+ Requires-Dist: pre-commit>=3.6.0; extra == "dev"
18
+ Requires-Dist: pylint>=3.3.6; extra == "dev"
19
+ Requires-Dist: ruff>=0.11.0; extra == "dev"
20
+ Dynamic: license-file
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ deepmirror/__init__.py
5
+ deepmirror/api.py
6
+ deepmirror/cli.py
7
+ deepmirror/config.py
8
+ deepmirror.egg-info/PKG-INFO
9
+ deepmirror.egg-info/SOURCES.txt
10
+ deepmirror.egg-info/dependency_links.txt
11
+ deepmirror.egg-info/entry_points.txt
12
+ deepmirror.egg-info/requires.txt
13
+ deepmirror.egg-info/top_level.txt
14
+ tests/test_train.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ dm = deepmirror.cli:cli
@@ -0,0 +1,14 @@
1
+ click>=8.1.8
2
+ requests>=2.31.0
3
+ pandas>=2.2.2
4
+ pydantic>=2.6.3
5
+ pydantic-settings>=2.9.1
6
+ scikit-learn>=1.6.1
7
+ matplotlib>=3.10.3
8
+ tqdm>=4.67.1
9
+
10
+ [dev]
11
+ pytest>=8.0.0
12
+ pre-commit>=3.6.0
13
+ pylint>=3.3.6
14
+ ruff>=0.11.0
@@ -0,0 +1 @@
1
+ deepmirror
@@ -0,0 +1,40 @@
1
+ [project]
2
+ name = "deepmirror"
3
+ version = "0.0.0"
4
+ description = "Command line interface for the deepmirror public API"
5
+ requires-python = ">=3.12"
6
+
7
+ dependencies = [
8
+ "click>=8.1.8", # BSD-3
9
+ "requests>=2.31.0", # Apache-2.0
10
+ "pandas>=2.2.2", # BSD-3
11
+ "pydantic>=2.6.3", # MIT
12
+ "pydantic-settings>=2.9.1", # MIT
13
+ "scikit-learn>=1.6.1", # BSD-3
14
+ "matplotlib>=3.10.3", # PSF
15
+ "tqdm>=4.67.1", # MPL-2.0
16
+ ]
17
+
18
+ [project.optional-dependencies]
19
+ dev = [
20
+ "pytest>=8.0.0",
21
+ "pre-commit>=3.6.0",
22
+ "pylint>=3.3.6",
23
+ "ruff>=0.11.0",
24
+ ]
25
+
26
+
27
+ [project.scripts]
28
+ dm = "deepmirror.cli:cli"
29
+
30
+ [build-system]
31
+ requires = ["setuptools>=61.2", "wheel"]
32
+ build-backend = "setuptools.build_meta"
33
+
34
+ [tool.setuptools]
35
+ packages = ["deepmirror"]
36
+
37
+
38
+ [tool.ruff]
39
+ line-length = 80
40
+ target-version = 'py313'
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,47 @@
1
+ """Test that the train functionality can correcelty load data from a CSV file."""
2
+
3
+ from pathlib import Path
4
+ from unittest import mock
5
+
6
+ import pytest
7
+
8
+ from deepmirror import api
9
+
10
+
11
+ @pytest.fixture
12
+ def csv_path(tmp_path):
13
+ """Create a temporary CSV file for testing."""
14
+ path = tmp_path / "data.csv"
15
+ return path
16
+
17
+
18
+ def test_train_valid_columns(test_csv_path: Path) -> None:
19
+ """Test training with valid columns."""
20
+ test_csv_path.write_text("smiles,value\nCCO,1\n")
21
+ with mock.patch("deepmirror.api.requests.post") as mock_post:
22
+ mock_post.return_value.status_code = 200
23
+ mock_post.return_value.json.return_value = {"ok": True}
24
+ result = api.train(
25
+ "mymodel",
26
+ str(test_csv_path),
27
+ "smiles",
28
+ "value",
29
+ False,
30
+ )
31
+ assert result == {"ok": True}
32
+ payload = mock_post.call_args.kwargs["json"]
33
+ assert payload["x"] == ["CCO"]
34
+ assert payload["y"] == [1.0]
35
+
36
+
37
+ def test_train_missing_columns(test_csv_path: Path) -> None:
38
+ """Test training with missing columns."""
39
+ test_csv_path.write_text("a,b\n1,2\n")
40
+ with pytest.raises(ValueError):
41
+ api.train(
42
+ "mymodel",
43
+ str(test_csv_path),
44
+ "smiles",
45
+ "value",
46
+ False,
47
+ )