BlueSkyXN's picture
Deploy GitHub a51e6f6df2b2d5093fd2526a7953c2ee6a422e37
8a28a8d verified
Raw
History Blame Contribute Delete
4.59 kB
"""
MCP Tool: run
Unified image generation task submission and execution interface.
"""
import time
import uuid
from core.task_scheduler import QueueFullError, submit_background
from core.runtime_config import CONFIG
from .common import (
_load_yaml,
_MODEL_LIST_PATH,
_TASK_DEFINITIONS,
_TASKS_DB,
_TASKS_LOCK,
_get_task_snapshot,
_execute_imagegen_pipeline,
)
from .error_schema import make_error, make_validation_error, make_not_found_error
def handle_run(params: dict) -> dict:
"""Unified image generation task execution interface."""
if not isinstance(params, dict):
return make_validation_error("Request params must be an object.")
missing = []
for req_field in ["task_type", "model", "prompt"]:
if req_field not in params or not params[req_field]:
missing.append(req_field)
if missing:
return make_validation_error(
f"Missing required parameter(s): {', '.join(missing)}",
missing_fields=missing,
)
task_type = params["task_type"]
valid_tasks = [t["task_type"] for t in _TASK_DEFINITIONS]
if task_type not in valid_tasks:
return make_validation_error(
f"Invalid task_type '{task_type}'. Must be one of {valid_tasks}.",
invalid_fields={"task_type": f"Must be in {valid_tasks}"},
)
model_list = _load_yaml(_MODEL_LIST_PATH)
checkpoints = model_list.get("Checkpoint", {})
all_models = set()
for arch_name, arch_data in checkpoints.items():
if isinstance(arch_data, dict):
for m in arch_data.get("models", []):
all_models.add(m.get("display_name"))
if params["model"] not in all_models:
return make_not_found_error("model", params["model"])
task_requirements = {
"txt2img": ("width", "height"),
"img2img": ("image",),
"inpaint": ("image",),
"outpaint": ("image", "pad_left", "pad_right", "pad_top", "pad_bottom"),
"hires_fix": ("image", "upscale_by"),
}
missing_task_fields = [
field
for field in task_requirements.get(task_type, ())
if field not in params or params[field] is None or params[field] == ""
]
if missing_task_fields:
return make_validation_error(
f"Missing required parameter(s) for {task_type}: {', '.join(missing_task_fields)}",
missing_fields=missing_task_fields,
)
try:
batch_size = int(params.get("batch_size", 1))
except (TypeError, ValueError):
batch_size = 0
if not 1 <= batch_size <= CONFIG.max_batch_size:
return make_validation_error(
f"batch_size must be between 1 and {CONFIG.max_batch_size}.",
invalid_fields={"batch_size": f"Expected 1..{CONFIG.max_batch_size}"},
)
with _TASKS_LOCK:
if len(_TASKS_DB) >= CONFIG.mcp_task_retention:
finished = sorted(
(
(old_task_id, task)
for old_task_id, task in _TASKS_DB.items()
if task.get("status") in {"completed", "failed"}
),
key=lambda pair: pair[1].get("completed_at", pair[1].get("failed_at", 0)),
)
remove_count = max(1, len(_TASKS_DB) - CONFIG.mcp_task_retention + 1)
for old_task_id, _ in finished[:remove_count]:
_TASKS_DB.pop(old_task_id, None)
if len(_TASKS_DB) >= CONFIG.mcp_task_retention:
return make_error(
"QUEUE_FULL",
"任务记录已满且当前任务均未结束,请稍后再试。",
)
task_id = f"img_task_{uuid.uuid4().hex[:10]}"
created_at = int(time.time())
_TASKS_DB[task_id] = {
"task_id": task_id,
"status": "queued",
"progress": 0,
"created_at": created_at,
}
async_exec = params.get("async_execution", False)
if async_exec:
try:
submit_background(_execute_imagegen_pipeline, task_id, params)
except QueueFullError as exc:
with _TASKS_LOCK:
_TASKS_DB.pop(task_id, None)
return make_error("QUEUE_FULL", str(exc))
return {
"status": "queued",
"task_id": task_id,
"poll_interval_ms": 2000,
"message": "Task queued successfully. Poll get_task_status for results.",
}
else:
_execute_imagegen_pipeline(task_id, params)
return _get_task_snapshot(task_id)