posttrain-arena / dev /run_readonly.py
Xiangyi Li
Run page backend: full-text search over a run's traces, token records, prompts and reports; a dev-only editor flag for local viewing.
ac8cc14
Raw History Blame Contribute Delete
3.29 kB
"""Dev only, not served by the Space: run the app locally against live HF data with every write path disabled.
HF_TOKEN=... python dev/run_readonly.py [port] [--as-editor]
HfApi write methods and non-GET httpx calls raise, so a local page load cannot upload, reconcile the
budget ledger, launch or cancel jobs, or touch the live Space.
--as-editor makes every GET and HEAD request to this local process act as a signed-in BenchFlow editor, so the dashboard
can open transcripts, token views and search without an OAuth sign-in. It patches auth.principal in this process only:
the Space runs app.py, which never imports this file, so its access rules are unchanged. Other methods keep the real check.
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import os
import httpx
import huggingface_hub
from huggingface_hub import HfApi
# Job logs, costs and stop reasons need a token; without one the page loads but those fields go quietly empty.
if not os.environ.get('HF_TOKEN'):
stored = huggingface_hub.get_token()
if not stored: sys.exit('No HF token: set HF_TOKEN or run `hf auth login`; job logs, costs and stop reasons need it.')
os.environ['HF_TOKEN'] = stored
print('Using the locally stored HF token (read-only: every write path below is disabled).')
WRITES = ('upload_file', 'upload_folder', 'upload_large_folder', 'create_commit', 'create_repo', 'delete_repo', 'delete_file', 'delete_folder',
'create_branch', 'delete_branch', 'create_tag', 'delete_tag', 'super_squash_history', 'update_repo_settings', 'move_repo',
'run_job', 'run_uv_job', 'cancel_job', 'create_scheduled_job', 'delete_scheduled_job', 'restart_space', 'pause_space',
'add_space_secret', 'delete_space_secret', 'add_space_variable', 'delete_space_variable', 'duplicate_space', 'request_space_hardware')
def blocked(name):
def refuse(*args, **kwargs): raise RuntimeError(f'dev read-only guard: {name} is disabled')
return refuse
for name in WRITES:
if hasattr(HfApi, name): setattr(HfApi, name, blocked('HfApi.' + name))
if hasattr(huggingface_hub, name): setattr(huggingface_hub, name, blocked('huggingface_hub.' + name))
for name in ('post', 'put', 'patch', 'delete'): setattr(httpx, name, blocked('httpx.' + name))
_stream = httpx.stream
def get_stream(method, *args, **kwargs):
if method.upper() != 'GET': raise RuntimeError('dev read-only guard: httpx.stream ' + method)
return _stream(method, *args, **kwargs)
httpx.stream = get_stream
import uvicorn
import app # noqa: E402 (imported after the guard on purpose)
import collab
collab.system_post = blocked('collab.system_post')
AS_EDITOR = '--as-editor' in sys.argv
if AS_EDITOR:
sys.argv.remove('--as-editor')
import auth
_principal = auth.principal
def principal(request):
if request.method in ('GET', 'HEAD'): return {'name': 'local-dev', 'orgs': [{'name': 'benchflow', 'roleInOrg': 'write'}]}
return _principal(request)
auth.principal = principal
print('--as-editor: local GET requests act as a BenchFlow editor (this process only).')
if __name__ == '__main__':
uvicorn.run(app.app, host='127.0.0.1', port=int(sys.argv[1]) if len(sys.argv) > 1 else 7861, access_log=False)