"""Publisher: upload reviewed staging folders, then atomically publish pair lock. Authentication: use `hf auth login`; never put a token into this file. Each folder must contain a complete MANIFEST.json produced for this release. """ import argparse import json from pathlib import Path import tempfile from huggingface_hub import HfApi from sync_models import read,verify,sha REPOS={'asr':'humoiww/PhonoCommand-ASR','llm':'humoiww/PhonoCommand-LLM-0.8B'} def publish(asr,llm,release_id,record): api=HfApi();lock={'schema':1,'release_id':release_id,'components':{}} for component,folder in [('asr',Path(asr)),('llm',Path(llm))]: manifest=read(folder/'MANIFEST.json') if manifest['release_id']!=release_id:raise ValueError('Release ID mismatch') verify(folder,manifest['files']) repo=REPOS[component] api.create_repo(repo_id=repo,repo_type='model',private=False,exist_ok=True) if api.model_info(repo).private:raise ValueError('Expected public repository: '+repo) commit=api.upload_folder(repo_id=repo,folder_path=folder, allow_patterns=[*manifest['files'],'MANIFEST.json'], commit_message='Publish '+release_id+' '+component) lock['components'][component]={'repo_id':repo,'revision':commit.oid, 'manifest_sha256':sha(folder/'MANIFEST.json'), 'files':[*manifest['files'],'MANIFEST.json']} print(component,commit.oid,flush=True) # One pointer file changes only after both immutable component uploads pass. with tempfile.TemporaryDirectory() as temporary: path=Path(temporary)/'release_set.json';path.write_text(json.dumps(lock,indent=2)+'\n') commit=api.upload_file(repo_id=REPOS['asr'],path_or_fileobj=path, path_in_repo='release_set.json',commit_message='Activate paired release '+release_id) record=Path(record);record.parent.mkdir(parents=True,exist_ok=True) record.write_text(json.dumps({'lock':lock,'pointer_commit':commit.oid},indent=2)+'\n') print('Published pair',release_id,flush=True) return lock if __name__=='__main__': p=argparse.ArgumentParser();p.add_argument('--asr',type=Path,required=True) p.add_argument('--llm',type=Path,required=True);p.add_argument('--release-id',required=True) p.add_argument('--record',type=Path,required=True) a=p.parse_args();publish(a.asr,a.llm,a.release_id,a.record)