Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
from common import ROOT, localize_workspace_path
|
||||
|
||||
DEFAULT_CONFIG = ROOT / "plugins" / "1c" / "training" / "configs" / "qwen3-coder-30b-a3b-lora.yaml"
|
||||
DEFAULT_REQUIRED_BASE_FILES = [
|
||||
"config.json",
|
||||
"tokenizer.json",
|
||||
"tokenizer_config.json",
|
||||
]
|
||||
|
||||
|
||||
def load_config(path: Path) -> dict:
|
||||
with path.open("r", encoding="utf-8") as handle:
|
||||
data = yaml.safe_load(handle)
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(f"{path} must contain a YAML mapping")
|
||||
return data
|
||||
|
||||
|
||||
def count_jsonl(path: Path) -> int:
|
||||
if not path.exists():
|
||||
return 0
|
||||
count = 0
|
||||
with path.open("r", encoding="utf-8") as handle:
|
||||
for line in handle:
|
||||
if line.strip():
|
||||
json.loads(line)
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def check_python_deps() -> list[str]:
|
||||
missing = []
|
||||
for module in ("torch", "transformers", "datasets", "peft", "accelerate"):
|
||||
try:
|
||||
__import__(module)
|
||||
except Exception as exc:
|
||||
missing.append(f"{module}: {type(exc).__name__}: {exc}")
|
||||
return missing
|
||||
|
||||
|
||||
def check_cuda() -> tuple[bool, str]:
|
||||
try:
|
||||
import torch
|
||||
except Exception as exc:
|
||||
return False, f"torch unavailable: {exc}"
|
||||
if not torch.cuda.is_available():
|
||||
return False, "torch.cuda.is_available() is false"
|
||||
return True, torch.cuda.get_device_name(0)
|
||||
|
||||
|
||||
def expected_base_files(config: dict) -> list[str]:
|
||||
configured = config.get("required_base_files")
|
||||
if isinstance(configured, list):
|
||||
return [str(item) for item in configured if str(item).strip()]
|
||||
return list(DEFAULT_REQUIRED_BASE_FILES)
|
||||
|
||||
|
||||
def missing_weight_shards(base_model_path: Path) -> list[str]:
|
||||
index_path = base_model_path / "model.safetensors.index.json"
|
||||
if not index_path.exists():
|
||||
return []
|
||||
try:
|
||||
index = json.loads(index_path.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError as exc:
|
||||
return [f"invalid model.safetensors.index.json: {exc}"]
|
||||
weight_map = index.get("weight_map")
|
||||
if not isinstance(weight_map, dict):
|
||||
return ["model.safetensors.index.json has no weight_map"]
|
||||
shards = sorted({str(value) for value in weight_map.values() if str(value).strip()})
|
||||
return [name for name in shards if not (base_model_path / name).exists()]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Preflight checks for 1C LoRA training.")
|
||||
parser.add_argument("--config", type=Path, default=DEFAULT_CONFIG)
|
||||
args = parser.parse_args()
|
||||
|
||||
errors: list[str] = []
|
||||
warnings: list[str] = []
|
||||
config = load_config(args.config)
|
||||
|
||||
dataset_path = localize_workspace_path(config["dataset_path"])
|
||||
dataset_count = count_jsonl(dataset_path)
|
||||
if dataset_count == 0:
|
||||
errors.append(f"training dataset is missing or empty: {dataset_path}")
|
||||
elif dataset_count < 50:
|
||||
warnings.append(f"dataset has only {dataset_count} record(s); this is not enough for useful fine-tuning")
|
||||
|
||||
base_model_path = localize_workspace_path(config["base_model_path"])
|
||||
missing_files = [name for name in expected_base_files(config) if not (base_model_path / name).exists()]
|
||||
if missing_files:
|
||||
errors.append(f"base model is incomplete at {base_model_path}: missing {', '.join(missing_files)}")
|
||||
missing_shards = missing_weight_shards(base_model_path)
|
||||
if missing_shards:
|
||||
errors.append(f"base model shard set is incomplete at {base_model_path}: missing {', '.join(missing_shards)}")
|
||||
|
||||
output_dir = localize_workspace_path(config["output_dir"])
|
||||
if not output_dir.parent.exists():
|
||||
warnings.append(f"adapter parent directory does not exist yet: {output_dir.parent}")
|
||||
|
||||
missing_deps = check_python_deps()
|
||||
if missing_deps:
|
||||
errors.append(f"missing Python training dependencies: {'; '.join(missing_deps)}")
|
||||
|
||||
cuda_ok, cuda_message = check_cuda()
|
||||
if cuda_ok:
|
||||
print(f"CUDA: {cuda_message}")
|
||||
else:
|
||||
errors.append(f"GPU/CUDA unavailable: {cuda_message}")
|
||||
|
||||
for warning in warnings:
|
||||
print(f"WARNING: {warning}")
|
||||
|
||||
if errors:
|
||||
print("1C training preflight failed:", file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f"- {error}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
print("1C training preflight passed.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user