AQIT 0.1.0
Loading...
Searching...
No Matches
simulate_inputs.py
Go to the documentation of this file.
1# Copyright (c) 2025-present Aquin Labs Private Limited. All Rights Reserved.
2# This file is part of the Aquin Engine. Unauthorized copying, modification,
3# distribution, or use of this file, via any medium, is strictly prohibited.
4# Proprietary and confidential. See LICENSE for terms.
5
6"""Resolve dataset and algo paths for simulate."""
7from __future__ import annotations
8
9import csv
10import json
11import os
12from pathlib import Path
13from typing import Any
14
15_HINT_KEYS = {
16 "rank", "alpha", "lr", "learning_rate", "epochs", "num_train_epochs",
17 "dropout", "target_modules", "targetModules", "warmup_steps", "warmupSteps",
18 "grad_clip", "gradClip", "weight_decay", "weightDecay", "grad_accum_steps",
19 "gradAccumSteps", "optimizer", "scheduler", "max_seq_len", "maxSeqLen",
20 "use_qlora", "useQlora", "use_rlhf", "rlhf_beta",
21}
22_KEY_ALIASES = {
23 "learning_rate": "lr",
24 "num_train_epochs": "epochs",
25 "target_modules": "targetModules",
26 "warmup_steps": "warmupSteps",
27 "grad_clip": "gradClip",
28 "weight_decay": "weightDecay",
29 "grad_accum_steps": "gradAccumSteps",
30 "max_seq_len": "maxSeqLen",
31 "use_qlora": "useQlora",
32}
33
34_INSTR_FIELDS = ("instruction", "prompt", "input", "query", "question", "text")
35_RESP_FIELDS = ("response", "output", "answer", "completion", "target", "label")
36
37
38def _expand(path: str) -> Path:
39 return Path(os.path.expanduser(path)).resolve()
40
41
42def _normalize_row(row: dict[str, Any]) -> dict[str, Any]:
43 if not isinstance(row, dict):
44 raise ValueError(f"Each dataset row must be an object, got {type(row).__name__}")
45 if row.get("instruction") and row.get("response"):
46 return row
47 instr = next((row[k] for k in _INSTR_FIELDS if row.get(k)), None)
48 resp = next((row[k] for k in _RESP_FIELDS if row.get(k)), None)
49 if instr is not None and resp is not None:
50 out = dict(row)
51 out.setdefault("instruction", str(instr))
52 out.setdefault("response", str(resp))
53 return out
54 return row
55
56
57def load_dataset_file(path: str) -> list[dict]:
58 """Load rows from the user's dataset file (.json, .jsonl, .csv)."""
59 p = _expand(path)
60 if not p.is_file():
61 raise FileNotFoundError(f"Dataset not found: {p}")
62
63 suffix = p.suffix.lower()
64 if suffix == ".jsonl":
65 rows: list[dict] = []
66 with p.open(encoding="utf-8") as f:
67 for i, line in enumerate(f, 1):
68 line = line.strip()
69 if not line:
70 continue
71 try:
72 rows.append(_normalize_row(json.loads(line)))
73 except json.JSONDecodeError as e:
74 raise ValueError(f"Invalid JSON on line {i} of {p}: {e}") from e
75 if not rows:
76 raise ValueError(f"Dataset file is empty: {p}")
77 return rows
78
79 if suffix == ".json":
80 with p.open(encoding="utf-8") as f:
81 data = json.load(f)
82 if isinstance(data, list):
83 rows = [_normalize_row(r) for r in data]
84 elif isinstance(data, dict):
85 for key in ("rows", "data", "dataset", "examples", "train"):
86 if isinstance(data.get(key), list):
87 rows = [_normalize_row(r) for r in data[key]]
88 break
89 else:
90 raise ValueError(f"Unrecognized JSON dataset layout: {p}")
91 else:
92 raise ValueError(f"JSON dataset must be an array or object: {p}")
93 if not rows:
94 raise ValueError(f"Dataset file is empty: {p}")
95 return rows
96
97 if suffix == ".csv":
98 with p.open(encoding="utf-8", newline="") as f:
99 reader = csv.DictReader(f)
100 rows = [_normalize_row(dict(r)) for r in reader]
101 if not rows:
102 raise ValueError(f"CSV dataset is empty: {p}")
103 return rows
104
105 raise ValueError(
106 f"Unsupported dataset format {suffix!r} at {p} — use your pipeline's .json, .jsonl, or .csv export."
107 )
108
109
110def _parse_structured_file(p: Path) -> Any | None:
111 suffix = p.suffix.lower()
112 if suffix in (".py", ".sh", ".bash"):
113 return None
114 text = p.read_text(encoding="utf-8")
115 if suffix in (".yaml", ".yml"):
116 try:
117 import yaml # type: ignore
118 except ImportError:
119 return None
120 return yaml.safe_load(text)
121 if suffix == ".json":
122 try:
123 return json.loads(text)
124 except json.JSONDecodeError:
125 return None
126 return None
127
128
129def _collect_hints(obj: Any, found: dict[str, Any], depth: int = 0) -> None:
130 if depth > 4 or obj is None:
131 return
132 if isinstance(obj, dict):
133 for k, v in obj.items():
134 if k in _HINT_KEYS and k not in found:
135 key = _KEY_ALIASES.get(k, k)
136 found[key] = v
137 elif isinstance(v, (dict, list)):
138 _collect_hints(v, found, depth + 1)
139 elif isinstance(obj, list):
140 for item in obj[:20]:
141 _collect_hints(item, found, depth + 1)
142
143
144def _try_training_hints(path: Path) -> dict[str, Any]:
145 """Best-effort scrape of rank/lr/etc. from the user's existing config — never required."""
146 data = _parse_structured_file(path)
147 if data is None:
148 return {}
149 found: dict[str, Any] = {}
150 _collect_hints(data, found)
151 return found
152
153
154def prepare_simulation_args(args: dict) -> dict:
155 """
156 Resolve --dataset and --algo paths from the user's existing pipeline.
157 No Aquin-specific config format — paths only. Optional CLI flags override hints.
158 """
159 out = dict(args)
160
161 algo_path = out.pop("algo_path", None) or out.get("algo")
162 if isinstance(algo_path, str):
163 p = _expand(algo_path)
164 if not p.is_file():
165 raise FileNotFoundError(f"Algo path not found: {p}")
166 out["algo_path"] = str(p)
167 out.pop("algo", None)
168 for k, v in _try_training_hints(p).items():
169 out.setdefault(k, v)
170
171 dataset_ref = out.get("dataset")
172 if isinstance(dataset_ref, str):
173 p = _expand(dataset_ref)
174 if not p.is_file():
175 raise FileNotFoundError(f"Dataset not found: {p}")
176 out["rows"] = load_dataset_file(str(p))
177 out["dataset_path"] = str(p)
178 out.pop("dataset", None)
179 elif isinstance(dataset_ref, list):
180 out["rows"] = dataset_ref
181 out.pop("dataset", None)
182
183 return out
None _collect_hints(Any obj, dict[str, Any] found, int depth=0)
list[dict] load_dataset_file(str path)
dict[str, Any] _try_training_hints(Path path)
dict[str, Any] _normalize_row(dict[str, Any] row)
dict prepare_simulation_args(dict args)
Any|None _parse_structured_file(Path p)