I am having the worst time of my life trying to get this Power Automate Flow to work.
I have made a Power Automate flow that was extracting a Purchase Order from Invoices. It was triggered on every new email on an inbox. I am cycling through the pdf attachments, calling the AI builder to extract information from the invoice, and get the Purchase Order.
I needed more accurate options in finding the Purchase Order, so I moved the logic of the extracting the purchase order to an API call. Now the flow just sends the pdf to the Endpoint and I can handle all the functionality in C#, and return the necessary details so the flow can continue.
This API call takes around 20 seconds in Postman. When I try to call it in my flow, it times out.
I attached a debugger to my API, and I found that right when I am waiting for a response from the AI client, the Flow is resending the request. It results in an endless loop. I know the call to the endpoint is working, because I can return an OK response before the AI call and it works.
How do I get my HTTP request to wait for this result? Or is it an issue with the async-ness of the client AI call? I have noticed when I'm debugging that my breakpoint is always lost on the first call to the aiClient, which is the first async operation in the call.
Here is the code-view of the HTTP call in Power Automate
"inputs": {
"method": "POST",
"uri": "REDACTED",
"headers": {
"Connection": "keep-alive",
"Accept": "*/*",
"Host": "REDACTED",
"Accept-Encoding": "gzip, deflate, br"
},
"body": {
"$content-type": "multipart/form-data",
"$multipart": [
{
"headers": {
"Content-Disposition": "form-data; name=\"invoicePdf\"; filename=\"invoicePdf.pdf\""
},
"body": "@outputs('Get_file_content')"
}
]
},
"authentication": {
"type": "Basic",
"username": "REDACTED",
"password": "REDACTED"
}
},
"operationOptions": "DisableAsyncPattern",
"metadata": {
"operationMetadataId": "38dae010-f79b-4c3e-ae7c-22a6047b4151"
}
}
Here is my C# method with the ai calls:
{
// Check if a file was uploaded
if (invoicePdf == null || invoicePdf.Length == 0)
{
return BadRequest("No file uploaded.");
}
// Check if the file is a PDF
if (!invoicePdf.FileName.EndsWith(".pdf"))
{
return BadRequest("Only PDF files are allowed.");
}
AzureKeyCredential credential = new AzureKeyCredential(AppSettings.AzureAiServiceKey);
DocumentAnalysisClient aiClient = new DocumentAnalysisClient(new Uri(AppSettings.AzureAiServiceEndpoint), credential);
//var operation = await aiClient.AnalyzeDocumentAsync(WaitUntil.Completed, "prebuilt-invoice", invoicePdf.OpenReadStream());
var invoiceOperation = aiClient.AnalyzeDocumentAsync(WaitUntil.Completed, "prebuilt-invoice",
invoicePdf.OpenReadStream(), new AnalyzeDocumentOptions
{
Pages = { "1-2" }
}).Result;
var invoiceResult = invoiceOperation.Value;
//check default area
for (int i = 0; i < invoiceResult.Documents.Count; i++)
{
var document = invoiceResult.Documents[i];
//try to find the purchase order from the default area
if (document.Fields.ContainsKey("PurchaseOrder"))
{
document.Fields.TryGetValue("PurchaseOrder", out var purchaseOrderObject);
var purchaseOrderValue = purchaseOrderObject.Content;
var purchaseOrderConfidence = purchaseOrderObject.Confidence;
var sanitizedPurchaseOrderValue = SanitizePurchaseOrder(purchaseOrderValue);
return Ok(new
{
Found = true,
PurchaseOrderValue = sanitizedPurchaseOrderValue,
PurchaseOrderConfidence = purchaseOrderConfidence,
FoundIn = "Default Location"
});
}
}
var layoutOperation = aiClient.AnalyzeDocumentAsync(WaitUntil.Completed, "prebuilt-document",
invoicePdf.OpenReadStream(), new AnalyzeDocumentOptions
{
Pages = { "1-2" }
}).Result;
var layoutResult = layoutOperation.Value;
DocumentKeyValuePair? poKeyValuePair;
poKeyValuePair = layoutResult.KeyValuePairs.FirstOrDefault(x => firstPriorityKeys.Contains(x.Key.Content));
if (poKeyValuePair == null)
{
poKeyValuePair = layoutResult.KeyValuePairs.FirstOrDefault(x => secondPriorityKeys.Contains(x.Key.Content));
if (poKeyValuePair == null)
{
poKeyValuePair = layoutResult.KeyValuePairs.FirstOrDefault(x => thirdPriorityKeys.Contains(x.Key.Content));
if (poKeyValuePair == null)
{
poKeyValuePair = layoutResult.KeyValuePairs.FirstOrDefault(x => fourthPriorityKeys.Contains(x.Key.Content));
if (poKeyValuePair == null)
{
poKeyValuePair = layoutResult.KeyValuePairs.FirstOrDefault(x => fifthPriorityKeys.Contains(x.Key.Content));
}
}
}
}
if (poKeyValuePair != null)
{
var sanitizedPurchaseOrderValue = SanitizePurchaseOrder(poKeyValuePair.Value.Content);
return Ok(new
{
Found = true,
PurchaseOrderValue = sanitizedPurchaseOrderValue,
PurchaseOrderConfidence = poKeyValuePair.Confidence,
FoundIn = $"{poKeyValuePair.Key.Content} (KVP)"
});
}
//check plain text with regex
Regex regex =
new Regex(@"([Pp][Oo]|[Pp][Uu][Rr][Cc][Hh][Aa][Ss][Ee]\s?[Oo][Rr][Dd][Ee][Rr]).{0,3}(\d{7})");
Match match = regex.Match(layoutResult.Content);
if (match.Success)
{
var foundPo = match.Value;
var sanitizedPurchaseOrderValue = SanitizePurchaseOrder(foundPo);
return Ok(new
{
Found = true,
PurchaseOrderValue = sanitizedPurchaseOrderValue,
PurchaseOrderConfidence = 0.5,
FoundIn = "Plain Text Regex Parse"
});
}
return Ok(new
{
Found = false
});
}
Summary
- The Power Automate Flow's HTTP call doesn't wait for a response
- It works in Postman, but takes around 20 seconds
- Is there a way I can get the Flow to wait for this response?
I am having the worst time of my life trying to get this Power Automate Flow to work.
I have made a Power Automate flow that was extracting a Purchase Order from Invoices. It was triggered on every new email on an inbox. I am cycling through the pdf attachments, calling the AI builder to extract information from the invoice, and get the Purchase Order.
I needed more accurate options in finding the Purchase Order, so I moved the logic of the extracting the purchase order to an API call. Now the flow just sends the pdf to the Endpoint and I can handle all the functionality in C#, and return the necessary details so the flow can continue.
This API call takes around 20 seconds in Postman. When I try to call it in my flow, it times out.
I attached a debugger to my API, and I found that right when I am waiting for a response from the AI client, the Flow is resending the request. It results in an endless loop. I know the call to the endpoint is working, because I can return an OK response before the AI call and it works.
How do I get my HTTP request to wait for this result? Or is it an issue with the async-ness of the client AI call? I have noticed when I'm debugging that my breakpoint is always lost on the first call to the aiClient, which is the first async operation in the call.
Here is the code-view of the HTTP call in Power Automate
"inputs": {
"method": "POST",
"uri": "REDACTED",
"headers": {
"Connection": "keep-alive",
"Accept": "*/*",
"Host": "REDACTED",
"Accept-Encoding": "gzip, deflate, br"
},
"body": {
"$content-type": "multipart/form-data",
"$multipart": [
{
"headers": {
"Content-Disposition": "form-data; name=\"invoicePdf\"; filename=\"invoicePdf.pdf\""
},
"body": "@outputs('Get_file_content')"
}
]
},
"authentication": {
"type": "Basic",
"username": "REDACTED",
"password": "REDACTED"
}
},
"operationOptions": "DisableAsyncPattern",
"metadata": {
"operationMetadataId": "38dae010-f79b-4c3e-ae7c-22a6047b4151"
}
}
Here is my C# method with the ai calls:
{
// Check if a file was uploaded
if (invoicePdf == null || invoicePdf.Length == 0)
{
return BadRequest("No file uploaded.");
}
// Check if the file is a PDF
if (!invoicePdf.FileName.EndsWith(".pdf"))
{
return BadRequest("Only PDF files are allowed.");
}
AzureKeyCredential credential = new AzureKeyCredential(AppSettings.AzureAiServiceKey);
DocumentAnalysisClient aiClient = new DocumentAnalysisClient(new Uri(AppSettings.AzureAiServiceEndpoint), credential);
//var operation = await aiClient.AnalyzeDocumentAsync(WaitUntil.Completed, "prebuilt-invoice", invoicePdf.OpenReadStream());
var invoiceOperation = aiClient.AnalyzeDocumentAsync(WaitUntil.Completed, "prebuilt-invoice",
invoicePdf.OpenReadStream(), new AnalyzeDocumentOptions
{
Pages = { "1-2" }
}).Result;
var invoiceResult = invoiceOperation.Value;
//check default area
for (int i = 0; i < invoiceResult.Documents.Count; i++)
{
var document = invoiceResult.Documents[i];
//try to find the purchase order from the default area
if (document.Fields.ContainsKey("PurchaseOrder"))
{
document.Fields.TryGetValue("PurchaseOrder", out var purchaseOrderObject);
var purchaseOrderValue = purchaseOrderObject.Content;
var purchaseOrderConfidence = purchaseOrderObject.Confidence;
var sanitizedPurchaseOrderValue = SanitizePurchaseOrder(purchaseOrderValue);
return Ok(new
{
Found = true,
PurchaseOrderValue = sanitizedPurchaseOrderValue,
PurchaseOrderConfidence = purchaseOrderConfidence,
FoundIn = "Default Location"
});
}
}
var layoutOperation = aiClient.AnalyzeDocumentAsync(WaitUntil.Completed, "prebuilt-document",
invoicePdf.OpenReadStream(), new AnalyzeDocumentOptions
{
Pages = { "1-2" }
}).Result;
var layoutResult = layoutOperation.Value;
DocumentKeyValuePair? poKeyValuePair;
poKeyValuePair = layoutResult.KeyValuePairs.FirstOrDefault(x => firstPriorityKeys.Contains(x.Key.Content));
if (poKeyValuePair == null)
{
poKeyValuePair = layoutResult.KeyValuePairs.FirstOrDefault(x => secondPriorityKeys.Contains(x.Key.Content));
if (poKeyValuePair == null)
{
poKeyValuePair = layoutResult.KeyValuePairs.FirstOrDefault(x => thirdPriorityKeys.Contains(x.Key.Content));
if (poKeyValuePair == null)
{
poKeyValuePair = layoutResult.KeyValuePairs.FirstOrDefault(x => fourthPriorityKeys.Contains(x.Key.Content));
if (poKeyValuePair == null)
{
poKeyValuePair = layoutResult.KeyValuePairs.FirstOrDefault(x => fifthPriorityKeys.Contains(x.Key.Content));
}
}
}
}
if (poKeyValuePair != null)
{
var sanitizedPurchaseOrderValue = SanitizePurchaseOrder(poKeyValuePair.Value.Content);
return Ok(new
{
Found = true,
PurchaseOrderValue = sanitizedPurchaseOrderValue,
PurchaseOrderConfidence = poKeyValuePair.Confidence,
FoundIn = $"{poKeyValuePair.Key.Content} (KVP)"
});
}
//check plain text with regex
Regex regex =
new Regex(@"([Pp][Oo]|[Pp][Uu][Rr][Cc][Hh][Aa][Ss][Ee]\s?[Oo][Rr][Dd][Ee][Rr]).{0,3}(\d{7})");
Match match = regex.Match(layoutResult.Content);
if (match.Success)
{
var foundPo = match.Value;
var sanitizedPurchaseOrderValue = SanitizePurchaseOrder(foundPo);
return Ok(new
{
Found = true,
PurchaseOrderValue = sanitizedPurchaseOrderValue,
PurchaseOrderConfidence = 0.5,
FoundIn = "Plain Text Regex Parse"
});
}
return Ok(new
{
Found = false
});
}
Summary
- The Power Automate Flow's HTTP call doesn't wait for a response
- It works in Postman, but takes around 20 seconds
- Is there a way I can get the Flow to wait for this response?
1 Answer
Reset to default 0How incredibly frustrating... I have found a workaround.
I have updated my endpoint to take in a base64 encoded string.
And I have updated to Power Automate flow to use the Get Attachment (V2) call instead of using the New email trigger's attachments.
For some reason it works now... I have no idea what the different between the attachments from the trigger and the attachments from the call are, but switching it has worked.
And I'm fairly confident the problem was with getting the attachments from the trigger, and not the Get Attachment (V2) call
.Result
? According to Microsoft's documentation, async methods should always be awaited usingawait asyncmethod()
and usingAyncMethod().Result
should be avoided. learn.microsoft/en-us/dotnet/csharp/… – akseli Commented Nov 21, 2024 at 23:12asyncs
andawait
, and.Results
, and updating the settings of the HTTP action in Flow, but nothing worked – impo Commented Nov 21, 2024 at 23:19