operation = contentUnderstandingAsyncClient
+ .beginAnalyze("prebuilt-documentSearch", requestBody, new RequestOptions());
+
+ // Use reactive pattern: chain operations using flatMap
+ // In a real application, you would use subscribe() instead of block()
+ BinaryData responseData = operation.last().flatMap(pollResponse -> {
+ if (pollResponse.getStatus().isComplete()) {
+ return pollResponse.getFinalResult();
+ } else {
+ return Mono.error(
+ new RuntimeException("Polling completed unsuccessfully with status: " + pollResponse.getStatus()));
+ }
+ }).block(); // block() is used here for testing; in production, use subscribe()
+ // END:ContentUnderstandingAnalyzeReturnRawJsonAsync
+
+ // BEGIN:Assertion_ContentUnderstandingAnalyzeReturnRawJsonAsync
+ assertTrue(Files.exists(filePath), "Sample file should exist at " + filePath);
+ assertTrue(fileBytes.length > 0, "File should not be empty");
+ System.out.println("File loaded: " + filePath + " (" + String.format("%,d", fileBytes.length) + " bytes)");
+
+ assertNotNull(operation, "Analysis operation should not be null");
+ assertNotNull(responseData, "Response data should not be null");
+ assertTrue(responseData.toBytes().length > 0, "Response data should not be empty");
+ System.out.println("Response data size: " + String.format("%,d", responseData.toBytes().length) + " bytes");
+
+ // Verify response data can be converted to string
+ String responseString = responseData.toString();
+ assertNotNull(responseString, "Response string should not be null");
+ assertTrue(responseString.length() > 0, "Response string should not be empty");
+ System.out.println("Response string length: " + String.format("%,d", responseString.length()) + " characters");
+
+ // Verify response is valid JSON format
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode jsonNode = mapper.readTree(responseData.toBytes());
+ assertNotNull(jsonNode, "Response should be valid JSON");
+ System.out.println("Response is valid JSON format");
+ } catch (Exception ex) {
+ fail("Response data is not valid JSON: " + ex.getMessage());
+ }
+
+ System.out.println("Raw JSON analysis operation completed successfully");
+ // END:Assertion_ContentUnderstandingAnalyzeReturnRawJsonAsync
+
+ // BEGIN:ContentUnderstandingParseRawJsonAsync
+ // Parse the raw JSON response
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode jsonNode = mapper.readTree(responseData.toBytes());
+
+ // Pretty-print the JSON
+ String prettyJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
+
+ // Create output directory if it doesn't exist
+ Path outputDir = Paths.get("target/sample_output");
+ Files.createDirectories(outputDir);
+
+ // Save to file
+ String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
+ String outputFileName = "analyze_result_" + timestamp + ".json";
+ Path outputPath = outputDir.resolve(outputFileName);
+ Files.write(outputPath, prettyJson.getBytes(java.nio.charset.StandardCharsets.UTF_8));
+
+ System.out.println("Raw JSON response saved to: " + outputPath);
+ System.out.println("File size: " + String.format("%,d", prettyJson.length()) + " characters");
+ // END:ContentUnderstandingParseRawJsonAsync
+
+ // BEGIN:Assertion_ContentUnderstandingParseRawJsonAsync
+ assertNotNull(jsonNode, "JSON node should not be null");
+ System.out.println("JSON document parsed successfully");
+
+ assertNotNull(prettyJson, "Pretty JSON string should not be null");
+ assertTrue(prettyJson.length() > 0, "Pretty JSON should not be empty");
+ assertTrue(prettyJson.length() >= responseData.toString().length(),
+ "Pretty JSON should be same size or larger than original (due to indentation)");
+ System.out.println("Pretty JSON generated: " + String.format("%,d", prettyJson.length()) + " characters");
+
+ // Verify JSON is properly indented
+ assertTrue(prettyJson.contains("\n"), "Pretty JSON should contain line breaks");
+ assertTrue(prettyJson.contains(" "), "Pretty JSON should contain indentation");
+ System.out.println("JSON is properly formatted with indentation");
+
+ // Verify output directory
+ assertNotNull(outputDir, "Output directory path should not be null");
+ assertTrue(Files.exists(outputDir), "Output directory should exist at " + outputDir);
+ System.out.println("Output directory verified: " + outputDir);
+
+ // Verify output file name format
+ assertNotNull(outputFileName, "Output file name should not be null");
+ assertTrue(outputFileName.startsWith("analyze_result_"),
+ "Output file name should start with 'analyze_result_'");
+ assertTrue(outputFileName.endsWith(".json"), "Output file name should end with '.json'");
+ System.out.println("Output file name: " + outputFileName);
+
+ // Verify output file path
+ assertNotNull(outputPath, "Output file path should not be null");
+ assertTrue(outputPath.toString().contains(outputDir.toString()), "Output path should contain output directory");
+ assertTrue(outputPath.toString().endsWith(".json"), "Output path should end with '.json'");
+ assertTrue(Files.exists(outputPath), "Output file should exist at " + outputPath);
+ System.out.println("Output file created: " + outputPath);
+
+ // Verify file content size
+ long fileSize = Files.size(outputPath);
+ assertTrue(fileSize > 0, "Output file should not be empty");
+ assertEquals(prettyJson.length(), fileSize, "File size should match pretty JSON length");
+ System.out.println("Output file size verified: " + String.format("%,d", fileSize) + " bytes");
+
+ System.out.println("Raw JSON parsing and saving completed successfully");
+ // END:Assertion_ContentUnderstandingParseRawJsonAsync
+ }
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample12_GetResultFile.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample12_GetResultFile.java
new file mode 100644
index 000000000000..02dc9d752876
--- /dev/null
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample12_GetResultFile.java
@@ -0,0 +1,297 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package com.azure.ai.contentunderstanding.tests.samples;
+
+import com.azure.ai.contentunderstanding.models.AnalysisInput;
+import com.azure.ai.contentunderstanding.models.AnalysisResult;
+import com.azure.ai.contentunderstanding.models.AudioVisualContent;
+import com.azure.ai.contentunderstanding.models.DocumentContent;
+import com.azure.core.util.BinaryData;
+import com.azure.core.util.polling.SyncPoller;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Sample demonstrates how to retrieve result files (like keyframe images) from video analysis operations.
+ */
+public class Sample12_GetResultFile extends ContentUnderstandingClientTestBase {
+
+ /**
+ * Synchronous sample for getting result files from a completed analysis operation.
+ *
+ * Note: The Azure Content Understanding service requires extended time after analysis
+ * completion for keyframe result files to become available. This test uses retry logic
+ * to handle the delay.
+ */
+ @Test
+ public void testGetResultFile() throws IOException {
+
+ // BEGIN: com.azure.ai.contentunderstanding.getResultFile
+ // For video analysis, use a video URL to get keyframes
+ String videoUrl
+ = "https://github.com/Azure-Samples/azure-ai-content-understanding-assets/raw/refs/heads/main/videos/sdk_samples/FlightSimulator.mp4";
+
+ // Step 1: Start the video analysis operation
+ AnalysisInput input = new AnalysisInput();
+ input.setUrl(videoUrl);
+
+ SyncPoller poller
+ = contentUnderstandingClient.beginAnalyze("prebuilt-videoSearch", Arrays.asList(input));
+
+ System.out.println("Started analysis operation");
+
+ // Wait for completion
+ AnalysisResult result = poller.getFinalResult();
+ System.out.println("Analysis completed successfully!");
+
+ // Get the operation ID from the polling result using the getId() convenience method
+ // The operation ID is extracted from the Operation-Location header and can be used with
+ // getResultFile() and deleteResult() APIs
+ String operationId = poller.poll().getValue().getId();
+ System.out.println("Operation ID: " + operationId);
+
+ // END: com.azure.ai.contentunderstanding.getResultFile
+
+ // Verify operation started and completed
+ assertNotNull(videoUrl, "Video URL should not be null");
+ System.out.println("Video URL: " + videoUrl);
+
+ assertNotNull(operationId, "Operation ID should not be null");
+ assertFalse(operationId.trim().isEmpty(), "Operation ID should not be empty");
+ assertTrue(operationId.length() > 0, "Operation ID should have length > 0");
+ assertFalse(operationId.contains(" "), "Operation ID should not contain spaces");
+ System.out.println("Operation ID obtained: " + operationId);
+ System.out.println(" Length: " + operationId.length() + " characters");
+
+ // Verify result
+ assertNotNull(result, "Analysis result should not be null");
+ assertNotNull(result.getContents(), "Result should contain contents");
+ assertTrue(result.getContents().size() > 0, "Result should have at least one content");
+ System.out.println("Analysis result contains " + result.getContents().size() + " content(s)");
+
+ // BEGIN: com.azure.ai.contentunderstanding.getResultFile.keyframes
+ // Step 2: Get keyframes from video analysis result
+ AudioVisualContent videoContent = null;
+ for (Object content : result.getContents()) {
+ if (content instanceof AudioVisualContent) {
+ videoContent = (AudioVisualContent) content;
+ break;
+ }
+ }
+
+ if (videoContent != null
+ && videoContent.getKeyFrameTimes() != null
+ && !videoContent.getKeyFrameTimes().isEmpty()) {
+ List keyFrameTimes
+ = videoContent.getKeyFrameTimes().stream().map(Duration::toMillis).collect(Collectors.toList());
+ System.out.println("Total keyframes: " + keyFrameTimes.size());
+
+ // Get the first keyframe
+ long firstFrameTimeMs = keyFrameTimes.get(0);
+ System.out.println("First keyframe time: " + firstFrameTimeMs + " ms");
+
+ // Construct the keyframe path
+ String framePath = "keyframes/" + firstFrameTimeMs;
+ System.out.println("Getting result file: " + framePath);
+
+ // Retrieve the keyframe image using convenience method with retry logic
+ // Result files may not be immediately available after analysis completion
+ BinaryData fileData = null;
+ int maxRetries = 12;
+ int retryDelayMs = 10000;
+ for (int attempt = 1; attempt <= maxRetries; attempt++) {
+ try {
+ fileData = contentUnderstandingClient.getResultFile(operationId, framePath);
+ break; // Success, exit retry loop
+ } catch (Exception e) {
+ if (attempt == maxRetries) {
+ throw e; // Re-throw on final attempt
+ }
+ System.out.println("Attempt " + attempt + " failed: " + e.getMessage());
+ System.out.println("Waiting " + (retryDelayMs / 1000) + " seconds before retry...");
+ try {
+ Thread.sleep(retryDelayMs);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Interrupted while waiting for retry", ie);
+ }
+ }
+ }
+ byte[] imageBytes = fileData.toBytes();
+ System.out.println("Retrieved keyframe image (" + String.format("%,d", imageBytes.length) + " bytes)");
+
+ // Save the keyframe image
+ Path outputDir = Paths.get("target", "sample_output");
+ Files.createDirectories(outputDir);
+ String outputFileName = "keyframe_" + firstFrameTimeMs + ".jpg";
+ Path outputPath = outputDir.resolve(outputFileName);
+ Files.write(outputPath, imageBytes);
+
+ System.out.println("Keyframe image saved to: " + outputPath.toAbsolutePath());
+ // END: com.azure.ai.contentunderstanding.getResultFile.keyframes
+
+ // Verify video content
+ assertNotNull(videoContent, "Video content should not be null");
+ assertNotNull(keyFrameTimes, "KeyFrameTimesMs should not be null");
+ assertTrue(keyFrameTimes.size() > 0, "Should have at least one keyframe");
+ System.out.println("\n🎬 Keyframe Information:");
+ System.out.println("Total keyframes: " + keyFrameTimes.size());
+
+ // Verify keyframe times are valid
+ for (long frameTime : keyFrameTimes) {
+ assertTrue(frameTime >= 0, "Keyframe time should be non-negative, but was " + frameTime);
+ }
+
+ // Get keyframe statistics
+ long lastFrameTimeMs = keyFrameTimes.get(keyFrameTimes.size() - 1);
+ double avgFrameInterval = keyFrameTimes.size() > 1
+ ? (double) (lastFrameTimeMs - firstFrameTimeMs) / (keyFrameTimes.size() - 1)
+ : 0;
+
+ assertTrue(firstFrameTimeMs >= 0, "First keyframe time should be >= 0");
+ assertTrue(lastFrameTimeMs >= firstFrameTimeMs, "Last keyframe time should be >= first keyframe time");
+
+ System.out.println(" First keyframe: " + firstFrameTimeMs + " ms ("
+ + String.format("%.2f", firstFrameTimeMs / 1000.0) + " seconds)");
+ System.out.println(" Last keyframe: " + lastFrameTimeMs + " ms ("
+ + String.format("%.2f", lastFrameTimeMs / 1000.0) + " seconds)");
+ if (keyFrameTimes.size() > 1) {
+ System.out.println(" Average interval: " + String.format("%.2f", avgFrameInterval) + " ms");
+ }
+
+ // Verify file data
+ System.out.println("\n📥 File Data Verification:");
+ assertNotNull(fileData, "File data should not be null");
+
+ // Verify image data
+ System.out.println("\nVerifying image data...");
+ assertNotNull(imageBytes, "Image bytes should not be null");
+ assertTrue(imageBytes.length > 0, "Image should have content");
+ assertTrue(imageBytes.length >= 100, "Image should have reasonable size (>= 100 bytes)");
+ System.out.println("Image size: " + String.format("%,d", imageBytes.length) + " bytes ("
+ + String.format("%.2f", imageBytes.length / 1024.0) + " KB)");
+
+ // Verify image format
+ String imageFormat = detectImageFormat(imageBytes);
+ System.out.println("Detected image format: " + imageFormat);
+ assertNotEquals("Unknown", imageFormat, "Image format should be recognized");
+
+ // Verify saved file
+ System.out.println("\n💾 Saved File Verification:");
+ assertTrue(Files.exists(outputPath), "Saved file should exist");
+ long fileSize = Files.size(outputPath);
+ assertTrue(fileSize > 0, "Saved file should have content");
+ assertEquals(imageBytes.length, fileSize, "Saved file size should match image size");
+ System.out.println("File saved: " + outputPath.toAbsolutePath());
+ System.out.println("File size verified: " + String.format("%,d", fileSize) + " bytes");
+
+ // Verify file can be read back
+ byte[] readBackBytes = Files.readAllBytes(outputPath);
+ assertEquals(imageBytes.length, readBackBytes.length, "Read back file size should match original");
+ System.out.println("File content verified (read back matches original)");
+
+ // Test additional keyframes if available
+ if (keyFrameTimes.size() > 1) {
+ System.out
+ .println("\nTesting additional keyframes (" + (keyFrameTimes.size() - 1) + " more available)...");
+ int middleIndex = keyFrameTimes.size() / 2;
+ long middleFrameTimeMs = keyFrameTimes.get(middleIndex);
+ String middleFramePath = "keyframes/" + middleFrameTimeMs;
+
+ BinaryData middleFileData = contentUnderstandingClient.getResultFile(operationId, middleFramePath);
+ assertNotNull(middleFileData, "Middle keyframe data should not be null");
+ assertTrue(middleFileData.toBytes().length > 0, "Middle keyframe should have content");
+ System.out.println(
+ "Successfully retrieved keyframe at index " + middleIndex + " (" + middleFrameTimeMs + " ms)");
+ System.out.println(" Size: " + String.format("%,d", middleFileData.toBytes().length) + " bytes");
+ }
+
+ // Summary
+ System.out.println("\n✅ Keyframe retrieval verification completed successfully:");
+ System.out.println(" Operation ID: " + operationId);
+ System.out.println(" Total keyframes: " + keyFrameTimes.size());
+ System.out.println(" First keyframe time: " + firstFrameTimeMs + " ms");
+ System.out.println(" Image format: " + imageFormat);
+ System.out.println(" Image size: " + String.format("%,d", imageBytes.length) + " bytes");
+ System.out.println(" Saved to: " + outputPath.toAbsolutePath());
+ System.out.println(" File verified: Yes");
+ } else {
+ // No video content (expected for document analysis)
+ System.out.println("\n📚 GetResultFile API Usage Example:");
+ System.out.println(" For video analysis with keyframes:");
+ System.out.println(" 1. Analyze video with prebuilt-videoSearch");
+ System.out.println(" 2. Get keyframe times from AudioVisualContent.getKeyFrameTimesMs()");
+ System.out.println(" 3. Retrieve keyframes using getResultFile():");
+ System.out.println(" BinaryData fileData = contentUnderstandingClient.getResultFile(\"" + operationId
+ + "\", \"keyframes/1000\");");
+ System.out.println(" 4. Save or process the keyframe image");
+
+ // Verify content type
+ if (result.getContents().get(0) instanceof DocumentContent) {
+ DocumentContent docContent = (DocumentContent) result.getContents().get(0);
+ System.out.println("\nContent type: DocumentContent (as expected)");
+ System.out.println(" MIME type: "
+ + (docContent.getMimeType() != null ? docContent.getMimeType() : "(not specified)"));
+ System.out
+ .println(" Pages: " + docContent.getStartPageNumber() + " - " + docContent.getEndPageNumber());
+ }
+
+ assertNotNull(operationId, "Operation ID should be available for GetResultFile API");
+ assertFalse(operationId.trim().isEmpty(), "Operation ID should not be empty");
+ System.out.println("Operation ID available for GetResultFile API: " + operationId);
+ }
+ }
+
+ /**
+ * Detect image format from magic bytes.
+ */
+ private String detectImageFormat(byte[] imageBytes) {
+ if (imageBytes.length < 2) {
+ return "Unknown";
+ }
+
+ // Check JPEG magic bytes (FF D8)
+ if (imageBytes[0] == (byte) 0xFF && imageBytes[1] == (byte) 0xD8) {
+ return "JPEG";
+ }
+
+ // Check PNG magic bytes (89 50 4E 47)
+ if (imageBytes.length >= 4
+ && imageBytes[0] == (byte) 0x89
+ && imageBytes[1] == 0x50
+ && imageBytes[2] == 0x4E
+ && imageBytes[3] == 0x47) {
+ return "PNG";
+ }
+
+ // Check GIF magic bytes (47 49 46)
+ if (imageBytes.length >= 3 && imageBytes[0] == 0x47 && imageBytes[1] == 0x49 && imageBytes[2] == 0x46) {
+ return "GIF";
+ }
+
+ // Check WebP magic bytes (52 49 46 46 ... 57 45 42 50)
+ if (imageBytes.length >= 12
+ && imageBytes[0] == 0x52
+ && imageBytes[1] == 0x49
+ && imageBytes[8] == 0x57
+ && imageBytes[9] == 0x45
+ && imageBytes[10] == 0x42
+ && imageBytes[11] == 0x50) {
+ return "WebP";
+ }
+
+ return "Unknown";
+ }
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample12_GetResultFileAsync.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample12_GetResultFileAsync.java
new file mode 100644
index 000000000000..f7844fdeb7a0
--- /dev/null
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample12_GetResultFileAsync.java
@@ -0,0 +1,310 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package com.azure.ai.contentunderstanding.tests.samples;
+
+import com.azure.ai.contentunderstanding.models.AnalysisInput;
+import com.azure.ai.contentunderstanding.models.AnalysisResult;
+import com.azure.ai.contentunderstanding.models.AudioVisualContent;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerAnalyzeOperationStatus;
+import com.azure.ai.contentunderstanding.models.DocumentContent;
+import com.azure.core.util.BinaryData;
+import com.azure.core.util.polling.PollerFlux;
+import org.junit.jupiter.api.Test;
+import reactor.core.publisher.Mono;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Async sample demonstrates how to retrieve result files (like keyframe images) from video analysis operations.
+ */
+public class Sample12_GetResultFileAsync extends ContentUnderstandingClientTestBase {
+
+ /**
+ * Asynchronous sample for getting result files from a completed analysis operation.
+ *
+ * Note: The Azure Content Understanding service requires extended time after analysis
+ * completion for keyframe result files to become available. This test uses retry logic
+ * to handle the delay.
+ */
+ @Test
+ public void testGetResultFileAsync() throws IOException {
+
+ // BEGIN: com.azure.ai.contentunderstanding.getResultFileAsync
+ // For video analysis, use a video URL to get keyframes
+ String videoUrl
+ = "https://github.com/Azure-Samples/azure-ai-content-understanding-assets/raw/refs/heads/main/videos/sdk_samples/FlightSimulator.mp4";
+
+ // Step 1: Start the video analysis operation
+ AnalysisInput input = new AnalysisInput();
+ input.setUrl(videoUrl);
+
+ PollerFlux poller
+ = contentUnderstandingAsyncClient.beginAnalyze("prebuilt-videoSearch", Arrays.asList(input));
+
+ System.out.println("Started analysis operation");
+
+ // Use reactive pattern: chain operations using flatMap
+ // In a real application, you would use subscribe() instead of block()
+ // Use AtomicReference to capture the operation ID from the polling response
+ AtomicReference operationIdRef = new AtomicReference<>();
+ AnalysisResult result = poller.last().flatMap(pollResponse -> {
+ if (pollResponse.getStatus().isComplete()) {
+ // Capture the operation ID for later use with getResultFile()
+ operationIdRef.set(pollResponse.getValue().getId());
+ return pollResponse.getFinalResult();
+ } else {
+ return Mono.error(
+ new RuntimeException("Polling completed unsuccessfully with status: " + pollResponse.getStatus()));
+ }
+ }).block(); // block() is used here for testing; in production, use subscribe()
+
+ System.out.println("Analysis completed successfully!");
+ String operationId = operationIdRef.get();
+ System.out.println("Operation ID: " + operationId);
+
+ // END: com.azure.ai.contentunderstanding.getResultFileAsync
+
+ // Verify operation started and completed
+ assertNotNull(videoUrl, "Video URL should not be null");
+ System.out.println("Video URL: " + videoUrl);
+
+ assertNotNull(operationId, "Operation ID should not be null");
+ assertFalse(operationId.trim().isEmpty(), "Operation ID should not be empty");
+ assertTrue(operationId.length() > 0, "Operation ID should have length > 0");
+ assertFalse(operationId.contains(" "), "Operation ID should not contain spaces");
+ System.out.println("Operation ID obtained: " + operationId);
+ System.out.println(" Length: " + operationId.length() + " characters");
+
+ // Verify result
+ assertNotNull(result, "Analysis result should not be null");
+ assertNotNull(result.getContents(), "Result should contain contents");
+ assertTrue(result.getContents().size() > 0, "Result should have at least one content");
+ System.out.println("Analysis result contains " + result.getContents().size() + " content(s)");
+
+ // BEGIN: com.azure.ai.contentunderstanding.getResultFileAsync.keyframes
+ // Step 2: Get keyframes from video analysis result
+ AudioVisualContent videoContent = null;
+ for (Object content : result.getContents()) {
+ if (content instanceof AudioVisualContent) {
+ videoContent = (AudioVisualContent) content;
+ break;
+ }
+ }
+
+ if (videoContent != null
+ && videoContent.getKeyFrameTimes() != null
+ && !videoContent.getKeyFrameTimes().isEmpty()) {
+ List keyFrameTimes
+ = videoContent.getKeyFrameTimes().stream().map(Duration::toMillis).collect(Collectors.toList());
+ System.out.println("Total keyframes: " + keyFrameTimes.size());
+
+ // Get the first keyframe
+ long firstFrameTimeMs = keyFrameTimes.get(0);
+ System.out.println("First keyframe time: " + firstFrameTimeMs + " ms");
+
+ // Construct the keyframe path
+ String framePath = "keyframes/" + firstFrameTimeMs;
+ System.out.println("Getting result file: " + framePath);
+
+ // Retrieve the keyframe image using convenience method with retry logic
+ // Result files may not be immediately available after analysis completion
+ BinaryData fileData = null;
+ int maxRetries = 12;
+ int retryDelayMs = 10000;
+ for (int attempt = 1; attempt <= maxRetries; attempt++) {
+ try {
+ fileData = contentUnderstandingAsyncClient.getResultFile(operationId, framePath).block();
+ break; // Success, exit retry loop
+ } catch (Exception e) {
+ if (attempt == maxRetries) {
+ throw e; // Re-throw on final attempt
+ }
+ System.out.println("Attempt " + attempt + " failed: " + e.getMessage());
+ System.out.println("Waiting " + (retryDelayMs / 1000) + " seconds before retry...");
+ try {
+ Thread.sleep(retryDelayMs);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Interrupted while waiting for retry", ie);
+ }
+ }
+ }
+ byte[] imageBytes = fileData.toBytes();
+ System.out.println("Retrieved keyframe image (" + String.format("%,d", imageBytes.length) + " bytes)");
+
+ // Save the keyframe image
+ Path outputDir = Paths.get("target", "sample_output");
+ Files.createDirectories(outputDir);
+ String outputFileName = "keyframe_" + firstFrameTimeMs + ".jpg";
+ Path outputPath = outputDir.resolve(outputFileName);
+ Files.write(outputPath, imageBytes);
+
+ System.out.println("Keyframe image saved to: " + outputPath.toAbsolutePath());
+ // END: com.azure.ai.contentunderstanding.getResultFileAsync.keyframes
+
+ // Verify video content
+ assertNotNull(videoContent, "Video content should not be null");
+ assertNotNull(keyFrameTimes, "KeyFrameTimesMs should not be null");
+ assertTrue(keyFrameTimes.size() > 0, "Should have at least one keyframe");
+ System.out.println("\n🎬 Keyframe Information:");
+ System.out.println("Total keyframes: " + keyFrameTimes.size());
+
+ // Verify keyframe times are valid
+ for (long frameTime : keyFrameTimes) {
+ assertTrue(frameTime >= 0, "Keyframe time should be non-negative, but was " + frameTime);
+ }
+
+ // Get keyframe statistics
+ long lastFrameTimeMs = keyFrameTimes.get(keyFrameTimes.size() - 1);
+ double avgFrameInterval = keyFrameTimes.size() > 1
+ ? (double) (lastFrameTimeMs - firstFrameTimeMs) / (keyFrameTimes.size() - 1)
+ : 0;
+
+ assertTrue(firstFrameTimeMs >= 0, "First keyframe time should be >= 0");
+ assertTrue(lastFrameTimeMs >= firstFrameTimeMs, "Last keyframe time should be >= first keyframe time");
+
+ System.out.println(" First keyframe: " + firstFrameTimeMs + " ms ("
+ + String.format("%.2f", firstFrameTimeMs / 1000.0) + " seconds)");
+ System.out.println(" Last keyframe: " + lastFrameTimeMs + " ms ("
+ + String.format("%.2f", lastFrameTimeMs / 1000.0) + " seconds)");
+ if (keyFrameTimes.size() > 1) {
+ System.out.println(" Average interval: " + String.format("%.2f", avgFrameInterval) + " ms");
+ }
+
+ // Verify file data
+ System.out.println("\n📥 File Data Verification:");
+ assertNotNull(fileData, "File data should not be null");
+
+ // Verify image data
+ System.out.println("\nVerifying image data...");
+ assertNotNull(imageBytes, "Image bytes should not be null");
+ assertTrue(imageBytes.length > 0, "Image should have content");
+ assertTrue(imageBytes.length >= 100, "Image should have reasonable size (>= 100 bytes)");
+ System.out.println("Image size: " + String.format("%,d", imageBytes.length) + " bytes ("
+ + String.format("%.2f", imageBytes.length / 1024.0) + " KB)");
+
+ // Verify image format
+ String imageFormat = detectImageFormat(imageBytes);
+ System.out.println("Detected image format: " + imageFormat);
+ assertNotEquals("Unknown", imageFormat, "Image format should be recognized");
+
+ // Verify saved file
+ System.out.println("\n💾 Saved File Verification:");
+ assertTrue(Files.exists(outputPath), "Saved file should exist");
+ long fileSize = Files.size(outputPath);
+ assertTrue(fileSize > 0, "Saved file should have content");
+ assertEquals(imageBytes.length, fileSize, "Saved file size should match image size");
+ System.out.println("File saved: " + outputPath.toAbsolutePath());
+ System.out.println("File size verified: " + String.format("%,d", fileSize) + " bytes");
+
+ // Verify file can be read back
+ byte[] readBackBytes = Files.readAllBytes(outputPath);
+ assertEquals(imageBytes.length, readBackBytes.length, "Read back file size should match original");
+ System.out.println("File content verified (read back matches original)");
+
+ // Test additional keyframes if available
+ if (keyFrameTimes.size() > 1) {
+ System.out
+ .println("\nTesting additional keyframes (" + (keyFrameTimes.size() - 1) + " more available)...");
+ int middleIndex = keyFrameTimes.size() / 2;
+ long middleFrameTimeMs = keyFrameTimes.get(middleIndex);
+ String middleFramePath = "keyframes/" + middleFrameTimeMs;
+
+ BinaryData middleFileData
+ = contentUnderstandingAsyncClient.getResultFile(operationId, middleFramePath).block();
+ assertNotNull(middleFileData, "Middle keyframe data should not be null");
+ assertTrue(middleFileData.toBytes().length > 0, "Middle keyframe should have content");
+ System.out.println(
+ "Successfully retrieved keyframe at index " + middleIndex + " (" + middleFrameTimeMs + " ms)");
+ System.out.println(" Size: " + String.format("%,d", middleFileData.toBytes().length) + " bytes");
+ }
+
+ // Summary
+ System.out.println("\n✅ Keyframe retrieval verification completed successfully:");
+ System.out.println(" Operation ID: " + operationId);
+ System.out.println(" Total keyframes: " + keyFrameTimes.size());
+ System.out.println(" First keyframe time: " + firstFrameTimeMs + " ms");
+ System.out.println(" Image format: " + imageFormat);
+ System.out.println(" Image size: " + String.format("%,d", imageBytes.length) + " bytes");
+ System.out.println(" Saved to: " + outputPath.toAbsolutePath());
+ System.out.println(" File verified: Yes");
+ } else {
+ // No video content (expected for document analysis)
+ System.out.println("\n📚 GetResultFile API Usage Example:");
+ System.out.println(" For video analysis with keyframes:");
+ System.out.println(" 1. Analyze video with prebuilt-videoSearch");
+ System.out.println(" 2. Get keyframe times from AudioVisualContent.getKeyFrameTimesMs()");
+ System.out.println(" 3. Retrieve keyframes using getResultFile():");
+ System.out.println(" BinaryData fileData = contentUnderstandingAsyncClient.getResultFile(\""
+ + operationId + "\", \"keyframes/1000\").block();");
+ System.out.println(" 4. Save or process the keyframe image");
+
+ // Verify content type
+ if (result.getContents().get(0) instanceof DocumentContent) {
+ DocumentContent docContent = (DocumentContent) result.getContents().get(0);
+ System.out.println("\nContent type: DocumentContent (as expected)");
+ System.out.println(" MIME type: "
+ + (docContent.getMimeType() != null ? docContent.getMimeType() : "(not specified)"));
+ System.out
+ .println(" Pages: " + docContent.getStartPageNumber() + " - " + docContent.getEndPageNumber());
+ }
+
+ assertNotNull(operationId, "Operation ID should be available for GetResultFile API");
+ assertFalse(operationId.trim().isEmpty(), "Operation ID should not be empty");
+ System.out.println("Operation ID available for GetResultFile API: " + operationId);
+ }
+ }
+
+ /**
+ * Detect image format from magic bytes.
+ */
+ private String detectImageFormat(byte[] imageBytes) {
+ if (imageBytes.length < 2) {
+ return "Unknown";
+ }
+
+ // Check JPEG magic bytes (FF D8)
+ if (imageBytes[0] == (byte) 0xFF && imageBytes[1] == (byte) 0xD8) {
+ return "JPEG";
+ }
+
+ // Check PNG magic bytes (89 50 4E 47)
+ if (imageBytes.length >= 4
+ && imageBytes[0] == (byte) 0x89
+ && imageBytes[1] == 0x50
+ && imageBytes[2] == 0x4E
+ && imageBytes[3] == 0x47) {
+ return "PNG";
+ }
+
+ // Check GIF magic bytes (47 49 46)
+ if (imageBytes.length >= 3 && imageBytes[0] == 0x47 && imageBytes[1] == 0x49 && imageBytes[2] == 0x46) {
+ return "GIF";
+ }
+
+ // Check WebP magic bytes (52 49 46 46 ... 57 45 42 50)
+ if (imageBytes.length >= 12
+ && imageBytes[0] == 0x52
+ && imageBytes[1] == 0x49
+ && imageBytes[8] == 0x57
+ && imageBytes[9] == 0x45
+ && imageBytes[10] == 0x42
+ && imageBytes[11] == 0x50) {
+ return "WebP";
+ }
+
+ return "Unknown";
+ }
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample13_DeleteResult.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample13_DeleteResult.java
new file mode 100644
index 000000000000..5b0c853cc9dd
--- /dev/null
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample13_DeleteResult.java
@@ -0,0 +1,109 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package com.azure.ai.contentunderstanding.tests.samples;
+
+import com.azure.ai.contentunderstanding.models.AnalysisInput;
+import com.azure.ai.contentunderstanding.models.AnalysisResult;
+import com.azure.ai.contentunderstanding.models.ContentField;
+import com.azure.ai.contentunderstanding.models.DocumentContent;
+import com.azure.core.util.polling.SyncPoller;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Sample demonstrates how to delete analysis results after they are no longer needed.
+ */
+public class Sample13_DeleteResult extends ContentUnderstandingClientTestBase {
+
+ /**
+ * Synchronous sample for analyzing a document and then deleting the result.
+ */
+ @Test
+ public void testDeleteResult() {
+
+ // BEGIN: com.azure.ai.contentunderstanding.deleteResult
+ // Step 1: Analyze a document
+ String documentUrl
+ = "https://github.com/Azure-Samples/cognitive-services-REST-api-samples/raw/master/curl/form-recognizer/sample-invoice.pdf";
+
+ AnalysisInput input = new AnalysisInput();
+ input.setUrl(documentUrl);
+
+ SyncPoller poller
+ = contentUnderstandingClient.beginAnalyze("prebuilt-invoice", Arrays.asList(input));
+
+ // Wait for operation to complete to get a result ID
+ System.out.println("Started analysis operation");
+
+ // Wait for completion
+ AnalysisResult result = poller.getFinalResult();
+ System.out.println("Analysis completed successfully!");
+
+ // Get the operation ID using the getId() convenience method
+ // This ID is extracted from the Operation-Location header and is needed for deleteResult()
+ String operationId = poller.poll().getValue().getId();
+ System.out.println("Operation ID: " + operationId);
+
+ // Display some sample results using getValue() convenience method
+ if (result.getContents() != null && !result.getContents().isEmpty()) {
+ Object firstContent = result.getContents().get(0);
+ if (firstContent instanceof DocumentContent) {
+ DocumentContent docContent = (DocumentContent) firstContent;
+ Map fields = docContent.getFields();
+ if (fields != null) {
+ System.out.println("Total fields extracted: " + fields.size());
+ ContentField customerNameField = fields.get("CustomerName");
+ if (customerNameField != null) {
+ // Use getValue() instead of casting to StringField
+ String customerName = (String) customerNameField.getValue();
+ System.out.println("Customer Name: " + (customerName != null ? customerName : "(not found)"));
+ }
+ }
+ }
+ }
+
+ // Step 2: Delete the analysis result using the operation ID
+ // This cleans up the server-side resources (including keyframe images for video analysis)
+ contentUnderstandingClient.deleteResult(operationId);
+ System.out.println("Analysis result deleted successfully!");
+ // END: com.azure.ai.contentunderstanding.deleteResult
+
+ // Verify operation
+ System.out.println("\n📋 Analysis Operation Verification:");
+ assertNotNull(documentUrl, "Document URL should not be null");
+ System.out.println("Document URL: " + documentUrl);
+ System.out.println("Analysis operation completed successfully");
+
+ // Verify result
+ assertNotNull(result, "Analysis result should not be null");
+ assertNotNull(result.getContents(), "Result should contain contents");
+ assertTrue(result.getContents().size() > 0, "Result should have at least one content");
+ assertEquals(1, result.getContents().size(), "Invoice should have exactly one content element");
+ System.out.println("Analysis result contains " + result.getContents().size() + " content(s)");
+
+ // Verify document content
+ Object firstContent = result.getContents().get(0);
+ assertTrue(firstContent instanceof DocumentContent, "Content should be DocumentContent");
+ DocumentContent documentContent = (DocumentContent) firstContent;
+ assertNotNull(documentContent.getFields(), "Document content should have fields");
+ System.out.println("Document content has " + documentContent.getFields().size() + " field(s)");
+
+ // API Pattern Demo
+ System.out.println("\n🗑️ Result Deletion API Pattern:");
+ System.out.println(" contentUnderstandingClient.deleteResultWithResponse(resultId, requestOptions)");
+ System.out.println(" Use the result ID from the analysis operation for cleanup");
+
+ // Summary
+ System.out.println("\n✅ DeleteResult API pattern demonstrated:");
+ System.out.println(" Analysis: Completed successfully");
+ System.out.println(" Fields extracted: " + documentContent.getFields().size());
+ System.out.println(" API: deleteResultWithResponse available for cleanup");
+ }
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample13_DeleteResultAsync.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample13_DeleteResultAsync.java
new file mode 100644
index 000000000000..926e9002b883
--- /dev/null
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample13_DeleteResultAsync.java
@@ -0,0 +1,121 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package com.azure.ai.contentunderstanding.tests.samples;
+
+import com.azure.ai.contentunderstanding.models.AnalysisInput;
+import com.azure.ai.contentunderstanding.models.AnalysisResult;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerAnalyzeOperationStatus;
+import com.azure.ai.contentunderstanding.models.ContentField;
+import com.azure.ai.contentunderstanding.models.DocumentContent;
+import com.azure.core.util.polling.PollerFlux;
+import org.junit.jupiter.api.Test;
+import reactor.core.publisher.Mono;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Async sample demonstrates how to delete analysis results after they are no longer needed.
+ */
+public class Sample13_DeleteResultAsync extends ContentUnderstandingClientTestBase {
+
+ /**
+ * Asynchronous sample for analyzing a document and then deleting the result.
+ */
+ @Test
+ public void testDeleteResultAsync() {
+
+ // BEGIN: com.azure.ai.contentunderstanding.deleteResultAsync
+ // Step 1: Analyze a document
+ String documentUrl
+ = "https://github.com/Azure-Samples/cognitive-services-REST-api-samples/raw/master/curl/form-recognizer/sample-invoice.pdf";
+
+ AnalysisInput input = new AnalysisInput();
+ input.setUrl(documentUrl);
+
+ PollerFlux poller
+ = contentUnderstandingAsyncClient.beginAnalyze("prebuilt-invoice", Arrays.asList(input));
+
+ // Wait for operation to complete to get a result ID
+ System.out.println("Started analysis operation");
+
+ // Use reactive pattern: chain operations using flatMap
+ // In a real application, you would use subscribe() instead of block()
+ // Use AtomicReference to capture the operation ID from the polling response
+ AtomicReference operationIdRef = new AtomicReference<>();
+ AnalysisResult result = poller.last().flatMap(pollResponse -> {
+ if (pollResponse.getStatus().isComplete()) {
+ // Capture the operation ID for later use with deleteResult()
+ operationIdRef.set(pollResponse.getValue().getId());
+ return pollResponse.getFinalResult();
+ } else {
+ return Mono.error(
+ new RuntimeException("Polling completed unsuccessfully with status: " + pollResponse.getStatus()));
+ }
+ }).block(); // block() is used here for testing; in production, use subscribe()
+
+ System.out.println("Analysis completed successfully!");
+ String operationId = operationIdRef.get();
+ System.out.println("Operation ID: " + operationId);
+
+ // Display some sample results using getValue() convenience method
+ if (result.getContents() != null && !result.getContents().isEmpty()) {
+ Object firstContent = result.getContents().get(0);
+ if (firstContent instanceof DocumentContent) {
+ DocumentContent docContent = (DocumentContent) firstContent;
+ Map fields = docContent.getFields();
+ if (fields != null) {
+ System.out.println("Total fields extracted: " + fields.size());
+ ContentField customerNameField = fields.get("CustomerName");
+ if (customerNameField != null) {
+ // Use getValue() instead of casting to StringField
+ String customerName = (String) customerNameField.getValue();
+ System.out.println("Customer Name: " + (customerName != null ? customerName : "(not found)"));
+ }
+ }
+ }
+ }
+
+ // Step 2: Delete the analysis result using the operation ID
+ // This cleans up the server-side resources (including keyframe images for video analysis)
+ contentUnderstandingAsyncClient.deleteResult(operationId).block();
+ System.out.println("Analysis result deleted successfully!");
+ // END: com.azure.ai.contentunderstanding.deleteResultAsync
+
+ // Verify operation
+ System.out.println("\n📋 Analysis Operation Verification:");
+ assertNotNull(documentUrl, "Document URL should not be null");
+ System.out.println("Document URL: " + documentUrl);
+ System.out.println("Analysis operation completed successfully");
+
+ // Verify result
+ assertNotNull(result, "Analysis result should not be null");
+ assertNotNull(result.getContents(), "Result should contain contents");
+ assertTrue(result.getContents().size() > 0, "Result should have at least one content");
+ assertEquals(1, result.getContents().size(), "Invoice should have exactly one content element");
+ System.out.println("Analysis result contains " + result.getContents().size() + " content(s)");
+
+ // Verify document content
+ Object firstContent = result.getContents().get(0);
+ assertTrue(firstContent instanceof DocumentContent, "Content should be DocumentContent");
+ DocumentContent documentContent = (DocumentContent) firstContent;
+ assertNotNull(documentContent.getFields(), "Document content should have fields");
+ System.out.println("Document content has " + documentContent.getFields().size() + " field(s)");
+
+ // API Pattern Demo
+ System.out.println("\n🗑️ Result Deletion API Pattern:");
+ System.out.println(" contentUnderstandingAsyncClient.deleteResult(resultId).block()");
+ System.out.println(" Use the result ID from the analysis operation for cleanup");
+
+ // Summary
+ System.out.println("\n✅ DeleteResult API pattern demonstrated:");
+ System.out.println(" Analysis: Completed successfully");
+ System.out.println(" Fields extracted: " + documentContent.getFields().size());
+ System.out.println(" API: deleteResult available for cleanup");
+ }
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzer.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzer.java
new file mode 100644
index 000000000000..dd2f03d17880
--- /dev/null
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzer.java
@@ -0,0 +1,370 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package com.azure.ai.contentunderstanding.tests.samples;
+
+import com.azure.ai.contentunderstanding.models.ContentAnalyzer;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerConfig;
+import com.azure.ai.contentunderstanding.models.ContentFieldDefinition;
+import com.azure.ai.contentunderstanding.models.ContentFieldSchema;
+import com.azure.ai.contentunderstanding.models.ContentFieldType;
+import com.azure.ai.contentunderstanding.models.GenerationMethod;
+import com.azure.core.util.polling.SyncPoller;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Sample demonstrates how to copy an analyzer within the same resource.
+ * For cross-resource copying, see Sample15_GrantCopyAuth.
+ */
+public class Sample14_CopyAnalyzer extends ContentUnderstandingClientTestBase {
+
+ /**
+ * Synchronous sample for copying an analyzer.
+ */
+ @Test
+ public void testCopyAnalyzer() {
+ System.out.println("✓ Client initialized successfully");
+
+ // Generate unique analyzer IDs for this test
+ String sourceAnalyzerId = testResourceNamer.randomName("test_analyzer_source_", 50);
+ String targetAnalyzerId = testResourceNamer.randomName("test_analyzer_target_", 50);
+
+ try {
+ // BEGIN: com.azure.ai.contentunderstanding.copyAnalyzer
+ // Step 1: Create the source analyzer
+ ContentAnalyzerConfig sourceConfig = new ContentAnalyzerConfig();
+ sourceConfig.setFormulaEnabled(false);
+ sourceConfig.setLayoutEnabled(true);
+ sourceConfig.setOcrEnabled(true);
+ sourceConfig.setEstimateFieldSourceAndConfidence(true);
+ sourceConfig.setReturnDetails(true);
+
+ Map fields = new HashMap<>();
+
+ ContentFieldDefinition companyNameField = new ContentFieldDefinition();
+ companyNameField.setType(ContentFieldType.STRING);
+ companyNameField.setMethod(GenerationMethod.EXTRACT);
+ companyNameField.setDescription("Name of the company");
+ fields.put("company_name", companyNameField);
+
+ ContentFieldDefinition totalAmountField = new ContentFieldDefinition();
+ totalAmountField.setType(ContentFieldType.NUMBER);
+ totalAmountField.setMethod(GenerationMethod.EXTRACT);
+ totalAmountField.setDescription("Total amount on the document");
+ fields.put("total_amount", totalAmountField);
+
+ ContentFieldSchema sourceFieldSchema = new ContentFieldSchema();
+ sourceFieldSchema.setName("company_schema");
+ sourceFieldSchema.setDescription("Schema for extracting company information");
+ sourceFieldSchema.setFields(fields);
+
+ ContentAnalyzer sourceAnalyzer = new ContentAnalyzer();
+ sourceAnalyzer.setBaseAnalyzerId("prebuilt-document");
+ sourceAnalyzer.setDescription("Source analyzer for copying");
+ sourceAnalyzer.setConfig(sourceConfig);
+ sourceAnalyzer.setFieldSchema(sourceFieldSchema);
+
+ Map models = new HashMap<>();
+ models.put("completion", "gpt-4.1");
+ sourceAnalyzer.setModels(models);
+
+ Map tags = new HashMap<>();
+ tags.put("modelType", "in_development");
+ sourceAnalyzer.setTags(tags);
+
+ // Create source analyzer
+ SyncPoller createPoller
+ = contentUnderstandingClient.beginCreateAnalyzer(sourceAnalyzerId, sourceAnalyzer);
+ ContentAnalyzer sourceResult = createPoller.getFinalResult();
+ System.out.println("Source analyzer '" + sourceAnalyzerId + "' created successfully!");
+
+ // Verify source analyzer is available before copying (ensure it's fully provisioned)
+ ContentAnalyzer verifiedSource = contentUnderstandingClient.getAnalyzer(sourceAnalyzerId);
+ System.out.println("Source analyzer verified: " + verifiedSource.getDescription());
+
+ // Step 2: Copy the source analyzer to target
+ // Note: This copies within the same resource using the simplified 2-parameter method.
+ ContentAnalyzer copiedAnalyzer = null;
+ try {
+ SyncPoller copyPoller
+ = contentUnderstandingClient.beginCopyAnalyzer(targetAnalyzerId, sourceAnalyzerId);
+ copiedAnalyzer = copyPoller.getFinalResult();
+ System.out.println("Analyzer copied to '" + targetAnalyzerId + "' successfully!");
+ // END: com.azure.ai.contentunderstanding.copyAnalyzer
+ } catch (com.azure.core.exception.ResourceNotFoundException e) {
+ // Some Content Understanding endpoints may not support same-resource copy operations
+ // This is a service-side configuration, not a SDK bug
+ System.out.println("⚠️ Copy operation not supported on this endpoint.");
+ System.out.println(" Error: " + e.getMessage());
+ System.out.println(" Note: For cross-resource copying, use Sample15_GrantCopyAuth.");
+ System.out.println("\n📋 CopyAnalyzer API Pattern Demonstrated:");
+ System.out.println(" contentUnderstandingClient.beginCopyAnalyzer(targetId, sourceId);");
+ System.out.println(
+ " For cross-resource: beginCopyAnalyzer(targetId, sourceId, allowReplace, sourceResourceId, sourceRegion);");
+ return; // Skip the rest of the test
+ }
+
+ // ========== VERIFICATION: Source Analyzer Creation ==========
+ System.out.println("\n📋 Source Analyzer Creation Verification:");
+
+ // Verify analyzer IDs
+ assertNotNull(sourceAnalyzerId, "Source analyzer ID should not be null");
+ assertFalse(sourceAnalyzerId.trim().isEmpty(), "Source analyzer ID should not be empty");
+ assertNotNull(targetAnalyzerId, "Target analyzer ID should not be null");
+ assertFalse(targetAnalyzerId.trim().isEmpty(), "Target analyzer ID should not be empty");
+ assertNotEquals(sourceAnalyzerId, targetAnalyzerId, "Source and target IDs should be different");
+ System.out.println(" ✓ Analyzer IDs validated");
+ System.out.println(" Source: " + sourceAnalyzerId);
+ System.out.println(" Target: " + targetAnalyzerId);
+
+ // Verify source config
+ assertNotNull(sourceConfig, "Source config should not be null");
+ assertEquals(false, sourceConfig.isFormulaEnabled(), "FormulaEnabled should be false");
+ assertEquals(true, sourceConfig.isLayoutEnabled(), "LayoutEnabled should be true");
+ assertEquals(true, sourceConfig.isOcrEnabled(), "OcrEnabled should be true");
+ assertEquals(true, sourceConfig.isEstimateFieldSourceAndConfidence(),
+ "EstimateFieldSourceAndConfidence should be true");
+ assertEquals(true, sourceConfig.isReturnDetails(), "ReturnDetails should be true");
+ System.out.println(" ✓ Source config verified");
+
+ // Verify source field schema
+ assertNotNull(sourceFieldSchema, "Source field schema should not be null");
+ assertEquals("company_schema", sourceFieldSchema.getName(), "Field schema name should match");
+ assertEquals("Schema for extracting company information", sourceFieldSchema.getDescription(),
+ "Field schema description should match");
+ assertEquals(2, sourceFieldSchema.getFields().size(), "Should have 2 fields");
+ System.out.println(" ✓ Source field schema verified: " + sourceFieldSchema.getName());
+
+ // Verify individual field definitions
+ assertTrue(sourceFieldSchema.getFields().containsKey("company_name"), "Should contain company_name field");
+ ContentFieldDefinition companyField = sourceFieldSchema.getFields().get("company_name");
+ assertEquals(ContentFieldType.STRING, companyField.getType(), "company_name should be STRING type");
+ assertEquals(GenerationMethod.EXTRACT, companyField.getMethod(), "company_name should use EXTRACT method");
+ assertEquals("Name of the company", companyField.getDescription(), "company_name description should match");
+ System.out.println(" ✓ company_name field verified");
+
+ assertTrue(sourceFieldSchema.getFields().containsKey("total_amount"), "Should contain total_amount field");
+ ContentFieldDefinition amountField = sourceFieldSchema.getFields().get("total_amount");
+ assertEquals(ContentFieldType.NUMBER, amountField.getType(), "total_amount should be NUMBER type");
+ assertEquals(GenerationMethod.EXTRACT, amountField.getMethod(), "total_amount should use EXTRACT method");
+ assertEquals("Total amount on the document", amountField.getDescription(),
+ "total_amount description should match");
+ System.out.println(" ✓ total_amount field verified");
+
+ // Verify source analyzer object
+ assertNotNull(sourceAnalyzer, "Source analyzer object should not be null");
+ assertEquals("prebuilt-document", sourceAnalyzer.getBaseAnalyzerId(), "Base analyzer ID should match");
+ assertEquals("Source analyzer for copying", sourceAnalyzer.getDescription(), "Description should match");
+ assertTrue(sourceAnalyzer.getModels().containsKey("completion"), "Should have completion model");
+ assertEquals("gpt-4.1", sourceAnalyzer.getModels().get("completion"), "Completion model should be gpt-4.1");
+ assertTrue(sourceAnalyzer.getTags().containsKey("modelType"), "Should have modelType tag");
+ assertEquals("in_development", sourceAnalyzer.getTags().get("modelType"),
+ "modelType tag should be in_development");
+ System.out.println(" ✓ Source analyzer object verified");
+
+ // Verify creation result
+ assertNotNull(sourceResult, "Source analyzer result should not be null");
+ assertEquals("prebuilt-document", sourceResult.getBaseAnalyzerId(), "Base analyzer ID should match");
+ assertEquals("Source analyzer for copying", sourceResult.getDescription(), "Description should match");
+ System.out.println(" ✓ Source analyzer created: " + sourceAnalyzerId);
+
+ // Verify config in result
+ assertNotNull(sourceResult.getConfig(), "Config should not be null in result");
+ assertEquals(false, sourceResult.getConfig().isFormulaEnabled(), "FormulaEnabled should be preserved");
+ assertEquals(true, sourceResult.getConfig().isLayoutEnabled(), "LayoutEnabled should be preserved");
+ assertEquals(true, sourceResult.getConfig().isOcrEnabled(), "OcrEnabled should be preserved");
+ System.out.println(" ✓ Config preserved in result");
+
+ // Verify field schema in result
+ assertNotNull(sourceResult.getFieldSchema(), "Field schema should not be null in result");
+ assertEquals("company_schema", sourceResult.getFieldSchema().getName(),
+ "Field schema name should be preserved");
+ assertEquals(2, sourceResult.getFieldSchema().getFields().size(), "Should have 2 fields in result");
+ assertTrue(sourceResult.getFieldSchema().getFields().containsKey("company_name"),
+ "Should contain company_name in result");
+ assertTrue(sourceResult.getFieldSchema().getFields().containsKey("total_amount"),
+ "Should contain total_amount in result");
+ System.out
+ .println(" ✓ Field schema preserved: " + sourceResult.getFieldSchema().getFields().size() + " fields");
+
+ // Verify tags in result
+ assertNotNull(sourceResult.getTags(), "Tags should not be null in result");
+ assertTrue(sourceResult.getTags().containsKey("modelType"), "Should contain modelType tag in result");
+ assertEquals("in_development", sourceResult.getTags().get("modelType"),
+ "modelType tag should be preserved");
+ System.out.println(" ✓ Tags preserved: " + sourceResult.getTags().size() + " tag(s)");
+
+ // Verify models in result
+ assertNotNull(sourceResult.getModels(), "Models should not be null in result");
+ assertTrue(sourceResult.getModels().containsKey("completion"), "Should have completion model in result");
+ assertEquals("gpt-4.1", sourceResult.getModels().get("completion"), "Completion model should be preserved");
+ System.out.println(" ✓ Models preserved: " + sourceResult.getModels().size() + " model(s)");
+
+ System.out.println("\n✅ Source analyzer creation completed:");
+ System.out.println(" ID: " + sourceAnalyzerId);
+ System.out.println(" Base: " + sourceResult.getBaseAnalyzerId());
+ System.out.println(" Fields: " + sourceResult.getFieldSchema().getFields().size());
+ System.out.println(" Tags: " + sourceResult.getTags().size());
+ System.out.println(" Models: " + sourceResult.getModels().size());
+
+ // Get the source analyzer to verify retrieval
+ ContentAnalyzer sourceAnalyzerInfo = contentUnderstandingClient.getAnalyzer(sourceAnalyzerId);
+
+ System.out.println("\n📋 Source Analyzer Retrieval Verification:");
+ assertNotNull(sourceAnalyzerInfo, "Source analyzer info should not be null");
+ assertEquals(sourceResult.getBaseAnalyzerId(), sourceAnalyzerInfo.getBaseAnalyzerId(),
+ "Base analyzer should match");
+ assertEquals(sourceResult.getDescription(), sourceAnalyzerInfo.getDescription(),
+ "Description should match");
+ System.out.println(" ✓ Source analyzer retrieved successfully");
+ System.out.println(" Description: " + sourceAnalyzerInfo.getDescription());
+ System.out.println(" Tags: " + String.join(", ",
+ sourceAnalyzerInfo.getTags()
+ .entrySet()
+ .stream()
+ .map(e -> e.getKey() + "=" + e.getValue())
+ .toArray(String[]::new)));
+
+ // ========== VERIFICATION: Analyzer Copy Operation ==========
+ System.out.println("\n📋 Analyzer Copy Verification:");
+ assertNotNull(copiedAnalyzer, "Copied analyzer should not be null");
+ System.out.println(" ✓ Copy operation completed");
+
+ // Verify base properties match source
+ assertEquals(sourceResult.getBaseAnalyzerId(), copiedAnalyzer.getBaseAnalyzerId(),
+ "Copied analyzer should have same base analyzer ID");
+ assertEquals(sourceResult.getDescription(), copiedAnalyzer.getDescription(),
+ "Copied analyzer should have same description");
+ System.out.println(" ✓ Base properties preserved");
+ System.out.println(" Base analyzer ID: " + copiedAnalyzer.getBaseAnalyzerId());
+ System.out.println(" Description: '" + copiedAnalyzer.getDescription() + "'");
+
+ // Verify field schema structure
+ assertNotNull(copiedAnalyzer.getFieldSchema(), "Copied analyzer should have field schema");
+ assertEquals(sourceResult.getFieldSchema().getName(), copiedAnalyzer.getFieldSchema().getName(),
+ "Field schema name should match");
+ assertEquals(sourceResult.getFieldSchema().getDescription(),
+ copiedAnalyzer.getFieldSchema().getDescription(), "Field schema description should match");
+ assertEquals(sourceResult.getFieldSchema().getFields().size(),
+ copiedAnalyzer.getFieldSchema().getFields().size(), "Field count should match");
+ System.out.println(" ✓ Field schema structure preserved");
+ System.out.println(" Schema: " + copiedAnalyzer.getFieldSchema().getName());
+ System.out.println(" Fields: " + copiedAnalyzer.getFieldSchema().getFields().size());
+
+ // Verify individual field definitions were copied correctly
+ assertTrue(copiedAnalyzer.getFieldSchema().getFields().containsKey("company_name"),
+ "Copied analyzer should contain company_name field");
+ ContentFieldDefinition copiedCompanyField = copiedAnalyzer.getFieldSchema().getFields().get("company_name");
+ assertEquals(ContentFieldType.STRING, copiedCompanyField.getType(),
+ "company_name type should be preserved");
+ assertEquals(GenerationMethod.EXTRACT, copiedCompanyField.getMethod(),
+ "company_name method should be preserved");
+ System.out.println(
+ " ✓ company_name field: " + copiedCompanyField.getType() + " / " + copiedCompanyField.getMethod());
+
+ assertTrue(copiedAnalyzer.getFieldSchema().getFields().containsKey("total_amount"),
+ "Copied analyzer should contain total_amount field");
+ ContentFieldDefinition copiedAmountField = copiedAnalyzer.getFieldSchema().getFields().get("total_amount");
+ assertEquals(ContentFieldType.NUMBER, copiedAmountField.getType(), "total_amount type should be preserved");
+ assertEquals(GenerationMethod.EXTRACT, copiedAmountField.getMethod(),
+ "total_amount method should be preserved");
+ System.out.println(
+ " ✓ total_amount field: " + copiedAmountField.getType() + " / " + copiedAmountField.getMethod());
+
+ // Verify tags were copied
+ assertNotNull(copiedAnalyzer.getTags(), "Copied analyzer should have tags");
+ assertEquals(sourceResult.getTags().size(), copiedAnalyzer.getTags().size(), "Tag count should match");
+ assertTrue(copiedAnalyzer.getTags().containsKey("modelType"),
+ "Copied analyzer should contain modelType tag");
+ assertEquals("in_development", copiedAnalyzer.getTags().get("modelType"),
+ "Copied analyzer should have same tag value");
+ System.out.println(" ✓ Tags preserved: " + copiedAnalyzer.getTags().size() + " tag(s)");
+ System.out.println(" modelType=" + copiedAnalyzer.getTags().get("modelType"));
+
+ // Verify config was copied
+ assertNotNull(copiedAnalyzer.getConfig(), "Copied analyzer should have config");
+ assertEquals(sourceResult.getConfig().isFormulaEnabled(), copiedAnalyzer.getConfig().isFormulaEnabled(),
+ "FormulaEnabled should match");
+ assertEquals(sourceResult.getConfig().isLayoutEnabled(), copiedAnalyzer.getConfig().isLayoutEnabled(),
+ "LayoutEnabled should match");
+ assertEquals(sourceResult.getConfig().isOcrEnabled(), copiedAnalyzer.getConfig().isOcrEnabled(),
+ "OcrEnabled should match");
+ assertEquals(sourceResult.getConfig().isEstimateFieldSourceAndConfidence(),
+ copiedAnalyzer.getConfig().isEstimateFieldSourceAndConfidence(),
+ "EstimateFieldSourceAndConfidence should match");
+ assertEquals(sourceResult.getConfig().isReturnDetails(), copiedAnalyzer.getConfig().isReturnDetails(),
+ "ReturnDetails should match");
+ System.out.println(" ✓ Config preserved");
+ System.out.println(" LayoutEnabled: " + copiedAnalyzer.getConfig().isLayoutEnabled());
+ System.out.println(" OcrEnabled: " + copiedAnalyzer.getConfig().isOcrEnabled());
+
+ // Verify models were copied
+ assertNotNull(copiedAnalyzer.getModels(), "Copied analyzer should have models");
+ assertEquals(sourceResult.getModels().size(), copiedAnalyzer.getModels().size(),
+ "Model count should match");
+ if (copiedAnalyzer.getModels().containsKey("completion")) {
+ assertEquals("gpt-4.1", copiedAnalyzer.getModels().get("completion"), "Completion model should match");
+ System.out.println(" ✓ Models preserved: " + copiedAnalyzer.getModels().size() + " model(s)");
+ System.out.println(" completion=" + copiedAnalyzer.getModels().get("completion"));
+ }
+
+ // Verify the copied analyzer via Get operation
+ ContentAnalyzer verifiedCopy = contentUnderstandingClient.getAnalyzer(targetAnalyzerId);
+
+ System.out.println("\n📋 Copied Analyzer Retrieval Verification:");
+ assertNotNull(verifiedCopy, "Retrieved copied analyzer should not be null");
+ assertEquals(copiedAnalyzer.getBaseAnalyzerId(), verifiedCopy.getBaseAnalyzerId(),
+ "Retrieved analyzer should match copied analyzer");
+ assertEquals(copiedAnalyzer.getDescription(), verifiedCopy.getDescription(),
+ "Retrieved description should match");
+ assertEquals(copiedAnalyzer.getFieldSchema().getFields().size(),
+ verifiedCopy.getFieldSchema().getFields().size(), "Retrieved field count should match");
+ System.out.println(" ✓ Copied analyzer verified via retrieval");
+
+ // Summary
+ String separator = new String(new char[60]).replace("\0", "═");
+ System.out.println("\n" + separator);
+ System.out.println("✅ ANALYZER COPY VERIFICATION COMPLETED SUCCESSFULLY");
+ System.out.println(separator);
+ System.out.println("Source Analyzer:");
+ System.out.println(" ID: " + sourceAnalyzerId);
+ System.out.println(" Base: " + sourceResult.getBaseAnalyzerId());
+ System.out.println(" Description: " + sourceResult.getDescription());
+ System.out.println(" Fields: " + sourceResult.getFieldSchema().getFields().size());
+ System.out.println(" Tags: " + sourceResult.getTags().size());
+ System.out.println(" Models: " + sourceResult.getModels().size());
+ System.out.println("\nTarget Analyzer (Copied):");
+ System.out.println(" ID: " + targetAnalyzerId);
+ System.out.println(" Base: " + copiedAnalyzer.getBaseAnalyzerId());
+ System.out.println(" Description: " + copiedAnalyzer.getDescription());
+ System.out.println(" Fields: " + copiedAnalyzer.getFieldSchema().getFields().size());
+ System.out.println(" Tags: " + copiedAnalyzer.getTags().size());
+ System.out.println(" Models: " + copiedAnalyzer.getModels().size());
+ System.out.println("\n✅ All properties successfully copied and verified!");
+ System.out.println(separator);
+
+ } finally {
+ // Cleanup: Delete the analyzers
+ try {
+ contentUnderstandingClient.deleteAnalyzer(sourceAnalyzerId);
+ System.out.println("\nSource analyzer deleted: " + sourceAnalyzerId);
+ } catch (Exception e) {
+ System.out.println("Note: Failed to delete source analyzer (may not exist): " + e.getMessage());
+ }
+
+ try {
+ contentUnderstandingClient.deleteAnalyzer(targetAnalyzerId);
+ System.out.println("Target analyzer deleted: " + targetAnalyzerId);
+ } catch (Exception e) {
+ System.out.println("Note: Failed to delete target analyzer (may not exist): " + e.getMessage());
+ }
+ }
+ }
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerAsync.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerAsync.java
new file mode 100644
index 000000000000..6d661bcdfe75
--- /dev/null
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerAsync.java
@@ -0,0 +1,392 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package com.azure.ai.contentunderstanding.tests.samples;
+
+import com.azure.ai.contentunderstanding.models.ContentAnalyzer;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerConfig;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerOperationStatus;
+import com.azure.ai.contentunderstanding.models.ContentFieldDefinition;
+import com.azure.ai.contentunderstanding.models.ContentFieldSchema;
+import com.azure.ai.contentunderstanding.models.ContentFieldType;
+import com.azure.ai.contentunderstanding.models.GenerationMethod;
+import com.azure.core.util.polling.PollerFlux;
+import org.junit.jupiter.api.Test;
+import reactor.core.publisher.Mono;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Async sample demonstrates how to copy an analyzer within the same resource.
+ * For cross-resource copying, see Sample15_GrantCopyAuthAsync.
+ */
+public class Sample14_CopyAnalyzerAsync extends ContentUnderstandingClientTestBase {
+
+ /**
+ * Asynchronous sample for copying an analyzer.
+ */
+ @Test
+ public void testCopyAnalyzerAsync() {
+ System.out.println("✓ Client initialized successfully");
+
+ // Generate unique analyzer IDs for this test
+ String sourceAnalyzerId = testResourceNamer.randomName("test_analyzer_source_", 50);
+ String targetAnalyzerId = testResourceNamer.randomName("test_analyzer_target_", 50);
+
+ try {
+ // BEGIN: com.azure.ai.contentunderstanding.copyAnalyzerAsync
+ // Step 1: Create the source analyzer
+ ContentAnalyzerConfig sourceConfig = new ContentAnalyzerConfig();
+ sourceConfig.setFormulaEnabled(false);
+ sourceConfig.setLayoutEnabled(true);
+ sourceConfig.setOcrEnabled(true);
+ sourceConfig.setEstimateFieldSourceAndConfidence(true);
+ sourceConfig.setReturnDetails(true);
+
+ Map fields = new HashMap<>();
+
+ ContentFieldDefinition companyNameField = new ContentFieldDefinition();
+ companyNameField.setType(ContentFieldType.STRING);
+ companyNameField.setMethod(GenerationMethod.EXTRACT);
+ companyNameField.setDescription("Name of the company");
+ fields.put("company_name", companyNameField);
+
+ ContentFieldDefinition totalAmountField = new ContentFieldDefinition();
+ totalAmountField.setType(ContentFieldType.NUMBER);
+ totalAmountField.setMethod(GenerationMethod.EXTRACT);
+ totalAmountField.setDescription("Total amount on the document");
+ fields.put("total_amount", totalAmountField);
+
+ ContentFieldSchema sourceFieldSchema = new ContentFieldSchema();
+ sourceFieldSchema.setName("company_schema");
+ sourceFieldSchema.setDescription("Schema for extracting company information");
+ sourceFieldSchema.setFields(fields);
+
+ ContentAnalyzer sourceAnalyzer = new ContentAnalyzer();
+ sourceAnalyzer.setBaseAnalyzerId("prebuilt-document");
+ sourceAnalyzer.setDescription("Source analyzer for copying");
+ sourceAnalyzer.setConfig(sourceConfig);
+ sourceAnalyzer.setFieldSchema(sourceFieldSchema);
+
+ Map models = new HashMap<>();
+ models.put("completion", "gpt-4.1");
+ sourceAnalyzer.setModels(models);
+
+ Map tags = new HashMap<>();
+ tags.put("modelType", "in_development");
+ sourceAnalyzer.setTags(tags);
+
+ // Create source analyzer
+ PollerFlux createPoller
+ = contentUnderstandingAsyncClient.beginCreateAnalyzer(sourceAnalyzerId, sourceAnalyzer);
+
+ // Use reactive pattern: chain operations using flatMap
+ // In a real application, you would use subscribe() instead of block()
+ ContentAnalyzer sourceResult = createPoller.last().flatMap(pollResponse -> {
+ if (pollResponse.getStatus().isComplete()) {
+ return pollResponse.getFinalResult();
+ } else {
+ return Mono.error(new RuntimeException(
+ "Polling completed unsuccessfully with status: " + pollResponse.getStatus()));
+ }
+ }).block(); // block() is used here for testing; in production, use subscribe()
+
+ System.out.println("Source analyzer '" + sourceAnalyzerId + "' created successfully!");
+
+ // Verify source analyzer is available before copying (ensure it's fully provisioned)
+ ContentAnalyzer verifiedSource = contentUnderstandingAsyncClient.getAnalyzer(sourceAnalyzerId).block();
+ System.out.println("Source analyzer verified: " + verifiedSource.getDescription());
+
+ // Step 2: Copy the source analyzer to target
+ // Note: This copies within the same resource using the simplified 2-parameter method.
+ ContentAnalyzer copiedAnalyzer = null;
+ try {
+ PollerFlux copyPoller
+ = contentUnderstandingAsyncClient.beginCopyAnalyzer(targetAnalyzerId, sourceAnalyzerId);
+
+ // Use reactive pattern for copy operation as well
+ copiedAnalyzer = copyPoller.last().flatMap(pollResponse -> {
+ if (pollResponse.getStatus().isComplete()) {
+ return pollResponse.getFinalResult();
+ } else {
+ return Mono.error(new RuntimeException(
+ "Polling completed unsuccessfully with status: " + pollResponse.getStatus()));
+ }
+ }).block(); // block() is used here for testing; in production, use subscribe()
+
+ System.out.println("Analyzer copied to '" + targetAnalyzerId + "' successfully!");
+ // END: com.azure.ai.contentunderstanding.copyAnalyzerAsync
+ } catch (com.azure.core.exception.ResourceNotFoundException e) {
+ // Some Content Understanding endpoints may not support same-resource copy operations
+ // This is a service-side configuration, not a SDK bug
+ System.out.println("⚠️ Copy operation not supported on this endpoint.");
+ System.out.println(" Error: " + e.getMessage());
+ System.out.println(" Note: For cross-resource copying, use Sample15_GrantCopyAuthAsync.");
+ System.out.println("\n📋 CopyAnalyzer API Pattern Demonstrated:");
+ System.out.println(" contentUnderstandingAsyncClient.beginCopyAnalyzer(targetId, sourceId);");
+ System.out.println(
+ " For cross-resource: beginCopyAnalyzer(targetId, sourceId, allowReplace, sourceResourceId, sourceRegion);");
+ return; // Skip the rest of the test
+ }
+
+ // ========== VERIFICATION: Source Analyzer Creation ==========
+ System.out.println("\n📋 Source Analyzer Creation Verification:");
+
+ // Verify analyzer IDs
+ assertNotNull(sourceAnalyzerId, "Source analyzer ID should not be null");
+ assertFalse(sourceAnalyzerId.trim().isEmpty(), "Source analyzer ID should not be empty");
+ assertNotNull(targetAnalyzerId, "Target analyzer ID should not be null");
+ assertFalse(targetAnalyzerId.trim().isEmpty(), "Target analyzer ID should not be empty");
+ assertNotEquals(sourceAnalyzerId, targetAnalyzerId, "Source and target IDs should be different");
+ System.out.println(" ✓ Analyzer IDs validated");
+ System.out.println(" Source: " + sourceAnalyzerId);
+ System.out.println(" Target: " + targetAnalyzerId);
+
+ // Verify source config
+ assertNotNull(sourceConfig, "Source config should not be null");
+ assertEquals(false, sourceConfig.isFormulaEnabled(), "FormulaEnabled should be false");
+ assertEquals(true, sourceConfig.isLayoutEnabled(), "LayoutEnabled should be true");
+ assertEquals(true, sourceConfig.isOcrEnabled(), "OcrEnabled should be true");
+ assertEquals(true, sourceConfig.isEstimateFieldSourceAndConfidence(),
+ "EstimateFieldSourceAndConfidence should be true");
+ assertEquals(true, sourceConfig.isReturnDetails(), "ReturnDetails should be true");
+ System.out.println(" ✓ Source config verified");
+
+ // Verify source field schema
+ assertNotNull(sourceFieldSchema, "Source field schema should not be null");
+ assertEquals("company_schema", sourceFieldSchema.getName(), "Field schema name should match");
+ assertEquals("Schema for extracting company information", sourceFieldSchema.getDescription(),
+ "Field schema description should match");
+ assertEquals(2, sourceFieldSchema.getFields().size(), "Should have 2 fields");
+ System.out.println(" ✓ Source field schema verified: " + sourceFieldSchema.getName());
+
+ // Verify individual field definitions
+ assertTrue(sourceFieldSchema.getFields().containsKey("company_name"), "Should contain company_name field");
+ ContentFieldDefinition companyField = sourceFieldSchema.getFields().get("company_name");
+ assertEquals(ContentFieldType.STRING, companyField.getType(), "company_name should be STRING type");
+ assertEquals(GenerationMethod.EXTRACT, companyField.getMethod(), "company_name should use EXTRACT method");
+ assertEquals("Name of the company", companyField.getDescription(), "company_name description should match");
+ System.out.println(" ✓ company_name field verified");
+
+ assertTrue(sourceFieldSchema.getFields().containsKey("total_amount"), "Should contain total_amount field");
+ ContentFieldDefinition amountField = sourceFieldSchema.getFields().get("total_amount");
+ assertEquals(ContentFieldType.NUMBER, amountField.getType(), "total_amount should be NUMBER type");
+ assertEquals(GenerationMethod.EXTRACT, amountField.getMethod(), "total_amount should use EXTRACT method");
+ assertEquals("Total amount on the document", amountField.getDescription(),
+ "total_amount description should match");
+ System.out.println(" ✓ total_amount field verified");
+
+ // Verify source analyzer object
+ assertNotNull(sourceAnalyzer, "Source analyzer object should not be null");
+ assertEquals("prebuilt-document", sourceAnalyzer.getBaseAnalyzerId(), "Base analyzer ID should match");
+ assertEquals("Source analyzer for copying", sourceAnalyzer.getDescription(), "Description should match");
+ assertTrue(sourceAnalyzer.getModels().containsKey("completion"), "Should have completion model");
+ assertEquals("gpt-4.1", sourceAnalyzer.getModels().get("completion"), "Completion model should be gpt-4.1");
+ assertTrue(sourceAnalyzer.getTags().containsKey("modelType"), "Should have modelType tag");
+ assertEquals("in_development", sourceAnalyzer.getTags().get("modelType"),
+ "modelType tag should be in_development");
+ System.out.println(" ✓ Source analyzer object verified");
+
+ // Verify creation result
+ assertNotNull(sourceResult, "Source analyzer result should not be null");
+ assertEquals("prebuilt-document", sourceResult.getBaseAnalyzerId(), "Base analyzer ID should match");
+ assertEquals("Source analyzer for copying", sourceResult.getDescription(), "Description should match");
+ System.out.println(" ✓ Source analyzer created: " + sourceAnalyzerId);
+
+ // Verify config in result
+ assertNotNull(sourceResult.getConfig(), "Config should not be null in result");
+ assertEquals(false, sourceResult.getConfig().isFormulaEnabled(), "FormulaEnabled should be preserved");
+ assertEquals(true, sourceResult.getConfig().isLayoutEnabled(), "LayoutEnabled should be preserved");
+ assertEquals(true, sourceResult.getConfig().isOcrEnabled(), "OcrEnabled should be preserved");
+ System.out.println(" ✓ Config preserved in result");
+
+ // Verify field schema in result
+ assertNotNull(sourceResult.getFieldSchema(), "Field schema should not be null in result");
+ assertEquals("company_schema", sourceResult.getFieldSchema().getName(),
+ "Field schema name should be preserved");
+ assertEquals(2, sourceResult.getFieldSchema().getFields().size(), "Should have 2 fields in result");
+ assertTrue(sourceResult.getFieldSchema().getFields().containsKey("company_name"),
+ "Should contain company_name in result");
+ assertTrue(sourceResult.getFieldSchema().getFields().containsKey("total_amount"),
+ "Should contain total_amount in result");
+ System.out
+ .println(" ✓ Field schema preserved: " + sourceResult.getFieldSchema().getFields().size() + " fields");
+
+ // Verify tags in result
+ assertNotNull(sourceResult.getTags(), "Tags should not be null in result");
+ assertTrue(sourceResult.getTags().containsKey("modelType"), "Should contain modelType tag in result");
+ assertEquals("in_development", sourceResult.getTags().get("modelType"),
+ "modelType tag should be preserved");
+ System.out.println(" ✓ Tags preserved: " + sourceResult.getTags().size() + " tag(s)");
+
+ // Verify models in result
+ assertNotNull(sourceResult.getModels(), "Models should not be null in result");
+ assertTrue(sourceResult.getModels().containsKey("completion"), "Should have completion model in result");
+ assertEquals("gpt-4.1", sourceResult.getModels().get("completion"), "Completion model should be preserved");
+ System.out.println(" ✓ Models preserved: " + sourceResult.getModels().size() + " model(s)");
+
+ System.out.println("\n✅ Source analyzer creation completed:");
+ System.out.println(" ID: " + sourceAnalyzerId);
+ System.out.println(" Base: " + sourceResult.getBaseAnalyzerId());
+ System.out.println(" Fields: " + sourceResult.getFieldSchema().getFields().size());
+ System.out.println(" Tags: " + sourceResult.getTags().size());
+ System.out.println(" Models: " + sourceResult.getModels().size());
+
+ // Get the source analyzer to verify retrieval
+ ContentAnalyzer sourceAnalyzerInfo = contentUnderstandingAsyncClient.getAnalyzer(sourceAnalyzerId).block();
+
+ System.out.println("\n📋 Source Analyzer Retrieval Verification:");
+ assertNotNull(sourceAnalyzerInfo, "Source analyzer info should not be null");
+ assertEquals(sourceResult.getBaseAnalyzerId(), sourceAnalyzerInfo.getBaseAnalyzerId(),
+ "Base analyzer should match");
+ assertEquals(sourceResult.getDescription(), sourceAnalyzerInfo.getDescription(),
+ "Description should match");
+ System.out.println(" ✓ Source analyzer retrieved successfully");
+ System.out.println(" Description: " + sourceAnalyzerInfo.getDescription());
+ System.out.println(" Tags: " + String.join(", ",
+ sourceAnalyzerInfo.getTags()
+ .entrySet()
+ .stream()
+ .map(e -> e.getKey() + "=" + e.getValue())
+ .toArray(String[]::new)));
+
+ // ========== VERIFICATION: Analyzer Copy Operation ==========
+ System.out.println("\n📋 Analyzer Copy Verification:");
+ assertNotNull(copiedAnalyzer, "Copied analyzer should not be null");
+ System.out.println(" ✓ Copy operation completed");
+
+ // Verify base properties match source
+ assertEquals(sourceResult.getBaseAnalyzerId(), copiedAnalyzer.getBaseAnalyzerId(),
+ "Copied analyzer should have same base analyzer ID");
+ assertEquals(sourceResult.getDescription(), copiedAnalyzer.getDescription(),
+ "Copied analyzer should have same description");
+ System.out.println(" ✓ Base properties preserved");
+ System.out.println(" Base analyzer ID: " + copiedAnalyzer.getBaseAnalyzerId());
+ System.out.println(" Description: '" + copiedAnalyzer.getDescription() + "'");
+
+ // Verify field schema structure
+ assertNotNull(copiedAnalyzer.getFieldSchema(), "Copied analyzer should have field schema");
+ assertEquals(sourceResult.getFieldSchema().getName(), copiedAnalyzer.getFieldSchema().getName(),
+ "Field schema name should match");
+ assertEquals(sourceResult.getFieldSchema().getDescription(),
+ copiedAnalyzer.getFieldSchema().getDescription(), "Field schema description should match");
+ assertEquals(sourceResult.getFieldSchema().getFields().size(),
+ copiedAnalyzer.getFieldSchema().getFields().size(), "Field count should match");
+ System.out.println(" ✓ Field schema structure preserved");
+ System.out.println(" Schema: " + copiedAnalyzer.getFieldSchema().getName());
+ System.out.println(" Fields: " + copiedAnalyzer.getFieldSchema().getFields().size());
+
+ // Verify individual field definitions were copied correctly
+ assertTrue(copiedAnalyzer.getFieldSchema().getFields().containsKey("company_name"),
+ "Copied analyzer should contain company_name field");
+ ContentFieldDefinition copiedCompanyField = copiedAnalyzer.getFieldSchema().getFields().get("company_name");
+ assertEquals(ContentFieldType.STRING, copiedCompanyField.getType(),
+ "company_name type should be preserved");
+ assertEquals(GenerationMethod.EXTRACT, copiedCompanyField.getMethod(),
+ "company_name method should be preserved");
+ System.out.println(
+ " ✓ company_name field: " + copiedCompanyField.getType() + " / " + copiedCompanyField.getMethod());
+
+ assertTrue(copiedAnalyzer.getFieldSchema().getFields().containsKey("total_amount"),
+ "Copied analyzer should contain total_amount field");
+ ContentFieldDefinition copiedAmountField = copiedAnalyzer.getFieldSchema().getFields().get("total_amount");
+ assertEquals(ContentFieldType.NUMBER, copiedAmountField.getType(), "total_amount type should be preserved");
+ assertEquals(GenerationMethod.EXTRACT, copiedAmountField.getMethod(),
+ "total_amount method should be preserved");
+ System.out.println(
+ " ✓ total_amount field: " + copiedAmountField.getType() + " / " + copiedAmountField.getMethod());
+
+ // Verify tags were copied
+ assertNotNull(copiedAnalyzer.getTags(), "Copied analyzer should have tags");
+ assertEquals(sourceResult.getTags().size(), copiedAnalyzer.getTags().size(), "Tag count should match");
+ assertTrue(copiedAnalyzer.getTags().containsKey("modelType"),
+ "Copied analyzer should contain modelType tag");
+ assertEquals("in_development", copiedAnalyzer.getTags().get("modelType"),
+ "Copied analyzer should have same tag value");
+ System.out.println(" ✓ Tags preserved: " + copiedAnalyzer.getTags().size() + " tag(s)");
+ System.out.println(" modelType=" + copiedAnalyzer.getTags().get("modelType"));
+
+ // Verify config was copied
+ assertNotNull(copiedAnalyzer.getConfig(), "Copied analyzer should have config");
+ assertEquals(sourceResult.getConfig().isFormulaEnabled(), copiedAnalyzer.getConfig().isFormulaEnabled(),
+ "FormulaEnabled should match");
+ assertEquals(sourceResult.getConfig().isLayoutEnabled(), copiedAnalyzer.getConfig().isLayoutEnabled(),
+ "LayoutEnabled should match");
+ assertEquals(sourceResult.getConfig().isOcrEnabled(), copiedAnalyzer.getConfig().isOcrEnabled(),
+ "OcrEnabled should match");
+ assertEquals(sourceResult.getConfig().isEstimateFieldSourceAndConfidence(),
+ copiedAnalyzer.getConfig().isEstimateFieldSourceAndConfidence(),
+ "EstimateFieldSourceAndConfidence should match");
+ assertEquals(sourceResult.getConfig().isReturnDetails(), copiedAnalyzer.getConfig().isReturnDetails(),
+ "ReturnDetails should match");
+ System.out.println(" ✓ Config preserved");
+ System.out.println(" LayoutEnabled: " + copiedAnalyzer.getConfig().isLayoutEnabled());
+ System.out.println(" OcrEnabled: " + copiedAnalyzer.getConfig().isOcrEnabled());
+
+ // Verify models were copied
+ assertNotNull(copiedAnalyzer.getModels(), "Copied analyzer should have models");
+ assertEquals(sourceResult.getModels().size(), copiedAnalyzer.getModels().size(),
+ "Model count should match");
+ if (copiedAnalyzer.getModels().containsKey("completion")) {
+ assertEquals("gpt-4.1", copiedAnalyzer.getModels().get("completion"), "Completion model should match");
+ System.out.println(" ✓ Models preserved: " + copiedAnalyzer.getModels().size() + " model(s)");
+ System.out.println(" completion=" + copiedAnalyzer.getModels().get("completion"));
+ }
+
+ // Verify the copied analyzer via Get operation
+ ContentAnalyzer verifiedCopy = contentUnderstandingAsyncClient.getAnalyzer(targetAnalyzerId).block();
+
+ System.out.println("\n📋 Copied Analyzer Retrieval Verification:");
+ assertNotNull(verifiedCopy, "Retrieved copied analyzer should not be null");
+ assertEquals(copiedAnalyzer.getBaseAnalyzerId(), verifiedCopy.getBaseAnalyzerId(),
+ "Retrieved analyzer should match copied analyzer");
+ assertEquals(copiedAnalyzer.getDescription(), verifiedCopy.getDescription(),
+ "Retrieved description should match");
+ assertEquals(copiedAnalyzer.getFieldSchema().getFields().size(),
+ verifiedCopy.getFieldSchema().getFields().size(), "Retrieved field count should match");
+ System.out.println(" ✓ Copied analyzer verified via retrieval");
+
+ // Summary
+ String separator = new String(new char[60]).replace("\0", "═");
+ System.out.println("\n" + separator);
+ System.out.println("✅ ANALYZER COPY VERIFICATION COMPLETED SUCCESSFULLY");
+ System.out.println(separator);
+ System.out.println("Source Analyzer:");
+ System.out.println(" ID: " + sourceAnalyzerId);
+ System.out.println(" Base: " + sourceResult.getBaseAnalyzerId());
+ System.out.println(" Description: " + sourceResult.getDescription());
+ System.out.println(" Fields: " + sourceResult.getFieldSchema().getFields().size());
+ System.out.println(" Tags: " + sourceResult.getTags().size());
+ System.out.println(" Models: " + sourceResult.getModels().size());
+ System.out.println("\nTarget Analyzer (Copied):");
+ System.out.println(" ID: " + targetAnalyzerId);
+ System.out.println(" Base: " + copiedAnalyzer.getBaseAnalyzerId());
+ System.out.println(" Description: " + copiedAnalyzer.getDescription());
+ System.out.println(" Fields: " + copiedAnalyzer.getFieldSchema().getFields().size());
+ System.out.println(" Tags: " + copiedAnalyzer.getTags().size());
+ System.out.println(" Models: " + copiedAnalyzer.getModels().size());
+ System.out.println("\n✅ All properties successfully copied and verified!");
+ System.out.println(separator);
+
+ } finally {
+ // Cleanup: Delete the analyzers
+ try {
+ contentUnderstandingAsyncClient.deleteAnalyzer(sourceAnalyzerId).block();
+ System.out.println("\nSource analyzer deleted: " + sourceAnalyzerId);
+ } catch (Exception e) {
+ System.out.println("Note: Failed to delete source analyzer (may not exist): " + e.getMessage());
+ }
+
+ try {
+ contentUnderstandingAsyncClient.deleteAnalyzer(targetAnalyzerId).block();
+ System.out.println("Target analyzer deleted: " + targetAnalyzerId);
+ } catch (Exception e) {
+ System.out.println("Note: Failed to delete target analyzer (may not exist): " + e.getMessage());
+ }
+ }
+ }
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerAsyncTest.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerAsyncTest.java
index cd09a2df14d5..b109cbf4ee78 100644
--- a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerAsyncTest.java
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerAsyncTest.java
@@ -152,9 +152,9 @@ public void testCopyAnalyzerAsync() {
// Verify source config
assertNotNull(sourceConfig, "Source config should not be null");
- assertEquals(false, sourceConfig.isFormulaEnabled(), "EnableFormula should be false");
- assertEquals(true, sourceConfig.isLayoutEnabled(), "EnableLayout should be true");
- assertEquals(true, sourceConfig.isOcrEnabled(), "EnableOcr should be true");
+ assertEquals(false, sourceConfig.isFormulaEnabled(), "FormulaEnabled should be false");
+ assertEquals(true, sourceConfig.isLayoutEnabled(), "LayoutEnabled should be true");
+ assertEquals(true, sourceConfig.isOcrEnabled(), "OcrEnabled should be true");
assertEquals(true, sourceConfig.isEstimateFieldSourceAndConfidence(),
"EstimateFieldSourceAndConfidence should be true");
assertEquals(true, sourceConfig.isReturnDetails(), "ReturnDetails should be true");
@@ -203,9 +203,9 @@ public void testCopyAnalyzerAsync() {
// Verify config in result
assertNotNull(sourceResult.getConfig(), "Config should not be null in result");
- assertEquals(false, sourceResult.getConfig().isFormulaEnabled(), "EnableFormula should be preserved");
- assertEquals(true, sourceResult.getConfig().isLayoutEnabled(), "EnableLayout should be preserved");
- assertEquals(true, sourceResult.getConfig().isOcrEnabled(), "EnableOcr should be preserved");
+ assertEquals(false, sourceResult.getConfig().isFormulaEnabled(), "FormulaEnabled should be preserved");
+ assertEquals(true, sourceResult.getConfig().isLayoutEnabled(), "LayoutEnabled should be preserved");
+ assertEquals(true, sourceResult.getConfig().isOcrEnabled(), "OcrEnabled should be preserved");
System.out.println(" ✓ Config preserved in result");
// Verify field schema in result
@@ -317,19 +317,19 @@ public void testCopyAnalyzerAsync() {
// Verify config was copied
assertNotNull(copiedAnalyzer.getConfig(), "Copied analyzer should have config");
assertEquals(sourceResult.getConfig().isFormulaEnabled(), copiedAnalyzer.getConfig().isFormulaEnabled(),
- "EnableFormula should match");
+ "FormulaEnabled should match");
assertEquals(sourceResult.getConfig().isLayoutEnabled(), copiedAnalyzer.getConfig().isLayoutEnabled(),
- "EnableLayout should match");
+ "LayoutEnabled should match");
assertEquals(sourceResult.getConfig().isOcrEnabled(), copiedAnalyzer.getConfig().isOcrEnabled(),
- "EnableOcr should match");
+ "OcrEnabled should match");
assertEquals(sourceResult.getConfig().isEstimateFieldSourceAndConfidence(),
copiedAnalyzer.getConfig().isEstimateFieldSourceAndConfidence(),
"EstimateFieldSourceAndConfidence should match");
assertEquals(sourceResult.getConfig().isReturnDetails(), copiedAnalyzer.getConfig().isReturnDetails(),
"ReturnDetails should match");
System.out.println(" ✓ Config preserved");
- System.out.println(" EnableLayout: " + copiedAnalyzer.getConfig().isLayoutEnabled());
- System.out.println(" EnableOcr: " + copiedAnalyzer.getConfig().isOcrEnabled());
+ System.out.println(" LayoutEnabled: " + copiedAnalyzer.getConfig().isLayoutEnabled());
+ System.out.println(" OcrEnabled: " + copiedAnalyzer.getConfig().isOcrEnabled());
// Verify models were copied
assertNotNull(copiedAnalyzer.getModels(), "Copied analyzer should have models");
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerTest.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerTest.java
index f9378d52c5fa..427cda69515c 100644
--- a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerTest.java
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample14_CopyAnalyzerTest.java
@@ -130,9 +130,9 @@ public void testCopyAnalyzer() {
// Verify source config
assertNotNull(sourceConfig, "Source config should not be null");
- assertEquals(false, sourceConfig.isFormulaEnabled(), "EnableFormula should be false");
- assertEquals(true, sourceConfig.isLayoutEnabled(), "EnableLayout should be true");
- assertEquals(true, sourceConfig.isOcrEnabled(), "EnableOcr should be true");
+ assertEquals(false, sourceConfig.isFormulaEnabled(), "FormulaEnabled should be false");
+ assertEquals(true, sourceConfig.isLayoutEnabled(), "LayoutEnabled should be true");
+ assertEquals(true, sourceConfig.isOcrEnabled(), "OcrEnabled should be true");
assertEquals(true, sourceConfig.isEstimateFieldSourceAndConfidence(),
"EstimateFieldSourceAndConfidence should be true");
assertEquals(true, sourceConfig.isReturnDetails(), "ReturnDetails should be true");
@@ -181,9 +181,9 @@ public void testCopyAnalyzer() {
// Verify config in result
assertNotNull(sourceResult.getConfig(), "Config should not be null in result");
- assertEquals(false, sourceResult.getConfig().isFormulaEnabled(), "EnableFormula should be preserved");
- assertEquals(true, sourceResult.getConfig().isLayoutEnabled(), "EnableLayout should be preserved");
- assertEquals(true, sourceResult.getConfig().isOcrEnabled(), "EnableOcr should be preserved");
+ assertEquals(false, sourceResult.getConfig().isFormulaEnabled(), "FormulaEnabled should be preserved");
+ assertEquals(true, sourceResult.getConfig().isLayoutEnabled(), "LayoutEnabled should be preserved");
+ assertEquals(true, sourceResult.getConfig().isOcrEnabled(), "OcrEnabled should be preserved");
System.out.println(" ✓ Config preserved in result");
// Verify field schema in result
@@ -295,19 +295,19 @@ public void testCopyAnalyzer() {
// Verify config was copied
assertNotNull(copiedAnalyzer.getConfig(), "Copied analyzer should have config");
assertEquals(sourceResult.getConfig().isFormulaEnabled(), copiedAnalyzer.getConfig().isFormulaEnabled(),
- "EnableFormula should match");
+ "FormulaEnabled should match");
assertEquals(sourceResult.getConfig().isLayoutEnabled(), copiedAnalyzer.getConfig().isLayoutEnabled(),
- "EnableLayout should match");
+ "LayoutEnabled should match");
assertEquals(sourceResult.getConfig().isOcrEnabled(), copiedAnalyzer.getConfig().isOcrEnabled(),
- "EnableOcr should match");
+ "OcrEnabled should match");
assertEquals(sourceResult.getConfig().isEstimateFieldSourceAndConfidence(),
copiedAnalyzer.getConfig().isEstimateFieldSourceAndConfidence(),
"EstimateFieldSourceAndConfidence should match");
assertEquals(sourceResult.getConfig().isReturnDetails(), copiedAnalyzer.getConfig().isReturnDetails(),
"ReturnDetails should match");
System.out.println(" ✓ Config preserved");
- System.out.println(" EnableLayout: " + copiedAnalyzer.getConfig().isLayoutEnabled());
- System.out.println(" EnableOcr: " + copiedAnalyzer.getConfig().isOcrEnabled());
+ System.out.println(" LayoutEnabled: " + copiedAnalyzer.getConfig().isLayoutEnabled());
+ System.out.println(" OcrEnabled: " + copiedAnalyzer.getConfig().isOcrEnabled());
// Verify models were copied
assertNotNull(copiedAnalyzer.getModels(), "Copied analyzer should have models");
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample15_GrantCopyAuth.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample15_GrantCopyAuth.java
new file mode 100644
index 000000000000..cbdf5258378b
--- /dev/null
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample15_GrantCopyAuth.java
@@ -0,0 +1,172 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package com.azure.ai.contentunderstanding.tests.samples;
+
+import com.azure.ai.contentunderstanding.ContentUnderstandingClient;
+import com.azure.ai.contentunderstanding.ContentUnderstandingClientBuilder;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzer;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerConfig;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerOperationStatus;
+import com.azure.ai.contentunderstanding.models.ContentFieldDefinition;
+import com.azure.ai.contentunderstanding.models.CopyAuthorization;
+import com.azure.ai.contentunderstanding.models.ContentFieldSchema;
+import com.azure.ai.contentunderstanding.models.ContentFieldType;
+import com.azure.ai.contentunderstanding.models.GenerationMethod;
+import com.azure.core.credential.AzureKeyCredential;
+import com.azure.core.test.annotation.LiveOnly;
+import com.azure.core.util.polling.SyncPoller;
+import com.azure.identity.DefaultAzureCredentialBuilder;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Sample demonstrates how to grant copy authorization and copy an analyzer from a source
+ * Microsoft Foundry resource to a target Microsoft Foundry resource (cross-resource copying).
+ *
+ * For same-resource copying, see Sample14_CopyAnalyzer.
+ *
+ * Required environment variables for cross-resource copying:
+ *
+ * - SOURCE_RESOURCE_ID: Azure resource ID of the source resource
+ * - SOURCE_REGION: Region of the source resource
+ * - TARGET_ENDPOINT: Endpoint of the target resource
+ * - TARGET_KEY (optional): API key for target resource
+ * - TARGET_RESOURCE_ID: Azure resource ID of the target resource
+ * - TARGET_REGION: Region of the target resource
+ *
+ *
+ * Note: If API key is not provided, DefaultAzureCredential will be used.
+ * Cross-resource copying with DefaultAzureCredential requires 'Cognitive Services User' role
+ * on both source and target resources.
+ */
+public class Sample15_GrantCopyAuth extends ContentUnderstandingClientTestBase {
+
+ /**
+ * Demonstrates cross-resource copying with actual resource information.
+ *
+ * This test is marked as LiveOnly because it requires connecting to two separate
+ * Azure resources, which cannot be reliably replayed in PLAYBACK mode.
+ */
+ @LiveOnly
+ @Test
+ public void testCrossResourceCopy() {
+ // Check for required environment variables (matching samples naming convention)
+ String sourceResourceId = System.getenv("SOURCE_RESOURCE_ID");
+ String sourceRegion = System.getenv("SOURCE_REGION");
+ String targetEndpoint = System.getenv("TARGET_ENDPOINT");
+ String targetKey = System.getenv("TARGET_KEY");
+ String targetResourceId = System.getenv("TARGET_RESOURCE_ID");
+ String targetRegion = System.getenv("TARGET_REGION");
+
+ if (sourceResourceId == null
+ || sourceRegion == null
+ || targetEndpoint == null
+ || targetResourceId == null
+ || targetRegion == null) {
+ System.out.println("⚠️ Cross-resource copying requires environment variables:");
+ System.out.println(" SOURCE_RESOURCE_ID, SOURCE_REGION");
+ System.out.println(" TARGET_ENDPOINT, TARGET_KEY (optional), TARGET_RESOURCE_ID, TARGET_REGION");
+ System.out.println(" Skipping cross-resource copy test.");
+ return;
+ }
+
+ // Build target client with appropriate authentication
+ ContentUnderstandingClientBuilder targetBuilder
+ = new ContentUnderstandingClientBuilder().endpoint(targetEndpoint);
+ ContentUnderstandingClient targetClient;
+ if (targetKey != null && !targetKey.trim().isEmpty()) {
+ targetClient = targetBuilder.credential(new AzureKeyCredential(targetKey)).buildClient();
+ } else {
+ targetClient = targetBuilder.credential(new DefaultAzureCredentialBuilder().build()).buildClient();
+ }
+
+ String sourceAnalyzerId = testResourceNamer.randomName("test_cross_resource_source_", 50);
+ String targetAnalyzerId = testResourceNamer.randomName("test_cross_resource_target_", 50);
+
+ try {
+ // Step 1: Create source analyzer
+ ContentAnalyzerConfig config = new ContentAnalyzerConfig();
+ config.setLayoutEnabled(true);
+ config.setOcrEnabled(true);
+
+ Map fields = new HashMap<>();
+ ContentFieldDefinition companyNameField = new ContentFieldDefinition();
+ companyNameField.setType(ContentFieldType.STRING);
+ companyNameField.setMethod(GenerationMethod.EXTRACT);
+ companyNameField.setDescription("Name of the company");
+ fields.put("company_name", companyNameField);
+
+ ContentFieldDefinition totalAmountField = new ContentFieldDefinition();
+ totalAmountField.setType(ContentFieldType.NUMBER);
+ totalAmountField.setMethod(GenerationMethod.EXTRACT);
+ totalAmountField.setDescription("Total amount on the document");
+ fields.put("total_amount", totalAmountField);
+
+ ContentFieldSchema fieldSchema = new ContentFieldSchema();
+ fieldSchema.setName("company_schema");
+ fieldSchema.setDescription("Schema for extracting company information");
+ fieldSchema.setFields(fields);
+
+ ContentAnalyzer sourceAnalyzer = new ContentAnalyzer();
+ sourceAnalyzer.setBaseAnalyzerId("prebuilt-document");
+ sourceAnalyzer.setDescription("Source analyzer for cross-resource copying");
+ sourceAnalyzer.setConfig(config);
+ sourceAnalyzer.setFieldSchema(fieldSchema);
+
+ Map models = new HashMap<>();
+ models.put("completion", "gpt-4.1");
+ sourceAnalyzer.setModels(models);
+
+ SyncPoller createPoller
+ = contentUnderstandingClient.beginCreateAnalyzer(sourceAnalyzerId, sourceAnalyzer);
+ ContentAnalyzer sourceResult = createPoller.getFinalResult();
+ System.out.println("Source analyzer '" + sourceAnalyzerId + "' created successfully!");
+
+ // Step 2: Grant copy authorization using convenience method
+ CopyAuthorization copyAuth
+ = contentUnderstandingClient.grantCopyAuthorization(sourceAnalyzerId, targetResourceId, targetRegion);
+
+ assertNotNull(copyAuth, "Copy authorization should not be null");
+ System.out.println("Copy authorization granted!");
+ System.out.println(" Target Azure Resource ID: " + copyAuth.getTargetAzureResourceId());
+ System.out.println(" Expires at: " + copyAuth.getExpiresAt());
+
+ // Step 3: Copy analyzer to target resource using convenience method
+ SyncPoller copyPoller = targetClient
+ .beginCopyAnalyzer(targetAnalyzerId, sourceAnalyzerId, false, sourceResourceId, sourceRegion);
+ ContentAnalyzer targetResult = copyPoller.getFinalResult();
+
+ System.out.println("Target analyzer '" + targetAnalyzerId + "' copied successfully!");
+ System.out.println(" Description: " + targetResult.getDescription());
+
+ // Verify copied analyzer
+ ContentAnalyzer copiedAnalyzer = targetClient.getAnalyzer(targetAnalyzerId);
+ assertNotNull(copiedAnalyzer, "Copied analyzer should not be null");
+ assertEquals(sourceResult.getBaseAnalyzerId(), copiedAnalyzer.getBaseAnalyzerId());
+ assertEquals(sourceResult.getDescription(), copiedAnalyzer.getDescription());
+ System.out.println("Cross-resource copy verification completed");
+
+ } finally {
+ // Cleanup: delete both analyzers
+ try {
+ contentUnderstandingClient.deleteAnalyzer(sourceAnalyzerId);
+ System.out.println("Source analyzer '" + sourceAnalyzerId + "' deleted.");
+ } catch (Exception e) {
+ // Ignore cleanup errors
+ }
+
+ try {
+ targetClient.deleteAnalyzer(targetAnalyzerId);
+ System.out.println("Target analyzer '" + targetAnalyzerId + "' deleted.");
+ } catch (Exception e) {
+ // Ignore cleanup errors
+ }
+ }
+ }
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample15_GrantCopyAuthAsync.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample15_GrantCopyAuthAsync.java
new file mode 100644
index 000000000000..9b7a861edb0a
--- /dev/null
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample15_GrantCopyAuthAsync.java
@@ -0,0 +1,195 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package com.azure.ai.contentunderstanding.tests.samples;
+
+import com.azure.ai.contentunderstanding.ContentUnderstandingAsyncClient;
+import com.azure.ai.contentunderstanding.ContentUnderstandingClientBuilder;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzer;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerConfig;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerOperationStatus;
+import com.azure.ai.contentunderstanding.models.ContentFieldDefinition;
+import com.azure.ai.contentunderstanding.models.CopyAuthorization;
+import com.azure.ai.contentunderstanding.models.ContentFieldSchema;
+import com.azure.ai.contentunderstanding.models.ContentFieldType;
+import com.azure.ai.contentunderstanding.models.GenerationMethod;
+import com.azure.core.credential.AzureKeyCredential;
+import com.azure.core.test.annotation.LiveOnly;
+import com.azure.core.util.polling.PollerFlux;
+import com.azure.identity.DefaultAzureCredentialBuilder;
+import org.junit.jupiter.api.Test;
+import reactor.core.publisher.Mono;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Async sample demonstrates how to grant copy authorization and copy an analyzer from a source
+ * Microsoft Foundry resource to a target Microsoft Foundry resource (cross-resource copying).
+ *
+ * For same-resource copying, see Sample14_CopyAnalyzerAsync.
+ *
+ * Required environment variables for cross-resource copying:
+ *
+ * - SOURCE_RESOURCE_ID: Azure resource ID of the source resource
+ * - SOURCE_REGION: Region of the source resource
+ * - TARGET_ENDPOINT: Endpoint of the target resource
+ * - TARGET_KEY (optional): API key for target resource
+ * - TARGET_RESOURCE_ID: Azure resource ID of the target resource
+ * - TARGET_REGION: Region of the target resource
+ *
+ *
+ * Note: If API key is not provided, DefaultAzureCredential will be used.
+ * Cross-resource copying with DefaultAzureCredential requires 'Cognitive Services User' role
+ * on both source and target resources.
+ */
+public class Sample15_GrantCopyAuthAsync extends ContentUnderstandingClientTestBase {
+
+ /**
+ * Demonstrates cross-resource copying with actual resource information.
+ *
+ * This test is marked as LiveOnly because it requires connecting to two separate
+ * Azure resources, which cannot be reliably replayed in PLAYBACK mode.
+ */
+ @LiveOnly
+ @Test
+ public void testCrossResourceCopyAsync() {
+ // Check for required environment variables (matching samples naming convention)
+ String sourceResourceId = System.getenv("SOURCE_RESOURCE_ID");
+ String sourceRegion = System.getenv("SOURCE_REGION");
+ String targetEndpoint = System.getenv("TARGET_ENDPOINT");
+ String targetKey = System.getenv("TARGET_KEY");
+ String targetResourceId = System.getenv("TARGET_RESOURCE_ID");
+ String targetRegion = System.getenv("TARGET_REGION");
+
+ if (sourceResourceId == null
+ || sourceRegion == null
+ || targetEndpoint == null
+ || targetResourceId == null
+ || targetRegion == null) {
+ System.out.println("⚠️ Cross-resource copying requires environment variables:");
+ System.out.println(" SOURCE_RESOURCE_ID, SOURCE_REGION");
+ System.out.println(" TARGET_ENDPOINT, TARGET_KEY (optional), TARGET_RESOURCE_ID, TARGET_REGION");
+ System.out.println(" Skipping cross-resource copy test.");
+ return;
+ }
+
+ // Build target client with appropriate authentication
+ ContentUnderstandingClientBuilder targetBuilder
+ = new ContentUnderstandingClientBuilder().endpoint(targetEndpoint);
+ ContentUnderstandingAsyncClient targetAsyncClient;
+ if (targetKey != null && !targetKey.trim().isEmpty()) {
+ targetAsyncClient = targetBuilder.credential(new AzureKeyCredential(targetKey)).buildAsyncClient();
+ } else {
+ targetAsyncClient
+ = targetBuilder.credential(new DefaultAzureCredentialBuilder().build()).buildAsyncClient();
+ }
+
+ String sourceAnalyzerId = testResourceNamer.randomName("test_cross_resource_source_", 50);
+ String targetAnalyzerId = testResourceNamer.randomName("test_cross_resource_target_", 50);
+
+ try {
+ // Step 1: Create source analyzer
+ ContentAnalyzerConfig config = new ContentAnalyzerConfig();
+ config.setLayoutEnabled(true);
+ config.setOcrEnabled(true);
+
+ Map fields = new HashMap<>();
+ ContentFieldDefinition companyNameField = new ContentFieldDefinition();
+ companyNameField.setType(ContentFieldType.STRING);
+ companyNameField.setMethod(GenerationMethod.EXTRACT);
+ companyNameField.setDescription("Name of the company");
+ fields.put("company_name", companyNameField);
+
+ ContentFieldDefinition totalAmountField = new ContentFieldDefinition();
+ totalAmountField.setType(ContentFieldType.NUMBER);
+ totalAmountField.setMethod(GenerationMethod.EXTRACT);
+ totalAmountField.setDescription("Total amount on the document");
+ fields.put("total_amount", totalAmountField);
+
+ ContentFieldSchema fieldSchema = new ContentFieldSchema();
+ fieldSchema.setName("company_schema");
+ fieldSchema.setDescription("Schema for extracting company information");
+ fieldSchema.setFields(fields);
+
+ ContentAnalyzer sourceAnalyzer = new ContentAnalyzer();
+ sourceAnalyzer.setBaseAnalyzerId("prebuilt-document");
+ sourceAnalyzer.setDescription("Source analyzer for cross-resource copying");
+ sourceAnalyzer.setConfig(config);
+ sourceAnalyzer.setFieldSchema(fieldSchema);
+
+ Map models = new HashMap<>();
+ models.put("completion", "gpt-4.1");
+ sourceAnalyzer.setModels(models);
+
+ PollerFlux createPoller
+ = contentUnderstandingAsyncClient.beginCreateAnalyzer(sourceAnalyzerId, sourceAnalyzer);
+
+ // Use reactive pattern: chain operations using flatMap
+ // In a real application, you would use subscribe() instead of block()
+ ContentAnalyzer sourceResult = createPoller.last().flatMap(pollResponse -> {
+ if (pollResponse.getStatus().isComplete()) {
+ return pollResponse.getFinalResult();
+ } else {
+ return Mono.error(new RuntimeException(
+ "Polling completed unsuccessfully with status: " + pollResponse.getStatus()));
+ }
+ }).block(); // block() is used here for testing; in production, use subscribe()
+
+ System.out.println("Source analyzer '" + sourceAnalyzerId + "' created successfully!");
+
+ // Step 2: Grant copy authorization using convenience method
+ CopyAuthorization copyAuth = contentUnderstandingAsyncClient
+ .grantCopyAuthorization(sourceAnalyzerId, targetResourceId, targetRegion)
+ .block();
+
+ assertNotNull(copyAuth, "Copy authorization should not be null");
+ System.out.println("Copy authorization granted!");
+ System.out.println(" Target Azure Resource ID: " + copyAuth.getTargetAzureResourceId());
+ System.out.println(" Expires at: " + copyAuth.getExpiresAt());
+
+ // Step 3: Copy analyzer to target resource using convenience method
+ PollerFlux copyPoller = targetAsyncClient
+ .beginCopyAnalyzer(targetAnalyzerId, sourceAnalyzerId, false, sourceResourceId, sourceRegion);
+
+ // Use reactive pattern for copy operation as well
+ ContentAnalyzer targetResult = copyPoller.last().flatMap(pollResponse -> {
+ if (pollResponse.getStatus().isComplete()) {
+ return pollResponse.getFinalResult();
+ } else {
+ return Mono.error(new RuntimeException(
+ "Polling completed unsuccessfully with status: " + pollResponse.getStatus()));
+ }
+ }).block(); // block() is used here for testing; in production, use subscribe()
+
+ System.out.println("Target analyzer '" + targetAnalyzerId + "' copied successfully!");
+ System.out.println(" Description: " + targetResult.getDescription());
+
+ // Verify copied analyzer
+ ContentAnalyzer copiedAnalyzer = targetAsyncClient.getAnalyzer(targetAnalyzerId).block();
+ assertNotNull(copiedAnalyzer, "Copied analyzer should not be null");
+ assertEquals(sourceResult.getBaseAnalyzerId(), copiedAnalyzer.getBaseAnalyzerId());
+ assertEquals(sourceResult.getDescription(), copiedAnalyzer.getDescription());
+ System.out.println("Cross-resource copy verification completed");
+
+ } finally {
+ // Cleanup: delete both analyzers
+ try {
+ contentUnderstandingAsyncClient.deleteAnalyzer(sourceAnalyzerId).block();
+ System.out.println("Source analyzer '" + sourceAnalyzerId + "' deleted.");
+ } catch (Exception e) {
+ // Ignore cleanup errors
+ }
+
+ try {
+ targetAsyncClient.deleteAnalyzer(targetAnalyzerId).block();
+ System.out.println("Target analyzer '" + targetAnalyzerId + "' deleted.");
+ } catch (Exception e) {
+ // Ignore cleanup errors
+ }
+ }
+ }
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabels.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabels.java
new file mode 100644
index 000000000000..26dfc76e9926
--- /dev/null
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabels.java
@@ -0,0 +1,329 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package com.azure.ai.contentunderstanding.tests.samples;
+
+import com.azure.ai.contentunderstanding.models.AnalysisInput;
+import com.azure.ai.contentunderstanding.models.AnalysisResult;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzer;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerConfig;
+import com.azure.ai.contentunderstanding.models.ContentField;
+import com.azure.ai.contentunderstanding.models.ContentFieldDefinition;
+import com.azure.ai.contentunderstanding.models.ContentFieldSchema;
+import com.azure.ai.contentunderstanding.models.ContentFieldType;
+import com.azure.ai.contentunderstanding.models.DocumentContent;
+import com.azure.ai.contentunderstanding.models.GenerationMethod;
+import com.azure.ai.contentunderstanding.models.KnowledgeSource;
+import com.azure.ai.contentunderstanding.models.LabeledDataKnowledgeSource;
+import com.azure.core.credential.TokenCredential;
+import com.azure.core.util.polling.SyncPoller;
+import com.azure.identity.DefaultAzureCredentialBuilder;
+import org.junit.jupiter.api.Test;
+
+import com.azure.core.test.TestMode;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Sample demonstrates how to build analyzers with training labels (labeled data from Azure Blob Storage).
+ *
+ * This sample is mainly to show the API pattern for creating an analyzer with labeled training data.
+ * For an easier labeling workflow, use Azure AI Content Understanding Studio at
+ * https://contentunderstanding.ai.azure.com/
+ *
+ * Labeled receipt data is available in this repo at {@code src/samples/resources/receipt_labels}
+ * (images and corresponding .labels.json files). To use it for training:
+ *
+ * Manual instructions to upload labels into Azure Blob Storage:
+ *
+ * - Create an Azure Blob Storage container (or use an existing one).
+ * - Upload the contents of {@code src/samples/resources/receipt_labels} into the container.
+ * You may upload into the container root or into a subfolder (e.g., "receipt_labels/").
+ * - Generate a SAS (Shared Access Signature) URL for the container with at least List and Read
+ * permissions. In Azure Portal: Storage account → Containers → your container → Shared access
+ * token; set expiry and permissions, then generate the SAS URL.
+ * - Set {@code CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL} to the full SAS URL
+ * (e.g., {@code https://.blob.core.windows.net/?sv=...&se=...}).
+ * - If you uploaded into a subfolder, set {@code CONTENTUNDERSTANDING_TRAINING_DATA_PREFIX} to
+ * that path (e.g., "receipt_labels/"). If files are at the container root, omit the prefix
+ * or leave it unset.
+ *
+ *
+ * Each labeled document in the training folder includes:
+ *
+ * - The original file (e.g., PDF or image).
+ * - A corresponding .labels.json file with labeled fields.
+ * - A corresponding .result.json file with OCR results (optional).
+ *
+ *
+ * Required environment variables:
+ *
+ * - {@code CONTENTUNDERSTANDING_ENDPOINT} – Azure Content Understanding endpoint URL
+ *
+ *
+ * Optional environment variables (for labeled training data):
+ *
+ * - {@code CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL} – SAS URL for the Azure Blob container
+ * with labeled training data. If set, the analyzer is created with a labeled-data knowledge
+ * source; otherwise, created without training data.
+ * - {@code CONTENTUNDERSTANDING_TRAINING_DATA_PREFIX} – Path prefix within the container
+ * (e.g., "receipt_labels/" or "CreateAnalyzerWithLabels/"). Omit or leave unset if files
+ * are at the container root.
+ * - {@code CONTENTUNDERSTANDING_TRAINING_DATA_STORAGE_ACCOUNT} – Storage account name for
+ * auto-upload (Option B). Used when SAS URL is not set.
+ * - {@code CONTENTUNDERSTANDING_TRAINING_DATA_CONTAINER} – Container name for auto-upload
+ * (Option B). Used when SAS URL is not set.
+ *
+ */
+public class Sample16_CreateAnalyzerWithLabels extends ContentUnderstandingClientTestBase {
+
+ /**
+ * Demonstrates creating an analyzer with labeled training data.
+ *
+ * This test creates an analyzer with field schema. If TRAINING_DATA_SAS_URL is provided,
+ * labeled training data will be used; otherwise falls back to auto-upload if storage account
+ * and container are configured, or demonstrates the API pattern without actual training data.
+ */
+ @Test
+ public void testCreateAnalyzerWithLabels() {
+
+ String analyzerId = testResourceNamer.randomName("test_receipt_analyzer_", 50);
+ // In PLAYBACK mode, use a placeholder URL to ensure consistent test behavior
+ String trainingDataSasUrl = getTestMode() == TestMode.PLAYBACK
+ ? "https://placeholder.blob.core.windows.net/container?sv=placeholder"
+ : System.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL");
+ // Save prefix in test proxy variable during RECORD, load back during PLAYBACK so request bodies match.
+ String trainingDataPrefix;
+ if (getTestMode() == TestMode.PLAYBACK) {
+ String recorded = interceptorManager.getProxyVariableSupplier().get();
+ trainingDataPrefix = (recorded == null || recorded.isEmpty()) ? null : recorded;
+ } else if (getTestMode() == TestMode.RECORD) {
+ trainingDataPrefix = System.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_PREFIX");
+ interceptorManager.getProxyVariableConsumer().accept(trainingDataPrefix != null ? trainingDataPrefix : "");
+ } else {
+ trainingDataPrefix = System.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_PREFIX");
+ }
+
+ // Option B fallback: upload local label files and generate SAS URL
+ if ((trainingDataSasUrl == null || trainingDataSasUrl.trim().isEmpty()) && getTestMode() != TestMode.PLAYBACK) {
+ String storageAccount = System.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_STORAGE_ACCOUNT");
+ String container = System.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_CONTAINER");
+ if (storageAccount != null
+ && !storageAccount.trim().isEmpty()
+ && container != null
+ && !container.trim().isEmpty()) {
+ TokenCredential credential = new DefaultAzureCredentialBuilder().build();
+ String localDir = new File("src/samples/resources/receipt_labels").getAbsolutePath();
+ uploadTrainingData(storageAccount, container, credential, localDir, trainingDataPrefix);
+ trainingDataSasUrl = generateUserDelegationSasUrl(storageAccount, container, credential);
+ }
+ }
+
+ try {
+ // BEGIN: com.azure.ai.contentunderstanding.createAnalyzerWithLabels
+ // Step 1: Define field schema for receipt extraction
+ Map fields = new HashMap<>();
+
+ // MerchantName field
+ ContentFieldDefinition merchantNameField = new ContentFieldDefinition();
+ merchantNameField.setType(ContentFieldType.STRING);
+ merchantNameField.setMethod(GenerationMethod.EXTRACT);
+ merchantNameField.setDescription("Name of the merchant");
+ fields.put("MerchantName", merchantNameField);
+
+ // Items array field - define item structure
+ ContentFieldDefinition itemDefinition = new ContentFieldDefinition();
+ itemDefinition.setType(ContentFieldType.OBJECT);
+ itemDefinition.setMethod(GenerationMethod.EXTRACT);
+ itemDefinition.setDescription("Individual item details");
+
+ Map itemProperties = new HashMap<>();
+
+ ContentFieldDefinition quantityField = new ContentFieldDefinition();
+ quantityField.setType(ContentFieldType.STRING);
+ quantityField.setMethod(GenerationMethod.EXTRACT);
+ quantityField.setDescription("Quantity of the item");
+ itemProperties.put("Quantity", quantityField);
+
+ ContentFieldDefinition nameField = new ContentFieldDefinition();
+ nameField.setType(ContentFieldType.STRING);
+ nameField.setMethod(GenerationMethod.EXTRACT);
+ nameField.setDescription("Name of the item");
+ itemProperties.put("Name", nameField);
+
+ ContentFieldDefinition priceField = new ContentFieldDefinition();
+ priceField.setType(ContentFieldType.STRING);
+ priceField.setMethod(GenerationMethod.EXTRACT);
+ priceField.setDescription("Price of the item");
+ itemProperties.put("Price", priceField);
+
+ itemDefinition.setProperties(itemProperties);
+
+ // Items array field
+ ContentFieldDefinition itemsField = new ContentFieldDefinition();
+ itemsField.setType(ContentFieldType.ARRAY);
+ itemsField.setMethod(GenerationMethod.GENERATE);
+ itemsField.setDescription("List of items purchased");
+ itemsField.setItemDefinition(itemDefinition);
+ fields.put("Items", itemsField);
+
+ // TotalPrice field
+ ContentFieldDefinition totalPriceField = new ContentFieldDefinition();
+ totalPriceField.setType(ContentFieldType.STRING);
+ totalPriceField.setMethod(GenerationMethod.EXTRACT);
+ totalPriceField.setDescription("Total amount");
+ fields.put("TotalPrice", totalPriceField);
+
+ ContentFieldSchema fieldSchema = new ContentFieldSchema();
+ fieldSchema.setName("receipt_schema");
+ fieldSchema.setDescription("Schema for receipt extraction with items");
+ fieldSchema.setFields(fields);
+
+ // Step 2: Create labeled data knowledge source (optional, based on environment variable)
+ List knowledgeSources = new ArrayList<>();
+ if (trainingDataSasUrl != null && !trainingDataSasUrl.trim().isEmpty()) {
+ LabeledDataKnowledgeSource knowledgeSource
+ = new LabeledDataKnowledgeSource().setContainerUrl(trainingDataSasUrl);
+ if (trainingDataPrefix != null && !trainingDataPrefix.trim().isEmpty()) {
+ knowledgeSource.setPrefix(trainingDataPrefix);
+ }
+ knowledgeSources.add(knowledgeSource);
+ System.out.println("Using labeled training data from: "
+ + trainingDataSasUrl.substring(0, Math.min(50, trainingDataSasUrl.length())) + "...");
+ } else {
+ System.out.println("No TRAINING_DATA_SAS_URL set, creating analyzer without labeled training data");
+ }
+
+ // Step 3: Create analyzer (with or without labeled data)
+ Map models = new HashMap<>();
+ models.put("completion", "gpt-4.1");
+ models.put("embedding", "text-embedding-3-large");
+
+ ContentAnalyzer analyzer = new ContentAnalyzer().setBaseAnalyzerId("prebuilt-document")
+ .setDescription("Receipt analyzer with labeled training data")
+ .setConfig(new ContentAnalyzerConfig().setLayoutEnabled(true).setOcrEnabled(true))
+ .setFieldSchema(fieldSchema)
+ .setModels(models);
+
+ if (!knowledgeSources.isEmpty()) {
+ analyzer.setKnowledgeSources(knowledgeSources);
+ }
+
+ SyncPoller createPoller
+ = contentUnderstandingClient.beginCreateAnalyzer(analyzerId, analyzer, true);
+ ContentAnalyzer result = createPoller.getFinalResult();
+
+ System.out.println("Analyzer created: " + analyzerId);
+ System.out.println(" Description: " + result.getDescription());
+ System.out.println(" Base analyzer: " + result.getBaseAnalyzerId());
+ System.out.println(" Fields: " + result.getFieldSchema().getFields().size());
+ System.out.println(" Knowledge srcs: "
+ + (result.getKnowledgeSources() != null ? result.getKnowledgeSources().size() : 0));
+ // END: com.azure.ai.contentunderstanding.createAnalyzerWithLabels
+
+ // BEGIN: Assertion_ContentUnderstandingCreateAnalyzerWithLabels
+ // Verify analyzer creation
+ System.out.println("\nAnalyzer Creation Verification:");
+ assertNotNull(result, "Analyzer should not be null");
+ assertEquals("prebuilt-document", result.getBaseAnalyzerId());
+ assertEquals("Receipt analyzer with labeled training data", result.getDescription());
+ assertNotNull(result.getFieldSchema());
+ assertEquals("receipt_schema", result.getFieldSchema().getName());
+ assertEquals(3, result.getFieldSchema().getFields().size());
+ System.out.println("Analyzer created successfully");
+
+ // Verify field schema
+ Map resultFields = result.getFieldSchema().getFields();
+ assertTrue(resultFields.containsKey("MerchantName"), "Should have MerchantName field");
+ assertTrue(resultFields.containsKey("Items"), "Should have Items field");
+ assertTrue(resultFields.containsKey("TotalPrice"), "Should have TotalPrice field");
+
+ ContentFieldDefinition itemsFieldResult = resultFields.get("Items");
+ assertEquals(ContentFieldType.ARRAY, itemsFieldResult.getType());
+ assertNotNull(itemsFieldResult.getItemDefinition());
+ assertEquals(ContentFieldType.OBJECT, itemsFieldResult.getItemDefinition().getType());
+ assertEquals(3, itemsFieldResult.getItemDefinition().getProperties().size());
+ System.out.println("Field schema verified:");
+ System.out.println(" MerchantName: String (Extract)");
+ System.out.println(" Items: Array of Objects (Generate)");
+ System.out.println(" - Quantity, Name, Price");
+ System.out.println(" TotalPrice: String (Extract)");
+ // END: Assertion_ContentUnderstandingCreateAnalyzerWithLabels
+
+ // If training data was provided, test the analyzer with a sample document
+ if (trainingDataSasUrl != null && !trainingDataSasUrl.trim().isEmpty()) {
+ System.out.println("\nTesting analyzer with sample document...");
+ String testDocUrl
+ = "https://github.com/Azure-Samples/cognitive-services-REST-api-samples/raw/master/curl/form-recognizer/sample-invoice.pdf";
+
+ AnalysisInput input = new AnalysisInput();
+ input.setUrl(testDocUrl);
+
+ AnalysisResult AnalysisResult
+ = contentUnderstandingClient.beginAnalyze(analyzerId, Arrays.asList(input)).getFinalResult();
+
+ System.out.println("Analysis completed!");
+ assertNotNull(AnalysisResult);
+ assertNotNull(AnalysisResult.getContents());
+ assertTrue(AnalysisResult.getContents().size() > 0);
+
+ if (AnalysisResult.getContents().get(0) instanceof DocumentContent) {
+ DocumentContent docContent = (DocumentContent) AnalysisResult.getContents().get(0);
+ System.out.println("Extracted fields: " + docContent.getFields().size());
+
+ // Display extracted values
+ if (docContent.getFields().containsKey("MerchantName")) {
+ ContentField merchantField = docContent.getFields().get("MerchantName");
+ if (merchantField != null) {
+ String merchantName = (String) merchantField.getValue();
+ System.out.println(" MerchantName: " + merchantName);
+ }
+ }
+ if (docContent.getFields().containsKey("TotalPrice")) {
+ ContentField totalPriceFieldValue = docContent.getFields().get("TotalPrice");
+ if (totalPriceFieldValue != null) {
+ String totalPrice = (String) totalPriceFieldValue.getValue();
+ System.out.println(" TotalPrice: " + totalPrice);
+ }
+ }
+ }
+ }
+
+ // Display API pattern information
+ System.out.println("\nCreateAnalyzerWithLabels API Pattern:");
+ System.out.println(" 1. Define field schema with nested structures (arrays, objects)");
+ System.out.println(" 2. Upload training data to Azure Blob Storage:");
+ System.out.println(" - Documents: receipt1.jpg, receipt2.jpg, ...");
+ System.out.println(" - Labels: receipt1.jpg.labels.json, receipt2.jpg.labels.json, ...");
+ System.out.println(" - OCR: receipt1.jpg.result.json, receipt2.jpg.result.json, ...");
+ System.out.println(" 3. Create LabeledDataKnowledgeSource with storage SAS URL");
+ System.out.println(" 4. Create analyzer with field schema and knowledge sources");
+ System.out.println(" 5. Use analyzer for document analysis");
+
+ System.out.println("\nCreateAnalyzerWithLabels pattern demonstration completed");
+ if (trainingDataSasUrl == null || trainingDataSasUrl.trim().isEmpty()) {
+ System.out.println(" Note: This sample demonstrates the API pattern.");
+ System.out.println(
+ " For actual training, provide CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL with labeled data.");
+ }
+
+ } finally {
+ // Cleanup
+ try {
+ contentUnderstandingClient.deleteAnalyzer(analyzerId);
+ System.out.println("\nAnalyzer deleted: " + analyzerId);
+ } catch (Exception e) {
+ System.out.println("Note: Failed to delete analyzer: " + e.getMessage());
+ }
+ }
+ }
+
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsAsync.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsAsync.java
new file mode 100644
index 000000000000..a81793f1f0dc
--- /dev/null
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsAsync.java
@@ -0,0 +1,350 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package com.azure.ai.contentunderstanding.tests.samples;
+
+import com.azure.ai.contentunderstanding.models.AnalysisInput;
+import com.azure.ai.contentunderstanding.models.AnalysisResult;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzer;
+import com.azure.ai.contentunderstanding.models.ContentAnalyzerConfig;
+import com.azure.ai.contentunderstanding.models.ContentField;
+import com.azure.ai.contentunderstanding.models.ContentFieldDefinition;
+import com.azure.ai.contentunderstanding.models.ContentFieldSchema;
+import com.azure.ai.contentunderstanding.models.ContentFieldType;
+import com.azure.ai.contentunderstanding.models.DocumentContent;
+import com.azure.ai.contentunderstanding.models.GenerationMethod;
+import com.azure.ai.contentunderstanding.models.KnowledgeSource;
+import com.azure.ai.contentunderstanding.models.LabeledDataKnowledgeSource;
+import com.azure.core.credential.TokenCredential;
+import com.azure.core.util.polling.PollerFlux;
+import com.azure.identity.DefaultAzureCredentialBuilder;
+import reactor.core.publisher.Mono;
+import org.junit.jupiter.api.Test;
+
+import com.azure.core.test.TestMode;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Async sample demonstrates how to build analyzers with training labels (labeled data from Azure Blob Storage).
+ *
+ * This sample is mainly to show the API pattern for creating an analyzer with labeled training data.
+ * For an easier labeling workflow, use Azure AI Content Understanding Studio at
+ * https://contentunderstanding.ai.azure.com/
+ *
+ * Labeled receipt data is available in this repo at {@code src/samples/resources/receipt_labels}
+ * (images and corresponding .labels.json files). To use it for training:
+ *
+ * Manual instructions to upload labels into Azure Blob Storage:
+ *
+ * - Create an Azure Blob Storage container (or use an existing one).
+ * - Upload the contents of {@code src/samples/resources/receipt_labels} into the container.
+ * You may upload into the container root or into a subfolder (e.g., "receipt_labels/").
+ * - Generate a SAS (Shared Access Signature) URL for the container with at least List and Read
+ * permissions. In Azure Portal: Storage account → Containers → your container → Shared access
+ * token; set expiry and permissions, then generate the SAS URL.
+ * - Set {@code CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL} to the full SAS URL
+ * (e.g., {@code https://.blob.core.windows.net/?sv=...&se=...}).
+ * - If you uploaded into a subfolder, set {@code CONTENTUNDERSTANDING_TRAINING_DATA_PREFIX} to
+ * that path (e.g., "receipt_labels/"). If files are at the container root, omit the prefix
+ * or leave it unset.
+ *
+ *
+ * Each labeled document in the training folder includes:
+ *
+ * - The original file (e.g., PDF or image).
+ * - A corresponding .labels.json file with labeled fields.
+ * - A corresponding .result.json file with OCR results (optional).
+ *
+ *
+ * Required environment variables:
+ *
+ * - {@code CONTENTUNDERSTANDING_ENDPOINT} – Azure Content Understanding endpoint URL
+ *
+ *
+ * Optional environment variables (for labeled training data):
+ *
+ * - {@code CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL} – SAS URL for the Azure Blob container
+ * with labeled training data. If set, the analyzer is created with a labeled-data knowledge
+ * source; otherwise, created without training data.
+ * - {@code CONTENTUNDERSTANDING_TRAINING_DATA_PREFIX} – Path prefix within the container
+ * (e.g., "receipt_labels/" or "CreateAnalyzerWithLabels/"). Omit or leave unset if files
+ * are at the container root.
+ * - {@code CONTENTUNDERSTANDING_TRAINING_DATA_STORAGE_ACCOUNT} – Storage account name for
+ * auto-upload (Option B). Used when SAS URL is not set.
+ * - {@code CONTENTUNDERSTANDING_TRAINING_DATA_CONTAINER} – Container name for auto-upload
+ * (Option B). Used when SAS URL is not set.
+ *
+ */
+public class Sample16_CreateAnalyzerWithLabelsAsync extends ContentUnderstandingClientTestBase {
+
+ /**
+ * Demonstrates creating an analyzer with labeled training data.
+ *
+ * This test creates an analyzer with field schema. If TRAINING_DATA_SAS_URL is provided,
+ * labeled training data will be used; otherwise falls back to auto-upload if storage account
+ * and container are configured, or demonstrates the API pattern without actual training data.
+ */
+ @Test
+ public void testCreateAnalyzerWithLabelsAsync() {
+
+ String analyzerId = testResourceNamer.randomName("test_receipt_analyzer_", 50);
+ // In PLAYBACK mode, use a placeholder URL to ensure consistent test behavior
+ String trainingDataSasUrl = getTestMode() == TestMode.PLAYBACK
+ ? "https://placeholder.blob.core.windows.net/container?sv=placeholder"
+ : System.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL");
+ // Save prefix in test proxy variable during RECORD, load back during PLAYBACK so request bodies match.
+ String trainingDataPrefix;
+ if (getTestMode() == TestMode.PLAYBACK) {
+ String recorded = interceptorManager.getProxyVariableSupplier().get();
+ trainingDataPrefix = (recorded == null || recorded.isEmpty()) ? null : recorded;
+ } else if (getTestMode() == TestMode.RECORD) {
+ trainingDataPrefix = System.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_PREFIX");
+ interceptorManager.getProxyVariableConsumer().accept(trainingDataPrefix != null ? trainingDataPrefix : "");
+ } else {
+ trainingDataPrefix = System.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_PREFIX");
+ }
+
+ // Option B fallback: upload local label files and generate SAS URL
+ if ((trainingDataSasUrl == null || trainingDataSasUrl.trim().isEmpty()) && getTestMode() != TestMode.PLAYBACK) {
+ String storageAccount = System.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_STORAGE_ACCOUNT");
+ String container = System.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_CONTAINER");
+ if (storageAccount != null
+ && !storageAccount.trim().isEmpty()
+ && container != null
+ && !container.trim().isEmpty()) {
+ TokenCredential credential = new DefaultAzureCredentialBuilder().build();
+ String localDir = new File("src/samples/resources/receipt_labels").getAbsolutePath();
+ uploadTrainingData(storageAccount, container, credential, localDir, trainingDataPrefix);
+ trainingDataSasUrl = generateUserDelegationSasUrl(storageAccount, container, credential);
+ }
+ }
+
+ try {
+ // BEGIN: com.azure.ai.contentunderstanding.createAnalyzerWithLabelsAsync
+ // Step 1: Define field schema for receipt extraction
+ Map fields = new HashMap<>();
+
+ // MerchantName field
+ ContentFieldDefinition merchantNameField = new ContentFieldDefinition();
+ merchantNameField.setType(ContentFieldType.STRING);
+ merchantNameField.setMethod(GenerationMethod.EXTRACT);
+ merchantNameField.setDescription("Name of the merchant");
+ fields.put("MerchantName", merchantNameField);
+
+ // Items array field - define item structure
+ ContentFieldDefinition itemDefinition = new ContentFieldDefinition();
+ itemDefinition.setType(ContentFieldType.OBJECT);
+ itemDefinition.setMethod(GenerationMethod.EXTRACT);
+ itemDefinition.setDescription("Individual item details");
+
+ Map itemProperties = new HashMap<>();
+
+ ContentFieldDefinition quantityField = new ContentFieldDefinition();
+ quantityField.setType(ContentFieldType.STRING);
+ quantityField.setMethod(GenerationMethod.EXTRACT);
+ quantityField.setDescription("Quantity of the item");
+ itemProperties.put("Quantity", quantityField);
+
+ ContentFieldDefinition nameField = new ContentFieldDefinition();
+ nameField.setType(ContentFieldType.STRING);
+ nameField.setMethod(GenerationMethod.EXTRACT);
+ nameField.setDescription("Name of the item");
+ itemProperties.put("Name", nameField);
+
+ ContentFieldDefinition priceField = new ContentFieldDefinition();
+ priceField.setType(ContentFieldType.STRING);
+ priceField.setMethod(GenerationMethod.EXTRACT);
+ priceField.setDescription("Price of the item");
+ itemProperties.put("Price", priceField);
+
+ itemDefinition.setProperties(itemProperties);
+
+ // Items array field
+ ContentFieldDefinition itemsField = new ContentFieldDefinition();
+ itemsField.setType(ContentFieldType.ARRAY);
+ itemsField.setMethod(GenerationMethod.GENERATE);
+ itemsField.setDescription("List of items purchased");
+ itemsField.setItemDefinition(itemDefinition);
+ fields.put("Items", itemsField);
+
+ // TotalPrice field
+ ContentFieldDefinition totalPriceField = new ContentFieldDefinition();
+ totalPriceField.setType(ContentFieldType.STRING);
+ totalPriceField.setMethod(GenerationMethod.EXTRACT);
+ totalPriceField.setDescription("Total amount");
+ fields.put("TotalPrice", totalPriceField);
+
+ ContentFieldSchema fieldSchema = new ContentFieldSchema();
+ fieldSchema.setName("receipt_schema");
+ fieldSchema.setDescription("Schema for receipt extraction with items");
+ fieldSchema.setFields(fields);
+
+ // Step 2: Create labeled data knowledge source (optional, based on environment variable)
+ List knowledgeSources = new ArrayList<>();
+ if (trainingDataSasUrl != null && !trainingDataSasUrl.trim().isEmpty()) {
+ LabeledDataKnowledgeSource knowledgeSource
+ = new LabeledDataKnowledgeSource().setContainerUrl(trainingDataSasUrl);
+ if (trainingDataPrefix != null && !trainingDataPrefix.trim().isEmpty()) {
+ knowledgeSource.setPrefix(trainingDataPrefix);
+ }
+ knowledgeSources.add(knowledgeSource);
+ System.out.println("Using labeled training data from: "
+ + trainingDataSasUrl.substring(0, Math.min(50, trainingDataSasUrl.length())) + "...");
+ } else {
+ System.out.println("No TRAINING_DATA_SAS_URL set, creating analyzer without labeled training data");
+ }
+
+ // Step 3: Create analyzer (with or without labeled data)
+ Map models = new HashMap<>();
+ models.put("completion", "gpt-4.1");
+ models.put("embedding", "text-embedding-3-large");
+
+ ContentAnalyzer analyzer = new ContentAnalyzer().setBaseAnalyzerId("prebuilt-document")
+ .setDescription("Receipt analyzer with labeled training data")
+ .setConfig(new ContentAnalyzerConfig().setLayoutEnabled(true).setOcrEnabled(true))
+ .setFieldSchema(fieldSchema)
+ .setModels(models);
+
+ if (!knowledgeSources.isEmpty()) {
+ analyzer.setKnowledgeSources(knowledgeSources);
+ }
+
+ PollerFlux createPoller
+ = contentUnderstandingAsyncClient.beginCreateAnalyzer(analyzerId, analyzer, true);
+
+ // Use reactive pattern: chain operations using flatMap
+ // In a real application, you would use subscribe() instead of block()
+ ContentAnalyzer result = createPoller.last().flatMap(pollResponse -> {
+ if (pollResponse.getStatus().isComplete()) {
+ return pollResponse.getFinalResult();
+ } else {
+ return Mono.error(new RuntimeException(
+ "Polling completed unsuccessfully with status: " + pollResponse.getStatus()));
+ }
+ }).block(); // block() is used here for testing; in production, use subscribe()
+
+ System.out.println("Analyzer created: " + analyzerId);
+ System.out.println(" Description: " + result.getDescription());
+ System.out.println(" Base analyzer: " + result.getBaseAnalyzerId());
+ System.out.println(" Fields: " + result.getFieldSchema().getFields().size());
+ System.out.println(" Knowledge srcs: "
+ + (result.getKnowledgeSources() != null ? result.getKnowledgeSources().size() : 0));
+ // END: com.azure.ai.contentunderstanding.createAnalyzerWithLabelsAsync
+
+ // BEGIN: Assertion_ContentUnderstandingCreateAnalyzerWithLabelsAsync
+ // Verify analyzer creation
+ System.out.println("\nAnalyzer Creation Verification:");
+ assertNotNull(result, "Analyzer should not be null");
+ assertEquals("prebuilt-document", result.getBaseAnalyzerId());
+ assertEquals("Receipt analyzer with labeled training data", result.getDescription());
+ assertNotNull(result.getFieldSchema());
+ assertEquals("receipt_schema", result.getFieldSchema().getName());
+ assertEquals(3, result.getFieldSchema().getFields().size());
+ System.out.println("Analyzer created successfully");
+
+ // Verify field schema
+ Map resultFields = result.getFieldSchema().getFields();
+ assertTrue(resultFields.containsKey("MerchantName"), "Should have MerchantName field");
+ assertTrue(resultFields.containsKey("Items"), "Should have Items field");
+ assertTrue(resultFields.containsKey("TotalPrice"), "Should have TotalPrice field");
+
+ ContentFieldDefinition itemsFieldResult = resultFields.get("Items");
+ assertEquals(ContentFieldType.ARRAY, itemsFieldResult.getType());
+ assertNotNull(itemsFieldResult.getItemDefinition());
+ assertEquals(ContentFieldType.OBJECT, itemsFieldResult.getItemDefinition().getType());
+ assertEquals(3, itemsFieldResult.getItemDefinition().getProperties().size());
+ System.out.println("Field schema verified:");
+ System.out.println(" MerchantName: String (Extract)");
+ System.out.println(" Items: Array of Objects (Generate)");
+ System.out.println(" - Quantity, Name, Price");
+ System.out.println(" TotalPrice: String (Extract)");
+ // END: Assertion_ContentUnderstandingCreateAnalyzerWithLabelsAsync
+
+ // If training data was provided, test the analyzer with a sample document
+ if (trainingDataSasUrl != null && !trainingDataSasUrl.trim().isEmpty()) {
+ System.out.println("\nTesting analyzer with sample document...");
+ String testDocUrl
+ = "https://github.com/Azure-Samples/cognitive-services-REST-api-samples/raw/master/curl/form-recognizer/sample-invoice.pdf";
+
+ AnalysisInput input = new AnalysisInput();
+ input.setUrl(testDocUrl);
+
+ PollerFlux analyzePoller
+ = contentUnderstandingAsyncClient.beginAnalyze(analyzerId, Arrays.asList(input));
+
+ // Use reactive pattern for analyze operation
+ AnalysisResult AnalysisResult = analyzePoller.last().flatMap(pollResponse -> {
+ if (pollResponse.getStatus().isComplete()) {
+ return pollResponse.getFinalResult();
+ } else {
+ return Mono.error(new RuntimeException(
+ "Polling completed unsuccessfully with status: " + pollResponse.getStatus()));
+ }
+ }).block(); // block() is used here for testing; in production, use subscribe()
+
+ System.out.println("Analysis completed!");
+ assertNotNull(AnalysisResult);
+ assertNotNull(AnalysisResult.getContents());
+ assertTrue(AnalysisResult.getContents().size() > 0);
+
+ if (AnalysisResult.getContents().get(0) instanceof DocumentContent) {
+ DocumentContent docContent = (DocumentContent) AnalysisResult.getContents().get(0);
+ System.out.println("Extracted fields: " + docContent.getFields().size());
+
+ // Display extracted values
+ if (docContent.getFields().containsKey("MerchantName")) {
+ ContentField merchantField = docContent.getFields().get("MerchantName");
+ if (merchantField != null) {
+ String merchantName = (String) merchantField.getValue();
+ System.out.println(" MerchantName: " + merchantName);
+ }
+ }
+ if (docContent.getFields().containsKey("TotalPrice")) {
+ ContentField totalPriceFieldValue = docContent.getFields().get("TotalPrice");
+ if (totalPriceFieldValue != null) {
+ String totalPrice = (String) totalPriceFieldValue.getValue();
+ System.out.println(" TotalPrice: " + totalPrice);
+ }
+ }
+ }
+ }
+
+ // Display API pattern information
+ System.out.println("\nCreateAnalyzerWithLabels API Pattern:");
+ System.out.println(" 1. Define field schema with nested structures (arrays, objects)");
+ System.out.println(" 2. Upload training data to Azure Blob Storage:");
+ System.out.println(" - Documents: receipt1.jpg, receipt2.jpg, ...");
+ System.out.println(" - Labels: receipt1.jpg.labels.json, receipt2.jpg.labels.json, ...");
+ System.out.println(" - OCR: receipt1.jpg.result.json, receipt2.jpg.result.json, ...");
+ System.out.println(" 3. Create LabeledDataKnowledgeSource with storage SAS URL");
+ System.out.println(" 4. Create analyzer with field schema and knowledge sources");
+ System.out.println(" 5. Use analyzer for document analysis");
+
+ System.out.println("\nCreateAnalyzerWithLabels pattern demonstration completed");
+ if (trainingDataSasUrl == null || trainingDataSasUrl.trim().isEmpty()) {
+ System.out.println(" Note: This sample demonstrates the API pattern.");
+ System.out.println(
+ " For actual training, provide CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL with labeled data.");
+ }
+
+ } finally {
+ // Cleanup
+ try {
+ contentUnderstandingAsyncClient.deleteAnalyzer(analyzerId).block();
+ System.out.println("\nAnalyzer deleted: " + analyzerId);
+ } catch (Exception e) {
+ System.out.println("Note: Failed to delete analyzer: " + e.getMessage());
+ }
+ }
+ }
+
+}
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsAsyncTest.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsAsyncTest.java
index a8ff69adff65..d53f550aa4b9 100644
--- a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsAsyncTest.java
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsAsyncTest.java
@@ -239,7 +239,7 @@ public void testCreateAnalyzerWithLabelsAsync() {
= contentUnderstandingAsyncClient.beginAnalyze(analyzerId, Arrays.asList(input));
// Use reactive pattern for analyze operation
- AnalysisResult analyzeResult = analyzePoller.last().flatMap(pollResponse -> {
+ AnalysisResult AnalysisResult = analyzePoller.last().flatMap(pollResponse -> {
if (pollResponse.getStatus().isComplete()) {
return pollResponse.getFinalResult();
} else {
@@ -249,12 +249,12 @@ public void testCreateAnalyzerWithLabelsAsync() {
}).block(); // block() is used here for testing; in production, use subscribe()
System.out.println("Analysis completed!");
- assertNotNull(analyzeResult);
- assertNotNull(analyzeResult.getContents());
- assertTrue(analyzeResult.getContents().size() > 0);
+ assertNotNull(AnalysisResult);
+ assertNotNull(AnalysisResult.getContents());
+ assertTrue(AnalysisResult.getContents().size() > 0);
- if (analyzeResult.getContents().get(0) instanceof DocumentContent) {
- DocumentContent docContent = (DocumentContent) analyzeResult.getContents().get(0);
+ if (AnalysisResult.getContents().get(0) instanceof DocumentContent) {
+ DocumentContent docContent = (DocumentContent) AnalysisResult.getContents().get(0);
System.out.println("Extracted fields: " + docContent.getFields().size());
// Display extracted values
diff --git a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsTest.java b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsTest.java
index 9a7813712e7c..0393e49ae812 100644
--- a/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsTest.java
+++ b/sdk/contentunderstanding/azure-ai-contentunderstanding/src/test/java/com/azure/ai/contentunderstanding/tests/samples/Sample16_CreateAnalyzerWithLabelsTest.java
@@ -224,16 +224,16 @@ public void testCreateAnalyzerWithLabels() {
AnalysisInput input = new AnalysisInput();
input.setUrl(testDocUrl);
- AnalysisResult analyzeResult
+ AnalysisResult AnalysisResult
= contentUnderstandingClient.beginAnalyze(analyzerId, Arrays.asList(input)).getFinalResult();
System.out.println("Analysis completed!");
- assertNotNull(analyzeResult);
- assertNotNull(analyzeResult.getContents());
- assertTrue(analyzeResult.getContents().size() > 0);
+ assertNotNull(AnalysisResult);
+ assertNotNull(AnalysisResult.getContents());
+ assertTrue(AnalysisResult.getContents().size() > 0);
- if (analyzeResult.getContents().get(0) instanceof DocumentContent) {
- DocumentContent docContent = (DocumentContent) analyzeResult.getContents().get(0);
+ if (AnalysisResult.getContents().get(0) instanceof DocumentContent) {
+ DocumentContent docContent = (DocumentContent) AnalysisResult.getContents().get(0);
System.out.println("Extracted fields: " + docContent.getFields().size());
// Display extracted values