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:
2026-08-12 22:42:39 +08:00
parent e3d023cac5
commit 7d0983529e
17 changed files with 246 additions and 155 deletions

View File

@@ -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))