londonaicentre-mesa-runtime 0.1.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.
- londonaicentre_mesa_runtime-0.1.0/PKG-INFO +51 -0
- londonaicentre_mesa_runtime-0.1.0/README.md +40 -0
- londonaicentre_mesa_runtime-0.1.0/pyproject.toml +31 -0
- londonaicentre_mesa_runtime-0.1.0/pyproject.toml.orig +30 -0
- londonaicentre_mesa_runtime-0.1.0/src/mesa_runtime/__init__.py +19 -0
- londonaicentre_mesa_runtime-0.1.0/src/mesa_runtime/client.py +331 -0
- londonaicentre_mesa_runtime-0.1.0/src/mesa_runtime/py.typed +0 -0
- londonaicentre_mesa_runtime-0.1.0/src/mesa_runtime/types.py +97 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: londonaicentre-mesa-runtime
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Author: Martin Chapman
|
|
5
|
+
Author-email: Martin Chapman <contact@martinchapman.co.uk>
|
|
6
|
+
Requires-Dist: boto3>=1.35
|
|
7
|
+
Requires-Dist: httpx>=0.28
|
|
8
|
+
Requires-Dist: pydantic>=2.0
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# mesa-runtime/lib
|
|
13
|
+
|
|
14
|
+
A thin, programmatic wrapper around CI in this repo comprising a simple [backend](../frontend/deploy/) and a library that communicates with it.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from mesa_runtime import MesaRuntime
|
|
20
|
+
|
|
21
|
+
client = MesaRuntime("https://example.cloudfront.net", "username", "password")
|
|
22
|
+
|
|
23
|
+
run_id = client.dispatch("batch-2026-01", "foo", "onco")
|
|
24
|
+
print(client.get_run(run_id))
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Batch inference uploads the batch file with the caller's AWS credentials:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
run_id = client.run_batch_inference("foo/bar", "baz", "qux.jsonl", "quux")
|
|
31
|
+
print(client.get_inference_run(run_id))
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## API
|
|
35
|
+
|
|
36
|
+
| Method | Description |
|
|
37
|
+
|--------|-------------|
|
|
38
|
+
| `list_training_data()` | List available training data objects from S3 |
|
|
39
|
+
| `dispatch(training_batch_name, model_name, schema, ...)` | Trigger a workflow run, returns its ID |
|
|
40
|
+
| `get_run(run_id)` | Fetch a single run by ID |
|
|
41
|
+
| `list_runs()` | List the ten most recent runs |
|
|
42
|
+
| `list_queued_runs()` | List all pending runs |
|
|
43
|
+
| `cancel(run_id)` | Cancel a run |
|
|
44
|
+
| `get_logs(run_id, ...)` | Fetch step logs for a run |
|
|
45
|
+
| `get_step_inputs(run_id)` | Extract dispatch inputs from step env vars |
|
|
46
|
+
| `get_runner_status()` | Get runner online/busy status |
|
|
47
|
+
| `run_batch_inference(job_id, model_id, batch_file, bucket, ...)` | Upload a Bedrock batch file and trigger an inference run, returns its ID |
|
|
48
|
+
| `get_inference_run(run_id)` | Fetch a single inference run by ID |
|
|
49
|
+
| `list_inference_runs()` | List the ten most recent inference runs |
|
|
50
|
+
| `cancel_inference(run_id)` | Cancel an inference run |
|
|
51
|
+
| `get_inference_logs(run_id, ...)` | Fetch step logs for an inference run |
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# mesa-runtime/lib
|
|
2
|
+
|
|
3
|
+
A thin, programmatic wrapper around CI in this repo comprising a simple [backend](../frontend/deploy/) and a library that communicates with it.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from mesa_runtime import MesaRuntime
|
|
9
|
+
|
|
10
|
+
client = MesaRuntime("https://example.cloudfront.net", "username", "password")
|
|
11
|
+
|
|
12
|
+
run_id = client.dispatch("batch-2026-01", "foo", "onco")
|
|
13
|
+
print(client.get_run(run_id))
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Batch inference uploads the batch file with the caller's AWS credentials:
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
run_id = client.run_batch_inference("foo/bar", "baz", "qux.jsonl", "quux")
|
|
20
|
+
print(client.get_inference_run(run_id))
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## API
|
|
24
|
+
|
|
25
|
+
| Method | Description |
|
|
26
|
+
|--------|-------------|
|
|
27
|
+
| `list_training_data()` | List available training data objects from S3 |
|
|
28
|
+
| `dispatch(training_batch_name, model_name, schema, ...)` | Trigger a workflow run, returns its ID |
|
|
29
|
+
| `get_run(run_id)` | Fetch a single run by ID |
|
|
30
|
+
| `list_runs()` | List the ten most recent runs |
|
|
31
|
+
| `list_queued_runs()` | List all pending runs |
|
|
32
|
+
| `cancel(run_id)` | Cancel a run |
|
|
33
|
+
| `get_logs(run_id, ...)` | Fetch step logs for a run |
|
|
34
|
+
| `get_step_inputs(run_id)` | Extract dispatch inputs from step env vars |
|
|
35
|
+
| `get_runner_status()` | Get runner online/busy status |
|
|
36
|
+
| `run_batch_inference(job_id, model_id, batch_file, bucket, ...)` | Upload a Bedrock batch file and trigger an inference run, returns its ID |
|
|
37
|
+
| `get_inference_run(run_id)` | Fetch a single inference run by ID |
|
|
38
|
+
| `list_inference_runs()` | List the ten most recent inference runs |
|
|
39
|
+
| `cancel_inference(run_id)` | Cancel an inference run |
|
|
40
|
+
| `get_inference_logs(run_id, ...)` | Fetch step logs for an inference run |
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "londonaicentre-mesa-runtime"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"boto3>=1.35",
|
|
8
|
+
"httpx>=0.28",
|
|
9
|
+
"pydantic>=2.0",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[[project.authors]]
|
|
13
|
+
name = "Martin Chapman"
|
|
14
|
+
email = "contact@martinchapman.co.uk"
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["uv_build>=0.11.19,<0.12.0"]
|
|
18
|
+
build-backend = "uv_build"
|
|
19
|
+
|
|
20
|
+
[dependency-groups]
|
|
21
|
+
dev = [
|
|
22
|
+
"boto3-stubs[s3]>=1.35",
|
|
23
|
+
"pytest>=9.1.0",
|
|
24
|
+
"pytest-cov>=6.0",
|
|
25
|
+
"pytest-mock>=3.0",
|
|
26
|
+
"ruff>=0.15.17",
|
|
27
|
+
"ty>=0.0.49",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[tool.uv.build-backend]
|
|
31
|
+
module-name = "mesa_runtime"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "londonaicentre-mesa-runtime"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Martin Chapman", email = "contact@martinchapman.co.uk" }
|
|
7
|
+
]
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"boto3>=1.35",
|
|
11
|
+
"httpx>=0.28",
|
|
12
|
+
"pydantic>=2.0",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["uv_build>=0.11.19,<0.12.0"]
|
|
17
|
+
build-backend = "uv_build"
|
|
18
|
+
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
dev = [
|
|
21
|
+
"boto3-stubs[s3]>=1.35",
|
|
22
|
+
"pytest>=9.1.0",
|
|
23
|
+
"pytest-cov>=6.0",
|
|
24
|
+
"pytest-mock>=3.0",
|
|
25
|
+
"ruff>=0.15.17",
|
|
26
|
+
"ty>=0.0.49",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[tool.uv.build-backend]
|
|
30
|
+
module-name = "mesa_runtime"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from mesa_runtime.client import MesaRuntime
|
|
2
|
+
from mesa_runtime.types import (
|
|
3
|
+
BucketObject,
|
|
4
|
+
RunnerStatus,
|
|
5
|
+
RunnerStatusResponse,
|
|
6
|
+
WorkflowConclusion,
|
|
7
|
+
WorkflowRun,
|
|
8
|
+
WorkflowStatus,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"BucketObject",
|
|
13
|
+
"MesaRuntime",
|
|
14
|
+
"RunnerStatus",
|
|
15
|
+
"RunnerStatusResponse",
|
|
16
|
+
"WorkflowConclusion",
|
|
17
|
+
"WorkflowRun",
|
|
18
|
+
"WorkflowStatus",
|
|
19
|
+
]
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import boto3
|
|
7
|
+
from httpx import BasicAuth, Client, Response
|
|
8
|
+
|
|
9
|
+
from mesa_runtime.types import (
|
|
10
|
+
BucketObject,
|
|
11
|
+
InferenceInputs,
|
|
12
|
+
RunnerStatusResponse,
|
|
13
|
+
TrainingInputs,
|
|
14
|
+
WorkflowRun,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MesaRuntime:
|
|
19
|
+
"""A synchronous Python client for the MESA Runtime Lambda API.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
base_url (str): Base URL of the Lambda/CloudFront endpoint
|
|
23
|
+
(e.g. ``"https://example.cloudfront.net"``).
|
|
24
|
+
username (str): Basic auth username.
|
|
25
|
+
password (str): Basic auth password.
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
__TRAINING: str = "/api/workflows"
|
|
30
|
+
__INFERENCE: str = "/api/inference"
|
|
31
|
+
|
|
32
|
+
def __init__(self, base_url: str, username: str, password: str) -> None:
|
|
33
|
+
self.__base_url: str = base_url.rstrip("/")
|
|
34
|
+
self.__auth: BasicAuth = BasicAuth(username, password)
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def _upload(batch_file: str, bucket: str, job_id: str) -> None:
|
|
38
|
+
boto3.client("s3").upload_file(
|
|
39
|
+
batch_file, bucket, f"{job_id}/input/{Path(batch_file).name}"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def _list_runs(self, prefix: str) -> list[WorkflowRun]:
|
|
43
|
+
return [
|
|
44
|
+
WorkflowRun.model_validate(run)
|
|
45
|
+
for run in self._request("GET", prefix).json()
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
def _get_run(self, prefix: str, run_id: int) -> WorkflowRun:
|
|
49
|
+
return WorkflowRun.model_validate(
|
|
50
|
+
self._request("GET", f"{prefix}/{run_id}").json()
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def _cancel(self, prefix: str, run_id: int) -> None:
|
|
54
|
+
self._request("DELETE", f"{prefix}/{run_id}")
|
|
55
|
+
|
|
56
|
+
def _get_logs(self, prefix: str, run_id: int, job: str, step: str) -> str:
|
|
57
|
+
return self._request(
|
|
58
|
+
"GET", f"{prefix}/{run_id}/logs", None, {"job": job, "step": step}
|
|
59
|
+
).text
|
|
60
|
+
|
|
61
|
+
def _request(
|
|
62
|
+
self,
|
|
63
|
+
method: str,
|
|
64
|
+
path: str,
|
|
65
|
+
body: dict[str, Any] | None = None,
|
|
66
|
+
params: dict[str, str] | None = None,
|
|
67
|
+
timeout: float | None = None,
|
|
68
|
+
) -> Response:
|
|
69
|
+
headers: dict[str, str] = {"x-requested-with": "fetch"}
|
|
70
|
+
content: bytes | None = None
|
|
71
|
+
if body is not None:
|
|
72
|
+
content = json.dumps(body).encode()
|
|
73
|
+
headers["content-type"] = "application/json"
|
|
74
|
+
headers["x-amz-content-sha256"] = hashlib.sha256(content).hexdigest()
|
|
75
|
+
with Client(auth=self.__auth, follow_redirects=True, timeout=timeout) as client:
|
|
76
|
+
response: Response = client.request(
|
|
77
|
+
method,
|
|
78
|
+
self.__base_url + path,
|
|
79
|
+
content=content,
|
|
80
|
+
headers=headers,
|
|
81
|
+
params=params,
|
|
82
|
+
)
|
|
83
|
+
response.raise_for_status()
|
|
84
|
+
return response
|
|
85
|
+
|
|
86
|
+
def list_training_data(self) -> list[BucketObject]:
|
|
87
|
+
"""Lists available training data objects from S3.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
list[BucketObject]: Training data objects with key,
|
|
91
|
+
last_modified, and size.
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
return [
|
|
95
|
+
BucketObject.model_validate(obj)
|
|
96
|
+
for obj in self._request("GET", "/api/trainingdata").json()
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
def list_runs(self) -> list[WorkflowRun]:
|
|
100
|
+
"""Lists the ten most recent workflow runs.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
list[WorkflowRun]: Most recent runs, newest first.
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
return self._list_runs(MesaRuntime.__TRAINING)
|
|
107
|
+
|
|
108
|
+
def list_queued_runs(self) -> list[WorkflowRun]:
|
|
109
|
+
"""Lists all pending workflow runs.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
list[WorkflowRun]: All queued/pending runs.
|
|
113
|
+
|
|
114
|
+
"""
|
|
115
|
+
return [
|
|
116
|
+
WorkflowRun.model_validate(run)
|
|
117
|
+
for run in self._request("GET", f"{MesaRuntime.__TRAINING}/queued").json()
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
def dispatch(
|
|
121
|
+
self,
|
|
122
|
+
training_batch_name: str,
|
|
123
|
+
model_name: str,
|
|
124
|
+
schema: str,
|
|
125
|
+
description: str = "",
|
|
126
|
+
version: str = "1.0.0",
|
|
127
|
+
config: str = "../../../mesa-runtime/config.build.finetune.yaml",
|
|
128
|
+
config_from_file: bool = False,
|
|
129
|
+
push_public: bool = False,
|
|
130
|
+
resume: bool = False,
|
|
131
|
+
post_process_only: bool = False,
|
|
132
|
+
) -> int:
|
|
133
|
+
"""Dispatches a workflow run and returns its run ID.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
training_batch_name (str): Name of the training data batch.
|
|
137
|
+
model_name (str): Model name (normalised to lowercase-hyphenated).
|
|
138
|
+
schema (str): Schema type (e.g. ``"onco"``).
|
|
139
|
+
description (str): Optional run description. Defaults to ``""``.
|
|
140
|
+
version (str): Model version string. Defaults to ``"1.0.0"``.
|
|
141
|
+
config (str): Path to a YAML config file. Interpreted by the
|
|
142
|
+
runner unless ``config_from_file`` is set, in which case it
|
|
143
|
+
is instead read locally and sent as ``config_yaml``.
|
|
144
|
+
Defaults to ``"../../../mesa-runtime/config.build.finetune.yaml"``.
|
|
145
|
+
config_from_file (bool): Read ``config`` as a local file and
|
|
146
|
+
send its contents as ``config_yaml`` instead of sending
|
|
147
|
+
``config`` as a path. Defaults to ``False``.
|
|
148
|
+
push_public (bool): Push the trained model to the public
|
|
149
|
+
registry after post-processing. Defaults to ``False``.
|
|
150
|
+
resume (bool): Resume the runner's currently crashed run from
|
|
151
|
+
its on-disk checkpoint instead of starting a new one.
|
|
152
|
+
Defaults to ``False``.
|
|
153
|
+
post_process_only (bool): Skip training; fuse and publish the
|
|
154
|
+
existing trainer.json's already-trained adapter. Mutually
|
|
155
|
+
exclusive with ``resume``. Defaults to ``False``.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
int: The ID of the dispatched run.
|
|
159
|
+
|
|
160
|
+
"""
|
|
161
|
+
return self._request(
|
|
162
|
+
"POST",
|
|
163
|
+
MesaRuntime.__TRAINING,
|
|
164
|
+
TrainingInputs(
|
|
165
|
+
training_batch_name=training_batch_name,
|
|
166
|
+
model_name=model_name,
|
|
167
|
+
schema=schema,
|
|
168
|
+
description=description,
|
|
169
|
+
version=version,
|
|
170
|
+
config="" if config_from_file else config,
|
|
171
|
+
config_yaml=Path(config).read_text() if config_from_file else "",
|
|
172
|
+
push_public=push_public,
|
|
173
|
+
resume=resume,
|
|
174
|
+
post_process_only=post_process_only,
|
|
175
|
+
).model_dump(by_alias=True),
|
|
176
|
+
timeout=30.0,
|
|
177
|
+
).json()["run_id"]
|
|
178
|
+
|
|
179
|
+
def get_run(self, run_id: int) -> WorkflowRun:
|
|
180
|
+
"""Gets a single workflow run by ID.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
run_id (int): The run ID.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
WorkflowRun: The run's current state.
|
|
187
|
+
|
|
188
|
+
"""
|
|
189
|
+
return self._get_run(MesaRuntime.__TRAINING, run_id)
|
|
190
|
+
|
|
191
|
+
def cancel(self, run_id: int) -> None:
|
|
192
|
+
"""Cancels a workflow run.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
run_id (int): The run ID.
|
|
196
|
+
|
|
197
|
+
"""
|
|
198
|
+
self._cancel(MesaRuntime.__TRAINING, run_id)
|
|
199
|
+
|
|
200
|
+
def get_logs(
|
|
201
|
+
self,
|
|
202
|
+
run_id: int,
|
|
203
|
+
job: str = "run",
|
|
204
|
+
step: str = "Run uv run mesa-build-mlx-finetune",
|
|
205
|
+
) -> str:
|
|
206
|
+
"""Gets the log output for a named step within a job.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
run_id (int): The run ID.
|
|
210
|
+
job (str): Name of the job to target. Defaults to ``"run"``.
|
|
211
|
+
step (str): Name of the step within the job. Defaults to
|
|
212
|
+
``"Run uv run mesa-build-mlx-finetune"``.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
str: The step's log output.
|
|
216
|
+
|
|
217
|
+
"""
|
|
218
|
+
return self._get_logs(MesaRuntime.__TRAINING, run_id, job, step)
|
|
219
|
+
|
|
220
|
+
def get_step_inputs(self, run_id: int) -> dict[str, str]:
|
|
221
|
+
"""Gets the dispatch inputs for a run by parsing its step logs.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
run_id (int): The run ID.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
dict[str, str]: Env vars from the step setup block.
|
|
228
|
+
|
|
229
|
+
"""
|
|
230
|
+
return self._request("GET", f"{MesaRuntime.__TRAINING}/{run_id}/inputs").json()
|
|
231
|
+
|
|
232
|
+
def get_runner_status(self) -> RunnerStatusResponse:
|
|
233
|
+
"""Gets the current runner online/busy status.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
RunnerStatusResponse: Runner status and busy flag.
|
|
237
|
+
|
|
238
|
+
"""
|
|
239
|
+
return RunnerStatusResponse.model_validate(
|
|
240
|
+
self._request("GET", "/api/workflows/runner/status").json()
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def run_batch_inference(
|
|
244
|
+
self,
|
|
245
|
+
job_id: str,
|
|
246
|
+
model_id: str,
|
|
247
|
+
batch_file: str,
|
|
248
|
+
bucket: str,
|
|
249
|
+
config: str = "../mesa-runtime/config.build.inference.yml",
|
|
250
|
+
config_from_file: bool = False,
|
|
251
|
+
) -> int:
|
|
252
|
+
"""Uploads a Bedrock batch file to S3 and dispatches an inference run
|
|
253
|
+
over it, in place of a Bedrock batch inference job.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
job_id (str): The id of the batch job, used as its S3 prefix.
|
|
257
|
+
model_id (str): Name of the model served by the runner's
|
|
258
|
+
OpenAI-compatible endpoint.
|
|
259
|
+
batch_file (str): Path to the local Bedrock batch input file.
|
|
260
|
+
bucket (str): The name of the bucket to which the batch file
|
|
261
|
+
should be uploaded and the runner should write output.
|
|
262
|
+
config (str): Path to a YAML config file. Interpreted by the
|
|
263
|
+
runner unless ``config_from_file`` is set, in which case it
|
|
264
|
+
is instead read locally and sent as ``config_yaml``.
|
|
265
|
+
Defaults to ``"../mesa-runtime/config.build.inference.yml"``.
|
|
266
|
+
config_from_file (bool): Read ``config`` as a local file and
|
|
267
|
+
send its contents as ``config_yaml`` instead of sending
|
|
268
|
+
``config`` as a path. Defaults to ``False``.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
int: The ID of the dispatched run.
|
|
272
|
+
|
|
273
|
+
"""
|
|
274
|
+
inputs: InferenceInputs = InferenceInputs(
|
|
275
|
+
job_id=job_id,
|
|
276
|
+
model_name=model_id,
|
|
277
|
+
bucket=bucket,
|
|
278
|
+
config="" if config_from_file else config,
|
|
279
|
+
config_yaml=Path(config).read_text() if config_from_file else "",
|
|
280
|
+
)
|
|
281
|
+
MesaRuntime._upload(batch_file, inputs.bucket, inputs.job_id)
|
|
282
|
+
return self._request(
|
|
283
|
+
"POST", MesaRuntime.__INFERENCE, inputs.model_dump(), timeout=30.0
|
|
284
|
+
).json()["run_id"]
|
|
285
|
+
|
|
286
|
+
def list_inference_runs(self) -> list[WorkflowRun]:
|
|
287
|
+
"""Lists the ten most recent inference runs.
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
list[WorkflowRun]: Most recent runs, newest first.
|
|
291
|
+
|
|
292
|
+
"""
|
|
293
|
+
return self._list_runs(MesaRuntime.__INFERENCE)
|
|
294
|
+
|
|
295
|
+
def get_inference_run(self, run_id: int) -> WorkflowRun:
|
|
296
|
+
"""Gets a single inference run by ID.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
run_id (int): The run ID.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
WorkflowRun: The run's current state.
|
|
303
|
+
|
|
304
|
+
"""
|
|
305
|
+
return self._get_run(MesaRuntime.__INFERENCE, run_id)
|
|
306
|
+
|
|
307
|
+
def cancel_inference(self, run_id: int) -> None:
|
|
308
|
+
"""Cancels an inference run.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
run_id (int): The run ID.
|
|
312
|
+
|
|
313
|
+
"""
|
|
314
|
+
self._cancel(MesaRuntime.__INFERENCE, run_id)
|
|
315
|
+
|
|
316
|
+
def get_inference_logs(
|
|
317
|
+
self, run_id: int, job: str = "run", step: str = "Infer"
|
|
318
|
+
) -> str:
|
|
319
|
+
"""Gets the log output for a named step within an inference job.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
run_id (int): The run ID.
|
|
323
|
+
job (str): Name of the job to target. Defaults to ``"run"``.
|
|
324
|
+
step (str): Name of the step within the job. Defaults to
|
|
325
|
+
``"Infer"``.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
str: The step's log output.
|
|
329
|
+
|
|
330
|
+
"""
|
|
331
|
+
return self._get_logs(MesaRuntime.__INFERENCE, run_id, job, step)
|
|
File without changes
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
|
|
4
|
+
from pydantic import (
|
|
5
|
+
BaseModel,
|
|
6
|
+
ConfigDict,
|
|
7
|
+
Field,
|
|
8
|
+
HttpUrl,
|
|
9
|
+
field_validator,
|
|
10
|
+
model_validator,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BucketObject(BaseModel):
|
|
15
|
+
key: str
|
|
16
|
+
last_modified: str
|
|
17
|
+
size: int
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class WorkflowStatus(StrEnum):
|
|
21
|
+
QUEUED = "queued"
|
|
22
|
+
IN_PROGRESS = "in_progress"
|
|
23
|
+
COMPLETED = "completed"
|
|
24
|
+
WAITING = "waiting"
|
|
25
|
+
REQUESTED = "requested"
|
|
26
|
+
PENDING = "pending"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class WorkflowConclusion(StrEnum):
|
|
30
|
+
ACTION_REQUIRED = "action_required"
|
|
31
|
+
CANCELLED = "cancelled"
|
|
32
|
+
FAILURE = "failure"
|
|
33
|
+
NEUTRAL = "neutral"
|
|
34
|
+
SUCCESS = "success"
|
|
35
|
+
SKIPPED = "skipped"
|
|
36
|
+
STALE = "stale"
|
|
37
|
+
TIMED_OUT = "timed_out"
|
|
38
|
+
STARTUP_FAILURE = "startup_failure"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class WorkflowRun(BaseModel):
|
|
42
|
+
id: int
|
|
43
|
+
status: WorkflowStatus
|
|
44
|
+
conclusion: WorkflowConclusion | None
|
|
45
|
+
created_at: datetime
|
|
46
|
+
html_url: HttpUrl
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class DispatchInputs(BaseModel):
|
|
50
|
+
config: str = Field(default="", max_length=256)
|
|
51
|
+
config_yaml: str = Field(default="", max_length=65536)
|
|
52
|
+
|
|
53
|
+
@model_validator(mode="after")
|
|
54
|
+
def _validate_config_exclusive(self) -> "DispatchInputs":
|
|
55
|
+
if self.config and self.config_yaml:
|
|
56
|
+
raise ValueError("cannot supply both config and config_yaml")
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TrainingInputs(DispatchInputs):
|
|
61
|
+
model_config = ConfigDict(extra="ignore", populate_by_name=True)
|
|
62
|
+
|
|
63
|
+
training_batch_name: str = Field(default="", max_length=256)
|
|
64
|
+
model_name: str = Field(default="", max_length=256)
|
|
65
|
+
schema_: str = Field(default="", alias="schema", max_length=256)
|
|
66
|
+
description: str = Field(default="", max_length=256)
|
|
67
|
+
version: str = Field(default="", max_length=64)
|
|
68
|
+
push_public: bool = Field(default=False)
|
|
69
|
+
resume: bool = Field(default=False)
|
|
70
|
+
post_process_only: bool = Field(default=False)
|
|
71
|
+
|
|
72
|
+
@field_validator("model_name", mode="before")
|
|
73
|
+
@classmethod
|
|
74
|
+
def _normalise_model_name(cls, value: str) -> str:
|
|
75
|
+
return value.replace(" ", "-").lower()
|
|
76
|
+
|
|
77
|
+
@model_validator(mode="after")
|
|
78
|
+
def _validate_resume_exclusive(self) -> "TrainingInputs":
|
|
79
|
+
if self.resume and self.post_process_only:
|
|
80
|
+
raise ValueError("cannot supply both resume and post_process_only")
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class InferenceInputs(DispatchInputs):
|
|
85
|
+
job_id: str = Field(max_length=256, pattern=r"^[A-Za-z0-9_-]+(/[A-Za-z0-9_-]+)*$")
|
|
86
|
+
model_name: str = Field(min_length=1, max_length=256)
|
|
87
|
+
bucket: str = Field(default="", pattern=r"^([a-z0-9][a-z0-9.-]{1,61}[a-z0-9])?$")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class RunnerStatus(StrEnum):
|
|
91
|
+
ONLINE = "online"
|
|
92
|
+
OFFLINE = "offline"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class RunnerStatusResponse(BaseModel):
|
|
96
|
+
status: RunnerStatus
|
|
97
|
+
busy: bool
|