{"id":2251,"date":"2022-10-26T11:29:56","date_gmt":"2022-10-26T18:29:56","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/azure-sdk\/?p=2251"},"modified":"2022-10-26T11:29:56","modified_gmt":"2022-10-26T18:29:56","slug":"announcing-new-stable-release-of-azure-form-recognizer-libraries","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/azure-sdk\/announcing-new-stable-release-of-azure-form-recognizer-libraries\/","title":{"rendered":"Announcing new stable release of Azure Form Recognizer libraries"},"content":{"rendered":"<p>We&#8217;re excited to announce the latest stable release for the Azure Form Recognizer SDKs for .NET, Python, Java, and JS. Form Recognizer is an Azure Applied AI Service that uses machine-learning models to extract information from documents. The Form Recognizer client libraries strive to provide an intuitive and simple way for people to use the service.<\/p>\n<p>The following table shows the latest stable release for each SDK:<\/p>\n<table>\n<thead>\n<tr>\n<th>SDK<\/th>\n<th>Latest stable version<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\/blob\/Azure.AI.FormRecognizer_4.0.0\/sdk\/formrecognizer\/Azure.AI.FormRecognizer\/README.md\">Azure Form Recognizer SDK &#8211; .NET<\/a><\/td>\n<td><a href=\"https:\/\/www.nuget.org\/packages\/Azure.AI.FormRecognizer\/4.0.0\">4.0.0<\/a><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-python\/blob\/azure-ai-formrecognizer_3.2.0\/sdk\/formrecognizer\/azure-ai-formrecognizer\/README.md\">Azure Form Recognizer SDK &#8211; Python<\/a><\/td>\n<td><a href=\"https:\/\/pypi.org\/project\/azure-ai-formrecognizer\/3.2.0\/\">3.2.0<\/a><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-java\/blob\/azure-ai-formrecognizer_4.0.0\/sdk\/formrecognizer\/azure-ai-formrecognizer\/README.md\">Azure Form Recognizer SDK &#8211; Java<\/a><\/td>\n<td><a href=\"https:\/\/mvnrepository.com\/artifact\/com.azure\/azure-ai-formrecognizer\/4.0.0\">4.0.0<\/a><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-js\/blob\/%40azure\/ai-form-recognizer_4.0.0\/sdk\/formrecognizer\/ai-form-recognizer\/README.md\">Azure Form Recognizer SDK &#8211; JS<\/a><\/td>\n<td><a href=\"https:\/\/www.npmjs.com\/package\/@azure\/ai-form-recognizer\/v\/4.0.0\">4.0.0<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>This release includes important changes and updates in each SDK. The most notable of which is the introduction of two new clients, the <code>DocumentAnalysisClient<\/code> and the <code>DocumentModelAdministrationClient<\/code>. The SDKs support the latest version of the service through these new clients. The previous <code>FormRecognizerClient<\/code> and <code>FormTrainingClient<\/code> are still supported for older versions of the service, if you&#8217;re using these clients and wish to migrate to the latest clients to use new service features, see the table below with the migration guides for each language:<\/p>\n<blockquote><p>NOTE: The JavaScript SDK doesn&#8217;t support the old clients when upgrading to the latest package version, the migration guide includes recommendations for how to handle this situation.<\/p><\/blockquote>\n<table>\n<thead>\n<tr>\n<th>SDK<\/th>\n<th>Migration guide<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Azure Form Recognizer SDK &#8211; .NET<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\/blob\/Azure.AI.FormRecognizer_4.0.0\/sdk\/formrecognizer\/Azure.AI.FormRecognizer\/MigrationGuide.md\">Migration guide<\/a><\/td>\n<\/tr>\n<tr>\n<td>Azure Form Recognizer SDK &#8211; Python<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-python\/blob\/azure-ai-formrecognizer_3.2.0\/sdk\/formrecognizer\/azure-ai-formrecognizer\/MIGRATION_GUIDE.md\">Migration guide<\/a><\/td>\n<\/tr>\n<tr>\n<td>Azure Form Recognizer SDK &#8211; Java<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-java\/blob\/azure-ai-formrecognizer_4.0.0\/sdk\/formrecognizer\/azure-ai-formrecognizer\/migration-guide.md\">Migration guide<\/a><\/td>\n<\/tr>\n<tr>\n<td>Azure Form Recognizer SDK &#8211; JS<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-js\/blob\/%40azure\/ai-form-recognizer_4.0.0\/sdk\/formrecognizer\/ai-form-recognizer\/MIGRATION-v3_v4.md\">Migration guide<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Key features<\/h2>\n<p>In this release, the Azure SDK team introduced two new clients in each SDK, the <code>DocumentAnalysisClient<\/code> and the <code>DocumentModelAdministrationClient<\/code>. These clients aim to improve the methods and responses used to interact with the Form Recognizer service and must be used with the latest stable service API version, <code>2022-08-31<\/code>, and later (NOTE: these new clients can&#8217;t be used with older API versions).<\/p>\n<h3>DocumentAnalysisClient<\/h3>\n<p>The <code>DocumentAnalysisClient<\/code> provides two document analysis methods (one for stream inputs and one for URL inputs) that can be used to analyze documents for both prebuilt and custom models. The document analysis methods accept a model ID parameter that will specify the desired model used for analysis requests. Many prebuilt models that have been trained by the service are enabled in each Form Recognizer resource. As part of the latest stable release of the service, new prebuilt document analysis models have been added. These new models provide varying features and elements that are extracted, such as the <code>\"prebuilt-document\"<\/code> model that analyzes documents and returns useful information in the shape of key-value pairs, tables, pages, among others. To find more information about models, including a list of supported prebuilt models, see the <a href=\"https:\/\/learn.microsoft.com\/azure\/applied-ai-services\/form-recognizer\/concept-model-overview\">Form Recognizer models<\/a> page.<\/p>\n<h4>Example: Instantiate DocumentAnalysisClient<\/h4>\n<p>The examples below showcase how to instantiate a client using an <code>AzureKeyCredential<\/code>, however our clients also support <code>Azure Identity<\/code> credentials that provide support for multiple authentication scenarios. Use the links below to find more information about <code>Azure Identity<\/code> and supported credentials for each SDK:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\/tree\/main\/sdk\/identity\/Azure.Identity\">Azure Identity SDK &#8211; .NET<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-python\/tree\/main\/sdk\/identity\/azure-identity\">Azure Identity SDK &#8211; Python<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-java\/tree\/main\/sdk\/identity\/azure-identity\">Azure Identity SDK &#8211; Java<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-js\/tree\/main\/sdk\/identity\/identity\">Azure Identity SDK &#8211; JS<\/a><\/li>\n<\/ul>\n<h5>.NET<\/h5>\n<pre><code class=\"language-cs\">var endpoint = new Uri(\"&lt;endpoint&gt;\");\r\nvar credential = new AzureKeyCredential(\"&lt;apiKey&gt;\");\r\nvar client = new DocumentAnalysisClient(endpoint, credential);<\/code><\/pre>\n<h5>Python<\/h5>\n<pre><code class=\"language-python\">document_analysis_client = DocumentAnalysisClient(\r\n    endpoint=\"&lt;endpoint&gt;\", credential=AzureKeyCredential(\"api_key\")\r\n)<\/code><\/pre>\n<h5>Java<\/h5>\n<pre><code class=\"language-java\">DocumentAnalysisClient client = new DocumentAnalysisClientBuilder()\r\n    .credential(new AzureKeyCredential(\"{key}\"))\r\n    .endpoint(\"https:\/\/{endpoint}.cognitiveservices.azure.com\/\")\r\n    .buildClient();<\/code><\/pre>\n<h5>JS<\/h5>\n<pre><code class=\"language-js\">const endpoint = \"&lt;endpoint&gt;\";\r\nconst credential = new AzureKeyCredential(\"&lt;api key&gt;\");\r\nconst client = new DocumentAnalysisClient(endpoint, credential);<\/code><\/pre>\n<h4>Example: Analyze a receipt using a prebuilt model<\/h4>\n<p>Below is an example of analyzing a receipt using the <code>\"prebuilt-receipt\"<\/code> model provided by the service and extracting fields specific to a receipt.<\/p>\n<h5>.NET<\/h5>\n<pre><code class=\"language-cs\">using var stream = new FileStream(\"&lt;path_to_receipt&gt;\", FileMode.Open);\r\n\r\nAnalyzeDocumentOperation operation = await client.AnalyzeDocumentAsync(WaitUntil.Completed, \"prebuilt-receipt\", stream);\r\nAnalyzeResult result = operation.Value;\r\n\r\nfor (int i = 0; i &lt; result.Documents.Count; i++)\r\n{\r\n    AnalyzedDocument receipt = result.Documents[i];\r\n\r\n    Console.WriteLine($\"--------Analysis of receipt #{i + 1}--------\");\r\n    Console.WriteLine($\"Receipt type: {receipt.DocumentType}\");\r\n\r\n    if (receipt.Fields.TryGetValue(\"MerchantName\", out DocumentField merchantName))\r\n    {\r\n        if (merchantName.FieldType == DocumentFieldType.String)\r\n        {\r\n            Console.WriteLine($\"Merchant Name: {merchantName.Value.AsString()} has confidence: {merchantName.Confidence}\");\r\n        }\r\n    }\r\n\r\n    if (receipt.Fields.TryGetValue(\"TotalTax\", out DocumentField tax))\r\n    {\r\n        if (tax.FieldType == DocumentFieldType.Double)\r\n        {\r\n            Console.WriteLine($\"Total tax: {tax.Value.AsDouble()} has confidence: {tax.Confidence}\");\r\n        }\r\n    }\r\n\r\n    if (receipt.Fields.TryGetValue(\"Total\", out DocumentField total))\r\n    {\r\n        if (total.FieldType == DocumentFieldType.Double)\r\n        {\r\n            Console.WriteLine($\"Total: {total.Value.AsDouble()} has confidence: {total.Confidence}\");\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<h5>Python<\/h5>\n<pre><code class=\"language-python\">with open(\"&lt;path_to_receipt&gt;\", \"rb\") as f:\r\n    poller = document_analysis_client.begin_analyze_document(\r\n        \"prebuilt-receipt\", document=f\r\n    )\r\nreceipts = poller.result()\r\n\r\nfor idx, receipt in enumerate(receipts.documents):\r\n    print(\"--------Analysis of receipt #{}--------\".format(idx + 1))\r\n    print(\"Receipt type: {}\".format(receipt.doc_type or \"N\/A\"))\r\n    merchant_name = receipt.fields.get(\"MerchantName\")\r\n    if merchant_name:\r\n        print(\r\n            \"Merchant Name: {} has confidence: {}\".format(\r\n                merchant_name.value, merchant_name.confidence\r\n            )\r\n        )\r\n    tax = receipt.fields.get(\"TotalTax\")\r\n    if tax:\r\n        print(\"Total tax: {} has confidence: {}\".format(tax.value, tax.confidence))\r\n    total = receipt.fields.get(\"Total\")\r\n    if total:\r\n        print(\"Total: {} has confidence: {}\".format(total.value, total.confidence))<\/code><\/pre>\n<h5>Java<\/h5>\n<pre><code class=\"language-java\">File sourceFile = new File(\"&lt;path_to_receipt&gt;\");\r\nPath filePath = sourceFile.toPath();\r\nBinaryData fileData = BinaryData.fromFile(filePath);\r\n\r\nSyncPoller&lt;OperationResult, AnalyzeResult&gt; analyzeReceiptPoller\r\n    = client.beginAnalyzeDocument(\"prebuilt-receipt\", fileData);\r\n\r\nAnalyzeResult analyzeResult = analyzeReceiptPoller.getFinalResult();\r\n\r\nfor (int i = 0; i &lt; analyzeResult.getDocuments().size(); i++) {\r\n    AnalyzedDocument analyzedReceipt = analyzeResult.getDocuments().get(i);\r\n    Map&lt;String, DocumentField&gt; receiptFields = analyzedReceipt.getFields();\r\n    System.out.printf(\"----------- Analyzing receipt info %d -----------%n\", i);\r\n    DocumentField merchantNameField = receiptFields.get(\"MerchantName\");\r\n    if (merchantNameField != null) {\r\n        if (DocumentFieldType.STRING == merchantNameField.getType()) {\r\n            String merchantName = merchantNameField.getValueAsString();\r\n            System.out.printf(\"Merchant Name: %s, confidence: %.2f%n\",\r\n                merchantName, merchantNameField.getConfidence());\r\n        }\r\n    }\r\n    DocumentField totalTaxField = receiptFields.get(\"TotalTax\");\r\n    if (totalTaxField != null) {\r\n        if (DocumentFieldType.DOUBLE == totalTaxField.getType()) {\r\n            Double totalTax = totalTaxField.getValueAsDouble();\r\n            System.out.printf(\"Total tax: %.2f, confidence: %.2f%n\",\r\n                totalTax, totalTaxField.getConfidence());\r\n        }\r\n    }\r\n    DocumentField totalTaxField = receiptFields.get(\"Total\");\r\n    if (totalField != null) {\r\n        if (DocumentFieldType.DOUBLE == totalField.getType()) {\r\n            Double total = totalField.getValueAsDouble();\r\n            System.out.printf(\"Total: %.2f, confidence: %.2f%n\",\r\n                total, totalField.getConfidence());\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<h5>JS<\/h5>\n<pre><code class=\"language-js\">const poller = await client.beginAnalyzeDocument(\r\n  \"prebuilt-receipt\",\r\n  fs.createReadStream(\"&lt;receipt file path&gt;\")\r\n);\r\n\r\nconst { documents } = await poller.pollUntilDone();\r\n\r\nfor (const receipt of documents ?? []) {\r\n  console.log(`- Receipt Type: ${receipt.docType}`);\r\n\r\n  const merchantNameField = receipt.fields[\"MerchantName\"];\r\n  if (merchantNameField) {\r\n    console.log(\r\n      `  Merchant Name: ${merchantNameField.value} (confidence: ${merchantNameField.confidence})`\r\n    );\r\n  }\r\n\r\n  const taxField = receipt.fields[\"TotalTax\"];\r\n  if (taxField) {\r\n    console.log(\r\n      `  Tax: ${taxField.value} (confidence: ${taxField.confidence})`\r\n    );\r\n  }\r\n\r\n  const totalField = receipt.fields[\"Total\"];\r\n  if (totalField) {\r\n    console.log(\r\n      `  Total: ${totalField.value} (confidence: ${totalField.confidence})`\r\n    );\r\n  }\r\n}<\/code><\/pre>\n<p>The document analysis methods return an <code>AnalyzeResult<\/code> model that is populated with the extracted data, such as the content, pages, paragraphs, tables, languages, styles, and key-value pairs, at the top level of the result. The fields on <code>AnalyzeResult<\/code> may or may not be populated with data depending on the model that is used for analysis. For a full table describing the data returned per model, see the <a href=\"https:\/\/learn.microsoft.com\/azure\/applied-ai-services\/form-recognizer\/concept-model-overview#model-data-extraction\">Model data extraction<\/a> table.<\/p>\n<h4>Example: Search for handwritten content using spans<\/h4>\n<p>Another feature of the new result structure is that many models have a span(s) field that indicates the offset and length where an item is found in the text content of the document. Some more unitary models have a single span, like document word, while larger components, like document style, can have a list of spans that they cover within the text content of the document. Below is an example from each SDK that searches the concatenated content of the document to find specific text sections that are handwritten.<\/p>\n<h5>.NET<\/h5>\n<pre><code class=\"language-cs\">using var stream = new FileStream(\"&lt;path_to_documents&gt;\", FileMode.Open);\r\n\r\nAnalyzeDocumentOperation operation = await client.AnalyzeDocumentAsync(WaitUntil.Completed, \"prebuilt-document\", stream);\r\nAnalyzeResult result = operation.Value;\r\n\r\nforeach (DocumentStyle style in result.Styles)\r\n{\r\n    if (style.IsHandwritten == true)\r\n    {\r\n        Console.WriteLine(\"Document contains handwritten content:\");\r\n\r\n        foreach (DocumentSpan span in style.Spans)\r\n        {\r\n            Console.WriteLine($\"  {result.Content.Substring(span.Index, span.Length)}\");\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<h5>Python<\/h5>\n<pre><code class=\"language-python\">with open(\"&lt;path_to_document&gt;\", \"rb\") as f:\r\n    poller = document_analysis_client.begin_analyze_document(\r\n        \"prebuilt-document\", document=f\r\n    )\r\nresult = poller.result()\r\n\r\nfor style in result.styles:\r\n    if style.is_handwritten:\r\n        print(\"Document contains handwritten content: \")\r\n        print(\",\".join([result.content[span.offset:span.offset + span.length] for span in style.spans]))<\/code><\/pre>\n<h5>Java<\/h5>\n<pre><code class=\"language-java\">File sourceFile = new File(\"&lt;path_to_receipt&gt;\");\r\nPath filePath = sourceFile.toPath();\r\nBinaryData fileData = BinaryData.fromFile(filePath);\r\nSyncPoller&lt;OperationResult, AnalyzeResult&gt; analyzePoller=\r\n    client.beginAnalyzeDocument(\"prebuilt-document\", fileData);\r\nAnalyzeResult analyzeResult= analyzeReceiptPoller.getFinalResult();\r\n\r\nanalyzeResult.getStyles()\r\n    .stream().filter(DocumentStyle::isHandwritten)\r\n    .forEach(documentStyle -&gt; documentStyle.getSpans()\r\n         .stream()\r\n         .map(documentSpan -&gt; analyzeResult.getContent().substring(documentSpan.getOffset(),\r\n             documentSpan.getOffset() + documentSpan.getLength()))\r\n         .forEach(System.out::println));<\/code><\/pre>\n<h5>JS<\/h5>\n<pre><code class=\"language-js\">const poller = await client.beginAnalyzeDocument(\r\n  \"prebuilt-document\",\r\n  fs.createReadStream(\"&lt;path to file&gt;\")\r\n);\r\n\r\nconst { styles, content } = await poller.pollUntilDone();\r\n\r\nconst handwrittenStyles = styles?.filter((style) =&gt; style.isHandwritten) ?? [];\r\n\r\nif (handwrittenStyles.length &gt; 0) {\r\n  console.log(\"Document contains handwritten text:\");\r\n\r\n  for (const style of handwrittenStyles) {\r\n    const slices = style.spans.map((span) =&gt;\r\n      content.slice(span.offset, span.offset + span.length)\r\n    );\r\n\r\n    console.log(`- ${slices.join(\",\")}`);\r\n  }\r\n}<\/code><\/pre>\n<h3>DocumentModelAdministrationClient<\/h3>\n<p>The <code>DocumentModelAdministrationClient<\/code> provides methods related to building, composing, copying, getting, and deleting document models in your Form Recognizer resource, as well as methods to get resource information, and list and get operations.<\/p>\n<h4>Example: Instantiate DocumentModelAdministrationClient<\/h4>\n<h5>.NET<\/h5>\n<pre><code class=\"language-cs\">var endpoint = new Uri(\"&lt;endpoint&gt;\");\r\nvar credential = new AzureKeyCredential(\"&lt;apiKey&gt;\");\r\nvar client = new DocumentModelAdministrationClient(endpoint, credential);<\/code><\/pre>\n<h5>Python<\/h5>\n<pre><code class=\"language-python\">document_model_admin_client = DocumentModelAdministrationClient(\r\n    \"&lt;endpoint&gt;\", AzureKeyCredential(\"&lt;api_key&gt;\")\r\n)<\/code><\/pre>\n<h5>Java<\/h5>\n<pre><code class=\"language-java\">DocumentModelAdministrationClient client = new DocumentModelAdministrationClientBuilder()\r\n    .credential(new AzureKeyCredential(\"{key}\"))\r\n    .endpoint(\"https:\/\/{endpoint}.cognitiveservices.azure.com\/\")\r\n    .buildClient();<\/code><\/pre>\n<h5>JS<\/h5>\n<pre><code class=\"language-js\">const endpoint = \"&lt;endpoint&gt;\";\r\nconst credential = new AzureKeyCredential(\"&lt;api key&gt;\");\r\nconst client = new DocumentModelAdministrationClient(endpoint, credential);<\/code><\/pre>\n<h4>Example: Build a custom document model<\/h4>\n<p>Below is an example of how to build a custom document model. In order to be able to build a custom model, you must provide a set of labeled training files that will be used by the machine-learning algorithm to create the model. These training files can be created and labeled through <a href=\"https:\/\/formrecognizer.appliedai.azure.com\/\">Form Recognizer Studio<\/a>, see more information about how to create your training dataset in <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/applied-ai-services\/form-recognizer\/build-training-data-set\">Building a training dataset<\/a>. Another important update in the latest release is the addition of build modes used to build custom models. Each build mode specifies a different machine-learning algorithm to use when creating a custom model. To get more information about each build mode, see the documentation on <a href=\"https:\/\/learn.microsoft.com\/azure\/applied-ai-services\/form-recognizer\/concept-custom#custom-model-types\">Custom model types<\/a>. Building a custom model relies on the document type in the training files and will only support that document type for analysis.<\/p>\n<blockquote><p>NOTE: The service provides composed model creation in order to group several custom models into one model ID. With a composed model, the service will perform a classification step when a document is sent to determine which custom model is the best option for analysis. An example of a composed model is the prebuilt model for analyzing identity documents, or <code>prebuilt-idDocument<\/code>, that supports analyzing U.S. driver&#8217;s licenses, U.S. state IDs, social security cards, permanent resident cards, and international passports. For more information, read about <a href=\"https:\/\/learn.microsoft.com\/azure\/applied-ai-services\/form-recognizer\/concept-composed-models?view=form-recog-3.0.0\">Composed custom models<\/a>.<\/p><\/blockquote>\n<h5>.NET<\/h5>\n<pre><code class=\"language-cs\">Uri blobContainerUri = new Uri(\"&lt;blobContainerUri&gt;\");\r\n\r\nBuildDocumentModelOperation operation = await client.BuildDocumentModelAsync(WaitUntil.Completed, blobContainerUri, DocumentBuildMode.Template);\r\nDocumentModelDetails model = operation.Value;\r\n\r\nConsole.WriteLine($\"Model ID: {model.ModelId}\");\r\nConsole.WriteLine($\"Description: {model.Description}\");\r\nConsole.WriteLine($\"Model created on: {model.CreatedOn}\");\r\nConsole.WriteLine($\"Document types the model can recognize:\");\r\n\r\nforeach (KeyValuePair&lt;string, DocumentTypeDetails&gt; docTypeKvp in model.DocumentTypes)\r\n{\r\n    string name = docTypeKvp.Key;\r\n    DocumentTypeDetails docType = docTypeKvp.Value;\r\n\r\n    Console.WriteLine($\"Document type: '{name}' built with '{docType.BuildMode}' mode which has the following fields:\");\r\n\r\n    foreach (KeyValuePair&lt;string, DocumentFieldSchema&gt; fieldKvp in docType.FieldSchema)\r\n    {\r\n        string fieldName = fieldKvp.Key;\r\n        DocumentFieldSchema field = fieldKvp.Value;\r\n        float confidence = docType.FieldConfidence[fieldName];\r\n\r\n        Console.WriteLine($\"Field: '{fieldName}' has type '{field.Type}' and confidence score {confidence}\");\r\n    }\r\n}<\/code><\/pre>\n<h5>Python<\/h5>\n<pre><code class=\"language-python\">poller = document_model_admin_client.begin_build_document_model(\r\n    ModelBuildMode.TEMPLATE, blob_container_url=\"&lt;container_sas_url&gt;\", description=\"my model description\"\r\n)\r\nmodel = poller.result()\r\n\r\nprint(\"Model ID: {}\".format(model.model_id))\r\nprint(\"Description: {}\".format(model.description))\r\nprint(\"Model created on: {}\\n\".format(model.created_on))\r\nprint(\"Doc types the model can recognize:\")\r\nfor name, doc_type in model.doc_types.items():\r\n    print(\"\\nDoc Type: '{}' built with '{}' mode which has the following fields:\".format(name, doc_type.build_mode))\r\n    for field_name, field in doc_type.field_schema.items():\r\n        print(\"Field: '{}' has type '{}' and confidence score {}\".format(\r\n            field_name, field[\"type\"], doc_type.field_confidence[field_name]\r\n        ))<\/code><\/pre>\n<h5>Java<\/h5>\n<pre><code class=\"language-java\">\/\/ Build custom document analysis model\r\nString blobContainerUrl = \"{SAS_URL_of_your_container_in_blob_storage}\";\r\n\/\/ The shared access signature (SAS) Url of your Azure Blob Storage container with your forms.\r\nString prefix = \"{blob_name_prefix}\";\r\nSyncPoller&lt;OperationResult, DocumentModelDetails&gt; buildOperationPoller =\r\n    client.beginBuildDocumentModel(blobContainerUrl,\r\n        DocumentModelBuildMode.TEMPLATE,\r\n        prefix,\r\n        new BuildDocumentModelOptions()\r\n            .setModelId(\"custom-model-id\")\r\n            .setDescription(\"model desc\"),\r\n        Context.NONE);\r\n\r\nDocumentModelDetails documentModelDetails = buildOperationPoller.getFinalResult();\r\n\r\n\/\/ Model Info\r\nSystem.out.printf(\"Model ID: %s%n\", documentModelDetails.getModelId());\r\nSystem.out.printf(\"Model Description: %s%n\", documentModelDetails.getDescription());\r\nSystem.out.printf(\"Model created on: %s%n%n\", documentModelDetails.getCreatedOn());\r\n\r\nSystem.out.println(\"Doc types the model can recognize:\");\r\ndocumentModelDetails.getDocumentTypes().forEach((name, documentTypeDetails) -&gt; {\r\n    System.out.printf(\"\\nDoc Type: %s built with %s mode which has the following fields:\",\r\n        name,\r\n        documentTypeDetails.getBuildMode());\r\n    documentTypeDetails.getFieldSchema().forEach((fieldName, documentFieldSchema) -&gt;\r\n        System.out.printf(\"Field: %s has type %s and confidence score %.2f\",\r\n            fieldName,\r\n            documentFieldSchema.getType(),\r\n            documentTypeDetails.getFieldConfidence()));\r\n});<\/code><\/pre>\n<h5>JS<\/h5>\n<pre><code class=\"language-js\">const poller = await client.beginBuildDocumentModel(\r\n  \"&lt;model id&gt;\",\r\n  \"&lt;training container SAS URL&gt;\",\r\n  DocumentModelBuildMode.Template,\r\n  {\r\n    description: \"an example model\",\r\n  }\r\n);\r\n\r\nconst model = await poller.pollUntilDone();\r\n\r\nconsole.log(\"Model ID:\", model.modelId);\r\nconsole.log(\"Description:\", model.description);\r\nconsole.log(\"Created:\", model.createdOn);\r\n\r\nconsole.log(\"Document Types:\");\r\nfor (const [\r\n  docType,\r\n  { description, fieldSchema: schema, buildMode, fieldConfidence },\r\n] of Object.entries(model.docTypes ?? {})) {\r\n  console.log(`- Name: \"${docType}\"`);\r\n  console.log(`  Build mode: ${buildMode}`);\r\n  console.log(`  Description: \"${description ?? \"&lt;no description&gt;\"}\"`);\r\n\r\n  \/\/ For simplicity, this example will only show top-level field names\r\n\r\n  console.log(\"  Fields:\");\r\n  for (const [fieldName, fieldSchema] of Object.entries(schema)) {\r\n    console.log(`  - \"${fieldName}\" (${fieldSchema.type})`);\r\n    console.log(\r\n      `    Description: ${fieldSchema.description ?? \"&lt;no description&gt;\"}`\r\n    );\r\n    console.log(\r\n      `    Confidence: ${fieldConfidence?.[fieldName] ?? \"&lt;unknown&gt;\"}`\r\n    );\r\n  }\r\n}<\/code><\/pre>\n<h4>Example: Analyze a document with your custom model<\/h4>\n<p>Once you&#8217;ve built your custom model, it can be used for the analysis of those custom document types it was trained on.<\/p>\n<h5>.NET<\/h5>\n<pre><code class=\"language-cs\">string modelId = \"&lt;model_id&gt;\";\r\nusing var stream = new FileStream(\"&lt;path_to_documents&gt;\", FileMode.Open);\r\n\r\nAnalyzeDocumentOperation operation = await client.AnalyzeDocumentAsync(WaitUntil.Completed, modelId, stream);\r\nAnalyzeResult result = operation.Value;\r\n\r\nfor (int i = 0; i &lt; result.Documents.Count; i++)\r\n{\r\n    AnalyzedDocument document = result.Documents[i];\r\n\r\n    Console.WriteLine($\"--------Analyzing document #{i + 1}--------\");\r\n    Console.WriteLine($\"Document has type: {document.DocumentType}\");\r\n    Console.WriteLine($\"Document has confidence: {document.Confidence}\");\r\n    Console.WriteLine($\"Document was analyzed by model with ID {result.ModelId}\");\r\n\r\n    foreach (DocumentField field in document.Fields.Values)\r\n    {\r\n        Console.WriteLine($\"......found field of type '{field.FieldType}' with content '{field.Content}' and with confidence {field.Confidence}\");\r\n    }\r\n}\r\n\r\nfor (int i = 0; i &lt; result.Tables.Count; i++)\r\n{\r\n    DocumentTable table = result.Tables[i];\r\n\r\n    Console.WriteLine($\"Table {i + 1} can be found on page:\");\r\n\r\n    foreach (BoundingRegion region in table.BoundingRegions)\r\n    {\r\n        Console.WriteLine($\"...{region.PageNumber}\");\r\n    }\r\n\r\n    foreach (DocumentTableCell cell in table.Cells)\r\n    {\r\n        Console.WriteLine($\"...Cell[{cell.RowIndex}][{cell.ColumnIndex}] has content '{cell.Content}'\");\r\n    }\r\n}<\/code><\/pre>\n<h5>Python<\/h5>\n<pre><code class=\"language-python\"># Make sure your document's type is included in the list of document types the custom model can analyze\r\nwith open(\"&lt;path_to_document&gt;\", \"rb\") as f:\r\n    poller = document_analysis_client.begin_analyze_document(\r\n        model_id=\"&lt;model_id&gt;\", document=f\r\n    )\r\nresult = poller.result()\r\n\r\nfor idx, document in enumerate(result.documents):\r\n    print(\"--------Analyzing document #{}--------\".format(idx + 1))\r\n    print(\"Document has type {}\".format(document.doc_type))\r\n    print(\"Document has confidence {}\".format(document.confidence))\r\n    print(\"Document was analyzed by model with ID {}\".format(result.model_id))\r\n    for name, field in document.fields.items():\r\n        field_value = field.value if field.value else field.content\r\n        print(\"......found field of type '{}' with value '{}' and with confidence {}\".format(field.value_type, field_value, field.confidence))\r\n\r\nfor i, table in enumerate(result.tables):\r\n    print(\"\\nTable {} can be found on page:\".format(i + 1))\r\n    for region in table.bounding_regions:\r\n        print(\"...{}\".format(region.page_number))\r\n    for cell in table.cells:\r\n        print(\r\n            \"...Cell[{}][{}] has content '{}'\".format(\r\n                cell.row_index, cell.column_index, cell.content\r\n            )\r\n        )<\/code><\/pre>\n<h5>Java<\/h5>\n<pre><code class=\"language-java\">String document = \"{document-path}\";\r\nString modelId = \"{custom-built-model-ID}\";\r\nSyncPoller&lt;OperationResult, AnalyzeResult&gt; analyzeDocumentPoller =\r\n    client.beginAnalyzeDocument(modelId, document);\r\n\r\nAnalyzeResult analyzeResult = analyzeDocumentPoller.getFinalResult();\r\n\r\nfor (int i = 0; i &lt; analyzeResult.getDocuments().size(); i++) {\r\n    final AnalyzedDocument analyzedDocument = analyzeResult.getDocuments().get(i);\r\n    System.out.printf(\"----------- Analyzing custom document %d -----------%n\", i);\r\n    System.out.printf(\"Analyzed document has doc type %s with confidence : %.2f%n\",\r\n        analyzedDocument.getDocType(), analyzedDocument.getConfidence());\r\n}\r\n\r\n\/\/ tables\r\nList&lt;DocumentTable&gt; tables = analyzeResult.getTables();\r\nfor (int i = 0; i &lt; tables.size(); i++) {\r\n    DocumentTable documentTable = tables.get(i);\r\n    System.out.printf(\"Table %d has %d rows and %d columns.%n\", i, documentTable.getRowCount(),\r\n        documentTable.getColumnCount());\r\n    documentTable.getCells().forEach(documentTableCell -&gt; {\r\n        System.out.printf(\"Cell '%s', has row index %d and column index %d.%n\",\r\n            documentTableCell.getContent(),\r\n            documentTableCell.getRowIndex(), documentTableCell.getColumnIndex());\r\n    });\r\n    System.out.println();\r\n}<\/code><\/pre>\n<h5>JS<\/h5>\n<pre><code class=\"language-js\">const poller = await client.beginAnalyzeDocument(\r\n  \"&lt;model id&gt;\",\r\n  fs.createReadStream(\"&lt;path to file&gt;\")\r\n);\r\n\r\nconst { modelId, documents, tables, pages } = await poller.pollUntilDone();\r\n\r\nconsole.log(`Results from model: \"${modelId}\"`);\r\n\r\nconsole.log(\"Documents:\");\r\n\r\nfor (const document of documents ?? []) {\r\n  console.log(\r\n    `- Document Type: ${document.docType} (confidence: ${document.confidence})`\r\n  );\r\n\r\n  console.log(\"  Fields:\");\r\n\r\n  for (const [name, field] of Object.entries(document.fields)) {\r\n    console.log(\r\n      `  - ${name} (${field.kind}): ${\r\n        field.value ?? \"&lt;unknown value&gt;\"\r\n      } (confidence: ${field.confidence})`\r\n    );\r\n  }\r\n}\r\n\r\nconsole.log(\"Tables:\");\r\n\r\nfor (const table of tables ?? []) {\r\n  console.log(\r\n    `- Table (${table.rowCount}x${table.columnCount}, ${table.cells.length} cells):`\r\n  );\r\n\r\n  console.log(\"  Bounding Regions:\");\r\n\r\n  for (const region of table.boundingRegions ?? []) {\r\n    const pageUnits = pages?.[region.pageNumber - 1]?.unit ?? \"&lt;unknown&gt;\";\r\n\r\n    console.log(\r\n      `  - Page ${region.pageNumber} (unit: ${pageUnits}), [${region.polygon\r\n        ?.map(({ x, y }) =&gt; `(${x},${y})`)\r\n        .join(\",\")}]`\r\n    );\r\n  }\r\n\r\n  console.log(\"  Cells:\");\r\n\r\n  for (const cell of table.cells) {\r\n    console.log(\r\n      `  - Cell (${cell.rowIndex},${cell.columnIndex}): \"${cell.content}\"`\r\n    );\r\n  }\r\n}<\/code><\/pre>\n<h4>Example: List the document models within your Form Recognizer resource<\/h4>\n<p>You can list the models that exist within your Form Recognizer resource, the list includes the prebuilt models that exist in the resource.<\/p>\n<h5>.NET<\/h5>\n<pre><code class=\"language-cs\">await foreach (DocumentModelSummary model in client.GetDocumentModelsAsync())\r\n{\r\n    Console.WriteLine($\"- Model ID: {model.ModelId}\");\r\n    Console.WriteLine($\"  Description: {model.Description}\");\r\n    Console.WriteLine($\"  Created on: {model.CreatedOn}\");\r\n}<\/code><\/pre>\n<h5>Python<\/h5>\n<pre><code class=\"language-python\">models = document_model_admin_client.list_document_models()\r\n\r\nprint(\"We have the following 'ready' models:\")\r\nprint(\"Model ID | Description | Created on\")\r\nfor model in models:\r\n    print(\"{} | {} | {}\".format(model.model_id, model.description, model.created_on))<\/code><\/pre>\n<h5>Java<\/h5>\n<pre><code class=\"language-java\">System.out.println(\"We have following models in the account:\");\r\nclient.listDocumentModels().forEach(documentModelInfo -&gt; {\r\n    System.out.printf(\"Model ID: %s%n\", documentModelInfo.getModelId());\r\n    System.out.printf(\"Model Description: %s%n\", documentModelInfo.getDescription());\r\n    System.out.printf(\"Model Created on: %s%n\", documentModelInfo.getCreatedOn());\r\n}<\/code><\/pre>\n<h5>JS<\/h5>\n<pre><code class=\"language-js\">for await (const modelSummary of client.listDocumentModels()) {\r\n    console.log(\"- ID:\", modelSummary.modelId);\r\n    console.log(\"  Created:\", modelSummary.createdOn);\r\n    console.log(\"  Description: \", modelSummary.description || \"&lt;none&gt;\");\r\n}<\/code><\/pre>\n<h2>Summary<\/h2>\n<p>The Azure Form Recognizer SDKs have released new stable versions that feature two new clients, the <code>DocumentAnalysisClient<\/code> and <code>DocumentModelAdministrationClient<\/code>. The new clients provide methods to use and interact with the latest stable API version of the Form Recognizer service. These SDKs look to help improve your interaction with the service. If you&#8217;re currently using the <code>FormRecognizerClient<\/code> and\/or <code>FormTrainingClient<\/code> and want to migrate to the latest versions, take a look at the migration guides linked above.<\/p>\n<p>Additionally, see the samples for each SDK below:<\/p>\n<table>\n<thead>\n<tr>\n<th>SDK<\/th>\n<th>Samples<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Azure Form Recognizer SDK &#8211; .NET<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\/tree\/main\/sdk\/formrecognizer\/Azure.AI.FormRecognizer\/samples\">Samples<\/a><\/td>\n<\/tr>\n<tr>\n<td>Azure Form Recognizer SDK &#8211; Python<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-python\/tree\/main\/sdk\/formrecognizer\/azure-ai-formrecognizer\/samples\">Samples<\/a><\/td>\n<\/tr>\n<tr>\n<td>Azure Form Recognizer SDK &#8211; Java<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-java\/tree\/main\/sdk\/formrecognizer\/azure-ai-formrecognizer\/src\/samples#examples\">Samples<\/a><\/td>\n<\/tr>\n<tr>\n<td>Azure Form Recognizer SDK &#8211; JS<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-js\/tree\/main\/sdk\/formrecognizer\/ai-form-recognizer\/samples\/v4\">Samples<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Feedback<\/h2>\n<p>The Azure SDK team is excited for you to try the client libraries and encourages feedback and questions. Feel free to reach out with feedback, questions, and\/or issues about the client library you&#8217;re using. The list below links to the repository for each Form Recognizer SDK:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\">Azure SDK for .NET<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-python\">Azure SDK for Python<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-java\">Azure SDK for Java<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-js\">Azure SDK for JS<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Azure Form Recognizer SDKs for .NET, Python, Java, and JS have released a new stable version (2022).<\/p>\n","protected":false},"author":102374,"featured_media":2269,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[786,706,703,161,749,160,159,162],"class_list":["post-2251","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-sdk","tag-ai","tag-azuresdk","tag-clientlibraries","tag-dotnet","tag-form-recognizer","tag-java","tag-javascript","tag-python"],"acf":[],"blog_post_summary":"<p>Azure Form Recognizer SDKs for .NET, Python, Java, and JS have released a new stable version (2022).<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/2251","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/users\/102374"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/comments?post=2251"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/2251\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media\/2269"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media?parent=2251"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/categories?post=2251"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/tags?post=2251"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}