apheris-foundry-cli 0.2.0__tar.gz → 0.3.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.
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/PKG-INFO +36 -17
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/README.md +35 -16
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/pyproject.toml +1 -1
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apheris_foundry_cli.egg-info/PKG-INFO +36 -17
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/commands/foundry.py +12 -1
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/commands/login.py +5 -5
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/auth.py +15 -13
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/hub_requests.py +15 -3
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/main.py +168 -17
- apheris_foundry_cli-0.3.0/src/apherisfold_cli/version.py +1 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/tests/test_auth.py +8 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/tests/test_cli.py +162 -6
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/tests/test_hub_requests.py +16 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/tests/test_main.py +5 -5
- apheris_foundry_cli-0.2.0/src/apherisfold_cli/version.py +0 -1
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/LICENSE +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/setup.cfg +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apheris_foundry_cli.egg-info/SOURCES.txt +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apheris_foundry_cli.egg-info/dependency_links.txt +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apheris_foundry_cli.egg-info/entry_points.txt +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apheris_foundry_cli.egg-info/requires.txt +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apheris_foundry_cli.egg-info/top_level.txt +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/__init__.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/commands/__init__.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/__init__.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/config.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/context.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/errors.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/io.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/login_profiles.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/render.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/result_artifacts.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/handlers.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/tests/test_config.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/tests/test_foundry_helpers.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/tests/test_handlers.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/tests/test_io.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/tests/test_render.py +0 -0
- {apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/tests/test_result_artifacts.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apheris-foundry-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Command-line interface for Apheris Foundry workflows.
|
|
5
5
|
Author: Apheris
|
|
6
6
|
License-Expression: LicenseRef-Apheris-Foundry-Software-License
|
|
@@ -39,37 +39,37 @@ Recommended if you already use conda for scientific Python work. A dedicated env
|
|
|
39
39
|
#### Linux / macOS
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
conda create -n
|
|
43
|
-
conda activate
|
|
44
|
-
pip install
|
|
42
|
+
conda create -n apheris-foundry python=3.11 -y
|
|
43
|
+
conda activate apheris-foundry
|
|
44
|
+
pip install apheris-foundry-cli
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
#### Windows (Anaconda Prompt or PowerShell)
|
|
48
48
|
|
|
49
49
|
```powershell
|
|
50
|
-
conda create -n
|
|
51
|
-
conda activate
|
|
52
|
-
pip install
|
|
50
|
+
conda create -n apheris-foundry python=3.11 -y
|
|
51
|
+
conda activate apheris-foundry
|
|
52
|
+
pip install apheris-foundry-cli
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
Activate the environment at the start of each session with `conda activate
|
|
55
|
+
Activate the environment at the start of each session with `conda activate apheris-foundry`.
|
|
56
56
|
|
|
57
57
|
### venv (standard Python)
|
|
58
58
|
|
|
59
59
|
#### Linux / macOS (venv)
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
python3 -m venv ~/.venvs/
|
|
63
|
-
source ~/.venvs/
|
|
64
|
-
pip install
|
|
62
|
+
python3 -m venv ~/.venvs/apheris-foundry
|
|
63
|
+
source ~/.venvs/apheris-foundry/bin/activate
|
|
64
|
+
pip install apheris-foundry-cli
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
#### Windows (PowerShell, venv)
|
|
68
68
|
|
|
69
69
|
```powershell
|
|
70
|
-
python -m venv $env:USERPROFILE\.venvs\
|
|
71
|
-
& $env:USERPROFILE\.venvs\
|
|
72
|
-
pip install
|
|
70
|
+
python -m venv $env:USERPROFILE\.venvs\apheris-foundry
|
|
71
|
+
& $env:USERPROFILE\.venvs\apheris-foundry\Scripts\Activate.ps1
|
|
72
|
+
pip install apheris-foundry-cli
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
Activate the environment at the start of each session with the `activate` command above.
|
|
@@ -84,7 +84,7 @@ apheris-foundry --help
|
|
|
84
84
|
### Upgrade
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
pip install --upgrade
|
|
87
|
+
pip install --upgrade apheris-foundry-cli
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
## Quick start
|
|
@@ -99,6 +99,22 @@ apheris-foundry workflows list
|
|
|
99
99
|
# Submit a run
|
|
100
100
|
apheris-foundry workflows run --workflow predict --input ./request.json
|
|
101
101
|
|
|
102
|
+
# Inspect the model IDs, weights, and default parameters advertised by Hub
|
|
103
|
+
apheris-foundry workflows models --workflow predict
|
|
104
|
+
|
|
105
|
+
# Override prediction parameters with a Hub-schema-validated JSON object
|
|
106
|
+
apheris-foundry workflows run --workflow predict --input ./request.json \
|
|
107
|
+
--model-params '{"seeds":[7,9],"num_diffusion_samples":3}'
|
|
108
|
+
|
|
109
|
+
# Or provide the same JSON object from a file
|
|
110
|
+
apheris-foundry workflows run --workflow predict --input ./request.json \
|
|
111
|
+
--model-params @./model-params.json
|
|
112
|
+
|
|
113
|
+
# Select a non-default model/weight advertised by the workflow
|
|
114
|
+
apheris-foundry workflows run --workflow predict --input ./request.json \
|
|
115
|
+
--param model=<model-id> --weight <weight-version> \
|
|
116
|
+
--model-params @./model-params.json
|
|
117
|
+
|
|
102
118
|
# List submitted runs
|
|
103
119
|
apheris-foundry jobs list
|
|
104
120
|
|
|
@@ -132,9 +148,10 @@ apheris-foundry jobs error --id <job-id>
|
|
|
132
148
|
| Command | Description |
|
|
133
149
|
| ------- | ----------- |
|
|
134
150
|
| `apheris-foundry workflows list` | List workflow definitions exposed by Hub |
|
|
135
|
-
| `apheris-foundry workflows get --workflow <id>` | Inspect one workflow definition |
|
|
151
|
+
| `apheris-foundry workflows get --workflow <id>` | Inspect one workflow definition and advertised model defaults |
|
|
152
|
+
| `apheris-foundry workflows models --workflow <id>` | List models, weights, and default prediction parameters advertised by Hub (`--json` includes full metadata) |
|
|
136
153
|
| `apheris-foundry workflows graph --workflow <id>` | Show the workflow DAG |
|
|
137
|
-
| `apheris-foundry workflows run --workflow <id
|
|
154
|
+
| `apheris-foundry workflows run --workflow <id> [--model-params <JSON\|@file>]` | Submit a workflow run with Hub-schema-validated model parameters |
|
|
138
155
|
|
|
139
156
|
### Run instances (`runs`)
|
|
140
157
|
|
|
@@ -153,6 +170,8 @@ apheris-foundry workflows run --help
|
|
|
153
170
|
apheris-foundry jobs get --help
|
|
154
171
|
```
|
|
155
172
|
|
|
173
|
+
`--model-params` accepts one JSON object. Use an inline object for short overrides, or prefix a JSON filename with `@` to load the object from that file. Before submitting the run, the CLI retrieves the selected model version, weight, and capability schema from Hub and validates the object. Use `workflows models` to discover available model IDs and weights.
|
|
174
|
+
|
|
156
175
|
## License
|
|
157
176
|
|
|
158
177
|
This package is licensed under the [Apheris Foundry Software License](https://www.apheris.com/docs/hub/hub-license.html). The full license text is also included in every published wheel as `LICENSE`.
|
|
@@ -16,37 +16,37 @@ Recommended if you already use conda for scientific Python work. A dedicated env
|
|
|
16
16
|
#### Linux / macOS
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
conda create -n
|
|
20
|
-
conda activate
|
|
21
|
-
pip install
|
|
19
|
+
conda create -n apheris-foundry python=3.11 -y
|
|
20
|
+
conda activate apheris-foundry
|
|
21
|
+
pip install apheris-foundry-cli
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
#### Windows (Anaconda Prompt or PowerShell)
|
|
25
25
|
|
|
26
26
|
```powershell
|
|
27
|
-
conda create -n
|
|
28
|
-
conda activate
|
|
29
|
-
pip install
|
|
27
|
+
conda create -n apheris-foundry python=3.11 -y
|
|
28
|
+
conda activate apheris-foundry
|
|
29
|
+
pip install apheris-foundry-cli
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
Activate the environment at the start of each session with `conda activate
|
|
32
|
+
Activate the environment at the start of each session with `conda activate apheris-foundry`.
|
|
33
33
|
|
|
34
34
|
### venv (standard Python)
|
|
35
35
|
|
|
36
36
|
#### Linux / macOS (venv)
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
python3 -m venv ~/.venvs/
|
|
40
|
-
source ~/.venvs/
|
|
41
|
-
pip install
|
|
39
|
+
python3 -m venv ~/.venvs/apheris-foundry
|
|
40
|
+
source ~/.venvs/apheris-foundry/bin/activate
|
|
41
|
+
pip install apheris-foundry-cli
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
#### Windows (PowerShell, venv)
|
|
45
45
|
|
|
46
46
|
```powershell
|
|
47
|
-
python -m venv $env:USERPROFILE\.venvs\
|
|
48
|
-
& $env:USERPROFILE\.venvs\
|
|
49
|
-
pip install
|
|
47
|
+
python -m venv $env:USERPROFILE\.venvs\apheris-foundry
|
|
48
|
+
& $env:USERPROFILE\.venvs\apheris-foundry\Scripts\Activate.ps1
|
|
49
|
+
pip install apheris-foundry-cli
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
Activate the environment at the start of each session with the `activate` command above.
|
|
@@ -61,7 +61,7 @@ apheris-foundry --help
|
|
|
61
61
|
### Upgrade
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
pip install --upgrade
|
|
64
|
+
pip install --upgrade apheris-foundry-cli
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
## Quick start
|
|
@@ -76,6 +76,22 @@ apheris-foundry workflows list
|
|
|
76
76
|
# Submit a run
|
|
77
77
|
apheris-foundry workflows run --workflow predict --input ./request.json
|
|
78
78
|
|
|
79
|
+
# Inspect the model IDs, weights, and default parameters advertised by Hub
|
|
80
|
+
apheris-foundry workflows models --workflow predict
|
|
81
|
+
|
|
82
|
+
# Override prediction parameters with a Hub-schema-validated JSON object
|
|
83
|
+
apheris-foundry workflows run --workflow predict --input ./request.json \
|
|
84
|
+
--model-params '{"seeds":[7,9],"num_diffusion_samples":3}'
|
|
85
|
+
|
|
86
|
+
# Or provide the same JSON object from a file
|
|
87
|
+
apheris-foundry workflows run --workflow predict --input ./request.json \
|
|
88
|
+
--model-params @./model-params.json
|
|
89
|
+
|
|
90
|
+
# Select a non-default model/weight advertised by the workflow
|
|
91
|
+
apheris-foundry workflows run --workflow predict --input ./request.json \
|
|
92
|
+
--param model=<model-id> --weight <weight-version> \
|
|
93
|
+
--model-params @./model-params.json
|
|
94
|
+
|
|
79
95
|
# List submitted runs
|
|
80
96
|
apheris-foundry jobs list
|
|
81
97
|
|
|
@@ -109,9 +125,10 @@ apheris-foundry jobs error --id <job-id>
|
|
|
109
125
|
| Command | Description |
|
|
110
126
|
| ------- | ----------- |
|
|
111
127
|
| `apheris-foundry workflows list` | List workflow definitions exposed by Hub |
|
|
112
|
-
| `apheris-foundry workflows get --workflow <id>` | Inspect one workflow definition |
|
|
128
|
+
| `apheris-foundry workflows get --workflow <id>` | Inspect one workflow definition and advertised model defaults |
|
|
129
|
+
| `apheris-foundry workflows models --workflow <id>` | List models, weights, and default prediction parameters advertised by Hub (`--json` includes full metadata) |
|
|
113
130
|
| `apheris-foundry workflows graph --workflow <id>` | Show the workflow DAG |
|
|
114
|
-
| `apheris-foundry workflows run --workflow <id
|
|
131
|
+
| `apheris-foundry workflows run --workflow <id> [--model-params <JSON\|@file>]` | Submit a workflow run with Hub-schema-validated model parameters |
|
|
115
132
|
|
|
116
133
|
### Run instances (`runs`)
|
|
117
134
|
|
|
@@ -130,6 +147,8 @@ apheris-foundry workflows run --help
|
|
|
130
147
|
apheris-foundry jobs get --help
|
|
131
148
|
```
|
|
132
149
|
|
|
150
|
+
`--model-params` accepts one JSON object. Use an inline object for short overrides, or prefix a JSON filename with `@` to load the object from that file. Before submitting the run, the CLI retrieves the selected model version, weight, and capability schema from Hub and validates the object. Use `workflows models` to discover available model IDs and weights.
|
|
151
|
+
|
|
133
152
|
## License
|
|
134
153
|
|
|
135
154
|
This package is licensed under the [Apheris Foundry Software License](https://www.apheris.com/docs/hub/hub-license.html). The full license text is also included in every published wheel as `LICENSE`.
|
{apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apheris_foundry_cli.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apheris-foundry-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Command-line interface for Apheris Foundry workflows.
|
|
5
5
|
Author: Apheris
|
|
6
6
|
License-Expression: LicenseRef-Apheris-Foundry-Software-License
|
|
@@ -39,37 +39,37 @@ Recommended if you already use conda for scientific Python work. A dedicated env
|
|
|
39
39
|
#### Linux / macOS
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
conda create -n
|
|
43
|
-
conda activate
|
|
44
|
-
pip install
|
|
42
|
+
conda create -n apheris-foundry python=3.11 -y
|
|
43
|
+
conda activate apheris-foundry
|
|
44
|
+
pip install apheris-foundry-cli
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
#### Windows (Anaconda Prompt or PowerShell)
|
|
48
48
|
|
|
49
49
|
```powershell
|
|
50
|
-
conda create -n
|
|
51
|
-
conda activate
|
|
52
|
-
pip install
|
|
50
|
+
conda create -n apheris-foundry python=3.11 -y
|
|
51
|
+
conda activate apheris-foundry
|
|
52
|
+
pip install apheris-foundry-cli
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
Activate the environment at the start of each session with `conda activate
|
|
55
|
+
Activate the environment at the start of each session with `conda activate apheris-foundry`.
|
|
56
56
|
|
|
57
57
|
### venv (standard Python)
|
|
58
58
|
|
|
59
59
|
#### Linux / macOS (venv)
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
python3 -m venv ~/.venvs/
|
|
63
|
-
source ~/.venvs/
|
|
64
|
-
pip install
|
|
62
|
+
python3 -m venv ~/.venvs/apheris-foundry
|
|
63
|
+
source ~/.venvs/apheris-foundry/bin/activate
|
|
64
|
+
pip install apheris-foundry-cli
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
#### Windows (PowerShell, venv)
|
|
68
68
|
|
|
69
69
|
```powershell
|
|
70
|
-
python -m venv $env:USERPROFILE\.venvs\
|
|
71
|
-
& $env:USERPROFILE\.venvs\
|
|
72
|
-
pip install
|
|
70
|
+
python -m venv $env:USERPROFILE\.venvs\apheris-foundry
|
|
71
|
+
& $env:USERPROFILE\.venvs\apheris-foundry\Scripts\Activate.ps1
|
|
72
|
+
pip install apheris-foundry-cli
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
Activate the environment at the start of each session with the `activate` command above.
|
|
@@ -84,7 +84,7 @@ apheris-foundry --help
|
|
|
84
84
|
### Upgrade
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
pip install --upgrade
|
|
87
|
+
pip install --upgrade apheris-foundry-cli
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
## Quick start
|
|
@@ -99,6 +99,22 @@ apheris-foundry workflows list
|
|
|
99
99
|
# Submit a run
|
|
100
100
|
apheris-foundry workflows run --workflow predict --input ./request.json
|
|
101
101
|
|
|
102
|
+
# Inspect the model IDs, weights, and default parameters advertised by Hub
|
|
103
|
+
apheris-foundry workflows models --workflow predict
|
|
104
|
+
|
|
105
|
+
# Override prediction parameters with a Hub-schema-validated JSON object
|
|
106
|
+
apheris-foundry workflows run --workflow predict --input ./request.json \
|
|
107
|
+
--model-params '{"seeds":[7,9],"num_diffusion_samples":3}'
|
|
108
|
+
|
|
109
|
+
# Or provide the same JSON object from a file
|
|
110
|
+
apheris-foundry workflows run --workflow predict --input ./request.json \
|
|
111
|
+
--model-params @./model-params.json
|
|
112
|
+
|
|
113
|
+
# Select a non-default model/weight advertised by the workflow
|
|
114
|
+
apheris-foundry workflows run --workflow predict --input ./request.json \
|
|
115
|
+
--param model=<model-id> --weight <weight-version> \
|
|
116
|
+
--model-params @./model-params.json
|
|
117
|
+
|
|
102
118
|
# List submitted runs
|
|
103
119
|
apheris-foundry jobs list
|
|
104
120
|
|
|
@@ -132,9 +148,10 @@ apheris-foundry jobs error --id <job-id>
|
|
|
132
148
|
| Command | Description |
|
|
133
149
|
| ------- | ----------- |
|
|
134
150
|
| `apheris-foundry workflows list` | List workflow definitions exposed by Hub |
|
|
135
|
-
| `apheris-foundry workflows get --workflow <id>` | Inspect one workflow definition |
|
|
151
|
+
| `apheris-foundry workflows get --workflow <id>` | Inspect one workflow definition and advertised model defaults |
|
|
152
|
+
| `apheris-foundry workflows models --workflow <id>` | List models, weights, and default prediction parameters advertised by Hub (`--json` includes full metadata) |
|
|
136
153
|
| `apheris-foundry workflows graph --workflow <id>` | Show the workflow DAG |
|
|
137
|
-
| `apheris-foundry workflows run --workflow <id
|
|
154
|
+
| `apheris-foundry workflows run --workflow <id> [--model-params <JSON\|@file>]` | Submit a workflow run with Hub-schema-validated model parameters |
|
|
138
155
|
|
|
139
156
|
### Run instances (`runs`)
|
|
140
157
|
|
|
@@ -153,6 +170,8 @@ apheris-foundry workflows run --help
|
|
|
153
170
|
apheris-foundry jobs get --help
|
|
154
171
|
```
|
|
155
172
|
|
|
173
|
+
`--model-params` accepts one JSON object. Use an inline object for short overrides, or prefix a JSON filename with `@` to load the object from that file. Before submitting the run, the CLI retrieves the selected model version, weight, and capability schema from Hub and validates the object. Use `workflows models` to discover available model IDs and weights.
|
|
174
|
+
|
|
156
175
|
## License
|
|
157
176
|
|
|
158
177
|
This package is licensed under the [Apheris Foundry Software License](https://www.apheris.com/docs/hub/hub-license.html). The full license text is also included in every published wheel as `LICENSE`.
|
{apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/commands/foundry.py
RENAMED
|
@@ -783,10 +783,14 @@ def foundry_models(workflow: str, *, json_output: bool) -> None:
|
|
|
783
783
|
table.add_column("VERSION", overflow="fold")
|
|
784
784
|
table.add_column("WEIGHT", no_wrap=True)
|
|
785
785
|
table.add_column("CAPABILITIES", overflow="fold")
|
|
786
|
+
table.add_column("DEFAULT PARAMETERS", overflow="fold")
|
|
786
787
|
table.add_column("DESCRIPTION", overflow="fold")
|
|
787
788
|
for model in models:
|
|
788
789
|
if not isinstance(model, dict):
|
|
789
790
|
continue
|
|
791
|
+
wrapper = model.get("wrapper") if isinstance(model.get("wrapper"), dict) else {}
|
|
792
|
+
defaults = wrapper.get("predictParamsDefaults") if isinstance(wrapper, dict) else {}
|
|
793
|
+
defaults_text = json.dumps(defaults, sort_keys=True) if isinstance(defaults, dict) and defaults else ""
|
|
790
794
|
weights = model.get("weights") if isinstance(model.get("weights"), list) else []
|
|
791
795
|
if not weights:
|
|
792
796
|
table.add_row(
|
|
@@ -794,6 +798,7 @@ def foundry_models(workflow: str, *, json_output: bool) -> None:
|
|
|
794
798
|
str(model.get("version", "")),
|
|
795
799
|
"",
|
|
796
800
|
", ".join(str(item) for item in model.get("capabilities", []) if item),
|
|
801
|
+
defaults_text,
|
|
797
802
|
"",
|
|
798
803
|
)
|
|
799
804
|
continue
|
|
@@ -810,14 +815,20 @@ def foundry_models(workflow: str, *, json_output: bool) -> None:
|
|
|
810
815
|
str(model.get("version", "")),
|
|
811
816
|
f"{weight.get('version', '')}{marker}",
|
|
812
817
|
", ".join(str(item) for item in capabilities if item),
|
|
818
|
+
defaults_text,
|
|
813
819
|
str(weight.get("description", "")),
|
|
814
820
|
)
|
|
815
821
|
|
|
816
822
|
examples = Table(box=box.SIMPLE_HEAD, show_header=False, padding=(0, 1), expand=False)
|
|
817
823
|
examples.add_column("COMMAND", style="cyan", overflow="fold")
|
|
824
|
+
examples.add_row(f"apheris-foundry workflows run --workflow {workflow} --input request.json")
|
|
818
825
|
examples.add_row(
|
|
819
|
-
f"apheris-foundry workflows run --workflow {workflow} --
|
|
826
|
+
f"apheris-foundry workflows run --workflow {workflow} --weight <weight-version> --input request.json"
|
|
820
827
|
)
|
|
828
|
+
examples.add_row(
|
|
829
|
+
f'apheris-foundry workflows run --workflow {workflow} --model-params \'{{"seeds":[42],"num_diffusion_samples":1}}\''
|
|
830
|
+
)
|
|
831
|
+
examples.add_row(f"apheris-foundry workflows run --workflow {workflow} --model-params @model-params.json")
|
|
821
832
|
examples.add_row(f"apheris-foundry workflows get --workflow {workflow}")
|
|
822
833
|
run_simple_listing(
|
|
823
834
|
task_name="foundry.models",
|
{apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/commands/login.py
RENAMED
|
@@ -43,15 +43,15 @@ def _is_verbose() -> bool:
|
|
|
43
43
|
def _build_examples_table() -> Table:
|
|
44
44
|
table = Table(box=box.SIMPLE_HEAD, show_header=False, padding=(0, 1))
|
|
45
45
|
table.add_column("command")
|
|
46
|
-
table.add_row(Text("
|
|
47
|
-
table.add_row(Text("
|
|
46
|
+
table.add_row(Text("apheris-foundry login --url https://hub.example.com", style="cyan"))
|
|
47
|
+
table.add_row(Text("apheris-foundry login --url https://hub.example.com --callback-host localhost", style="cyan"))
|
|
48
48
|
table.add_row(
|
|
49
49
|
Text(
|
|
50
|
-
"
|
|
50
|
+
"apheris-foundry login --url https://hub.example.com --issuer https://dex.example.com --client-id apheris-hub",
|
|
51
51
|
style="cyan",
|
|
52
52
|
)
|
|
53
53
|
)
|
|
54
|
-
table.add_row(Text("
|
|
54
|
+
table.add_row(Text("apheris-foundry login status", style="cyan"))
|
|
55
55
|
return table
|
|
56
56
|
|
|
57
57
|
|
|
@@ -70,7 +70,7 @@ def _render_login_overview() -> None:
|
|
|
70
70
|
|
|
71
71
|
console.print(
|
|
72
72
|
Group(
|
|
73
|
-
Text("Usage:
|
|
73
|
+
Text("Usage: apheris-foundry login --url HUB_URL [--callback-host HOST]", style="bold"),
|
|
74
74
|
Text(""),
|
|
75
75
|
Panel(options_table, title="Login Options", border_style="dim"),
|
|
76
76
|
Panel(_build_examples_table(), title="Examples", border_style="dim"),
|
|
@@ -242,10 +242,10 @@ def token_exchange_url(config: RuntimeAuthConfig) -> str:
|
|
|
242
242
|
|
|
243
243
|
|
|
244
244
|
def config_directory() -> Path:
|
|
245
|
-
override = os.getenv("APHERISFOLD_CONFIG_DIR")
|
|
245
|
+
override = os.getenv("APHERIS_FOUNDRY_CONFIG_DIR") or os.getenv("APHERISFOLD_CONFIG_DIR")
|
|
246
246
|
if override:
|
|
247
247
|
return Path(override)
|
|
248
|
-
return Path.home() / ".config" / "
|
|
248
|
+
return Path.home() / ".config" / "apheris-foundry"
|
|
249
249
|
|
|
250
250
|
|
|
251
251
|
def config_path() -> Path:
|
|
@@ -756,23 +756,23 @@ def _login_status_error(
|
|
|
756
756
|
if session is not None and not bool(expired):
|
|
757
757
|
return None, None
|
|
758
758
|
if config is None:
|
|
759
|
-
return "No Hub runtime configuration found.", "Run '
|
|
759
|
+
return "No Hub runtime configuration found.", "Run 'apheris-foundry login' to configure a Hub."
|
|
760
760
|
if not config.auth_enabled:
|
|
761
761
|
return None, None
|
|
762
762
|
if active_session is None:
|
|
763
|
-
return "No saved login session found.", "Run '
|
|
763
|
+
return "No saved login session found.", "Run 'apheris-foundry login' to create a session."
|
|
764
764
|
if not _session_matches_config(config, active_session):
|
|
765
765
|
return (
|
|
766
766
|
"Saved login session belongs to a different Hub or client.",
|
|
767
|
-
f"Configured Hub is {config.hub_url}; saved session Hub is {active_session.hub_url}. Run '
|
|
767
|
+
f"Configured Hub is {config.hub_url}; saved session Hub is {active_session.hub_url}. Run 'apheris-foundry login' for the configured Hub.",
|
|
768
768
|
)
|
|
769
769
|
if refresh_error:
|
|
770
770
|
return "Token refresh failed.", refresh_error
|
|
771
771
|
if expired and not active_session.refresh_token:
|
|
772
|
-
return "Saved login session is expired and has no refresh token.", "Run '
|
|
772
|
+
return "Saved login session is expired and has no refresh token.", "Run 'apheris-foundry login' again."
|
|
773
773
|
if expired:
|
|
774
|
-
return "Saved login session is expired.", "Run '
|
|
775
|
-
return "No usable authenticated session found.", "Run '
|
|
774
|
+
return "Saved login session is expired.", "Run 'apheris-foundry login' again."
|
|
775
|
+
return "No usable authenticated session found.", "Run 'apheris-foundry login' again."
|
|
776
776
|
|
|
777
777
|
|
|
778
778
|
def format_expires_at(expires_at: int | None) -> str | None:
|
|
@@ -925,10 +925,12 @@ def _browser_open(url: str) -> bool:
|
|
|
925
925
|
|
|
926
926
|
|
|
927
927
|
def _browser_auto_open_enabled() -> bool:
|
|
928
|
-
open_raw = os.getenv("APHERISFOLD_OPEN_BROWSER", "").strip().lower()
|
|
928
|
+
open_raw = (os.getenv("APHERIS_FOUNDRY_OPEN_BROWSER") or os.getenv("APHERISFOLD_OPEN_BROWSER", "")).strip().lower()
|
|
929
929
|
if open_raw in {"1", "true", "yes", "on"}:
|
|
930
930
|
return True
|
|
931
|
-
no_browser_raw =
|
|
931
|
+
no_browser_raw = (
|
|
932
|
+
(os.getenv("APHERIS_FOUNDRY_NO_BROWSER") or os.getenv("APHERISFOLD_NO_BROWSER", "")).strip().lower()
|
|
933
|
+
)
|
|
932
934
|
if no_browser_raw in {"1", "true", "yes", "on"}:
|
|
933
935
|
return False
|
|
934
936
|
return False
|
|
@@ -977,7 +979,7 @@ class _CallbackServer:
|
|
|
977
979
|
<head>
|
|
978
980
|
<meta charset="utf-8">
|
|
979
981
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
980
|
-
<title>
|
|
982
|
+
<title>Apheris Foundry Hub Login</title>
|
|
981
983
|
<style>
|
|
982
984
|
:root {
|
|
983
985
|
color-scheme: light;
|
|
@@ -1042,7 +1044,7 @@ class _CallbackServer:
|
|
|
1042
1044
|
</head>
|
|
1043
1045
|
<body>
|
|
1044
1046
|
<main class="shell">
|
|
1045
|
-
<div class="brand" aria-label="
|
|
1047
|
+
<div class="brand" aria-label="Apheris Foundry Hub">
|
|
1046
1048
|
<svg width="263" height="43" viewBox="0 0 263 43" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
1047
1049
|
<path opacity="0.5" d="M172.148 20.701H184.391V23.671H172.148V34H168.518V10.438H184.919V13.474H172.148V20.701ZM198.859 9.91C201.081 9.91 203.028 10.416 204.7 11.428C206.394 12.418 207.703 13.848 208.627 15.718C209.551 17.566 210.013 19.733 210.013 22.219C210.013 24.705 209.551 26.883 208.627 28.753C207.703 30.601 206.394 32.031 204.7 33.043C203.028 34.033 201.081 34.528 198.859 34.528C196.637 34.528 194.679 34.033 192.985 33.043C191.313 32.031 190.015 30.601 189.091 28.753C188.167 26.883 187.705 24.705 187.705 22.219C187.705 19.733 188.167 17.566 189.091 15.718C190.015 13.848 191.313 12.418 192.985 11.428C194.679 10.416 196.637 9.91 198.859 9.91ZM198.859 31.492C201.147 31.492 202.962 30.678 204.304 29.05C205.646 27.4 206.317 25.123 206.317 22.219C206.317 19.315 205.646 17.049 204.304 15.421C202.962 13.771 201.147 12.946 198.859 12.946C196.571 12.946 194.756 13.771 193.414 15.421C192.072 17.049 191.401 19.315 191.401 22.219C191.401 25.123 192.072 27.4 193.414 29.05C194.756 30.678 196.571 31.492 198.859 31.492ZM213.585 10.438H217.215V30.865H230.085V34H213.585V10.438ZM242.113 10.438C244.379 10.438 246.348 10.889 248.02 11.791C249.714 12.671 251.023 13.991 251.947 15.751C252.871 17.511 253.333 19.667 253.333 22.219C253.333 24.771 252.871 26.927 251.947 28.687C251.023 30.447 249.714 31.778 248.02 32.68C246.348 33.56 244.379 34 242.113 34H233.17V10.438H242.113ZM241.651 30.964C244.203 30.964 246.172 30.216 247.558 28.72C248.944 27.224 249.637 25.057 249.637 22.219C249.637 19.381 248.944 17.214 247.558 15.718C246.172 14.222 244.203 13.474 241.651 13.474H236.8V30.964H241.651Z" fill="#21484D"/>
|
|
1048
1050
|
<path d="M16.4623 27.0782H6.04042L3.6931 33.5385H0L8.96258 10.4609H13.6538L22.5785 33.5385H18.8097L16.4623 27.0782ZM7.00071 24.3866H15.4608L14.4213 21.5402C13.4611 18.9623 12.4595 16.0023 11.7298 13.5793H10.7696C10.002 16.0402 9.00043 18.9657 8.04015 21.5402L7.00071 24.3866Z" fill="#21484D"/>
|
|
@@ -1059,7 +1061,7 @@ class _CallbackServer:
|
|
|
1059
1061
|
<div class="brand-subtitle">Hub</div>
|
|
1060
1062
|
</div>
|
|
1061
1063
|
<div class="status">Sign-in completed</div>
|
|
1062
|
-
<p>Return to your terminal to continue using the
|
|
1064
|
+
<p>Return to your terminal to continue using the Apheris Foundry CLI.</p>
|
|
1063
1065
|
</main>
|
|
1064
1066
|
</body>
|
|
1065
1067
|
</html>"""
|
{apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/hub_requests.py
RENAMED
|
@@ -28,7 +28,7 @@ def _request_headers(
|
|
|
28
28
|
raise CLIError(
|
|
29
29
|
code=EXIT_PRECONDITION,
|
|
30
30
|
category="precondition",
|
|
31
|
-
message="No Hub runtime configuration found. Run '
|
|
31
|
+
message="No Hub runtime configuration found. Run 'apheris-foundry login' first.",
|
|
32
32
|
task="hub",
|
|
33
33
|
)
|
|
34
34
|
|
|
@@ -42,7 +42,7 @@ def _request_headers(
|
|
|
42
42
|
raise CLIError(
|
|
43
43
|
code=EXIT_PRECONDITION,
|
|
44
44
|
category="precondition",
|
|
45
|
-
message="No authenticated Hub session found. Run '
|
|
45
|
+
message="No authenticated Hub session found. Run 'apheris-foundry login' first.",
|
|
46
46
|
task="hub",
|
|
47
47
|
)
|
|
48
48
|
headers["Authorization"] = f"Bearer {session.access_token}"
|
|
@@ -110,7 +110,7 @@ def _hub_url(path: str) -> str:
|
|
|
110
110
|
raise CLIError(
|
|
111
111
|
code=EXIT_PRECONDITION,
|
|
112
112
|
category="precondition",
|
|
113
|
-
message="No Hub runtime configuration found. Run '
|
|
113
|
+
message="No Hub runtime configuration found. Run 'apheris-foundry login' first.",
|
|
114
114
|
task="hub",
|
|
115
115
|
)
|
|
116
116
|
return f"{config.hub_url.rstrip('/')}{path}"
|
|
@@ -208,6 +208,18 @@ def get_foundry_workflow(workflow_id: str) -> dict[str, Any]:
|
|
|
208
208
|
return _request_json(path=f"/api/v1/foundry/workflows/{encoded}", method="GET", task="foundry.workflows.get")
|
|
209
209
|
|
|
210
210
|
|
|
211
|
+
def get_model_schema(*, model_version_id: str, weight_version: str, capability: str) -> dict[str, Any]:
|
|
212
|
+
"""Return the Hub OpenAPI schema for one model weight and capability."""
|
|
213
|
+
version_key = urllib.parse.quote(model_version_id, safe="")
|
|
214
|
+
weight_key = urllib.parse.quote(weight_version, safe="")
|
|
215
|
+
capability_key = urllib.parse.quote(capability, safe="")
|
|
216
|
+
return _request_json(
|
|
217
|
+
path=f"/api/v1/schemas/{version_key}/{weight_key}?capability={capability_key}",
|
|
218
|
+
method="GET",
|
|
219
|
+
task="foundry.model-schema",
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
|
|
211
223
|
def list_foundry_runs() -> dict[str, Any]:
|
|
212
224
|
return _request_json(path="/api/v1/foundry/runs", method="GET", task="foundry.runs.list")
|
|
213
225
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
from pathlib import Path
|
|
4
|
-
from typing import Annotated
|
|
5
|
+
from typing import Annotated, Any
|
|
5
6
|
|
|
6
7
|
import typer
|
|
8
|
+
from jsonschema import ValidationError, validate
|
|
7
9
|
from rich import box
|
|
8
10
|
from rich.console import Console, Group
|
|
9
11
|
from rich.panel import Panel
|
|
@@ -18,6 +20,7 @@ from apherisfold_cli.commands.foundry import (
|
|
|
18
20
|
foundry_get,
|
|
19
21
|
foundry_list,
|
|
20
22
|
foundry_logs,
|
|
23
|
+
foundry_models,
|
|
21
24
|
foundry_run,
|
|
22
25
|
foundry_workflow_graph,
|
|
23
26
|
)
|
|
@@ -245,18 +248,36 @@ def workflows_get_cmd(
|
|
|
245
248
|
info.add_row("Output", str(payload.get("output") or ""))
|
|
246
249
|
info.add_row("Profiles", ", ".join(str(item) for item in payload.get("profiles", []) if item))
|
|
247
250
|
|
|
251
|
+
models_value = payload.get("models")
|
|
252
|
+
models = models_value if isinstance(models_value, list) else []
|
|
253
|
+
for model in models:
|
|
254
|
+
if not isinstance(model, dict):
|
|
255
|
+
continue
|
|
256
|
+
defaults = _predict_params_defaults(model)
|
|
257
|
+
if defaults:
|
|
258
|
+
info.add_row(f"{model.get('id', 'model')} defaults", json.dumps(defaults, sort_keys=True))
|
|
259
|
+
|
|
248
260
|
examples = Table(box=box.SIMPLE_HEAD, show_header=False, padding=(0, 1), expand=False)
|
|
249
261
|
examples.add_column("COMMAND", style="cyan", overflow="fold")
|
|
250
262
|
examples.add_row(f"apheris-foundry workflows run --workflow {workflow_id} --input input.csv")
|
|
263
|
+
if models:
|
|
264
|
+
examples.add_row(f"apheris-foundry workflows run --workflow {workflow_id} --model-params '{{\"seeds\":[42]}}'")
|
|
265
|
+
examples.add_row(f"apheris-foundry workflows run --workflow {workflow_id} --model-params @model-params.json")
|
|
251
266
|
examples.add_row(f"apheris-foundry workflows graph --workflow {workflow_id}")
|
|
252
267
|
|
|
268
|
+
parameter_help = "Workflow-specific values can be passed with repeated --param key=value options."
|
|
269
|
+
if models:
|
|
270
|
+
parameter_help = (
|
|
271
|
+
"Use --param key=value for workflow values and --model-params JSON (or @file.json) for model values."
|
|
272
|
+
)
|
|
273
|
+
|
|
253
274
|
run_simple_listing(
|
|
254
275
|
task_name="foundry.workflows.get",
|
|
255
276
|
payload=payload,
|
|
256
277
|
renderable_output=Group(
|
|
257
278
|
Panel(info, title="Foundry Workflow", border_style="cyan"),
|
|
258
279
|
Panel(
|
|
259
|
-
Text(
|
|
280
|
+
Text(parameter_help),
|
|
260
281
|
title="Parameters",
|
|
261
282
|
border_style="dim",
|
|
262
283
|
expand=False,
|
|
@@ -266,6 +287,14 @@ def workflows_get_cmd(
|
|
|
266
287
|
)
|
|
267
288
|
|
|
268
289
|
|
|
290
|
+
@workflows_app.command("models", help="List models and prediction parameters for a Foundry workflow.")
|
|
291
|
+
def workflows_models_cmd(
|
|
292
|
+
workflow_id: Annotated[str, typer.Option("--workflow", help="Workflow ID.")],
|
|
293
|
+
json_output: Annotated[bool, typer.Option("--json", help="Render as JSON.")] = False,
|
|
294
|
+
) -> None:
|
|
295
|
+
foundry_models(workflow_id, json_output=json_output)
|
|
296
|
+
|
|
297
|
+
|
|
269
298
|
@workflows_app.command("graph", help="Show a Foundry workflow graph served by Hub.")
|
|
270
299
|
def workflows_graph_cmd(
|
|
271
300
|
workflow_id: Annotated[str, typer.Option("--workflow", help="Workflow ID.")],
|
|
@@ -297,6 +326,17 @@ def workflows_run_cmd(
|
|
|
297
326
|
list[str] | None,
|
|
298
327
|
typer.Option("--param", help="Nextflow parameter as key=value. Repeat for multiple parameters."),
|
|
299
328
|
] = None,
|
|
329
|
+
weight: Annotated[
|
|
330
|
+
str | None,
|
|
331
|
+
typer.Option("--weight", help="Selected model weight version."),
|
|
332
|
+
] = None,
|
|
333
|
+
model_params_raw: Annotated[
|
|
334
|
+
str | None,
|
|
335
|
+
typer.Option(
|
|
336
|
+
"--model-params",
|
|
337
|
+
help="Model parameters as an inline JSON object or @path/to/model-params.json.",
|
|
338
|
+
),
|
|
339
|
+
] = None,
|
|
300
340
|
tag: Annotated[
|
|
301
341
|
list[str] | None,
|
|
302
342
|
typer.Option("--tag", help="Tag to attach to the run. Repeat for multiple tags."),
|
|
@@ -305,7 +345,22 @@ def workflows_run_cmd(
|
|
|
305
345
|
wait: Annotated[bool, typer.Option("--wait", help="Block until the run reaches a terminal status.")] = False,
|
|
306
346
|
json_output: Annotated[bool, typer.Option("--json", help="Render the run response as JSON.")] = False,
|
|
307
347
|
) -> None:
|
|
308
|
-
|
|
348
|
+
try:
|
|
349
|
+
params = _foundry_params_from_options(param)
|
|
350
|
+
if weight:
|
|
351
|
+
params = {**(params or {}), "weight": weight}
|
|
352
|
+
model_params = _load_model_params(model_params_raw)
|
|
353
|
+
if model_params is not None:
|
|
354
|
+
params = _validate_foundry_model_params(workflow_id, params, model_params)
|
|
355
|
+
params = {**(params or {}), "modelParams": json.dumps(model_params, separators=(",", ":"))}
|
|
356
|
+
elif weight:
|
|
357
|
+
# A weight identifies a model version only in the context of a
|
|
358
|
+
# model. For a one-model workflow we can make that selection
|
|
359
|
+
# explicit without burdening the user with --param model=... .
|
|
360
|
+
_, params = _resolve_foundry_model_selection(workflow_id, params)
|
|
361
|
+
except typer.BadParameter as error:
|
|
362
|
+
typer.echo(str(error), err=True)
|
|
363
|
+
raise typer.Exit(code=2) from error
|
|
309
364
|
inputs = _foundry_input_files(workflow_id, input_path)
|
|
310
365
|
foundry_run(
|
|
311
366
|
workflow_id,
|
|
@@ -327,10 +382,116 @@ def _foundry_params_from_options(param: list[str] | None) -> dict[str, str] | No
|
|
|
327
382
|
if "=" not in kv:
|
|
328
383
|
raise typer.BadParameter(f"--param must be key=value, got: {kv!r}")
|
|
329
384
|
key, value = kv.split("=", 1)
|
|
385
|
+
if key == "modelParams":
|
|
386
|
+
raise typer.BadParameter("use --model-params JSON (or @file.json) for model parameters")
|
|
387
|
+
if key == "weight":
|
|
388
|
+
raise typer.BadParameter("use --weight for the selected model weight version")
|
|
330
389
|
params[key] = value
|
|
331
390
|
return params
|
|
332
391
|
|
|
333
392
|
|
|
393
|
+
def _load_model_params(raw_value: str | None) -> dict[str, Any] | None:
|
|
394
|
+
if raw_value is None:
|
|
395
|
+
return None
|
|
396
|
+
|
|
397
|
+
source = raw_value
|
|
398
|
+
if raw_value.startswith("@"):
|
|
399
|
+
path = Path(raw_value[1:])
|
|
400
|
+
if not raw_value[1:] or not path.is_file():
|
|
401
|
+
raise typer.BadParameter(f"--model-params file does not exist or is not a file: {path}")
|
|
402
|
+
try:
|
|
403
|
+
source = path.read_text(encoding="utf-8")
|
|
404
|
+
except OSError as error:
|
|
405
|
+
raise typer.BadParameter(f"could not read --model-params file: {path}") from error
|
|
406
|
+
try:
|
|
407
|
+
value = json.loads(source)
|
|
408
|
+
except json.JSONDecodeError as error:
|
|
409
|
+
raise typer.BadParameter("--model-params must be a valid JSON object or @path/to/file.json") from error
|
|
410
|
+
if not isinstance(value, dict):
|
|
411
|
+
raise typer.BadParameter("--model-params must be a JSON object")
|
|
412
|
+
return value
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _predict_params_defaults(model: dict[str, Any]) -> dict[str, Any]:
|
|
416
|
+
wrapper = model.get("wrapper")
|
|
417
|
+
if not isinstance(wrapper, dict):
|
|
418
|
+
return {}
|
|
419
|
+
defaults = wrapper.get("predictParamsDefaults")
|
|
420
|
+
return defaults if isinstance(defaults, dict) else {}
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _validate_foundry_model_params(
|
|
424
|
+
workflow_id: str,
|
|
425
|
+
params: dict[str, str] | None,
|
|
426
|
+
model_params: dict[str, Any],
|
|
427
|
+
) -> dict[str, str]:
|
|
428
|
+
selected_model, resolved_params = _resolve_foundry_model_selection(workflow_id, params)
|
|
429
|
+
schema = _foundry_model_params_schema(selected_model)
|
|
430
|
+
try:
|
|
431
|
+
validate(instance=model_params, schema=schema)
|
|
432
|
+
except ValidationError as error:
|
|
433
|
+
raise typer.BadParameter(f"invalid --model-params: {error.message}") from error
|
|
434
|
+
return resolved_params
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def _resolve_foundry_model_selection(
|
|
438
|
+
workflow_id: str, params: dict[str, str] | None
|
|
439
|
+
) -> tuple[dict[str, Any], dict[str, str]]:
|
|
440
|
+
payload = get_foundry_workflow(workflow_id)
|
|
441
|
+
models_value = payload.get("models")
|
|
442
|
+
models = models_value if isinstance(models_value, list) else []
|
|
443
|
+
resolved_params = dict(params or {})
|
|
444
|
+
selected_model_id = resolved_params.get("model")
|
|
445
|
+
selected_model = next(
|
|
446
|
+
(model for model in models if isinstance(model, dict) and model.get("id") == selected_model_id),
|
|
447
|
+
None,
|
|
448
|
+
)
|
|
449
|
+
if selected_model is None and len(models) == 1 and isinstance(models[0], dict):
|
|
450
|
+
selected_model = models[0]
|
|
451
|
+
model_id = selected_model.get("id")
|
|
452
|
+
if isinstance(model_id, str) and model_id:
|
|
453
|
+
resolved_params["model"] = model_id
|
|
454
|
+
if selected_model is None:
|
|
455
|
+
raise typer.BadParameter("this option requires a workflow with one selected model; pass --param model=<id>")
|
|
456
|
+
|
|
457
|
+
weights = selected_model.get("weights") if isinstance(selected_model.get("weights"), list) else []
|
|
458
|
+
requested_weight = resolved_params.get("weight")
|
|
459
|
+
selected_weight = (
|
|
460
|
+
next(
|
|
461
|
+
(item for item in weights if isinstance(item, dict) and item.get("version") == requested_weight),
|
|
462
|
+
None,
|
|
463
|
+
)
|
|
464
|
+
if requested_weight
|
|
465
|
+
else next((item for item in weights if isinstance(item, dict) and item.get("default")), None)
|
|
466
|
+
)
|
|
467
|
+
if selected_weight is None and not requested_weight:
|
|
468
|
+
selected_weight = next((item for item in weights if isinstance(item, dict)), None)
|
|
469
|
+
if not isinstance(selected_weight, dict) or not isinstance(selected_weight.get("version"), str):
|
|
470
|
+
raise typer.BadParameter("the selected model does not advertise the requested weight")
|
|
471
|
+
if not requested_weight:
|
|
472
|
+
resolved_params["weight"] = selected_weight["version"]
|
|
473
|
+
return selected_model, resolved_params
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def _foundry_model_params_schema(model: dict[str, Any]) -> dict[str, Any]:
|
|
477
|
+
defaults = _predict_params_defaults(model)
|
|
478
|
+
schemas = model.get("schemas")
|
|
479
|
+
tasks = schemas.get("tasks") if isinstance(schemas, dict) else None
|
|
480
|
+
task = (
|
|
481
|
+
next((item for item in tasks if isinstance(item, dict) and item.get("name") == "predict-openfold3"), None)
|
|
482
|
+
if isinstance(tasks, list)
|
|
483
|
+
else None
|
|
484
|
+
)
|
|
485
|
+
parameters = task.get("parameters") if isinstance(task, dict) else None
|
|
486
|
+
properties = parameters.get("properties") if isinstance(parameters, dict) else None
|
|
487
|
+
if not defaults or not isinstance(properties, dict):
|
|
488
|
+
raise typer.BadParameter("Hub did not provide model parameter schema for the selected model")
|
|
489
|
+
model_properties = {name: properties[name] for name in defaults if isinstance(properties.get(name), dict)}
|
|
490
|
+
if set(model_properties) != set(defaults):
|
|
491
|
+
raise typer.BadParameter("Hub model parameter defaults do not match its advertised schema")
|
|
492
|
+
return {"type": "object", "additionalProperties": False, "properties": model_properties}
|
|
493
|
+
|
|
494
|
+
|
|
334
495
|
def _generic_input_files(input_paths: list[Path] | None) -> dict[str, str] | None:
|
|
335
496
|
if not input_paths:
|
|
336
497
|
return None
|
|
@@ -345,20 +506,10 @@ def _generic_input_files(input_paths: list[Path] | None) -> dict[str, str] | Non
|
|
|
345
506
|
|
|
346
507
|
|
|
347
508
|
def _foundry_input_files(workflow: str, input_paths: list[Path] | None) -> dict[str, str] | None:
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
json_names = [name for name in inputs if name.lower().endswith(".json")]
|
|
353
|
-
if not json_names:
|
|
354
|
-
return inputs
|
|
355
|
-
|
|
356
|
-
request_name = "queries.json" if "queries.json" in inputs else json_names[0]
|
|
357
|
-
request_content = inputs[request_name]
|
|
358
|
-
if request_name != "queries.json":
|
|
359
|
-
del inputs[request_name]
|
|
360
|
-
inputs["queries.json"] = request_content
|
|
361
|
-
return inputs
|
|
509
|
+
del workflow
|
|
510
|
+
# The Foundry MSA task accepts one request JSON by content, not filename.
|
|
511
|
+
# Preserve the user-provided name so staging never creates a second JSON.
|
|
512
|
+
return _generic_input_files(input_paths)
|
|
362
513
|
|
|
363
514
|
|
|
364
515
|
def main() -> int:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
@@ -15,6 +15,7 @@ from apherisfold_cli.core.auth import (
|
|
|
15
15
|
build_authorization_parameters,
|
|
16
16
|
build_token_exchange_form,
|
|
17
17
|
clear_session,
|
|
18
|
+
config_directory,
|
|
18
19
|
fetch_discovery_document,
|
|
19
20
|
fetch_runtime_auth_config,
|
|
20
21
|
format_expires_at,
|
|
@@ -39,6 +40,13 @@ def isolate_auth_config_dir(tmp_path: Path, monkeypatch) -> None:
|
|
|
39
40
|
monkeypatch.setenv("APHERISFOLD_CONFIG_DIR", str(tmp_path / "auth-config"))
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
def test_config_directory_prefers_foundry_environment_variable(tmp_path: Path, monkeypatch) -> None:
|
|
44
|
+
expected = tmp_path / "foundry-config"
|
|
45
|
+
monkeypatch.setenv("APHERIS_FOUNDRY_CONFIG_DIR", str(expected))
|
|
46
|
+
|
|
47
|
+
assert config_directory() == expected
|
|
48
|
+
|
|
49
|
+
|
|
42
50
|
def test_fetch_runtime_auth_config_parses_forgerock_payload(monkeypatch) -> None:
|
|
43
51
|
payload = {
|
|
44
52
|
"AUTH0_ENABLED": True,
|
|
@@ -244,6 +244,14 @@ def hub_api_fixture(monkeypatch):
|
|
|
244
244
|
],
|
|
245
245
|
},
|
|
246
246
|
},
|
|
247
|
+
{
|
|
248
|
+
"id": "predict",
|
|
249
|
+
"description": "Predict structures",
|
|
250
|
+
"title": "Predict",
|
|
251
|
+
"input": "Request JSON",
|
|
252
|
+
"output": "Structure predictions",
|
|
253
|
+
"profiles": ["local", "k8s"],
|
|
254
|
+
},
|
|
247
255
|
{
|
|
248
256
|
"id": "fragment-analysis",
|
|
249
257
|
"description": "Fragment analysis",
|
|
@@ -275,20 +283,55 @@ def hub_api_fixture(monkeypatch):
|
|
|
275
283
|
for workflow in workflows:
|
|
276
284
|
if workflow["id"] != workflow_id:
|
|
277
285
|
continue
|
|
278
|
-
if workflow_id
|
|
286
|
+
if workflow_id in {"legacy_predict", "predict"}:
|
|
279
287
|
return {
|
|
280
288
|
**workflow,
|
|
281
289
|
"models": [
|
|
282
290
|
{
|
|
283
|
-
"id": "
|
|
284
|
-
"version": "
|
|
291
|
+
"id": "openfold3",
|
|
292
|
+
"version": "openfold3:0.0.0",
|
|
293
|
+
"wrapper": {
|
|
294
|
+
"predictParamsDefaults": {
|
|
295
|
+
"num_diffusion_samples": 1,
|
|
296
|
+
"seeds": [42],
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
"schemas": {
|
|
300
|
+
"tasks": [
|
|
301
|
+
{
|
|
302
|
+
"name": "predict-openfold3",
|
|
303
|
+
"parameters": {
|
|
304
|
+
"type": "object",
|
|
305
|
+
"additionalProperties": False,
|
|
306
|
+
"required": ["weights"],
|
|
307
|
+
"properties": {
|
|
308
|
+
"weights": {"type": "string"},
|
|
309
|
+
"num_diffusion_samples": {
|
|
310
|
+
"type": "integer",
|
|
311
|
+
"minimum": 1,
|
|
312
|
+
},
|
|
313
|
+
"seeds": {
|
|
314
|
+
"type": "array",
|
|
315
|
+
"items": {"type": "integer", "minimum": 0},
|
|
316
|
+
"minItems": 1,
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
},
|
|
285
323
|
"weights": [
|
|
286
324
|
{
|
|
287
325
|
"version": "0.0.0",
|
|
288
326
|
"description": "Mock model weights",
|
|
289
327
|
"capabilities": ["inference"],
|
|
290
328
|
"default": True,
|
|
291
|
-
}
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
"version": "0.3.0",
|
|
332
|
+
"description": "Alternative model weights",
|
|
333
|
+
"capabilities": ["inference"],
|
|
334
|
+
},
|
|
292
335
|
],
|
|
293
336
|
}
|
|
294
337
|
],
|
|
@@ -359,9 +402,27 @@ def test_foundry_workflows_get_shows_workflow_contract(hub_api_fixture) -> None:
|
|
|
359
402
|
assert "Molecule CSV" in result.stdout
|
|
360
403
|
assert "Fragment frequency" in result.stdout
|
|
361
404
|
assert "--param key=value" in result.stdout
|
|
405
|
+
assert "--model-params" not in result.stdout
|
|
362
406
|
assert hub_api_fixture["get_foundry_workflow"] == ["fragment-analysis"]
|
|
363
407
|
|
|
364
408
|
|
|
409
|
+
def test_foundry_workflows_get_shows_prediction_defaults(hub_api_fixture) -> None:
|
|
410
|
+
result = runner.invoke(app, ["workflows", "get", "--workflow", "predict"])
|
|
411
|
+
|
|
412
|
+
assert result.exit_code == 0
|
|
413
|
+
assert '"num_diffusion_samples": 1' in result.stdout
|
|
414
|
+
assert "--model-params" in result.stdout
|
|
415
|
+
assert "@model-params.json" in result.stdout
|
|
416
|
+
assert '"seeds": [42]' in result.stdout
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def test_foundry_workflows_models_shows_prediction_defaults(hub_api_fixture) -> None:
|
|
420
|
+
result = runner.invoke(app, ["workflows", "models", "--workflow", "predict"], env={"COLUMNS": "180"})
|
|
421
|
+
|
|
422
|
+
assert result.exit_code == 0
|
|
423
|
+
assert '"num_diffusion_samples": 1' in result.stdout
|
|
424
|
+
|
|
425
|
+
|
|
365
426
|
def test_foundry_workflows_graph_supports_mounted_workflows(hub_api_fixture) -> None:
|
|
366
427
|
result = runner.invoke(app, ["workflows", "graph", "--workflow", "fragment-analysis"])
|
|
367
428
|
|
|
@@ -403,6 +464,101 @@ def test_foundry_workflows_run_stages_generic_input_and_params(hub_api_fixture)
|
|
|
403
464
|
assert body["inputs"]["molecules.csv"] == "smiles,name\nCCO,ethanol\n"
|
|
404
465
|
|
|
405
466
|
|
|
467
|
+
def test_foundry_workflows_run_serializes_inline_validated_model_params(hub_api_fixture) -> None:
|
|
468
|
+
result = runner.invoke(
|
|
469
|
+
app,
|
|
470
|
+
[
|
|
471
|
+
"workflows",
|
|
472
|
+
"run",
|
|
473
|
+
"--workflow",
|
|
474
|
+
"predict",
|
|
475
|
+
"--model-params",
|
|
476
|
+
'{"seeds":[7,9],"num_diffusion_samples":3}',
|
|
477
|
+
],
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
assert result.exit_code == 0
|
|
481
|
+
body = hub_api_fixture["create_foundry_run"][0]
|
|
482
|
+
assert json.loads(body["params"]["modelParams"]) == {
|
|
483
|
+
"num_diffusion_samples": 3,
|
|
484
|
+
"seeds": [7, 9],
|
|
485
|
+
}
|
|
486
|
+
assert body["params"]["model"] == "openfold3"
|
|
487
|
+
assert body["params"]["weight"] == "0.0.0"
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def test_foundry_workflows_run_loads_model_params_from_json_file(tmp_path: Path, hub_api_fixture) -> None:
|
|
491
|
+
params_file = tmp_path / "model-params.json"
|
|
492
|
+
params_file.write_text('{"seeds":[7],"num_diffusion_samples":2}', encoding="utf-8")
|
|
493
|
+
|
|
494
|
+
result = runner.invoke(
|
|
495
|
+
app,
|
|
496
|
+
["workflows", "run", "--workflow", "predict", "--model-params", f"@{params_file}"],
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
assert result.exit_code == 0
|
|
500
|
+
assert json.loads(hub_api_fixture["create_foundry_run"][0]["params"]["modelParams"]) == {
|
|
501
|
+
"seeds": [7],
|
|
502
|
+
"num_diffusion_samples": 2,
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def test_foundry_workflows_run_serializes_selected_weight(hub_api_fixture) -> None:
|
|
507
|
+
result = runner.invoke(
|
|
508
|
+
app,
|
|
509
|
+
["workflows", "run", "--workflow", "predict", "--weight", "0.3.0"],
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
assert result.exit_code == 0
|
|
513
|
+
assert hub_api_fixture["create_foundry_run"][0]["params"] == {"model": "openfold3", "weight": "0.3.0"}
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def test_foundry_workflows_run_preserves_predict_request_filename(tmp_path: Path, hub_api_fixture) -> None:
|
|
517
|
+
request = tmp_path / "request-protein-ligand.json"
|
|
518
|
+
request.write_text('{"queries": []}', encoding="utf-8")
|
|
519
|
+
|
|
520
|
+
result = runner.invoke(app, ["workflows", "run", "--workflow", "predict", "--input", str(request)])
|
|
521
|
+
|
|
522
|
+
assert result.exit_code == 0
|
|
523
|
+
assert hub_api_fixture["create_foundry_run"][0]["inputs"] == {"request-protein-ligand.json": '{"queries": []}'}
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def test_foundry_workflows_run_rejects_model_params_outside_schema(hub_api_fixture) -> None:
|
|
527
|
+
result = runner.invoke(
|
|
528
|
+
app,
|
|
529
|
+
[
|
|
530
|
+
"workflows",
|
|
531
|
+
"run",
|
|
532
|
+
"--workflow",
|
|
533
|
+
"predict",
|
|
534
|
+
"--model-params",
|
|
535
|
+
'{"seed":7}',
|
|
536
|
+
],
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
assert result.exit_code != 0
|
|
540
|
+
assert "invalid --model-params" in result.stderr
|
|
541
|
+
assert hub_api_fixture["create_foundry_run"] == []
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def test_foundry_workflows_run_rejects_raw_model_params(hub_api_fixture) -> None:
|
|
545
|
+
result = runner.invoke(
|
|
546
|
+
app,
|
|
547
|
+
[
|
|
548
|
+
"workflows",
|
|
549
|
+
"run",
|
|
550
|
+
"--workflow",
|
|
551
|
+
"predict",
|
|
552
|
+
"--param",
|
|
553
|
+
'modelParams={"seeds":[7]}',
|
|
554
|
+
],
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
assert result.exit_code != 0
|
|
558
|
+
assert "use --model-params" in result.stderr
|
|
559
|
+
assert hub_api_fixture["create_foundry_run"] == []
|
|
560
|
+
|
|
561
|
+
|
|
406
562
|
def test_foundry_list_shows_parallel_active_steps(monkeypatch) -> None:
|
|
407
563
|
monkeypatch.setattr(
|
|
408
564
|
"apherisfold_cli.commands.foundry.list_foundry_runs",
|
|
@@ -695,14 +851,14 @@ def test_login_requires_profile_or_url() -> None:
|
|
|
695
851
|
result = runner.invoke(app, ["login"])
|
|
696
852
|
try:
|
|
697
853
|
assert result.exit_code == 2
|
|
698
|
-
assert "Usage:
|
|
854
|
+
assert "Usage: apheris-foundry login --url HUB_URL [--callback-host HOST]" in result.stdout
|
|
699
855
|
assert "Login Options" in result.stdout
|
|
700
856
|
assert "Built-In Profiles" not in result.stdout
|
|
701
857
|
assert "--profile" not in result.stdout
|
|
702
858
|
assert "apheris-integ" not in result.stdout
|
|
703
859
|
assert "apheris-staging" not in result.stdout
|
|
704
860
|
assert "Examples" in result.stdout
|
|
705
|
-
assert "
|
|
861
|
+
assert "apheris-foundry login --url https://hub.example.com" in result.stdout
|
|
706
862
|
assert "--callback-host" in result.stdout
|
|
707
863
|
assert "localhost" in result.stdout
|
|
708
864
|
finally:
|
|
@@ -17,6 +17,7 @@ from apherisfold_cli.core.hub_requests import (
|
|
|
17
17
|
get_foundry_run,
|
|
18
18
|
get_foundry_run_logs,
|
|
19
19
|
get_foundry_workflow,
|
|
20
|
+
get_model_schema,
|
|
20
21
|
list_foundry_runs,
|
|
21
22
|
list_foundry_workflows,
|
|
22
23
|
)
|
|
@@ -189,6 +190,21 @@ def test_get_foundry_workflow_encodes_id(tmp_path: Path, monkeypatch) -> None:
|
|
|
189
190
|
assert "fragment-analysis" in seen_url[0]
|
|
190
191
|
|
|
191
192
|
|
|
193
|
+
def test_get_model_schema_encodes_model_selection(tmp_path: Path, monkeypatch) -> None:
|
|
194
|
+
_save_refreshable_session(tmp_path, monkeypatch)
|
|
195
|
+
seen_url: list[str] = []
|
|
196
|
+
|
|
197
|
+
def fake_urlopen(request: urllib.request.Request, **_kwargs: object) -> FakeResponse:
|
|
198
|
+
seen_url.append(request.full_url)
|
|
199
|
+
return FakeResponse(b'{"paths": {}}')
|
|
200
|
+
|
|
201
|
+
monkeypatch.setattr("apherisfold_cli.core.hub_requests.urllib.request.urlopen", fake_urlopen)
|
|
202
|
+
assert get_model_schema(model_version_id="openfold3:0.0.0", weight_version="0.0.0", capability="inference") == {
|
|
203
|
+
"paths": {}
|
|
204
|
+
}
|
|
205
|
+
assert "/api/v1/schemas/openfold3%3A0.0.0/0.0.0?capability=inference" in seen_url[0]
|
|
206
|
+
|
|
207
|
+
|
|
192
208
|
# ---------------------------------------------------------------------------
|
|
193
209
|
# get_foundry_run_logs
|
|
194
210
|
# ---------------------------------------------------------------------------
|
|
@@ -87,7 +87,7 @@ def test_main_writes_error_file_when_write_errors_set(monkeypatch, tmp_path) ->
|
|
|
87
87
|
assert data["message"] == "Input error"
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def
|
|
90
|
+
def test_foundry_predict_input_preserves_request_filename(tmp_path: Path) -> None:
|
|
91
91
|
request = '{"queries": [{"chains": [{"chain_id": "A", "polymer_type": "protein", "sequence": "ACDE"}]}]}'
|
|
92
92
|
request_path = tmp_path / "request-minimal.json"
|
|
93
93
|
request_path.write_text(request, encoding="utf-8")
|
|
@@ -95,8 +95,8 @@ def test_foundry_predict_input_uses_queries_json(tmp_path: Path) -> None:
|
|
|
95
95
|
inputs = _foundry_input_files("predict", [request_path])
|
|
96
96
|
|
|
97
97
|
assert inputs is not None
|
|
98
|
-
assert list(inputs) == ["
|
|
99
|
-
assert inputs["
|
|
98
|
+
assert list(inputs) == ["request-minimal.json"]
|
|
99
|
+
assert inputs["request-minimal.json"] == request
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
def test_foundry_predict_new_schema_passes_through(tmp_path: Path) -> None:
|
|
@@ -109,7 +109,7 @@ def test_foundry_predict_new_schema_passes_through(tmp_path: Path) -> None:
|
|
|
109
109
|
assert inputs == {"queries.json": request}
|
|
110
110
|
|
|
111
111
|
|
|
112
|
-
def
|
|
112
|
+
def test_foundry_predict_preserves_msa_asset_and_request_name(tmp_path: Path) -> None:
|
|
113
113
|
request = tmp_path / "request.json"
|
|
114
114
|
request.write_text(
|
|
115
115
|
'{"queries": [{"chains": [{"chain_id": "A", "sequence": "ACDE", "msa_path": "protein.a3m"}]}]}',
|
|
@@ -121,7 +121,7 @@ def test_foundry_predict_preserves_msa_asset_and_rewrites_request_name(tmp_path:
|
|
|
121
121
|
inputs = _foundry_input_files("predict", [request, msa])
|
|
122
122
|
|
|
123
123
|
assert inputs == {
|
|
124
|
-
"
|
|
124
|
+
"request.json": request.read_text(encoding="utf-8"),
|
|
125
125
|
"protein.a3m": "##4\nA\n",
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.0"
|
|
File without changes
|
|
File without changes
|
{apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apheris_foundry_cli.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/commands/__init__.py
RENAMED
|
File without changes
|
{apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/login_profiles.py
RENAMED
|
File without changes
|
|
File without changes
|
{apheris_foundry_cli-0.2.0 → apheris_foundry_cli-0.3.0}/src/apherisfold_cli/core/result_artifacts.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
|