Refactor AI Host to Backend Service and update related components
- Renamed references from "AI Host" to "Backend Service" throughout the codebase for consistency. - Updated the registration and unregistration of context menus to include the Outliner. - Changed the job protocol versioning and related constants for clarity. - Modified the runtime installation messages and error handling to reflect the new terminology. - Adjusted the user interface labels and messages in panels and operators to align with the new Backend Service terminology. - Updated tests to ensure they reflect the changes in naming and functionality. - Changed default value for background depth in geometry nodes asset to -1.0 for better handling of depth planes.
This commit is contained in:
@@ -60,6 +60,7 @@ def register():
|
||||
)
|
||||
bpy.types.STATUSBAR_HT_header.append(draw_status_bar)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.append(draw_image_context_menu)
|
||||
bpy.types.OUTLINER_MT_object.append(draw_image_context_menu)
|
||||
runtime.schedule_host_start()
|
||||
|
||||
|
||||
@@ -69,6 +70,7 @@ def unregister():
|
||||
runtime.unschedule_host()
|
||||
runtime.detach_host_client()
|
||||
bpy.types.VIEW3D_MT_object_context_menu.remove(draw_image_context_menu)
|
||||
bpy.types.OUTLINER_MT_object.remove(draw_image_context_menu)
|
||||
bpy.types.STATUSBAR_HT_header.remove(draw_status_bar)
|
||||
if hasattr(bpy.types.Scene, "snapform_settings"):
|
||||
del bpy.types.Scene.snapform_settings
|
||||
|
||||
@@ -19,7 +19,7 @@ if str(SCRIPT_DIRECTORY) not in sys.path:
|
||||
import worker
|
||||
|
||||
|
||||
HOST_SCHEMA = 2
|
||||
JOB_PROTOCOL_VERSION = 2
|
||||
CLIENT_TIMEOUT_SECONDS = 10.0
|
||||
IDLE_SHUTDOWN_SECONDS = 20.0
|
||||
|
||||
@@ -212,7 +212,7 @@ class HostState:
|
||||
with self.lock:
|
||||
return {
|
||||
"ready": True,
|
||||
"host_schema": HOST_SCHEMA,
|
||||
"job_protocol_version": JOB_PROTOCOL_VERSION,
|
||||
"instance_id": self.instance_id,
|
||||
"busy": self.active_job is not None,
|
||||
"active_job": dict(self.active_job) if self.active_job else None,
|
||||
@@ -265,7 +265,7 @@ class HostState:
|
||||
worker.write_status(
|
||||
status_path,
|
||||
0.0,
|
||||
"AI Host accepted the task",
|
||||
"Backend Service accepted the task",
|
||||
state="running",
|
||||
)
|
||||
try:
|
||||
@@ -372,7 +372,7 @@ def create_app(token, instance_id="test"):
|
||||
state.close()
|
||||
|
||||
app = FastAPI(
|
||||
title="SnapForm AI Host",
|
||||
title="SnapForm Backend Service",
|
||||
docs_url=None,
|
||||
redoc_url=None,
|
||||
openapi_url=None,
|
||||
@@ -445,7 +445,7 @@ def create_app(token, instance_id="test"):
|
||||
command,
|
||||
parameters["status"],
|
||||
):
|
||||
raise HTTPException(status_code=409, detail="AI Host is busy")
|
||||
raise HTTPException(status_code=409, detail="Backend Service is busy")
|
||||
worker.write_status(
|
||||
parameters["status"],
|
||||
0.0,
|
||||
@@ -485,7 +485,7 @@ def create_app(token, instance_id="test"):
|
||||
def clear_models(x_snapform_token: str = Header(default="")):
|
||||
authorize(x_snapform_token)
|
||||
if not state.clear_models():
|
||||
raise HTTPException(status_code=409, detail="AI Host is busy")
|
||||
raise HTTPException(status_code=409, detail="Backend Service is busy")
|
||||
return {"state": "cleared"}
|
||||
|
||||
@app.post("/shutdown", status_code=202)
|
||||
@@ -498,7 +498,7 @@ def create_app(token, instance_id="test"):
|
||||
if result == "shared":
|
||||
raise HTTPException(status_code=409, detail="Other Blender clients are connected")
|
||||
if result == "busy":
|
||||
raise HTTPException(status_code=409, detail="AI Host is busy")
|
||||
raise HTTPException(status_code=409, detail="Backend Service is busy")
|
||||
if result != "accepted":
|
||||
raise HTTPException(status_code=404, detail="Client is not registered")
|
||||
return {"state": "stopping"}
|
||||
@@ -524,7 +524,7 @@ def write_discovery(args):
|
||||
temporary.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"host_schema": HOST_SCHEMA,
|
||||
"job_protocol_version": JOB_PROTOCOL_VERSION,
|
||||
"port": args.port,
|
||||
"token": args.token,
|
||||
"pid": os.getpid(),
|
||||
|
||||
Binary file not shown.
@@ -12,7 +12,7 @@ platforms = ["windows-x64", "macos-arm64", "linux-x64"]
|
||||
license = ["SPDX:GPL-3.0-or-later"]
|
||||
|
||||
[permissions]
|
||||
network = "Access the local AI Host and download runtime dependencies and model weights"
|
||||
network = "Access the local Backend Service and download environment dependencies and model weights"
|
||||
files = "Read source images and store runtime data"
|
||||
|
||||
[build]
|
||||
|
||||
@@ -34,7 +34,8 @@ XFORMERS_VERSION = "0.0.33.post2"
|
||||
ADDICT_VERSION = "2.4.0"
|
||||
FASTAPI_VERSION = "0.139.2"
|
||||
UVICORN_VERSION = "0.51.0"
|
||||
RUNTIME_SCHEMA = 4
|
||||
ENVIRONMENT_MANIFEST_REVISION = 4
|
||||
ENVIRONMENT_MANIFEST_FILENAME = "environment_manifest.json"
|
||||
TORCH_INDEXES = {
|
||||
"cu128": "https://download.pytorch.org/whl/cu128",
|
||||
"cpu": "https://download.pytorch.org/whl/cpu",
|
||||
@@ -429,14 +430,17 @@ def install_stages(args, runtime_dir, python, uv):
|
||||
|
||||
|
||||
def runtime_ready(runtime_dir):
|
||||
ready_path = runtime_dir / "snapform_ready.json"
|
||||
ready_path = runtime_dir / ENVIRONMENT_MANIFEST_FILENAME
|
||||
if not environment_python(runtime_dir).is_file() or not ready_path.is_file():
|
||||
return False
|
||||
try:
|
||||
payload = json.loads(ready_path.read_text(encoding="utf-8"))
|
||||
except (json.JSONDecodeError, OSError):
|
||||
return False
|
||||
return payload.get("runtime_schema") == RUNTIME_SCHEMA
|
||||
return (
|
||||
payload.get("environment_manifest_revision")
|
||||
== ENVIRONMENT_MANIFEST_REVISION
|
||||
)
|
||||
|
||||
|
||||
def installed_devices(args):
|
||||
@@ -505,7 +509,7 @@ def run_verify(
|
||||
write_status(
|
||||
status_path,
|
||||
progress,
|
||||
"Checking Runtime",
|
||||
"Checking Backend Environment",
|
||||
stage=stage,
|
||||
stages=stages,
|
||||
stage_label=stage_label,
|
||||
@@ -777,11 +781,13 @@ def install(args):
|
||||
installed_packages.extend(stage_installed)
|
||||
devices = installed_devices(args)
|
||||
runtime_dir.mkdir(parents=True, exist_ok=True)
|
||||
ready_path = runtime_dir / "snapform_ready.json"
|
||||
ready_path = runtime_dir / ENVIRONMENT_MANIFEST_FILENAME
|
||||
ready_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"runtime_schema": RUNTIME_SCHEMA,
|
||||
"environment_manifest_revision": (
|
||||
ENVIRONMENT_MANIFEST_REVISION
|
||||
),
|
||||
"python": "3.12",
|
||||
"torch": TORCH_VERSION,
|
||||
"xformers": XFORMERS_VERSION,
|
||||
@@ -828,10 +834,14 @@ def install(args):
|
||||
write_status(
|
||||
args.status,
|
||||
1.0,
|
||||
f"Runtime is ready; retry {labels} from Models",
|
||||
f"Backend Environment is ready; retry {labels} from Models",
|
||||
)
|
||||
return
|
||||
write_status(args.status, 1.0, "Runtime and recommended models ready")
|
||||
write_status(
|
||||
args.status,
|
||||
1.0,
|
||||
"Backend Environment and recommended models ready",
|
||||
)
|
||||
|
||||
|
||||
def parse_args():
|
||||
@@ -852,7 +862,12 @@ def main():
|
||||
try:
|
||||
install(args)
|
||||
except Exception as error:
|
||||
write_status(args.status, 1.0, "Runtime installation failed", str(error))
|
||||
write_status(
|
||||
args.status,
|
||||
1.0,
|
||||
"Backend Environment installation failed",
|
||||
str(error),
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@ class ConvertImageToDepthPlane(bpy.types.Operator):
|
||||
available_models = image_depth_model_items(self, context)
|
||||
if not available_models:
|
||||
estimate_box.label(text="No DA3 models installed", icon="ERROR")
|
||||
estimate_box.label(text="Download one in Host > Models")
|
||||
estimate_box.label(text="Download one in AI Backend > Models")
|
||||
else:
|
||||
estimate_box.prop(self, "snapform_model")
|
||||
estimate_box.prop(self, "snapform_use_ray_pose")
|
||||
@@ -1917,8 +1917,18 @@ class SnapFormImageMenu(bpy.types.Menu):
|
||||
bl_label = "SnapForm"
|
||||
|
||||
def draw(self, _context):
|
||||
from .runtime import runtime_ready
|
||||
|
||||
layout = self.layout
|
||||
layout.operator_context = "INVOKE_DEFAULT"
|
||||
if not runtime_ready():
|
||||
operator = layout.operator(
|
||||
"snapform.run_job",
|
||||
text="Install Backend",
|
||||
icon="IMPORT",
|
||||
)
|
||||
operator.job_type = "INSTALL_RUNTIME"
|
||||
return
|
||||
layout.operator(
|
||||
"snapform.convert_image_to_depth_plane",
|
||||
text="Convert to Depth Plane",
|
||||
|
||||
@@ -22,6 +22,22 @@ BACKGROUND_REMOVAL_JOB = "REMOVE_BACKGROUND"
|
||||
IMAGE_BACKGROUND_JOB = "IMAGE_BACKGROUND"
|
||||
ORIGINAL_BACKGROUND_JOB = "ORIGINAL_BACKGROUND"
|
||||
ORIGINAL_FRAMES_JOB = "ORIGINAL_FRAMES"
|
||||
DA3_IMAGE_JOBS = frozenset({IMAGE_DEPTH_JOB, IMAGE_MESH_JOB})
|
||||
IMAGE_EMPTY_JOBS = frozenset(
|
||||
{
|
||||
*DA3_IMAGE_JOBS,
|
||||
IMAGE_BACKGROUND_JOB,
|
||||
ORIGINAL_BACKGROUND_JOB,
|
||||
ORIGINAL_FRAMES_JOB,
|
||||
}
|
||||
)
|
||||
PRODUCTION_AI_JOBS = frozenset(
|
||||
{
|
||||
*DA3_IMAGE_JOBS,
|
||||
IMAGE_BACKGROUND_JOB,
|
||||
ORIGINAL_BACKGROUND_JOB,
|
||||
}
|
||||
)
|
||||
IMAGE_DEPTH_DEFAULT_PROCESS_RES = 1024
|
||||
IMAGE_DEPTH_PROCESS_RES_METHOD = "upper_bound_resize"
|
||||
BEN2_IMAGE_SUFFIXES = {".jpg", ".jpeg", ".png", ".webp"}
|
||||
@@ -134,7 +150,7 @@ class RunJob(bpy.types.Operator):
|
||||
|
||||
job_type: bpy.props.EnumProperty(
|
||||
items=(
|
||||
("INSTALL_RUNTIME", "Install Runtime", ""),
|
||||
("INSTALL_RUNTIME", "Install Backend", ""),
|
||||
("DOWNLOAD_MODEL", "Download Model", ""),
|
||||
(RECONSTRUCTION_JOB, "Export DA3 Result", ""),
|
||||
(IMAGE_DEPTH_JOB, "Convert Image to Depth Plane", ""),
|
||||
@@ -242,13 +258,7 @@ class RunJob(bpy.types.Operator):
|
||||
),
|
||||
temporary_input_path=(
|
||||
Path(self.snapform_input_path)
|
||||
if self.job_type in {
|
||||
IMAGE_DEPTH_JOB,
|
||||
IMAGE_MESH_JOB,
|
||||
IMAGE_BACKGROUND_JOB,
|
||||
ORIGINAL_BACKGROUND_JOB,
|
||||
ORIGINAL_FRAMES_JOB,
|
||||
}
|
||||
if self.job_type in IMAGE_EMPTY_JOBS
|
||||
and self.snapform_delete_input
|
||||
else None
|
||||
),
|
||||
@@ -311,13 +321,7 @@ class RunJob(bpy.types.Operator):
|
||||
paths = runtime.new_job_paths(self.job_type)
|
||||
if (
|
||||
getattr(self, "snapform_delete_input", False)
|
||||
and self.job_type in {
|
||||
IMAGE_DEPTH_JOB,
|
||||
IMAGE_MESH_JOB,
|
||||
IMAGE_BACKGROUND_JOB,
|
||||
ORIGINAL_BACKGROUND_JOB,
|
||||
ORIGINAL_FRAMES_JOB,
|
||||
}
|
||||
and self.job_type in IMAGE_EMPTY_JOBS
|
||||
):
|
||||
archived_input = runtime.move_input_into_job(
|
||||
self.snapform_input_path,
|
||||
@@ -333,7 +337,7 @@ class RunJob(bpy.types.Operator):
|
||||
return runtime.install_runtime_command(paths["status"]), paths
|
||||
|
||||
if not runtime.runtime_ready():
|
||||
raise RuntimeError("Install the SnapForm Runtime first")
|
||||
raise RuntimeError("Install the SnapForm Backend first")
|
||||
|
||||
if self.job_type == "DOWNLOAD_MODEL":
|
||||
self._require_online_access()
|
||||
@@ -343,13 +347,7 @@ class RunJob(bpy.types.Operator):
|
||||
paths,
|
||||
)
|
||||
|
||||
if self.job_type in {
|
||||
IMAGE_DEPTH_JOB,
|
||||
IMAGE_MESH_JOB,
|
||||
IMAGE_BACKGROUND_JOB,
|
||||
ORIGINAL_BACKGROUND_JOB,
|
||||
ORIGINAL_FRAMES_JOB,
|
||||
}:
|
||||
if self.job_type in IMAGE_EMPTY_JOBS:
|
||||
configured_input = self.snapform_input_path
|
||||
elif self.job_type == BACKGROUND_REMOVAL_JOB:
|
||||
configured_input = settings.background_input_path
|
||||
@@ -413,7 +411,7 @@ class RunJob(bpy.types.Operator):
|
||||
use_ray_pose = None
|
||||
process_res = None
|
||||
model_key = selected_model_key(settings, export_format)
|
||||
if self.job_type in {IMAGE_DEPTH_JOB, IMAGE_MESH_JOB}:
|
||||
if self.job_type in DA3_IMAGE_JOBS:
|
||||
model_key = self.snapform_model_key
|
||||
use_ray_pose = self.snapform_use_ray_pose
|
||||
process_res = self.snapform_process_res
|
||||
@@ -421,7 +419,7 @@ class RunJob(bpy.types.Operator):
|
||||
if not runtime.model_ready(model_key):
|
||||
raise RuntimeError(f"Download {model.label} first")
|
||||
if (
|
||||
self.job_type in {IMAGE_DEPTH_JOB, IMAGE_MESH_JOB}
|
||||
self.job_type in DA3_IMAGE_JOBS
|
||||
and remove_background
|
||||
and not runtime.model_ready(BEN2_MODEL.key)
|
||||
):
|
||||
@@ -441,13 +439,13 @@ class RunJob(bpy.types.Operator):
|
||||
process_res=process_res,
|
||||
process_res_method=(
|
||||
IMAGE_DEPTH_PROCESS_RES_METHOD
|
||||
if self.job_type in {IMAGE_DEPTH_JOB, IMAGE_MESH_JOB}
|
||||
if self.job_type in DA3_IMAGE_JOBS
|
||||
else None
|
||||
),
|
||||
use_ray_pose=use_ray_pose,
|
||||
start_frame=start_frame,
|
||||
remove_background=(
|
||||
self.job_type in {IMAGE_DEPTH_JOB, IMAGE_MESH_JOB}
|
||||
self.job_type in DA3_IMAGE_JOBS
|
||||
and remove_background
|
||||
),
|
||||
device=self._task_device(settings),
|
||||
@@ -457,12 +455,7 @@ class RunJob(bpy.types.Operator):
|
||||
|
||||
def _task_device(self, settings):
|
||||
"""Debug panels use the scene device; production tasks use Preferences."""
|
||||
if self.job_type in {
|
||||
IMAGE_DEPTH_JOB,
|
||||
IMAGE_MESH_JOB,
|
||||
IMAGE_BACKGROUND_JOB,
|
||||
ORIGINAL_BACKGROUND_JOB,
|
||||
}:
|
||||
if self.job_type in PRODUCTION_AI_JOBS:
|
||||
return runtime.production_device()
|
||||
return settings.device
|
||||
|
||||
@@ -474,7 +467,7 @@ class RunJob(bpy.types.Operator):
|
||||
|
||||
def _starting_message(self, settings):
|
||||
labels = {
|
||||
"INSTALL_RUNTIME": "Starting Runtime installation",
|
||||
"INSTALL_RUNTIME": "Starting Backend installation",
|
||||
"DOWNLOAD_MODEL": "Starting model download",
|
||||
}
|
||||
if self.job_type == RECONSTRUCTION_JOB:
|
||||
@@ -612,7 +605,7 @@ class RunJob(bpy.types.Operator):
|
||||
settings.status = "Task failed"
|
||||
settings.progress = 0.0
|
||||
fallback_error = (
|
||||
"AI Host task failed"
|
||||
"Backend Service task failed"
|
||||
if job.uses_host
|
||||
else "Task subprocess failed"
|
||||
)
|
||||
@@ -845,15 +838,15 @@ class CancelJob(bpy.types.Operator):
|
||||
|
||||
class ControlHost(bpy.types.Operator):
|
||||
bl_idname = "snapform.control_host"
|
||||
bl_label = "Control SnapForm AI Host"
|
||||
bl_label = "Control SnapForm Backend Service"
|
||||
|
||||
action: bpy.props.EnumProperty(
|
||||
items=(
|
||||
("START", "Start", "Start or connect to the shared AI Host"),
|
||||
("START", "Start", "Start or connect to the Backend Service"),
|
||||
("CLEAR", "Clear Models", "Release cached AI models"),
|
||||
("RESTART", "Restart", "Restart the shared AI Host"),
|
||||
("STOP", "Stop", "Stop the shared AI Host"),
|
||||
("LOG", "Open Log", "Open the AI Host log"),
|
||||
("RESTART", "Restart", "Restart the Backend Service"),
|
||||
("STOP", "Stop", "Stop the Backend Service"),
|
||||
("LOG", "Open Log", "Open the Backend Service log"),
|
||||
),
|
||||
options={"HIDDEN", "SKIP_SAVE"},
|
||||
)
|
||||
@@ -862,22 +855,22 @@ class ControlHost(bpy.types.Operator):
|
||||
try:
|
||||
if self.action == "START":
|
||||
runtime.enable_host()
|
||||
message = "AI Host is ready"
|
||||
message = "Backend Service is ready"
|
||||
elif self.action == "CLEAR":
|
||||
runtime.clear_host_models()
|
||||
message = "AI model cache is cleared"
|
||||
elif self.action == "RESTART":
|
||||
runtime.restart_host()
|
||||
message = "AI Host is restarting"
|
||||
message = "Backend Service is restarting"
|
||||
elif self.action == "STOP":
|
||||
runtime.stop_host()
|
||||
message = "AI Host is stopping"
|
||||
message = "Backend Service is stopping"
|
||||
else:
|
||||
log_path = runtime.host_log_path()
|
||||
target = log_path if log_path.is_file() else log_path.parent
|
||||
result = bpy.ops.wm.path_open(filepath=str(target))
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError("Unable to open the AI Host log")
|
||||
raise RuntimeError("Unable to open the Backend Service log")
|
||||
return {"FINISHED"}
|
||||
except (OSError, RuntimeError) as error:
|
||||
self.report({"ERROR"}, str(error))
|
||||
|
||||
@@ -7,7 +7,7 @@ from .runtime import model_ready, runtime_ready
|
||||
|
||||
|
||||
class HostPanel(bpy.types.Panel):
|
||||
bl_label = "Host"
|
||||
bl_label = "AI Backend"
|
||||
bl_idname = "SNAPFORM_PT_host"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
@@ -18,12 +18,15 @@ class HostPanel(bpy.types.Panel):
|
||||
layout = self.layout
|
||||
running = active_job() is not None or runtime.host_busy()
|
||||
if not runtime_ready():
|
||||
layout.label(text="Runtime is not installed", icon="ERROR")
|
||||
layout.label(
|
||||
text="Backend Environment is not installed",
|
||||
icon="ERROR",
|
||||
)
|
||||
install_row = layout.row()
|
||||
install_row.enabled = not running
|
||||
operator = install_row.operator(
|
||||
"snapform.run_job",
|
||||
text="Install Runtime",
|
||||
text="Install Backend",
|
||||
icon="IMPORT",
|
||||
)
|
||||
operator.job_type = "INSTALL_RUNTIME"
|
||||
|
||||
@@ -11,7 +11,7 @@ class SnapFormPreferences(bpy.types.AddonPreferences):
|
||||
storage_root: bpy.props.StringProperty(
|
||||
name="Storage Root",
|
||||
description=(
|
||||
"Shared user-writable storage for Runtime, models and jobs; "
|
||||
"Shared user-writable storage for Backend Environment, models and jobs; "
|
||||
"all Blender versions can use the same directory"
|
||||
),
|
||||
subtype="DIR_PATH",
|
||||
@@ -35,22 +35,22 @@ class SnapFormPreferences(bpy.types.AddonPreferences):
|
||||
layout.prop(self, "storage_root")
|
||||
layout.prop(self, "device")
|
||||
layout.separator()
|
||||
layout.label(text=f"Runtime: {runtime_directory()}")
|
||||
layout.label(text=f"Backend Environment: {runtime_directory()}")
|
||||
if runtime_ready():
|
||||
layout.label(text="Runtime is ready", icon="CHECKMARK")
|
||||
layout.label(text="Backend Environment is ready", icon="CHECKMARK")
|
||||
else:
|
||||
install_row = layout.row()
|
||||
install_row.enabled = active_job() is None
|
||||
operator = install_row.operator(
|
||||
"snapform.run_job",
|
||||
text="Install Runtime",
|
||||
text="Install Backend",
|
||||
icon="IMPORT",
|
||||
)
|
||||
operator.job_type = "INSTALL_RUNTIME"
|
||||
layout.separator()
|
||||
operator = layout.operator(
|
||||
"snapform.control_host",
|
||||
text="Open Host Log",
|
||||
text="Open Service Log",
|
||||
icon="TEXT",
|
||||
)
|
||||
operator.action = "LOG"
|
||||
|
||||
@@ -28,8 +28,9 @@ from .models import (
|
||||
|
||||
DA3_COMMIT = "3d835ec1a5802d64a8b8b15f817a1ab54809bfe4"
|
||||
BEN2_COMMIT = "2c99a5da477b5523585bfa5c893888a6e818a8f6"
|
||||
RUNTIME_SCHEMA = 4
|
||||
HOST_SCHEMA = 2
|
||||
ENVIRONMENT_MANIFEST_REVISION = 4
|
||||
JOB_PROTOCOL_VERSION = 2
|
||||
ENVIRONMENT_MANIFEST_FILENAME = "environment_manifest.json"
|
||||
HOST_ADDRESS = "127.0.0.1"
|
||||
DEFAULT_STORAGE_ROOT = Path.home() / ".snapform"
|
||||
JOB_FOLDER_NAMES = {
|
||||
@@ -103,14 +104,17 @@ def runtime_python():
|
||||
|
||||
|
||||
def runtime_ready():
|
||||
ready_path = runtime_directory() / "snapform_ready.json"
|
||||
ready_path = runtime_directory() / ENVIRONMENT_MANIFEST_FILENAME
|
||||
if not runtime_python().is_file() or not ready_path.is_file():
|
||||
return False
|
||||
try:
|
||||
ready = json.loads(ready_path.read_text(encoding="utf-8"))
|
||||
except (json.JSONDecodeError, OSError):
|
||||
return False
|
||||
return ready.get("runtime_schema") == RUNTIME_SCHEMA
|
||||
return (
|
||||
ready.get("environment_manifest_revision")
|
||||
== ENVIRONMENT_MANIFEST_REVISION
|
||||
)
|
||||
|
||||
|
||||
def models_directory():
|
||||
@@ -140,8 +144,8 @@ def model_ready(model_key):
|
||||
|
||||
|
||||
def runtime_devices():
|
||||
"""Devices detected by the installed Runtime, or None when unknown."""
|
||||
ready_path = runtime_directory() / "snapform_ready.json"
|
||||
"""Devices detected by the Backend Environment, or None when unknown."""
|
||||
ready_path = runtime_directory() / ENVIRONMENT_MANIFEST_FILENAME
|
||||
if not ready_path.is_file():
|
||||
return None
|
||||
try:
|
||||
@@ -155,7 +159,7 @@ def runtime_devices():
|
||||
|
||||
|
||||
def machine_devices():
|
||||
"""Devices the machine can run, fastest first; used before Runtime install."""
|
||||
"""Devices the machine can run before Backend Environment installation."""
|
||||
devices = ["cpu"]
|
||||
if shutil.which("nvidia-smi"):
|
||||
devices.insert(0, "cuda")
|
||||
@@ -329,9 +333,11 @@ def _host_http_request(connection, method, path, payload=None, timeout=2.0):
|
||||
detail = json.loads(error.read().decode("utf-8")).get("detail")
|
||||
except (json.JSONDecodeError, UnicodeDecodeError):
|
||||
detail = None
|
||||
raise RuntimeError(detail or f"AI Host returned HTTP {error.code}")
|
||||
raise RuntimeError(
|
||||
detail or f"Backend Service returned HTTP {error.code}"
|
||||
)
|
||||
except (OSError, URLError) as error:
|
||||
raise RuntimeError(f"Unable to reach AI Host: {error}")
|
||||
raise RuntimeError(f"Unable to reach Backend Service: {error}")
|
||||
|
||||
|
||||
def _read_host_discovery():
|
||||
@@ -354,10 +360,10 @@ def _check_host(connection, timeout=0.3):
|
||||
"/health",
|
||||
timeout=timeout,
|
||||
)
|
||||
if health.get("host_schema") != HOST_SCHEMA:
|
||||
raise RuntimeError("AI Host protocol is out of date")
|
||||
if health.get("job_protocol_version") != JOB_PROTOCOL_VERSION:
|
||||
raise RuntimeError("Backend Service job protocol is out of date")
|
||||
if health.get("instance_id") != connection.instance_id:
|
||||
raise RuntimeError("AI Host discovery is stale")
|
||||
raise RuntimeError("Backend Service discovery is stale")
|
||||
return health
|
||||
|
||||
|
||||
@@ -439,7 +445,9 @@ def ensure_host(timeout=60.0):
|
||||
start_host()
|
||||
last_launch = time.monotonic()
|
||||
time.sleep(0.05)
|
||||
raise RuntimeError(f"AI Host did not become ready; log: {host_log_path()}")
|
||||
raise RuntimeError(
|
||||
f"Backend Service did not become ready; log: {host_log_path()}"
|
||||
)
|
||||
|
||||
|
||||
def submit_host_job(request):
|
||||
@@ -485,7 +493,7 @@ def stop_host():
|
||||
break
|
||||
time.sleep(0.05)
|
||||
else:
|
||||
raise RuntimeError("AI Host did not stop")
|
||||
raise RuntimeError("Backend Service did not stop")
|
||||
_host_connection = None
|
||||
_registered_instance_id = None
|
||||
_close_finished_launched_host()
|
||||
@@ -563,7 +571,7 @@ def detach_host_client():
|
||||
pass
|
||||
_host_connection = None
|
||||
_registered_instance_id = None
|
||||
# 如果 Host 是由当前 Blender 启动的,关闭 Blender 时直接终止它,无需等待空闲超时
|
||||
# 当前 Blender 启动的 Service 随 Blender 关闭,不等待空闲超时。
|
||||
_close_launched_host(terminate=True)
|
||||
|
||||
|
||||
@@ -616,7 +624,7 @@ def _service_host_timer():
|
||||
if not runtime_ready():
|
||||
_host_snapshot = {
|
||||
"state": "UNAVAILABLE",
|
||||
"message": "Runtime is not installed",
|
||||
"message": "Backend Environment is not installed",
|
||||
}
|
||||
return 2.0
|
||||
connection, health = _discover_live_host(timeout=0.2)
|
||||
|
||||
@@ -1282,7 +1282,7 @@ def infer_foreground_frames(
|
||||
|
||||
def remove_background(args, model_cache=None, cancel_check=None):
|
||||
total_started = time.perf_counter()
|
||||
write_status(args.status, 0.02, "Starting BEN2 Runtime")
|
||||
write_status(args.status, 0.02, "Starting Backend Environment")
|
||||
check_cancelled(cancel_check)
|
||||
|
||||
if model_cache is None:
|
||||
@@ -1501,7 +1501,7 @@ def flatten_exported_paths(
|
||||
|
||||
|
||||
def reconstruct(args, model_cache=None, cancel_check=None):
|
||||
write_status(args.status, 0.02, "Starting AI runtime")
|
||||
write_status(args.status, 0.02, "Starting Backend Environment")
|
||||
check_cancelled(cancel_check)
|
||||
|
||||
if model_cache is None:
|
||||
|
||||
Reference in New Issue
Block a user