feat: per-line render overrides — override any output type setting at order time

Instead of duplicating output types for every variation (WebP vs PNG,
different resolution), keep one canonical output type and override
specific fields per order line via render_overrides JSONB.

Backend:
- render_overrides JSONB column on OrderLine (DB migration)
- Render task merges overrides with output type settings (format, width,
  height, samples, engine, denoiser, transparent_bg, cycles_device)
- POST /orders/{id}/batch-render-overrides endpoint for bulk override
- PatchLineBody accepts render_overrides for per-line patching

Frontend:
- Batch render overrides section on OrderDetail: output format dropdown
  (PNG/JPG/WebP) + resolution dropdown (512-4096)
- Clear button to remove overrides

MCP:
- create_order tool: accepts product_ids, output_type, render_overrides,
  material_override — enables "render all products as WebP" via Claude
- set_render_overrides tool: batch override on existing orders

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 12:26:38 +01:00
parent 5a148554c0
commit b892f72f7e
9 changed files with 281 additions and 2 deletions
+87
View File
@@ -294,6 +294,93 @@ def set_material_override(order_id: str, material_name: str = "") -> str:
return json.dumps(data, indent=2)
@mcp.tool()
def create_order(
product_ids: list[str],
output_type_id: str = "",
output_type_name: str = "",
render_overrides: dict | None = None,
material_override: str = "",
notes: str = "",
) -> str:
"""Create a new order with the given products and output type.
Either output_type_id or output_type_name must be provided. If both are given,
output_type_id takes priority. render_overrides allows overriding render settings
like output_format, width, height, samples, engine per line.
Args:
product_ids: List of product UUIDs to include in the order.
output_type_id: UUID of the output type (takes priority over name).
output_type_name: Name of the output type (used if output_type_id is empty).
render_overrides: Optional dict of render setting overrides (e.g. {"output_format": "webp", "width": 2048, "height": 2048}).
material_override: Optional SCHAEFFLER library material name to apply to all lines.
notes: Optional notes for the order.
"""
# Resolve output_type_id from name if needed
ot_id = output_type_id or None
if not ot_id and output_type_name:
rows = _db_query(
"SELECT id FROM output_types WHERE name ILIKE %s AND is_active = true LIMIT 1",
(output_type_name,),
)
if rows:
ot_id = rows[0]["id"]
else:
return f"Error: No active output type found matching '{output_type_name}'."
lines = []
for pid in product_ids:
line: dict = {"product_id": pid}
if ot_id:
line["output_type_id"] = ot_id
if render_overrides:
line["render_overrides"] = render_overrides
if material_override:
line["material_override"] = material_override
lines.append(line)
body: dict = {"lines": lines}
if notes:
body["notes"] = notes
try:
data = _api_post("/api/orders", body)
return json.dumps(
{
"order_id": data["id"],
"order_number": data["order_number"],
"status": data["status"],
"line_count": data.get("line_count", len(lines)),
"render_overrides": render_overrides,
},
indent=2,
)
except Exception as e:
return f"Error creating order: {e}"
@mcp.tool()
def set_render_overrides(order_id: str, render_overrides: dict | None = None) -> str:
"""Set render overrides on all lines of an order (batch).
Overrides any output type render settings at order time. Common overrides:
output_format (png/jpg/webp), width, height, samples, engine (cycles/eevee),
transparent_bg, bg_color, noise_threshold, denoiser.
Pass None/empty to clear all overrides.
Args:
order_id: UUID of the order.
render_overrides: Dict of render setting overrides, or None to clear.
"""
data = _api_post(
f"/api/orders/{order_id}/batch-render-overrides",
{"render_overrides": render_overrides},
)
return json.dumps(data, indent=2)
@mcp.tool()
def get_queue_status() -> str:
"""Get current render queue status — pending, active, completed/failed counts."""