Memory-Quilt / scripts /share_traces_to_hf_dataset.py
Abhishek
Initialize project files and updated hackathon tags
485459a
Raw
History Blame Contribute Delete
9.4 kB
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
for candidate in (ROOT / 'src', ROOT):
if candidate.is_dir() and str(candidate) not in sys.path:
sys.path.insert(0, str(candidate))
from app_kit.tracing import canonicalize_trace_payload
SCHEMA_FIELDS = ['timestamp', 'inputs', 'parsed_outputs', 'model_name', 'model_id', 'adapter_name', 'generation_stats', 'checkpoint_path', 'checkpoint_source']
def _relative_or_absolute(path: Path, root: Path) -> str:
try:
return str(path.resolve().relative_to(root.resolve()))
except Exception:
return str(path.resolve())
def discover_trace_files(traces_root: str | Path) -> list[Path]:
root = Path(traces_root).expanduser()
if not root.exists():
raise FileNotFoundError(f'Trace directory not found: {root}')
if root.is_file():
return [root]
if root.name == 'traces':
candidates = [path for path in sorted(root.glob('*.json')) if path.is_file()]
else:
candidates = [path for path in sorted(root.rglob('traces/*.json')) if path.is_file()]
return candidates
def default_output_dir(repo_root: str | Path, repo_name: str | None = None, today: str | None = None) -> Path:
root = Path(repo_root).expanduser()
repo_name = repo_name or root.name
today = today or datetime.now(timezone.utc).date().isoformat()
return root / 'artifacts' / 'verification' / today / 'sharing_is_caring' / repo_name
def build_dataset_row(trace_file: Path, repo_root: Path, repo_name: str) -> dict[str, Any]:
trace_payload = json.loads(trace_file.read_text(encoding='utf-8'))
trace = canonicalize_trace_payload(trace_payload)
row: dict[str, Any] = {
'repo_name': repo_name,
'source_trace_path': _relative_or_absolute(trace_file, repo_root),
'timestamp': trace['timestamp'],
'inputs': trace['inputs'],
'parsed_outputs': trace['parsed_outputs'],
'model_name': trace['model_name'],
'kind': trace.get('kind', 'trace'),
}
for key in ('project', 'pack_id', 'pack_name', 'pack_path', 'model_id', 'adapter_name', 'checkpoint_path', 'checkpoint_source', 'generation_stats'):
value = trace.get(key)
if value not in (None, '', [], {}, ()):
row[key] = value
return row
def materialize_dataset(
traces_dir: str | Path,
output_dir: str | Path | None = None,
repo_root: str | Path | None = None,
repo_name: str | None = None,
today: str | None = None,
) -> dict[str, Any]:
traces_dir = Path(traces_dir).expanduser()
repo_root = Path(repo_root).expanduser() if repo_root is not None else ROOT
repo_name = repo_name or repo_root.name
today = today or datetime.now(timezone.utc).date().isoformat()
output_dir = Path(output_dir).expanduser() if output_dir is not None else default_output_dir(repo_root, repo_name, today)
trace_files = discover_trace_files(traces_dir)
if not trace_files:
raise FileNotFoundError(f'No trace JSON files found under {traces_dir}')
rows = [build_dataset_row(trace_file, repo_root=repo_root, repo_name=repo_name) for trace_file in trace_files]
rows.sort(key=lambda row: row['source_trace_path'])
output_dir.mkdir(parents=True, exist_ok=True)
dataset_jsonl = output_dir / 'dataset.jsonl'
metadata_json = output_dir / 'dataset-metadata.json'
with dataset_jsonl.open('w', encoding='utf-8') as handle:
for row in rows:
handle.write(json.dumps(row, ensure_ascii=False, sort_keys=True) + '\n')
metadata = {
'repo_name': repo_name,
'repo_root': str(repo_root),
'source_traces_dir': str(traces_dir),
'source_trace_count': len(trace_files),
'records_written': len(trace_files),
'source_trace_files': [row['source_trace_path'] for row in rows],
'generated_date': today,
'schema_fields': SCHEMA_FIELDS,
'output_files': {
'dataset_jsonl': dataset_jsonl.name,
'metadata_json': metadata_json.name,
},
}
metadata_json.write_text(json.dumps(metadata, indent=2, ensure_ascii=False, sort_keys=True), encoding='utf-8')
return {
'repo_name': repo_name,
'repo_root': repo_root,
'source_traces_dir': traces_dir,
'trace_files': trace_files,
'source_trace_count': len(trace_files),
'records_written': len(trace_files),
'output_dir': output_dir,
'dataset_jsonl': dataset_jsonl,
'metadata_json': metadata_json,
}
def _run_git(args: list[str], cwd: Path) -> subprocess.CompletedProcess[str]:
proc = subprocess.run(['git', *args], cwd=cwd, text=True, capture_output=True)
if proc.returncode != 0:
output = (proc.stdout + '\n' + proc.stderr).strip()
raise RuntimeError(f"git {' '.join(args)} failed in {cwd}: {output}")
return proc
def build_hf_git_url(repo_id: str, token: str) -> str:
return f'https://__token__:{token}@huggingface.co/datasets/{repo_id}.git'
def git_push_dataset(output_dir: str | Path, remote_url: str, branch: str = 'main') -> dict[str, Any]:
output_dir = Path(output_dir).expanduser()
if not (output_dir / '.git').exists():
_run_git(['init', '-b', branch], cwd=output_dir)
_run_git(['config', 'user.name', 'Hermes Trace Sharer'], cwd=output_dir)
_run_git(['config', 'user.email', 'trace-sharer@example.com'], cwd=output_dir)
_run_git(['add', 'dataset.jsonl', 'dataset-metadata.json'], cwd=output_dir)
commit = subprocess.run(
['git', 'commit', '-m', 'Share traces to Hugging Face Dataset'],
cwd=output_dir,
text=True,
capture_output=True,
)
commit_output = (commit.stdout + '\n' + commit.stderr).strip().lower()
if commit.returncode != 0 and 'nothing to commit' not in commit_output:
raise RuntimeError(f'git commit failed in {output_dir}: {commit_output}')
push = subprocess.run(
['git', 'push', remote_url, f'HEAD:{branch}'],
cwd=output_dir,
text=True,
capture_output=True,
)
if push.returncode != 0:
output = (push.stdout + '\n' + push.stderr).strip()
raise RuntimeError(f'git push failed in {output_dir}: {output}')
return {
'pushed': True,
'branch': branch,
}
def push_to_hf_dataset(output_dir: str | Path, repo_id: str, token: str, branch: str = 'main') -> dict[str, Any]:
return git_push_dataset(output_dir, build_hf_git_url(repo_id, token), branch=branch)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description='Materialize and optionally share traces as a Hugging Face Dataset bundle')
parser.add_argument('traces_dir', nargs='?', default='.', help='Local trace directory or repo root to scan for trace JSON files')
parser.add_argument('--output-dir', default=None, help='Optional output directory for the materialized dataset bundle')
parser.add_argument('--repo-id', default=os.environ.get('HF_DATASET_REPO'), help='Hugging Face dataset repo id (namespace/name) for --push')
parser.add_argument('--branch', default='main', help='Target branch when pushing')
parser.add_argument('--push', action='store_true', help='Commit and push to Hugging Face when HF_TOKEN is present')
parser.add_argument('--dry-run', action='store_true', help='Force local-only materialization even if --push is supplied')
args = parser.parse_args(argv)
materialized = materialize_dataset(args.traces_dir, output_dir=args.output_dir, repo_root=ROOT, repo_name=ROOT.name)
push_status: dict[str, Any] = {
'attempted': False,
'pushed': False,
'reason': 'local materialization only',
}
if args.push and not args.dry_run:
repo_id = args.repo_id
token = os.environ.get('HF_TOKEN') or os.environ.get('HUGGINGFACE_HUB_TOKEN')
push_status['attempted'] = True
if not repo_id:
push_status['reason'] = 'HF dataset repo id missing; pass --repo-id or set HF_DATASET_REPO'
elif not token:
push_status['reason'] = 'HF_TOKEN missing; local materialization only'
else:
push_status = {
'attempted': True,
'pushed': True,
'repo_id': repo_id,
'branch': args.branch,
**push_to_hf_dataset(materialized['output_dir'], repo_id, token, branch=args.branch),
}
elif args.push and args.dry_run:
push_status = {
'attempted': False,
'pushed': False,
'reason': 'dry-run requested',
}
summary = {
'repo_name': materialized['repo_name'],
'records_written': materialized['source_trace_count'],
'output_dir': str(materialized['output_dir']),
'dataset_jsonl': str(materialized['dataset_jsonl']),
'metadata_json': str(materialized['metadata_json']),
'trace_files': [str(path) for path in materialized['trace_files']],
'push': push_status,
}
print(json.dumps(summary, indent=2, ensure_ascii=False, sort_keys=True))
return 0
if __name__ == '__main__':
raise SystemExit(main())