Skip to content

Commit 70bcd19

Browse files
changjian-wangChangjian WangCopilotwangchangjian1130Copilot
authored
Update setup script and skills for Azure AI Content Understanding (#46618)
* Add setup script and GitHub Copilot skills for Azure AI Content Understanding SDK - Created setup_user_env.sh for environment setup, including virtual environment creation, SDK installation, and .env configuration. - Added sdk-common-knowledge skill for domain knowledge reference on Content Understanding concepts and SDK usage. - Introduced sdk-py-sample-run skill to guide users in running specific samples interactively. - Implemented run_sample.sh script to facilitate running sync and async samples with user prompts and error handling. - Updated README.md to include information about new skills and usage instructions for GitHub Copilot. * Update REST API Reference link in SKILL.md * Rename skill from sdk-py-setup to cu-sdk-setup and update script path in SKILL.md * Update SKILL.md and run_sample.sh to reference cu-sdk-setup instead of sdk-py-setup * Update SKILL.md files to enhance clarity and detail for SDK setup and usage Co-authored-by: Copilot <copilot@github.com> * Update SKILL.md to remove outdated sections and add related skills for improved guidance Co-authored-by: Copilot <copilot@github.com> * Add SKILL.md files for cu-sdk-common-knowledge and cu-sdk-py-sample-run with detailed guidance and sample execution instructions Co-authored-by: Copilot <copilot@github.com> * Update skill references in README.md for consistency and clarity * Add cu-sdk-sample-run skill and update related documentation - Introduced `cu-sdk-sample-run` skill to guide users in running specific samples interactively. - Updated `SKILL.md` for `cu-sdk-common-knowledge` and `cu-sdk-setup` to include references to the new skill. - Corrected skill name in README and adjusted example prompts accordingly. Co-authored-by: Copilot <copilot@github.com> * Enhance setup scripts for Azure AI Content Understanding SDK with Python version checks and model deployment probing Co-authored-by: Copilot <copilot@github.com> * Enhance Python environment setup guidance for Windows users in SKILL.md and setup_user_env.ps1 Co-authored-by: Copilot <copilot@github.com> * Enhance Python installation prompts and dependency checks in setup scripts for Azure AI Content Understanding SDK Co-authored-by: Copilot <copilot@github.com> * Soften runtime wording for sample_analyze_url.py per PR feedback * Add inline cspell:ignore directive in setup_user_env.sh for pyver/esac * Add Python SDK resources and helper script documentation to SKILL.md * Update SKILL.md to correct link for creating custom analyzer to Python programming language Co-authored-by: Copilot <copilot@github.com> * Fix Copilot review comments on cu-sdk-setup scripts - setup_user_env.sh L374: remove stray trailing 'gpt41mini' token from the read fallback. With 'set -e', running the script non-interactively (or hitting EOF) would treat the token as a command and abort the script. - setup_user_env.ps1 Set-EnvValue: switch [regex]::Replace to a MatchEvaluator script block so values containing regex replacement metacharacters (\, \$&, \$\$, \, \\) are written to .env literally. Affects user-supplied API keys and endpoints that may contain '\$'. * [CU SDK] Improve cu-sdk-sample-run skill alignment and workflow SKILL.md: - Add 4 missing alignment items (1 sample, 4 env vars) to match net/java/js - Restructure Workflow into 5 steps: Navigate, Activate, Choose, Configure, Run - Move sample selection (Step 3) before sample-specific configuration (Step 4) - Reorder Step 4 subsections: training data option (A/B) before local files - Add sample_create_analyzer_with_labels to Step 3 quick list - Fix Troubleshooting cross-references (Step 3 -> Step 4) - Tighten gating language on conditional [ASK USER] prompts run_sample.sh: - Add --help / -h and --list / -l flags (mirrors Java/JS pattern) * [CU SDK] Enhance sample_create_analyzer_with_labels functionality and add demo mode warnings Co-authored-by: Copilot <copilot@github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Address Copilot review: load .env in run_sample.sh, async test uses local PDF * Refactor load_env_file function to use consistent variable naming for environment file Co-authored-by: Copilot <copilot@github.com> * Fix mypy errors in sample_create_analyzer_with_labels - Annotate knowledge_sources as list[KnowledgeSource] to avoid list invariance issue when passing list[LabeledDataKnowledgeSource] to ContentAnalyzer.knowledge_sources - Use StringField.value_string (the underlying generated attribute) instead of the .value property which mypy does not see through the _patch.py TYPE_CHECKING redefinition * Fix test_sample_grant_copy_auth playback failure by stripping trailing slash from target endpoint --------- Co-authored-by: Changjian Wang <v-changjwang@microsoft.com> Co-authored-by: Copilot <copilot@github.com> Co-authored-by: aluneth <wangchangjian1130@163.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent cdf82ba commit 70bcd19

12 files changed

Lines changed: 691 additions & 84 deletions

File tree

sdk/contentunderstanding/azure-ai-contentunderstanding/.github/skills/cu-sdk-common-knowledge/SKILL.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Always read the relevant page (via `fetch_webpage`) before answering if the refe
3030
| **Service limits** | https://learn.microsoft.com/azure/ai-services/content-understanding/service-limits |
3131
| **Region & language support** | https://learn.microsoft.com/azure/ai-services/content-understanding/language-region-support |
3232
| **Prebuilt analyzers** | https://learn.microsoft.com/azure/ai-services/content-understanding/concepts/prebuilt-analyzers |
33-
| **Create custom analyzer** | https://learn.microsoft.com/azure/ai-services/content-understanding/tutorial/create-custom-analyzer?tabs=portal%2Cdocument&pivots=programming-language-rest |
33+
| **Create custom analyzer** | https://learn.microsoft.com/azure/ai-services/content-understanding/tutorial/create-custom-analyzer?tabs=portal%2Cdocument&pivots=programming-language-python |
3434
| **Document markdown** | https://learn.microsoft.com/azure/ai-services/content-understanding/document/markdown |
3535
| **Document elements** | https://learn.microsoft.com/azure/ai-services/content-understanding/document/elements |
3636
| **Video overview** | https://learn.microsoft.com/azure/ai-services/content-understanding/video/overview |
@@ -41,6 +41,14 @@ Always read the relevant page (via `fetch_webpage`) before answering if the refe
4141

4242
> **Search tip:** If the above pages don't cover the user's question, search the doc tree at `https://learn.microsoft.com/azure/ai-services/content-understanding/`.
4343
44+
### Python SDK Resources
45+
46+
| Resource | Link |
47+
|----------|------|
48+
| **Python package on PyPI** | https://pypi.org/project/azure-ai-contentunderstanding/ |
49+
| **Python SDK README** | https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/contentunderstanding/azure-ai-contentunderstanding/README.md |
50+
| **Python SDK Samples** | https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/contentunderstanding/azure-ai-contentunderstanding/samples |
51+
4452
## Related Skills
4553

4654
- `cu-sdk-setup` — Set up Python environment and run samples

sdk/contentunderstanding/azure-ai-contentunderstanding/.github/skills/cu-sdk-sample-run/SKILL.md

Lines changed: 229 additions & 17 deletions
Large diffs are not rendered by default.

sdk/contentunderstanding/azure-ai-contentunderstanding/.github/skills/cu-sdk-sample-run/scripts/run_sample.sh

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/bin/bash
2+
# cspell:ignore esac
23
# Run a specific sample for Azure AI Content Understanding SDK
34
# Usage: ./run_sample.sh <sample_name>
45
# Example: ./run_sample.sh sample_analyze_url
@@ -40,11 +41,33 @@ list_samples() {
4041
echo ""
4142
}
4243

43-
# Check for --list flag
44-
if [ "$1" == "--list" ] || [ "$1" == "-l" ]; then
45-
list_samples
46-
exit 0
47-
fi
44+
show_help() {
45+
cat <<EOF
46+
Usage: $(basename "$0") <sample_name> [options]
47+
48+
Run a Python SDK sample for Azure AI Content Understanding.
49+
50+
Arguments:
51+
<sample_name> Sample name (e.g. sample_analyze_url), with or without .py
52+
extension. Append "_async" to run the async variant.
53+
54+
Options:
55+
--list, -l List all available samples and exit.
56+
--help, -h Show this help message.
57+
58+
Examples:
59+
$(basename "$0") sample_analyze_url
60+
$(basename "$0") sample_analyze_invoice.py
61+
$(basename "$0") sample_analyze_url_async
62+
$(basename "$0") --list
63+
EOF
64+
}
65+
66+
# Check for --help / --list flags
67+
case "$1" in
68+
--help|-h) show_help; exit 0 ;;
69+
--list|-l) list_samples; exit 0 ;;
70+
esac
4871

4972
# Check if sample name is provided
5073
if [ -z "$1" ]; then
@@ -106,12 +129,80 @@ else
106129
print_info "Using active virtual environment: $VIRTUAL_ENV"
107130
fi
108131

109-
# Check for .env file
110-
if [ ! -f ".env" ] && [ ! -f "$RUN_DIR/.env" ]; then
132+
# Load environment variables from a .env file (safe parser).
133+
# Only accepts `[export ]NAME=VALUE` lines where NAME is a valid shell
134+
# identifier; strips a single matching pair of surrounding double or single
135+
# quotes from the value. Lines that don't match are skipped silently. We avoid
136+
# `eval`/`source` so a malicious or malformed .env file cannot execute
137+
# arbitrary shell code in this script's process.
138+
load_env_file() {
139+
local env_file="$1"
140+
[ -f "$env_file" ] || return 0
141+
while IFS= read -r line || [ -n "$line" ]; do
142+
# Strip leading whitespace and skip empties / comments
143+
line="${line#"${line%%[![:space:]]*}"}"
144+
[ -z "$line" ] && continue
145+
case "$line" in \#*) continue ;; esac
146+
# Optional leading `export `
147+
case "$line" in export\ *) line="${line#export }" ;; esac
148+
# Must contain `=` and start with a valid identifier
149+
case "$line" in
150+
[a-zA-Z_]*=*) ;;
151+
*) continue ;;
152+
esac
153+
local name="${line%%=*}"
154+
local value="${line#*=}"
155+
# Validate identifier (letters, digits, underscore only)
156+
case "$name" in *[!a-zA-Z0-9_]*) continue ;; esac
157+
# Strip a matching pair of surrounding quotes
158+
if [[ "$value" == \"*\" ]]; then
159+
value="${value%\"}"
160+
value="${value#\"}"
161+
elif [[ "$value" == \'*\' ]]; then
162+
value="${value%\'}"
163+
value="${value#\'}"
164+
fi
165+
export "$name=$value"
166+
done < "$env_file"
167+
}
168+
169+
# Check for .env file and load it so later checks (e.g. DEMO MODE) and the
170+
# sample subprocess both see the configured variables.
171+
ENV_FILE=""
172+
if [ -f ".env" ]; then
173+
ENV_FILE=".env"
174+
elif [ -f "$RUN_DIR/.env" ]; then
175+
ENV_FILE="$RUN_DIR/.env"
176+
fi
177+
if [ -n "$ENV_FILE" ]; then
178+
print_info "Loading environment from $ENV_FILE"
179+
load_env_file "$ENV_FILE"
180+
else
111181
print_warning "⚠ No .env file found. Some samples may fail without environment variables."
112182
echo " Run: cp env.sample .env && edit .env"
113183
fi
114184

185+
# sample_create_analyzer_with_labels demo-mode banner: warn if the user is about
186+
# to run the labeled-data sample without configuring either Option A (SAS URL)
187+
# or Option B (storage account + container) — the sample will still run but skip
188+
# the labeled-data code path AND the analyze-test step.
189+
if [[ "$SAMPLE_NAME" == sample_create_analyzer_with_labels* ]]; then
190+
if [[ -z "${CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL:-}" ]]; then
191+
if [[ -z "${CONTENTUNDERSTANDING_TRAINING_DATA_STORAGE_ACCOUNT:-}" \
192+
|| -z "${CONTENTUNDERSTANDING_TRAINING_DATA_CONTAINER:-}" ]]; then
193+
print_warning "⚠ DEMO MODE: no training data configured for $SAMPLE_NAME."
194+
echo " The analyzer will be created without labeled data ('Knowledge sources: 0')."
195+
echo " To exercise the labeled-data API path AND test the analyzer with a sample"
196+
echo " document, configure ONE of:"
197+
echo " Option A: CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL=<container SAS URL>"
198+
echo " Option B: CONTENTUNDERSTANDING_TRAINING_DATA_STORAGE_ACCOUNT=<account>"
199+
echo " CONTENTUNDERSTANDING_TRAINING_DATA_CONTAINER=<container>"
200+
echo " then re-run this script (it will reload .env automatically)."
201+
echo ""
202+
fi
203+
fi
204+
fi
205+
115206
# Run the sample
116207
echo ""
117208
print_info "=== Running: $SAMPLE_NAME ==="

sdk/contentunderstanding/azure-ai-contentunderstanding/.github/skills/cu-sdk-setup/scripts/setup_user_env.ps1

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,14 @@ function Set-EnvValue {
311311
$pattern = "(?m)^$([regex]::Escape($Key))=.*$"
312312
$replacement = "$Key=$Value"
313313
if ([regex]::IsMatch($content, $pattern)) {
314-
$content = [regex]::Replace($content, $pattern, $replacement)
314+
# Use a MatchEvaluator so $ and \ in user-supplied values (API keys,
315+
# endpoints) are written literally instead of being interpreted as
316+
# regex replacement metacharacters ($1, $&, $$, etc.).
317+
$content = [regex]::Replace(
318+
$content,
319+
$pattern,
320+
[System.Text.RegularExpressions.MatchEvaluator] { param($match) $replacement }
321+
)
315322
} else {
316323
if ($content -and -not $content.EndsWith("`n")) { $content += "`n" }
317324
$content += "$replacement`n"

sdk/contentunderstanding/azure-ai-contentunderstanding/.github/skills/cu-sdk-setup/scripts/setup_user_env.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ PY
371371

372372
# GPT_4_1_MINI_DEPLOYMENT
373373
if [ -z "$gpt41mini" ]; then
374-
read -r -p "Enter GPT_4_1_MINI_DEPLOYMENT (default: gpt-4.1-mini): " gpt41mini || gpt41mini="" gpt41mini
374+
read -r -p "Enter GPT_4_1_MINI_DEPLOYMENT (default: gpt-4.1-mini): " gpt41mini || gpt41mini=""
375375
gpt41mini="${gpt41mini:-gpt-4.1-mini}"
376376
else
377377
echo " ✓ Using detected GPT_4_1_MINI_DEPLOYMENT=$gpt41mini"

sdk/contentunderstanding/azure-ai-contentunderstanding/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
### Features Added
1414
- Added `to_llm_input` helper function that converts `AnalysisResult` objects into LLM-friendly text with YAML front matter and markdown content. Supports documents, audio/video, and classification hierarchies.
1515

16+
### Other Changes
17+
- Aligned `sample_create_analyzer_with_labels` (sync + async) with the .NET and Java equivalents: added an analyze step (calls `begin_analyze` on the newly created analyzer to extract `MerchantName` / `TotalPrice` from a sample invoice when training data is configured), a `DEMO MODE` banner when no training data is configured, a field-schema verification banner, and `try` / `finally` cleanup so the analyzer is deleted even if creation fails.
18+
1619
## 1.1.0 (2026-04-20)
1720

1821
### Features Added

sdk/contentunderstanding/azure-ai-contentunderstanding/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/contentunderstanding/azure-ai-contentunderstanding",
5-
"Tag": "python/contentunderstanding/azure-ai-contentunderstanding_3b4e92c5d3"
5+
"Tag": "python/contentunderstanding/azure-ai-contentunderstanding_afaf0a7032"
66
}

sdk/contentunderstanding/azure-ai-contentunderstanding/samples/async_samples/sample_create_analyzer_with_labels_async.py

Lines changed: 124 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,23 @@
9191
import time
9292
from datetime import datetime, timedelta, timezone
9393
from pathlib import Path
94+
from typing import cast
9495

9596
from dotenv import load_dotenv
9697
from azure.ai.contentunderstanding.aio import ContentUnderstandingClient
9798
from azure.ai.contentunderstanding.models import (
99+
AnalysisInput,
100+
AnalysisResult,
98101
ContentAnalyzer,
99102
ContentAnalyzerConfig,
100103
ContentFieldDefinition,
101104
ContentFieldSchema,
102105
ContentFieldType,
106+
DocumentContent,
103107
GenerationMethod,
108+
KnowledgeSource,
104109
LabeledDataKnowledgeSource,
110+
StringField,
105111
)
106112
from azure.core.credentials import AzureKeyCredential
107113
from azure.identity.aio import DefaultAzureCredential
@@ -245,7 +251,7 @@ async def main() -> None:
245251

246252
# Step 3: Create knowledge source from labeled data (if available)
247253
training_data_prefix = os.getenv("CONTENTUNDERSTANDING_TRAINING_DATA_PREFIX")
248-
knowledge_sources = []
254+
knowledge_sources: list[KnowledgeSource] = []
249255
if training_data_sas_url:
250256
labeled_source = LabeledDataKnowledgeSource(
251257
container_url=training_data_sas_url,
@@ -254,8 +260,25 @@ async def main() -> None:
254260
if training_data_prefix:
255261
labeled_source.prefix = training_data_prefix
256262
knowledge_sources.append(labeled_source)
263+
redacted_training_data_url = training_data_sas_url.split("?", 1)[0]
264+
print(
265+
f"Using labeled training data from configured container: {redacted_training_data_url}"
266+
)
267+
else:
268+
print(
269+
"DEMO MODE: no training data configured. The analyzer will be created without labeled data."
270+
)
271+
print(
272+
" Set CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL (Option A), or both"
273+
)
274+
print(
275+
" CONTENTUNDERSTANDING_TRAINING_DATA_STORAGE_ACCOUNT and CONTENTUNDERSTANDING_TRAINING_DATA_CONTAINER (Option B),"
276+
)
277+
print(
278+
" to fully exercise the labeled-data API path."
279+
)
257280

258-
# Step 4: Create the analyzer
281+
# Step 4: Create the analyzer (with or without labeled data)
259282
custom_analyzer = ContentAnalyzer(
260283
base_analyzer_id="prebuilt-document",
261284
description="Receipt analyzer with labeled training data",
@@ -265,35 +288,108 @@ async def main() -> None:
265288
"completion": "gpt-4.1",
266289
"embedding": "text-embedding-3-large",
267290
},
268-
knowledge_sources=[],
291+
knowledge_sources=knowledge_sources or None,
269292
)
270-
for source in knowledge_sources:
271-
if custom_analyzer.knowledge_sources is not None:
272-
custom_analyzer.knowledge_sources.append(source)
273-
274-
poller = await client.begin_create_analyzer(
275-
analyzer_id=analyzer_id,
276-
resource=custom_analyzer,
277-
allow_replace=True,
278-
)
279-
result = await poller.result()
280293

281-
print(f"Analyzer created: {analyzer_id}")
282-
print(f" Description: {result.description}")
283-
print(f" Base analyzer: {result.base_analyzer_id}")
284-
print(
285-
f" Fields: {len(result.field_schema.fields) if result.field_schema and result.field_schema.fields else 0}"
286-
)
287-
print(
288-
f" Knowledge sources: {len(result.knowledge_sources) if result.knowledge_sources else 0}"
289-
)
290-
# [END create_analyzer_with_labels]
294+
try:
295+
poller = await client.begin_create_analyzer(
296+
analyzer_id=analyzer_id,
297+
resource=custom_analyzer,
298+
allow_replace=True,
299+
)
300+
result = await poller.result()
301+
302+
print(f"Analyzer created: {analyzer_id}")
303+
print(f" Description: {result.description}")
304+
print(f" Base analyzer: {result.base_analyzer_id}")
305+
print(
306+
f" Fields: {len(result.field_schema.fields) if result.field_schema and result.field_schema.fields else 0}"
307+
)
308+
print(
309+
f" Knowledge sources: {len(result.knowledge_sources) if result.knowledge_sources else 0}"
310+
)
311+
# [END create_analyzer_with_labels]
312+
313+
# Verify analyzer creation
314+
print("\nAnalyzer Creation Verification:")
315+
print("Analyzer created successfully")
316+
317+
# Verify field schema
318+
print("Field schema verified:")
319+
print(" MerchantName: String (Extract)")
320+
print(" Items: Array of Objects (Generate)")
321+
print(" - Quantity, Name, Price")
322+
print(" TotalPrice: String (Extract)")
323+
324+
if result.field_schema and result.field_schema.fields:
325+
items_field_result = result.field_schema.fields.get("Items")
326+
if items_field_result and items_field_result.item_definition:
327+
print("Items field verified:")
328+
print(f" Type: {items_field_result.type}")
329+
print(
330+
f" Item properties: {len(items_field_result.item_definition.properties or {})}"
331+
)
332+
333+
# If training data was provided, test the analyzer with a sample document.
334+
if training_data_sas_url:
335+
print("\nTesting analyzer with sample document...")
336+
sample_document_path = (
337+
Path(__file__).resolve().parent.parent / "sample_files" / "sample_invoice.pdf"
338+
)
339+
with open(sample_document_path, "rb") as sample_document:
340+
sample_document_bytes = sample_document.read()
341+
analyze_poller = await client.begin_analyze(
342+
analyzer_id=analyzer_id,
343+
inputs=[AnalysisInput(data=sample_document_bytes)],
344+
)
345+
analyze_result: AnalysisResult = await analyze_poller.result()
346+
print("Analysis completed!")
347+
348+
if analyze_result.contents:
349+
content = analyze_result.contents[0]
350+
if isinstance(content, DocumentContent):
351+
doc_content = cast(DocumentContent, content)
352+
print(
353+
f"Extracted fields: {len(doc_content.fields or {})}"
354+
)
355+
if doc_content.fields:
356+
merchant_field = doc_content.fields.get("MerchantName")
357+
if isinstance(merchant_field, StringField) and merchant_field.value_string:
358+
print(f" MerchantName: {merchant_field.value_string}")
359+
total_field = doc_content.fields.get("TotalPrice")
360+
if isinstance(total_field, StringField) and total_field.value_string:
361+
print(f" TotalPrice: {total_field.value_string}")
362+
363+
# Display API pattern information
364+
print("\nCreateAnalyzerWithLabels API Pattern:")
365+
print(" 1. Define field schema with nested structures (arrays, objects)")
366+
print(" 2. Upload training data to Azure Blob Storage:")
367+
print(" - Documents: receipt1.jpg, receipt2.jpg, ...")
368+
print(" - Labels: receipt1.jpg.labels.json, receipt2.jpg.labels.json, ...")
369+
print(" - OCR: receipt1.jpg.result.json, receipt2.jpg.result.json, ...")
370+
print(" 3. Create LabeledDataKnowledgeSource with storage SAS URL")
371+
print(" 4. Create analyzer with field schema and knowledge sources")
372+
print(" 5. Use analyzer for document analysis")
373+
374+
print("\nCreateAnalyzerWithLabels pattern demonstration completed")
375+
if not training_data_sas_url:
376+
print(" Note: This sample demonstrates the API pattern.")
377+
print(
378+
" For actual training, provide CONTENTUNDERSTANDING_TRAINING_DATA_SAS_URL (Option A)"
379+
)
380+
print(
381+
" or CONTENTUNDERSTANDING_TRAINING_DATA_STORAGE_ACCOUNT + ..._CONTAINER (Option B)."
382+
)
291383

292-
# Clean up - delete the analyzer
293-
# Note: In production code, you typically keep analyzers and reuse them for
294-
# multiple analyses. Deletion is mainly useful for testing and development cleanup.
295-
await client.delete_analyzer(analyzer_id=analyzer_id)
296-
print(f"Analyzer '{analyzer_id}' deleted.")
384+
finally:
385+
# Clean up - delete the analyzer
386+
# Note: In production code, you typically keep analyzers and reuse them for
387+
# multiple analyses. Deletion is mainly useful for testing and development cleanup.
388+
try:
389+
await client.delete_analyzer(analyzer_id=analyzer_id)
390+
print(f"\nAnalyzer deleted: {analyzer_id}")
391+
except Exception as cleanup_err: # pylint: disable=broad-except
392+
print(f"Note: Failed to delete analyzer: {cleanup_err}")
297393

298394
if not isinstance(credential, AzureKeyCredential):
299395
await credential.close()

0 commit comments

Comments
 (0)