File size: 3,419 Bytes
a57b67c
 
 
 
 
 
53f4c20
a57b67c
 
9744c13
a57b67c
 
 
 
 
 
 
 
381de33
 
53f4c20
a57b67c
 
 
53f4c20
 
 
 
381de33
53f4c20
 
 
 
 
 
 
 
381de33
53f4c20
381de33
 
 
 
 
 
 
 
 
 
 
a57b67c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
"""Run real inference on an explicitly named deployed Space and verify its downloaded GLB/ZIP."""
import argparse
import json
import os
from pathlib import Path
import zipfile
import time

from gradio_client import Client, handle_file
from huggingface_hub import get_token
import trimesh


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--space', required=True, help='Hugging Face owner/space')
    parser.add_argument('--image', type=Path)
    parser.add_argument('--prompt', default='')
    parser.add_argument('--animation-prompt', default='')
    parser.add_argument('--animation-seconds', type=float, default=6)
    parser.add_argument('--output-dir', type=Path, default=Path('outputs/hf-verification'))
    args = parser.parse_args()
    if not args.image and not args.prompt.strip():
        parser.error('Supply --image, --prompt, or both')
    args.output_dir.mkdir(parents=True, exist_ok=True)
    client = Client(args.space, token=os.environ.get('HF_TOKEN') or get_token(),
                    download_files=args.output_dir.resolve())
    job = client.submit(handle_file(str(args.image)) if args.image else None,
                        args.prompt, 8, 0, args.animation_prompt, args.animation_seconds, api_name='/generate')
    previous = None
    while not job.done():
        update = job.status()
        current = (update.code.name, tuple(p.desc for p in update.progress_data or []))
        if current != previous:
            print(json.dumps({'state': current[0], 'progress': current[1]}), flush=True)
            previous = current
        time.sleep(2)
    model, reference, views, bundle, status, manifest, video = job.result()
    (args.output_dir / f"{manifest['id']}.json").write_text(json.dumps(manifest, indent=2))
    if args.animation_prompt:
        import struct
        raw = Path(model).read_bytes()
        size, kind = struct.unpack_from('<II', raw, 12)
        if kind != 0x4e4f534a:
            raise RuntimeError('Animated GLB has no JSON chunk')
        gltf = json.loads(raw[20:20 + size])
        if not gltf.get('skins') or not gltf.get('animations'):
            raise RuntimeError('Requested animation returned a static mesh')
        if not video or not manifest.get('animation', {}).get('accepted'):
            raise RuntimeError('Animation lacks video or failed its technical/visual review')
    if manifest['status'] != 'complete':
        raise RuntimeError('Space did not complete all stages')
    scene = trimesh.load(model, force='scene', process=False)
    if len(scene.geometry) != manifest['metrics']['components']:
        raise RuntimeError('Downloaded component count differs from manifest')
    for mesh in scene.geometry.values():
        if not len(mesh.faces) or mesh.visual.kind != 'texture' or mesh.visual.material.baseColorTexture is None:
            raise RuntimeError('Downloaded mesh has missing geometry or texture')
    with zipfile.ZipFile(bundle) as archive:
        embedded = json.loads(archive.read('manifest.json'))
        if embedded['id'] != manifest['id'] or 'final.glb' not in archive.namelist():
            raise RuntimeError('Download archive does not match the completed run')
    print(json.dumps({'verified': True, 'run': manifest['id'], 'model': model, 'bundle': bundle,
                      'components': len(scene.geometry)}, indent=2))


if __name__ == '__main__':
    main()