mantrakp's picture
Deploy pinned isolated SkinTokens worker
32127cb verified
Raw History Blame Contribute Delete
4.32 kB
"""Strict SkinTokens worker: prewarmed model, separate Blender process, preserved input."""
from pathlib import Path
import hashlib
import json
import shutil
import uuid
import time
import traceback
import spaces
import gradio as gr
import torch
from prepare import prepare, LOCK, ROOT
from preservation import verify
prepare()
from src.data.dataset import DatasetConfig, RigDatasetModule # noqa: E402
from src.data.transform import Transform # noqa: E402
from src.tokenizer.parse import get_tokenizer # noqa: E402
from src.server.spec import BpyClient, bpy_server_request, get_model # noqa: E402
# Blender CPU imports and all CUDA model placement happen before a user books a GPU.
BPY = BpyClient.launch()
MODEL = get_model(LOCK['checkpoint']).eval()
TOKENIZER = get_tokenizer(**MODEL.tokenizer_config)
TRANSFORM = Transform.parse(**MODEL.transform_config['predict_transform'])
def sha256(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
@spaces.GPU(duration=240)
def predict(source):
config = DatasetConfig.parse(
shuffle=False, batch_size=1, num_workers=0, pin_memory=False, persistent_workers=False,
datapath={'data_name': None, 'loader': 'bpy_server', 'filepaths': {'articulation': [str(source)]}},
).split_by_cls()
module = RigDatasetModule(predict_dataset_config=config, predict_transform=TRANSFORM,
tokenizer=TOKENIZER, process_fn=MODEL._process_fn)
batch = next(iter(module.predict_dataloader()['articulation']))
batch = {key: value.to(MODEL.device) if isinstance(value, torch.Tensor) else value
for key, value in batch.items()}
batch.pop('skeleton_tokens', None)
batch.pop('skeleton_mask', None)
batch['generate_kwargs'] = dict(max_length=2048, top_k=5, top_p=.95, temperature=1.0,
repetition_penalty=2.0, num_return_sequences=1, num_beams=10, do_sample=True)
with torch.inference_mode():
asset = MODEL.predict_step(batch, skeleton_tokens=None, make_asset=True)['results'][0].asset
if asset is None or asset.skin is None:
raise RuntimeError('SkinTokens produced no skinned asset')
# Asset stores NumPy data; no CUDA tensors leave this worker.
return asset
def rig(source):
global BPY
output = ROOT / 'outputs' / ('rig-' + uuid.uuid4().hex)
output.mkdir(parents=True)
original = output / 'source.glb'
shutil.copyfile(source, original)
receipt = {'provider': 'native SkinTokens', 'lock': LOCK, 'source_sha256': sha256(original),
'started': time.time(), 'stage': 'load', 'status': 'running'}
try:
# Surface a Blender/input failure before requesting the GPU.
if not BPY.is_alive():
BPY = BpyClient.launch()
bpy_server_request('load', str(original))
receipt['stage'] = 'predict'
asset = predict(original)
receipt['stage'] = 'transfer'
result = output / 'rigged.glb'
transfer = bpy_server_request('transfer', dict(source_asset=asset, target_path=str(original),
export_path=str(result), group_per_vertex=4))
if transfer != 'ok':
raise RuntimeError(f'Blender transfer failed: {transfer}')
receipt['stage'] = 'verify'
receipt['preservation'] = verify(original, result)
receipt.update(status='complete', output_sha256=sha256(result), finished=time.time())
return str(result), receipt
except Exception as error:
traceback.print_exc()
message = f"{receipt['stage']}: {type(error).__name__}: {error}"
receipt.update(status='failed', error=message, finished=time.time())
raise gr.Error(message) from error
finally:
(output / 'provenance.json').write_text(json.dumps(receipt, indent=2))
with gr.Blocks(title='Component Studio SkinTokens') as demo:
source = gr.File(file_types=['.glb'], type='filepath', label='Textured GLB')
button = gr.Button('Rig character')
result = gr.File(label='Rigged GLB')
report = gr.JSON(label='Preservation and provenance')
button.click(rig, source, [result, report], api_name='rig', concurrency_limit=1)
demo.queue(max_size=4).launch(server_name='0.0.0.0', server_port=7860,
allowed_paths=[str(ROOT / 'outputs')])