harmony-client 0.1.0__cp312-cp312-macosx_11_0_arm64.whl
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.
- harmony_client/__init__.py +78 -0
- harmony_client/artifacts/__init__.py +5 -0
- harmony_client/artifacts/custom_artifact.py +46 -0
- harmony_client/artifacts/dataset_artifact.py +268 -0
- harmony_client/artifacts/model_artifact.py +34 -0
- harmony_client/file_storage.py +378 -0
- harmony_client/harmony_client.cpython-312-darwin.so +0 -0
- harmony_client/harmony_client.pyi +1615 -0
- harmony_client/internal/__init__.py +7 -0
- harmony_client/internal/eval_samples_html.py +122 -0
- harmony_client/internal/utils.py +9 -0
- harmony_client/logging_table.py +121 -0
- harmony_client/parameters/__init__.py +295 -0
- harmony_client/parameters/dataset_kinds.py +49 -0
- harmony_client/parameters/model_kinds.py +13 -0
- harmony_client/py.typed +0 -0
- harmony_client/runtime/__init__.py +29 -0
- harmony_client/runtime/context.py +191 -0
- harmony_client/runtime/data.py +76 -0
- harmony_client/runtime/decorators.py +19 -0
- harmony_client/runtime/dto/AdaptiveDataset.py +23 -0
- harmony_client/runtime/dto/AdaptiveGrader.py +68 -0
- harmony_client/runtime/dto/AdaptiveModel.py +19 -0
- harmony_client/runtime/dto/DatasetSampleFormats.py +93 -0
- harmony_client/runtime/dto/__init__.py +2 -0
- harmony_client/runtime/dto/base.py +7 -0
- harmony_client/runtime/model_artifact_save.py +23 -0
- harmony_client/runtime/runner.py +368 -0
- harmony_client/runtime/simple_notifier.py +21 -0
- harmony_client-0.1.0.dist-info/METADATA +38 -0
- harmony_client-0.1.0.dist-info/RECORD +32 -0
- harmony_client-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import importlib.machinery
|
|
3
|
+
import importlib.util
|
|
4
|
+
import inspect
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import site
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
import traceback
|
|
11
|
+
import types
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Optional
|
|
14
|
+
|
|
15
|
+
import tomli
|
|
16
|
+
from loguru import logger
|
|
17
|
+
from pydantic import Field
|
|
18
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
19
|
+
|
|
20
|
+
from harmony_client.runtime.context import RecipeConfig, RecipeContext
|
|
21
|
+
from harmony_client.runtime.data import InputConfig
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RunnerArgs(RecipeConfig, BaseSettings):
|
|
25
|
+
model_config = SettingsConfigDict(env_prefix="ADAPTIVE_", cli_parse_args=True, cli_kebab_case=True)
|
|
26
|
+
|
|
27
|
+
recipe_file: Optional[str] = Field(default=None, description="the python recipe file to execute")
|
|
28
|
+
recipe_file_url: Optional[str] = Field(
|
|
29
|
+
default=None, description="Url of recipe in zip format to download and extract to execute"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def main():
|
|
34
|
+
runner_args = RunnerArgs() # type: ignore
|
|
35
|
+
context = asyncio.run(RecipeContext.from_config(runner_args))
|
|
36
|
+
logger.trace("Loaded config: {}", context.config)
|
|
37
|
+
try:
|
|
38
|
+
if runner_args.recipe_file:
|
|
39
|
+
_load_and_run_recipe(context, runner_args.recipe_file)
|
|
40
|
+
elif runner_args.recipe_file_url:
|
|
41
|
+
recipe_folder = _download_and_extract_recipe(context, runner_args.recipe_file_url)
|
|
42
|
+
_load_and_run_recipe(context, recipe_folder)
|
|
43
|
+
else:
|
|
44
|
+
raise ValueError("recipe_file or recipe_file_url must be provided")
|
|
45
|
+
except Exception as e:
|
|
46
|
+
stack_trace = traceback.format_exc()
|
|
47
|
+
recipe_source = runner_args.recipe_file if runner_args.recipe_file else runner_args.recipe_file_url
|
|
48
|
+
logger.exception(f"Error while running recipe file {recipe_source}", exception=e)
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
context.job.report_error(stack_trace)
|
|
52
|
+
except Exception as e2:
|
|
53
|
+
logger.error(f"Error while reporting error: {e2}")
|
|
54
|
+
logger.error(f"Stack trace: {traceback.format_exc()}")
|
|
55
|
+
|
|
56
|
+
sys.exit(1)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _load_and_run_recipe(context: RecipeContext, recipe_path: str):
|
|
60
|
+
entry = Path(recipe_path).resolve()
|
|
61
|
+
|
|
62
|
+
_install_recipe_dependencies(entry)
|
|
63
|
+
# Reload site to pick up .pth files created by editable install
|
|
64
|
+
importlib.reload(site)
|
|
65
|
+
|
|
66
|
+
if entry.is_dir():
|
|
67
|
+
entry_file = entry / "main.py"
|
|
68
|
+
if not entry_file.exists():
|
|
69
|
+
raise FileNotFoundError(f"main.py not found in {entry}")
|
|
70
|
+
pkg_dir = entry
|
|
71
|
+
module_name = "main"
|
|
72
|
+
else:
|
|
73
|
+
if entry.suffix != ".py":
|
|
74
|
+
raise ValueError(f"Expected a Python file or directory, got: {entry}")
|
|
75
|
+
entry_file = entry
|
|
76
|
+
pkg_dir = entry.parent
|
|
77
|
+
module_name = entry.stem
|
|
78
|
+
|
|
79
|
+
# Create a stable synthetic package name tied to the directory
|
|
80
|
+
synthetic_pkg = f"_adhoc_recipe_{abs(hash(str(pkg_dir))) & 0xFFFFFFFF:x}"
|
|
81
|
+
|
|
82
|
+
# Clear any previous loads of this synthetic package in the current process
|
|
83
|
+
for key in list(sys.modules.keys()):
|
|
84
|
+
if key == synthetic_pkg or key.startswith(synthetic_pkg + "."):
|
|
85
|
+
del sys.modules[key]
|
|
86
|
+
|
|
87
|
+
# Build a synthetic namespace package pointing at pkg_dir
|
|
88
|
+
pkg_mod = types.ModuleType(synthetic_pkg)
|
|
89
|
+
pkg_mod.__path__ = [str(pkg_dir)] # allow submodule search in this directory
|
|
90
|
+
pkg_mod.__package__ = synthetic_pkg
|
|
91
|
+
spec_pkg = importlib.machinery.ModuleSpec(synthetic_pkg, loader=None, is_package=True)
|
|
92
|
+
spec_pkg.submodule_search_locations = [str(pkg_dir)]
|
|
93
|
+
pkg_mod.__spec__ = spec_pkg
|
|
94
|
+
sys.modules[synthetic_pkg] = pkg_mod
|
|
95
|
+
|
|
96
|
+
# Load the entry file as a submodule of the synthetic package
|
|
97
|
+
fullname = f"{synthetic_pkg}.{module_name}"
|
|
98
|
+
spec = importlib.util.spec_from_file_location(fullname, str(entry_file))
|
|
99
|
+
if spec is None or spec.loader is None:
|
|
100
|
+
raise ImportError(f"Cannot load spec for {entry_file}")
|
|
101
|
+
module = importlib.util.module_from_spec(spec)
|
|
102
|
+
sys.modules[fullname] = module
|
|
103
|
+
spec.loader.exec_module(module)
|
|
104
|
+
|
|
105
|
+
# get recipe_main function
|
|
106
|
+
functions = inspect.getmembers(module, inspect.isfunction)
|
|
107
|
+
recipe_main_functions = [(name, func) for name, func in functions if getattr(func, "is_recipe_main", False)]
|
|
108
|
+
|
|
109
|
+
if len(recipe_main_functions) == 0:
|
|
110
|
+
logger.warning("No function annotated with @recipe_main")
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
if len(recipe_main_functions) != 1:
|
|
114
|
+
names = [name for (name, _) in recipe_main_functions]
|
|
115
|
+
raise ValueError(f"You must have only one function annotated with @recipe_main. Found {names}")
|
|
116
|
+
|
|
117
|
+
(func_name, func) = recipe_main_functions[0]
|
|
118
|
+
logger.trace("Getting recipe function parameters")
|
|
119
|
+
args = _get_params(func, context)
|
|
120
|
+
|
|
121
|
+
logger.info(f"Executing recipe function {func_name}")
|
|
122
|
+
if inspect.iscoroutinefunction(func):
|
|
123
|
+
asyncio.run(func(*args))
|
|
124
|
+
else:
|
|
125
|
+
func(*args)
|
|
126
|
+
logger.info(f"Recipe {func_name} completed successfully.")
|
|
127
|
+
# Give time for any pending notifications (like register_artifact) to be sent
|
|
128
|
+
import time
|
|
129
|
+
|
|
130
|
+
time.sleep(0.1)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _download_and_extract_recipe(context: RecipeContext, file_url: str) -> str:
|
|
134
|
+
import tempfile
|
|
135
|
+
import zipfile
|
|
136
|
+
|
|
137
|
+
assert file_url.endswith(".zip"), "Recipe url must point to a zip file"
|
|
138
|
+
|
|
139
|
+
# Download the zip file to a temporary location
|
|
140
|
+
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as temp_zip:
|
|
141
|
+
temp_zip_path = temp_zip.name
|
|
142
|
+
context.file_storage.download_locally(file_url, temp_zip_path, use_raw_path=True)
|
|
143
|
+
|
|
144
|
+
# Extract to a temporary directory
|
|
145
|
+
temp_dir = tempfile.mkdtemp(prefix="user_recipe")
|
|
146
|
+
with zipfile.ZipFile(temp_zip_path, "r") as zip_ref:
|
|
147
|
+
zip_ref.extractall(temp_dir)
|
|
148
|
+
|
|
149
|
+
# Clean up the temp zip file
|
|
150
|
+
os.unlink(temp_zip_path)
|
|
151
|
+
|
|
152
|
+
recipe_files = [f for f in os.listdir(temp_dir) if f.endswith(".py")]
|
|
153
|
+
if not recipe_files:
|
|
154
|
+
raise FileNotFoundError("No Python recipe file found in the extracted zip")
|
|
155
|
+
main_files = [f for f in recipe_files if f == "main.py"]
|
|
156
|
+
if len(main_files) == 0:
|
|
157
|
+
raise RuntimeError("Recipe zip file must contain a main.py file")
|
|
158
|
+
|
|
159
|
+
return temp_dir
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _parse_script_metadata(file_path: Path) -> Optional[list[str]]:
|
|
163
|
+
"""Parse metadata block from a Python script to extract dependencies.
|
|
164
|
+
cf. [python doc](https://packaging.python.org/en/latest/specifications/inline-script-metadata)
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
file_path: Path to the Python file to parse
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
List of dependencies if found, None otherwise
|
|
171
|
+
"""
|
|
172
|
+
try:
|
|
173
|
+
content = file_path.read_text()
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.warning(f"Failed to read file {file_path} for metadata parsing: {e}")
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
# Regex pattern to match metadata blocks
|
|
179
|
+
metadata_pattern = r"(?m)^# /// (?P<type>[a-zA-Z0-9-]+)$\s(?P<content>(^#(| .*)$\s)+?)^# ///$"
|
|
180
|
+
matches = list(re.finditer(metadata_pattern, content))
|
|
181
|
+
|
|
182
|
+
for match in matches:
|
|
183
|
+
metadata_type = match.group("type")
|
|
184
|
+
if metadata_type != "adaptive":
|
|
185
|
+
continue
|
|
186
|
+
metadata_content = match.group("content")
|
|
187
|
+
# Clean up metadata content by removing leading '#' and whitespaces from each line
|
|
188
|
+
cleaned_lines = []
|
|
189
|
+
for line in metadata_content.split("\n"):
|
|
190
|
+
line = line.lstrip("#").strip()
|
|
191
|
+
if line:
|
|
192
|
+
cleaned_lines.append(line)
|
|
193
|
+
metadata_content = "\n".join(cleaned_lines)
|
|
194
|
+
|
|
195
|
+
# Parse metadata content as TOML
|
|
196
|
+
try:
|
|
197
|
+
metadata_dict = tomli.loads(metadata_content)
|
|
198
|
+
except Exception as e:
|
|
199
|
+
logger.warning(f"Failed to parse metadata as TOML: {e}")
|
|
200
|
+
metadata_dict = None
|
|
201
|
+
if metadata_dict and "dependencies" in metadata_dict:
|
|
202
|
+
deps = metadata_dict["dependencies"]
|
|
203
|
+
if isinstance(deps, list):
|
|
204
|
+
return deps
|
|
205
|
+
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
INSTALL_TIMEOUT_SECS = int(os.getenv("ADAPTIVE_INSTALL_TIMEOUT_SECS", "300")) # Default 5 minutes
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _install_recipe_dependencies(entry: Path):
|
|
213
|
+
"""Install Python dependencies from pyproject.toml, requirements.txt, requirements.in, or script metadata.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
entry: Path to a file or directory. If it's a directory, looks for dependency files.
|
|
217
|
+
If it's a file, parses metadata block for dependencies.
|
|
218
|
+
"""
|
|
219
|
+
# Check for pyproject.toml first, then requirements.txt/requirements.in
|
|
220
|
+
pyproject_file = entry / "pyproject.toml"
|
|
221
|
+
requirements_file = entry / "requirements.txt"
|
|
222
|
+
requirements_in_file = entry / "requirements.in"
|
|
223
|
+
|
|
224
|
+
install_kwargs = {
|
|
225
|
+
"check": True,
|
|
226
|
+
"timeout": INSTALL_TIMEOUT_SECS,
|
|
227
|
+
"capture_output": True,
|
|
228
|
+
"text": True,
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if pyproject_file.exists():
|
|
232
|
+
logger.info(
|
|
233
|
+
f"Found pyproject.toml in {entry}, installing dependencies with uv pip (timeout: {INSTALL_TIMEOUT_SECS}s)"
|
|
234
|
+
)
|
|
235
|
+
try:
|
|
236
|
+
result = subprocess.run(
|
|
237
|
+
["uv", "pip", "install", "-e", str(entry)],
|
|
238
|
+
**install_kwargs,
|
|
239
|
+
)
|
|
240
|
+
if result.stdout:
|
|
241
|
+
logger.debug(f"Install output: {result.stdout}")
|
|
242
|
+
logger.info("Successfully installed dependencies from pyproject.toml")
|
|
243
|
+
except subprocess.TimeoutExpired as e:
|
|
244
|
+
logger.error(f"Timeout ({INSTALL_TIMEOUT_SECS}s) installing dependencies from pyproject.toml")
|
|
245
|
+
if e.stdout:
|
|
246
|
+
logger.debug(f"Partial stdout: {e.stdout}")
|
|
247
|
+
if e.stderr:
|
|
248
|
+
logger.debug(f"Partial stderr: {e.stderr}")
|
|
249
|
+
raise
|
|
250
|
+
except subprocess.CalledProcessError as e:
|
|
251
|
+
logger.error(f"Failed to install dependencies from pyproject.toml: {e.stderr}")
|
|
252
|
+
if e.stdout:
|
|
253
|
+
logger.debug(f"stdout: {e.stdout}")
|
|
254
|
+
raise
|
|
255
|
+
elif requirements_file.exists():
|
|
256
|
+
logger.info(
|
|
257
|
+
f"Found requirements.txt in {entry}, installing dependencies with uv pip (timeout: {INSTALL_TIMEOUT_SECS}s)"
|
|
258
|
+
)
|
|
259
|
+
try:
|
|
260
|
+
result = subprocess.run(
|
|
261
|
+
["uv", "pip", "install", "--verbose", "-r", str(requirements_file)], **install_kwargs
|
|
262
|
+
)
|
|
263
|
+
if result.stdout:
|
|
264
|
+
logger.debug(f"Install output: {result.stdout}")
|
|
265
|
+
logger.info("Successfully installed dependencies from requirements.txt")
|
|
266
|
+
except subprocess.TimeoutExpired as e:
|
|
267
|
+
logger.error(f"Timeout ({INSTALL_TIMEOUT_SECS}s) installing dependencies from requirements.txt")
|
|
268
|
+
if e.stdout:
|
|
269
|
+
logger.debug(f"Partial stdout: {e.stdout}")
|
|
270
|
+
if e.stderr:
|
|
271
|
+
logger.debug(f"Partial stderr: {e.stderr}")
|
|
272
|
+
raise
|
|
273
|
+
except subprocess.CalledProcessError as e:
|
|
274
|
+
logger.error(f"Failed to install dependencies from requirements.txt: {e.stderr}")
|
|
275
|
+
if e.stdout:
|
|
276
|
+
logger.debug(f"stdout: {e.stdout}")
|
|
277
|
+
raise
|
|
278
|
+
elif requirements_in_file.exists():
|
|
279
|
+
logger.info(
|
|
280
|
+
f"Found requirements.in in {entry}, compiling to requirements.txt with uv pip (timeout: {INSTALL_TIMEOUT_SECS}s)"
|
|
281
|
+
)
|
|
282
|
+
try:
|
|
283
|
+
# Compile requirements.in to requirements.txt
|
|
284
|
+
result = subprocess.run(
|
|
285
|
+
["uv", "pip", "compile", str(requirements_in_file), "-o", str(requirements_file)], **install_kwargs
|
|
286
|
+
)
|
|
287
|
+
if result.stdout:
|
|
288
|
+
logger.debug(f"Compile output: {result.stdout}")
|
|
289
|
+
logger.debug("Successfully compiled requirements.in to requirements.txt")
|
|
290
|
+
result = subprocess.run(
|
|
291
|
+
["uv", "pip", "install", "-r", str(requirements_file)],
|
|
292
|
+
**install_kwargs,
|
|
293
|
+
)
|
|
294
|
+
if result.stdout:
|
|
295
|
+
logger.debug(f"Install output: {result.stdout}")
|
|
296
|
+
logger.info("Successfully installed dependencies from requirements.in")
|
|
297
|
+
except subprocess.TimeoutExpired as e:
|
|
298
|
+
logger.error(f"Timeout ({INSTALL_TIMEOUT_SECS}s) compiling or installing dependencies from requirements.in")
|
|
299
|
+
if e.stdout:
|
|
300
|
+
logger.debug(f"Partial stdout: {e.stdout}")
|
|
301
|
+
if e.stderr:
|
|
302
|
+
logger.debug(f"Partial stderr: {e.stderr}")
|
|
303
|
+
raise
|
|
304
|
+
except subprocess.CalledProcessError as e:
|
|
305
|
+
logger.error(f"Failed to compile or install dependencies from requirements.in: {e.stderr}")
|
|
306
|
+
if e.stdout:
|
|
307
|
+
logger.debug(f"stdout: {e.stdout}")
|
|
308
|
+
raise
|
|
309
|
+
else:
|
|
310
|
+
# Check main file for script metadata
|
|
311
|
+
main_file = entry if entry.is_file() else entry / "main.py"
|
|
312
|
+
if main_file.is_file():
|
|
313
|
+
if main_file.suffix != ".py":
|
|
314
|
+
logger.debug(f"{main_file} is not a Python file, skipping dependency installation")
|
|
315
|
+
return
|
|
316
|
+
|
|
317
|
+
dependencies = _parse_script_metadata(main_file)
|
|
318
|
+
if dependencies:
|
|
319
|
+
logger.info(
|
|
320
|
+
f"Found {len(dependencies)} dependencies in script metadata: {dependencies}, installing them (timeout: {INSTALL_TIMEOUT_SECS}s)..."
|
|
321
|
+
)
|
|
322
|
+
try:
|
|
323
|
+
result = subprocess.run(["uv", "pip", "install"] + dependencies, **install_kwargs)
|
|
324
|
+
if result.stdout:
|
|
325
|
+
logger.debug(f"Install output: {result.stdout}")
|
|
326
|
+
logger.info("Successfully installed dependencies from script metadata")
|
|
327
|
+
except subprocess.TimeoutExpired as e:
|
|
328
|
+
logger.error(f"Timeout ({INSTALL_TIMEOUT_SECS}s) installing dependencies from script metadata")
|
|
329
|
+
if e.stdout:
|
|
330
|
+
logger.debug(f"Partial stdout: {e.stdout}")
|
|
331
|
+
if e.stderr:
|
|
332
|
+
logger.debug(f"Partial stderr: {e.stderr}")
|
|
333
|
+
raise
|
|
334
|
+
except subprocess.CalledProcessError as e:
|
|
335
|
+
logger.error(f"Failed to install dependencies from script metadata: {e.stderr}")
|
|
336
|
+
if e.stdout:
|
|
337
|
+
logger.debug(f"stdout: {e.stdout}")
|
|
338
|
+
raise
|
|
339
|
+
else:
|
|
340
|
+
logger.debug(f"No dependencies found in script metadata for {entry}")
|
|
341
|
+
return
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _get_params(func, context: RecipeContext) -> list[Any]:
|
|
345
|
+
args: list[Any] = []
|
|
346
|
+
sig = inspect.signature(func)
|
|
347
|
+
assert len(sig.parameters.items()) <= 2, "Support only functions with 2 parameters or less"
|
|
348
|
+
|
|
349
|
+
for _, param in sig.parameters.items():
|
|
350
|
+
# Ensure param.annotation is a type before using issubclass
|
|
351
|
+
if isinstance(param.annotation, type):
|
|
352
|
+
if issubclass(param.annotation, RecipeContext):
|
|
353
|
+
args.append(context)
|
|
354
|
+
elif issubclass(param.annotation, InputConfig):
|
|
355
|
+
if context.config.user_input_file:
|
|
356
|
+
user_input = param.annotation.load_from_file(context.config.user_input_file)
|
|
357
|
+
else:
|
|
358
|
+
user_input = param.annotation()
|
|
359
|
+
logger.trace("Loaded user input: {}", user_input)
|
|
360
|
+
args.append(user_input)
|
|
361
|
+
else:
|
|
362
|
+
raise TypeError(f"Parameter '{param.name}' must have a type annotation.")
|
|
363
|
+
|
|
364
|
+
return args
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
if __name__ == "__main__":
|
|
368
|
+
main()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from harmony_client import StageNotifier
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SimpleProgressNotifier:
|
|
5
|
+
def __init__(self, job_notifier: StageNotifier, monitoring_link=None):
|
|
6
|
+
self.job_notifier = job_notifier
|
|
7
|
+
self.monitoring_link = monitoring_link
|
|
8
|
+
|
|
9
|
+
def __enter__(self):
|
|
10
|
+
self.job_notifier.report_progress(1, 0, self.monitoring_link)
|
|
11
|
+
return self
|
|
12
|
+
|
|
13
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
14
|
+
self.job_notifier.report_progress(1, 1, self.monitoring_link)
|
|
15
|
+
|
|
16
|
+
async def __aenter__(self):
|
|
17
|
+
self.job_notifier.report_progress(1, 0, self.monitoring_link)
|
|
18
|
+
return self
|
|
19
|
+
|
|
20
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
21
|
+
self.job_notifier.report_progress(1, 1, self.monitoring_link)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: harmony-client
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
7
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
8
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
9
|
+
Requires-Dist: rich>=13.7.0
|
|
10
|
+
Requires-Dist: datasets>=2.14.0
|
|
11
|
+
Requires-Dist: hf-xet>=1.1.2
|
|
12
|
+
Requires-Dist: loguru>=0.7.2
|
|
13
|
+
Requires-Dist: pydantic-settings>=2.9.1
|
|
14
|
+
Requires-Dist: pydantic>=2.10.5
|
|
15
|
+
Requires-Dist: pybars3>=0.9.7
|
|
16
|
+
Requires-Dist: pysbd>=0.3.4
|
|
17
|
+
Requires-Dist: websockets>=15.0.1
|
|
18
|
+
Requires-Dist: setproctitle>=1.3.3
|
|
19
|
+
Requires-Dist: pydantic-xml>=2.16.0
|
|
20
|
+
Requires-Dist: openai>=1.42.0
|
|
21
|
+
Requires-Dist: pillow>=11.3.0
|
|
22
|
+
Requires-Dist: boto3>=1.40
|
|
23
|
+
Requires-Dist: tomli>=2
|
|
24
|
+
Requires-Dist: aiofiles>=25.1.0
|
|
25
|
+
Requires-Dist: tensorboardx>=2.6.2.2 ; extra == 'all-monitoring'
|
|
26
|
+
Requires-Dist: mlflow>=3.4.0 ; extra == 'all-monitoring'
|
|
27
|
+
Requires-Dist: wandb>=0.19.11 ; extra == 'all-monitoring'
|
|
28
|
+
Requires-Dist: mlflow>=3.4.0 ; extra == 'mlflow'
|
|
29
|
+
Requires-Dist: mlflow-skinny>=3.4.0 ; extra == 'mlflow-skinny'
|
|
30
|
+
Requires-Dist: tensorboardx>=2.6.2.2 ; extra == 'tensorboard'
|
|
31
|
+
Requires-Dist: wandb>=0.19.11 ; extra == 'wandb'
|
|
32
|
+
Provides-Extra: all_monitoring
|
|
33
|
+
Provides-Extra: mlflow
|
|
34
|
+
Provides-Extra: mlflow_skinny
|
|
35
|
+
Provides-Extra: tensorboard
|
|
36
|
+
Provides-Extra: wandb
|
|
37
|
+
Summary: Python client for Adaptive Harmony, with Rust-powered RPC communication
|
|
38
|
+
Requires-Python: >=3.12
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
harmony_client/__init__.py,sha256=i536amUmkWy5jDbyDyZmRg4NZDJT5vUR2SnNSM8-5-c,1684
|
|
2
|
+
harmony_client/file_storage.py,sha256=UOlXohu7eH4J1WV-JuOtJeZxGSBf2ijS8gfVFDwENK4,13400
|
|
3
|
+
harmony_client/logging_table.py,sha256=D32Jk6QXsxUwmpwXHaHdVAZpmcbiFEBbY8YEKpeTHwo,4165
|
|
4
|
+
harmony_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
harmony_client/harmony_client.pyi,sha256=cP1F73XM5OCOYBB8W7Bhlzooh5ER1Yyuv7Pq6mzhQ88,51978
|
|
6
|
+
harmony_client/harmony_client.cpython-312-darwin.so,sha256=9Siy7JTR8fyTMdMRU0cF2BRvshG83thbrJnCgc2LlfA,18757504
|
|
7
|
+
harmony_client/artifacts/__init__.py,sha256=vPUCeEBDIkHMlDAZXwcvruaCG1onOlvwJT_2wdWrdkQ,310
|
|
8
|
+
harmony_client/artifacts/dataset_artifact.py,sha256=c9NZgQ8vuW0pI96c743eMtg48bTBKBBLNuXl-e3Q77c,9573
|
|
9
|
+
harmony_client/artifacts/model_artifact.py,sha256=Ks4o80oaRLUqxExXgWmYaKzS_f7medBc1Nan7EwqaoI,740
|
|
10
|
+
harmony_client/artifacts/custom_artifact.py,sha256=E8-PH6UMQrpwXxnScXUviey_7U_Y9NRatmY9CaUA3iE,1303
|
|
11
|
+
harmony_client/runtime/model_artifact_save.py,sha256=bi9Wp2KwbW9jTqM9oHaFILeIWSc81vihirSCo_Yxdjw,741
|
|
12
|
+
harmony_client/runtime/runner.py,sha256=6XEicGGhlozLZMc4Wk2uyfxkegM_bSWxV-jl-8Bu8vI,15090
|
|
13
|
+
harmony_client/runtime/simple_notifier.py,sha256=kzExKB3g28w5-4-md3dAae4nPIr3Q9fqgg1IgMwXS18,726
|
|
14
|
+
harmony_client/runtime/__init__.py,sha256=r3a1FVwuQh_KzV8bF88OpuX1CO7uOJ24pMO9YYmFK4c,638
|
|
15
|
+
harmony_client/runtime/context.py,sha256=AdIPAJwdfVXztENUW1B8sz720HM9QMGQbwOyhYrDj2o,8389
|
|
16
|
+
harmony_client/runtime/data.py,sha256=WxYS-hNKm9T0-SAyI1rWQTGFekg2Tu_5pNprRomr4xo,2749
|
|
17
|
+
harmony_client/runtime/decorators.py,sha256=QA6cHb1ChuT6GVVDN58ReNG2B4Kj19BZzyh9nSSMFcU,436
|
|
18
|
+
harmony_client/runtime/dto/AdaptiveDataset.py,sha256=DgVcMY7uWA2pvGAsvxlpwe85EqGyfkTK9Tw1SkIRk04,539
|
|
19
|
+
harmony_client/runtime/dto/DatasetSampleFormats.py,sha256=eUsEgCwyKVUiem_POf6q8aYTdPM4MN6GrEZXUJrVIYE,2147
|
|
20
|
+
harmony_client/runtime/dto/AdaptiveGrader.py,sha256=qOJisXiuXJov2cUThLzxo3FEnSkV6DpRtcjd1p2sJHI,1497
|
|
21
|
+
harmony_client/runtime/dto/__init__.py,sha256=ym2-ZRfDfDnaX37gxOMbRCkd8Wxk0RQC16aTyyGUVGQ,60
|
|
22
|
+
harmony_client/runtime/dto/AdaptiveModel.py,sha256=mcN4OS-2FphCnBOjUk36f-i6a6nltly0AAa8CN0DmpA,542
|
|
23
|
+
harmony_client/runtime/dto/base.py,sha256=n61FFqnZVqe0rVdc7giIYnaiTPX7iXt5rBQ6BQlyfuY,126
|
|
24
|
+
harmony_client/internal/__init__.py,sha256=IsboJmHPL7BQef7fI4zK6GqFV0E3jxlxlTPgi6RC-3k,232
|
|
25
|
+
harmony_client/internal/eval_samples_html.py,sha256=x6b6A6KW_64dnlF5gefsNJwzqIprGNWmUmQOOabpGBM,4309
|
|
26
|
+
harmony_client/internal/utils.py,sha256=eIm5kR9LX63c6D9q-EAPw_uNYkRKUcQaBEmu8eOcMjc,316
|
|
27
|
+
harmony_client/parameters/__init__.py,sha256=4s3O5pMiE3Qc_5Zg5ftpMDHNlm34bxRJ-tPR_4NoM5Y,11898
|
|
28
|
+
harmony_client/parameters/dataset_kinds.py,sha256=4lEEsbfKzrBd6OYpm3Sz7VP4-AJGzA3uxAkzmbeBTRE,1144
|
|
29
|
+
harmony_client/parameters/model_kinds.py,sha256=_EiCdHVyIhvPfr7bil6QxrxrEX-71oOttCLP_g7sdI4,255
|
|
30
|
+
harmony_client-0.1.0.dist-info/RECORD,,
|
|
31
|
+
harmony_client-0.1.0.dist-info/WHEEL,sha256=qr8B0oB3ZR0cwIaW0mxUADH9b4sNRMHcgJlpYNTDAPw,105
|
|
32
|
+
harmony_client-0.1.0.dist-info/METADATA,sha256=NlzjKVbWRhqHhNsy4dbYZLQmURlvGq0Da2DKO4LHxX0,1480
|