public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("Entity Search Custom Skill: C# HTTP trigger function processed a request."); string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } string bingApiKey = Environment.GetEnvironmentVariable(bingApiKeySetting, EnvironmentVariableTarget.Process); WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords, async (inRecord, outRecord) => { var entityName = inRecord.Data["name"] as string; string uri = bingApiEndpoint + "?q=" + Uri.EscapeDataString(entityName) + "&mkt=en-us&count=10&offset=0&safesearch=Moderate"; IEnumerable <BingEntity> entities = await WebApiSkillHelpers.Fetch <BingEntity>(uri, "Ocp-Apim-Subscription-Key", bingApiKey, "entities.value"); ExtractTopEntityMetadata(entities, outRecord.Data); outRecord.Data["entities"] = entities; return(outRecord); }); return(new OkObjectResult(response)); }
public async Task <IActionResult> RunVideoIndexer( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("Video Indexer Custom Skill: C# HTTP trigger function processed a request"); var skillName = executionContext.FunctionName; var requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } var response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords, async (inRecord, outRecord) => { var encodedVideoUrl = (string)inRecord.Data["metadata_storage_path"]; var videoUrl = UrlSafeBase64Decode(encodedVideoUrl); var videoName = (string)inRecord.Data["metadata_storage_name"]; var sasKey = await _videoIndexerBlobClient.GetSasKey(videoUrl); var videoId = await _videoIndexerClient.SubmitVideoIndexingJob(videoUrl + sasKey, encodedVideoUrl, videoName); log.LogInformation("Uploaded video {VideoName} - Video Id in indexer: {Id}", videoName, videoId); outRecord.Data["videoId"] = videoId; return(outRecord); }); return(new OkObjectResult(response)); }
public static async Task <IActionResult> RunAnalyzeForm( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("Analyze Form Custom Skill: C# HTTP trigger function processed a request."); string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } string formsRecognizerApiKey = Environment.GetEnvironmentVariable(formsRecognizerApiKeySetting, EnvironmentVariableTarget.Process); string modelId = Environment.GetEnvironmentVariable(modelIdSetting, EnvironmentVariableTarget.Process); WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords, async (inRecord, outRecord) => { var formUrl = inRecord.Data["formUrl"] as string; var formSasToken = inRecord.Data["formSasToken"] as string; // Fetch the document byte[] documentBytes; using (var webClient = new WebClient()) { documentBytes = await webClient.DownloadDataTaskAsync(new Uri(formUrl + formSasToken)); } string uri = formsRecognizerApiEndpoint + "/models/" + Uri.EscapeDataString(modelId) + "/analyze"; List <Page> pages = (await WebApiSkillHelpers.FetchAsync <Page>( uri, apiKeyHeader: "Ocp-Apim-Subscription-Key", apiKey: formsRecognizerApiKey, collectionPath: "pages", method: HttpMethod.Post, postBody: documentBytes, contentType)) .ToList(); foreach (KeyValuePair <string, string> kvp in fieldMappings) { var value = GetField(pages, kvp.Key); if (!string.IsNullOrWhiteSpace(value)) { outRecord.Data[kvp.Value] = value; } } return(outRecord); }); return(new OkObjectResult(response)); }
public static async Task <IActionResult> RunCustomVision( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("Custom Vision Custom Skill: C# HTTP trigger function processed a request."); string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } var predictionUrl = Environment.GetEnvironmentVariable(customVisionPredictionUrlSetting, EnvironmentVariableTarget.Process); var predictionKey = Environment.GetEnvironmentVariable(customVisionApiKeySetting, EnvironmentVariableTarget.Process); var maxPages = Convert.ToInt32(Environment.GetEnvironmentVariable(maxPagesSetting, EnvironmentVariableTarget.Process)); if (maxPages == 0) { maxPages = 1; } var threshold = Convert.ToDouble(Environment.GetEnvironmentVariable(minProbabilityThresholdSetting, EnvironmentVariableTarget.Process)); if (threshold == 0.0) { threshold = 0.5; } WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords, async (inRecord, outRecord) => { var pages = inRecord.Data["pages"] as JArray; var tags = new List <string>(); var predictions = new List <object>(); foreach (var page in pages.Take(maxPages)) { var pageBinaryData = Convert.FromBase64String(page.ToString()); var pagePredictions = await GetPredictionsForImageAsync(pageBinaryData, predictionUrl, predictionKey); var analyzeResult = JsonConvert.DeserializeObject <AnalyzeResult>(pagePredictions); var pageTags = analyzeResult.Predictions.Where(p => p.Probability >= threshold).Select(p => p.TagName).Distinct(); tags.AddRange(pageTags); } outRecord.Data["tags"] = tags.Distinct().ToArray(); return(outRecord); }); return(new OkObjectResult(response)); }
public static async Task <IActionResult> RunStoreImage( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("Image Store Custom Skill: C# HTTP trigger function processed a request."); string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null || requestRecords.Count() != 1) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array: Skill requires exactly 1 image per request.")); } string blobStorageConnectionString = Environment.GetEnvironmentVariable(blobStorageConnectionStringSetting, EnvironmentVariableTarget.Process); string blobContainerName = String.IsNullOrEmpty(req.Headers[blobContainerHeaderName]) ? Environment.GetEnvironmentVariable(blobStorageContainerNameSetting, EnvironmentVariableTarget.Process) : (string)req.Headers[blobContainerHeaderName]; if (String.IsNullOrEmpty(blobStorageConnectionString) || String.IsNullOrEmpty(blobContainerName)) { return(new BadRequestObjectResult($"{skillName} - Information for the blob storage account is missing")); } var imageStore = new BlobImageStore(blobStorageConnectionString, blobContainerName); WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords, async (inRecord, outRecord) => { var imageData = inRecord.Data["imageData"] as string; var imageName = inRecord.Data["imageName"] as string; if (String.IsNullOrEmpty(imageName)) { imageName = Guid.NewGuid().ToString(); } var mimeType = inRecord.Data["mimeType"] as string; if (String.IsNullOrEmpty(mimeType)) { mimeType = "image/jpeg"; } string imageUri = await imageStore.UploadToBlobAsync(imageData, imageName, mimeType); outRecord.Data["imageStoreUri"] = imageUri; return(outRecord); }); return(new OkObjectResult(response)); }
public static async Task <IActionResult> RunGeoPointFromName( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("Geo Point From Name Custom skill: C# HTTP trigger function processed a request."); string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } string azureMapsKey = Environment.GetEnvironmentVariable(azureMapsKeySetting, EnvironmentVariableTarget.Process); WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords, async (inRecord, outRecord) => { var address = inRecord.Data["address"] as string; string uri = azureMapsUri + "?api-version=1.0&query=" + Uri.EscapeDataString(address) + "&subscription-key=" + Uri.EscapeDataString(azureMapsKey); IEnumerable <Geography> geographies = await WebApiSkillHelpers.FetchAsync <Geography>(uri, "results"); if (geographies.FirstOrDefault() is Geography mainGeoPoint) { outRecord.Data["mainGeoPoint"] = new { Type = "Point", Coordinates = new double[] { mainGeoPoint.Position.Lon, mainGeoPoint.Position.Lat } }; } outRecord.Data["results"] = geographies; return(outRecord); }); return(new OkObjectResult(response)); }
public static async Task <IActionResult> RunAnalyzeForm( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("Analyze Form Custom Skill: C# HTTP trigger function processed a request."); string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } string formsRecognizerEndpointUrl = Environment.GetEnvironmentVariable(formsRecognizerApiEndpointSetting, EnvironmentVariableTarget.Process).TrimEnd('/'); string formsRecognizerApiKey = Environment.GetEnvironmentVariable(formsRecognizerApiKeySetting, EnvironmentVariableTarget.Process); // string modelId = Environment.GetEnvironmentVariable(modelIdSetting, EnvironmentVariableTarget.Process); int retryDelay = int.TryParse(Environment.GetEnvironmentVariable(retryDelaySetting, EnvironmentVariableTarget.Process), out int parsedRetryDelay) ? parsedRetryDelay : 1000; int maxAttempts = int.TryParse(Environment.GetEnvironmentVariable(maxAttemptsSetting, EnvironmentVariableTarget.Process), out int parsedMaxAttempts) ? parsedMaxAttempts : 100; Dictionary <string, string> fieldMappings = JsonConvert.DeserializeObject <Dictionary <string, string> >( File.ReadAllText($"{executionContext.FunctionAppDirectory}\\field-mappings.json")); WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords, async (inRecord, outRecord) => { var formUrl = inRecord.Data["formUrl"] as string; var formSasToken = inRecord.Data["formSasToken"] as string; string formUri = WebApiSkillHelpers.CombineSasTokenWithUri(formUrl, formSasToken); // Create the job string jobId = await GetJobId(formsRecognizerEndpointUrl, formUri, formsRecognizerApiKey); // Get the results for (int attempt = 0; attempt < maxAttempts; attempt++) { (string status, JToken result) = await GetJobStatus(jobId, formsRecognizerApiKey); if (status == "failed") { var errors = result.SelectToken("analyzeResult.errors") as JArray; outRecord.Errors.AddRange(errors.Children().Select(error => new WebApiErrorWarningContract { Message = error.SelectToken("message").ToObject <string>() })); return(outRecord); } if (status == "succeeded") { //List<Page> pages = result.SelectToken("analyzeResult.pageResults").ToObject<List<Page>>(); List <Page> pages = result.SelectToken("analyzeResult.readResults").ToObject <List <Page> >(); foreach (KeyValuePair <string, string> kvp in fieldMappings) { string value = GetField(pages, kvp.Key); if (!string.IsNullOrWhiteSpace(value)) { outRecord.Data[kvp.Value] = value; } } return(outRecord); } await Task.Delay(retryDelay); } outRecord.Errors.Add(new WebApiErrorWarningContract { Message = $"The forms recognizer did not finish the job after {maxAttempts} attempts." }); return(outRecord); }); return(new OkObjectResult(response)); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } // Get Endpoint and access key from App Settings string apiKey = Environment.GetEnvironmentVariable(textAnalyticsApiKeySetting, EnvironmentVariableTarget.Process); string apiEndpoint = Environment.GetEnvironmentVariable(textAnalyticsApiEndpointSetting, EnvironmentVariableTarget.Process); if (apiKey == null || apiEndpoint == null) { return(new BadRequestObjectResult($"{skillName} - TextAnalyticsForHealth API key or Endpoint is missing. Make sure to set it in the Environment Variables.")); } var client = new TextAnalyticsClient(new Uri(apiEndpoint), new AzureKeyCredential(apiKey)); // Get a custom timeout from the header, if it exists. If not use the default timeout. int timeout; if (!int.TryParse(req.Headers["timeout"].ToString(), out timeout)) { timeout = defaultTimeout; } timeout = Math.Clamp(timeout - timeoutBuffer, 1, maxTimeout - timeoutBuffer); var timeoutMiliseconds = timeout * 1000; var timeoutTask = Task.Delay(timeoutMiliseconds); // Get a custom default language, if none is provided, use english string defaultLanguage = req.Headers.ContainsKey("defaultLanguageCode")? req.Headers["defaultLanguageCode"].ToString(): "en"; WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords, async (inRecord, outRecord) => { if (timeoutTask.IsCompleted) { // The time limit for all the skills has been met outRecord.Errors.Add(new WebApiErrorWarningContract { Message = "TextAnalyticsForHealth Error: The Text Analysis Operation took too long to complete." }); return(outRecord); } // Prepare analysis operation input if (!inRecord.Data.ContainsKey("text")) { outRecord.Errors.Add(new WebApiErrorWarningContract { Message = "TextAnalyticsForHealth Error: The skill request did not contain 'text' in the input." }); return(outRecord); } var document = inRecord.Data["text"] as string; var language = inRecord.Data.ContainsKey("languageCode") ? inRecord.Data["languageCode"] as string : defaultLanguage; var docInfo = new StringInfo(document); if (docInfo.LengthInTextElements >= maxCharLength) { outRecord.Warnings.Add(new WebApiErrorWarningContract { Message = $"TextAnalyticsForHealth Warning: The submitted document was over {maxCharLength} elements. It has been truncated to fit this requirement." }); document = docInfo.SubstringByTextElements(0, maxCharLength); } var options = new AnalyzeHealthcareEntitiesOptions { }; List <string> batchInput = new List <string>() { document }; // start analysis process var timer = System.Diagnostics.Stopwatch.StartNew(); AnalyzeHealthcareEntitiesOperation healthOperation = await client.StartAnalyzeHealthcareEntitiesAsync(batchInput, language, options); var healthOperationTask = healthOperation.WaitForCompletionAsync().AsTask(); if (await Task.WhenAny(healthOperationTask, timeoutTask) == healthOperationTask) { // Task Completed, now lets process the result. outRecord.Data["status"] = healthOperation.Status.ToString(); if (healthOperation.Status != TextAnalyticsOperationStatus.Succeeded || !healthOperation.HasValue) { // The operation was not a success outRecord.Errors.Add(new WebApiErrorWarningContract { Message = "TextAnalyticsForHealth Error: Health Operation returned a non-succeeded status." }); } else { // The operation was a success, so lets add the results to our output. await ExtractEntityData(healthOperation.Value, outRecord); } } else { // Timeout outRecord.Errors.Add(new WebApiErrorWarningContract { Message = "TextAnalyticsForHealth Error: The Text Analysis Operation took too long to complete." }); } // Record how long this task took to complete. timer.Stop(); var timeToComplete = timer.Elapsed.TotalSeconds; log.LogInformation($"Time to complete request for document with ID {inRecord.RecordId}: {timeToComplete}"); return(outRecord); }); return(new OkObjectResult(response)); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("Submit S2T Skill: C# HTTP trigger function processed a request."); log.LogInformation($"REQUEST: {new StreamReader(req.Body).ReadToEnd()}"); req.Body.Position = 0; try { string destSasUrl; string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } string storageConnectionString = Environment.GetEnvironmentVariable("StorageConnection"); string region = Environment.GetEnvironmentVariable("region"); CloudStorageAccount storageAccount; if (CloudStorageAccount.TryParse(storageConnectionString, out storageAccount)) { CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference("transcribed"); await cloudBlobContainer.CreateIfNotExistsAsync(); destSasUrl = GetContainerSasUri(cloudBlobContainer); } else { // Otherwise, let the user know that they need to define the environment variable. throw new Exception("Cannot access storage account"); } WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords, async (inRecord, outRecord) => { Uri jobId; var recUrl = inRecord.Data["recUrl"] as string; var recSasToken = inRecord.Data["recSasToken"] as string; Transcription tc = new Transcription() { ContentUrls = new[] { new Uri(recUrl + recSasToken) }, DisplayName = "Transcription", Locale = "en-US", model = null, properties = new Properties { diarizationEnabled = true, wordLevelTimestampsEnabled = true, PunctuationMode = "DictatedAndAutomatic", destinationContainerUrl = destSasUrl } }; var client = new HttpClient(); client.Timeout = TimeSpan.FromMinutes(25); client.BaseAddress = new UriBuilder(Uri.UriSchemeHttps, $"{region}.api.cognitive.microsoft.com", 443).Uri; string path = "speechtotext/v3.0/transcriptions"; client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", Environment.GetEnvironmentVariable("Ocp-Apim-Subscription-Key")); string res = Newtonsoft.Json.JsonConvert.SerializeObject(tc); StringContent sc = new StringContent(res); sc.Headers.ContentType = JsonMediaTypeFormatter.DefaultMediaType; using (var resp = await client.PostAsync(path, sc)) { if (!resp.IsSuccessStatusCode) { throw new HttpRequestException("Failed to create S2T transcription job"); } IEnumerable <string> headerValues; if (resp.Headers.TryGetValues(TranscriptionLocationHeaderKey, out headerValues)) { if (headerValues.Any()) { jobId = new Uri(headerValues.First()); outRecord.Data["jobId"] = jobId; } } } return(outRecord); }); log.LogInformation(JsonConvert.SerializeObject(response)); return(new OkObjectResult(response)); } catch (Exception ex) { log.LogError(ex.Message); log.LogError(ex.StackTrace); } return(null); }