diff --git a/backend/app/services/chat_service.py b/backend/app/services/chat_service.py index a782c6a..3532aad 100644 --- a/backend/app/services/chat_service.py +++ b/backend/app/services/chat_service.py @@ -284,6 +284,11 @@ TOOLS = [ "description": "Only return renders with transparent background.", "default": False, }, + "material_override": { + "type": "string", + "description": "Filter by material override (partial match). E.g. 'Durotect' to find renders using Durotect material.", + "default": "", + }, }, }, }, @@ -700,6 +705,7 @@ async def _tool_list_materials(db: AsyncSession, tenant_id: str, query: str = "" async def _tool_find_product_renders( db: AsyncSession, tenant_id: str, product_name: str = "", product_id: str = "", transparent_only: bool = False, + material_override: str = "", ) -> str: """Find existing render images for a product, returning viewable URLs.""" conditions = ["o.tenant_id = :tenant_id", "ol.render_status = 'completed'", "ol.result_path IS NOT NULL"] @@ -715,13 +721,25 @@ async def _tool_find_product_renders( if transparent_only: conditions.append("ot.transparent_bg = true") + if material_override: + conditions.append("(ol.material_override ILIKE :mat OR ot.material_override ILIKE :mat)") + params["mat"] = f"%{material_override}%" where = " AND ".join(conditions) sql = f""" SELECT p.name AS product_name, p.id AS product_id, ot.name AS output_type, ot.transparent_bg, + ot.output_format, ot.is_animation, + ot.material_override AS ot_material_override, + ot.render_settings->>'width' AS ot_width, + ot.render_settings->>'height' AS ot_height, + ot.render_settings->>'engine' AS ot_engine, + ot.render_settings->>'samples' AS ot_samples, + ol.material_override AS line_material_override, + ol.render_overrides, ol.result_path, ol.id AS line_id, - ol.render_completed_at + ol.render_completed_at, + o.order_number, o.id AS order_id FROM order_lines ol JOIN orders o ON o.id = ol.order_id JOIN products p ON p.id = ol.product_id @@ -734,7 +752,7 @@ async def _tool_find_product_renders( rows = result.mappings().all() if not rows: - return json.dumps({"message": "No completed renders found for this product.", "renders": []}) + return json.dumps({"message": "No completed renders found.", "renders": []}) renders = [] for r in rows: @@ -746,18 +764,31 @@ async def _tool_find_product_renders( elif "/thumbnails/" in path: url = path[path.index("/thumbnails/"):] + # Effective material override (line overrides output type) + material = r["line_material_override"] or r["ot_material_override"] or None + # Effective render overrides + overrides = r["render_overrides"] or {} + renders.append({ "product_name": r["product_name"], "product_id": str(r["product_id"]), + "order_number": r["order_number"], + "order_id": str(r["order_id"]), "output_type": r["output_type"], + "output_format": overrides.get("output_format") or r["output_format"], "transparent_bg": r["transparent_bg"], + "is_animation": r["is_animation"], + "resolution": f"{overrides.get('width') or r['ot_width'] or '?'}x{overrides.get('height') or r['ot_height'] or '?'}", + "engine": overrides.get("engine") or r["ot_engine"], + "samples": overrides.get("samples") or r["ot_samples"], + "material_override": material, "image_url": url, "line_id": str(r["line_id"]), "rendered_at": str(r["render_completed_at"]), }) return json.dumps({ - "message": f"Found {len(renders)} render(s). Show the user these image URLs as clickable links.", + "message": f"Found {len(renders)} render(s). Each has metadata about format, resolution, material override, transparency. Show image URLs as Markdown images or links.", "renders": renders, }, indent=2, default=str)