This is step's output. It seems like the flow's environment isn't including the expected python_pptx dependency. The step is working alone, but when running the complete flow this is the output of that specific step.
{
"statusCode": 200,
"headers": {
"Cache-Control": "no-cache",
"Vary": "Accept-Encoding",
"Set-Cookie": "ARRAffinity=09567363724ac641439ccd2e0951d20c5e8861b3d612fdb901824df53fb8daf415134d20c556b0b34b9b6ae43ec3f5dcdad61788de889ffc592af7aca85fc1c508DE8BAC218706330000000698960703; path=/; secure; HttpOnly,ReqClientId=f26cf5a0-d1f1-4a94-a6b2-5168b2622869; expires=Fri, 27-Mar-2076 02:24:14 GMT; path=/; secure; HttpOnly,ARRAffinity=09567363724ac641439ccd2e0951d20c5e8861b3d612fdb901824df53fb8daf415134d20c556b0b34b9b6ae43ec3f5dcdad61788de889ffc592af7aca85fc1c508DE8BAC218706330000000698960703; path=/; secure; HttpOnly",
"x-ms-service-request-id": "866ebc9d-e7d0-4b04-8956-49f8b30fc756",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
"REQ_ID": "866ebc9d-e7d0-4b04-8956-49f8b30fc756",
"AuthActivityId": "6ad7d91c-c82a-4425-bd35-e587870c5d7b",
"x-ms-dop-hint": "4",
"x-ms-ratelimit-time-remaining-xrm-requests": "1,200.00",
"x-ms-ratelimit-burst-remaining-xrm-requests": "7999",
"X-Content-Type-Options": "nosniff",
"OData-Version": "4.0",
"X-Source": "71187112410315413524686291672251433821015395227225167101901686649174901181852123846,449810714843116422124517414172198240166138218117220249211228178487224319216622312535202",
"Public": "OPTIONS,GET,HEAD,POST",
"Date": "Fri, 27 Mar 2026 02:24:17 GMT",
"Allow": "OPTIONS,GET,HEAD,POST",
"Content-Type": "application/json; odata.metadata=minimal",
"Expires": "-1",
"Content-Length": "5094"
},
"body": {
"@odata.context": "https://orga775a8d0.crm.dynamics.com/api/data/v9.1/$metadata#Microsoft.Dynamics.CRM.PredictResponse",
"responsev2": {
"@odata.type": "#Microsoft.Dynamics.CRM.expando",
"operationStatus": "Success",
"predictionOutput": {
"@odata.type": "#Microsoft.Dynamics.CRM.expando",
"text": "{\r\n \"error\": {\r\n \"message\": \"No module named 'python_pptx'\"\r\n }\r\n}",
"mimetype": "",
"textMimeType": "",
"finishReason": "stop",
"code": "\nimport logging\nfrom workerinterfaces import ExecutorInterface, ConnectorClient, ExecutionResult, read_multiple_files_from_input, write_file_to_output\nfrom typing import Any, Dict\nimport io\nimport os\nfrom python_pptx import Presentation\n\nclass PromptExecutor(ExecutorInterface):\n async def execute(self, logger: logging.Logger, connector_client: ConnectorClient, input: Dict[str, Any]) -> ExecutionResult:\n output = {}\n output[\"files\"] = []\n output[\"sourceFiles\"] = []\n message_id = input.get(\"RequestId\", \"\")\n pptx_filename = \"template\"\n output_filename = \"updated_template.pptx\"\n\n # Step 1: Read the PowerPoint template\n logger.info(\"Reading PowerPoint template file.\")\n files_read = read_multiple_files_from_input(input, message_id, [pptx_filename])\n if pptx_filename not in files_read or files_read[pptx_filename] is None:\n logger.error(\"PowerPoint file not found in inputs.\")\n return ExecutionResult(400, {}, {\"error\": \"PowerPoint template file is missing.\"})\n pptx_file = files_read[pptx_filename]\n output[\"sourceFiles\"].append(pptx_filename)\n\n # Input values for shape text\n shape_texts = {\n \"title\": input.get(\"title\", \"\"),\n \"subtitle\": input.get(\"subtitle\", \"\"),\n \"shortdescription\": input.get(\"shortdescription\", \"\")\n }\n logger.info(\"Shape texts to update: %s\", {k: \"[REDACTED]\" if len(v) > 30 else v for k, v in shape_texts.items()})\n\n # Step 2: Load PPTX and gather shape names\n if isinstance(pptx_file, io.BytesIO):\n pptx_bytes = pptx_file.getvalue()\n ppt_stream = io.BytesIO(pptx_bytes)\n elif isinstance(pptx_file, str):\n ppt_stream = pptx_file # This will be a file path\n else:\n logger.error(\"Unsupported file input type.\")\n return ExecutionResult(400, {}, {\"error\": \"Unsupported file type for template.\"})\n\n prs = Presentation(ppt_stream)\n shape_names = []\n # Step 3: Update shapes as needed\n for slide in prs.slides:\n for shape in slide.shapes:\n if hasattr(shape, \"name\"):\n shape_names.append(shape.name)\n # Update only specific named shapes\n for key in shape_texts:\n if shape.name.lower() == key.lower():\n # Only update text-containing shapes\n if hasattr(shape, \"text\"):\n logger.info(f\"Updating shape '{shape.name}' with new text.\")\n shape.text = shape_texts[key]\n elif hasattr(shape, \"text_frame\"):\n logger.info(f\"Updating shape '{shape.name}' with new text (text_frame).\")\n shape.text_frame.text = shape_texts[key]\n else:\n logger.warning(f\"Shape '{shape.name}' does not support text update.\")\n \n # Step 4: Save updated pptx\n pptx_out = io.BytesIO()\n prs.save(pptx_out)\n pptx_out.seek(0)\n logger.info(f\"Saving updated PowerPoint file as '{output_filename}'.\")\n\n # Output file handling\n write_file_to_output(input, output, message_id, output_filename, \"application/vnd.openxmlformats-officedocument.presentationml.presentation\", pptx_out)\n\n # Return result: output is the list of files, as per schema\n return ExecutionResult(status_code=200, headers={}, body=output)\n\n",
"signature": "AQAAAK4AAADvu797Ikdlb2dyYXBoeSI6InVzIiwiRW52aXJvbm1lbnQiOiJwcm9kIiwiQ2x1c3RlcklkIjoid3VzIiwiS2V5VmVyc2lvbiI6IjIyMDk1NTZmNDFjYzQ2MmFiOWNlNDYwOTVjZDRmZjFlIiwiU2lnbmF0dXJlVmVyc2lvbiI6InYxIiwiU2lnbmF0dXJlRGF0ZUJpbmFyeSI6NTI1MDc4Njk5NzM5NDA1ODAzNX0gAAAAhsOAfpLoQTtEJr9wf3MYDQlDP4jgZ5utPo3V/tOQppw=",
"logs": "",
"dataUsed": "[]",
"costAsAiBuilderCredits@odata.type": "#Int64",
"costAsAiBuilderCredits": 0,
"costAsCopilotCredits": 0,
"codeThinking": {
"@odata.type": "#Microsoft.Dynamics.CRM.expando"
},
"files@odata.type": "#Collection(Microsoft.Dynamics.CRM.crmbaseentity)",
"files": [],
"structuredOutput": {
"@odata.type": "#Microsoft.Dynamics.CRM.expando",
"error": {
"@odata.type": "#Microsoft.Dynamics.CRM.expando",
"message": "No module named 'python_pptx'"
}
},
"artifacts": {
"@odata.type": "#Microsoft.Dynamics.CRM.expando"
}
}
}
}
}