Spaces:
Sleeping
Sleeping
Download scripts/lib/chat_eval/state_io.py from defenestrate/asa: direct link, hf CLI and curl.
- Browser
- Download file 4.66 kB
-
https://huggingface.co/spaces/defenestrate/asa/resolve/main/scripts/lib/chat_eval/state_io.py
- Command line
-
hf download hf://spaces/defenestrate/asa/scripts/lib/chat_eval/state_io.py
-
curl -L -o state_io.py https://huggingface.co/spaces/defenestrate/asa/resolve/main/scripts/lib/chat_eval/state_io.py
4.66 kB
| """HF Dataset I/O for the retrieval-tuning loop. | |
| Three files are exchanged between the GitHub Actions tuner and the | |
| chat-logs HF Dataset: | |
| - turns.jsonl (chat writes; tuner annotates with judges) | |
| - retrieval_tuning.json (tuner writes; chat reads at boot) | |
| - tuning_log.jsonl (tuner-only audit trail) | |
| fetch_state() downloads all three to derived/student-chat/, materializing | |
| defaults for any file missing from the dataset (cold start). | |
| push_state() uploads the same three local paths back. | |
| Lazy imports of huggingface_hub via re-exported wrappers so tests can patch | |
| this module's symbols and so this module imports without huggingface_hub | |
| installed (local dev). | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import logging | |
| import shutil | |
| from pathlib import Path | |
| from scripts.lib.chat_eval.tuning import empty_state | |
| from scripts.lib.turns_merge import is_not_found_error | |
| logger = logging.getLogger(__name__) | |
| _DATASET_FILES = ("turns.jsonl", "retrieval_tuning.json", "tuning_log.jsonl") | |
| def hf_hub_download(*args, **kwargs): | |
| """Re-export so tests can patch this module's reference.""" | |
| from huggingface_hub import hf_hub_download as _real | |
| return _real(*args, **kwargs) | |
| def HfApi(*args, **kwargs): | |
| """Re-export so tests can patch this module's reference.""" | |
| from huggingface_hub import HfApi as _real | |
| return _real(*args, **kwargs) | |
| def fetch_state(*, repo_id: str, token: str, derived_dir: Path) -> None: | |
| """Download the three dataset files into derived_dir. | |
| Missing files get sensible defaults so the rest of the loop has real | |
| files to read: | |
| - turns.jsonl missing -> empty file (no turns to judge) | |
| - retrieval_tuning.json missing -> empty_state() | |
| - tuning_log.jsonl missing -> empty file | |
| """ | |
| derived_dir.mkdir(parents=True, exist_ok=True) | |
| for fname in _DATASET_FILES: | |
| local = derived_dir / fname | |
| try: | |
| tmp_path = hf_hub_download( | |
| repo_id=repo_id, | |
| filename=fname, | |
| repo_type="dataset", | |
| token=token, | |
| ) | |
| shutil.copyfile(tmp_path, local) | |
| logger.info("state_io: fetched %s", fname) | |
| except Exception as e: | |
| if not is_not_found_error(e): | |
| raise | |
| if fname == "retrieval_tuning.json": | |
| local.write_text(json.dumps(empty_state()), encoding="utf-8") | |
| else: | |
| local.write_text("", encoding="utf-8") | |
| logger.info("state_io: %s missing on remote; wrote default.", fname) | |
| def push_state(*, repo_id: str, token: str, derived_dir: Path, | |
| commit_message: str) -> None: | |
| """Upload the three files from derived_dir to the dataset. | |
| For turns.jsonl: refetch remote and merge by turn_id (local wins on | |
| conflict; remote-only turns preserved). retrieval_tuning.json and | |
| tuning_log.jsonl are tuner-only writes and overwrite remote. | |
| Raises FileNotFoundError if any file is missing locally — we never | |
| push an empty over a populated remote by accident. | |
| """ | |
| api = HfApi() | |
| for fname in _DATASET_FILES: | |
| local = derived_dir / fname | |
| if not local.is_file(): | |
| raise FileNotFoundError( | |
| f"state_io.push_state: missing local file {local}; " | |
| f"refusing to push." | |
| ) | |
| staging: Path | None = None | |
| if fname == "turns.jsonl": | |
| merged_bytes = _merge_turns_with_remote( | |
| local, repo_id=repo_id, token=token, | |
| ) | |
| staging = local.with_suffix(local.suffix + ".merged") | |
| staging.write_bytes(merged_bytes) | |
| upload_path = str(staging) | |
| else: | |
| upload_path = str(local) | |
| try: | |
| api.upload_file( | |
| path_or_fileobj=upload_path, | |
| path_in_repo=fname, | |
| repo_id=repo_id, | |
| repo_type="dataset", | |
| token=token, | |
| commit_message=commit_message, | |
| ) | |
| logger.info("state_io: pushed %s", fname) | |
| finally: | |
| if staging is not None: | |
| try: | |
| staging.unlink() | |
| except OSError: | |
| pass | |
| def _merge_turns_with_remote(local_path: Path, *, repo_id: str, | |
| token: str) -> bytes: | |
| """Tuner-side adapter: read local file, delegate to turns_merge.""" | |
| from scripts.lib.turns_merge import merge_turns_with_remote | |
| return merge_turns_with_remote(local_path.read_bytes(), | |
| repo_id=repo_id, token=token) | |