diff --git a/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py b/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py index 77e65d21..ac44f86e 100644 --- a/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py +++ b/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py @@ -1891,3 +1891,93 @@ def _registerCoreTools(registry: ToolRegistry, services): }, readOnly=False, ) + + # ── generateImage tool ───────────────────────────────────────────── + + async def _generateImage(args: Dict[str, Any], context: Dict[str, Any]): + """Generate an image from a text prompt using AI (DALL-E).""" + import re as _re + + prompt = (args.get("prompt") or "").strip() + style = (args.get("style") or "").strip() or None + title = (args.get("title") or "").strip() or "Generated Image" + + if not prompt: + return ToolResult(toolCallId="", toolName="generateImage", success=False, error="prompt is required") + + try: + from modules.serviceCenter.services.serviceGeneration.paths.imagePath import ImageGenerationPath + + imagePath = ImageGenerationPath(services) + aiResponse = await imagePath.generateImages( + userPrompt=prompt, + count=1, + style=style, + format="png", + title=title, + ) + + if not aiResponse.documents: + return ToolResult(toolCallId="", toolName="generateImage", success=False, error="Image generation returned no image data") + + sideEvents = [] + savedFiles = [] + chatService = services.chat + sanitizedTitle = _re.sub(r'[^\w._-]', '_', title, flags=_re.UNICODE).strip('_') or "generated_image" + + for doc in aiResponse.documents: + docData = doc.documentData if hasattr(doc, "documentData") else b"" + docName = doc.documentName if hasattr(doc, "documentName") else f"{sanitizedTitle}.png" + docMime = doc.mimeType if hasattr(doc, "mimeType") else "image/png" + + if not docName.lower().endswith(".png"): + docName = f"{sanitizedTitle}.png" + + fileItem = None + if hasattr(chatService.interfaceDbComponent, "saveGeneratedFile"): + fileItem = chatService.interfaceDbComponent.saveGeneratedFile(docData, docName, docMime) + else: + fileItem, _ = chatService.interfaceDbComponent.saveUploadedFile(docData, docName) + + if fileItem: + fid = fileItem.id if hasattr(fileItem, "id") else fileItem.get("id", "?") + fiId = context.get("featureInstanceId") or (services.featureInstanceId if services else "") + if fiId: + chatService.interfaceDbComponent.updateFile(fid, {"featureInstanceId": fiId}) + savedFiles.append(f"- {docName} (id: {fid})") + sideEvents.append({ + "type": "fileCreated", + "data": { + "fileId": fid, + "fileName": docName, + "mimeType": docMime, + "fileSize": len(docData), + }, + }) + + result = f"Generated {len(aiResponse.documents)} image(s):\n" + "\n".join(savedFiles) + return ToolResult(toolCallId="", toolName="generateImage", success=True, data=result, sideEvents=sideEvents) + + except Exception as e: + logger.error(f"generateImage failed: {e}") + return ToolResult(toolCallId="", toolName="generateImage", success=False, error=str(e)) + + registry.register( + "generateImage", _generateImage, + description=( + "Generate an image from a text description using AI (DALL-E). " + "The generated image is saved as a file in the workspace. " + "Use this when the user asks to create, generate, draw, or design an image, illustration, icon, logo, diagram, or any visual content. " + "Provide a detailed, descriptive prompt for best results." + ), + parameters={ + "type": "object", + "properties": { + "prompt": {"type": "string", "description": "Detailed description of the image to generate. Be specific about subject, composition, colors, style, and mood."}, + "style": {"type": "string", "description": "Optional style modifier (e.g. 'photorealistic', 'watercolor', 'digital art', 'minimalist', 'sketch')"}, + "title": {"type": "string", "description": "Title/filename for the generated image", "default": "Generated Image"}, + }, + "required": ["prompt"], + }, + readOnly=False, + )