PhonoCommand-ASR / sync_models.py
humoiww's picture
Publish 20261023-native-r1 asr
f75155d verified
Raw History Blame Contribute Delete
2.97 kB
"""Download a pinned release pair. Native Windows; never changes app files."""
import argparse
import hashlib
import json
import os
from pathlib import Path
from huggingface_hub import hf_hub_download,snapshot_download
ASR_REPO='humoiww/PhonoCommand-ASR'
def read(path):return json.loads(Path(path).read_text(encoding='utf-8'))
def sha(path):
h=hashlib.sha256()
with Path(path).open('rb') as f:
for block in iter(lambda:f.read(8*1024*1024),b''):h.update(block)
return h.hexdigest()
def verify(directory,files):
for name,info in files.items():
relative=Path(name)
if relative.is_absolute() or '..' in relative.parts:raise ValueError('Unsafe manifest path')
path=Path(directory)/relative
if path.stat().st_size!=info['size'] or sha(path)!=info['sha256']:
raise ValueError('Checksum mismatch: '+name)
def sync(output,release_lock=None,with_lid=False):
lock=read(release_lock or hf_hub_download(ASR_REPO,'release_set.json',revision='main'))
output=Path(output).resolve();output.mkdir(parents=True,exist_ok=True)
release_dir=output/'releases'/lock['release_id'];release_dir.mkdir(parents=True,exist_ok=True)
paths={}
for component,item in lock['components'].items():
if len(item['revision'])!=40:raise ValueError('Expected immutable component commit')
directory=release_dir/component/item['revision']
snapshot_download(item['repo_id'],revision=item['revision'],local_dir=directory,
allow_patterns=item['files'])
manifest_path=directory/'MANIFEST.json'
if sha(manifest_path)!=item['manifest_sha256']:raise ValueError('Release manifest mismatch')
verify(directory,read(manifest_path)['files'])
paths[component]=str(directory)
upstream=read(Path(paths['asr'])/'upstream_lock.json')
for key,item in upstream.items():
if key=='sensevoice' and not with_lid:continue
directory=output/'upstream'/key/item['revision']
snapshot_download(item['repo_id'],revision=item['revision'],local_dir=directory,
allow_patterns=list(item['files']))
verify(directory,item['files']);paths[key]=str(directory)
state={'release_id':lock['release_id'],'lock':lock,'paths':paths,
'includes_sensevoice':with_lid,'app_reload_required':True}
(release_dir/'release_set.json').write_text(json.dumps(lock,indent=2)+'\n',encoding='utf-8')
target=output/'active_release.json';temporary=output/'active_release.json.tmp'
temporary.write_text(json.dumps(state,indent=2)+'\n',encoding='utf-8')
os.replace(temporary,target)
print(target)
return state
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('--output',type=Path,required=True)
p.add_argument('--release-lock',type=Path);p.add_argument('--with-lid',action='store_true')
args=p.parse_args();sync(args.output,args.release_lock,args.with_lid)