feat: build bento from an existing bento or a cpack file
Signed-off-by: Frost Ming <me@frostming.com>
This commit is contained in:
+8
-46
@@ -20,7 +20,7 @@ from server import PromptServer
|
||||
|
||||
from comfy_pack.hash import async_batch_get_sha256
|
||||
from comfy_pack.model_helper import alookup_model_source
|
||||
from comfy_pack.utils import get_self_git_commit
|
||||
from comfy_pack.package import build_bento
|
||||
|
||||
ZPath = Union[Path, zipfile.Path]
|
||||
TEMP_FOLDER = Path(__file__).parent.parent / "temp"
|
||||
@@ -248,10 +248,7 @@ async def pack_workspace(request):
|
||||
|
||||
with zipfile.ZipFile(TEMP_FOLDER / zip_filename, "w") as zf:
|
||||
path = zipfile.Path(zf)
|
||||
await _prepare_bento_project(
|
||||
path,
|
||||
data,
|
||||
)
|
||||
await _prepare_pack(path, data)
|
||||
|
||||
return web.json_response({"download_url": f"/bentoml/download/{zip_filename}"})
|
||||
|
||||
@@ -447,11 +444,11 @@ async def download_workspace(request):
|
||||
return web.FileResponse(TEMP_FOLDER / zip_filename)
|
||||
|
||||
|
||||
async def _prepare_bento_project(
|
||||
async def _prepare_pack(
|
||||
working_dir: ZPath,
|
||||
data: dict,
|
||||
store_models: bool = False,
|
||||
):
|
||||
) -> None:
|
||||
model_filter = set(data.get("models", []))
|
||||
models = await _get_models(
|
||||
store_models=store_models,
|
||||
@@ -462,20 +459,6 @@ async def _prepare_bento_project(
|
||||
await _write_snapshot(working_dir, data, models)
|
||||
await _write_workflow(working_dir, data)
|
||||
await _write_inputs(working_dir, data)
|
||||
with working_dir.joinpath("service.py").open("w") as f:
|
||||
f.write(Path(__file__).with_name("service.py").read_text())
|
||||
|
||||
# Copy comfy_pack directory
|
||||
if isinstance(working_dir, Path):
|
||||
shutil.copytree(COMFY_PACK_DIR, working_dir / COMFY_PACK_DIR.name)
|
||||
else: # zipfile.Path
|
||||
for src in COMFY_PACK_DIR.rglob("*"):
|
||||
if src.is_file():
|
||||
rel_path = src.relative_to(COMFY_PACK_DIR.parent)
|
||||
with working_dir.joinpath(rel_path).open("wb") as f:
|
||||
f.write(src.read_bytes())
|
||||
models = [m for m in models if not m["disabled"]] # filter out disabled models
|
||||
return models
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/bentoml/model/query")
|
||||
@@ -490,7 +473,7 @@ async def get_models(request):
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/bentoml/build")
|
||||
async def build_bento(request):
|
||||
async def build_bento_api(request):
|
||||
"""Request body: {
|
||||
workflow_api: dict,
|
||||
workflow: dict,
|
||||
@@ -509,33 +492,12 @@ async def build_bento(request):
|
||||
|
||||
with tempfile.TemporaryDirectory(suffix="-bento", prefix="comfy-pack-") as temp_dir:
|
||||
temp_dir_path = Path(temp_dir)
|
||||
models = await _prepare_bento_project(temp_dir_path, data, store_models=True)
|
||||
await _prepare_pack(temp_dir_path, data, store_models=True)
|
||||
|
||||
# create a bento
|
||||
try:
|
||||
bento = bentoml.build(
|
||||
"service:ComfyService",
|
||||
name=data["bento_name"],
|
||||
build_ctx=temp_dir,
|
||||
labels={"comfy-pack-version": get_self_git_commit() or "unknown"},
|
||||
models=[m["model_tag"] for m in models if "model_tag" in m],
|
||||
docker={
|
||||
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}",
|
||||
"system_packages": [
|
||||
"git",
|
||||
"libglib2.0-0",
|
||||
"libsm6",
|
||||
"libxrender1",
|
||||
"libxext6",
|
||||
"ffmpeg",
|
||||
"libstdc++-12-dev",
|
||||
*data.get("system_packages", []),
|
||||
],
|
||||
"setup_script": Path(__file__)
|
||||
.with_name("setup_workspace.py")
|
||||
.as_posix(),
|
||||
},
|
||||
python={"requirements_txt": "requirements.txt", "lock_packages": True},
|
||||
bento = build_bento(
|
||||
data["bento_name"], temp_dir_path, data.get("system_packages")
|
||||
)
|
||||
except bentoml.exceptions.BentoMLException as e:
|
||||
return web.json_response(
|
||||
|
||||
@@ -11,7 +11,6 @@ dependencies = [
|
||||
"bentoml>=1.3.13",
|
||||
"click>=8.1.7",
|
||||
"comfy-cli>=1.2.8",
|
||||
"pydantic>=2.9",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
|
||||
@@ -2,4 +2,3 @@ bentoml
|
||||
fastapi
|
||||
comfy-cli
|
||||
duckduckgo-search
|
||||
uv
|
||||
|
||||
+49
-7
@@ -1,12 +1,15 @@
|
||||
import click
|
||||
import functools
|
||||
import json
|
||||
from pathlib import Path
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from .const import WORKSPACE_DIR, COMFYUI_REPO, COMFY_PACK_REPO, COMFYUI_MANAGER_REPO
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
|
||||
from .const import COMFY_PACK_REPO, COMFYUI_MANAGER_REPO, COMFYUI_REPO, WORKSPACE_DIR
|
||||
from .hash import get_sha256
|
||||
from .utils import get_self_git_commit
|
||||
|
||||
@@ -50,9 +53,10 @@ def main():
|
||||
help="Increase verbosity level",
|
||||
)
|
||||
def init(dir: str, verbose: int):
|
||||
from rich.console import Console
|
||||
import os
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
|
||||
# Check if directory path is valid
|
||||
@@ -258,9 +262,10 @@ def init(dir: str, verbose: int):
|
||||
help="Increase verbosity level (use multiple times for more verbosity)",
|
||||
)
|
||||
def unpack_cmd(cpack: str, dir: str, include_disabled_models: bool, verbose: int):
|
||||
from .package import install
|
||||
from rich.console import Console
|
||||
|
||||
from .package import install
|
||||
|
||||
console = Console()
|
||||
|
||||
install(cpack, dir, verbose=verbose, all_models=include_disabled_models)
|
||||
@@ -276,8 +281,8 @@ def unpack_cmd(cpack: str, dir: str, include_disabled_models: bool, verbose: int
|
||||
|
||||
|
||||
def _print_schema(schema, verbose: int = 0):
|
||||
from rich.table import Table
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
table = Table(title="")
|
||||
|
||||
@@ -335,10 +340,11 @@ def _get_cache_workspace(cpack: str):
|
||||
)
|
||||
@click.pass_context
|
||||
def run(ctx, cpack: str, output_dir: str, help: bool, verbose: int):
|
||||
from .utils import generate_input_model
|
||||
from pydantic import ValidationError
|
||||
from rich.console import Console
|
||||
|
||||
from .utils import generate_input_model
|
||||
|
||||
inputs = dict(
|
||||
zip([k.lstrip("-").replace("-", "_") for k in ctx.args[::2]], ctx.args[1::2])
|
||||
)
|
||||
@@ -411,3 +417,39 @@ def run(ctx, cpack: str, output_dir: str, help: bool, verbose: int):
|
||||
console.print(f"{i}: {value}")
|
||||
else:
|
||||
console.print(results)
|
||||
|
||||
|
||||
@main.command(name="build-bento")
|
||||
@click.option("--name", help="Name of the bento service")
|
||||
@click.option("--version", help="Version of the bento service")
|
||||
@click.argument("source")
|
||||
def bento_cmd(source: str, name: str | None, version: str | None):
|
||||
"""Build a bento from the source, which can be either a .cpack.zip file or a bento tag."""
|
||||
import bentoml
|
||||
from bentoml.bentos import BentoBuildConfig
|
||||
|
||||
from .package import build_bento
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
if source.endswith(".cpack.zip"):
|
||||
name = name or os.path.basename(source).replace(".cpack.zip", "")
|
||||
shutil.unpack_archive(source, temp_dir)
|
||||
system_packages = None
|
||||
include_default_system_packages = True
|
||||
else:
|
||||
existing_bento = bentoml.get(source)
|
||||
name = name or existing_bento.tag.name
|
||||
shutil.copytree(existing_bento.path, temp_dir, dirs_exist_ok=True)
|
||||
build_config = BentoBuildConfig.from_bento_dir(
|
||||
existing_bento.path_of("src")
|
||||
)
|
||||
system_packages = build_config.docker.system_packages
|
||||
include_default_system_packages = False
|
||||
|
||||
build_bento(
|
||||
name,
|
||||
Path(temp_dir),
|
||||
version=version,
|
||||
system_packages=system_packages,
|
||||
include_default_system_packages=include_default_system_packages,
|
||||
)
|
||||
|
||||
@@ -3,15 +3,23 @@ from __future__ import annotations
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import urllib.parse
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from .hash import get_sha256
|
||||
from pathlib import Path
|
||||
from .const import MODEL_DIR, COMFYUI_REPO
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .const import COMFYUI_REPO, MODEL_DIR
|
||||
from .hash import get_sha256
|
||||
from .utils import get_self_git_commit
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bentoml
|
||||
|
||||
COMFY_PACK_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def _clone_commit(url: str, commit: str, dir: Path, verbose: int = 0):
|
||||
@@ -238,7 +246,7 @@ def create_model_symlink(global_path: Path, sha: str, target_path: Path, filenam
|
||||
os.symlink(source, target)
|
||||
|
||||
|
||||
def retrive_models(
|
||||
def retrieve_models(
|
||||
snapshot: dict,
|
||||
workspace: Path,
|
||||
download: bool = True,
|
||||
@@ -387,7 +395,7 @@ def install(
|
||||
elif f.is_dir():
|
||||
shutil.copytree(f, workspace / "input" / f.name, dirs_exist_ok=True)
|
||||
|
||||
retrive_models(
|
||||
retrieve_models(
|
||||
snapshot,
|
||||
workspace,
|
||||
verbose=verbose,
|
||||
@@ -405,4 +413,61 @@ def install(
|
||||
) as _:
|
||||
pass
|
||||
|
||||
retrive_models(snapshot, workspace, verbose=verbose, all_models=all_models)
|
||||
retrieve_models(snapshot, workspace, verbose=verbose, all_models=all_models)
|
||||
|
||||
|
||||
required_files = ["snapshot.json", "requirements.txt"]
|
||||
|
||||
|
||||
def build_bento(
|
||||
bento_name: str,
|
||||
source_dir: Path,
|
||||
*,
|
||||
version: str | None = None,
|
||||
system_packages: list[str] | None = None,
|
||||
include_default_system_packages: bool = True,
|
||||
) -> bentoml.Bento:
|
||||
import bentoml
|
||||
|
||||
for f in required_files:
|
||||
if not (source_dir / f).exists():
|
||||
raise FileNotFoundError(f"Not a valid comfy-pack package: missing `{f}`")
|
||||
|
||||
if include_default_system_packages:
|
||||
system_packages = [
|
||||
"git",
|
||||
"libglib2.0-0",
|
||||
"libsm6",
|
||||
"libxrender1",
|
||||
"libxext6",
|
||||
"ffmpeg",
|
||||
"libstdc++-12-dev",
|
||||
*(system_packages or []),
|
||||
]
|
||||
else:
|
||||
system_packages = system_packages or []
|
||||
|
||||
shutil.copy2(Path(__file__).with_name("service.py"), source_dir / "service.py")
|
||||
# Copy comfy-pack package
|
||||
shutil.copytree(
|
||||
COMFY_PACK_DIR, source_dir / COMFY_PACK_DIR.name, dirs_exist_ok=True
|
||||
)
|
||||
snapshot = json.loads((source_dir / "snapshot.json").read_text())
|
||||
return bentoml.build(
|
||||
"service:ComfyService",
|
||||
name=bento_name,
|
||||
version=version,
|
||||
build_ctx=str(source_dir),
|
||||
labels={"comfy-pack-version": get_self_git_commit() or "unknown"},
|
||||
models=[
|
||||
m["model_tag"]
|
||||
for m in snapshot["models"]
|
||||
if "model_tag" in m and not m.get("disabled", False)
|
||||
],
|
||||
docker={
|
||||
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}",
|
||||
"system_packages": system_packages,
|
||||
"setup_script": Path(__file__).with_name("setup_workspace.py").as_posix(),
|
||||
},
|
||||
python={"requirements_txt": "requirements.txt", "lock_packages": True},
|
||||
)
|
||||
|
||||
@@ -177,10 +177,8 @@ class ComfyService:
|
||||
)
|
||||
|
||||
|
||||
if not EXISTING_COMFYUI_SERVER:
|
||||
if False and not EXISTING_COMFYUI_SERVER:
|
||||
for model in snapshot["models"]:
|
||||
if True:
|
||||
continue
|
||||
if model.get("disabled"):
|
||||
continue
|
||||
source = model["source"]
|
||||
@@ -523,13 +523,12 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "comfy-pack"
|
||||
version = "0.1.4.dev10+g1d19c05"
|
||||
version = "0.2.2.dev7+g5238115.d20241231"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "bentoml" },
|
||||
{ name = "click" },
|
||||
{ name = "comfy-cli" },
|
||||
{ name = "pydantic" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
@@ -537,7 +536,6 @@ requires-dist = [
|
||||
{ name = "bentoml", specifier = ">=1.3.13" },
|
||||
{ name = "click", specifier = ">=8.1.7" },
|
||||
{ name = "comfy-cli", specifier = ">=1.2.8" },
|
||||
{ name = "pydantic", specifier = ">=2.9" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user