diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 512855e91ac8..38c56166564d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -235,7 +235,7 @@ /sdk/durabletask/ @berndverst @cgillum @kaibocai @philliphoff @RyanLettieri @torosent # PRLabel: %EngSys -/sdk/template/ @benbp @scbedd @danieljurek @weshaggard +/sdk/template/ @benbp @mikeharder @danieljurek # PRLabel: %Evaluation /sdk/evaluation/ @Azure/azure-sdk-write-evaluation @@ -810,25 +810,25 @@ ########### # Eng Sys and other ########### -/eng/ @scbedd @danieljurek @weshaggard @benbp +/eng/ @danieljurek @mikeharder @benbp /eng/common/ @Azure/azure-sdk-eng -/eng/tools/ @scbedd @danieljurek @mccoyp +/eng/tools/ @danieljurek @mccoyp /.github/workflows/ @Azure/azure-sdk-eng /.github/workflows/typespec-python-regenerate.yml @Azure/azure-sdk-eng @tadelesh @msyyc @iscai-msft @ChenxiJiang333 /.github/CODEOWNERS @lmazuel @kashifkhan @Azure/azure-sdk-eng /.github/copilot-instructions.md @l0lawrence @praveenkuttappan @maririos /.github/prompts/ @mccoyp @l0lawrence @benbp /.github/skills/ @mccoyp @l0lawrence @benbp -/sdk/pullrequest.yml @scbedd @danieljurek @weshaggard @benbp +/sdk/pullrequest.yml @danieljurek @mikeharder @benbp -/.config/1espt/ @benbp @weshaggard -/.devcontainer/ @scbedd @danieljurek @mccoyp @benbp @weshaggard -/.vscode/ @scbedd @danieljurek @mccoyp @benbp @weshaggard +/.config/1espt/ @benbp @mikeharder +/.devcontainer/ @danieljurek @mccoyp @benbp @mikeharder +/.vscode/ @danieljurek @mccoyp @benbp @mikeharder -/scripts/ @scbedd @danieljurek @mccoyp +/scripts/ @danieljurek @mccoyp /scripts/breaking_changes_checker/ @catalinaperalta -/doc/ @scbedd @danieljurek @Azure/azure-python-sdk -/conda/ @scbedd @danieljurek @lmazuel +/doc/ @danieljurek @Azure/azure-python-sdk +/conda/ @danieljurek @xiangyan99 @lmazuel /shared_requirements.txt @azure/azure-sdk-eng @kashifkhan @xirzec @@ -836,7 +836,7 @@ /eng/pipelines/templates/jobs/tests-nightly-python.yml @lmazuel @mccoyp /eng/pipelines/aggregate-reports.yml @lmazuel @mccoyp /eng/common/pipelines/codeowners-linter.yml @lmazuel @mccoyp -/eng/pipelines/docindex.yml @danieljurek @scbedd @weshaggard @benbp +/eng/pipelines/docindex.yml @danieljurek @benbp # Add approvers for typespec-python emitter version updates /eng/emitter-package.json @mccoyp @catalinaperalta @iscai-msft @msyyc @ChenxiJiang333 @@ -845,8 +845,8 @@ # TypeSpec Python generated tests and regeneration workflow /eng/tools/emitter/ @tadelesh @msyyc @iscai-msft @lmazuel @lirenhe @ChenxiJiang333 -/pylintrc @l0lawrence @scbedd @danieljurek @mccoyp -/sdk/**/ci.yml @msyyc @lmazuel @scbedd @danieljurek +/pylintrc @l0lawrence @danieljurek @mccoyp +/sdk/**/ci.yml @msyyc @lmazuel @danieljurek # Add Johnathan Walker as code owner for Event Hubs SDK /sdk/eventhub/azure-eventhub/* @j7nw4r @SwayGom @sagar0207 diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 000000000000..07a7d5c18898 --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,3 @@ +self-hosted-runner: + labels: + - 1ES.Pool=azsdk-pool-github-runners diff --git a/.github/matchers/actionlint.json b/.github/matchers/actionlint.json new file mode 100644 index 000000000000..4613e1617bfe --- /dev/null +++ b/.github/matchers/actionlint.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "actionlint", + "pattern": [ + { + "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$", + "file": 1, + "line": 2, + "column": 3, + "message": 4, + "code": 5 + } + ] + } + ] +} diff --git a/.github/shared/README.md b/.github/shared/README.md new file mode 100644 index 000000000000..64add81cf5be --- /dev/null +++ b/.github/shared/README.md @@ -0,0 +1,3 @@ +# `.github/shared` + +Copied from https://github.com/Azure/azure-rest-api-specs/tree/main/.github/shared diff --git a/.github/shared/src/cache.js b/.github/shared/src/cache.js new file mode 100644 index 000000000000..444a7ce2f932 --- /dev/null +++ b/.github/shared/src/cache.js @@ -0,0 +1,61 @@ +/** + * Caches values in memory with a single key of any type. + * + * @template K, V + */ +export class KeyedCache { + /** @type {Map} */ + #map = new Map(); + + /** + * Returns cached value, initializing if necessary + * + * @param {K} key + * @param {() => V} factory + * @returns {V} cached value + * + * @example + * const result = cache.getOrCreate(42, async () => await doWork(42)); + */ + getOrCreate(key, factory) { + let value = this.#map.get(key); + + if (value === undefined) { + value = factory(); + this.#map.set(key, value); + } + + return value; + } +} + +/** + * Caches values in memory with an ordered pair of keys of any types. + * + * @template K1, K2, V + */ +export class KeyedPairCache { + // Two-layer nested cache + /** @type {KeyedCache>} */ + #cache1 = new KeyedCache(); + + /** + * Returns cached value, initializing if necessary. + * Keys are ordered, so (key1, key2) != (key2, key1). + * + * @param {K1} key1 + * @param {K2} key2 + * @param {() => V} factory + * @returns {V} cached value + * + * @example + * const result = cache.getOrCreate(42, 7, async () => await doWork(42, 7)); + */ + getOrCreate(key1, key2, factory) { + // key1 => cache for the next layer + const cache2 = this.#cache1.getOrCreate(key1, () => new KeyedCache()); + + // key2 => final value + return cache2.getOrCreate(key2, factory); + } +} diff --git a/.github/shared/src/exec.js b/.github/shared/src/exec.js new file mode 100644 index 000000000000..1edc25700919 --- /dev/null +++ b/.github/shared/src/exec.js @@ -0,0 +1,152 @@ +import child_process from "child_process"; +import { dirname, join } from "path"; +import { promisify } from "util"; +const execFileImpl = promisify(child_process.execFile); + +/** + * @typedef {Object} ExecOptions + * @property {string} [cwd] Current working directory. Default: process.cwd(). + * @property {import('./logger.js').ILogger} [logger] + * @property {boolean} [logOutput] Log captured stdout/stderr at info level. Default: false. + * @property {number} [maxBuffer] Max bytes allowed on stdout or stderr. Default: 16 * 1024 * 1024. + */ + +/** + * @typedef {Object} NpmPrefixOptions + * @property {string} [prefix] Prefix to pass to npm via "--prefix". + */ + +/** + * @typedef {ExecOptions & NpmPrefixOptions} ExecNpmOptions + */ + +/** + * @typedef {Object} ExecResult + * @property {string} stdout + * @property {string} stderr + */ + +/** + * @typedef {Error & { stdout?: string, stderr?: string, code?: number }} ExecError + */ + +/** + * Checks whether an unknown error object is an ExecError. + * @param {unknown} error + * @returns {error is ExecError} + */ +export function isExecError(error) { + if (!(error instanceof Error)) return false; + + const e = /** @type {ExecError} */ (error); + return typeof e.stdout === "string" || typeof e.stderr === "string"; +} + +/** + * Wraps `child_process.execFile()`, adding logging and a larger default maxBuffer. + * + * @param {string} file + * @param {string[]} [args] + * @param {ExecOptions} [options] + * @returns {Promise} + * @throws {ExecError} + */ +export async function execFile(file, args, options = {}) { + const { + cwd, + logger, + logOutput = false, + // Node default is 1024 * 1024, which is too small for some git commands returning many entities or large file content. + // To support "git show", should be larger than the largest swagger file in the repo (2.5 MB as of 2/28/2025). + maxBuffer = 16 * 1024 * 1024, + } = options; + + logger?.info(`execFile("${file}", ${JSON.stringify(args)})`); + + try { + // execFile(file, args) is more secure than exec(cmd), since the latter is vulnerable to shell injection + const result = await execFileImpl(file, args, { + cwd, + maxBuffer, + }); + + logger?.debug(`stdout: '${result.stdout}'`); + logger?.debug(`stderr: '${result.stderr}'`); + if (logOutput) { + if (result.stdout) { + logger?.info(result.stdout.trimEnd()); + } + if (result.stderr) { + logger?.info(result.stderr.trimEnd()); + } + } + + return result; + } catch (error) { + /* v8 ignore next */ + logger?.debug(`error: '${JSON.stringify(error)}'`); + if (logOutput && isExecError(error)) { + if (error.stdout) { + logger?.info(error.stdout.trimEnd()); + } + if (error.stderr) { + logger?.info(error.stderr.trimEnd()); + } + } + + throw error; + } +} + +/** + * Calls `execFile()` with appropriate arguments to run `npm` on all platforms + * + * @param {string[]} args + * @param {ExecNpmOptions} [options] + * @returns {Promise} + * @throws {ExecError} + */ +export async function execNpm(args, options = {}) { + const { prefix } = options; + + // Exclude platform-specific code from coverage + /* v8 ignore start */ + const { file, defaultArgs } = + process.platform === "win32" + ? { + // Only way I could find to run "npm" on Windows, without using the shell (e.g. "cmd /c npm ...") + // + // "node.exe", ["--", "npm-cli.js", ...args] + // + // The "--" MUST come BEFORE "npm-cli.js", to ensure args are sent to the script unchanged. + // If the "--" comes after "npm-cli.js", the args sent to the script will be ["--", ...args], + // which is NOT equivalent, and can break if args itself contains another "--". + + // example: "C:\Program Files\nodejs\node.exe" + file: process.execPath, + + // example: "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js" + defaultArgs: [ + "--", + join(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js"), + ], + } + : { file: "npm", defaultArgs: [] }; + /* v8 ignore stop */ + + const prefixArgs = prefix ? ["--prefix", prefix] : []; + + return await execFile(file, [...defaultArgs, ...prefixArgs, ...args], options); +} + +/** + * Calls `execNpm()` with arguments ["exec", "--no", "--"] prepended. + * + * @param {string[]} args + * @param {ExecNpmOptions} [options] + * @returns {Promise} + * @throws {ExecError} + */ +export async function execNpmExec(args, options = {}) { + return await execNpm(["exec", "--no", "--", ...args], options); +} diff --git a/.github/shared/src/logger.js b/.github/shared/src/logger.js new file mode 100644 index 000000000000..dc43a06d9782 --- /dev/null +++ b/.github/shared/src/logger.js @@ -0,0 +1,64 @@ +/** + * @typedef {Object} ILogger + * @property {(message:string) => void} debug + * @property {(message:string) => void} error + * @property {(message:string) => void} info + * @property {(message:string) => void} warning + * @property {() => boolean} isDebug + */ + +/** + * @implements {ILogger} + */ +export class ConsoleLogger { + /** @type {boolean} */ + #isDebug; + + /** + * @param {boolean} [isDebug] - If true, debug logs will be printed. Default: false. + */ + constructor(isDebug = false) { + this.#isDebug = isDebug; + } + + /** + * @param {string} message + */ + debug(message) { + if (this.isDebug()) { + console.debug(message); + } + } + + /** + * @param {string} message + */ + error(message) { + console.error(message); + } + + /** + * @param {string} message + */ + info(message) { + console.log(message); + } + + /** + * @returns {boolean} + */ + isDebug() { + return this.#isDebug; + } + + /** + * @param {string} message + */ + warning(message) { + console.warn(message); + } +} + +// Singleton loggers +export const defaultLogger = new ConsoleLogger(); +export const debugLogger = new ConsoleLogger(/*isDebug*/ true); diff --git a/.github/shared/src/path.js b/.github/shared/src/path.js new file mode 100644 index 000000000000..929116c931b6 --- /dev/null +++ b/.github/shared/src/path.js @@ -0,0 +1,104 @@ +import { basename, dirname, resolve } from "path"; + +import { KeyedCache, KeyedPairCache } from "./cache.js"; + +/** @type {KeyedCache} */ +const resolveCache = new KeyedCache(); + +/** @type {KeyedPairCache} */ +const resolvePairCache = new KeyedPairCache(); + +/** + * + * @param {string} path Absolute or relative path + * @param {string} segment File or folder + * @returns {boolean} True if resolved path contains segment + * + * @example + * includesSegment("stable/2025-01-01/examples/foo.json", "examples") + * // -> true + */ +export function includesSegment(path, segment) { + return untilLastSegment(path, segment) !== ""; +} + +/** + * Wraps `path.resolve(path)` with a cache to improve performance + * + * @param {string} path + * @returns {string} + */ +export function resolveCached(path) { + return resolveCache.getOrCreate(path, () => resolve(path)); +} + +/** + * Wraps `path.resolve(from, to)` with a cache to improve performance + +* @param {string} from + * @param {string} to + * @returns {string} + */ +export function resolvePairCached(from, to) { + return resolvePairCache.getOrCreate(from, to, () => resolve(from, to)); +} + +/** + * @param {string} path Absolute or relative path + * @param {string} segment File or folder + * @returns {string} Portion of resolved path up to (and including) the last occurrence of segment + * + * @example + * untilLastSegment("stable/2025-01-01/examples/foo.json", "examples") + * // -> "{cwd}/stable/2025-01-01/examples" + */ +export function untilLastSegment(path, segment) { + // Shares code with `untilLastSegmentWithParent()`, but not worth refactoring yet + + let current = resolveCached(path); + + while (true) { + const parent = dirname(current); + + if (basename(current) === segment) { + // Found the target folder. Return it. + return current; + } else if (parent === current) { + // Reached the filesystem root (folder not found). Return empty string. + return ""; + } else { + // Keep walking upward + current = parent; + } + } +} + +/** + * @param {string} path Absolute or relative path + * @param {string} segment File or folder + * @returns {string} Portion of resolved path up to (and including) the last segment with the specified parent + * + * @example + * untilLastSegmentWithParent("specification/foo/data-plane/stable/2025-01-01/foo.json", "specification") + * // -> "{cwd}/specification/foo" + */ +export function untilLastSegmentWithParent(path, segment) { + // Shares code with `untilLastSegment()`, but not worth refactoring yet + + let current = resolveCached(path); + + while (true) { + const parent = dirname(current); + + if (basename(parent) === segment) { + // Found the target parent. Return current; + return current; + } else if (parent === current) { + // Reached the filesystem root (folder not found). Return empty string. + return ""; + } else { + // Keep walking upward + current = parent; + } + } +} diff --git a/.github/skills/azsdk-common-apiview-feedback-resolution/SKILL.md b/.github/skills/azsdk-common-apiview-feedback-resolution/SKILL.md index da94bafab13c..65c6785e6000 100644 --- a/.github/skills/azsdk-common-apiview-feedback-resolution/SKILL.md +++ b/.github/skills/azsdk-common-apiview-feedback-resolution/SKILL.md @@ -4,13 +4,25 @@ license: MIT metadata: version: "1.0.0" distribution: shared -description: "Analyze and resolve APIView review feedback on Azure SDK PRs. **UTILITY SKILL**. USE FOR: APIView comments, API review feedback, SDK API surface changes. DO NOT USE FOR: general code review, non-APIView feedback. INVOKES: azure-sdk-mcp:azsdk_apiview_get_comments, azure-sdk-mcp:azsdk_customized_code_update." +description: "Analyze and resolve APIView review feedback on Azure SDK PRs. **UTILITY SKILL**. USE FOR: APIView comments, API review feedback, SDK API surface changes. DO NOT USE FOR: general code review, non-APIView feedback. INVOKES: azure-sdk-mcp:azsdk_apiview_get_comments, azure-sdk-mcp:azsdk_customized_code_update, azure-sdk-mcp:azsdk_typespec_delegate_apiview_feedback, azure-sdk-mcp:azsdk_run_typespec_validation, azure-sdk-mcp:azsdk_package_generate_code." compatibility: "azure-sdk-mcp server, SDK pull request with APIView review link" --- # APIView Feedback Resolution -**Prerequisites:** azure-sdk-mcp server required; no CLI fallback. Without MCP, this skill cannot retrieve APIView comments or apply TypeSpec changes. Connect the `azure-sdk-mcp` server before use. +This skill analyzes and resolves APIView review feedback on Azure SDK pull requests by retrieving reviewer comments, categorizing them, and applying TypeSpec or customization updates that bring the SDK API surface into compliance before re-review. + +## Triggers + +USE FOR: APIView comments, API review feedback, SDK API surface changes +WHEN: "resolve APIView comments", "address API review feedback", "update SDK API surface after APIView" +DO NOT USE FOR: general code review, non-APIView feedback + +## Rules + +- Requires the `azure-sdk-mcp` server; there is no CLI fallback for retrieving APIView comments or applying TypeSpec changes. +- Retrieve and categorize APIView comments before making changes so fixes map to reviewer intent. +- Validate, regenerate, build, and test after applying fixes before requesting re-review. ## MCP Tools diff --git a/.github/skills/azsdk-common-generate-sdk-locally/SKILL.md b/.github/skills/azsdk-common-generate-sdk-locally/SKILL.md index 7184ed59f6dd..6e5ccbe572b5 100644 --- a/.github/skills/azsdk-common-generate-sdk-locally/SKILL.md +++ b/.github/skills/azsdk-common-generate-sdk-locally/SKILL.md @@ -10,6 +10,20 @@ compatibility: "azure-sdk-mcp server, local azure-sdk-for-{language} clone, lang # Generate SDK Locally +This skill generates, builds, and tests Azure SDKs locally from TypeSpec with automatic customization support, covering the end-to-end workflow for fixing generation issues, applying SDK-specific updates, and refreshing package metadata when needed. + +## Triggers + +USE FOR: generate, build, and test Azure SDKs locally from TypeSpec with automatic customization; update changelog; fix SDK build errors; fix breaking changes; resolve SDK generation errors; customize TypeSpec; rename SDK client or model; hide operation from SDK; fix analyzer errors; resolve customization drift; create subclient; update metadata; update version +WHEN: "generate SDK locally", "build SDK", "run SDK tests", "update changelog", "fix SDK build errors", "fix breaking changes", "resolve SDK generation errors", "customize TypeSpec", "rename SDK client", "rename SDK model", "hide operation from SDK", "fix analyzer errors", "resolve customization drift", "create subclient", "update metadata", "update version" +DO NOT USE FOR: publishing to package registries, CI pipeline configuration, API design review + +## Rules + +- Requires the `azure-sdk-mcp` server for the MCP workflow; without MCP, use `npm exec --prefix eng/common/tsp-client -- tsp-client` CLI. +- Verify the target language repo and the correct TypeSpec configuration file before generation. +- After generation or customization, run the check and test steps before updating metadata or finalizing changes. + ## MCP Tools | Tool | Purpose | diff --git a/.github/skills/azsdk-common-pipeline-troubleshooting/SKILL.md b/.github/skills/azsdk-common-pipeline-troubleshooting/SKILL.md index 8606348918a2..96968ec100cc 100644 --- a/.github/skills/azsdk-common-pipeline-troubleshooting/SKILL.md +++ b/.github/skills/azsdk-common-pipeline-troubleshooting/SKILL.md @@ -4,12 +4,26 @@ license: MIT metadata: version: "1.0.0" distribution: shared -description: "Diagnose and resolve failures in Azure SDK CI and generation pipelines. **UTILITY SKILL**. USE FOR: \"pipeline failed\", \"build failure\", \"CI check failing\", \"SDK generation error\", \"reproduce pipeline locally\", \"debug SDK pipeline\". DO NOT USE FOR: local build issues without pipeline context, API design review, SDK publishing. INVOKES: azure-sdk-mcp:azsdk_analyze_pipeline, azure-sdk-mcp:azsdk_package_build_code, azure-sdk-mcp:azsdk_package_run_check." +description: "Diagnose and resolve failures in Azure SDK CI and generation pipelines. **UTILITY SKILL**. USE FOR: \"pipeline failed\", \"build failure\", \"CI check failing\", \"SDK generation error\", \"reproduce pipeline locally\", \"debug SDK pipeline\". DO NOT USE FOR: local build issues without pipeline context, API design review, SDK publishing. INVOKES: azure-sdk-mcp:azsdk_analyze_pipeline, azure-sdk-mcp:azsdk_verify_setup, azure-sdk-mcp:azsdk_package_build_code, azure-sdk-mcp:azsdk_package_run_check, azure-sdk-mcp:azsdk_package_pack." compatibility: "azure-sdk-mcp server, Azure DevOps pipeline build ID" --- # Pipeline Troubleshooting +This skill diagnoses and resolves failures in Azure SDK CI and generation pipelines by analyzing pipeline runs, reproducing issues locally, and applying targeted fixes for build, validation, or TypeSpec problems before verifying the rerun. + +## Triggers + +USE FOR: pipeline failed, build failure, CI check failing, SDK generation error, reproduce pipeline locally, debug SDK pipeline +WHEN: "pipeline failed", "build failure", "CI check failing", "SDK generation error", "reproduce pipeline locally", "debug SDK pipeline" +DO NOT USE FOR: local build issues without pipeline context, API design review, SDK publishing + +## Rules + +- Requires the `azure-sdk-mcp` server; without MCP, inspect logs in the Azure DevOps UI. +- Start with the pipeline build ID and run pipeline analysis before attempting local reproduction. +- Verify the local environment before running build or check commands to reproduce the failure. + ## MCP Tools | Tool | Purpose | diff --git a/.github/skills/azsdk-common-prepare-release-plan/SKILL.md b/.github/skills/azsdk-common-prepare-release-plan/SKILL.md index 07a170cdefbb..a34341e69e37 100644 --- a/.github/skills/azsdk-common-prepare-release-plan/SKILL.md +++ b/.github/skills/azsdk-common-prepare-release-plan/SKILL.md @@ -4,14 +4,25 @@ license: MIT metadata: version: "1.0.0" distribution: shared -description: 'Create, get, update, abandon, and link SDK PRs to release plan work items for Azure SDK releases. **UTILITY SKILL**. USE FOR: "create release plan", "get release plan", "update release plan", "update API spec in release plan", "update SDK details in release plan", "abandon release plan", "link SDK PR to plan", "namespace approval", "check release plan status". DO NOT USE FOR: SDK code generation, pipeline troubleshooting, API review feedback. INVOKES: azure-sdk-mcp:azsdk_create_release_plan, azure-sdk-mcp:azsdk_get_release_plan, azure-sdk-mcp:azsdk_get_release_plan_for_spec_pr, azure-sdk-mcp:azsdk_update_release_plan, azure-sdk-mcp:azsdk_update_api_spec_pull_request_in_release_plan, azure-sdk-mcp:azsdk_update_sdk_details_in_release_plan, azure-sdk-mcp:azsdk_abandon_release_plan, azure-sdk-mcp:azsdk_link_sdk_pull_request_to_release_plan.' -compatibility: - requires: "azure-sdk-mcp server, API spec PR in Azure/azure-rest-api-specs" +description: 'Create, get, update, abandon, and link SDK PRs to release plan work items for Azure SDK releases. **UTILITY SKILL**. USE FOR: "create release plan", "get release plan", "update release plan", "update API spec in release plan", "update SDK details in release plan", "abandon release plan", "link SDK PR to plan", "namespace approval", "check release plan status". DO NOT USE FOR: SDK code generation, pipeline troubleshooting, API review feedback. INVOKES: azure-sdk-mcp:azsdk_create_release_plan, azure-sdk-mcp:azsdk_get_release_plan, azure-sdk-mcp:azsdk_get_release_plan_for_spec_pr, azure-sdk-mcp:azsdk_update_release_plan, azure-sdk-mcp:azsdk_update_api_spec_pull_request_in_release_plan, azure-sdk-mcp:azsdk_update_sdk_details_in_release_plan, azure-sdk-mcp:azsdk_abandon_release_plan, azure-sdk-mcp:azsdk_link_sdk_pull_request_to_release_plan, azure-sdk-mcp:azsdk_link_namespace_approval_issue.' +compatibility: "azure-sdk-mcp server, API spec PR in Azure/azure-rest-api-specs" --- # Prepare Release Plan -> **CRITICAL**: Do not display Azure DevOps work item URLs. Only provide Release Plan Link and Release Plan ID to the user. +This skill creates, gets, updates, abandons, and links SDK PRs to release plan work items for Azure SDK releases, helping gather required release data, validate spec inputs, and link related approvals or SDK pull requests without exposing internal work item URLs. + +## Triggers + +USE FOR: create release plan, get release plan, update release plan, update API spec in release plan, update SDK details in release plan, abandon release plan, link SDK PR to plan, namespace approval, check release plan status +WHEN: "create release plan", "get release plan", "update release plan", "abandon release plan", "link SDK PR to plan", "namespace approval", "check release plan status" +DO NOT USE FOR: SDK code generation, pipeline troubleshooting, API review feedback + +## Rules + +- Do not display Azure DevOps work item URLs; only provide the Release Plan Link and ID. +- Require an API spec PR link or a TypeSpec project path before creating or updating a plan. +- Validate that the spec PR repository matches the requested API release type before creation. ## MCP Tools diff --git a/.github/skills/azsdk-common-sdk-release/SKILL.md b/.github/skills/azsdk-common-sdk-release/SKILL.md index 521d0ded09bf..10fef179d696 100644 --- a/.github/skills/azsdk-common-sdk-release/SKILL.md +++ b/.github/skills/azsdk-common-sdk-release/SKILL.md @@ -10,6 +10,20 @@ compatibility: "azure-sdk-mcp server, SDK package merged on release branch. Supp # SDK Release +This skill checks release readiness and triggers the Azure SDK release pipeline for packages that are already prepared, guiding the user through prerequisite validation, readiness review, and the final pipeline launch with required approval steps. + +## Triggers + +USE FOR: release SDK, trigger release, check release readiness, release pipeline, publish package, ship SDK +WHEN: "release SDK", "trigger release", "check release readiness", "release pipeline", "publish package", "ship SDK" +DO NOT USE FOR: SDK development, code generation, pipeline debugging, release plan creation + +## Rules + +- Requires the `azure-sdk-mcp` server; there is no CLI fallback for the release workflow. +- Collect `packageName` and `language`, then run the readiness check before attempting to trigger release. +- If release is triggered, show the pipeline link and remind the user that the release stage still requires approval. + ## MCP Tools | Tool | Purpose | diff --git a/.github/skills/create-api-review-pr/SKILL.md b/.github/skills/create-api-review-pr/SKILL.md new file mode 100644 index 000000000000..f0b999ed3c8e --- /dev/null +++ b/.github/skills/create-api-review-pr/SKILL.md @@ -0,0 +1,92 @@ +--- +name: create-api-review-pr +description: Create a GitHub PR for API review by comparing a baseline API surface against a target tag or branch. Use this when the user wants to create an API review PR, compare API changes between versions, or review API surface differences for a package. +--- + +# Create API Review PR + +Creates a dedicated API review PR that shows the diff between a baseline release and a target tag or branch's API surface using `scripts/api_md_workflow/create_api_review_pr.py`. + +## Unsupported Requests + +If the user asks to create an API review PR for a new package, explain that new packages do not use API review PRs and stop. Do not gather script inputs or run `create_api_review_pr.py` for new packages. + +## Prerequisites + +1. The user must have `gh` CLI installed and authenticated (`gh auth login`), or `GITHUB_TOKEN`/`GH_TOKEN` set with permission to create and update pull requests in this repository. +2. The working tree must be clean (no uncommitted changes). +3. Python 3.10 or later must be available. +4. `azpysdk` must be installed (`pip install -e ./eng/tools/azure-sdk-tools`). +5. ApiView stub generator dependencies must be installed (`pip install -r ./eng/apiview_reqs.txt`). + +## Information to Gather + +Ask the user for the following using `vscode_askQuestions`: + +### 1. Package Name (required) +The Azure SDK package name (e.g. `azure-storage-blob`, `azure-ai-projects`, `azure-servicebus`, `azure-planetarycomputer`). + +### 2. Baseline (required) +The release tag to use as the baseline for comparison. Tags follow the format `_` (e.g. `azure-storage-blob_12.29.0`). + +- If the user provides a package name and version separately, construct the tag as `_`. + +### 3. Target (optional) +The branch or PR to generate the "current" API surface from. Can be: +- A package release tag (e.g. `azure-storage-blob_12.30.0`) — used directly as a tag ref +- A branch name (e.g. `main`, `feature-branch`) — fetched from `origin` +- An `owner:branch` reference (e.g. `someone:their-branch`) — fetched from the fork +- If omitted, defaults to `origin/main` + +## Validation Steps + +Before running the script: + +1. **Validate the package exists**: Confirm a directory matching `sdk/*/` exists with a `pyproject.toml` or `setup.py`. +2. **Validate the baseline tag**: Run `git tag -l ""` to confirm the tag exists. If the user provided a version like `12.29.0`, construct the full tag as `_` and validate that. +3. **Validate the target tag when applicable**: If the user provided a target version or tag, construct or validate the full tag as `_` and run `git tag -l ""`. +4. **Confirm the working tree is clean**: Run `git status --porcelain` and warn if there are uncommitted changes. + +## Execution + +This is a long-running operation. The script may take several minutes because it generates API surfaces for both the baseline and target, creates or reuses review branches, pushes branches, and then opens the draft PR. Do not treat quiet terminal periods during `apistub` generation as failure unless the command exits, prints an error, or waits for input. + +If `create_api_review_pr.py` fails while running this skill, do not patch the script, modify package files, retry with workaround edits, or try to manually complete branch/PR creation. Stop the workflow, report the failure clearly, include the relevant error details, and suggest practical next steps. + +If the script reports that there are no API differences, relay that message to the user and stop. Do not create branches or a PR manually. + +Run the following command from the repository root: + +```bash +python scripts/api_md_workflow/create_api_review_pr.py --package-name --base [--target ] +``` + +### Examples + +**Standard review (comparing a release tag to a PR branch):** +```bash +python scripts/api_md_workflow/create_api_review_pr.py --package-name azure-storage-blob --base azure-storage-blob_12.29.0 --target someone:feature-branch +``` + +**Release-to-release review (comparing two package tags):** +```bash +python scripts/api_md_workflow/create_api_review_pr.py --package-name azure-ai-projects --base azure-ai-projects_2.1.0 --target azure-ai-projects_2.2.0 +``` + +**Review against main (no target specified):** +```bash +python scripts/api_md_workflow/create_api_review_pr.py --package-name azure-cosmos --base azure-cosmos_4.14.0 +``` + +## Post-Execution + +The script will: +1. Generate `api.md` for both baseline and target +2. Push `apireview/base__` and `apireview/review__` branches +3. Open a draft PR (or print a compare URL if `gh pr create` fails) + +During execution, report progress at major phases: baseline generation, target generation, branch creation or reuse, branch push, and PR creation. If the terminal is quiet, check whether the process is still running before assuming it is hung. + +When the target is a tag, the PR body labels it as `Target tag`. Branch and fork targets are labeled as `Working branch`. + +Report the PR URL to the user when complete. diff --git a/.github/skills/generate-api-markdown/SKILL.md b/.github/skills/generate-api-markdown/SKILL.md index f3f96e32c839..dbf28fcbb551 100644 --- a/.github/skills/generate-api-markdown/SKILL.md +++ b/.github/skills/generate-api-markdown/SKILL.md @@ -7,7 +7,7 @@ description: Generate an API markdown file and token file using ApiView. Use thi ## Prerequisites -1. Activate your virtual environment with a Python version that is strictly less than the version limit specified in `eng/tools/azure-sdk-tools/azpysdk/apistub.py`. +1. Activate your virtual environment. 2. Install the required dependencies: ```bash cd @@ -19,5 +19,6 @@ description: Generate an API markdown file and token file using ApiView. Use thi 1. Navigate to the desired package directory 2. Run the command: ```bash - azpysdk apistub --md . + azpysdk apistub --md --extract-metadata --install-deps --dest-dir . . + ``` 3. The command outputs the location of the generated markdown file. Provide this file to the user for review. \ No newline at end of file diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 000000000000..4f3c0386c11d --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,35 @@ +name: Actionlint + +on: + push: + branches: + - main + paths: + - .github/** + pull_request: + paths: + - .github/** + workflow_dispatch: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + sparse-checkout: | + .github + + # Content copied from https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/.github/actionlint-matcher.json + - name: Add ActionLint Problem Matcher + run: echo "::add-matcher::.github/matchers/actionlint.json" + + - name: Lint workflows + uses: docker://rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 + with: + args: -color -verbose diff --git a/.github/workflows/api-consistency.yml b/.github/workflows/api-consistency.yml new file mode 100644 index 000000000000..e57e8c539b58 --- /dev/null +++ b/.github/workflows/api-consistency.yml @@ -0,0 +1,61 @@ +name: API.md Consistency + +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited + # re-run if PR changes to/from draft + - converted_to_draft + - ready_for_review + paths: + - "sdk/**" + +permissions: + contents: read + +jobs: + consistency: + if: ${{ !github.event.pull_request.draft }} + runs-on: ubuntu-latest + outputs: + changed_count: ${{ steps.consistency.outputs.changed_count || '0' }} + mismatch_count: ${{ steps.consistency.outputs.mismatch_count || '0' }} + missing_count: ${{ steps.consistency.outputs.missing_count || '0' }} + issue_count: ${{ steps.consistency.outputs.issue_count || '0' }} + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 2 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install azpysdk + shell: bash + run: | + python -m pip install --upgrade pip + python -m pip install -r eng/apiview_reqs.txt --index-url=https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/ + python -m pip install ./eng/tools/azure-sdk-tools + + - name: Run API.md consistency checks + id: consistency + uses: actions/github-script@v8 + env: + API_MD_BASE_REF: ${{ github.event.pull_request.base.ref }} + API_MD_CHANGED_FILE: .artifacts/changed_package_dirs.txt + API_MD_PACKAGES_FILE: .artifacts/affected_package_dirs.txt + API_MD_MISMATCHES_FILE: .artifacts/mismatched_api_files.txt + API_MD_MISSING_FILE: .artifacts/missing_api_files.txt + with: + script: | + const { default: apiMdConsistency } = + await import('${{ github.workspace }}/.github/workflows/src/api-md-consistency/api-md-consistency.js'); + return await apiMdConsistency({ github, context, core }); diff --git a/.github/workflows/api-md-workflow-tests.yml b/.github/workflows/api-md-workflow-tests.yml new file mode 100644 index 000000000000..12c4b34515c9 --- /dev/null +++ b/.github/workflows/api-md-workflow-tests.yml @@ -0,0 +1,26 @@ +name: API.md Workflow Unit Tests + +on: + workflow_dispatch: + pull_request: + branches: [ main ] + paths: + - "scripts/api_md_workflow/**" + - ".github/workflows/api-md-workflow-tests.yml" + +permissions: + contents: read + +jobs: + unit-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Run API.md workflow unit tests + run: python -m unittest discover scripts/api_md_workflow "*_test.py" diff --git a/.github/workflows/azure-sdk-tools.yml b/.github/workflows/azure-sdk-tools.yml index a308ba49501e..e4a132167652 100644 --- a/.github/workflows/azure-sdk-tools.yml +++ b/.github/workflows/azure-sdk-tools.yml @@ -12,10 +12,10 @@ jobs: build-and-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Set up Python 3.13 - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: 3.13 @@ -24,7 +24,7 @@ jobs: - name: Install azure-sdk-tools run: | - python -m pip install -e eng/tools/azure-sdk-tools[ghtools,conda] + python -m pip install -e 'eng/tools/azure-sdk-tools[ghtools,conda]' python -m pip freeze shell: bash @@ -37,10 +37,10 @@ jobs: static-analysis: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Set up Python 3.13 - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: 3.13 @@ -49,7 +49,7 @@ jobs: - name: Install azure-sdk-tools run: | - python -m pip install -e eng/tools/azure-sdk-tools[ghtools,conda] + python -m pip install -e 'eng/tools/azure-sdk-tools[ghtools,conda]' python -m pip install black==24.4.0 python -m pip freeze shell: bash @@ -62,10 +62,10 @@ jobs: verify-azpysdk-checks: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Set up Python 3.13 - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: 3.13 @@ -79,7 +79,7 @@ jobs: - name: Install azure-sdk-tools on in global uv, discover azpysdk checks run: | - uv pip install --system -e eng/tools/azure-sdk-tools[ghtools,conda,systemperf] + uv pip install --system -e 'eng/tools/azure-sdk-tools[ghtools,conda,systemperf]' # Discover available azpysdk commands from the {command1,command2,...} line in help output CHECKS=$(azpysdk -h 2>&1 | \ @@ -102,20 +102,20 @@ jobs: - name: Run all discovered checks against azure-template using uv as package manager run: | - sdk_build azure-template -d $(pwd)/wheels --build_id 20250101.1 - python eng/scripts/dispatch_checks.py --checks "$AZPYSDK_CHECKS" --wheel_dir $(pwd)/wheels azure-template + sdk_build azure-template -d "$(pwd)/wheels" --build_id 20250101.1 + python eng/scripts/dispatch_checks.py --checks "$AZPYSDK_CHECKS" --wheel_dir "$(pwd)/wheels" azure-template shell: bash env: AZPYSDK_PIP_IMPL: "uv" - name: Install azure-sdk-tools on global pip env run: | - python -m pip install -e eng/tools/azure-sdk-tools[ghtools,conda] + python -m pip install -e 'eng/tools/azure-sdk-tools[ghtools,conda]' shell: bash - name: Run all discovered checks against azure-template using pip as package manager run: | - python eng/scripts/dispatch_checks.py --checks "$AZPYSDK_CHECKS" --wheel_dir $(pwd)/wheels azure-template + python eng/scripts/dispatch_checks.py --checks "$AZPYSDK_CHECKS" --wheel_dir "$(pwd)/wheels" azure-template shell: bash env: AZPYSDK_PIP_IMPL: "pip" diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index ac9b7148ec66..22c876c5252c 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -11,10 +11,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: '3.10' cache: 'pip' @@ -23,8 +23,8 @@ jobs: run: | python -m venv .venv source .venv/bin/activate - echo "VIRTUAL_ENV=$(pwd)/.venv" >> $GITHUB_ENV - echo "$(pwd)/.venv/bin" >> $GITHUB_PATH + echo "VIRTUAL_ENV=$(pwd)/.venv" >> "$GITHUB_ENV" + echo "$(pwd)/.venv/bin" >> "$GITHUB_PATH" - name: Install Dependencies run: | diff --git a/.github/workflows/dependency-checker.yml b/.github/workflows/dependency-checker.yml index ccc17e88e56b..29b27f912f37 100644 --- a/.github/workflows/dependency-checker.yml +++ b/.github/workflows/dependency-checker.yml @@ -13,10 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: 3.11 diff --git a/.github/workflows/src/api-md-consistency/adapter_config.js b/.github/workflows/src/api-md-consistency/adapter_config.js new file mode 100644 index 000000000000..89f562f4c50d --- /dev/null +++ b/.github/workflows/src/api-md-consistency/adapter_config.js @@ -0,0 +1,46 @@ +#!/usr/bin/env node + +import fs from "fs"; +import path from "path"; +import { fileURLToPath, pathToFileURL } from "url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const DEFAULT_CONFIG = { + adapter: "python", +}; + +export function loadWorkflowConfig() { + const configPath = path.join(__dirname, "api_md_workflow.config.json"); + if (!fs.existsSync(configPath)) { + return { ...DEFAULT_CONFIG }; + } + + const raw = fs.readFileSync(configPath, "utf-8"); + let parsed; + try { + parsed = JSON.parse(raw); + } catch (error) { + throw new Error( + `ERROR: invalid JSON in ${configPath}: ${error instanceof Error ? error.message : String(error)}`, + ); + } + + if (!parsed || typeof parsed !== "object") { + throw new Error(`ERROR: ${configPath} must contain a JSON object.`); + } + + return { + ...DEFAULT_CONFIG, + ...parsed, + }; +} + +export async function loadAdapter(name) { + const adapterPath = path.join(__dirname, "adapters", `${name}.js`); + if (!fs.existsSync(adapterPath)) { + throw new Error(`ERROR: adapter '${name}' not found at ${adapterPath}`); + } + + return import(pathToFileURL(adapterPath).href); +} diff --git a/.github/workflows/src/api-md-consistency/adapters/python.js b/.github/workflows/src/api-md-consistency/adapters/python.js new file mode 100644 index 000000000000..aace3e8fbea9 --- /dev/null +++ b/.github/workflows/src/api-md-consistency/adapters/python.js @@ -0,0 +1,164 @@ +#!/usr/bin/env node + +import fs from "fs"; +import path from "path"; +import { spawnSync } from "child_process"; + +function run(cmd, args, options = {}) { + const logger = options.logger || console; + const printable = [cmd, ...args].join(" "); + logger.info(`$ ${printable}`); + const result = spawnSync(cmd, args, { + cwd: options.cwd, + env: options.env, + encoding: "utf-8", + stdio: options.capture ? "pipe" : "inherit", + shell: options.shell ?? false, + }); + + if (result.error) { + const errorMessage = result.error instanceof Error ? result.error.message : String(result.error); + throw new Error(`Command failed to start: ${printable}\n${errorMessage}`); + } + + if ((options.check ?? true) && result.status !== 0) { + throw new Error(`Command failed (${result.status}): ${printable}`); + } + + return result; +} + +function findPackageDir(repoRoot, packageName) { + const sdkDir = path.join(repoRoot, "sdk"); + const serviceDirs = fs.readdirSync(sdkDir, { withFileTypes: true }); + const matches = []; + + for (const serviceDir of serviceDirs) { + if (!serviceDir.isDirectory()) { + continue; + } + + const candidate = path.join(sdkDir, serviceDir.name, packageName); + if (!fs.existsSync(candidate) || !fs.statSync(candidate).isDirectory()) { + continue; + } + + const hasBuildFile = fs.existsSync(path.join(candidate, "pyproject.toml")) || fs.existsSync(path.join(candidate, "setup.py")); + if (hasBuildFile) { + matches.push(candidate); + } + } + + if (matches.length === 0) { + throw new Error(`ERROR: package '${packageName}' not found under sdk/*/`); + } + + if (matches.length > 1) { + throw new Error(`ERROR: multiple matches for '${packageName}': ${matches.join(", ")}`); + } + + return matches[0]; +} + +function isPackageDir(repoRoot, packageDirRelative) { + const candidate = path.join(repoRoot, packageDirRelative); + if (!fs.existsSync(candidate) || !fs.statSync(candidate).isDirectory()) { + return false; + } + + return fs.existsSync(path.join(candidate, "pyproject.toml")) || fs.existsSync(path.join(candidate, "setup.py")); +} + +function* walkFiles(startDir) { + const entries = fs.readdirSync(startDir, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = path.join(startDir, entry.name); + if (entry.isDirectory()) { + yield* walkFiles(fullPath); + } else { + yield fullPath; + } + } +} + +function readVersion(packageDir) { + const versionRegex = /^\s*VERSION\s*[:=]\s*["']([^"']+)["']/m; + const candidates = []; + + for (const file of walkFiles(packageDir)) { + const name = path.basename(file); + if (name === "_version.py" || name === "version.py") { + // Skip generated code directories — they often contain stale versions + const relative = path.relative(packageDir, file); + if (relative.includes("_generated") || relative.includes("generated_")) { + continue; + } + candidates.push(file); + } + } + + for (const candidate of candidates) { + let text; + try { + text = fs.readFileSync(candidate, "utf-8"); + } catch { + continue; + } + + const match = text.match(versionRegex); + if (match) { + return match[1]; + } + } + + throw new Error(`ERROR: could not find a version string in ${packageDir}`); +} + +function generateApiForPackage({ + repoRoot, + packageName, + runtimeExecutable, + logger, + refLabel, +}) { + const activeLogger = logger || console; + if (refLabel) { + activeLogger.info(`--- Generating api.md on ${refLabel} ---`); + } + + const packageDir = findPackageDir(repoRoot, packageName); + if (runtimeExecutable || process.env.RUNTIME_EXECUTABLE) { + const pythonExecutable = runtimeExecutable || process.env.RUNTIME_EXECUTABLE; + run( + pythonExecutable, + ["-m", "azpysdk.main", "apistub", "--md", "--extract-metadata", "--dest-dir", packageDir, packageName], + { + cwd: repoRoot, + check: true, + logger: activeLogger, + }, + ); + return; + } + + run("azpysdk", ["apistub", "--md", "--extract-metadata", "--dest-dir", packageDir, packageName], { + cwd: repoRoot, + check: true, + logger: activeLogger, + shell: process.platform === "win32", + }); +} + +// Fields in api.metadata.yml that must match between working tree and committed version. +// pythonVersion is excluded because it varies across CI environments. +const metadataFieldsToValidate = ["apiMdSha256", "parserVersion"]; +const name = "python"; + +export { + name, + isPackageDir, + findPackageDir, + readVersion, + generateApiForPackage, + metadataFieldsToValidate, +}; diff --git a/.github/workflows/src/api-md-consistency/api-md-consistency.js b/.github/workflows/src/api-md-consistency/api-md-consistency.js new file mode 100644 index 000000000000..e7635e68b199 --- /dev/null +++ b/.github/workflows/src/api-md-consistency/api-md-consistency.js @@ -0,0 +1,112 @@ +import fs from "fs"; +import path from "path"; +import { execFile } from "../../../shared/src/exec.js"; + +const ANSI = { + bold: "\u001b[1m", + cyan: "\u001b[36m", + yellow: "\u001b[33m", + reset: "\u001b[0m", +}; + +function styleLog(text, ...styles) { + return `${styles.join("")}${text}${ANSI.reset}`; +} + +async function runNode(scriptRelativePath, workspace, core) { + await execFile("node", [scriptRelativePath], { + cwd: workspace, + logger: core, + logOutput: true, + }); +} + +function readLines(fileRelativePath, workspace) { + const fullPath = path.join(workspace, fileRelativePath); + if (!fs.existsSync(fullPath)) { + return []; + } + + return fs + .readFileSync(fullPath, "utf-8") + .split(/\r?\n/) + .map((line) => line.trim()) + .filter((line) => Boolean(line)); +} + +function formatIssueSection(title, apiFiles) { + if (!apiFiles.length) { + return ""; + } + + const lines = [title]; + for (const apiFile of apiFiles) { + const packageDir = apiFile.replace(/\/(api\.md|api\.metadata\.yml)$/, ""); + const packageName = path.basename(packageDir); + lines.push("============================================================"); + lines.push(styleLog(`PACKAGE: ${packageName}`, ANSI.bold, ANSI.cyan)); + lines.push(`PATH: ${packageDir}`); + lines.push(`API FILE: ${apiFile}`); + lines.push(styleLog("Regenerate from the repository root:", ANSI.bold, ANSI.yellow)); + lines.push(styleLog(` azpysdk apistub --md --extract-metadata ${packageName} --dest-dir .`, ANSI.bold, ANSI.yellow)); + lines.push("============================================================"); + } + lines.push(""); + return lines.join("\n"); +} + +export default async function apiMdConsistency({ core }) { + const workspace = process.env.GITHUB_WORKSPACE || process.cwd(); + + await runNode(".github/workflows/src/api-md-consistency/find_affected.js", workspace, core); + + const affected = readLines(process.env.API_MD_PACKAGES_FILE, workspace); + const changedCount = affected.length; + core.setOutput("changed_count", String(changedCount)); + + if (changedCount === 0) { + core.setOutput("mismatch_count", "0"); + core.setOutput("missing_count", "0"); + core.setOutput("issue_count", "0"); + return { + changedCount, + mismatchCount: 0, + missingCount: 0, + issueCount: 0, + }; + } + + await runNode(".github/workflows/src/api-md-consistency/regenerate.js", workspace, core); + await runNode(".github/workflows/src/api-md-consistency/find_mismatches.js", workspace, core); + + const mismatches = readLines(process.env.API_MD_MISMATCHES_FILE, workspace); + const missing = readLines(process.env.API_MD_MISSING_FILE, workspace); + + const mismatchCount = mismatches.length; + const missingCount = missing.length; + const issueCount = mismatchCount + missingCount; + + core.setOutput("mismatch_count", String(mismatchCount)); + core.setOutput("missing_count", String(missingCount)); + core.setOutput("issue_count", String(issueCount)); + + if (issueCount > 0) { + const messageParts = [ + "Generated api.md or api.metadata.yml does not match the committed files, or required API files are missing, for one or more affected packages.", + "api.metadata.yml must be committed alongside api.md, and selected metadata fields are part of pass/fail gating.", + "", + formatIssueSection("Mismatched packages:", mismatches), + formatIssueSection("Missing required API files:", missing), + "To regenerate api.md locally, run the command shown for each package from the repository root.", + ].filter((part) => part !== ""); + + core.setFailed(messageParts.join("\n")); + } + + return { + changedCount, + mismatchCount, + missingCount, + issueCount, + }; +}; diff --git a/.github/workflows/src/api-md-consistency/api_md_workflow.config.json b/.github/workflows/src/api-md-consistency/api_md_workflow.config.json new file mode 100644 index 000000000000..34b0ae2b8ce7 --- /dev/null +++ b/.github/workflows/src/api-md-consistency/api_md_workflow.config.json @@ -0,0 +1,3 @@ +{ + "adapter": "python" +} \ No newline at end of file diff --git a/.github/workflows/src/api-md-consistency/common.js b/.github/workflows/src/api-md-consistency/common.js new file mode 100644 index 000000000000..9b1df6f0f490 --- /dev/null +++ b/.github/workflows/src/api-md-consistency/common.js @@ -0,0 +1,100 @@ +#!/usr/bin/env node + +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import { execFile, isExecError } from "../../../shared/src/exec.js"; +import { defaultLogger } from "../../../shared/src/logger.js"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const REPO_ROOT = path.resolve(__dirname, "..", "..", "..", ".."); + +async function getDefaultLogger() { + return defaultLogger; +} + +async function runAsync(cmd, args, options = {}) { + const check = options.check ?? true; + const logger = options.logger ?? (await getDefaultLogger()); + + try { + const result = await execFile(cmd, args, { + cwd: options.cwd, + logger, + maxBuffer: options.maxBuffer, + }); + + return { + status: 0, + stdout: result.stdout ?? "", + stderr: result.stderr ?? "", + }; + } catch (error) { + if (!isExecError(error)) { + throw error; + } + + const status = Number.isInteger(error.code) ? error.code : 1; + const stdout = error.stdout ?? ""; + const stderr = error.stderr ?? ""; + + if (!check) { + return { status, stdout, stderr }; + } + + throw new Error(`Command failed (${status}): ${[cmd, ...args].join(" ")}`); + } +} + +function readLines(filePath) { + if (!fs.existsSync(filePath)) { + return []; + } + + return fs + .readFileSync(filePath, "utf-8") + .split(/\r?\n/) + .map((line) => line.trim()) + .filter((line) => Boolean(line)); +} + +function writeLines(filePath, lines) { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + if (!lines.length) { + fs.writeFileSync(filePath, "", "utf-8"); + return; + } + fs.writeFileSync(filePath, `${lines.join("\n")}\n`, "utf-8"); +} + +function appendGithubOutput(key, value) { + const outputPath = process.env.GITHUB_OUTPUT; + if (!outputPath) { + return; + } + + fs.appendFileSync(outputPath, `${key}=${value}\n`, "utf-8"); +} + +function envPath(name, fallback) { + return process.env[name] || fallback; +} + +function requireEnv(name) { + const value = process.env[name]; + if (!value) { + throw new Error(`Environment variable ${name} is required`); + } + return value; +} + +export { + REPO_ROOT, + getDefaultLogger, + runAsync, + readLines, + writeLines, + appendGithubOutput, + envPath, + requireEnv, +}; diff --git a/.github/workflows/src/api-md-consistency/find_affected.js b/.github/workflows/src/api-md-consistency/find_affected.js new file mode 100644 index 000000000000..9ba4edaa7e40 --- /dev/null +++ b/.github/workflows/src/api-md-consistency/find_affected.js @@ -0,0 +1,72 @@ +#!/usr/bin/env node + +import { + REPO_ROOT, + appendGithubOutput, + envPath, + getDefaultLogger, + requireEnv, + runAsync, + writeLines, +} from "./common.js"; +import { loadAdapter, loadWorkflowConfig } from "./adapter_config.js"; +import { includesSegment } from "../../../shared/src/path.js"; + +async function main() { + const config = loadWorkflowConfig(); + const adapterName = config.adapter; + const adapter = await loadAdapter(adapterName); + if (typeof adapter.isPackageDir !== "function") { + throw new Error(`ERROR: adapter '${adapterName}' does not implement isPackageDir(repoRoot, packageDirRelative).`); + } + + const baseRef = requireEnv("API_MD_BASE_REF"); + const packagesFile = envPath("API_MD_PACKAGES_FILE", ".artifacts/affected_package_dirs.txt"); + const changedFile = envPath("API_MD_CHANGED_FILE", ".artifacts/changed_package_dirs.txt"); + + await runAsync("git", ["fetch", "--no-tags", "--depth=1", "origin", baseRef], { + cwd: REPO_ROOT, + }); + const diff = ( + await runAsync("git", ["diff", "--name-only", `origin/${baseRef}..HEAD`], { + cwd: REPO_ROOT, + }) + ).stdout; + + const changedDirs = new Set(); + for (const filePath of diff.split(/\r?\n/)) { + const trimmed = filePath.trim(); + if (!trimmed) { + continue; + } + if (!includesSegment(trimmed, "sdk")) { + continue; + } + + const parts = trimmed.split("/"); + if (parts.length < 3 || parts[0] !== "sdk") { + continue; + } + + changedDirs.add(parts.slice(0, 3).join("/")); + } + + const sortedChanged = [...changedDirs].sort(); + writeLines(changedFile, sortedChanged); + + const affected = []; + for (const packageDir of sortedChanged) { + if (adapter.isPackageDir(REPO_ROOT, packageDir)) { + affected.push(packageDir); + } + } + + writeLines(packagesFile, affected); + appendGithubOutput("count", affected.length); +} + +main().catch(async (error) => { + const logger = await getDefaultLogger(); + logger.error(error instanceof Error ? error.message : String(error)); + process.exit(1); +}); diff --git a/.github/workflows/src/api-md-consistency/find_mismatches.js b/.github/workflows/src/api-md-consistency/find_mismatches.js new file mode 100644 index 000000000000..fff3cbc06017 --- /dev/null +++ b/.github/workflows/src/api-md-consistency/find_mismatches.js @@ -0,0 +1,99 @@ +#!/usr/bin/env node + +import fs from "fs"; + +import { appendGithubOutput, envPath, getDefaultLogger, readLines, runAsync, writeLines } from "./common.js"; +import { loadAdapter, loadWorkflowConfig } from "./adapter_config.js"; + +/** + * Parse a simple key: value YAML file into an object. + * Only handles flat scalar mappings (no nesting, no multi-line values). + */ +function parseSimpleYaml(text) { + const result = {}; + for (const line of text.split(/\r?\n/)) { + const match = line.match(/^(\w+)\s*:\s*(.*)$/); + if (match) { + result[match[1]] = match[2].trim(); + } + } + return result; +} + +async function main() { + const config = loadWorkflowConfig(); + const adapter = await loadAdapter(config.adapter); + + // Fields to compare in api.metadata.yml. If the adapter doesn't specify, + // compare all fields (strict default for languages that don't opt out). + const fieldsToValidate = adapter.metadataFieldsToValidate || null; + + const packagesFile = envPath("API_MD_PACKAGES_FILE", ".artifacts/affected_package_dirs.txt"); + const mismatchesFile = envPath("API_MD_MISMATCHES_FILE", ".artifacts/mismatched_api_files.txt"); + const missingFile = envPath("API_MD_MISSING_FILE", ".artifacts/missing_api_files.txt"); + const packages = readLines(packagesFile); + + const mismatches = []; + const missing = []; + for (const pkgDir of packages) { + const apiFile = `${pkgDir}/api.md`; + const metadataFile = `${pkgDir}/api.metadata.yml`; + + // Enforce that each affected package has a committed api.md file. + if (!fs.existsSync(apiFile) || !fs.statSync(apiFile).isFile()) { + missing.push(apiFile); + continue; + } + + const diffResult = await runAsync("git", ["ls-files", "--error-unmatch", "--", apiFile], { + check: false, + }); + if (diffResult.status !== 0) { + missing.push(apiFile); + continue; + } + + // api.metadata.yml must be present alongside api.md. + if (!fs.existsSync(metadataFile) || !fs.statSync(metadataFile).isFile()) { + missing.push(metadataFile); + } else { + const committedMeta = await runAsync("git", ["show", `HEAD:${metadataFile}`], { + check: false, + }); + if (committedMeta.status !== 0) { + // Not yet committed — treat as missing + missing.push(metadataFile); + } else { + const current = parseSimpleYaml(fs.readFileSync(metadataFile, "utf-8")); + const committed = parseSimpleYaml(committedMeta.stdout); + + // Compare only adapter-specified fields, or all fields if not specified. + const keys = fieldsToValidate || Object.keys({ ...committed, ...current }); + const mismatch = keys.some((key) => current[key] !== committed[key]); + if (mismatch) { + mismatches.push(metadataFile); + } + } + } + + // Diff-gate the full api.md content; metadata is field-gated above. + const quietDiffResult = await runAsync("git", ["diff", "--quiet", "--", apiFile], { + check: false, + }); + if (quietDiffResult.status !== 0) { + mismatches.push(apiFile); + } + } + + writeLines(mismatchesFile, mismatches); + writeLines(missingFile, missing); + appendGithubOutput("mismatch_count", mismatches.length); + appendGithubOutput("missing_count", missing.length); + appendGithubOutput("issue_count", mismatches.length + missing.length); +} + +main().catch(async (error) => { + const logger = await getDefaultLogger(); + logger.error(error instanceof Error ? error.message : String(error)); + process.exit(1); +}); diff --git a/.github/workflows/src/api-md-consistency/regenerate.js b/.github/workflows/src/api-md-consistency/regenerate.js new file mode 100644 index 000000000000..498ca5d4e8ec --- /dev/null +++ b/.github/workflows/src/api-md-consistency/regenerate.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +import path from "path"; + +import { REPO_ROOT, envPath, getDefaultLogger, readLines } from "./common.js"; +import { loadAdapter, loadWorkflowConfig } from "./adapter_config.js"; + +async function main() { + const logger = await getDefaultLogger(); + const config = loadWorkflowConfig(); + const adapter = await loadAdapter(config.adapter); + if (typeof adapter.generateApiForPackage !== "function") { + throw new Error( + `ERROR: adapter '${config.adapter}' does not implement generateApiForPackage({ repoRoot, packageName, runtimeExecutable }).`, + ); + } + + const packagesFile = envPath("API_MD_PACKAGES_FILE", ".artifacts/affected_package_dirs.txt"); + const packages = readLines(packagesFile); + if (!packages.length) { + return; + } + + const runtimeExecutable = process.env.RUNTIME_EXECUTABLE || null; + for (const pkgDir of packages) { + const packageName = path.basename(pkgDir); + logger.info(`Generating api.md for ${packageName}`); + await adapter.generateApiForPackage({ + repoRoot: REPO_ROOT, + packageName, + runtimeExecutable, + logger, + }); + } +} + +main().catch(async (error) => { + const logger = await getDefaultLogger(); + logger.error(error instanceof Error ? error.message : String(error)); + process.exit(1); +}); diff --git a/.github/workflows/typespec-python-regenerate.yml b/.github/workflows/typespec-python-regenerate.yml index 3245764319a6..e1e6fd7f951d 100644 --- a/.github/workflows/typespec-python-regenerate.yml +++ b/.github/workflows/typespec-python-regenerate.yml @@ -80,11 +80,13 @@ jobs: exit 1 fi - echo "typespec_repo=$REPO" >> $GITHUB_OUTPUT - echo "typespec_ref=$REF" >> $GITHUB_OUTPUT - echo "typespec_display_ref=$DISPLAY_REF" >> $GITHUB_OUTPUT - echo "typespec_ref_url=$REF_URL" >> $GITHUB_OUTPUT - echo "typespec_pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + { + echo "typespec_repo=$REPO" + echo "typespec_ref=$REF" + echo "typespec_display_ref=$DISPLAY_REF" + echo "typespec_ref_url=$REF_URL" + echo "typespec_pr_number=$PR_NUMBER" + } >> "$GITHUB_OUTPUT" echo "::notice::Regenerating from ${REPO}@${DISPLAY_REF}" - name: Checkout microsoft/typespec @@ -128,17 +130,47 @@ jobs: run: | npm run regenerate + - name: Stage regenerated tests + run: | + set -euo pipefail + GENERATED_STAGE_DIR="${RUNNER_TEMP}/typespec-python-generated" + rm -rf "$GENERATED_STAGE_DIR" + mkdir -p "$GENERATED_STAGE_DIR" + cp -r "_typespec/packages/http-client-python/tests/generated/azure" "$GENERATED_STAGE_DIR/azure" + cp -r "_typespec/packages/http-client-python/tests/generated/unbranded" "$GENERATED_STAGE_DIR/unbranded" + echo "GENERATED_STAGE_DIR=$GENERATED_STAGE_DIR" >> "$GITHUB_ENV" + + - name: Checkout generated tests source branch + run: | + set -euo pipefail + TARGET_BRANCH="typespec-python-generated-tests" + PR_NUMBER="${{ steps.typespec-info.outputs.typespec_pr_number }}" + if [ -n "$PR_NUMBER" ]; then + SOURCE_BRANCH="regen/typespec-python-pr-${PR_NUMBER}" + else + SOURCE_BRANCH="regen/typespec-python-main" + fi + if ! git fetch --no-tags --depth=1 origin "$TARGET_BRANCH" 2>/dev/null; then + echo "::error::Branch $TARGET_BRANCH not found. Initialize it from the generated-tests history so eng/tools/azure-sdk-tools/emitter/generated/template/README.md is present before rerunning this workflow." + exit 1 + fi + git checkout -B "$SOURCE_BRANCH" "origin/$TARGET_BRANCH" + - name: Copy regenerated tests run: | set -euo pipefail TARGET="eng/tools/azure-sdk-tools/emitter/generated" rm -rf "$TARGET/azure" "$TARGET/unbranded" mkdir -p "$TARGET" - cp -r "_typespec/packages/http-client-python/tests/generated/azure" "$TARGET/azure" - cp -r "_typespec/packages/http-client-python/tests/generated/unbranded" "$TARGET/unbranded" + cp -r "$GENERATED_STAGE_DIR/azure" "$TARGET/azure" + cp -r "$GENERATED_STAGE_DIR/unbranded" "$TARGET/unbranded" - name: Clean up typespec checkout - run: rm -rf "_typespec" + run: | + rm -rf "_typespec" + if [ -n "${GENERATED_STAGE_DIR:-}" ]; then + rm -rf "$GENERATED_STAGE_DIR" + fi - name: Apply README template to generated test packages run: | @@ -185,30 +217,6 @@ jobs: SOURCE_LABEL="microsoft/typespec@main" SOURCE_BRANCH="regen/typespec-python-main" fi - - # Save regenerated files to a temp dir before switching branches, - # since they are untracked and would be lost on checkout. - TMPDIR=$(mktemp -d) - cp -r "$GENERATED_DIR"/. "$TMPDIR" - - # Ensure the target branch exists; create from origin/main if not. - git fetch --no-tags --depth=1 origin main - if ! git fetch --no-tags --depth=1 origin "$TARGET_BRANCH" 2>/dev/null; then - git push origin "origin/main:refs/heads/$TARGET_BRANCH" - git fetch --no-tags --depth=1 origin "$TARGET_BRANCH" - fi - - # Clean up untracked generated files so checkout doesn't conflict. - rm -rf "$GENERATED_DIR" - - # Create source branch based on the target branch. - git checkout -B "$SOURCE_BRANCH" "origin/$TARGET_BRANCH" - - # Restore regenerated files from the temp dir. - mkdir -p "$GENERATED_DIR" - rm -rf "$GENERATED_DIR"/* - cp -r "$TMPDIR"/. "$GENERATED_DIR" - rm -rf "$TMPDIR" git add -f "$GENERATED_DIR"/ if git diff --cached --quiet; then diff --git a/.vscode/cspell.json b/.vscode/cspell.json index d85c58018529..2d7349a88660 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -139,6 +139,7 @@ "conda/conda-recipes/uamqp/meta.yaml", "conda/conda-releaselogs/azure-mgmt.md", ".github/workflows/azure-sdk-tools.yml", + ".github/workflows/actionlint.yml", "sdk/ai/azure-ai-voicelive/azure/ai/voicelive/models/_*.py", "sdk/ai/azure-ai-voicelive/samples/**" ], diff --git a/doc/eng_sys_checks.md b/doc/eng_sys_checks.md index 3515f24c3f8c..28839bf6d6af 100644 --- a/doc/eng_sys_checks.md +++ b/doc/eng_sys_checks.md @@ -177,8 +177,6 @@ analyze_python_version = "3.11" This setting is read by `eng/scripts/dispatch_checks.py` and is passed to `azpysdk` via the `--python` flag (which requires `--isolate` and `uv`). This is useful for packages that use newer syntax or type features that require a more recent Python interpreter. > **Note:** This setting only affects the Python interpreter version used for the analyze venv; it does not change the minimum supported Python version declared in `setup.py`/`pyproject.toml`. -> -> **Warning:** This override applies to _all_ analyze checks dispatched by `dispatch_checks.py`, including `apistub`. The `apistub` tool currently requires Python < 3.11 (`PYTHON_VERSION_LIMIT = (3, 11)` in `azpysdk/apistub.py`). Do not set `analyze_python_version` to `3.11` or higher for packages that still run `apistub` through the standard dispatched analyze flow. ## Environment variables important to CI diff --git a/eng/emitter-package-lock.json b/eng/emitter-package-lock.json index 8ada17490f1b..380b978415e9 100644 --- a/eng/emitter-package-lock.json +++ b/eng/emitter-package-lock.json @@ -6,33 +6,53 @@ "": { "name": "dist/src/index.js", "dependencies": { - "@azure-tools/typespec-python": "0.63.1" + "@azure-tools/typespec-python": "0.63.0" }, "devDependencies": { +<<<<<<< HEAD +<<<<<<< HEAD "@azure-tools/openai-typespec": "1.20.0", - "@azure-tools/typespec-autorest": "~0.69.0", - "@azure-tools/typespec-azure-core": "~0.69.0", - "@azure-tools/typespec-azure-resource-manager": "~0.69.0", - "@azure-tools/typespec-azure-rulesets": "~0.69.0", - "@azure-tools/typespec-client-generator-core": "~0.69.0", +======= + "@azure-tools/openai-typespec": "1.19.0", +>>>>>>> 04139a93e3 ([azure-ai-projects] Emit SDK from TypeSpec, using latest OpenAI TypeSpec package (#47318)) +======= + "@azure-tools/openai-typespec": "1.20.0", +>>>>>>> 8c5e21f58f (restore packages) + "@azure-tools/typespec-autorest": "~0.68.0", + "@azure-tools/typespec-azure-core": "~0.68.0", + "@azure-tools/typespec-azure-resource-manager": "~0.68.0", + "@azure-tools/typespec-azure-rulesets": "~0.68.0", + "@azure-tools/typespec-client-generator-core": "~0.68.4", "@azure-tools/typespec-liftr-base": "0.14.0", - "@typespec/compiler": "^1.13.0", - "@typespec/events": "~0.83.0", - "@typespec/http": "^1.13.0", - "@typespec/http-client-python": "^0.31.1", - "@typespec/openapi": "^1.13.0", - "@typespec/openapi3": "1.13.0", - "@typespec/rest": "~0.83.0", - "@typespec/sse": "~0.83.0", - "@typespec/streams": "~0.83.0", - "@typespec/versioning": "~0.83.0", - "@typespec/xml": "~0.83.0" + "@typespec/compiler": "^1.12.0", + "@typespec/events": "~0.82.0", + "@typespec/http": "^1.12.0", + "@typespec/http-client-python": "^0.31.0", + "@typespec/openapi": "^1.12.0", + "@typespec/openapi3": "1.12.0", + "@typespec/rest": "~0.82.0", + "@typespec/sse": "~0.82.0", + "@typespec/streams": "~0.82.0", + "@typespec/versioning": "~0.82.0", + "@typespec/xml": "~0.82.0" } }, "node_modules/@azure-tools/openai-typespec": { +<<<<<<< HEAD +<<<<<<< HEAD + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@azure-tools/openai-typespec/-/openai-typespec-1.20.0.tgz", + "integrity": "sha512-VSSLReGSpPdRl7XizD+W/D4LLsoGekQAwI3mKWNT7bxVsOQJAGtfVXie8pYB4shJf6QxRxHo7h59/g4easkz+Q==", +======= + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@azure-tools/openai-typespec/-/openai-typespec-1.19.0.tgz", + "integrity": "sha512-RzUTgxjZ/FgYihbHxQVKagzziJOeRb1myrA/nSLZ0xr2RNXgqz7rTercoxu2/nk6zFjnvahI8bhD+No0rIA2iA==", +>>>>>>> 04139a93e3 ([azure-ai-projects] Emit SDK from TypeSpec, using latest OpenAI TypeSpec package (#47318)) +======= "version": "1.20.0", "resolved": "https://registry.npmjs.org/@azure-tools/openai-typespec/-/openai-typespec-1.20.0.tgz", "integrity": "sha512-VSSLReGSpPdRl7XizD+W/D4LLsoGekQAwI3mKWNT7bxVsOQJAGtfVXie8pYB4shJf6QxRxHo7h59/g4easkz+Q==", +>>>>>>> 8c5e21f58f (restore packages) "dev": true, "license": "MIT", "peerDependencies": { @@ -41,23 +61,23 @@ } }, "node_modules/@azure-tools/typespec-autorest": { - "version": "0.69.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.69.0.tgz", - "integrity": "sha512-6lOOe3NWfLI8M5NGLM1ZzIFRe34gVPj2GXzti9ag6o3fVpC6eMUfacv1sU4zmz9dkpKTdOUXNO5qm3DvqPRC8Q==", + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.68.0.tgz", + "integrity": "sha512-ywcB68x0jOuplKg1u9ZpjOamHbIEEgAaMuXTP72cvWXE7q1eGLCN1DQx1Uk5ME8VLJKAX6cMOMHK4hcmg9tvuw==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.69.0", - "@azure-tools/typespec-azure-resource-manager": "^0.69.0", - "@azure-tools/typespec-client-generator-core": "^0.69.0", - "@typespec/compiler": "^1.13.0", - "@typespec/http": "^1.13.0", - "@typespec/openapi": "^1.13.0", - "@typespec/rest": "^0.83.0", - "@typespec/versioning": "^0.83.0", - "@typespec/xml": "^0.83.0" + "@azure-tools/typespec-azure-core": "^0.68.0", + "@azure-tools/typespec-azure-resource-manager": "^0.68.0", + "@azure-tools/typespec-client-generator-core": "^0.68.0", + "@typespec/compiler": "^1.12.0", + "@typespec/http": "^1.12.0", + "@typespec/openapi": "^1.12.0", + "@typespec/rest": "^0.82.0", + "@typespec/versioning": "^0.82.0", + "@typespec/xml": "^0.82.0" }, "peerDependenciesMeta": { "@typespec/xml": { @@ -66,23 +86,23 @@ } }, "node_modules/@azure-tools/typespec-azure-core": { - "version": "0.69.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.69.0.tgz", - "integrity": "sha512-UNdPb/DgMvXqwWk9hb54QOAumCJ6u6GGy+bj3RIIT1Sht6FR9rIn8AQ/UQ7WtrhbJBoqvQo5dxtS565a9/VRZw==", + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.68.0.tgz", + "integrity": "sha512-p0qUkRZav5fdQvGe2gSCvlgsvpM0y9xVhgH2GpXi5ZzpYfNGzxd8oZr8VOCP8mjMVfGQ3AtnowbmrHALEZgz7Q==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.13.0", - "@typespec/http": "^1.13.0", - "@typespec/rest": "^0.83.0" + "@typespec/compiler": "^1.12.0", + "@typespec/http": "^1.12.0", + "@typespec/rest": "^0.82.0" } }, "node_modules/@azure-tools/typespec-azure-resource-manager": { - "version": "0.69.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.69.0.tgz", - "integrity": "sha512-q/kdsGhVpvn2wb3OedxFHg7hp+al3FynUAPsz2gwqJx62z6UGOEJhtYCWP3osatVgxvKRhhh8uYl5mHRMDFi3g==", + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.68.0.tgz", + "integrity": "sha512-1zgXpOb/fGfB7SrFqawKasSOTIi9cZPWyK8V3RHyNWFZVQclEMGBzSvBHi6an4AEChqcjCSMh5MEr4BSujW4Ew==", "license": "MIT", "dependencies": { "change-case": "^5.4.4", @@ -92,33 +112,33 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.69.0", - "@typespec/compiler": "^1.13.0", - "@typespec/http": "^1.13.0", - "@typespec/openapi": "^1.13.0", - "@typespec/rest": "^0.83.0", - "@typespec/versioning": "^0.83.0" + "@azure-tools/typespec-azure-core": "^0.68.0", + "@typespec/compiler": "^1.12.0", + "@typespec/http": "^1.12.0", + "@typespec/openapi": "^1.12.0", + "@typespec/rest": "^0.82.0", + "@typespec/versioning": "^0.82.0" } }, "node_modules/@azure-tools/typespec-azure-rulesets": { - "version": "0.69.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.69.0.tgz", - "integrity": "sha512-+7KThtfHupWBDSwDR9rRHNmBb15gxACH8iPOOohRr1J28Gu25YWlz1G00r62X9VUBFLZTxYc4rQ2QCxPFT0uFw==", + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.68.0.tgz", + "integrity": "sha512-cXZ3jiDNqJgpBQLguNgXjvAsvYo7VwtlQxFMzTr96gpoAiuBViH+3eOhVd5HA4H96NgAWSddTMHXZJZzcsnE5A==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.69.0", - "@azure-tools/typespec-azure-resource-manager": "^0.69.0", - "@azure-tools/typespec-client-generator-core": "^0.69.0", - "@typespec/compiler": "^1.13.0" + "@azure-tools/typespec-azure-core": "^0.68.0", + "@azure-tools/typespec-azure-resource-manager": "^0.68.0", + "@azure-tools/typespec-client-generator-core": "^0.68.0", + "@typespec/compiler": "^1.12.0" } }, "node_modules/@azure-tools/typespec-client-generator-core": { - "version": "0.69.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.69.0.tgz", - "integrity": "sha512-ro8zzOeiN/74r0wM19R77gzLtbfjIFgKgr1Rusii/vhCfJIoVC7IcqLxhbJl0RVkjyhRFKt4GCRAw4iurnrDnw==", + "version": "0.68.4", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.68.4.tgz", + "integrity": "sha512-p32EXsrSC9giZUNdsQ2gmvDENFIEW2E0zto3FmjBZ3OeB5wCw1ZAZ+KnO0rsoKFovBvHSsQatNCKJvM/x89AgA==", "license": "MIT", "dependencies": { "change-case": "^5.4.4", @@ -129,16 +149,16 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.69.0", - "@typespec/compiler": "^1.13.0", - "@typespec/events": "^0.83.0", - "@typespec/http": "^1.13.0", - "@typespec/openapi": "^1.13.0", - "@typespec/rest": "^0.83.0", - "@typespec/sse": "^0.83.0", - "@typespec/streams": "^0.83.0", - "@typespec/versioning": "^0.83.0", - "@typespec/xml": "^0.83.0" + "@azure-tools/typespec-azure-core": "^0.68.0", + "@typespec/compiler": "^1.12.0", + "@typespec/events": "^0.82.0", + "@typespec/http": "^1.12.0", + "@typespec/openapi": "^1.12.0", + "@typespec/rest": "^0.82.0", + "@typespec/sse": "^0.82.0", + "@typespec/streams": "^0.82.0", + "@typespec/versioning": "^0.82.0", + "@typespec/xml": "^0.82.0" } }, "node_modules/@azure-tools/typespec-liftr-base": { @@ -148,13 +168,13 @@ "dev": true }, "node_modules/@azure-tools/typespec-python": { - "version": "0.63.1", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-python/-/typespec-python-0.63.1.tgz", - "integrity": "sha512-+hRzFg+pE4nGu0kko8TFcwW7Wc9irQe1QNhfu5jJj8OmMbk4idYXWKh+yo+25HUJ54Eu6L4/vgX8RzYGAfJogQ==", + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-python/-/typespec-python-0.63.0.tgz", + "integrity": "sha512-8CSHcHhI1egOE9BabVD3P8pjkYEt38vF85bA//ZEMOwgTVn0OCpOeuJN/uCy3uEYNSQUSOVKin35H2m04xupJQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@typespec/http-client-python": "^0.31.1", + "@typespec/http-client-python": "^0.30.0", "semver": "^7.7.4", "tsx": "^4.21.0" }, @@ -162,20 +182,69 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-autorest": "^0.69.0", - "@azure-tools/typespec-azure-core": "^0.69.0", - "@azure-tools/typespec-azure-resource-manager": "^0.69.0", - "@azure-tools/typespec-azure-rulesets": "^0.69.0", - "@azure-tools/typespec-client-generator-core": "^0.69.0", - "@typespec/compiler": "^1.13.0", - "@typespec/events": "^0.83.0", - "@typespec/http": "^1.13.0", - "@typespec/openapi": "^1.13.0", - "@typespec/rest": "^0.83.0", - "@typespec/sse": "^0.83.0", - "@typespec/streams": "^0.83.0", - "@typespec/versioning": "^0.83.0", - "@typespec/xml": "^0.83.0" + "@azure-tools/typespec-autorest": "^0.68.0", + "@azure-tools/typespec-azure-core": "^0.68.0", + "@azure-tools/typespec-azure-resource-manager": "^0.68.0", + "@azure-tools/typespec-azure-rulesets": "^0.68.0", + "@azure-tools/typespec-client-generator-core": "^0.68.2", + "@typespec/compiler": "^1.12.0", + "@typespec/events": "^0.82.0", + "@typespec/http": "^1.12.0", + "@typespec/openapi": "^1.12.0", + "@typespec/rest": "^0.82.0", + "@typespec/sse": "^0.82.0", + "@typespec/streams": "^0.82.0", + "@typespec/versioning": "^0.82.0", + "@typespec/xml": "^0.82.0" + } + }, +<<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> 8c5e21f58f (restore packages) + "node_modules/@azure-tools/typespec-python/node_modules/@typespec/http-client-python": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@typespec/http-client-python/-/http-client-python-0.30.1.tgz", + "integrity": "sha512-fo78R2GhPjUntPwZGDHxZ/ddRQZgLRSCzO3F+u8hPrHPzmBXA0rMkeS2WKCWYmT/wcFDPAojjH9pMUl27dLQ0w==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "js-yaml": "~4.1.0", + "marked": "^15.0.6", + "pyodide": "0.26.2", + "semver": "~7.6.2", + "tsx": "^4.21.0" + }, + "engines": { + "node": ">=22.0.0" + }, + "peerDependencies": { + "@azure-tools/typespec-autorest": ">=0.68.0 <1.0.0", + "@azure-tools/typespec-azure-core": ">=0.68.0 <1.0.0", + "@azure-tools/typespec-azure-resource-manager": ">=0.68.0 <1.0.0", + "@azure-tools/typespec-azure-rulesets": ">=0.68.0 <1.0.0", + "@azure-tools/typespec-client-generator-core": ">=0.68.2 <1.0.0", + "@typespec/compiler": "^1.12.0", + "@typespec/events": ">=0.82.0 <1.0.0", + "@typespec/http": "^1.12.0", + "@typespec/openapi": "^1.12.0", + "@typespec/rest": ">=0.82.0 <1.0.0", + "@typespec/sse": ">=0.82.0 <1.0.0", + "@typespec/streams": ">=0.82.0 <1.0.0", + "@typespec/versioning": ">=0.82.0 <1.0.0", + "@typespec/xml": ">=0.82.0 <1.0.0" + } + }, + "node_modules/@azure-tools/typespec-python/node_modules/@typespec/http-client-python/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/@babel/code-frame": { @@ -184,6 +253,14 @@ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "license": "MIT", "dependencies": { +======= + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { +>>>>>>> 04139a93e3 ([azure-ai-projects] Emit SDK from TypeSpec, using latest OpenAI TypeSpec package (#47318)) "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" @@ -958,9 +1035,9 @@ } }, "node_modules/@scalar/helpers": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@scalar/helpers/-/helpers-0.8.2.tgz", - "integrity": "sha512-qNbqUjSB3S4Gr4A0oANcm5G1Ip+EqBxICYKhe9YzmnaBpbmW6shxqpiivApTvvuDf+uIhR3uMwWyVQbYcGLsxA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@scalar/helpers/-/helpers-0.8.1.tgz", + "integrity": "sha512-yuiuBCadP5bjAnIv23QvifVN/NaMi9xBF6b8Wdk4QOzwzLPJmp699MAdf33J0A5i2qKcvnu32iz/VkEJmQRe5g==", "dev": true, "license": "MIT", "engines": { @@ -968,13 +1045,13 @@ } }, "node_modules/@scalar/json-magic": { - "version": "0.12.16", - "resolved": "https://registry.npmjs.org/@scalar/json-magic/-/json-magic-0.12.16.tgz", - "integrity": "sha512-w8cDbZhHCzmIblWx92IVWoAXsbI4Fz3m++jiBANTSO1hgphF6UqEPQiCt3wnMPaxaanjMQxjS/iBk1UGXR2EGA==", + "version": "0.12.15", + "resolved": "https://registry.npmjs.org/@scalar/json-magic/-/json-magic-0.12.15.tgz", + "integrity": "sha512-ZYgdYZ0jSZXQeyhG2lJ20FjzvKsaDRXk4bPguF/Ytl2nGBh9a6RIIr9NvVy4zAD67a/ahm+xipXlfoR1KtB5fg==", "dev": true, "license": "MIT", "dependencies": { - "@scalar/helpers": "0.8.2", + "@scalar/helpers": "0.8.1", "pathe": "^2.0.3", "yaml": "^2.8.3" }, @@ -983,31 +1060,66 @@ } }, "node_modules/@scalar/openapi-parser": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@scalar/openapi-parser/-/openapi-parser-0.28.7.tgz", - "integrity": "sha512-E6beEdTsJxUStxOmY1knQvSNJq6LTiXOsRX2WTrfmU6d/kiATn6IKkAU0kXtAZkaYCGU4UCEmBFHCMmNKn0JLA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@scalar/openapi-parser/-/openapi-parser-0.25.12.tgz", + "integrity": "sha512-1hajBAbc7cbEcsSZEQxaPXZyCjMf6h6hObV+SO32jkC6rrxinPXQIucDu9HTu/jm/FaaMnNhc8/XDWz5/E49cQ==", "dev": true, "license": "MIT", "dependencies": { - "@scalar/helpers": "0.8.2", - "@scalar/json-magic": "0.12.16", - "@scalar/openapi-types": "0.9.1", - "@scalar/openapi-upgrader": "0.2.9", + "@scalar/helpers": "0.5.2", + "@scalar/json-magic": "0.12.8", + "@scalar/openapi-types": "0.8.0", + "@scalar/openapi-upgrader": "0.2.6", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "jsonpointer": "^5.0.1", "leven": "^4.0.0", - "yaml": "^2.8.3" + "yaml": "^2.8.0" }, "engines": { "node": ">=22" } }, + "node_modules/@scalar/openapi-parser/node_modules/@scalar/helpers": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@scalar/helpers/-/helpers-0.5.2.tgz", + "integrity": "sha512-Pi1GAl8jO6ungmGj2sjDfCfqiBNrKW6HXDZmminV94ybGU/KtRLOqHwd0n9FIhY3j0RYGpGC0VCuniCICfQPHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@scalar/openapi-parser/node_modules/@scalar/json-magic": { + "version": "0.12.8", + "resolved": "https://registry.npmjs.org/@scalar/json-magic/-/json-magic-0.12.8.tgz", + "integrity": "sha512-a559iO8tmFeA90JJAAM3U5x1Asf3mr0Z8uDC1PmyLTDjdSOfajP7EY9VzNoXE2cM48ilf9qrjmkbw/d4VCFjQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@scalar/helpers": "0.5.2", + "pathe": "^2.0.3", + "yaml": "^2.8.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@scalar/openapi-parser/node_modules/@scalar/openapi-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@scalar/openapi-types/-/openapi-types-0.8.0.tgz", + "integrity": "sha512-WmaxVSfvY5K/TwcG2B2TU1WOe1As1uc2s7myswtP6dBlcjU3hM08SApxv/jmyGaCE8t4gO5BBhmHY4pDUfmr2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, "node_modules/@scalar/openapi-types": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@scalar/openapi-types/-/openapi-types-0.9.1.tgz", - "integrity": "sha512-gkGhSkxSzADaBiNg+ZAbJuwj+ZUmzP2Pg9CWZ7ZP+0fck2WjPeDDM7aAbouAm0aQQMF9xBjSPXSA9a/qTHYaTw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@scalar/openapi-types/-/openapi-types-0.7.0.tgz", + "integrity": "sha512-kN0PwlJW0de4bwQ4ib+mBHzKJUvBCyR/gwU4zLEq6SCbj+GfgYUh+2a0/yl1WYVUiSkkwFsHjfmQ8KjhR3HK0Q==", "dev": true, "license": "MIT", "engines": { @@ -1015,18 +1127,28 @@ } }, "node_modules/@scalar/openapi-upgrader": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@scalar/openapi-upgrader/-/openapi-upgrader-0.2.9.tgz", - "integrity": "sha512-D5b0rGLLZgmkO9mdW2j/ND1KBlH1u3RCpr87HPxv9P9ZSr6PtM5iLqFOJq0ACiaHjY2mikCrxgDmnUEhTzRpHQ==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@scalar/openapi-upgrader/-/openapi-upgrader-0.2.6.tgz", + "integrity": "sha512-pvEmfSCDNYR4+lygidUqfo+shzyp4OSh9+UgK110rzA8Oot6WbJBM03Fuq3M255G7G6R9iXyfsebB7MBUocPkw==", "dev": true, "license": "MIT", "dependencies": { - "@scalar/openapi-types": "0.9.1" + "@scalar/openapi-types": "0.8.0" }, "engines": { "node": ">=22" } }, + "node_modules/@scalar/openapi-upgrader/node_modules/@scalar/openapi-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@scalar/openapi-types/-/openapi-types-0.8.0.tgz", + "integrity": "sha512-WmaxVSfvY5K/TwcG2B2TU1WOe1As1uc2s7myswtP6dBlcjU3hM08SApxv/jmyGaCE8t4gO5BBhmHY4pDUfmr2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, "node_modules/@typespec/asset-emitter": { "version": "0.79.1", "resolved": "https://registry.npmjs.org/@typespec/asset-emitter/-/asset-emitter-0.79.1.tgz", @@ -1041,9 +1163,9 @@ } }, "node_modules/@typespec/compiler": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.13.0.tgz", - "integrity": "sha512-DonoHiyAMx0UjSmssqTrFtya+v97wny1aHcTLU5QF2wFzLATtcwUU9hbPC+eXhepuTunMOCHf8yk3pEsH6PZYA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.12.0.tgz", + "integrity": "sha512-hKCkHEEDdCpXFyOU8ln+TzBBwonFMbkeUV0zIc+vBETyO8p/Upui3XvEyLOyB4CpKUReHzGeGm3gcFjNc73ygg==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.0", @@ -1072,28 +1194,28 @@ } }, "node_modules/@typespec/events": { - "version": "0.83.0", - "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.83.0.tgz", - "integrity": "sha512-3EP1EIjdLgwStgd2rGWaF/QqY7YRAt+DIYnnYG2VsdPwa8s2t6K6eJ9YJDXveeHImAkHs+cpFuwxnjKMl4hOyw==", + "version": "0.82.0", + "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.82.0.tgz", + "integrity": "sha512-4gxwWndMVmYF6e5ETrwW6b77h1DsSc2ZiIbNo98XePaynD6yz/ooHKKtNKacjC2gmWhfRz1ArPioYn0YHvQkxw==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.13.0" + "@typespec/compiler": "^1.12.0" } }, "node_modules/@typespec/http": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.13.0.tgz", - "integrity": "sha512-tf8XFddU6g1MZSAVCLC/0Xa4fNfUO0CcHe6PWpmC3bvUojxMnpRcERI2DdoRJ+aycB9Q+Z8wN8bJO3up6u+sCw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.12.0.tgz", + "integrity": "sha512-3Bb1M6VSuEVPWOecXj3h3I/ddMpb9cmKRQQq34oq7LatiK4fwVBp+EdWbqzEzaRUGHm9mZtqsMsxZf5FndT8dg==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.13.0", - "@typespec/streams": "^0.83.0" + "@typespec/compiler": "^1.12.0", + "@typespec/streams": "^0.82.0" }, "peerDependenciesMeta": { "@typespec/streams": { @@ -1102,9 +1224,10 @@ } }, "node_modules/@typespec/http-client-python": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@typespec/http-client-python/-/http-client-python-0.31.1.tgz", - "integrity": "sha512-69lB3EzV/eVprES14ud6hDf6EgIuPu/Q/lhkCEzDZ9qNH5Q7ntScSweWDp71uoT56vpyl2rr41Zjb+FixQhS2g==", + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/@typespec/http-client-python/-/http-client-python-0.31.0.tgz", + "integrity": "sha512-cG7ky7WMRfEeSp6YadVwHwgFSFLNFN82LTQziu6doNxQ0UipAIaobIELfVT5D/PDIz/Pa3w9mNG9CWF08uO7qA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -1118,26 +1241,27 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-autorest": ">=0.69.0 <1.0.0", - "@azure-tools/typespec-azure-core": ">=0.69.0 <1.0.0", - "@azure-tools/typespec-azure-resource-manager": ">=0.69.0 <1.0.0", - "@azure-tools/typespec-azure-rulesets": ">=0.69.0 <1.0.0", - "@azure-tools/typespec-client-generator-core": ">=0.69.0 <1.0.0", - "@typespec/compiler": "^1.13.0", - "@typespec/events": ">=0.83.0 <1.0.0", - "@typespec/http": "^1.13.0", - "@typespec/openapi": "^1.13.0", - "@typespec/rest": ">=0.83.0 <1.0.0", - "@typespec/sse": ">=0.83.0 <1.0.0", - "@typespec/streams": ">=0.83.0 <1.0.0", - "@typespec/versioning": ">=0.83.0 <1.0.0", - "@typespec/xml": ">=0.83.0 <1.0.0" + "@azure-tools/typespec-autorest": ">=0.68.0 <1.0.0", + "@azure-tools/typespec-azure-core": ">=0.68.0 <1.0.0", + "@azure-tools/typespec-azure-resource-manager": ">=0.68.0 <1.0.0", + "@azure-tools/typespec-azure-rulesets": ">=0.68.0 <1.0.0", + "@azure-tools/typespec-client-generator-core": ">=0.68.4 <1.0.0", + "@typespec/compiler": "^1.12.0", + "@typespec/events": ">=0.82.0 <1.0.0", + "@typespec/http": "^1.12.0", + "@typespec/openapi": "^1.12.0", + "@typespec/rest": ">=0.82.0 <1.0.0", + "@typespec/sse": ">=0.82.0 <1.0.0", + "@typespec/streams": ">=0.82.0 <1.0.0", + "@typespec/versioning": ">=0.82.0 <1.0.0", + "@typespec/xml": ">=0.82.0 <1.0.0" } }, "node_modules/@typespec/http-client-python/node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1147,28 +1271,28 @@ } }, "node_modules/@typespec/openapi": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.13.0.tgz", - "integrity": "sha512-omPc9n+LM2WvjYwnIf31RCxmG17fFUOVLBRsWg4T1mbcsNCj4grnNP7Lwt+irIZCiKtmLKxq3ViE7jYixCkZ3g==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.12.0.tgz", + "integrity": "sha512-XtkCMPpzXFfuIzmx/BQrCMUCCk7d37lkqZe5ubJmvJ02Fr7yvAbofrgtNUZ1BbFe3TBBUS2nB3E3mjT3tE4zCQ==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.13.0", - "@typespec/http": "^1.13.0" + "@typespec/compiler": "^1.12.0", + "@typespec/http": "^1.12.0" } }, "node_modules/@typespec/openapi3": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-1.13.0.tgz", - "integrity": "sha512-G6ayl30kXYVhJvL2/zFwtTdWnCt6N6jZWxs8rAwYiFlZfaG8R/dzjRgcap9Dr0v+6AyWmRwooxRKM3TLi5R/mg==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-1.12.0.tgz", + "integrity": "sha512-r2CechzwGyr+BtLo7ApoaaTsrYybaE0gsGCLVWbkfH9Fs2KTu0ilpMKGxXjhKRS6cWHMkGGuB3hM0OJjFyFydw==", "dev": true, "license": "MIT", "dependencies": { - "@scalar/json-magic": "^0.12.15", - "@scalar/openapi-parser": "^0.28.6", - "@scalar/openapi-types": "^0.9.1", + "@scalar/json-magic": "^0.12.5", + "@scalar/openapi-parser": "^0.25.8", + "@scalar/openapi-types": "^0.7.0", "@typespec/asset-emitter": "^0.79.1", "yaml": "^2.8.3" }, @@ -1179,14 +1303,14 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.13.0", - "@typespec/events": "^0.83.0", - "@typespec/http": "^1.13.0", - "@typespec/json-schema": "^1.13.0", - "@typespec/openapi": "^1.13.0", - "@typespec/sse": "^0.83.0", - "@typespec/streams": "^0.83.0", - "@typespec/versioning": "^0.83.0" + "@typespec/compiler": "^1.12.0", + "@typespec/events": "^0.82.0", + "@typespec/http": "^1.12.0", + "@typespec/json-schema": "^1.12.0", + "@typespec/openapi": "^1.12.0", + "@typespec/sse": "^0.82.0", + "@typespec/streams": "^0.82.0", + "@typespec/versioning": "^0.82.0" }, "peerDependenciesMeta": { "@typespec/events": { @@ -1210,67 +1334,67 @@ } }, "node_modules/@typespec/rest": { - "version": "0.83.0", - "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.83.0.tgz", - "integrity": "sha512-WMEwEe1kdaOdZ0c+ct5BVmTSBXkrPniUYDWCz3K52T4in2dNc7J6YGP6tL8bXgQz5B0CsP0VNO12N+UysQDsLw==", + "version": "0.82.0", + "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.82.0.tgz", + "integrity": "sha512-cKjKEd8lgE3EdU9b5xXLoSdKBcifITOhHS2n9LPbEG9w6APfWDWGdtUe4UKV3wxWq9HlT143wpECW7IjrPhjnA==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.13.0", - "@typespec/http": "^1.13.0" + "@typespec/compiler": "^1.12.0", + "@typespec/http": "^1.12.0" } }, "node_modules/@typespec/sse": { - "version": "0.83.0", - "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.83.0.tgz", - "integrity": "sha512-04WNaju2rwBbcF5pG+HrKQtdcrmSGuTVziLHNA9XOqj1qM7Uon3+wo2g+ZZ3Z6tngfqQoTCPyDcRHqZtGRdNuw==", + "version": "0.82.0", + "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.82.0.tgz", + "integrity": "sha512-4jBByfLsS7yQAIqmbLkfrw4XoPm9kOqawvW5gVXmKtnMMDYR0RmfBhsnAXBqhUXGbnFq0bDJGEw3GX+6k3mKnA==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.13.0", - "@typespec/events": "^0.83.0", - "@typespec/http": "^1.13.0", - "@typespec/streams": "^0.83.0" + "@typespec/compiler": "^1.12.0", + "@typespec/events": "^0.82.0", + "@typespec/http": "^1.12.0", + "@typespec/streams": "^0.82.0" } }, "node_modules/@typespec/streams": { - "version": "0.83.0", - "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.83.0.tgz", - "integrity": "sha512-wbO6sdH1Uf+UwjxxsWdHQkjJ3wwiYsAKI+L66qnDYVXAFe02sUdMKd0mxH5o9ipGXE52MZ+yvZ52vHAD+g3RFQ==", + "version": "0.82.0", + "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.82.0.tgz", + "integrity": "sha512-cr/6h6VV/6OJeG8RNcSd0SDes5iEXiuGUcKGrMN6wF8qKTTrY2hXNhfqCCn3lamDOg00wbi7ke+laz6pHWN3tg==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.13.0" + "@typespec/compiler": "^1.12.0" } }, "node_modules/@typespec/versioning": { - "version": "0.83.0", - "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.83.0.tgz", - "integrity": "sha512-nE66ta0ixpHB6FQpSzqnj8QnVfgFsxeK/4Xv+DxYx2nB/w18f6VjkF+hW+A/zs1tZIYvBZVbCNa/Rcr8zM6fhg==", + "version": "0.82.0", + "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.82.0.tgz", + "integrity": "sha512-s8giuYQTQPniy2YxNfKXYpAU2Vm4L74TdOsbFWe0tG+jnOy/9tt7kKTH4QF1sB8nRvmjv8h31EoHtZYOPe1GvA==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.13.0" + "@typespec/compiler": "^1.12.0" } }, "node_modules/@typespec/xml": { - "version": "0.83.0", - "resolved": "https://registry.npmjs.org/@typespec/xml/-/xml-0.83.0.tgz", - "integrity": "sha512-2/dtAD8jGPkIdwpQ1G1P+5+qdMPeafQiIKCd8NdAnOo0w9OZ59Io52jINm9HdN8+FcbOrqK8+B2N9rlPRj7PqA==", + "version": "0.82.0", + "resolved": "https://registry.npmjs.org/@typespec/xml/-/xml-0.82.0.tgz", + "integrity": "sha512-/Bwlt7HwSltojSbalkh7hGRh/lB5aMJllnb7gAAf3xRPMlnmu5VJqDFFcbZKqDvkwgGXZX/xHo458c4kott5Ug==", "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.13.0" + "@typespec/compiler": "^1.12.0" } }, "node_modules/ajv": { @@ -1709,9 +1833,9 @@ } }, "node_modules/prettier": { - "version": "3.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz", - "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -1751,9 +1875,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", - "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.3.tgz", + "integrity": "sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg==", "license": "ISC", "bin": { "semver": "bin/semver.js" diff --git a/eng/emitter-package.json b/eng/emitter-package.json index 6d4440af96a1..12a6ce18f93f 100644 --- a/eng/emitter-package.json +++ b/eng/emitter-package.json @@ -1,26 +1,35 @@ { "name": "dist/src/index.js", "dependencies": { - "@azure-tools/typespec-python": "0.63.1" + "@azure-tools/typespec-python": "0.63.0" }, "devDependencies": { - "@typespec/compiler": "^1.13.0", - "@typespec/http": "^1.13.0", - "@typespec/rest": "~0.83.0", - "@typespec/versioning": "~0.83.0", - "@typespec/openapi": "^1.13.0", - "@typespec/events": "~0.83.0", - "@typespec/sse": "~0.83.0", - "@typespec/streams": "~0.83.0", - "@typespec/xml": "~0.83.0", - "@typespec/openapi3": "1.13.0", - "@typespec/http-client-python": "^0.31.1", + "@typespec/compiler": "^1.12.0", + "@typespec/http": "^1.12.0", + "@typespec/rest": "~0.82.0", + "@typespec/versioning": "~0.82.0", + "@typespec/openapi": "^1.12.0", + "@typespec/events": "~0.82.0", + "@typespec/sse": "~0.82.0", + "@typespec/streams": "~0.82.0", + "@typespec/xml": "~0.82.0", + "@typespec/openapi3": "1.12.0", +<<<<<<< HEAD +<<<<<<< HEAD + "@typespec/http-client-python": "^0.31.0", "@azure-tools/openai-typespec": "1.20.0", - "@azure-tools/typespec-autorest": "~0.69.0", - "@azure-tools/typespec-azure-core": "~0.69.0", - "@azure-tools/typespec-azure-resource-manager": "~0.69.0", - "@azure-tools/typespec-azure-rulesets": "~0.69.0", - "@azure-tools/typespec-client-generator-core": "~0.69.0", +======= + "@azure-tools/openai-typespec": "1.19.0", +>>>>>>> 04139a93e3 ([azure-ai-projects] Emit SDK from TypeSpec, using latest OpenAI TypeSpec package (#47318)) +======= + "@typespec/http-client-python": "^0.31.0", + "@azure-tools/openai-typespec": "1.20.0", +>>>>>>> 8c5e21f58f (restore packages) + "@azure-tools/typespec-autorest": "~0.68.0", + "@azure-tools/typespec-azure-core": "~0.68.0", + "@azure-tools/typespec-azure-resource-manager": "~0.68.0", + "@azure-tools/typespec-azure-rulesets": "~0.68.0", + "@azure-tools/typespec-client-generator-core": "~0.68.4", "@azure-tools/typespec-liftr-base": "0.14.0" } } diff --git a/eng/scripts/Extract-APIViewMetadata-Python.ps1 b/eng/scripts/Extract-APIViewMetadata-Python.ps1 new file mode 100644 index 000000000000..abf162e71f20 --- /dev/null +++ b/eng/scripts/Extract-APIViewMetadata-Python.ps1 @@ -0,0 +1,153 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. + +<# +.SYNOPSIS +Extracts Python APIView metadata from API markdown and writes api.metadata.yml. + +.DESCRIPTION +Reads an API markdown file, extracts parser and Python runtime versions from the +Python APIView metadata header, removes that header from the markdown, trims leading +blank lines from the markdown body, and writes api.metadata.yml beside the markdown file. + +.PARAMETER ApiMarkdownPath +Optional. Path to API markdown file. If omitted, api.md will be resolved from OutputPath. + +.PARAMETER OutputPath +Optional. Directory containing API markdown output. Defaults to current directory. + +.EXAMPLE +./Extract-APIViewMetadata-Python.ps1 -OutputPath ./sdk/template/azure-template + +.EXAMPLE +./Extract-APIViewMetadata-Python.ps1 -ApiMarkdownPath ./sdk/template/azure-template/api.md +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $false)] + [string]$ApiMarkdownPath, + + [Parameter(Mandatory = $false)] + [string]$OutputPath = "." +) + +Set-StrictMode -Version 3 +$ErrorActionPreference = 'Stop' + +function Resolve-ApiMarkdownPath { + param( + [string]$ProvidedPath, + [string]$OutputDirectory + ) + + if ($ProvidedPath) { + return $ProvidedPath + } + + $resolvedOutput = Resolve-Path -LiteralPath $OutputDirectory -ErrorAction Stop + $apiLower = Join-Path $resolvedOutput.Path "api.md" + if (Test-Path -LiteralPath $apiLower -PathType Leaf) { + return $apiLower + } + + throw "Could not find API markdown file in '$OutputDirectory'. Expected api.md." +} + +function Trim-LeadingBlankLines { + param([string[]]$Lines) + + $start = 0 + while ($start -lt $Lines.Count -and [string]::IsNullOrWhiteSpace($Lines[$start])) { + $start++ + } + + if ($start -eq 0) { + return $Lines + } + + if ($start -ge $Lines.Count) { + return @() + } + + return $Lines[$start..($Lines.Count - 1)] +} + +function Get-Sha256Hex { + param([string]$Text) + + $sha256 = [System.Security.Cryptography.SHA256]::Create() + try { + $bytes = [System.Text.Encoding]::UTF8.GetBytes($Text) + $hashBytes = $sha256.ComputeHash($bytes) + return ([System.BitConverter]::ToString($hashBytes)).Replace("-", "").ToLowerInvariant() + } + finally { + $sha256.Dispose() + } +} + +$resolvedApiPath = Resolve-ApiMarkdownPath -ProvidedPath $ApiMarkdownPath -OutputDirectory $OutputPath +if (-not (Test-Path -LiteralPath $resolvedApiPath -PathType Leaf)) { + throw "API markdown file not found: $resolvedApiPath" +} + +$metadataPattern = '^# Package is parsed using apiview-stub-generator\(version:([^\)]+)\), Python version:\s*([^\s]+)\s*$' + +$fileText = Get-Content -LiteralPath $resolvedApiPath -Raw +$lineEnding = if ($fileText -match "`r`n") { "`r`n" } else { "`n" } +$lines = $fileText -split '\r?\n' + +$metadata = [ordered]@{} +$filtered = [System.Collections.Generic.List[string]]::new() + +foreach ($line in $lines) { + $match = [regex]::Match($line, $metadataPattern) + if ($match.Success) { + # Alphabetical keys in output YAML. + $metadata['parserVersion'] = $match.Groups[1].Value + $metadata['pythonVersion'] = $match.Groups[2].Value + continue + } + + $filtered.Add($line) +} + +# Remove blank lines after opening fence so markdown body starts at namespace. +if ($filtered.Count -gt 0 -and $filtered[0].StartsWith('```')) { + $fence = $filtered[0] + $body = Trim-LeadingBlankLines -Lines @($filtered | Select-Object -Skip 1) + $rewritten = [System.Collections.Generic.List[string]]::new() + $rewritten.Add($fence) + foreach ($line in $body) { + $rewritten.Add($line) + } + $filtered = $rewritten +} +else { + $trimmed = Trim-LeadingBlankLines -Lines @($filtered) + $filtered = [System.Collections.Generic.List[string]]::new($trimmed) +} + +$normalizedLinesForHash = @($filtered | ForEach-Object { $_.TrimEnd() }) +$newlineForHash = [string][char]10 +$normalizedTextForHash = $normalizedLinesForHash -join $newlineForHash +$metadata['apiMdSha256'] = Get-Sha256Hex -Text $normalizedTextForHash + +Set-Content -LiteralPath $resolvedApiPath -Value ($filtered -join $lineEnding) -NoNewline -Encoding utf8 +Write-Host "Updated markdown: $resolvedApiPath" + +$metadataPath = Join-Path (Split-Path -Parent $resolvedApiPath) "api.metadata.yml" +if ($metadata.Count -gt 0) { + $yamlLines = [System.Collections.Generic.List[string]]::new() + foreach ($key in ($metadata.Keys | Sort-Object)) { + $yamlLines.Add(("{0}: {1}" -f $key, $metadata[$key])) + } + + Set-Content -LiteralPath $metadataPath -Value ($yamlLines -join $lineEnding) -Encoding utf8 + Write-Host "Generated metadata: $metadataPath" +} +elseif (Test-Path -LiteralPath $metadataPath) { + Remove-Item -LiteralPath $metadataPath -Force + Write-Host "Removed stale metadata: $metadataPath" +} diff --git a/eng/scripts/dispatch_checks.py b/eng/scripts/dispatch_checks.py index 3f6c56b6b7cc..df490a56cde3 100644 --- a/eng/scripts/dispatch_checks.py +++ b/eng/scripts/dispatch_checks.py @@ -214,6 +214,8 @@ async def run_check( async with semaphore: start = time.time() cmd = base_args + [check, "--isolate", package] + if check == "apistub": + cmd += ["--install-deps"] if python_version: cmd += ["--python", python_version] if service: diff --git a/eng/tools/azure-sdk-tools/azpysdk/apistub.py b/eng/tools/azure-sdk-tools/azpysdk/apistub.py index 4465562950c3..84e2cd8c964f 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/apistub.py +++ b/eng/tools/azure-sdk-tools/azpysdk/apistub.py @@ -13,7 +13,6 @@ from ci_tools.parsing import ParsedSetup REPO_ROOT = discover_repo_root() -PYTHON_VERSION_LIMIT = (3, 11) # apistub doesn't support Python 3.11+ def get_package_wheel_path(pkg_root: str) -> str: @@ -70,16 +69,31 @@ def register( action="store_true", help="Generate api.md from the JSON token file using Export-APIViewMarkdown.ps1. Output directory for api.md is the same as the generated token file.", ) + p.add_argument( + "--extract-metadata", + dest="extract_metadata", + default=False, + action="store_true", + help="Extract language-specific metadata from generated api.md into api.metadata.yml and remove metadata header from api.md.", + ) + p.add_argument( + "--install-deps", + dest="install_deps", + default=False, + action="store_true", + help=( + "Install dev requirements and apiview dependencies before running. " + "Skipped by default for faster local iteration." + ), + ) p.set_defaults(func=self.run) def run(self, args: argparse.Namespace) -> int: """Run the apistub check command.""" logger.info("Running apistub check...") - if sys.version_info >= PYTHON_VERSION_LIMIT: - logger.error( - f"Python version {sys.version_info.major}.{sys.version_info.minor} is not supported. Version must be less than {PYTHON_VERSION_LIMIT[0]}.{PYTHON_VERSION_LIMIT[1]}." - ) + if getattr(args, "extract_metadata", False) and not getattr(args, "generate_md", False): + logger.error("--extract-metadata requires --md.") return 1 set_envvar_defaults() @@ -101,22 +115,25 @@ def run(self, args: argparse.Namespace) -> int: ) logger.info(f"Processing {package_name} for apistub check") - # install dependencies - self.install_dev_reqs(executable, args, package_dir) - - try: - install_into_venv( - executable, - [ - "-r", - os.path.join(REPO_ROOT, "eng", "apiview_reqs.txt"), - "--index-url=https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/", - ], - package_dir, - ) - except CalledProcessError as e: - logger.error(f"Failed to install dependencies: {e}") - return e.returncode + install_deps = getattr(args, "install_deps", False) + + if install_deps: + # install dependencies + self.install_dev_reqs(executable, args, package_dir) + + try: + install_into_venv( + executable, + [ + "-r", + os.path.join(REPO_ROOT, "eng", "apiview_reqs.txt"), + "--index-url=https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/", + ], + package_dir, + ) + except CalledProcessError as e: + logger.error(f"Failed to install dependencies: {e}") + return e.returncode if not os.getenv("PREBUILT_WHEEL_DIR"): create_package_and_install( @@ -131,14 +148,15 @@ def run(self, args: argparse.Namespace) -> int: python_executable=executable, ) - self.pip_freeze(executable) + if install_deps: + self.pip_freeze(executable) pkg_path = get_package_wheel_path(package_dir) pkg_path = os.path.abspath(pkg_path) dest_dir = getattr(args, "dest_dir", None) if dest_dir: - out_token_path = os.path.join(os.path.abspath(dest_dir), package_name) + out_token_path = os.path.abspath(dest_dir) os.makedirs(out_token_path, exist_ok=True) else: out_token_path = os.path.abspath(staging_directory) @@ -164,6 +182,7 @@ def run(self, args: argparse.Namespace) -> int: if getattr(args, "generate_md", False): token_json_path = os.path.join(out_token_path, f"{package_name}_python.json") md_script = os.path.join(REPO_ROOT, "eng", "common", "scripts", "Export-APIViewMarkdown.ps1") + metadata_script = os.path.join(REPO_ROOT, "eng", "scripts", "Extract-APIViewMetadata-Python.ps1") logger.info(f"Generating api.md for {package_name}") try: result = run( @@ -175,11 +194,22 @@ def run(self, args: argparse.Namespace) -> int: # pwsh script logs the api.md location if result.stdout: logger.info(result.stdout) + + if getattr(args, "extract_metadata", False): + logger.info(f"Extracting API metadata for {package_name}") + metadata_result = run( + ["pwsh", metadata_script, "-OutputPath", out_token_path], + check=True, + capture_output=True, + text=True, + ) + if metadata_result.stdout: + logger.info(metadata_result.stdout) except FileNotFoundError: logger.error("Failed to generate api.md: pwsh (PowerShell) is not installed or not on PATH.") results.append(1) except CalledProcessError as e: - logger.error(f"Failed to generate api.md (exit code {e.returncode}):") + logger.error(f"Failed to generate api.md or extract metadata (exit code {e.returncode}):") if e.stderr: logger.error(e.stderr) if e.stdout: diff --git a/eng/tools/azure-sdk-tools/azpysdk/changelog.py b/eng/tools/azure-sdk-tools/azpysdk/changelog.py index 0f996bf99509..9a69ff4c69b0 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/changelog.py +++ b/eng/tools/azure-sdk-tools/azpysdk/changelog.py @@ -9,10 +9,10 @@ from ci_tools.functions import get_package_from_repo from ci_tools.logging import logger -# Chronus is pinned as a dev dependency in .github/chronus/package.json with -# a committed lockfile so both the top-level version and all transitive +# Chronus is pinned as a dev dependency in .github/package.json with a +# committed lockfile so both the top-level version and all transitive # dependencies are reproducible. -_CHRONUS_INSTALL_DIR = os.path.join(".github", "chronus") +_CHRONUS_INSTALL_DIR = ".github" _CHRONUS_BIN_NAME = "chronus.cmd" if os.name == "nt" else "chronus" _CHRONUS_BIN_PATH = os.path.join(_CHRONUS_INSTALL_DIR, "node_modules", ".bin", _CHRONUS_BIN_NAME) @@ -127,7 +127,7 @@ def _is_chronus_installed() -> bool: def _ensure_chronus_installed(self) -> None: """Verify Chronus is installed locally, offering to install if not. - Runs ``npm ci`` against ``.github/chronus`` so only the exact + Runs ``npm ci`` against ``.github`` so only the exact versions recorded in ``package-lock.json`` are installed. Raises ``SystemExit`` if the user declines or installation fails. """ @@ -198,9 +198,15 @@ def _resolve_package(package_arg: Optional[str]) -> Optional[str]: """Resolve a package argument to a Chronus package name.""" if not package_arg: return None - # Resolve relative paths (e.g. ".") to absolute so get_package_from_repo - # doesn't accidentally glob against the repo root. - target = os.path.abspath(package_arg) if os.path.exists(package_arg) else package_arg + path_like = ( + os.path.isabs(package_arg) or package_arg.startswith(".") or os.sep in package_arg or "/" in package_arg + ) + if os.path.isabs(package_arg): + target = os.path.abspath(package_arg) + elif path_like: + target = os.path.join(REPO_ROOT, package_arg) + else: + target = package_arg try: parsed = get_package_from_repo(target, REPO_ROOT) return parsed.name if parsed else package_arg diff --git a/eng/tools/azure-sdk-tools/emitter/generated/README.md b/eng/tools/azure-sdk-tools/emitter/generated/README.md deleted file mode 100644 index 2fc43d6e5172..000000000000 --- a/eng/tools/azure-sdk-tools/emitter/generated/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# TypeSpec Python Generated Tests - -This folder contains the generated Python SDK test code produced by -`@typespec/http-client-python` against the [http-specs](https://github.com/microsoft/typespec/tree/main/packages/http-specs) -and [azure-http-specs](https://github.com/Azure/typespec-azure/tree/main/packages/azure-http-specs) test suites. - -> **These files live on the [`typespec-python-generated-tests`](https://github.com/Azure/azure-sdk-for-python/tree/typespec-python-generated-tests/eng/tools/azure-sdk-tools/emitter/generated) branch, not `main`.** -> The branch is machine-managed and may be force-pushed by the regeneration workflow. - -## Structure - -``` -azure/ # Generated from azure-http-specs (branded/Azure flavor) -unbranded/ # Generated from http-specs (unbranded flavor) -``` - -## Regeneration - -These files are automatically regenerated by the -[`typespec-python-regenerate.yml`](https://github.com/Azure/azure-sdk-for-python/blob/main/.github/workflows/typespec-python-regenerate.yml) workflow -and pushed directly to the [`typespec-python-generated-tests`](https://github.com/Azure/azure-sdk-for-python/tree/typespec-python-generated-tests/eng/tools/azure-sdk-tools/emitter/generated) branch. - -To regenerate manually, trigger the workflow via GitHub Actions or run locally: - -```bash -# From the microsoft/typespec repo (packages/http-client-python): -npm install --ignore-scripts -npm run build -npm run install -npm run prepare -npm run regenerate - -# Then copy tests/generated/{azure,unbranded} to this folder -``` diff --git a/eng/tools/azure-sdk-tools/emitter/generated/template/README.md b/eng/tools/azure-sdk-tools/emitter/generated/template/README.md deleted file mode 100644 index e168787b1af1..000000000000 --- a/eng/tools/azure-sdk-tools/emitter/generated/template/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# ⚠️ TEST CODE — DO NOT INSTALL FROM PYPI - -This is not a published Azure SDK package; it exists only for testing purposes. Do not install from PyPI. diff --git a/eng/tools/azure-sdk-tools/packaging_tools/sdk_generator.py b/eng/tools/azure-sdk-tools/packaging_tools/sdk_generator.py index e9624a5db813..c5a40c6e3496 100644 --- a/eng/tools/azure-sdk-tools/packaging_tools/sdk_generator.py +++ b/eng/tools/azure-sdk-tools/packaging_tools/sdk_generator.py @@ -51,6 +51,72 @@ def execute_func_with_timeout(func, timeout: int = 900) -> Any: return multiprocessing.Pool(processes=1).apply_async(func).get(timeout) +# Hard-coded name of the optional post-emitter script. If a service team places a +# script with this name in the generated package folder (sdk//azure-*), +# it will be executed after code generation. +POST_EMITTER_SCRIPT_NAME = "PostEmitter.ps1" + + +def run_post_emitter_script(sdk_code_path: str) -> None: + """Run the optional post-emitter PowerShell script for a package, if present. + + When a script whose name matches ``POST_EMITTER_SCRIPT_NAME`` exists directly + inside the generated package folder (``sdk//azure-*``), it is executed + after code generation so service teams can run custom post-processing on the + generated SDK. The script's stdout/stderr are captured and logged so they appear + in the pipeline output. Failures are logged but never fail the overall generation. + """ + package_folder = Path(sdk_code_path).resolve() + script_path = package_folder / POST_EMITTER_SCRIPT_NAME + + # Run the script only when it exists; otherwise this is a no-op. + if not script_path.is_file(): + _LOGGER.info(f"[POST-EMITTER] Skip running post-emitter script since file {script_path} was not found.") + return + + pwsh = shutil.which("pwsh") or shutil.which("powershell") + if not pwsh: + _LOGGER.warning( + f"[POST-EMITTER] Found {script_path} but no PowerShell executable (pwsh/powershell) is available; skipping." + ) + return + + _LOGGER.info(f"[POST-EMITTER] Running post-emitter script: {script_path}") + post_emitter_start_time = time.time() + try: + process = subprocess.run( + [ + pwsh, + "-NonInteractive", + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + str(script_path), + ], + cwd=str(package_folder), + capture_output=True, + text=True, + timeout=600, + ) + if process.stdout: + _LOGGER.info(f"[POST-EMITTER] stdout:\n{process.stdout}") + if process.stderr: + _LOGGER.warning(f"[POST-EMITTER] stderr:\n{process.stderr}") + if process.returncode != 0: + _LOGGER.warning(f"[POST-EMITTER] Script {script_path} exited with non-zero code {process.returncode}.") + else: + _LOGGER.info(f"[POST-EMITTER] Script {script_path} completed successfully.") + except subprocess.TimeoutExpired: + _LOGGER.warning(f"[POST-EMITTER] Script {script_path} timed out after 600 seconds.") + except Exception as e: + _LOGGER.warning(f"[POST-EMITTER] Fail to run script {script_path}: {str(e)}") + finally: + _LOGGER.info( + f"[POST-EMITTER] post-emitter script cost time: {int(time.time() - post_emitter_start_time)} seconds" + ) + + # return relative path like: network/azure-mgmt-network def extract_sdk_folder(python_md: List[str]) -> str: pattern = ["$(python-sdks-folder)", "azure-mgmt-"] @@ -242,6 +308,9 @@ def main(generate_input, generate_output): readme_or_tsp=readme_or_tsp, ) + # Run optional post-emitter script provided by the service team + run_post_emitter_script(sdk_code_path) + # Generate ApiView if data.get("runMode") in ["spec-pull-request"]: apiview_start_time = time.time() diff --git a/eng/tools/azure-sdk-tools/tests/test_apistub.py b/eng/tools/azure-sdk-tools/tests/test_apistub.py index 85a60a407794..26af6468945e 100644 --- a/eng/tools/azure-sdk-tools/tests/test_apistub.py +++ b/eng/tools/azure-sdk-tools/tests/test_apistub.py @@ -73,29 +73,101 @@ def test_no_prebuilt_dir_falls_back_to_pkg_root(self, mock_find_whl, mock_parsed class TestRunOutputDirectory: """Verify that dest_dir controls where the output token path ends up.""" - def _make_args(self, dest_dir=None, generate_md=False): + def _make_args(self, dest_dir=None, generate_md=False, isolate=False, install_deps=False): return argparse.Namespace( target=".", - isolate=False, + isolate=isolate, command="apistub", service=None, dest_dir=dest_dir, generate_md=generate_md, + install_deps=install_deps, ) @patch( "azpysdk.apistub.REPO_ROOT", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) ) - @patch("azpysdk.apistub.PYTHON_VERSION_LIMIT", (99, 99)) @patch("azpysdk.apistub.get_cross_language_mapping_path", return_value=None) @patch("azpysdk.apistub.get_package_wheel_path", return_value="/fake/pkg.whl") @patch("azpysdk.apistub.create_package_and_install") @patch("azpysdk.apistub.install_into_venv") @patch("azpysdk.apistub.set_envvar_defaults") - def test_dest_dir_creates_package_subfolder( + def test_isolate_does_not_install_dependencies( + self, _env, install_into_venv, _create, _get_whl, _get_mapping, tmp_path, monkeypatch + ): + """When only --isolate is passed, apistub should not install dependencies.""" + monkeypatch.chdir(os.getcwd()) + stub = apistub() + staging = str(tmp_path / "staging") + os.makedirs(staging, exist_ok=True) + fake_parsed = MagicMock() + fake_parsed.folder = str(tmp_path) + fake_parsed.name = "azure-core" + + with patch.object(stub, "get_targeted_directories", return_value=[fake_parsed]), patch.object( + stub, "get_executable", return_value=(sys.executable, staging) + ), patch.object(stub, "install_dev_reqs") as install_dev_reqs, patch.object( + stub, "pip_freeze" + ) as pip_freeze, patch.object( + stub, "run_venv_command" + ): + stub.run(self._make_args(isolate=True)) + + install_dev_reqs.assert_not_called() + install_into_venv.assert_not_called() + pip_freeze.assert_not_called() + + @patch( + "azpysdk.apistub.REPO_ROOT", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) + ) + @patch("azpysdk.apistub.get_cross_language_mapping_path", return_value=None) + @patch("azpysdk.apistub.get_package_wheel_path", return_value="/fake/pkg.whl") + @patch("azpysdk.apistub.create_package_and_install") + @patch("azpysdk.apistub.install_into_venv") + @patch("azpysdk.apistub.set_envvar_defaults") + def test_install_deps_installs_dependencies( + self, _env, install_into_venv, _create, _get_whl, _get_mapping, tmp_path, monkeypatch + ): + """When --install-deps is passed, apistub should install dependencies.""" + monkeypatch.chdir(os.getcwd()) + stub = apistub() + staging = str(tmp_path / "staging") + os.makedirs(staging, exist_ok=True) + fake_parsed = MagicMock() + fake_parsed.folder = str(tmp_path) + fake_parsed.name = "azure-core" + + with patch.object(stub, "get_targeted_directories", return_value=[fake_parsed]), patch.object( + stub, "get_executable", return_value=(sys.executable, staging) + ), patch.object(stub, "install_dev_reqs") as install_dev_reqs, patch.object( + stub, "pip_freeze" + ) as pip_freeze, patch.object( + stub, "run_venv_command" + ): + args = self._make_args(install_deps=True) + stub.run(args) + + install_dev_reqs.assert_called_once_with(sys.executable, args, str(tmp_path)) + install_into_venv.assert_called_once() + repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) + assert install_into_venv.call_args.args[1][0:2] == [ + "-r", + os.path.join(repo_root, "eng", "apiview_reqs.txt"), + ] + pip_freeze.assert_called_once_with(sys.executable) + + @patch( + "azpysdk.apistub.REPO_ROOT", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) + ) + @patch("azpysdk.apistub.get_cross_language_mapping_path", return_value=None) + @patch("azpysdk.apistub.get_package_wheel_path", return_value="/fake/pkg.whl") + @patch("azpysdk.apistub.create_package_and_install") + @patch("azpysdk.apistub.install_into_venv") + @patch("azpysdk.apistub.set_envvar_defaults") + def test_dest_dir_uses_destination_directory( self, _env, _install, _create, _get_whl, _get_mapping, tmp_path, monkeypatch ): - """When --dest-dir is given, output should go to //.""" + """When --dest-dir is given, output should go directly to /.""" monkeypatch.chdir(os.getcwd()) dest = tmp_path / "output" dest.mkdir() @@ -131,7 +203,7 @@ def fake_pwsh(cmd, **kwargs): stub.run(self._make_args(dest_dir=str(dest), generate_md=True)) - expected_out = os.path.join(str(dest), "azure-core") + expected_out = str(dest) assert os.path.isdir(expected_out) assert os.path.exists(os.path.join(expected_out, "api.md")) assert os.path.exists(os.path.join(expected_out, "azure-core_python.json")) @@ -139,7 +211,6 @@ def fake_pwsh(cmd, **kwargs): @patch( "azpysdk.apistub.REPO_ROOT", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) ) - @patch("azpysdk.apistub.PYTHON_VERSION_LIMIT", (99, 99)) @patch("azpysdk.apistub.get_cross_language_mapping_path", return_value=None) @patch("azpysdk.apistub.get_package_wheel_path", return_value="/fake/pkg.whl") @patch("azpysdk.apistub.create_package_and_install") @@ -191,7 +262,6 @@ def fake_pwsh(cmd, **kwargs): @patch( "azpysdk.apistub.REPO_ROOT", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) ) - @patch("azpysdk.apistub.PYTHON_VERSION_LIMIT", (99, 99)) @patch("azpysdk.apistub.get_cross_language_mapping_path", return_value=None) @patch("azpysdk.apistub.get_package_wheel_path", return_value="/fake/pkg.whl") @patch("azpysdk.apistub.create_package_and_install") @@ -235,7 +305,6 @@ def fake_pwsh(cmd, **kwargs): @patch( "azpysdk.apistub.REPO_ROOT", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) ) - @patch("azpysdk.apistub.PYTHON_VERSION_LIMIT", (99, 99)) @patch("azpysdk.apistub.get_cross_language_mapping_path", return_value=None) @patch("azpysdk.apistub.get_package_wheel_path", return_value="/fake/pkg.whl") @patch("azpysdk.apistub.create_package_and_install") diff --git a/eng/tools/azure-sdk-tools/tests/test_changelog_commands.py b/eng/tools/azure-sdk-tools/tests/test_changelog_commands.py index 7e07d9ff177a..ef02b7a69ad8 100644 --- a/eng/tools/azure-sdk-tools/tests/test_changelog_commands.py +++ b/eng/tools/azure-sdk-tools/tests/test_changelog_commands.py @@ -161,7 +161,7 @@ class TestChangelogExecution: All tests in this class patch ``_is_chronus_installed`` to return True so the installation check is bypassed. Chronus is invoked via the - pinned binary at ``.github/chronus/node_modules/.bin/chronus``. + pinned binary at ``.github/node_modules/.bin/chronus``. """ @patch("azpysdk.changelog.changelog._is_chronus_installed", return_value=True) @@ -303,7 +303,7 @@ def test_explicit_path_resolves_name(self, mock_get_pkg): mock_get_pkg.return_value = SimpleNamespace(name="azure-core") result = changelog._resolve_package("sdk/core/azure-core") assert result == "azure-core" - mock_get_pkg.assert_called_once_with("sdk/core/azure-core", REPO_ROOT) + mock_get_pkg.assert_called_once_with(os.path.join(REPO_ROOT, "sdk/core/azure-core"), REPO_ROOT) @patch("azpysdk.changelog.get_package_from_repo") def test_bare_name_resolves(self, mock_get_pkg): @@ -356,9 +356,9 @@ def test_non_interactive_with_auto_install_env(self, mock_stdin, mock_which, moc mock_call.assert_called_once() cmd = mock_call.call_args[0][0] assert cmd == ["/usr/bin/npm", "ci"] - # And it must run from the .github/chronus directory, not repo root. + # And it must run from the .github directory, not repo root. _, kwargs = mock_call.call_args - assert kwargs["cwd"].endswith(os.path.join(".github", "chronus")) + assert kwargs["cwd"].endswith(".github") @patch("azpysdk.changelog.changelog._is_chronus_installed", return_value=False) @patch("azpysdk.changelog.shutil.which", return_value="/usr/bin/npm") diff --git a/eng/tools/emitter/gen/template/README.md b/eng/tools/emitter/gen/template/README.md deleted file mode 100644 index e168787b1af1..000000000000 --- a/eng/tools/emitter/gen/template/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# ⚠️ TEST CODE — DO NOT INSTALL FROM PYPI - -This is not a published Azure SDK package; it exists only for testing purposes. Do not install from PyPI. diff --git a/scripts/api_md_workflow/README.md b/scripts/api_md_workflow/README.md new file mode 100644 index 000000000000..4d96401c29a5 --- /dev/null +++ b/scripts/api_md_workflow/README.md @@ -0,0 +1,21 @@ +# API Review PR Helper + +This folder contains the standalone Python helper used to create API review PRs from generated `api.md` files. + +## Purpose + +`create_api_review_pr.py` compares a baseline package release tag with a target API surface, creates or reuses dedicated API review branches, and opens a draft PR that shows the `api.md` diff. + +The API consistency workflow helpers live under `.github/workflows/src/api-md-consistency`. + +## Usage + +The script includes Python package discovery, version parsing, `api.md` generation, git branch orchestration, and GitHub PR creation in one file. + +`create_api_review_pr.py` compares a baseline package release tag with a target API surface. The target can be a package release tag, an `origin` branch, or an `owner:branch` fork reference. When the target is a tag, the generated PR body identifies it as a target tag instead of a working branch. + +Example comparing two package release tags: + +```bash +python scripts/api_md_workflow/create_api_review_pr.py --package-name azure-ai-projects --base azure-ai-projects_2.1.0 --target azure-ai-projects_2.2.0 +``` diff --git a/scripts/api_md_workflow/create_api_review_pr.py b/scripts/api_md_workflow/create_api_review_pr.py new file mode 100644 index 000000000000..e9e823682cf5 --- /dev/null +++ b/scripts/api_md_workflow/create_api_review_pr.py @@ -0,0 +1,994 @@ +#!/usr/bin/env python3 + +import argparse +import json +import os +import re +import shutil +import subprocess +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Callable +from urllib.error import HTTPError, URLError +from urllib.parse import quote, urlencode +from urllib.request import Request, urlopen + + +REPO_ROOT = Path(__file__).resolve().parents[2] +REPO_OWNER = "Azure" +REPO_NAME = "azure-sdk-for-python" +REPO_SLUG = f"{REPO_OWNER}/{REPO_NAME}" +REMOTE = "origin" +MAIN_REF = f"{REMOTE}/main" +SYNC_METADATA_MARKER = "api-md-review-sync" +SYNC_METADATA_WARNING = "DO NOT MODIFY THESE CONTENTS!" +GITHUB_API_TIMEOUT_SECONDS = 30 + + +class GitHubApiError(Exception): + def __init__(self, status: int, message: str): + super().__init__(message) + self.status = status + + +@dataclass +class CommandResult: + status: int + stdout: str = "" + stderr: str = "" + + +@dataclass +class ApiResult: + api_md: bytes + metadata: bytes | None + version: str + + +@dataclass +class BranchState: + has_api_md: bool + has_metadata: bool + api_md_sha256: str | None + + +@dataclass +class BranchSelection: + branch_name: str + reused: bool + remote_ref: str | None + + +GitRunner = Callable[[list[str], bool], CommandResult] +_git_runner: GitRunner | None = None +_github_api: "GitHubApi | None" = None + + +def set_command_runner_for_test(git_runner: GitRunner | None) -> None: + global _git_runner + _git_runner = git_runner + + +def set_github_api_for_test(api: "GitHubApi | None") -> None: + global _github_api + _github_api = api + + +def log_info(message: str) -> None: + print(message) + + +def log_warning(message: str) -> None: + print(message, file=sys.stderr) + + +def log_error(message: str) -> None: + print(message, file=sys.stderr) + + +def run(args: list[str], *, cwd: Path = REPO_ROOT, check: bool = True, capture: bool = False, shell: bool = False) -> CommandResult: + printable = " ".join(args) + log_info(f"$ {printable}") + completed = subprocess.run( + args, + cwd=cwd, + check=False, + capture_output=capture, + text=True, + shell=shell, + ) + result = CommandResult(completed.returncode, completed.stdout or "", completed.stderr or "") + if check and result.status != 0: + raise RuntimeError(f"Command failed ({result.status}): {printable}") + return result + + +def git(args: list[str], *, check: bool = True) -> CommandResult: + if _git_runner: + result = _git_runner(args, check) + if check and result.status != 0: + raise RuntimeError(f"Command failed ({result.status}): {' '.join(['git', *args])}") + return result + return run(["git", *args], check=check, capture=True) + + +def git_out(args: list[str]) -> str: + return git(args).stdout.strip() + + +def resolve_github_token() -> str | None: + token = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN") + if token: + return token + + gh = shutil.which("gh") + if not gh: + return None + + try: + return subprocess.run( + [gh, "auth", "token"], + cwd=REPO_ROOT, + check=True, + capture_output=True, + text=True, + ).stdout.strip() + except subprocess.SubprocessError: + return None + + +def normalize_pull_request(pr: dict[str, Any] | None) -> dict[str, Any] | None: + if not isinstance(pr, dict): + return None + + owner_login = None + if isinstance(pr.get("headRepositoryOwner"), dict): + owner_login = pr["headRepositoryOwner"].get("login") + elif isinstance(pr.get("head"), dict): + repo = pr["head"].get("repo") if isinstance(pr["head"].get("repo"), dict) else {} + owner = repo.get("owner") if isinstance(repo.get("owner"), dict) else {} + owner_login = owner.get("login") + + return { + "number": pr.get("number"), + "url": pr.get("url") or pr.get("html_url"), + "state": pr.get("state"), + "updatedAt": pr.get("updatedAt") or pr.get("updated_at"), + "body": pr.get("body"), + "headRefName": pr.get("headRefName") or (pr.get("head") or {}).get("ref"), + "headRepositoryOwner": {"login": owner_login}, + } + + +class GitHubApi: + def __init__(self, token: str | None): + self.token = token + + def _request(self, method: str, url: str, payload: dict[str, Any] | None = None) -> Any: + data = json.dumps(payload).encode("utf-8") if payload is not None else None + headers = { + "Accept": "application/vnd.github+json", + "User-Agent": "azure-sdk-python-api-md-workflow", + "X-GitHub-Api-Version": "2022-11-28", + } + if self.token: + headers["Authorization"] = f"Bearer {self.token}" + if data is not None: + headers["Content-Type"] = "application/json" + + request = Request(url, data=data, headers=headers, method=method) + try: + with urlopen(request, timeout=GITHUB_API_TIMEOUT_SECONDS) as response: + body = response.read().decode("utf-8") + return json.loads(body) if body else None + except HTTPError as error: + details = error.read().decode("utf-8", errors="replace") + raise GitHubApiError(error.code, details or str(error)) from error + except TimeoutError as error: + raise GitHubApiError(1, f"GitHub API request timed out: {error}") from error + except URLError as error: + raise GitHubApiError(1, str(error)) from error + + def _rest_url(self, path: str, query: dict[str, Any] | None = None) -> str: + url = f"https://api.github.com{path}" + if query: + url = f"{url}?{urlencode(query)}" + return url + + def list_pull_requests_by_head(self, head: str, limit: int) -> list[dict[str, Any]]: + data = self._request( + "GET", + self._rest_url( + f"/repos/{REPO_OWNER}/{REPO_NAME}/pulls", + {"head": head, "state": "open", "per_page": limit}, + ), + ) + return [pr for pr in (normalize_pull_request(item) for item in data or []) if pr] + + def search_pull_requests(self, query: str, limit: int) -> list[dict[str, Any]]: + graphql_query = """ +query($query: String!, $first: Int!) { + search(query: $query, type: ISSUE, first: $first) { + nodes { + ... on PullRequest { + number + url + state + updatedAt + body + headRefName + headRepositoryOwner { login } + } + } + } +} +""" + data = self._request( + "POST", + "https://api.github.com/graphql", + {"query": graphql_query, "variables": {"query": query, "first": limit}}, + ) + nodes = ((data or {}).get("data") or {}).get("search", {}).get("nodes", []) + return [pr for pr in (normalize_pull_request(item) for item in nodes) if pr] + + def list_pull_requests_by_branches(self, base: str, head: str, limit: int) -> list[dict[str, Any]]: + data = self._request( + "GET", + self._rest_url( + f"/repos/{REPO_OWNER}/{REPO_NAME}/pulls", + {"base": base, "head": f"{REPO_OWNER}:{head}", "state": "open", "per_page": limit}, + ), + ) + return [pr for pr in (normalize_pull_request(item) for item in data or []) if pr] + + def update_pull_request_body(self, number: int, body: str) -> None: + self._request("PATCH", self._rest_url(f"/repos/{REPO_OWNER}/{REPO_NAME}/pulls/{number}"), {"body": body}) + + def create_draft_pull_request(self, base: str, head: str, title: str, body: str) -> dict[str, Any]: + return self._request( + "POST", + self._rest_url(f"/repos/{REPO_OWNER}/{REPO_NAME}/pulls"), + {"base": base, "head": head, "title": title, "body": body, "draft": True}, + ) + + +def get_github_api() -> GitHubApi: + global _github_api + if _github_api is None: + _github_api = GitHubApi(resolve_github_token()) + return _github_api + + +def ensure_clean_worktree() -> None: + status = git_out(["status", "--porcelain"]) + if status: + raise RuntimeError(f"ERROR: working tree is not clean. Commit or stash changes before running.\n{status}") + + +def current_branch() -> str: + return git_out(["rev-parse", "--abbrev-ref", "HEAD"]) + + +def tag_exists(tag: str) -> bool: + return git(["rev-parse", "--verify", "--quiet", f"refs/tags/{tag}"], check=False).status == 0 + + +def validate_base_tag(package_name: str, base_tag: str) -> str: + prefix = f"{package_name}_" + if not base_tag.startswith(prefix): + raise RuntimeError(f"ERROR: --base tag '{base_tag}' must start with '{prefix}'.") + + version = base_tag[len(prefix) :] + if not version: + raise RuntimeError(f"ERROR: --base tag '{base_tag}' is missing the version suffix.") + + if not tag_exists(base_tag): + raise RuntimeError(f"ERROR: tag '{base_tag}' does not exist in this repository.") + + return version + + +def is_explicit_package_tag(target: str, package_name: str | None = None) -> bool: + if ":" in target: + return False + if package_name: + return target.startswith(f"{package_name}_") + return "_" in target + + +def resolve_target_tag(target: str, package_name: str | None = None) -> str | None: + if not is_explicit_package_tag(target, package_name): + return None + if tag_exists(target): + return target + git(["fetch", REMOTE, "tag", target], check=False) + return target if tag_exists(target) else None + + +def try_remote_branch_ref(branch: str) -> str | None: + remote_ref = f"refs/remotes/{REMOTE}/{branch}" + result = git(["fetch", REMOTE, f"{branch}:{remote_ref}"], check=False) + return f"{REMOTE}/{branch}" if result.status == 0 else None + + +def fork_url(owner: str) -> str: + return f"https://github.com/{owner}/{REPO_NAME}.git" + + +def try_fork_branch_ref(owner: str, branch: str) -> str | None: + result = git(["fetch", fork_url(owner), branch], check=False) + return "FETCH_HEAD" if result.status == 0 else None + + +def resolve_target_ref(target: str, package_name: str | None = None) -> str: + if ":" not in target: + target_tag = resolve_target_tag(target, package_name) + if target_tag: + return target_tag + + branch_ref = try_remote_branch_ref(target) + if branch_ref: + return branch_ref + + raise RuntimeError(f"ERROR: --target '{target}' is neither a branch on {REMOTE} nor a tag in this repository.") + + owner, branch = target.split(":", 1) + if not owner or not branch: + raise RuntimeError(f"ERROR: invalid --target '{target}'. Expected 'tag', 'branch', or 'owner:branch'.") + + branch_ref = try_fork_branch_ref(owner, branch) + if not branch_ref: + raise RuntimeError(f"ERROR: branch '{branch}' does not exist in fork '{owner}'.") + return branch_ref + + +def walk_files(start_dir: Path): + for root, _, files in os.walk(start_dir): + for file_name in files: + yield Path(root) / file_name + + +def find_package_dir(package_name: str) -> Path: + sdk_dir = REPO_ROOT / "sdk" + matches: list[Path] = [] + for service_dir in sdk_dir.iterdir(): + if not service_dir.is_dir(): + continue + candidate = service_dir / package_name + if not candidate.is_dir(): + continue + if (candidate / "pyproject.toml").exists() or (candidate / "setup.py").exists(): + matches.append(candidate) + + if not matches: + raise RuntimeError(f"ERROR: package '{package_name}' not found under sdk/*/") + if len(matches) > 1: + raise RuntimeError(f"ERROR: multiple matches for '{package_name}': {', '.join(str(match) for match in matches)}") + return matches[0] + + +def read_version(package_dir: Path) -> str: + version_regex = re.compile(r"^\s*VERSION\s*[:=]\s*[\"']([^\"']+)[\"']", re.MULTILINE) + candidates: list[Path] = [] + for file_path in walk_files(package_dir): + if file_path.name not in {"_version.py", "version.py"}: + continue + relative = file_path.relative_to(package_dir).as_posix() + if "_generated" in relative or "generated_" in relative: + continue + candidates.append(file_path) + + for candidate in candidates: + try: + text = candidate.read_text(encoding="utf-8") + except OSError: + continue + match = version_regex.search(text) + if match: + return match.group(1) + + raise RuntimeError(f"ERROR: could not find a version string in {package_dir}") + + +def generate_api_for_package(package_name: str, runtime_executable: str | None, ref_label: str | None = None) -> None: + if ref_label: + log_info(f"--- Generating api.md on {ref_label} ---") + + package_dir = find_package_dir(package_name) + if runtime_executable or os.environ.get("RUNTIME_EXECUTABLE"): + python_executable = runtime_executable or os.environ["RUNTIME_EXECUTABLE"] + run( + [ + python_executable, + "-m", + "azpysdk.main", + "apistub", + "--md", + "--extract-metadata", + "--dest-dir", + str(package_dir), + package_name, + ], + check=True, + ) + return + + run( + ["azpysdk", "apistub", "--md", "--extract-metadata", "--dest-dir", str(package_dir), package_name], + check=True, + shell=sys.platform == "win32", + ) + + +def package_rel_dir(package_dir: Path) -> str: + return package_dir.relative_to(REPO_ROOT).as_posix() + + +def normalize_package_dir(package_dir: Path | str) -> str: + path_value = Path(package_dir) + if path_value.is_absolute(): + return path_value.relative_to(REPO_ROOT).as_posix() + return str(package_dir).replace("\\", "/") + + +def api_md_path(package_dir: Path) -> Path: + return package_dir / "api.md" + + +def api_md_rel(package_dir: Path) -> str: + return f"{package_rel_dir(package_dir)}/api.md" + + +def metadata_path(package_dir: Path) -> Path: + return package_dir / "api.metadata.yml" + + +def metadata_rel(package_dir: Path) -> str: + return f"{package_rel_dir(package_dir)}/api.metadata.yml" + + +def api_review_branch_name(kind: str, package_name: str, version: str) -> str: + return f"apireview/{kind}_{package_name}_{version}" + + +def parse_simple_yaml(text: str) -> dict[str, str]: + result: dict[str, str] = {} + for line in text.splitlines(): + match = re.match(r"^(\w+)\s*:\s*(.*)$", line) + if match: + result[match.group(1)] = match.group(2).strip() + return result + + +def metadata_sha_or_none(metadata_bytes: bytes | None) -> str | None: + if not metadata_bytes: + return None + metadata = parse_simple_yaml(metadata_bytes.decode("utf-8")) + return metadata.get("apiMdSha256") + + +def branch_remote_ref(branch: str) -> str: + return f"{REMOTE}/{branch}" + + +def list_remote_branches_with_prefix(prefix: str) -> list[str]: + result = git(["ls-remote", "--heads", REMOTE, f"refs/heads/{prefix}*"], check=False) + if result.status != 0 or not result.stdout.strip(): + return [] + + branches = [] + for line in result.stdout.splitlines(): + parts = line.strip().split(None, 1) + if len(parts) < 2 or not parts[1].startswith("refs/heads/"): + continue + branch = parts[1][len("refs/heads/") :] + if branch == prefix or branch.startswith(f"{prefix}_"): + branches.append(branch) + return branches + + +def fetch_remote_branch(branch: str) -> str: + git(["fetch", REMOTE, branch]) + return branch_remote_ref(branch) + + +def read_ref_file_bytes(ref: str, relative_path: str) -> bytes | None: + result = git(["show", f"{ref}:{relative_path}"], check=False) + if result.status != 0: + return None + return result.stdout.encode("utf-8") + + +def desired_branch_state(result: ApiResult | None) -> BranchState: + if result is None: + return BranchState(False, False, None) + return BranchState(True, bool(result.metadata), metadata_sha_or_none(result.metadata)) + + +def api_results_have_api_diff(base_result: ApiResult, target_result: ApiResult) -> bool: + return base_result.api_md != target_result.api_md + + +def branch_state_matches_desired(actual: BranchState, desired: BranchState) -> bool: + return actual == desired + + +def read_branch_state(ref: str, api_relative: str, meta_relative: str) -> BranchState: + metadata_bytes = read_ref_file_bytes(ref, meta_relative) + api_md_bytes = read_ref_file_bytes(ref, api_relative) + return BranchState(bool(api_md_bytes), bool(metadata_bytes), metadata_sha_or_none(metadata_bytes)) + + +def branch_suffix_from_index(index: int) -> str: + value = index + suffix = "" + while True: + suffix = chr(97 + (value % 26)) + suffix + value = value // 26 - 1 + if value < 0: + return suffix + + +def next_available_branch_name(preferred_branch: str, existing_branches: set[str]) -> str: + if preferred_branch not in existing_branches: + return preferred_branch + + index = 0 + while f"{preferred_branch}_{branch_suffix_from_index(index)}" in existing_branches: + index += 1 + return f"{preferred_branch}_{branch_suffix_from_index(index)}" + + +def is_ancestor_ref(ancestor_ref: str, branch_ref: str) -> bool: + return git(["merge-base", "--is-ancestor", ancestor_ref, branch_ref], check=False).status == 0 + + +def resolve_branch_selection( + *, + preferred_branch: str, + desired_state: BranchState, + api_relative: str, + meta_relative: str, + required_ancestor_ref: str | None = None, +) -> BranchSelection: + existing_branches = set(list_remote_branches_with_prefix(preferred_branch)) + ordered_candidates = sorted(existing_branches, key=lambda branch: (branch != preferred_branch, branch)) + + for candidate_branch in ordered_candidates: + remote_ref = fetch_remote_branch(candidate_branch) + actual_state = read_branch_state(remote_ref, api_relative, meta_relative) + if not branch_state_matches_desired(actual_state, desired_state): + continue + if required_ancestor_ref and not is_ancestor_ref(required_ancestor_ref, remote_ref): + continue + return BranchSelection(candidate_branch, True, remote_ref) + + return BranchSelection(next_available_branch_name(preferred_branch, existing_branches), False, None) + + +def ensure_branch_state_has_metadata_sha(branch_label: str, state: BranchState) -> None: + if state.has_api_md and not state.api_md_sha256: + raise RuntimeError(f"ERROR: {branch_label} is missing apiMdSha256 in api.metadata.yml.") + + +def select_best_pr(prs: list[dict[str, Any]]) -> dict[str, Any] | None: + candidates = [pr for pr in prs if pr.get("number") is not None and pr.get("url") and pr.get("state") and pr.get("updatedAt")] + if not candidates: + return None + open_prs = [pr for pr in candidates if str(pr.get("state", "")).lower() == "open"] + pool = open_prs or candidates + return sorted(pool, key=lambda pr: str(pr.get("updatedAt") or ""), reverse=True)[0] + + +def branch_reference_parts(head_selector: str) -> dict[str, str]: + if head_selector == MAIN_REF: + return {"owner": REPO_OWNER, "branch": "main", "display": head_selector} + if ":" in head_selector: + owner, branch = head_selector.split(":", 1) + return {"owner": owner, "branch": branch, "display": head_selector} + return {"owner": REPO_OWNER, "branch": head_selector, "display": head_selector} + + +def target_branch_exists(head_selector: str) -> bool: + parts = branch_reference_parts(head_selector) + if parts["owner"] == REPO_OWNER: + return bool(try_remote_branch_ref(parts["branch"])) + return bool(try_fork_branch_ref(parts["owner"], parts["branch"])) + + +def sync_working_branch_info(head_selector: str | None, package_name: str | None = None) -> dict[str, str] | None: + if not head_selector: + return None + if resolve_target_tag(head_selector, package_name): + return None + if target_branch_exists(head_selector): + parts = branch_reference_parts(head_selector) + return {"owner": parts["owner"], "branch": parts["branch"]} + return None + + +def build_sync_metadata_object( + *, + package_name: str, + package_dir: Path | str, + base_branch: str, + review_branch: str, + head_selector: str, +) -> dict[str, Any] | None: + working_branch = sync_working_branch_info(head_selector, package_name) + if not working_branch: + return None + + metadata: dict[str, Any] = { + "schemaVersion": 1, + "repository": REPO_SLUG, + "packageName": package_name, + "packageDir": normalize_package_dir(package_dir), + "baseBranch": base_branch, + "reviewBranch": review_branch, + "workingOwner": working_branch["owner"], + "workingBranch": working_branch["branch"], + } + working_pr = find_open_pr_for_head(head_selector) + metadata["workingPrNumber"] = working_pr.get("number") if working_pr and isinstance(working_pr.get("number"), int) else None + return metadata + + +def build_sync_metadata_block(metadata: dict[str, Any] | None) -> str | None: + if not metadata: + return None + return "\n".join( + [ + f"", + ] + ) + + +def replace_sync_metadata_block(body: str | None, metadata_block: str | None) -> str: + cleaned_body = re.sub(rf"\s*", "", str(body or "")).rstrip() + if not metadata_block: + return cleaned_body + return f"{cleaned_body}\n\n{metadata_block}" + + +def build_review_pr_body( + *, + package_name: str, + target_version: str, + base_version: str, + working_reference: dict[str, str], + baseline_ref: str, + sync_metadata_block: str | None, +) -> str: + lines = [ + f"Automated API review PR for {package_name}.", + "", + f"- **{working_reference['label']}:** {working_reference['markdown']} (version {target_version})", + f"- **Baseline:** {baseline_ref} (version {base_version})", + ] + if working_reference["label"] == "Target tag": + lines.extend( + [ + "", + "> [!WARNING]", + "> Static tag-to-tag review; this PR cannot be automatically updated from a working branch.", + ] + ) + lines.extend(["", "Generated by scripts/api_md_workflow/create_api_review_pr.py."]) + return replace_sync_metadata_block("\n".join(lines), sync_metadata_block) + + +def update_pr_body(pr_number: int, body: str) -> None: + get_github_api().update_pull_request_body(pr_number, body) + + +def ensure_pr_body_sync_metadata(pr: dict[str, Any] | None, metadata_block: str | None) -> None: + if not metadata_block or not pr or not isinstance(pr.get("number"), int): + return + desired_body = replace_sync_metadata_block(pr.get("body") or "", metadata_block) + if desired_body == (pr.get("body") or ""): + return + try: + update_pr_body(pr["number"], desired_body) + log_info(f"Updated API review sync metadata on PR #{pr['number']}.") + except Exception as error: # pylint: disable=broad-except + details = str(error) + log_warning(f"WARNING: failed to update API review sync metadata on PR #{pr['number']}." + (f"\n {details}" if details else "")) + + +def find_open_pr_for_head(head_selector: str) -> dict[str, Any] | None: + parts = branch_reference_parts(head_selector) + selector = f"{parts['owner']}:{parts['branch']}" + all_prs: list[dict[str, Any]] = [] + github = get_github_api() + + try: + all_prs.extend(github.list_pull_requests_by_head(selector, 50)) + except Exception: # pylint: disable=broad-except + pass + + try: + all_prs.extend(github.search_pull_requests(f"repo:{REPO_SLUG} is:pr is:open head:{parts['branch']}", 50)) + except Exception: # pylint: disable=broad-except + pass + + deduped: dict[int, dict[str, Any]] = {} + for pr in all_prs: + if ( + pr.get("number") is not None + and pr.get("headRefName") == parts["branch"] + and (pr.get("headRepositoryOwner") or {}).get("login") == parts["owner"] + ): + deduped[int(pr["number"])] = pr + return select_best_pr(list(deduped.values())) + + +def find_open_pr_for_branches(base_branch: str, head_branch: str) -> dict[str, Any] | None: + github = get_github_api() + try: + prs = github.list_pull_requests_by_branches(base_branch, head_branch, 20) + if prs: + return select_best_pr(prs) + except Exception: # pylint: disable=broad-except + pass + + try: + prs = github.search_pull_requests(f"repo:{REPO_SLUG} is:pr is:open head:{head_branch} base:{base_branch}", 20) + return select_best_pr(prs) + except Exception: # pylint: disable=broad-except + return None + + +def create_draft_pr(base_branch: str, head_branch: str, title: str, body: str) -> dict[str, Any]: + try: + created_pr = get_github_api().create_draft_pull_request(base_branch, head_branch, title, body) + return {"ok": True, "url": created_pr.get("html_url") or created_pr.get("url") or "", "stderr": "", "stdout": ""} + except GitHubApiError as error: + return {"ok": False, "status": error.status, "stdout": "", "stderr": str(error)} + + +def branch_reference_markdown(head_selector: str) -> str: + parts = branch_reference_parts(head_selector) + branch_url = f"https://github.com/{parts['owner']}/{REPO_NAME}/tree/{quote(parts['branch'], safe='')}" + return f"[branch `{parts['display']}`]({branch_url})" + + +def baseline_reference_markdown(base_tag: str | None) -> str: + if not base_tag: + return "empty" + commit_sha = git_out(["rev-list", "-n", "1", base_tag]) + commit_url = f"https://github.com/{REPO_SLUG}/commit/{commit_sha}" + return f"[tag `{base_tag}`]({commit_url})" + + +def target_reference_info(head_selector: str, package_name: str | None = None) -> dict[str, str]: + target_tag = resolve_target_tag(head_selector, package_name) + if target_tag: + return {"label": "Target tag", "markdown": baseline_reference_markdown(target_tag)} + + if target_branch_exists(head_selector): + pr = find_open_pr_for_head(head_selector) + if pr: + return {"label": "Working PR", "markdown": f"[PR #{pr['number']}]({pr['url']})"} + return {"label": "Working branch", "markdown": branch_reference_markdown(head_selector)} + + return {"label": "Working branch", "markdown": branch_reference_markdown(head_selector)} + + +def write_bytes(file_path: Path, contents: bytes) -> None: + file_path.parent.mkdir(parents=True, exist_ok=True) + file_path.write_bytes(contents) + + +def generate_api_bytes_for_ref( + *, + package_name: str, + package_dir: Path, + runtime_executable: str | None, + ref: str, + ref_label: str, +) -> ApiResult: + package_relative = package_rel_dir(package_dir) + log_info(f"Overlaying package source from {ref_label} ({ref})") + + git(["checkout", ref, "--", package_relative]) + try: + version = read_version(package_dir) + generate_api_for_package(package_name, runtime_executable, ref_label) + + output_path = api_md_path(package_dir) + if not output_path.exists(): + raise RuntimeError(f"ERROR: did not produce {output_path}") + + metadata = metadata_path(package_dir).read_bytes() if metadata_path(package_dir).exists() else None + return ApiResult(output_path.read_bytes(), metadata, version) + finally: + git(["reset", "--", package_relative], check=False) + git(["checkout", "HEAD", "--", package_relative]) + git(["clean", "-fd", "--", package_relative], check=False) + + +def parse_args(argv: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Create an API review PR for a Python package api.md diff.") + parser.add_argument("--package-name", required=True) + parser.add_argument("--base", required=True) + parser.add_argument("--target") + parser.add_argument("--python", "--runtime", dest="runtime_executable", default=os.environ.get("RUNTIME_EXECUTABLE")) + return parser.parse_args(argv) + + +def main(argv: list[str] | None = None) -> int: + args = parse_args(argv or sys.argv[1:]) + package_dir = find_package_dir(args.package_name) + log_info(f"Found package at: {package_dir}") + + ensure_clean_worktree() + original_branch = current_branch() + if original_branch == "HEAD": + raise RuntimeError("ERROR: refusing to run from a detached HEAD.") + + git(["fetch", REMOTE, "main"]) + base_version = validate_base_tag(args.package_name, args.base) + target_ref = resolve_target_ref(args.target, args.package_name) if args.target else MAIN_REF + + try: + log_info(f"\n=== Capturing baseline api.md from tag {args.base} ===") + base_result = generate_api_bytes_for_ref( + package_name=args.package_name, + package_dir=package_dir, + runtime_executable=args.runtime_executable, + ref=args.base, + ref_label=args.base, + ) + + log_info(f"\n=== Capturing target api.md from {target_ref} ===") + target_result = generate_api_bytes_for_ref( + package_name=args.package_name, + package_dir=package_dir, + runtime_executable=args.runtime_executable, + ref=target_ref, + ref_label=target_ref, + ) + target_version = target_result.version + + if not api_results_have_api_diff(base_result, target_result): + log_info( + f"\nNo API differences found for {args.package_name} between {args.base} " + f"(version {base_version}) and {target_ref} (version {target_version}). " + "No API review branches or PR were created." + ) + return 0 + + api_path = api_md_path(package_dir) + api_relative = api_md_rel(package_dir) + meta_file_path = metadata_path(package_dir) + meta_relative = metadata_rel(package_dir) + desired_base_state = desired_branch_state(base_result) + desired_review_state = desired_branch_state(target_result) + + ensure_branch_state_has_metadata_sha("baseline API result", desired_base_state) + ensure_branch_state_has_metadata_sha("target API result", desired_review_state) + + base_selection = resolve_branch_selection( + preferred_branch=api_review_branch_name("base", args.package_name, base_version), + desired_state=desired_base_state, + api_relative=api_relative, + meta_relative=meta_relative, + ) + base_branch = base_selection.branch_name + + if base_selection.reused: + log_info(f"\n=== Reusing base branch {base_branch} ===") + git(["checkout", "-B", base_branch, base_selection.remote_ref or ""]) + else: + log_info(f"\n=== Creating base branch {base_branch} ===") + git(["checkout", "-B", base_branch, MAIN_REF]) + write_bytes(api_path, base_result.api_md) + git(["add", api_relative]) + if base_result.metadata: + write_bytes(meta_file_path, base_result.metadata) + git(["add", meta_relative]) + git(["commit", "-m", f"[API Review] Baseline api.md for {args.package_name} {base_version}"]) + git(["push", "--force-with-lease", REMOTE, base_branch]) + + review_selection = resolve_branch_selection( + preferred_branch=api_review_branch_name("review", args.package_name, target_version), + desired_state=desired_review_state, + api_relative=api_relative, + meta_relative=meta_relative, + required_ancestor_ref=base_branch, + ) + review_branch = review_selection.branch_name + + if review_selection.reused: + log_info(f"\n=== Reusing review branch {review_branch} ===") + git(["checkout", "-B", review_branch, review_selection.remote_ref or ""]) + else: + log_info(f"\n=== Creating review branch {review_branch} ===") + git(["checkout", "-B", review_branch, base_branch]) + write_bytes(api_path, target_result.api_md) + git(["add", api_relative]) + if target_result.metadata: + write_bytes(meta_file_path, target_result.metadata) + git(["add", meta_relative]) + git(["commit", "-m", f"[API Review] api.md for {args.package_name} {target_version}"]) + git(["push", "--force-with-lease", REMOTE, review_branch]) + + title = f"[API Review] {args.package_name} {target_version} (base {base_version})" + working_selector = args.target or "main" + working_reference = target_reference_info(working_selector, args.package_name) + baseline_ref = baseline_reference_markdown(args.base) + sync_metadata = build_sync_metadata_object( + package_name=args.package_name, + package_dir=package_dir, + base_branch=base_branch, + review_branch=review_branch, + head_selector=working_selector, + ) + sync_metadata_block = build_sync_metadata_block(sync_metadata) + body = build_review_pr_body( + package_name=args.package_name, + target_version=target_version, + base_version=base_version, + working_reference=working_reference, + baseline_ref=baseline_ref, + sync_metadata_block=sync_metadata_block, + ) + + if base_selection.reused and review_selection.reused: + existing_pr = find_open_pr_for_branches(base_branch, review_branch) + if existing_pr: + ensure_pr_body_sync_metadata(existing_pr, sync_metadata_block) + log_info(f"\n=== Reusing existing PR #{existing_pr['number']} ===") + log_info(existing_pr["url"]) + return 0 + + log_info("\n=== Opening PR ===") + compare_url = f"https://github.com/{REPO_SLUG}/compare/{base_branch}...{review_branch}?expand=1" + pr_create = create_draft_pr(base_branch, review_branch, title, body) + if pr_create["ok"]: + if pr_create.get("url"): + log_info(pr_create["url"]) + else: + existing_pr = find_open_pr_for_branches(base_branch, review_branch) + if existing_pr: + ensure_pr_body_sync_metadata(existing_pr, sync_metadata_block) + log_info(f"\n=== Reusing existing PR #{existing_pr['number']} ===") + log_info(existing_pr["url"]) + return 0 + + error_details = "\n ".join( + item + for item in [ + f"Exit code: {pr_create.get('status')}", + f"stderr: {str(pr_create.get('stderr') or '').replace(chr(10), ' ').replace(chr(13), ' ').strip()}" + if pr_create.get("stderr") + else "", + f"stdout: {str(pr_create.get('stdout') or '').replace(chr(10), ' ').replace(chr(13), ' ').strip()}" + if pr_create.get("stdout") + else "", + f"Debug repro: use the GitHub REST API endpoint POST /repos/{REPO_SLUG}/pulls with base/head/title/body/draft=true.", + ] + if item + ) + log_warning( + "\nWARNING: GitHub PR creation failed. Both branches were pushed successfully -- open the PR manually here:\n" + f" {compare_url}\n" + f" Title: {title}" + + (f"\n {error_details}" if error_details else "") + ) + return 0 + finally: + git(["checkout", original_branch], check=False) + + +if __name__ == "__main__": + try: + sys.exit(main()) + except Exception as error: # pylint: disable=broad-except + log_error(str(error)) + sys.exit(1) diff --git a/scripts/api_md_workflow/create_api_review_pr_test.py b/scripts/api_md_workflow/create_api_review_pr_test.py new file mode 100644 index 000000000000..89dd5211d2b4 --- /dev/null +++ b/scripts/api_md_workflow/create_api_review_pr_test.py @@ -0,0 +1,324 @@ +import json +import unittest +from unittest.mock import MagicMock, patch + +from scripts.api_md_workflow import create_api_review_pr as workflow + + +def command_result(stdout="", status=0): + return workflow.CommandResult(status=status, stdout=stdout, stderr="") + + +def stub_git_branches(branches): + branch_set = set(branches) + + def runner(args, check): + if args[0] == "fetch" and len(args) > 2 and args[2].split(":", 1)[0] in branch_set: + return command_result() + return command_result(status=1) + + return runner + + +class StubGithubApi: + def __init__(self, head_results=None, search_results=None, on_lookup=None): + self.head_results = head_results or [] + self.search_results = search_results or [] + self.on_lookup = on_lookup + + def _lookup(self, results): + if self.on_lookup: + self.on_lookup() + return results + + def list_pull_requests_by_head(self, _head, _limit): + return self._lookup(self.head_results) + + def search_pull_requests(self, _query, _limit): + return self._lookup(self.search_results) + + def list_pull_requests_by_branches(self, _base, _head, _limit): + return [] + + def update_pull_request_body(self, _number, _body): + return None + + def create_draft_pull_request(self, _base, _head, _title, _body): + return {"html_url": "https://github.com/Azure/azure-sdk-for-python/pull/1"} + + +class ApiReviewPrTests(unittest.TestCase): + def tearDown(self): + workflow.set_command_runner_for_test(None) + workflow.set_github_api_for_test(None) + + def test_github_api_request_uses_timeout(self): + response = MagicMock() + response.read.return_value = b'{"ok": true}' + response_context = MagicMock() + response_context.__enter__.return_value = response + + with patch.object(workflow, "urlopen", return_value=response_context) as urlopen: + self.assertEqual(workflow.GitHubApi(None)._request("GET", "https://example.test"), {"ok": True}) + + urlopen.assert_called_once() + self.assertEqual(urlopen.call_args.kwargs["timeout"], workflow.GITHUB_API_TIMEOUT_SECONDS) + + def test_target_reference_info_links_matching_open_pr_from_direct_head_query(self): + workflow.set_command_runner_for_test(stub_git_branches(["users/example/direct-feature"])) + workflow.set_github_api_for_test(StubGithubApi( + head_results=[ + { + "number": 45678, + "url": "https://github.com/Azure/azure-sdk-for-python/pull/45678", + "state": "OPEN", + "updatedAt": "2026-06-05T00:00:00Z", + "headRefName": "users/example/direct-feature", + "headRepositoryOwner": {"login": "example"}, + } + ] + )) + + self.assertEqual( + workflow.target_reference_info("example:users/example/direct-feature"), + { + "label": "Working PR", + "markdown": "[PR #45678](https://github.com/Azure/azure-sdk-for-python/pull/45678)", + }, + ) + + def test_target_reference_info_keeps_origin_main_as_branch(self): + workflow.set_command_runner_for_test(stub_git_branches(["main"])) + workflow.set_github_api_for_test(StubGithubApi( + search_results=[ + { + "number": 23456, + "url": "https://github.com/Azure/azure-sdk-for-python/pull/23456", + "state": "OPEN", + "updatedAt": "2026-06-05T00:00:00Z", + "headRefName": "main", + "headRepositoryOwner": {"login": "example"}, + } + ] + )) + + self.assertEqual( + workflow.target_reference_info("origin/main"), + { + "label": "Working branch", + "markdown": "[branch `origin/main`](https://github.com/Azure/azure-sdk-for-python/tree/main)", + }, + ) + + def test_target_reference_info_treats_existing_target_tag_as_tag(self): + pr_lookup_count = 0 + + def runner(args, check): + if args[0] == "rev-parse" and "refs/tags/azure-example_1.2.3" in args: + return command_result() + if args[0] == "rev-list": + return command_result("abc123def456\n") + return command_result(status=1) + + def on_lookup(): + nonlocal pr_lookup_count + pr_lookup_count += 1 + + workflow.set_command_runner_for_test(runner) + workflow.set_github_api_for_test(StubGithubApi(on_lookup=on_lookup)) + + self.assertEqual( + workflow.target_reference_info("azure-example_1.2.3"), + { + "label": "Target tag", + "markdown": "[tag `azure-example_1.2.3`](https://github.com/Azure/azure-sdk-for-python/commit/abc123def456)", + }, + ) + self.assertEqual(pr_lookup_count, 0) + + def test_explicit_package_tag_target_wins_over_same_named_remote_branch(self): + pr_lookup_count = 0 + + def runner(args, check): + if args == ["rev-parse", "--verify", "--quiet", "refs/tags/azure-example_1.2.3"]: + return command_result() + if args == ["rev-list", "-n", "1", "azure-example_1.2.3"]: + return command_result("abc123def456\n") + if args == ["fetch", "origin", "azure-example_1.2.3:refs/remotes/origin/azure-example_1.2.3"]: + return command_result() + return command_result(status=1) + + def on_lookup(): + nonlocal pr_lookup_count + pr_lookup_count += 1 + + workflow.set_command_runner_for_test(runner) + workflow.set_github_api_for_test(StubGithubApi(on_lookup=on_lookup)) + + self.assertEqual(workflow.resolve_target_ref("azure-example_1.2.3", "azure-example"), "azure-example_1.2.3") + self.assertIsNone(workflow.sync_working_branch_info("azure-example_1.2.3", "azure-example")) + self.assertEqual( + workflow.target_reference_info("azure-example_1.2.3", "azure-example"), + { + "label": "Target tag", + "markdown": "[tag `azure-example_1.2.3`](https://github.com/Azure/azure-sdk-for-python/commit/abc123def456)", + }, + ) + self.assertEqual(pr_lookup_count, 0) + + def test_build_sync_metadata_object_records_fork_owner_and_branch(self): + workflow.set_command_runner_for_test(stub_git_branches(["users/example/feature"])) + workflow.set_github_api_for_test(StubGithubApi( + search_results=[ + { + "number": 47204, + "url": "https://github.com/Azure/azure-sdk-for-python/pull/47204", + "state": "OPEN", + "updatedAt": "2026-06-05T00:00:00Z", + "headRefName": "users/example/feature", + "headRepositoryOwner": {"login": "example"}, + } + ] + )) + + metadata = workflow.build_sync_metadata_object( + package_name="azure-example", + package_dir="sdk/service/azure-example", + base_branch="apireview/base_azure-example_1.0.0", + review_branch="apireview/review_azure-example_1.1.0", + head_selector="example:users/example/feature", + ) + + self.assertEqual(metadata["workingOwner"], "example") + self.assertEqual(metadata["workingBranch"], "users/example/feature") + self.assertEqual(metadata["workingPrNumber"], 47204) + + def test_build_sync_metadata_object_omits_metadata_for_tag_targets(self): + pr_lookup_count = 0 + + def runner(args, check): + if args[0] == "rev-parse" and "refs/tags/azure-example_1.2.3" in args: + return command_result() + return command_result(status=1) + + def on_lookup(): + nonlocal pr_lookup_count + pr_lookup_count += 1 + + workflow.set_command_runner_for_test(runner) + workflow.set_github_api_for_test(StubGithubApi(on_lookup=on_lookup)) + + self.assertIsNone( + workflow.build_sync_metadata_object( + package_name="azure-example", + package_dir="sdk/service/azure-example", + base_branch="apireview/base_azure-example_1.0.0", + review_branch="apireview/review_azure-example_1.1.0", + head_selector="azure-example_1.2.3", + ) + ) + self.assertEqual(pr_lookup_count, 0) + + def test_build_review_pr_body_calls_out_static_tag_to_tag_reviews(self): + body = workflow.build_review_pr_body( + package_name="azure-example", + target_version="1.2.3", + base_version="1.2.2", + working_reference={ + "label": "Target tag", + "markdown": "[tag `azure-example_1.2.3`](https://github.com/Azure/azure-sdk-for-python/commit/abc123)", + }, + baseline_ref="[tag `azure-example_1.2.2`](https://github.com/Azure/azure-sdk-for-python/commit/def456)", + sync_metadata_block=None, + ) + + self.assertIn( + "> [!WARNING]\n" + "> Static tag-to-tag review; this PR cannot be automatically updated from a working branch.", + body, + ) + self.assertNotIn("Update behavior", body) + self.assertNotIn("api-md-review-sync", body) + + def test_build_review_pr_body_includes_sync_metadata_for_working_branch_reviews(self): + metadata_block = workflow.build_sync_metadata_block( + { + "schemaVersion": 1, + "repository": "Azure/azure-sdk-for-python", + "packageName": "azure-example", + "packageDir": "sdk/service/azure-example", + "baseBranch": "apireview/base_azure-example_1.0.0", + "reviewBranch": "apireview/review_azure-example_1.1.0", + "workingOwner": "Azure", + "workingBranch": "main", + "workingPrNumber": None, + } + ) + + body = workflow.build_review_pr_body( + package_name="azure-example", + target_version="1.1.0b1", + base_version="1.0.0", + working_reference={ + "label": "Working branch", + "markdown": "[branch `main`](https://github.com/Azure/azure-sdk-for-python/tree/main)", + }, + baseline_ref="[tag `azure-example_1.0.0`](https://github.com/Azure/azure-sdk-for-python/commit/def456)", + sync_metadata_block=metadata_block, + ) + + self.assertIn("- **Working branch:**", body) + self.assertNotIn("Static tag-to-tag review", body) + self.assertIn("", "") + self.assertEqual(json.loads(json_text), {"schemaVersion": 1, "workingPrNumber": None}) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/sdk/ai/azure-ai-projects/.github/skills/README.md b/sdk/ai/azure-ai-projects/.github/skills/README.md index 7ea120484d10..5fa39f44900e 100644 --- a/sdk/ai/azure-ai-projects/.github/skills/README.md +++ b/sdk/ai/azure-ai-projects/.github/skills/README.md @@ -7,7 +7,7 @@ git clone https://github.com/Azure/azure-sdk-for-python.git ``` * Change to the directory `sdk\ai\azure-ai-projects`. -* Switch to the current feature branch: `git switch feature/azure-ai-projects/2.2.0`. +* Switch to the current feature branch, for example: `git switch feature/azure-ai-projects/2.3.0`. * Make sure you don't have any files edited or added in this branch (clean `git status` state). ## Emit from TypeSpec and create a PR @@ -17,16 +17,26 @@ * Open VSCode in the current folder. * Open the CoPilot chat window ("Toggle Chat"). * Make sure you are in "Agent" mode. -* Start typing `/azure-ai-projects` and press tab to auto complete it to `/azure-ai-projects-emit-from-typespec`, then press Enter. +* Start typing `/` followed by the skill name, like `/azure-ai-projects` and press tab to auto complete it to the designed skill, like `/azure-ai-projects-emit-from-typespec`, then press Enter. * Answer some questions and approve execution to go through the workflow ### Using CoPilot CLI or Agency Copilot CLI * Install [GitHub CoPilot CLI](https://docs.github.com/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli) or [Agency CoPilot CLI](https://aka.ms/agency) (VPN required) if you don't already have it. * Run CoPilot CLI by typing `copilot` -* Start typing `/azure-ai-projects` and press tab to auto complete it to `/azure-ai-projects-emit-from-typespec`, then press Enter. +* Start typing `/` followed by the skill name, like `/azure-ai-projects` and press tab to auto complete it to the desired skill, like `/azure-ai-projects-emit-from-typespec`, then press Enter. * Answer some questions and approve execution to go through the workflow +## Skills + +### azure-ai-projects-emit-from-typespec + +This skill creates a new topic branch, emits SDK from TypeSpec, runs some post-processing and creates a PR. + +### azure-ai-projects-update-changelog + +This skill updates the file CHANGELOG.md, comparing the source in the current branch which the source of the latest public release. It does not create a new topic branch or a PR. + diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-emit-from-typespec/SKILL.md b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-emit-from-typespec/SKILL.md index 190df28bcbd0..a64f2072daa8 100644 --- a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-emit-from-typespec/SKILL.md +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-emit-from-typespec/SKILL.md @@ -174,18 +174,7 @@ In the folder `sdk\ai\azure-ai-projects`, run `pip install -e .` to install the --- -## Step 12: Update CHANGELOG.md - -Use the **`azsdk-common-generate-sdk-locally`** skill's changelog capability (`azsdk_package_update_changelog_content`) to update `CHANGELOG.md` in the `sdk/ai/azure-ai-projects` folder with a summary of changes from the TypeSpec emit. Some guidelines to follow: -* Start by examining the public SDK API surface of the latest released version of the azure-ai-projects package. The source code for this version can be found in the Main branch of the `azure-sdk-for-python` repository, in the folder `sdk\ai\azure-ai-projects`. -* Then compare it to the public SDK API surface of current version in this topic branch. -* Look at the existing change log from the latest version (if exists) and edit or add to it to capture all the changes you see. If a change log does not exist for the current version at the top of `CHANGELOG.md`, create a new one. -* If a new method was added, there is no need to add the list of all new classes that define the inputs and output of the method. It's enough to mention that the new method was added. -* Show the user the proposed changelog entry and ask for confirmation or edits before saving. - ---- - -## Step 13: Commit and push +## Step 12: Commit and push Stage all changes (excluding file names that start with `.env`), commit, and push the topic branch: @@ -199,7 +188,7 @@ git push -u origin --- -## Step 14: Create a Pull Request +## Step 13: Create a Pull Request Create a draft PR from the **topic branch** to the **base branch** (recorded in Step 2): @@ -216,7 +205,7 @@ Open a new tab in the default browser and navigate to the PR URL. --- -## Step 15: Optionally run tests locally +## Step 14: Optionally run tests locally Prompt the user with this message: "Tests will run as part of the Pull Request. However, you can optionally run tests locally in a Python virtual environment, right now. It will take a few minutes. Do you want to run tests locally? (yes/no)" diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-update-changelog/SKILL.md b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-update-changelog/SKILL.md new file mode 100644 index 000000000000..eeb810776294 --- /dev/null +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-update-changelog/SKILL.md @@ -0,0 +1,342 @@ +--- +name: azure-ai-projects-update-changelog +license: MIT +metadata: + version: "1.0.0" + distribution: local +description: "Update CHANGELOG.md by comparing public APIs between the current branch and the latest released version on PyPI. WHEN: \"update changelog\", \"generate changelog\", \"add changelog entry\", \"what changed in this version\". DO NOT USE FOR: other Azure SDK packages. INVOKES: PyPI API, GitHub API (for tags), file operations." +compatibility: + requires: "local azure-sdk-for-python clone, git, internet access" +--- + +# Update azure-ai-projects Changelog + +This skill guides Copilot through updating the CHANGELOG.md file for the azure-ai-projects package by comparing public APIs between the current branch and the latest released version. + +**Working directory:** `sdk/ai/azure-ai-projects` + +--- + +## Overview + +The skill performs these steps: +1. Read the current version from `azure/ai/projects/_version.py` +2. Fetch the latest released version from PyPI +3. Compare public classes, methods, and properties between current branch and released version +4. Update CHANGELOG.md with a new section or update the existing "(Unreleased)" section + +--- + +## Step 1: Read the current version + +Read the current version from `azure/ai/projects/_version.py`. The file contains a line like: +```python +VERSION = "X.Y.Z" +``` + +Extract this version number and save it as `CURRENT_VERSION`. + +--- + +## Step 2: Fetch the latest released version from PyPI + +Use the PyPI JSON API to get the latest released version: + +``` +https://pypi.org/pypi/azure-ai-projects/json +``` + +From the JSON response: +- Extract `info.version` as `LATEST_PYPI_VERSION` +- This is the version we will compare against + +--- + +## Step 3: Determine if CHANGELOG needs updating + +Check if CHANGELOG.md already has a section for the current version: +- If there's a section `## {CURRENT_VERSION} (Unreleased)` — we will update it +- If there's a section `## {CURRENT_VERSION} (YYYY-MM-DD)` with an actual date — the version is already released, report this to the user and stop +- If there's no section for `CURRENT_VERSION` — we will create a new one + +--- + +## Step 4: Construct the GitHub tag for the released version + +The tag name for a released version follows this pattern: +``` +azure-ai-projects_{VERSION} +``` + +For example, for version `2.2.0`, the tag is `azure-ai-projects_2.2.0`. + +The source code for that release can be found at: +``` +https://github.com/Azure/azure-sdk-for-python/tree/azure-ai-projects_{VERSION}/sdk/ai/azure-ai-projects +``` + +--- + +## Step 5: Compare public APIs + +Compare the public APIs between the current branch and the latest released version. Focus on these locations: + +### 5a. Public classes and enums in `azure/ai/projects/models/__init__.py` + +Compare the `__all__` list and imports in both versions to identify: +- **New classes/enums**: Present in current branch but not in released version +- **Removed classes/enums**: Present in released version but not in current branch +- **Renamed classes/enums**: Check if a removed class has a similar new class (likely a rename) + +### 5b. Public operations in `azure/ai/projects/operations/__init__.py` + +Compare the `__all__` list to identify new or removed operation classes. + +### 5c. Public methods on sub-clients + +For each operations class (like `AgentsOperations`, `BetaOperations`, etc.), compare the public methods: +- Look at files under `azure/ai/projects/operations/` and `azure/ai/projects/aio/operations/` +- Also check `_patch.py` files which may define additional public methods +- Public methods are those that don't start with underscore `_` + +### 5d. Properties on model classes + +For significant model classes, compare public properties (attributes) between versions: +- Properties are defined in `azure/ai/projects/models/_models.py` +- Look for new, removed, or renamed properties +- Pay attention to required vs optional changes + +### 5e. Beta sub-clients on `BetaOperations` + +The `BetaOperations` class exposes beta/preview functionality. Check for: +- New sub-client properties (like `.beta.datasets`, `.beta.models`, `.beta.routines`, etc.) +- Removed sub-client properties +- Check both `azure/ai/projects/operations/_patch.py` and the released version + +### 5f. Sample files in `samples/` folder + +Compare sample files between the current branch and the released version: + +1. **List all `.py` files** recursively under `samples/` in both versions +2. **Identify new samples**: Files present in current branch but not in released version +3. **Identify removed samples**: Files present in released version but not in current branch +4. **Ignore async variants**: If a sample has both sync and async versions (e.g., `sample_foo.py` and `sample_foo_async.py`), only report the sync version +5. **Check existing changelog entries**: If a sample is already mentioned in the current changelog section, leave it as is +6. **Remove stale entries**: If a sample mentioned in the changelog has been removed from the codebase, remove it from the changelog + +For each new sample, provide a one-line description of what it demonstrates. Read the sample file to understand its purpose — typically the docstring at the top or the `if __name__ == "__main__"` block explains what it does. + +--- + +## Step 6: Categorize the changes + +Organize detected changes into these categories: + +### Features Added +- New sub-clients (e.g., "New `.beta.routines` sub-client with routine operations: `create_or_update`, `get`, `enable`, ...") +- New methods on existing sub-clients (e.g., "New methods on `.beta.agents` for optimization jobs: `create_optimization_job`, `get_optimization_job`, ...") +- New model classes that represent significant features (e.g., "Support integration of external Agents. See new `ExternalAgentDefinition` class.") +- New properties on existing classes (e.g., "New optional `force` parameter on `agents.delete` method.") +- New tools (e.g., "New Agent tool in preview `FabricIQPreviewTool`.") + +### Breaking Changes +List breaking changes in beta methods and classes separately: +- Renamed methods (e.g., "Method `.beta.agents.get_session_files` renamed to `.beta.agents.list_session_files`.") +- Renamed arguments (e.g., "Argument `body` in method `.beta.skills.create_from_files()` renamed to `content`.") +- Signature changes (e.g., "Method `.beta.skills.create` signature changed — now takes `name` and keyword `inline_content: SkillInlineContent`; returns `SkillVersion`.") +- Renamed classes (e.g., "Renamed class `AgentEndpoint` to `AgentEndpointConfig`.") +- Property changes (e.g., "Required property `isolation_key_source` removed from class `EntraAuthorizationScheme`.") +- Renamed properties (e.g., "Property `skill_id` renamed to `id` on class `SkillDetails`.") + +**Format for beta changes:** +```markdown +Breaking changes in beta methods: +* ... + +Breaking changes in beta classes: +* ... +``` + +### Bugs Fixed +This section typically contains bug fixes. Leave empty unless you have specific bug fix information to add. + +### Sample updates +List new sample files that were added, with a one-line description of what they demonstrate: +- Compare sample files in the `samples/` folder between current branch and released version +- Only report the sync version — do not list async samples separately (files ending with `_async.py`) +- If a sample is already mentioned in the existing changelog section, preserve that entry +- If a sample mentioned in the changelog has been removed from the codebase, remove it from the changelog +- Group related samples together (e.g., all agent samples, all evaluation samples) +- Use format: `Added \`sample_name.py\` demonstrating [brief description].` + +--- + +## Step 7: Format the changelog entry + +Use this format for the changelog entry: + +```markdown +## {CURRENT_VERSION} (Unreleased) + +### Features Added + +* [List each feature on its own bullet point] + +### Breaking Changes + +Breaking changes in beta methods: +* [List method changes] + +Breaking changes in beta classes: +* [List class changes] + +### Bugs Fixed + +* [List bug fixes, if any] + +### Sample updates + +* [List sample updates, if any] +``` + +**Guidelines for writing entries:** +- For new methods: mention the sub-client and method name, briefly describe what it does. Only report the sync version — do not list both sync and async versions separately. +- For new sub-clients: list all the methods it provides (sync versions only) +- For new tools: just mention the class name +- For property changes: mention the class name and the affected property +- For renames: show "X renamed to Y" format +- Use backticks for code references: `.beta.agents`, `create_version()`, `AgentDetails` + +--- + +## Step 8: Update CHANGELOG.md + +Insert or update the changelog entry in `CHANGELOG.md`: + +1. If updating an existing "(Unreleased)" section: + - Replace the existing section content with the new content + - Preserve any manually-added entries that aren't API-related (like "Sample updates" written by developers) + +2. If creating a new section: + - Insert the new section immediately after the `# Release History` header + - Keep all previous version sections intact + +--- + +## Step 9: Report to user + +After updating the changelog, report: +1. The current version and latest PyPI version compared +2. Summary of changes detected: + - Number of new classes/enums + - Number of new methods + - Number of breaking changes + - Number of removed items +3. Remind the user to: + - Review the generated changelog for accuracy + - Add any bug fixes that were made + - Review sample descriptions for accuracy + - Verify method descriptions are accurate + +--- + +## Tips for API Comparison + +### Using git to compare files + +You can compare files between the current branch and a tag: +```bash +git diff azure-ai-projects_{VERSION} -- azure/ai/projects/models/__init__.py +``` + +### Using GitHub raw URLs + +To fetch files from the released version: +``` +https://raw.githubusercontent.com/Azure/azure-sdk-for-python/azure-ai-projects_{VERSION}/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py +``` + +### Identifying renames vs additions/removals + +If a class was removed and a similar class was added, it's likely a rename. Look for: +- Similar names (e.g., `SkillObject` → `SkillDetails`) +- Similar structure/properties +- Check if there's a corresponding note in the TypeSpec changes + +--- + +## Example Output + +Here's an example of a well-formatted changelog entry: + +```markdown +## 2.3.0 (Unreleased) + +### Features Added + +* Support integration of external Agents (in preview). See new `ExternalAgentDefinition` class. +* New Agent tool in preview `FabricIQPreviewTool`. +* New Agent tool in preview `ToolboxSearchPreviewTool`. +* New methods on `.beta.agents` for + * Code-based hosted agents: `create_version_from_code`, `download_code`. + * Optimization jobs: `create_optimization_job`, `get_optimization_job`, `list_optimization_jobs`, `cancel_optimization_job`, `list_optimization_candidates`. + * Optimization candidate management: `list_optimization_candidates`, `get_optimization_candidate`, `get_optimization_candidate_config`, `get_optimization_candidate_results`, `get_candidate_file`, `promote_candidate`. + * `stop_session` to stop a running agent session. +* New `.beta.datasets` sub-client with data generation job operations: `create_generation_job`, `get_generation_job`, `list_generation_jobs`, `cancel_generation_job`, `delete_generation_job`. +* New `.beta.models` sub-client to handle AI model weights: `create`, `list_versions`, `list`, `get`, `delete`, `update`, `pending_create_version`, `pending_upload`, `get_credentials`. +* New `.beta.routines` sub-client with routine operations: `create_or_update`, `get`, `enable`, `disable`, `list`, `delete`, `list_runs`, `dispatch`. +* New methods on `.beta.evaluators` for evaluator generation jobs: `create_generation_job`, `get_generation_job`, `list_generation_jobs`, `cancel_generation_job`, `delete_generation_job`. +* New methods on `.beta.memory_stores` to handle individual memory items: `create_memory`, `update_memory`, `list_memories`, `get_memory`, `delete_memory`. +* New methods on `.beta.skills` for versioned skill management: `create`, `list_versions`, `get_version`, `download_version`, `delete_version`. +* New optional string properties `description` and `name` added to Agent tools classes which did not have them before. +* New optional `tool_configs` added to Agent tool classes. +* New read-only property `content_hash` on `CodeConfiguration`, returning the SHA-256 hex digest of the uploaded code zip. +* New optional `force` parameter on `agents.delete` and `agents.delete_version` methods. +* New optional `blueprint_reference` parameters on `agents.create_version` method. + + +### Breaking Changes + +Breaking changes in beta methods: +* Argument `isolation_key` in methods `.beta.agents.create_session()` and `.beta.agents.delete_session()` renamed to `user_isolation_key`. +* Argument `body` in methods `.beta.evaluation_taxonomies.create()` and `.beta.evaluation_taxonomies.update()` renamed to `taxonomy`. +* Argument `body` in method `.beta.skills.create_from_files()` renamed to `content`. +* Method `.beta.agents.get_session_files` renamed to `.beta.agents.list_session_files`. +* Method `.beta.skills.create` signature changed — now takes `name` and keyword `inline_content: SkillInlineContent`; returns `SkillVersion`. +* Method `.beta.skills.create_from_package` renamed to `.beta.skills.create_from_files`. +* Method `.beta.skills.create_from_files` signature changed — now takes `name` and `content: CreateSkillVersionFromFilesBody`; returns `SkillVersion`. +* Method `.beta.skills.update` signature changed — now only accepts keyword `default_version`; returns `SkillDetails`. + +Breaking changes in beta classes: +* Required property `isolation_key_source` removed from class `EntraAuthorizationScheme`. +* Renamed class `AgentEndpoint` to `AgentEndpointConfig`. +* Renamed class `DeleteSkillResponse` to `DeleteSkillResult`. +* Renamed class `SessionDirectoryListResponse` to `SessionDirectoryListResult`. +* Renamed class `SessionFileWriteResponse` to `SessionFileWriteResult`. +* Renamed class `SkillObject` to `SkillDetails`. Property `skill_id` renamed to `id`. Properties `has_blob` and `metadata` were removed. +* Renamed class `Target` to `EvaluationTarget`. +* Renamed class `TargetConfig` to `RedTeamTargetConfig`. + +### Bugs Fixed + +* Fixed telemetry instrumentor to correctly call is_recording() as a method on spans, ensuring non-recording spans are properly skipped (e.g., when sampling is configured) ([GitHub issue 46544](https://github.com/Azure/azure-sdk-for-python/issues/46544)). + +### Sample updates + +* Added new Agent tool samples `sample_agent_work_iq.py` and `sample_agent_work_iq_async.py` demonstrating use of `WorkIQPreviewTool`. +* Added new Agent tool samples `sample_agent_fabric_iq.py` and `sample_agent_fabric_iq_async.py` demonstrating use of `FabricIQPreviewTool`. +* Hosted Agents: + * Added Hosted Agent creation samples `sample_create_hosted_agent.py` and `sample_create_hosted_agent_async.py`, demonstrating hosted agent version creation and retrieval with `AIProjectClient`. + * Added Hosted Agent code-upload samples `sample_create_hosted_agent_from_code.py` and `sample_create_hosted_agent_from_code_async.py`, demonstrating uploading a code package (zip) as a new hosted agent version. + * The Hosted Agent creation sample also demonstrates assigning the hosted agent managed identity the Azure AI User RBAC role on the backing Azure AI account. + * Updated the other Hosted Agent samples to reuse an existing Hosted Agent as a prerequisite, instead of creating a new hosted agent version in each sample. +* Added Toolbox tool-search sample `sample_toolboxes_with_search_preview.py` and `sample_toolboxes_with_search_preview_async.py`, demonstrating creating a Toolbox version with `ToolboxSearchPreviewTool` and invoking `MCPTool`. +* Added `.beta.models` samples under `samples/models/`: + * `sample_models_basic.py` — synchronous end-to-end registration via the `create` helper (uses `azcopy`), followed by `get`, `list_versions`, `list`, `get_credentials`, `update`, and `delete`. + * `sample_models_create_and_poll.py` — alternative synchronous registration that hand-rolls the spec's three-step flow (`pending_upload` → upload via `azure-storage-blob` → `pending_create_version` + poll), without taking a dependency on `azcopy`. + * `sample_models_basic_async.py` — asynchronous version of the same three-step flow using `azure.ai.projects.aio.AIProjectClient` and `azure.storage.blob.aio.ContainerClient`. +* Added new evaluation sample `sample_model_evaluation_instant_model.py` demonstrating model evaluation with an instant model. +* Refreshed evaluation samples under `samples/evaluations/` and `samples/evaluations/agentic_evaluators/` (including `sample_agent_evaluation`, `sample_agent_response_evaluation`, `sample_eval_catalog_prompt_based_evaluators`, `sample_evaluations_ai_assisted`, `sample_evaluations_builtin_with_csv`, `sample_evaluations_builtin_with_dataset_id`, `sample_evaluations_builtin_with_inline_data`, `sample_evaluations_builtin_with_inline_data_oai`, `sample_scheduled_evaluations`, `sample_coherence`, `sample_fluency`, `sample_intent_resolution`, `sample_relevance`, `sample_response_completeness`, `sample_tool_call_accuracy`, `sample_tool_call_success`, `sample_tool_input_accuracy`, `sample_tool_output_utilization`, `sample_tool_selection`, and `sample_generic_agentic_evaluator`). +* New sample `sample_dataset_generation_job_simpleqna_with_prompt_source.py` showing an end-to-end flow that generates a QnA dataset via `.beta.datasets.create_generation_job` and runs an OpenAI evaluation. + +``` diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index b601407b5999..f2f0df63cdd1 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -2,11 +2,19 @@ ## 2.3.0 (Unreleased) +### Breaking Changes + +Breaking changes in beta methods: +* Agent Optimization methods `.beta.agents.*optimization*` were re-written. Updated from v1 to v2 preview, focusing on cleanup to better align with Foundry job guidelines and platform standards. The v1 surface accumulated unused candidate sub-resources, internal-detail properties, and custom operation patterns inconsistent with the Foundry platform. The v2 API removes redundant models and operations, adopts shared Foundry job patterns (`JobLike<>`, standard job verbs), and introduces typed discriminated unions for dataset inputs and evaluator references. +* Method `.beta.agents.list_optimization_candidates` now returns `ItemPaged[OptimizationCandidate]` instead of `AgentsPagedResultOptimizationCandidate`. The `after` parameter has been removed (use continuation-token-based paging instead). + ### Sample updates * Added `sample_routines_crud.py` to demonstrate CRUD operations. * Added `sample_routines_with_timer_trigger.py` to demonstrate triggering a routine with a timer. * Added `sample_routines_with_schedule_trigger.py` to demonstrate triggering a routine on a recurring cron schedule via `ScheduleRoutineTrigger`. +* Added `sample_routines_with_dispatch.py` to demonstrate manually firing a routine on demand via `routines.dispatch(...)` using a `CustomRoutineTrigger`. +* Added `sample_skill_in_toolbox.py` demonstrating how to expose a Skill to a Prompt Agent via a Toolbox using `MCPTool`. * Updated `sample_dataset_generation_job_traces_for_evaluation.py` and `sample_dataset_generation_job_traces_for_finetuning.py` to create a temporary agent, seed conversations, retry the data generation job over the trace window, and clean up all created resources. * Updated `sample_memory_crud.py` and `sample_memory_crud_async.py` to demonstrate memory item CRUD (`create_memory`, `get_memory`, `update_memory`, `list_memories`, `delete_memory`) in addition to memory store CRUD. * Updated the rubric evaluator generation samples (`sample_rubric_evaluator_generation_basic.py`, `sample_rubric_evaluator_generation_iterate.py`, `sample_rubric_evaluator_generation_lifecycle.py`, `sample_rubric_evaluator_generation_all_sources.py`) to use the typed `EvaluatorGenerationJob` / `EvaluatorGenerationInputs` / `*EvaluatorGenerationJobSource` models. The job inputs are now nested under `inputs` per the service contract, and the traces source uses `datetime` values for `start_time` / `end_time`. diff --git a/sdk/ai/azure-ai-projects/apiview-properties.json b/sdk/ai/azure-ai-projects/apiview-properties.json index 9aebac269df8..600e3cf11cca 100644 --- a/sdk/ai/azure-ai-projects/apiview-properties.json +++ b/sdk/ai/azure-ai-projects/apiview-properties.json @@ -20,11 +20,9 @@ "azure.ai.projects.models.AgentEvaluatorGenerationJobSource": "Azure.AI.Projects.AgentEvaluatorGenerationJobSource", "azure.ai.projects.models.BaseCredentials": "Azure.AI.Projects.BaseCredentials", "azure.ai.projects.models.AgenticIdentityPreviewCredentials": "Azure.AI.Projects.AgenticIdentityPreviewCredentials", - "azure.ai.projects.models.AgentIdentifier": "Azure.AI.Projects.AgentIdentifier", "azure.ai.projects.models.AgentIdentity": "Azure.AI.Projects.AgentIdentity", "azure.ai.projects.models.AgentObjectVersions": "Azure.AI.Projects.AgentObject.versions.anonymous", "azure.ai.projects.models.AgentSessionResource": "Azure.AI.Projects.AgentSessionResource", - "azure.ai.projects.models.AgentsPagedResultOptimizationCandidate": "Azure.AI.Projects.AgentsPagedResult", "azure.ai.projects.models.EvaluationTaxonomyInput": "Azure.AI.Projects.EvaluationTaxonomyInput", "azure.ai.projects.models.AgentTaxonomyInput": "Azure.AI.Projects.AgentTaxonomyInput", "azure.ai.projects.models.AgentVersionDetails": "Azure.AI.Projects.AgentVersionObject", @@ -63,10 +61,6 @@ "azure.ai.projects.models.BrowserAutomationPreviewTool": "Azure.AI.Projects.BrowserAutomationPreviewTool", "azure.ai.projects.models.BrowserAutomationToolConnectionParameters": "Azure.AI.Projects.BrowserAutomationToolConnectionParameters", "azure.ai.projects.models.BrowserAutomationToolParameters": "Azure.AI.Projects.BrowserAutomationToolParameters", - "azure.ai.projects.models.CandidateDeployConfig": "Azure.AI.Projects.CandidateDeployConfig", - "azure.ai.projects.models.CandidateFileInfo": "Azure.AI.Projects.CandidateFileInfo", - "azure.ai.projects.models.CandidateMetadata": "Azure.AI.Projects.CandidateMetadata", - "azure.ai.projects.models.CandidateResults": "Azure.AI.Projects.CandidateResults", "azure.ai.projects.models.CaptureStructuredOutputsTool": "Azure.AI.Projects.CaptureStructuredOutputsTool", "azure.ai.projects.models.ChartCoordinate": "Azure.AI.Projects.ChartCoordinate", "azure.ai.projects.models.MemoryItem": "Azure.AI.Projects.MemoryItem", @@ -79,6 +73,7 @@ "azure.ai.projects.models.CodeInterpreterTool": "OpenAI.CodeInterpreterTool", "azure.ai.projects.models.ComparisonFilter": "OpenAI.ComparisonFilter", "azure.ai.projects.models.CompoundFilter": "OpenAI.CompoundFilter", + "azure.ai.projects.models.ComputerTool": "OpenAI.ComputerTool", "azure.ai.projects.models.ComputerUsePreviewTool": "OpenAI.ComputerUsePreviewTool", "azure.ai.projects.models.Connection": "Azure.AI.Projects.Connection", "azure.ai.projects.models.FunctionShellToolParamEnvironment": "OpenAI.FunctionShellToolParamEnvironment", @@ -118,8 +113,6 @@ "azure.ai.projects.models.DatasetCredential": "Azure.AI.Projects.AssetCredentialResponse", "azure.ai.projects.models.DatasetDataGenerationJobOutput": "Azure.AI.Projects.DatasetDataGenerationJobOutput", "azure.ai.projects.models.DatasetEvaluatorGenerationJobSource": "Azure.AI.Projects.DatasetEvaluatorGenerationJobSource", - "azure.ai.projects.models.DatasetInfo": "Azure.AI.Projects.DatasetInfo", - "azure.ai.projects.models.DatasetRef": "Azure.AI.Projects.DatasetRef", "azure.ai.projects.models.DatasetReference": "Azure.AI.Projects.DatasetReference", "azure.ai.projects.models.DatasetVersion": "Azure.AI.Projects.DatasetVersion", "azure.ai.projects.models.DeleteAgentResponse": "Azure.AI.Projects.DeleteAgentResponse", @@ -132,6 +125,7 @@ "azure.ai.projects.models.Dimension": "Azure.AI.Projects.Dimension", "azure.ai.projects.models.DispatchRoutineResult": "Azure.AI.Projects.DispatchRoutineResponse", "azure.ai.projects.models.EmbeddingConfiguration": "Azure.AI.Projects.EmbeddingConfiguration", + "azure.ai.projects.models.EmptyModelParam": "OpenAI.EmptyModelParam", "azure.ai.projects.models.EntraAuthorizationScheme": "Azure.AI.Projects.EntraAuthorizationScheme", "azure.ai.projects.models.EntraIDCredentials": "Azure.AI.Projects.EntraIDCredentials", "azure.ai.projects.models.IsolationKeySource": "Azure.AI.Projects.IsolationKeySource", @@ -174,6 +168,7 @@ "azure.ai.projects.models.FunctionShellToolParamEnvironmentContainerReferenceParam": "OpenAI.FunctionShellToolParamEnvironmentContainerReferenceParam", "azure.ai.projects.models.FunctionShellToolParamEnvironmentLocalEnvironmentParam": "OpenAI.FunctionShellToolParamEnvironmentLocalEnvironmentParam", "azure.ai.projects.models.FunctionTool": "OpenAI.FunctionTool", + "azure.ai.projects.models.FunctionToolParam": "OpenAI.FunctionToolParam", "azure.ai.projects.models.GitHubIssueRoutineTrigger": "Azure.AI.Projects.GitHubIssueRoutineTrigger", "azure.ai.projects.models.HeaderIsolationKeySource": "Azure.AI.Projects.HeaderIsolationKeySource", "azure.ai.projects.models.TelemetryEndpointAuth": "Azure.AI.Projects.TelemetryEndpointAuth", @@ -229,6 +224,7 @@ "azure.ai.projects.models.ModelSourceData": "Azure.AI.Projects.ModelSourceData", "azure.ai.projects.models.ModelVersion": "Azure.AI.Projects.ModelVersion", "azure.ai.projects.models.MonthlyRecurrenceSchedule": "Azure.AI.Projects.MonthlyRecurrenceSchedule", + "azure.ai.projects.models.NamespaceToolParam": "OpenAI.NamespaceToolParam", "azure.ai.projects.models.NoAuthenticationCredentials": "Azure.AI.Projects.NoAuthenticationCredentials", "azure.ai.projects.models.OneTimeTrigger": "Azure.AI.Projects.OneTimeTrigger", "azure.ai.projects.models.OpenApiAuthDetails": "Azure.AI.Projects.OpenApiAuthDetails", @@ -240,21 +236,25 @@ "azure.ai.projects.models.OpenApiProjectConnectionAuthDetails": "Azure.AI.Projects.OpenApiProjectConnectionAuthDetails", "azure.ai.projects.models.OpenApiProjectConnectionSecurityScheme": "Azure.AI.Projects.OpenApiProjectConnectionSecurityScheme", "azure.ai.projects.models.OpenApiTool": "Azure.AI.Projects.OpenApiTool", - "azure.ai.projects.models.OptimizationAgentDefinition": "Azure.AI.Projects.OptimizationAgentDefinition", + "azure.ai.projects.models.OptimizationAgentIdentifier": "Azure.AI.Projects.OptimizationAgentIdentifier", "azure.ai.projects.models.OptimizationCandidate": "Azure.AI.Projects.OptimizationCandidate", + "azure.ai.projects.models.OptimizationDatasetCriterion": "Azure.AI.Projects.OptimizationDatasetCriterion", + "azure.ai.projects.models.OptimizationDatasetInput": "Azure.AI.Projects.OptimizationDatasetInput", + "azure.ai.projects.models.OptimizationDatasetItem": "Azure.AI.Projects.OptimizationDatasetItem", + "azure.ai.projects.models.OptimizationEvaluatorRef": "Azure.AI.Projects.OptimizationEvaluatorRef", + "azure.ai.projects.models.OptimizationInlineDatasetInput": "Azure.AI.Projects.OptimizationInlineDatasetInput", "azure.ai.projects.models.OptimizationJob": "Azure.AI.Projects.OptimizationJob", "azure.ai.projects.models.OptimizationJobInputs": "Azure.AI.Projects.OptimizationJobInputs", + "azure.ai.projects.models.OptimizationJobListItem": "Azure.AI.Projects.OptimizationJobListItem", "azure.ai.projects.models.OptimizationJobProgress": "Azure.AI.Projects.OptimizationJobProgress", "azure.ai.projects.models.OptimizationJobResult": "Azure.AI.Projects.OptimizationJobResult", "azure.ai.projects.models.OptimizationOptions": "Azure.AI.Projects.OptimizationOptions", - "azure.ai.projects.models.OptimizationTaskResult": "Azure.AI.Projects.OptimizationTaskResult", + "azure.ai.projects.models.OptimizationReferenceDatasetInput": "Azure.AI.Projects.OptimizationReferenceDatasetInput", "azure.ai.projects.models.TelemetryEndpoint": "Azure.AI.Projects.TelemetryEndpoint", "azure.ai.projects.models.OtlpTelemetryEndpoint": "Azure.AI.Projects.OtlpTelemetryEndpoint", "azure.ai.projects.models.PendingUploadRequest": "Azure.AI.Projects.PendingUploadRequest", "azure.ai.projects.models.PendingUploadResponse": "Azure.AI.Projects.PendingUploadResponse", "azure.ai.projects.models.ProceduralMemoryItem": "Azure.AI.Projects.ProceduralMemoryItem", - "azure.ai.projects.models.PromoteCandidateRequest": "Azure.AI.Projects.PromoteCandidateRequest", - "azure.ai.projects.models.PromoteCandidateResponse": "Azure.AI.Projects.PromoteCandidateResponse", "azure.ai.projects.models.PromotionInfo": "Azure.AI.Projects.PromotionInfo", "azure.ai.projects.models.PromptAgentDefinition": "Azure.AI.Projects.PromptAgentDefinition", "azure.ai.projects.models.PromptAgentDefinitionTextOptions": "Azure.AI.Projects.PromptAgentDefinitionTextOptions", @@ -308,6 +308,8 @@ "azure.ai.projects.models.ToolboxVersionObject": "Azure.AI.Projects.ToolboxVersionObject", "azure.ai.projects.models.ToolChoiceAllowed": "OpenAI.ToolChoiceAllowed", "azure.ai.projects.models.ToolChoiceCodeInterpreter": "OpenAI.ToolChoiceCodeInterpreter", + "azure.ai.projects.models.ToolChoiceComputer": "OpenAI.ToolChoiceComputer", + "azure.ai.projects.models.ToolChoiceComputerUse": "OpenAI.ToolChoiceComputerUse", "azure.ai.projects.models.ToolChoiceComputerUsePreview": "OpenAI.ToolChoiceComputerUsePreview", "azure.ai.projects.models.ToolChoiceCustom": "OpenAI.ToolChoiceCustom", "azure.ai.projects.models.ToolChoiceFileSearch": "OpenAI.ToolChoiceFileSearch", @@ -319,6 +321,7 @@ "azure.ai.projects.models.ToolConfig": "Azure.AI.Projects.ToolConfig", "azure.ai.projects.models.ToolDescription": "Azure.AI.Projects.ToolDescription", "azure.ai.projects.models.ToolProjectConnection": "Azure.AI.Projects.ToolProjectConnection", + "azure.ai.projects.models.ToolSearchToolParam": "OpenAI.ToolSearchToolParam", "azure.ai.projects.models.ToolUseFineTuningDataGenerationJobOptions": "Azure.AI.Projects.ToolUseFineTuningDataGenerationJobOptions", "azure.ai.projects.models.TracesDataGenerationJobOptions": "Azure.AI.Projects.TracesDataGenerationJobOptions", "azure.ai.projects.models.TracesDataGenerationJobSource": "Azure.AI.Projects.TracesDataGenerationJobSource", @@ -352,7 +355,9 @@ "azure.ai.projects.models.OpenApiAuthType": "Azure.AI.Projects.OpenApiAuthType", "azure.ai.projects.models.FunctionShellToolParamEnvironmentType": "OpenAI.FunctionShellToolParamEnvironmentType", "azure.ai.projects.models.ContainerSkillType": "OpenAI.ContainerSkillType", + "azure.ai.projects.models.ToolSearchExecutionType": "OpenAI.ToolSearchExecutionType", "azure.ai.projects.models.SearchContextSize": "OpenAI.SearchContextSize", + "azure.ai.projects.models.SearchContentType": "OpenAI.SearchContentType", "azure.ai.projects.models.AgentProtocol": "Azure.AI.Projects.AgentProtocol", "azure.ai.projects.models.CodeDependencyResolution": "Azure.AI.Projects.CodeDependencyResolution", "azure.ai.projects.models.TelemetryEndpointKind": "Azure.AI.Projects.TelemetryEndpointKind", @@ -371,8 +376,9 @@ "azure.ai.projects.models.AgentSessionStatus": "Azure.AI.Projects.AgentSessionStatus", "azure.ai.projects.models.PageOrder": "Azure.AI.Projects.PageOrder", "azure.ai.projects.models.SessionLogEventType": "Azure.AI.Projects.SessionLogEventType", - "azure.ai.projects.models.JobStatus": "Azure.AI.Projects.JobStatus", + "azure.ai.projects.models.OptimizationDatasetInputType": "Azure.AI.Projects.OptimizationDatasetInputType", "azure.ai.projects.models.EvaluationLevel": "Azure.AI.Projects.EvaluationLevel", + "azure.ai.projects.models.JobStatus": "Azure.AI.Projects.JobStatus", "azure.ai.projects.models.EvaluationTaxonomyInputType": "Azure.AI.Projects.EvaluationTaxonomyInputType", "azure.ai.projects.models.RiskCategory": "Azure.AI.Projects.RiskCategory", "azure.ai.projects.models.EvaluatorType": "Azure.AI.Projects.EvaluatorType", @@ -475,5 +481,17 @@ "azure.ai.projects.operations.IndexesOperations.create_or_update": "Azure.AI.Projects.Indexes.createOrUpdateVersion", "azure.ai.projects.aio.operations.IndexesOperations.create_or_update": "Azure.AI.Projects.Indexes.createOrUpdateVersion" }, - "CrossLanguageVersion": "c44eac94eea1" +<<<<<<< HEAD +<<<<<<< HEAD +<<<<<<< HEAD + "CrossLanguageVersion": "9cf16cec0d0a" +======= + "CrossLanguageVersion": "3ae42dbde611" +>>>>>>> 5fce783a23 ([azure-ai-projects] Emit SDK from TypeSpec (commit 6aa89cf) (#47294)) +======= + "CrossLanguageVersion": "ca205130211f" +>>>>>>> 04139a93e3 ([azure-ai-projects] Emit SDK from TypeSpec, using latest OpenAI TypeSpec package (#47318)) +======= + "CrossLanguageVersion": "9cf16cec0d0a" +>>>>>>> 5757d1c726 (Emit from latest TypeSpec, including new Agent Optimization methods (#47482)) } \ No newline at end of file diff --git a/sdk/ai/azure-ai-projects/assets.json b/sdk/ai/azure-ai-projects/assets.json index e52797a1c639..c546cf197dd0 100644 --- a/sdk/ai/azure-ai-projects/assets.json +++ b/sdk/ai/azure-ai-projects/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "python", "TagPrefix": "python/ai/azure-ai-projects", - "Tag": "python/ai/azure-ai-projects_059be0eaf8" + "Tag": "python/ai/azure-ai-projects_cc9f48b0e7" } diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/_types.py b/sdk/ai/azure-ai-projects/azure/ai/projects/_types.py index 5e23b3911701..abad0c3afee4 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/_types.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/_types.py @@ -11,3 +11,4 @@ if TYPE_CHECKING: from . import models as _models Filters = Union["_models.ComparisonFilter", "_models.CompoundFilter"] +RoutineRunStatus = str diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py b/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py index dcc813297e02..c91d6470e2bf 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py @@ -31,25 +31,21 @@ def serialize_multipart_data_entry(data_entry: Any) -> Any: def _normalize_multipart_file_entry(field_name: str, entry: Any, index: int) -> Any: - """Ensure each multipart file entry carries a filename so that it is encoded - as a file part (with ``filename=``) rather than a plain form field. - - Servers commonly distinguish multipart file parts from form-data parts by - the presence of ``filename=`` in the part's ``Content-Disposition`` header. - When callers pass bare bytes / str / IO objects (e.g. - ``Path("x.zip").read_bytes()``), the underlying HTTP client emits the part - without a filename, which several Foundry endpoints reject with errors like - "At least one file must be uploaded". This helper synthesizes a filename - from the IO object's ``name`` attribute when available, otherwise falls - back to a stable default. - - :param field_name: The multipart form field name, used as a fallback filename. - :type field_name: str - :param entry: The file entry to normalize. May be a tuple, bytes, str, or IO object. + """Ensure a multipart file entry carries a filename for Content-Disposition. + + Servers distinguish file parts from plain form fields by the presence of + ``filename=`` in the ``Content-Disposition`` header. When callers pass + bare bytes/str/IO the HTTP client omits the filename and the server may + reject the upload. This helper wraps bare values into a (filename, content) + tuple, deriving the name from IO.name when available. + + :param str field_name: The multipart field name used as a filename fallback. + :param entry: The user-provided file entry (tuple, bytes, str, or IO). :type entry: any - :param index: Position of the entry within its field's list, used to disambiguate fallback filenames. - :type index: int - :return: A ``(filename, entry)`` tuple if ``entry`` was not already a tuple, otherwise ``entry`` unchanged. + :param int index: The positional index of the entry within the field, used + to disambiguate fallback filenames when multiple entries are provided. + :return: Either the original tuple entry, or a ``(filename, content)`` tuple + wrapping the bare value. :rtype: any """ if isinstance(entry, tuple): @@ -60,7 +56,14 @@ def _normalize_multipart_file_entry(field_name: str, entry: Any, index: int) -> filename = os.path.basename(name_attr) if not filename: filename = f"{field_name}_{index}" if index else field_name - return (filename, entry) + + # Return a 3-tuple with an explicit "application/octet-stream" content type. + # A 2-tuple (filename, content) would leave the part's Content-Type unset, and + # the sdk core library only defaults to "application/octet-stream" for bare + # (non-tuple) values - a tuple bypasses that default and falls back to the + # HTTP "text/plain" default instead. Setting it explicitly preserves the + # pre-existing behavior for bare bytes/IO across all transports. + return (filename, entry, "application/octet-stream") def prepare_multipart_form_data( @@ -68,11 +71,8 @@ def prepare_multipart_form_data( ) -> list[FileType]: files: list[FileType] = [] - # Append data fields first so they appear before file parts in the encoded - # multipart body. Some streaming server-side parsers (e.g. the Foundry - # hosted-agents `create_agent_version_from_code` endpoint) require small - # JSON metadata parts to precede large binary file parts; otherwise they - # report the metadata part as missing. + # Data fields first so streaming server-side parsers see metadata before + # binary file parts. for data_field in data_fields: data_entry = body.get(data_field) if data_entry: @@ -83,7 +83,7 @@ def prepare_multipart_form_data( if isinstance(multipart_entry, list): for idx, e in enumerate(multipart_entry): files.append((multipart_field, _normalize_multipart_file_entry(multipart_field, e, idx))) - elif multipart_entry: + elif multipart_entry is not None: files.append((multipart_field, _normalize_multipart_file_entry(multipart_field, multipart_entry, 0))) return files diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py index 118e80092cf3..ead83bf80860 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py @@ -54,19 +54,13 @@ build_beta_agents_delete_session_request, build_beta_agents_download_code_request, build_beta_agents_download_session_file_request, - build_beta_agents_get_candidate_file_request, - build_beta_agents_get_optimization_candidate_config_request, - build_beta_agents_get_optimization_candidate_request, - build_beta_agents_get_optimization_candidate_results_request, build_beta_agents_get_optimization_job_request, build_beta_agents_get_session_log_stream_request, build_beta_agents_get_session_request, - build_beta_agents_list_optimization_candidates_request, build_beta_agents_list_optimization_jobs_request, build_beta_agents_list_session_files_request, build_beta_agents_list_sessions_request, build_beta_agents_patch_agent_details_request, - build_beta_agents_promote_candidate_request, build_beta_agents_stop_session_request, build_beta_agents_upload_session_file_request, build_beta_datasets_cancel_generation_job_request, @@ -235,7 +229,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace_async async def get(self, agent_name: str, **kwargs: Any) -> _models.AgentDetails: - """Retrieves the agent. + """Get an agent. + + Retrieves an agent definition by its unique name. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -302,7 +298,9 @@ async def get(self, agent_name: str, **kwargs: Any) -> _models.AgentDetails: async def delete( self, agent_name: str, *, force: Optional[bool] = None, **kwargs: Any ) -> _models.DeleteAgentResponse: - """Deletes an agent. For hosted agents, if any version has active sessions, the request is + """Delete an agent. + + Deletes an agent. For hosted agents, if any version has active sessions, the request is rejected with HTTP 409 unless ``force`` is set to true. When force is true, all associated sessions are cascade-deleted along with the agent and its versions. @@ -383,7 +381,9 @@ def list( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.AgentDetails"]: - """Returns the list of all agents. + """List agents. + + Returns a paged collection of agent resources. :keyword kind: Filter agents by kind. If not provided, all agents are returned. Known values are: "prompt", "hosted", "workflow", and "external". Default value is None. @@ -480,7 +480,9 @@ async def create_version( blueprint_reference: Optional[_models.AgentBlueprintReference] = None, **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version. + """Create an agent version. + + Creates a new version for the specified agent and returns the created version resource. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -515,7 +517,9 @@ async def create_version( async def create_version( self, agent_name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version. + """Create an agent version. + + Creates a new version for the specified agent and returns the created version resource. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -538,7 +542,9 @@ async def create_version( async def create_version( self, agent_name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version. + """Create an agent version. + + Creates a new version for the specified agent and returns the created version resource. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -569,7 +575,9 @@ async def create_version( blueprint_reference: Optional[_models.AgentBlueprintReference] = None, **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version. + """Create an agent version. + + Creates a new version for the specified agent and returns the created version resource. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -685,7 +693,9 @@ async def create_version_from_manifest( description: Optional[str] = None, **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version from a manifest. + """Create an agent version from manifest. + + Imports the provided manifest to create a new version for the specified agent. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -720,7 +730,9 @@ async def create_version_from_manifest( async def create_version_from_manifest( self, agent_name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version from a manifest. + """Create an agent version from manifest. + + Imports the provided manifest to create a new version for the specified agent. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -743,7 +755,9 @@ async def create_version_from_manifest( async def create_version_from_manifest( self, agent_name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version from a manifest. + """Create an agent version from manifest. + + Imports the provided manifest to create a new version for the specified agent. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -774,7 +788,9 @@ async def create_version_from_manifest( description: Optional[str] = None, **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version from a manifest. + """Create an agent version from manifest. + + Imports the provided manifest to create a new version for the specified agent. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -882,7 +898,9 @@ async def create_version_from_manifest( @distributed_trace_async async def get_version(self, agent_name: str, agent_version: str, **kwargs: Any) -> _models.AgentVersionDetails: - """Retrieves a specific version of an agent. + """Get an agent version. + + Retrieves the specified version of an agent by its agent name and version identifier. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -952,7 +970,9 @@ async def get_version(self, agent_name: str, agent_version: str, **kwargs: Any) async def delete_version( self, agent_name: str, agent_version: str, *, force: Optional[bool] = None, **kwargs: Any ) -> _models.DeleteAgentVersionResponse: - """Deletes a specific version of an agent. For hosted agents, if the version has active sessions, + """Delete an agent version. + + Deletes a specific version of an agent. For hosted agents, if the version has active sessions, the request is rejected with HTTP 409 unless ``force`` is set to true. When force is true, all sessions associated with this version are cascade-deleted. @@ -1037,7 +1057,9 @@ def list_versions( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.AgentVersionDetails"]: - """Returns the list of versions of an agent. + """List agent versions. + + Returns a paged collection of versions for the specified agent. :param agent_name: The name of the agent to retrieve versions for. Required. :type agent_name: str @@ -1143,6 +1165,8 @@ def __init__(self, *args, **kwargs) -> None: async def get(self, id: str, **kwargs: Any) -> _models.EvaluationRule: """Get an evaluation rule. + Retrieves the specified evaluation rule and its configuration. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping @@ -1204,6 +1228,8 @@ async def get(self, id: str, **kwargs: Any) -> _models.EvaluationRule: async def delete(self, id: str, **kwargs: Any) -> None: """Delete an evaluation rule. + Removes the specified evaluation rule from the project. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :return: None @@ -1254,6 +1280,8 @@ async def create_or_update( ) -> _models.EvaluationRule: """Create or update an evaluation rule. + Creates a new evaluation rule, or replaces the existing rule when the identifier matches. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :param evaluation_rule: Evaluation rule resource. Required. @@ -1272,6 +1300,8 @@ async def create_or_update( ) -> _models.EvaluationRule: """Create or update an evaluation rule. + Creates a new evaluation rule, or replaces the existing rule when the identifier matches. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :param evaluation_rule: Evaluation rule resource. Required. @@ -1290,6 +1320,8 @@ async def create_or_update( ) -> _models.EvaluationRule: """Create or update an evaluation rule. + Creates a new evaluation rule, or replaces the existing rule when the identifier matches. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :param evaluation_rule: Evaluation rule resource. Required. @@ -1308,6 +1340,8 @@ async def create_or_update( ) -> _models.EvaluationRule: """Create or update an evaluation rule. + Creates a new evaluation rule, or replaces the existing rule when the identifier matches. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :param evaluation_rule: Evaluation rule resource. Is one of the following types: @@ -1387,7 +1421,10 @@ def list( enabled: Optional[bool] = None, **kwargs: Any ) -> AsyncItemPaged["_models.EvaluationRule"]: - """List all evaluation rules. + """List evaluation rules. + + Returns the evaluation rules configured for the project, optionally filtered by action type, + agent name, or enabled state. :keyword action_type: Filter by the type of evaluation rule. Known values are: "continuousEvaluation" and "humanEvaluationPreview". Default value is None. @@ -1503,7 +1540,10 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace_async async def _get(self, name: str, **kwargs: Any) -> _models.Connection: - """Get a connection by name, without populating connection credentials. + """Get a connection. + + Retrieves the specified connection and its configuration details without including credential + values. :param name: The friendly name of the connection, provided by the user. Required. :type name: str @@ -1569,7 +1609,9 @@ async def _get(self, name: str, **kwargs: Any) -> _models.Connection: @distributed_trace_async async def _get_with_credentials(self, name: str, **kwargs: Any) -> _models.Connection: - """Get a connection by name, with its connection credentials. + """Get a connection with credentials. + + Retrieves the specified connection together with its credential values. :param name: The friendly name of the connection, provided by the user. Required. :type name: str @@ -1641,13 +1683,16 @@ def list( default_connection: Optional[bool] = None, **kwargs: Any ) -> AsyncItemPaged["_models.Connection"]: - """List all connections in the project, without populating connection credentials. + """List connections. + + Returns the connections available in the current project, optionally filtered by type or + default status. - :keyword connection_type: List connections of this specific type. Known values are: + :keyword connection_type: Lists connections of this specific type. Known values are: "AzureOpenAI", "AzureBlob", "AzureStorageAccount", "CognitiveSearch", "CosmosDB", "ApiKey", "AppConfig", "AppInsights", "CustomKeys", and "RemoteTool_Preview". Default value is None. :paramtype connection_type: str or ~azure.ai.projects.models.ConnectionType - :keyword default_connection: List connections that are default connections. Default value is + :keyword default_connection: Lists connections that are default connections. Default value is None. :paramtype default_connection: bool :return: An iterator like instance of Connection @@ -1756,7 +1801,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def list_versions(self, name: str, **kwargs: Any) -> AsyncItemPaged["_models.DatasetVersion"]: - """List all versions of the given DatasetVersion. + """List versions. + + List all versions of the given DatasetVersion. :param name: The name of the resource. Required. :type name: str @@ -1847,7 +1894,9 @@ async def get_next(next_link=None): @distributed_trace def list(self, **kwargs: Any) -> AsyncItemPaged["_models.DatasetVersion"]: - """List the latest version of each DatasetVersion. + """List latest versions. + + List the latest version of each DatasetVersion. :return: An iterator like instance of DatasetVersion :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.DatasetVersion] @@ -1935,7 +1984,9 @@ async def get_next(next_link=None): @distributed_trace_async async def get(self, name: str, version: str, **kwargs: Any) -> _models.DatasetVersion: - """Get the specific version of the DatasetVersion. The service returns 404 Not Found error if the + """Get a version. + + Get the specific version of the DatasetVersion. The service returns 404 Not Found error if the DatasetVersion does not exist. :param name: The name of the resource. Required. @@ -2000,7 +2051,9 @@ async def get(self, name: str, version: str, **kwargs: Any) -> _models.DatasetVe @distributed_trace_async async def delete(self, name: str, version: str, **kwargs: Any) -> None: - """Delete the specific version of the DatasetVersion. The service returns 204 No Content if the + """Delete a version. + + Delete the specific version of the DatasetVersion. The service returns 204 No Content if the DatasetVersion was deleted successfully or if the DatasetVersion does not exist. :param name: The name of the resource. Required. @@ -2060,7 +2113,9 @@ async def create_or_update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + """Create or update a version. + + Create a new or update an existing DatasetVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -2086,7 +2141,9 @@ async def create_or_update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + """Create or update a version. + + Create a new or update an existing DatasetVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -2112,7 +2169,9 @@ async def create_or_update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + """Create or update a version. + + Create a new or update an existing DatasetVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -2132,7 +2191,9 @@ async def create_or_update( async def create_or_update( self, name: str, version: str, dataset_version: Union[_models.DatasetVersion, JSON, IO[bytes]], **kwargs: Any ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + """Create or update a version. + + Create a new or update an existing DatasetVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -2217,7 +2278,9 @@ async def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of a dataset for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified dataset version. :param name: The name of the resource. Required. :type name: str @@ -2243,7 +2306,9 @@ async def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of a dataset for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified dataset version. :param name: The name of the resource. Required. :type name: str @@ -2269,7 +2334,9 @@ async def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of a dataset for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified dataset version. :param name: The name of the resource. Required. :type name: str @@ -2293,7 +2360,9 @@ async def pending_upload( pending_upload_request: Union[_models.PendingUploadRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of a dataset for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified dataset version. :param name: The name of the resource. Required. :type name: str @@ -2371,7 +2440,9 @@ async def pending_upload( @distributed_trace_async async def get_credentials(self, name: str, version: str, **kwargs: Any) -> _models.DatasetCredential: - """Get the SAS credential to access the storage account associated with a Dataset version. + """Get dataset credentials. + + Gets the SAS credential to access the storage account associated with a Dataset version. :param name: The name of the resource. Required. :type name: str @@ -2453,7 +2524,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace_async async def get(self, name: str, **kwargs: Any) -> _models.Deployment: - """Get a deployed model. + """Get a deployment. + + Gets a deployed model. :param name: Name of the deployment. Required. :type name: str @@ -2526,7 +2599,10 @@ def list( deployment_type: Optional[Union[str, _models.DeploymentType]] = None, **kwargs: Any ) -> AsyncItemPaged["_models.Deployment"]: - """List all deployed models in the project. + """List deployments. + + Returns the deployed models available in the current project, optionally filtered by publisher, + model name, or deployment type. :keyword model_publisher: Model publisher to filter models by. Default value is None. :paramtype model_publisher: str @@ -2643,7 +2719,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def list_versions(self, name: str, **kwargs: Any) -> AsyncItemPaged["_models.Index"]: - """List all versions of the given Index. + """List versions. + + List all versions of the given Index. :param name: The name of the resource. Required. :type name: str @@ -2734,7 +2812,9 @@ async def get_next(next_link=None): @distributed_trace def list(self, **kwargs: Any) -> AsyncItemPaged["_models.Index"]: - """List the latest version of each Index. + """List latest versions. + + List the latest version of each Index. :return: An iterator like instance of Index :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.Index] @@ -2822,7 +2902,9 @@ async def get_next(next_link=None): @distributed_trace_async async def get(self, name: str, version: str, **kwargs: Any) -> _models.Index: - """Get the specific version of the Index. The service returns 404 Not Found error if the Index + """Get a version. + + Get the specific version of the Index. The service returns 404 Not Found error if the Index does not exist. :param name: The name of the resource. Required. @@ -2887,7 +2969,9 @@ async def get(self, name: str, version: str, **kwargs: Any) -> _models.Index: @distributed_trace_async async def delete(self, name: str, version: str, **kwargs: Any) -> None: - """Delete the specific version of the Index. The service returns 204 No Content if the Index was + """Delete a version. + + Delete the specific version of the Index. The service returns 204 No Content if the Index was deleted successfully or if the Index does not exist. :param name: The name of the resource. Required. @@ -2947,7 +3031,9 @@ async def create_or_update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.Index: - """Create a new or update an existing Index with the given version id. + """Create or update a version. + + Create a new or update an existing Index with the given version id. :param name: The name of the resource. Required. :type name: str @@ -2967,7 +3053,9 @@ async def create_or_update( async def create_or_update( self, name: str, version: str, index: JSON, *, content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.Index: - """Create a new or update an existing Index with the given version id. + """Create or update a version. + + Create a new or update an existing Index with the given version id. :param name: The name of the resource. Required. :type name: str @@ -2993,7 +3081,9 @@ async def create_or_update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.Index: - """Create a new or update an existing Index with the given version id. + """Create or update a version. + + Create a new or update an existing Index with the given version id. :param name: The name of the resource. Required. :type name: str @@ -3013,7 +3103,9 @@ async def create_or_update( async def create_or_update( self, name: str, version: str, index: Union[_models.Index, JSON, IO[bytes]], **kwargs: Any ) -> _models.Index: - """Create a new or update an existing Index with the given version id. + """Create or update a version. + + Create a new or update an existing Index with the given version id. :param name: The name of the resource. Required. :type name: str @@ -3089,7 +3181,7 @@ async def create_or_update( return deserialized # type: ignore -class BetaAgentsOperations: # pylint: disable=too-many-public-methods +class BetaAgentsOperations: """ .. warning:: **DO NOT** instantiate this class directly. @@ -3116,7 +3208,9 @@ async def patch_agent_details( agent_card: Optional[_models.AgentCard] = None, **kwargs: Any ) -> _models.AgentDetails: - """Updates an agent endpoint. + """Update an agent endpoint. + + Applies a merge-patch update to the specified agent endpoint configuration. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -3136,7 +3230,9 @@ async def patch_agent_details( async def patch_agent_details( self, agent_name: str, body: JSON, *, content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.AgentDetails: - """Updates an agent endpoint. + """Update an agent endpoint. + + Applies a merge-patch update to the specified agent endpoint configuration. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -3154,7 +3250,9 @@ async def patch_agent_details( async def patch_agent_details( self, agent_name: str, body: IO[bytes], *, content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.AgentDetails: - """Updates an agent endpoint. + """Update an agent endpoint. + + Applies a merge-patch update to the specified agent endpoint configuration. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -3178,7 +3276,9 @@ async def patch_agent_details( agent_card: Optional[_models.AgentCard] = None, **kwargs: Any ) -> _models.AgentDetails: - """Updates an agent endpoint. + """Update an agent endpoint. + + Applies a merge-patch update to the specified agent endpoint configuration. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -3269,7 +3369,12 @@ async def create_version_from_code( code_zip_sha256: str, **kwargs: Any ) -> _models.AgentVersionDetails: - """create_version_from_code. + """Create an agent version from code. + + Creates a new agent version from code. Uploads the code zip and creates a new version for an + existing agent. The SHA-256 hex digest of the zip is provided in the ``x-ms-code-zip-sha256`` + header for integrity and dedup. The request body is multipart/form-data with a JSON metadata + part and a binary code part (part order is irrelevant). Maximum upload size is 250 MB. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -3292,7 +3397,12 @@ async def create_version_from_code( async def create_version_from_code( self, agent_name: str, content: JSON, *, code_zip_sha256: str, **kwargs: Any ) -> _models.AgentVersionDetails: - """create_version_from_code. + """Create an agent version from code. + + Creates a new agent version from code. Uploads the code zip and creates a new version for an + existing agent. The SHA-256 hex digest of the zip is provided in the ``x-ms-code-zip-sha256`` + header for integrity and dedup. The request body is multipart/form-data with a JSON metadata + part and a binary code part (part order is irrelevant). Maximum upload size is 250 MB. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -3320,7 +3430,12 @@ async def create_version_from_code( code_zip_sha256: str, **kwargs: Any ) -> _models.AgentVersionDetails: - """create_version_from_code. + """Create an agent version from code. + + Creates a new agent version from code. Uploads the code zip and creates a new version for an + existing agent. The SHA-256 hex digest of the zip is provided in the ``x-ms-code-zip-sha256`` + header for integrity and dedup. The request body is multipart/form-data with a JSON metadata + part and a binary code part (part order is irrelevant). Maximum upload size is 250 MB. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -3404,7 +3519,9 @@ async def create_version_from_code( async def download_code( self, agent_name: str, *, agent_version: Optional[str] = None, **kwargs: Any ) -> AsyncIterator[bytes]: - """Download the code zip for a code-based hosted agent. + """Download agent code. + + Downloads the code zip for a code-based hosted agent. Returns the previously-uploaded zip (``application/zip``). If ``agent_version`` is supplied, returns that version's code zip; otherwise @@ -3490,7 +3607,9 @@ async def create_session( agent_session_id: Optional[str] = None, **kwargs: Any ) -> _models.AgentSessionResource: - """Creates a new session for an agent endpoint. The endpoint resolves the backing agent version + """Create a session. + + Creates a new session for an agent endpoint. The endpoint resolves the backing agent version from ``version_indicator`` and enforces session ownership using the provided isolation key for session-mutating operations. @@ -3522,7 +3641,9 @@ async def create_session( content_type: str = "application/json", **kwargs: Any ) -> _models.AgentSessionResource: - """Creates a new session for an agent endpoint. The endpoint resolves the backing agent version + """Create a session. + + Creates a new session for an agent endpoint. The endpoint resolves the backing agent version from ``version_indicator`` and enforces session ownership using the provided isolation key for session-mutating operations. @@ -3551,7 +3672,9 @@ async def create_session( content_type: str = "application/json", **kwargs: Any ) -> _models.AgentSessionResource: - """Creates a new session for an agent endpoint. The endpoint resolves the backing agent version + """Create a session. + + Creates a new session for an agent endpoint. The endpoint resolves the backing agent version from ``version_indicator`` and enforces session ownership using the provided isolation key for session-mutating operations. @@ -3581,7 +3704,9 @@ async def create_session( agent_session_id: Optional[str] = None, **kwargs: Any ) -> _models.AgentSessionResource: - """Creates a new session for an agent endpoint. The endpoint resolves the backing agent version + """Create a session. + + Creates a new session for an agent endpoint. The endpoint resolves the backing agent version from ``version_indicator`` and enforces session ownership using the provided isolation key for session-mutating operations. @@ -3676,7 +3801,9 @@ async def create_session( async def get_session( self, agent_name: str, session_id: str, *, user_isolation_key: Optional[str] = None, **kwargs: Any ) -> _models.AgentSessionResource: - """Retrieves a session by ID. + """Get a session. + + Retrieves the details of a hosted agent session by agent name and session identifier. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -3750,7 +3877,9 @@ async def get_session( async def delete_session( self, agent_name: str, session_id: str, *, user_isolation_key: Optional[str] = None, **kwargs: Any ) -> None: - """Deletes a session synchronously. Returns 204 No Content when the session is deleted or does not + """Delete a session. + + Deletes a session synchronously. Returns 204 No Content when the session is deleted or does not exist. :param agent_name: The name of the agent. Required. @@ -3810,7 +3939,10 @@ async def delete_session( @distributed_trace_async async def stop_session(self, agent_name: str, session_id: str, **kwargs: Any) -> None: - """Stops a session. Returns 204 No Content when the stop succeeds. + """Stop a session. + + Terminates the specified hosted agent session and returns 204 No Content when the request + succeeds. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -3874,7 +4006,9 @@ def list_sessions( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.AgentSessionResource"]: - """Returns a list of sessions for the specified agent. + """List sessions for an agent. + + Returns a paged collection of sessions associated with the specified agent endpoint. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -3966,7 +4100,9 @@ async def get_next(_continuation_token=None): async def get_session_log_stream( self, agent_name: str, agent_version: str, session_id: str, **kwargs: Any ) -> _models.SessionLogEvent: - """Streams console logs (stdout / stderr) for a specific hosted agent session + """Stream console logs for a hosted agent session. + + Streams console logs (stdout / stderr) for a specific hosted agent session as a Server-Sent Events (SSE) stream. Each SSE frame contains: @@ -4074,8 +4210,10 @@ async def _upload_session_file( user_isolation_key: Optional[str] = None, **kwargs: Any ) -> _models.SessionFileWriteResult: - """Upload a file to the session sandbox via binary stream. Maximum file size is 50 MB. Uploads - exceeding this limit return 413 Payload Too Large. + """Upload a session file. + + Uploads binary file content to the specified path in the session sandbox. The service stores + the file relative to the session home directory and rejects payloads larger than 50 MB. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -4166,7 +4304,10 @@ async def download_session_file( user_isolation_key: Optional[str] = None, **kwargs: Any ) -> AsyncIterator[bytes]: - """Download a file from the session sandbox as a binary stream. + """Download a session file. + + Downloads the file at the specified sandbox path as a binary stream. The path is resolved + relative to the session home directory. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -4250,9 +4391,11 @@ def list_session_files( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.SessionDirectoryEntry"]: - """List files and directories at a given path in the session sandbox. Returns only the immediate - children of the specified directory (non-recursive). If path is not provided, lists the session - home directory. + """List session files. + + Returns files and directories at the specified path in the session sandbox. The response + includes only the immediate children of the target directory and defaults to the session home + directory when no path is supplied. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -4359,8 +4502,10 @@ async def delete_session_file( user_isolation_key: Optional[str] = None, **kwargs: Any ) -> None: - """Delete a file or directory from the session sandbox. If ``recursive`` is false (default) and - the target is a non-empty directory, the API returns 409 Conflict. + """Delete a session file. + + Deletes the specified file or directory from the session sandbox. When ``recursive`` is false, + deleting a non-empty directory returns 409 Conflict. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -4428,7 +4573,7 @@ async def delete_session_file( @overload async def create_optimization_job( self, - inputs: _models.OptimizationJobInputs, + job: _models.OptimizationJob, *, operation_id: Optional[str] = None, content_type: str = "application/json", @@ -4439,8 +4584,8 @@ async def create_optimization_job( Create an optimization job. Returns 201 with the queued job. Honours ``Operation-Id`` for idempotent retry. - :param inputs: The optimization job inputs. Required. - :type inputs: ~azure.ai.projects.models.OptimizationJobInputs + :param job: The job to create. Required. + :type job: ~azure.ai.projects.models.OptimizationJob :keyword operation_id: Client-generated unique ID for idempotent retries. When absent, the server creates the job unconditionally. Default value is None. :paramtype operation_id: str @@ -4454,15 +4599,15 @@ async def create_optimization_job( @overload async def create_optimization_job( - self, inputs: JSON, *, operation_id: Optional[str] = None, content_type: str = "application/json", **kwargs: Any + self, job: JSON, *, operation_id: Optional[str] = None, content_type: str = "application/json", **kwargs: Any ) -> _models.OptimizationJob: """Creates an agent optimization job. Create an optimization job. Returns 201 with the queued job. Honours ``Operation-Id`` for idempotent retry. - :param inputs: The optimization job inputs. Required. - :type inputs: JSON + :param job: The job to create. Required. + :type job: JSON :keyword operation_id: Client-generated unique ID for idempotent retries. When absent, the server creates the job unconditionally. Default value is None. :paramtype operation_id: str @@ -4477,7 +4622,7 @@ async def create_optimization_job( @overload async def create_optimization_job( self, - inputs: IO[bytes], + job: IO[bytes], *, operation_id: Optional[str] = None, content_type: str = "application/json", @@ -4488,8 +4633,8 @@ async def create_optimization_job( Create an optimization job. Returns 201 with the queued job. Honours ``Operation-Id`` for idempotent retry. - :param inputs: The optimization job inputs. Required. - :type inputs: IO[bytes] + :param job: The job to create. Required. + :type job: IO[bytes] :keyword operation_id: Client-generated unique ID for idempotent retries. When absent, the server creates the job unconditionally. Default value is None. :paramtype operation_id: str @@ -4503,20 +4648,16 @@ async def create_optimization_job( @distributed_trace_async async def create_optimization_job( - self, - inputs: Union[_models.OptimizationJobInputs, JSON, IO[bytes]], - *, - operation_id: Optional[str] = None, - **kwargs: Any + self, job: Union[_models.OptimizationJob, JSON, IO[bytes]], *, operation_id: Optional[str] = None, **kwargs: Any ) -> _models.OptimizationJob: """Creates an agent optimization job. Create an optimization job. Returns 201 with the queued job. Honours ``Operation-Id`` for idempotent retry. - :param inputs: The optimization job inputs. Is one of the following types: - OptimizationJobInputs, JSON, IO[bytes] Required. - :type inputs: ~azure.ai.projects.models.OptimizationJobInputs or JSON or IO[bytes] + :param job: The job to create. Is one of the following types: OptimizationJob, JSON, IO[bytes] + Required. + :type job: ~azure.ai.projects.models.OptimizationJob or JSON or IO[bytes] :keyword operation_id: Client-generated unique ID for idempotent retries. When absent, the server creates the job unconditionally. Default value is None. :paramtype operation_id: str @@ -4540,10 +4681,10 @@ async def create_optimization_job( content_type = content_type or "application/json" _content = None - if isinstance(inputs, (IOBase, bytes)): - _content = inputs + if isinstance(job, (IOBase, bytes)): + _content = job else: - _content = json.dumps(inputs, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(job, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_beta_agents_create_optimization_job_request( operation_id=operation_id, @@ -4597,7 +4738,7 @@ async def create_optimization_job( async def get_optimization_job(self, job_id: str, **kwargs: Any) -> _models.OptimizationJob: """Get info about an agent optimization job. - Get an optimization job by id. Returns 202 while in progress, 200 when terminal. + Get an optimization job by id. :param job_id: The ID of the job. Required. :type job_id: str @@ -4637,7 +4778,7 @@ async def get_optimization_job(self, job_id: str, **kwargs: Any) -> _models.Opti response = pipeline_response.http_response - if response.status_code not in [200, 202]: + if response.status_code not in [200]: if _stream: try: await response.read() # Load the body in memory and close the socket @@ -4673,7 +4814,7 @@ def list_optimization_jobs( status: Optional[Union[str, _models.JobStatus]] = None, agent_name: Optional[str] = None, **kwargs: Any - ) -> AsyncItemPaged["_models.OptimizationJob"]: + ) -> AsyncItemPaged["_models.OptimizationJobListItem"]: """Returns a list of agent optimization jobs. List optimization jobs. Supports cursor pagination and optional status / agent_name filters. @@ -4697,14 +4838,15 @@ def list_optimization_jobs( :paramtype status: str or ~azure.ai.projects.models.JobStatus :keyword agent_name: Filter to jobs targeting this agent name. Default value is None. :paramtype agent_name: str - :return: An iterator like instance of OptimizationJob - :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.OptimizationJob] + :return: An iterator like instance of OptimizationJobListItem + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.OptimizationJobListItem] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.OptimizationJob]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.OptimizationJobListItem]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -4736,7 +4878,7 @@ def prepare_request(_continuation_token=None): async def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.OptimizationJob], + List[_models.OptimizationJobListItem], deserialized.get("data", []), ) if cls: @@ -4768,7 +4910,8 @@ async def get_next(_continuation_token=None): async def cancel_optimization_job(self, job_id: str, **kwargs: Any) -> _models.OptimizationJob: """Cancels an agent optimization job. - Request cancellation. Idempotent on terminal states. + Request cancellation of a running or queued job. Returns an error if the job is already in a + terminal state. :param job_id: The ID of the job to cancel. Required. :type job_id: str @@ -4832,16 +4975,13 @@ async def cancel_optimization_job(self, job_id: str, **kwargs: Any) -> _models.O return deserialized # type: ignore @distributed_trace_async - async def delete_optimization_job(self, job_id: str, *, force: Optional[bool] = None, **kwargs: Any) -> None: + async def delete_optimization_job(self, job_id: str, **kwargs: Any) -> None: """Deletes an agent optimization job. Delete the job and its candidate artifacts. Cancels first if non-terminal. :param job_id: The ID of the job to delete. Required. :type job_id: str - :keyword force: When true, force-delete even if the job is in a non-terminal state. Default - value is None. - :paramtype force: bool :return: None :rtype: None :raises ~azure.core.exceptions.HttpResponseError: @@ -4861,7 +5001,6 @@ async def delete_optimization_job(self, job_id: str, *, force: Optional[bool] = _request = build_beta_agents_delete_optimization_job_request( job_id=job_id, - force=force, api_version=self._config.api_version, headers=_headers, params=_params, @@ -4889,17 +5028,19 @@ async def delete_optimization_job(self, job_id: str, *, force: Optional[bool] = if cls: return cls(pipeline_response, None, {}) # type: ignore - @distributed_trace_async - async def list_optimization_candidates( +<<<<<<< HEAD +<<<<<<< HEAD +======= + @distributed_trace + def list_optimization_candidates( self, job_id: str, *, limit: Optional[int] = None, order: Optional[Union[str, _models.PageOrder]] = None, - after: Optional[str] = None, before: Optional[str] = None, **kwargs: Any - ) -> _models.AgentsPagedResultOptimizationCandidate: + ) -> AsyncItemPaged["_models.OptimizationCandidate"]: """Returns a list of candidates for an optimization job. List candidates produced by a job. @@ -4914,23 +5055,22 @@ async def list_optimization_candidates( ascending order and``desc`` for descending order. Known values are: "asc" and "desc". Default value is None. :paramtype order: str or ~azure.ai.projects.models.PageOrder - :keyword after: A cursor for use in pagination. ``after`` is an object ID that defines your - place in the list. - For instance, if you make a list request and receive 100 objects, ending with obj_foo, your - subsequent call can include after=obj_foo in order to fetch the next page of the list. Default - value is None. - :paramtype after: str :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. Default value is None. :paramtype before: str - :return: AgentsPagedResultOptimizationCandidate. The AgentsPagedResultOptimizationCandidate is - compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentsPagedResultOptimizationCandidate + :return: An iterator like instance of OptimizationCandidate + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.OptimizationCandidate] :raises ~azure.core.exceptions.HttpResponseError: """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.OptimizationCandidate]] = kwargs.pop("cls", None) + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -4939,56 +5079,54 @@ async def list_optimization_candidates( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[_models.AgentsPagedResultOptimizationCandidate] = kwargs.pop("cls", None) + def prepare_request(_continuation_token=None): - _request = build_beta_agents_list_optimization_candidates_request( - job_id=job_id, - limit=limit, - order=order, - after=after, - before=before, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) + _request = build_beta_agents_list_optimization_candidates_request( + job_id=job_id, + limit=limit, + order=order, + after=_continuation_token, + before=before, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + return _request - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) - pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.OptimizationCandidate], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, AsyncList(list_of_elem) - response = pipeline_response.http_response + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) - if response.status_code not in [200]: - if _stream: - try: - await response.read() # Load the body in memory and close the socket - except (StreamConsumedError, StreamClosedError): - pass - map_error(status_code=response.status_code, response=response, error_map=error_map) - error = _failsafe_deserialize( - _models.ApiErrorResponse, - response, + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs ) - raise HttpResponseError(response=response, model=error) + response = pipeline_response.http_response - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.AgentsPagedResultOptimizationCandidate, response.json()) + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return pipeline_response - return deserialized # type: ignore + return AsyncItemPaged(get_next, extract_data) @distributed_trace_async async def get_optimization_candidate( @@ -5456,6 +5594,9 @@ async def promote_candidate( return deserialized # type: ignore +>>>>>>> 5fce783a23 ([azure-ai-projects] Emit SDK from TypeSpec (commit 6aa89cf) (#47294)) +======= +>>>>>>> 5757d1c726 (Emit from latest TypeSpec, including new Agent Optimization methods (#47482)) class BetaEvaluationTaxonomiesOperations: """ @@ -5476,7 +5617,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace_async async def get(self, name: str, **kwargs: Any) -> _models.EvaluationTaxonomy: - """Get an evaluation run by name. + """Get an evaluation taxonomy. + + Retrieves the specified evaluation taxonomy. :param name: The name of the resource. Required. :type name: str @@ -5541,6 +5684,9 @@ def list( ) -> AsyncItemPaged["_models.EvaluationTaxonomy"]: """List evaluation taxonomies. + Returns the evaluation taxonomies available in the project, optionally filtered by input name + or input type. + :keyword input_name: Filter by the evaluation input name. Default value is None. :paramtype input_name: str :keyword input_type: Filter by taxonomy input type. Default value is None. @@ -5633,7 +5779,9 @@ async def get_next(next_link=None): @distributed_trace_async async def delete(self, name: str, **kwargs: Any) -> None: - """Delete an evaluation taxonomy by name. + """Delete an evaluation taxonomy. + + Removes the specified evaluation taxonomy from the project. :param name: The name of the resource. Required. :type name: str @@ -5685,6 +5833,8 @@ async def create( ) -> _models.EvaluationTaxonomy: """Create an evaluation taxonomy. + Creates or replaces the specified evaluation taxonomy with the provided definition. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -5703,6 +5853,8 @@ async def create( ) -> _models.EvaluationTaxonomy: """Create an evaluation taxonomy. + Creates or replaces the specified evaluation taxonomy with the provided definition. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -5721,6 +5873,8 @@ async def create( ) -> _models.EvaluationTaxonomy: """Create an evaluation taxonomy. + Creates or replaces the specified evaluation taxonomy with the provided definition. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -5739,6 +5893,8 @@ async def create( ) -> _models.EvaluationTaxonomy: """Create an evaluation taxonomy. + Creates or replaces the specified evaluation taxonomy with the provided definition. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, @@ -5815,6 +5971,8 @@ async def update( ) -> _models.EvaluationTaxonomy: """Update an evaluation taxonomy. + Update an evaluation taxonomy. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -5833,6 +5991,8 @@ async def update( ) -> _models.EvaluationTaxonomy: """Update an evaluation taxonomy. + Update an evaluation taxonomy. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -5851,6 +6011,8 @@ async def update( ) -> _models.EvaluationTaxonomy: """Update an evaluation taxonomy. + Update an evaluation taxonomy. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -5869,6 +6031,8 @@ async def update( ) -> _models.EvaluationTaxonomy: """Update an evaluation taxonomy. + Update an evaluation taxonomy. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, @@ -5966,7 +6130,9 @@ def list_versions( limit: Optional[int] = None, **kwargs: Any ) -> AsyncItemPaged["_models.EvaluatorVersion"]: - """List all versions of the given evaluator. + """List evaluator versions. + + Returns the available versions for the specified evaluator. :param name: The name of the resource. Required. :type name: str @@ -6072,7 +6238,9 @@ def list( limit: Optional[int] = None, **kwargs: Any ) -> AsyncItemPaged["_models.EvaluatorVersion"]: - """List the latest version of each evaluator. + """List latest evaluator versions. + + Lists the latest version of each evaluator. :keyword type: Filter evaluators by type. Possible values: 'all', 'custom', 'builtin'. Is one of the following types: Literal["builtin"], Literal["custom"], Literal["all"], str Default @@ -6169,8 +6337,9 @@ async def get_next(next_link=None): @distributed_trace_async async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.EvaluatorVersion: - """Get the specific version of the EvaluatorVersion. The service returns 404 Not Found error if - the EvaluatorVersion does not exist. + """Get an evaluator version. + + Retrieves the specified evaluator version, returning 404 if it does not exist. :param name: The name of the resource. Required. :type name: str @@ -6234,8 +6403,9 @@ async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.E @distributed_trace_async async def delete_version(self, name: str, version: str, **kwargs: Any) -> None: - """Delete the specific version of the EvaluatorVersion. The service returns 204 No Content if the - EvaluatorVersion was deleted successfully or if the EvaluatorVersion does not exist. + """Delete an evaluator version. + + Removes the specified evaluator version. Returns 204 whether the version existed or not. :param name: The name of the resource. Required. :type name: str @@ -6293,7 +6463,9 @@ async def create_version( content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Create a new EvaluatorVersion with auto incremented version id. + """Create an evaluator version. + + Creates a new evaluator version with an auto-incremented version identifier. :param name: The name of the resource. Required. :type name: str @@ -6311,7 +6483,9 @@ async def create_version( async def create_version( self, name: str, evaluator_version: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Create a new EvaluatorVersion with auto incremented version id. + """Create an evaluator version. + + Creates a new evaluator version with an auto-incremented version identifier. :param name: The name of the resource. Required. :type name: str @@ -6329,7 +6503,9 @@ async def create_version( async def create_version( self, name: str, evaluator_version: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Create a new EvaluatorVersion with auto incremented version id. + """Create an evaluator version. + + Creates a new evaluator version with an auto-incremented version identifier. :param name: The name of the resource. Required. :type name: str @@ -6347,7 +6523,9 @@ async def create_version( async def create_version( self, name: str, evaluator_version: Union[_models.EvaluatorVersion, JSON, IO[bytes]], **kwargs: Any ) -> _models.EvaluatorVersion: - """Create a new EvaluatorVersion with auto incremented version id. + """Create an evaluator version. + + Creates a new evaluator version with an auto-incremented version identifier. :param name: The name of the resource. Required. :type name: str @@ -6429,7 +6607,9 @@ async def update_version( content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + """Update an evaluator version. + + Updates the specified evaluator version in place. :param name: The name of the resource. Required. :type name: str @@ -6449,7 +6629,9 @@ async def update_version( async def update_version( self, name: str, version: str, evaluator_version: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + """Update an evaluator version. + + Updates the specified evaluator version in place. :param name: The name of the resource. Required. :type name: str @@ -6475,7 +6657,9 @@ async def update_version( content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + """Update an evaluator version. + + Updates the specified evaluator version in place. :param name: The name of the resource. Required. :type name: str @@ -6499,7 +6683,9 @@ async def update_version( evaluator_version: Union[_models.EvaluatorVersion, JSON, IO[bytes]], **kwargs: Any ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + """Update an evaluator version. + + Updates the specified evaluator version in place. :param name: The name of the resource. Required. :type name: str @@ -6584,7 +6770,10 @@ async def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of an evaluator for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified evaluator + version. :param name: Required. :type name: str @@ -6610,7 +6799,10 @@ async def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of an evaluator for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified evaluator + version. :param name: Required. :type name: str @@ -6636,7 +6828,10 @@ async def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of an evaluator for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified evaluator + version. :param name: Required. :type name: str @@ -6660,7 +6855,10 @@ async def pending_upload( pending_upload_request: Union[_models.PendingUploadRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of an evaluator for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified evaluator + version. :param name: Required. :type name: str @@ -6750,7 +6948,10 @@ async def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get the SAS credential to access the storage account associated with an Evaluator version. + """Get evaluator credentials. + + Retrieves SAS credentials for accessing the storage account associated with the specified + evaluator version. :param name: Required. :type name: str @@ -6776,7 +6977,10 @@ async def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get the SAS credential to access the storage account associated with an Evaluator version. + """Get evaluator credentials. + + Retrieves SAS credentials for accessing the storage account associated with the specified + evaluator version. :param name: Required. :type name: str @@ -6802,7 +7006,10 @@ async def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get the SAS credential to access the storage account associated with an Evaluator version. + """Get evaluator credentials. + + Retrieves SAS credentials for accessing the storage account associated with the specified + evaluator version. :param name: Required. :type name: str @@ -6826,7 +7033,10 @@ async def get_credentials( credential_request: Union[_models.EvaluatorCredentialRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.DatasetCredential: - """Get the SAS credential to access the storage account associated with an Evaluator version. + """Get evaluator credentials. + + Retrieves SAS credentials for accessing the storage account associated with the specified + evaluator version. :param name: Required. :type name: str @@ -6915,7 +7125,7 @@ async def create_generation_job( content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorGenerationJob: - """Creates an evaluator generation job. + """Create an evaluator generation job. Creates an evaluator generation job. The service generates rubric-based evaluator definitions from the provided source materials asynchronously. @@ -6937,7 +7147,7 @@ async def create_generation_job( async def create_generation_job( self, job: JSON, *, operation_id: Optional[str] = None, content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorGenerationJob: - """Creates an evaluator generation job. + """Create an evaluator generation job. Creates an evaluator generation job. The service generates rubric-based evaluator definitions from the provided source materials asynchronously. @@ -6964,7 +7174,7 @@ async def create_generation_job( content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorGenerationJob: - """Creates an evaluator generation job. + """Create an evaluator generation job. Creates an evaluator generation job. The service generates rubric-based evaluator definitions from the provided source materials asynchronously. @@ -6990,7 +7200,7 @@ async def create_generation_job( operation_id: Optional[str] = None, **kwargs: Any ) -> _models.EvaluatorGenerationJob: - """Creates an evaluator generation job. + """Create an evaluator generation job. Creates an evaluator generation job. The service generates rubric-based evaluator definitions from the provided source materials asynchronously. @@ -7076,7 +7286,7 @@ async def create_generation_job( @distributed_trace_async async def get_generation_job(self, job_id: str, **kwargs: Any) -> _models.EvaluatorGenerationJob: - """Get info about an evaluator generation job. + """Get an evaluator generation job. Gets the details of an evaluator generation job by its ID. @@ -7153,9 +7363,11 @@ def list_generation_jobs( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.EvaluatorGenerationJob"]: - """Returns a list of evaluator generation jobs. + """List evaluator generation jobs. - Returns a list of evaluator generation jobs. + Returns a list of evaluator generation jobs. The List API has up to a few seconds of + propagation delay, so a recently created job may not appear immediately; use the Get evaluator + generation job API with the job ID to retrieve a specific job without delay. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -7239,7 +7451,7 @@ async def get_next(_continuation_token=None): @distributed_trace_async async def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.EvaluatorGenerationJob: - """Cancels an evaluator generation job. + """Cancel an evaluator generation job. Cancels an evaluator generation job by its ID. @@ -7306,7 +7518,9 @@ async def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.Eva @distributed_trace_async async def delete_generation_job(self, job_id: str, **kwargs: Any) -> None: - """Deletes an evaluator generation job by its ID. Deletes the job record only; the generated + """Delete an evaluator generation job. + + Deletes an evaluator generation job by its ID. Deletes the job record only; the generated evaluator (if any) is preserved. :param job_id: The ID of the job to delete. Required. @@ -7379,7 +7593,9 @@ def __init__(self, *args, **kwargs) -> None: async def generate( self, insight: _models.Insight, *, content_type: str = "application/json", **kwargs: Any ) -> _models.Insight: - """Generate Insights. + """Generate insights. + + Generates an insights report from the provided evaluation configuration. :param insight: Complete evaluation configuration including data source, evaluators, and result settings. Required. @@ -7396,7 +7612,9 @@ async def generate( async def generate( self, insight: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.Insight: - """Generate Insights. + """Generate insights. + + Generates an insights report from the provided evaluation configuration. :param insight: Complete evaluation configuration including data source, evaluators, and result settings. Required. @@ -7413,7 +7631,9 @@ async def generate( async def generate( self, insight: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.Insight: - """Generate Insights. + """Generate insights. + + Generates an insights report from the provided evaluation configuration. :param insight: Complete evaluation configuration including data source, evaluators, and result settings. Required. @@ -7428,7 +7648,9 @@ async def generate( @distributed_trace_async async def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwargs: Any) -> _models.Insight: - """Generate Insights. + """Generate insights. + + Generates an insights report from the provided evaluation configuration. :param insight: Complete evaluation configuration including data source, evaluators, and result settings. Is one of the following types: Insight, JSON, IO[bytes] Required. @@ -7505,7 +7727,9 @@ async def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwa async def get( self, insight_id: str, *, include_coordinates: Optional[bool] = None, **kwargs: Any ) -> _models.Insight: - """Get a specific insight by Id. + """Get an insight. + + Retrieves the specified insight report and its results. :param insight_id: The unique identifier for the insights report. Required. :type insight_id: str @@ -7583,7 +7807,9 @@ def list( include_coordinates: Optional[bool] = None, **kwargs: Any ) -> AsyncItemPaged["_models.Insight"]: - """List all insights in reverse chronological order (newest first). + """List insights. + + Returns insights in reverse chronological order, with the most recent entries first. :keyword type: Filter by the type of analysis. Known values are: "EvaluationRunClusterInsight", "AgentClusterInsight", and "EvaluationComparison". Default value is None. @@ -7721,6 +7947,8 @@ async def create( ) -> _models.MemoryStoreDetails: """Create a memory store. + Creates a memory store resource with the provided configuration. + :keyword name: The name of the memory store. Required. :paramtype name: str :keyword definition: The memory store definition. Required. @@ -7744,6 +7972,8 @@ async def create( ) -> _models.MemoryStoreDetails: """Create a memory store. + Creates a memory store resource with the provided configuration. + :param body: Required. :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. @@ -7760,6 +7990,8 @@ async def create( ) -> _models.MemoryStoreDetails: """Create a memory store. + Creates a memory store resource with the provided configuration. + :param body: Required. :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. @@ -7783,6 +8015,8 @@ async def create( ) -> _models.MemoryStoreDetails: """Create a memory store. + Creates a memory store resource with the provided configuration. + :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] :keyword name: The name of the memory store. Required. @@ -7881,6 +8115,8 @@ async def update( ) -> _models.MemoryStoreDetails: """Update a memory store. + Updates the specified memory store with the supplied configuration changes. + :param name: The name of the memory store to update. Required. :type name: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. @@ -7902,6 +8138,8 @@ async def update( ) -> _models.MemoryStoreDetails: """Update a memory store. + Updates the specified memory store with the supplied configuration changes. + :param name: The name of the memory store to update. Required. :type name: str :param body: Required. @@ -7920,6 +8158,8 @@ async def update( ) -> _models.MemoryStoreDetails: """Update a memory store. + Updates the specified memory store with the supplied configuration changes. + :param name: The name of the memory store to update. Required. :type name: str :param body: Required. @@ -7944,6 +8184,8 @@ async def update( ) -> _models.MemoryStoreDetails: """Update a memory store. + Updates the specified memory store with the supplied configuration changes. + :param name: The name of the memory store to update. Required. :type name: str :param body: Is either a JSON type or a IO[bytes] type. Required. @@ -8027,7 +8269,9 @@ async def update( @distributed_trace_async async def get(self, name: str, **kwargs: Any) -> _models.MemoryStoreDetails: - """Retrieve a memory store. + """Get a memory store. + + Retrieves the specified memory store and its current configuration. :param name: The name of the memory store to retrieve. Required. :type name: str @@ -8099,7 +8343,9 @@ def list( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.MemoryStoreDetails"]: - """List all memory stores. + """List memory stores. + + Returns the memory stores available to the caller. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -8184,6 +8430,8 @@ async def get_next(_continuation_token=None): async def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreResult: """Delete a memory store. + Deletes the specified memory store. + :param name: The name of the memory store to delete. Required. :type name: str :return: DeleteMemoryStoreResult. The DeleteMemoryStoreResult is compatible with MutableMapping @@ -8278,7 +8526,9 @@ async def _search_memories( options: Optional[_models.MemorySearchOptions] = None, **kwargs: Any ) -> _models.MemoryStoreSearchResult: - """Search for relevant memories from a memory store based on conversation context. + """Search memories. + + Searches the specified memory store for memories relevant to the provided conversation context. :param name: The name of the memory store to search. Required. :type name: str @@ -8486,7 +8736,10 @@ async def _begin_update_memories( update_delay: Optional[int] = None, **kwargs: Any ) -> AsyncLROPoller[_models.MemoryStoreUpdateCompletedResult]: - """Update memory store with conversation memories. + """Update memories. + + Starts an update that writes conversation memories into the specified memory store. The + operation returns a long-running status location for polling the update result. :param name: The name of the memory store to update. Required. :type name: str @@ -8577,7 +8830,9 @@ def get_long_running_output(pipeline_response): async def delete_scope( self, name: str, *, scope: str, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryStoreDeleteScopeResult: - """Delete all memories associated with a specific scope from a memory store. + """Delete memories by scope. + + Deletes all memories in the specified memory store that are associated with the provided scope. :param name: The name of the memory store. Required. :type name: str @@ -8597,7 +8852,9 @@ async def delete_scope( async def delete_scope( self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryStoreDeleteScopeResult: - """Delete all memories associated with a specific scope from a memory store. + """Delete memories by scope. + + Deletes all memories in the specified memory store that are associated with the provided scope. :param name: The name of the memory store. Required. :type name: str @@ -8616,7 +8873,9 @@ async def delete_scope( async def delete_scope( self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryStoreDeleteScopeResult: - """Delete all memories associated with a specific scope from a memory store. + """Delete memories by scope. + + Deletes all memories in the specified memory store that are associated with the provided scope. :param name: The name of the memory store. Required. :type name: str @@ -8635,7 +8894,9 @@ async def delete_scope( async def delete_scope( self, name: str, body: Union[JSON, IO[bytes]] = _Unset, *, scope: str = _Unset, **kwargs: Any ) -> _models.MemoryStoreDeleteScopeResult: - """Delete all memories associated with a specific scope from a memory store. + """Delete memories by scope. + + Deletes all memories in the specified memory store that are associated with the provided scope. :param name: The name of the memory store. Required. :type name: str @@ -8730,7 +8991,9 @@ async def create_memory( content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Create a memory item in a memory store. + """Create a memory item. + + Creates a memory item in the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -8754,7 +9017,9 @@ async def create_memory( async def create_memory( self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Create a memory item in a memory store. + """Create a memory item. + + Creates a memory item in the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -8772,7 +9037,9 @@ async def create_memory( async def create_memory( self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Create a memory item in a memory store. + """Create a memory item. + + Creates a memory item in the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -8797,7 +9064,9 @@ async def create_memory( kind: Union[str, _models.MemoryItemKind] = _Unset, **kwargs: Any ) -> _models.MemoryItem: - """Create a memory item in a memory store. + """Create a memory item. + + Creates a memory item in the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -8893,7 +9162,9 @@ async def create_memory( async def update_memory( self, name: str, memory_id: str, *, content: str, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Update a memory item in a memory store. + """Update a memory item. + + Updates the specified memory item in the memory store. :param name: The name of the memory store. Required. :type name: str @@ -8913,7 +9184,9 @@ async def update_memory( async def update_memory( self, name: str, memory_id: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Update a memory item in a memory store. + """Update a memory item. + + Updates the specified memory item in the memory store. :param name: The name of the memory store. Required. :type name: str @@ -8933,7 +9206,9 @@ async def update_memory( async def update_memory( self, name: str, memory_id: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Update a memory item in a memory store. + """Update a memory item. + + Updates the specified memory item in the memory store. :param name: The name of the memory store. Required. :type name: str @@ -8953,7 +9228,9 @@ async def update_memory( async def update_memory( self, name: str, memory_id: str, body: Union[JSON, IO[bytes]] = _Unset, *, content: str = _Unset, **kwargs: Any ) -> _models.MemoryItem: - """Update a memory item in a memory store. + """Update a memory item. + + Updates the specified memory item in the memory store. :param name: The name of the memory store. Required. :type name: str @@ -9040,7 +9317,9 @@ async def update_memory( @distributed_trace_async async def get_memory(self, name: str, memory_id: str, **kwargs: Any) -> _models.MemoryItem: - """Retrieve a memory item from a memory store. + """Get a memory item. + + Retrieves the specified memory item from the memory store. :param name: The name of the memory store. Required. :type name: str @@ -9119,7 +9398,9 @@ def list_memories( content_type: str = "application/json", **kwargs: Any ) -> AsyncItemPaged["_models.MemoryItem"]: - """List all memory items in a memory store. + """List memory items. + + Returns memory items from the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -9164,7 +9445,9 @@ def list_memories( content_type: str = "application/json", **kwargs: Any ) -> AsyncItemPaged["_models.MemoryItem"]: - """List all memory items in a memory store. + """List memory items. + + Returns memory items from the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -9208,7 +9491,9 @@ def list_memories( content_type: str = "application/json", **kwargs: Any ) -> AsyncItemPaged["_models.MemoryItem"]: - """List all memory items in a memory store. + """List memory items. + + Returns memory items from the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -9252,7 +9537,9 @@ def list_memories( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.MemoryItem"]: - """List all memory items in a memory store. + """List memory items. + + Returns memory items from the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -9369,7 +9656,9 @@ async def get_next(_continuation_token=None): @distributed_trace_async async def delete_memory(self, name: str, memory_id: str, **kwargs: Any) -> _models.DeleteMemoryResult: - """Delete a memory item from a memory store. + """Delete a memory item. + + Deletes the specified memory item from the memory store. :param name: The name of the memory store. Required. :type name: str @@ -9455,7 +9744,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def list_versions(self, name: str, **kwargs: Any) -> AsyncItemPaged["_models.ModelVersion"]: - """List all versions of the given ModelVersion. + """List versions. + + List all versions of the given ModelVersion. :param name: The name of the resource. Required. :type name: str @@ -9546,7 +9837,9 @@ async def get_next(next_link=None): @distributed_trace def list(self, **kwargs: Any) -> AsyncItemPaged["_models.ModelVersion"]: - """List the latest version of each ModelVersion. + """List latest versions. + + List the latest version of each ModelVersion. :return: An iterator like instance of ModelVersion :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.ModelVersion] @@ -9634,8 +9927,9 @@ async def get_next(next_link=None): @distributed_trace_async async def get(self, name: str, version: str, **kwargs: Any) -> _models.ModelVersion: - """Get the specific version of the ModelVersion. The service returns 404 Not Found error if the - ModelVersion does not exist. + """Get a model version. + + Retrieves the specified model version, returning 404 if it does not exist. :param name: The name of the resource. Required. :type name: str @@ -9699,7 +9993,9 @@ async def get(self, name: str, version: str, **kwargs: Any) -> _models.ModelVers @distributed_trace_async async def delete(self, name: str, version: str, **kwargs: Any) -> None: - """Delete the specific version of the ModelVersion. The service returns 200 OK if the ModelVersion + """Delete a model version. + + Delete the specific version of the ModelVersion. The service returns 200 OK if the ModelVersion was deleted successfully or if the ModelVersion does not exist. :param name: The name of the resource. Required. @@ -9759,7 +10055,9 @@ async def update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.ModelVersion: - """Update an existing ModelVersion with the given version id. + """Update a model version. + + Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -9786,7 +10084,9 @@ async def update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.ModelVersion: - """Update an existing ModelVersion with the given version id. + """Update a model version. + + Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -9813,7 +10113,9 @@ async def update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.ModelVersion: - """Update an existing ModelVersion with the given version id. + """Update a model version. + + Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -9838,7 +10140,9 @@ async def update( model_version_update: Union[_models.UpdateModelVersionRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.ModelVersion: - """Update an existing ModelVersion with the given version id. + """Update a model version. + + Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -9925,8 +10229,10 @@ async def pending_create_version( content_type: str = "application/json", **kwargs: Any ) -> _models.CreateAsyncResponse: - """Creates a model version asynchronously with blob content validation. Returns 202 Accepted with - a Location header for polling. + """Create a model version async. + + Creates a model version asynchronously with blob content validation. Returns 202 Accepted with + a location header for polling the operation status. :param name: Name of the model. Required. :type name: str @@ -9946,8 +10252,10 @@ async def pending_create_version( async def pending_create_version( self, name: str, version: str, model_version: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.CreateAsyncResponse: - """Creates a model version asynchronously with blob content validation. Returns 202 Accepted with - a Location header for polling. + """Create a model version async. + + Creates a model version asynchronously with blob content validation. Returns 202 Accepted with + a location header for polling the operation status. :param name: Name of the model. Required. :type name: str @@ -9973,8 +10281,10 @@ async def pending_create_version( content_type: str = "application/json", **kwargs: Any ) -> _models.CreateAsyncResponse: - """Creates a model version asynchronously with blob content validation. Returns 202 Accepted with - a Location header for polling. + """Create a model version async. + + Creates a model version asynchronously with blob content validation. Returns 202 Accepted with + a location header for polling the operation status. :param name: Name of the model. Required. :type name: str @@ -9994,8 +10304,10 @@ async def pending_create_version( async def pending_create_version( self, name: str, version: str, model_version: Union[_models.ModelVersion, JSON, IO[bytes]], **kwargs: Any ) -> _models.CreateAsyncResponse: - """Creates a model version asynchronously with blob content validation. Returns 202 Accepted with - a Location header for polling. + """Create a model version async. + + Creates a model version asynchronously with blob content validation. Returns 202 Accepted with + a location header for polling the operation status. :param name: Name of the model. Required. :type name: str @@ -10083,7 +10395,9 @@ async def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.ModelPendingUploadResponse: - """Start or retrieve a pending upload for a model version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified model version. :param name: Name of the model. Required. :type name: str @@ -10110,7 +10424,9 @@ async def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.ModelPendingUploadResponse: - """Start or retrieve a pending upload for a model version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified model version. :param name: Name of the model. Required. :type name: str @@ -10137,7 +10453,9 @@ async def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.ModelPendingUploadResponse: - """Start or retrieve a pending upload for a model version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified model version. :param name: Name of the model. Required. :type name: str @@ -10162,7 +10480,9 @@ async def pending_upload( pending_upload_request: Union[_models.ModelPendingUploadRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.ModelPendingUploadResponse: - """Start or retrieve a pending upload for a model version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified model version. :param name: Name of the model. Required. :type name: str @@ -10249,7 +10569,9 @@ async def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get credentials for a model version asset. + """Get model asset credentials. + + Retrieves temporary credentials for accessing the storage backing the specified model version. :param name: Name of the model. Required. :type name: str @@ -10275,7 +10597,9 @@ async def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get credentials for a model version asset. + """Get model asset credentials. + + Retrieves temporary credentials for accessing the storage backing the specified model version. :param name: Name of the model. Required. :type name: str @@ -10301,7 +10625,9 @@ async def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get credentials for a model version asset. + """Get model asset credentials. + + Retrieves temporary credentials for accessing the storage backing the specified model version. :param name: Name of the model. Required. :type name: str @@ -10325,7 +10651,9 @@ async def get_credentials( credential_request: Union[_models.ModelCredentialRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.DatasetCredential: - """Get credentials for a model version asset. + """Get model asset credentials. + + Retrieves temporary credentials for accessing the storage backing the specified model version. :param name: Name of the model. Required. :type name: str @@ -10420,7 +10748,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace_async async def get(self, name: str, **kwargs: Any) -> _models.RedTeam: - """Get a redteam by name. + """Get a redteam. + + Retrieves the specified redteam and its configuration. :param name: Identifier of the red team run. Required. :type name: str @@ -10481,7 +10811,9 @@ async def get(self, name: str, **kwargs: Any) -> _models.RedTeam: @distributed_trace def list(self, **kwargs: Any) -> AsyncItemPaged["_models.RedTeam"]: - """List a redteam by name. + """List redteams. + + Returns the redteams available in the current project. :return: An iterator like instance of RedTeam :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.projects.models.RedTeam] @@ -10571,7 +10903,9 @@ async def get_next(next_link=None): async def create( self, red_team: _models.RedTeam, *, content_type: str = "application/json", **kwargs: Any ) -> _models.RedTeam: - """Creates a redteam run. + """Create a redteam run. + + Submits a new redteam run for execution with the provided configuration. :param red_team: Redteam to be run. Required. :type red_team: ~azure.ai.projects.models.RedTeam @@ -10585,7 +10919,9 @@ async def create( @overload async def create(self, red_team: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.RedTeam: - """Creates a redteam run. + """Create a redteam run. + + Submits a new redteam run for execution with the provided configuration. :param red_team: Redteam to be run. Required. :type red_team: JSON @@ -10601,7 +10937,9 @@ async def create(self, red_team: JSON, *, content_type: str = "application/json" async def create( self, red_team: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.RedTeam: - """Creates a redteam run. + """Create a redteam run. + + Submits a new redteam run for execution with the provided configuration. :param red_team: Redteam to be run. Required. :type red_team: IO[bytes] @@ -10615,7 +10953,9 @@ async def create( @distributed_trace_async async def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwargs: Any) -> _models.RedTeam: - """Creates a redteam run. + """Create a redteam run. + + Submits a new redteam run for execution with the provided configuration. :param red_team: Redteam to be run. Is one of the following types: RedTeam, JSON, IO[bytes] Required. @@ -10720,6 +11060,8 @@ async def create_or_update( ) -> _models.Routine: """Create or update a routine. + Creates a new routine or replaces an existing routine with the supplied definition. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. @@ -10745,6 +11087,8 @@ async def create_or_update( ) -> _models.Routine: """Create or update a routine. + Creates a new routine or replaces an existing routine with the supplied definition. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Required. @@ -10763,6 +11107,8 @@ async def create_or_update( ) -> _models.Routine: """Create or update a routine. + Creates a new routine or replaces an existing routine with the supplied definition. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Required. @@ -10789,6 +11135,8 @@ async def create_or_update( ) -> _models.Routine: """Create or update a routine. + Creates a new routine or replaces an existing routine with the supplied definition. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Is either a JSON type or a IO[bytes] type. Required. @@ -10876,7 +11224,9 @@ async def create_or_update( @distributed_trace_async async def get(self, routine_name: str, **kwargs: Any) -> _models.Routine: - """Retrieve a routine. + """Get a routine. + + Retrieves the specified routine and its current configuration. :param routine_name: The unique name of the routine. Required. :type routine_name: str @@ -10943,6 +11293,8 @@ async def get(self, routine_name: str, **kwargs: Any) -> _models.Routine: async def enable(self, routine_name: str, **kwargs: Any) -> _models.Routine: """Enable a routine. + Enables the specified routine so it can be dispatched. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :return: Routine. The Routine is compatible with MutableMapping @@ -11008,6 +11360,8 @@ async def enable(self, routine_name: str, **kwargs: Any) -> _models.Routine: async def disable(self, routine_name: str, **kwargs: Any) -> _models.Routine: """Disable a routine. + Disables the specified routine so it no longer runs. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :return: Routine. The Routine is compatible with MutableMapping @@ -11075,6 +11429,8 @@ def list( ) -> AsyncItemPaged["_models.Routine"]: """List routines. + Returns the routines available in the current project. + :keyword limit: The maximum number of routines to return. Default value is None. :paramtype limit: int :keyword before: Unsupported. Reserved for future backward pagination support. Default value is @@ -11152,6 +11508,8 @@ async def get_next(_continuation_token=None): async def delete(self, routine_name: str, **kwargs: Any) -> None: """Delete a routine. + Deletes the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :return: None @@ -11213,6 +11571,8 @@ def list_runs( ) -> AsyncItemPaged["_models.RoutineRun"]: """List prior runs for a routine. + Returns prior runs recorded for the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :keyword filter: An optional MLflow search-runs filter expression applied within the routine's @@ -11304,6 +11664,8 @@ async def dispatch( ) -> _models.DispatchRoutineResult: """Queue an asynchronous routine dispatch. + Queues an asynchronous dispatch for the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. @@ -11323,6 +11685,8 @@ async def dispatch( ) -> _models.DispatchRoutineResult: """Queue an asynchronous routine dispatch. + Queues an asynchronous dispatch for the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Required. @@ -11341,6 +11705,8 @@ async def dispatch( ) -> _models.DispatchRoutineResult: """Queue an asynchronous routine dispatch. + Queues an asynchronous dispatch for the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Required. @@ -11364,6 +11730,8 @@ async def dispatch( ) -> _models.DispatchRoutineResult: """Queue an asynchronous routine dispatch. + Queues an asynchronous dispatch for the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Is either a JSON type or a IO[bytes] type. Required. @@ -11465,6 +11833,8 @@ def __init__(self, *args, **kwargs) -> None: async def delete(self, schedule_id: str, **kwargs: Any) -> None: """Delete a schedule. + Deletes the specified schedule resource. + :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str :return: None @@ -11511,7 +11881,9 @@ async def delete(self, schedule_id: str, **kwargs: Any) -> None: @distributed_trace_async async def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: - """Get a schedule by id. + """Get a schedule. + + Retrieves the specified schedule resource. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -11578,7 +11950,9 @@ def list( enabled: Optional[bool] = None, **kwargs: Any ) -> AsyncItemPaged["_models.Schedule"]: - """List all schedules. + """List schedules. + + Returns schedules that match the supplied type and enabled filters. :keyword type: Filter by the type of schedule. Known values are: "Evaluation" and "Insight". Default value is None. @@ -11675,7 +12049,9 @@ async def get_next(next_link=None): async def create_or_update( self, schedule_id: str, schedule: _models.Schedule, *, content_type: str = "application/json", **kwargs: Any ) -> _models.Schedule: - """Create or update operation template. + """Create or update a schedule. + + Creates a new schedule or updates an existing schedule with the supplied definition. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -11693,7 +12069,9 @@ async def create_or_update( async def create_or_update( self, schedule_id: str, schedule: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.Schedule: - """Create or update operation template. + """Create or update a schedule. + + Creates a new schedule or updates an existing schedule with the supplied definition. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -11711,7 +12089,9 @@ async def create_or_update( async def create_or_update( self, schedule_id: str, schedule: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.Schedule: - """Create or update operation template. + """Create or update a schedule. + + Creates a new schedule or updates an existing schedule with the supplied definition. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -11729,7 +12109,9 @@ async def create_or_update( async def create_or_update( self, schedule_id: str, schedule: Union[_models.Schedule, JSON, IO[bytes]], **kwargs: Any ) -> _models.Schedule: - """Create or update operation template. + """Create or update a schedule. + + Creates a new schedule or updates an existing schedule with the supplied definition. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -11803,7 +12185,9 @@ async def create_or_update( @distributed_trace_async async def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models.ScheduleRun: - """Get a schedule run by id. + """Get a schedule run. + + Retrieves the specified run for a schedule. :param schedule_id: The unique identifier of the schedule. Required. :type schedule_id: str @@ -11878,7 +12262,9 @@ def list_runs( enabled: Optional[bool] = None, **kwargs: Any ) -> AsyncItemPaged["_models.ScheduleRun"]: - """List all schedule runs. + """List schedule runs. + + Returns schedule runs that match the supplied filters. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -12005,7 +12391,9 @@ async def create_version( policies: Optional[_models.ToolboxPolicies] = None, **kwargs: Any ) -> _models.ToolboxVersionObject: - """Create a new version of a toolbox. If the toolbox does not exist, it will be created. + """Create a new version of a toolbox. + + Creates a new toolbox version, provisioning the toolbox itself if it does not already exist. :param name: The name of the toolbox. If the toolbox does not exist, it will be created. Required. @@ -12035,7 +12423,9 @@ async def create_version( async def create_version( self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.ToolboxVersionObject: - """Create a new version of a toolbox. If the toolbox does not exist, it will be created. + """Create a new version of a toolbox. + + Creates a new toolbox version, provisioning the toolbox itself if it does not already exist. :param name: The name of the toolbox. If the toolbox does not exist, it will be created. Required. @@ -12054,7 +12444,9 @@ async def create_version( async def create_version( self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.ToolboxVersionObject: - """Create a new version of a toolbox. If the toolbox does not exist, it will be created. + """Create a new version of a toolbox. + + Creates a new toolbox version, provisioning the toolbox itself if it does not already exist. :param name: The name of the toolbox. If the toolbox does not exist, it will be created. Required. @@ -12082,7 +12474,9 @@ async def create_version( policies: Optional[_models.ToolboxPolicies] = None, **kwargs: Any ) -> _models.ToolboxVersionObject: - """Create a new version of a toolbox. If the toolbox does not exist, it will be created. + """Create a new version of a toolbox. + + Creates a new toolbox version, provisioning the toolbox itself if it does not already exist. :param name: The name of the toolbox. If the toolbox does not exist, it will be created. Required. @@ -12186,6 +12580,8 @@ async def create_version( async def get(self, name: str, **kwargs: Any) -> _models.ToolboxObject: """Retrieve a toolbox. + Retrieves the specified toolbox and its current configuration. + :param name: The name of the toolbox to retrieve. Required. :type name: str :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping @@ -12256,7 +12652,9 @@ def list( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.ToolboxObject"]: - """List all toolboxes. + """List toolboxes. + + Returns the toolboxes available in the current project. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -12347,7 +12745,9 @@ def list_versions( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.ToolboxVersionObject"]: - """List all versions of a toolbox. + """List toolbox versions. + + Returns the available versions for the specified toolbox. :param name: The name of the toolbox to list versions for. Required. :type name: str @@ -12435,6 +12835,8 @@ async def get_next(_continuation_token=None): async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.ToolboxVersionObject: """Retrieve a specific version of a toolbox. + Retrieves the specified version of a toolbox by name and version identifier. + :param name: The name of the toolbox. Required. :type name: str :param version: The version identifier to retrieve. Required. @@ -12505,6 +12907,8 @@ async def update( ) -> _models.ToolboxObject: """Update a toolbox to point to a specific version. + Updates the toolbox's default version pointer to the specified version. + :param name: The name of the toolbox to update. Required. :type name: str :keyword default_version: The version identifier that the toolbox should point to. When set, @@ -12524,6 +12928,8 @@ async def update( ) -> _models.ToolboxObject: """Update a toolbox to point to a specific version. + Updates the toolbox's default version pointer to the specified version. + :param name: The name of the toolbox to update. Required. :type name: str :param body: Required. @@ -12542,6 +12948,8 @@ async def update( ) -> _models.ToolboxObject: """Update a toolbox to point to a specific version. + Updates the toolbox's default version pointer to the specified version. + :param name: The name of the toolbox to update. Required. :type name: str :param body: Required. @@ -12560,6 +12968,8 @@ async def update( ) -> _models.ToolboxObject: """Update a toolbox to point to a specific version. + Updates the toolbox's default version pointer to the specified version. + :param name: The name of the toolbox to update. Required. :type name: str :param body: Is either a JSON type or a IO[bytes] type. Required. @@ -12643,7 +13053,9 @@ async def update( @distributed_trace_async async def delete(self, name: str, **kwargs: Any) -> None: - """Delete a toolbox and all its versions. + """Delete a toolbox. + + Removes the specified toolbox along with all of its versions. :param name: The name of the toolbox to delete. Required. :type name: str @@ -12697,6 +13109,8 @@ async def delete(self, name: str, **kwargs: Any) -> None: async def delete_version(self, name: str, version: str, **kwargs: Any) -> None: """Delete a specific version of a toolbox. + Removes the specified version of a toolbox. + :param name: The name of the toolbox. Required. :type name: str :param version: The version identifier to delete. Required. @@ -12768,7 +13182,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace_async async def get(self, name: str, **kwargs: Any) -> _models.SkillDetails: - """Retrieves a skill. + """Retrieve a skill. + + Retrieves the specified skill and its current configuration. :param name: The unique name of the skill. Required. :type name: str @@ -12840,7 +13256,9 @@ def list( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.SkillDetails"]: - """Returns the list of all skills. + """List skills. + + Returns the skills available in the current project. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -12927,6 +13345,8 @@ async def update( ) -> _models.SkillDetails: """Update a skill. + Modifies the specified skill's configuration. + :param name: The name of the skill to update. Required. :type name: str :keyword default_version: The version identifier that the skill should point to. When set, the @@ -12946,6 +13366,8 @@ async def update( ) -> _models.SkillDetails: """Update a skill. + Modifies the specified skill's configuration. + :param name: The name of the skill to update. Required. :type name: str :param body: Required. @@ -12964,6 +13386,8 @@ async def update( ) -> _models.SkillDetails: """Update a skill. + Modifies the specified skill's configuration. + :param name: The name of the skill to update. Required. :type name: str :param body: Required. @@ -12982,6 +13406,8 @@ async def update( ) -> _models.SkillDetails: """Update a skill. + Modifies the specified skill's configuration. + :param name: The name of the skill to update. Required. :type name: str :param body: Is either a JSON type or a IO[bytes] type. Required. @@ -13065,7 +13491,9 @@ async def update( @distributed_trace_async async def delete(self, name: str, **kwargs: Any) -> _models.DeleteSkillResult: - """Deletes a skill. + """Delete a skill. + + Removes the specified skill and its associated versions. :param name: The unique name of the skill. Required. :type name: str @@ -13138,7 +13566,9 @@ async def create( default: Optional[bool] = None, **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill. If the skill does not exist, it will be created. + """Create a new version of a skill. + + Creates a new version of a skill. If the skill does not exist, it will be created. :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str @@ -13159,7 +13589,9 @@ async def create( async def create( self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill. If the skill does not exist, it will be created. + """Create a new version of a skill. + + Creates a new version of a skill. If the skill does not exist, it will be created. :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str @@ -13177,7 +13609,9 @@ async def create( async def create( self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill. If the skill does not exist, it will be created. + """Create a new version of a skill. + + Creates a new version of a skill. If the skill does not exist, it will be created. :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str @@ -13201,7 +13635,9 @@ async def create( default: Optional[bool] = None, **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill. If the skill does not exist, it will be created. + """Create a new version of a skill. + + Creates a new version of a skill. If the skill does not exist, it will be created. :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str @@ -13288,7 +13724,9 @@ async def create( async def create_from_files( self, name: str, content: _models.CreateSkillVersionFromFilesBody, **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill from uploaded files via multipart form data. + """Create a skill version from uploaded files. + + Creates a new version of a skill from uploaded files via multipart form data. :param name: The name of the skill. Required. :type name: str @@ -13301,7 +13739,9 @@ async def create_from_files( @overload async def create_from_files(self, name: str, content: JSON, **kwargs: Any) -> _models.SkillVersion: - """Creates a new version of a skill from uploaded files via multipart form data. + """Create a skill version from uploaded files. + + Creates a new version of a skill from uploaded files via multipart form data. :param name: The name of the skill. Required. :type name: str @@ -13316,7 +13756,9 @@ async def create_from_files(self, name: str, content: JSON, **kwargs: Any) -> _m async def create_from_files( self, name: str, content: Union[_models.CreateSkillVersionFromFilesBody, JSON], **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill from uploaded files via multipart form data. + """Create a skill version from uploaded files. + + Creates a new version of a skill from uploaded files via multipart form data. :param name: The name of the skill. Required. :type name: str @@ -13397,7 +13839,9 @@ def list_versions( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.SkillVersion"]: - """List all versions of a skill. + """List skill versions. + + Returns the available versions for the specified skill. :param name: The name of the skill to list versions for. Required. :type name: str @@ -13485,6 +13929,8 @@ async def get_next(_continuation_token=None): async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.SkillVersion: """Retrieve a specific version of a skill. + Retrieves the specified version of a skill by name and version identifier. + :param name: The name of the skill. Required. :type name: str :param version: The version identifier to retrieve. Required. @@ -13553,6 +13999,8 @@ async def get_version(self, name: str, version: str, **kwargs: Any) -> _models.S async def download(self, name: str, **kwargs: Any) -> AsyncIterator[bytes]: """Download the zip content for the default version of a skill. + Downloads the zip content for the default version of a skill. + :param name: The name of the skill. Required. :type name: str :return: AsyncIterator[bytes] @@ -13618,6 +14066,8 @@ async def download(self, name: str, **kwargs: Any) -> AsyncIterator[bytes]: async def download_version(self, name: str, version: str, **kwargs: Any) -> AsyncIterator[bytes]: """Download the zip content for a specific version of a skill. + Downloads the zip content for a specific version of a skill. + :param name: The name of the skill. Required. :type name: str :param version: The version to download content for. Required. @@ -13686,6 +14136,8 @@ async def download_version(self, name: str, version: str, **kwargs: Any) -> Asyn async def delete_version(self, name: str, version: str, **kwargs: Any) -> _models.DeleteSkillVersionResult: """Delete a specific version of a skill. + Removes the specified version of a skill. + :param name: The name of the skill. Required. :type name: str :param version: The version identifier to delete. Required. @@ -13771,9 +14223,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace_async async def get_generation_job(self, job_id: str, **kwargs: Any) -> _models.DataGenerationJob: - """Get info about a data generation job. + """Get a data generation job. - Gets the details of a data generation job by its ID. + Retrieves the specified data generation job and its current status. :param job_id: The ID of the job. Required. :type job_id: str @@ -13848,7 +14300,7 @@ def list_generation_jobs( before: Optional[str] = None, **kwargs: Any ) -> AsyncItemPaged["_models.DataGenerationJob"]: - """Returns a list of data generation jobs. + """List data generation jobs. Returns a list of data generation jobs. @@ -13940,9 +14392,9 @@ async def create_generation_job( content_type: str = "application/json", **kwargs: Any ) -> _models.DataGenerationJob: - """Creates a data generation job. + """Create a data generation job. - Creates a data generation job. + Submits a new data generation job for asynchronous execution. :param job: The job to create. Required. :type job: ~azure.ai.projects.models.DataGenerationJob @@ -13961,9 +14413,9 @@ async def create_generation_job( async def create_generation_job( self, job: JSON, *, operation_id: Optional[str] = None, content_type: str = "application/json", **kwargs: Any ) -> _models.DataGenerationJob: - """Creates a data generation job. + """Create a data generation job. - Creates a data generation job. + Submits a new data generation job for asynchronous execution. :param job: The job to create. Required. :type job: JSON @@ -13987,9 +14439,9 @@ async def create_generation_job( content_type: str = "application/json", **kwargs: Any ) -> _models.DataGenerationJob: - """Creates a data generation job. + """Create a data generation job. - Creates a data generation job. + Submits a new data generation job for asynchronous execution. :param job: The job to create. Required. :type job: IO[bytes] @@ -14012,9 +14464,9 @@ async def create_generation_job( operation_id: Optional[str] = None, **kwargs: Any ) -> _models.DataGenerationJob: - """Creates a data generation job. + """Create a data generation job. - Creates a data generation job. + Submits a new data generation job for asynchronous execution. :param job: The job to create. Is one of the following types: DataGenerationJob, JSON, IO[bytes] Required. @@ -14097,9 +14549,9 @@ async def create_generation_job( @distributed_trace_async async def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.DataGenerationJob: - """Cancels a data generation job. + """Cancel a data generation job. - Cancels a data generation job by its ID. + Cancels the specified data generation job if it is still in progress. :param job_id: The ID of the job to cancel. Required. :type job_id: str @@ -14164,9 +14616,9 @@ async def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.Dat @distributed_trace_async async def delete_generation_job(self, job_id: str, **kwargs: Any) -> None: - """Deletes a data generation job. + """Delete a data generation job. - Deletes a data generation job by its ID. + Removes the specified data generation job and its associated output. :param job_id: The ID of the job to delete. Required. :type job_id: str diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py index 29197fc4bbad..692d897c2926 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py @@ -27,14 +27,12 @@ AgentEndpointAuthorizationScheme, AgentEndpointConfig, AgentEvaluatorGenerationJobSource, - AgentIdentifier, AgentIdentity, AgentObjectVersions, AgentSessionResource, AgentTaxonomyInput, AgentVersionDetails, AgenticIdentityPreviewCredentials, - AgentsPagedResultOptimizationCandidate, ApiError, ApiErrorResponse, ApiKeyCredentials, @@ -67,10 +65,6 @@ BrowserAutomationPreviewTool, BrowserAutomationToolConnectionParameters, BrowserAutomationToolParameters, - CandidateDeployConfig, - CandidateFileInfo, - CandidateMetadata, - CandidateResults, CaptureStructuredOutputsTool, ChartCoordinate, ChatSummaryMemoryItem, @@ -81,6 +75,7 @@ CodeInterpreterTool, ComparisonFilter, CompoundFilter, + ComputerTool, ComputerUsePreviewTool, Connection, ContainerAutoParam, @@ -116,8 +111,6 @@ DatasetCredential, DatasetDataGenerationJobOutput, DatasetEvaluatorGenerationJobSource, - DatasetInfo, - DatasetRef, DatasetReference, DatasetVersion, DeleteAgentResponse, @@ -130,6 +123,7 @@ Dimension, DispatchRoutineResult, EmbeddingConfiguration, + EmptyModelParam, EntraAuthorizationScheme, EntraIDCredentials, EntraIsolationKeySource, @@ -174,6 +168,7 @@ FunctionShellToolParamEnvironmentContainerReferenceParam, FunctionShellToolParamEnvironmentLocalEnvironmentParam, FunctionTool, + FunctionToolParam, GitHubIssueRoutineTrigger, HeaderIsolationKeySource, HeaderTelemetryEndpointAuth, @@ -232,6 +227,7 @@ ModelSourceData, ModelVersion, MonthlyRecurrenceSchedule, + NamespaceToolParam, NoAuthenticationCredentials, OneTimeTrigger, OpenApiAnonymousAuthDetails, @@ -243,20 +239,24 @@ OpenApiProjectConnectionAuthDetails, OpenApiProjectConnectionSecurityScheme, OpenApiTool, - OptimizationAgentDefinition, + OptimizationAgentIdentifier, OptimizationCandidate, + OptimizationDatasetCriterion, + OptimizationDatasetInput, + OptimizationDatasetItem, + OptimizationEvaluatorRef, + OptimizationInlineDatasetInput, OptimizationJob, OptimizationJobInputs, + OptimizationJobListItem, OptimizationJobProgress, OptimizationJobResult, OptimizationOptions, - OptimizationTaskResult, + OptimizationReferenceDatasetInput, OtlpTelemetryEndpoint, PendingUploadRequest, PendingUploadResponse, ProceduralMemoryItem, - PromoteCandidateRequest, - PromoteCandidateResponse, PromotionInfo, PromptAgentDefinition, PromptAgentDefinitionTextOptions, @@ -312,6 +312,8 @@ Tool, ToolChoiceAllowed, ToolChoiceCodeInterpreter, + ToolChoiceComputer, + ToolChoiceComputerUse, ToolChoiceComputerUsePreview, ToolChoiceCustom, ToolChoiceFileSearch, @@ -324,6 +326,7 @@ ToolConfig, ToolDescription, ToolProjectConnection, + ToolSearchToolParam, ToolUseFineTuningDataGenerationJobOptions, ToolboxObject, ToolboxPolicies, @@ -409,6 +412,7 @@ MemoryStoreUpdateStatus, OpenApiAuthType, OperationState, + OptimizationDatasetInputType, PageOrder, PendingUploadType, RankerVersionType, @@ -422,6 +426,7 @@ SampleType, ScheduleProvisioningStatus, ScheduleTaskType, + SearchContentType, SearchContextSize, SessionLogEventType, SimpleQnAFineTuningQuestionType, @@ -431,6 +436,7 @@ TelemetryTransportProtocol, TextResponseFormatConfigurationType, ToolChoiceParamType, + ToolSearchExecutionType, ToolType, TreatmentEffectType, TriggerType, @@ -455,14 +461,12 @@ "AgentEndpointAuthorizationScheme", "AgentEndpointConfig", "AgentEvaluatorGenerationJobSource", - "AgentIdentifier", "AgentIdentity", "AgentObjectVersions", "AgentSessionResource", "AgentTaxonomyInput", "AgentVersionDetails", "AgenticIdentityPreviewCredentials", - "AgentsPagedResultOptimizationCandidate", "ApiError", "ApiErrorResponse", "ApiKeyCredentials", @@ -495,10 +499,6 @@ "BrowserAutomationPreviewTool", "BrowserAutomationToolConnectionParameters", "BrowserAutomationToolParameters", - "CandidateDeployConfig", - "CandidateFileInfo", - "CandidateMetadata", - "CandidateResults", "CaptureStructuredOutputsTool", "ChartCoordinate", "ChatSummaryMemoryItem", @@ -509,6 +509,7 @@ "CodeInterpreterTool", "ComparisonFilter", "CompoundFilter", + "ComputerTool", "ComputerUsePreviewTool", "Connection", "ContainerAutoParam", @@ -544,8 +545,6 @@ "DatasetCredential", "DatasetDataGenerationJobOutput", "DatasetEvaluatorGenerationJobSource", - "DatasetInfo", - "DatasetRef", "DatasetReference", "DatasetVersion", "DeleteAgentResponse", @@ -558,6 +557,7 @@ "Dimension", "DispatchRoutineResult", "EmbeddingConfiguration", + "EmptyModelParam", "EntraAuthorizationScheme", "EntraIDCredentials", "EntraIsolationKeySource", @@ -602,6 +602,7 @@ "FunctionShellToolParamEnvironmentContainerReferenceParam", "FunctionShellToolParamEnvironmentLocalEnvironmentParam", "FunctionTool", + "FunctionToolParam", "GitHubIssueRoutineTrigger", "HeaderIsolationKeySource", "HeaderTelemetryEndpointAuth", @@ -660,6 +661,7 @@ "ModelSourceData", "ModelVersion", "MonthlyRecurrenceSchedule", + "NamespaceToolParam", "NoAuthenticationCredentials", "OneTimeTrigger", "OpenApiAnonymousAuthDetails", @@ -671,20 +673,24 @@ "OpenApiProjectConnectionAuthDetails", "OpenApiProjectConnectionSecurityScheme", "OpenApiTool", - "OptimizationAgentDefinition", + "OptimizationAgentIdentifier", "OptimizationCandidate", + "OptimizationDatasetCriterion", + "OptimizationDatasetInput", + "OptimizationDatasetItem", + "OptimizationEvaluatorRef", + "OptimizationInlineDatasetInput", "OptimizationJob", "OptimizationJobInputs", + "OptimizationJobListItem", "OptimizationJobProgress", "OptimizationJobResult", "OptimizationOptions", - "OptimizationTaskResult", + "OptimizationReferenceDatasetInput", "OtlpTelemetryEndpoint", "PendingUploadRequest", "PendingUploadResponse", "ProceduralMemoryItem", - "PromoteCandidateRequest", - "PromoteCandidateResponse", "PromotionInfo", "PromptAgentDefinition", "PromptAgentDefinitionTextOptions", @@ -740,6 +746,8 @@ "Tool", "ToolChoiceAllowed", "ToolChoiceCodeInterpreter", + "ToolChoiceComputer", + "ToolChoiceComputerUse", "ToolChoiceComputerUsePreview", "ToolChoiceCustom", "ToolChoiceFileSearch", @@ -752,6 +760,7 @@ "ToolConfig", "ToolDescription", "ToolProjectConnection", + "ToolSearchToolParam", "ToolUseFineTuningDataGenerationJobOptions", "ToolboxObject", "ToolboxPolicies", @@ -834,6 +843,7 @@ "MemoryStoreUpdateStatus", "OpenApiAuthType", "OperationState", + "OptimizationDatasetInputType", "PageOrder", "PendingUploadType", "RankerVersionType", @@ -847,6 +857,7 @@ "SampleType", "ScheduleProvisioningStatus", "ScheduleTaskType", + "SearchContentType", "SearchContextSize", "SessionLogEventType", "SimpleQnAFineTuningQuestionType", @@ -856,6 +867,7 @@ "TelemetryTransportProtocol", "TextResponseFormatConfigurationType", "ToolChoiceParamType", + "ToolSearchExecutionType", "ToolType", "TreatmentEffectType", "TriggerType", diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py index fb8d84b06bce..53716def91a6 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py @@ -49,8 +49,8 @@ class _FoundryFeaturesOptInKeys(str, Enum, metaclass=CaseInsensitiveEnumMeta): """DATA_GENERATION_JOBS_V1_PREVIEW.""" MODELS_V1_PREVIEW = "Models=V1Preview" """MODELS_V1_PREVIEW.""" - AGENTS_OPTIMIZATION_V1_PREVIEW = "AgentsOptimization=V1Preview" - """AGENTS_OPTIMIZATION_V1_PREVIEW.""" + AGENTS_OPTIMIZATION_V2_PREVIEW = "AgentsOptimization=V2Preview" + """AGENTS_OPTIMIZATION_V2_PREVIEW.""" class AgentBlueprintReferenceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -123,6 +123,8 @@ class AgentProtocol(str, Enum, metaclass=CaseInsensitiveEnumMeta): """ACTIVITY_PROTOCOL.""" RESPONSES = "responses" """RESPONSES.""" + A2A = "a2a" + """A2A.""" MCP = "mcp" """MCP.""" INVOCATIONS = "invocations" @@ -792,6 +794,15 @@ class OperationState(str, Enum, metaclass=CaseInsensitiveEnumMeta): """The operation has been canceled by the user.""" +class OptimizationDatasetInputType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Discriminator values for the dataset input union.""" + + INLINE = "inline" + """Inline dataset — items are provided directly in the request body.""" + REFERENCE = "reference" + """Reference to a registered Foundry dataset by name and version.""" + + class PageOrder(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Type of PageOrder.""" @@ -950,6 +961,15 @@ class ScheduleTaskType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Insight task.""" +class SearchContentType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of SearchContentType.""" + + TEXT = "text" + """TEXT.""" + IMAGE = "image" + """IMAGE.""" + + class SearchContextSize(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Type of SearchContextSize.""" @@ -1051,6 +1071,19 @@ class ToolChoiceParamType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """IMAGE_GENERATION.""" CODE_INTERPRETER = "code_interpreter" """CODE_INTERPRETER.""" + COMPUTER = "computer" + """COMPUTER.""" + COMPUTER_USE = "computer_use" + """COMPUTER_USE.""" + + +class ToolSearchExecutionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of ToolSearchExecutionType.""" + + SERVER = "server" + """SERVER.""" + CLIENT = "client" + """CLIENT.""" class ToolType(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -1060,6 +1093,8 @@ class ToolType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """FUNCTION.""" FILE_SEARCH = "file_search" """FILE_SEARCH.""" + COMPUTER = "computer" + """COMPUTER.""" COMPUTER_USE_PREVIEW = "computer_use_preview" """COMPUTER_USE_PREVIEW.""" WEB_SEARCH = "web_search" @@ -1076,6 +1111,10 @@ class ToolType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """SHELL.""" CUSTOM = "custom" """CUSTOM.""" + NAMESPACE = "namespace" + """NAMESPACE.""" + TOOL_SEARCH = "tool_search" + """TOOL_SEARCH.""" WEB_SEARCH_PREVIEW = "web_search_preview" """WEB_SEARCH_PREVIEW.""" APPLY_PATCH = "apply_patch" diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py index 308e0e1be187..8c31b279e262 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py @@ -39,6 +39,7 @@ MemoryStoreKind, MemoryStoreObjectType, OpenApiAuthType, + OptimizationDatasetInputType, PendingUploadType, RecurrenceType, RoutineActionType, @@ -66,31 +67,32 @@ class Tool(_Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: A2APreviewTool, ApplyPatchToolParam, AzureAISearchTool, AzureFunctionTool, BingCustomSearchPreviewTool, BingGroundingTool, BrowserAutomationPreviewTool, - CaptureStructuredOutputsTool, CodeInterpreterTool, ComputerUsePreviewTool, CustomToolParam, - MicrosoftFabricPreviewTool, FabricIQPreviewTool, FileSearchTool, FunctionTool, ImageGenTool, - LocalShellToolParam, MCPTool, MemorySearchPreviewTool, OpenApiTool, SharepointPreviewTool, - FunctionShellToolParam, ToolboxSearchPreviewTool, WebSearchTool, WebSearchPreviewTool, - WorkIQPreviewTool - - :ivar type: Required. Known values are: "function", "file_search", "computer_use_preview", - "web_search", "mcp", "code_interpreter", "image_generation", "local_shell", "shell", "custom", - "web_search_preview", "apply_patch", "a2a_preview", "bing_custom_search_preview", - "browser_automation_preview", "fabric_dataagent_preview", "sharepoint_grounding_preview", - "memory_search_preview", "work_iq_preview", "fabric_iq_preview", "toolbox_search_preview", - "azure_ai_search", "azure_function", "bing_grounding", "capture_structured_outputs", and - "openapi". + CaptureStructuredOutputsTool, CodeInterpreterTool, ComputerTool, ComputerUsePreviewTool, + CustomToolParam, MicrosoftFabricPreviewTool, FabricIQPreviewTool, FileSearchTool, FunctionTool, + ImageGenTool, LocalShellToolParam, MCPTool, MemorySearchPreviewTool, NamespaceToolParam, + OpenApiTool, SharepointPreviewTool, FunctionShellToolParam, ToolSearchToolParam, + ToolboxSearchPreviewTool, WebSearchTool, WebSearchPreviewTool, WorkIQPreviewTool + + :ivar type: Required. Known values are: "function", "file_search", "computer", + "computer_use_preview", "web_search", "mcp", "code_interpreter", "image_generation", + "local_shell", "shell", "custom", "namespace", "tool_search", "web_search_preview", + "apply_patch", "a2a_preview", "bing_custom_search_preview", "browser_automation_preview", + "fabric_dataagent_preview", "sharepoint_grounding_preview", "memory_search_preview", + "work_iq_preview", "fabric_iq_preview", "toolbox_search_preview", "azure_ai_search", + "azure_function", "bing_grounding", "capture_structured_outputs", and "openapi". :vartype type: str or ~azure.ai.projects.models.ToolType """ __mapping__: dict[str, _Model] = {} type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) - """Required. Known values are: \"function\", \"file_search\", \"computer_use_preview\", - \"web_search\", \"mcp\", \"code_interpreter\", \"image_generation\", \"local_shell\", - \"shell\", \"custom\", \"web_search_preview\", \"apply_patch\", \"a2a_preview\", - \"bing_custom_search_preview\", \"browser_automation_preview\", \"fabric_dataagent_preview\", - \"sharepoint_grounding_preview\", \"memory_search_preview\", \"work_iq_preview\", - \"fabric_iq_preview\", \"toolbox_search_preview\", \"azure_ai_search\", \"azure_function\", - \"bing_grounding\", \"capture_structured_outputs\", and \"openapi\".""" + """Required. Known values are: \"function\", \"file_search\", \"computer\", + \"computer_use_preview\", \"web_search\", \"mcp\", \"code_interpreter\", \"image_generation\", + \"local_shell\", \"shell\", \"custom\", \"namespace\", \"tool_search\", \"web_search_preview\", + \"apply_patch\", \"a2a_preview\", \"bing_custom_search_preview\", + \"browser_automation_preview\", \"fabric_dataagent_preview\", \"sharepoint_grounding_preview\", + \"memory_search_preview\", \"work_iq_preview\", \"fabric_iq_preview\", + \"toolbox_search_preview\", \"azure_ai_search\", \"azure_function\", \"bing_grounding\", + \"capture_structured_outputs\", and \"openapi\".""" @overload def __init__( @@ -857,40 +859,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = CredentialType.AGENTIC_IDENTITY_PREVIEW # type: ignore -class AgentIdentifier(_Model): - """Identifies the registered Foundry agent to optimize (request-only). Skills, tools, and - system_prompt are specified in options.optimization_config. - - :ivar agent_name: Registered Foundry agent name (required). Required. - :vartype agent_name: str - :ivar agent_version: Pinned agent version. Defaults to latest if omitted. - :vartype agent_version: str - """ - - agent_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Registered Foundry agent name (required). Required.""" - agent_version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Pinned agent version. Defaults to latest if omitted.""" - - @overload - def __init__( - self, - *, - agent_name: str, - agent_version: Optional[str] = None, - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - class AgentIdentity(_Model): """AgentIdentity. @@ -1013,51 +981,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class AgentsPagedResultOptimizationCandidate(_Model): - """The response data for a requested list of items. - - :ivar data: The requested list of items. Required. - :vartype data: list[~azure.ai.projects.models.OptimizationCandidate] - :ivar first_id: The first ID represented in this list. - :vartype first_id: str - :ivar last_id: The last ID represented in this list. - :vartype last_id: str - :ivar has_more: A value indicating whether there are additional values available not captured - in this list. Required. - :vartype has_more: bool - """ - - data: list["_models.OptimizationCandidate"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The requested list of items. Required.""" - first_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The first ID represented in this list.""" - last_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The last ID represented in this list.""" - has_more: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """A value indicating whether there are additional values available not captured in this list. - Required.""" - - @overload - def __init__( - self, - *, - data: list["_models.OptimizationCandidate"], - has_more: bool, - first_id: Optional[str] = None, - last_id: Optional[str] = None, - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - class EvaluationTaxonomyInput(_Model): """Input configuration for the evaluation taxonomy. @@ -2692,208 +2615,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class CandidateDeployConfig(_Model): - """Deploy-config blob for a candidate. Suitable for setting OPTIMIZATION_CONFIG on a hosted-agent - version. - - :ivar instructions: System prompt / instructions. - :vartype instructions: str - :ivar model: Foundry deployment name. - :vartype model: str - :ivar temperature: Optional sampling temperature. - :vartype temperature: float - :ivar skills: Optional skill overrides. - :vartype skills: list[dict[str, any]] - :ivar tools: Optional tool overrides. - :vartype tools: list[dict[str, any]] - """ - - instructions: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """System prompt / instructions.""" - model: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Foundry deployment name.""" - temperature: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Optional sampling temperature.""" - skills: Optional[list[dict[str, Any]]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Optional skill overrides.""" - tools: Optional[list[dict[str, Any]]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Optional tool overrides.""" - - @overload - def __init__( - self, - *, - instructions: Optional[str] = None, - model: Optional[str] = None, - temperature: Optional[float] = None, - skills: Optional[list[dict[str, Any]]] = None, - tools: Optional[list[dict[str, Any]]] = None, - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - -class CandidateFileInfo(_Model): - """File entry in a candidate's blob directory. - - :ivar path: Relative path of the file. Required. - :vartype path: str - :ivar type: File type category (e.g. 'config', 'results'). Required. - :vartype type: str - :ivar size_bytes: File size in bytes. Required. - :vartype size_bytes: int - """ - - path: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Relative path of the file. Required.""" - type: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """File type category (e.g. 'config', 'results'). Required.""" - size_bytes: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """File size in bytes. Required.""" - - @overload - def __init__( - self, - *, - path: str, - type: str, - size_bytes: int, - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - -class CandidateMetadata(_Model): - """Candidate metadata returned by GET /candidates/{id}. - - :ivar candidate_id: Server-assigned candidate identifier. Required. - :vartype candidate_id: str - :ivar job_id: Owning optimization job id. Required. - :vartype job_id: str - :ivar candidate_name: Display name of the candidate. Required. - :vartype candidate_name: str - :ivar status: Candidate lifecycle status. Required. - :vartype status: str - :ivar score: Candidate's aggregate score. - :vartype score: float - :ivar has_results: Whether detailed results are available for this candidate. Required. - :vartype has_results: bool - :ivar created_at: Timestamp when the candidate was created, represented in Unix time. Required. - :vartype created_at: ~datetime.datetime - :ivar updated_at: Timestamp when the candidate was last updated, represented in Unix time. - Required. - :vartype updated_at: ~datetime.datetime - :ivar promotion: Promotion metadata. Null if not promoted. - :vartype promotion: ~azure.ai.projects.models.PromotionInfo - :ivar files: Files in the candidate's blob directory. Required. - :vartype files: list[~azure.ai.projects.models.CandidateFileInfo] - """ - - candidate_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Server-assigned candidate identifier. Required.""" - job_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Owning optimization job id. Required.""" - candidate_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Display name of the candidate. Required.""" - status: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Candidate lifecycle status. Required.""" - score: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Candidate's aggregate score.""" - has_results: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Whether detailed results are available for this candidate. Required.""" - created_at: datetime.datetime = rest_field( - visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" - ) - """Timestamp when the candidate was created, represented in Unix time. Required.""" - updated_at: datetime.datetime = rest_field( - visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" - ) - """Timestamp when the candidate was last updated, represented in Unix time. Required.""" - promotion: Optional["_models.PromotionInfo"] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """Promotion metadata. Null if not promoted.""" - files: list["_models.CandidateFileInfo"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Files in the candidate's blob directory. Required.""" - - @overload - def __init__( - self, - *, - candidate_id: str, - job_id: str, - candidate_name: str, - status: str, - has_results: bool, - created_at: datetime.datetime, - updated_at: datetime.datetime, - files: list["_models.CandidateFileInfo"], - score: Optional[float] = None, - promotion: Optional["_models.PromotionInfo"] = None, - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - -class CandidateResults(_Model): - """Full per-task evaluation results for a candidate, returned by GET /candidates/{id}/results. - - :ivar candidate_id: Owning candidate id. Required. - :vartype candidate_id: str - :ivar results: Per-task evaluation rows. Required. - :vartype results: list[~azure.ai.projects.models.OptimizationTaskResult] - """ - - candidate_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Owning candidate id. Required.""" - results: list["_models.OptimizationTaskResult"] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """Per-task evaluation rows. Required.""" - - @overload - def __init__( - self, - *, - candidate_id: str, - results: list["_models.OptimizationTaskResult"], - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - class CaptureStructuredOutputsTool(Tool, discriminator="capture_structured_outputs"): """A tool for capturing structured outputs. @@ -3459,8 +3180,8 @@ class ComparisonFilter(_Model): * `lte`: less than or equal * `in`: in * `nin`: not in. Required. Is one of the following types: Literal["eq"], Literal["ne"], - Literal["gt"], Literal["gte"], Literal["lt"], Literal["lte"] - :vartype type: str or str or str or str or str or str + Literal["gt"], Literal["gte"], Literal["lt"], Literal["lte"], Literal["in"], Literal["nin"] + :vartype type: str or str or str or str or str or str or str or str :ivar key: The key to compare against the value. Required. :vartype key: str :ivar value: The value to compare against the attribute key; supports string, number, or @@ -3468,7 +3189,7 @@ class ComparisonFilter(_Model): :vartype value: str or float or bool or list[str or float] """ - type: Literal["eq", "ne", "gt", "gte", "lt", "lte"] = rest_field( + type: Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) """Specifies the comparison operator: ``eq``, ``ne``, ``gt``, ``gte``, ``lt``, ``lte``, ``in``, @@ -3482,7 +3203,8 @@ class ComparisonFilter(_Model): * `lte`: less than or equal * `in`: in * `nin`: not in. Required. Is one of the following types: Literal[\"eq\"], - Literal[\"ne\"], Literal[\"gt\"], Literal[\"gte\"], Literal[\"lt\"], Literal[\"lte\"]""" + Literal[\"ne\"], Literal[\"gt\"], Literal[\"gte\"], Literal[\"lt\"], Literal[\"lte\"], + Literal[\"in\"], Literal[\"nin\"]""" key: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The key to compare against the value. Required.""" value: Union[str, float, bool, list[Union[str, float]]] = rest_field( @@ -3495,7 +3217,7 @@ class ComparisonFilter(_Model): def __init__( self, *, - type: Literal["eq", "ne", "gt", "gte", "lt", "lte"], + type: Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"], key: str, value: Union[str, float, bool, list[Union[str, float]]], ) -> None: ... @@ -3549,6 +3271,33 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ComputerTool(Tool, discriminator="computer"): + """Computer. + + :ivar type: The type of the computer tool. Always ``computer``. Required. COMPUTER. + :vartype type: str or ~azure.ai.projects.models.COMPUTER + """ + + type: Literal[ToolType.COMPUTER] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the computer tool. Always ``computer``. Required. COMPUTER.""" + + @overload + def __init__( + self, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.type = ToolType.COMPUTER # type: ignore + + class ComputerUsePreviewTool(Tool, discriminator="computer_use_preview"): """Computer use preview. @@ -3803,7 +3552,7 @@ class ContainerNetworkPolicyAllowlistParam(ContainerNetworkPolicyParam, discrimi allowed_domains: list[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """A list of allowed domains when type is ``allowlist``. Required.""" domain_secrets: Optional[list["_models.ContainerNetworkPolicyDomainSecretParam"]] = rest_field( - visibility=["read", "create", "update", "delete", "query"] + visibility=["create"] ) """Optional domain-scoped secrets for allowlisted domains.""" @@ -3967,6 +3716,10 @@ class ContinuousEvaluationRuleAction(EvaluationRuleAction, discriminator="contin :vartype eval_id: str :ivar max_hourly_runs: Maximum number of evaluation runs allowed per hour. :vartype max_hourly_runs: int + :ivar sampling_rate: Percentage (0-100] chance that a matching event triggers an evaluation. + When omitted, the service-default is to evaluate every event, which is equivalent to setting a + sampling rate of 100. + :vartype sampling_rate: float """ type: Literal[EvaluationRuleActionType.CONTINUOUS_EVALUATION] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -3977,6 +3730,12 @@ class ContinuousEvaluationRuleAction(EvaluationRuleAction, discriminator="contin name="maxHourlyRuns", visibility=["read", "create", "update", "delete", "query"] ) """Maximum number of evaluation runs allowed per hour.""" + sampling_rate: Optional[float] = rest_field( + name="samplingRate", visibility=["read", "create", "update", "delete", "query"] + ) + """Percentage (0-100] chance that a matching event triggers an evaluation. When omitted, the + service-default is to evaluate every event, which is equivalent to setting a sampling rate of + 100.""" @overload def __init__( @@ -3984,6 +3743,7 @@ def __init__( *, eval_id: str, max_hourly_runs: Optional[int] = None, + sampling_rate: Optional[float] = None, ) -> None: ... @overload @@ -4532,6 +4292,8 @@ class CustomToolParam(Tool, discriminator="custom"): :vartype description: str :ivar format: The input format for the custom tool. Default is unconstrained text. :vartype format: ~azure.ai.projects.models.CustomToolParamFormat + :ivar defer_loading: Whether this tool should be deferred and discovered via tool search. + :vartype defer_loading: bool """ type: Literal[ToolType.CUSTOM] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -4544,6 +4306,8 @@ class CustomToolParam(Tool, discriminator="custom"): visibility=["read", "create", "update", "delete", "query"] ) """The input format for the custom tool. Default is unconstrained text.""" + defer_loading: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether this tool should be deferred and discovered via tool search.""" @overload def __init__( @@ -4552,6 +4316,7 @@ def __init__( name: str, description: Optional[str] = None, format: Optional["_models.CustomToolParamFormat"] = None, + defer_loading: Optional[bool] = None, ) -> None: ... @overload @@ -5099,83 +4864,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = EvaluatorGenerationJobSourceType.DATASET # type: ignore -class DatasetInfo(_Model): - """Metadata about the dataset used for optimization, surfaced in the response. - - :ivar name: Dataset name when using a registered dataset reference. Null for inline datasets. - :vartype name: str - :ivar version: Dataset version when using a registered dataset reference. Null for inline - datasets. - :vartype version: str - :ivar task_count: Number of tasks/rows in the dataset. Required. - :vartype task_count: int - :ivar is_inline: True when the dataset was provided inline in the request body. Required. - :vartype is_inline: bool - """ - - name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Dataset name when using a registered dataset reference. Null for inline datasets.""" - version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Dataset version when using a registered dataset reference. Null for inline datasets.""" - task_count: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Number of tasks/rows in the dataset. Required.""" - is_inline: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """True when the dataset was provided inline in the request body. Required.""" - - @overload - def __init__( - self, - *, - task_count: int, - is_inline: bool, - name: Optional[str] = None, - version: Optional[str] = None, - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - -class DatasetRef(_Model): - """Reference to a registered dataset in the Foundry Dataset Service. - - :ivar name: Dataset name. Required. - :vartype name: str - :ivar version: Dataset version. If not specified, the latest version is used. - :vartype version: str - """ - - name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Dataset name. Required.""" - version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Dataset version. If not specified, the latest version is used.""" - - @overload - def __init__( - self, - *, - name: str, - version: Optional[str] = None, - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - class DatasetReference(_Model): """Reference to a versioned Foundry Dataset. @@ -5699,6 +5387,10 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class EmptyModelParam(_Model): + """EmptyModelParam.""" + + class EntraAuthorizationScheme(AgentEndpointAuthorizationScheme, discriminator="Entra"): """EntraAuthorizationScheme. @@ -6422,14 +6114,14 @@ class EvaluationScheduleTask(ScheduleTask, discriminator="Evaluation"): :ivar eval_id: Identifier of the evaluation group. Required. :vartype eval_id: str :ivar eval_run: The evaluation run payload. Required. - :vartype eval_run: any + :vartype eval_run: dict[str, any] """ type: Literal[ScheduleTaskType.EVALUATION] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore """Required. Evaluation task.""" eval_id: str = rest_field(name="evalId", visibility=["read", "create", "update", "delete", "query"]) """Identifier of the evaluation group. Required.""" - eval_run: Any = rest_field(name="evalRun", visibility=["read", "create", "update", "delete", "query"]) + eval_run: dict[str, Any] = rest_field(name="evalRun", visibility=["read", "create", "update", "delete", "query"]) """The evaluation run payload. Required.""" @overload @@ -6437,7 +6129,7 @@ def __init__( self, *, eval_id: str, - eval_run: Any, + eval_run: dict[str, Any], configuration: Optional[dict[str, str]] = None, ) -> None: ... @@ -6854,6 +6546,12 @@ class EvaluatorVersion(_Model): :vartype evaluator_type: str or ~azure.ai.projects.models.EvaluatorType :ivar categories: The categories of the evaluator. Required. :vartype categories: list[str or ~azure.ai.projects.models.EvaluatorCategory] + :ivar supported_evaluation_levels: Evaluation levels this evaluator supports (e.g., ``turn``, + ``conversation``). When omitted on create, the service defaults to ``["turn"]``. On update, + omitting this field leaves it unchanged; an empty list is rejected. Custom code-based + evaluators support only ``turn``; custom prompt-based evaluators support exactly one level + (``turn`` or ``conversation``). + :vartype supported_evaluation_levels: list[str or ~azure.ai.projects.models.EvaluationLevel] :ivar definition: Definition of the evaluator. Required. :vartype definition: ~azure.ai.projects.models.EvaluatorDefinition :ivar generation_artifacts: Provenance artifacts from the generation pipeline. Read-only; @@ -6889,6 +6587,13 @@ class EvaluatorVersion(_Model): visibility=["read", "create", "update", "delete", "query"] ) """The categories of the evaluator. Required.""" + supported_evaluation_levels: Optional[list[Union[str, "_models.EvaluationLevel"]]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Evaluation levels this evaluator supports (e.g., ``turn``, ``conversation``). When omitted on + create, the service defaults to ``[\"turn\"]``. On update, omitting this field leaves it + unchanged; an empty list is rejected. Custom code-based evaluators support only ``turn``; + custom prompt-based evaluators support exactly one level (``turn`` or ``conversation``).""" definition: "_models.EvaluatorDefinition" = rest_field(visibility=["read", "create"]) """Definition of the evaluator. Required.""" generation_artifacts: Optional["_models.EvaluatorGenerationArtifacts"] = rest_field(visibility=["read"]) @@ -6921,6 +6626,7 @@ def __init__( definition: "_models.EvaluatorDefinition", display_name: Optional[str] = None, metadata: Optional[dict[str, str]] = None, + supported_evaluation_levels: Optional[list[Union[str, "_models.EvaluationLevel"]]] = None, description: Optional[str] = None, tags: Optional[dict[str, str]] = None, ) -> None: ... @@ -7646,6 +7352,8 @@ class FunctionTool(Tool, discriminator="function"): :vartype parameters: dict[str, any] :ivar strict: Required. :vartype strict: bool + :ivar defer_loading: Whether this function is deferred and loaded via tool search. + :vartype defer_loading: bool """ type: Literal[ToolType.FUNCTION] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -7657,6 +7365,8 @@ class FunctionTool(Tool, discriminator="function"): """Required.""" strict: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Required.""" + defer_loading: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether this function is deferred and loaded via tool search.""" @overload def __init__( @@ -7666,6 +7376,7 @@ def __init__( parameters: dict[str, Any], strict: bool, description: Optional[str] = None, + defer_loading: Optional[bool] = None, ) -> None: ... @overload @@ -7680,6 +7391,58 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = ToolType.FUNCTION # type: ignore +class FunctionToolParam(_Model): + """FunctionToolParam. + + :ivar name: Required. + :vartype name: str + :ivar description: + :vartype description: str + :ivar parameters: + :vartype parameters: ~azure.ai.projects.models.EmptyModelParam + :ivar strict: + :vartype strict: bool + :ivar type: Required. Default value is "function". + :vartype type: str + :ivar defer_loading: Whether this function should be deferred and discovered via tool search. + :vartype defer_loading: bool + """ + + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + parameters: Optional["_models.EmptyModelParam"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + strict: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + type: Literal["function"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required. Default value is \"function\".""" + defer_loading: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether this function should be deferred and discovered via tool search.""" + + @overload + def __init__( + self, + *, + name: str, + description: Optional[str] = None, + parameters: Optional["_models.EmptyModelParam"] = None, + strict: Optional[bool] = None, + defer_loading: Optional[bool] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.type: Literal["function"] = "function" + + class GitHubIssueRoutineTrigger(RoutineTrigger, discriminator="github_issue"): """A GitHub issue routine trigger. @@ -8038,10 +7801,18 @@ class ImageGenTool(Tool, discriminator="image_generation"): ``auto``. Default: ``auto``. Is one of the following types: Literal["low"], Literal["medium"], Literal["high"], Literal["auto"] :vartype quality: str or str or str or str - :ivar size: The size of the generated image. One of ``1024x1024``, ``1024x1536``, - ``1536x1024``, or ``auto``. Default: ``auto``. Is one of the following types: - Literal["1024x1024"], Literal["1024x1536"], Literal["1536x1024"], Literal["auto"] - :vartype size: str or str or str or str + :ivar size: The size of the generated images. For ``gpt-image-2`` and + ``gpt-image-2-2026-04-21``, arbitrary resolutions are supported as ``WIDTHxHEIGHT`` strings, + for example ``1536x864``. Width and height must both be divisible by 16 and the requested + aspect ratio must be between 1:3 and 3:1. Resolutions above ``2560x1440`` are experimental, and + the maximum supported resolution is ``3840x2160``. The requested size must also satisfy the + model's current pixel and edge limits. The standard sizes ``1024x1024``, ``1536x1024``, and + ``1024x1536`` are supported by the GPT image models; ``auto`` is supported for models that + allow automatic sizing. For ``dall-e-2``, use one of ``256x256``, ``512x512``, or + ``1024x1024``. For ``dall-e-3``, use one of ``1024x1024``, ``1792x1024``, or ``1024x1792``. Is + one of the following types: Literal["1024x1024"], Literal["1024x1536"], Literal["1536x1024"], + Literal["auto"], str + :vartype size: str or str or str or str or str :ivar output_format: The output format of the generated image. One of ``png``, ``webp``, or ``jpeg``. Default: ``png``. Is one of the following types: Literal["png"], Literal["webp"], Literal["jpeg"] @@ -8089,12 +7860,19 @@ class ImageGenTool(Tool, discriminator="image_generation"): """The quality of the generated image. One of ``low``, ``medium``, ``high``, or ``auto``. Default: ``auto``. Is one of the following types: Literal[\"low\"], Literal[\"medium\"], Literal[\"high\"], Literal[\"auto\"]""" - size: Optional[Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] = rest_field( - visibility=["read", "create", "update", "delete", "query"] + size: Optional[Union[Literal["1024x1024"], Literal["1024x1536"], Literal["1536x1024"], Literal["auto"], str]] = ( + rest_field(visibility=["read", "create", "update", "delete", "query"]) ) - """The size of the generated image. One of ``1024x1024``, ``1024x1536``, ``1536x1024``, or - ``auto``. Default: ``auto``. Is one of the following types: Literal[\"1024x1024\"], - Literal[\"1024x1536\"], Literal[\"1536x1024\"], Literal[\"auto\"]""" + """The size of the generated images. For ``gpt-image-2`` and ``gpt-image-2-2026-04-21``, arbitrary + resolutions are supported as ``WIDTHxHEIGHT`` strings, for example ``1536x864``. Width and + height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. + Resolutions above ``2560x1440`` are experimental, and the maximum supported resolution is + ``3840x2160``. The requested size must also satisfy the model's current pixel and edge limits. + The standard sizes ``1024x1024``, ``1536x1024``, and ``1024x1536`` are supported by the GPT + image models; ``auto`` is supported for models that allow automatic sizing. For ``dall-e-2``, + use one of ``256x256``, ``512x512``, or ``1024x1024``. For ``dall-e-3``, use one of + ``1024x1024``, ``1792x1024``, or ``1024x1792``. Is one of the following types: + Literal[\"1024x1024\"], Literal[\"1024x1536\"], Literal[\"1536x1024\"], Literal[\"auto\"], str""" output_format: Optional[Literal["png", "webp", "jpeg"]] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -8148,7 +7926,9 @@ def __init__( Union[Literal["gpt-image-1"], Literal["gpt-image-1-mini"], Literal["gpt-image-1.5"], str] ] = None, quality: Optional[Literal["low", "medium", "high", "auto"]] = None, - size: Optional[Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] = None, + size: Optional[ + Union[Literal["1024x1024"], Literal["1024x1536"], Literal["1536x1024"], Literal["auto"], str] + ] = None, output_format: Optional[Literal["png", "webp", "jpeg"]] = None, output_compression: Optional[int] = None, moderation: Optional[Literal["auto", "low"]] = None, @@ -9069,6 +8849,8 @@ class MCPTool(Tool, discriminator="mcp"): :ivar require_approval: Is one of the following types: MCPToolRequireApproval, Literal["always"], Literal["never"] :vartype require_approval: ~azure.ai.projects.models.MCPToolRequireApproval or str or str + :ivar defer_loading: Whether this MCP tool is deferred and discovered via tool search. + :vartype defer_loading: bool :ivar project_connection_id: The connection ID in the project for the MCP server. The connection stores authentication and other connection details needed to connect to the MCP server. @@ -9128,6 +8910,8 @@ class MCPTool(Tool, discriminator="mcp"): rest_field(visibility=["read", "create", "update", "delete", "query"]) ) """Is one of the following types: MCPToolRequireApproval, Literal[\"always\"], Literal[\"never\"]""" + defer_loading: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether this MCP tool is deferred and discovered via tool search.""" project_connection_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The connection ID in the project for the MCP server. The connection stores authentication and other connection details needed to connect to the MCP server.""" @@ -9161,6 +8945,7 @@ def __init__( headers: Optional[dict[str, str]] = None, allowed_tools: Optional[Union[list[str], "_models.MCPToolFilter"]] = None, require_approval: Optional[Union["_models.MCPToolRequireApproval", Literal["always"], Literal["never"]]] = None, + defer_loading: Optional[bool] = None, project_connection_id: Optional[str] = None, tool_configs: Optional[dict[str, "_models.ToolConfig"]] = None, ) -> None: ... @@ -10347,6 +10132,52 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = RecurrenceType.MONTHLY # type: ignore +class NamespaceToolParam(Tool, discriminator="namespace"): + """Namespace. + + :ivar type: The type of the tool. Always ``namespace``. Required. NAMESPACE. + :vartype type: str or ~azure.ai.projects.models.NAMESPACE + :ivar name: The namespace name used in tool calls (for example, ``crm``). Required. + :vartype name: str + :ivar description: A description of the namespace shown to the model. Required. + :vartype description: str + :ivar tools: The function/custom tools available inside this namespace. Required. + :vartype tools: list[~azure.ai.projects.models.FunctionToolParam or + ~azure.ai.projects.models.CustomToolParam] + """ + + type: Literal[ToolType.NAMESPACE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the tool. Always ``namespace``. Required. NAMESPACE.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The namespace name used in tool calls (for example, ``crm``). Required.""" + description: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A description of the namespace shown to the model. Required.""" + tools: list[Union["_models.FunctionToolParam", "_models.CustomToolParam"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The function/custom tools available inside this namespace. Required.""" + + @overload + def __init__( + self, + *, + name: str, + description: str, + tools: list[Union["_models.FunctionToolParam", "_models.CustomToolParam"]], + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.type = ToolType.NAMESPACE # type: ignore + + class NoAuthenticationCredentials(BaseCredentials, discriminator="None"): """Credentials that do not require authentication. @@ -10743,46 +10574,27 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = ToolType.OPENAPI # type: ignore -class OptimizationAgentDefinition(_Model): - """Agent definition returned in response payloads (includes resolved config). +class OptimizationAgentIdentifier(_Model): + """Identifies the registered Foundry agent to optimize (request-only). Skills, tools, and + system_prompt are specified in options.optimization_config. - :ivar agent_name: Agent name. + :ivar agent_name: Registered Foundry agent name (required). Required. :vartype agent_name: str - :ivar agent_version: Agent version. + :ivar agent_version: Pinned agent version. Defaults to latest if omitted. :vartype agent_version: str - :ivar model: Model deployment name. - :vartype model: str - :ivar system_prompt: System prompt / instructions. - :vartype system_prompt: str - :ivar skills: Agent skills. - :vartype skills: list[dict[str, any]] - :ivar tools: Agent tools. - :vartype tools: list[dict[str, any]] """ - agent_name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Agent name.""" + agent_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Registered Foundry agent name (required). Required.""" agent_version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Agent version.""" - model: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Model deployment name.""" - system_prompt: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """System prompt / instructions.""" - skills: Optional[list[dict[str, Any]]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Agent skills.""" - tools: Optional[list[dict[str, Any]]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Agent tools.""" + """Pinned agent version. Defaults to latest if omitted.""" @overload def __init__( self, *, - agent_name: Optional[str] = None, + agent_name: str, agent_version: Optional[str] = None, - model: Optional[str] = None, - system_prompt: Optional[str] = None, - skills: Optional[list[dict[str, Any]]] = None, - tools: Optional[list[dict[str, Any]]] = None, ) -> None: ... @overload @@ -10804,22 +10616,12 @@ class OptimizationCandidate(_Model): :vartype candidate_id: str :ivar name: Display name of the candidate (e.g., 'baseline', 'instruction-v2'). Required. :vartype name: str - :ivar config: The agent configuration that produced this candidate. Required. - :vartype config: ~azure.ai.projects.models.OptimizationAgentDefinition :ivar mutations: What was mutated from the baseline (e.g., {system_prompt: 'new prompt'}). - Required. :vartype mutations: dict[str, any] :ivar avg_score: Average composite score across all tasks. Required. :vartype avg_score: float :ivar avg_tokens: Average token usage across all tasks. Required. :vartype avg_tokens: float - :ivar pass_rate: Fraction of tasks that met the pass threshold. Required. - :vartype pass_rate: float - :ivar task_scores: Individual task-level scores. Required. - :vartype task_scores: list[~azure.ai.projects.models.OptimizationTaskResult] - :ivar is_pareto_optimal: Whether this candidate is on the Pareto frontier (score vs cost). - Required. - :vartype is_pareto_optimal: bool :ivar eval_id: Foundry evaluation identifier used to score this candidate. :vartype eval_id: str :ivar eval_run_id: Foundry evaluation run identifier for this candidate's scoring run. @@ -10832,24 +10634,12 @@ class OptimizationCandidate(_Model): """Server-assigned candidate identifier. Use with GET /candidates/{id} sub-endpoints.""" name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Display name of the candidate (e.g., 'baseline', 'instruction-v2'). Required.""" - config: "_models.OptimizationAgentDefinition" = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """The agent configuration that produced this candidate. Required.""" - mutations: dict[str, Any] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """What was mutated from the baseline (e.g., {system_prompt: 'new prompt'}). Required.""" + mutations: Optional[dict[str, Any]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """What was mutated from the baseline (e.g., {system_prompt: 'new prompt'}).""" avg_score: float = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Average composite score across all tasks. Required.""" avg_tokens: float = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Average token usage across all tasks. Required.""" - pass_rate: float = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Fraction of tasks that met the pass threshold. Required.""" - task_scores: list["_models.OptimizationTaskResult"] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """Individual task-level scores. Required.""" - is_pareto_optimal: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Whether this candidate is on the Pareto frontier (score vs cost). Required.""" eval_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Foundry evaluation identifier used to score this candidate.""" eval_run_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -10864,14 +10654,10 @@ def __init__( self, *, name: str, - config: "_models.OptimizationAgentDefinition", - mutations: dict[str, Any], avg_score: float, avg_tokens: float, - pass_rate: float, - task_scores: list["_models.OptimizationTaskResult"], - is_pareto_optimal: bool, candidate_id: Optional[str] = None, + mutations: Optional[dict[str, Any]] = None, eval_id: Optional[str] = None, eval_run_id: Optional[str] = None, promotion: Optional["_models.PromotionInfo"] = None, @@ -10888,6 +10674,187 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class OptimizationDatasetCriterion(_Model): + """Evaluation criterion: a name + instruction pair used for per-item scoring. + + :ivar name: Criterion name. Required. + :vartype name: str + :ivar instruction: Criterion instruction / description. Required. + :vartype instruction: str + """ + + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Criterion name. Required.""" + instruction: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Criterion instruction / description. Required.""" + + @overload + def __init__( + self, + *, + name: str, + instruction: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class OptimizationDatasetInput(_Model): + """Base discriminated model for dataset input. Either inline items or a registered reference. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + OptimizationInlineDatasetInput, OptimizationReferenceDatasetInput + + :ivar type: Dataset input type discriminator. Required. Known values are: "inline" and + "reference". + :vartype type: str or ~azure.ai.projects.models.OptimizationDatasetInputType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Dataset input type discriminator. Required. Known values are: \"inline\" and \"reference\".""" + + @overload + def __init__( + self, + *, + type: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class OptimizationDatasetItem(_Model): + """A single item in an inline dataset. + + :ivar query: The user query / prompt. + :vartype query: str + :ivar ground_truth: Expected ground truth answer. + :vartype ground_truth: str + :ivar desired_num_turns: Desired number of conversation turns for simulation mode (1-20). + :vartype desired_num_turns: int + :ivar criteria: Per-item evaluation criteria. + :vartype criteria: list[~azure.ai.projects.models.OptimizationDatasetCriterion] + """ + + query: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The user query / prompt.""" + ground_truth: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Expected ground truth answer.""" + desired_num_turns: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Desired number of conversation turns for simulation mode (1-20).""" + criteria: Optional[list["_models.OptimizationDatasetCriterion"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-item evaluation criteria.""" + + @overload + def __init__( + self, + *, + query: Optional[str] = None, + ground_truth: Optional[str] = None, + desired_num_turns: Optional[int] = None, + criteria: Optional[list["_models.OptimizationDatasetCriterion"]] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class OptimizationEvaluatorRef(_Model): + """Reference to a named evaluator, optionally pinned to a version. + + :ivar name: Evaluator name. Required. + :vartype name: str + :ivar version: Evaluator version. If not specified, the latest version is used. + :vartype version: str + """ + + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Evaluator name. Required.""" + version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Evaluator version. If not specified, the latest version is used.""" + + @overload + def __init__( + self, + *, + name: str, + version: Optional[str] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class OptimizationInlineDatasetInput(OptimizationDatasetInput, discriminator="inline"): + """Inline dataset — items supplied directly in the request body. + + :ivar type: Dataset input type discriminator. Required. Inline dataset — items are provided + directly in the request body. + :vartype type: str or ~azure.ai.projects.models.INLINE + :ivar items_property: Dataset items. Required. + :vartype items_property: list[~azure.ai.projects.models.OptimizationDatasetItem] + """ + + type: Literal[OptimizationDatasetInputType.INLINE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Dataset input type discriminator. Required. Inline dataset — items are provided directly in the + request body.""" + items_property: list["_models.OptimizationDatasetItem"] = rest_field( + name="items", visibility=["read", "create", "update", "delete", "query"], original_tsp_name="items" + ) + """Dataset items. Required.""" + + @overload + def __init__( + self, + *, + items_property: list["_models.OptimizationDatasetItem"], + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.type = OptimizationDatasetInputType.INLINE # type: ignore + + class OptimizationJob(_Model): """Agent optimization job resource — a long-running job that optimizes an agent's configuration (instructions, model, skills, tools) to maximize evaluation scores. On success, the result @@ -10895,48 +10862,48 @@ class OptimizationJob(_Model): :ivar id: Server-assigned unique identifier. Required. :vartype id: str + :ivar inputs: Caller-supplied inputs. + :vartype inputs: ~azure.ai.projects.models.OptimizationJobInputs + :ivar result: Result produced on success. + :vartype result: ~azure.ai.projects.models.OptimizationJobResult :ivar status: Current lifecycle status. Required. Known values are: "queued", "in_progress", "succeeded", "failed", and "cancelled". :vartype status: str or ~azure.ai.projects.models.JobStatus :ivar error: Error details — populated only on failure. :vartype error: ~azure.ai.projects.models.ApiError - :ivar result: Result produced on success. - :vartype result: ~azure.ai.projects.models.OptimizationJobResult - :ivar inputs: Caller-supplied inputs. - :vartype inputs: ~azure.ai.projects.models.OptimizationJobInputs :ivar created_at: The timestamp when the job was created, represented in Unix time. Required. :vartype created_at: ~datetime.datetime - :ivar updated_at: The timestamp when the job was last updated (status, progress, or result - change), represented in Unix time. + :ivar updated_at: The timestamp when the job was last updated, represented in Unix time. + Required. :vartype updated_at: ~datetime.datetime - :ivar progress: Progress while in flight. Absent in terminal states. + :ivar progress: Progress snapshot. May be present in terminal states reflecting last-known + progress. :vartype progress: ~azure.ai.projects.models.OptimizationJobProgress - :ivar dataset: Metadata about the dataset used for this optimization job. - :vartype dataset: ~azure.ai.projects.models.DatasetInfo + :ivar warnings: Non-fatal warnings emitted at any point during optimization. + :vartype warnings: list[str] """ id: str = rest_field(visibility=["read"]) """Server-assigned unique identifier. Required.""" + inputs: Optional["_models.OptimizationJobInputs"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Caller-supplied inputs.""" + result: Optional["_models.OptimizationJobResult"] = rest_field(visibility=["read"]) + """Result produced on success.""" status: Union[str, "_models.JobStatus"] = rest_field(visibility=["read"]) """Current lifecycle status. Required. Known values are: \"queued\", \"in_progress\", \"succeeded\", \"failed\", and \"cancelled\".""" error: Optional["_models.ApiError"] = rest_field(visibility=["read"]) """Error details — populated only on failure.""" - result: Optional["_models.OptimizationJobResult"] = rest_field(visibility=["read"]) - """Result produced on success.""" - inputs: Optional["_models.OptimizationJobInputs"] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """Caller-supplied inputs.""" created_at: datetime.datetime = rest_field(visibility=["read"], format="unix-timestamp") """The timestamp when the job was created, represented in Unix time. Required.""" - updated_at: Optional[datetime.datetime] = rest_field(visibility=["read"], format="unix-timestamp") - """The timestamp when the job was last updated (status, progress, or result change), represented - in Unix time.""" + updated_at: datetime.datetime = rest_field(visibility=["read"], format="unix-timestamp") + """The timestamp when the job was last updated, represented in Unix time. Required.""" progress: Optional["_models.OptimizationJobProgress"] = rest_field(visibility=["read"]) - """Progress while in flight. Absent in terminal states.""" - dataset: Optional["_models.DatasetInfo"] = rest_field(visibility=["read"]) - """Metadata about the dataset used for this optimization job.""" + """Progress snapshot. May be present in terminal states reflecting last-known progress.""" + warnings: Optional[list[str]] = rest_field(visibility=["read"]) + """Non-fatal warnings emitted at any point during optimization.""" @overload def __init__( @@ -10960,32 +10927,38 @@ class OptimizationJobInputs(_Model): """Caller-supplied inputs for an optimization job. :ivar agent: The agent (and pinned version) being optimized. Required. - :vartype agent: ~azure.ai.projects.models.AgentIdentifier - :ivar train_dataset_reference: Reference to a registered training dataset (required). Required. - :vartype train_dataset_reference: ~azure.ai.projects.models.DatasetRef - :ivar validation_dataset_reference: Optional held-out validation dataset for measuring - generalization of the final candidate. - :vartype validation_dataset_reference: ~azure.ai.projects.models.DatasetRef - :ivar evaluators: Job-level evaluators (referenced by name). Per-task criteria may override. - Default: ['task_adherence']. - :vartype evaluators: list[str] + :vartype agent: ~azure.ai.projects.models.OptimizationAgentIdentifier + :ivar train_dataset: Training dataset — either inline items or a reference to a registered + dataset. Required. Required. + :vartype train_dataset: ~azure.ai.projects.models.OptimizationDatasetInput + :ivar validation_dataset: Optional held-out validation dataset for measuring generalization of + the final candidate. + :vartype validation_dataset: ~azure.ai.projects.models.OptimizationDatasetInput + :ivar evaluators: Job-level evaluators referenced by name and optional version. Required; at + least one must be provided. Required. + :vartype evaluators: list[~azure.ai.projects.models.OptimizationEvaluatorRef] :ivar options: Tuning knobs and run-mode. :vartype options: ~azure.ai.projects.models.OptimizationOptions """ - agent: "_models.AgentIdentifier" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + agent: "_models.OptimizationAgentIdentifier" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) """The agent (and pinned version) being optimized. Required.""" - train_dataset_reference: "_models.DatasetRef" = rest_field( + train_dataset: "_models.OptimizationDatasetInput" = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """Reference to a registered training dataset (required). Required.""" - validation_dataset_reference: Optional["_models.DatasetRef"] = rest_field( + """Training dataset — either inline items or a reference to a registered dataset. Required. + Required.""" + validation_dataset: Optional["_models.OptimizationDatasetInput"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) """Optional held-out validation dataset for measuring generalization of the final candidate.""" - evaluators: Optional[list[str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Job-level evaluators (referenced by name). Per-task criteria may override. Default: - ['task_adherence'].""" + evaluators: list["_models.OptimizationEvaluatorRef"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Job-level evaluators referenced by name and optional version. Required; at least one must be + provided. Required.""" options: Optional["_models.OptimizationOptions"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -10995,10 +10968,10 @@ class OptimizationJobInputs(_Model): def __init__( self, *, - agent: "_models.AgentIdentifier", - train_dataset_reference: "_models.DatasetRef", - validation_dataset_reference: Optional["_models.DatasetRef"] = None, - evaluators: Optional[list[str]] = None, + agent: "_models.OptimizationAgentIdentifier", + train_dataset: "_models.OptimizationDatasetInput", + evaluators: list["_models.OptimizationEvaluatorRef"], + validation_dataset: Optional["_models.OptimizationDatasetInput"] = None, options: Optional["_models.OptimizationOptions"] = None, ) -> None: ... @@ -11013,11 +10986,51 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class OptimizationJobListItem(_Model): + """Slim job representation returned by the LIST endpoint. + + :ivar id: Server-assigned unique identifier. Required. + :vartype id: str + :ivar status: Current lifecycle status. Required. Known values are: "queued", "in_progress", + "succeeded", "failed", and "cancelled". + :vartype status: str or ~azure.ai.projects.models.JobStatus + :ivar error: Error details — populated only on failure. + :vartype error: ~azure.ai.projects.models.ApiError + :ivar created_at: The timestamp when the job was created, represented in Unix time. Required. + :vartype created_at: ~datetime.datetime + :ivar updated_at: The timestamp when the job was last updated, represented in Unix time. + Required. + :vartype updated_at: ~datetime.datetime + :ivar progress: Progress snapshot. May be present in terminal states reflecting last-known + progress. + :vartype progress: ~azure.ai.projects.models.OptimizationJobProgress + :ivar agent: The agent targeted by this optimization job. + :vartype agent: ~azure.ai.projects.models.OptimizationAgentIdentifier + """ + + id: str = rest_field(visibility=["read"]) + """Server-assigned unique identifier. Required.""" + status: Union[str, "_models.JobStatus"] = rest_field(visibility=["read"]) + """Current lifecycle status. Required. Known values are: \"queued\", \"in_progress\", + \"succeeded\", \"failed\", and \"cancelled\".""" + error: Optional["_models.ApiError"] = rest_field(visibility=["read"]) + """Error details — populated only on failure.""" + created_at: datetime.datetime = rest_field(visibility=["read"], format="unix-timestamp") + """The timestamp when the job was created, represented in Unix time. Required.""" + updated_at: datetime.datetime = rest_field(visibility=["read"], format="unix-timestamp") + """The timestamp when the job was last updated, represented in Unix time. Required.""" + progress: Optional["_models.OptimizationJobProgress"] = rest_field(visibility=["read"]) + """Progress snapshot. May be present in terminal states reflecting last-known progress.""" + agent: Optional["_models.OptimizationAgentIdentifier"] = rest_field(visibility=["read"]) + """The agent targeted by this optimization job.""" + + class OptimizationJobProgress(_Model): """In-flight progress; only populated while status is queued or in_progress. - :ivar current_iteration: 1-based current iteration index. Required. - :vartype current_iteration: int + :ivar candidates_completed: Number of candidates whose evaluation has completed so far. + Required. + :vartype candidates_completed: int :ivar best_score: Best score observed so far across all candidates. Required. :vartype best_score: float :ivar elapsed_seconds: Wall-clock time elapsed in seconds since the job began executing. @@ -11025,8 +11038,8 @@ class OptimizationJobProgress(_Model): :vartype elapsed_seconds: float """ - current_iteration: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """1-based current iteration index. Required.""" + candidates_completed: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Number of candidates whose evaluation has completed so far. Required.""" best_score: float = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Best score observed so far across all candidates. Required.""" elapsed_seconds: float = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -11036,7 +11049,7 @@ class OptimizationJobProgress(_Model): def __init__( self, *, - current_iteration: int, + candidates_completed: int, best_score: float, elapsed_seconds: float, ) -> None: ... @@ -11055,56 +11068,30 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OptimizationJobResult(_Model): """Terminal-state result body. Populated when status is succeeded or failed. - :ivar baseline: Evaluation scores for the original (un-optimized) agent configuration. - :vartype baseline: ~azure.ai.projects.models.OptimizationCandidate - :ivar best: The highest-scoring candidate found during optimization. - :vartype best: ~azure.ai.projects.models.OptimizationCandidate + :ivar baseline: Candidate ID of the original (un-optimized) baseline evaluation. + :vartype baseline: str + :ivar best: Candidate ID of the highest-scoring candidate found during optimization. + :vartype best: str :ivar candidates: All evaluated candidates including baseline. :vartype candidates: list[~azure.ai.projects.models.OptimizationCandidate] - :ivar options: The options used for this optimization run. - :vartype options: ~azure.ai.projects.models.OptimizationOptions - :ivar warnings: Non-fatal warnings from the optimization run (e.g., target attribute failures - that were skipped). - :vartype warnings: list[str] - :ivar all_target_attributes_failed: True when all target attributes failed — only the baseline - was evaluated. - :vartype all_target_attributes_failed: bool """ - baseline: Optional["_models.OptimizationCandidate"] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """Evaluation scores for the original (un-optimized) agent configuration.""" - best: Optional["_models.OptimizationCandidate"] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """The highest-scoring candidate found during optimization.""" + baseline: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Candidate ID of the original (un-optimized) baseline evaluation.""" + best: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Candidate ID of the highest-scoring candidate found during optimization.""" candidates: Optional[list["_models.OptimizationCandidate"]] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) """All evaluated candidates including baseline.""" - options: Optional["_models.OptimizationOptions"] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """The options used for this optimization run.""" - warnings: Optional[list[str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Non-fatal warnings from the optimization run (e.g., target attribute failures that were - skipped).""" - all_target_attributes_failed: Optional[bool] = rest_field( - visibility=["read", "create", "update", "delete", "query"] - ) - """True when all target attributes failed — only the baseline was evaluated.""" @overload def __init__( self, *, - baseline: Optional["_models.OptimizationCandidate"] = None, - best: Optional["_models.OptimizationCandidate"] = None, + baseline: Optional[str] = None, + best: Optional[str] = None, candidates: Optional[list["_models.OptimizationCandidate"]] = None, - options: Optional["_models.OptimizationOptions"] = None, - warnings: Optional[list[str]] = None, - all_target_attributes_failed: Optional[bool] = None, ) -> None: ... @overload @@ -11121,8 +11108,9 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OptimizationOptions(_Model): """Tuning knobs and run-mode for an optimization job. - :ivar max_iterations: Maximum optimization iterations per strategy. Must be >= 1. Default: 5. - :vartype max_iterations: int + :ivar max_candidates: Maximum number of optimization candidates to generate. Must be >= 1. + Default: 5. + :vartype max_candidates: int :ivar optimization_config: Per-target-attribute configuration overrides. Contains skills, tools, system_prompt for the agent, plus model space for model optimization. :vartype optimization_config: dict[str, any] @@ -11138,8 +11126,8 @@ class OptimizationOptions(_Model): :vartype evaluation_level: str or ~azure.ai.projects.models.EvaluationLevel """ - max_iterations: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Maximum optimization iterations per strategy. Must be >= 1. Default: 5.""" + max_candidates: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Maximum number of optimization candidates to generate. Must be >= 1. Default: 5.""" optimization_config: Optional[dict[str, Any]] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -11161,7 +11149,7 @@ class OptimizationOptions(_Model): def __init__( self, *, - max_iterations: Optional[int] = None, + max_candidates: Optional[int] = None, optimization_config: Optional[dict[str, Any]] = None, eval_model: Optional[str] = None, optimization_model: Optional[str] = None, @@ -11179,71 +11167,32 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class OptimizationTaskResult(_Model): - """Per-task evaluation result for a single candidate. +class OptimizationReferenceDatasetInput(OptimizationDatasetInput, discriminator="reference"): + """Reference to a registered Foundry dataset. - :ivar task_name: Task name (from the dataset). Required. - :vartype task_name: str - :ivar query: The user query / input for the task. - :vartype query: str - :ivar scores: Per-evaluator scores keyed by evaluator name. Required. - :vartype scores: dict[str, float] - :ivar composite_score: Composite score combining all evaluator scores. Required. - :vartype composite_score: float - :ivar tokens: Total tokens consumed during the agent run for this task. Required. - :vartype tokens: int - :ivar duration_seconds: Wall-clock seconds for this task's agent execution. Required. - :vartype duration_seconds: float - :ivar passed: Whether the task met the pass threshold. Required. - :vartype passed: bool - :ivar error_message: Error message if the task failed during execution. - :vartype error_message: str - :ivar rationales: Per-evaluator reasoning keyed by evaluator name. - :vartype rationales: dict[str, str] - :ivar response: Raw agent response text. - :vartype response: str - :ivar run_id: Identifier of the agent run that produced this result. - :vartype run_id: str + :ivar type: Dataset input type discriminator. Required. Reference to a registered Foundry + dataset by name and version. + :vartype type: str or ~azure.ai.projects.models.REFERENCE + :ivar name: Registered dataset name. Required. + :vartype name: str + :ivar version: Dataset version. If not specified, the latest version is used. + :vartype version: str """ - task_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Task name (from the dataset). Required.""" - query: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The user query / input for the task.""" - scores: dict[str, float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Per-evaluator scores keyed by evaluator name. Required.""" - composite_score: float = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Composite score combining all evaluator scores. Required.""" - tokens: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Total tokens consumed during the agent run for this task. Required.""" - duration_seconds: float = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Wall-clock seconds for this task's agent execution. Required.""" - passed: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Whether the task met the pass threshold. Required.""" - error_message: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Error message if the task failed during execution.""" - rationales: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Per-evaluator reasoning keyed by evaluator name.""" - response: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Raw agent response text.""" - run_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Identifier of the agent run that produced this result.""" + type: Literal[OptimizationDatasetInputType.REFERENCE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Dataset input type discriminator. Required. Reference to a registered Foundry dataset by name + and version.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Registered dataset name. Required.""" + version: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Dataset version. If not specified, the latest version is used.""" @overload def __init__( self, *, - task_name: str, - scores: dict[str, float], - composite_score: float, - tokens: int, - duration_seconds: float, - passed: bool, - query: Optional[str] = None, - error_message: Optional[str] = None, - rationales: Optional[dict[str, str]] = None, - response: Optional[str] = None, - run_id: Optional[str] = None, + name: str, + version: Optional[str] = None, ) -> None: ... @overload @@ -11255,6 +11204,7 @@ def __init__(self, mapping: Mapping[str, Any]) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) + self.type = OptimizationDatasetInputType.REFERENCE # type: ignore class TelemetryEndpoint(_Model): @@ -11496,89 +11446,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.kind = MemoryItemKind.PROCEDURAL # type: ignore -class PromoteCandidateRequest(_Model): - """Request body for promoting a candidate to a Foundry agent version. - - :ivar agent_name: Name of the Foundry agent to promote to. Required. - :vartype agent_name: str - :ivar agent_version: Version of the Foundry agent to promote to. Required. - :vartype agent_version: str - """ - - agent_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Name of the Foundry agent to promote to. Required.""" - agent_version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Version of the Foundry agent to promote to. Required.""" - - @overload - def __init__( - self, - *, - agent_name: str, - agent_version: str, - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - -class PromoteCandidateResponse(_Model): - """Response after successfully promoting a candidate. - - :ivar candidate_id: The promoted candidate id. Required. - :vartype candidate_id: str - :ivar status: Status after promotion. Required. - :vartype status: str - :ivar promoted_at: Timestamp when promotion occurred, represented in Unix time. Required. - :vartype promoted_at: ~datetime.datetime - :ivar agent_name: Name of the Foundry agent promoted to. Required. - :vartype agent_name: str - :ivar agent_version: Version of the Foundry agent promoted to. Required. - :vartype agent_version: str - """ - - candidate_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The promoted candidate id. Required.""" - status: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Status after promotion. Required.""" - promoted_at: datetime.datetime = rest_field( - visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" - ) - """Timestamp when promotion occurred, represented in Unix time. Required.""" - agent_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Name of the Foundry agent promoted to. Required.""" - agent_version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """Version of the Foundry agent promoted to. Required.""" - - @overload - def __init__( - self, - *, - candidate_id: str, - status: str, - promoted_at: datetime.datetime, - agent_name: str, - agent_version: str, - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - class PromotionInfo(_Model): """Promotion metadata recorded when a candidate is deployed to a Foundry agent. @@ -11884,7 +11751,7 @@ class ProtocolVersionRecord(_Model): """A record mapping for a single protocol and its version. :ivar protocol: The protocol type. Required. Known values are: "activity_protocol", - "responses", "mcp", "invocations", and "invocations_ws". + "responses", "a2a", "mcp", "invocations", and "invocations_ws". :vartype protocol: str or ~azure.ai.projects.models.AgentProtocol :ivar version: The version string for the protocol, e.g. 'v0.1.1'. Required. :vartype version: str @@ -11893,8 +11760,8 @@ class ProtocolVersionRecord(_Model): protocol: Union[str, "_models.AgentProtocol"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """The protocol type. Required. Known values are: \"activity_protocol\", \"responses\", \"mcp\", - \"invocations\", and \"invocations_ws\".""" + """The protocol type. Required. Known values are: \"activity_protocol\", \"responses\", \"a2a\", + \"mcp\", \"invocations\", and \"invocations_ws\".""" version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The version string for the protocol, e.g. 'v0.1.1'. Required.""" @@ -12315,7 +12182,7 @@ class RoutineRun(_Model): :ivar id: The unique run identifier for the routine attempt. Required. :vartype id: str - :ivar status: The run status. + :ivar status: The run status. Is one of the following types: str :vartype status: str :ivar phase: The AgentExtensions lifecycle phase for the routine attempt. Known values are: "queued", "dispatching", "completed", and "failed". @@ -12368,8 +12235,8 @@ class RoutineRun(_Model): id: str = rest_field(visibility=["read"]) """The unique run identifier for the routine attempt. Required.""" - status: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) - """The run status.""" + status: Optional["_types.RoutineRunStatus"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The run status. Is one of the following types: str""" phase: Optional[Union[str, "_models.RoutineRunPhase"]] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) @@ -12435,7 +12302,7 @@ class RoutineRun(_Model): def __init__( self, *, - status: Optional[str] = None, + status: Optional["_types.RoutineRunStatus"] = None, phase: Optional[Union[str, "_models.RoutineRunPhase"]] = None, trigger_type: Optional[Union[str, "_models.RoutineTriggerType"]] = None, trigger_name: Optional[str] = None, @@ -13222,14 +13089,15 @@ class ToolChoiceParam(_Model): ``tools`` parameter to see how to specify which tools the model can call. You probably want to use the sub-classes and not this class directly. Known sub-classes are: - ToolChoiceAllowed, SpecificApplyPatchParam, ToolChoiceCodeInterpreter, - ToolChoiceComputerUsePreview, ToolChoiceCustom, ToolChoiceFileSearch, ToolChoiceFunction, - ToolChoiceImageGeneration, ToolChoiceMCP, SpecificFunctionShellParam, + ToolChoiceAllowed, SpecificApplyPatchParam, ToolChoiceCodeInterpreter, ToolChoiceComputer, + ToolChoiceComputerUse, ToolChoiceComputerUsePreview, ToolChoiceCustom, ToolChoiceFileSearch, + ToolChoiceFunction, ToolChoiceImageGeneration, ToolChoiceMCP, SpecificFunctionShellParam, ToolChoiceWebSearchPreview, ToolChoiceWebSearchPreview20250311 :ivar type: Required. Known values are: "allowed_tools", "function", "mcp", "custom", "apply_patch", "shell", "file_search", "web_search_preview", "computer_use_preview", - "web_search_preview_2025_03_11", "image_generation", and "code_interpreter". + "web_search_preview_2025_03_11", "image_generation", "code_interpreter", "computer", and + "computer_use". :vartype type: str or ~azure.ai.projects.models.ToolChoiceParamType """ @@ -13237,7 +13105,8 @@ class ToolChoiceParam(_Model): type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) """Required. Known values are: \"allowed_tools\", \"function\", \"mcp\", \"custom\", \"apply_patch\", \"shell\", \"file_search\", \"web_search_preview\", \"computer_use_preview\", - \"web_search_preview_2025_03_11\", \"image_generation\", and \"code_interpreter\".""" + \"web_search_preview_2025_03_11\", \"image_generation\", \"code_interpreter\", \"computer\", + and \"computer_use\".""" @overload def __init__( @@ -14139,6 +14008,62 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = ToolChoiceParamType.CODE_INTERPRETER # type: ignore +class ToolChoiceComputer(ToolChoiceParam, discriminator="computer"): + """Indicates that the model should use a built-in tool to generate a response. `Learn more about + built-in tools `_. + + :ivar type: Required. COMPUTER. + :vartype type: str or ~azure.ai.projects.models.COMPUTER + """ + + type: Literal[ToolChoiceParamType.COMPUTER] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. COMPUTER.""" + + @overload + def __init__( + self, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.type = ToolChoiceParamType.COMPUTER # type: ignore + + +class ToolChoiceComputerUse(ToolChoiceParam, discriminator="computer_use"): + """Indicates that the model should use a built-in tool to generate a response. `Learn more about + built-in tools `_. + + :ivar type: Required. COMPUTER_USE. + :vartype type: str or ~azure.ai.projects.models.COMPUTER_USE + """ + + type: Literal[ToolChoiceParamType.COMPUTER_USE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. COMPUTER_USE.""" + + @overload + def __init__( + self, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.type = ToolChoiceParamType.COMPUTER_USE # type: ignore + + class ToolChoiceComputerUsePreview(ToolChoiceParam, discriminator="computer_use_preview"): """Indicates that the model should use a built-in tool to generate a response. `Learn more about built-in tools `_. @@ -14483,6 +14408,53 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ToolSearchToolParam(Tool, discriminator="tool_search"): + """Tool search tool. + + :ivar type: The type of the tool. Always ``tool_search``. Required. TOOL_SEARCH. + :vartype type: str or ~azure.ai.projects.models.TOOL_SEARCH + :ivar execution: Whether tool search is executed by the server or by the client. Known values + are: "server" and "client". + :vartype execution: str or ~azure.ai.projects.models.ToolSearchExecutionType + :ivar description: + :vartype description: str + :ivar parameters: + :vartype parameters: ~azure.ai.projects.models.EmptyModelParam + """ + + type: Literal[ToolType.TOOL_SEARCH] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the tool. Always ``tool_search``. Required. TOOL_SEARCH.""" + execution: Optional[Union[str, "_models.ToolSearchExecutionType"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Whether tool search is executed by the server or by the client. Known values are: \"server\" + and \"client\".""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + parameters: Optional["_models.EmptyModelParam"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + + @overload + def __init__( + self, + *, + execution: Optional[Union[str, "_models.ToolSearchExecutionType"]] = None, + description: Optional[str] = None, + parameters: Optional["_models.EmptyModelParam"] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.type = ToolType.TOOL_SEARCH # type: ignore + + class ToolUseFineTuningDataGenerationJobOptions( DataGenerationJobOptions, discriminator="tool_use" ): # pylint: disable=name-too-long @@ -15004,6 +14976,8 @@ class WebSearchPreviewTool(Tool, discriminator="web_search_preview"): for the search. One of ``low``, ``medium``, or ``high``. ``medium`` is the default. Known values are: "low", "medium", and "high". :vartype search_context_size: str or ~azure.ai.projects.models.SearchContextSize + :ivar search_content_types: + :vartype search_content_types: list[str or ~azure.ai.projects.models.SearchContentType] """ type: Literal[ToolType.WEB_SEARCH_PREVIEW] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore @@ -15018,6 +14992,9 @@ class WebSearchPreviewTool(Tool, discriminator="web_search_preview"): """High level guidance for the amount of context window space to use for the search. One of ``low``, ``medium``, or ``high``. ``medium`` is the default. Known values are: \"low\", \"medium\", and \"high\".""" + search_content_types: Optional[list[Union[str, "_models.SearchContentType"]]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) @overload def __init__( @@ -15025,6 +15002,7 @@ def __init__( *, user_location: Optional["_models.ApproximateLocation"] = None, search_context_size: Optional[Union[str, "_models.SearchContextSize"]] = None, + search_content_types: Optional[list[Union[str, "_models.SearchContentType"]]] = None, ) -> None: ... @overload diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py index a907ab276781..532e98b62daf 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py @@ -52,7 +52,7 @@ _AgentDefinitionOptInKeys.AGENT_ENDPOINT_V1_PREVIEW.value, _AgentDefinitionOptInKeys.CODE_AGENTS_V1_PREVIEW.value, _AgentDefinitionOptInKeys.EXTERNAL_AGENTS_V1_PREVIEW.value, - _FoundryFeaturesOptInKeys.AGENTS_OPTIMIZATION_V1_PREVIEW.value, + _FoundryFeaturesOptInKeys.AGENTS_OPTIMIZATION_V2_PREVIEW.value, ] ) diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py index 2ae3231aea70..f35e38282aeb 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py @@ -1328,7 +1328,7 @@ def build_beta_agents_cancel_optimization_job_request( # pylint: disable=name-t def build_beta_agents_delete_optimization_job_request( # pylint: disable=name-too-long - job_id: str, *, force: Optional[bool] = None, **kwargs: Any + job_id: str, **kwargs: Any ) -> HttpRequest: _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -1342,192 +1342,11 @@ def build_beta_agents_delete_optimization_job_request( # pylint: disable=name-t _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - if force is not None: - _params["force"] = _SERIALIZER.query("force", force, "bool") _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") return HttpRequest(method="DELETE", url=_url, params=_params, **kwargs) -def build_beta_agents_list_optimization_candidates_request( # pylint: disable=name-too-long - job_id: str, - *, - limit: Optional[int] = None, - order: Optional[Union[str, _models.PageOrder]] = None, - after: Optional[str] = None, - before: Optional[str] = None, - **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/agent_optimization_jobs/{jobId}/candidates" - path_format_arguments = { - "jobId": _SERIALIZER.url("job_id", job_id, "str"), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - if limit is not None: - _params["limit"] = _SERIALIZER.query("limit", limit, "int") - if order is not None: - _params["order"] = _SERIALIZER.query("order", order, "str") - if after is not None: - _params["after"] = _SERIALIZER.query("after", after, "str") - if before is not None: - _params["before"] = _SERIALIZER.query("before", before, "str") - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_beta_agents_get_optimization_candidate_request( # pylint: disable=name-too-long - job_id: str, candidate_id: str, **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/agent_optimization_jobs/{jobId}/candidates/{candidateId}" - path_format_arguments = { - "jobId": _SERIALIZER.url("job_id", job_id, "str"), - "candidateId": _SERIALIZER.url("candidate_id", candidate_id, "str"), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_beta_agents_get_optimization_candidate_config_request( # pylint: disable=name-too-long - job_id: str, candidate_id: str, **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/agent_optimization_jobs/{jobId}/candidates/{candidateId}/config" - path_format_arguments = { - "jobId": _SERIALIZER.url("job_id", job_id, "str"), - "candidateId": _SERIALIZER.url("candidate_id", candidate_id, "str"), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_beta_agents_get_optimization_candidate_results_request( # pylint: disable=name-too-long - job_id: str, candidate_id: str, **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/agent_optimization_jobs/{jobId}/candidates/{candidateId}/results" - path_format_arguments = { - "jobId": _SERIALIZER.url("job_id", job_id, "str"), - "candidateId": _SERIALIZER.url("candidate_id", candidate_id, "str"), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_beta_agents_get_candidate_file_request( # pylint: disable=name-too-long - job_id: str, candidate_id: str, *, path: str, **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) - accept = _headers.pop("Accept", "application/octet-stream") - - # Construct URL - _url = "/agent_optimization_jobs/{jobId}/candidates/{candidateId}/files" - path_format_arguments = { - "jobId": _SERIALIZER.url("job_id", job_id, "str"), - "candidateId": _SERIALIZER.url("candidate_id", candidate_id, "str"), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["path"] = _SERIALIZER.query("path", path, "str") - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_beta_agents_promote_candidate_request( # pylint: disable=name-too-long - job_id: str, candidate_id: str, **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/agent_optimization_jobs/{jobId}/candidates/{candidateId}:promote" - path_format_arguments = { - "jobId": _SERIALIZER.url("job_id", job_id, "str"), - "candidateId": _SERIALIZER.url("candidate_id", candidate_id, "str"), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) - - def build_beta_evaluation_taxonomies_get_request( # pylint: disable=name-too-long name: str, **kwargs: Any ) -> HttpRequest: @@ -3839,7 +3658,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def get(self, agent_name: str, **kwargs: Any) -> _models.AgentDetails: - """Retrieves the agent. + """Get an agent. + + Retrieves an agent definition by its unique name. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -3904,7 +3725,9 @@ def get(self, agent_name: str, **kwargs: Any) -> _models.AgentDetails: @distributed_trace def delete(self, agent_name: str, *, force: Optional[bool] = None, **kwargs: Any) -> _models.DeleteAgentResponse: - """Deletes an agent. For hosted agents, if any version has active sessions, the request is + """Delete an agent. + + Deletes an agent. For hosted agents, if any version has active sessions, the request is rejected with HTTP 409 unless ``force`` is set to true. When force is true, all associated sessions are cascade-deleted along with the agent and its versions. @@ -3985,7 +3808,9 @@ def list( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.AgentDetails"]: - """Returns the list of all agents. + """List agents. + + Returns a paged collection of agent resources. :keyword kind: Filter agents by kind. If not provided, all agents are returned. Known values are: "prompt", "hosted", "workflow", and "external". Default value is None. @@ -4082,7 +3907,9 @@ def create_version( blueprint_reference: Optional[_models.AgentBlueprintReference] = None, **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version. + """Create an agent version. + + Creates a new version for the specified agent and returns the created version resource. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -4117,7 +3944,9 @@ def create_version( def create_version( self, agent_name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version. + """Create an agent version. + + Creates a new version for the specified agent and returns the created version resource. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -4140,7 +3969,9 @@ def create_version( def create_version( self, agent_name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version. + """Create an agent version. + + Creates a new version for the specified agent and returns the created version resource. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -4171,7 +4002,9 @@ def create_version( blueprint_reference: Optional[_models.AgentBlueprintReference] = None, **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version. + """Create an agent version. + + Creates a new version for the specified agent and returns the created version resource. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -4287,7 +4120,9 @@ def create_version_from_manifest( description: Optional[str] = None, **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version from a manifest. + """Create an agent version from manifest. + + Imports the provided manifest to create a new version for the specified agent. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -4322,7 +4157,9 @@ def create_version_from_manifest( def create_version_from_manifest( self, agent_name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version from a manifest. + """Create an agent version from manifest. + + Imports the provided manifest to create a new version for the specified agent. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -4345,7 +4182,9 @@ def create_version_from_manifest( def create_version_from_manifest( self, agent_name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version from a manifest. + """Create an agent version from manifest. + + Imports the provided manifest to create a new version for the specified agent. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -4376,7 +4215,9 @@ def create_version_from_manifest( description: Optional[str] = None, **kwargs: Any ) -> _models.AgentVersionDetails: - """Create a new agent version from a manifest. + """Create an agent version from manifest. + + Imports the provided manifest to create a new version for the specified agent. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -4484,7 +4325,9 @@ def create_version_from_manifest( @distributed_trace def get_version(self, agent_name: str, agent_version: str, **kwargs: Any) -> _models.AgentVersionDetails: - """Retrieves a specific version of an agent. + """Get an agent version. + + Retrieves the specified version of an agent by its agent name and version identifier. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -4554,7 +4397,9 @@ def get_version(self, agent_name: str, agent_version: str, **kwargs: Any) -> _mo def delete_version( self, agent_name: str, agent_version: str, *, force: Optional[bool] = None, **kwargs: Any ) -> _models.DeleteAgentVersionResponse: - """Deletes a specific version of an agent. For hosted agents, if the version has active sessions, + """Delete an agent version. + + Deletes a specific version of an agent. For hosted agents, if the version has active sessions, the request is rejected with HTTP 409 unless ``force`` is set to true. When force is true, all sessions associated with this version are cascade-deleted. @@ -4639,7 +4484,9 @@ def list_versions( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.AgentVersionDetails"]: - """Returns the list of versions of an agent. + """List agent versions. + + Returns a paged collection of versions for the specified agent. :param agent_name: The name of the agent to retrieve versions for. Required. :type agent_name: str @@ -4745,6 +4592,8 @@ def __init__(self, *args, **kwargs) -> None: def get(self, id: str, **kwargs: Any) -> _models.EvaluationRule: """Get an evaluation rule. + Retrieves the specified evaluation rule and its configuration. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :return: EvaluationRule. The EvaluationRule is compatible with MutableMapping @@ -4806,6 +4655,8 @@ def get(self, id: str, **kwargs: Any) -> _models.EvaluationRule: def delete(self, id: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements """Delete an evaluation rule. + Removes the specified evaluation rule from the project. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :return: None @@ -4856,6 +4707,8 @@ def create_or_update( ) -> _models.EvaluationRule: """Create or update an evaluation rule. + Creates a new evaluation rule, or replaces the existing rule when the identifier matches. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :param evaluation_rule: Evaluation rule resource. Required. @@ -4874,6 +4727,8 @@ def create_or_update( ) -> _models.EvaluationRule: """Create or update an evaluation rule. + Creates a new evaluation rule, or replaces the existing rule when the identifier matches. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :param evaluation_rule: Evaluation rule resource. Required. @@ -4892,6 +4747,8 @@ def create_or_update( ) -> _models.EvaluationRule: """Create or update an evaluation rule. + Creates a new evaluation rule, or replaces the existing rule when the identifier matches. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :param evaluation_rule: Evaluation rule resource. Required. @@ -4910,6 +4767,8 @@ def create_or_update( ) -> _models.EvaluationRule: """Create or update an evaluation rule. + Creates a new evaluation rule, or replaces the existing rule when the identifier matches. + :param id: Unique identifier for the evaluation rule. Required. :type id: str :param evaluation_rule: Evaluation rule resource. Is one of the following types: @@ -4989,7 +4848,10 @@ def list( enabled: Optional[bool] = None, **kwargs: Any ) -> ItemPaged["_models.EvaluationRule"]: - """List all evaluation rules. + """List evaluation rules. + + Returns the evaluation rules configured for the project, optionally filtered by action type, + agent name, or enabled state. :keyword action_type: Filter by the type of evaluation rule. Known values are: "continuousEvaluation" and "humanEvaluationPreview". Default value is None. @@ -5105,7 +4967,10 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def _get(self, name: str, **kwargs: Any) -> _models.Connection: - """Get a connection by name, without populating connection credentials. + """Get a connection. + + Retrieves the specified connection and its configuration details without including credential + values. :param name: The friendly name of the connection, provided by the user. Required. :type name: str @@ -5171,7 +5036,9 @@ def _get(self, name: str, **kwargs: Any) -> _models.Connection: @distributed_trace def _get_with_credentials(self, name: str, **kwargs: Any) -> _models.Connection: - """Get a connection by name, with its connection credentials. + """Get a connection with credentials. + + Retrieves the specified connection together with its credential values. :param name: The friendly name of the connection, provided by the user. Required. :type name: str @@ -5243,13 +5110,16 @@ def list( default_connection: Optional[bool] = None, **kwargs: Any ) -> ItemPaged["_models.Connection"]: - """List all connections in the project, without populating connection credentials. + """List connections. - :keyword connection_type: List connections of this specific type. Known values are: + Returns the connections available in the current project, optionally filtered by type or + default status. + + :keyword connection_type: Lists connections of this specific type. Known values are: "AzureOpenAI", "AzureBlob", "AzureStorageAccount", "CognitiveSearch", "CosmosDB", "ApiKey", "AppConfig", "AppInsights", "CustomKeys", and "RemoteTool_Preview". Default value is None. :paramtype connection_type: str or ~azure.ai.projects.models.ConnectionType - :keyword default_connection: List connections that are default connections. Default value is + :keyword default_connection: Lists connections that are default connections. Default value is None. :paramtype default_connection: bool :return: An iterator like instance of Connection @@ -5358,7 +5228,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def list_versions(self, name: str, **kwargs: Any) -> ItemPaged["_models.DatasetVersion"]: - """List all versions of the given DatasetVersion. + """List versions. + + List all versions of the given DatasetVersion. :param name: The name of the resource. Required. :type name: str @@ -5449,7 +5321,9 @@ def get_next(next_link=None): @distributed_trace def list(self, **kwargs: Any) -> ItemPaged["_models.DatasetVersion"]: - """List the latest version of each DatasetVersion. + """List latest versions. + + List the latest version of each DatasetVersion. :return: An iterator like instance of DatasetVersion :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.DatasetVersion] @@ -5537,7 +5411,9 @@ def get_next(next_link=None): @distributed_trace def get(self, name: str, version: str, **kwargs: Any) -> _models.DatasetVersion: - """Get the specific version of the DatasetVersion. The service returns 404 Not Found error if the + """Get a version. + + Get the specific version of the DatasetVersion. The service returns 404 Not Found error if the DatasetVersion does not exist. :param name: The name of the resource. Required. @@ -5602,7 +5478,9 @@ def get(self, name: str, version: str, **kwargs: Any) -> _models.DatasetVersion: @distributed_trace def delete(self, name: str, version: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements - """Delete the specific version of the DatasetVersion. The service returns 204 No Content if the + """Delete a version. + + Delete the specific version of the DatasetVersion. The service returns 204 No Content if the DatasetVersion was deleted successfully or if the DatasetVersion does not exist. :param name: The name of the resource. Required. @@ -5662,7 +5540,9 @@ def create_or_update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + """Create or update a version. + + Create a new or update an existing DatasetVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -5688,7 +5568,9 @@ def create_or_update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + """Create or update a version. + + Create a new or update an existing DatasetVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -5714,7 +5596,9 @@ def create_or_update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + """Create or update a version. + + Create a new or update an existing DatasetVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -5734,7 +5618,9 @@ def create_or_update( def create_or_update( self, name: str, version: str, dataset_version: Union[_models.DatasetVersion, JSON, IO[bytes]], **kwargs: Any ) -> _models.DatasetVersion: - """Create a new or update an existing DatasetVersion with the given version id. + """Create or update a version. + + Create a new or update an existing DatasetVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -5819,7 +5705,9 @@ def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of a dataset for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified dataset version. :param name: The name of the resource. Required. :type name: str @@ -5845,7 +5733,9 @@ def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of a dataset for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified dataset version. :param name: The name of the resource. Required. :type name: str @@ -5871,7 +5761,9 @@ def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of a dataset for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified dataset version. :param name: The name of the resource. Required. :type name: str @@ -5895,7 +5787,9 @@ def pending_upload( pending_upload_request: Union[_models.PendingUploadRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of a dataset for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified dataset version. :param name: The name of the resource. Required. :type name: str @@ -5973,7 +5867,9 @@ def pending_upload( @distributed_trace def get_credentials(self, name: str, version: str, **kwargs: Any) -> _models.DatasetCredential: - """Get the SAS credential to access the storage account associated with a Dataset version. + """Get dataset credentials. + + Gets the SAS credential to access the storage account associated with a Dataset version. :param name: The name of the resource. Required. :type name: str @@ -6055,7 +5951,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def get(self, name: str, **kwargs: Any) -> _models.Deployment: - """Get a deployed model. + """Get a deployment. + + Gets a deployed model. :param name: Name of the deployment. Required. :type name: str @@ -6128,7 +6026,10 @@ def list( deployment_type: Optional[Union[str, _models.DeploymentType]] = None, **kwargs: Any ) -> ItemPaged["_models.Deployment"]: - """List all deployed models in the project. + """List deployments. + + Returns the deployed models available in the current project, optionally filtered by publisher, + model name, or deployment type. :keyword model_publisher: Model publisher to filter models by. Default value is None. :paramtype model_publisher: str @@ -6245,7 +6146,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def list_versions(self, name: str, **kwargs: Any) -> ItemPaged["_models.Index"]: - """List all versions of the given Index. + """List versions. + + List all versions of the given Index. :param name: The name of the resource. Required. :type name: str @@ -6336,7 +6239,9 @@ def get_next(next_link=None): @distributed_trace def list(self, **kwargs: Any) -> ItemPaged["_models.Index"]: - """List the latest version of each Index. + """List latest versions. + + List the latest version of each Index. :return: An iterator like instance of Index :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.Index] @@ -6424,7 +6329,9 @@ def get_next(next_link=None): @distributed_trace def get(self, name: str, version: str, **kwargs: Any) -> _models.Index: - """Get the specific version of the Index. The service returns 404 Not Found error if the Index + """Get a version. + + Get the specific version of the Index. The service returns 404 Not Found error if the Index does not exist. :param name: The name of the resource. Required. @@ -6489,7 +6396,9 @@ def get(self, name: str, version: str, **kwargs: Any) -> _models.Index: @distributed_trace def delete(self, name: str, version: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements - """Delete the specific version of the Index. The service returns 204 No Content if the Index was + """Delete a version. + + Delete the specific version of the Index. The service returns 204 No Content if the Index was deleted successfully or if the Index does not exist. :param name: The name of the resource. Required. @@ -6549,7 +6458,9 @@ def create_or_update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.Index: - """Create a new or update an existing Index with the given version id. + """Create or update a version. + + Create a new or update an existing Index with the given version id. :param name: The name of the resource. Required. :type name: str @@ -6569,7 +6480,9 @@ def create_or_update( def create_or_update( self, name: str, version: str, index: JSON, *, content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.Index: - """Create a new or update an existing Index with the given version id. + """Create or update a version. + + Create a new or update an existing Index with the given version id. :param name: The name of the resource. Required. :type name: str @@ -6595,7 +6508,9 @@ def create_or_update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.Index: - """Create a new or update an existing Index with the given version id. + """Create or update a version. + + Create a new or update an existing Index with the given version id. :param name: The name of the resource. Required. :type name: str @@ -6615,7 +6530,9 @@ def create_or_update( def create_or_update( self, name: str, version: str, index: Union[_models.Index, JSON, IO[bytes]], **kwargs: Any ) -> _models.Index: - """Create a new or update an existing Index with the given version id. + """Create or update a version. + + Create a new or update an existing Index with the given version id. :param name: The name of the resource. Required. :type name: str @@ -6691,7 +6608,7 @@ def create_or_update( return deserialized # type: ignore -class BetaAgentsOperations: # pylint: disable=too-many-public-methods +class BetaAgentsOperations: """ .. warning:: **DO NOT** instantiate this class directly. @@ -6718,7 +6635,9 @@ def patch_agent_details( agent_card: Optional[_models.AgentCard] = None, **kwargs: Any ) -> _models.AgentDetails: - """Updates an agent endpoint. + """Update an agent endpoint. + + Applies a merge-patch update to the specified agent endpoint configuration. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -6738,7 +6657,9 @@ def patch_agent_details( def patch_agent_details( self, agent_name: str, body: JSON, *, content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.AgentDetails: - """Updates an agent endpoint. + """Update an agent endpoint. + + Applies a merge-patch update to the specified agent endpoint configuration. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -6756,7 +6677,9 @@ def patch_agent_details( def patch_agent_details( self, agent_name: str, body: IO[bytes], *, content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.AgentDetails: - """Updates an agent endpoint. + """Update an agent endpoint. + + Applies a merge-patch update to the specified agent endpoint configuration. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -6780,7 +6703,9 @@ def patch_agent_details( agent_card: Optional[_models.AgentCard] = None, **kwargs: Any ) -> _models.AgentDetails: - """Updates an agent endpoint. + """Update an agent endpoint. + + Applies a merge-patch update to the specified agent endpoint configuration. :param agent_name: The name of the agent to retrieve. Required. :type agent_name: str @@ -6871,7 +6796,12 @@ def create_version_from_code( code_zip_sha256: str, **kwargs: Any ) -> _models.AgentVersionDetails: - """create_version_from_code. + """Create an agent version from code. + + Creates a new agent version from code. Uploads the code zip and creates a new version for an + existing agent. The SHA-256 hex digest of the zip is provided in the ``x-ms-code-zip-sha256`` + header for integrity and dedup. The request body is multipart/form-data with a JSON metadata + part and a binary code part (part order is irrelevant). Maximum upload size is 250 MB. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -6894,7 +6824,12 @@ def create_version_from_code( def create_version_from_code( self, agent_name: str, content: JSON, *, code_zip_sha256: str, **kwargs: Any ) -> _models.AgentVersionDetails: - """create_version_from_code. + """Create an agent version from code. + + Creates a new agent version from code. Uploads the code zip and creates a new version for an + existing agent. The SHA-256 hex digest of the zip is provided in the ``x-ms-code-zip-sha256`` + header for integrity and dedup. The request body is multipart/form-data with a JSON metadata + part and a binary code part (part order is irrelevant). Maximum upload size is 250 MB. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -6922,7 +6857,12 @@ def create_version_from_code( code_zip_sha256: str, **kwargs: Any ) -> _models.AgentVersionDetails: - """create_version_from_code. + """Create an agent version from code. + + Creates a new agent version from code. Uploads the code zip and creates a new version for an + existing agent. The SHA-256 hex digest of the zip is provided in the ``x-ms-code-zip-sha256`` + header for integrity and dedup. The request body is multipart/form-data with a JSON metadata + part and a binary code part (part order is irrelevant). Maximum upload size is 250 MB. :param agent_name: The unique name that identifies the agent. Name can be used to retrieve/update/delete the agent. @@ -7004,7 +6944,9 @@ def create_version_from_code( @distributed_trace def download_code(self, agent_name: str, *, agent_version: Optional[str] = None, **kwargs: Any) -> Iterator[bytes]: - """Download the code zip for a code-based hosted agent. + """Download agent code. + + Downloads the code zip for a code-based hosted agent. Returns the previously-uploaded zip (``application/zip``). If ``agent_version`` is supplied, returns that version's code zip; otherwise @@ -7090,7 +7032,9 @@ def create_session( agent_session_id: Optional[str] = None, **kwargs: Any ) -> _models.AgentSessionResource: - """Creates a new session for an agent endpoint. The endpoint resolves the backing agent version + """Create a session. + + Creates a new session for an agent endpoint. The endpoint resolves the backing agent version from ``version_indicator`` and enforces session ownership using the provided isolation key for session-mutating operations. @@ -7122,7 +7066,9 @@ def create_session( content_type: str = "application/json", **kwargs: Any ) -> _models.AgentSessionResource: - """Creates a new session for an agent endpoint. The endpoint resolves the backing agent version + """Create a session. + + Creates a new session for an agent endpoint. The endpoint resolves the backing agent version from ``version_indicator`` and enforces session ownership using the provided isolation key for session-mutating operations. @@ -7151,7 +7097,9 @@ def create_session( content_type: str = "application/json", **kwargs: Any ) -> _models.AgentSessionResource: - """Creates a new session for an agent endpoint. The endpoint resolves the backing agent version + """Create a session. + + Creates a new session for an agent endpoint. The endpoint resolves the backing agent version from ``version_indicator`` and enforces session ownership using the provided isolation key for session-mutating operations. @@ -7181,7 +7129,9 @@ def create_session( agent_session_id: Optional[str] = None, **kwargs: Any ) -> _models.AgentSessionResource: - """Creates a new session for an agent endpoint. The endpoint resolves the backing agent version + """Create a session. + + Creates a new session for an agent endpoint. The endpoint resolves the backing agent version from ``version_indicator`` and enforces session ownership using the provided isolation key for session-mutating operations. @@ -7276,7 +7226,9 @@ def create_session( def get_session( self, agent_name: str, session_id: str, *, user_isolation_key: Optional[str] = None, **kwargs: Any ) -> _models.AgentSessionResource: - """Retrieves a session by ID. + """Get a session. + + Retrieves the details of a hosted agent session by agent name and session identifier. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -7350,7 +7302,9 @@ def get_session( def delete_session( # pylint: disable=inconsistent-return-statements self, agent_name: str, session_id: str, *, user_isolation_key: Optional[str] = None, **kwargs: Any ) -> None: - """Deletes a session synchronously. Returns 204 No Content when the session is deleted or does not + """Delete a session. + + Deletes a session synchronously. Returns 204 No Content when the session is deleted or does not exist. :param agent_name: The name of the agent. Required. @@ -7412,7 +7366,10 @@ def delete_session( # pylint: disable=inconsistent-return-statements def stop_session( # pylint: disable=inconsistent-return-statements self, agent_name: str, session_id: str, **kwargs: Any ) -> None: - """Stops a session. Returns 204 No Content when the stop succeeds. + """Stop a session. + + Terminates the specified hosted agent session and returns 204 No Content when the request + succeeds. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -7476,7 +7433,9 @@ def list_sessions( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.AgentSessionResource"]: - """Returns a list of sessions for the specified agent. + """List sessions for an agent. + + Returns a paged collection of sessions associated with the specified agent endpoint. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -7568,7 +7527,9 @@ def get_next(_continuation_token=None): def get_session_log_stream( self, agent_name: str, agent_version: str, session_id: str, **kwargs: Any ) -> _models.SessionLogEvent: - """Streams console logs (stdout / stderr) for a specific hosted agent session + """Stream console logs for a hosted agent session. + + Streams console logs (stdout / stderr) for a specific hosted agent session as a Server-Sent Events (SSE) stream. Each SSE frame contains: @@ -7676,8 +7637,10 @@ def _upload_session_file( user_isolation_key: Optional[str] = None, **kwargs: Any ) -> _models.SessionFileWriteResult: - """Upload a file to the session sandbox via binary stream. Maximum file size is 50 MB. Uploads - exceeding this limit return 413 Payload Too Large. + """Upload a session file. + + Uploads binary file content to the specified path in the session sandbox. The service stores + the file relative to the session home directory and rejects payloads larger than 50 MB. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -7768,7 +7731,10 @@ def download_session_file( user_isolation_key: Optional[str] = None, **kwargs: Any ) -> Iterator[bytes]: - """Download a file from the session sandbox as a binary stream. + """Download a session file. + + Downloads the file at the specified sandbox path as a binary stream. The path is resolved + relative to the session home directory. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -7852,9 +7818,11 @@ def list_session_files( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.SessionDirectoryEntry"]: - """List files and directories at a given path in the session sandbox. Returns only the immediate - children of the specified directory (non-recursive). If path is not provided, lists the session - home directory. + """List session files. + + Returns files and directories at the specified path in the session sandbox. The response + includes only the immediate children of the target directory and defaults to the session home + directory when no path is supplied. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -7960,8 +7928,10 @@ def delete_session_file( # pylint: disable=inconsistent-return-statements user_isolation_key: Optional[str] = None, **kwargs: Any ) -> None: - """Delete a file or directory from the session sandbox. If ``recursive`` is false (default) and - the target is a non-empty directory, the API returns 409 Conflict. + """Delete a session file. + + Deletes the specified file or directory from the session sandbox. When ``recursive`` is false, + deleting a non-empty directory returns 409 Conflict. :param agent_name: The name of the agent. Required. :type agent_name: str @@ -8029,7 +7999,7 @@ def delete_session_file( # pylint: disable=inconsistent-return-statements @overload def create_optimization_job( self, - inputs: _models.OptimizationJobInputs, + job: _models.OptimizationJob, *, operation_id: Optional[str] = None, content_type: str = "application/json", @@ -8040,8 +8010,8 @@ def create_optimization_job( Create an optimization job. Returns 201 with the queued job. Honours ``Operation-Id`` for idempotent retry. - :param inputs: The optimization job inputs. Required. - :type inputs: ~azure.ai.projects.models.OptimizationJobInputs + :param job: The job to create. Required. + :type job: ~azure.ai.projects.models.OptimizationJob :keyword operation_id: Client-generated unique ID for idempotent retries. When absent, the server creates the job unconditionally. Default value is None. :paramtype operation_id: str @@ -8055,15 +8025,15 @@ def create_optimization_job( @overload def create_optimization_job( - self, inputs: JSON, *, operation_id: Optional[str] = None, content_type: str = "application/json", **kwargs: Any + self, job: JSON, *, operation_id: Optional[str] = None, content_type: str = "application/json", **kwargs: Any ) -> _models.OptimizationJob: """Creates an agent optimization job. Create an optimization job. Returns 201 with the queued job. Honours ``Operation-Id`` for idempotent retry. - :param inputs: The optimization job inputs. Required. - :type inputs: JSON + :param job: The job to create. Required. + :type job: JSON :keyword operation_id: Client-generated unique ID for idempotent retries. When absent, the server creates the job unconditionally. Default value is None. :paramtype operation_id: str @@ -8078,7 +8048,7 @@ def create_optimization_job( @overload def create_optimization_job( self, - inputs: IO[bytes], + job: IO[bytes], *, operation_id: Optional[str] = None, content_type: str = "application/json", @@ -8089,8 +8059,8 @@ def create_optimization_job( Create an optimization job. Returns 201 with the queued job. Honours ``Operation-Id`` for idempotent retry. - :param inputs: The optimization job inputs. Required. - :type inputs: IO[bytes] + :param job: The job to create. Required. + :type job: IO[bytes] :keyword operation_id: Client-generated unique ID for idempotent retries. When absent, the server creates the job unconditionally. Default value is None. :paramtype operation_id: str @@ -8104,20 +8074,16 @@ def create_optimization_job( @distributed_trace def create_optimization_job( - self, - inputs: Union[_models.OptimizationJobInputs, JSON, IO[bytes]], - *, - operation_id: Optional[str] = None, - **kwargs: Any + self, job: Union[_models.OptimizationJob, JSON, IO[bytes]], *, operation_id: Optional[str] = None, **kwargs: Any ) -> _models.OptimizationJob: """Creates an agent optimization job. Create an optimization job. Returns 201 with the queued job. Honours ``Operation-Id`` for idempotent retry. - :param inputs: The optimization job inputs. Is one of the following types: - OptimizationJobInputs, JSON, IO[bytes] Required. - :type inputs: ~azure.ai.projects.models.OptimizationJobInputs or JSON or IO[bytes] + :param job: The job to create. Is one of the following types: OptimizationJob, JSON, IO[bytes] + Required. + :type job: ~azure.ai.projects.models.OptimizationJob or JSON or IO[bytes] :keyword operation_id: Client-generated unique ID for idempotent retries. When absent, the server creates the job unconditionally. Default value is None. :paramtype operation_id: str @@ -8141,10 +8107,10 @@ def create_optimization_job( content_type = content_type or "application/json" _content = None - if isinstance(inputs, (IOBase, bytes)): - _content = inputs + if isinstance(job, (IOBase, bytes)): + _content = job else: - _content = json.dumps(inputs, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(job, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_beta_agents_create_optimization_job_request( operation_id=operation_id, @@ -8198,7 +8164,7 @@ def create_optimization_job( def get_optimization_job(self, job_id: str, **kwargs: Any) -> _models.OptimizationJob: """Get info about an agent optimization job. - Get an optimization job by id. Returns 202 while in progress, 200 when terminal. + Get an optimization job by id. :param job_id: The ID of the job. Required. :type job_id: str @@ -8238,7 +8204,7 @@ def get_optimization_job(self, job_id: str, **kwargs: Any) -> _models.Optimizati response = pipeline_response.http_response - if response.status_code not in [200, 202]: + if response.status_code not in [200]: if _stream: try: response.read() # Load the body in memory and close the socket @@ -8274,7 +8240,7 @@ def list_optimization_jobs( status: Optional[Union[str, _models.JobStatus]] = None, agent_name: Optional[str] = None, **kwargs: Any - ) -> ItemPaged["_models.OptimizationJob"]: + ) -> ItemPaged["_models.OptimizationJobListItem"]: """Returns a list of agent optimization jobs. List optimization jobs. Supports cursor pagination and optional status / agent_name filters. @@ -8298,14 +8264,14 @@ def list_optimization_jobs( :paramtype status: str or ~azure.ai.projects.models.JobStatus :keyword agent_name: Filter to jobs targeting this agent name. Default value is None. :paramtype agent_name: str - :return: An iterator like instance of OptimizationJob - :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.OptimizationJob] + :return: An iterator like instance of OptimizationJobListItem + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.OptimizationJobListItem] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[List[_models.OptimizationJob]] = kwargs.pop("cls", None) + cls: ClsType[List[_models.OptimizationJobListItem]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -8337,7 +8303,7 @@ def prepare_request(_continuation_token=None): def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - List[_models.OptimizationJob], + List[_models.OptimizationJobListItem], deserialized.get("data", []), ) if cls: @@ -8369,7 +8335,8 @@ def get_next(_continuation_token=None): def cancel_optimization_job(self, job_id: str, **kwargs: Any) -> _models.OptimizationJob: """Cancels an agent optimization job. - Request cancellation. Idempotent on terminal states. + Request cancellation of a running or queued job. Returns an error if the job is already in a + terminal state. :param job_id: The ID of the job to cancel. Required. :type job_id: str @@ -8434,7 +8401,7 @@ def cancel_optimization_job(self, job_id: str, **kwargs: Any) -> _models.Optimiz @distributed_trace def delete_optimization_job( # pylint: disable=inconsistent-return-statements - self, job_id: str, *, force: Optional[bool] = None, **kwargs: Any + self, job_id: str, **kwargs: Any ) -> None: """Deletes an agent optimization job. @@ -8442,9 +8409,6 @@ def delete_optimization_job( # pylint: disable=inconsistent-return-statements :param job_id: The ID of the job to delete. Required. :type job_id: str - :keyword force: When true, force-delete even if the job is in a non-terminal state. Default - value is None. - :paramtype force: bool :return: None :rtype: None :raises ~azure.core.exceptions.HttpResponseError: @@ -8464,7 +8428,6 @@ def delete_optimization_job( # pylint: disable=inconsistent-return-statements _request = build_beta_agents_delete_optimization_job_request( job_id=job_id, - force=force, api_version=self._config.api_version, headers=_headers, params=_params, @@ -8492,6 +8455,9 @@ def delete_optimization_job( # pylint: disable=inconsistent-return-statements if cls: return cls(pipeline_response, None, {}) # type: ignore +<<<<<<< HEAD +<<<<<<< HEAD +======= @distributed_trace def list_optimization_candidates( self, @@ -8499,10 +8465,9 @@ def list_optimization_candidates( *, limit: Optional[int] = None, order: Optional[Union[str, _models.PageOrder]] = None, - after: Optional[str] = None, before: Optional[str] = None, **kwargs: Any - ) -> _models.AgentsPagedResultOptimizationCandidate: + ) -> ItemPaged["_models.OptimizationCandidate"]: """Returns a list of candidates for an optimization job. List candidates produced by a job. @@ -8517,23 +8482,21 @@ def list_optimization_candidates( ascending order and``desc`` for descending order. Known values are: "asc" and "desc". Default value is None. :paramtype order: str or ~azure.ai.projects.models.PageOrder - :keyword after: A cursor for use in pagination. ``after`` is an object ID that defines your - place in the list. - For instance, if you make a list request and receive 100 objects, ending with obj_foo, your - subsequent call can include after=obj_foo in order to fetch the next page of the list. Default - value is None. - :paramtype after: str :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. Default value is None. :paramtype before: str - :return: AgentsPagedResultOptimizationCandidate. The AgentsPagedResultOptimizationCandidate is - compatible with MutableMapping - :rtype: ~azure.ai.projects.models.AgentsPagedResultOptimizationCandidate + :return: An iterator like instance of OptimizationCandidate + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.OptimizationCandidate] :raises ~azure.core.exceptions.HttpResponseError: """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[_models.OptimizationCandidate]] = kwargs.pop("cls", None) + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, @@ -8542,56 +8505,54 @@ def list_optimization_candidates( } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[_models.AgentsPagedResultOptimizationCandidate] = kwargs.pop("cls", None) + def prepare_request(_continuation_token=None): - _request = build_beta_agents_list_optimization_candidates_request( - job_id=job_id, - limit=limit, - order=order, - after=after, - before=before, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) + _request = build_beta_agents_list_optimization_candidates_request( + job_id=job_id, + limit=limit, + order=order, + after=_continuation_token, + before=before, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + return _request - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + List[_models.OptimizationCandidate], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, iter(list_of_elem) - response = pipeline_response.http_response + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) - if response.status_code not in [200]: - if _stream: - try: - response.read() # Load the body in memory and close the socket - except (StreamConsumedError, StreamClosedError): - pass - map_error(status_code=response.status_code, response=response, error_map=error_map) - error = _failsafe_deserialize( - _models.ApiErrorResponse, - response, + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs ) - raise HttpResponseError(response=response, model=error) + response = pipeline_response.http_response - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.AgentsPagedResultOptimizationCandidate, response.json()) + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore + return pipeline_response - return deserialized # type: ignore + return ItemPaged(get_next, extract_data) @distributed_trace def get_optimization_candidate(self, job_id: str, candidate_id: str, **kwargs: Any) -> _models.CandidateMetadata: @@ -9055,6 +9016,9 @@ def promote_candidate( return deserialized # type: ignore +>>>>>>> 5fce783a23 ([azure-ai-projects] Emit SDK from TypeSpec (commit 6aa89cf) (#47294)) +======= +>>>>>>> 5757d1c726 (Emit from latest TypeSpec, including new Agent Optimization methods (#47482)) class BetaEvaluationTaxonomiesOperations: """ @@ -9075,7 +9039,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def get(self, name: str, **kwargs: Any) -> _models.EvaluationTaxonomy: - """Get an evaluation run by name. + """Get an evaluation taxonomy. + + Retrieves the specified evaluation taxonomy. :param name: The name of the resource. Required. :type name: str @@ -9140,6 +9106,9 @@ def list( ) -> ItemPaged["_models.EvaluationTaxonomy"]: """List evaluation taxonomies. + Returns the evaluation taxonomies available in the project, optionally filtered by input name + or input type. + :keyword input_name: Filter by the evaluation input name. Default value is None. :paramtype input_name: str :keyword input_type: Filter by taxonomy input type. Default value is None. @@ -9232,7 +9201,9 @@ def get_next(next_link=None): @distributed_trace def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements - """Delete an evaluation taxonomy by name. + """Delete an evaluation taxonomy. + + Removes the specified evaluation taxonomy from the project. :param name: The name of the resource. Required. :type name: str @@ -9284,6 +9255,8 @@ def create( ) -> _models.EvaluationTaxonomy: """Create an evaluation taxonomy. + Creates or replaces the specified evaluation taxonomy with the provided definition. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -9302,6 +9275,8 @@ def create( ) -> _models.EvaluationTaxonomy: """Create an evaluation taxonomy. + Creates or replaces the specified evaluation taxonomy with the provided definition. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -9320,6 +9295,8 @@ def create( ) -> _models.EvaluationTaxonomy: """Create an evaluation taxonomy. + Creates or replaces the specified evaluation taxonomy with the provided definition. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -9338,6 +9315,8 @@ def create( ) -> _models.EvaluationTaxonomy: """Create an evaluation taxonomy. + Creates or replaces the specified evaluation taxonomy with the provided definition. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, @@ -9414,6 +9393,8 @@ def update( ) -> _models.EvaluationTaxonomy: """Update an evaluation taxonomy. + Update an evaluation taxonomy. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -9432,6 +9413,8 @@ def update( ) -> _models.EvaluationTaxonomy: """Update an evaluation taxonomy. + Update an evaluation taxonomy. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -9450,6 +9433,8 @@ def update( ) -> _models.EvaluationTaxonomy: """Update an evaluation taxonomy. + Update an evaluation taxonomy. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Required. @@ -9468,6 +9453,8 @@ def update( ) -> _models.EvaluationTaxonomy: """Update an evaluation taxonomy. + Update an evaluation taxonomy. + :param name: The name of the evaluation taxonomy. Required. :type name: str :param taxonomy: The evaluation taxonomy. Is one of the following types: EvaluationTaxonomy, @@ -9565,7 +9552,9 @@ def list_versions( limit: Optional[int] = None, **kwargs: Any ) -> ItemPaged["_models.EvaluatorVersion"]: - """List all versions of the given evaluator. + """List evaluator versions. + + Returns the available versions for the specified evaluator. :param name: The name of the resource. Required. :type name: str @@ -9671,7 +9660,9 @@ def list( limit: Optional[int] = None, **kwargs: Any ) -> ItemPaged["_models.EvaluatorVersion"]: - """List the latest version of each evaluator. + """List latest evaluator versions. + + Lists the latest version of each evaluator. :keyword type: Filter evaluators by type. Possible values: 'all', 'custom', 'builtin'. Is one of the following types: Literal["builtin"], Literal["custom"], Literal["all"], str Default @@ -9768,8 +9759,9 @@ def get_next(next_link=None): @distributed_trace def get_version(self, name: str, version: str, **kwargs: Any) -> _models.EvaluatorVersion: - """Get the specific version of the EvaluatorVersion. The service returns 404 Not Found error if - the EvaluatorVersion does not exist. + """Get an evaluator version. + + Retrieves the specified evaluator version, returning 404 if it does not exist. :param name: The name of the resource. Required. :type name: str @@ -9835,8 +9827,9 @@ def get_version(self, name: str, version: str, **kwargs: Any) -> _models.Evaluat def delete_version( # pylint: disable=inconsistent-return-statements self, name: str, version: str, **kwargs: Any ) -> None: - """Delete the specific version of the EvaluatorVersion. The service returns 204 No Content if the - EvaluatorVersion was deleted successfully or if the EvaluatorVersion does not exist. + """Delete an evaluator version. + + Removes the specified evaluator version. Returns 204 whether the version existed or not. :param name: The name of the resource. Required. :type name: str @@ -9894,7 +9887,9 @@ def create_version( content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Create a new EvaluatorVersion with auto incremented version id. + """Create an evaluator version. + + Creates a new evaluator version with an auto-incremented version identifier. :param name: The name of the resource. Required. :type name: str @@ -9912,7 +9907,9 @@ def create_version( def create_version( self, name: str, evaluator_version: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Create a new EvaluatorVersion with auto incremented version id. + """Create an evaluator version. + + Creates a new evaluator version with an auto-incremented version identifier. :param name: The name of the resource. Required. :type name: str @@ -9930,7 +9927,9 @@ def create_version( def create_version( self, name: str, evaluator_version: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Create a new EvaluatorVersion with auto incremented version id. + """Create an evaluator version. + + Creates a new evaluator version with an auto-incremented version identifier. :param name: The name of the resource. Required. :type name: str @@ -9948,7 +9947,9 @@ def create_version( def create_version( self, name: str, evaluator_version: Union[_models.EvaluatorVersion, JSON, IO[bytes]], **kwargs: Any ) -> _models.EvaluatorVersion: - """Create a new EvaluatorVersion with auto incremented version id. + """Create an evaluator version. + + Creates a new evaluator version with an auto-incremented version identifier. :param name: The name of the resource. Required. :type name: str @@ -10030,7 +10031,9 @@ def update_version( content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + """Update an evaluator version. + + Updates the specified evaluator version in place. :param name: The name of the resource. Required. :type name: str @@ -10050,7 +10053,9 @@ def update_version( def update_version( self, name: str, version: str, evaluator_version: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + """Update an evaluator version. + + Updates the specified evaluator version in place. :param name: The name of the resource. Required. :type name: str @@ -10076,7 +10081,9 @@ def update_version( content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + """Update an evaluator version. + + Updates the specified evaluator version in place. :param name: The name of the resource. Required. :type name: str @@ -10100,7 +10107,9 @@ def update_version( evaluator_version: Union[_models.EvaluatorVersion, JSON, IO[bytes]], **kwargs: Any ) -> _models.EvaluatorVersion: - """Update an existing EvaluatorVersion with the given version id. + """Update an evaluator version. + + Updates the specified evaluator version in place. :param name: The name of the resource. Required. :type name: str @@ -10185,7 +10194,10 @@ def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of an evaluator for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified evaluator + version. :param name: Required. :type name: str @@ -10211,7 +10223,10 @@ def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of an evaluator for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified evaluator + version. :param name: Required. :type name: str @@ -10237,7 +10252,10 @@ def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of an evaluator for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified evaluator + version. :param name: Required. :type name: str @@ -10261,7 +10279,10 @@ def pending_upload( pending_upload_request: Union[_models.PendingUploadRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.PendingUploadResponse: - """Start a new or get an existing pending upload of an evaluator for a specific version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified evaluator + version. :param name: Required. :type name: str @@ -10351,7 +10372,10 @@ def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get the SAS credential to access the storage account associated with an Evaluator version. + """Get evaluator credentials. + + Retrieves SAS credentials for accessing the storage account associated with the specified + evaluator version. :param name: Required. :type name: str @@ -10377,7 +10401,10 @@ def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get the SAS credential to access the storage account associated with an Evaluator version. + """Get evaluator credentials. + + Retrieves SAS credentials for accessing the storage account associated with the specified + evaluator version. :param name: Required. :type name: str @@ -10403,7 +10430,10 @@ def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get the SAS credential to access the storage account associated with an Evaluator version. + """Get evaluator credentials. + + Retrieves SAS credentials for accessing the storage account associated with the specified + evaluator version. :param name: Required. :type name: str @@ -10427,7 +10457,10 @@ def get_credentials( credential_request: Union[_models.EvaluatorCredentialRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.DatasetCredential: - """Get the SAS credential to access the storage account associated with an Evaluator version. + """Get evaluator credentials. + + Retrieves SAS credentials for accessing the storage account associated with the specified + evaluator version. :param name: Required. :type name: str @@ -10516,7 +10549,7 @@ def create_generation_job( content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorGenerationJob: - """Creates an evaluator generation job. + """Create an evaluator generation job. Creates an evaluator generation job. The service generates rubric-based evaluator definitions from the provided source materials asynchronously. @@ -10538,7 +10571,7 @@ def create_generation_job( def create_generation_job( self, job: JSON, *, operation_id: Optional[str] = None, content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorGenerationJob: - """Creates an evaluator generation job. + """Create an evaluator generation job. Creates an evaluator generation job. The service generates rubric-based evaluator definitions from the provided source materials asynchronously. @@ -10565,7 +10598,7 @@ def create_generation_job( content_type: str = "application/json", **kwargs: Any ) -> _models.EvaluatorGenerationJob: - """Creates an evaluator generation job. + """Create an evaluator generation job. Creates an evaluator generation job. The service generates rubric-based evaluator definitions from the provided source materials asynchronously. @@ -10591,7 +10624,7 @@ def create_generation_job( operation_id: Optional[str] = None, **kwargs: Any ) -> _models.EvaluatorGenerationJob: - """Creates an evaluator generation job. + """Create an evaluator generation job. Creates an evaluator generation job. The service generates rubric-based evaluator definitions from the provided source materials asynchronously. @@ -10677,7 +10710,7 @@ def create_generation_job( @distributed_trace def get_generation_job(self, job_id: str, **kwargs: Any) -> _models.EvaluatorGenerationJob: - """Get info about an evaluator generation job. + """Get an evaluator generation job. Gets the details of an evaluator generation job by its ID. @@ -10754,9 +10787,11 @@ def list_generation_jobs( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.EvaluatorGenerationJob"]: - """Returns a list of evaluator generation jobs. + """List evaluator generation jobs. - Returns a list of evaluator generation jobs. + Returns a list of evaluator generation jobs. The List API has up to a few seconds of + propagation delay, so a recently created job may not appear immediately; use the Get evaluator + generation job API with the job ID to retrieve a specific job without delay. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -10839,7 +10874,7 @@ def get_next(_continuation_token=None): @distributed_trace def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.EvaluatorGenerationJob: - """Cancels an evaluator generation job. + """Cancel an evaluator generation job. Cancels an evaluator generation job by its ID. @@ -10908,7 +10943,9 @@ def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.Evaluator def delete_generation_job( # pylint: disable=inconsistent-return-statements self, job_id: str, **kwargs: Any ) -> None: - """Deletes an evaluator generation job by its ID. Deletes the job record only; the generated + """Delete an evaluator generation job. + + Deletes an evaluator generation job by its ID. Deletes the job record only; the generated evaluator (if any) is preserved. :param job_id: The ID of the job to delete. Required. @@ -10981,7 +11018,9 @@ def __init__(self, *args, **kwargs) -> None: def generate( self, insight: _models.Insight, *, content_type: str = "application/json", **kwargs: Any ) -> _models.Insight: - """Generate Insights. + """Generate insights. + + Generates an insights report from the provided evaluation configuration. :param insight: Complete evaluation configuration including data source, evaluators, and result settings. Required. @@ -10996,7 +11035,9 @@ def generate( @overload def generate(self, insight: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.Insight: - """Generate Insights. + """Generate insights. + + Generates an insights report from the provided evaluation configuration. :param insight: Complete evaluation configuration including data source, evaluators, and result settings. Required. @@ -11011,7 +11052,9 @@ def generate(self, insight: JSON, *, content_type: str = "application/json", **k @overload def generate(self, insight: IO[bytes], *, content_type: str = "application/json", **kwargs: Any) -> _models.Insight: - """Generate Insights. + """Generate insights. + + Generates an insights report from the provided evaluation configuration. :param insight: Complete evaluation configuration including data source, evaluators, and result settings. Required. @@ -11026,7 +11069,9 @@ def generate(self, insight: IO[bytes], *, content_type: str = "application/json" @distributed_trace def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwargs: Any) -> _models.Insight: - """Generate Insights. + """Generate insights. + + Generates an insights report from the provided evaluation configuration. :param insight: Complete evaluation configuration including data source, evaluators, and result settings. Is one of the following types: Insight, JSON, IO[bytes] Required. @@ -11101,7 +11146,9 @@ def generate(self, insight: Union[_models.Insight, JSON, IO[bytes]], **kwargs: A @distributed_trace def get(self, insight_id: str, *, include_coordinates: Optional[bool] = None, **kwargs: Any) -> _models.Insight: - """Get a specific insight by Id. + """Get an insight. + + Retrieves the specified insight report and its results. :param insight_id: The unique identifier for the insights report. Required. :type insight_id: str @@ -11179,7 +11226,9 @@ def list( include_coordinates: Optional[bool] = None, **kwargs: Any ) -> ItemPaged["_models.Insight"]: - """List all insights in reverse chronological order (newest first). + """List insights. + + Returns insights in reverse chronological order, with the most recent entries first. :keyword type: Filter by the type of analysis. Known values are: "EvaluationRunClusterInsight", "AgentClusterInsight", and "EvaluationComparison". Default value is None. @@ -11317,6 +11366,8 @@ def create( ) -> _models.MemoryStoreDetails: """Create a memory store. + Creates a memory store resource with the provided configuration. + :keyword name: The name of the memory store. Required. :paramtype name: str :keyword definition: The memory store definition. Required. @@ -11340,6 +11391,8 @@ def create( ) -> _models.MemoryStoreDetails: """Create a memory store. + Creates a memory store resource with the provided configuration. + :param body: Required. :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. @@ -11356,6 +11409,8 @@ def create( ) -> _models.MemoryStoreDetails: """Create a memory store. + Creates a memory store resource with the provided configuration. + :param body: Required. :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. @@ -11379,6 +11434,8 @@ def create( ) -> _models.MemoryStoreDetails: """Create a memory store. + Creates a memory store resource with the provided configuration. + :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] :keyword name: The name of the memory store. Required. @@ -11477,6 +11534,8 @@ def update( ) -> _models.MemoryStoreDetails: """Update a memory store. + Updates the specified memory store with the supplied configuration changes. + :param name: The name of the memory store to update. Required. :type name: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. @@ -11498,6 +11557,8 @@ def update( ) -> _models.MemoryStoreDetails: """Update a memory store. + Updates the specified memory store with the supplied configuration changes. + :param name: The name of the memory store to update. Required. :type name: str :param body: Required. @@ -11516,6 +11577,8 @@ def update( ) -> _models.MemoryStoreDetails: """Update a memory store. + Updates the specified memory store with the supplied configuration changes. + :param name: The name of the memory store to update. Required. :type name: str :param body: Required. @@ -11540,6 +11603,8 @@ def update( ) -> _models.MemoryStoreDetails: """Update a memory store. + Updates the specified memory store with the supplied configuration changes. + :param name: The name of the memory store to update. Required. :type name: str :param body: Is either a JSON type or a IO[bytes] type. Required. @@ -11623,7 +11688,9 @@ def update( @distributed_trace def get(self, name: str, **kwargs: Any) -> _models.MemoryStoreDetails: - """Retrieve a memory store. + """Get a memory store. + + Retrieves the specified memory store and its current configuration. :param name: The name of the memory store to retrieve. Required. :type name: str @@ -11695,7 +11762,9 @@ def list( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.MemoryStoreDetails"]: - """List all memory stores. + """List memory stores. + + Returns the memory stores available to the caller. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -11780,6 +11849,8 @@ def get_next(_continuation_token=None): def delete(self, name: str, **kwargs: Any) -> _models.DeleteMemoryStoreResult: """Delete a memory store. + Deletes the specified memory store. + :param name: The name of the memory store to delete. Required. :type name: str :return: DeleteMemoryStoreResult. The DeleteMemoryStoreResult is compatible with MutableMapping @@ -11874,7 +11945,9 @@ def _search_memories( options: Optional[_models.MemorySearchOptions] = None, **kwargs: Any ) -> _models.MemoryStoreSearchResult: - """Search for relevant memories from a memory store based on conversation context. + """Search memories. + + Searches the specified memory store for memories relevant to the provided conversation context. :param name: The name of the memory store to search. Required. :type name: str @@ -12082,7 +12155,10 @@ def _begin_update_memories( update_delay: Optional[int] = None, **kwargs: Any ) -> LROPoller[_models.MemoryStoreUpdateCompletedResult]: - """Update memory store with conversation memories. + """Update memories. + + Starts an update that writes conversation memories into the specified memory store. The + operation returns a long-running status location for polling the update result. :param name: The name of the memory store to update. Required. :type name: str @@ -12172,7 +12248,9 @@ def get_long_running_output(pipeline_response): def delete_scope( self, name: str, *, scope: str, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryStoreDeleteScopeResult: - """Delete all memories associated with a specific scope from a memory store. + """Delete memories by scope. + + Deletes all memories in the specified memory store that are associated with the provided scope. :param name: The name of the memory store. Required. :type name: str @@ -12192,7 +12270,9 @@ def delete_scope( def delete_scope( self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryStoreDeleteScopeResult: - """Delete all memories associated with a specific scope from a memory store. + """Delete memories by scope. + + Deletes all memories in the specified memory store that are associated with the provided scope. :param name: The name of the memory store. Required. :type name: str @@ -12211,7 +12291,9 @@ def delete_scope( def delete_scope( self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryStoreDeleteScopeResult: - """Delete all memories associated with a specific scope from a memory store. + """Delete memories by scope. + + Deletes all memories in the specified memory store that are associated with the provided scope. :param name: The name of the memory store. Required. :type name: str @@ -12230,7 +12312,9 @@ def delete_scope( def delete_scope( self, name: str, body: Union[JSON, IO[bytes]] = _Unset, *, scope: str = _Unset, **kwargs: Any ) -> _models.MemoryStoreDeleteScopeResult: - """Delete all memories associated with a specific scope from a memory store. + """Delete memories by scope. + + Deletes all memories in the specified memory store that are associated with the provided scope. :param name: The name of the memory store. Required. :type name: str @@ -12325,7 +12409,9 @@ def create_memory( content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Create a memory item in a memory store. + """Create a memory item. + + Creates a memory item in the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -12349,7 +12435,9 @@ def create_memory( def create_memory( self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Create a memory item in a memory store. + """Create a memory item. + + Creates a memory item in the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -12367,7 +12455,9 @@ def create_memory( def create_memory( self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Create a memory item in a memory store. + """Create a memory item. + + Creates a memory item in the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -12392,7 +12482,9 @@ def create_memory( kind: Union[str, _models.MemoryItemKind] = _Unset, **kwargs: Any ) -> _models.MemoryItem: - """Create a memory item in a memory store. + """Create a memory item. + + Creates a memory item in the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -12488,7 +12580,9 @@ def create_memory( def update_memory( self, name: str, memory_id: str, *, content: str, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Update a memory item in a memory store. + """Update a memory item. + + Updates the specified memory item in the memory store. :param name: The name of the memory store. Required. :type name: str @@ -12508,7 +12602,9 @@ def update_memory( def update_memory( self, name: str, memory_id: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Update a memory item in a memory store. + """Update a memory item. + + Updates the specified memory item in the memory store. :param name: The name of the memory store. Required. :type name: str @@ -12528,7 +12624,9 @@ def update_memory( def update_memory( self, name: str, memory_id: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.MemoryItem: - """Update a memory item in a memory store. + """Update a memory item. + + Updates the specified memory item in the memory store. :param name: The name of the memory store. Required. :type name: str @@ -12548,7 +12646,9 @@ def update_memory( def update_memory( self, name: str, memory_id: str, body: Union[JSON, IO[bytes]] = _Unset, *, content: str = _Unset, **kwargs: Any ) -> _models.MemoryItem: - """Update a memory item in a memory store. + """Update a memory item. + + Updates the specified memory item in the memory store. :param name: The name of the memory store. Required. :type name: str @@ -12635,7 +12735,9 @@ def update_memory( @distributed_trace def get_memory(self, name: str, memory_id: str, **kwargs: Any) -> _models.MemoryItem: - """Retrieve a memory item from a memory store. + """Get a memory item. + + Retrieves the specified memory item from the memory store. :param name: The name of the memory store. Required. :type name: str @@ -12714,7 +12816,9 @@ def list_memories( content_type: str = "application/json", **kwargs: Any ) -> ItemPaged["_models.MemoryItem"]: - """List all memory items in a memory store. + """List memory items. + + Returns memory items from the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -12759,7 +12863,9 @@ def list_memories( content_type: str = "application/json", **kwargs: Any ) -> ItemPaged["_models.MemoryItem"]: - """List all memory items in a memory store. + """List memory items. + + Returns memory items from the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -12803,7 +12909,9 @@ def list_memories( content_type: str = "application/json", **kwargs: Any ) -> ItemPaged["_models.MemoryItem"]: - """List all memory items in a memory store. + """List memory items. + + Returns memory items from the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -12847,7 +12955,9 @@ def list_memories( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.MemoryItem"]: - """List all memory items in a memory store. + """List memory items. + + Returns memory items from the specified memory store. :param name: The name of the memory store. Required. :type name: str @@ -12964,7 +13074,9 @@ def get_next(_continuation_token=None): @distributed_trace def delete_memory(self, name: str, memory_id: str, **kwargs: Any) -> _models.DeleteMemoryResult: - """Delete a memory item from a memory store. + """Delete a memory item. + + Deletes the specified memory item from the memory store. :param name: The name of the memory store. Required. :type name: str @@ -13050,7 +13162,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def list_versions(self, name: str, **kwargs: Any) -> ItemPaged["_models.ModelVersion"]: - """List all versions of the given ModelVersion. + """List versions. + + List all versions of the given ModelVersion. :param name: The name of the resource. Required. :type name: str @@ -13141,7 +13255,9 @@ def get_next(next_link=None): @distributed_trace def list(self, **kwargs: Any) -> ItemPaged["_models.ModelVersion"]: - """List the latest version of each ModelVersion. + """List latest versions. + + List the latest version of each ModelVersion. :return: An iterator like instance of ModelVersion :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.ModelVersion] @@ -13229,8 +13345,9 @@ def get_next(next_link=None): @distributed_trace def get(self, name: str, version: str, **kwargs: Any) -> _models.ModelVersion: - """Get the specific version of the ModelVersion. The service returns 404 Not Found error if the - ModelVersion does not exist. + """Get a model version. + + Retrieves the specified model version, returning 404 if it does not exist. :param name: The name of the resource. Required. :type name: str @@ -13294,7 +13411,9 @@ def get(self, name: str, version: str, **kwargs: Any) -> _models.ModelVersion: @distributed_trace def delete(self, name: str, version: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements - """Delete the specific version of the ModelVersion. The service returns 200 OK if the ModelVersion + """Delete a model version. + + Delete the specific version of the ModelVersion. The service returns 200 OK if the ModelVersion was deleted successfully or if the ModelVersion does not exist. :param name: The name of the resource. Required. @@ -13354,7 +13473,9 @@ def update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.ModelVersion: - """Update an existing ModelVersion with the given version id. + """Update a model version. + + Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -13381,7 +13502,9 @@ def update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.ModelVersion: - """Update an existing ModelVersion with the given version id. + """Update a model version. + + Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -13408,7 +13531,9 @@ def update( content_type: str = "application/merge-patch+json", **kwargs: Any ) -> _models.ModelVersion: - """Update an existing ModelVersion with the given version id. + """Update a model version. + + Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -13433,7 +13558,9 @@ def update( model_version_update: Union[_models.UpdateModelVersionRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.ModelVersion: - """Update an existing ModelVersion with the given version id. + """Update a model version. + + Update an existing ModelVersion with the given version id. :param name: The name of the resource. Required. :type name: str @@ -13520,8 +13647,10 @@ def pending_create_version( content_type: str = "application/json", **kwargs: Any ) -> _models.CreateAsyncResponse: - """Creates a model version asynchronously with blob content validation. Returns 202 Accepted with - a Location header for polling. + """Create a model version async. + + Creates a model version asynchronously with blob content validation. Returns 202 Accepted with + a location header for polling the operation status. :param name: Name of the model. Required. :type name: str @@ -13541,8 +13670,10 @@ def pending_create_version( def pending_create_version( self, name: str, version: str, model_version: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.CreateAsyncResponse: - """Creates a model version asynchronously with blob content validation. Returns 202 Accepted with - a Location header for polling. + """Create a model version async. + + Creates a model version asynchronously with blob content validation. Returns 202 Accepted with + a location header for polling the operation status. :param name: Name of the model. Required. :type name: str @@ -13568,8 +13699,10 @@ def pending_create_version( content_type: str = "application/json", **kwargs: Any ) -> _models.CreateAsyncResponse: - """Creates a model version asynchronously with blob content validation. Returns 202 Accepted with - a Location header for polling. + """Create a model version async. + + Creates a model version asynchronously with blob content validation. Returns 202 Accepted with + a location header for polling the operation status. :param name: Name of the model. Required. :type name: str @@ -13589,8 +13722,10 @@ def pending_create_version( def pending_create_version( self, name: str, version: str, model_version: Union[_models.ModelVersion, JSON, IO[bytes]], **kwargs: Any ) -> _models.CreateAsyncResponse: - """Creates a model version asynchronously with blob content validation. Returns 202 Accepted with - a Location header for polling. + """Create a model version async. + + Creates a model version asynchronously with blob content validation. Returns 202 Accepted with + a location header for polling the operation status. :param name: Name of the model. Required. :type name: str @@ -13678,7 +13813,9 @@ def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.ModelPendingUploadResponse: - """Start or retrieve a pending upload for a model version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified model version. :param name: Name of the model. Required. :type name: str @@ -13705,7 +13842,9 @@ def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.ModelPendingUploadResponse: - """Start or retrieve a pending upload for a model version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified model version. :param name: Name of the model. Required. :type name: str @@ -13732,7 +13871,9 @@ def pending_upload( content_type: str = "application/json", **kwargs: Any ) -> _models.ModelPendingUploadResponse: - """Start or retrieve a pending upload for a model version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified model version. :param name: Name of the model. Required. :type name: str @@ -13757,7 +13898,9 @@ def pending_upload( pending_upload_request: Union[_models.ModelPendingUploadRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.ModelPendingUploadResponse: - """Start or retrieve a pending upload for a model version. + """Start a pending upload. + + Initiates a new pending upload or retrieves an existing one for the specified model version. :param name: Name of the model. Required. :type name: str @@ -13844,7 +13987,9 @@ def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get credentials for a model version asset. + """Get model asset credentials. + + Retrieves temporary credentials for accessing the storage backing the specified model version. :param name: Name of the model. Required. :type name: str @@ -13870,7 +14015,9 @@ def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get credentials for a model version asset. + """Get model asset credentials. + + Retrieves temporary credentials for accessing the storage backing the specified model version. :param name: Name of the model. Required. :type name: str @@ -13896,7 +14043,9 @@ def get_credentials( content_type: str = "application/json", **kwargs: Any ) -> _models.DatasetCredential: - """Get credentials for a model version asset. + """Get model asset credentials. + + Retrieves temporary credentials for accessing the storage backing the specified model version. :param name: Name of the model. Required. :type name: str @@ -13920,7 +14069,9 @@ def get_credentials( credential_request: Union[_models.ModelCredentialRequest, JSON, IO[bytes]], **kwargs: Any ) -> _models.DatasetCredential: - """Get credentials for a model version asset. + """Get model asset credentials. + + Retrieves temporary credentials for accessing the storage backing the specified model version. :param name: Name of the model. Required. :type name: str @@ -14015,7 +14166,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def get(self, name: str, **kwargs: Any) -> _models.RedTeam: - """Get a redteam by name. + """Get a redteam. + + Retrieves the specified redteam and its configuration. :param name: Identifier of the red team run. Required. :type name: str @@ -14076,7 +14229,9 @@ def get(self, name: str, **kwargs: Any) -> _models.RedTeam: @distributed_trace def list(self, **kwargs: Any) -> ItemPaged["_models.RedTeam"]: - """List a redteam by name. + """List redteams. + + Returns the redteams available in the current project. :return: An iterator like instance of RedTeam :rtype: ~azure.core.paging.ItemPaged[~azure.ai.projects.models.RedTeam] @@ -14166,7 +14321,9 @@ def get_next(next_link=None): def create( self, red_team: _models.RedTeam, *, content_type: str = "application/json", **kwargs: Any ) -> _models.RedTeam: - """Creates a redteam run. + """Create a redteam run. + + Submits a new redteam run for execution with the provided configuration. :param red_team: Redteam to be run. Required. :type red_team: ~azure.ai.projects.models.RedTeam @@ -14180,7 +14337,9 @@ def create( @overload def create(self, red_team: JSON, *, content_type: str = "application/json", **kwargs: Any) -> _models.RedTeam: - """Creates a redteam run. + """Create a redteam run. + + Submits a new redteam run for execution with the provided configuration. :param red_team: Redteam to be run. Required. :type red_team: JSON @@ -14194,7 +14353,9 @@ def create(self, red_team: JSON, *, content_type: str = "application/json", **kw @overload def create(self, red_team: IO[bytes], *, content_type: str = "application/json", **kwargs: Any) -> _models.RedTeam: - """Creates a redteam run. + """Create a redteam run. + + Submits a new redteam run for execution with the provided configuration. :param red_team: Redteam to be run. Required. :type red_team: IO[bytes] @@ -14208,7 +14369,9 @@ def create(self, red_team: IO[bytes], *, content_type: str = "application/json", @distributed_trace def create(self, red_team: Union[_models.RedTeam, JSON, IO[bytes]], **kwargs: Any) -> _models.RedTeam: - """Creates a redteam run. + """Create a redteam run. + + Submits a new redteam run for execution with the provided configuration. :param red_team: Redteam to be run. Is one of the following types: RedTeam, JSON, IO[bytes] Required. @@ -14313,6 +14476,8 @@ def create_or_update( ) -> _models.Routine: """Create or update a routine. + Creates a new routine or replaces an existing routine with the supplied definition. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. @@ -14338,6 +14503,8 @@ def create_or_update( ) -> _models.Routine: """Create or update a routine. + Creates a new routine or replaces an existing routine with the supplied definition. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Required. @@ -14356,6 +14523,8 @@ def create_or_update( ) -> _models.Routine: """Create or update a routine. + Creates a new routine or replaces an existing routine with the supplied definition. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Required. @@ -14382,6 +14551,8 @@ def create_or_update( ) -> _models.Routine: """Create or update a routine. + Creates a new routine or replaces an existing routine with the supplied definition. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Is either a JSON type or a IO[bytes] type. Required. @@ -14469,7 +14640,9 @@ def create_or_update( @distributed_trace def get(self, routine_name: str, **kwargs: Any) -> _models.Routine: - """Retrieve a routine. + """Get a routine. + + Retrieves the specified routine and its current configuration. :param routine_name: The unique name of the routine. Required. :type routine_name: str @@ -14536,6 +14709,8 @@ def get(self, routine_name: str, **kwargs: Any) -> _models.Routine: def enable(self, routine_name: str, **kwargs: Any) -> _models.Routine: """Enable a routine. + Enables the specified routine so it can be dispatched. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :return: Routine. The Routine is compatible with MutableMapping @@ -14601,6 +14776,8 @@ def enable(self, routine_name: str, **kwargs: Any) -> _models.Routine: def disable(self, routine_name: str, **kwargs: Any) -> _models.Routine: """Disable a routine. + Disables the specified routine so it no longer runs. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :return: Routine. The Routine is compatible with MutableMapping @@ -14668,6 +14845,8 @@ def list( ) -> ItemPaged["_models.Routine"]: """List routines. + Returns the routines available in the current project. + :keyword limit: The maximum number of routines to return. Default value is None. :paramtype limit: int :keyword before: Unsupported. Reserved for future backward pagination support. Default value is @@ -14745,6 +14924,8 @@ def get_next(_continuation_token=None): def delete(self, routine_name: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements """Delete a routine. + Deletes the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :return: None @@ -14806,6 +14987,8 @@ def list_runs( ) -> ItemPaged["_models.RoutineRun"]: """List prior runs for a routine. + Returns prior runs recorded for the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :keyword filter: An optional MLflow search-runs filter expression applied within the routine's @@ -14897,6 +15080,8 @@ def dispatch( ) -> _models.DispatchRoutineResult: """Queue an asynchronous routine dispatch. + Queues an asynchronous dispatch for the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. @@ -14916,6 +15101,8 @@ def dispatch( ) -> _models.DispatchRoutineResult: """Queue an asynchronous routine dispatch. + Queues an asynchronous dispatch for the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Required. @@ -14934,6 +15121,8 @@ def dispatch( ) -> _models.DispatchRoutineResult: """Queue an asynchronous routine dispatch. + Queues an asynchronous dispatch for the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Required. @@ -14957,6 +15146,8 @@ def dispatch( ) -> _models.DispatchRoutineResult: """Queue an asynchronous routine dispatch. + Queues an asynchronous dispatch for the specified routine. + :param routine_name: The unique name of the routine. Required. :type routine_name: str :param body: Is either a JSON type or a IO[bytes] type. Required. @@ -15058,6 +15249,8 @@ def __init__(self, *args, **kwargs) -> None: def delete(self, schedule_id: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements """Delete a schedule. + Deletes the specified schedule resource. + :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str :return: None @@ -15104,7 +15297,9 @@ def delete(self, schedule_id: str, **kwargs: Any) -> None: # pylint: disable=in @distributed_trace def get(self, schedule_id: str, **kwargs: Any) -> _models.Schedule: - """Get a schedule by id. + """Get a schedule. + + Retrieves the specified schedule resource. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -15171,7 +15366,9 @@ def list( enabled: Optional[bool] = None, **kwargs: Any ) -> ItemPaged["_models.Schedule"]: - """List all schedules. + """List schedules. + + Returns schedules that match the supplied type and enabled filters. :keyword type: Filter by the type of schedule. Known values are: "Evaluation" and "Insight". Default value is None. @@ -15268,7 +15465,9 @@ def get_next(next_link=None): def create_or_update( self, schedule_id: str, schedule: _models.Schedule, *, content_type: str = "application/json", **kwargs: Any ) -> _models.Schedule: - """Create or update operation template. + """Create or update a schedule. + + Creates a new schedule or updates an existing schedule with the supplied definition. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -15286,7 +15485,9 @@ def create_or_update( def create_or_update( self, schedule_id: str, schedule: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.Schedule: - """Create or update operation template. + """Create or update a schedule. + + Creates a new schedule or updates an existing schedule with the supplied definition. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -15304,7 +15505,9 @@ def create_or_update( def create_or_update( self, schedule_id: str, schedule: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.Schedule: - """Create or update operation template. + """Create or update a schedule. + + Creates a new schedule or updates an existing schedule with the supplied definition. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -15322,7 +15525,9 @@ def create_or_update( def create_or_update( self, schedule_id: str, schedule: Union[_models.Schedule, JSON, IO[bytes]], **kwargs: Any ) -> _models.Schedule: - """Create or update operation template. + """Create or update a schedule. + + Creates a new schedule or updates an existing schedule with the supplied definition. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -15396,7 +15601,9 @@ def create_or_update( @distributed_trace def get_run(self, schedule_id: str, run_id: str, **kwargs: Any) -> _models.ScheduleRun: - """Get a schedule run by id. + """Get a schedule run. + + Retrieves the specified run for a schedule. :param schedule_id: The unique identifier of the schedule. Required. :type schedule_id: str @@ -15471,7 +15678,9 @@ def list_runs( enabled: Optional[bool] = None, **kwargs: Any ) -> ItemPaged["_models.ScheduleRun"]: - """List all schedule runs. + """List schedule runs. + + Returns schedule runs that match the supplied filters. :param schedule_id: Identifier of the schedule. Required. :type schedule_id: str @@ -15598,7 +15807,9 @@ def create_version( policies: Optional[_models.ToolboxPolicies] = None, **kwargs: Any ) -> _models.ToolboxVersionObject: - """Create a new version of a toolbox. If the toolbox does not exist, it will be created. + """Create a new version of a toolbox. + + Creates a new toolbox version, provisioning the toolbox itself if it does not already exist. :param name: The name of the toolbox. If the toolbox does not exist, it will be created. Required. @@ -15628,7 +15839,9 @@ def create_version( def create_version( self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.ToolboxVersionObject: - """Create a new version of a toolbox. If the toolbox does not exist, it will be created. + """Create a new version of a toolbox. + + Creates a new toolbox version, provisioning the toolbox itself if it does not already exist. :param name: The name of the toolbox. If the toolbox does not exist, it will be created. Required. @@ -15647,7 +15860,9 @@ def create_version( def create_version( self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.ToolboxVersionObject: - """Create a new version of a toolbox. If the toolbox does not exist, it will be created. + """Create a new version of a toolbox. + + Creates a new toolbox version, provisioning the toolbox itself if it does not already exist. :param name: The name of the toolbox. If the toolbox does not exist, it will be created. Required. @@ -15675,7 +15890,9 @@ def create_version( policies: Optional[_models.ToolboxPolicies] = None, **kwargs: Any ) -> _models.ToolboxVersionObject: - """Create a new version of a toolbox. If the toolbox does not exist, it will be created. + """Create a new version of a toolbox. + + Creates a new toolbox version, provisioning the toolbox itself if it does not already exist. :param name: The name of the toolbox. If the toolbox does not exist, it will be created. Required. @@ -15779,6 +15996,8 @@ def create_version( def get(self, name: str, **kwargs: Any) -> _models.ToolboxObject: """Retrieve a toolbox. + Retrieves the specified toolbox and its current configuration. + :param name: The name of the toolbox to retrieve. Required. :type name: str :return: ToolboxObject. The ToolboxObject is compatible with MutableMapping @@ -15849,7 +16068,9 @@ def list( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.ToolboxObject"]: - """List all toolboxes. + """List toolboxes. + + Returns the toolboxes available in the current project. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -15940,7 +16161,9 @@ def list_versions( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.ToolboxVersionObject"]: - """List all versions of a toolbox. + """List toolbox versions. + + Returns the available versions for the specified toolbox. :param name: The name of the toolbox to list versions for. Required. :type name: str @@ -16028,6 +16251,8 @@ def get_next(_continuation_token=None): def get_version(self, name: str, version: str, **kwargs: Any) -> _models.ToolboxVersionObject: """Retrieve a specific version of a toolbox. + Retrieves the specified version of a toolbox by name and version identifier. + :param name: The name of the toolbox. Required. :type name: str :param version: The version identifier to retrieve. Required. @@ -16098,6 +16323,8 @@ def update( ) -> _models.ToolboxObject: """Update a toolbox to point to a specific version. + Updates the toolbox's default version pointer to the specified version. + :param name: The name of the toolbox to update. Required. :type name: str :keyword default_version: The version identifier that the toolbox should point to. When set, @@ -16117,6 +16344,8 @@ def update( ) -> _models.ToolboxObject: """Update a toolbox to point to a specific version. + Updates the toolbox's default version pointer to the specified version. + :param name: The name of the toolbox to update. Required. :type name: str :param body: Required. @@ -16135,6 +16364,8 @@ def update( ) -> _models.ToolboxObject: """Update a toolbox to point to a specific version. + Updates the toolbox's default version pointer to the specified version. + :param name: The name of the toolbox to update. Required. :type name: str :param body: Required. @@ -16153,6 +16384,8 @@ def update( ) -> _models.ToolboxObject: """Update a toolbox to point to a specific version. + Updates the toolbox's default version pointer to the specified version. + :param name: The name of the toolbox to update. Required. :type name: str :param body: Is either a JSON type or a IO[bytes] type. Required. @@ -16236,7 +16469,9 @@ def update( @distributed_trace def delete(self, name: str, **kwargs: Any) -> None: # pylint: disable=inconsistent-return-statements - """Delete a toolbox and all its versions. + """Delete a toolbox. + + Removes the specified toolbox along with all of its versions. :param name: The name of the toolbox to delete. Required. :type name: str @@ -16292,6 +16527,8 @@ def delete_version( # pylint: disable=inconsistent-return-statements ) -> None: """Delete a specific version of a toolbox. + Removes the specified version of a toolbox. + :param name: The name of the toolbox. Required. :type name: str :param version: The version identifier to delete. Required. @@ -16363,7 +16600,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def get(self, name: str, **kwargs: Any) -> _models.SkillDetails: - """Retrieves a skill. + """Retrieve a skill. + + Retrieves the specified skill and its current configuration. :param name: The unique name of the skill. Required. :type name: str @@ -16435,7 +16674,9 @@ def list( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.SkillDetails"]: - """Returns the list of all skills. + """List skills. + + Returns the skills available in the current project. :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the @@ -16522,6 +16763,8 @@ def update( ) -> _models.SkillDetails: """Update a skill. + Modifies the specified skill's configuration. + :param name: The name of the skill to update. Required. :type name: str :keyword default_version: The version identifier that the skill should point to. When set, the @@ -16541,6 +16784,8 @@ def update( ) -> _models.SkillDetails: """Update a skill. + Modifies the specified skill's configuration. + :param name: The name of the skill to update. Required. :type name: str :param body: Required. @@ -16559,6 +16804,8 @@ def update( ) -> _models.SkillDetails: """Update a skill. + Modifies the specified skill's configuration. + :param name: The name of the skill to update. Required. :type name: str :param body: Required. @@ -16577,6 +16824,8 @@ def update( ) -> _models.SkillDetails: """Update a skill. + Modifies the specified skill's configuration. + :param name: The name of the skill to update. Required. :type name: str :param body: Is either a JSON type or a IO[bytes] type. Required. @@ -16660,7 +16909,9 @@ def update( @distributed_trace def delete(self, name: str, **kwargs: Any) -> _models.DeleteSkillResult: - """Deletes a skill. + """Delete a skill. + + Removes the specified skill and its associated versions. :param name: The unique name of the skill. Required. :type name: str @@ -16733,7 +16984,9 @@ def create( default: Optional[bool] = None, **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill. If the skill does not exist, it will be created. + """Create a new version of a skill. + + Creates a new version of a skill. If the skill does not exist, it will be created. :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str @@ -16754,7 +17007,9 @@ def create( def create( self, name: str, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill. If the skill does not exist, it will be created. + """Create a new version of a skill. + + Creates a new version of a skill. If the skill does not exist, it will be created. :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str @@ -16772,7 +17027,9 @@ def create( def create( self, name: str, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill. If the skill does not exist, it will be created. + """Create a new version of a skill. + + Creates a new version of a skill. If the skill does not exist, it will be created. :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str @@ -16796,7 +17053,9 @@ def create( default: Optional[bool] = None, **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill. If the skill does not exist, it will be created. + """Create a new version of a skill. + + Creates a new version of a skill. If the skill does not exist, it will be created. :param name: The name of the skill. If the skill does not exist, it will be created. Required. :type name: str @@ -16883,7 +17142,9 @@ def create( def create_from_files( self, name: str, content: _models.CreateSkillVersionFromFilesBody, **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill from uploaded files via multipart form data. + """Create a skill version from uploaded files. + + Creates a new version of a skill from uploaded files via multipart form data. :param name: The name of the skill. Required. :type name: str @@ -16896,7 +17157,9 @@ def create_from_files( @overload def create_from_files(self, name: str, content: JSON, **kwargs: Any) -> _models.SkillVersion: - """Creates a new version of a skill from uploaded files via multipart form data. + """Create a skill version from uploaded files. + + Creates a new version of a skill from uploaded files via multipart form data. :param name: The name of the skill. Required. :type name: str @@ -16911,7 +17174,9 @@ def create_from_files(self, name: str, content: JSON, **kwargs: Any) -> _models. def create_from_files( self, name: str, content: Union[_models.CreateSkillVersionFromFilesBody, JSON], **kwargs: Any ) -> _models.SkillVersion: - """Creates a new version of a skill from uploaded files via multipart form data. + """Create a skill version from uploaded files. + + Creates a new version of a skill from uploaded files via multipart form data. :param name: The name of the skill. Required. :type name: str @@ -16992,7 +17257,9 @@ def list_versions( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.SkillVersion"]: - """List all versions of a skill. + """List skill versions. + + Returns the available versions for the specified skill. :param name: The name of the skill to list versions for. Required. :type name: str @@ -17080,6 +17347,8 @@ def get_next(_continuation_token=None): def get_version(self, name: str, version: str, **kwargs: Any) -> _models.SkillVersion: """Retrieve a specific version of a skill. + Retrieves the specified version of a skill by name and version identifier. + :param name: The name of the skill. Required. :type name: str :param version: The version identifier to retrieve. Required. @@ -17148,6 +17417,8 @@ def get_version(self, name: str, version: str, **kwargs: Any) -> _models.SkillVe def download(self, name: str, **kwargs: Any) -> Iterator[bytes]: """Download the zip content for the default version of a skill. + Downloads the zip content for the default version of a skill. + :param name: The name of the skill. Required. :type name: str :return: Iterator[bytes] @@ -17213,6 +17484,8 @@ def download(self, name: str, **kwargs: Any) -> Iterator[bytes]: def download_version(self, name: str, version: str, **kwargs: Any) -> Iterator[bytes]: """Download the zip content for a specific version of a skill. + Downloads the zip content for a specific version of a skill. + :param name: The name of the skill. Required. :type name: str :param version: The version to download content for. Required. @@ -17281,6 +17554,8 @@ def download_version(self, name: str, version: str, **kwargs: Any) -> Iterator[b def delete_version(self, name: str, version: str, **kwargs: Any) -> _models.DeleteSkillVersionResult: """Delete a specific version of a skill. + Removes the specified version of a skill. + :param name: The name of the skill. Required. :type name: str :param version: The version identifier to delete. Required. @@ -17366,9 +17641,9 @@ def __init__(self, *args, **kwargs) -> None: @distributed_trace def get_generation_job(self, job_id: str, **kwargs: Any) -> _models.DataGenerationJob: - """Get info about a data generation job. + """Get a data generation job. - Gets the details of a data generation job by its ID. + Retrieves the specified data generation job and its current status. :param job_id: The ID of the job. Required. :type job_id: str @@ -17443,7 +17718,7 @@ def list_generation_jobs( before: Optional[str] = None, **kwargs: Any ) -> ItemPaged["_models.DataGenerationJob"]: - """Returns a list of data generation jobs. + """List data generation jobs. Returns a list of data generation jobs. @@ -17535,9 +17810,9 @@ def create_generation_job( content_type: str = "application/json", **kwargs: Any ) -> _models.DataGenerationJob: - """Creates a data generation job. + """Create a data generation job. - Creates a data generation job. + Submits a new data generation job for asynchronous execution. :param job: The job to create. Required. :type job: ~azure.ai.projects.models.DataGenerationJob @@ -17556,9 +17831,9 @@ def create_generation_job( def create_generation_job( self, job: JSON, *, operation_id: Optional[str] = None, content_type: str = "application/json", **kwargs: Any ) -> _models.DataGenerationJob: - """Creates a data generation job. + """Create a data generation job. - Creates a data generation job. + Submits a new data generation job for asynchronous execution. :param job: The job to create. Required. :type job: JSON @@ -17582,9 +17857,9 @@ def create_generation_job( content_type: str = "application/json", **kwargs: Any ) -> _models.DataGenerationJob: - """Creates a data generation job. + """Create a data generation job. - Creates a data generation job. + Submits a new data generation job for asynchronous execution. :param job: The job to create. Required. :type job: IO[bytes] @@ -17607,9 +17882,9 @@ def create_generation_job( operation_id: Optional[str] = None, **kwargs: Any ) -> _models.DataGenerationJob: - """Creates a data generation job. + """Create a data generation job. - Creates a data generation job. + Submits a new data generation job for asynchronous execution. :param job: The job to create. Is one of the following types: DataGenerationJob, JSON, IO[bytes] Required. @@ -17692,9 +17967,9 @@ def create_generation_job( @distributed_trace def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.DataGenerationJob: - """Cancels a data generation job. + """Cancel a data generation job. - Cancels a data generation job by its ID. + Cancels the specified data generation job if it is still in progress. :param job_id: The ID of the job to cancel. Required. :type job_id: str @@ -17761,9 +18036,9 @@ def cancel_generation_job(self, job_id: str, **kwargs: Any) -> _models.DataGener def delete_generation_job( # pylint: disable=inconsistent-return-statements self, job_id: str, **kwargs: Any ) -> None: - """Deletes a data generation job. + """Delete a data generation job. - Deletes a data generation job by its ID. + Removes the specified data generation job and its associated output. :param job_id: The ID of the job to delete. Required. :type job_id: str diff --git a/sdk/ai/azure-ai-projects/docs/subclients.md b/sdk/ai/azure-ai-projects/docs/subclients.md index e6b40e3e6e1b..e6e1523ad1e2 100644 --- a/sdk/ai/azure-ai-projects/docs/subclients.md +++ b/sdk/ai/azure-ai-projects/docs/subclients.md @@ -4,7 +4,7 @@ This document lists all sub-clients available on `AIProjectClient` and their pub ## Summary -There are a total of 139 unique public methods across all sub-clients. +There are a total of 134 unique public methods across all sub-clients. ### Top-level Sub-clients (stable operations) @@ -22,13 +22,13 @@ There are a total of 139 unique public methods across all sub-clients. | Subclient | Class Name | Methods Count | |-----------|------------|----------------| -| `beta.agents` | BetaAgentsOperations | 24 | +| `beta.agents` | BetaAgentsOperations | 18 | | `beta.datasets` | BetaDatasetsOperations | 5 | | `beta.evaluation_taxonomies` | BetaEvaluationTaxonomiesOperations | 5 | | `beta.evaluators` | BetaEvaluatorsOperations | 13 | | `beta.insights` | BetaInsightsOperations | 3 | | `beta.memory_stores` | BetaMemoryStoresOperations | 13 | -| `beta.models` | BetaModelsOperations | 8 | +| `beta.models` | BetaModelsOperations | 9 | | `beta.red_teams` | BetaRedTeamsOperations | 3 | | `beta.routines` | BetaRoutinesOperations | 8 | | `beta.schedules` | BetaSchedulesOperations | 6 | @@ -38,7 +38,7 @@ There are a total of 139 unique public methods across all sub-clients. ## Method list table -Alphabetically sorted, with ".beta" sub-client at the end. +Alphabetically sorted, with ".beta" sub-client at the end. If the method is a new hand-written method, there will be an asterisk at the end. ``` .agents.create_version @@ -50,8 +50,8 @@ Alphabetically sorted, with ".beta" sub-client at the end. .agents.list .agents.list_versions -.connections.get -.connections.get_default +.connections.get* +.connections.get_default* .connections.list .datasets.create_or_update @@ -61,8 +61,8 @@ Alphabetically sorted, with ".beta" sub-client at the end. .datasets.list .datasets.list_versions .datasets.pending_upload -.datasets.upload_file -.datasets.upload_folder +.datasets.upload_file* +.datasets.upload_folder* .deployments.get .deployments.list @@ -78,7 +78,7 @@ Alphabetically sorted, with ".beta" sub-client at the end. .indexes.list .indexes.list_versions -.telemetry.get_application_insights_connection_string +.telemetry.get_application_insights_connection_string* .beta.agents.cancel_optimization_job .beta.agents.create_optimization_job @@ -89,21 +89,15 @@ Alphabetically sorted, with ".beta" sub-client at the end. .beta.agents.delete_session_file .beta.agents.download_code .beta.agents.download_session_file -.beta.agents.get_candidate_file -.beta.agents.get_optimization_candidate -.beta.agents.get_optimization_candidate_config -.beta.agents.get_optimization_candidate_results .beta.agents.get_optimization_job .beta.agents.get_session .beta.agents.get_session_log_stream -.beta.agents.list_optimization_candidates .beta.agents.list_optimization_jobs .beta.agents.list_session_files .beta.agents.list_sessions .beta.agents.patch_agent_details -.beta.agents.promote_candidate .beta.agents.stop_session -.beta.agents.upload_session_file +.beta.agents.upload_session_file* .beta.datasets.cancel_generation_job .beta.datasets.create_generation_job @@ -135,7 +129,7 @@ Alphabetically sorted, with ".beta" sub-client at the end. .beta.insights.get .beta.insights.list -.beta.memory_stores.begin_update_memories +.beta.memory_stores.begin_update_memories* .beta.memory_stores.create .beta.memory_stores.create_memory .beta.memory_stores.delete @@ -145,16 +139,17 @@ Alphabetically sorted, with ".beta" sub-client at the end. .beta.memory_stores.get_memory .beta.memory_stores.list .beta.memory_stores.list_memories -.beta.memory_stores.search_memories +.beta.memory_stores.search_memories* .beta.memory_stores.update .beta.memory_stores.update_memory -.beta.models.pending_create_version +.beta.models.create* .beta.models.delete .beta.models.get .beta.models.get_credentials .beta.models.list .beta.models.list_versions +.beta.models.pending_create_version .beta.models.pending_upload .beta.models.update diff --git a/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd b/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd index 029f54360f28..1001de9c2f8e 100644 --- a/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd +++ b/sdk/ai/azure-ai-projects/post-emitter-fixes.cmd @@ -70,24 +70,8 @@ REM Fix Sphinx docutils warnings in get_session_log_stream docstrings (sync + as REM The emitter wraps bullet/code-block lines with insufficient indentation. powershell -Command "$files='azure\ai\projects\operations\_operations.py','azure\ai\projects\aio\operations\_operations.py'; foreach ($f in $files) { $c=Get-Content $f -Raw; $c=$c -replace 'schema\r?\n\s+is not contractual and may include additional keys or change format\r?\n\s+over time [^\r\n]*clients should treat it as an opaque string\)', 'schema is not contractual and may include additional keys or change format over time; clients should treat it as an opaque string)'; $c=$c -replace '(message\":\"Starting)\r?\n\s+(FoundryCBAgent server on port 8088\"})', '$1 $2'; $c=$c -replace '(message\":\"INFO: Application)\r?\n\s+(startup complete\.\"})', '$1 $2'; $c=$c -replace '(message\":\"Successfully)\r?\n\s+(connected to container\"})', '$1 $2'; $c=$c -replace '(message\":\"No logs since)\r?\n\s+(last 60 seconds\"})', '$1 $2'; Set-Content $f $c -NoNewline }" -REM Reorder loops in `prepare_multipart_form_data` and synthesize filenames for -REM bare bytes/str/IO file entries (azure\ai\projects\_utils\utils.py). -REM 1) The emitter generates the multipart-file loop before the data-field loop, -REM so JSON metadata parts end up after large binary file parts in the encoded -REM body. Some streaming server-side parsers (e.g. the Foundry hosted-agents -REM `create_agent_version_from_code` endpoint) require the small JSON metadata -REM parts to precede the binary file parts; otherwise they report the metadata -REM part as missing. -REM 2) When callers pass bare bytes (e.g. `Path("x.zip").read_bytes()`) for a -REM multipart file field, the part is emitted without a `filename=` in its -REM Content-Disposition, and servers (e.g. Foundry `create_from_files` for -REM skills) reject it with "At least one file must be uploaded". This rewrite -REM synthesizes a filename for bare bytes/str/IO entries (from `.name` when -REM available, otherwise a stable default) so the part is encoded as a file. -powershell -Command "$f='azure\ai\projects\_utils\utils.py'; $c=Get-Content $f -Raw; if ($c -notmatch '(?m)^import os\r?$') { $c=$c -replace '(?m)^import json\r?$', \"import json`r`nimport os\" }; if ($c -notmatch ':param field_name: The multipart form field name') { $c=$c -replace '(?s)def _normalize_multipart_file_entry\(.*?return \(filename, entry\)\r?\n\r?\n\r?\n', ''; $helper=(@('def _normalize_multipart_file_entry(field_name: str, entry: Any, index: int) -> Any:',' \"\"\"Ensure each multipart file entry carries a filename so that it is encoded',' as a file part (with ``filename=``) rather than a plain form field.','',' Servers commonly distinguish multipart file parts from form-data parts by',' the presence of ``filename=`` in the part''s ``Content-Disposition`` header.',' When callers pass bare bytes / str / IO objects (e.g.',' ``Path(\"x.zip\").read_bytes()``), the underlying HTTP client emits the part',' without a filename, which several Foundry endpoints reject with errors like',' \"At least one file must be uploaded\". This helper synthesizes a filename',' from the IO object''s ``name`` attribute when available, otherwise falls',' back to a stable default.','',' :param field_name: The multipart form field name, used as a fallback filename.',' :type field_name: str',' :param entry: The file entry to normalize. May be a tuple, bytes, str, or IO object.',' :type entry: any',' :param index: Position of the entry within its field''s list, used to disambiguate fallback filenames.',' :type index: int',' :return: A ``(filename, entry)`` tuple if ``entry`` was not already a tuple, otherwise ``entry`` unchanged.',' :rtype: any',' \"\"\"',' if isinstance(entry, tuple):',' return entry',' filename: Optional[str] = None',' name_attr = getattr(entry, \"name\", None)',' if isinstance(name_attr, str) and name_attr:',' filename = os.path.basename(name_attr)',' if not filename:',' filename = f\"{field_name}_{index}\" if index else field_name',' return (filename, entry)','','') -join [Environment]::NewLine); $c=$c -replace '(?m)^def prepare_multipart_form_data\(', ($helper + 'def prepare_multipart_form_data('); $pattern='(?s) files: list\[FileType\] = \[\]\r?\n.*? return files'; $new=(@(' files: list[FileType] = []','',' # Append data fields first so they appear before file parts in the encoded',' # multipart body. Some streaming server-side parsers (e.g. the Foundry',' # hosted-agents `create_agent_version_from_code` endpoint) require small',' # JSON metadata parts to precede large binary file parts; otherwise they',' # report the metadata part as missing.',' for data_field in data_fields:',' data_entry = body.get(data_field)',' if data_entry:',' files.append((data_field, str(serialize_multipart_data_entry(data_entry))))','',' for multipart_field in multipart_fields:',' multipart_entry = body.get(multipart_field)',' if isinstance(multipart_entry, list):',' for idx, e in enumerate(multipart_entry):',' files.append((multipart_field, _normalize_multipart_file_entry(multipart_field, e, idx)))',' elif multipart_entry:',' files.append((multipart_field, _normalize_multipart_file_entry(multipart_field, multipart_entry, 0)))','',' return files') -join [Environment]::NewLine); $c=[regex]::Replace($c, $pattern, $new) }; Set-Content $f $c -NoNewline" - REM Finishing by running 'black' tool to format code. pip install black -black --config ../../../eng/black-pyproject.toml . || echo black not found, skipping formatting. +black --config ../../../eng/black-pyproject.toml . diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_routines_crud.py b/sdk/ai/azure-ai-projects/samples/routines/sample_routines_crud.py similarity index 97% rename from sdk/ai/azure-ai-projects/samples/hosted_agents/sample_routines_crud.py rename to sdk/ai/azure-ai-projects/samples/routines/sample_routines_crud.py index f71a74b33b48..45aadc1f8b26 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_routines_crud.py +++ b/sdk/ai/azure-ai-projects/samples/routines/sample_routines_crud.py @@ -58,7 +58,7 @@ def print_routine_state(routine: Routine) -> None: with ( DefaultAzureCredential() as credential, - AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, ): routine_name = "sample-routine" diff --git a/sdk/ai/azure-ai-projects/samples/routines/sample_routines_with_dispatch.py b/sdk/ai/azure-ai-projects/samples/routines/sample_routines_with_dispatch.py new file mode 100644 index 000000000000..1c5164cf00c8 --- /dev/null +++ b/sdk/ai/azure-ai-projects/samples/routines/sample_routines_with_dispatch.py @@ -0,0 +1,130 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +""" +DESCRIPTION: + This sample demonstrates how to create a Routine with a manual (custom) + trigger and fire it on demand via `dispatch(...)`, then record the + resulting run by polling `list_runs(...)` using the synchronous + AIProjectClient. + + The routine is bound to an existing hosted agent. Because the trigger is + a `CustomRoutineTrigger`, the routine never fires on its own; the sample + explicitly invokes it with `project_client.beta.routines.dispatch(...)` + passing an `InvokeAgentResponsesApiDispatchPayload` carrying the input + sent to the agent. The sample then polls the run history until a + terminal phase is reached (or a deadline elapses), printing each + observed transition. The routine is deleted at the end of the sample. + + Routines are currently a preview feature. In the Python SDK, you access + these operations via `project_client.beta.routines`. + +USAGE: + python sample_routines_with_dispatch.py + + Before running the sample: + + pip install "azure-ai-projects>=2.2.0" python-dotenv + + Set these environment variables with your own values: + 1) FOUNDRY_PROJECT_ENDPOINT - The Azure AI Project endpoint, as found in the Overview + page of your Microsoft Foundry portal. + 2) FOUNDRY_HOSTED_AGENT_NAME - The name of an existing Hosted Agent to invoke + when the routine is dispatched. +""" + +import json +import os +import time + +from dotenv import load_dotenv + +from azure.core.exceptions import ResourceNotFoundError +from azure.identity import DefaultAzureCredential + +from azure.ai.projects import AIProjectClient +from azure.ai.projects.models import ( + CustomRoutineTrigger, + InvokeAgentResponsesApiDispatchPayload, + InvokeAgentResponsesApiRoutineAction, + RoutineRun, + RoutineRunPhase, +) + +load_dotenv() + +endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] +agent_name = os.environ["FOUNDRY_HOSTED_AGENT_NAME"] + + +with ( + DefaultAzureCredential() as credential, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, +): + + routine_name = "sample-routine-dispatch" + + try: + project_client.beta.routines.delete(routine_name) + print(f"Routine `{routine_name}` deleted") + except ResourceNotFoundError: + pass + + created = project_client.beta.routines.create_or_update( + routine_name, + description="Routine used by the dispatch sample.", + enabled=True, + triggers={ + "manual": CustomRoutineTrigger( + provider="sample-provider", + event_name="sample-event", + parameters={}, + ), + }, + action=InvokeAgentResponsesApiRoutineAction(agent_name=agent_name), + ) + print(f"Created routine: {created.name} enabled={created.enabled}") + + dispatch_result = project_client.beta.routines.dispatch( + routine_name, + payload=InvokeAgentResponsesApiDispatchPayload( + input="Say hello from a manually dispatched routine.", + ), + ) + print(f"Dispatched routine: dispatch_id={dispatch_result.dispatch_id} task_id={dispatch_result.task_id}") + + seen_phases: dict[str, RoutineRunPhase] = {} + final_run: RoutineRun | None = None + + deadline = time.monotonic() + 180 + while time.monotonic() < deadline: + runs = list(project_client.beta.routines.list_runs(routine_name, limit=20, order="desc")) + for run in runs: + if seen_phases.get(run.id) == run.phase: + continue + seen_phases[run.id] = run.phase # type: ignore[assignment] + print( + f" - run_id={run.id} phase={run.phase} status={run.status} " + f"trigger_type={run.trigger_type} triggered_at={run.triggered_at} ended_at={run.ended_at}" + ) + if str(run.status).lower() == "finished": + final_run = run + + if final_run is not None: + break + time.sleep(5) + + if final_run: + print("Final run:") + print(json.dumps(final_run.as_dict(), indent=2, default=str)) + # Note: retrieving the response body produced by a routine-dispatched + # run via `openai_client.responses.retrieve(final_run.response_id)` is + # not yet supported by the service for this scenario. + else: + print("Dispatch did not produce a terminal run within the deadline.") + + project_client.beta.routines.delete(routine_name) + print("Routine deleted") diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_routines_with_schedule_trigger.py b/sdk/ai/azure-ai-projects/samples/routines/sample_routines_with_schedule_trigger.py similarity index 99% rename from sdk/ai/azure-ai-projects/samples/hosted_agents/sample_routines_with_schedule_trigger.py rename to sdk/ai/azure-ai-projects/samples/routines/sample_routines_with_schedule_trigger.py index 7bd7c0780661..7f3b6404d644 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_routines_with_schedule_trigger.py +++ b/sdk/ai/azure-ai-projects/samples/routines/sample_routines_with_schedule_trigger.py @@ -62,7 +62,7 @@ def main() -> None: with ( DefaultAzureCredential() as credential, - AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, ): routine_name = "sample-routine-schedule" diff --git a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_routines_with_timer_trigger.py b/sdk/ai/azure-ai-projects/samples/routines/sample_routines_with_timer_trigger.py similarity index 98% rename from sdk/ai/azure-ai-projects/samples/hosted_agents/sample_routines_with_timer_trigger.py rename to sdk/ai/azure-ai-projects/samples/routines/sample_routines_with_timer_trigger.py index 96ed2f533e81..1ef31fb4a108 100644 --- a/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_routines_with_timer_trigger.py +++ b/sdk/ai/azure-ai-projects/samples/routines/sample_routines_with_timer_trigger.py @@ -74,7 +74,7 @@ with ( DefaultAzureCredential() as credential, - AIProjectClient(endpoint=endpoint, credential=credential, allow_preview=True) as project_client, + AIProjectClient(endpoint=endpoint, credential=credential) as project_client, ): # Azure Monitor exporter: same spans also sent to the Application Insights # resource attached to the Foundry project, viewable in the "Tracing" tab diff --git a/sdk/ai/azure-ai-projects/tests/foundry_features_header/foundry_features_header_test_base.py b/sdk/ai/azure-ai-projects/tests/foundry_features_header/foundry_features_header_test_base.py index 70c218bc1e47..8aaa0adc7fcc 100644 --- a/sdk/ai/azure-ai-projects/tests/foundry_features_header/foundry_features_header_test_base.py +++ b/sdk/ai/azure-ai-projects/tests/foundry_features_header/foundry_features_header_test_base.py @@ -46,7 +46,7 @@ "toolboxes": "Toolboxes=V1Preview", "skills": "Skills=V1Preview", "datasets": "DataGenerationJobs=V1Preview", - "agents": "HostedAgents=V1Preview,WorkflowAgents=V1Preview,AgentEndpoints=V1Preview,CodeAgents=V1Preview,ExternalAgents=V1Preview,AgentsOptimization=V1Preview", + "agents": "HostedAgents=V1Preview,WorkflowAgents=V1Preview,AgentEndpoints=V1Preview,CodeAgents=V1Preview,ExternalAgents=V1Preview,AgentsOptimization=V2Preview", } # Methods on .beta sub-clients that are NOT simple one-HTTP-call wrappers and @@ -84,7 +84,7 @@ # The test id is derived automatically from method_name. pytest.param( "agents.create_version", - "HostedAgents=V1Preview,WorkflowAgents=V1Preview,AgentEndpoints=V1Preview,CodeAgents=V1Preview,ExternalAgents=V1Preview,AgentsOptimization=V1Preview", + "HostedAgents=V1Preview,WorkflowAgents=V1Preview,AgentEndpoints=V1Preview,CodeAgents=V1Preview,ExternalAgents=V1Preview,AgentsOptimization=V2Preview", ), pytest.param( "evaluation_rules.create_or_update", diff --git a/sdk/ai/azure-ai-projects/tests/redteams/test_redteams.py b/sdk/ai/azure-ai-projects/tests/redteams/test_redteams.py index 3285c1afb474..1c5727c583b9 100644 --- a/sdk/ai/azure-ai-projects/tests/redteams/test_redteams.py +++ b/sdk/ai/azure-ai-projects/tests/redteams/test_redteams.py @@ -30,7 +30,6 @@ def test_red_teams(self, **kwargs): with self.create_client(**kwargs) as project_client: - # [START red_team_sample] print("Creating a Red Team scan for direct model testing") # Create target configuration for testing an Azure OpenAI model diff --git a/sdk/ai/azure-ai-projects/tests/redteams/test_redteams_async.py b/sdk/ai/azure-ai-projects/tests/redteams/test_redteams_async.py index 3fda37b15180..17f494cb2f8e 100644 --- a/sdk/ai/azure-ai-projects/tests/redteams/test_redteams_async.py +++ b/sdk/ai/azure-ai-projects/tests/redteams/test_redteams_async.py @@ -30,7 +30,6 @@ async def test_red_teams_async(self, **kwargs): async with self.create_async_client(**kwargs) as project_client: - # [START red_team_sample] print("Creating a Red Team scan for direct model testing") # Create target configuration for testing an Azure OpenAI model diff --git a/sdk/ai/azure-ai-projects/tests/samples/test_samples.py b/sdk/ai/azure-ai-projects/tests/samples/test_samples.py index c3fa6905f7df..d27d0e8b5975 100644 --- a/sdk/ai/azure-ai-projects/tests/samples/test_samples.py +++ b/sdk/ai/azure-ai-projects/tests/samples/test_samples.py @@ -240,31 +240,52 @@ def test_chat_completions_samples(self, sample_path: str, **kwargs) -> None: "FOUNDRY_HOSTED_AGENT_REMOTE_BUILD": "true", }, ), - AdditionalSampleTestDetail( - test_id="sample_routines_with_schedule_trigger", - sample_filename="sample_routines_with_schedule_trigger.py", - env_vars={ - "POLL_INTERVAL_SECONDS": "300", - }, - ), ] ) @pytest.mark.parametrize( "sample_path", get_sample_paths( "hosted_agents", + samples_to_skip=[], + ), + ) + @SamplePathPasser() + @recorded_by_proxy(RecordedTransport.AZURE_CORE, RecordedTransport.HTTPX) + def test_hosted_agents_samples(self, sample_path: str, **kwargs) -> None: + if os.path.basename(sample_path).startswith("sample_create_hosted_agent") and not self.is_live: + pytest.skip("sample_create_hosted_agent.py is skipped in replay mode due to RBAC complications.") + env_vars = get_sample_env_vars(kwargs) + executor = SyncSampleExecutor(self, sample_path, env_vars=env_vars, **kwargs) + executor.execute() + executor.validate_print_calls_by_llm() + + @servicePreparer() + # @additionalSampleTests( + # [ + # AdditionalSampleTestDetail( + # test_id="sample_routines_with_schedule_trigger", + # sample_filename="sample_routines_with_schedule_trigger.py", + # env_vars={ + # "POLL_INTERVAL_SECONDS": "300", + # }, + # ), + # ] + # ) + @pytest.mark.parametrize( + "sample_path", + get_sample_paths( + "routines", samples_to_skip=[ "sample_routines_with_schedule_trigger.py", # Specify through AdditionalSampleTestDetail "sample_routines_crud.py", # Skipped due to service serialization issues "sample_routines_with_timer_trigger.py", # Skipped due to service serialization issues + "sample_routines_with_dispatch.py", # 403: test identity lacks routines/dispatch data-action ], ), ) @SamplePathPasser() @recorded_by_proxy(RecordedTransport.AZURE_CORE, RecordedTransport.HTTPX) - def test_hosted_agents_samples(self, sample_path: str, **kwargs) -> None: - if os.path.basename(sample_path).startswith("sample_create_hosted_agent") and not self.is_live: - pytest.skip("sample_create_hosted_agent.py is skipped in replay mode due to RBAC complications.") + def test_routines_samples(self, sample_path: str, **kwargs) -> None: env_vars = get_sample_env_vars(kwargs) executor = SyncSampleExecutor(self, sample_path, env_vars=env_vars, **kwargs) executor.execute() diff --git a/sdk/ai/azure-ai-projects/tsp-location.yaml b/sdk/ai/azure-ai-projects/tsp-location.yaml new file mode 100644 index 000000000000..52fc108fa8f3 --- /dev/null +++ b/sdk/ai/azure-ai-projects/tsp-location.yaml @@ -0,0 +1,29 @@ +directory: specification/ai-foundry/data-plane/Foundry/src/sdk-python-js-azure-ai-projects +commit: 1a46f23eb6ff991b777135f70d7d0782fc0fb324 +repo: Azure/azure-rest-api-specs +additionalDirectories: + - specification/ai-foundry/data-plane/Foundry/src/agents + - specification/ai-foundry/data-plane/Foundry/src/agents-optimization + - specification/ai-foundry/data-plane/Foundry/src/agents-session-files + - specification/ai-foundry/data-plane/Foundry/src/common + - specification/ai-foundry/data-plane/Foundry/src/connections + - specification/ai-foundry/data-plane/Foundry/src/data_generation_jobs + - specification/ai-foundry/data-plane/Foundry/src/datasets + - specification/ai-foundry/data-plane/Foundry/src/deployments + - specification/ai-foundry/data-plane/Foundry/src/evaluation-rules + - specification/ai-foundry/data-plane/Foundry/src/evaluation-taxonomies + - specification/ai-foundry/data-plane/Foundry/src/evaluators + - specification/ai-foundry/data-plane/Foundry/src/indexes + - specification/ai-foundry/data-plane/Foundry/src/insights + - specification/ai-foundry/data-plane/Foundry/src/memory-stores + - specification/ai-foundry/data-plane/Foundry/src/models + - specification/ai-foundry/data-plane/Foundry/src/openai-conversations + - specification/ai-foundry/data-plane/Foundry/src/openai-evaluations + - specification/ai-foundry/data-plane/Foundry/src/openai-responses + - specification/ai-foundry/data-plane/Foundry/src/red-teams + - specification/ai-foundry/data-plane/Foundry/src/routines + - specification/ai-foundry/data-plane/Foundry/src/schedules + - specification/ai-foundry/data-plane/Foundry/src/sdk-common + - specification/ai-foundry/data-plane/Foundry/src/skills + - specification/ai-foundry/data-plane/Foundry/src/toolboxes + - specification/ai-foundry/data-plane/Foundry/src/tools diff --git a/sdk/ai/azure-ai-projects/tsp-location.yaml.saved b/sdk/ai/azure-ai-projects/tsp-location.yaml.saved deleted file mode 100644 index ce66d3088118..000000000000 --- a/sdk/ai/azure-ai-projects/tsp-location.yaml.saved +++ /dev/null @@ -1,4 +0,0 @@ -directory: specification/ai-foundry/data-plane/Foundry -commit: e041b5a178996b62fe86a267dc468df5c1677c54 -repo: Azure/azure-rest-api-specs -additionalDirectories: diff --git a/sdk/storage/azure-storage-queue/tests/test_queue.py b/sdk/storage/azure-storage-queue/tests/test_queue.py index 9d52bed47640..f831b61cad69 100644 --- a/sdk/storage/azure-storage-queue/tests/test_queue.py +++ b/sdk/storage/azure-storage-queue/tests/test_queue.py @@ -1492,7 +1492,7 @@ def test_queue_cross_tenant_sas(self, **kwargs): start = datetime.utcnow() expiry = datetime.utcnow() + timedelta(hours=1) token = token_credential.get_token("https://storage.azure.com/.default") - decoded = jwt.decode(token.token, options={"verify_signature": False}) + decoded = jwt.decode(token.token, options={"verify_signature": False}) # pylint: disable=no-member user_delegation_oid = decoded.get("oid") delegated_user_tid = decoded.get("tid") user_delegation_key = qsc.get_user_delegation_key( diff --git a/sdk/storage/azure-storage-queue/tests/test_queue_async.py b/sdk/storage/azure-storage-queue/tests/test_queue_async.py index afd0c99f95c5..a890d4136fc8 100644 --- a/sdk/storage/azure-storage-queue/tests/test_queue_async.py +++ b/sdk/storage/azure-storage-queue/tests/test_queue_async.py @@ -1507,7 +1507,7 @@ async def test_queue_cross_tenant_sas(self, **kwargs): start = datetime.utcnow() expiry = datetime.utcnow() + timedelta(hours=1) token = await token_credential.get_token("https://storage.azure.com/.default") - decoded = jwt.decode(token.token, options={"verify_signature": False}) + decoded = jwt.decode(token.token, options={"verify_signature": False}) # pylint: disable=no-member user_delegation_oid = decoded.get("oid") delegated_user_tid = decoded.get("tid") user_delegation_key = await qsc.get_user_delegation_key(