Aduc-sdr-2_5 / services /vincie.py
carlex3321's picture
Update services/vincie.py
4304413 verified
raw
history blame
5.5 kB
#!/usr/bin/env python3
import os, sys, json, subprocess
from pathlib import Path
from typing import List, Optional
from time import time, sleep
from huggingface_hub import hf_hub_download
class VincieService:
def __init__(
self,
repo_dir: str = "/app/VINCIE",
ckpt_dir: str = "/app/ckpt/VINCIE-3B",
python_bin: str = "python3",
repo_id: str = "ByteDance-Seed/VINCIE-3B",
):
self.repo_dir = Path(repo_dir)
self.ckpt_dir = Path(ckpt_dir)
self.python = python_bin
self.repo_id = repo_id
self.generate_yaml = self.repo_dir / "configs" / "generate.yaml"
self.output_root = Path("/app/outputs")
self.output_root.mkdir(parents=True, exist_ok=True)
(self.repo_dir / "ckpt").mkdir(parents=True, exist_ok=True)
def ensure_repo(self, git_url: str = "https://github.com/ByteDance-Seed/VINCIE") -> None:
if not self.repo_dir.exists():
subprocess.run(["git", "clone", "--depth", "1", git_url, str(self.repo_dir)], check=True)
def ensure_model(self, hf_token: Optional[str] = None) -> None:
self.ckpt_dir.mkdir(parents=True, exist_ok=True)
token = hf_token or os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_TOKEN")
def _need(p: Path) -> bool:
try:
return not (p.exists() and p.stat().st_size > 1_000_000)
except FileNotFoundError:
return True
for fname in ["dit.pth", "vae.pth"]:
dst = self.ckpt_dir / fname
if _need(dst):
print(f"[vince] downloading {fname} from {self.repo_id} ...")
hf_hub_download(
repo_id=self.repo_id,
filename=fname,
local_dir=str(self.ckpt_dir),
token=token,
force_download=False,
local_files_only=False,
)
link = self.repo_dir / "ckpt" / "VINCIE-3B"
try:
if link.is_symlink() or link.exists():
try:
link.unlink()
except IsADirectoryError:
pass
if not link.exists():
link.symlink_to(self.ckpt_dir, target_is_directory=True)
except Exception as e:
print("[vince] symlink warning:", e)
def ready(self) -> bool:
have_repo = self.repo_dir.exists() and self.generate_yaml.exists()
dit_ok = (self.ckpt_dir / "dit.pth").exists()
vae_ok = (self.ckpt_dir / "vae.pth").exists()
return bool(have_repo and dit_ok and vae_ok)
def _wait_until_outputs(self, out_dir: Path, timeout_s: int = 300) -> None:
exts = (".png", ".jpg", ".jpeg", ".gif", ".mp4")
deadline = time() + timeout_s
while time() < deadline:
if any(p.is_file() and p.suffix.lower() in exts for p in out_dir.rglob("*")):
print(f"[vince] outputs detected in {out_dir}")
return
sleep(1)
print(f"[vince] warning: no outputs detected in {out_dir} within {timeout_s}s")
def _run_vincie(self, overrides: List[str], work_output: Path, wait_outputs: bool = True) -> None:
work_output.mkdir(parents=True, exist_ok=True)
cmd = [
self.python,
"main.py",
str(self.generate_yaml),
*overrides,
f"generation.output.dir={str(work_output)}",
]
print("[vince] CWD=", self.repo_dir)
print("[vince] CMD=", " ".join(cmd))
subprocess.run(cmd, cwd=self.repo_dir, check=True, env=os.environ.copy())
if wait_outputs:
self._wait_until_outputs(work_output, timeout_s=int(os.getenv("VINCIE_WAIT_OUTPUTS_SEC", "300")))
def multi_turn_edit(self, input_image: str, turns: List[str], **kwargs) -> Path:
out_dir = self.output_root / f"multi_turn_{Path(input_image).stem}"
overrides = [
f'generation.positive_prompt.image_path="{str(input_image)}"',
f"generation.positive_prompt.prompts={json.dumps(turns)}",
f"generation.seed={int(kwargs.get('seed', 1))}",
f"diffusion.timesteps.sampling.steps={int(kwargs.get('steps', 50))}",
f"diffusion.cfg.scale={float(kwargs.get('cfg_scale', 7.5))}",
f'generation.negative_prompt="{kwargs.get("negative_prompt","")}"',
f"generation.resolution={int(kwargs.get('resolution', 512))}",
f"generation.batch_size={int(kwargs.get('batch_size', 1))}",
]
self._run_vincie(overrides, out_dir, wait_outputs=True)
return out_dir
def multi_concept_compose(self, files: List[str], descs: List[str], final_prompt: str, **kwargs) -> Path:
out_dir = self.output_root / f"multi_concept_{len(files)}"
overrides = [
f"generation.concepts.files={json.dumps(files)}",
f"generation.concepts.descs={json.dumps(descs)}",
f'generation.final_prompt="{final_prompt}"',
f"generation.seed={int(kwargs.get('seed', 1))}",
f"diffusion.timesteps.sampling.steps={int(kwargs.get('steps', 50))}",
f"diffusion.cfg.scale={float(kwargs.get('cfg_scale', 7.5))}",
f"generation.resolution={int(kwargs.get('resolution', 512))}",
f"generation.batch_size={int(kwargs.get('batch_size', 1))}",
]
self._run_vincie(overrides, out_dir, wait_outputs=True)
return out_dir