Implement AI Host functionality with model caching and job management
- Added ai_host.py to handle AI tasks with FastAPI integration. - Introduced HostState and ModelCache classes for managing client connections and model loading. - Implemented job submission and cancellation endpoints in the AI host. - Enhanced tests for AI host functionality, including client tracking and job cancellation. - Updated existing tests to accommodate changes in the operator and command structure. - Refactored runtime commands to use request-based architecture instead of command-line arguments. - Improved model loading logic to handle CUDA memory management and reuse models efficiently.
This commit is contained in:
@@ -26,8 +26,13 @@ IMAGE_NAME_SUFFIXES = (
|
||||
|
||||
def image_depth_model_items(owner, context):
|
||||
from .models import model_enum_items
|
||||
from .runtime import model_ready
|
||||
|
||||
return model_enum_items(owner, context)
|
||||
return [
|
||||
item
|
||||
for item in model_enum_items(owner, context)
|
||||
if model_ready(item[0])
|
||||
]
|
||||
|
||||
|
||||
_last_depth_plane_model = "BASE"
|
||||
@@ -921,8 +926,13 @@ class RemoveImageBackground(bpy.types.Operator):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
from .operators import active_job
|
||||
from .runtime import host_busy
|
||||
|
||||
return is_image_empty(context.object) and active_job() is None
|
||||
return (
|
||||
is_image_empty(context.object)
|
||||
and active_job() is None
|
||||
and not host_busy()
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
from .operators import IMAGE_BACKGROUND_JOB
|
||||
@@ -1001,7 +1011,7 @@ class ConvertImageToDepthPlane(bpy.types.Operator):
|
||||
snapform_process_res_factor: bpy.props.FloatProperty(
|
||||
name="Resolution Scale",
|
||||
description="Scale applied to the source image long-edge resolution",
|
||||
default=0.5,
|
||||
default=1.0,
|
||||
min=0.1,
|
||||
max=1.0,
|
||||
step=10,
|
||||
@@ -1019,18 +1029,30 @@ class ConvertImageToDepthPlane(bpy.types.Operator):
|
||||
min=0,
|
||||
max=10,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
from .operators import active_job
|
||||
from .runtime import host_busy
|
||||
|
||||
return is_image_empty(context.object) and active_job() is None
|
||||
return (
|
||||
is_image_empty(context.object)
|
||||
and active_job() is None
|
||||
and not host_busy()
|
||||
)
|
||||
|
||||
def invoke(self, context, _event):
|
||||
global _last_depth_plane_model
|
||||
|
||||
if not self.properties.is_property_set("snapform_model"):
|
||||
self.snapform_model = _last_depth_plane_model
|
||||
available_keys = [
|
||||
item[0] for item in image_depth_model_items(self, context)
|
||||
]
|
||||
if available_keys:
|
||||
self.snapform_model = (
|
||||
_last_depth_plane_model
|
||||
if _last_depth_plane_model in available_keys
|
||||
else available_keys[0]
|
||||
)
|
||||
return context.window_manager.invoke_props_dialog(self, width=340)
|
||||
|
||||
def draw(self, context):
|
||||
@@ -1038,21 +1060,32 @@ class ConvertImageToDepthPlane(bpy.types.Operator):
|
||||
layout.prop(self, "snapform_depth_source")
|
||||
if self.snapform_depth_source == "ESTIMATE":
|
||||
estimate_box = layout.box()
|
||||
estimate_box.prop(self, "snapform_model")
|
||||
estimate_box.prop(self, "snapform_use_ray_pose")
|
||||
process_res = scaled_process_resolution(
|
||||
context.object.data,
|
||||
self.snapform_process_res_factor,
|
||||
)
|
||||
estimate_box.prop(
|
||||
self,
|
||||
"snapform_process_res_factor",
|
||||
text=f"Resolution Scale ({process_res} px)",
|
||||
)
|
||||
layout.prop(self, "snapform_remove_background")
|
||||
layout.prop(self, "snapform_displacement_via")
|
||||
layout.prop(self, "snapform_unshaded")
|
||||
layout.prop(self, "snapform_subdivision_level")
|
||||
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")
|
||||
else:
|
||||
estimate_box.prop(self, "snapform_model")
|
||||
estimate_box.prop(self, "snapform_use_ray_pose")
|
||||
process_res = scaled_process_resolution(
|
||||
context.object.data,
|
||||
self.snapform_process_res_factor,
|
||||
)
|
||||
estimate_box.prop(
|
||||
self,
|
||||
"snapform_process_res_factor",
|
||||
text=f"Resolution Scale ({process_res} px)",
|
||||
)
|
||||
settings_header, settings_body = layout.panel(
|
||||
"snapform_depth_plane_options",
|
||||
default_closed=True,
|
||||
)
|
||||
settings_header.label(text="Options")
|
||||
if settings_body is not None:
|
||||
settings_body.prop(self, "snapform_displacement_via")
|
||||
settings_body.prop(self, "snapform_subdivision_level")
|
||||
settings_body.prop(self, "snapform_remove_background")
|
||||
settings_body.prop(self, "snapform_unshaded")
|
||||
layout.prop(self, "snapform_delete_original")
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
Reference in New Issue
Block a user