From b26c0ada282789f3a2e65b469f912bc94eaf6d03 Mon Sep 17 00:00:00 2001 From: xiangyan99 Date: Fri, 24 Apr 2026 09:12:06 -0700 Subject: [PATCH 1/5] added the create-package-skill and updated search skill based on it. --- .github/skills/create-package-skill/SKILL.md | 81 +++++ .../phases/00-scan-package.md | 93 +++++ .../phases/01-scaffold-skill.md | 229 ++++++++++++ .../phases/02-generate-references.md | 99 ++++++ .../phases/03-validate.md | 46 +++ .../phases/04-register.md | 48 +++ .../references/skill-template.md | 112 ++++++ .../references/validation-tools.md | 24 ++ .../skills/azure-search-documents/SKILL.md | 220 +++++++----- .../references/customizations.md | 326 +++++++++++++----- 10 files changed, 1100 insertions(+), 178 deletions(-) create mode 100644 .github/skills/create-package-skill/SKILL.md create mode 100644 .github/skills/create-package-skill/phases/00-scan-package.md create mode 100644 .github/skills/create-package-skill/phases/01-scaffold-skill.md create mode 100644 .github/skills/create-package-skill/phases/02-generate-references.md create mode 100644 .github/skills/create-package-skill/phases/03-validate.md create mode 100644 .github/skills/create-package-skill/phases/04-register.md create mode 100644 .github/skills/create-package-skill/references/skill-template.md create mode 100644 .github/skills/create-package-skill/references/validation-tools.md diff --git a/.github/skills/create-package-skill/SKILL.md b/.github/skills/create-package-skill/SKILL.md new file mode 100644 index 000000000000..9e2ef9a0d8bd --- /dev/null +++ b/.github/skills/create-package-skill/SKILL.md @@ -0,0 +1,81 @@ +--- +name: create-package-skill +description: 'Interactive wizard that walks service teams through creating a package-specific skill for their Azure SDK package. Scans the package, detects customization patterns, scaffolds a SKILL.md with references, validates with vally lint, and registers in find-package-skill. WHEN: create package skill; add service skill; bootstrap skill for package; new package skill; skill for my SDK package; write skill for search; write skill for cosmos.' +--- + +# Create Package Skill Wizard + +> **Minimal beats comprehensive. Human-written beats auto-generated. Scaffold and iterate.** + +> Skills encode tribal knowledge — the "I wish someone had told me" stuff that's hard to learn from just reading code. Focus on what's non-obvious and package-specific. + +## Interaction Protocols + +**CONFIRM Protocol** (asset-producing steps — creating files): +1. PRESENT the proposed assets and explain why. +2. ASK exactly one question: "Create now (recommended), edit first, or skip?" +3. ACT immediately. Create → write files this turn. Edit → refine, re-ask. Skip → move on. + +**DECIDE Protocol** (informational/correction steps — no files created): +1. PRESENT the information or findings. +2. ASK one specific question appropriate to the decision. +3. PROCEED based on the answer. + +One question at a time. Respect "skip" — never re-ask or defer. + +## Wizard Flow + +Run each phase in order. **Progressive loading:** Read only the current phase file. + +| Phase | Description | Instructions | +|---|---|---| +| **Phase 0** | 🧭 Scan Package — detect architecture, customizations, key files | [phases/00-scan-package.md](phases/00-scan-package.md) | +| **Phase 1** | 📝 Scaffold SKILL.md — generate skill with step-by-step post-regen workflow (Option A) or reference-manual structure (Option B) | [phases/01-scaffold-skill.md](phases/01-scaffold-skill.md) | +| **Phase 2** | 📚 Generate References — create customizations.md (required) and optionally architecture.md | [phases/02-generate-references.md](phases/02-generate-references.md) | +| **Phase 3** | Validate -- run vally lint | [phases/03-validate.md](phases/03-validate.md) | +| **Phase 4** | 📋 Register — add to find-package-skill table | [phases/04-register.md](phases/04-register.md) | + +## Guardrails + +**Content:** +- Every line must be non-obvious and package-specific. No generic Python/SDK boilerplate. +- SKILL.md should be under 500 tokens (soft limit). Move details to references/. +- References under 1000 tokens each. Split if larger. +- Never duplicate what's already in `.github/copilot-instructions.md` or shared skills. + +**Relationship to existing SDK tools:** +- Package skills **complement** the Azure SDK MCP tools (`azsdk_package_generate_code`, `azsdk_package_build_code`, `azsdk_customized_code_update`, etc.) — they do NOT replace them. +- MCP tools handle deterministic operations (generate, build, test). Package skills provide the reasoning context an agent needs to use those tools correctly for a specific package, plus the package-specific verification commands the tools do not know about. +- Reference the existing tools for the overall workflow (e.g., "Run `tsp-client update` or `azsdk_package_generate_code`"), but DO include the copy-pasteable verification commands an agent needs: import smoke tests (`python -c "from ... import ..."`), `ApiVersion` reconciliation (`grep`, `python -c "import json; ..."`), diff commands (`git diff --name-only | grep ...`), and lint invocations (`azpysdk mypy`, `azpysdk pylint`). +- Never paraphrase an MCP tool's entire contract. Do include the one-line invocations the agent will run. + +**Structure:** +- Skill directory: `sdk///.github/skills//` +- Directory name MUST match `name` field in frontmatter (vally lint enforces this). +- `name` is the distribution package name, e.g. `azure-search-documents`, `azure-ai-projects`, `azure-mgmt-appconfiguration`. +- Use semicolons to separate trigger phrases in description (YAML-safe). + +**Security:** +- Never embed secrets or credentials in skill content. +- Never instruct agents to bypass CI, pylint, mypy, pyright, or other gating tools. +- Never instruct agents to edit files inside `_generated/` (or any file with the `"Code generated by Microsoft (R) Python Code Generator"` header) — always route through `_patch.py` or sibling hand-written modules. + +## Key Principles (from eval data) + +Our eval showed that skill **structure** matters more than **volume**: + +| Pattern | Impact | +|---|---| +| Numbered step-by-step post-regen workflow (Option A) | Agent executes verification top-to-bottom without inventing steps | +| Copy-pasteable verification commands (import smoke tests, `ApiVersion` reconciliation, git diff of generated operations/models) | Agent actually runs the checks instead of guessing whether customizations still work | +| "Expose new generated methods through `_patch.py`" step (mixin inheritance / override / polymorphic / create-or-update / list / `__all__` re-export) | Agent wires new generated operations into the public surface instead of leaving them unreachable | +| "Check `_patch.py` FIRST" directives | Changes agent default from "fix the error location" to "check the customization layer" | +| Per-file `Depends On` / `Defines` / `After Regeneration, Verify` inventory in `customizations.md` | Agent can pinpoint exactly which generated symbol a breakage maps to | +| Generated-file detection guidance (directory OR header comment) | Agent correctly identifies auto-generated files under both `_generated/` and inline layouts | +| Async parity checklist | Agent remembers to mirror sync changes into `aio/` | +| CHANGELOG / README update step | Prevents "silent" releases that ship new operations without docs | + +## References (load on demand) + +- [references/skill-template.md](references/skill-template.md) -- SKILL.md template with required sections +- [references/validation-tools.md](references/validation-tools.md) -- vally lint, CI workflow setup diff --git a/.github/skills/create-package-skill/phases/00-scan-package.md b/.github/skills/create-package-skill/phases/00-scan-package.md new file mode 100644 index 000000000000..b3bb2316309a --- /dev/null +++ b/.github/skills/create-package-skill/phases/00-scan-package.md @@ -0,0 +1,93 @@ +# Phase 0: Scan Package 🧭 + +> 📍 **Phase 0 — Scan Package** | Detect the package's architecture, customization patterns, and key files. + +## Step 1 — Identify the Package + +Ask the user: "Which SDK package should I create a skill for?" + +The user should provide either: +- A package name (e.g., `azure-search-documents`, `azure-ai-projects`, `azure-mgmt-appconfiguration`) +- A path (e.g., `sdk/search/azure-search-documents`, `sdk/ai/azure-ai-projects`) + +Resolve to the package root directory. Verify it exists and contains at least one of: `pyproject.toml`, `setup.py`, `tsp-location.yaml`, or a `swagger/README.md` (legacy autorest). + +## Step 2 — Scan the Package + +Scan the package using the checklist below. Use file_search / grep_search / read_file tools. + +### Scan Checklist + +1. **Code generation toolchain**: + - `tsp-location.yaml` → TypeSpec-generated package. Note the spec directory and commit SHA. + - `_metadata.json` → if present, record that `apiVersion` lives here (the skill will reconcile it against the hand-maintained `ApiVersion` enum). + - `swagger/README.md` → legacy autorest-generated (mgmt packages typically). + - Neither → hand-written package (rare). + +2. **Namespace root**: Derive from the package name by replacing `-` with `/` (e.g., `azure-ai-projects` → `/azure/ai/projects`). Verify the directory exists. + +3. **Generated layout detection**: + - If `/_generated/` exists → **nested layout**. Generated files are confined to `_generated/`; everything else is hand-written/customization. + - If `_generated/` is absent → **inline layout** (newer pattern). Generated files are identified by the header comment `"Code generated by Microsoft (R) Python Code Generator"` at the top of each file. + +4. **Customizations** (`_patch.py` at each level): + - `/_patch.py` — top-level client customization + - `/models/_patch.py` — model customization + - `/operations/_patch.py` — sync operations customization + - `/aio/_patch.py` — async client customization + - `/aio/operations/_patch.py` — async operations customization + + For each, check if it is **empty** (only copyright header, default docstring, empty `__all__`, empty `patch_sdk()`) or **non-empty** (defines classes/functions, or `__all__` has entries). Record the non-empty patches. + +5. **Hand-written utility modules**: Glob all `.py` files under `` excluding `_generated/`, `__init__.py`, `_version.py`, `_patch.py`. Classify each as generated (has the header) or custom. For custom utility modules, read the first few lines to get a one-line purpose. + +6. **Public API surface**: Read each `__init__.py` (top-level, `models/`, `operations/`, `aio/`, `aio/operations/`). Note re-exports from `_patch.py` and any renames/omissions vs `_generated/__init__.py` (when nested layout). + +7. **Async parity**: For each non-empty sync customization file, check whether the async counterpart exists under `aio/`. Record `identical`, `equivalent`, `divergent`, `sync-only`, or `async-only`. + +8. **Service API versions**: Search for an `ApiVersion` enum or `api_version` constants under `` (often inside `_patch.py` itself, not `_generated/`). Record the source file path, but do **not** hard-code the list of versions into the skill — point to the source file. + +9. **Hand-authored named classes**: Grep for `class .*Buffered|class .*Batching|class .*Sender|class .*Paged|class .*PageIterator` under ``. These are hand-authored utilities that the skill must name in its Customization Patterns Reference. + +10. **Top public symbols (for import smoke tests)**: Read the top-level `__init__.py` and `aio/__init__.py`. Record up to 3–5 most important public names (clients, senders, key models/enums) so Phase 1 can generate accurate `python -c "from import "` commands. + +11. **Tests**: Check `tests/` directory. Look for `conftest.py`, `*test_base*.py`, `assets.json` (recorded tests via Test Proxy), `testpreparer*.py`. Note whether tests are live, recorded, or both. + +12. **Documentation surfaces**: Check for `CHANGELOG.md` and `README.md` at the package root. Both are near-universal; the skill's Step 8 (Update Documentation and Samples) will reference them. + +13. **Management vs data plane**: If package name starts with `azure-mgmt-` → management-plane rules apply (see copilot-instructions). Otherwise data-plane. + +## Step 3 — Present Package Profile + +Print a concise summary: + +📋 **Package Profile** + +| Field | Value | +|---|---| +| Package | `` (e.g., `azure-ai-projects`) | +| Path | `sdk//` | +| Plane | data / mgmt | +| Generated | TypeSpec (`tsp-location.yaml`) / autorest (`swagger/README.md`) / hand-written | +| Metadata | `_metadata.json` present: Y/N | +| Layout | nested (`_generated/`) / inline (header-based) | +| Non-empty `_patch.py` files | List which levels have customizations (top / models / operations / aio / aio/operations / indexes…) | +| Custom utility modules | N hand-written `.py` files outside `_patch.py` | +| Hand-authored named classes | e.g., `SearchIndexingBufferedSender`, `SearchItemPaged` (or `none`) | +| Top public symbols | List of 3–5 names used for Phase 1 import smoke tests | +| Public API surface | Notes on re-exports, renames, omissions | +| Async parity | summary (identical / divergent / sync-only / async-only) | +| `ApiVersion` enum | source file + present Y/N | +| Tests | Unit: Y, Live: Y/N, Recorded (assets.json): Y/N | +| Docs | `CHANGELOG.md`: Y/N, `README.md`: Y/N | +| Suggested structure | Option A (step-by-step) / Option B (reference manual) | + +## Step 4 — DECIDE + +Question: "Does this profile look right? Anything to add or correct?" + +📍 **Phase 0 complete** | Package scanned | Next: Phase 1 + +--- +## → Next: Phase 1 — Scaffold SKILL.md +Read [01-scaffold-skill.md](01-scaffold-skill.md) and begin immediately. diff --git a/.github/skills/create-package-skill/phases/01-scaffold-skill.md b/.github/skills/create-package-skill/phases/01-scaffold-skill.md new file mode 100644 index 000000000000..ce9ce46e4457 --- /dev/null +++ b/.github/skills/create-package-skill/phases/01-scaffold-skill.md @@ -0,0 +1,229 @@ +# Phase 1: Scaffold SKILL.md 📝 + +> 📍 **Phase 1 — Scaffold SKILL.md** | Generate the main skill file. + +> 📖 Read `references/skill-template.md` for the full template and the two allowed structure options. + +Using the package profile from Phase 0, generate a `SKILL.md` at: +``` +sdk///.github/skills//SKILL.md +``` + +The skill directory name MUST match the Python distribution package name +(e.g., `azure-search-documents`, `azure-ai-projects`, `azure-mgmt-appconfiguration`). +This is both the `name` in frontmatter and the directory name (vally lint enforces the match). + +## Structure decision + +From the Phase 0 profile, pick the structure option that fits: + +- **Option A — step-by-step workflow** (preferred when the package has multiple non-empty `_patch.py` files, hand-authored utility modules, or a hand-maintained `ApiVersion` enum). This is how `azure-search-documents` is structured. +- **Option B — reference manual** (simpler packages with few customizations; use when Option A would feel padded). + +Ask the user to confirm the choice, defaulting to A when any of these are true: >=2 non-empty `_patch.py`; `ApiVersion`/`DEFAULT_VERSION` present in `_patch.py`; hand-authored `Buffered`/`Batching`/`Paged` class detected. + +## Content principles (recap) + +- **Keep it static** regarding release state (no version numbers, no current default API version) — but **do** include copy-pasteable commands the agent will need. +- **Point to source** for mutable things (API version enum → point at `_patch.py`; generator target → point at `_metadata.json`). +- **Prefer TypeSpec over `_patch.py`** when the customization is expressible in the spec — note this in the customization patterns section. +- **Include MCP tool invocations** where appropriate (`azsdk_package_generate_code`, `azsdk_package_run_check`, etc.), but also include the direct CLI equivalents (`tsp-client update`, `azpysdk mypy`, `azpysdk pylint`) because agents routinely run those too. + +## Required sections — Option A (step-by-step) + +### Frontmatter + +```yaml +--- +name: +description: '. WHEN: regenerate ; modify ; fix bug; add feature; tsp-client update; edit _patch.py.' +--- +``` + +Semicolons separate triggers (YAML-safe). Every trigger should include the distribution package name. + +### Intro (2–4 sentences) + +State the core premise in plain language: +- The generator never touches `_patch.py`, so customizations survive regeneration. +- But `_patch.py` imports from generated modules, which DO change. +- A rename / new parameter / removed enum value silently breaks `_patch.py`. +- The following steps catch those breaks. + +### Step 1 — Run Regeneration + +Minimal shell block with `cd sdk//`, then `tsp-client update` +(or `azsdk_package_generate_code`). Mention updating `tsp-location.yaml` with the new +spec commit SHA, and `_metadata.json` if the API version changed. + +### Step 2 — Verify Imports in All `_patch.py` Files + +Provide import smoke-test commands for the top public symbols (fill from Phase 0 scan): + +```bash +python -c "from import " +python -c "from .aio import " +python -c "from . import , " +``` + +List every non-empty `_patch.py` file in a tree diagram with its role (one line each). +Tell the agent: if any import fails, a generated class/enum was renamed or removed — +check `references/customizations.md` for the exact import each `_patch.py` depends on. + +### Step 3 — Check ApiVersion (only if `ApiVersion` enum exists in a `_patch.py`) + +Concrete commands to reconcile `_metadata.json` with the hand-maintained enum: + +```bash +python -c "import json; print(json.load(open('_metadata.json'))['apiVersion'])" +grep -A 20 'class ApiVersion' +grep 'DEFAULT_VERSION' +``` + +If the generated API version is not in the enum: add the member, update `DEFAULT_VERSION`, +update the docstring. Include a round-trip verification: + +```bash +python -c "from ._patch import ApiVersion, DEFAULT_VERSION; print(DEFAULT_VERSION.value)" +``` + +### Step 4 — Expose New Generated Methods Through `_patch.py` + +New generated operations do NOT automatically appear on the public surface in the +shape users expect. For each new method, decide which exposure path applies: + +```bash +# What new operations did regeneration add? +git diff --name-only | grep "_operations\.py" | grep -v _patch +git diff | grep -E '^\+\s+(async\s+)?def ' +``` + +Exposure paths (pick the one that applies to each new method): + +1. **Pass-through via generated mixin inheritance** — if the generated signature is + already user-friendly, the `_OperationsMixin` subclass in `_patch.py` + inherits it automatically. Only verify the method name does NOT collide with an + existing `_patch.py` override. + +2. **Override in the operations mixin** — if the method needs custom paging, custom + request building, response conversion, or error handling, add the override to + the `_OperationsMixin` class in the appropriate `_operations/_patch.py` + (and the async mirror). Typical triggers: custom `ItemPaged`, request-body + rewriting, `@search.*` field extraction, 413 batch splitting. + +3. **Polymorphic str-or-model wrapper** — for `delete_*` operations on a named + resource. Accept either a `str` name or the resource model; forward `e_tag` / + `match_condition` when the caller passes a model. + +4. **Create-or-update wrapper** — for `create_or_update_*` operations. Forward + `prefer="return=representation"`, `match_condition`, and `etag`; plus any + package-specific flags (e.g., `allow_index_downtime`, `skip_indexer_reset`, + `skip/disable cache`). + +5. **List projection wrapper** — for `list_*` operations that need a `select` + parameter, a name-only projection via `cls` callback, or a conversion from a + generated projection type back to the canonical model + (e.g., `_convert__response`). + +6. **Re-export via `__all__`** — any NEW public symbol defined or overridden in + `_patch.py` MUST be added to that file's `__all__`. Without this, `patch_sdk()` + will not surface it and the symbol will be missing from `from import *` + and from the `__init__.py` public API. Verify with: + ```bash + python -c "import ; print(sorted(.__all__))" + ``` + +Reminder: every sync exposure/wrapper needs a matching async mirror under `aio/`. +Skip the categories above that do not apply to this package. + +### Step 5 — Check for Model/Enum Changes That Affect Customizations + +```bash +git diff +``` + +List what to watch for, tailored to the package (from Phase 0 scan): +- Renamed enum values → backward-compat aliases in `_patch.py` reference old names +- Changed model constructors → subclassed models in `_patch.py` may break +- New fields on response models → extractor helpers need to be updated +- Changed request models → builder helpers need to wire new parameters through + +### Step 6 — Ensure mypy pass + +```bash +cd sdk// +azpysdk mypy +``` + +Mention what `mypy.ini` already ignores (typically generated internals). + +### Step 7 — Ensure pylint pass + +```bash +cd sdk// +azpysdk pylint +``` + +Mention what `pylintrc` excludes (typically `_generated/`, `_vendor/`, `tests/`, `samples/`). + +### Step 8 — Update Documentation and Samples + +**CHANGELOG**: +- Find the topmost `## (Unreleased)` section in `CHANGELOG.md`. +- Add entries under `### Features Added`, `### Breaking Changes`, or `### Bugs Fixed`. +- If no `(Unreleased)` section exists, create one above the latest release with the next version from `_version.py`. +- Include a small example entry block. + +**README**: +- Update usage examples for new client classes or operations. +- Update Key Concepts for new resource types. +- Update listed API version in Getting Started if it changed. + +### Customization Patterns Reference (inline) + +For each hand-written pattern that actually exists in the package (from Phase 0 scan), +add a 2–5 sentence subsection naming the pattern and describing *what it does* and +*what would break it*. Typical patterns: + +- Constructor reorder on a public client subclass +- Custom paging (standard `ItemPaged` replaced with a custom iterator) +- Hand-authored batching / buffered sender +- Polymorphic delete / create-or-update pattern +- Backward-compatible enum aliases (camelCase → UPPER_CASE) +- Field builders +- Monkey-patched staticmethod helpers +- Wire-format encoding helpers (e.g., pipe-delimited parameters) + +Each subsection should point to the specific `_patch.py` it lives in and, where relevant, +note: "Use `_patch.py` when TypeSpec cannot express the behavior, or when the behavior +is Python-specific. For TypeSpec-level customizations (preferred when possible), see +[typespec-python emitter docs](https://github.com/Azure/autorest.python/blob/main/packages/typespec-python/README.md)." + +### Pointer to `references/customizations.md` + +Single line / short paragraph telling the agent to read `references/customizations.md` +for the exhaustive file-by-file inventory. + +## Required sections — Option B (reference manual) + +See `references/skill-template.md` *Option B* for the section list. Apply the same +required content (tree of non-empty `_patch.py`, async parity reminder, import smoke +tests, `ApiVersion` reconciliation, CHANGELOG/README step, per-pattern reference). + +## Step 1 — Present + +Generate the full SKILL.md content and print it. Fill in package-specific details from +the Phase 0 profile. Mark anything you could not confirm with `` or +``. + +## Step 2 — CONFIRM + +Question: "Create this SKILL.md now (recommended), edit first, or skip?" + +If confirmed, create the skill directory and SKILL.md file. + +📍 **Phase 1 complete** | Created: SKILL.md | Next: Phase 2 + +--- +## → Next: Phase 2 — Generate References +Read [02-generate-references.md](02-generate-references.md) and begin immediately. diff --git a/.github/skills/create-package-skill/phases/02-generate-references.md b/.github/skills/create-package-skill/phases/02-generate-references.md new file mode 100644 index 000000000000..608d14d88541 --- /dev/null +++ b/.github/skills/create-package-skill/phases/02-generate-references.md @@ -0,0 +1,99 @@ +# Phase 2: Generate References 📚 + +> 📍 **Phase 2 — Generate References** | Create supporting reference documents. + +Only two reference files are ever considered, and only one is required: + +| File | Required? | Purpose | +|---|---|---| +| `references/customizations.md` | **Required** if any `_patch.py` file is non-empty, or hand-written utility modules exist | File-by-file inventory of every non-empty `_patch.py` | +| `references/architecture.md` | **Optional** | Only create if the layout is complex enough that a tree diagram inline in SKILL.md is insufficient | + +Packages like `azure-search-documents` intentionally ship with `customizations.md` only — the layout fits comfortably inline in SKILL.md. + +## references/customizations.md (canonical format) + +Use one **section per non-empty `_patch.py` file**, in this structure: + +``` +## File: `/_patch.py` + +### Depends On (from generated code) +- `` as `` (base class / direct import) +- ... + +### Defines +| Symbol | Type | What It Does | +|--------|------|-------------| +| `` | class / function / constant | | +| ... | ... | ... | + +### After Regeneration, Verify +- [ ] +- [ ] +- [ ] ... +``` + +If the file imports shared helpers from the sync partner instead of duplicating them, +add an **Imports From Sync (NOT Duplicated)** subsection listing the symbols. + +For each polymorphic-wrapper mixin or multi-method class, use a nested table of +`Method | Customization`. + +End the file with a section listing **Empty `_patch.py` Files (No Customizations)** — +just file paths, to tell the agent "no action needed after regeneration" for these. + +### Extraction rules + +- Read the actual `_patch.py` files. Extract imports, class names, function names, + method names, and monkey-patched assignments (e.g., `X.Foo = Y.FOO`). +- Use real snippets — do not fabricate code. +- For each `Defines` row, the "What It Does" cell MUST be grounded in the actual source. +- For each `After Regeneration, Verify` checkbox, pick the specific generated symbol + whose change would break this `_patch.py` — list that symbol by name. +- Do NOT include version numbers, current default API version values, or release-specific info. + +### Patterns worth calling out (when they exist) + +If any of these patterns appear in the package, each MUST be documented: + +- Constructor reorder / parameter swap on a public client subclass +- Custom paging (replacement of `ItemPaged` / `AsyncItemPaged`) +- Hand-authored batching / buffered sender (not generated at all) +- Polymorphic delete / create-or-update taking str or model object +- Backward-compatible enum aliases (assignment statements like `E.Foo = E.FOO`) +- Monkey-patched staticmethod (e.g., `E.Collection = staticmethod(Collection)`) +- Field builders (e.g., `SimpleField`, `SearchableField`, `ComplexField`) +- Response-model conversion helpers (e.g., `_convert_*_response`) +- Request-model builder helpers (e.g., `_build_*_request`) +- Continuation-token pack / unpack helpers + +## references/architecture.md (optional) + +Only create this file when the layout is complex enough that a tree diagram inline in SKILL.md is insufficient. + +If you do create it, include: + +- **Repository layout** — directory tree with generated/hand-written annotations +- **Namespace layout** — tree under `/` +- **Code generation** — toolchain (TypeSpec `@azure-tools/typespec-python` or autorest), `tsp-location.yaml` format, `_metadata.json` +- **Generated vs custom** — table with how to identify each +- **Public client types** — sync/async client classes and file locations +- **API version management** — where the version enum lives, how `_patch.py` interacts with it +- **Key supporting files** — policies, helpers, `mypy.ini`, `assets.json`, etc. +- **Dependencies** — key runtime / dev / test deps +- **Build / test / lint** — MCP tools and `azpysdk` entry points (not full command re-documentation) + +## Step 1 — Present + +Print the proposed reference files content. + +## Step 2 — CONFIRM + +Question: "Create these reference files now (recommended), edit first, or skip?" + +📍 **Phase 2 complete** | Created: references/ | Next: Phase 3 + +--- +## → Next: Phase 3 — Validate +Read [03-validate.md](03-validate.md) and begin immediately. diff --git a/.github/skills/create-package-skill/phases/03-validate.md b/.github/skills/create-package-skill/phases/03-validate.md new file mode 100644 index 000000000000..4c4d160b55ec --- /dev/null +++ b/.github/skills/create-package-skill/phases/03-validate.md @@ -0,0 +1,46 @@ +# Phase 3: Validate + +> 📍 **Phase 3 — Validate** | Run structural validation on the new skill. + +> 📖 Read `references/validation-tools.md` for tool details. + +## Step 1 — Structural Validation (vally lint) + +Run vally lint against the skill: + +```powershell +# If vally is installed (npm package from microsoft/evaluate) +vally lint sdk///.github/skills/ + +# If vally is not installed, check manually: +# - SKILL.md exists with valid YAML frontmatter +# - name field matches directory name (= distribution package name, e.g. azure-ai-projects) +# - All markdown links in SKILL.md resolve to existing files +# - No orphaned files in references/ (every file linked from SKILL.md) +``` + +Expected: 3/3 checks pass (spec-compliance, valid-refs, orphan-files). + +**Common failure**: `name-directory-mismatch` — the `name` in frontmatter doesn't match the directory name. Fix by renaming the directory to match the distribution package name (e.g., `azure-search-documents`, not `search-documents` or `com.azure.search-documents`). + +## Step 2 — Token Budget Check + +Verify token budgets: +- SKILL.md: under 500 tokens (soft), under 5000 (hard) +- Each reference file: under 1000 tokens + +If over budget, split content into additional reference files (for example, break `customizations.md` into `customizations-client.md`, `customizations-models.md`, `customizations-operations.md`). + +## Step 3 — DECIDE + +Present validation results. If all pass: +Question: "Validation passed. Proceed to register the skill?" + +If failures exist, present them and ask: +Question: "These issues need fixing. Fix now, or skip validation?" + +📍 **Phase 3 complete** | Validation: pass/fail | Next: Phase 4 + +--- +## → Next: Phase 4 — Register +Read [04-register.md](04-register.md) and begin immediately. diff --git a/.github/skills/create-package-skill/phases/04-register.md b/.github/skills/create-package-skill/phases/04-register.md new file mode 100644 index 000000000000..c9c36103a2dd --- /dev/null +++ b/.github/skills/create-package-skill/phases/04-register.md @@ -0,0 +1,48 @@ +# Phase 4: Register 📋 + +> 📍 **Phase 4 — Register** | Add the skill to the find-package-skill discovery table. + +## Step 1 — Update find-package-skill + +Add a row to `.github/skills/find-package-skill/SKILL.md` in the **Package Skills** table: + +```markdown +| `` | `sdk///.github/skills//SKILL.md` | +``` + +Example: + +```markdown +| `azure-ai-projects` | `sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects/SKILL.md` | +``` + +## Step 2 — Summary + +Print a summary of everything created: + +📋 **Package Skill Created** + +| Item | Path | Status | +|---|---|---| +| SKILL.md | `sdk///.github/skills//SKILL.md` | Created | +| architecture.md | `...references/architecture.md` | Created/Skipped | +| customizations.md | `...references/customizations.md` | Created/Skipped | +| find-package-skill | `.github/skills/find-package-skill/SKILL.md` | Updated | +| vally lint | 3/3 checks | Passed | + +**Next steps for the service team:** +1. Fill in any `` sections with domain-specific knowledge. +2. Test the skill by asking an agent to regenerate your package (`azsdk_package_generate_code`) and apply a small TypeSpec change — the agent should consult the skill, not guess. +3. Iterate: agent gets something wrong → update skill → test again. +4. Submit a PR. + +**Maintaining your skill:** +- When your package's `_patch.py` files or utility modules change, update the skill. +- Keep content static — no version numbers, no current API version values, no release-specific info. Point to source files for things that change. +- When adding a new sync customization, make sure the skill reminds the agent about the async counterpart under `aio/`. + +## Step 3 — CONFIRM + +Question: "Register the skill in find-package-skill now (recommended), or skip?" + +📍 **Phase 4 complete** | Skill registered | Wizard done 🎉 diff --git a/.github/skills/create-package-skill/references/skill-template.md b/.github/skills/create-package-skill/references/skill-template.md new file mode 100644 index 000000000000..52f452d941c2 --- /dev/null +++ b/.github/skills/create-package-skill/references/skill-template.md @@ -0,0 +1,112 @@ +# SKILL.md Template for Azure SDK for Python Package Skills + +Use this template when creating a new package skill. Replace placeholders with package-specific content. + +```yaml +--- +name: +description: '. WHEN: regenerate ; modify ; fix bug; add feature; tsp-client update.' +--- +``` + +The `name` field and directory name MUST match the Python distribution package name (e.g., `azure-search-documents`, `azure-ai-projects`, `azure-mgmt-appconfiguration`). + +## Content Principles + +- **Workflow-first.** The primary audience is an agent running post-regeneration cleanup. Structure the skill so the agent can execute it top-to-bottom: regenerate → verify imports → check API version → check for new operations → check for model/enum changes → lint/type-check → update docs. See *Structure Options* below. +- **Include the commands the agent needs to copy-paste.** `tsp-client update`, `python -c "from import X"` import smoke tests, `grep -A 20 'class ApiVersion' `, `git diff --name-only | grep _operations\.py`, `azpysdk mypy`, `azpysdk pylint`, etc. Do not force the agent to invent commands. "Don't re-document MCP tools" means don't paraphrase an entire MCP tool contract — it does **not** mean omit the one-line invocation an agent is expected to run. +- **Point to source for things that change.** Never hardcode API version strings, release numbers, or the current `DEFAULT_VERSION` value. Point at `_patch.py`, `_metadata.json`, or `_version.py`. +- **Prefer TypeSpec-level customizations over `_patch.py`.** Note when a customization could be expressed by a TypeSpec decorator or emitter option instead. +- **Focus on the convenience layer.** What does the agent need to know to write/maintain `_patch.py`, hand-written utilities, and async parity correctly. + +## Structure Options + +Pick the one that fits how the package is maintained. + +### Option A — Step-by-step workflow (recommended when non-empty `_patch.py` files exist) + +Numbered steps the agent executes in order. Canonical skeleton: + +1. Run Regeneration (`tsp-client update` or `azsdk_package_generate_code`) +2. Verify Imports in All `_patch.py` Files (import smoke tests) +3. Check ApiVersion (reconcile `_metadata.json` with the hand-maintained `ApiVersion` enum) +4. Check for New Operations That Need Wrappers (git diff generated operations) +5. Check for Model/Enum Changes That Affect Customizations (git diff models/enums) +6. Ensure mypy passes +7. Ensure pylint passes +8. Update Documentation and Samples (CHANGELOG, README) + +Followed by a *Customization Patterns Reference* section that names and briefly describes each hand-written pattern in the codebase (constructor reorder, polymorphic delete, enum aliases, hand-authored batching sender, custom paging, etc.), each with 2–5 sentences on *what it does* and *what would break it*. Exhaustive file-by-file detail goes to `references/customizations.md`. + +### Option B — Reference-manual (simpler packages, or when the workflow is shared at a higher level) + +Sections in this order: +1. **Common Pitfalls** — 3–5 most dangerous mistakes, FIRST. Always include: never edit generated files; check `_patch.py` FIRST; maintain async parity. +2. **Architecture** — namespace layout, generated vs hand-written, non-empty `_patch.py` files. +3. **After Regeneration** — error categorization table, API version management, breaking-change detection. +4. **Post-Regeneration Customizations** — per-pattern notes with async-counterpart guidance. +5. **Testing Notes** — optional. +6. **References** — table linking to `references/*.md`. + +## Required Content (both options) + +Regardless of structure, the skill MUST include: + +- Frontmatter with `name` == directory name == distribution package name, and a `WHEN:` trigger list that includes the package name. +- A list or tree of the non-empty `_patch.py` files in the package (the agent's map of "where customizations live"). Example: + ``` + azure// + ├── _patch.py # + ├── _operations/_patch.py # + ├── models/_patch.py # + └── aio/… # async mirror + ``` +- A reminder that `_patch.py` files are never overwritten by regeneration, but their imports from generated modules CAN break silently. +- Guidance that every sync customization must have an async mirror under `aio/`, and that shared helpers are **imported** (not duplicated) from sync into async. +- Explicit import smoke-test commands for the top public symbols. Example: + ```bash + python -c "from import " + python -c "from .aio import " + python -c "from . import , " + ``` +- An `ApiVersion` management note: where the hand-maintained enum and `DEFAULT_VERSION` live, and how to reconcile them with `_metadata.json`'s `apiVersion` after regeneration. Include the exact reconciliation commands, e.g.: + ```bash + python -c "import json; print(json.load(open('_metadata.json'))['apiVersion'])" + grep -A 20 'class ApiVersion' + ``` +- A **"Expose New Generated Methods Through `_patch.py`"** step with a diff command and the exposure paths that apply to the package: + - Pass-through via generated mixin inheritance (just verify no collision) + - Override in the operations mixin (custom paging, request rewriting, response conversion, error handling) + - Polymorphic str-or-model `delete_*` wrapper (forward `e_tag` / `match_condition`) + - `create_or_update_*` wrapper (forward `prefer="return=representation"`, `match_condition`, `etag`, plus package-specific flags like `allow_index_downtime`, `skip_indexer_reset`) + - List projection wrapper (`select`, name-only via `cls`, generated-projection-to-canonical-model conversion) + - **`__all__` re-export** — any new public symbol added/overridden in `_patch.py` MUST be appended to that file's `__all__`, otherwise `patch_sdk()` will not expose it. Include the verification command `python -c "import ; print(sorted(.__all__))"`. + Skip categories that do not apply. +- A "Check for Model/Enum Changes" step listing what a diff of the generated models/enums would break in the customizations (renamed enum values → aliases; changed model constructors → subclass constructors; new fields on response models → extractor helpers; new parameters on request models → builder helpers). +- A linting / type-checking step. Prefer `azpysdk mypy` and `azpysdk pylint` (or the equivalent `azsdk_package_run_check`). Mention what the repo-level / package-level config already ignores so the agent knows where errors will come from. +- A **Documentation and Samples** step covering: + - CHANGELOG: find the topmost `## (Unreleased)` section; add entries under `### Features Added` / `### Breaking Changes` / `### Bugs Fixed`; create the section above the latest release if missing. Include a small example block. + - README: when to update usage examples, Key Concepts, and the listed API version. +- A per-pattern *Customization Patterns Reference* naming each hand-written pattern that actually exists in the codebase (NOT a generic list). + +## Optional Content + +- **Architecture reference file** — only create `references/architecture.md` if the layout is complex enough that readers need a separate map. Most packages are fine describing layout inline with a tree diagram. +- **Testing Notes** — include if the package has non-default test setup (Test Proxy `assets.json`, service-specific fixtures, required env vars). +- **References table** — useful if there are multiple reference files. + +## Structural Rules + +| Rule | Enforced By | +|---|---| +| `name` matches directory name (= distribution package name) | `vally lint` | +| All markdown links resolve | `vally lint` | +| No orphaned reference files | `vally lint` | +| Code references still exist in codebase | Manual review | +| SKILL.md under 5000 tokens | `vally lint` | +| No version numbers or release-specific info | Manual review | +| Trigger phrases include distribution package name | Manual review | +| No cross-language content | Manual review | +| Async parity reminder present | Manual review | +| Import smoke-test commands present | Manual review | +| CHANGELOG / README update step present | Manual review | diff --git a/.github/skills/create-package-skill/references/validation-tools.md b/.github/skills/create-package-skill/references/validation-tools.md new file mode 100644 index 000000000000..514c1298941f --- /dev/null +++ b/.github/skills/create-package-skill/references/validation-tools.md @@ -0,0 +1,24 @@ +# Validation Tools for Package Skills + +## vally lint (Structural Validation) + +**Source**: `microsoft/evaluate` (currently private, will be public) + +```bash +# Install (once published) +npm install -g @microsoft/vally-cli + +# Lint a skill +vally lint sdk///.github/skills/ + +# Lint all skills in repo +vally lint .github/skills +``` + +### What it checks + +| Check | What it does | Common failure | +|---|---|---| +| `spec-compliance` | Validates frontmatter (name, description), name-directory match | Directory name doesn't match `name` field (name must equal the distribution package name, e.g. `azure-ai-projects`) | +| `valid-refs` | All markdown links resolve to existing files | Broken link to reference file | +| `orphan-files` | No unreferenced files in references/ | File in references/ not linked from SKILL.md | diff --git a/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md b/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md index 475fff5bbf73..78aac01b950f 100644 --- a/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md +++ b/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md @@ -1,13 +1,13 @@ --- name: azure-search-documents -description: 'Post-regeneration customization guide for azure-search-documents. Verifies _patch.py imports, ApiVersion, enum aliases, mypy/pylint, and changelog after codegen. USE WHEN: running tsp-client update, regenerating from TypeSpec, fixing _patch.py breaks, bumping ApiVersion, adding operation wrappers, or updating search SDK documentation.' +description: 'Post-regeneration customization guide for azure-search-documents. Verifies _patch.py imports, ApiVersion, enum aliases, mypy/pylint, and changelog after codegen. WHEN: regenerate azure-search-documents; modify azure-search-documents; fix azure-search-documents bug; add azure-search-documents feature; azure-search-documents tsp-client update; edit azure-search-documents _patch.py.' --- # azure-search-documents — Post-Regeneration Customization Guide -After running `tsp-client update`, the generated code is a raw skeleton — not a shippable SDK. This skill tells you exactly what customizations exist in `_patch.py` files and how to verify they still work after regeneration. +After running `tsp-client update`, the generated code is a raw skeleton — not a shippable SDK. This skill tells you exactly which `_patch.py` files customize it and how to verify those customizations still work after regeneration. -The generator never touches `_patch.py` files, so your customizations survive regeneration. But they import from generated modules that **do** change. A renamed model, a new parameter, or a removed enum value will silently break a `_patch.py` file. The verification steps below catch these breaks. +The generator never touches `_patch.py` files, so your customizations survive regeneration. But they import from generated modules that **do** change. A renamed model, a new parameter, or a removed enum value will silently break a `_patch.py` file. The verification steps below catch those breaks. ## Step 1: Run Regeneration @@ -16,40 +16,49 @@ cd sdk/search/azure-search-documents # Update tsp-location.yaml with the new spec commit SHA, then: tsp-client update +# or: azsdk_package_generate_code -# If API version changed, update _metadata.json too +# If the API version changed, _metadata.json updates automatically; +# reconcile the hand-maintained ApiVersion enum in Step 3. ``` ## Step 2: Verify Imports in All `_patch.py` Files -Every `_patch.py` with customizations imports from generated modules. After regeneration, check that these imports still resolve. The fastest way: +Every non-empty `_patch.py` imports from generated modules. After regeneration, check those imports still resolve: ```bash -python -c "from azure.search.documents import SearchClient" -python -c "from azure.search.documents.aio import SearchClient" -python -c "from azure.search.documents.indexes.models import SearchField, SearchFieldDataType" -python -c "from azure.search.documents.models import IndexDocumentsBatch" +python -c "from azure.search.documents import SearchClient, SearchIndexingBufferedSender, ApiVersion, DEFAULT_VERSION, IndexDocumentsBatch" +python -c "from azure.search.documents.aio import SearchClient, SearchIndexingBufferedSender" +python -c "from azure.search.documents.indexes import SearchIndexClient, SearchIndexerClient" +python -c "from azure.search.documents.indexes.aio import SearchIndexClient, SearchIndexerClient" +python -c "from azure.search.documents.indexes.models import SearchField, SearchFieldDataType, SimpleField, SearchableField, ComplexField, KnowledgeBase" +python -c "from azure.search.documents.knowledgebases import KnowledgeBaseRetrievalClient" +python -c "from azure.search.documents.knowledgebases.aio import KnowledgeBaseRetrievalClient" ``` -If any fail, a generated class/enum was renamed or removed. Read `references/customizations.md` for the exact import each `_patch.py` depends on. +If any fail, a generated class/enum was renamed or removed. See `references/customizations.md` for the exact import each `_patch.py` depends on. -The `_patch.py` files that have customizations (all others are empty boilerplate): +The `_patch.py` files that have customizations (others are empty boilerplate): ``` azure/search/documents/ -├── _patch.py # SearchClient, SearchIndexingBufferedSender, ApiVersion -├── _operations/_patch.py # SearchItemPaged, search(), document CRUD -├── models/_patch.py # IndexDocumentsBatch, RequestEntityTooLargeError -├── aio/_patch.py # Async SearchClient, async SearchIndexingBufferedSender -├── aio/_operations/_patch.py # AsyncSearchItemPaged, async search()/CRUD -├── indexes/_operations/_patch.py # Polymorphic delete/update, list helpers -├── indexes/models/_patch.py # SearchField, field builders, enum aliases -└── indexes/aio/_operations/_patch.py # Async index/indexer operations +├── _patch.py # SearchClient subclass, SearchIndexingBufferedSender, ApiVersion enum, DEFAULT_VERSION +├── _operations/_patch.py # SearchItemPaged, search(), custom index_documents/413 splitting, continuation-token helpers +├── models/_patch.py # IndexDocumentsBatch, RequestEntityTooLargeError +├── aio/_patch.py # Async SearchClient, async SearchIndexingBufferedSender +├── aio/_operations/_patch.py # AsyncSearchItemPaged, async search(), reuses sync helpers +├── indexes/_patch.py # SearchIndexClient, SearchIndexerClient (audience kwarg plumbing) +├── indexes/_operations/_patch.py # Polymorphic delete/create-or-update, list_index_names, _convert_index_response +├── indexes/models/_patch.py # SearchField(hidden), field builders, SearchIndexerDataSourceConnection overloads, KnowledgeBase, SearchFieldDataType aliases +├── indexes/aio/_patch.py # Async SearchIndexClient/SearchIndexerClient mirrors +├── indexes/aio/_operations/_patch.py # Async mirror of indexes operations mixin +├── knowledgebases/_patch.py # KnowledgeBaseRetrievalClient (audience kwarg plumbing) +└── knowledgebases/aio/_patch.py # Async KnowledgeBaseRetrievalClient ``` ## Step 3: Check ApiVersion -After regeneration, the generated code targets the API version in `_metadata.json`. The hand-maintained `ApiVersion` enum in `_patch.py` must include this version. +The generator targets the version in `_metadata.json`. The hand-maintained `ApiVersion` enum in `azure/search/documents/_patch.py` must include it. ```bash # 1. See what API version the generator used @@ -62,167 +71,204 @@ grep -A 20 'class ApiVersion' azure/search/documents/_patch.py grep 'DEFAULT_VERSION' azure/search/documents/_patch.py ``` -If the generated API version is **not** in the `ApiVersion` enum: -1. Add the new member to `ApiVersion` in `azure/search/documents/_patch.py` (e.g., `V2026_05_01_PREVIEW = "2026-05-01-preview"`) -2. Update `DEFAULT_VERSION` to point to the new member -3. Update the `ApiVersion` docstring in the class if one exists +If the generated API version is **not** in the enum: +1. Add the new member to `ApiVersion` in `azure/search/documents/_patch.py` (e.g., `V2026_05_01_PREVIEW = "2026-05-01-preview"`). +2. Update `DEFAULT_VERSION` to point to the new member. +3. Update docstrings on the public client subclasses (`SearchClient`, `SearchIndexClient`, `SearchIndexerClient`, `KnowledgeBaseRetrievalClient`) that name the default version. -Verify the default round-trips correctly: +Verify the default round-trips: ```bash python -c "from azure.search.documents._patch import ApiVersion, DEFAULT_VERSION; print(DEFAULT_VERSION.value)" ``` -## Step 4: Check for New Operations That Need Wrappers +## Step 4: Expose New Generated Methods Through `_patch.py` -Look at what changed in the generated operations files: +New generated operations do NOT automatically surface in the user-expected shape. Diff what regeneration added: ```bash git diff --name-only | grep "_operations\.py" | grep -v _patch +git diff azure/search/documents/indexes/_operations/_operations.py | grep -E '^\+\s+(async\s+)?def ' +git diff azure/search/documents/_operations/_operations.py | grep -E '^\+\s+(async\s+)?def ' ``` -If a new operation was added to the generated `_operations.py`, decide whether it needs a convenience wrapper in `_patch.py`. Operations that need wrappers: -- **Delete operations** — need the polymorphic str-or-model pattern (see "Polymorphic Delete Pattern" below) -- **Create-or-update operations** — need `prefer="return=representation"`, `match_condition`, and `etag` forwarding -- **List operations** — may need a `select` parameter or name-only projection +For each new method, pick the exposure path: -Operations that pass through without a wrapper (if the generated signature is already user-friendly) can be left alone — they're inherited from the generated mixin. +1. **Pass-through via generated mixin inheritance** — if the generated signature is already user-friendly, the `_OperationsMixin` subclass in `_patch.py` inherits it automatically. Verify the method name does NOT collide with an existing `_patch.py` override. -Remember: every sync wrapper in `_operations/_patch.py` needs a matching async wrapper in `aio/_operations/_patch.py`. +2. **Override in the operations mixin** — needed when the method requires custom paging (`SearchItemPaged`), request-body rewriting (`_build_search_request`), response conversion (`_convert_search_result`, `_convert_index_response`), or 413 batch splitting. + +3. **Polymorphic str-or-model wrapper** — for `delete_*` on a named resource (index, synonym map, alias, indexer, data source, skillset, knowledge base, knowledge source). Accept either the `str` name or the resource model; forward `e_tag` + `match_condition` when a model is passed. + +4. **Create-or-update wrapper** — for `create_or_update_*`. Forward `prefer="return=representation"`, `match_condition`, `etag`, plus package-specific flags: + - `allow_index_downtime` for `create_or_update_index` + - `skip_indexer_reset_requirement_for_cache` for indexers + - `skip_indexer_reset` / `disable_cache_reprocessing_change_detection` for skillsets + +5. **List projection wrapper** — for `list_*`. Add a `select` parameter, a name-only projection via `cls` callback (`list_index_names`, `list_indexer_names`, `list_skillset_names`), or convert a generated projection type back to the canonical model via `_convert_index_response`. + +6. **Re-export via `__all__`** — any NEW symbol you add or override in `_patch.py` MUST be appended to that file's `__all__`. Otherwise `patch_sdk()` will not surface it. + + ```bash + python -c "import azure.search.documents as m; print(sorted(m.__all__))" + python -c "import azure.search.documents.indexes as m; print(sorted(m.__all__))" + ``` + +Every sync wrapper needs a matching async mirror under `aio/`. Shared helpers (`_build_search_request`, `_convert_search_result`, `_convert_index_response`, `_pack_continuation_token`, `_unpack_continuation_token`) live in the **sync** `_operations/_patch.py` and are **imported** by async — do not duplicate them. ## Step 5: Check for Model/Enum Changes That Affect Customizations ```bash -git diff models/_enums.py models/_models.py indexes/models/_enums.py indexes/models/_models.py +git diff azure/search/documents/models/_models.py azure/search/documents/models/_enums.py +git diff azure/search/documents/indexes/models/_models.py azure/search/documents/indexes/models/_enums.py ``` Watch for: -- **Renamed enum values** — backward-compat aliases in `_patch.py` may reference old names -- **Changed model constructors** — `IndexDocumentsBatch`, `SearchField`, `SearchIndexerDataSourceConnection`, `KnowledgeBase` are subclassed in `_patch.py` and may break if the base constructor changes -- **New fields on `SearchResult`** — `_convert_search_result()` in `_operations/_patch.py` extracts `@search.*` metadata fields; new ones need to be added -- **Changed `SearchRequest` model** — `_build_search_request()` constructs this model directly; new parameters need to be wired through -- **Changed `SearchFieldDataType` model** — `indexes/models/_patch.py` monkey-patches `SearchFieldDataType.Collection` as a `staticmethod` and adds camelCase backward-compat aliases (e.g., `Int32` → `INT32`). If values are added, removed, or renamed in the generated enum, the aliases and `Collection` helper must be updated to match +- **Renamed enum values** — `indexes/models/_patch.py` monkey-patches camelCase aliases onto `SearchFieldDataType` (`String`, `Int32`, `Int64`, `Single`, `Double`, `Boolean`, `DateTimeOffset`, `GeographyPoint`, `ComplexType`). All right-hand-side UPPER_CASE members must still exist. +- **Changed model constructors** — `IndexDocumentsBatch`, `SearchField`, `SearchIndexerDataSourceConnection`, `KnowledgeBase` are subclassed in `_patch.py`; base-class constructor changes break them. +- **New fields on `SearchResult`** — `_convert_search_result()` extracts `@search.*` metadata (`score`, `reranker_score`, `highlights`, `captions`, `document_debug_info`, `reranker_boosted_score`). New metadata fields must be added there. +- **Changed `SearchRequest`** — `_build_search_request()` constructs this model directly; new query parameters must be wired through, including any new pipe-delimited semantic encoding. +- **`SearchIndexResponse.semantic`** — used by `_convert_index_response()`; if the field is renamed, update the mapping. +- **`SearchField.retrievable`** — the `hidden` property in `SearchField` subclass is its inverse; if renamed, update the property. -## Step 6: Ensure mypy pass +## Step 6: Ensure mypy passes ```bash cd sdk/search/azure-search-documents - -# Preferred — uses azpysdk CLI (install via: pip install -e eng/tools/azure-sdk-tools) azpysdk mypy +# or: azsdk_package_run_check with checkType="All" ``` -The package-level `mypy.ini` already ignores `_generated` internals — you only need to fix errors in `_patch.py` files and `samples/`. +`mypy.ini` at the package root already ignores generated internals — errors will originate in `_patch.py` files or `samples/`. -## Step 7: Ensure pylint pass +## Step 7: Ensure pylint passes ```bash cd sdk/search/azure-search-documents - -# Preferred azpysdk pylint ``` -The repo-level `pylintrc` already excludes `_generated/`, `_vendor/`, `tests/`, and `samples/` — only `_patch.py` customizations are linted. +The repo-level `pylintrc` excludes generated, `_vendor/`, `tests/`, and `samples/` — only `_patch.py` customizations are linted. ## Step 8: Update Documentation and Samples -If the regeneration added new operations, models, or changed the API version, update the docs and samples: +### CHANGELOG -### Changelog +Open `CHANGELOG.md` and find the topmost `## (Unreleased)` section. Add entries under: -Open `CHANGELOG.md` and find the topmost `## (Unreleased)` section. Add entries under the appropriate heading: +- `### Features Added` — new operations, models, parameters, API version support +- `### Breaking Changes` — renamed/removed models, changed signatures, dropped API versions +- `### Bugs Fixed` — fixes to `_patch.py` logic, pagination, encoding, batching -- **`### Features Added`** — new operations, models, parameters, or API version support -- **`### Breaking Changes`** — renamed/removed models, changed signatures, dropped API versions -- **`### Bugs Fixed`** — fixes to `_patch.py` logic, pagination, encoding, etc. +Example: -Example entry: ```markdown ## 11.x.0bN (Unreleased) ### Features Added -- Added `create_or_update_knowledge_base` to `SearchIndexClient` -- Support for API version `2026-05-01-preview` +- Added `create_or_update_knowledge_base` to `SearchIndexClient`. +- Support for API version `2026-05-01-preview`. ### Breaking Changes -- Renamed `OldModel` to `NewModel` +- Renamed `OldModel` to `NewModel`. ``` -If no `(Unreleased)` section exists, create one above the latest release with the next version from `_version.py`. +If no `(Unreleased)` section exists, create one above the latest release using the next version from `azure/search/documents/_version.py`. ### README -If new client classes or major features were added, update `README.md`: +- Add usage examples for new client classes (`KnowledgeBaseRetrievalClient` etc.) or operations. +- Update "Key concepts" for new resource types. +- Update the listed API version in "Getting started" if `DEFAULT_VERSION` changed. -- Add usage examples for new client classes or operations -- Update the "Key concepts" section if new resource types were introduced -- Update the listed API version in the "Getting started" section if it changed +--- # Customization Patterns Reference -Each pattern below describes a customization that exists in the codebase today. After regeneration, verify each one still works. Read `references/customizations.md` for the exhaustive file-by-file inventory. +Each pattern below exists in the codebase today. After regeneration, verify each still works. See `references/customizations.md` for the exhaustive file-by-file inventory. -## SearchClient Constructor Reordering +## Client Constructor Customizations (audience / parameter reorder) -`SearchClient` in `_patch.py` swaps parameter order to `(endpoint, index_name, credential)`. The generated base uses `(endpoint, credential, index_name)`. If regeneration changes the base constructor signature, update the subclass. +- `SearchClient` in `_patch.py` (sync + async) reorders params to `(endpoint, index_name, credential)`. The generated base uses `(endpoint, credential, index_name)`. +- `SearchIndexClient`, `SearchIndexerClient` (`indexes/_patch.py` + async) and `KnowledgeBaseRetrievalClient` (`knowledgebases/_patch.py` + async) all pop the `audience` kwarg and convert it into `credential_scopes=[audience + "/.default"]`. + +If regeneration changes the base constructor signature (param order, required kwargs), update each subclass. ## Custom Search Pagination `search()` does **not** use standard Azure SDK paging. It uses `SearchItemPaged` / `AsyncSearchItemPaged` with a custom `SearchPageIterator` because: -- Search paginates via POST with `nextPageParameters` body, not GET with `nextLink` -- First-page metadata (facets, count, answers) must be available before iteration -- Results are converted to dicts with `@search.*` keys +- Search paginates via POST with `nextPageParameters` body, not GET with `nextLink`. +- First-page metadata (facets, count, coverage, answers, debug info) must be available before iteration starts. +- Results are converted to dicts with `@search.*` keys. -The continuation token is Base64-encoded JSON: `{"apiVersion": "...", "nextLink": "...", "nextPageParameters": {...}}` +Continuation token is Base64-encoded JSON: `{"apiVersion": ..., "nextLink": ..., "nextPageParameters": {...}}`. -Shared helpers (`_build_search_request`, `_convert_search_result`, `_pack_continuation_token`, `_unpack_continuation_token`) live in sync `_operations/_patch.py` and are imported by async. Don't duplicate them. +Shared helpers (`_build_search_request`, `_convert_search_result`, `_pack_continuation_token`, `_unpack_continuation_token`) live in sync `_operations/_patch.py` and are imported by async. ## Pipe-Delimited Semantic Encoding `_build_search_request()` encodes semantic parameters into pipe-delimited wire format: + ``` -query_answer="extractive", count=3 → "extractive|count-3" -query_caption="extractive", highlight=True → "extractive|highlight-true" +query_answer="extractive", count=3 → "extractive|count-3" +query_caption="extractive", highlight=True → "extractive|highlight-true" ``` -New semantic parameters should follow this pattern. -## SearchIndexingBufferedSender +New semantic-search parameters follow this pattern. + +## 413 Batch Splitting -Entirely hand-authored in `_patch.py` (sync) and `aio/_patch.py` (async). Not generated. Wraps `SearchClient` with auto-flush timer, 413 recursive batch splitting, retry per document key (409/422/503), and key field auto-detection. The async version supports both sync and async callbacks via `asyncio.iscoroutinefunction()`. +`index_documents()` in `_operations/_patch.py` recursively splits an `IndexDocumentsBatch` in half when the service returns `RequestEntityTooLargeError` (413). Retry-on-split uses `is_retryable_status_code()` (409/422/503). -## Field Builders +## SearchIndexingBufferedSender -`SimpleField()`, `SearchableField()`, `ComplexField()` in `indexes/models/_patch.py`. `SimpleField` explicitly sets `searchable=False`. `SearchableField` auto-sets type to `String`/`Collection(String)`. +Entirely hand-authored in `_patch.py` (sync) and `aio/_patch.py` (async); not generated. Wraps `SearchClient` with: +- Auto-flush timer (`threading.Timer` sync / `asyncio.Task` async). +- 413 recursive batch splitting. +- Retry per document key (409/422/503) bounded by `max_retries_per_action`. +- Key-field auto-detection against the index schema. +- The async version detects sync vs async callbacks via `asyncio.iscoroutinefunction()`. -`SearchField` subclass adds `hidden` property (inverse of `retrievable`). +## Field Builders and `SearchFieldDataType` Helpers (`indexes/models/_patch.py`) -`SearchFieldDataType.Collection` is a `staticmethod` monkey-patched onto the generated enum. +- `SimpleField(...)` — sets `searchable=False` explicitly. +- `SearchableField(...)` — auto-types to `String` / `Collection(String)`. +- `ComplexField(...)` — sets type to `Complex` / `Collection(Complex)`. +- `SearchField` subclass adds `hidden` property (inverse of `retrievable`). +- `SearchFieldDataType.Collection = staticmethod(_collection_helper)` — monkey-patched onto the generated enum. ## Backward-Compatible Enum Aliases -Monkey-patched at module load in `_patch.py` files: +Applied at module load in `indexes/models/_patch.py`: + ```python SearchFieldDataType.Int32 = SearchFieldDataType.INT32 # camelCase → UPPER +# (String, Int64, Single, Double, Boolean, DateTimeOffset, GeographyPoint, ComplexType) ``` -After regeneration, verify the right-hand-side names still exist in the generated enums. If a new enum collides with a Python keyword, add an alias. -## Polymorphic Delete Pattern +After regeneration, verify every right-hand-side UPPER_CASE member still exists in the generated enum. If a new enum value collides with a Python keyword, add an alias. + +## Polymorphic Delete / Create-Or-Update Pattern + +All delete/update operations in `indexes/_operations/_patch.py` accept str or model: -All delete/update operations in `indexes/_operations/_patch.py` accept str or model object: ```python def delete_index(self, index, *, match_condition=MatchConditions.Unconditionally, **kwargs): try: name = index.name # model object return self._delete_index(name=name, etag=index.e_tag, match_condition=match_condition, **kwargs) except AttributeError: - name = index # string - return self._delete_index(name=name, **kwargs) + return self._delete_index(name=index, **kwargs) # string ``` -New resource types need this same wrapper in both sync and async. + +Covered resources: index, synonym map, alias, indexer, data source connection, skillset, knowledge base, knowledge source. New resource types added to the spec need the same pair of wrappers in sync **and** async. ## `_convert_index_response` Helper -`list_indexes(select=...)` returns `SearchIndexResponse` (projection type). `_convert_index_response()` maps it to `SearchIndex`, notably `response.semantic` → `semantic_search`. Shared between sync and async via import. +`list_indexes(select=...)` returns `SearchIndexResponse` (projection type). `_convert_index_response()` maps it to `SearchIndex` — notably `response.semantic` → `semantic_search`. Shared between sync and async via import; do not duplicate. + +--- + +When to use `_patch.py` vs TypeSpec: prefer TypeSpec decorators / emitter options for things the spec can express (renames, flattening, client names). Use `_patch.py` for Python-specific behavior: pagination contracts, threading/asyncio, polymorphic call patterns, 413 splitting, monkey-patched enum helpers, and backward-compat aliases. See [typespec-python emitter docs](https://github.com/Azure/autorest.python/blob/main/packages/typespec-python/README.md). diff --git a/sdk/search/azure-search-documents/.github/skills/azure-search-documents/references/customizations.md b/sdk/search/azure-search-documents/.github/skills/azure-search-documents/references/customizations.md index 7f5d99d19f71..dbadf11a7a76 100644 --- a/sdk/search/azure-search-documents/.github/skills/azure-search-documents/references/customizations.md +++ b/sdk/search/azure-search-documents/.github/skills/azure-search-documents/references/customizations.md @@ -1,6 +1,6 @@ # Post-Regeneration Customization Checklist -Use this file after running `tsp-client update` to verify every customization. Each section is a `_patch.py` file with the exact classes, functions, and aliases it defines, plus what generated symbols it depends on. +File-by-file inventory of every non-empty `_patch.py` in `azure-search-documents`. Use this after running `tsp-client update` to verify each customization still holds. --- @@ -8,82 +8,125 @@ Use this file after running `tsp-client update` to verify every customization. E ### Depends On (from generated code) - `._client.SearchClient` as `_SearchClient` (base class) +- `._operations._patch.SearchItemPaged` +- `.models._patch.RequestEntityTooLargeError`, `.models._patch.IndexDocumentsBatch` +- `.models.IndexAction`, `.models.IndexingResult` +- `.indexes.SearchIndexClient` (for schema lookup in buffered sender) ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `SearchClient` | class | Subclass of `_SearchClient`; reorders constructor to `(endpoint, index_name, credential)` | -| `SearchIndexingBufferedSender` | class | Hand-authored batching sender. Uses `threading.Timer` for auto-flush, recursive 413 splitting, retry per doc key (409/422/503), key field auto-detection | +| `ApiVersion` | enum | Hand-maintained list of supported API versions (`CaseInsensitiveEnumMeta`) | +| `DEFAULT_VERSION` | constant | Current default (points at an `ApiVersion` member) | | `is_retryable_status_code()` | function | Returns True for 409, 422, 503 | +| `SearchClient` | class | Subclass of `_SearchClient`; reorders constructor to `(endpoint, index_name, credential)` and pops `audience` into `credential_scopes` | +| `SearchIndexingBufferedSender` | class | Hand-authored batching sender; `threading.Timer` auto-flush, recursive 413 splitting, per-doc retry (409/422/503), key field auto-detection | + +### `__all__` +```python +["SearchClient", "SearchItemPaged", "SearchIndexingBufferedSender", + "ApiVersion", "DEFAULT_VERSION", "RequestEntityTooLargeError", "IndexDocumentsBatch"] +``` ### After Regeneration, Verify -- [ ] `_SearchClient` base class constructor signature unchanged -- [ ] If API version changed, add new `ApiVersion` member and update default +- [ ] `_SearchClient` base constructor signature unchanged (positional and keyword params) +- [ ] `_metadata.json` `apiVersion` is a member of `ApiVersion`; otherwise add member + update `DEFAULT_VERSION` +- [ ] Docstrings on `SearchClient` / `SearchIndexingBufferedSender` still list the correct default version +- [ ] Re-exports from `.models._patch` (`IndexDocumentsBatch`, `RequestEntityTooLargeError`) and `._operations._patch` (`SearchItemPaged`) still resolve --- -## File: `azure/search/documents/models/_patch.py` +## File: `azure/search/documents/_operations/_patch.py` ### Depends On (from generated code) -- `.._generated.models.IndexDocumentsBatch` as `IndexDocumentsBatchGenerated` (base class) -- `._enums.ScoringStatistics` +- `._operations._SearchClientOperationsMixin` as `_SearchClientOperationsMixinGenerated` (base class) +- `..models._models.SearchRequest`, `..models._models.SearchDocumentsResult`, `..models._models.SearchResult` +- `..models` re-exports +- `..models._patch.RequestEntityTooLargeError` +- `azure.core.paging.ItemPaged`, `PageIterator` ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `IndexDocumentsBatch` | class | Adds `add_upload_actions`, `add_delete_actions`, `add_merge_actions`, `add_merge_or_upload_actions`, `dequeue_actions`, `enqueue_actions`, `actions` property | -| `RequestEntityTooLargeError` | class | `HttpResponseError` subclass for 413 | +| `_convert_search_result(result)` | function | Extracts `@search.score`, `@search.reranker_score`, `@search.highlights`, `@search.captions`, `@search.document_debug_info`, `@search.reranker_boosted_score` | +| `_pack_continuation_token(response, api_version)` | function | Base64-encoded JSON: `{apiVersion, nextLink, nextPageParameters}` | +| `_unpack_continuation_token(token)` | function | Decodes token → `(next_link, next_page_request)` | +| `_build_search_request(...)` | function | Constructs `SearchRequest`; pipe-delimited encoding for `answers`/`captions`/`rewrites` | +| `SearchPageIterator` | class | Custom `PageIterator`; exposes `get_facets()`, `get_count()`, `get_coverage()`, `get_answers()`, `get_debug_info()` | +| `SearchItemPaged` | class | Extends `ItemPaged`; forwards the metadata accessors | +| `_SearchClientOperationsMixin` | class | Overrides `search()`, `index_documents()` (413 recursive splitting), `upload_documents()`, `delete_documents()`, `merge_documents()`, `merge_or_upload_documents()`, `get_document_count()` convenience helpers | + +### `__all__` +```python +["_SearchClientOperationsMixin", "SearchItemPaged"] +``` ### After Regeneration, Verify -- [ ] `IndexDocumentsBatchGenerated` base class still exists with compatible constructor +- [ ] `SearchRequest` fields still match what `_build_search_request` sets; new search parameters must be wired through +- [ ] `SearchResult` fields still match what `_convert_search_result` extracts; new `@search.*` fields must be added there +- [ ] Generated mixin methods invoked via `super()` (e.g., `_search_post`, `_index`) still exist with compatible signatures +- [ ] `SearchDocumentsResult.next_page_parameters` / `next_link` still exist (used by `_pack_continuation_token`) --- -## File: `azure/search/documents/_operations/_patch.py` +## File: `azure/search/documents/models/_patch.py` ### Depends On (from generated code) -- `..models._models.SearchRequest` (constructed directly in `_build_search_request`) -- `..models._models.SearchResult` (field access in `_convert_search_result`) -- `azure.core.paging.ItemPaged`, `azure.core.paging.PageIterator` +- `._models.IndexDocumentsBatch` as `IndexDocumentsBatchGenerated` (base class) +- `._models.IndexAction` +- `._enums.IndexActionType` ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `_convert_search_result(result)` | function | Extracts `@search.score`, `@search.reranker_score`, `@search.highlights`, `@search.captions`, `@search.document_debug_info`, `@search.reranker_boosted_score` | -| `_pack_continuation_token(response, api_version)` | function | Returns base64 JSON: `{apiVersion, nextLink, nextPageParameters}` | -| `_unpack_continuation_token(token)` | function | Decodes token to `(next_link, next_page_request)` | -| `_build_search_request(search_text, **kwargs)` | function | Builds `SearchRequest`. Pipe-delimited encoding for answers/captions/rewrites | -| `SearchPageIterator` | class | Custom page iterator with `get_facets()`, `get_count()`, `get_coverage()`, `get_answers()`, `get_debug_info()` | -| `SearchItemPaged` | class | Extends `ItemPaged` with same metadata accessors | -| `_SearchClientOperationsMixin` | class | Overrides: `search()`, `index_documents()` (413 splitting), `upload_documents()`, `delete_documents()`, `merge_documents()`, `merge_or_upload_documents()` | +| `RequestEntityTooLargeError` | class | `HttpResponseError` subclass for 413 | +| `IndexDocumentsBatch` | class | Adds `add_upload_actions`, `add_delete_actions`, `add_merge_actions`, `add_merge_or_upload_actions`, `dequeue_actions`, `enqueue_actions`, `actions` property | +| `_flatten_args(...)` | function | Flattens variadic doc args (supports both `fn([doc1, doc2])` and `fn(doc1, doc2)`) | + +Sets `IndexDocumentsBatch.__module__ = "azure.search.documents"` so Sphinx documents it at the public namespace and avoids duplicate object-description warnings. + +### `__all__` +```python +["IndexDocumentsBatch"] +``` ### After Regeneration, Verify -- [ ] `SearchRequest` model fields still match what `_build_search_request` sets — new search parameters need to be wired through -- [ ] `SearchResult` model fields still match what `_convert_search_result` extracts — new `@search.*` fields need to be added -- [ ] Generated mixin methods that `_SearchClientOperationsMixin` calls (e.g., `_search_post`) still exist with same signatures +- [ ] `IndexDocumentsBatchGenerated` base constructor still accepts `actions=` (stored under key `"value"`) +- [ ] `IndexAction` + `IndexActionType` still exist with same action type values (`upload`, `delete`, `merge`, `mergeOrUpload`) --- ## File: `azure/search/documents/aio/_patch.py` ### Depends On (from generated code) -- `.._client.SearchClient` as async `_SearchClient` (base class) +- `._client.SearchClient` as async `_SearchClient` (base class) +- `._operations._patch.AsyncSearchItemPaged` +- `..models._patch.RequestEntityTooLargeError`, `..models._patch.IndexDocumentsBatch` +- `..models.IndexAction`, `..models.IndexingResult` +- `..indexes.aio.SearchIndexClient` (for schema lookup) +- `.._patch.DEFAULT_VERSION`, `.._patch.is_retryable_status_code` ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `SearchClient` | class | Async subclass, same reorder as sync | -| `SearchIndexingBufferedSender` | class | Async version: `asyncio.Task` instead of `threading.Timer`, `iscoroutinefunction()` callback detection | +| `SearchClient` | class | Async subclass, same reorder + audience kwarg pop as sync | +| `SearchIndexingBufferedSender` | class | Async batching sender; `asyncio.Task` auto-flush, `asyncio.iscoroutinefunction()` callback dispatch, same 413/retry behaviour | + +### `__all__` +```python +["SearchClient", "SearchIndexingBufferedSender"] +``` ### After Regeneration, Verify -- [ ] Same checks as sync `_patch.py` +- [ ] Same base-class checks as sync `_patch.py` +- [ ] `DEFAULT_VERSION`, `is_retryable_status_code` imports from sync `_patch.py` still resolve --- ## File: `azure/search/documents/aio/_operations/_patch.py` ### Depends On (from generated code) -- Same models as sync `_operations/_patch.py` +- Same generated models as sync `_operations/_patch.py` - `azure.core.async_paging.AsyncItemPaged`, `AsyncPageIterator` ### Imports From Sync (NOT Duplicated) @@ -94,56 +137,39 @@ Use this file after running `tsp-client update` to verify every customization. E |--------|------|-------------| | `AsyncSearchPageIterator` | class | Async version of `SearchPageIterator` | | `AsyncSearchItemPaged` | class | Async version of `SearchItemPaged` | -| `_SearchClientOperationsMixin` | class | Async operations mixin, same methods as sync | +| `_SearchClientOperationsMixin` | class | Async operations mixin; mirrors sync overrides | + +### `__all__` +```python +["_SearchClientOperationsMixin", "AsyncSearchItemPaged"] +``` ### After Regeneration, Verify - [ ] Same checks as sync `_operations/_patch.py` -- [ ] Imports from sync `_operations/_patch.py` still resolve +- [ ] Imports from sync `_operations/_patch.py` still resolve (don't let async drift into duplication) --- -## File: `azure/search/documents/indexes/models/_patch.py` +## File: `azure/search/documents/indexes/_patch.py` ### Depends On (from generated code) -- `._models.SearchField` as `_SearchField` (base class) -- `._models.SearchIndexerDataSourceConnection` as `_SearchIndexerDataSourceConnection` (base class) -- `._models.KnowledgeBase` as `_KnowledgeBase` (base class) -- `._enums.SearchFieldDataType`, `OcrSkillLanguage`, `SplitSkillLanguage`, `TextTranslationSkillLanguage` -- Various model imports for type annotations +- `._client.SearchIndexClient` as `_SearchIndexClient` (base class) +- `._client.SearchIndexerClient` as `_SearchIndexerClient` (base class) ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `SearchField` | class | Adds `hidden` property (inverse of `retrievable`), constructor accepts `hidden` kwarg | -| `SearchIndexerDataSourceConnection` | class | Accepts `connection_string` str or `credentials` object | -| `KnowledgeBase` | class | Deserializes `retrieval_reasoning_effort` from dict | -| `SimpleField()` | function | Builder: sets `searchable=False` explicitly | -| `SearchableField()` | function | Builder: auto-types to `String`/`Collection(String)` | -| `ComplexField()` | function | Builder: sets type to `Complex`/`Collection(Complex)` | -| `Collection()` | function | Wraps type in `"Collection(...)"` format | - -### Enum Aliases +| `SearchIndexClient` | class | Subclass; pops `audience` kwarg into `credential_scopes=[audience + "/.default"]` | +| `SearchIndexerClient` | class | Subclass; same `audience` pattern | + +### `__all__` ```python -# SearchFieldDataType (old camelCase -> generated UPPER_CASE) -SearchFieldDataType.String = SearchFieldDataType.STRING -SearchFieldDataType.Int32 = SearchFieldDataType.INT32 -SearchFieldDataType.Int64 = SearchFieldDataType.INT64 -SearchFieldDataType.Single = SearchFieldDataType.SINGLE -SearchFieldDataType.Double = SearchFieldDataType.DOUBLE -SearchFieldDataType.Boolean = SearchFieldDataType.BOOLEAN -SearchFieldDataType.DateTimeOffset = SearchFieldDataType.DATE_TIME_OFFSET -SearchFieldDataType.GeographyPoint = SearchFieldDataType.GEOGRAPHY_POINT -SearchFieldDataType.ComplexType = SearchFieldDataType.COMPLEX - -# Monkey-patched staticmethod -SearchFieldDataType.Collection = staticmethod(Collection) +["SearchIndexClient", "SearchIndexerClient"] ``` ### After Regeneration, Verify -- [ ] All right-hand-side enum members (`STRING`, `INT32`, etc.) still exist -- [ ] `_SearchField`, `_SearchIndexerDataSourceConnection`, `_KnowledgeBase` base constructors unchanged -- [ ] No new enum values that collide with Python keywords — if so, add aliases -- [ ] `SearchField.retrievable` property still exists (used by `hidden` inversion) +- [ ] Generated `_SearchIndexClient` / `_SearchIndexerClient` still accept `endpoint`, `credential`, and `credential_scopes` kwarg +- [ ] No conflicting `audience` kwarg introduced at the generated layer --- @@ -156,61 +182,179 @@ SearchFieldDataType.Collection = staticmethod(Collection) ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `_convert_index_response(response)` | function | Maps `SearchIndexResponse` → `SearchIndex` (semantic → semantic_search) | +| `_convert_index_response(response)` | function | Maps `SearchIndexResponse` → `SearchIndex` (`semantic` → `semantic_search`) | **`_SearchIndexClientOperationsMixin`** wraps: + | Method | Customization | |--------|--------------| -| `delete_index(index)` | Polymorphic: str or SearchIndex | -| `create_or_update_index(index)` | Adds prefer, match_condition, allow_index_downtime | -| `delete_synonym_map(synonym_map)` | Polymorphic: str or SynonymMap | -| `create_or_update_synonym_map(synonym_map)` | Adds prefer, match_condition | -| `delete_alias(alias)` | Polymorphic: str or SearchAlias | -| `create_or_update_alias(alias)` | Adds prefer, match_condition | -| `delete_knowledge_base(kb)` | Polymorphic: str or KnowledgeBase | -| `create_or_update_knowledge_base(kb)` | Adds prefer, match_condition | -| `delete_knowledge_source(ks)` | Polymorphic: str or KnowledgeSource | -| `create_or_update_knowledge_source(ks)` | Adds prefer, match_condition | +| `delete_index(index)` | Polymorphic: str or `SearchIndex` | +| `create_or_update_index(index)` | Forwards `prefer="return=representation"`, `match_condition`, `allow_index_downtime` | +| `delete_synonym_map(synonym_map)` | Polymorphic: str or `SynonymMap` | +| `create_or_update_synonym_map(synonym_map)` | Forwards `prefer`, `match_condition` | +| `delete_alias(alias)` | Polymorphic: str or `SearchAlias` | +| `create_or_update_alias(alias)` | Forwards `prefer`, `match_condition` | +| `delete_knowledge_base(kb)` | Polymorphic: str or `KnowledgeBase` | +| `create_or_update_knowledge_base(kb)` | Forwards `prefer`, `match_condition` | +| `delete_knowledge_source(ks)` | Polymorphic: str or `KnowledgeSource` | +| `create_or_update_knowledge_source(ks)` | Forwards `prefer`, `match_condition` | | `list_indexes(*, select)` | Uses `_convert_index_response` for projections | -| `list_index_names()` | Name-only projection via `cls` callback | +| `list_index_names()` | Name-only via `cls` callback | | `get_synonym_maps(*, select)` | Returns list | **`_SearchIndexerClientOperationsMixin`** wraps: + | Method | Customization | |--------|--------------| -| `delete_data_source_connection(dsc)` | Polymorphic: str or object | -| `create_or_update_data_source_connection(dsc)` | Adds prefer, match_condition, skip_indexer_reset | -| `delete_indexer(indexer)` | Polymorphic: str or SearchIndexer | -| `create_or_update_indexer(indexer)` | Adds prefer, match_condition, skip/disable cache | -| `delete_skillset(skillset)` | Polymorphic: str or SearchIndexerSkillset | -| `create_or_update_skillset(skillset)` | Adds prefer, match_condition, skip/disable cache | +| `delete_data_source_connection(dsc)` | Polymorphic: str or `SearchIndexerDataSourceConnection` | +| `create_or_update_data_source_connection(dsc)` | Forwards `prefer`, `match_condition`, `skip_indexer_reset_requirement_for_cache` | +| `delete_indexer(indexer)` | Polymorphic: str or `SearchIndexer` | +| `create_or_update_indexer(indexer)` | Forwards `prefer`, `match_condition`, `skip_indexer_reset_requirement_for_cache`, `disable_cache_reprocessing_change_detection` | +| `delete_skillset(skillset)` | Polymorphic: str or `SearchIndexerSkillset` | +| `create_or_update_skillset(skillset)` | Forwards `prefer`, `match_condition`, `skip_indexer_reset_requirement_for_cache`, `disable_cache_reprocessing_change_detection` | +| `get_skillset_names()` | Name-only projection | + +### `__all__` +```python +["_SearchIndexClientOperationsMixin", "_SearchIndexerClientOperationsMixin"] +``` + +### After Regeneration, Verify +- [ ] Generated `_delete_*`, `_create_or_update_*`, `_list_*` base methods still exist with compatible signatures +- [ ] `SearchIndexResponse.semantic` still exists (used by `_convert_index_response`) +- [ ] New resource types added to the spec → add polymorphic delete + create-or-update wrappers here (and in the async mirror) +- [ ] `prefer="return=representation"` header still accepted by the service + +--- + +## File: `azure/search/documents/indexes/models/_patch.py` + +### Depends On (from generated code) +- `._models.SearchField` as `_SearchField` (base class) +- `._models.SearchIndexerDataSourceConnection` as `_SearchIndexerDataSourceConnection` (base class) +- `._models.KnowledgeBase` as `_KnowledgeBase` (base class) +- `._enums.SearchFieldDataType` as `_SearchFieldDataType` +- `._enums.LexicalAnalyzerName` +- Type-only imports for `DataChangeDetectionPolicy`, `DataDeletionDetectionPolicy`, `DataSourceCredentials`, `SearchIndexerDataContainer`, `SearchIndexerDataIdentity`, `SearchResourceEncryptionKey`, `SearchIndexerDataSourceType` + +### Defines +| Symbol | Type | What It Does | +|--------|------|-------------| +| `SearchField` | class | Adds `hidden` property (inverse of `retrievable`); accepts `hidden` kwarg | +| `SearchIndexerDataSourceConnection` | class | Three `@overload`s (credentials / connection_string / mapping) | +| `KnowledgeBase` | class | Placeholder subclass (entry point for future customizations) | +| `SimpleField(...)` | function | Builder; forces `searchable=False` | +| `SearchableField(...)` | function | Builder; auto-types to `String` / `Collection(String)` | +| `ComplexField(...)` | function | Builder; sets type to `Complex` / `Collection(Complex)` | +| `Collection(typ)` | function | Wraps type in `"Collection(...)"` string form | +| `_collection_helper(typ)` | function | Private helper used by `SearchFieldDataType.Collection` staticmethod | + +### Monkey-Patched on `SearchFieldDataType` +```python +SearchFieldDataType.Collection = staticmethod(_collection_helper) +SearchFieldDataType.String = SearchFieldDataType.STRING +SearchFieldDataType.Int32 = SearchFieldDataType.INT32 +SearchFieldDataType.Int64 = SearchFieldDataType.INT64 +SearchFieldDataType.Single = SearchFieldDataType.SINGLE +SearchFieldDataType.Double = SearchFieldDataType.DOUBLE +SearchFieldDataType.Boolean = SearchFieldDataType.BOOLEAN +SearchFieldDataType.DateTimeOffset = SearchFieldDataType.DATE_TIME_OFFSET +SearchFieldDataType.GeographyPoint = SearchFieldDataType.GEOGRAPHY_POINT +SearchFieldDataType.ComplexType = SearchFieldDataType.COMPLEX +``` + +### `__all__` +```python +["KnowledgeBase", "SearchField", "SearchFieldDataType", + "SearchIndexerDataSourceConnection", "SimpleField", "SearchableField", "ComplexField"] +``` + +### After Regeneration, Verify +- [ ] All right-hand-side UPPER_CASE enum members (`STRING`, `INT32`, `INT64`, `SINGLE`, `DOUBLE`, `BOOLEAN`, `DATE_TIME_OFFSET`, `GEOGRAPHY_POINT`, `COMPLEX`) still exist on generated `SearchFieldDataType` +- [ ] `_SearchField`, `_SearchIndexerDataSourceConnection`, `_KnowledgeBase` base constructors unchanged +- [ ] `SearchField.retrievable` still exists (the `hidden` property inverts it) +- [ ] If a new generated enum value collides with a Python keyword, add an alias + +--- + +## File: `azure/search/documents/indexes/aio/_patch.py` + +### Depends On (from generated code) +- `._client.SearchIndexClient` as async `_SearchIndexClient` (base class) +- `._client.SearchIndexerClient` as async `_SearchIndexerClient` (base class) + +### Defines +| Symbol | Type | What It Does | +|--------|------|-------------| +| `SearchIndexClient` | class | Async subclass; same `audience` → `credential_scopes` pattern | +| `SearchIndexerClient` | class | Async subclass; same pattern | + +### `__all__` +```python +["SearchIndexClient", "SearchIndexerClient"] +``` ### After Regeneration, Verify -- [ ] All generated `_delete_*`, `_create_or_update_*`, `_list_*` base methods still exist with compatible signatures -- [ ] `SearchIndexResponse` still has `.semantic` field (used by `_convert_index_response`) -- [ ] New resource types added to spec → add polymorphic delete/update wrappers here -- [ ] Verify prefer header value `"return=representation"` still applies +- [ ] Same checks as sync `indexes/_patch.py` --- ## File: `azure/search/documents/indexes/aio/_operations/_patch.py` -Async mirror of `indexes/_operations/_patch.py`. Same methods, all `async`. +Async mirror of `indexes/_operations/_patch.py`. Same polymorphic delete / create-or-update wrappers, all `async`. Imports `_convert_index_response` from sync — NOT duplicated. +### `__all__` +```python +["_SearchIndexClientOperationsMixin", "_SearchIndexerClientOperationsMixin"] +``` + ### After Regeneration, Verify - [ ] Same checks as sync `indexes/_operations/_patch.py` - [ ] Import of `_convert_index_response` from sync still resolves --- +## File: `azure/search/documents/knowledgebases/_patch.py` + +### Depends On (from generated code) +- `._client.KnowledgeBaseRetrievalClient` as `_KnowledgeBaseRetrievalClient` (base class) + +### Defines +| Symbol | Type | What It Does | +|--------|------|-------------| +| `KnowledgeBaseRetrievalClient` | class | Subclass; pops `audience` kwarg into `credential_scopes` | + +### `__all__` +```python +["KnowledgeBaseRetrievalClient"] +``` + +### After Regeneration, Verify +- [ ] Generated base still accepts `endpoint`, `credential`, `knowledge_base_name`, `credential_scopes` +- [ ] No conflicting `audience` kwarg introduced at the generated layer + +--- + +## File: `azure/search/documents/knowledgebases/aio/_patch.py` + +Async mirror of `knowledgebases/_patch.py`. `KnowledgeBaseRetrievalClient` subclass applying the same `audience` → `credential_scopes` pattern against the async generated base. + +### `__all__` +```python +["KnowledgeBaseRetrievalClient"] +``` + +### After Regeneration, Verify +- [ ] Same checks as sync `knowledgebases/_patch.py` + +--- + ## Empty `_patch.py` Files (No Customizations) These contain only `__all__ = []` and empty `patch_sdk()`. No action needed after regeneration. -- `azure/search/documents/indexes/_patch.py` -- `azure/search/documents/indexes/aio/_patch.py` -- `azure/search/documents/knowledgebases/_patch.py` +- `azure/search/documents/knowledgebases/_operations/_patch.py` - `azure/search/documents/knowledgebases/models/_patch.py` -- `azure/search/documents/knowledgebases/aio/_patch.py` +- `azure/search/documents/knowledgebases/aio/_operations/_patch.py` From 5c9b140e4dd5fd003a35873ed733bf02c1dbee74 Mon Sep 17 00:00:00 2001 From: xiangyan99 Date: Fri, 24 Apr 2026 11:12:39 -0700 Subject: [PATCH 2/5] update cspell --- .vscode/cspell.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/cspell.json b/.vscode/cspell.json index 4204a4d75848..5e528b871567 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -156,6 +156,7 @@ "sdk/ai/azure-ai-voicelive/samples/**" ], "words": [ + "vally", "qnas", "msedge", "spinup", From 583c0d337ae1f55d515bf56e018d6389af814c66 Mon Sep 17 00:00:00 2001 From: xiangyan99 Date: Fri, 24 Apr 2026 12:12:48 -0700 Subject: [PATCH 3/5] update --- .vscode/cspell.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.vscode/cspell.json b/.vscode/cspell.json index 5e528b871567..4c9e7a14dfcc 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -157,6 +157,8 @@ ], "words": [ "vally", + "regen", + "pylintrc", "qnas", "msedge", "spinup", From 7bf9b7ab719b5aa491010e58becfe1be4ef5b610 Mon Sep 17 00:00:00 2001 From: xiangyan99 Date: Wed, 29 Apr 2026 09:30:35 -0700 Subject: [PATCH 4/5] updates --- .github/skills/create-package-skill/SKILL.md | 2 +- .../phases/00-scan-package.md | 2 +- .../phases/01-scaffold-skill.md | 22 ++++++------------- .../phases/02-generate-references.md | 2 +- .../references/skill-template.md | 9 ++++---- .../skills/azure-search-documents/SKILL.md | 21 +++++------------- 6 files changed, 20 insertions(+), 38 deletions(-) diff --git a/.github/skills/create-package-skill/SKILL.md b/.github/skills/create-package-skill/SKILL.md index 9e2ef9a0d8bd..aff78ada323d 100644 --- a/.github/skills/create-package-skill/SKILL.md +++ b/.github/skills/create-package-skill/SKILL.md @@ -46,7 +46,7 @@ Run each phase in order. **Progressive loading:** Read only the current phase fi **Relationship to existing SDK tools:** - Package skills **complement** the Azure SDK MCP tools (`azsdk_package_generate_code`, `azsdk_package_build_code`, `azsdk_customized_code_update`, etc.) — they do NOT replace them. - MCP tools handle deterministic operations (generate, build, test). Package skills provide the reasoning context an agent needs to use those tools correctly for a specific package, plus the package-specific verification commands the tools do not know about. -- Reference the existing tools for the overall workflow (e.g., "Run `tsp-client update` or `azsdk_package_generate_code`"), but DO include the copy-pasteable verification commands an agent needs: import smoke tests (`python -c "from ... import ..."`), `ApiVersion` reconciliation (`grep`, `python -c "import json; ..."`), diff commands (`git diff --name-only | grep ...`), and lint invocations (`azpysdk mypy`, `azpysdk pylint`). +- Reference the existing tools for the overall workflow (e.g., "Run `tsp-client update` or `azsdk_package_generate_code`"), but DO include the copy-pasteable verification commands an agent needs: import smoke tests (`python -c "from ... import ..."`), `ApiVersion` reconciliation (`grep`, `python -c "import json; ..."`), diff commands (`git diff --name-only | grep ...`), and the package validation invocation (`azsdk_package_run_check with checkType="All"`). - Never paraphrase an MCP tool's entire contract. Do include the one-line invocations the agent will run. **Structure:** diff --git a/.github/skills/create-package-skill/phases/00-scan-package.md b/.github/skills/create-package-skill/phases/00-scan-package.md index b3bb2316309a..f3ca568d502b 100644 --- a/.github/skills/create-package-skill/phases/00-scan-package.md +++ b/.github/skills/create-package-skill/phases/00-scan-package.md @@ -53,7 +53,7 @@ Scan the package using the checklist below. Use file_search / grep_search / read 11. **Tests**: Check `tests/` directory. Look for `conftest.py`, `*test_base*.py`, `assets.json` (recorded tests via Test Proxy), `testpreparer*.py`. Note whether tests are live, recorded, or both. -12. **Documentation surfaces**: Check for `CHANGELOG.md` and `README.md` at the package root. Both are near-universal; the skill's Step 8 (Update Documentation and Samples) will reference them. +12. **Documentation surfaces**: Check for `CHANGELOG.md` and `README.md` at the package root. Both are near-universal; the skill's Step 7 (Update Documentation and Samples) will reference them. 13. **Management vs data plane**: If package name starts with `azure-mgmt-` → management-plane rules apply (see copilot-instructions). Otherwise data-plane. diff --git a/.github/skills/create-package-skill/phases/01-scaffold-skill.md b/.github/skills/create-package-skill/phases/01-scaffold-skill.md index ce9ce46e4457..f19d47d2d66f 100644 --- a/.github/skills/create-package-skill/phases/01-scaffold-skill.md +++ b/.github/skills/create-package-skill/phases/01-scaffold-skill.md @@ -27,7 +27,7 @@ Ask the user to confirm the choice, defaulting to A when any of these are true: - **Keep it static** regarding release state (no version numbers, no current default API version) — but **do** include copy-pasteable commands the agent will need. - **Point to source** for mutable things (API version enum → point at `_patch.py`; generator target → point at `_metadata.json`). - **Prefer TypeSpec over `_patch.py`** when the customization is expressible in the spec — note this in the customization patterns section. -- **Include MCP tool invocations** where appropriate (`azsdk_package_generate_code`, `azsdk_package_run_check`, etc.), but also include the direct CLI equivalents (`tsp-client update`, `azpysdk mypy`, `azpysdk pylint`) because agents routinely run those too. +- **Include MCP tool invocations** where appropriate (`azsdk_package_generate_code`, `azsdk_package_run_check`, etc.). For validation, use `azsdk_package_run_check with checkType="All"`. Include the direct `tsp-client update` CLI fallback for generation since agents routinely run it. ## Required sections — Option A (step-by-step) @@ -148,25 +148,17 @@ List what to watch for, tailored to the package (from Phase 0 scan): - New fields on response models → extractor helpers need to be updated - Changed request models → builder helpers need to wire new parameters through -### Step 6 — Ensure mypy pass +### Step 6 — Run package validation -```bash -cd sdk// -azpysdk mypy -``` - -Mention what `mypy.ini` already ignores (typically generated internals). +Run the full validation suite in one shot: -### Step 7 — Ensure pylint pass - -```bash -cd sdk// -azpysdk pylint +``` +azsdk_package_run_check with checkType="All" ``` -Mention what `pylintrc` excludes (typically `_generated/`, `_vendor/`, `tests/`, `samples/`). +Mention what the package- and repo-level config already ignore (typically generated internals, `_vendor/`, `tests/`, `samples/`) so the agent knows errors will originate in `_patch.py` customizations. -### Step 8 — Update Documentation and Samples +### Step 7 — Update Documentation and Samples **CHANGELOG**: - Find the topmost `## (Unreleased)` section in `CHANGELOG.md`. diff --git a/.github/skills/create-package-skill/phases/02-generate-references.md b/.github/skills/create-package-skill/phases/02-generate-references.md index 608d14d88541..ef1e194c89cd 100644 --- a/.github/skills/create-package-skill/phases/02-generate-references.md +++ b/.github/skills/create-package-skill/phases/02-generate-references.md @@ -82,7 +82,7 @@ If you do create it, include: - **API version management** — where the version enum lives, how `_patch.py` interacts with it - **Key supporting files** — policies, helpers, `mypy.ini`, `assets.json`, etc. - **Dependencies** — key runtime / dev / test deps -- **Build / test / lint** — MCP tools and `azpysdk` entry points (not full command re-documentation) +- **Build / test / lint** — MCP tools (`azsdk_package_run_check with checkType="All"`) and any package-specific test entry points (not full command re-documentation) ## Step 1 — Present diff --git a/.github/skills/create-package-skill/references/skill-template.md b/.github/skills/create-package-skill/references/skill-template.md index 52f452d941c2..ee416a2bcfb0 100644 --- a/.github/skills/create-package-skill/references/skill-template.md +++ b/.github/skills/create-package-skill/references/skill-template.md @@ -14,7 +14,7 @@ The `name` field and directory name MUST match the Python distribution package n ## Content Principles - **Workflow-first.** The primary audience is an agent running post-regeneration cleanup. Structure the skill so the agent can execute it top-to-bottom: regenerate → verify imports → check API version → check for new operations → check for model/enum changes → lint/type-check → update docs. See *Structure Options* below. -- **Include the commands the agent needs to copy-paste.** `tsp-client update`, `python -c "from import X"` import smoke tests, `grep -A 20 'class ApiVersion' `, `git diff --name-only | grep _operations\.py`, `azpysdk mypy`, `azpysdk pylint`, etc. Do not force the agent to invent commands. "Don't re-document MCP tools" means don't paraphrase an entire MCP tool contract — it does **not** mean omit the one-line invocation an agent is expected to run. +- **Include the commands the agent needs to copy-paste.** `tsp-client update`, `python -c "from import X"` import smoke tests, `grep -A 20 'class ApiVersion' `, `git diff --name-only | grep _operations\.py`, `azsdk_package_run_check with checkType="All"`, etc. Do not force the agent to invent commands. "Don't re-document MCP tools" means don't paraphrase an entire MCP tool contract — it does **not** mean omit the one-line invocation an agent is expected to run. - **Point to source for things that change.** Never hardcode API version strings, release numbers, or the current `DEFAULT_VERSION` value. Point at `_patch.py`, `_metadata.json`, or `_version.py`. - **Prefer TypeSpec-level customizations over `_patch.py`.** Note when a customization could be expressed by a TypeSpec decorator or emitter option instead. - **Focus on the convenience layer.** What does the agent need to know to write/maintain `_patch.py`, hand-written utilities, and async parity correctly. @@ -32,9 +32,8 @@ Numbered steps the agent executes in order. Canonical skeleton: 3. Check ApiVersion (reconcile `_metadata.json` with the hand-maintained `ApiVersion` enum) 4. Check for New Operations That Need Wrappers (git diff generated operations) 5. Check for Model/Enum Changes That Affect Customizations (git diff models/enums) -6. Ensure mypy passes -7. Ensure pylint passes -8. Update Documentation and Samples (CHANGELOG, README) +6. Run package validation (`azsdk_package_run_check with checkType="All"`) +7. Update Documentation and Samples (CHANGELOG, README) Followed by a *Customization Patterns Reference* section that names and briefly describes each hand-written pattern in the codebase (constructor reorder, polymorphic delete, enum aliases, hand-authored batching sender, custom paging, etc.), each with 2–5 sentences on *what it does* and *what would break it*. Exhaustive file-by-file detail goes to `references/customizations.md`. @@ -83,7 +82,7 @@ Regardless of structure, the skill MUST include: - **`__all__` re-export** — any new public symbol added/overridden in `_patch.py` MUST be appended to that file's `__all__`, otherwise `patch_sdk()` will not expose it. Include the verification command `python -c "import ; print(sorted(.__all__))"`. Skip categories that do not apply. - A "Check for Model/Enum Changes" step listing what a diff of the generated models/enums would break in the customizations (renamed enum values → aliases; changed model constructors → subclass constructors; new fields on response models → extractor helpers; new parameters on request models → builder helpers). -- A linting / type-checking step. Prefer `azpysdk mypy` and `azpysdk pylint` (or the equivalent `azsdk_package_run_check`). Mention what the repo-level / package-level config already ignores so the agent knows where errors will come from. +- A linting / type-checking step that runs `azsdk_package_run_check with checkType="All"` (covers mypy, pylint, and the rest of the validation suite in one invocation). Mention what the repo-level / package-level config already ignores so the agent knows where errors will come from. - A **Documentation and Samples** step covering: - CHANGELOG: find the topmost `## (Unreleased)` section; add entries under `### Features Added` / `### Breaking Changes` / `### Bugs Fixed`; create the section above the latest release if missing. Include a small example block. - README: when to update usage examples, Key Concepts, and the listed API version. diff --git a/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md b/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md index 78aac01b950f..5be03ab59fcc 100644 --- a/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md +++ b/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md @@ -131,26 +131,17 @@ Watch for: - **`SearchIndexResponse.semantic`** — used by `_convert_index_response()`; if the field is renamed, update the mapping. - **`SearchField.retrievable`** — the `hidden` property in `SearchField` subclass is its inverse; if renamed, update the property. -## Step 6: Ensure mypy passes +## Step 6: Run package validation -```bash -cd sdk/search/azure-search-documents -azpysdk mypy -# or: azsdk_package_run_check with checkType="All" -``` - -`mypy.ini` at the package root already ignores generated internals — errors will originate in `_patch.py` files or `samples/`. +Run the full validation suite (mypy, pylint, and the rest) in one shot: -## Step 7: Ensure pylint passes - -```bash -cd sdk/search/azure-search-documents -azpysdk pylint +``` +azsdk_package_run_check with checkType="All" ``` -The repo-level `pylintrc` excludes generated, `_vendor/`, `tests/`, and `samples/` — only `_patch.py` customizations are linted. +`mypy.ini` and the repo-level `pylintrc` already exclude generated internals, `_vendor/`, `tests/`, and `samples/` — errors will originate in `_patch.py` customizations. -## Step 8: Update Documentation and Samples +## Step 7: Update Documentation and Samples ### CHANGELOG From 159986b0e5c5f59f8aab06f176809e572aa1379d Mon Sep 17 00:00:00 2001 From: xiangyan99 Date: Wed, 29 Apr 2026 10:52:31 -0700 Subject: [PATCH 5/5] revert search skill changes. --- .../skills/azure-search-documents/SKILL.md | 229 ++++++------ .../references/customizations.md | 326 +++++------------- 2 files changed, 187 insertions(+), 368 deletions(-) diff --git a/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md b/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md index 5be03ab59fcc..475fff5bbf73 100644 --- a/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md +++ b/sdk/search/azure-search-documents/.github/skills/azure-search-documents/SKILL.md @@ -1,13 +1,13 @@ --- name: azure-search-documents -description: 'Post-regeneration customization guide for azure-search-documents. Verifies _patch.py imports, ApiVersion, enum aliases, mypy/pylint, and changelog after codegen. WHEN: regenerate azure-search-documents; modify azure-search-documents; fix azure-search-documents bug; add azure-search-documents feature; azure-search-documents tsp-client update; edit azure-search-documents _patch.py.' +description: 'Post-regeneration customization guide for azure-search-documents. Verifies _patch.py imports, ApiVersion, enum aliases, mypy/pylint, and changelog after codegen. USE WHEN: running tsp-client update, regenerating from TypeSpec, fixing _patch.py breaks, bumping ApiVersion, adding operation wrappers, or updating search SDK documentation.' --- # azure-search-documents — Post-Regeneration Customization Guide -After running `tsp-client update`, the generated code is a raw skeleton — not a shippable SDK. This skill tells you exactly which `_patch.py` files customize it and how to verify those customizations still work after regeneration. +After running `tsp-client update`, the generated code is a raw skeleton — not a shippable SDK. This skill tells you exactly what customizations exist in `_patch.py` files and how to verify they still work after regeneration. -The generator never touches `_patch.py` files, so your customizations survive regeneration. But they import from generated modules that **do** change. A renamed model, a new parameter, or a removed enum value will silently break a `_patch.py` file. The verification steps below catch those breaks. +The generator never touches `_patch.py` files, so your customizations survive regeneration. But they import from generated modules that **do** change. A renamed model, a new parameter, or a removed enum value will silently break a `_patch.py` file. The verification steps below catch these breaks. ## Step 1: Run Regeneration @@ -16,49 +16,40 @@ cd sdk/search/azure-search-documents # Update tsp-location.yaml with the new spec commit SHA, then: tsp-client update -# or: azsdk_package_generate_code -# If the API version changed, _metadata.json updates automatically; -# reconcile the hand-maintained ApiVersion enum in Step 3. +# If API version changed, update _metadata.json too ``` ## Step 2: Verify Imports in All `_patch.py` Files -Every non-empty `_patch.py` imports from generated modules. After regeneration, check those imports still resolve: +Every `_patch.py` with customizations imports from generated modules. After regeneration, check that these imports still resolve. The fastest way: ```bash -python -c "from azure.search.documents import SearchClient, SearchIndexingBufferedSender, ApiVersion, DEFAULT_VERSION, IndexDocumentsBatch" -python -c "from azure.search.documents.aio import SearchClient, SearchIndexingBufferedSender" -python -c "from azure.search.documents.indexes import SearchIndexClient, SearchIndexerClient" -python -c "from azure.search.documents.indexes.aio import SearchIndexClient, SearchIndexerClient" -python -c "from azure.search.documents.indexes.models import SearchField, SearchFieldDataType, SimpleField, SearchableField, ComplexField, KnowledgeBase" -python -c "from azure.search.documents.knowledgebases import KnowledgeBaseRetrievalClient" -python -c "from azure.search.documents.knowledgebases.aio import KnowledgeBaseRetrievalClient" +python -c "from azure.search.documents import SearchClient" +python -c "from azure.search.documents.aio import SearchClient" +python -c "from azure.search.documents.indexes.models import SearchField, SearchFieldDataType" +python -c "from azure.search.documents.models import IndexDocumentsBatch" ``` -If any fail, a generated class/enum was renamed or removed. See `references/customizations.md` for the exact import each `_patch.py` depends on. +If any fail, a generated class/enum was renamed or removed. Read `references/customizations.md` for the exact import each `_patch.py` depends on. -The `_patch.py` files that have customizations (others are empty boilerplate): +The `_patch.py` files that have customizations (all others are empty boilerplate): ``` azure/search/documents/ -├── _patch.py # SearchClient subclass, SearchIndexingBufferedSender, ApiVersion enum, DEFAULT_VERSION -├── _operations/_patch.py # SearchItemPaged, search(), custom index_documents/413 splitting, continuation-token helpers -├── models/_patch.py # IndexDocumentsBatch, RequestEntityTooLargeError -├── aio/_patch.py # Async SearchClient, async SearchIndexingBufferedSender -├── aio/_operations/_patch.py # AsyncSearchItemPaged, async search(), reuses sync helpers -├── indexes/_patch.py # SearchIndexClient, SearchIndexerClient (audience kwarg plumbing) -├── indexes/_operations/_patch.py # Polymorphic delete/create-or-update, list_index_names, _convert_index_response -├── indexes/models/_patch.py # SearchField(hidden), field builders, SearchIndexerDataSourceConnection overloads, KnowledgeBase, SearchFieldDataType aliases -├── indexes/aio/_patch.py # Async SearchIndexClient/SearchIndexerClient mirrors -├── indexes/aio/_operations/_patch.py # Async mirror of indexes operations mixin -├── knowledgebases/_patch.py # KnowledgeBaseRetrievalClient (audience kwarg plumbing) -└── knowledgebases/aio/_patch.py # Async KnowledgeBaseRetrievalClient +├── _patch.py # SearchClient, SearchIndexingBufferedSender, ApiVersion +├── _operations/_patch.py # SearchItemPaged, search(), document CRUD +├── models/_patch.py # IndexDocumentsBatch, RequestEntityTooLargeError +├── aio/_patch.py # Async SearchClient, async SearchIndexingBufferedSender +├── aio/_operations/_patch.py # AsyncSearchItemPaged, async search()/CRUD +├── indexes/_operations/_patch.py # Polymorphic delete/update, list helpers +├── indexes/models/_patch.py # SearchField, field builders, enum aliases +└── indexes/aio/_operations/_patch.py # Async index/indexer operations ``` ## Step 3: Check ApiVersion -The generator targets the version in `_metadata.json`. The hand-maintained `ApiVersion` enum in `azure/search/documents/_patch.py` must include it. +After regeneration, the generated code targets the API version in `_metadata.json`. The hand-maintained `ApiVersion` enum in `_patch.py` must include this version. ```bash # 1. See what API version the generator used @@ -71,195 +62,167 @@ grep -A 20 'class ApiVersion' azure/search/documents/_patch.py grep 'DEFAULT_VERSION' azure/search/documents/_patch.py ``` -If the generated API version is **not** in the enum: -1. Add the new member to `ApiVersion` in `azure/search/documents/_patch.py` (e.g., `V2026_05_01_PREVIEW = "2026-05-01-preview"`). -2. Update `DEFAULT_VERSION` to point to the new member. -3. Update docstrings on the public client subclasses (`SearchClient`, `SearchIndexClient`, `SearchIndexerClient`, `KnowledgeBaseRetrievalClient`) that name the default version. +If the generated API version is **not** in the `ApiVersion` enum: +1. Add the new member to `ApiVersion` in `azure/search/documents/_patch.py` (e.g., `V2026_05_01_PREVIEW = "2026-05-01-preview"`) +2. Update `DEFAULT_VERSION` to point to the new member +3. Update the `ApiVersion` docstring in the class if one exists -Verify the default round-trips: +Verify the default round-trips correctly: ```bash python -c "from azure.search.documents._patch import ApiVersion, DEFAULT_VERSION; print(DEFAULT_VERSION.value)" ``` -## Step 4: Expose New Generated Methods Through `_patch.py` +## Step 4: Check for New Operations That Need Wrappers -New generated operations do NOT automatically surface in the user-expected shape. Diff what regeneration added: +Look at what changed in the generated operations files: ```bash git diff --name-only | grep "_operations\.py" | grep -v _patch -git diff azure/search/documents/indexes/_operations/_operations.py | grep -E '^\+\s+(async\s+)?def ' -git diff azure/search/documents/_operations/_operations.py | grep -E '^\+\s+(async\s+)?def ' ``` -For each new method, pick the exposure path: +If a new operation was added to the generated `_operations.py`, decide whether it needs a convenience wrapper in `_patch.py`. Operations that need wrappers: +- **Delete operations** — need the polymorphic str-or-model pattern (see "Polymorphic Delete Pattern" below) +- **Create-or-update operations** — need `prefer="return=representation"`, `match_condition`, and `etag` forwarding +- **List operations** — may need a `select` parameter or name-only projection -1. **Pass-through via generated mixin inheritance** — if the generated signature is already user-friendly, the `_OperationsMixin` subclass in `_patch.py` inherits it automatically. Verify the method name does NOT collide with an existing `_patch.py` override. +Operations that pass through without a wrapper (if the generated signature is already user-friendly) can be left alone — they're inherited from the generated mixin. -2. **Override in the operations mixin** — needed when the method requires custom paging (`SearchItemPaged`), request-body rewriting (`_build_search_request`), response conversion (`_convert_search_result`, `_convert_index_response`), or 413 batch splitting. - -3. **Polymorphic str-or-model wrapper** — for `delete_*` on a named resource (index, synonym map, alias, indexer, data source, skillset, knowledge base, knowledge source). Accept either the `str` name or the resource model; forward `e_tag` + `match_condition` when a model is passed. - -4. **Create-or-update wrapper** — for `create_or_update_*`. Forward `prefer="return=representation"`, `match_condition`, `etag`, plus package-specific flags: - - `allow_index_downtime` for `create_or_update_index` - - `skip_indexer_reset_requirement_for_cache` for indexers - - `skip_indexer_reset` / `disable_cache_reprocessing_change_detection` for skillsets - -5. **List projection wrapper** — for `list_*`. Add a `select` parameter, a name-only projection via `cls` callback (`list_index_names`, `list_indexer_names`, `list_skillset_names`), or convert a generated projection type back to the canonical model via `_convert_index_response`. - -6. **Re-export via `__all__`** — any NEW symbol you add or override in `_patch.py` MUST be appended to that file's `__all__`. Otherwise `patch_sdk()` will not surface it. - - ```bash - python -c "import azure.search.documents as m; print(sorted(m.__all__))" - python -c "import azure.search.documents.indexes as m; print(sorted(m.__all__))" - ``` - -Every sync wrapper needs a matching async mirror under `aio/`. Shared helpers (`_build_search_request`, `_convert_search_result`, `_convert_index_response`, `_pack_continuation_token`, `_unpack_continuation_token`) live in the **sync** `_operations/_patch.py` and are **imported** by async — do not duplicate them. +Remember: every sync wrapper in `_operations/_patch.py` needs a matching async wrapper in `aio/_operations/_patch.py`. ## Step 5: Check for Model/Enum Changes That Affect Customizations ```bash -git diff azure/search/documents/models/_models.py azure/search/documents/models/_enums.py -git diff azure/search/documents/indexes/models/_models.py azure/search/documents/indexes/models/_enums.py +git diff models/_enums.py models/_models.py indexes/models/_enums.py indexes/models/_models.py ``` Watch for: -- **Renamed enum values** — `indexes/models/_patch.py` monkey-patches camelCase aliases onto `SearchFieldDataType` (`String`, `Int32`, `Int64`, `Single`, `Double`, `Boolean`, `DateTimeOffset`, `GeographyPoint`, `ComplexType`). All right-hand-side UPPER_CASE members must still exist. -- **Changed model constructors** — `IndexDocumentsBatch`, `SearchField`, `SearchIndexerDataSourceConnection`, `KnowledgeBase` are subclassed in `_patch.py`; base-class constructor changes break them. -- **New fields on `SearchResult`** — `_convert_search_result()` extracts `@search.*` metadata (`score`, `reranker_score`, `highlights`, `captions`, `document_debug_info`, `reranker_boosted_score`). New metadata fields must be added there. -- **Changed `SearchRequest`** — `_build_search_request()` constructs this model directly; new query parameters must be wired through, including any new pipe-delimited semantic encoding. -- **`SearchIndexResponse.semantic`** — used by `_convert_index_response()`; if the field is renamed, update the mapping. -- **`SearchField.retrievable`** — the `hidden` property in `SearchField` subclass is its inverse; if renamed, update the property. +- **Renamed enum values** — backward-compat aliases in `_patch.py` may reference old names +- **Changed model constructors** — `IndexDocumentsBatch`, `SearchField`, `SearchIndexerDataSourceConnection`, `KnowledgeBase` are subclassed in `_patch.py` and may break if the base constructor changes +- **New fields on `SearchResult`** — `_convert_search_result()` in `_operations/_patch.py` extracts `@search.*` metadata fields; new ones need to be added +- **Changed `SearchRequest` model** — `_build_search_request()` constructs this model directly; new parameters need to be wired through +- **Changed `SearchFieldDataType` model** — `indexes/models/_patch.py` monkey-patches `SearchFieldDataType.Collection` as a `staticmethod` and adds camelCase backward-compat aliases (e.g., `Int32` → `INT32`). If values are added, removed, or renamed in the generated enum, the aliases and `Collection` helper must be updated to match -## Step 6: Run package validation +## Step 6: Ensure mypy pass -Run the full validation suite (mypy, pylint, and the rest) in one shot: +```bash +cd sdk/search/azure-search-documents +# Preferred — uses azpysdk CLI (install via: pip install -e eng/tools/azure-sdk-tools) +azpysdk mypy ``` -azsdk_package_run_check with checkType="All" + +The package-level `mypy.ini` already ignores `_generated` internals — you only need to fix errors in `_patch.py` files and `samples/`. + +## Step 7: Ensure pylint pass + +```bash +cd sdk/search/azure-search-documents + +# Preferred +azpysdk pylint ``` -`mypy.ini` and the repo-level `pylintrc` already exclude generated internals, `_vendor/`, `tests/`, and `samples/` — errors will originate in `_patch.py` customizations. +The repo-level `pylintrc` already excludes `_generated/`, `_vendor/`, `tests/`, and `samples/` — only `_patch.py` customizations are linted. -## Step 7: Update Documentation and Samples +## Step 8: Update Documentation and Samples -### CHANGELOG +If the regeneration added new operations, models, or changed the API version, update the docs and samples: -Open `CHANGELOG.md` and find the topmost `## (Unreleased)` section. Add entries under: +### Changelog -- `### Features Added` — new operations, models, parameters, API version support -- `### Breaking Changes` — renamed/removed models, changed signatures, dropped API versions -- `### Bugs Fixed` — fixes to `_patch.py` logic, pagination, encoding, batching +Open `CHANGELOG.md` and find the topmost `## (Unreleased)` section. Add entries under the appropriate heading: -Example: +- **`### Features Added`** — new operations, models, parameters, or API version support +- **`### Breaking Changes`** — renamed/removed models, changed signatures, dropped API versions +- **`### Bugs Fixed`** — fixes to `_patch.py` logic, pagination, encoding, etc. +Example entry: ```markdown ## 11.x.0bN (Unreleased) ### Features Added -- Added `create_or_update_knowledge_base` to `SearchIndexClient`. -- Support for API version `2026-05-01-preview`. +- Added `create_or_update_knowledge_base` to `SearchIndexClient` +- Support for API version `2026-05-01-preview` ### Breaking Changes -- Renamed `OldModel` to `NewModel`. +- Renamed `OldModel` to `NewModel` ``` -If no `(Unreleased)` section exists, create one above the latest release using the next version from `azure/search/documents/_version.py`. +If no `(Unreleased)` section exists, create one above the latest release with the next version from `_version.py`. ### README -- Add usage examples for new client classes (`KnowledgeBaseRetrievalClient` etc.) or operations. -- Update "Key concepts" for new resource types. -- Update the listed API version in "Getting started" if `DEFAULT_VERSION` changed. +If new client classes or major features were added, update `README.md`: ---- +- Add usage examples for new client classes or operations +- Update the "Key concepts" section if new resource types were introduced +- Update the listed API version in the "Getting started" section if it changed # Customization Patterns Reference -Each pattern below exists in the codebase today. After regeneration, verify each still works. See `references/customizations.md` for the exhaustive file-by-file inventory. - -## Client Constructor Customizations (audience / parameter reorder) +Each pattern below describes a customization that exists in the codebase today. After regeneration, verify each one still works. Read `references/customizations.md` for the exhaustive file-by-file inventory. -- `SearchClient` in `_patch.py` (sync + async) reorders params to `(endpoint, index_name, credential)`. The generated base uses `(endpoint, credential, index_name)`. -- `SearchIndexClient`, `SearchIndexerClient` (`indexes/_patch.py` + async) and `KnowledgeBaseRetrievalClient` (`knowledgebases/_patch.py` + async) all pop the `audience` kwarg and convert it into `credential_scopes=[audience + "/.default"]`. +## SearchClient Constructor Reordering -If regeneration changes the base constructor signature (param order, required kwargs), update each subclass. +`SearchClient` in `_patch.py` swaps parameter order to `(endpoint, index_name, credential)`. The generated base uses `(endpoint, credential, index_name)`. If regeneration changes the base constructor signature, update the subclass. ## Custom Search Pagination `search()` does **not** use standard Azure SDK paging. It uses `SearchItemPaged` / `AsyncSearchItemPaged` with a custom `SearchPageIterator` because: -- Search paginates via POST with `nextPageParameters` body, not GET with `nextLink`. -- First-page metadata (facets, count, coverage, answers, debug info) must be available before iteration starts. -- Results are converted to dicts with `@search.*` keys. +- Search paginates via POST with `nextPageParameters` body, not GET with `nextLink` +- First-page metadata (facets, count, answers) must be available before iteration +- Results are converted to dicts with `@search.*` keys -Continuation token is Base64-encoded JSON: `{"apiVersion": ..., "nextLink": ..., "nextPageParameters": {...}}`. +The continuation token is Base64-encoded JSON: `{"apiVersion": "...", "nextLink": "...", "nextPageParameters": {...}}` -Shared helpers (`_build_search_request`, `_convert_search_result`, `_pack_continuation_token`, `_unpack_continuation_token`) live in sync `_operations/_patch.py` and are imported by async. +Shared helpers (`_build_search_request`, `_convert_search_result`, `_pack_continuation_token`, `_unpack_continuation_token`) live in sync `_operations/_patch.py` and are imported by async. Don't duplicate them. ## Pipe-Delimited Semantic Encoding `_build_search_request()` encodes semantic parameters into pipe-delimited wire format: - ``` -query_answer="extractive", count=3 → "extractive|count-3" -query_caption="extractive", highlight=True → "extractive|highlight-true" +query_answer="extractive", count=3 → "extractive|count-3" +query_caption="extractive", highlight=True → "extractive|highlight-true" ``` +New semantic parameters should follow this pattern. -New semantic-search parameters follow this pattern. - -## 413 Batch Splitting +## SearchIndexingBufferedSender -`index_documents()` in `_operations/_patch.py` recursively splits an `IndexDocumentsBatch` in half when the service returns `RequestEntityTooLargeError` (413). Retry-on-split uses `is_retryable_status_code()` (409/422/503). +Entirely hand-authored in `_patch.py` (sync) and `aio/_patch.py` (async). Not generated. Wraps `SearchClient` with auto-flush timer, 413 recursive batch splitting, retry per document key (409/422/503), and key field auto-detection. The async version supports both sync and async callbacks via `asyncio.iscoroutinefunction()`. -## SearchIndexingBufferedSender +## Field Builders -Entirely hand-authored in `_patch.py` (sync) and `aio/_patch.py` (async); not generated. Wraps `SearchClient` with: -- Auto-flush timer (`threading.Timer` sync / `asyncio.Task` async). -- 413 recursive batch splitting. -- Retry per document key (409/422/503) bounded by `max_retries_per_action`. -- Key-field auto-detection against the index schema. -- The async version detects sync vs async callbacks via `asyncio.iscoroutinefunction()`. +`SimpleField()`, `SearchableField()`, `ComplexField()` in `indexes/models/_patch.py`. `SimpleField` explicitly sets `searchable=False`. `SearchableField` auto-sets type to `String`/`Collection(String)`. -## Field Builders and `SearchFieldDataType` Helpers (`indexes/models/_patch.py`) +`SearchField` subclass adds `hidden` property (inverse of `retrievable`). -- `SimpleField(...)` — sets `searchable=False` explicitly. -- `SearchableField(...)` — auto-types to `String` / `Collection(String)`. -- `ComplexField(...)` — sets type to `Complex` / `Collection(Complex)`. -- `SearchField` subclass adds `hidden` property (inverse of `retrievable`). -- `SearchFieldDataType.Collection = staticmethod(_collection_helper)` — monkey-patched onto the generated enum. +`SearchFieldDataType.Collection` is a `staticmethod` monkey-patched onto the generated enum. ## Backward-Compatible Enum Aliases -Applied at module load in `indexes/models/_patch.py`: - +Monkey-patched at module load in `_patch.py` files: ```python SearchFieldDataType.Int32 = SearchFieldDataType.INT32 # camelCase → UPPER -# (String, Int64, Single, Double, Boolean, DateTimeOffset, GeographyPoint, ComplexType) ``` +After regeneration, verify the right-hand-side names still exist in the generated enums. If a new enum collides with a Python keyword, add an alias. -After regeneration, verify every right-hand-side UPPER_CASE member still exists in the generated enum. If a new enum value collides with a Python keyword, add an alias. - -## Polymorphic Delete / Create-Or-Update Pattern - -All delete/update operations in `indexes/_operations/_patch.py` accept str or model: +## Polymorphic Delete Pattern +All delete/update operations in `indexes/_operations/_patch.py` accept str or model object: ```python def delete_index(self, index, *, match_condition=MatchConditions.Unconditionally, **kwargs): try: name = index.name # model object return self._delete_index(name=name, etag=index.e_tag, match_condition=match_condition, **kwargs) except AttributeError: - return self._delete_index(name=index, **kwargs) # string + name = index # string + return self._delete_index(name=name, **kwargs) ``` - -Covered resources: index, synonym map, alias, indexer, data source connection, skillset, knowledge base, knowledge source. New resource types added to the spec need the same pair of wrappers in sync **and** async. +New resource types need this same wrapper in both sync and async. ## `_convert_index_response` Helper -`list_indexes(select=...)` returns `SearchIndexResponse` (projection type). `_convert_index_response()` maps it to `SearchIndex` — notably `response.semantic` → `semantic_search`. Shared between sync and async via import; do not duplicate. - ---- - -When to use `_patch.py` vs TypeSpec: prefer TypeSpec decorators / emitter options for things the spec can express (renames, flattening, client names). Use `_patch.py` for Python-specific behavior: pagination contracts, threading/asyncio, polymorphic call patterns, 413 splitting, monkey-patched enum helpers, and backward-compat aliases. See [typespec-python emitter docs](https://github.com/Azure/autorest.python/blob/main/packages/typespec-python/README.md). +`list_indexes(select=...)` returns `SearchIndexResponse` (projection type). `_convert_index_response()` maps it to `SearchIndex`, notably `response.semantic` → `semantic_search`. Shared between sync and async via import. diff --git a/sdk/search/azure-search-documents/.github/skills/azure-search-documents/references/customizations.md b/sdk/search/azure-search-documents/.github/skills/azure-search-documents/references/customizations.md index dbadf11a7a76..7f5d99d19f71 100644 --- a/sdk/search/azure-search-documents/.github/skills/azure-search-documents/references/customizations.md +++ b/sdk/search/azure-search-documents/.github/skills/azure-search-documents/references/customizations.md @@ -1,6 +1,6 @@ # Post-Regeneration Customization Checklist -File-by-file inventory of every non-empty `_patch.py` in `azure-search-documents`. Use this after running `tsp-client update` to verify each customization still holds. +Use this file after running `tsp-client update` to verify every customization. Each section is a `_patch.py` file with the exact classes, functions, and aliases it defines, plus what generated symbols it depends on. --- @@ -8,125 +8,82 @@ File-by-file inventory of every non-empty `_patch.py` in `azure-search-documents ### Depends On (from generated code) - `._client.SearchClient` as `_SearchClient` (base class) -- `._operations._patch.SearchItemPaged` -- `.models._patch.RequestEntityTooLargeError`, `.models._patch.IndexDocumentsBatch` -- `.models.IndexAction`, `.models.IndexingResult` -- `.indexes.SearchIndexClient` (for schema lookup in buffered sender) ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `ApiVersion` | enum | Hand-maintained list of supported API versions (`CaseInsensitiveEnumMeta`) | -| `DEFAULT_VERSION` | constant | Current default (points at an `ApiVersion` member) | +| `SearchClient` | class | Subclass of `_SearchClient`; reorders constructor to `(endpoint, index_name, credential)` | +| `SearchIndexingBufferedSender` | class | Hand-authored batching sender. Uses `threading.Timer` for auto-flush, recursive 413 splitting, retry per doc key (409/422/503), key field auto-detection | | `is_retryable_status_code()` | function | Returns True for 409, 422, 503 | -| `SearchClient` | class | Subclass of `_SearchClient`; reorders constructor to `(endpoint, index_name, credential)` and pops `audience` into `credential_scopes` | -| `SearchIndexingBufferedSender` | class | Hand-authored batching sender; `threading.Timer` auto-flush, recursive 413 splitting, per-doc retry (409/422/503), key field auto-detection | - -### `__all__` -```python -["SearchClient", "SearchItemPaged", "SearchIndexingBufferedSender", - "ApiVersion", "DEFAULT_VERSION", "RequestEntityTooLargeError", "IndexDocumentsBatch"] -``` ### After Regeneration, Verify -- [ ] `_SearchClient` base constructor signature unchanged (positional and keyword params) -- [ ] `_metadata.json` `apiVersion` is a member of `ApiVersion`; otherwise add member + update `DEFAULT_VERSION` -- [ ] Docstrings on `SearchClient` / `SearchIndexingBufferedSender` still list the correct default version -- [ ] Re-exports from `.models._patch` (`IndexDocumentsBatch`, `RequestEntityTooLargeError`) and `._operations._patch` (`SearchItemPaged`) still resolve +- [ ] `_SearchClient` base class constructor signature unchanged +- [ ] If API version changed, add new `ApiVersion` member and update default --- -## File: `azure/search/documents/_operations/_patch.py` +## File: `azure/search/documents/models/_patch.py` ### Depends On (from generated code) -- `._operations._SearchClientOperationsMixin` as `_SearchClientOperationsMixinGenerated` (base class) -- `..models._models.SearchRequest`, `..models._models.SearchDocumentsResult`, `..models._models.SearchResult` -- `..models` re-exports -- `..models._patch.RequestEntityTooLargeError` -- `azure.core.paging.ItemPaged`, `PageIterator` +- `.._generated.models.IndexDocumentsBatch` as `IndexDocumentsBatchGenerated` (base class) +- `._enums.ScoringStatistics` ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `_convert_search_result(result)` | function | Extracts `@search.score`, `@search.reranker_score`, `@search.highlights`, `@search.captions`, `@search.document_debug_info`, `@search.reranker_boosted_score` | -| `_pack_continuation_token(response, api_version)` | function | Base64-encoded JSON: `{apiVersion, nextLink, nextPageParameters}` | -| `_unpack_continuation_token(token)` | function | Decodes token → `(next_link, next_page_request)` | -| `_build_search_request(...)` | function | Constructs `SearchRequest`; pipe-delimited encoding for `answers`/`captions`/`rewrites` | -| `SearchPageIterator` | class | Custom `PageIterator`; exposes `get_facets()`, `get_count()`, `get_coverage()`, `get_answers()`, `get_debug_info()` | -| `SearchItemPaged` | class | Extends `ItemPaged`; forwards the metadata accessors | -| `_SearchClientOperationsMixin` | class | Overrides `search()`, `index_documents()` (413 recursive splitting), `upload_documents()`, `delete_documents()`, `merge_documents()`, `merge_or_upload_documents()`, `get_document_count()` convenience helpers | - -### `__all__` -```python -["_SearchClientOperationsMixin", "SearchItemPaged"] -``` +| `IndexDocumentsBatch` | class | Adds `add_upload_actions`, `add_delete_actions`, `add_merge_actions`, `add_merge_or_upload_actions`, `dequeue_actions`, `enqueue_actions`, `actions` property | +| `RequestEntityTooLargeError` | class | `HttpResponseError` subclass for 413 | ### After Regeneration, Verify -- [ ] `SearchRequest` fields still match what `_build_search_request` sets; new search parameters must be wired through -- [ ] `SearchResult` fields still match what `_convert_search_result` extracts; new `@search.*` fields must be added there -- [ ] Generated mixin methods invoked via `super()` (e.g., `_search_post`, `_index`) still exist with compatible signatures -- [ ] `SearchDocumentsResult.next_page_parameters` / `next_link` still exist (used by `_pack_continuation_token`) +- [ ] `IndexDocumentsBatchGenerated` base class still exists with compatible constructor --- -## File: `azure/search/documents/models/_patch.py` +## File: `azure/search/documents/_operations/_patch.py` ### Depends On (from generated code) -- `._models.IndexDocumentsBatch` as `IndexDocumentsBatchGenerated` (base class) -- `._models.IndexAction` -- `._enums.IndexActionType` +- `..models._models.SearchRequest` (constructed directly in `_build_search_request`) +- `..models._models.SearchResult` (field access in `_convert_search_result`) +- `azure.core.paging.ItemPaged`, `azure.core.paging.PageIterator` ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `RequestEntityTooLargeError` | class | `HttpResponseError` subclass for 413 | -| `IndexDocumentsBatch` | class | Adds `add_upload_actions`, `add_delete_actions`, `add_merge_actions`, `add_merge_or_upload_actions`, `dequeue_actions`, `enqueue_actions`, `actions` property | -| `_flatten_args(...)` | function | Flattens variadic doc args (supports both `fn([doc1, doc2])` and `fn(doc1, doc2)`) | - -Sets `IndexDocumentsBatch.__module__ = "azure.search.documents"` so Sphinx documents it at the public namespace and avoids duplicate object-description warnings. - -### `__all__` -```python -["IndexDocumentsBatch"] -``` +| `_convert_search_result(result)` | function | Extracts `@search.score`, `@search.reranker_score`, `@search.highlights`, `@search.captions`, `@search.document_debug_info`, `@search.reranker_boosted_score` | +| `_pack_continuation_token(response, api_version)` | function | Returns base64 JSON: `{apiVersion, nextLink, nextPageParameters}` | +| `_unpack_continuation_token(token)` | function | Decodes token to `(next_link, next_page_request)` | +| `_build_search_request(search_text, **kwargs)` | function | Builds `SearchRequest`. Pipe-delimited encoding for answers/captions/rewrites | +| `SearchPageIterator` | class | Custom page iterator with `get_facets()`, `get_count()`, `get_coverage()`, `get_answers()`, `get_debug_info()` | +| `SearchItemPaged` | class | Extends `ItemPaged` with same metadata accessors | +| `_SearchClientOperationsMixin` | class | Overrides: `search()`, `index_documents()` (413 splitting), `upload_documents()`, `delete_documents()`, `merge_documents()`, `merge_or_upload_documents()` | ### After Regeneration, Verify -- [ ] `IndexDocumentsBatchGenerated` base constructor still accepts `actions=` (stored under key `"value"`) -- [ ] `IndexAction` + `IndexActionType` still exist with same action type values (`upload`, `delete`, `merge`, `mergeOrUpload`) +- [ ] `SearchRequest` model fields still match what `_build_search_request` sets — new search parameters need to be wired through +- [ ] `SearchResult` model fields still match what `_convert_search_result` extracts — new `@search.*` fields need to be added +- [ ] Generated mixin methods that `_SearchClientOperationsMixin` calls (e.g., `_search_post`) still exist with same signatures --- ## File: `azure/search/documents/aio/_patch.py` ### Depends On (from generated code) -- `._client.SearchClient` as async `_SearchClient` (base class) -- `._operations._patch.AsyncSearchItemPaged` -- `..models._patch.RequestEntityTooLargeError`, `..models._patch.IndexDocumentsBatch` -- `..models.IndexAction`, `..models.IndexingResult` -- `..indexes.aio.SearchIndexClient` (for schema lookup) -- `.._patch.DEFAULT_VERSION`, `.._patch.is_retryable_status_code` +- `.._client.SearchClient` as async `_SearchClient` (base class) ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `SearchClient` | class | Async subclass, same reorder + audience kwarg pop as sync | -| `SearchIndexingBufferedSender` | class | Async batching sender; `asyncio.Task` auto-flush, `asyncio.iscoroutinefunction()` callback dispatch, same 413/retry behaviour | - -### `__all__` -```python -["SearchClient", "SearchIndexingBufferedSender"] -``` +| `SearchClient` | class | Async subclass, same reorder as sync | +| `SearchIndexingBufferedSender` | class | Async version: `asyncio.Task` instead of `threading.Timer`, `iscoroutinefunction()` callback detection | ### After Regeneration, Verify -- [ ] Same base-class checks as sync `_patch.py` -- [ ] `DEFAULT_VERSION`, `is_retryable_status_code` imports from sync `_patch.py` still resolve +- [ ] Same checks as sync `_patch.py` --- ## File: `azure/search/documents/aio/_operations/_patch.py` ### Depends On (from generated code) -- Same generated models as sync `_operations/_patch.py` +- Same models as sync `_operations/_patch.py` - `azure.core.async_paging.AsyncItemPaged`, `AsyncPageIterator` ### Imports From Sync (NOT Duplicated) @@ -137,39 +94,56 @@ Sets `IndexDocumentsBatch.__module__ = "azure.search.documents"` so Sphinx docum |--------|------|-------------| | `AsyncSearchPageIterator` | class | Async version of `SearchPageIterator` | | `AsyncSearchItemPaged` | class | Async version of `SearchItemPaged` | -| `_SearchClientOperationsMixin` | class | Async operations mixin; mirrors sync overrides | - -### `__all__` -```python -["_SearchClientOperationsMixin", "AsyncSearchItemPaged"] -``` +| `_SearchClientOperationsMixin` | class | Async operations mixin, same methods as sync | ### After Regeneration, Verify - [ ] Same checks as sync `_operations/_patch.py` -- [ ] Imports from sync `_operations/_patch.py` still resolve (don't let async drift into duplication) +- [ ] Imports from sync `_operations/_patch.py` still resolve --- -## File: `azure/search/documents/indexes/_patch.py` +## File: `azure/search/documents/indexes/models/_patch.py` ### Depends On (from generated code) -- `._client.SearchIndexClient` as `_SearchIndexClient` (base class) -- `._client.SearchIndexerClient` as `_SearchIndexerClient` (base class) +- `._models.SearchField` as `_SearchField` (base class) +- `._models.SearchIndexerDataSourceConnection` as `_SearchIndexerDataSourceConnection` (base class) +- `._models.KnowledgeBase` as `_KnowledgeBase` (base class) +- `._enums.SearchFieldDataType`, `OcrSkillLanguage`, `SplitSkillLanguage`, `TextTranslationSkillLanguage` +- Various model imports for type annotations ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `SearchIndexClient` | class | Subclass; pops `audience` kwarg into `credential_scopes=[audience + "/.default"]` | -| `SearchIndexerClient` | class | Subclass; same `audience` pattern | - -### `__all__` +| `SearchField` | class | Adds `hidden` property (inverse of `retrievable`), constructor accepts `hidden` kwarg | +| `SearchIndexerDataSourceConnection` | class | Accepts `connection_string` str or `credentials` object | +| `KnowledgeBase` | class | Deserializes `retrieval_reasoning_effort` from dict | +| `SimpleField()` | function | Builder: sets `searchable=False` explicitly | +| `SearchableField()` | function | Builder: auto-types to `String`/`Collection(String)` | +| `ComplexField()` | function | Builder: sets type to `Complex`/`Collection(Complex)` | +| `Collection()` | function | Wraps type in `"Collection(...)"` format | + +### Enum Aliases ```python -["SearchIndexClient", "SearchIndexerClient"] +# SearchFieldDataType (old camelCase -> generated UPPER_CASE) +SearchFieldDataType.String = SearchFieldDataType.STRING +SearchFieldDataType.Int32 = SearchFieldDataType.INT32 +SearchFieldDataType.Int64 = SearchFieldDataType.INT64 +SearchFieldDataType.Single = SearchFieldDataType.SINGLE +SearchFieldDataType.Double = SearchFieldDataType.DOUBLE +SearchFieldDataType.Boolean = SearchFieldDataType.BOOLEAN +SearchFieldDataType.DateTimeOffset = SearchFieldDataType.DATE_TIME_OFFSET +SearchFieldDataType.GeographyPoint = SearchFieldDataType.GEOGRAPHY_POINT +SearchFieldDataType.ComplexType = SearchFieldDataType.COMPLEX + +# Monkey-patched staticmethod +SearchFieldDataType.Collection = staticmethod(Collection) ``` ### After Regeneration, Verify -- [ ] Generated `_SearchIndexClient` / `_SearchIndexerClient` still accept `endpoint`, `credential`, and `credential_scopes` kwarg -- [ ] No conflicting `audience` kwarg introduced at the generated layer +- [ ] All right-hand-side enum members (`STRING`, `INT32`, etc.) still exist +- [ ] `_SearchField`, `_SearchIndexerDataSourceConnection`, `_KnowledgeBase` base constructors unchanged +- [ ] No new enum values that collide with Python keywords — if so, add aliases +- [ ] `SearchField.retrievable` property still exists (used by `hidden` inversion) --- @@ -182,179 +156,61 @@ Sets `IndexDocumentsBatch.__module__ = "azure.search.documents"` so Sphinx docum ### Defines | Symbol | Type | What It Does | |--------|------|-------------| -| `_convert_index_response(response)` | function | Maps `SearchIndexResponse` → `SearchIndex` (`semantic` → `semantic_search`) | +| `_convert_index_response(response)` | function | Maps `SearchIndexResponse` → `SearchIndex` (semantic → semantic_search) | **`_SearchIndexClientOperationsMixin`** wraps: - | Method | Customization | |--------|--------------| -| `delete_index(index)` | Polymorphic: str or `SearchIndex` | -| `create_or_update_index(index)` | Forwards `prefer="return=representation"`, `match_condition`, `allow_index_downtime` | -| `delete_synonym_map(synonym_map)` | Polymorphic: str or `SynonymMap` | -| `create_or_update_synonym_map(synonym_map)` | Forwards `prefer`, `match_condition` | -| `delete_alias(alias)` | Polymorphic: str or `SearchAlias` | -| `create_or_update_alias(alias)` | Forwards `prefer`, `match_condition` | -| `delete_knowledge_base(kb)` | Polymorphic: str or `KnowledgeBase` | -| `create_or_update_knowledge_base(kb)` | Forwards `prefer`, `match_condition` | -| `delete_knowledge_source(ks)` | Polymorphic: str or `KnowledgeSource` | -| `create_or_update_knowledge_source(ks)` | Forwards `prefer`, `match_condition` | +| `delete_index(index)` | Polymorphic: str or SearchIndex | +| `create_or_update_index(index)` | Adds prefer, match_condition, allow_index_downtime | +| `delete_synonym_map(synonym_map)` | Polymorphic: str or SynonymMap | +| `create_or_update_synonym_map(synonym_map)` | Adds prefer, match_condition | +| `delete_alias(alias)` | Polymorphic: str or SearchAlias | +| `create_or_update_alias(alias)` | Adds prefer, match_condition | +| `delete_knowledge_base(kb)` | Polymorphic: str or KnowledgeBase | +| `create_or_update_knowledge_base(kb)` | Adds prefer, match_condition | +| `delete_knowledge_source(ks)` | Polymorphic: str or KnowledgeSource | +| `create_or_update_knowledge_source(ks)` | Adds prefer, match_condition | | `list_indexes(*, select)` | Uses `_convert_index_response` for projections | -| `list_index_names()` | Name-only via `cls` callback | +| `list_index_names()` | Name-only projection via `cls` callback | | `get_synonym_maps(*, select)` | Returns list | **`_SearchIndexerClientOperationsMixin`** wraps: - | Method | Customization | |--------|--------------| -| `delete_data_source_connection(dsc)` | Polymorphic: str or `SearchIndexerDataSourceConnection` | -| `create_or_update_data_source_connection(dsc)` | Forwards `prefer`, `match_condition`, `skip_indexer_reset_requirement_for_cache` | -| `delete_indexer(indexer)` | Polymorphic: str or `SearchIndexer` | -| `create_or_update_indexer(indexer)` | Forwards `prefer`, `match_condition`, `skip_indexer_reset_requirement_for_cache`, `disable_cache_reprocessing_change_detection` | -| `delete_skillset(skillset)` | Polymorphic: str or `SearchIndexerSkillset` | -| `create_or_update_skillset(skillset)` | Forwards `prefer`, `match_condition`, `skip_indexer_reset_requirement_for_cache`, `disable_cache_reprocessing_change_detection` | -| `get_skillset_names()` | Name-only projection | - -### `__all__` -```python -["_SearchIndexClientOperationsMixin", "_SearchIndexerClientOperationsMixin"] -``` - -### After Regeneration, Verify -- [ ] Generated `_delete_*`, `_create_or_update_*`, `_list_*` base methods still exist with compatible signatures -- [ ] `SearchIndexResponse.semantic` still exists (used by `_convert_index_response`) -- [ ] New resource types added to the spec → add polymorphic delete + create-or-update wrappers here (and in the async mirror) -- [ ] `prefer="return=representation"` header still accepted by the service - ---- - -## File: `azure/search/documents/indexes/models/_patch.py` - -### Depends On (from generated code) -- `._models.SearchField` as `_SearchField` (base class) -- `._models.SearchIndexerDataSourceConnection` as `_SearchIndexerDataSourceConnection` (base class) -- `._models.KnowledgeBase` as `_KnowledgeBase` (base class) -- `._enums.SearchFieldDataType` as `_SearchFieldDataType` -- `._enums.LexicalAnalyzerName` -- Type-only imports for `DataChangeDetectionPolicy`, `DataDeletionDetectionPolicy`, `DataSourceCredentials`, `SearchIndexerDataContainer`, `SearchIndexerDataIdentity`, `SearchResourceEncryptionKey`, `SearchIndexerDataSourceType` - -### Defines -| Symbol | Type | What It Does | -|--------|------|-------------| -| `SearchField` | class | Adds `hidden` property (inverse of `retrievable`); accepts `hidden` kwarg | -| `SearchIndexerDataSourceConnection` | class | Three `@overload`s (credentials / connection_string / mapping) | -| `KnowledgeBase` | class | Placeholder subclass (entry point for future customizations) | -| `SimpleField(...)` | function | Builder; forces `searchable=False` | -| `SearchableField(...)` | function | Builder; auto-types to `String` / `Collection(String)` | -| `ComplexField(...)` | function | Builder; sets type to `Complex` / `Collection(Complex)` | -| `Collection(typ)` | function | Wraps type in `"Collection(...)"` string form | -| `_collection_helper(typ)` | function | Private helper used by `SearchFieldDataType.Collection` staticmethod | - -### Monkey-Patched on `SearchFieldDataType` -```python -SearchFieldDataType.Collection = staticmethod(_collection_helper) -SearchFieldDataType.String = SearchFieldDataType.STRING -SearchFieldDataType.Int32 = SearchFieldDataType.INT32 -SearchFieldDataType.Int64 = SearchFieldDataType.INT64 -SearchFieldDataType.Single = SearchFieldDataType.SINGLE -SearchFieldDataType.Double = SearchFieldDataType.DOUBLE -SearchFieldDataType.Boolean = SearchFieldDataType.BOOLEAN -SearchFieldDataType.DateTimeOffset = SearchFieldDataType.DATE_TIME_OFFSET -SearchFieldDataType.GeographyPoint = SearchFieldDataType.GEOGRAPHY_POINT -SearchFieldDataType.ComplexType = SearchFieldDataType.COMPLEX -``` - -### `__all__` -```python -["KnowledgeBase", "SearchField", "SearchFieldDataType", - "SearchIndexerDataSourceConnection", "SimpleField", "SearchableField", "ComplexField"] -``` - -### After Regeneration, Verify -- [ ] All right-hand-side UPPER_CASE enum members (`STRING`, `INT32`, `INT64`, `SINGLE`, `DOUBLE`, `BOOLEAN`, `DATE_TIME_OFFSET`, `GEOGRAPHY_POINT`, `COMPLEX`) still exist on generated `SearchFieldDataType` -- [ ] `_SearchField`, `_SearchIndexerDataSourceConnection`, `_KnowledgeBase` base constructors unchanged -- [ ] `SearchField.retrievable` still exists (the `hidden` property inverts it) -- [ ] If a new generated enum value collides with a Python keyword, add an alias - ---- - -## File: `azure/search/documents/indexes/aio/_patch.py` - -### Depends On (from generated code) -- `._client.SearchIndexClient` as async `_SearchIndexClient` (base class) -- `._client.SearchIndexerClient` as async `_SearchIndexerClient` (base class) - -### Defines -| Symbol | Type | What It Does | -|--------|------|-------------| -| `SearchIndexClient` | class | Async subclass; same `audience` → `credential_scopes` pattern | -| `SearchIndexerClient` | class | Async subclass; same pattern | - -### `__all__` -```python -["SearchIndexClient", "SearchIndexerClient"] -``` +| `delete_data_source_connection(dsc)` | Polymorphic: str or object | +| `create_or_update_data_source_connection(dsc)` | Adds prefer, match_condition, skip_indexer_reset | +| `delete_indexer(indexer)` | Polymorphic: str or SearchIndexer | +| `create_or_update_indexer(indexer)` | Adds prefer, match_condition, skip/disable cache | +| `delete_skillset(skillset)` | Polymorphic: str or SearchIndexerSkillset | +| `create_or_update_skillset(skillset)` | Adds prefer, match_condition, skip/disable cache | ### After Regeneration, Verify -- [ ] Same checks as sync `indexes/_patch.py` +- [ ] All generated `_delete_*`, `_create_or_update_*`, `_list_*` base methods still exist with compatible signatures +- [ ] `SearchIndexResponse` still has `.semantic` field (used by `_convert_index_response`) +- [ ] New resource types added to spec → add polymorphic delete/update wrappers here +- [ ] Verify prefer header value `"return=representation"` still applies --- ## File: `azure/search/documents/indexes/aio/_operations/_patch.py` -Async mirror of `indexes/_operations/_patch.py`. Same polymorphic delete / create-or-update wrappers, all `async`. +Async mirror of `indexes/_operations/_patch.py`. Same methods, all `async`. Imports `_convert_index_response` from sync — NOT duplicated. -### `__all__` -```python -["_SearchIndexClientOperationsMixin", "_SearchIndexerClientOperationsMixin"] -``` - ### After Regeneration, Verify - [ ] Same checks as sync `indexes/_operations/_patch.py` - [ ] Import of `_convert_index_response` from sync still resolves --- -## File: `azure/search/documents/knowledgebases/_patch.py` - -### Depends On (from generated code) -- `._client.KnowledgeBaseRetrievalClient` as `_KnowledgeBaseRetrievalClient` (base class) - -### Defines -| Symbol | Type | What It Does | -|--------|------|-------------| -| `KnowledgeBaseRetrievalClient` | class | Subclass; pops `audience` kwarg into `credential_scopes` | - -### `__all__` -```python -["KnowledgeBaseRetrievalClient"] -``` - -### After Regeneration, Verify -- [ ] Generated base still accepts `endpoint`, `credential`, `knowledge_base_name`, `credential_scopes` -- [ ] No conflicting `audience` kwarg introduced at the generated layer - ---- - -## File: `azure/search/documents/knowledgebases/aio/_patch.py` - -Async mirror of `knowledgebases/_patch.py`. `KnowledgeBaseRetrievalClient` subclass applying the same `audience` → `credential_scopes` pattern against the async generated base. - -### `__all__` -```python -["KnowledgeBaseRetrievalClient"] -``` - -### After Regeneration, Verify -- [ ] Same checks as sync `knowledgebases/_patch.py` - ---- - ## Empty `_patch.py` Files (No Customizations) These contain only `__all__ = []` and empty `patch_sdk()`. No action needed after regeneration. -- `azure/search/documents/knowledgebases/_operations/_patch.py` +- `azure/search/documents/indexes/_patch.py` +- `azure/search/documents/indexes/aio/_patch.py` +- `azure/search/documents/knowledgebases/_patch.py` - `azure/search/documents/knowledgebases/models/_patch.py` -- `azure/search/documents/knowledgebases/aio/_operations/_patch.py` +- `azure/search/documents/knowledgebases/aio/_patch.py`