adaptive-harmony 0.1.23__py3-none-any.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.
- adaptive_harmony/__init__.py +162 -0
- adaptive_harmony/common/__init__.py +40 -0
- adaptive_harmony/common/callbacks.py +219 -0
- adaptive_harmony/common/checkpointing.py +163 -0
- adaptive_harmony/common/dpo.py +92 -0
- adaptive_harmony/common/env_grpo.py +361 -0
- adaptive_harmony/common/grpo.py +260 -0
- adaptive_harmony/common/gspo.py +70 -0
- adaptive_harmony/common/ppo.py +303 -0
- adaptive_harmony/common/rm.py +79 -0
- adaptive_harmony/common/sft.py +121 -0
- adaptive_harmony/core/__init__.py +0 -0
- adaptive_harmony/core/dataset.py +72 -0
- adaptive_harmony/core/display.py +93 -0
- adaptive_harmony/core/image_utils.py +110 -0
- adaptive_harmony/core/reasoning.py +12 -0
- adaptive_harmony/core/reward_client/__init__.py +19 -0
- adaptive_harmony/core/reward_client/client.py +160 -0
- adaptive_harmony/core/reward_client/reward_types.py +49 -0
- adaptive_harmony/core/reward_client/websocket_utils.py +18 -0
- adaptive_harmony/core/rich_counter.py +351 -0
- adaptive_harmony/core/rl_utils.py +38 -0
- adaptive_harmony/core/schedulers.py +38 -0
- adaptive_harmony/core/structured_output.py +385 -0
- adaptive_harmony/core/utils.py +365 -0
- adaptive_harmony/environment/__init__.py +8 -0
- adaptive_harmony/environment/environment.py +121 -0
- adaptive_harmony/evaluation/__init__.py +1 -0
- adaptive_harmony/evaluation/evaluation_artifact.py +67 -0
- adaptive_harmony/graders/__init__.py +20 -0
- adaptive_harmony/graders/answer_relevancy_judge/__init__.py +3 -0
- adaptive_harmony/graders/answer_relevancy_judge/answer_relevancy_judge.py +102 -0
- adaptive_harmony/graders/answer_relevancy_judge/prompts.py +58 -0
- adaptive_harmony/graders/base_grader.py +265 -0
- adaptive_harmony/graders/binary_judge/__init__.py +8 -0
- adaptive_harmony/graders/binary_judge/binary_judge.py +202 -0
- adaptive_harmony/graders/binary_judge/prompts.py +125 -0
- adaptive_harmony/graders/combined_grader.py +118 -0
- adaptive_harmony/graders/context_relevancy_judge/__init__.py +3 -0
- adaptive_harmony/graders/context_relevancy_judge/context_relevancy_judge.py +128 -0
- adaptive_harmony/graders/context_relevancy_judge/prompts.py +84 -0
- adaptive_harmony/graders/exceptions.py +9 -0
- adaptive_harmony/graders/faithfulness_judge/__init__.py +3 -0
- adaptive_harmony/graders/faithfulness_judge/faithfulness_judge.py +159 -0
- adaptive_harmony/graders/faithfulness_judge/prompts.py +22 -0
- adaptive_harmony/graders/range_judge/__init__.py +7 -0
- adaptive_harmony/graders/range_judge/prompts.py +232 -0
- adaptive_harmony/graders/range_judge/range_judge.py +188 -0
- adaptive_harmony/graders/range_judge/types.py +12 -0
- adaptive_harmony/graders/reward_server_grader.py +36 -0
- adaptive_harmony/graders/templated_prompt_judge.py +237 -0
- adaptive_harmony/graders/utils.py +79 -0
- adaptive_harmony/logging_table.py +1 -0
- adaptive_harmony/metric_logger.py +452 -0
- adaptive_harmony/parameters/__init__.py +2 -0
- adaptive_harmony/py.typed +0 -0
- adaptive_harmony/runtime/__init__.py +2 -0
- adaptive_harmony/runtime/context.py +2 -0
- adaptive_harmony/runtime/data.py +2 -0
- adaptive_harmony/runtime/decorators.py +2 -0
- adaptive_harmony/runtime/model_artifact_save.py +2 -0
- adaptive_harmony/runtime/runner.py +27 -0
- adaptive_harmony/runtime/simple_notifier.py +2 -0
- adaptive_harmony-0.1.23.dist-info/METADATA +37 -0
- adaptive_harmony-0.1.23.dist-info/RECORD +67 -0
- adaptive_harmony-0.1.23.dist-info/WHEEL +5 -0
- adaptive_harmony-0.1.23.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import html
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import tempfile
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from collections import defaultdict
|
|
7
|
+
from json.decoder import JSONDecodeError
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Mapping
|
|
10
|
+
|
|
11
|
+
from loguru import logger as loguru
|
|
12
|
+
from rich.pretty import pprint
|
|
13
|
+
|
|
14
|
+
from adaptive_harmony.logging_table import Table
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Logger(ABC):
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def __call__(self, metrics: Mapping[str, str | int | float | Table]): ...
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def log_config(self, config: dict[str, Any]): ...
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def is_available() -> bool: ...
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def training_monitoring_link(self) -> str | None:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
def close(self):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MLFlowLogger(Logger):
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
project_name: str,
|
|
40
|
+
run_name: str,
|
|
41
|
+
tracking_uri: str,
|
|
42
|
+
monitoring_link: str | None = None,
|
|
43
|
+
experiment_tags: dict[str, str] | None = None,
|
|
44
|
+
run_tags: dict[str, str] | None = None,
|
|
45
|
+
min_table_logging_step_interval: int = 5,
|
|
46
|
+
):
|
|
47
|
+
try:
|
|
48
|
+
import mlflow
|
|
49
|
+
except ImportError:
|
|
50
|
+
raise ImportError(
|
|
51
|
+
"MLFlow is not available."
|
|
52
|
+
"To install adaptive-harmony with MLFlow support, install `adaptive-harmony[mlflow]`"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self._mlflow = mlflow
|
|
56
|
+
mlflow.set_tracking_uri(tracking_uri)
|
|
57
|
+
_ = mlflow.set_experiment(project_name)
|
|
58
|
+
if experiment_tags:
|
|
59
|
+
_ = mlflow.set_experiment_tags(experiment_tags)
|
|
60
|
+
self.run = mlflow.start_run(run_name=run_name, log_system_metrics=None, tags=run_tags)
|
|
61
|
+
self.monitoring_link = monitoring_link
|
|
62
|
+
self.step = 0
|
|
63
|
+
self.last_logged_tables_step = 0
|
|
64
|
+
self.min_table_logging_step_interval = min_table_logging_step_interval
|
|
65
|
+
self._log_tables_as_artifacts: bool = False
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def is_available() -> bool:
|
|
69
|
+
try:
|
|
70
|
+
import mlflow # noqa: F401
|
|
71
|
+
|
|
72
|
+
return True
|
|
73
|
+
except ImportError:
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
def __call__(self, metrics: Mapping[str, str | int | float | Table]):
|
|
77
|
+
metric_logs: dict[str, float] = {}
|
|
78
|
+
str_logs: dict[str, str] = {}
|
|
79
|
+
tables: dict[str, Table] = {}
|
|
80
|
+
for entry, data in metrics.items():
|
|
81
|
+
if isinstance(data, int) or isinstance(data, float):
|
|
82
|
+
metric_logs.update({entry: float(data)})
|
|
83
|
+
elif isinstance(data, Table):
|
|
84
|
+
tables.update({entry: data})
|
|
85
|
+
elif isinstance(data, str):
|
|
86
|
+
str_logs.update({entry: data})
|
|
87
|
+
else:
|
|
88
|
+
print(f"MLFlow logger does not support type: {type(data)}")
|
|
89
|
+
self._mlflow.log_metrics(metric_logs, step=self.step, synchronous=True)
|
|
90
|
+
self._mlflow.log_params(str_logs)
|
|
91
|
+
|
|
92
|
+
if self.step == 0 or (self.step - self.last_logged_tables_step > self.min_table_logging_step_interval):
|
|
93
|
+
for table_entry, table in tables.items():
|
|
94
|
+
headers, rows = table.export()
|
|
95
|
+
# Transpose rows to columns for MLflow
|
|
96
|
+
if rows:
|
|
97
|
+
columns = list(zip(*rows))
|
|
98
|
+
table_dict = {header: list(column) for header, column in zip(headers, columns)}
|
|
99
|
+
table_file_name = f"{table_entry}/step_{self.step}.json"
|
|
100
|
+
if not self._log_tables_as_artifacts:
|
|
101
|
+
try:
|
|
102
|
+
self._mlflow.log_table(data=table_dict, artifact_file=table_file_name)
|
|
103
|
+
except JSONDecodeError:
|
|
104
|
+
loguru.warning(
|
|
105
|
+
f"Reached limit of # tables that can be logged to a single run at step {self.step}"
|
|
106
|
+
"logging tables as artifacts from now on"
|
|
107
|
+
)
|
|
108
|
+
self._log_tables_as_artifacts = True
|
|
109
|
+
else:
|
|
110
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
111
|
+
path = Path(tmp_dir, table_file_name.replace("/", "_"))
|
|
112
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
113
|
+
path.write_text(json.dumps(table_dict))
|
|
114
|
+
self._mlflow.log_artifact(local_path=str(path))
|
|
115
|
+
self.last_logged_tables_step = self.step
|
|
116
|
+
|
|
117
|
+
self.step += 1
|
|
118
|
+
|
|
119
|
+
def log_config(self, config: dict[str, Any]):
|
|
120
|
+
for key, value in config.items():
|
|
121
|
+
try:
|
|
122
|
+
# MLflow handles params differently - convert to string for complex types
|
|
123
|
+
if isinstance(value, (dict, list)):
|
|
124
|
+
self._mlflow.log_param(key, json.dumps(value))
|
|
125
|
+
else:
|
|
126
|
+
self._mlflow.log_param(key, value)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
print(
|
|
129
|
+
f"Warning: Argument '{key}' with value '{str(value)[:100]}...' "
|
|
130
|
+
f"(type: {type(value).__name__}) could not be logged to mlflow. Error: {e}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def training_monitoring_link(self) -> str:
|
|
135
|
+
return self.monitoring_link or self._mlflow.get_tracking_uri()
|
|
136
|
+
|
|
137
|
+
def close(self):
|
|
138
|
+
self._mlflow.end_run()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class WandbLogger(Logger):
|
|
142
|
+
def __init__(
|
|
143
|
+
self,
|
|
144
|
+
project_name: str,
|
|
145
|
+
run_name: str,
|
|
146
|
+
entity: str | None = None,
|
|
147
|
+
):
|
|
148
|
+
try:
|
|
149
|
+
import wandb
|
|
150
|
+
except ImportError:
|
|
151
|
+
raise ImportError(
|
|
152
|
+
"WandB is not available."
|
|
153
|
+
"To install adaptive-harmony with WandB support, install `adaptive-harmony[wandb]`"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
self._wandb = wandb
|
|
157
|
+
self.run = wandb.init(project=project_name, name=run_name, entity=entity)
|
|
158
|
+
self.step = 0
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def is_available() -> bool:
|
|
162
|
+
try:
|
|
163
|
+
import wandb # noqa: F401
|
|
164
|
+
|
|
165
|
+
return True
|
|
166
|
+
except ImportError:
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
def __call__(self, metrics: Mapping[str, str | int | float | Table]):
|
|
170
|
+
logs = {k: self._process_metric(v) for k, v in metrics.items()}
|
|
171
|
+
str_logs = {k: str(v) for k, v in logs.items() if isinstance(v, str)}
|
|
172
|
+
metric_logs = {k: v for k, v in logs.items() if not isinstance(v, str)}
|
|
173
|
+
self._wandb.log(metric_logs, step=self.step, commit=True)
|
|
174
|
+
self.run.config.update(str_logs)
|
|
175
|
+
self.step += 1
|
|
176
|
+
|
|
177
|
+
def log_config(self, config: dict[str, Any]):
|
|
178
|
+
for key, value in config.items():
|
|
179
|
+
try:
|
|
180
|
+
self.run.config[key] = value
|
|
181
|
+
except Exception as e:
|
|
182
|
+
print(
|
|
183
|
+
f"Warning: Argument '{key}' with value '{str(value)[:100]}...' "
|
|
184
|
+
f"(type: {type(value).__name__}) could not be logged to wandb. Error: {e}"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def _process_metric(self, metric: str | int | float | Table):
|
|
188
|
+
if isinstance(metric, Table):
|
|
189
|
+
headers, rows = metric.export()
|
|
190
|
+
return self._wandb.Table(columns=headers, data=rows)
|
|
191
|
+
else:
|
|
192
|
+
return metric
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def training_monitoring_link(self) -> str:
|
|
196
|
+
return self.run.get_url() # type: ignore
|
|
197
|
+
|
|
198
|
+
def close(self):
|
|
199
|
+
self._wandb.finish()
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class TBMetricsLogger(Logger):
|
|
203
|
+
def __init__(self, run_name: str, logging_dir: str, monitoring_link: str | None = None):
|
|
204
|
+
try:
|
|
205
|
+
from tensorboardX import SummaryWriter
|
|
206
|
+
except ImportError:
|
|
207
|
+
loguru.error(
|
|
208
|
+
"Tensorboard is not available."
|
|
209
|
+
"To install adaptive-harmony with TB support, install `adaptive-harmony[tensorboard]"
|
|
210
|
+
)
|
|
211
|
+
raise
|
|
212
|
+
|
|
213
|
+
self.monitoring_link = monitoring_link
|
|
214
|
+
self.logging_dir = os.path.join(logging_dir, run_name)
|
|
215
|
+
self.writer = SummaryWriter(str(self.logging_dir), flush_secs=15)
|
|
216
|
+
self.step = 0
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def is_available() -> bool:
|
|
220
|
+
try:
|
|
221
|
+
from tensorboardX import SummaryWriter # noqa: F401
|
|
222
|
+
|
|
223
|
+
return True
|
|
224
|
+
except ImportError:
|
|
225
|
+
return False
|
|
226
|
+
|
|
227
|
+
def __call__(self, logs: Mapping[str, str | int | float | Table]):
|
|
228
|
+
modified_tables = False
|
|
229
|
+
for entry, data in logs.items():
|
|
230
|
+
if isinstance(data, int) or isinstance(data, float):
|
|
231
|
+
self.writer.add_scalar(entry, float(data), self.step)
|
|
232
|
+
elif isinstance(data, Table):
|
|
233
|
+
modified_tables = True
|
|
234
|
+
table_path = os.path.join(self.logging_dir, "html_tables", f"{entry}_{self.step}.html")
|
|
235
|
+
os.makedirs(os.path.dirname(table_path), exist_ok=True)
|
|
236
|
+
with open(table_path, "w") as f:
|
|
237
|
+
f.write(data.to_html_table())
|
|
238
|
+
elif isinstance(data, str):
|
|
239
|
+
self.writer.add_text(entry, data)
|
|
240
|
+
else:
|
|
241
|
+
print(f"TensorBoard logger does not support type: {type(data)}")
|
|
242
|
+
|
|
243
|
+
if modified_tables:
|
|
244
|
+
self.create_index_page(os.path.join(self.logging_dir, "html_tables"), sample_files=None)
|
|
245
|
+
|
|
246
|
+
self.step += 1
|
|
247
|
+
self.writer.flush()
|
|
248
|
+
|
|
249
|
+
def log_config(self, config: dict[str, Any]):
|
|
250
|
+
print("Warming: TensorBoard logger does not support logging config parameters.")
|
|
251
|
+
|
|
252
|
+
def close(self):
|
|
253
|
+
self.writer.close()
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def training_monitoring_link(self) -> str:
|
|
257
|
+
return self.monitoring_link or self.logging_dir
|
|
258
|
+
|
|
259
|
+
def create_index_page(self, root_dir: str, sample_files: list[str] | None = None):
|
|
260
|
+
"""
|
|
261
|
+
Traverses subdirectories to find all .html files OR uses a sample list
|
|
262
|
+
to create a grouped index.html file with relative links to them.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
root_dir (str): The starting directory to search from.
|
|
266
|
+
Defaults to the current directory.
|
|
267
|
+
sample_files (list, optional): A list of dummy file paths to use
|
|
268
|
+
for a layout preview. If None, the
|
|
269
|
+
script will search the file system.
|
|
270
|
+
"""
|
|
271
|
+
if sample_files:
|
|
272
|
+
print("Using sample data to generate layout preview.")
|
|
273
|
+
html_files = sample_files
|
|
274
|
+
else:
|
|
275
|
+
print("Searching for HTML files...")
|
|
276
|
+
html_files = []
|
|
277
|
+
# os.walk is perfect for recursively exploring a directory tree
|
|
278
|
+
for dirpath, _, filenames in os.walk(root_dir):
|
|
279
|
+
for filename in filenames:
|
|
280
|
+
# We are looking for HTML files, but we want to ignore any
|
|
281
|
+
# index files we might have created previously.
|
|
282
|
+
if filename.endswith(".html") and filename != "index.html":
|
|
283
|
+
# Get the full path to the file
|
|
284
|
+
full_path = os.path.join(dirpath, filename)
|
|
285
|
+
# Get the path relative to the root_dir, which is what
|
|
286
|
+
# we need for the hyperlink.
|
|
287
|
+
relative_path = os.path.relpath(full_path, root_dir)
|
|
288
|
+
# On Windows, paths use backslashes. For HTML links, we need
|
|
289
|
+
# forward slashes.
|
|
290
|
+
html_files.append(relative_path.replace("\\", "/"))
|
|
291
|
+
|
|
292
|
+
# Sort the list alphabetically for a clean, predictable order
|
|
293
|
+
html_files.sort()
|
|
294
|
+
|
|
295
|
+
# --- Group files by their parent directory ---
|
|
296
|
+
grouped_files = defaultdict(list)
|
|
297
|
+
for file_path in html_files:
|
|
298
|
+
parent_dir = os.path.dirname(file_path)
|
|
299
|
+
if not parent_dir:
|
|
300
|
+
parent_dir = "Top-Level Reports" # Group for files in root
|
|
301
|
+
grouped_files[parent_dir].append(file_path)
|
|
302
|
+
|
|
303
|
+
# --- Start Generating the HTML for the index page ---
|
|
304
|
+
|
|
305
|
+
# A simple, clean stylesheet for the index page
|
|
306
|
+
css_style = """
|
|
307
|
+
<style>
|
|
308
|
+
body {
|
|
309
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
310
|
+
line-height: 1.6;
|
|
311
|
+
color: #333;
|
|
312
|
+
max-width: 800px;
|
|
313
|
+
margin: 40px auto;
|
|
314
|
+
padding: 0 20px;
|
|
315
|
+
}
|
|
316
|
+
h1 {
|
|
317
|
+
color: #111;
|
|
318
|
+
border-bottom: 2px solid #f0f0f0;
|
|
319
|
+
padding-bottom: 10px;
|
|
320
|
+
}
|
|
321
|
+
.group-container {
|
|
322
|
+
margin-bottom: 30px;
|
|
323
|
+
}
|
|
324
|
+
h2 {
|
|
325
|
+
font-size: 1.2rem;
|
|
326
|
+
color: #444;
|
|
327
|
+
margin-bottom: 10px;
|
|
328
|
+
padding-bottom: 5px;
|
|
329
|
+
border-bottom: 1px solid #e0e0e0;
|
|
330
|
+
}
|
|
331
|
+
ul {
|
|
332
|
+
list-style-type: none;
|
|
333
|
+
padding: 0;
|
|
334
|
+
}
|
|
335
|
+
li {
|
|
336
|
+
margin-bottom: 10px;
|
|
337
|
+
background-color: #f9f9f9;
|
|
338
|
+
border-radius: 5px;
|
|
339
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
340
|
+
transition: box-shadow 0.2s ease-in-out;
|
|
341
|
+
}
|
|
342
|
+
li:hover {
|
|
343
|
+
box-shadow: 0 3px 8px rgba(0,0,0,0.1);
|
|
344
|
+
}
|
|
345
|
+
a {
|
|
346
|
+
text-decoration: none;
|
|
347
|
+
color: #0056b3;
|
|
348
|
+
display: block;
|
|
349
|
+
padding: 12px 15px;
|
|
350
|
+
font-weight: 500;
|
|
351
|
+
}
|
|
352
|
+
a:hover {
|
|
353
|
+
background-color: #f0f5fa;
|
|
354
|
+
border-radius: 5px;
|
|
355
|
+
}
|
|
356
|
+
.file-count {
|
|
357
|
+
font-size: 1rem;
|
|
358
|
+
color: #666;
|
|
359
|
+
margin-top: -10px;
|
|
360
|
+
}
|
|
361
|
+
</style>
|
|
362
|
+
"""
|
|
363
|
+
|
|
364
|
+
html_content = f"""
|
|
365
|
+
<!DOCTYPE html>
|
|
366
|
+
<html lang="en">
|
|
367
|
+
<head>
|
|
368
|
+
<meta charset="UTF-8">
|
|
369
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
370
|
+
<title>Index of Reports</title>
|
|
371
|
+
{css_style}
|
|
372
|
+
</head>
|
|
373
|
+
<body>
|
|
374
|
+
<h1>Index of Generated Reports</h1>
|
|
375
|
+
<p class="file-count">Found {len(html_files)} report(s).</p>
|
|
376
|
+
"""
|
|
377
|
+
|
|
378
|
+
if not html_files:
|
|
379
|
+
html_content += "<p>No HTML reports were found in the subdirectories.</p>"
|
|
380
|
+
else:
|
|
381
|
+
# Iterate over the grouped dictionary
|
|
382
|
+
for parent_dir, files in grouped_files.items():
|
|
383
|
+
html_content += " <div class='group-container'>\n"
|
|
384
|
+
html_content += f" <h2>{html.escape(parent_dir)}</h2>\n"
|
|
385
|
+
html_content += " <ul>\n"
|
|
386
|
+
for file_path in files:
|
|
387
|
+
# We use html.escape to ensure that any special characters in the
|
|
388
|
+
# filename don't break the HTML.
|
|
389
|
+
safe_path = html.escape(file_path)
|
|
390
|
+
# Display the filename only, not the full path, as the group provides context
|
|
391
|
+
display_name = html.escape(os.path.basename(file_path))
|
|
392
|
+
html_content += f" <li><a href='{safe_path}'>{display_name}</a></li>\n"
|
|
393
|
+
html_content += " </ul>\n"
|
|
394
|
+
html_content += " </div>\n"
|
|
395
|
+
|
|
396
|
+
html_content += """
|
|
397
|
+
</body>
|
|
398
|
+
</html>
|
|
399
|
+
"""
|
|
400
|
+
|
|
401
|
+
# Write the generated HTML to the index.html file
|
|
402
|
+
index_file_path = os.path.join(root_dir, "index.html")
|
|
403
|
+
try:
|
|
404
|
+
with open(index_file_path, "w", encoding="utf-8") as f:
|
|
405
|
+
f.write(html_content)
|
|
406
|
+
print(f"Successfully created index.html with {len(html_files)} links.")
|
|
407
|
+
except OSError as e:
|
|
408
|
+
print(f"Error writing to file {index_file_path}: {e}")
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
class StdoutLogger(Logger):
|
|
412
|
+
def __init__(self): ...
|
|
413
|
+
|
|
414
|
+
@staticmethod
|
|
415
|
+
def is_available() -> bool:
|
|
416
|
+
return True
|
|
417
|
+
|
|
418
|
+
def __call__(self, logs: Mapping[str, str | int | float | Table]):
|
|
419
|
+
pprint(logs)
|
|
420
|
+
|
|
421
|
+
def log_config(self, config: dict[str, Any]):
|
|
422
|
+
print("\n" + "=" * 50)
|
|
423
|
+
pprint(config)
|
|
424
|
+
print("=" * 50 + "\n")
|
|
425
|
+
|
|
426
|
+
def close(self):
|
|
427
|
+
pass
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def get_prod_logger() -> Logger:
|
|
431
|
+
job_id = os.environ.get("HARMONY_JOB_ID") or "unknown-job"
|
|
432
|
+
use_case_id = os.environ.get("HARMONY_USE_CASE_ID") or "unknown-use-case"
|
|
433
|
+
use_case_name = os.environ.get("HARMONY_USE_CASE") or "unknown-use-case"
|
|
434
|
+
recipe_name = os.environ.get("HARMONY_RECIPE_NAME") or "unknown-recipe"
|
|
435
|
+
run_name = os.environ.get("HARMONY_JOB_NAME") or "unknown-run"
|
|
436
|
+
if os.environ.get("WANDB_API_KEY") and WandbLogger.is_available():
|
|
437
|
+
return WandbLogger(project_name=use_case_name, run_name=f"{recipe_name}/{run_name}")
|
|
438
|
+
elif (uri := os.environ.get("MLFLOW_TRACKING_URI")) and MLFlowLogger.is_available():
|
|
439
|
+
monitoring_link = os.environ["MLFLOW_MONITORING_LINK"]
|
|
440
|
+
return MLFlowLogger(
|
|
441
|
+
project_name=use_case_name,
|
|
442
|
+
run_name=f"{recipe_name}/{run_name}",
|
|
443
|
+
tracking_uri=uri,
|
|
444
|
+
monitoring_link=monitoring_link,
|
|
445
|
+
experiment_tags={"adaptive.use_case_id": use_case_id},
|
|
446
|
+
run_tags={"adaptive.job_id": job_id},
|
|
447
|
+
)
|
|
448
|
+
elif (log_dir := os.environ.get("TENSORBOARD_LOGGING_DIR")) and TBMetricsLogger.is_available():
|
|
449
|
+
monitoring_link = os.path.join(os.environ["TENSORBOARD_MONITORING_LINK"], job_id)
|
|
450
|
+
return TBMetricsLogger(run_name=job_id, logging_dir=log_dir, monitoring_link=monitoring_link)
|
|
451
|
+
else:
|
|
452
|
+
return StdoutLogger()
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Re-export from harmony_client.runtime.runner
|
|
2
|
+
from harmony_client.runtime.context import RecipeConfig as RecipeConfig # noqa: F401
|
|
3
|
+
from harmony_client.runtime.context import RecipeContext as RecipeContext
|
|
4
|
+
from harmony_client.runtime.runner import ( # noqa: F401
|
|
5
|
+
RunnerArgs as RunnerArgs,
|
|
6
|
+
)
|
|
7
|
+
from harmony_client.runtime.runner import (
|
|
8
|
+
_download_and_extract_recipe as _download_and_extract_recipe,
|
|
9
|
+
)
|
|
10
|
+
from harmony_client.runtime.runner import (
|
|
11
|
+
_get_params as _get_params,
|
|
12
|
+
)
|
|
13
|
+
from harmony_client.runtime.runner import (
|
|
14
|
+
_install_recipe_dependencies as _install_recipe_dependencies,
|
|
15
|
+
)
|
|
16
|
+
from harmony_client.runtime.runner import (
|
|
17
|
+
_load_and_run_recipe as _load_and_run_recipe,
|
|
18
|
+
)
|
|
19
|
+
from harmony_client.runtime.runner import (
|
|
20
|
+
_parse_script_metadata as _parse_script_metadata,
|
|
21
|
+
)
|
|
22
|
+
from harmony_client.runtime.runner import (
|
|
23
|
+
main as main,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
main()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: adaptive-harmony
|
|
3
|
+
Version: 0.1.23
|
|
4
|
+
Summary: Adaptive Harmony training recipes and utilities for LLM fine-tuning
|
|
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-Python: >=3.12
|
|
10
|
+
Requires-Dist: harmony-client
|
|
11
|
+
Requires-Dist: rich>=13.7.0
|
|
12
|
+
Requires-Dist: datasets>=2.14.0
|
|
13
|
+
Requires-Dist: hf-xet>=1.1.2
|
|
14
|
+
Requires-Dist: loguru>=0.7.2
|
|
15
|
+
Requires-Dist: pydantic-settings>=2.9.1
|
|
16
|
+
Requires-Dist: pydantic>=2.10.5
|
|
17
|
+
Requires-Dist: pybars3>=0.9.7
|
|
18
|
+
Requires-Dist: pysbd>=0.3.4
|
|
19
|
+
Requires-Dist: websockets>=15.0.1
|
|
20
|
+
Requires-Dist: setproctitle>=1.3.3
|
|
21
|
+
Requires-Dist: pydantic-xml>=2.16.0
|
|
22
|
+
Requires-Dist: openai>=1.42.0
|
|
23
|
+
Requires-Dist: pillow>=11.3.0
|
|
24
|
+
Requires-Dist: boto3>=1.40
|
|
25
|
+
Requires-Dist: tomli>=2
|
|
26
|
+
Provides-Extra: tensorboard
|
|
27
|
+
Requires-Dist: tensorboardX>=2.6.2.2; extra == "tensorboard"
|
|
28
|
+
Provides-Extra: mlflow
|
|
29
|
+
Requires-Dist: mlflow>=3.4.0; extra == "mlflow"
|
|
30
|
+
Provides-Extra: wandb
|
|
31
|
+
Requires-Dist: wandb>=0.19.11; extra == "wandb"
|
|
32
|
+
Provides-Extra: all-monitoring
|
|
33
|
+
Requires-Dist: tensorboardX>=2.6.2.2; extra == "all-monitoring"
|
|
34
|
+
Requires-Dist: mlflow>=3.4.0; extra == "all-monitoring"
|
|
35
|
+
Requires-Dist: wandb>=0.19.11; extra == "all-monitoring"
|
|
36
|
+
Provides-Extra: mlflow-skinny
|
|
37
|
+
Requires-Dist: mlflow-skinny>=3.4.0; extra == "mlflow-skinny"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
adaptive_harmony/__init__.py,sha256=_KoDEWVU-mCtXWp7ZXXlWcTWSVVkE6_r8xlJDXyOxRw,4849
|
|
2
|
+
adaptive_harmony/logging_table.py,sha256=kN5jS-PO0Y1B6KFicv3BnSyXz5OfThV4L1pCY3_kUmk,56
|
|
3
|
+
adaptive_harmony/metric_logger.py,sha256=6KAp7UhhygiHgWj5l9Bhwc7Sg9cIhxSzAilpxp_7iZM,16619
|
|
4
|
+
adaptive_harmony/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
adaptive_harmony/common/__init__.py,sha256=qebnYmwNBurtouGDbK27mtwt9zLm3P0tHR_M9LnFZT4,967
|
|
6
|
+
adaptive_harmony/common/callbacks.py,sha256=Q5qxVOAdnQRUZxy_ZcBAVxXTmSNA3o7L-cfEZ3JPnWs,8636
|
|
7
|
+
adaptive_harmony/common/checkpointing.py,sha256=rNfzwTEvWzNbUMjkl4CUD3zfsYdsWU_ksR3Lqn-Ghck,6569
|
|
8
|
+
adaptive_harmony/common/dpo.py,sha256=ioionFEnxzagfBVnIvLBh6rb6-d8WeWtVHgp-VDBKf8,3463
|
|
9
|
+
adaptive_harmony/common/env_grpo.py,sha256=bQEBJ7TojBxnCIRopu_pkjzfTl1zAXRcW8olRDMDtIE,15149
|
|
10
|
+
adaptive_harmony/common/grpo.py,sha256=LlG0NxpTtFga06YguTNDnEOVfBjRYHJoRyz4fbAFCRc,10384
|
|
11
|
+
adaptive_harmony/common/gspo.py,sha256=O4z-BrKLusGeM8P6LWz77h8i0HrUhLR7_wxrAluxdxQ,2407
|
|
12
|
+
adaptive_harmony/common/ppo.py,sha256=owJlajLDnOxq4LpjjIn-dLXJVmKlsQh3wMG0zfnbUxU,12393
|
|
13
|
+
adaptive_harmony/common/rm.py,sha256=0HgJXUL722DWhqBOFk_do3k9NUk6g09bEW0qDZxGYfQ,3504
|
|
14
|
+
adaptive_harmony/common/sft.py,sha256=A_g72b6Et7wCSApdc8TFcCn5JoRWQgYwG1WsKm-aiaY,4883
|
|
15
|
+
adaptive_harmony/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
adaptive_harmony/core/dataset.py,sha256=MnECz5f0ff3D8MYOvG4Q9RXAJmuoCSk5vi7ysgo4TBM,2474
|
|
17
|
+
adaptive_harmony/core/display.py,sha256=Wid2sRv4MPUqDlpEcAhncBmkixT32JhkLCN9oAxw658,3073
|
|
18
|
+
adaptive_harmony/core/image_utils.py,sha256=NJeK3IOB-hbTuc5XR5-eSqAzomwmDOwoZChsdcjyUV0,3775
|
|
19
|
+
adaptive_harmony/core/reasoning.py,sha256=36HW5jBkAtWibx90CAVes_E8MlbzSAI0v0K4ITSzk-Q,445
|
|
20
|
+
adaptive_harmony/core/rich_counter.py,sha256=z0uIq1pLES8F7wcdyQALxD3NU-vTOmlLPyeeyVotZRg,12334
|
|
21
|
+
adaptive_harmony/core/rl_utils.py,sha256=gEY37o-h3jIF7lW9E9SGnFoIcBasBn3byO6-0SUK4PI,1023
|
|
22
|
+
adaptive_harmony/core/schedulers.py,sha256=tqjxaIsjlXAkGWfTCnvnKhTCblrIJStHaUxfAGK3qwU,1361
|
|
23
|
+
adaptive_harmony/core/structured_output.py,sha256=xvtQEor9Cjce5T4lYFLsN1NkZPqbomw15Jq7Cl3-MFM,14940
|
|
24
|
+
adaptive_harmony/core/utils.py,sha256=1q2ouNOti5IH43vGIYrx5Jm7qPokQIOVbUKJw9aV5ss,13412
|
|
25
|
+
adaptive_harmony/core/reward_client/__init__.py,sha256=q513qDHyTPmZZCYpedXbevHnykimn1hDqo6zzyTc65c,459
|
|
26
|
+
adaptive_harmony/core/reward_client/client.py,sha256=4UcgWPuaptGNJz2isp895u1szA1chwLKn6WlpefnPn8,5946
|
|
27
|
+
adaptive_harmony/core/reward_client/reward_types.py,sha256=1K0yfOzNAHT1GQfa8vpuWkt5o0Afe7iZ4lkzvdxWelY,817
|
|
28
|
+
adaptive_harmony/core/reward_client/websocket_utils.py,sha256=3oedoZDhKLnwp98g1QanepWHCXv92G3k_IJC3_MPH0E,499
|
|
29
|
+
adaptive_harmony/environment/__init__.py,sha256=6WpEGBGfQ4Cnr2rRncZ-S6nKDBllWimhwnN7DEqgDHQ,185
|
|
30
|
+
adaptive_harmony/environment/environment.py,sha256=yDAcR2VzlU-2-OdWbqVi1IPtMCqNDwiDFjKstK1axpo,3907
|
|
31
|
+
adaptive_harmony/evaluation/__init__.py,sha256=wsMYryTeiITYV0PHW86Alkz2z6WDdrKM6EkYON4VdLw,74
|
|
32
|
+
adaptive_harmony/evaluation/evaluation_artifact.py,sha256=9rx0mDg5BENIWH6Zzq9br8CsKyJrkc_Ls8jXz0bPY0w,1958
|
|
33
|
+
adaptive_harmony/graders/__init__.py,sha256=id10TznTfd5G0xmVNkjCbe-bWKuykHxOFCCqWrcVbXQ,580
|
|
34
|
+
adaptive_harmony/graders/base_grader.py,sha256=TYOeFxXgoUoOK1HaiKgjnRAucyjdlX42e2kDs0jNi2Q,9988
|
|
35
|
+
adaptive_harmony/graders/combined_grader.py,sha256=wmJDxBVS3U3kNPBC1nROlthfm3ZhfzWVAESzizGo9Bs,4597
|
|
36
|
+
adaptive_harmony/graders/exceptions.py,sha256=YBUQpx1_3RkN_GcxvKLCtODhqPUqCdOiwmdxJ01vG9s,330
|
|
37
|
+
adaptive_harmony/graders/reward_server_grader.py,sha256=93qkUIDdCvyvoe6OniJS500UuFtI74i1_e9fqCm_zWc,1360
|
|
38
|
+
adaptive_harmony/graders/templated_prompt_judge.py,sha256=0TNg9agPABXn0aIrnpMyjQFW2Dr5303L53xirIfaatk,10076
|
|
39
|
+
adaptive_harmony/graders/utils.py,sha256=iZHILLwQn72DXyyuFlb-6XUUEzG0t78VbaDs0yXfsWI,2533
|
|
40
|
+
adaptive_harmony/graders/answer_relevancy_judge/__init__.py,sha256=942BWEsIpWdkNfjzp-Ma1ZXtpmop1MF0E7b1u0CcJKY,95
|
|
41
|
+
adaptive_harmony/graders/answer_relevancy_judge/answer_relevancy_judge.py,sha256=KdYi6urGrVxgYBUExMGt5wit6Pf5f-K5Vfys5QgfM6w,4495
|
|
42
|
+
adaptive_harmony/graders/answer_relevancy_judge/prompts.py,sha256=ncIbN2UVpc9lnVimkiL3w7QrjseXoeBRk6mJvEAbL0k,1894
|
|
43
|
+
adaptive_harmony/graders/binary_judge/__init__.py,sha256=HmdU6eJyv4-EQPXqEtXG1kQ8Sco6X4D2iUzoOFZwRDY,188
|
|
44
|
+
adaptive_harmony/graders/binary_judge/binary_judge.py,sha256=Y5DiqiGSo4A68rBYrjqY70Wxyny5v5HwWHjfzPWnyVw,8307
|
|
45
|
+
adaptive_harmony/graders/binary_judge/prompts.py,sha256=3VQ5DebmMUBxqO9hAvLAsqfaTQw5aYqvExZozlGszZs,6415
|
|
46
|
+
adaptive_harmony/graders/context_relevancy_judge/__init__.py,sha256=wrxO_2-cVZvFXOtMm2O1_1hdv__eqNrJ0bvxRrE9Pvc,98
|
|
47
|
+
adaptive_harmony/graders/context_relevancy_judge/context_relevancy_judge.py,sha256=IATqNn_Uw6cp5jxdBl-fXMemg5webQcC-893e4wvggw,5294
|
|
48
|
+
adaptive_harmony/graders/context_relevancy_judge/prompts.py,sha256=eb8tmrNuHT4I7qunf5pCfr3BVis2pWfgS-BBKo3XJQs,3647
|
|
49
|
+
adaptive_harmony/graders/faithfulness_judge/__init__.py,sha256=8Dx5_aujk3TPR9p3iBIS8Xj1S9G2wxiQ8x44WIACUYc,85
|
|
50
|
+
adaptive_harmony/graders/faithfulness_judge/faithfulness_judge.py,sha256=5qOcjfPeT_7-dUGZ4MO5GeYuy32U90Yg2GXVs-YZ7BE,6044
|
|
51
|
+
adaptive_harmony/graders/faithfulness_judge/prompts.py,sha256=_3otDjyjUaKfebeE0yYZXU99zImi-uJ_E7D1sHEhZmg,984
|
|
52
|
+
adaptive_harmony/graders/range_judge/__init__.py,sha256=h2LObCYzY0h8QumTBcfjNuBW31WfZ7rkojmrL0m4rbU,200
|
|
53
|
+
adaptive_harmony/graders/range_judge/prompts.py,sha256=V2iVWUDGP54-I_pHR2yPyR9SuVYMa-yaOtCRag5Dq9s,9331
|
|
54
|
+
adaptive_harmony/graders/range_judge/range_judge.py,sha256=Av8qMqcWm8J3c8Tli_yPqMJGTlQos9dlwAbi32HcBOo,8199
|
|
55
|
+
adaptive_harmony/graders/range_judge/types.py,sha256=UlS_HhYiD5mNXEGK7M8jugFlk-WtvVNqMQh3IYqBUno,398
|
|
56
|
+
adaptive_harmony/parameters/__init__.py,sha256=MBCDZ2YzqM8J2oU3BIH2t1mnr85xHrvlLQyU-lcEHdg,114
|
|
57
|
+
adaptive_harmony/runtime/__init__.py,sha256=AQ9pixPP4KrHaElpDTtghaDN4VVrfh5meQJithPtnWo,108
|
|
58
|
+
adaptive_harmony/runtime/context.py,sha256=G-aijdP0WLHfNU8zfPFm9czGiowU79yHRjvdqAC4mlc,113
|
|
59
|
+
adaptive_harmony/runtime/data.py,sha256=oP1WKOLueVHuLodnz_ZHge8JCWR_YInZI7ug2LXKvQk,107
|
|
60
|
+
adaptive_harmony/runtime/decorators.py,sha256=zDNnG_fNz-zgHnb-d5WCPNLMMKFRtL_ncz4wEGVSp-o,119
|
|
61
|
+
adaptive_harmony/runtime/model_artifact_save.py,sha256=1Ui-Q1hP_eDAhKBFOXpEVix5Q3TY9_d11viXs0xsk3o,137
|
|
62
|
+
adaptive_harmony/runtime/runner.py,sha256=70lNz2pe2dGEgqH8Igwp8ppGLDLxHVwNmxcyV4Y6HMM,898
|
|
63
|
+
adaptive_harmony/runtime/simple_notifier.py,sha256=iVXtZwfcOvkZlWQgFC0qjE1P-yA6Y7Wx0SxQ9FoJ-0s,129
|
|
64
|
+
adaptive_harmony-0.1.23.dist-info/METADATA,sha256=vSt6Q78VZyskfevyOZyIACXAKoSSfFEIm1FeTwgeipE,1429
|
|
65
|
+
adaptive_harmony-0.1.23.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
66
|
+
adaptive_harmony-0.1.23.dist-info/top_level.txt,sha256=ZEmoKxkFM4M7H2mgH15wQ4Tf0Eb13FBmghRvC2seacU,17
|
|
67
|
+
adaptive_harmony-0.1.23.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
adaptive_harmony
|